次のコードの最後のフレーズでは、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
わかりましたので、人々は私が言っていることを理解していないようです。言い換えると、上記のコンテキストを前提として、次の2つは同じですか?
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
で宣言されたバインディングのスコープに関する別の質問: here that:
where
句。
Where節を必要とするいくつかの保護された方程式にバインドをスコープすると便利な場合があります。
f x y | y>z = ...
| y==z = ...
| y<z = ...
where z = x*x
これは、それが囲む式のみをスコープとするlet式では実行できないことに注意してください。
私の質問:そのため、変数の数字は最後の印刷フレーズに表示されるべきではありません。ここで何かが恋しいですか?
簡単な答え:doブロックの本体とリスト内包表記の|
の後の部分でlet
なしでin
を使用します。それ以外の場合は、let ... in ...
を使用します。
Haskellでは、キーワードlet
は3つの方法で使用されます。
最初の形式はlet-expressionです。
let variable = expression in expression
これは、式が許可されている場所であればどこでも使用できます。
> (let x = 2 in x*2) + 3
7
2番目はlet-statementです。この形式はdo表記の内部でのみ使用され、in
は使用しません。
do statements
let variable = expression
statements
3番目は2番に似ており、リスト内包表記の中で使用されます。繰り返しますが、in
はありません。
> [(x, y) | x <- [1..3], let y = 2*x]
[(1,2),(2,4),(3,6)]
この形式は、後続のジェネレーターと|
の前の式のスコープ内にある変数をバインドします。
ここで混乱する理由は、式(正しい型)をdoブロック内のステートメントとして使用でき、let .. in ..
は単なる式であるためです。
Haskellのインデント規則により、前の行よりもインデントされた行は、前の行の継続であることを意味するため、
do let x = 42 in
foo
として解析されます
do (let x = 42 in foo)
インデントなしでは、解析エラーが発生します:
do (let x = 42 in)
foo
結論として、リスト内包表記またはdoブロックでin
を使用しないでください。これらの構成体はすでに独自の形式のlet
を持っているため、不必要でわかりにくいです。
まず、なぜ抱擁しますか? Haskell Platform は、GHCに付属している初心者向けの一般的な推奨方法です。
次に、let
keywordに進みます。このキーワードの最も単純な形式は、in
と共にalwaysを使用することを意味します。
_let {assignments} in {expression}
_
例えば、
_let two = 2; three = 3 in two * three
_
_{assignments}
_は、対応する_{expression}
_.のスコープ内のonlyです。つまり、in
は少なくとも対応するlet
とインデントし、let
式に関連するサブ式も同様に少なくともインデントする必要があります。これは実際には100%真実ではありませんが、大まかな目安です。 Haskellのレイアウトルールは、Haskellコードを読み書きするときに慣れるでしょう。インデントの量は、どのコードがどの式に関係するかを示す主な方法であることに留意してください。
Haskellには、in
を記述する必要がないという2つの便利なケースがあります。表記法とリスト内包表記(実際には、モナド内包表記)です。これらの便利なケースの割り当ての範囲は事前に定義されています。
_do foo
let {assignments}
bar
baz
_
do
表記の場合、_{assignments}
_は、後続のステートメントのスコープ内にあります。この場合、bar
およびbaz
であり、foo
ではありません。書いたようです
_do foo
let {assignments}
in do bar
baz
_
リスト内包表記(または実際には、任意のモナド内包表記)はdo表記に脱糖するため、同様の機能を提供します。
_[ 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
_allは、このwhere
句の_{assignments}
_にアクセスできます。リンクしたチュートリアルで述べたように、これは複数のガードが同じ式を再利用する場合に役立ちます。
do
表記では、let
の有無にかかわらずin
を実際に使用できます。それを同等にするために(あなたの場合、2番目のdo
を追加する必要があるため、インデントを増やす必要がある例を後で示します)、発見したとおりにインデントする必要があります(使用している場合)レイアウト-明示的な中括弧とセミコロンを使用する場合、それらはまったく同じです)。
whyと同等であると理解するには、実際にモナドを(少なくともある程度)理解し、do
表記の脱糖規則を調べる必要があります。特に、次のようなコード:
_do let x = ...
stmts -- the rest of the do block
_
_let x = ... in do { stmts }
_に変換されます。あなたの場合、stmts = print (problem_8 digits)
。脱糖されたlet
バインディング全体を評価すると、IOアクション(_print $ ...
_から))になります。ここでは、モナドを理解する必要があります。 do
表記法と、モナド値をもたらす計算を記述する「通常の」言語要素。
両方の理由が考えられます。まあ、_let ... in ...
_には幅広いアプリケーションがあり(そのほとんどは特にモナドとは関係ありません)、ブートする長い歴史があります。一方、let
表記法のin
はdo
なしで、わずかな構文糖に過ぎないようです。利点は明らかです。無意味な_val <- return $ ...
_に頼らず、do
ブロックを2つに分割することなく、純粋な(モナドではなく)計算の結果を名前にバインドできます。
_do stuff
let val = ...
in do more
stuff $ using val
_
do
に続くものに余分なlet
ブロックが不要な理由は、1行しか取得できないからです。 _do e
_はe
であることを忘れないでください。
編集に関して:digit
は次の行に表示されていることがポイントです。そして、それ以外にも何も例外はありません。 do
表記は単一の式になり、let
は単一の式で問題なく機能します。 where
は、式ではないものにのみ必要です。
デモのために、do
ブロックの脱糖バージョンを示します。モナドにまだあまり慣れていない場合(すぐに変更する必要があるもの)、_>>=
_演算子を無視して、let
に注目してください。また、インデントは問題ではありません。
_main = readFile "p8.log" >>= (\t ->
let digits = map digitToInt $ concat $ lines t
in print (problem_8 digits))
_
「次の2つは同じです」に関するいくつかの初心者メモ。
例えば、 add1
は、数値に1を加算する関数です。
add1 :: Int -> Int
add1 x =
let inc = 1
in x + inc
それで、add1 x = x + inc
incをlet
キーワードから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
キーワードが実際に何をするかについての多くの例があります。