L'operatore ':' di Haskell

0

Attualmente sto studiando l'abbinamento di modelli in Haskell da qui . L'autore fornisce un esempio dell'implementazione della funzione "head" (che restituisce il primo elemento di una lista) come segue:

head' (x:_) = x

In un certo senso spiega l'uso dell'operatore, ma non mi è chiaro. Capisco che possiamo usarlo per aggiungere elementi di fronte a un elenco:

5:[6,7] will give [5,6,7]

Non riesco a vedere come possiamo usare l'operatore ':' come parte dell'argomento (x: _) o (x: xs) in generale.

Come funziona quel meccanismo di legame che lega il primo elemento a x?

    
posta Michael 13.10.2014 - 20:10
fonte

4 risposte

1

È un po 'di confusione, perché : fa due cose diverse a seconda del contesto. In un contesto regolare, : fa come descrivi. Prepende l'elemento a sinistra con l'elenco sulla destra.

In un contesto di pattern matching, fa l'esatto opposto. È un'operazione completamente diversa, anche se usa la stessa sintassi. Dietro le quinte, il compilatore genera codice completamente diverso. In questo contesto, prende il primo elemento dell'elenco e lo assegna alla variabile prima di : , e prende il resto dell'elenco e lo assegna alla variabile dopo : . Se c'era una costante invece di una variabile, controlla che l'argomento corrisponda a quella costante, invece di eseguire un assegnamento.

Se : viene visualizzato sul lato sinistro di = o -> , si utilizza la definizione di corrispondenza del modello. In caso contrario, si utilizza la definizione prepending. La sintassi è la stessa in modo che la costruzione di una lista abbia lo stesso aspetto della decostruzione di uno.

Si sta verificando un sovraccarico su quell'unico operatore, ma quando ci si abitua, si ottiene un codice molto conciso.

    
risposta data 13.10.2014 - 21:11
fonte
10

Stai pensando a questo in una mentalità molto imperativa. (:) non fa nulla.

Significa che qualsiasi cosa viene prima che sia il primo elemento della lista. 2:[3, 4] è [2,3,4]

È importante considerare quando si osserva la corrispondenza del modello.

fun (x:xs) = ...

Questo è un modo per dare al primo elemento e alla coda di un elenco un nome . Non c'è nient'altro.

    
risposta data 28.12.2014 - 13:13
fonte
10

(:) non è un operatore regolare; è un costruttore di dati. Le liste sono costruite usando (:) al livello base. Ciò significa che i pattern possono essere abbinati all'input a seconda dell'input. Funziona con tutti i tipi di dati con costruttori.

Ecco alcuni esempi, che illustrano che tutti i costruttori possono essere usati in questo modo per i pattern:

removeJust (Just n) = n
removeJust Nothing  = error "Can't remove from Nothing"

head (x:xs) = x
head []     = error "Oh, no!"

tail (x:xs) = xs
tail []     = error "Oh, no!"

In ogni caso qui, un costruttore viene utilizzato per estrarre, o abbinare, i valori dalla struttura dei dati in qualche modo. Solo il costruttore può essere utilizzato in questa corrispondenza del modello, quindi (:) , Just e Nothing sono tutti costruttori che possono essere abbinati.

In effetti, possiamo creare il nostro tipo di dati List per illustrare come funziona:

data List t = Empty | Link t (List t)
-- Imagine Empty = [], Link 6 Empty = [6] = 6 : []
headList :: List t -> t
headList (Link x xs) = x
headList Empty       = error "Oh, no!"

Questo è essenzialmente il tipo di dati dell'elenco. Certo, è un tipo incorporato, quindi ha lo zucchero sintattico, ma alla fine è lo stesso.

    
risposta data 28.12.2014 - 11:42
fonte
1

Come altri hanno sottolineato, l'operatore : è un costruttore di dati che consente di creare un elenco aggiungendo un elemento davanti a un elenco esistente.

L'espressione (o il termine) 1 : 2 : [] è in realtà un albero 1 : (2 : []) , dove

  • Il nodo radice è etichettato con :
  • La radice ha due figli: una foglia etichettata con 1 e la sottostruttura 2 : []

Un termine può essere visto come una struttura di dati che denota un valore: il valore è ottenuto valutando / riducendo il termine:

(1 + 2) + 5 => 3 + 5 => 8

Un termine può anche essere usato come modello contro il quale vogliamo abbinare i valori. Ad esempio: il termine 1 : 2 : [] è un modello che corrisponde all'elenco [1, 2].

I pattern possono contenere variabili, ad es. %codice%. Questo è un albero con un nodo radice etichettato con x : y e due foglie etichettate con le variabili : e x . Puoi definire una sostituzione per le variabili, ad es. %codice%. Con questa sostituzione, il termine y può essere unificato con il termine x --> 1, y --> 2 : [] .

Come probabilmente già sai, la variabile speciale x : y viene usata quando siamo interessati a trovare una sostituzione, ma non siamo interessati alla sostituzione effettiva, cioè vogliamo solo sapere che ce n'è uno.

Quando si definisce una funzione in Haskell, ogni funzione sul lato sinistro contiene schemi che devono essere confrontati con gli argomenti effettivi. Se viene trovata una sostituzione, viene valutata la parte destra, con gli argomenti formali sostituiti dai termini nella sostituzione.

Quindi, un termine può essere usato per la corrispondenza del modello (ad esempio nel lato sinistro di un'equazione di funzione) o per denotare un valore (ad esempio nel lato destro di un'equazione). Affinché un termine nella parte destra rappresenti un valore, è necessario

  • non contengono nessuna variabile libera, come in 1 : 2 : []
  • o contiene solo variabili libere che sono state associate facendo corrispondere il lato sinistro con gli argomenti della chiamata di funzione.

Riassumere: si usa la stessa sintassi per schemi ed espressioni perché in entrambi i casi si stanno costruendo termini, anche se questi sono usati in modi diversi.

    
risposta data 28.12.2014 - 14:59
fonte

Leggi altre domande sui tag