Dans le code suivant, la dernière phrase que je peux mettre un in
devant. Cela changera-t-il quelque chose?
Une autre question: si je décide de mettre in
devant la dernière phrase, dois-je l'indenter?
J'ai essayé sans indentation et les câlins se plaignent
Le dernier générateur de do {...} doit être une expression
import Data.Char
groupsOf _ [] = []
groupsOf n xs =
take n xs : groupsOf n ( tail xs )
problem_8 x = maximum . map product . groupsOf 5 $ x
main = do t <- readFile "p8.log"
let digits = map digitToInt $concat $ lines t
print $ problem_8 digits
Ok, donc les gens ne semblent pas comprendre ce que je dis. Permettez-moi de reformuler: les deux suivants sont-ils les mêmes, compte tenu du contexte ci-dessus?
1.
let digits = map digitToInt $concat $ lines t
print $ problem_8 digits
2.
let digits = map digitToInt $concat $ lines t
in print $ problem_8 digits
Une autre question concernant la portée des liaisons déclarées dans let
: J'ai lu ici que:
where
Clauses.
Parfois, il est pratique d'étendre les liaisons sur plusieurs équations protégées, ce qui nécessite une clause where:
f x y | y>z = ...
| y==z = ...
| y<z = ...
where z = x*x
Notez que cela ne peut pas être fait avec une expression let, qui ne couvre que l'expression qu'elle contient .
Ma question: donc, les chiffres variables ne devraient pas être visibles pour la dernière phrase imprimée. Dois-je manquer quelque chose ici?
Réponse courte: Utilisez let
sans in
dans le corps d'un do-block et dans la partie après le |
dans une liste de compréhension. Partout ailleurs, utilisez let ... in ...
.
Le mot clé let
est utilisé de trois façons dans Haskell.
La première forme est une let-expression .
let variable = expression in expression
Cela peut être utilisé partout où une expression est autorisée, par ex.
> (let x = 2 in x*2) + 3
7
Le second est une instruction let . Ce formulaire n'est utilisé qu'à l'intérieur de la do-notation et n'utilise pas in
.
do statements
let variable = expression
statements
Le troisième est similaire au numéro 2 et est utilisé dans les listes de compréhension. Encore une fois, pas de in
.
> [(x, y) | x <- [1..3], let y = 2*x]
[(1,2),(2,4),(3,6)]
Ce formulaire lie une variable qui est dans la portée des générateurs suivants et dans l'expression avant le |
.
La raison de votre confusion ici est que les expressions (du type correct) peuvent être utilisées comme des instructions dans un do-block, et let .. in ..
n'est qu'une expression.
En raison des règles d'indentation de haskell, une ligne plus en retrait que la précédente signifie que c'est une continuation de la ligne précédente, donc cela
do let x = 42 in
foo
obtient analysé comme
do (let x = 42 in foo)
Sans indentation, vous obtenez une erreur d'analyse:
do (let x = 42 in)
foo
En conclusion, n'utilisez jamais in
dans une compréhension de liste ou un do-block. C'est inutile et déroutant, car ces constructions ont déjà leur propre forme de let
.
Tout d'abord, pourquoi des câlins? La Haskell Platform est généralement la voie à suivre recommandée pour les débutants, qui est fournie avec GHC.
Maintenant, passons au mot clé let
. La forme la plus simple de ce mot-clé est destinée à toujours être utilisée avec in
.
let {assignments} in {expression}
Par exemple,
let two = 2; three = 3 in two * three
Le {assignments}
sont seulement dans la portée dans le {expression}
. Des règles de mise en page régulières s'appliquent, ce qui signifie que in
doit être mis en retrait au moins autant que le let
auquel il correspond, et toutes les sous-expressions appartenant à l'expression let
doit également être mis en retrait au moins autant. Ce n'est pas vrai à 100%, mais c'est une bonne règle de base; Les règles de mise en page Haskell sont quelque chose que vous allez simplement vous habituer au fil du temps que vous lisez et écrivez le code Haskell. Gardez juste à l'esprit que la quantité d'indentation est le principal moyen d'indiquer quel code appartient à quelle expression.
Haskell fournit deux cas pratiques où vous ne faites pas devez écrire in
: faites des notations et listez les compréhensions (en fait, les compréhensions monades). La portée des affectations pour ces cas pratiques est prédéfinie.
do foo
let {assignments}
bar
baz
Pour la notation do
, le {assignments}
sont à la portée de toutes les instructions qui suivent, dans ce cas, bar
et baz
, mais pas foo
. C'est comme si nous avions écrit
do foo
let {assignments}
in do bar
baz
Répertoriez les compréhensions (ou vraiment, toute compréhension de monade) en notation do, afin qu'elles fournissent une facilité similaire.
[ baz | foo, let {assignments}, bar ]
Le {assignments}
sont à la portée des expressions bar
et baz
, mais pas pour foo
.
where
est quelque peu différent. Si je ne me trompe pas, la portée de where
correspond à une définition de fonction particulière. Alors
someFunc x y | guard1 = blah1
| guard2 = blah2
where {assignments}
le {assignments}
dans cette clause where
a accès à x
et y
. guard1
, guard2
, blah1
, et blah2
tous ont accès à {assignments}
de cette clause where
. Comme mentionné dans le didacticiel que vous avez lié, cela peut être utile si plusieurs gardes réutilisent les mêmes expressions.
En notation do
, vous pouvez en effet utiliser let
avec et sans in
. Pour qu'il soit équivalent (dans votre cas, je montrerai plus tard un exemple où vous devez ajouter un deuxième do
et donc plus d'indentation), vous devez l'indenter comme vous l'avez découvert (si vous utilisez layout - si vous utilisez des accolades et des points-virgules explicites, ils sont exactement équivalents).
Pour comprendre pourquoi c'est équivalent, vous devez en fait grogner des monades (au moins dans une certaine mesure) et regarder les règles de désucréation pour la notation do
. En particulier, un code comme celui-ci:
do let x = ...
stmts -- the rest of the do block
est traduit en let x = ... in do { stmts }
. Dans votre cas, stmts = print (problem_8 digits)
. L'évaluation de l'ensemble de la liaison désugarée let
se traduit par une action IO (from print $ ...
). Et ici, vous devez comprendre les monades pour convenir intuitivement qu'il n'y a pas de différence entre do
notations et éléments de langage "réguliers" décrivant un calcul aboutissant à des valeurs monadiques.
Quant aux deux, pourquoi sont possibles: Eh bien, let ... in ...
A un large éventail d'applications (dont la plupart n'ont rien à voir avec les monades en particulier), et une longue histoire à démarrer. let
sans in
pour la notation do
, d'autre part, ne semble être qu'un petit morceau de sucre syntaxique. L'avantage est évident: vous pouvez lier les résultats de calculs purs (comme dans, pas monadiques) à un nom sans recourir à un val <- return $ ...
Inutile et sans diviser le bloc do
en deux:
do stuff
let val = ...
in do more
stuff $ using val
La raison pour laquelle vous n'avez pas besoin d'un bloc do
supplémentaire pour ce qui suit le let
est que vous n'avez obtenu qu'une seule ligne. N'oubliez pas que do e
Est e
.
En ce qui concerne votre édition: digit
être visible dans la ligne suivante est le point entier. Et il n'y a aucune exception pour cela ou quoi que ce soit. do
la notation devient une seule expression, et let
fonctionne très bien dans une seule expression. where
n'est nécessaire que pour les choses qui ne sont pas des expressions.
À des fins de démonstration, je vais vous montrer la version désucrée de votre bloc do
. Si vous n'êtes pas encore familier avec les monades (quelque chose que vous devriez changer bientôt à mon humble avis), ignorez l'opérateur >>=
Et concentrez-vous sur le let
. Notez également que l'indentation n'a plus d'importance.
main = readFile "p8.log" >>= (\t ->
let digits = map digitToInt $ concat $ lines t
in print (problem_8 digits))
Quelques notes de débutant sur "suivent les mêmes deux".
Par exemple, add1
est une fonction qui ajoute 1 au nombre:
add1 :: Int -> Int
add1 x =
let inc = 1
in x + inc
Donc, c'est comme add1 x = x + inc
avec substitution inc par 1 du mot clé let
.
Lorsque vous essayez de supprimer le mot clé in
add1 :: Int -> Int
add1 x =
let inc = 1
x + inc
vous avez une erreur d'analyse.
De documentation :
Within do-blocks or list comprehensions
let { d1 ; ... ; dn }
without `in` serves to introduce local bindings.
Btw, il y a Belle explication avec de nombreux exemples sur ce que font réellement les mots clés where
et in
.