Perché non più lingue hanno la possibilità di confrontare un valore con più di un altro valore? [chiuso]


10

Considera quanto segue:

if(a == b or c)

Nella maggior parte delle lingue, questo dovrebbe essere scritto come:

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

che è leggermente ingombrante e ripete le informazioni.

So che la mia sintassi di esempio sopra è leggermente goffa, ma sono sicuro che ci sono modi migliori per trasmettere l'idea.

Perché non lo offrono più lingue? Esistono problemi di prestazioni o di sintassi?


6
SQL offre questo: dove A IN (B, C)
giovedì

4
Non stavo chiedendo lingue che lo offrano o che possano averlo, ma perché non lo offrono più lingue? Esistono problemi di prestazioni o di sintassi?
Zeroth,

8
per generalizzare la risposta di @s Thursdaysgeek, nella maggior parte delle lingue, di solito lo fai con il contenimento impostato. (O un elenco o una tupla se è più facile.) Funziona allo stesso modo ed evita alcuni problemi di sintassi potenzialmente complicati. Dal tuo esempio, "b o c" significa l'insieme "{b, c}" o è o un operatore come || ? In pitone "b o c" significa "il valore di b se vero, oppure il valore di c"
Rob

4
Essenzialmente questo è un problema di sintassi. Il problema attuale è avere un modo intuitivo per chiarire la differenza tra "b o c" e "b or'd con c".
Young John

2
È piuttosto confuso nel caso speciale a == b or c, e non funziona nemmeno bene IMHO.

Risposte:


24

Il problema della sintassi è che richiede sintassi.

Qualunque sia la sintassi della tua lingua, le persone che usano la lingua devono impararla. Altrimenti corrono il rischio di vedere il codice e di non sapere cosa fa. Quindi è generalmente considerato positivo se una lingua ha una sintassi semplice che gestisce in modo pulito molti casi.

Nel tuo esempio specifico, stai provando a prendere un operatore infix (una funzione che accetta due argomenti ma è scritto Argument1 Operator Argument2) e stai cercando di estenderlo a più argomenti. Ciò non funziona in modo molto chiaro perché l'intero punto degli operatori di infissione, nella misura in cui ce n'è uno, è mettere l'operatore tra i 2 argomenti. L'estensione a (Argument1 Operator Argument2 MagicallyClearSymbol Argument3...)non sembra aggiungere molta chiarezza Equals(Arg1,Arg2,...). In genere, Infix viene utilizzato per emulare convenzioni matematiche con cui le persone hanno familiarità, il che non sarebbe vero per una sintassi alternativa.

Non ci sarebbero particolari problemi di prestazioni associati alla tua idea, a parte il fatto che il parser dovrebbe avere a che fare con una grammatica con un'altra o due regole di produzione, che potrebbe avere un leggero effetto sulla velocità di analisi. Ciò potrebbe fare la differenza per un linguaggio interpretato o compilato JIT, ma probabilmente non è una grande differenza.

Il problema più grande dell'idea è proprio che fare molti casi speciali in una lingua tende a essere una cattiva idea .


1
A parte: Scala ha operatori infix con un numero arbitrario di argomenti, poiché gli operatori infix sono solo chiamate di metodo senza a .. Quindi sarebbero stati scritti come arg1 op (arg2, arg3). Non esattamente bello, ma necessario in alcuni punti nel contesto di quella lingua.
amon,

e if my_var in (a, b)allora? non è più una questione di utilizzo dello strumento giusto per il lavoro?

Grandi punti. La sintassi della lingua dovrebbe essere essenziale per la lingua, e quindi si creano librerie su di essa. Se la lingua è troppo ingombra di zucchero sintetico "utile", diventa più difficile da usare. Non tutti hanno bisogno a == b or cmentre gli altri vogliono a == b or c but not d. IMO è qui che le funzioni / librerie di utilità vengono in soccorso.
Allan,

Forse ciò che è necessario è un mezzo attraverso il quale un metodo potrebbe specificare che una chiamata con un numero arbitrario di argomenti dovrebbe essere gestita come più chiamate, con i risultati combinati in qualche modo. Se f().Equals(a,b,c); potrebbe essere valutato in quanto (var temp=f(); temp.Equals(a)||temp.Equals(b)||temp.Equals(c))tale sintassi sarebbe perfetta, ma se fosse valutato in quanto int[] arr = {a,b,c}; f().Equals(arr);non sarebbe così buono, soprattutto se fosse necessario creare un nuovo array per ogni chiamata.
supercat

6

Perché non è un problema, e risolverlo porta sostanzialmente zero benefici, ma implementarlo comporta un costo diverso da zero.

Le funzioni esistenti basate sulla portata e tali che praticamente ogni lingua offre possono funzionare perfettamente in questa situazione se si adattano a una dimensione a == b || a == cche non lo taglierà.


