2012-11-17 13 views
21

Ho la seguente funzione in HaskellCome posso riscrivere una funzione Haskell di due argomenti al punto-stile libero

agreeLen :: (Eq a) => [a] -> [a] -> Int 
agreeLen x y = length $ takeWhile (\(a,b) -> a == b) (zip x y) 

sto cercando di imparare a scrivere 'idiomatica' Haskell, che sembrano preferire l'utilizzo di . e di $ anziché di parentesi e anche di preferire il codice pointfree ove possibile. Non riesco proprio a liberarmi di menzionare esplicitamente x e . Qualche idea?

Penso che avrei lo stesso problema con il pointfreeing qualsiasi funzione di due argomenti.

BTW, questo è solo per cercare di scrivere un buon codice; non alcuni "usano tutto ciò che serve per renderlo pointfree" esercizio di compiti a casa.

Grazie.


(Commento aggiunto) Grazie per le risposte. Mi hai convinto che questa funzione non beneficia del pointfree. E mi hai anche dato dei grandi esempi per praticare le espressioni di trasformazione. E 'ancora difficile per me, e sembrano essere il più essenziale per Haskell come puntatori sono a C.

+2

Ho trovato hlint (http://community.haskell.org/~ndm/darcs/hlint/hlint.htm) molto utile per aiutarmi ad apprendere altri modi per scrivere espressioni, incluso l'uso. e $. – mhwombat

risposta

41

e anche preferire il codice pointfree ove possibile.

Non "dove possibile", ma "dove migliora la leggibilità (o ha altri vantaggi evidenti)".

A punto-libera la tua

agreeLen x y = length $ takeWhile (\(a,b) -> a == b) (zip x y) 

Un primo passo sarebbe quello di spostare il ($) destra, e sostituire quello che hai con un (.):

agreeLen x y = length . takeWhile (\(a,b) -> a == b) $ zip x y 

Ora, è possibile spostarla ancora più a destra:

agreeLen x y = length . takeWhile (uncurry (==)) . zip x $ y 

e lì si può immediatamente tagliare off un argomento,

agreeLen x = length . takeWhile (uncurry (==)) . zip x 

Quindi è possibile riscrivere come applicazione prefisso dell'operatore composizione,

agreeLen x = (.) (length . takeWhile (uncurry (==))) (zip x) 

e si può scrivere

f (gx)

come

f . g $ x 

generalmente, riguardo a

f = (.) (length . takeWhile (uncurry (==))) 

e g = zip, dando

agreeLen x = ((.) (length . takeWhile (uncurry (==)))) . zip $ x 

da cui l'argomento x viene facilmente rimosso. Poi si può trasformare l'applicazione prefisso (.) in una sezione e ottenere

agreeLen = ((length . takeWhile (uncurry (==))) .) . zip 

Ma, che è meno leggibile di quella originale, in modo da non consiglia di fare che, fatta eccezione per praticare la trasformazione delle espressioni in point-gratis stile.

+9

La cosa davvero notevole è che un ragazzo di nome Schönfinkel ha inventato questa tecnica nel 1920. –

11

Si potrebbe anche usare:

agreeLen :: (Eq a) => [a] -> [a] -> Int 
agreeLen x y = length $ takeWhile id $ zipWith (==) x y 

Idiomatic Haskell è tutto ciò che è più facile da leggere, non necessariamente ciò che è la maggior parte senza punti.

+0

Utilizzo di zipCon sembra molto più bello il mio. Ma la mia versione di "più facile da leggere" è molto diversa dalla maggior parte degli scrittori di Haskell, quindi sto cercando di spostare la mia versione più vicina alla loro. – JonathanZ

+3

@Jonathan Bene, anche la community ha opinioni diverse sullo stile, ma posso dirvi che la mia convenzione è usare '.' ogni volta che si hanno catene di funzioni di un argomento, e se si usano due o più argomenti basta fare loro espliciti. Per quanto riguarda '$', lo si usa principalmente per rimuovere parentesi penzoloni alla fine di un blocco 'do', come con' foo $ do ... ', o per riordinare parentesi con nidificazione multipla. –

Problemi correlati