functions ejemplos haskell keyword where let

functions - if en haskell ejemplos



Haskell: donde vs. (4)

Soy nuevo en Haskell y estoy muy confundido con Where vs. Let . Ambos parecen proporcionar un propósito similar. He leído algunas comparaciones entre Where vs. Let, pero tengo problemas para discernir cuándo usar cada una. ¿Podría alguien proporcionar algún contexto o quizás algunos ejemplos que demuestren cuándo usar uno sobre el otro?

Donde vs.

Una cláusula where solo se puede definir en el nivel de una definición de función. Por lo general, eso es idéntico al alcance de let definition. La única diferencia es cuando se usan guardias . El alcance de la cláusula where extiende a todos los guardias. Por el contrario, el alcance de una expresión let es solo la cláusula de función actual y guard, si hay alguno.

Hoja de trucos Haskell

La Wiki Haskell es muy detallada y proporciona varios casos, pero utiliza ejemplos hipotéticos. Encuentro sus explicaciones demasiado breves para un principiante.

Ventajas de Let :

f :: State s a f = State $ /x -> y where y = ... x ...

Control.Monad.State

no funcionará, porque donde se refiere al patrón que coincide con f =, donde no x está en el alcance. Por el contrario, si hubieras comenzado con let, entonces no tendrías problemas.

Haskell Wiki sobre las ventajas de Let

f :: State s a f = State $ /x -> let y = ... x ... in y

Ventajas de Dónde :

f x | cond1 x = a | cond2 x = g a | otherwise = f (h x a) where a = w x f x = let a = w x in case () of _ | cond1 x = a | cond2 x = g a | otherwise = f (h x a)

Declaración vs. Expresión

La wiki de Haskell menciona que la cláusula Where es declarativa mientras que la expresión Let es expresiva. Aparte del estilo, ¿cómo se comportan de manera diferente?

Declaration style | Expression-style --------------------------------------+--------------------------------------------- where clause | let expression arguments LHS: f x = x*x | Lambda abstraction: f = /x -> x*x Pattern matching: f [] = 0 | case expression: f xs = case xs of [] -> 0 Guards: f [x] | x>0 = ''a'' | if expression: f [x] = if x>0 then ''a'' else ...

  1. En el primer ejemplo, ¿por qué el alcance de entrada es Where is not?
  2. ¿Es posible aplicar Dónde al primer ejemplo?
  3. ¿Pueden algunos aplicar esto a ejemplos reales donde las variables representan expresiones reales?
  4. ¿Hay una regla general a seguir cuando usar cada uno?

Actualizar

Para aquellos que vengan más tarde a este hilo, encontré la mejor explicación que se puede encontrar aquí: " Una introducción suave a Haskell ".

Let Expressions.

Las expresiones de let de Haskell son útiles siempre que se requiera un conjunto anidado de enlaces. Como un simple ejemplo, considere:

let y = a*b f x = (x+y)/y in f c + f d

El conjunto de enlaces creado por una expresión let es mutuamente recursivo, y los enlaces de patrón se tratan como patrones perezosos (es decir, llevan un ~ implícito). El único tipo de declaraciones permitidas son firmas de tipo, enlaces de función y enlaces de patrón.

Donde Cláusulas.

A veces es conveniente realizar enlaces a varias ecuaciones protegidas, lo que requiere una cláusula where:

f x y | y>z = ... | y==z = ... | y<z = ... where z = x*x

Tenga en cuenta que esto no se puede hacer con una expresión let, que solo abarca la expresión que encierra. Una cláusula where solo está permitida en el nivel superior de un conjunto de ecuaciones o expresión de caso. Las mismas propiedades y restricciones en los enlaces en las expresiones de let se aplican a aquellos en las cláusulas where. Estas dos formas de ámbito anidado parecen muy similares, pero recuerde que una expresión let es una expresión, mientras que una cláusula where no lo es, es parte de la sintaxis de declaraciones de funciones y expresiones de casos.


1: El problema en el ejemplo

f :: State s a f = State $ /x -> y where y = ... x ...

es el parámetro x . Las cosas en la cláusula where pueden referirse solo a los parámetros de la función f (no hay ninguno) y cosas en ámbitos externos.

2: para usar a where en el primer ejemplo, puede introducir una segunda función nombrada que toma la x como un parámetro, como este:

f = State f'' f'' x = y where y = ... x ...

o así:

f = State f'' where f'' x = y where y = ... x ...

3: Aquí hay un ejemplo completo sin los ... ''s:

module StateExample where data State a s = State (s -> (a, s)) f1 :: State Int (Int, Int) f1 = State $ /state@(a, b) -> let hypot = a^2 + b^2 result = (hypot, state) in result f2 :: State Int (Int, Int) f2 = State f where f state@(a, b) = result where hypot = a^2 + b^2 result = (hypot, state)

4: Cuándo usar let o where es una cuestión de gusto. Utilizo let para enfatizar un cálculo (moviéndolo al frente) y where enfatizar el flujo del programa (moviendo el cálculo hacia atrás).


Encontré este ejemplo de LYHFGG útil:

ghci> 4 * (let a = 9 in a + 1) + 2 42

let es una expresión para que puedas poner en cualquier lugar (!) donde puedan ir las expresiones.

En otras palabras, en el ejemplo anterior no es posible usar where simplemente reemplazar let (sin quizás usar alguna expresión de case más detallada combinada con where ).


Legal:

main = print (1 + (let i = 10 in 2 * i + 1))

Ilegal:

main = print (1 + (2 * i + 1 where i = 10))

Legal:

hasVowel [] = False hasVowel (x:xs) | x `elem` vowels = True | otherwise = False where vowels = "AEIOUaeiou"

No es legal: (a diferencia de ML)

let vowels = "AEIOUaeiou" in hasVowel = ...


Si bien existe una diferencia técnica con respecto a los guardias que ephemient señaló, también hay una diferencia conceptual en si desea colocar la fórmula principal por adelantado con las variables adicionales definidas a continuación ( where ) o si desea definir todo por adelantado y poner el fórmula a continuación ( let ). Cada estilo tiene un énfasis diferente y se ven ambos utilizados en documentos de matemáticas, libros de texto, etc. En general, las variables que son lo suficientemente intuitivas como para que la fórmula no tenga sentido sin ellas deberían definirse anteriormente; las variables que son intuitivas debido al contexto o sus nombres se deben definir a continuación. Por ejemplo, en el ejemplo de ephemient hasVowel, el significado de las vowels es obvio y por lo tanto no es necesario definirlo por encima de su uso (sin tener en cuenta el hecho de que let no funciona debido a la guardia).