Por que mais idiomas não conseguem comparar um valor com mais de um outro valor? [fechadas]

10

Considere o seguinte:

if(a == b or c)

Na maioria dos idiomas, isso precisa ser escrito como:

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

que é um pouco complicado e repete as informações.

Sei que minha sintaxe de amostra acima é um pouco desajeitada, mas tenho certeza de que existem maneiras melhores de transmitir a ideia.

Por que mais idiomas não oferecem isso? Há problemas de desempenho ou sintaxe?

Zeroth
fonte
6
O SQL oferece o seguinte: where A IN (B, C)
quinta
4
Eu não estava pedindo idiomas que o ofereçam ou possam tê-lo, mas por que mais idiomas não o oferecem? Há problemas de desempenho ou sintaxe?
Zeroth
8
Para generalizar a resposta de @ thursdaysgeek, na maioria dos idiomas, geralmente você faz isso com uma contenção definida. (Ou uma lista ou uma tupla, se for mais fácil.) Funciona da mesma maneira e evita alguns problemas de sintaxe potencialmente complicados. No seu exemplo, "b ou c" significa o conjunto "{b, c}" ou é ou é um operador como || ? Em python "b ou c" significa "o valor de b Se for verdade, ou então o valor de C"
Rob
4
Essencialmente, esse é um problema de sintaxe. O problema em questão é ter uma maneira intuitiva de desambiguar a diferença entre "b ou c" e "b ou d com c".
YoungJohn
2
É bastante hacky para um caso especial a == b or c, e nem mesmo parece bom com o IMHO.

Respostas:

24

O problema da sintaxe é - que requer sintaxe.

Qualquer que seja a sintaxe do seu idioma, as pessoas que o usam precisam aprender. Caso contrário, eles correm o risco de ver o código e não saber o que ele faz. Portanto, geralmente é considerado bom se um idioma tiver uma sintaxe simples que lide com muitos casos.

No seu exemplo específico, você está tentando pegar um operador infix (uma função que aceita dois argumentos, mas está gravada Argument1 Operator Argument2) e tentando estendê-lo para vários argumentos. Isso não funciona de maneira muito limpa, porque todo o ponto dos operadores de infix, na medida em que existe um, é colocar o operador entre os dois argumentos. A extensão para (Argument1 Operator Argument2 MagicallyClearSymbol Argument3...)não parece acrescentar muita clareza Equals(Arg1,Arg2,...). O Infix também é normalmente usado para emular convenções matemáticas com as quais as pessoas estão familiarizadas, o que não seria verdade para uma sintaxe alternativa.

Não haveria nenhum problema específico de desempenho associado à sua ideia, além do fato de o analisador precisar lidar com uma gramática com outra regra de produção ou duas, o que pode ter um pequeno efeito na velocidade da análise. Isso pode fazer alguma diferença para uma linguagem interpretada ou compilada pelo JIT, mas provavelmente não uma grande diferença.

O maior problema com a idéia é que fazer muitos casos especiais em um idioma tende a ser uma má idéia .

psr
fonte
1
Além: Scala tem operadores de infix com um número arbitrário de argumentos, como operadores de infix são apenas chamadas de método sem a .. Então eles seriam escritos como arg1 op (arg2, arg3). Não é exatamente bonito, mas é necessário em alguns lugares no contexto dessa linguagem.
amon
e if my_var in (a, b)então? isso não é mais uma questão de usar a ferramenta certa para o trabalho?
Ótimos pontos. A sintaxe da linguagem deve ser essencial para a linguagem e, em seguida, você cria bibliotecas. Se o idioma estiver muito cheio de açúcar sintático "útil", fica mais difícil de usar. Nem todo mundo precisa a == b or cenquanto outros querem a == b or c but not d. A IMO é onde as funções / bibliotecas utilitárias vêm em socorro.
Allan
Talvez o necessário seja um meio pelo qual um método possa especificar que uma chamada com um número arbitrário de argumentos seja tratada como várias chamadas, com os resultados combinados de alguma maneira. Se f().Equals(a,b,c); pode ser avaliado como (var temp=f(); temp.Equals(a)||temp.Equals(b)||temp.Equals(c))essa sintaxe seria perfeita, mas se for avaliado como int[] arr = {a,b,c}; f().Equals(arr);isso não seria tão bom, especialmente se um novo array tiver que ser criado para cada chamada.
supercat
6

Porque não é um problema, e resolvê-lo traz basicamente nenhum benefício, mas sua implementação traz um custo diferente de zero.

As funções existentes baseadas em intervalo e que praticamente todos os idiomas oferecem podem funcionar perfeitamente bem nessa situação se forem dimensionadas para um tamanho em a == b || a == cque não serão cortadas.

