Почему больше языков не могут сравнивать значение с несколькими другими значениями? [закрыто]

10

Учтите следующее:

if(a == b or c)

На большинстве языков это должно быть записано как:

if(a == b or a == c)

что немного громоздко и повторяет информацию.

Я знаю, что мой приведенный выше пример синтаксиса немного неуклюж, но я уверен, что есть лучшие способы донести эту идею.

Почему больше языков не предлагают это? Есть ли проблемы с производительностью или синтаксисом?

Нулевой
источник
6
SQL предлагает что: где A IN (B, C)
thursdaysgeek
4
Я не спрашивал о языках, которые предлагают или могут иметь его, но почему больше языков не предлагают его? Есть ли проблемы с производительностью или синтаксисом?
Zeroth
8
чтобы обобщить ответ @ thursdaysgeek, на большинстве языков это обычно делается с помощью набора условий. (Или список или кортеж, если это проще.) Он работает так же и позволяет избежать некоторых потенциально сложных синтаксических проблем. Из вашего примера, «b или c» означает набор «{b, c}» или is или оператор, такой как || ? В python «b или c» означает «значение b, если оно истинно, или же значение c»
Роб
4
По сути, это проблема синтаксиса. Проблема в том, чтобы иметь интуитивно понятный способ устранить разницу между «b или c» и «b or'd с c».
YoungJohn
2
Это довольно хакерское дело в особом случае a == b or c, и это даже не очень хорошо ИМХО.

Ответы:

24

Синтаксическая проблема заключается в том, что он требует синтаксиса.

Какой бы синтаксис ни был у вашего языка, люди, использующие язык, должны выучить его. В противном случае они рискуют увидеть код и не знать, что он делает. Таким образом, обычно считается хорошей вещью, если язык имеет простой синтаксис, который четко обрабатывает множество случаев.

В вашем конкретном примере вы пытаетесь использовать инфиксный оператор (функция, которая принимает два аргумента, но записана Argument1 Operator Argument2) и пытаетесь расширить его до нескольких аргументов. Это не очень хорошо работает, потому что весь смысл инфиксных операторов, если таковой имеется, состоит в том, чтобы поместить оператор прямо между двумя аргументами. Расширение до, (Argument1 Operator Argument2 MagicallyClearSymbol Argument3...)кажется, не добавляет много ясности Equals(Arg1,Arg2,...). Infix также обычно используется для эмуляции математических соглашений, с которыми знакомы люди, что было бы неверно для альтернативного синтаксиса.

С вашей идеей не возникнет никаких особых проблем с производительностью, кроме того, что анализатору придется иметь дело с грамматикой с другим или двумя производственными правилами, что может незначительно повлиять на скорость синтаксического анализа. Это может иметь некоторое значение для интерпретируемого или JIT-компилируемого языка, но, вероятно, не большая разница.

Большая проблема с этой идеей состоит в том, что создание множества особых случаев на языке, как правило, является плохой идеей .

PSR
источник
1
Кроме того: в Scala есть инфиксные операторы с произвольным числом аргументов, поскольку инфиксные операторы - это просто вызовы методов без .. Таким образом, они будут написаны как arg1 op (arg2, arg3). Не совсем красиво, но нужно в некоторых местах в контексте этого языка.
Амон
что if my_var in (a, b)тогда? разве это не вопрос использования правильного инструмента для работы?
Великолепные моменты. Синтаксис языка должен быть основным языком, а затем вы создаете библиотеки поверх него. Если язык слишком загроможден «полезным» синтаксическим сахаром, его становится сложнее использовать. Не всем нужно, a == b or cа другим нужно a == b or c but not d. ИМО, вот где полезные функции / библиотеки приходят на помощь.
Аллан
Возможно, необходимо средство, с помощью которого метод может указать, что вызов с произвольным числом аргументов должен обрабатываться как множественные вызовы, а результаты объединяются каким-либо образом. Если f().Equals(a,b,c); можно было бы оценить, что (var temp=f(); temp.Equals(a)||temp.Equals(b)||temp.Equals(c))этот синтаксис был бы идеальным, но если бы он оценивался так, int[] arr = {a,b,c}; f().Equals(arr);это было бы не очень хорошо, особенно если для каждого вызова нужно было создавать новый массив.
Суперкат
6

Потому что это не проблема, и ее решение приносит практически нулевую выгоду, но ее реализация приносит ненулевые затраты.

Существующие функции, основанные на диапазоне, и такие, которые предлагает практически каждый язык, могут прекрасно работать в этой ситуации, если они масштабируются до размера, a == b || a == cкоторый не позволяет их сократить.

