Usando foldr per aggiungere due liste insieme (Haskell)

4

Mi è stata data la seguente domanda come parte di un incarico universitario. A causa del fatto che il modulo è molto corto, stiamo usando solo un sottoinsieme di Haskell, senza alcuno zucchero sintattico o scorciatoie idiomatiche .... Devo scrivere:

aggiungi xs ys: l'elenco formato unendo gli elenchi xs e ys , in quell'ordine

append (5:8:3:[]) (4:7:[]) => 5:8:3:4:7:[]

Capisco il concetto di come funziona foldr, ma sto solo iniziando con la programmazione funzionale. Sono riuscito a scrivere la seguente soluzione di lavoro (nascosta a beneficio degli altri della mia classe ...):

append = \xs -> \ys -> foldr (\x -> \y -> x:y) ys xs

Tuttavia, non posso proprio per la vita di me, spiega che diavolo sta succedendo!? L'ho scritto girando intorno all'interprete, ad esempio, la seguente riga:

foldr (\x -> \y -> x:y) [] (2:3:4:[])

che ha restituito [2:3:4] , che mi ha indotto a provare,

foldr (\x -> \y -> x:y) (2:3:4:[]) (5:6:7:[])

che ha restituito [5,6,7,2,3,4]

quindi l'ho risolto da lì. Sono arrivato alla soluzione giusta attraverso il lavoro di congettura e un po 'di fortuna ...

Sto lavorando sulla seguente definizione di foldr:

foldr = \f -> \s -> \xs -> if null xs then
                              s
                           else
                              f (head xs) (foldr f s (tail xs) )

Qualcuno può aiutarmi con la mia corretta soluzione? Non riesco a capirlo ... Ho già setacciato il web, e ho anche letto alcuni thread di SE, come Come funziona foldr

    
posta lwm 04.11.2012 - 13:56
fonte

3 risposte

7

Gli elenchi di piegature sono composti da tre elementi: l'elenco da piegare, una funzione di accumulatore f e un valore iniziale.

Trasformano la lista a:b:c:[] in (a f (b f (c f init))) dove init è l'elemento iniziale, ovvero sostituiscono il costruttore cons : con la funzione accumulatore e la lista vuota [] con il valore iniziale fornito.

Puoi pensare alla tua funzione append come trasformare la lista x1:x2:..:xn nella lista x1:x2:..:xn:ys per un dato elenco ys . Questo può essere fatto semplicemente usando ys come sostituto per la lista vuota [] che termina la tua lista xs .

Il tuo codice può essere scritto come

append xs ys = foldr (\x y -> x:y) ys xs

La tua funzione accumulatore f ha il tipo a -> [a] -> [a] ed è uguale alla funzione (:) , quindi potresti scriverlo come

append xs ys = foldr (:) ys xs

Se il primo argomento xs è la lista x1: x2: ...: xn allora il risultato di append è la lista x1:x2:...xn:ys come richiesto.

    
risposta data 04.11.2012 - 14:45
fonte
4

Per prima cosa, semplifichiamo la tua soluzione un po 'con Haskell standard per renderlo più facile da comprendere:

append = \xs -> \ys -> foldr (\x -> \y -> x:y) ys xs

può essere scritto come

append xs ys = foldr (:) ys xs

perché \x -> \y -> x : y equivale a \x -> y -> (:) x y che equivale a (:) (questo è chiamato riduzione η , qui l'abbiamo applicata due volte).

Suppongo tu sappia come funziona foldr quindi diamo un'occhiata a questo caso speciale: Qui foldr è specializzato per digitare (a -> [a] -> [a]) -> [a] -> [a] -> [a] . Il valore accumulato durante la piegatura è di tipo [a] , lo stesso tipo della lista che stiamo consumando. (Questo è probabilmente ciò che rende questo un po 'confuso.) Iniziamo con ys come valore accumulato. Quindi foldr elabora elementi di xs da destra a sinistra e ad ogni passo antepone (usando : ) l'elemento attualmente ispezionato al valore accumulato al momento. Quindi inizia anteponendo l'ultimo elemento di xs davanti a ys , quindi il penultimo elemento di xs a quello ecc., Infine costruendo l'intero xs anteposto a ys .

(Come altro esercizio per comprendere le pieghe, ti suggerisco di provare a implementare foldl usando solo foldr . Passa il mouse sopra l'area seguente per un suggerimento.)

The accumulated/returned value produced by foldr needs to be a function.

    
risposta data 04.11.2012 - 14:55
fonte
-2

append :: [a] - > [a] - > [A]

append = flip (foldr (:))

Cerco di spiegare foldr:

Quando usiamo foldr (:) [1,2,3] [4,5,6] senza il flip

prendiamo il secondo argomento che è [4,5,6] l'ultimo elemento dell'argomento è 6, che applichiamo con (:) al nostro primo argomento [1,2,3] che ci darà poi [6,1,2,3].

L'ultimo elemento successivo è 5, su cui faremo la stessa operazione, che ci dà [5,6,1,2,3].

Infine finiamo con [4,5,6,1,2,3], che non è l'ordine giusto solo se scambiamo la posizione degli argomenti [1,2,3] con [4,5,6] questo lo facciamo usando flip (foldr (:)), che ci darà il giusto ordine di argomenti

e risultati in [1,2,3,4,5,6]

    
risposta data 11.11.2014 - 03:36
fonte

Leggi altre domande sui tag