在编程语言中,如何编写类似于"x == 1或2"的代码?

25

可能是重复问题:
为什么大多数编程语言只有二元相等比较运算符?

我有一个简单的问题,自从我开始学习编程语言以来就一直存在。

我想写类似于"如果 x 是 1 或者 2 => 真 (否则为假)"的代码。

但当我在编程语言中写它,比如 C 语言时,

( x == 1 || x == 2 )

它真的可以工作,但看起来很笨拙且难以阅读。我想应该可以简化这样的或操作,如果您有任何想法,请告诉我。谢谢,Nathan


8
你想开发自己的编程语言? - Andre Haverdings
6
学习编程语言时,阅读代码并快速理解这些结构是必要的一部分。布尔逻辑只是冰山一角。关键问题在于人们不会说逻辑符号,例如常说:“如果x等于a或b,则…”,而应该说:“如果x等于a或者a等于b”。 - JeffV
1
应该是社区维基:这是一个投票/讨论。 - SilentGhost
@Murph,不傻啊...代码越简洁,节省的时间就越多。如果该语言支持这些工具,那么编码人员拥有自己的风格,而不是语言使自己变得更糟。 - user942821
@haibison,你见过APL吗?那很简洁...它也相当接近于“只写”代码,同样的还有Forth。而且我注意到查看“IN”关系会更有趣一些。 - Murph
显示剩余4条评论
22个回答

37

Python允许检查一个序列中是否包含某个元素:

if x in (1, 2):

17
那不是一个数组,它是一个叫做“元组”的结构。通常用于保存5个或更少的值;超过这个数量,使用“集合”将更加高效(在序列中进行包含性测试的时间复杂度为O(n),而在集合中进行的时间复杂度为O(1))。 - Ignacio Vazquez-Abrams
1
Python相比于一些更传统的编程语言,可以为你提供优美的代码,这是其中一个很好的例子 :)。 - bastijn
2
@progo:因为集合被表示为哈希表,而哈希表具有恒定的平均查找时间。 - Martin Jonáš
7
创建这个集合的时间复杂度是O(n)吗? - configurator
2
Pascal中有相同的语法,而且你可以使用集合(1、2、3)或区间(1..3)。这是我在学习Pascal之后学习C时遇到的主要问题之一。在Pascal中,switch/case的强大功能与case/of相比,允许测试集合和区间。 - jv42
显示剩余13条评论

18

一个C#扩展版本

步骤1:创建一个扩展方法

public static class ObjectExtensions
{
    public static bool Either(this object value, params object[] array)
    {
        return array.Any(p => Equals(value, p));
    }
}

步骤2:使用扩展方法

if (x.Either(1,2,3,4,5,6)) 
{
}
else
{
}

@Piers Myers:为什么要创建一个新的List<T>? - Joh
2
@Piers Myers 只需使用 (Array.IndexOf(values, value) > -1) 即可,这样您就不必创建新列表。 - Jess
哇,我非常喜欢这个!扩展方法真是太棒了! - John Gietzen
@Andrew Koester - 是的,那甚至更快,谢谢 - 学到新东西总是好的。 - Piers Myers

17

虽然这个帖子中有一些相当有趣的回答,但我想指出,如果你在循环内部执行此类逻辑,这些回答可能会对性能产生影响,具体取决于语言。就计算机理解而言,if (x == 1 || x == 2) 明显是最容易理解和优化的,因为它可以编译成机器代码。


2
дҪҶиҝҷеҸ–еҶідәҺзј–зЁӢиҜӯиЁҖгҖӮжҲ‘жңҹжңӣPythonз”ҹжҲҗзҡ„д»Јз Ғx in (1, 2)иҮіе°‘дёҺPythonз”ҹжҲҗзҡ„д»Јз Ғx == 1 or x == 2дёҖж ·дјҳеҢ–гҖӮеҪ“然пјҢеңЁCиҜӯиЁҖдёӯпјҢзӣёеҸҚзҡ„жғ…еҶөд№ҹжҳҜеҰӮжӯӨгҖӮ - intuited
真的,但正如intuited所提到的,这将取决于底层实现。但我认为这并不能回答OP的问题,即使它是真实的并值得一提。 - haylem
3
@intuited - 只是提供信息:在Python 3.1.2中,两者的性能几乎相同。 我使用time进行测量,如果x为1,则in需要0.042秒,而or需要0.047秒;如果x为2或0,则两者都需要0.08秒。 因此,如果您正在进行Python的微优化,请注意您正在测试的条件的顺序... - Tim Pietzcker

