144 votes

Haskell : Où vs. Let

Je suis nouveau en Haskell et je suis très confus par vs. Let. Ils semblent tous les deux avoir un but similaire. J'ai lu quelques comparaisons entre vs. Let mais j'ai du mal à discerner quand utiliser chacun. Est-ce que quelqu'un pourrait fournir un peu de contexte ou peut-être quelques exemples pour montrer quand utiliser l'un plutôt que l'autre?

Où vs. Let

Une clause where ne peut être définie qu'au niveau de la définition d'une fonction. Habituellement, cela correspond à la portée de la définition let. La seule différence est lorsqu'on utilise des gardes. La portée de la clause where s'étend à travers toutes les gardes. En revanche, la portée d'une expression let est uniquement la clause de fonction actuelle et la garde, le cas échéant.

Feuille de triche Haskell

Le Wiki Haskell est très détaillé et fournit différents cas mais il utilise des exemples hypothétiques. Je trouve ses explications trop succinctes pour un débutant.

Avantages du Let:

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

Control.Monad.State

ne fonctionnera pas, car where se réfère à la correspondance des motifs f =, où aucun x n'est en portée. En revanche, si vous aviez commencé avec let, alors vous n'auriez pas eu de problème.

Wiki Haskell sur les avantages du Let

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

Avantages du Where:

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)

Déclaration vs. Expression

Le wiki Haskell mentionne que la clause Where est déclarative tandis que l'expression Let est expressive. Mis à part le style, comment fonctionnent-ils différemment?

Style de déclaration                     | Style d'expression
--------------------------------------+---------------------------------------------
clause where                          | expression let
arguments LHS:     f x = x*x          | Abstraction lambda: f = \x -> x*x
Correspondance des motifs:  f [] = 0           | expression case:    f xs = case xs de [] -> 0
Gardes:            f [x] | x>0 = 'a'  | expression if:      f [x] = si x>0 alors 'a' else ...
  1. Dans le premier exemple, pourquoi le Let est-il dans la portée mais pas le ?
  2. Est-il possible d'appliquer au premier exemple?
  3. Est-ce que quelqu'un peut appliquer cela à des exemples réels où les variables représentent des expressions réelles?
  4. Y a-t-il une règle générale à suivre pour savoir quand utiliser chacun?

Mise à jour

Pour ceux qui consultent ce fil plus tard, j'ai trouvé la meilleure explication ici: "Une introduction douce à Haskell".

Expressions Let.

Les expressions let de Haskell sont utiles chaque fois qu'un ensemble imbriqué de liaisons est nécessaire. À titre d'exemple simple, considérez:

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

L'ensemble de liaisons créé par une expression let est mutuellement récursif, et les liaisons de motifs sont traitées comme des motifs paresseux (c'est-à-dire qu'elles portent un implicite ~). Le seul type de déclarations autorisé est les signatures de type, les liaisons de fonction, et les liaisons de motifs.

Clauses Where.

Parfois, il est pratique de définir une portée de liaisons sur plusieurs équations gardées, ce qui nécessite une clause where:

f x y  |  y>z           =  ...
       |  y==z          =  ...
       |  y

`

Remarquez que cela ne peut pas être fait avec une expression let, qui ne s'applique qu'à l'expression qu'elle englobe. Une clause where n'est autorisée qu'au niveau supérieur d'un ensemble d'équations ou d'une expression case. Les mêmes propriétés et contraintes sur les liaisons dans les expressions let s'appliquent à celles dans les clauses where. Ces deux formes de portée imbriquée semblent très similaires, mais rappelez-vous qu'une expression let est une expression, tandis qu'une clause where ne l'est pas -- elle fait partie de la syntaxe des déclarations de fonction et des expressions de cas.

`

10 votes

J'étais perplexe par la différence entre let et where lorsque j'ai commencé à apprendre Haskell. Je pense que la meilleure façon de le comprendre est de réaliser qu'il y a très peu de différence entre les deux, et donc rien à craindre. Le sens de where est donné en termes de let à travers une transformation mécanique très simple. Voir haskell.org/onlinereport/decls.html#sect4.4.3.2 Cette transformation existe uniquement pour des raisons de commodité notationale, en réalité.

0 votes

Je vais généralement utiliser l'un ou l'autre en fonction de ce que je veux définir en premier. Par exemple, les gens utilisent souvent les fonctions, puis les définissent là où ils veulent. Let est utilisé si on veut une sorte de fonction à l'aspect impératif.

0 votes

