Почему проверка равноценности одной переменной по многим значениям всегда возвращает true?
У меня есть переменная v
в моей программе, и она может принимать любое значение из набора значений
"a", "b", "c", ..., "z"
И моя цель состоит в том, чтобы выполнить некоторый оператор только тогда, когда v
не "x"
, "y"
или "z"
.
Я пробовал,
-
для 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 всегда выполняются. Я делаю что-то неправильно?
Ответы
Ответ 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 (например, 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' 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
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
Я полагал, что внес бы ответ для оболочки 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
Есть и другие способы, мне просто лень.
Используйте свое воображение и просто напишите тот, который вам нравится больше!