11

当我开始编程时,我也觉得很奇怪,因为它并不像:

(1 < x < 10)

我不得不写:

(1 < x && x < 10)
但这是大多数编程语言的工作方式,过一段时间后你会习惯的。
因此,我认为写下下面的代码是完全可以的。
( x == 1 || x == 2 )

这样编写代码的优势在于其他程序员可以轻松理解你的写法。使用一个函数来封装它可能只会让事情变得更加复杂,因为其他程序员需要找到那个函数并看看它是做什么的。

只有像Python、Ruby等较新的编程语言才允许您以更简单、更美观的方式编写代码。这主要是因为这些编程语言旨在提高程序员的生产力,而较旧的编程语言的主要目标是应用程序的性能,而不是程序员的生产力。


4
您可以使用Python完成第一个示例! - Grant Paul

9

自然但依赖于语言

你的方法似乎更自然,但这实际上取决于你用于实现的编程语言。

混乱原因

由于C是一种系统编程语言,并且与硬件接近(尽管我们曾经认为它是“高级”语言,而不是编写机器代码),因此它并不是非常表达能力强

现代高级语言(虽然历史上来说Lisp并不算现代,但它可以很好地完成这个任务)通过使用内置结构或库支持(例如,在PythonRubyGroovyML-languagesHaskell等语言中使用Ranges、Tuples或相当的结构)使您能够完成这样的事情。

可能的解决方案

选项1

您的一个选择是实现一个函数或子例程,它接受一个值数组并对它们进行检查。

这里是一个基本的原型,我将实现留作练习给您:

/* returns non-zero value if check is in values */
int is_in(int check, int *values, int size);

然而,很快就会发现,这只是非常基本的,不太灵活:

  • 它只适用于整数,
  • 它只适用于比较相同的值。

选项2

在复杂性(语言方面)的阶梯上更进一步,另一个选择是使用预处理器 C(或C ++)中实现类似的行为,但要注意副作用

其他选项

下一步可以将函数指针作为额外参数传递以在调用点定义行为,为此定义几个变体和别名,并构建自己的比较器小库。

接下来的步骤就是使用模板C++中实现类似的东西,以便使用单一实现处理不同类型。
然后继续向更高级别的语言发展。

选择正确的语言(或学会放手!)

通常,支持functional programming的语言将内置对此类事物的支持,原因显而易见。

或者只需学会接受某些语言可以做到其他语言无法做到的事情,并且根据工作和环境的不同,这就是事实。这主要是语法糖,你无能为力。此外,一些语言将通过更新其规范来解决其缺点,而其他语言则会停滞不前。

也许已经有一个库实现了这样的功能,而我不知道。


7

这里有很多有趣的替代方案。我很惊讶没有人提到switch...case - 所以我来说一下:

switch(x) {
case 1:
case 2:
  // do your work
  break;
default:
  // the else part
}
  1. 相比于一堆 x == 1 || x == 2 || ...,这种写法更易读
  2. 在执行成员检查时,比使用数组/集合/列表更加优化

2
我不认为这样写更易读。在我看来,x==1 || x==2比使用switch语句要直观得多。它可能比数组检查更优,但可能并不比原始语句更优。 - Patrick
2
当你有两个比较时,可能并不明显,但是当你有10个比较时,使用 x == 1 || ... x == 10 可能会变得很繁琐且容易出错。虽然你正在比较同一个变量 "x",但你需要重复10次 - 并且你需要检查所有10个比较以确保它们中没有一个是 "y"。 - kartheek
1
那么我仍然肯定更喜欢使用迭代数组的实现,而不是使用 switch case。后者每次都需要大量代码行数,却没有任何可读性或可维护性上的显著优势,同时每次重新实现时容易出错。 - haylem
1
“遍历数组”解决方案只在脚本语言中易读。如果你仔细想想,它在C、C++或Java中并不那么易读。相比之下,你会发现使用switch...case更易读。此外,switch...case几乎可以直接转换成机器码(跳转指令),因此比遍历列表的性能更好。 - kartheek
它几乎没有更易读或简洁的方式;对于一个短列表来说更是如此。 - haylem

