![](/img/trans.png)
[英]Are we allowed to use conditional expression inside the let-in expression in Haskell?
[英]In Haskell, when do we use in with let?
在下面的代碼,最后一句我可以把一個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
好的,所以人們似乎不明白我在說什么。 讓我重新說一下:鑒於上述背景,以下兩個是否相同?
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
關於let
聲明的綁定范圍的另一個問題:我在這里讀到:
where
條款。
有時,將綁定范圍擴展到幾個保護方程是很方便的,這需要where子句:
f x y | y>z = ...
| y==z = ...
| y<z = ...
where z = x*x
請注意,這不能通過let表達式來完成,該表達式僅覆蓋它所包含的表達式。
我的問題:所以,可變數字不應該對最后一個印刷短語可見。 我在這里想念一下嗎?
簡短的回答 :使用let
沒有in
在一個do塊體,並在之后的部分|
在列表理解中。 在其他任何地方,使用let ... in ...
關鍵字let
在Haskell中以三種方式使用。
第一種形式是let-expression 。
let variable = expression in expression
這可以在允許表達式的任何地方使用,例如
> (let x = 2 in x*2) + 3 7
第二個是let-statement 。 此表單僅在do-notation中使用in
不使用。
do statements let variable = expression statements
第三個類似於數字2,在列表推導中使用。 同樣,沒有in
。
> [(x, y) | x <- [1..3], let y = 2*x] [(1,2),(2,4),(3,6)]
此形式綁定一個變量,該變量在后續生成器和|
之前的表達式中 。
你在這里混淆的原因是表達式(正確類型)可以用作do-block中的語句,而let .. in ..
只是一個表達式。
由於haskell的縮進規則,比前一行縮進的行意味着它是前一行的延續,所以這
do let x = 42 in
foo
被解析為
do (let x = 42 in foo)
沒有縮進,您會得到一個解析錯誤:
do (let x = 42 in)
foo
總之,永遠不要in
列表理解或阻止中使用。 這是不必要的和令人困惑的,因為這些構造已經有自己的let
形式。
首先,為什么擁抱? Haskell平台通常是推薦GHC新手的方式。
現在,轉到let
關鍵字。 此關鍵字的最簡單形式是始終與in
一起使用。
let {assignments} in {expression}
例如,
let two = 2; three = 3 in two * three
{assignments}
僅在相應{expression}
范圍內。 常規布局規則,這意味着in
必須縮進至少不亞於let
,它對應,以及涉及到任何子表達式let
表達同樣必須縮進至少之多。 這實際上並非100%正確,但這是一個很好的經驗法則; Haskell布局規則是您在讀取和編寫Haskell代碼時會習慣的。 請記住,縮進量是指示哪些代碼與哪個表達式相關的主要方式。
哈斯克爾提供了兩個方便的情況下,您不必寫in
:做標記和list解析(實際上,單子內涵)。 這些便利案例的分配范圍是預定義的。
do foo
let {assignments}
bar
baz
對於do
記號,在{assignments}
在范圍為遵循,在這種情況下,任何陳述bar
和baz
,而不是foo
。 就好像我們寫了一樣
do foo
let {assignments}
in do bar
baz
列表理解(或者實際上,任何monad理解)desugar into do notation,所以他們提供類似的設施。
[ baz | foo, let {assignments}, bar ]
{assignments}
在表達式bar
和baz
范圍內,但不適用於foo
。
where
有所不同。 如果我沒有弄錯的話, where
的范圍與特定的函數定義一致。 所以
someFunc x y | guard1 = blah1
| guard2 = blah2
where {assignments}
此where
子句中的{assignments}
可以訪問x
和y
。 guard1
, guard2
, blah1
和blah2
都可以訪問此where
子句的{assignments}
。 正如您鏈接的教程中所提到的,如果多個警衛重用相同的表達式,這可能會有所幫助。
在do
記號,你的確可以使用let
有和沒有in
。 為了它是等價的(在你的情況下,我稍后將展示你需要添加第二個do
並因此更多縮進的示例),你需要在發現時縮進它(如果你使用布局 - 如果你使用顯式括號和分號,它們完全相同)。
要理解為什么它是等價的,你必須真正神交單子(至少在一定程度上),並查看了脫糖的規則do
記號。 特別是,像這樣的代碼:
do let x = ...
stmts -- the rest of the do block
被轉換為let x = ... in do { stmts }
。 在您的情況下, stmts = print (problem_8 digits)
。 評估整個desugared let
綁定會導致IO操作(來自print $ ...
)。 在這里,你需要理解monad,直觀地認為do
notations和描述導致monadic值的計算的“常規”語言元素之間沒有區別。
至於兩者為什么都有可能:嗯, let ... in ...
有廣泛的應用程序(其中大多數與monad無關),以及很長的啟動歷史。 let
沒有in
為do
記號,在另一方面,似乎只是一小片的語法糖。 優點是顯而易見的:你可以將純(不是monadic)計算的結果綁定到一個名稱,而不需要求助於無意義的val <- return $ ...
並且不將do
塊拆分為兩個:
do stuff
let val = ...
in do more
stuff $ using val
你並不需要一個額外的理由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))
一些初學者注意到“跟隨兩個相同”。
例如, add1
是一個函數,它為數字加1:
add1 :: Int -> Int
add1 x =
let inc = 1
in x + inc
所以,就像add1 x = x + inc
, let
關鍵字的替換inc為1。
當你試圖壓制in
關鍵字
add1 :: Int -> Int
add1 x =
let inc = 1
x + inc
你有解析錯誤。
來自文檔 :
Within do-blocks or list comprehensions
let { d1 ; ... ; dn }
without `in` serves to introduce local bindings.
順便說一句,有很好的解釋有什么例子很多where
並in
關鍵字實際上做。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.