Pourquoi plusieurs langues n'ont-elles pas la possibilité de comparer une valeur à plusieurs autres valeurs? [fermé]


10

Considérer ce qui suit:

if(a == b or c)

Dans la plupart des langues, cela devrait être écrit comme suit:

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

ce qui est un peu lourd et répète des informations.

Je sais que ma syntaxe d'échantillon ci-dessus est légèrement maladroite, mais je suis sûr qu'il existe de meilleures façons de transmettre l'idée.

Pourquoi plus de langues ne le proposent-elles pas? Y a-t-il des problèmes de performances ou de syntaxe?


6
SQL propose que: où A IN (B, C)
jeudi jeudi

4
Je ne demandais pas les langues qui l'offrent, ou peuvent l'avoir, mais pourquoi plus de langues ne l'offrent-elles pas? Y a-t-il des problèmes de performances ou de syntaxe?
Zeroth

8
pour généraliser la réponse de @ thursdaysgeek, dans la plupart des langues, vous le faites généralement avec un confinement défini. (Ou une liste ou un tuple si c'est plus facile.) Cela fonctionne de la même manière et évite certains problèmes de syntaxe potentiellement délicats. D'après votre exemple, est-ce que "b ou c" signifie l'ensemble "{b, c}" ou est ou un opérateur comme || ? En python "b ou c" signifie "la valeur de b si true, ou bien la valeur de c"
Rob

4
Il s'agit essentiellement d'un problème de syntaxe. Le problème est d'avoir une manière intuitive de lever l'ambiguïté de la différence entre "b ou c" et "b ou'd avec c".
YoungJohn

2
C'est assez hacky dans un cas spécial a == b or c, et il n'est même pas bien lu à mon humble avis.

Réponses:


24

Le problème de syntaxe est - qu'il nécessite une syntaxe.

Quelle que soit la syntaxe de votre langue, les personnes qui l'utilisent doivent l'apprendre. Sinon, ils courent le risque de voir du code et de ne pas savoir ce qu'il fait. Ainsi, il est généralement considéré comme une bonne chose si une langue a une syntaxe simple qui gère proprement de nombreux cas.

Dans votre exemple spécifique, vous essayez de prendre un opérateur infixe (une fonction qui prend deux arguments mais est écrite Argument1 Operator Argument2) et essayez de l'étendre à plusieurs arguments. Cela ne fonctionne pas très bien parce que l'intérêt des opérateurs d'infixe, dans la mesure où il y en a un, est de placer l'opérateur entre les deux arguments. L'extension à (Argument1 Operator Argument2 MagicallyClearSymbol Argument3...)ne semble pas ajouter beaucoup de clarté Equals(Arg1,Arg2,...). Infix est également généralement utilisé pour émuler des conventions mathématiques que les gens connaissent, ce qui ne serait pas le cas d'une syntaxe alternative.

Il n'y aurait pas de problèmes de performances particuliers associés à votre idée, à part que l'analyseur aurait à gérer une grammaire avec une ou deux autres règles de production, ce qui pourrait avoir un léger effet sur la vitesse d'analyse. Cela peut faire une différence pour un langage interprété ou compilé JIT, mais probablement pas une grande différence.

Le plus gros problème avec l'idée est juste que faire beaucoup de cas spéciaux dans une langue a tendance à être une mauvaise idée .


1
En plus: Scala a des opérateurs infixes avec un nombre arbitraire d'arguments, car les opérateurs infixes ne sont que des appels de méthode sans a .. Ils seraient donc écrits comme arg1 op (arg2, arg3). Pas vraiment beau, mais nécessaire à certains endroits dans le contexte de cette langue.
amon

Et if my_var in (a, b)alors? n'est-ce pas plus une question d'utiliser le bon outil pour le travail?

Grands points. La syntaxe du langage doit être un élément essentiel du langage, puis vous créez des bibliothèques par-dessus. Si le langage est trop encombré de sucre syntaxique "utile", il devient plus difficile à utiliser. Tout le monde n'a pas besoin a == b or ctandis que d'autres le veulent a == b or c but not d. IMO, c'est là que les fonctions / bibliothèques utilitaires viennent à la rescousse.
Allan

Ce qui est peut-être nécessaire, c'est un moyen par lequel une méthode pourrait spécifier qu'un appel avec un nombre arbitraire d'arguments doit être traité comme plusieurs appels, avec les résultats combinés d'une manière ou d'une autre. Si f().Equals(a,b,c); pourrait être évaluée car (var temp=f(); temp.Equals(a)||temp.Equals(b)||temp.Equals(c))cette syntaxe serait parfaite, mais si elle est évaluée comme int[] arr = {a,b,c}; f().Equals(arr);cela ne serait pas si bonne, surtout si un nouveau tableau devait être créé pour chaque appel.
supercat

6

Parce que ce n'est pas un problème, et sa résolution n'apporte pratiquement aucun avantage, mais sa mise en œuvre entraîne un coût non nul.

Les fonctions existantes basées sur la plage et telles que pratiquement tous les langages offrent peuvent parfaitement fonctionner dans cette situation si elles évoluent à une taille où a == b || a == celles ne le coupent pas.


2
+1, mais je pense que la réponse serait améliorée en montrant une ou deux de ces "fonctions existantes basées sur la plage que pratiquement chaque langue [propose]", juste pour que cette alternative soit plus claire.
Avner Shahar-Kashtan

Pouvez-vous prouver qu'elle "n'apporte pratiquement aucun avantage, mais sa mise en œuvre entraîne un coût non nul"?
Darek Nędza

3
@ DarekNędza La seconde moitié ne devrait pas être litigieuse: chaque fonctionnalité doit être pensée, implémentée, testée, documentée et prise en charge. Aucune de ces étapes n'est gratuite sous aucune mesure raisonnable (temps des gens, coût d'opportunité, complexité, coût monétaire si quelqu'un est payé pour y travailler, etc.).

@ AvnerShahar-Kashtan D'accord - pour moi, ce n'est pas évident à quoi cela ressemblerait, disons, java, ou sh, ou zsh? Ok, il peut avoir impliqué un langage «moderne». Sensationnel?
Volker Siegel

En PHP, ça ressemblerait in_array($a, [$b, $c, $d, $e, $f]). : P
cHao

6

Certaines langues ont de telles fonctionnalités. Par exemple, en Perl6, nous pouvons utiliser des jonctions , qui sont des «superpositions» de deux valeurs:

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

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

Les jonctions nous permettent d'exprimer les opérations sur un ensemble de données de manière assez succincte, de la même manière que les opérations scalaires se répartissent sur les collections dans certaines langues. Par exemple, en utilisant Python avec numpy, la comparaison peut être répartie sur toutes les valeurs:

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

Cependant, cela ne fonctionne que pour les types primitifs sélectionnés.

Pourquoi les jonctions sont-elles problématiques? Étant donné que les opérations sur une jonction se répartissent sur les valeurs contenues, l'objet de jonction lui-même se comporte comme un proxy pour les appels de méthode - quelque chose que peu de systèmes de type en dehors de la frappe de canard peuvent gérer.

Les problèmes de système de type peuvent être évités si de telles jonctions ne sont autorisées que comme syntaxe spéciale autour des opérateurs de comparaison. Mais dans ce cas, ils sont tellement limités qu'ils n'ajoutent pas une valeur suffisante pour être ajoutés à une langue saine. Le même comportement pourrait être exprimé en utilisant des opérations d'ensemble ou en précisant manuellement toutes les comparaisons, et la plupart des langues ne croient pas à l'ajout d'une syntaxe redondante s'il existe déjà une solution parfaitement adaptée.


Cet exemple numpy particulier pourrait être réécrit plus clairement en 2 in [1, 2, 3]. D'un autre côté, si numpy a un .all()ou quelque chose, le python ordinaire équivalent n'est pas aussi concis.
Izkata

@Izkata Je n'ai pas spécifiquement utilisé d'opérations d'ensemble. Bien que mon exemple ait utilisé l' ==opérateur, nous pouvons également utiliser à la <place - où en êtes-vous inmaintenant? Jonctions sont plus générales que d' inclure des tests d'adhésion, parce que les opérations sur la jonction de distribuer sur tous les membres - (x|y).fooest x.foo|y.foo, jusqu'à ce que la jonction est finalement effondré à une valeur unique. Le code NumPy fourni montre une traduction exactement équivalente mais plus détaillée des jonctions Perl6, en supposant des types primitifs.
amon

2

Dans les langues avec des macros, il est facile d'ajouter quelque chose comme ça s'il n'est pas déjà là. Envisagez la raquette

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

Dans d'autres langues sans métaprogrammation, vous pouvez peut-être reformuler cela en vérifiant l'appartenance à une liste / liste, peut-être:

if a ∈ {b, c}

2
Les deux premiers vérifient si tous les arguments sont égaux; OP veut vérifier si le premier argument est égal à l'un des suivants. Curieusement, le troisième extrait que vous montrez respecte cela.

@delnan Désolé, j'ai mal compris les choses. Je l'ai édité.
Phil

2

Dans certaines langues (populaires), l' ==opérateur n'est pas transitif. Par exemple, en JavaScript 0est égal aux deux ''et '0', mais alors ''et '0'ne sont pas égaux les uns aux autres. Plus de ces bizarreries en PHP.

Cela signifie que a == b == ccela ajouterait une autre ambiguïté, car cela pourrait donner un résultat différent selon qu'il est interprété comme (a == b) & (a == c)ou (a == b) & (a == c) & (b == c).


2

Dans la plupart des langues, cela devrait être trivialement réalisable en écrivant une Infonction, alors pourquoi en faire une partie du langage réel?

Linq, par exemple, l'a fait Contains().

D'accord, pour tous vous pédants, voici mon implémentation en 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;
}