DeadMG
источник
2
+1, но я думаю, что ответ будет улучшен, если показать одну или две из этих «существующих функций на основе диапазонов, которые практически каждый язык [предлагает]», просто чтобы эта альтернатива была более понятной.
Авнер Шахар-Каштан
Можете ли вы доказать, что это «приносит практически нулевую выгоду, но его реализация приносит ненулевые затраты»?
Дарек Нендза
3
@ DarekNędza Вторая половина не должна быть спорной: Каждая функция должна быть продумана, реализуемая, испытанная, документирована и поддерживается. Ни один из этих шагов не является бесплатным по любой разумной метрике (время людей, альтернативные издержки, сложность, денежные затраты, если кому-то платят за работу над ним, и так далее).
@ AvnerShahar-Kashtan Согласен - для меня не очевидно, как это будет выглядеть, скажем, в java, или sh, или zsh? Хорошо, он мог подразумевать «современный» язык. Groovy?
Фолькер Сигел
В PHP это будет выглядеть так in_array($a, [$b, $c, $d, $e, $f]). : P
cHao
6

Некоторые языки имеют такие функции. Например, в Perl6 мы можем использовать соединения , которые являются «суперпозициями» двух значений:

if $a == any($b, $c) {
    say "yes";
}

# syntactic sugar for the above
if $a == $b | $c {
    say "yes";
}

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

import numpy as np
2 == np.array([1, 2, 3])
#=> np.array([False, True, False], dtype=np.bool)
(2 == np.array([1, 2, 3])).any()
#=> True

Однако это работает только для выбранных примитивных типов.

Почему развязки проблематичны? Поскольку операции над соединением распределяются по содержащимся значениям, сам объект соединения ведет себя как прокси-сервер для вызовов методов - то, с чем могут справиться немногие системы типов, кроме утки.

Проблем системы типов можно избежать, если такие соединения разрешены только в качестве специального синтаксиса вокруг операторов сравнения. Но в этом случае они настолько ограничены, что не добавляют достаточной ценности для добавления к любому здравомыслящему языку. Такое же поведение может быть выражено с помощью операций набора или написания всех сравнений вручную, и большинство языков не верят в добавление избыточного синтаксиса, если уже есть идеальное решение.

Амон
источник
Этот конкретный пример может быть переписан более четко, как 2 in [1, 2, 3]. С другой стороны, если у numpy есть .all()или что-то, эквивалентный простой питон не так лаконичен.
Изката
@Izkata Я специально не использовал операции над множествами. Хотя в моем примере использовался ==оператор, мы также можем использовать <вместо него - где ваш inсейчас? Перекрестки являются более общими , чем членство набора тестов, поскольку операции на стыке распределить по всем членам - (x|y).fooэто x.foo|y.foo, пока узел не будет , наконец , рухнул на одно значение. Предоставленный код NumPy показывает точно эквивалентный, но более подробный перевод переходов Perl6, предполагая примитивные типы.
Амон
2

В языки с макросами легко добавить что-то подобное, если его там еще нет. Рассмотреть ракетку

(define-syntax-rule (equal-any? a b ...)
  (or (equal? a b) ...))
(equal-any? "a" "b" "a")
> #t

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

if a ∈ {b, c}
Фил
источник
2
Первые два проверяют, все ли аргументы равны; OP хочет проверить, равен ли первый аргумент любому из следующего. Любопытно, что третий фрагмент, который вы показываете, уважает это.
@delnan Извините, я неправильно понял материал. Я редактировал это.
Фил
2

В некоторых (популярных) языках ==оператор не транзитивен. Например, в JavaScript 0равно ''и '0', и , но тогда ''и '0'не равны друг другу. Больше таких причуд в PHP.

Это означает, что a == b == cэто добавит еще одну двусмысленность, потому что это может дать другой результат в зависимости от того, интерпретируется ли он как (a == b) & (a == c)или (a == b) & (a == c) & (b == c).

Конрад Моравский
источник
2

На большинстве языков это должно быть легко достижимо путем написания Inфункции, так зачем делать ее частью реального языка?

Линк, например, имеет Contains().

Хорошо, для всех вас, педанты, вот моя реализация в C #:

public static bool In<T>(this T obj, params T[] values)
{
    for(int i=0; i < values.Length; i++)
    {
        if (object.Equals(obj, values[i]))
            return true;
    }
    return false;
}
Роберт Харви
источник
Это работает в диапазоне значений времени выполнения, а не в кортеже, поскольку код OP может быть выражен как.
DeadMG
Кажется, просто потому, что это легко, не значит, что этого не следует делать. Его ... рассмотрим строительство. Почему нам всегда приходится вручную писать все эти базовые функции и алгоритмы снова и снова, снова и снова?
Zeroth
5
@ Zeroth, может быть, вы пишете одно и то же снова и снова, но другие, как правило, используют вместо этого механизмы абстракции, предлагаемые их языком. Если вы видите себя писать a == b || a == cнесколько раз, может быть , пришло время дляequals_any(a, {b, c})
Амон
Реализация «содержит» нелегко распространиться на такие вещи, как if (a > (b or c))и if (a mod (b or c) == 2).
tobyink
1
Кто-то говорил педанты? :) Это циклы foreach, поэтому здесь нет iпеременных. И в целом это, кажется, написано после того, как у вас был долгий день :) Потому что включение return trueи return falseвнутри, и внутри цикла означает, что нет никакого способа, которым он когда-либо выйдет за пределы первой итерации. Ты сравниваешь только с первым value. Кстати, почему бы не использовать, Anyкак предложил @Bob, и упростить это доreturn values.Any(value => Object.Equals(obj, value));
Конрад Моравский
1

