In the following code, the last phrase I can put an in
in front. Will it change anything?
在下面的代码中,我可以把最后一个短语放在前面。它会改变什么吗?
Another question: If I decide to put in
in front of the last phrase, do I need to indent it?
另一个问题是:如果我决定在最后一个短语前面加上,是否需要对它进行缩进?
I tried without indenting and hugs complains
我试着不缩进和拥抱抱怨
Last generator in do {...} must be an expression
在do{…}必须是一个表达式
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
Edit
Ok, so people don't seem to understand what I'm saying. Let me rephrase: are the following two the same, given the context above?
人们似乎不明白我说的是什么。让我重新表述一下:考虑到上面的上下文,下面的两个是相同的吗?
1.
1。
let digits = map digitToInt $concat $ lines t
print $ problem_8 digits
2.
2。
let digits = map digitToInt $concat $ lines t
in print $ problem_8 digits
Another question concerning the scope of bindings declared in let
: I read here that:
关于let中声明的绑定范围的另一个问题:我在这里读到:
where
Clauses.where子句。
Sometimes it is convenient to scope bindings over several guarded equations, which requires a where clause:
有时可以方便地对几个有保护的方程进行绑定,这些方程需要一个where子句:
f x y | y>z = ...
| y==z = ...
| y<z = ...
where z = x*x
Note that this cannot be done with a let expression, which only scopes over the expression which it encloses.
注意,这不能用let表达式来实现,它只作用于它所包含的表达式。
My question: so, the variable digits shouldn't be visible to the last print phrase. Do I miss something here?
我的问题是,可变数字不应该出现在最后一个打印短语中。我是不是漏掉了什么?
4 个解决方案
#1
96
Short answer: Use let
without in
in the body of a do-block, and in the part after the |
in a list comprehension. Anywhere else, use let ... in ...
.
简短的回答:在一个do-block的主体中使用let,在|之后的一个列表理解中使用。在其他地方,使用let…在....
The keyword let
is used in three ways in Haskell.
这个关键字在Haskell中有三种用法。
-
The first form is a let-expression.
第一种形式是let。
let variable = expression in expression
This can be used wherever an expression is allowed, e.g.
在允许表达式的任何地方都可以使用它。
> (let x = 2 in x*2) + 3 7
-
The second is a let-statement. This form is only used inside of do-notation, and does not use
in
.第二个是“让人”的声明。此表单只在do-notation内部使用,不使用in。
do statements let variable = expression statements
-
The third is similar to number 2 and is used inside of list comprehensions. Again, no
in
.第三个类似于第2个,用于列表理解。再次,没有在。
> [(x, y) | x <- [1..3], let y = 2*x] [(1,2),(2,4),(3,6)]
This form binds a variable which is in scope in subsequent generators and in the expression before the
|
.该表单绑定了一个变量,该变量在后续生成器和|之前的表达式中。
The reason for your confusion here is that expressions (of the correct type) can be used as statements within a do-block, and let .. in ..
is just an expression.
这里出现混乱的原因是表达式(正确的类型)可以作为语句在do-block中使用,然后。在. .只是一个表情。
Because of the indentation rules of haskell, a line indented further than the previous one means it's a continuation of the previous line, so this
由于haskell的缩进规则,缩进比前一行更深的一行意味着它是前一行的延续,所以这是
do let x = 42 in
foo
gets parsed as
被解析为
do (let x = 42 in foo)
Without indentation, you get a parse error:
没有缩进,您会得到一个解析错误:
do (let x = 42 in)
foo
In conclusion, never use in
in a list comprehension or a do-block. It is unneccesary and confusing, as those constructs already have their own form of let
.
总之,永远不要用在一个列表理解或一个do-block中。这是不必要的和混乱的,因为那些构念已经有它们自己的形式的let。
#2
16
First off, why hugs? The Haskell Platform is generally the recommended way to go for newbies, which comes with GHC.
首先,为什么要拥抱?Haskell平台通常是推荐的适合新手的方法,它与GHC一起提供。
Now then, on to the let
keyword. The simplest form of this keyword is meant to always be used with in
.
现在,我们来看看关键字。这个关键字最简单的形式是始终与in一起使用。
let {assignments} in {expression}
For example,
例如,
let two = 2; three = 3 in two * three
The {assignments}
are only in scope in the corresponding {expression}
. Regular layout rules apply, meaning that in
must be indented at least as much as the let
that it corresponds to, and any sub-expressions pertaining to the let
expression must likewise be indented at least as much. This isn't actually 100% true, but is a good rule of thumb; Haskell layout rules are something you will just get used to over time as you read and write Haskell code. Just keep in mind that the amount of indentation is the main way to indicate which code pertains to what expression.
{赋值}仅在相应的{表达式}中。应用正则布局规则,这意味着in必须缩进至少与它所对应的let相同,与let表达式相关的任何子表达式也必须同样缩进。这并不是百分之百正确,但这是一个很好的经验法则;Haskell布局规则是您在阅读和编写Haskell代码时逐渐习惯的。请记住,缩进的数量是指示哪些代码属于哪个表达式的主要方式。
Haskell provides two convenience cases where you don't have to write in
: do notation and list comprehensions (actually, monad comprehensions). The scope of the assignments for these convenience cases is predefined.
Haskell提供了两种方便的情况,您无需编写:做符号和列表理解(实际上是monad理解)。这些方便案例的分配范围是预定义的。
do foo
let {assignments}
bar
baz
For do
notation, the {assignments}
are in scope for any statements that follow, in this case, bar
and baz
, but not foo
. It is as if we had written
对于do表示法,{assignments}作用于后面的任何语句,在本例中是bar和baz,而不是foo。就好像我们写过一样
do foo
let {assignments}
in do bar
baz
List comprehensions (or really, any monad comprehension) desugar into do notation, so they provide a similar facility.
列出理解(或任何单一理解)desugar到do表示法,因此它们提供了类似的功能。
[ baz | foo, let {assignments}, bar ]
The {assignments}
are in scope for the expressions bar
and baz
, but not for foo
.
{赋值}在表达式栏和baz的范围内,而不是foo。
where
is somewhat different. If I'm not mistaken, the scope of where
lines up with a particular function definition. So
有些不同的地方。如果我没有弄错的话,它的作用域与特定函数的定义是一致的。所以
someFunc x y | guard1 = blah1
| guard2 = blah2
where {assignments}
the {assignments}
in this where
clause have access to x
and y
. guard1
, guard2
, blah1
, and blah2
all have access to the {assignments}
of this where
clause. As is mentioned in the tutorial you linked, this can be helpful if multiple guards reuse the same expressions.
在这个where子句可以访问x和y. guard1、guard2、blah1和blah2都可以访问该where子句的{赋值}。正如在您所链接的教程中所提到的,如果多个警卫重用相同的表达式,这将非常有用。
#3
6
In do
notation, you can indeed use let
with and without in
. For it to be equivalent (in your case, I'll later show an example where you need to add a second do
and thus more indentation), you need to indent it as you discovered (if you're using layout - if you use explicit braces and semicolons, they're exactly equivalent).
在do表示法中,你确实可以用let with和without In。为了使它是等价的(在您的例子中,我稍后将展示一个示例,您需要添加第二个do,从而添加更多的缩进),您需要对它进行缩进(如果使用布局——如果使用显式大括号和分号,它们是完全等价的)。
To understand why it's equivalent, you have to actually grok monads (at least to some degree) and look at the desugaring rules for do
notation. In particular, code like this:
要理解为什么它是等价的,你必须去摸索monads(至少在某种程度上),并查看do符号的去苏加令规则。特别是像这样的代码:
do let x = ...
stmts -- the rest of the do block
is translated to let x = ... in do { stmts }
. In your case, stmts = print (problem_8 digits)
. Evaluating the whole desugared let
binding results in an IO action (from print $ ...
). And here, you need understanding of monads to intuitively agree that there's no difference between do
notations and "regular" language elements describing a computation resulting in monadic values.
译为让x =…在do {stmts}。在您的例子中,stmts = print(问题8位)。评估整个desugared let绑定将导致IO操作(打印$…)。在这里,您需要了解monads,以直观地同意在做记号和“常规”语言元素之间并没有区别,这些元素描述了计算结果的一元值。
As for both why are possible: Well, let ... in ...
has a broad range of applications (most of which have nothing to do with monads in particular), and a long history to boot. let
without in
for do
notation, on the other hand, seems to be nothing but a small piece of syntactic sugar. The advantage is obvious: You can bind the results of pure (as in, not monadic) computations to a name without resorting to a pointless val <- return $ ...
and without splitting up the do
block in two:
至于为什么两者都是可能的:嗯,让我们……在…具有广泛的应用程序(其中大部分与monads没有特别的关系),并且具有很长的历史。另一方面,让我们不做表示法,似乎只是一小块语法糖。优点是显而易见的:您可以将纯(如in,不是mon)计算的结果绑定到一个名称,而不需要使用无意义的val <- return $…不把do块分成两部分
do stuff
let val = ...
in do more
stuff $ using val
The reason you don't need an extra do
block for what follows the let
is that you only got a single line. Remember, do e
is e
.
你不需要一个额外的do块来做let后面的事情的原因是你只有一行。记住,e是e。
Regarding your edit: digit
being visible in the next line is the whole point. And there's no exception for it or anything. do
notation becomes one single expression, and let
works just fine in a single expression. where
is only needed for things which aren't expressions.
关于您的编辑:下一行显示的数字就是整个点。也没有例外。do表示法变成了一个单独的表达式,让它在一个单独的表达式中正常工作。这里只需要非表达式的东西。
For the sake of demonstration, I'll show the desugared version of your do
block. If you aren't too familiar with monads yet (something you should change soon IMHO), ignore the >>=
operator and focus on the let
. Also note that indentation doesn't matter any more.
为了演示,我将展示您的do块的desugared版本。如果您还不太熟悉monads(您应该尽快更改IMHO),请忽略>>=操作符,而关注let。还要注意缩进不再重要。
main = readFile "p8.log" >>= (\t ->
let digits = map digitToInt $ concat $ lines t
in print (problem_8 digits))
#4
1
Some beginner notes about "are following two the same".
一些初学者的笔记是关于“遵循两个相同的”。
For example, add1
is a function, that add 1 to number:
例如,add1是一个函数,它加1到number:
add1 :: Int -> Int
add1 x =
let inc = 1
in x + inc
So, it's like add1 x = x + inc
with substitution inc by 1 from let
keyword.
因此,它就像add1 x = x + inc。
When you try to suppress in
keyword
当你尝试抑制关键字
add1 :: Int -> Int
add1 x =
let inc = 1
x + inc
you've got parse error.
你有解析错误。
From documentation:
从文档:
Within do-blocks or list comprehensions
let { d1 ; ... ; dn }
without `in` serves to introduce local bindings.
Btw, there are nice explanation with many examples about what where
and in
keyword actually do.
顺便说一句,这里有很好的解释,有很多例子可以说明关键字的作用。
#1
96
Short answer: Use let
without in
in the body of a do-block, and in the part after the |
in a list comprehension. Anywhere else, use let ... in ...
.
简短的回答:在一个do-block的主体中使用let,在|之后的一个列表理解中使用。在其他地方,使用let…在....
The keyword let
is used in three ways in Haskell.
这个关键字在Haskell中有三种用法。
-
The first form is a let-expression.
第一种形式是let。
let variable = expression in expression
This can be used wherever an expression is allowed, e.g.
在允许表达式的任何地方都可以使用它。
> (let x = 2 in x*2) + 3 7
-
The second is a let-statement. This form is only used inside of do-notation, and does not use
in
.第二个是“让人”的声明。此表单只在do-notation内部使用,不使用in。
do statements let variable = expression statements
-
The third is similar to number 2 and is used inside of list comprehensions. Again, no
in
.第三个类似于第2个,用于列表理解。再次,没有在。
> [(x, y) | x <- [1..3], let y = 2*x] [(1,2),(2,4),(3,6)]
This form binds a variable which is in scope in subsequent generators and in the expression before the
|
.该表单绑定了一个变量,该变量在后续生成器和|之前的表达式中。
The reason for your confusion here is that expressions (of the correct type) can be used as statements within a do-block, and let .. in ..
is just an expression.
这里出现混乱的原因是表达式(正确的类型)可以作为语句在do-block中使用,然后。在. .只是一个表情。
Because of the indentation rules of haskell, a line indented further than the previous one means it's a continuation of the previous line, so this
由于haskell的缩进规则,缩进比前一行更深的一行意味着它是前一行的延续,所以这是
do let x = 42 in
foo
gets parsed as
被解析为
do (let x = 42 in foo)
Without indentation, you get a parse error:
没有缩进,您会得到一个解析错误:
do (let x = 42 in)
foo
In conclusion, never use in
in a list comprehension or a do-block. It is unneccesary and confusing, as those constructs already have their own form of let
.
总之,永远不要用在一个列表理解或一个do-block中。这是不必要的和混乱的,因为那些构念已经有它们自己的形式的let。
#2
16
First off, why hugs? The Haskell Platform is generally the recommended way to go for newbies, which comes with GHC.
首先,为什么要拥抱?Haskell平台通常是推荐的适合新手的方法,它与GHC一起提供。
Now then, on to the let
keyword. The simplest form of this keyword is meant to always be used with in
.
现在,我们来看看关键字。这个关键字最简单的形式是始终与in一起使用。
let {assignments} in {expression}
For example,
例如,
let two = 2; three = 3 in two * three
The {assignments}
are only in scope in the corresponding {expression}
. Regular layout rules apply, meaning that in
must be indented at least as much as the let
that it corresponds to, and any sub-expressions pertaining to the let
expression must likewise be indented at least as much. This isn't actually 100% true, but is a good rule of thumb; Haskell layout rules are something you will just get used to over time as you read and write Haskell code. Just keep in mind that the amount of indentation is the main way to indicate which code pertains to what expression.
{赋值}仅在相应的{表达式}中。应用正则布局规则,这意味着in必须缩进至少与它所对应的let相同,与let表达式相关的任何子表达式也必须同样缩进。这并不是百分之百正确,但这是一个很好的经验法则;Haskell布局规则是您在阅读和编写Haskell代码时逐渐习惯的。请记住,缩进的数量是指示哪些代码属于哪个表达式的主要方式。
Haskell provides two convenience cases where you don't have to write in
: do notation and list comprehensions (actually, monad comprehensions). The scope of the assignments for these convenience cases is predefined.
Haskell提供了两种方便的情况,您无需编写:做符号和列表理解(实际上是monad理解)。这些方便案例的分配范围是预定义的。
do foo
let {assignments}
bar
baz
For do
notation, the {assignments}
are in scope for any statements that follow, in this case, bar
and baz
, but not foo
. It is as if we had written
对于do表示法,{assignments}作用于后面的任何语句,在本例中是bar和baz,而不是foo。就好像我们写过一样
do foo
let {assignments}
in do bar
baz
List comprehensions (or really, any monad comprehension) desugar into do notation, so they provide a similar facility.
列出理解(或任何单一理解)desugar到do表示法,因此它们提供了类似的功能。
[ baz | foo, let {assignments}, bar ]
The {assignments}
are in scope for the expressions bar
and baz
, but not for foo
.
{赋值}在表达式栏和baz的范围内,而不是foo。
where
is somewhat different. If I'm not mistaken, the scope of where
lines up with a particular function definition. So
有些不同的地方。如果我没有弄错的话,它的作用域与特定函数的定义是一致的。所以
someFunc x y | guard1 = blah1
| guard2 = blah2
where {assignments}
the {assignments}
in this where
clause have access to x
and y
. guard1
, guard2
, blah1
, and blah2
all have access to the {assignments}
of this where
clause. As is mentioned in the tutorial you linked, this can be helpful if multiple guards reuse the same expressions.
在这个where子句可以访问x和y. guard1、guard2、blah1和blah2都可以访问该where子句的{赋值}。正如在您所链接的教程中所提到的,如果多个警卫重用相同的表达式,这将非常有用。
#3
6
In do
notation, you can indeed use let
with and without in
. For it to be equivalent (in your case, I'll later show an example where you need to add a second do
and thus more indentation), you need to indent it as you discovered (if you're using layout - if you use explicit braces and semicolons, they're exactly equivalent).
在do表示法中,你确实可以用let with和without In。为了使它是等价的(在您的例子中,我稍后将展示一个示例,您需要添加第二个do,从而添加更多的缩进),您需要对它进行缩进(如果使用布局——如果使用显式大括号和分号,它们是完全等价的)。
To understand why it's equivalent, you have to actually grok monads (at least to some degree) and look at the desugaring rules for do
notation. In particular, code like this:
要理解为什么它是等价的,你必须去摸索monads(至少在某种程度上),并查看do符号的去苏加令规则。特别是像这样的代码:
do let x = ...
stmts -- the rest of the do block
is translated to let x = ... in do { stmts }
. In your case, stmts = print (problem_8 digits)
. Evaluating the whole desugared let
binding results in an IO action (from print $ ...
). And here, you need understanding of monads to intuitively agree that there's no difference between do
notations and "regular" language elements describing a computation resulting in monadic values.
译为让x =…在do {stmts}。在您的例子中,stmts = print(问题8位)。评估整个desugared let绑定将导致IO操作(打印$…)。在这里,您需要了解monads,以直观地同意在做记号和“常规”语言元素之间并没有区别,这些元素描述了计算结果的一元值。
As for both why are possible: Well, let ... in ...
has a broad range of applications (most of which have nothing to do with monads in particular), and a long history to boot. let
without in
for do
notation, on the other hand, seems to be nothing but a small piece of syntactic sugar. The advantage is obvious: You can bind the results of pure (as in, not monadic) computations to a name without resorting to a pointless val <- return $ ...
and without splitting up the do
block in two:
至于为什么两者都是可能的:嗯,让我们……在…具有广泛的应用程序(其中大部分与monads没有特别的关系),并且具有很长的历史。另一方面,让我们不做表示法,似乎只是一小块语法糖。优点是显而易见的:您可以将纯(如in,不是mon)计算的结果绑定到一个名称,而不需要使用无意义的val <- return $…不把do块分成两部分
do stuff
let val = ...
in do more
stuff $ using val
The reason you don't need an extra do
block for what follows the let
is that you only got a single line. Remember, do e
is e
.
你不需要一个额外的do块来做let后面的事情的原因是你只有一行。记住,e是e。
Regarding your edit: digit
being visible in the next line is the whole point. And there's no exception for it or anything. do
notation becomes one single expression, and let
works just fine in a single expression. where
is only needed for things which aren't expressions.
关于您的编辑:下一行显示的数字就是整个点。也没有例外。do表示法变成了一个单独的表达式,让它在一个单独的表达式中正常工作。这里只需要非表达式的东西。
For the sake of demonstration, I'll show the desugared version of your do
block. If you aren't too familiar with monads yet (something you should change soon IMHO), ignore the >>=
operator and focus on the let
. Also note that indentation doesn't matter any more.
为了演示,我将展示您的do块的desugared版本。如果您还不太熟悉monads(您应该尽快更改IMHO),请忽略>>=操作符,而关注let。还要注意缩进不再重要。
main = readFile "p8.log" >>= (\t ->
let digits = map digitToInt $ concat $ lines t
in print (problem_8 digits))
#4
1
Some beginner notes about "are following two the same".
一些初学者的笔记是关于“遵循两个相同的”。
For example, add1
is a function, that add 1 to number:
例如,add1是一个函数,它加1到number:
add1 :: Int -> Int
add1 x =
let inc = 1
in x + inc
So, it's like add1 x = x + inc
with substitution inc by 1 from let
keyword.
因此,它就像add1 x = x + inc。
When you try to suppress in
keyword
当你尝试抑制关键字
add1 :: Int -> Int
add1 x =
let inc = 1
x + inc
you've got parse error.
你有解析错误。
From documentation:
从文档:
Within do-blocks or list comprehensions
let { d1 ; ... ; dn }
without `in` serves to introduce local bindings.
Btw, there are nice explanation with many examples about what where
and in
keyword actually do.
顺便说一句,这里有很好的解释,有很多例子可以说明关键字的作用。