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

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

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

И моя цель состоит в том, чтобы выполнить некоторый оператор только тогда, когда v не "x", "y" или "z".

Я пробовал,

  • для C-подобных языков (где операторы равенства сравнивают фактические строковые значения, например , , )

    if (v != "x" || v != "y" || v != "z")
    {
        // the statements I want to be executed
        // if v is neither "x", nor "y", nor "z"
    }
    
  • для Pascal-подобных языков (например, )

    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 всегда выполняются. Я делаю что-то неправильно?

Ответ 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", что неверно.
    v != "y" становится "x" != "y", что верно.
    v != "z" становится "x" != "z", что верно.

    Выражение оценивается как false || 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 (например, , - (может потребоваться оператор строгого равенства !==), )

    if (v != "x" && v != "y" && v != "z")
    {
        // the statements I want to be executed
        // if v is neither "x", nor "y", nor "z"
    }
    
  • для языков, подобных Паскалю

    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")).

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

Определенные языки

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

Ответ 2

Я полагал, что внес бы ответ для оболочки Bourne script, так как синтаксис несколько своеобразен.

В традиционном /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-like" но это похоже на то, что он не мог рассказать Алисе Купер от девушки, которая отправилась в Страну Чудес, а ракушка Рыба имеет свои особенности, о которых я даже не могу комментировать.)

Ответ 3

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

Есть и другие способы, мне просто лень.

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