DeadMG
fonte
2
+1, mas acho que a resposta seria aprimorada mostrando uma ou duas dessas "funções baseadas em intervalo existentes que praticamente todos os idiomas [oferecem]", apenas para que essa alternativa seja mais clara.
Avner Shahar-Kashtan
Você pode provar que "traz basicamente nenhum benefício, mas sua implementação traz um custo diferente de zero"?
Darek Nędza
3
@ DarekNędza A segunda metade não deve ser controversa: todos os recursos devem ser pensados, implementados, testados, documentados e suportados. Nenhuma dessas etapas é gratuita de acordo com uma métrica razoável (tempo das pessoas, custo de oportunidade, complexidade, custo monetário se alguém for pago para trabalhar nela e assim por diante).
@ AvnerShahar-Kashtan concordou - para mim, não é óbvio como ficaria, digamos, java, sh ou zsh? Ok, ele pode ter implícito uma linguagem 'moderna'. Groovy?
Volker Siegel
No PHP, seria semelhante in_array($a, [$b, $c, $d, $e, $f]). : P
cHao
6

Alguns idiomas possuem esses recursos. Por exemplo, no Perl6, podemos usar junções , que são "superposições" de dois valores:

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

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

As junções nos permitem expressar operações de um conjunto de dados de maneira bastante sucinta, semelhante à maneira como as operações escalares distribuem as coleções em alguns idiomas. Por exemplo, usando Python com numpy, a comparação pode ser distribuída por todos os valores:

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

No entanto, isso funciona apenas para tipos primitivos selecionados.

Por que as junções são problemáticas? Como as operações em uma junção distribuem sobre os valores contidos, o próprio objeto de junção se comporta como um proxy para chamadas de método - algo que poucos sistemas de tipos, além da digitação com patos, podem lidar.

Problemas no sistema de tipos podem ser evitados se tais junções forem permitidas apenas como sintaxe especial em torno dos operadores de comparação. Mas, neste caso, eles são tão limitados que não agregam valor suficiente para serem adicionados a qualquer linguagem sã. O mesmo comportamento pode ser expresso usando operações definidas ou explicitando manualmente todas as comparações, e a maioria dos idiomas não acredita em adicionar sintaxe redundante se já houver uma solução perfeitamente adequada.

amon
fonte
Esse exemplo numpy em particular pode ser reescrito de maneira mais clara como 2 in [1, 2, 3]. Por outro lado, se numpy tem um .all()ou algo assim, o python simples equivalente não é tão conciso.
Izkata
@ Izkata: especificamente, não usei operações definidas. Embora meu exemplo tenha usado o ==operador, também podemos usar <- onde está o seu inagora? As junções são mais gerais do que os testes de associação definidos, porque as operações na junção são distribuídas por todos os membros - ou (x|y).fooseja x.foo|y.foo, até que a junção seja finalmente recolhida para um único valor. O código NumPy fornecido mostra uma tradução exatamente equivalente, mas mais detalhada, das junções Perl6, assumindo tipos primitivos.
amon
2

Em idiomas com macros, é fácil adicionar algo assim se ainda não estiver lá. Considere Raquete

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

Em outros idiomas sem metaprogramação, talvez você possa reformular isso como verificação de associação de conjunto / lista, talvez:

if a ∈ {b, c}
Phil
fonte
2
Os dois primeiros verificam se todos os argumentos são iguais; O OP deseja verificar se o primeiro argumento é igual a um dos seguintes. Curiosamente, o terceiro trecho que você mostra respeita isso.
@ Delnan Desculpe, eu entendi mal as coisas. Eu editei.
Phil
2

Em alguns idiomas (populares), o ==operador não é transitivo. Por exemplo, no JavaScript 0é igual a ambos ''e '0', mas então ''e '0'não são iguais um ao outro. Mais dessas peculiaridades em PHP.

Isso significa que a == b == cadicionaria outra ambiguidade, pois poderia gerar um resultado diferente, dependendo de ser interpretado como (a == b) & (a == c)ou (a == b) & (a == c) & (b == c).

Konrad Morawski
fonte
2

Na maioria dos idiomas, isso deve ser trivialmente possível ao escrever uma Infunção; então, por que torná-la parte da linguagem real?

Linq, por exemplo, tem Contains().