Cela fonctionne sur une plage de valeurs au moment de l'exécution, et non sur un tuple car le code de l'OP pourrait être exprimé sous la forme.
DeadMG

Il semble que ce n'est pas parce que c'est facile que cela ne devrait pas être fait. Son ... pensez à la construction. Pourquoi devons-nous toujours écrire manuellement toutes ces fonctionnalités et algorithmes de base, encore et encore et encore et encore?
Zeroth

5
@Zeroth vous écrivez peut-être la même chose encore et encore, mais d'autres ont tendance à utiliser à la place les mécanismes d'abstraction offerts par leur langage. Si vous voyez écrit vous - même a == b || a == cplusieurs fois, peut - être qu'il est temps pourequals_any(a, {b, c})
amon

Une implémentation «contient» ne s'étend pas facilement pour couvrir des choses comme if (a > (b or c))et if (a mod (b or c) == 2).
tobyink

1
Quelqu'un a dit pédants? :) C'est une boucle foreach, donc il n'y a pas de ivariable. Et dans l'ensemble, cela semble être écrit après une longue journée :) Parce que mettre les deux return trueet return falseà l'intérieur de la boucle ici signifie qu'il n'y a aucun moyen que cela se produise au-delà de la première itération. Vous ne comparez que le premier value. Soit dit en passant, pourquoi ne pas l'utiliser Anycomme @Bob l'a suggéré et le simplifierreturn values.Any(value => Object.Equals(obj, value));
Konrad Morawski

