2011-12-11 20 views
10

Estoy tratando de entender cómo convertir funciones a notación sin puntos en Haskell. Vi this example, pero es más complicado de lo que estoy buscando. Siento que entiendo la lógica detrás de esto, pero cuando trato de ejecutar algunos ejemplos simples en el código obtengo errores de compilación. Quiero tratar de escribir esta función en el estilo libre de puntos:funciones simples de Haskell en estilo sin puntos

f x = 5 + 8/x cuales reacomodé como f x = (+) 5 $ (/) 8 x

lo tanto, pensé que podría ser algo como esto:

f = (+) 5 $ (/) 8 

pero cuando corro esto en ghci me sale este mensaje:

No instance for (Num (a0 -> a0)) 
    arising from the literal `5' at Test.hs:3:9 
Possible fix: add an instance declaration for (Num (a0 -> a0)) 
In the first argument of `(+)', namely `5' 
In the first argument of `($)', namely `(+) 5' 
In the expression: (+) 5 $ (/) 8 
Failed, modules loaded: none. 

No entiendo el mensaje "No hay instancia para ...". ¿Qué debo hacer para escribir esta función en un estilo sin puntos?

+3

que cree que puede estar confundido acerca de la [diferencia entre el '' y '$ operadores .'] (http://stackoverflow.com/questions/940382/haskell-difference-between-dot-and-dollar-sign) – hammar

Respuesta

16

$ tiene una precedencia muy baja. Por lo tanto, f x = (+) 5 $ (/) 8 x en realidad significa f x = (+) 5 $ ((/) 8 x). En su lugar, vuelva a escribir que a medida que

f x = (+) 5 ((/) 8 x) 
f x = ((+) 5) (((/) 8) x) 
f x = ((+) 5) . (((/) 8)) x 
f = ((+) 5) . ((/) 8) 
f = (5+) . (8/) 

La última expresión tiene sentido: f es la composición de dos operaciones, antes divida 8 por lo que uno tiene, y luego añadir 5 al resultado. Recuerde, g.h significa "aplicar h, luego aplicar g el resultado de eso".

+0

Sí, eso tiene sentido ahora, gracias! – KJ50

+0

Eso es genial: las tres respuestas subidas de tono muestran los diferentes ángulos de la pregunta. –

10

El programa "pointfree" se puede instalar con cabal install pointfree, y le muestra cómo escribir una expresión en estilo pointfree. Por ejemplo:

$ pointfree "f x = 5 + 8/x" 
f = (5 +) . (8 /) 

Explicación de esta conversión:

  1. puede utilizar "secciones" para las funciones infija/operador. (a +) == \b -> a + b y (+ a) == \b -> b + a
  2. La función . toma el resultado del segundo parámetro, que es una función de un argumento, y lo aplica al primer argumento.
+0

¿Por qué querría usar estilo sin puntos? –

+0

Personalmente me obligo a utilizar el estilo pointfree, porque: 1. Me veo obligado a simplificar mis funciones, haciéndolas más reutilizables. 2. Existe una mayor probabilidad de que realmente me moleste en reutilizar funciones ya escritas, reduciendo la duplicación de código. – dflemstr

+1

Tenga en cuenta que (.) Es asociativo pero ($) no lo es, por lo que (.) Proporciona más flexibilidad en la refactorización.Y para usar (.) Necesita dominar el estilo sin puntos. También los combinadores monádicos como liftM2, fmap, >> = y> => casi te obligan a aprender el estilo sin puntos. – nponeccop

4

Estabas muy cerca. Permítanme añadir una más $ para ilustrar:

f x = (+) 5 $ (/) 8 $ x 

Debe quedar claro que la expresión (+) 5 es una función que toma una entrada numérica y produce una salida numérica. Lo mismo ocurre con la expresión (/) 8. Por lo tanto, tome cualquier número que ingrese, x, y primero aplique la "función" (/) 8, y luego aplique la "función" (+) 5.

Siempre que tenga una cadena de funciones separadas por $, puede reemplazar todos excepto el extremo derecho con . Significado, si usted tiene a $ b $ c $ d, esto es equivalente a a . b . c $ d.

f x = (+) 5 . (/) 8 $ x 

En este punto, vamos a eliminar realidad la $ y un paréntesis en su lugar.

f x = ((+) 5 . (/) 8) x 

Ahora debe quedar claro que se puede quitar el x detrás de ambos lados:

f = (+) 5 . (/) 8 

Esa es la idea principal. Si tiene f x = expr x, puede "eta reducirlo" a f = expr. Para producir código pointfree, simplemente necesita reconocer cómo la función más grande se compone de funciones más pequeñas. La aplicación parcial a veces es necesaria para el código de punto libre (como en este caso, (+) 5 y (/) 8 se aplican parcialmente). El programa "pointfree" es bastante útil para cuando no quieres pensar en eso; Lambdabot en el canal #haskell irc usa este programa como un complemento, por lo que ni siquiera tiene que instalarlo usted mismo; sólo hay que preguntar:

<DanBurton> @pl let f x = 5 + 8/x in f 
<lambdabot> (5 +) . (8 /) 
10

conversión de lambda-cálculo (que Haskell es una variante de) los términos a SKI términos (en total funciones pointfree, utilizando sólo const (K), id (I) y <*> (S)) se puede realizar con las siguientes reglas simples:

  1. \x -> x traduce en id;
  2. \x -> y sin x en y se traduce en const y;
  3. \x -> f gf' <*> g' se traduce en donde
    • f' es una traducción del \x -> f y
    • g' es una traducción del \x -> g.

Ahora usted puede preguntarse de dónde viene el . vienen en Hay un caso especial de la última traducción:. Si f no tiene ningún ocurrencias libres de x, entonces \x -> f g se traduce en const f <*> (\x -> g), que es igual a f . (\x -> g).

Usando estas reglas podemos convertir su función:

f = \x -> ((+) 5) (((/) 8) x) = -- by the special-case (.) rule 
((+) 5) . (\x -> (((/) 8) x)) = -- by eta-reduction ((\x -> f x) = f) 
((+) 5) . ((/) 8) 

Eta-reducción no es necesario completar la traducción, pero sin ella que tendríamos algo más desordenado. Por ejemplo, el último paso produciría ((+) 5) . ((/) 8) . id en su lugar.

Cuestiones relacionadas