Чому більше мов не мають можливості порівнювати значення з кількома іншими значеннями? [зачинено]


10

Розглянемо наступне:

if(a == b or c)

У більшості мов це потрібно писати так:

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

яка трохи громіздка і повторює інформацію.

Я знаю, що мій синтаксис вищезгаданого зразка трохи незграбний, але я впевнений, що є кращі способи донести цю ідею.

Чому більше мов не пропонують? Чи є проблеми з продуктивністю чи синтаксисом?


6
SQL пропонує: де A IN (B, C)
четвер,

4
Я не запитував мов, які пропонують це, чи можуть його мати, але чому б більше мов не пропонували? Чи є проблеми з продуктивністю чи синтаксисом?
Зерот

8
щоб узагальнити відповідь @ th Thursdaysgeek, у більшості мов, як правило, ви робите це з обмеженим вмістом. (Або список, або кортеж, якщо це простіше.) Це працює так само і уникає деяких потенційно складних проблем із синтаксисом. З вашого прикладу, чи означає "b або c" набір "{b, c}" або є чи таким оператором, як || ? У python "b або c" означає "значення b, якщо є істинним, інакше значення c"
Роб

4
По суті це питання синтаксису. Проблема полягає в інтуїтивно зрозумілому способі розмежування різниці між "b або c" і "b або б з c".
YoungJohn

2
До особливих випадків a == b or cце досить гакітно, і навіть це не дуже добре IMHO.

Відповіді:


24

Проблема синтаксису полягає в тому, що він вимагає синтаксису.

Який би синтаксис не мав, люди, які використовують цю мову, повинні її вивчати. Інакше вони ризикують побачити код і не знати, що він робить. Таким чином, це взагалі вважається хорошою справою, якщо мова має простий синтаксис, який чітко обробляє багато випадків.

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

З вашою ідеєю не було б жодних конкретних проблем, пов'язаних з ефективністю, крім того, що аналізатору доведеться мати справу з граматикою з іншим виробничим правилом або двома, що може незначно вплинути на швидкість розбору. Це може призвести до певної різниці для інтерпретованої мови чи мови, складеної JIT, але, ймовірно, не має великої різниці.

Більша проблема ідеї полягає лише в тому, що створення багатьох особливих випадків мовою, як правило, є поганою ідеєю .


1
Убік: Scala має інфіксні оператори з довільною кількістю аргументів, оскільки оператори infix - це лише виклики методів без a .. Отже, вони будуть записані як 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);це було б не так добре, особливо якщо для кожного виклику потрібно було створити новий масив.
supercat

6

Тому що це не проблема, і її вирішення приносить в основному нульову користь, але реалізація приносить ненульову вартість.

Існуючі функції, що базуються на діапазоні, і такі, що практично кожна мова пропонує, можуть в цій ситуації чудово працювати, якщо вона масштабується до розміру, де a == b || a == cїї не вирізати.


2
+1, але я думаю, що відповідь було б покращено, показавши одну або дві з тих "існуючих функцій на основі діапазону, які практично кожна мова [пропонує]", тільки щоб ця альтернатива була зрозумілішою.
Авнер Шахар-Каштан

Чи можете ви довести, що вона "приносить в основному нульову користь, але реалізація приносить ненульову вартість"?
Darek Nędza

3
@ DarekNędza Друга половина не повинна бути спірною: кожну функцію потрібно продумати, реалізувати, перевірити, задокументувати та підтримати. Жоден із цих кроків не є безкоштовним за будь-якої розумної метрики (час людей, можливі витрати, складність, грошові витрати, якщо хтось заплатив за роботу над цим і так далі).

@ AvnerShahar-Kashtan Погодився - мені не очевидно, як це виглядатиме в скажімо, java, або sh, або zsh? Гаразд, він, можливо, мав на увазі "сучасну" мову. Groovy?
Волкер Зігель

У PHP це виглядатиме так in_array($a, [$b, $c, $d, $e, $f]). : P
cHao

6

Деякі мови мають такі функції. Наприклад, в Perl6 ми можемо використовувати Junctions , які є "суперпозиціями" двох значень:

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
Перші два перевіряють, чи всі аргументи рівні; ОП хоче перевірити, чи дорівнює перший аргумент будь-якому з наступних. Цікаво, що третій фрагмент, який ви показуєте, це поважає.

@delnan Вибачте, я нерозумів речі. Я це відредагував.
Філ

2

У деяких (популярних) мовах ==оператор не є перехідним. Наприклад, у JavaScript 0дорівнює і те, ''і '0', але тоді ''і '0'не дорівнює одному. Більше таких примх у PHP.

Це означає, що a == b == cце додасть чергової неоднозначності, оскільки це може дати інший результат залежно від того, чи трактується це як (a == b) & (a == c)чи (a == b) & (a == c) & (b == c).


2

У більшості мов це має бути тривіально досяжним шляхом написання Inфункції, то чому б це зробити частиною фактичної мови?

Наприклад, Linq 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

Здається, це просто тому, що це легко, не означає, що цього не слід робити. Його ... врахуйте будівництво. Чому нам завжди потрібно вручну писати всі ці основні фрагменти функціональності та алгоритмів знову і знову і знову?
Зерот

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 не є негативним, null або zero.

Поскаржившись на те, що це багатослівний, не вистачає сенсу: вам не слід складати десяток речей в умовне. Якщо вам потрібно порівняти одне значення з довільною кількістю інших значень, тоді побудуйте підпрограму.


3
Які мови складають "найбільше"?
FrustratedWithFormsDesigner

1
@FrustratedWithFormsDesigner, ну якщо cоцінюється як булева, то майже будь-яка мова може впоратися a == b || c:)
Brian S

@BrianS: Я припускав, що ОП означав буквальний синтаксис 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
Чому б хотілося звести синтаксис до мінімуму? Що саме там йде на компроміс? Не робіть таких проголошень, не підтримуючи аргументів. ;)
Зерот

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
>>> 

EDIT: Наступне також зробить щось подібне, знову ж таки в 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, який використовується в масиві, який мають усі мови, дозволяє порівнювати значення з кількома іншими, тому я думаю, що спеціальний оператор не має особливого сенсу.

У 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, інку ( онлайн-перекладач ).

   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.