为啥一个变量对多个值的不等式检查总是返回真?

Posted

技术标签:

【中文标题】为啥一个变量对多个值的不等式检查总是返回真?【英文标题】:Why does non-equality check of one variable against many values always return true?为什么一个变量对多个值的不等式检查总是返回真? 【发布时间】:2022-01-12 07:00:14 【问题描述】:

我的程序中有一个变量v,它可以从值集中获取任何

"a", "b", "c", ..., "z"

我的目标是仅在v 不是"x""y""z" 时执行某些语句。

我试过了,

对于类 C 语言(相等运算符比较 实际 字符串值;例如 c#、javascriptphp

if (v != "x" || v != "y" || v != "z")

    // the statements I want to be executed
    // if v is neither "x", nor "y", nor "z"

对于类似 Pascal 的语言(例如 plsql)

IF (v != 'x' OR v != 'y' OR v != 'z') THEN
    -- the statements I want to be executed
    -- if v is neither "x", nor "y", nor "z"
END IF;

if 条件中的语句总是被执行。我做错什么了吗?

【问题讨论】:

这是为元帖子Canonical, language-agnostic question for if(var != “x” || var != “y” …) 描述的问题类型创建规范问题/答案对的尝试。 在一些类 C 语言(尤其是 C)中,字符串相等不能这样工作。 +1 @Rhymoid。是的,这是误导。我已经更改了标签,我正在考虑提一些关于字符串相等性的简短内容。 请注意,在 Java 中,比较 String==!=(如 v != "x")是不正确的。我们需要改用.equals 方法,如!v.equals("x")(或!"x".equals(v),如果v 可能是null)。请参阅How do I compare strings in Java? 了解更多信息。 【参考方案1】:

使用&&/AND/and,而不是||/OR/or

v != "x" && v != "y" && v != "z"

问题

如果总是执行 if 块,则 if 块的条件总是计算为 true。逻辑表达式一定是错误的。

让我们考虑v != "x" || v != "y" || v != "z" 对应v 的每个值。

v = "x"

v != "x" 变为 "x" != "x",这是 falsev != "y" 变为 "x" != "y",这是 truev != "z" 变为 "x" != "z",这是 true

表达式的计算结果为false || true || true,这是true

v = "y"时,表达式变为

  "y" != "x" || "y" != "y" || "y" != "z"

true || false || true,这是true

v = "z"时,表达式变为

  "z" != "x" || "z" != "y" || "z" != "z"

true || true || false,这是true

对于v 的任何其他值,表达式的计算结果为true || true || true,即true

或者,考虑真值表:

       │     A          B          C      │
  v    │  v != "x"   v != "y"   v != "z"  │  A || B || C
───────┼──────────────────────────────────┼──────────────
 "x"   │    false      true       true    │     true
 "y"   │    true       false      true    │     true
 "z"   │    true       true       false   │     true
other  │    true       true       true    │     true

如您所见,您的逻辑表达式总是计算结果为true

解决方案

您要做的是,找到一个逻辑表达式,其计算结果为true when

(v is not "x")and(v is not "y")and(v is not "z").

正确的结构是,

对于类 C 语言(例如 c#、javascript-(可能需要严格相等运算符 !==)、php)

  if (v != "x" && v != "y" && v != "z")
  
      // the statements I want to be executed
      // if v is neither "x", nor "y", nor "z"
  

对于类似 Pascal 的语言 plsql

  IF (v != 'x' AND v != 'y' AND v != 'z') THEN
      -- the statements I want to be executed
      -- if v is neither "x", nor "y", nor "z"
  END IF;

德摩根定律

通过De Morgan's law,表达式也可以重写为(使用类C语法)

!(v == "x" || v == "y" || v == "z")

意义

not((v is "x")or(v is "y")or(v is "z"))

这使得逻辑更加明显。

特定语言

某些语言具有用于测试集合成员资格的特定结构,或者您可以使用数组/列表操作。

sql:v NOT IN ('x', 'y', 'z')

javascript:["x", "y", "z"].indexOf(v) == -1

python:v not in "x", "y", "z"

java:!Arrays.asList("x", "y", "z").contains(v)

java-9(及以上):!Set.of("x", "y", "z").contains(v)

【讨论】:

【参考方案2】:

对于 PHP,您可以使用类似的东西:

if(strpos('xyz',$v[0])===false)//example 1
//strpos returns false when the letter isn't in the string
//returns the position (0 based) of the substring
//we must use a strict comparison to see if it isn't in the substring

if(!in_array($v[0],array('x','y','z')))//example 2

//example 3
$out=array('x'=>1,'y'=>1,'z'=>1); //create an array
if(!$out[$v[0]]) //check if it's not 1

if(!preg_match('/^[xyz]$/',$v))//example 4, using regex

if(str_replace(array('x','y','z'),'',$v[0]))//example 5


if(trim($v[0],'xyz'))//example 6

对于 Javascript:

if(~'xyz'.search(v[0]))//example 1(.indexOf() works too)

if(!(v[0] in x:0,y:0,z:0))//example 2

if(~['x','y','z'].indexOf(v[0]))//example 3, incompatible with older browsers.

if(!/^[xyz]$/.match(v))//example 4

if(v.replace(/^[xyz]$/))//example 5

对于 mysql

Select not locate(@v,'xyz'); -- example 1

select @v not in ('x','y','z'); -- example 2

-- repetition of the same pattern for the others

对于 C:

if(!strstr("xyz",v))//example 1, untested

还有很多方法,我就是太懒了。

发挥你的想象力,写出你更喜欢的一个!

【讨论】:

那个 C 例子是非常错误的。 C 中的字符串用双引号括起来。 @JL2210 这不是“大错特错”。但我继续纠正它。我总是混淆,因为对于 C、C++ 和 C#,单引号字符串实际上是一个字符。 不,这至少会导致段错误,并且可能会导致编译器警告/错误。在编辑之前,它是一个“多字符常量”,一个 GCC 扩展。【参考方案3】:

我想我会为 Bourne shell 脚本提供一个答案,因为语法有些特殊。

在传统/POSIX sh 中,字符串相等测试是 [ 命令的一个功能(是的,这是一个独特的命令名称!),它对引用等有一些讨厌的要求。

#### WRONG
if [ "$v" != 'x' ] || [ "$v" != 'y'] || [ "$v" != 'z' ]; then
    : some code which should happen when $v is not 'x' or 'y' or 'z'
fi

Ksh、Bash、Zsh 等现代 shell 也有 [[,这有点不那么讨厌了。

#### STILL WRONG
if [[ $v != 'x' || $v != 'y' || $v != 'z' ]]; then
    :  some code which should happen when $v is not 'x' or 'y' or 'z'
fi

我们应该强调每个标记周围有空格的要求,这是许多初学者忽略的(即你不能说 if[[$v$v!='y' 在命令和运算符周围没有空格),并且 明显 引用的可选性。没有引用一个值通常不是语法错误,但是如果你没有引用一个需要引用的值,它会导致严重的语义麻烦。 (More on this elsewhere.)

这里明显的解决方法是使用&& 而不是||,但您还应该注意[[ 通常支持正则表达式,因此您可以这样说

if [[ ! $v =~ ^(x|y|z)$ ]]; then
    : yeah
fi

不要忘记可信赖的旧 case 声明,这很自然,并且可以追溯到 1970 年代后期:

case $v in
    x | y | z)
       ;; # don't actually do anything in this switch
    *) # anything else, we fall through to this switch
       yeah
       some more yeah
       in fact, lots of yeah;;
 esac

后面的双分号一开始会引起动脉瘤,但你很快就会康复,学会欣赏,甚至爱上它们。 POSIX 允许您在匹配表达式之前放置一个左括号,这样您就没有不成对的右括号,但这种用法并不常见。

(对于不属于 Bourne 家族的 Unix shell,这显然不是一个合适的答案。C 家族的 shell——包括仍然有些流行的tcsh——使用一种据说是“类 C”的语法但这就像无法区分爱丽丝库珀和去仙境的女孩一样;鱼壳有其独特之处,我什至没有资格评论。)

【讨论】:

以上是关于为啥一个变量对多个值的不等式检查总是返回真?的主要内容,如果未能解决你的问题,请参考以下文章

如何从异步数据中检查多个不等式?

为啥这个不等式在 C 中是 False 的? [复制]

SQL语句中Case 的用法

想让性能测试更高效?“性能等式”了解一下

Python3基础 ,= 一个等式给多个变量赋值

如何使用mathematica判定含有变量的不等式的真假?