McB*_*den 56 whitespace haskell scope let where-clause
在下面的代码中,我可以把最后一个词放在in前面.它会改变什么吗?
另一个问题:如果我决定放在in最后一个短语的前面,我是否需要缩进它?
我试着没有缩进和拥抱抱怨
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
Run Code Online (Sandbox Code Playgroud)
好的,所以人们似乎不明白我在说什么.让我重新说一下:鉴于上述背景,以下两个是否相同?
1.
let digits = map digitToInt $concat $ lines t
print $ problem_8 digits
Run Code Online (Sandbox Code Playgroud)
2.
let digits = map digitToInt $concat $ lines t
in print $ problem_8 digits
Run Code Online (Sandbox Code Playgroud)
关于声明的绑定范围的另一个问题let:我在这里读到:
where条款.
有时,将绑定范围扩展到几个保护方程是很方便的,这需要where子句:
f x y | y>z = ...
| y==z = ...
| y<z = ...
where z = x*x
Run Code Online (Sandbox Code Playgroud)
请注意,这不能通过let表达式来完成,该表达式仅覆盖它所包含的表达式.
我的问题:所以,可变数字不应该对最后一个印刷短语可见.我在这里想念一下吗?
ham*_*mar 115
简短的回答:使用let没有in在做块体,并在之后的部分|以列表的理解.在其他地方,使用let ... in ....
该关键字let在Haskell中以三种方式使用.
第一种形式是let-expression.
let variable = expression in expression
Run Code Online (Sandbox Code Playgroud)
这可以在允许表达式的任何地方使用,例如
> (let x = 2 in x*2) + 3
7
Run Code Online (Sandbox Code Playgroud)第二个是let-statement.此表单仅在do-notation中使用,不使用in.
do statements
let variable = expression
statements
Run Code Online (Sandbox Code Playgroud)第三个类似于数字2,在列表推导中使用.再一次,没有in.
> [(x, y) | x <- [1..3], let y = 2*x]
[(1,2),(2,4),(3,6)]
Run Code Online (Sandbox Code Playgroud)
此形式绑定一个变量,该变量在后续生成器和之前的表达式中的范围内|.
你在这里混淆的原因是表达式(正确的类型)可以用作do-block中的语句,并且let .. in ..只是一个表达式.
由于haskell的缩进规则,比前一行缩进的行意味着它是前一行的延续,所以这
do let x = 42 in
foo
Run Code Online (Sandbox Code Playgroud)
被解析为
do (let x = 42 in foo)
Run Code Online (Sandbox Code Playgroud)
没有缩进,您会得到一个解析错误:
do (let x = 42 in)
foo
Run Code Online (Sandbox Code Playgroud)
总之,永远不要in在列表理解或阻止中使用.这是不必要的和令人困惑的,因为这些结构已经有了自己的形式let.
Dan*_*ton 18
首先,为什么拥抱?该哈斯克尔平台一般是去新手,附带GHC的推荐方式.
现在,转到let关键字.此关键字的最简单形式是始终与之一起使用in.
let {assignments} in {expression}
Run Code Online (Sandbox Code Playgroud)
例如,
let two = 2; three = 3 in two * three
Run Code Online (Sandbox Code Playgroud)
的{assignments}是仅在对应的范围{expression}.应用常规布局规则,这意味着in必须缩进至少let与其对应的缩进量,并且与表达式相关的任何子let表达式同样必须至少缩进.这实际上并非100%正确,但这是一个很好的经验法则; Haskell布局规则是您在读写Haskell代码时会习惯的.请记住,缩进量是指示哪些代码与哪个表达式相关的主要方式.
Haskell提供了两个方便的例子,你不必写in:do notation和list comprehensions(实际上,monad comprehensions).这些便利案例的分配范围是预定义的.
do foo
let {assignments}
bar
baz
Run Code Online (Sandbox Code Playgroud)
对于do符号中,{assignments}在范围为遵循,在这种情况下,任何陈述bar和baz,但不会foo.就好像我们写了一样
do foo
let {assignments}
in do bar
baz
Run Code Online (Sandbox Code Playgroud)
列表理解(或者实际上,任何monad理解)desugar into do notation,所以他们提供类似的设施.
[ baz | foo, let {assignments}, bar ]
Run Code Online (Sandbox Code Playgroud)
将{assignments}在范围内的表达bar和baz,但不适合foo.
where有点不同.如果我没有弄错的话,where用特定的函数定义排列的范围.所以
someFunc x y | guard1 = blah1
| guard2 = blah2
where {assignments}
Run Code Online (Sandbox Code Playgroud)
在{assignments}这where条访问x和y.guard1,guard2,blah1,和blah2 所有有机会获得{assignments}本的where条款.正如您链接的教程中所提到的,如果多个警卫重用相同的表达式,这可能会有所帮助.
小智 7
在do表示法中,您确实可以使用let和不使用in.为了它是等价的(在你的情况下,我稍后将展示一个你需要添加第二个do因此更多缩进的例子),你需要在发现时缩进它(如果你正在使用布局 - 如果你使用显式括号和分号,它们完全相同).
要理解为什么它是等价的,你必须实际上修改monad(至少在某种程度上)并查看do符号的desugaring规则.特别是,像这样的代码:
do let x = ...
stmts -- the rest of the do block
Run Code Online (Sandbox Code Playgroud)
被翻译成let x = ... in do { stmts }.在你的情况下,stmts = print (problem_8 digits).评估整个desugared let绑定会导致IO操作(来自print $ ...).在这里,你需要理解monad,直观地认为do符号和描述导致monadic值的计算的"常规"语言元素之间没有区别.
至于两者为什么都可能:嗯,let ... in ...有广泛的应用程序(其中大多数与monad无关),以及很长的启动历史.let不in为do符号,在另一方面,似乎只是一小片的语法糖.优点是显而易见的:你可以将纯(如in,而不是monadic)计算的结果绑定到一个名称,而不需要求助于无意义的val <- return $ ...并且不将do块分成两部分:
do stuff
let val = ...
in do more
stuff $ using val
Run Code Online (Sandbox Code Playgroud)
你之后不需要额外do块的原因let是你只有一行.记住,do e是e.
关于你的编辑:digit在下一行中可见是重点.它也不例外.do符号变成一个单独的表达式,并且let在单个表达式中工作得很好.where只有非表达式的东西才需要.
为了演示,我将展示你的do块的desugared版本.如果你还不太熟悉monad(你应该尽快更改恕我直言),忽略>>=操作员并专注于let.另请注意,缩进不再重要.
main = readFile "p8.log" >>= (\t ->
let digits = map digitToInt $ concat $ lines t
in print (problem_8 digits))
Run Code Online (Sandbox Code Playgroud)