Tudo bem, para todos os pedantes, aqui está minha implementação em 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;
}
Robert Harvey
fonte
Isso opera em um intervalo de valores em tempo de execução, não em uma tupla, pois o código do OP pode ser expresso como.
DeadMG
Parece que, só porque é fácil, não significa que não deva ser feito. É ... considere a construção. Por que sempre precisamos escrever manualmente todas essas peças básicas de funcionalidade e algoritmos, repetidas vezes, repetidas vezes?
Zeroth
5
@Zeroth, talvez você esteja escrevendo a mesma coisa repetidamente, mas outros tendem a usar os mecanismos de abstração oferecidos pelo idioma deles. Se você se vê escrito a == b || a == cvárias vezes, talvez seja hora paraequals_any(a, {b, c})
amon
Uma implementação "contém" não se estende facilmente para cobrir coisas como if (a > (b or c))e if (a mod (b or c) == 2).
tobyink
1
Alguém disse pedantes? :) É um loop foreach, então não há ivariável. E, no geral, parece ter sido escrito depois de um longo dia :) Porque colocar aqui return truee return falsedentro do loop aqui significa que nunca haverá como ir além da primeira iteração. Você está apenas comparando com o primeiro value. By the way, por que não usar Anycomo @Bob sugerido e simplificá-lo emreturn values.Any(value => Object.Equals(obj, value));
Konrad Morawski
1

"if (a == b ou c)" funciona na maioria dos idiomas: se a == b ou se c não for negativo, nulo ou zero.

Reclamar que é detalhado erra o objetivo: você não deve acumular uma dúzia de coisas em uma condição. Se você precisar comparar um valor a um número arbitrário de outros valores, crie uma sub-rotina.

Satanicpuppy
fonte
3
Quais idiomas compõem "a maioria"?
FrustratedWithFormsDesigner
1
@FrustratedWithFormsDesigner, bem como, se cfor avaliada como um booleano, em seguida, praticamente qualquer idioma pode lidar a == b || c:)
Brian S
@ Brian: Eu assumi que o OP significava a sintaxe literal if(a == b or c). Eu preciso fazer uma pausa, eu acho ...: P
FrustratedWithFormsDesigner
@FrustratedWithFormsDesigner Lisp! ... huh? ... :)
Volker Siegel
3
Isso realmente erra o objetivo da pergunta. if (a == b or c)é um pseudocódigo para verificar se aé igual bou aigual a c. Não pretende verificar se cé diferente de zero.
hvd
1

Geralmente, você deseja manter sua sintaxe no mínimo e permitir que essas construções sejam definidas no próprio idioma.

Por exemplo, no Haskell, você pode converter qualquer função com dois ou mais argumentos em um operador infix usando backticks. Isso permite que você escreva:

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

onde elemé apenas uma função normal usando dois argumentos - um valor e uma lista de valores - e verifica se o primeiro é um elemento do segundo.

E se você quiser usar em andvez de or? No Haskell, você pode simplesmente usar o seguinte, em vez de esperar que o fornecedor do compilador implemente um novo recurso:

 if all (== a) [b, c] then ... else ...
Tobias Brandt
fonte
1
Por que alguém iria querer manter a sintaxe no mínimo? O que exatamente é o trade-off lá? Não faça proclamações desse tipo sem apoiar argumentos. ;)
Zero
1

Alguns idiomas oferecem isso - até certo ponto.

Talvez não seja o seu exemplo específico , mas tome por exemplo uma linha 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

Portanto, alguns idiomas são adequados com várias comparações, desde que você a expresse corretamente.

Infelizmente, ele não funciona exatamente como você esperaria para comparações.

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

"O que você quer dizer ele retorna True ou 4?" - o aluguel depois de você

Uma solução nesse caso, pelo menos com o Python, é usá-lo de maneira um pouco diferente:

>>> 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: O seguinte também faria algo semelhante, novamente em 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
>>> 

Portanto, qualquer que seja o idioma que você esteja usando, pode não ser que você não consiga fazê-lo, apenas que você deve primeiro examinar mais de perto como a lógica realmente funciona. Normalmente, é apenas uma questão de saber o que você está 'realmente pedindo' o idioma para lhe dizer.


fonte
1

O método indexOf, usado em uma matriz, que quase todas as línguas têm, permite comparar um valor a várias outras, então acho que um operador especial não faz muito sentido.

Em javascript que escreveria:

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

Você pergunta por que não podemos fazer isso: if(a == b or c)

O Python faz isso de maneira muito eficiente, de fato, com mais eficiência set:

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

Para teste de associação, 'set' verifica se os hashes do elemento são iguais e só então compara a igualdade; portanto, os elementos, bec devem ser laváveis; caso contrário, uma lista compara diretamente a igualdade:

if a in [b, c]:
    then_do_this()
Aaron Hall
fonte
0

As linguagens no estilo APL permitem comparar um escalar com cada elemento em um vetor em uma única operação. Isso produz um vetor booleano. Por exemplo, gostaria de promover descaradamente a minha calculadora de aplicativos de recursos mínimos, o inca ( intérprete on-line ).

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

Para reduzir isso a um valor único, podemos fazer uma inclusão ou somar e verificar se há zero.

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

Portanto, como as outras respostas dizem, o problema é de sintaxe. Até certo ponto, as soluções de sintaxe ter sido encontrado, com o custo talvez pesada de aprender o paradigma matriz.

luser droog
fonte