Почему проверка на неравноправие одной переменной со многими значениями всегда возвращает истину?

В моей программе есть переменная v, и она может принимать любое значение из набора значений

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

И моя цель — выполнить некоторый оператор только тогда, когда v не является "x", "y" или "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' 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 всегда выполняются. Я делаю что-то не так?


person sampathsris    schedule 13.10.2014    source источник
comment
Это была попытка создать каноническую пару вопрос/ответ для типов вопросов, описанных в метасообщении Канонический вопрос, не зависящий от языка. для if(var != “x” || var != “y” …).   -  person sampathsris    schedule 17.11.2015
comment
В некоторых C-подобных языках (в частности, C) равенство строк не может работать таким образом.   -  person    schedule 03.10.2016
comment
+1 @Римоид. Да, это заблуждение. Я изменил теги и думаю упомянуть что-нибудь короткое о равенстве строк.   -  person sampathsris    schedule 04.10.2016
comment
Обратите внимание, что в Java сравнение String с == и != (как в v != "x") некорректно. Нам нужно использовать .equals вместо этого, как в !v.equals("x") (или !"x".equals(v), если v может быть null). См. раздел Как сравнивать строки в Java? для получения дополнительной информации.   -  person Radiodef    schedule 22.09.2018


Ответы (3)


Используйте &&/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", что является false.
    v != "y" становится "x" != "y", что верно.
    v != "z" становится "x" != "z", что верно.

    Выражение оценивается как false || true || true, что равно true.

  • Когда v = "y", выражение становится

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

    или true || false || true, что соответствует истине.

  • Когда v = "z", выражение становится

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

    или true || true || false, что правда.

  • Для любого другого значения v выражение оценивается как 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, когда

(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;
    

Закон де Моргана

По закону де Моргана выражение также можно переписать как (используя синтаксис C-подобного )

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

значение

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

Это делает логику немного более очевидной.

Конкретные языки

В некоторых языках есть специальные конструкции для проверки членства в наборах, или вы можете использовать операции с массивами/списками.

person Community    schedule 13.10.2014

Я решил, что внесу ответ для сценария оболочки Bourne, поскольку синтаксис несколько своеобразен.

В традиционном/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 и т. Д., Также имеют [[, что несколько менее надоедливо.

#### 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' без пробелов вокруг команд и операторов), а также очевидную необязательность цитирования. . Неспособность заключить значение в кавычки часто не является синтаксической ошибкой, но приведет к серьезным нежелательным семантически проблемам, если вы не заключите в кавычки значение, которое должно быть заключено в кавычки. (Подробнее об этом в другом месте.)

Очевидным исправлением здесь является использование && вместо ||, но вы также должны отметить, что [[ обычно поддерживает регулярные выражения, поэтому вы можете сказать что-то вроде

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 позволяет помещать открывающую круглую скобку перед выражением соответствия, чтобы у вас не было непарных правых круглых скобок, но такое использование встречается довольно редко.

(Очевидно, что это не подходящий ответ для оболочек Unix, которые не относятся к семейству Bourne. Семейство оболочек C, включая все еще довольно популярную tcsh, использует синтаксис, который предположительно «C-подобный», но это все равно, что неспособность чтобы отличить Элиса Купера от девушки, которая отправилась в Страну Чудес, а у Раковины есть свои особенности, которые я даже не компетентен комментировать.)

person tripleee    schedule 08.01.2018

Вы можете использовать что-то вроде этого для 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

Для С:

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

Есть еще способы, мне просто лень.

Используйте свое воображение и просто напишите тот, который вам больше нравится!

person Ismael Miguel    schedule 14.10.2014
comment
Этот пример C ужасно неверен. Строки в C заключены в двойные кавычки. - person S.S. Anne; 29.09.2019
comment
@ JL2210 Это было не так уж плохо. Но я пошел вперед и исправил это. Я всегда путаю, потому что для C, C++ и C# строки в одинарных кавычках на самом деле являются одним символом. - person Ismael Miguel; 30.09.2019
comment
Нет, это вызвало бы как минимум ошибку сегментации и, возможно, предупреждение/ошибку компилятора. До редактирования это была многосимвольная константа, расширение GCC. - person S.S. Anne; 30.09.2019