«if (a == b или c)» работает в большинстве языков: если a == b или c не отрицательно, равно нулю или нулю.

Жаловаться, что это многословно, упускает суть: вы не должны складывать дюжину вещей в условные выражения. Если вам нужно сравнить одно значение с произвольным числом других значений, то создайте подпрограмму.

Satanicpuppy
источник
3
Какие языки составляют "большинство"?
FrustratedWithFormsDesigner
1
@FrustratedWithFormsDesigner, хорошо, если cвычисляется как логическое значение, то почти любой язык может обрабатывать a == b || c:)
Brian S
@BrianS: я предположил, что OP означает буквальный синтаксис if(a == b or c). Мне нужно сделать перерыв, я думаю ...: P
FrustratedWithFormsDesigner
@FrustratedWithFormsDesigner Lisp! ... да? ... :)
Фолькер Сигел
3
Это действительно не соответствует сути вопроса. if (a == b or c)является псевдокодом, чтобы проверить, aравен bили aравен c. Он не предназначен для проверки того, что cэто не ноль.
HVd
1

Обычно вы хотите сохранить синтаксис минимальным и вместо этого позволить таким конструкциям определяться в самом языке.

Например, в Haskell вы можете преобразовать любую функцию с двумя или более аргументами в инфиксный оператор, используя обратные ссылки. Это позволяет вам написать:

if a `elem` [b, c] then ... else ...

где elemобычная функция принимает два аргумента - значение и список значений - и проверяет, является ли первый элементом второго.

Что делать, если вы хотите использовать andвместо or? В Haskell вы можете просто использовать следующее вместо ожидания, пока поставщик компилятора реализует новую функцию:

 if all (== a) [b, c] then ... else ...
Тобиас Брандт
источник
1
Почему кто-то хочет свести синтаксис к минимуму? Что именно там происходит? Не делайте таких прокламаций без поддержки аргументов. ;)
Zeroth
1

Некоторые языки предлагают это - до некоторой степени.

Может быть, не как ваш конкретный пример, но возьмем, например, строку Python:

def minmax(min, max):
    def answer(value):
        return max > value > min
    return answer

inbounds = minmax(5, 15)
inbounds(7) ##returns True
inbounds(3) ##returns False
inbounds(18) ##returns False

Итак, некоторые языки подходят для множественных сравнений, если вы правильно их выражаете.

К сожалению, это не работает так, как вы ожидаете, для сравнения.

>>> def foo(a, b):
...     def answer(value):
...         return value == a or b
...     return answer
... 
>>> tester = foo(2, 4)
>>> tester(3)
4
>>> tester(2)
True
>>> tester(4)
4
>>> 

«Что вы имеете в виду, что он возвращает либо True, либо 4?» - прокат за тобой

Одним из решений в этом случае, по крайней мере с Python, является использование его немного по-другому:

>>> def bar(a, b):
...     def ans(val):
...             return val == a or val == b
...     return ans
... 
>>> this = bar(4, 10)
>>> this(5)
False
>>> this(4)
True
>>> this(10)
True
>>> this(9)
False
>>> 

РЕДАКТИРОВАТЬ: следующее также будет делать нечто подобное, снова в Python ...

>>> def bar(a, b):
...     def answer(val):
...             return val in (a, b)
...     return answer
... 
>>> this = bar(3, 5)
>>> this(3)
True
>>> this(4)
False
>>> this(5)
True
>>> 

Таким образом, какой бы язык вы ни использовали, возможно, вы этого не сделаете, просто вы должны сначала поближе познакомиться с тем, как на самом деле работает логика. Как правило, это просто вопрос знания того, что вы на самом деле «просите», чтобы язык сказал вам.


источник
1

Метод indexOf, используемый в массиве Array, который есть практически во всех языках, позволяет сравнивать значение с несколькими другими, поэтому, я думаю, специальный оператор не имеет особого смысла.

В JavaScript, который будет писать:

if ( [b, c].indexOf(a) != -1 ) { ....  }
GameAlchemist
источник
0

Вы спрашиваете, почему мы не можем сделать это: if(a == b or c)

Python делает это очень эффективно, фактически наиболее эффективно с set:

if a in set([b, c]):
    then_do_this()

Для проверки членства 'set' проверяет, что хэши элемента одинаковы, и только после этого сравниваются на равенство, поэтому элементы b и c должны быть хэшируемыми, в противном случае список напрямую сравнивается на равенство:

if a in [b, c]:
    then_do_this()
Аарон Холл
источник
0

Языки в стиле APL позволяют сравнивать скаляр с каждым элементом вектора за одну операцию. Это производит логический вектор. Например, я хотел бы безбожно рекламировать свой минимально-функциональный калькулятор apl, inca ( онлайн-переводчик ).

   a<5
5 
   b<4
4 
   c<5
5 
   a=b c
0 1 

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

   0!+/a=b c
1 
   c<6
6 
   0!+/a=b c
0

Итак, как говорят другие ответы, проблема в синтаксисе. В какой - то степени, синтаксические решения были найдены, на , возможно , тяжелой стоимости обучения парадигму массива.

Люзер Дрог
источник