@Tom Ellis, Tom, j'essayais de comprendre le lien auquel vous faites référence, mais c'était trop difficile pour moi. Pourriez-vous expliquer cette simple transformation aux simples mortels?

47voto

antonakos Points 4921

1: Le problème dans l'exemple

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

est le paramètre x. Les éléments dans la clause where ne peuvent se référer qu'aux paramètres de la fonction f (il n'y en a pas) et aux éléments dans les scopes externes.

2: Pour utiliser un where dans le premier exemple, vous pouvez introduire une deuxième fonction nommée qui prend le x en tant que paramètre, comme ceci :

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

ou comme ceci :

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

3: Voici un exemple complet sans les ... :

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: Quand utiliser let ou where est une question de préférence. J'utilise let pour mettre en évidence un calcul (en le déplaçant vers l'avant) et where pour mettre en évidence le flux du programme (en déplaçant le calcul vers l'arrière).

2 votes

"Les éléments dans la clause where peuvent se référer seulement aux paramètres de la fonction f (il n'y en a pas) et aux éléments des scopes extérieurs." - Cela m'a vraiment aidé à comprendre.

30voto

gdj Points 780

Alors qu'il existe une différence technique en ce qui concerne les gardes que ephemient a soulignée, il existe également une différence conceptuelle quant à la question de savoir si vous voulez mettre la formule principale en premier avec des variables supplémentaires définies ci-dessous (where) ou si vous voulez tout définir au préalable et mettre la formule ci-dessous (let). Chaque style met l'accent sur une différence et vous les voyez utilisés dans des articles mathématiques, des manuels, etc. En général, les variables qui sont suffisamment peu intuitives pour que la formule n'ait pas de sens sans elles doivent être définies au-dessus; les variables qui sont intuitives en raison du contexte ou de leurs noms doivent être définies en dessous. Par exemple, dans l'exemple de hasVowel d'ephemient, la signification de vowels est évidente et donc elle n'a pas besoin d'être définie au-dessus de son utilisation (en ignorant le fait que let ne fonctionnerait pas en raison de la garde).

2 votes

Cela fournit une bonne règle de base. Pourriez-vous élaborer sur la raison pour laquelle let semble avoir une portée différente de where?

0 votes

Parce que la syntaxe de Haskell le dit. Désolé, je n'ai pas de bonne réponse. Peut-être que les définitions de niveau supérieur sont difficiles à lire lorsqu'elles sont cachées sous un "let" donc cela a été interdit.

14voto

Bora M. Alper Points 1046

Malheureusement, la plupart des réponses ici sont trop techniques pour un débutant.

LHYFGG a un chapitre pertinent à ce sujet - que vous devriez lire si ce n'est pas déjà fait, mais en essence :

  • where est juste une construction syntaxique (pas un sucre) qui est utile seulement dans les définitions de fonction.
  • let ... in est une expression en soi, donc vous pouvez les utiliser partout où vous pouvez mettre une expression. Étant une expression en soi, il ne peut pas être utilisé pour lier des choses pour des gardes.

Enfin, vous pouvez utiliser let dans les compréhensions de listes également :

calcBmis :: (RealFloat a) => [(a, a)] -> [a]
calcBmis xs = [bmi | (w, h) <- xs, let bmi = w / h ^ 2, bmi >= 25.0]
-- w: largeur
-- h: hauteur

Nous incluons un let à l'intérieur d'une compréhension de liste tout comme nous le ferions pour un prédicat, seulement il ne filtre pas la liste, il se contente de lier des noms. Les noms définis dans un let à l'intérieur d'une compréhension de liste sont visibles pour la fonction de sortie (la partie avant le |) et tous les prédicats et sections qui viennent après de la liaison. Ainsi, nous pourrions faire en sorte que notre fonction ne renvoie que les IMC des personnes >= 25 :

14voto

ephemient Points 87003

Légal:

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

Non légal:

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

Légal:

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

Non légal: (contrairement à ML)

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

15 votes

Pouvez-vous expliquer pourquoi les exemples suivants sont valides alors que l'autre ne l'est pas?

4 votes

Pour ceux qui ne le savent pas, ceci est légal : hasVowel = let^M vowels = "AEIOUaeiou"^M in ... (^M est un retour à la ligne)

11voto

jhegedus Points 1314

J'ai trouvé cet exemple de LYHFGG utile :

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

let est une expression, vous pouvez donc placer un let n'importe où(!) où des expressions peuvent être placées.

En d'autres termes, dans l'exemple ci-dessus, il n'est pas possible d'utiliser where pour simplement remplacer let (sans peut-être utiliser une expression case plus verbeuse combinée avec where).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X