Domanda

Ho questo codice che voglio fare gratis-point;

(\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))

Come faccio a farlo?

Inoltre ci sono alcune regole generali per stile libero punto diverso da "pensare a questo amd inventarsi qualcosa"?

È stato utile?

Soluzione

Per attivare una funzione

func x y z = (some expression in x, y and z)

in forma libera punti, io in genere cerco di seguire ciò che viene fatto per l'ultimo parametro z e scrivere la funzione come

func x y z = (some function pipeline built using x and y) z

Poi posso cancellare i zs per ottenere

func x y = (some function pipeline built using x and y)

Quindi ripetere il processo per y e x devono finire con func in forma libera-point. Una trasformazione essenziale riconoscere in questo processo è:

    f z = foo $ bar z    -- or f z = foo (bar z)
<=> f z = foo . bar $ z
<=> f   = foo . bar

E 'anche importante ricordare che con la valutazione parziale, si può "rompere" l'ultimo argomento a una funzione:

foo $ bar x y == foo . bar x $ y    -- foo applied to ((bar x) applied to y)

Per la vostra funzione particolare, considerano il flusso che k e t passare attraverso:

  1. Applica ord a ciascuno di essi
  2. Aggiungi i risultati
  3. Sottrai 2 * a
  4. Prendere il risultato mod 26
  5. Aggiungi un
  6. Applica chr

Così come un primo tentativo di semplificare, otteniamo:

func k t = chr . (+a) . (`mod` 26) . subtract (2*a) $ ord k + ord t

Si noti che è possibile evitare flip utilizzando una sezione sul mod, e le sezioni utilizzando - un casino in Haskell quindi non c'è una funzione subtract (si scontrano con la sintassi per la scrittura di numeri negativi: (-2) significa negativo 2, e non è il stessa subtract 2).

In questa funzione, ord k + ord t è un ottimo candidato per l'utilizzo di Data.Function.on ( link ). Questo combinatore utile permette di sostituire ord k + ord t con una funzione applicata a k e t:

func k t = chr . (+a) . (`mod` 26) . subtract (2*a) $ ((+) `on` ord) k t

Ora siamo molto vicino ad avere

func k t = (function pipeline) k t

e quindi

func = (function pipeline)

Purtroppo Haskell è un po 'disordinato, quando si tratta di comporre una funzione binaria con una successione di funzioni unari, ma c'è un trucco (Vedrò se riesco a trovare un buon riferimento per esso), e si finisce con :

import Data.Function (on)

func = ((chr . (+a) . (`mod` 26) . subtract (2*a)) .) . ((+) `on` ord)

che è quasi una bella pulita priva di punti funzione gasdotto, tranne che per quel trucco composizione brutto. Definendo l'operatore .: suggerito nella commenti in questa pagina , questo riordina un po 'per:

import Data.Function (on)

(.:) = (.).(.)

func = (chr . (+a) . (`mod` 26) . subtract (2*a)) .: ((+) `on` ord)

Per lucidare questo un po 'di più, si potrebbe aggiungere alcune funzioni di supporto per separare la lettera <-> Int conversione dalla Cesare cifratura aritmetica. Ad esempio: letterToInt = subtract a . ord

Altri suggerimenti

  

Inoltre ci sono alcune regole generali per stile libero punto diverso da "pensare a questo amd inventarsi qualcosa"?

Si può sempre barare e utilizzare il "pl" strumento da lambdabot (sia andando a #haskell su freenode o utilizzando ad esempio ghci di acido ). Per il vostro codice di pl dà:

((chr . (a +) . flip mod 26) .) . flip flip (2 * a) . ((-) .) . (. ord) . (+) . ord

Il che non è in realtà un miglioramento, se mi chiedete.

C'è sicuramente una serie di trucchi per trasformare un'espressione in stile libero-point. Non pretendo di essere un esperto, ma ecco alcuni consigli.

In primo luogo, si desidera isolare gli argomenti della funzione in più a destra termine dell'espressione. I suoi principali strumenti qui saranno flip e $, utilizzando le regole:

f a b ==> flip f b a
f (g a) ==> f $ g a

dove f e g sono funzioni e a e b sono espressioni. Quindi, per iniziare:

(\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
-- replace parens with ($)
(\k t -> chr $ (a +) . flip mod 26 $ ord k + ord t - 2*a)
-- prefix and flip (-)
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ ord k + ord t)
-- prefix (+)
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ (+) (ord k) (ord t))

