Domanda

Ho appena iniziato a giocare con Scala, e ho appena imparato a conoscere come i metodi possono essere fatte destra-associativo (in contrasto con il più tradizionale a sinistra associatività comune nei linguaggi orientati agli oggetti imperativi).

In un primo momento, quando ho visto il codice esempio per cons una lista in Scala, avevo notato che ogni esempio ha avuto sempre l'elenco sul lato destro:

println(1 :: List(2, 3, 4))
newList = 42 :: originalList

Tuttavia, anche dopo aver visto questo più e più volte, non ho pensato su due volte, perché non sapevo (al momento) che :: è un metodo su List. Ho solo pensato che era un operatore (di nuovo, nel senso di operatore in Java) e che l'associatività non importava. Il fatto che il List sempre apparso sul lato destro nel codice di esempio mi sembrava una coincidenza (ho pensato che fosse forse solo lo "stile preferito").

Ora so meglio: deve essere scritto in questo modo perché è giusto ::-associativa.

La mia domanda è, qual è il punto di essere in grado di definire destra-associativa metodi?

E 'puramente per ragioni estetiche, o può destro del associatività in realtà avere un qualche tipo di beneficio rispetto sinistra associatività in certe situazioni?

Dal mio (novizio) punto di vista, io non vedo proprio come

1 :: myList

è niente di meglio di

myList :: 1

ma questo è ovviamente un esempio così banale che dubito si tratta di un confronto equo.

È stato utile?

Soluzione

La risposta breve è che il diritto-associatività può migliorare la leggibilità facendo ciò che il tipo di programmatore in linea con ciò che il programma fa in realtà.
Quindi, se si digita '1 :: 2 :: 3', si ottiene un elenco (1, 2, 3) di nuovo, invece di ottenere un elenco di nuovo in un ordine completamente diverso.
Sarebbe perche '1 :: 2 :: 3 :: Nil' è in realtà

List[Int].3.prepend(2).prepend(1)

scala> 1 :: 2 :: 3:: Nil
res0: List[Int] = List(1, 2, 3)

che è sia:

  • più leggibile
  • più efficiente (O (1) per prepend, vs. O (n) per un metodo append ipotetico)

(Promemoria, estratto dal libro Programmazione a Scala )
Se un metodo viene utilizzato in notazione dell'operatore, come ad esempio a * b, il metodo viene richiamato sulla operando a sinistra, come nel a.*(b) - a meno che il nome del metodo termina in due punti
. Se il nome del metodo termina in due punti, il metodo viene richiamato sulla operando di destra.
Pertanto, in 1 :: twoThree, il metodo :: viene richiamato twoThree, passando 1, in questo modo:. twoThree.::(1)

Per List, si gioca il ruolo di un'operazione di aggiunta (la lista sembra essere aggiunto dopo il '1' per formare '1 2 3', dove in realtà è uno che è anteposto per la lista).
Lista di classe non offre una vera e propria operazione di aggiunta, in quanto il tempo necessario per aggiungere a una lista cresce linearmente con la dimensione della lista, mentre anteporre con :: richiede costante di tempo .
myList :: 1 avrebbe cercato di anteporre l'intero contenuto di myList a '1', che sarebbe superiore anteponendo 1 a myList (come in '1 :: myList')

Nota: Non importa quale sia l'associatività un operatore ha, tuttavia, i suoi operandi sono sempre valutata da sinistra a destra.
Quindi, se b è un'espressione che non è solo un semplice riferimento ad un valore immutabile, allora ::: b è più precisamente trattato come il seguente blocco:

{ val x = a; b.:::(x) }

In questo blocco A rimane ancora valutato prima b, e quindi il risultato di questa valutazione è passato come operando per B ::: metodo.


  

perché fare la distinzione tra i metodi di sinistra-associative e destro associativi a tutti?

Ciò permette di mantenere l'aspetto di una normale operazione di sinistra-associativa ( '1 :: myList'), mentre in realtà applicando l'operazione sul espressione giusta causa;

  • è più efficiente.
  • , ma è più leggibile, con un ordine associativa inversa ( '1 :: myList' vs 'myList.prepend(1)')

Quindi, come dici tu, "zucchero sintattico", per quanto ne so.
Nota, nel caso di foldLeft, per esempio, potrebbero avere andato un po 'troppo lontano (con la '/:' diritto-associativa operatore equivalente)


Per includere alcuni dei vostri commenti, leggermente riformulato:

se si considera una funzione di 'append', sinistra-associativo, allora si può scrivere 'oneTwo append 3 append 4 append 5'.
Tuttavia, se fosse per aggiungere 3, 4, e 5 per oneTwo (che si potrebbe pensare dal modo in cui è scritto), sarebbe O (N).
Stessa cosa con '::' se fosse per "aggiungere". Ma non è. In realtà è per "anteporre"

Che significa 'a :: b :: Nil' è per 'List[].b.prepend(a)'

Se '::' erano anteporre e rimanere ancora partito-associativa, allora la lista risultante sarebbe nell'ordine sbagliato.
Ci si potrebbe aspettare che ritorni List (1, 2, 3, 4, 5), ma finirebbe per tornare List (5, 4, 3, 1, 2), che potrebbe essere inaspettato per il programmatore.
Questo perché, ciò che avete fatto sarebbe stato, secondo l'ordine sinistra-associativo:

(1,2).prepend(3).prepend(4).prepend(5) : (5,4,3,1,2)

Quindi, il diritto-associatività rende il codice match up con l'ordine reale del valore di ritorno.

Altri suggerimenti

  

Qual è il punto di essere in grado di definire i metodi destro associative?

Credo che il punto di metodo giusto-associativo è quello di dare a qualcuno la possibilità di estendere il linguaggio, che è il punto di operatore primario in generale.

L'overloading degli operatori è una cosa utile, in modo da Scala ha detto: Perché non aprirlo fino a qualsiasi combinazione di simboli? Piuttosto, perché fare distinzione tra gli operatori e metodi? Ora, come un programmatore libreria interagire con i tipi built-in come Int? In C ++, che lei avrebbe usato la funzione friend nell'ambito globale. Che cosa succede se vogliamo tutti tipi per attuare il :: operatore?

Il diritto-associatività offre un modo pulito per aggiungere operatore :: a tutti i tipi. Naturalmente, tecnicamente l'operatore :: è un metodo di tipo List. Ma è anche una finzione dell'operatore per built-in int e tutti degli altri tipi, almeno se si poteva ignorare il :: Nil alla fine.

Credo che riflette la filosofia della Scala di implementare tanto le cose nella biblioteca e rendendo il linguaggio flessibile per sostenerli. Questo permette l'occasione per qualcuno a venire con SuperList, che potrebbe essere chiamato come:

1 :: 2 :: SuperNil

E 'un po' un peccato che l'associatività destra è attualmente hardcoded solo per i due punti alla fine, ma credo che lo rende abbastanza facile da ricordare.

tasto destro del associatività e Left-associatività svolge un ruolo importante quando si eseguono operazioni di Lista piega. Per esempio:

def concat[T](xs: List[T], ys: List[T]): List[T] = (xs foldRight ys)(_::_)

Questo funziona perfettamente bene. Ma non è possibile eseguire la stessa operazione foldLeft usando

def concat[T](xs: List[T], ys: List[T]): List[T] = (xs foldLeft ys)(_::_)

, perché è giusto ::-associativa.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top