2
+1, ma penso che la risposta sarebbe migliorata mostrando una o due di quelle "funzioni basate sul range esistenti che praticamente ogni lingua [offre]", solo così questa alternativa sarebbe più chiara.
Avner Shahar-Kashtan,

Puoi provare che "porta sostanzialmente zero benefici, ma implementandolo porta un costo diverso da zero"?
Darek Nędza,

3
@ DarekNędza La seconda metà non dovrebbe essere controversa: ogni funzionalità deve essere pensata, implementata, testata, documentata e supportata. Nessuno di questi passaggi è gratuito secondo una metrica ragionevole (tempo delle persone, costo delle opportunità, complessità, costo monetario se qualcuno è pagato per lavorarci sopra e così via).

@ AvnerShahar-Kashtan D'accordo - per me, non è ovvio come apparirebbe, diciamo, java, sh o zsh? Ok, potrebbe aver implicato un linguaggio "moderno". Groovy?
Volker Siegel,

In PHP, sembrerebbe in_array($a, [$b, $c, $d, $e, $f]). : P
cHao,

6

Alcune lingue hanno tali caratteristiche. Ad esempio in Perl6 possiamo usare le giunzioni , che sono "sovrapposizioni" di due valori:

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

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

Le giunzioni ci consentono di esprimere le operazioni su un insieme di dati in modo abbastanza succinto, in modo simile al modo in cui le operazioni scalari si distribuiscono su raccolte in alcune lingue. Ad esempio usando Python con numpy, il confronto può essere distribuito su tutti i valori:

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

Tuttavia, questo funziona solo per i tipi primitivi selezionati.

Perché le giunzioni sono problematiche? Poiché le operazioni su una giunzione si distribuiscono sui valori contenuti, l'oggetto di giunzione stesso si comporta come un proxy per le chiamate di metodo - qualcosa che pochi sistemi di tipo oltre alla tipizzazione duck possono gestire.

I problemi di sistema di tipo possono essere evitati se tali giunzioni sono consentite solo come sintassi speciale attorno agli operatori di confronto. Ma in questo caso, sono così limitati che non aggiungono un valore sufficiente per essere aggiunti a qualsiasi linguaggio sano. Lo stesso comportamento potrebbe essere espresso utilizzando le operazioni impostate o spiegando manualmente tutti i confronti e la maggior parte delle lingue non crede nell'aggiunta di sintassi ridondante se esiste già una soluzione perfettamente valida.


Quel particolare esempio intorpidito potrebbe essere riscritto più chiaramente come 2 in [1, 2, 3]. D'altra parte, se numpy ha un .all()o qualcosa del genere, il pitone semplice equivalente non è altrettanto conciso.
Izkata,

@Izkata In particolare non ho usato le operazioni impostate. Mentre il mio esempio ha usato l' ==operatore, possiamo anche usare <invece: dov'è il tuo inadesso? Giunzioni sono più generale di test di adesione stabiliti, poiché le operazioni di giunzione distribuiscono su tutti i membri - (x|y).fooè x.foo|y.foo, fino a quando la giunzione è finalmente crollata ad un singolo valore. Il codice NumPy fornito mostra una traduzione esattamente equivalente ma più dettagliata delle giunzioni Perl6, assumendo tipi primitivi.
amon,

2

Nelle lingue con macro, è facile aggiungere qualcosa del genere se non è già lì. Considera la racchetta

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

In altre lingue senza metaprogrammazione, forse puoi riformularla come controllo dell'appartenenza set / list forse:

if a ∈ {b, c}

2
I primi due controllano se tutti gli argomenti sono uguali; OP vuole verificare se il primo argomento è uguale a uno dei seguenti. Curiosamente, il terzo frammento che mostri lo rispetta.

@delnan Mi dispiace di aver frainteso le cose. L'ho modificato.
Phil

2

In alcune lingue (popolari) l' ==operatore non è transitivo. Ad esempio in JavaScript 0è uguale a entrambi ''e '0', ma poi ''e '0'non sono uguali a vicenda. Altre di queste stranezze in PHP.

Significa che a == b == caggiungerebbe un'altra ambiguità, perché potrebbe produrre un risultato diverso a seconda che sia interpretato come (a == b) & (a == c)o (a == b) & (a == c) & (b == c).


2

Nella maggior parte delle lingue, questo dovrebbe essere banalmente raggiungibile scrivendo una Infunzione, quindi perché renderlo parte della lingua attuale?

Linq, per esempio, ha Contains().

Bene, per tutti voi pedanti, ecco la mia implementazione in 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;
}

Ciò funziona su un intervallo di valori di runtime, non su una tupla in quanto il codice dell'OP potrebbe essere espresso come.
DeadMG

Sembra che, solo perché è facile, non significa che non dovrebbe essere fatto. È ... considera la costruzione. Perché dobbiamo sempre scrivere manualmente tutte queste funzionalità di base e algoritmi, ancora e ancora e ancora e ancora?
Zeroth,