Ora abbiamo bisogno di ottenere t sul lato destro della strada. Per fare questo, utilizzare la regola:

f (g a) ==> (f . g) a

E così:

-- pull the t out on the rhs
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ ((+) (ord k) . ord) t)
-- flip (.) (using a section)
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ ((. ord) $ (+) (ord k)) t)
-- pull the k out
(\k t -> chr $ (a +) . flip mod 26 $ flip (-) (2*a) $ ((. ord) . ((+) . ord)) k t)

Ora, abbiamo bisogno di trasformare tutto a sinistra del k e t in un unico grande termine funzione, in modo da avere un'espressione della forma (\k t -> f k t). Questo è dove le cose si fanno un po 'mind-bending. Per cominciare, si noti che tutti i termini fino all'ultimo $ sono funzioni con un singolo argomento, in modo da poterli comporre:

(\k t -> chr . (a +) . flip mod 26 . flip (-) (2*a) $ ((. ord) . ((+) . ord)) k t)

Ora, abbiamo una funzione di tipo Char -> Char -> Int che vogliamo comporre con una funzione di tipo Int -> Char, ottenendo una funzione di tipo Char -> Char -> Char. Possiamo ottenere che usando la regola (molto strano-looking)

f (g a b) ==> ((f .) . g) a b

Questo ci dà:

(\k t -> (((chr . (a +) . flip mod 26 . flip (-) (2*a)) .) . ((. ord) . ((+) . ord))) k t)

Ora possiamo solo applicare una riduzione beta:

((chr . (a +) . flip mod 26) .) . (flip flip (2*a) . ((-) . ) . ((. ord) . (+) .ord))

Io parto dal presupposto che il punto del punto-liberazione è quello di rendere il codice più conciso e più leggibile. Penso quindi che sia saggio fare anche alcune altre refactoring di semplificazione che poi potrebbero rendere più facile per rimuovere le variabili.

(\k t -> chr $ a + flip mod 26 (ord k + ord t - 2*a))

Prima di tutto, il flip non è necessaria:

(\k t -> chr $ a + (ord k + ord t - 2*a) `mod` 26)

Avanti, vorrei usare nome e conquistare per scomporre una sottofunzione autonomamente utilizzabile:

encode_characters k t = chr $ encode (ord k) (ord t)
encode x y = (x + y - 2*a) `mod` 26 + a

Ho anche dato un nome alla prima espressione per renderla più chiara e riutilizzabile. encode_characters ora è facile da fare gratis-point con la tecnica da @Nefrubyr:

encode_characters = chr . encode `on` ord

Per quanto riguarda la seconda espressione, non posso produrre una forma che è più leggibile rispetto a qualsiasi mostrato nelle altre risposte e sono tutti meno leggibile rispetto alla forma punto-saggio. Vorrei quindi suggerire di fermarsi refactoring a questo punto e ammirare la pulizia e la riusabilità del codice risultante.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~

PS: come un esercizio, a seconda del contesto del problema, alcune leggere modifiche delle interfacce funzionali (quali dati in quale forma è passato attraverso i funzioni) può risultare più semplificazioni generalizzando problema

.

A. Implementare e semplificare funzione encode_n_characters :: [Char] -> Char dove encode_characters k t = encode_n_characters [k, t]. E 'il risultato più semplice del funzione a due argomenti specializzato?

B. Implementare una funzione definita encode' via encode' (x + y) = encode x y e reimplementare encode_characters utilizzando questa funzione. Fa una delle funzioni diventano più semplici? È l'implementazione più semplice complessiva? È encode' più o meno riutilizzabili di encode?

IRC, #haskell , e chiedere lambdabot :

<you> @pl (\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
<lambdabot> [the answer]
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top