是否有更短的方法来写复合'if'条件?

Opt*_*mus 35 c c++ if-statement

只是代替的:

if  ( ch == 'A' || ch == 'B' || ch == 'C' || .....
Run Code Online (Sandbox Code Playgroud)

例如,要做到这样:

if  ( ch == 'A', 'B', 'C', ...
Run Code Online (Sandbox Code Playgroud)

是否有更短的方式来总结条件?

usr*_*usr 85

strchr() 可用于查看字符是否在列表中.

const char* list = "ABCXZ";
if (strchr(list, ch)) {
  // 'ch' is 'A', 'B', 'C', 'X', or 'Z'
}
Run Code Online (Sandbox Code Playgroud)

  • @ monkeyman79你等待几十秒对某个按钮的反应并不是因为人们编写了这样的代码.这是因为,当实际*优化的时候,人们没有正确找到真正的瓶颈.而且,"糟糕的回应"有点苛刻; OP甚至没有询问围绕此代码的性能瓶颈.这是一个100%的答案. (15认同)
  • 也许这是个人风格,但我会消除中间的`list`变量并直接将字符串作为参数传递. (8认同)
  • 这个快速而肮脏的解决方案实际上有一个问题:`ch ='\ 0'`也将**匹配.如果你已经知道`ch`不能为null,那就没关系,否则,这是一个bug.您可以通过以下方式避免这种情况:`if(memchr("ABCXY",ch,5){...}`,无论如何都应该更快. (6认同)
  • @L然而,它与连续的角色完美配合. (5认同)
  • 如果角色是随机的,那就好了,但如果它们是连续的则不是 (3认同)

dbu*_*ush 39

在这种情况下,您可以使用switch:

switch (ch) {
case 'A':
case 'B':
case 'C':
    // do something
    break;
case 'D':
case 'E':
case 'F':
    // do something else
    break;
...
}
Run Code Online (Sandbox Code Playgroud)

虽然这比使用稍微冗长strchr,但它不涉及任何函数调用.它也适用于C和C++.

请注意,由于使用了逗号运算符,您建议的替代语法将无法正常工作:

if  ( ch == 'A', 'B', 'C', 'D', 'E', 'F' )
Run Code Online (Sandbox Code Playgroud)

这首先比较ch'A',然后丢弃结果.然后'B'评估并丢弃,然后'C'等等,直到'F'评估为止.然后'F'成为条件的值.由于任何非零值在布尔上下文中计算为true(并且'F'非零),因此上述表达式将始终为true.


Ric*_*ges 33

模板允许我们以这种方式表达自己:

if (range("A-F").contains(ch)) { ... }
Run Code Online (Sandbox Code Playgroud)

它需要一点管道,你可以把它放在一个图书馆.

这实际上编译得非常有效(至少在gcc和clang上).

#include <cstdint>
#include <tuple>
#include <utility>
#include <iostream>

namespace detail {
    template<class T>
    struct range
    {
        constexpr range(T first, T last)
        : _begin(first), _end(last)
        {}

        constexpr T begin() const { return _begin; }
        constexpr T end() const { return _end; }

        template<class U>
        constexpr bool contains(const U& u) const
        {
            return _begin <= u and u <= _end;
        }

    private:
        T _begin;
        T _end;
    };

    template<class...Ranges>
    struct ranges
    {
        constexpr ranges(Ranges...ranges) : _ranges(std::make_tuple(ranges...)) {}

        template<class U>
        struct range_check
        {
            template<std::size_t I>
            bool contains_impl(std::integral_constant<std::size_t, I>,
                               const U& u,
                               const std::tuple<Ranges...>& ranges) const
            {
                return std::get<I>(ranges).contains(u)
                or contains_impl(std::integral_constant<std::size_t, I+1>(),u, ranges);
            }

            bool contains_impl(std::integral_constant<std::size_t, sizeof...(Ranges)>,
                               const U& u,
                               const std::tuple<Ranges...>& ranges) const
            {
                return false;
            }


            constexpr bool operator()(const U& u, std::tuple<Ranges...> const& ranges) const
            {
                return contains_impl(std::integral_constant<std::size_t, 0>(), u, ranges);
            }
        };

        template<class U>
        constexpr bool contains(const U& u) const
        {
            range_check<U> check {};
            return check(u, _ranges);
        }

        std::tuple<Ranges...> _ranges;
    };
}

template<class T>
constexpr auto range(T t) { return detail::range<T>(t, t); }

template<class T>
constexpr auto range(T from, T to) { return detail::range<T>(from, to); }

// this is the little trick which turns an ascii string into
// a range of characters at compile time. It's probably a bit naughty
// as I am not checking syntax. You could write "ApZ" and it would be
// interpreted as "A-Z".
constexpr auto range(const char (&s)[4])
{
    return range(s[0], s[2]);
}

template<class...Rs>
constexpr auto ranges(Rs...rs)
{
    return detail::ranges<Rs...>(rs...);
}

int main()
{
    std::cout << range(1,7).contains(5) << std::endl;
    std::cout << range("a-f").contains('b') << std::endl;

    auto az = ranges(range('a'), range('z'));
    std::cout << az.contains('a') << std::endl;
    std::cout << az.contains('z') << std::endl;
    std::cout << az.contains('p') << std::endl;

    auto rs = ranges(range("a-f"), range("p-z"));
    for (char ch = 'a' ; ch <= 'z' ; ++ch)
    {
        std::cout << ch << rs.contains(ch) << " ";
    }
    std::cout << std::endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

预期产量:

1
1
1
1
0
a1 b1 c1 d1 e1 f1 g0 h0 i0 j0 k0 l0 m0 n0 o0 p1 q1 r1 s1 t1 u1 v1 w1 x1 y1 z1 
Run Code Online (Sandbox Code Playgroud)

作为参考,这是我原来的答案:

template<class X, class Y>
bool in(X const& x, Y const& y)
{
    return x == y;
}

template<class X, class Y, class...Rest>
bool in(X const& x, Y const& y, Rest const&...rest)
{
    return in(x, y) or in(x, rest...);
}

int main()
{
    int ch = 6;
    std::cout << in(ch, 1,2,3,4,5,6,7) << std::endl;

    std::string foo = "foo";
    std::cout << in(foo, "bar", "foo", "baz") << std::endl;

    std::cout << in(foo, "bar", "baz") << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

  • 在内心深处,每个人都想在LISP中编写代码. (37认同)
  • 这很好,但如果只需要`char`比较,看起来就像一个巨大的矫枉过正. (7认同)
  • 在内心深处,每个人都想从角支架键上取下油漆. (5认同)
  • @RichardHodges你的意思是,教训是总是写一个基本案例,或者教训是错误是不可读的? (3认同)
  • @hyde谁在乎,真的.OP评论的观点非常明确,没有进一步的迂腐分析.除了不受欢迎的评论噪音之外,你的提问线都无法获得. (2认同)

Xir*_*ema 14

如果你需要针对任意一组字符检查一个字符,你可以尝试写这个:

std::set<char> allowed_chars = {'A', 'B', 'C', 'D', 'E', 'G', 'Q', '7', 'z'};
if(allowed_chars.find(ch) != allowed_chars.end()) {
    /*...*/
}
Run Code Online (Sandbox Code Playgroud)

  • 一个`静态const``可能? (4认同)
  • @Xirema:为什么你认为它比`strchr`更快?为什么`strchr`版本需要注释? (4认同)
  • @Xirema O(n)与O(log(n))对于一组9个项目在这里是无关紧要的,特别是考虑到开销和推测的其他处理.如果你由于某种原因试图创建一个性能参数,请使用`bool charIsAllowed [256]`查找表,用'unsigned char`s代替O(1). (4认同)
  • @Xirema:我非常怀疑渐近复杂性在这里很重要,而且`std :: set`是一个非常低效的数据结构来存储字符(Chandler在cppcon14上很好地解释了这个imho:https://www.youtube.com/watch? v = fHNmRkzxHWs).如果你有时间,你可能想看看我的伪"基准":https://ideone.com/h6WxBI.在我的机器上,`strchr`比'std :: set`解决方案的性能提高了2倍或更多,但这可能是高度系统,编译器和基准测试特有的.它实际上没有我想象的那么糟糕. (3认同)

Jas*_*n C 14

关于这个过度回答的问题的另一个答案,我只是为了完整性而包括在内.在这里的所有答案之间,你应该找到适合你的应用程序的东西.

所以另一个选项是查找表:

// On initialization:
bool isAcceptable[256] = { false };
isAcceptable[(unsigned char)'A'] = true;
isAcceptable[(unsigned char)'B'] = true;
isAcceptable[(unsigned char)'C'] = true;

// When you want to check:
char c = ...;
if (isAcceptable[(unsigned char)c]) {
   // it's 'A', 'B', or 'C'.
}
Run Code Online (Sandbox Code Playgroud)

如果必须的话,嘲笑C风格的静态演员,但他们确实完成了工作.我想你可以使用一个std::vector<bool>if阵列让你在晚上.您还可以使用其他类型bool.但是你明白了.

显然,这变得很麻烦,例如wchar_t,并且几乎不能使用多字节编码.但是对于你的char例子,或任何适合查找表的东西,它都可以.因人而异.

  • 对于那些稀疏,多字节的情况,提及`std :: map`可能值得编辑. (2认同)

jag*_*ire 12

与C strchr答案类似,在C++中,您可以构造一个字符串并根据其内容检查字符:

#include <string>
...
std::string("ABCDEFGIKZ").find(c) != std::string::npos;
Run Code Online (Sandbox Code Playgroud)

上面的代码将返回true'F''Z'false'z''O'.此代码不假定字符的连续表示.

这是有效的,因为它无法在字符串中找到字符时std::string::find返回std::string::npos.

住在Coliru

编辑:

还有另一种C++方法,它不涉及动态分配,但确实涉及更长的代码:

#include <algorithm> // std::find
#include <iterator> // std::begin and std::end
...
char const chars[] = "ABCDEFGIKZ";
return std::find(std::begin(chars), std::end(chars), c) != std::end(chars);
Run Code Online (Sandbox Code Playgroud)

这与第一个代码片段的工作方式类似:std::find搜索给定范围,如果找不到该项,则返回特定值.这里,所述特定值是范围的结束.

住在Coliru


小智 6

一种选择是unordered_set.将感兴趣的字符放入集合中.然后只需检查相关字符的计数:

#include <iostream>
#include <unordered_set>

using namespace std;

int main() {
  unordered_set<char> characters;
  characters.insert('A');
  characters.insert('B');
  characters.insert('C');
  // ...

  if (characters.count('A')) {
    cout << "found" << endl;
  } else {
    cout << "not found" << endl;
  }

  return 0;
}
Run Code Online (Sandbox Code Playgroud)


dis*_*ame 6

您的问题有解决方案,不是语言,而是编码实践 - 重构.

我非常肯定读者会发现这个答案非常不正统,但是 - 重构可以并且经常被用来隐藏方法调用背后的混乱代码.该方法可以在以后清理,也可以保留原样.

您可以创建以下方法:

private bool characterIsValid(char ch) {
    return (ch == 'A' || ch == 'B' || ch == 'C' || ..... );
}
Run Code Online (Sandbox Code Playgroud)

然后可以用以下简短形式调用此方法:

if (characterIsValid(ch)) ...
Run Code Online (Sandbox Code Playgroud)

在许多检查中重用该方法,只在任何地方返回一个布尔值.

  • `private bool methodName()`看起来既不像C也不像C++ ...... (5认同)