5
@Zeroth forse stai scrivendo sempre la stessa cosa, ma altri tendono invece a usare i meccanismi di astrazione offerti dalla loro lingua. Se ti vedi scrivere a == b || a == cpiù volte, forse è il momento diequals_any(a, {b, c})
amon

Un'implementazione "contiene" non si estende facilmente a cose come if (a > (b or c))e if (a mod (b or c) == 2).
tobyink

1
Qualcuno ha detto pedanti? :) È un ciclo foreach, quindi non ci sono ivariabili. E nel complesso sembra che sia stato scritto dopo che hai avuto una lunga giornata :) Perché mettere entrambi return truee return falsedentro il ciclo qui significa che non c'è modo che riuscirà mai oltre la prima iterazione. Stai solo confrontando con il primo value. A proposito, perché non usare Anycome suggerito da @Bob e semplificarloreturn values.Any(value => Object.Equals(obj, value));
Konrad Morawski

1

"if (a == b o c)" funziona nella maggior parte delle lingue: se a == b o se c non è negativo, nullo o zero.

Lamentarsi del fatto che sia prolisso manca il punto: non dovresti accumulare una dozzina di cose in un condizionale. Se è necessario confrontare un valore con un numero arbitrario di altri valori, creare una subroutine.


3
Quali lingue compongono "la maggior parte"?
FrustratedWithFormsDesigner

1
@FrustratedWithFormsDesigner, beh, se viene cvalutato in modo booleano, praticamente qualsiasi lingua può gestire a == b || c:)
Brian S,

@BrianS: supponevo che OP significasse la sintassi letterale if(a == b or c). Devo fare una pausa, penso ...: P
FrustratedWithFormsDesigner

@FrustratedWithFormsDesigner Lisp! ... eh? ... :)
Volker Siegel,

3
Questo manca davvero il punto della domanda. if (a == b or c)è pseudo-codice per verificare se aè uguale bo auguale a c. Non ha lo scopo di verificare che csia diverso da zero.
hvd,

1

Di solito, vuoi mantenere la tua sintassi al minimo e invece consentire a tali costrutti di essere definiti nella lingua stessa.

Ad esempio, in Haskell è possibile convertire qualsiasi funzione con due o più argomenti in un operatore infix usando i backtick. Questo ti permette di scrivere:

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

dove elemè solo una normale funzione che accetta due argomenti - un valore e un elenco di valori - e verifica se il primo è un elemento del secondo.

Cosa succede se si desidera utilizzare andanziché or? In Haskell, puoi semplicemente utilizzare quanto segue invece di attendere che il fornitore del compilatore implementi una nuova funzionalità:

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

1
Perché dovremmo voler ridurre al minimo la sintassi? Cosa sta succedendo esattamente il trade-off? Non fare proclami del genere senza sostenere argomenti. ;)
Zeroth,

1

Alcune lingue lo offrono - fino a un certo punto.

Forse non come esempio specifico , ma prendi ad esempio una linea 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

Quindi, alcune lingue vanno bene con confronti multipli, purché tu lo esprima correttamente.

Sfortunatamente, non funziona come ci si aspetterebbe per un confronto.

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

"Cosa vuoi dire con restituisce True o 4?" - il noleggio dopo di te

Una soluzione in questo caso, almeno con Python, è usarla in modo leggermente diverso:

>>> 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: Quanto segue farebbe anche qualcosa di simile, sempre in 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
>>> 

Quindi, qualunque sia la lingua che stai usando, potrebbe non essere che non puoi farlo, solo che devi prima dare un'occhiata più da vicino a come funziona effettivamente la logica. In genere si tratta solo di sapere cosa stai "chiedendo" alla lingua.


1

Il metodo indexOf, usato su un array, che quasi tutte le lingue hanno, permette di confrontare un valore con molti altri, quindi immagino che un operatore speciale non abbia molto senso.

In javascript che scriverebbe:

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

0

Mi chiedi perché non possiamo farlo: if(a == b or c)

Python lo fa in modo molto efficiente, infatti, in modo più efficiente con set:

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

Per i test di appartenenza, 'set' verifica che gli hash dell'elemento siano gli stessi e solo allora confronta per uguaglianza, quindi gli elementi, bec, devono essere hash, altrimenti un elenco confronta direttamente per uguaglianza:

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

0

I linguaggi in stile APL ti consentono di confrontare uno scalare con ogni elemento in un vettore in una singola operazione. Questo produce un vettore booleano. Ad esempio, vorrei promuovere spudoratamente la mia calcolatrice apl con funzionalità minime, inca ( interprete online ).

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

Per ridurlo a un singolo valore, possiamo fare un inclusivo o sommando e controllando lo zero.

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

Quindi, come dicono le altre risposte, il problema è la sintassi. In una certa misura, sono state trovate soluzioni di sintassi , a un costo forse considerevole per l'apprendimento del paradigma dell'array.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.