1

"if (a == b ou c)" fonctionne dans la plupart des langues: si a == b ou si c n'est pas négatif, nul ou nul.

Se plaindre qu'il est verbeux manque le point: vous ne devriez pas empiler une douzaine de choses dans un conditionnel. Si vous devez comparer une valeur à un nombre arbitraire d'autres valeurs, créez un sous-programme.


3
Quelles langues constituent "le plus"?
FrustratedWithFormsDesigner

1
@FrustratedWithFormsDesigner, eh bien, si l' cévaluation est booléenne, alors pratiquement n'importe quelle langue peut gérer a == b || c:)
Brian S

@BrianS: J'ai supposé que l'OP signifiait la syntaxe littérale if(a == b or c). Je dois faire une pause, je pense ...: P
FrustratedWithFormsDesigner

@FrustratedWithFormsDesigner Lisp! ... hein? ... :)
Volker Siegel

3
Cela manque vraiment le point de la question. if (a == b or c)est un pseudo-code pour vérifier s'il aest égal bou aégal à c. Il ne s'agit pas de vérifier que ce cn'est pas zéro.
hvd

1

Habituellement, vous voulez garder votre syntaxe au minimum et permettre à la place à de telles constructions d'être définies dans le langage lui-même.

Par exemple, dans Haskell, vous pouvez convertir n'importe quelle fonction avec deux ou plusieurs arguments en un opérateur infixe à l'aide de raccourcis. Cela vous permet d'écrire:

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

