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


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-компилируемого языка, но, вероятно, не большая разница.

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


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который не позволяет их сократить.


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 не отрицательно, равно нулю или нулю.

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


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 ) { ....  }

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.