PDA

View Full Version : [Haskell] Eseguire un ciclo in haskell


guylmaster
16-10-2011, 17:53
Dunque sto seguendo (a lezione in università) le seguenti slide:

ttp://www.cs.nott.ac.uk/~gmh/book.html

Arrivato al capitolo 5, sulla comprensione delle liste, mi da il seguente esercizio:

Using a list comprehension, define a function that returns the scalar product of two lists.


Dove per prodotto scalare di due liste intende la sommatoria del prodotto membro membro delle due liste (sono so come copia-incollarvi la formula, ad ogni modo vi ho likato le slide :D).

Ora io volevo scomporre il problema in:

1. Funzione che fa il prodotto membro membro;
2. Funzione che applica il prodotto membro membro a tutti i numeri delle due liste e ne restituisce la sommatoria.

Il punto uno l'ho sviluppato in questo modo:


single_product xs ys i = (xs !! i) * (ys !! i)


Ora quello che non saprei proprio come fare è il punto due, ovvero, come faccio a dire di applicare questo tante volte quanto sono lunghe le due liste (che per forza di cose devono essere lunghe uguali) e fargli fare la somma volta per volta??

A me non viene in mente nulla, qualche aiutino? :fagiano:

Vi ringrazio in anticipo,
guylmaster

nico159
16-10-2011, 19:14
sum ( zipWith (*) xs ys )

Non so se va, dovrebbe :D

AnonimoVeneziano
16-10-2011, 20:19
Dunque sto seguendo (a lezione in università) le seguenti slide:

ttp://www.cs.nott.ac.uk/~gmh/book.html

Arrivato al capitolo 5, sulla comprensione delle liste, mi da il seguente esercizio:

Using a list comprehension, define a function that returns the scalar product of two lists.
....

A me non viene in mente nulla, qualche aiutino? :fagiano:

Vi ringrazio in anticipo,
guylmaster

Usando le list comprhension:


scalar_product xs ys = sum $ [ x*y | x <- xs, y <- ys ]


Non posso testare il codice perchè in questo momento non ho il ghc installato , ma dovrebbe essere corretto

nico159
16-10-2011, 20:26
Usando le list comprhension:


scalar_product xs ys = sum $ [ x*y | x <- xs, y <- ys ]


Non posso testare il codice perchè in questo momento non ho il ghc installato , ma dovrebbe essere corretto
Mi sa di no, perchè per le liste:
[1, 2, 3] e [1, 2, 3]
Il risultato di [ x*y | x <- xs, y <- ys ] dovrebbe essere:
[1, 2, 3, 2, 4, 6, 3, 6, 9]
Invece il risultato corretto dovrebbe essere:
[1, 4, 9]

marco.r
16-10-2011, 20:38
Arrivato al capitolo 5, sulla comprensione delle liste, mi da il seguente esercizio:

Using a list comprehension, define a function that returns the scalar product of two lists.

<cut>
Il punto uno l'ho sviluppato in questo modo:


single_product xs ys i = (xs !! i) * (ys !! i)



