Pregunta

Tengo este código que quiero que no tenga puntos;

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

¿Cómo puedo hacer eso?

¿También existen algunas reglas generales para el estilo libre de puntos además de "pensar en esto y pensar en algo"?

¿Fue útil?

Solución

Para activar una función

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

en forma libre puntos, por lo general trato de seguir lo que se hace al último parámetro z y escribir la función como

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

Entonces puedo anular los zs para obtener

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

A continuación, repetir el proceso para y y x debe terminar con func en forma libre-punto. Una transformación esencial reconocer en este proceso es:

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

También es importante recordar que la evaluación parcial, se puede "romper" el último argumento a una función:

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

Para su función en particular, consideran que el flujo k y t pasan por:

  1. Aplicar ord a cada uno de ellos
  2. Añadir los resultados
  3. Reste 2 * a
  4. Tome el resultado mod 26
  5. Añadir a
  6. Aplicar chr

Así como un primer intento de simplificar, obtenemos:

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

Tenga en cuenta que usted puede evitar flip mediante el uso de una sección sobre mod, y las secciones usando - causar problemas en Haskell por lo que hay una función subtract (que chocan con la sintaxis para escribir los números negativos: significa (-2) 2 negativo, y no es el mismo que subtract 2).

En esta función, ord k + ord t es un excelente candidato para el uso de Data.Function.on ( enlace ). Esto nos deja combinador útil reemplazar ord k + ord t con una función aplicada a k y t:

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

Ahora estamos muy cerca de tener

func k t = (function pipeline) k t

y por lo tanto

func = (function pipeline)

Desafortunadamente Haskell es un poco complicado cuando se trata de componer una función binaria con una secuencia de funciones unarios, pero hay un truco (Voy a ver si puedo encontrar una buena referencia para ello), y terminamos con :

import Data.Function (on)

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

que es casi una buena canalización función libre de punto limpio, a excepción de que la composición truco feo. Al definir el operador .: sugiere en la rel="noreferrer"> href="http://www.reddit.com/r/programming/comments/a9tb2/secret_haskell_operators/" comentarios en esta página , este limpia y organiza un poco para:

import Data.Function (on)

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

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

Para pulir esto un poco más, se podría añadir algunas funciones de ayuda a separar la letra <-> Int conversión de la cifrado César aritmética. Por ejemplo: letterToInt = subtract a . ord

Otros consejos

  

También hay algunas reglas generales para el estilo libre de punto que no sea "pensar en esto amd llegar a algo"?

Siempre se puede engañar y utilizar la función "PL" de lambdabot (ya sea yendo a #haskell en freenode o mediante el uso por ejemplo ghci en ácido). Para su código PL da:

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

Lo que no es realmente una mejora si me preguntas.

Hay definitivamente una serie de trucos para transformar una expresión en el estilo libre de puntos. No pretendo ser un experto, pero aquí hay algunos consejos.

En primer lugar, se desea aislar los argumentos de la función en el extremo derecho término de la expresión. Sus herramientas principales que aquí se flip y $, usando las reglas:

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

donde f y g son funciones, y a y b son expresiones. Así que para empezar:

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

Ahora tenemos que conseguir t a cabo en el lado derecho. Para ello, utilice la regla:

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

Y así:

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

Ahora, tenemos que convertir todo a la izquierda de k y t en un gran término función, por lo que tenemos una expresión de la forma (\k t -> f k t). Aquí es donde las cosas se ponen un poco alucinantes. Para empezar, tenga en cuenta que todos los términos hasta el último $ son funciones con un único argumento, por lo que podemos componerlos:

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

Ahora, tenemos una función del tipo Char -> Char -> Int que queremos componer con una función del tipo Int -> Char, produciendo una función del tipo Char -> Char -> Char. Podemos lograr que el uso de la regla (muy extraño aspecto)

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

Eso nos da:

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

Ahora podemos simplemente aplicar una reducción beta:

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

Supongo que el objetivo de liberar puntos es hacer que el código sea más conciso y más legible.Por lo tanto, creo que es aconsejable realizar también otras refactorizaciones hacia la simplificación que luego podrían facilitar la eliminación de las variables.

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

En primer lugar, el flip es innecesario:

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

A continuación, usaría nombrar y conquistar para factorizar una subfunción utilizable de forma independiente:

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

También le di un nombre a la primera expresión para que sea más clara y reutilizable. encode_characters Ahora es fácil hacerlo sin puntos usando la técnica de @Nefrubyr:

encode_characters = chr . encode `on` ord

En cuanto a la segunda expresión, no puedo producir una forma que sea más legible que cualquiera de las que se muestran en las otras respuestas y todas son menos legibles que la forma puntual.Por lo tanto, sugeriría dejar de refactorizar en este punto y admirar la limpieza y reutilización del código resultante.

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

PD:Como ejercicio, dependiendo del contexto del problema, alguna ligera modificación de las interfaces de las funciones (qué datos y en qué forma se pasan a las funciones) podría generar más simplificaciones al generalizar el problema.

A.Implementar y simplificar la función. encode_n_characters :: [Char] -> Char dónde encode_characters k t = encode_n_characters [k, t].¿Es el resultado más simple que la función especializada de dos argumentos?

B.Implementar una función encode' definido a través de encode' (x + y) = encode x y y reimplementar encode_characters utilizando esta función.¿Alguna de las funciones se vuelve más simple?¿La implementación es más sencilla en general?Es encode' más o menos reutilizable que encode?

Conectar el IRC, #haskell , y pedir lambdabot :

<you> @pl (\k t -> chr $ a + flip mod 26 (ord k + ord t -2*a))
<lambdabot> [the answer]
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top