Domanda

Sto cercando di capire cosa sta facendo l'operatore punto in questo codice Haskell:

sumEuler = sum . (map euler) . mkList

L'intero codice sorgente è sotto.

La mia comprensione

L'operatore punto sta prendendo le due funzioni sum e il risultato di map euler e il risultato di mkList come input.

Ma (map euler) non è una funzione, è l'argomento della funzione, giusto? Quindi cosa sta succedendo qui?

Inoltre, cosa sta facendo <=>?

Codice

mkList :: Int -> [Int]
mkList n = [1..n-1]

euler :: Int -> Int
euler n = length (filter (relprime n) (mkList n))

sumEuler :: Int -> Int
sumEuler = sum . (map euler) . mkList
È stato utile?

Soluzione

In parole povere, . è composizione delle funzioni, proprio come in matematica:

f (g x) = (f . g) x

Nel tuo caso, stai creando una nuova funzione, sumEuler che potrebbe anche essere definita in questo modo:

sumEuler x = sum (map euler (mkList x))

Lo stile nel tuo esempio è chiamato " point-free " stile: gli argomenti della funzione vengono omessi. Ciò rende il codice più chiaro in molti casi. (Può essere difficile brontolare la prima volta che lo vedi, ma ti abituerai dopo un po '. È un linguaggio comune di Haskell.)

Se sei ancora confuso, può essere utile correlare f a qualcosa come una pipe UNIX. Se l'output di g diventa l'input di h, il cui output diventa l'input di f < x | g | h, lo scriverei sulla riga di comando come |. In Haskell, h . g . f $ x funziona come UNIX map (\x -> x * 2 + 10) [1..10], ma & Quot; indietro & Quot; - (+10) . (*2) <$> [1..10]. Trovo che questa notazione sia molto utile quando, diciamo, elabora un elenco. Invece di alcune costruzioni ingombranti come (+10) . (*2) $ 10, potresti semplicemente scrivere <=>. (E, se vuoi applicare quella funzione solo a un singolo valore; è <=>. Coerente!)

La wiki di Haskell ha un buon articolo con qualche dettaglio in più: http://www.haskell.org/ haskellwiki / Pointfree

Altri suggerimenti

Il. l'operatore compone le funzioni. Ad esempio,

a . b

Dove a e b sono funzioni è una nuova funzione che esegue b sui suoi argomenti, quindi a su quei risultati. Il tuo codice

sumEuler = sum . (map euler) . mkList

è esattamente lo stesso di:

sumEuler myArgument = sum (map euler (mkList myArgument))

ma si spera che sia più facile da leggere. La ragione per cui ci sono parentesi attorno a map euler è perché rende più chiaro che ci sono 3 funzioni che sono composte: sum , map euler e mkList - map euler è una singola funzione.

sum è una funzione nel preludio di Haskell, non un argomento per sumEuler. Ha il tipo

Num a => [a] -> a

L'operatore di composizione della funzione . ha tipo

(b -> c) -> (a -> b) -> a -> c

Quindi abbiamo

sum                        :: Num a => [a] -> a
map                        :: (a -> b) -> [a] -> [b]
euler                      :: Int -> Int
mkList                     :: Int -> [Int]
(map euler)                :: [Int] -> [Int]
(map euler) . mkList       :: Int -> [Int]
sum . (map euler) . mkList :: Int -> Int

Nota che Int è un'istanza di Num.

Il. l'operatore viene utilizzato per la composizione delle funzioni. Proprio come la matematica, se devi funzioni f (x) eg (x) f. g diventa f (g (x)).

map è una funzione integrata che applica una funzione a un elenco. Mettendo la funzione tra parentesi la funzione viene trattata come un argomento. Un termine per questo è currying . Dovresti cercarlo.

Quello che fa è che prende una funzione con diciamo due argomenti, applica l'argomento eulero. (map euler) giusto? e il risultato è una nuova funzione, che accetta solo un argomento.

somma. (mappa eulero). mkList è fondamentalmente un modo elegante di mettere tutto insieme. Devo dire che il mio Haskell è un po 'arrugginito ma forse puoi mettere insieme l'ultima funzione da solo?

L'operatore punto applica la funzione a sinistra (sum) all'output della funzione a destra. Nel tuo caso, stai concatenando diverse funzioni insieme: stai passando il risultato da mkList a (map euler) e quindi passando il risultato a <=>. Questo sito ha una buona introduzione a molti dei concetti.

  

Operatore punto in Haskell

     

Sto cercando di capire cosa sta facendo l'operatore punto in questo codice Haskell:

sumEuler = sum . (map euler) . mkList

Risposta breve

Codice equivalente senza punti, questo è solo

sumEuler = \x -> sum ((map euler) (mkList x))

o senza lambda

sumEuler x = sum ((map euler) (mkList x))

perché il punto (.) indica la composizione della funzione.

Risposta più lunga

Innanzitutto, semplifichiamo l'applicazione parziale da euler a map:

map_euler = map euler
sumEuler = sum . map_euler . mkList

Ora abbiamo solo i punti. Cosa è indicato da questi punti?

Da la fonte :

(.)    :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)

Pertanto (.) è operatore di composizione .

Componi

In matematica, potremmo scrivere la composizione di funzioni, f (x) e g (x), cioè f (g (x)), come

  

(f & # 8728; g) (x)

che può essere letto " f composto da g " ;.

Quindi in Haskell, f & # 8728; g, o f composto con g, può essere scritto:

f . g

La composizione è associativa, il che significa che f (g (h (x))), scritto con l'operatore di composizione, può tralasciare le parentesi senza alcuna ambiguità.

Cioè, poiché (f & # 8728; g) & # 8728; h è equivalente a f & # 8728; (g & # 8728; h), possiamo semplicemente scrivere f & # 8728; g & # 8728; h.

Tornando indietro

Tornando alla nostra semplificazione precedente, questo:

sumEuler = sum . map_euler . mkList

significa solo che sumEuler è una composizione non applicata di tali funzioni:

sumEuler = \x -> sum (map_euler (mkList x))
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top