Quando hai a che fare con delle liste non metterti a prendere l'i-esimo elemento, e' altamenet inefficiente e "sbagliato" (una lista non e' un array).

Lascia quindi stare il moltiplicare lo i-esimo elemento e considera solo il moltiplicare i due elementi.

Il passo di iterare su due liste e applicare una funzione alle coppie si puo' fare in molti modi. Il piu' semplice e' probabilmente quello proposto da nico159 con zipWith
In pratica

zipWith (*) [1,2,3,4] [5,6,7,8]

ritorna [5,12,21,32]

Nel tuo caso pero' e' chiesto di farlo con una list comprehension.
Il metodo piu' standard per farlo e' il seguente

[ x*y | (x,y) <- zip xs ys ]

Che si legge come "crea la lista che ha come elementi il prodotto x*y, dove x e y sono le coppie che ti ottieni accoppiando le due liste xs e ys)

Il passo successivo e' quello di sommarli e lo puoi fare con la funzione sum
per cui viene fuori qualcosa tipo

sum [ x*y | (x,y) <- zip xs ys ]


Detto questo, ci sono mille modi diversi per ottenere il risultato...

marco.r
16-10-2011, 20:41
Argh, sto diventando lento a postare XD.


scalar_product xs ys = sum $ [ x*y | x <- xs, y <- ys ]

No dovresti usare le parallel list comprehension, che pero' non sono standard
(e togliere un $ di troppo)

scalar_product xs ys = sum [ x*y | x <- xs | y <- ys ]

Oppure zippi come nel mio esempio.

AnonimoVeneziano
16-10-2011, 21:07
scalar_product xs ys = sum [ x*y | x <- xs | y <- ys ]

Oppure zippi come nel mio esempio.

Volevo scrivere questo :p

guylmaster
16-10-2011, 21:44
Argh, sto diventando lento a postare XD.


scalar_product xs ys = sum $ [ x*y | x <- xs, y <- ys ]

No dovresti usare le parallel list comprehension, che pero' non sono standard
(e togliere un $ di troppo)

scalar_product xs ys = sum [ x*y | x <- xs | y <- ys ]

Oppure zippi come nel mio esempio.

Ho provato a scrivere questa:


scalar_product :: [Int] -> [Int] -> Int
scalar_product xs ys = sum [ x*y | x <- xs, y <- ys ]


Però il risultato non è quello aspettato, ovvero ho:


Main> scalar_product [1,2,3] [4,5,6]
90


Non dovrebbe essere 1*4 + 2*5 + 6*3 = 4 * 10 + 18 ? Come fa a fare 90 ?!?

guylmaster
16-10-2011, 21:46
Se invece uso la versione con zip:


scalar_product2 xs ys = sum [ x*y | (x,y) <- zip xs ys ]


va tutto apposto, perchè? l'altra cosè che fa?!

marco.r
16-10-2011, 22:07
edit: non mi ero accorto del post piu' sopra, ora sistemo la risposta


[ x*y | x<- xs, y <- ys ]

Questa forma applica la * al prodotto cartesiano di xs e ys, ovvero TUTTE le coppie (x,y) con x in xs e y in ys
Se xs = [1,2,3] e yz = [4,5,6], il risultato e' [ 1*4, 1*5, 1*6, 2*4, 2*5, 2*6, 3*4, 3*5, 3*6 ]
Mentre la seguente

[ x*y | (x,y) <- zip xs ys ]

Prima ti genera la lista [ (1,4), (2,5), (3,6) ] e poi ti moltiplica i valori delle varie coppie, e ottieni [ 1*4, 2*5, 3*6 ]
che una volta sommati danno il risultato previsto.

guylmaster
16-10-2011, 22:34
edit: non mi ero accorto del post piu' sopra, ora sistemo la risposta


[ x*y | x<- xs, y <- ys ]

Questa forma applica la * al prodotto cartesiano di xs e ys, ovvero TUTTE le coppie (x,y) con x in xs e y in ys
Se xs = [1,2,3] e yz = [4,5,6], il risultato e' [ 1*4, 1*5, 1*6, 2*4, 2*5, 2*6, 3*4, 3*5, 3*6 ]
Mentre la seguente

[ x*y | (x,y) <- zip xs ys ]

Prima ti genera la lista [ (1,4), (2,5), (3,6) ] e poi ti moltiplica i valori delle varie coppie, e ottieni [ 1*4, 2*5, 3*6 ]
che una volta sommati danno il risultato previsto.

Ok perfetta la versione con zip, considerando anche che era una funzione già vista a lezione.

Devo ammettere però che sto incrociando non poche difficoltà... ma è normale impazzirci su questo metodo di programmazione o basta trovare qualche guida fatta meglio?!

Perchè non riesco ad immergermi al meglio nella logica di programmazione, cioè mai mi sarebbe venuto in mente l'idea di scrivere [x*y|..], credevo che potessi scrivere, trattandosi di una lista, semplicemente "l'insieme delle x dato che rispecchia le proprietà dopo il segno |".

Come anche questa cosa del prodotto cartesiano, praticamente non ragiona nel nostro modo nel senso di mettere "prima una x e poi una y" ma lo vede come "ogni x assieme ad ogni y".

Probabilmente se incorressi in qualche esempio in più di questi esercizietti svolti con le liste a mo di insiemi ci prendere meglio la mano!

marco.r
16-10-2011, 23:10
Ok perfetta la versione con zip, considerando anche che era una funzione già vista a lezione.

Devo ammettere però che sto incrociando non poche difficoltà... ma è normale impazzirci su questo metodo di programmazione o basta trovare qualche guida fatta meglio?!

Si' :D. E' un modo di pensare parecchio alternativo, e che se porti avanti contaminera' irrimediabilmente anche il tuo modo di programmare tradizionale :D.
Non scherzo :p.


Perchè non riesco ad immergermi al meglio nella logica di programmazione, cioè mai mi sarebbe venuto in mente l'idea di scrivere [x*y|..], credevo che potessi scrivere, trattandosi di una lista, semplicemente "l'insieme delle x dato che rispecchia le proprietà dopo il segno |".

A sinistra della "|" puoi mettere quello che vuoi, a patto ovviamente che le variabili referenziate siano presenti a destra del segno, oppure nel contesto.
La notazione rispecchia un po' la notazione matematica di insieme, anche se usa le [] invece che le {}.
In termini matematici il prodotto cartesiano lo avresti scritto come
{ xy | x <- xs, y <- ys }
Praticamente identico (a parte l'usuale ellissi della moltiplicazione).


Come anche questa cosa del prodotto cartesiano, praticamente non ragiona nel nostro modo nel senso di mettere "prima una x e poi una y" ma lo vede come "ogni x assieme ad ogni y".

Beh ma in matematica la definizione di un insieme come prodotto cartesiano non e' poi differente. E' quindi soll questione di abituarsi alla notazione


Probabilmente se incorressi in qualche esempio in più di questi esercizietti svolti con le liste a mo di insiemi ci prendere meglio la mano!
Ah sicuro, ci vuole pratica. Non ci sono esercizi sul tuo libro ?
Purtroppo non ho molta dimestichezza con i libri di testo o siti di esercizi su haskell, ce ne sono diversi ma non quando imparavo io, per cui... :D

guylmaster
16-10-2011, 23:53
Si' :D. E' un modo di pensare parecchio alternativo, e che se porti avanti contaminera' irrimediabilmente anche il tuo modo di programmare tradizionale :D.
Non scherzo :p.


A sinistra della "|" puoi mettere quello che vuoi, a patto ovviamente che le variabili referenziate siano presenti a destra del segno, oppure nel contesto.
La notazione rispecchia un po' la notazione matematica di insieme, anche se usa le [] invece che le {}.
In termini matematici il prodotto cartesiano lo avresti scritto come
{ xy | x <- xs, y <- ys }
Praticamente identico (a parte l'usuale ellissi della moltiplicazione).


Beh ma in matematica la definizione di un insieme come prodotto cartesiano non e' poi differente. E' quindi soll questione di abituarsi alla notazione


Ah sicuro, ci vuole pratica. Non ci sono esercizi sul tuo libro ?
Purtroppo non ho molta dimestichezza con i libri di testo o siti di esercizi su haskell, ce ne sono diversi ma non quando imparavo io, per cui... :D

Ma fammi capire in che ambito lavorativo riesci ad applicare la programmazione in haskell? Io solo per torvare un link non corrotto alla versione di Hugs per MacOs ci ho messo parecchio!

A fine slide ci sono quei 2-3 esercizi proposti, ma come vedi non c'è la soluzione. E poi secondo me 2-3 non sono abbastanza, bisognerebbe farne parecchi per prendere dimestichezza.

banryu79
17-10-2011, 08:13
A fine slide ci sono quei 2-3 esercizi proposti, ma come vedi non c'è la soluzione. E poi secondo me 2-3 non sono abbastanza, bisognerebbe farne parecchi per prendere dimestichezza.
Sicuramente, ma prima ancora è importante impadronirsi dei concetti che ci stanno dietro. Se ad esempio a lezione avevi già visto le tuple, ma non ti è venuto in mente di usarle, questo è un segnale di insufficiente padronanza di concetti base in questo linguaggio.
Il che è normale, dato che non è il solito linguaggio a paradigma imperativo/procedurale, e richiede all'utente di pensare in modo differente. Magari dipende dal testo, dal modo in cui tratta gli argomenti, non so.

Se vuoi un complemento da affiancare per lo studio (anche solo per avere sotto mano esempi diversi d'uso dei vari costrutti) e non hai problemi con l'inglese ti consiglio questo (io mi ci sto trovando parecchio bene):
http://learnyouahaskell.com/chapters

Prova a "farti" i primi te capitoli (compreso quello che introduce il type system), penso che ti aiuterà a fare chiarezza :)

marco.r
17-10-2011, 11:15
Ma fammi capire in che ambito lavorativo riesci ad applicare la programmazione in haskell? Io solo per torvare un link non corrotto alla versione di Hugs per MacOs ci ho messo parecchio!

Uhm gli stessi ambiti in cui applichi gli altri linguaggi :D, anche se per alcuni task Haskell e' piu' portato e per altri meno, quello che cambia non e' tanto cosa fai ma come lo fai.
Io lo trovo comodo soprattutto per elaborazioni batch (traduttori/generatori di codice etc.)

Per quel che riguarda hugs... ormai tutti usano ghc, a parte chi insegna :P

shinya
17-10-2011, 12:21
Per quel che riguarda hugs... ormai tutti usano ghc, a parte chi insegna :P
Già Haskell è un linguaggio da hipster... poi usare hugs! Della serie "GHC is too mainstream".

guylmaster
17-10-2011, 12:41
Sicuramente, ma prima ancora è importante impadronirsi dei concetti che ci stanno dietro. Se ad esempio a lezione avevi già visto le tuple, ma non ti è venuto in mente di usarle, questo è un segnale di insufficiente padronanza di concetti base in questo linguaggio.
Il che è normale, dato che non è il solito linguaggio a paradigma imperativo/procedurale, e richiede all'utente di pensare in modo differente. Magari dipende dal testo, dal modo in cui tratta gli argomenti, non so.

Se vuoi un complemento da affiancare per lo studio (anche solo per avere sotto mano esempi diversi d'uso dei vari costrutti) e non hai problemi con l'inglese ti consiglio questo (io mi ci sto trovando parecchio bene):
http://learnyouahaskell.com/chapters

Prova a "farti" i primi te capitoli (compreso quello che introduce il type system), penso che ti aiuterà a fare chiarezza :)


Umm lo avevo già tra i preferiti quella guida, già che me l'hai consigliata spulcerò per bene anche quella.

Comunque avevo già svolto due esercizi prima di questo con le liste, solo che non avevo visto nessun esempio in cui mettevi un operatore anche a sinistra (x*y) e semplicemente non mi era venuto in mente. Lo zip di venirmi mi era venuto, ma senza quel prodotto affianco non sapevo come sfruttarlo.

Diciamo che facendo qualche altro esercizio e vedendo qualche altro esercizio già svolto magari prendo più dimestichezza. Perchè almeno la mia mente funziona per "già visto" e poi compone le varie cose già viste. Raramente mi viene da dire "ma forse posso fare anche questo" :fagiano:

Uhm gli stessi ambiti in cui applichi gli altri linguaggi :D, anche se per alcuni task Haskell e' piu' portato e per altri meno, quello che cambia non e' tanto cosa fai ma come lo fai.
Io lo trovo comodo soprattutto per elaborazioni batch (traduttori/generatori di codice etc.)

Per quel che riguarda hugs... ormai tutti usano ghc, a parte chi insegna :P


Eh questo l'avevo immaginato ma inanzi tutto all'esame dovro utilizzare hugs quindi prenderci quel minimo di dimestichezza che serve non è una brutta cosa, inoltre per ghc ho il seguente problema su macos:

- Richiede Xcode, ma Xcode 4, che è pure gratuito è solo Lion, io che ho snow leopard come faccio? Ho pure provato (anche se non con molta insistenza) a cercare qualche versione vecchia di xcode3 ma nulla. Daltronde ho pure paura ad aggiornare a Lion perchè almeno all'inizi mi parlavano di qualche incompatibilità con del vecchio software, e vorrei evitare rotture di scatole.

banryu79
17-10-2011, 13:14
Comunque avevo già svolto due esercizi prima di questo con le liste, solo che non avevo visto nessun esempio in cui mettevi un operatore anche a sinistra (x*y) e semplicemente non mi era venuto in mente. Lo zip di venirmi mi era venuto, ma senza quel prodotto affianco non sapevo come sfruttarlo.

Chiaro.
Nel tutorial al link, quando si arriva alle list comprehension, hanno già spiegato due punti:
1) le funzioni in Haskell esistono nella forma prefissa e infissa. Quando definisci una funzione, di default, è prefissa, a meno che il nome della funzione non sia composto solamente di simboli, nel qual caso è infissa.
Per usare una fuzione prefissa come se fosse infissa devi racchiuderne il nome tra i caratteri di backtick '`'.
E' il caso della funzione elem, o div:

{- elem (prefissa) -}
elem 'o' "Hello!"

{- elem, in forma infissa -}
'o' `elem` "Hello!"

{- div (prefissa) -}
div 10 2

{- div, in forma infissa -}
10 `div` 2

Se invece vuoi usare una funzione infissa come prefissa, devi racchiuderla tra parentesi. Ad esempio le funzioni * + - /:

{- * (infissa) -}
3 * 2

{- *, in forma prefissa -}
(*) 3 2


2) una list comprehension è "formata" da tre parti:

[output function | input set, predicate]

La variabile viene "bindata" nella parte nominata input set
Sapendo che cos'è la parte prima del pipe (output function) non avresti avuto difficoltà a pensare a quell'x*y. ;)

Questo intendevo con il "capire i concetti". Senza di questi, e basandoti solo su esempi di codice, rischi di far confusione.
Comunque buono studio :)

banryu79
20-10-2011, 08:58
@guylmaster:
Ciao, se ti interessa, ti segnalo questa pagina, ci sono degli esercizi in Haskell "for beginners" (e le soluzioni):
http://blog.tmorris.net/haskell-exercises-for-beginners/

E qua altri 99 problemi (e soluzioni):
http://www.haskell.org/haskellwiki/99_Haskell_exercises