可能重复:
为什么大多数编程语言只有二进制相等比较运算符?
很长一段时间以来,我一直有一个简单的问题——自从我开始学习编程语言以来。
我想写成“如果 x 是 1 或 2 => TRUE(否则为 FALSE)”。
但是当我用编程语言编写它时,比如用 C 语言,
( x == 1 || x == 2 )
它确实有效,但看起来很尴尬且难以阅读。我想应该可以简化这样的 or 操作,所以如果你有任何想法,请告诉我。谢谢,内森
可能重复:
为什么大多数编程语言只有二进制相等比较运算符?
很长一段时间以来,我一直有一个简单的问题——自从我开始学习编程语言以来。
我想写成“如果 x 是 1 或 2 => TRUE(否则为 FALSE)”。
但是当我用编程语言编写它时,比如用 C 语言,
( x == 1 || x == 2 )
它确实有效,但看起来很尴尬且难以阅读。我想应该可以简化这样的 or 操作,所以如果你有任何想法,请告诉我。谢谢,内森
Python 允许按顺序测试成员资格:
if x in (1, 2):
C# 中的扩展版本
第 1 步:创建扩展方法
public static class ObjectExtensions
{
public static bool Either(this object value, params object[] array)
{
return array.Any(p => Equals(value, p));
}
}
第二步:使用扩展方法
if (x.Either(1,2,3,4,5,6))
{
}
else
{
}
尽管此线程中有许多非常有趣的答案,但我想指出,如果您根据语言在循环内执行这种逻辑,它们可能会对性能产生影响。就计算机if (x == 1 || x == 2)
而言,当它被编译成机器代码时,它是迄今为止最容易理解和优化的。
当我开始编程时,我也觉得很奇怪,而不是像这样:
(1 < x < 10)
我不得不写:
(1 < x && x < 10)
但这是大多数编程语言的工作方式,过一段时间你就会习惯它。
所以我相信写起来完全没问题
( x == 1 || x == 2 )
以这种方式编写它还具有其他程序员可以轻松理解您编写的内容的优点。使用函数来封装它可能只会让事情变得更复杂,因为其他程序员需要找到该函数并查看它的作用。
只有较新的编程语言(如 Python、Ruby 等)允许您以更简单、更好的方式编写它。这主要是因为这些编程语言旨在提高程序员的生产力,而旧的编程语言的主要目标是应用程序性能,而不是程序员的生产力。
您的方法确实看起来更自然,但这实际上取决于您用于实现的语言。
C是一种系统编程语言,并且非常接近硬件(虽然很有趣,因为我们过去认为是一种“高级”语言,而不是编写机器代码),它并不完全具有表达能力。
现代高级语言(同样,有争议,从历史上讲,lisp 不是那么现代,但可以让你很好地做到这一点)允许你通过使用内置结构或库支持来做这些事情(例如,使用 Ranges,元组或类似Python、Ruby、Groovy、ML-languages、Haskell等语言的等价物...)。
您的一个选择是实现一个函数或子例程,获取一组值并检查它们。
这是一个基本的原型,我把实现留给你做练习:
/* returns non-zero value if check is in values */
int is_in(int check, int *values, int size);
但是,您很快就会看到,这是非常基本的并且不是很灵活:
在复杂性阶梯(就语言方面)上高出一步,另一种方法是使用C(或C++ )中的预处理器 宏来实现类似的行为,但要注意副作用。
下一步可能是将函数指针作为额外参数传递,以定义调用点的行为,为此定义几个变体和别名,并为自己构建一个小型比较器库。
下一步将是使用模板在C++中实现类似的东西,通过单个实现在不同类型上执行此操作。
然后继续从那里到更高级别的语言。
通常,出于显而易见的原因,偏爱函数式编程的语言将内置对此类事物的支持。
或者只是学会接受某些语言可以做其他语言不能做的事情,并且取决于工作和环境,这就是它的方式。它主要是语法糖,您无能为力。此外,随着时间的推移,一些语言会通过更新规范来解决它们的缺点,而另一些语言则会停滞不前。
也许图书馆已经实现了这样的事情,而我不知道。
那是很多有趣的选择。我很惊讶没有人提到 switch...case - 所以这里是:
switch(x) {
case 1:
case 2:
// do your work
break;
default:
// the else part
}
呃,有什么问题吗?哦,好吧,如果您真的经常使用它并且讨厌它的外观,请在 c# 中执行以下操作:
#region minimizethisandneveropen
public bool either(value,x,y){
return (value == x || value == y);
}
#endregion
在你使用它的地方:
if(either(value,1,2))
//yaddayadda
或者其他语言的类似内容:)。
我怀疑我是否会这样做,但要回答你的问题,这是在 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)
.
在 php 中你可以使用
$ret = in_array($x, array(1, 2));
据我所知,在 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'
在 .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 );
在 T-SQL 中
where x in (1,2)
在 COBOL 中(我已经很长时间没有简单地瞥过 COBOL 了,所以我在这里可能有一两个细节错误):
IF X EQUALS 1 OR 2
...
所以语法绝对是可能的。然后问题归结为“为什么不经常使用它?”
嗯,问题是,解析这样的表达式有点笨。注意,不是像那样独自站立时,而是在复合表达中更多。语法开始变得不透明(从编译器实现者的角度来看)并且语义完全毛茸茸的。IIRC,如果您使用这样的语法,许多 COBOL 编译器甚至会警告您,因为存在潜在问题。
Perl 5 with Perl6::Junction:
use Perl6::Junction 'any';
say 'yes' if 2 == any(qw/1 2 3/);
Perl 6:
say 'yes' if 2 == 1|2|3;
This version is so readable and concise I’d use it instead of the ||
operator.
谢谢伊格纳西奥!我把它翻译成 Ruby:
[ 1, 2 ].include?( x )
它也有效,但我不确定它是否看起来清晰正常。如果你了解 Ruby,请指教。另外,如果有人知道如何用 C 写这个,请告诉我。谢谢。-内森
帕斯卡有一个(有限的)集合概念,所以你可以这样做:
if x in [1, 2] then
(几十年来没有接触过 Pascal 编译器,所以语法可能不正确)
你说这个符号(x==1 || x==2)
“尴尬且难以阅读”。我不敢苟同。它与自然语言不同,但非常清晰易懂。你只需要像电脑一样思考。
此外,此线程中提到的符号在x in (1,2)
语义上与您真正要问的不同,它们询问是否x
是set (1,2)
的成员,这不是您要问的。您要问的是if x equals to 1 or to 2
which 在逻辑上(和语义上)等价于if x equals to 1 or x equals to 2
which 翻译为(x==1 || x==2)
.
在java中:
List list = Arrays.asList(new Integer[]{1,2});
Set set = new HashSet(list);
set.contains(1)
仅使用一个非按位布尔运算符的尝试(不建议,未经测试):
if( (x&3) ^ x ^ ((x>>1)&1) ^ (x&1) ^ 1 == 0 )
该(x&3) ^ x
部分应等于 0,这确保 x 介于 0 和 3 之间。其他操作数将仅设置最后一位。
^ 1 部分确保最后一位和倒数第二((x>>1)&1) ^ (x&1)
位不同。这将适用于 1 和 2,但不适用于 0 和 3。
我有一个我经常使用的宏,它有点接近你想要的。
#define ISBETWEEN(Var, Low, High) ((Var) >= (Low) && (Var) <= (High))
ISBETWEEN(x, 1, 2)
如果 x 为 1 或 2,将返回 true。
C、C++、VB.net、C#.net 以及我所知道的任何其他此类语言都没有一种有效的方法来测试作为几种选择之一的东西。尽管 (x==1 || x==2) 通常是编写这种结构的最自然的方式,但这种方式有时需要创建一个额外的临时变量:
tempvar = somefunction(); // tempvar 仅用于 'if' 测试: if (tempvar == 1 || tempvar == 2) ...
当然,优化器应该能够有效地摆脱临时变量(在使用的短暂时间内将其放入寄存器中),但我仍然认为代码很难看。此外,在某些嵌入式处理器上,最紧凑且可能最快的编写方式 (x == const1 || x==const2 || x==const3) 是:
movf _x,w ; 将变量 X 加载到累加器中 xorlw const1 ; 与 const1 异或 btfss 状态,零;如果为零则跳过下一条指令 xorlw const1 ^ const2 ; 与 (const1 ^ const2) 异或 btfss 状态,零;如果为零则跳过下一条指令 xorlw const2 ^ const3 ; 与 (const2 ^ const3) 异或 btfss 状态,零;如果为零则跳过下一条指令 转到 NOPE
这种方法需要为每个常数增加两条指令;所有指令都会执行。如果采用分支,提前退出测试将节省时间,否则会浪费时间。使用单独比较的字面解释进行编码将需要针对每个常量使用四个指令。
如果一种语言具有“如果变量是几个常量之一”的构造,我希望编译器使用上述代码模式。太糟糕了,通用语言中不存在这样的结构。
(注意:Pascal 确实有这样的结构,但运行时实现通常非常浪费时间和代码空间)。
返回 x === 1 || x === 2 在javascript中