Question

J'ai ce code que je veux faire sans le point;

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

Comment puis-je faire?

En outre, il existe des règles générales pour le style libre de point autre que « penser à ce amd trouver quelque chose »?

Était-ce utile?

La solution

Pour activer une fonction

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

en forme de point libre, j'essaie généralement de suivre ce qui se fait au dernier paramètre z et écrire la fonction comme

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

Alors je peux annuler les zs pour obtenir

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

Ensuite, en répétant le processus pour y et x doivent se retrouver avec func sous forme de points-libre. Une transformation essentielle pour reconnaître dans ce processus est le suivant:

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

Il est également important de se rappeler que l'évaluation partielle, vous pouvez « casser » le dernier argument à une fonction:

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

Pour votre fonction particulière, tenez compte des flux qui k et t passent par:

  1. Appliquer ord à chacun d'eux
  2. Ajoutez les résultats
  3. Soustraire 2 * a
  4. Prenez le résultat mod 26
  5. Ajoutez un
  6. Appliquer chr

comme une première tentative de simplification, nous obtenons:

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

Notez que vous pouvez éviter flip en utilisant une section sur mod et sections à l'aide - obtenir désordre dans Haskell donc il y a une fonction subtract (ils entrent en conflit avec la syntaxe pour écrire les nombres négatifs: (-2) signifie 2 négatif, et n'est pas même que subtract 2).

Dans cette fonction, ord k + ord t est un excellent candidat pour l'utilisation Data.Function.on ( lien ). Ce combinateur utile nous permet de remplacer ord k + ord t une fonction appliquée à k et t:

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

Nous sommes maintenant très près d'avoir

func k t = (function pipeline) k t

et donc

func = (function pipeline)

Malheureusement, Haskell est un peu confus quand il s'agit de composer une fonction binaire avec une suite de fonctions unaires, mais il y a un truc (je vais voir si je peux trouver une bonne référence pour elle), et nous nous retrouvons avec :

import Data.Function (on)

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

qui est presque une belle conduite de fonction de point sans propre, sauf pour ce truc de composition laid. En définissant l'opérateur .: suggéré dans les commentaires sur cette page , ce ranges-un peu à:

import Data.Function (on)

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

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

Pour polir ce un peu plus, vous pouvez ajouter des fonctions d'aide à séparer la lettre <-> Int conversion de la arithmétique de chiffrement de César. Par exemple: letterToInt = subtract a . ord

Autres conseils

Il y a certainement un ensemble de trucs pour transformer une expression en style point libre. Je ne prétends pas être un expert, mais voici quelques conseils.

D'abord, vous voulez isoler les arguments de la fonction dans le droit le plus terme de l'expression. Vos principaux outils ici seront flip et $, en utilisant les règles:

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

f et g sont des fonctions et a et b sont des expressions. Donc, pour commencer:

(\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))

Maintenant, nous avons besoin de t sur le côté droit. Pour ce faire, utilisez la règle:

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

Et:

-- 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)

Maintenant, nous devons tourner tout à gauche de k et t dans un grand terme de fonction, de sorte que nous avons une expression de la forme (\k t -> f k t). C'est là les choses deviennent un bending bits. Pour commencer, notez que tous les termes jusqu'à la dernière $ sont des fonctions avec un seul argument, afin que nous puissions les composer:

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

Maintenant, nous avons une fonction de type Char -> Char -> Int que nous voulons composer avec une fonction de type Int -> Char, ce qui donne une fonction de type Char -> Char -> Char. Nous pouvons y parvenir en utilisant la règle (très bizarre)

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

Cela nous donne:

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

Maintenant, nous pouvons simplement appliquer une réduction de la bêta:

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

Je suppose que le point de votre point est de rendre libération le code plus concis et plus lisible. Je pense donc qu'il est sage de faire aussi quelques autres refactorisations de simplification qui pourrait alors le rendre plus facile d'enlever les variables.

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

Tout d'abord, la flip est inutile:

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

Ensuite, j'utiliser nom et conquer pour factoriser une sous-fonction indépendamment utilisable:

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

J'ai aussi donné un nom à la première expression pour la rendre plus claire et réutilisable. encode_characters est maintenant facile à faire sans point en utilisant la technique de @Nefrubyr:

encode_characters = chr . encode `on` ord

En ce qui concerne la deuxième expression, je ne peux pas produire une forme qui est plus lisible que tout montré dans les autres réponses et ils sont tous moins lisible que la forme de point sage. Je propose donc d'arrêter refactorisation à ce point et admirer la propreté et la réutilisabilité du code résultant.

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

PS: comme un exercice, en fonction du contexte du problème, une légère modification des interfaces de fonction (les données sous quelle forme est passé dans les fonctions) pourrait donner plus de simplifications en généralisant le problème

.

A. Mettre en œuvre et simplifier la fonction encode_n_characters :: [Char] -> Charencode_characters k t = encode_n_characters [k, t]. Le résultat est plus simple que la fonction à deux arguments spécialisés?

B. Mettre en oeuvre un encode' de fonction définie par encode' (x + y) = encode x y et réimplémenter encode_characters utilisant cette fonction. Est-ce que l'une des fonctions deviennent plus simples? Est-ce la mise en œuvre plus simple dans son ensemble? Est-ce encode' plus ou moins réutilisable que encode?

Connectez-vous sur IRC, #haskell et demander lambdabot :

<you> @pl (\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
<lambdabot> [the answer]
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top