elemest juste une fonction normale prenant deux arguments - une valeur et une liste de valeurs - et vérifie si le premier est un élément du second.

Et si vous souhaitez utiliser à la andplace de or? Dans Haskell, vous pouvez simplement utiliser ce qui suit au lieu d'attendre que le fournisseur du compilateur implémente une nouvelle fonctionnalité:

 if all (== a) [b, c] then ... else ...

1
Pourquoi voudrait-on garder la syntaxe au minimum? Quel est exactement le compromis qui se passe là-bas? Ne faites pas de proclamations comme ça sans soutenir les arguments. ;)
Zeroth

1

Certaines langues offrent cela - dans une certaine mesure.

Peut-être pas comme exemple spécifique , mais prenez par exemple une ligne 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

Ainsi, certaines langues sont très bien avec des comparaisons multiples, tant que vous l'exprimez correctement.

Malheureusement, cela ne fonctionne pas exactement comme vous vous y attendez pour les comparaisons.

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

"Que voulez-vous dire, il renvoie True ou 4?" - la location après vous

Une solution dans ce cas, au moins avec Python, est de l'utiliser légèrement différemment:

>>> 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: Ce qui suit ferait également quelque chose de similaire, encore une fois en 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
>>> 

Donc, quelle que soit la langue que vous utilisez, il se peut que vous ne puissiez pas le faire, mais que vous devez d'abord regarder de plus près comment la logique fonctionne réellement. En général, il s'agit simplement de savoir ce que vous «demandez» réellement la langue à vous dire.


1

La méthode indexOf, utilisée sur un tableau, que presque toutes les langues ont, permet de comparer une valeur à plusieurs autres, donc je suppose qu'un opérateur spécial n'a pas beaucoup de sens.

En javascript qui écrirait:

if ( [b, c].indexOf(a) != -1 ) { ....  }

0

Vous demandez pourquoi nous ne pouvons pas faire cela: if(a == b or c)

Python le fait très efficacement, en fait, le plus efficacement avec set:

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

Pour les tests d'appartenance, 'set' vérifie que les hachages de l'élément sont les mêmes et ne compare ensuite que l'égalité, donc les éléments, b et c, doivent être lavables, sinon une liste compare directement l'égalité:

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

0

Les langages de style APL vous permettent de comparer un scalaire avec chaque élément d'un vecteur en une seule opération. Cela produit un vecteur booléen. Par exemple, je voudrais promouvoir sans vergogne ma calculatrice apl à fonctionnalités minimales, inca ( interprète en ligne ).

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

Pour réduire cela à une seule valeur, nous pouvons faire une inclusion ou en additionnant et en vérifiant la valeur non nulle.

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

Ainsi, comme le disent les autres réponses, le problème est la syntaxe. Dans une certaine mesure, des solutions de syntaxe ont été trouvées, au prix peut-être lourd de l'apprentissage du paradigme des tableaux.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.