6

出错了,它有什么问题?好吧,如果你真的经常使用它并且不喜欢外观,请在c#中执行以下操作:

#region minimizethisandneveropen
public bool either(value,x,y){       
    return (value == x || value == y);
}
#endregion

并在使用它的地方:

if(either(value,1,2))
   //yaddayadda 

或者在另一种语言中类似的东西 :).


3
您可以避免在函数中使用以下条件:return (value == x || value == y); - Karel Petranek
哎呀,太对了哈哈。还早呢 ;) - bastijn
如果我可以发表一点评论 - 这有点令人困惑。我希望 value 放在第一位(但这只是我的个人看法),而且 x 被使用了两次,一次作为 value,一次作为第一个参数。 - Kobi
@dark_charlie:啊,我不知道怎么做:( 请给我提示! - legends2k
好的,这只是为了展示想法,但我已经为您修复了它们 :)。 - bastijn

6

我觉得我不会这样做,但为了回答你的问题,这里有一种在C#中实现它的方法,它涉及一些泛型类型推断和对运算符重载的滥用。您可以编写以下代码:

if (x == Any.Of(1, 2)) {
    Console.WriteLine("In the set.");
}

在这里,Any类的定义如下:

public static class Any {
    public static Any2<T> Of<T>(T item1, T item2) {
        return new Any2<T>(item1, item2);
    }
    public struct Any2<T> {
        T item1;
        T item2;
        public Any2(T item1, T item2) {
            this.item1 = item1;
            this.item2 = item2;
        }
        public static bool operator ==(T item, Any2<T> set) {
            return item.Equals(set.item1) || item.Equals(set.item2);
        }
        // Defining the operator== requires these three methods to be defined as well:
        public static bool operator !=(T item, Any2<T> set) {
            return !(item == set);
        }
        public override bool Equals(object obj) { throw new NotImplementedException(); }
        public override int GetHashCode() { throw new NotImplementedException(); }
    }
}

您可以考虑使用多个重载版本的 Any.Of 方法来处理3、4甚至更多个参数。也可以提供其他操作符,并且一个伴随类 All 可以执行非常类似的操作,但将 && 替换为 ||
观察反汇编代码,由于需要调用 Equals ,因此会发生相当多的装箱操作,所以这比明显的(x == 1)||(x == 2)构造要慢。但是,如果您将所有<T>更改为 int 并将 Equals 替换为 == ,则得到的结果似乎可以很好地内联,速度与(x == 1)||(x == 2)大致相同。

+1:因为我怀疑我会这样做,但这是一个非常好的答案。 - haylem

3
据我所知,在C语言中没有内置的方法来完成这个任务。你可以添加自己的内联函数来扫描一个整数数组,找到值等于x的元素...
具体操作如下:
inline int contains(int[] set, int n, int x)
{ 
  int i;
  for(i=0; i<n; i++)
    if(set[i] == x)
      return 1;

  return 0;
}

// To implement the check, you declare the set
int mySet[2] = {1,2};
// And evaluate like this:
contains(mySet,2,x) // returns non-zero if 'x' is contained in 'mySet'

3
在 .Net 中,您可以使用 Linq:
int[] wanted = new int{1, 2};

// you can use Any to return true for the first item in the list that passes
bool result = wanted.Any( i => i == x );

// or use Contains
bool result = wanted.Contains( x );

虽然我个人认为基本的||非常简单:

bool result = ( x == 1 || x == 2 );

哦,你修改了代码但没有更新注释。淘气 =) - demoncodemonkey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接