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

У меня есть переменная 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"
    }
    
  • для языков, подобных Паскалю (например, 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 всегда выполняются. Я делаю что-то не так?

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", который дает ложь.
    v != "y" становится "x" != "y", что дает верно.
    v != "z" становится "x" != "y", что дает верно.

    Выражение оценивается как 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-подобных языков (например, 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")),

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


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

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

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

  • питон: v not in {"x", "y", "z"}

  • Ява: Arrays.asList("x", "y", "z").contains(v)

  • Java-9 (и выше): Set.of("x", "y", "z").contains(v)

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

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

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

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

Другие вопросы по тегам