Haskell でループを実装することに興味があります。Haskell(疑似コード)で同様のことを行うにはどうすればよいですか:
var i = 0
for (int i1 = 0; i1 < 10; i1++) {
println(i1)
i += 2
}
println(i)
Haskell でループを実装することに興味があります。Haskell(疑似コード)で同様のことを行うにはどうすればよいですか:
var i = 0
for (int i1 = 0; i1 < 10; i1++) {
println(i1)
i += 2
}
println(i)
機能的に言えば、整数のリストをフォールディングして、整数ごとに要素を出力し、アキュムレータを 2 増やします。何かを出力する (つまり、I/O を行う) ため、モナドをフォールドする必要がありますが、それ以外の場合は、標準のleft-foldです。
foldM (\i i1 -> print i1 >> return (i + 2)) 0 [0..9] >>= print
コードと同じ変数名を使用するラムダ関数で折りたたみます。つまりi1
、現在の要素でi
あり、アキュムレータです。
i = 0
次のパラメータは、コードに対応するアキュムレータの初期値です。
最後のパラメータは、(両端を含む) 折り重ねた数値のリストです。
( >>=
bind 演算子) は、フォールドの結果 (つまり、 accumulator の最終値i
) を print 関数にパイプします。
編集:これは、あなたが書くつもりだったと仮定しています
var i = 0
for (int i1 = 0; i1 < 10; i1++) {
println(i1)
i += 2
}
println(i)
i
for句とループ本体の両方でインクリメントする代わりに。
という 2 つの仮定に従うと、
プリントアウトしたい方(同想定上製)
0
1
2
3
4
5
6
7
8
9
20
私はHaskellで書くだろう
do
let xs = [0..9]
mapM_ print xs
print (length xs * 2)
元の計算が 3 つの別々の (そして独立した!) 計算にどのように分割されたかがわかります。
i1
をリストに変換します。境界は 0 と 9 を含むことがわかっているので、ループ変数は list で表すことができます[0..9]
。i
リストについて知っていることから" " を計算し、同様に出力します。3 番目の計算は、従来の命令型プログラミングと Haskell の違いを強調しているため、特に興味深いものです。
リストの反復ごとに数値に 2 を足すことは、リストの長さを 2 倍することと同じです。私の目に大きな違いはi = length xs * 2
、瞬く間にそれが何を意味するのかを読んで理解できることです. i
ただし、ループのすべての反復をカウントアップすることは、それが実際に何を意味するのかを理解するために少し考えなければなりません。
3 つのサブ計算がすべて独立しているという事実は、テストがはるかに簡単であることを意味します。一度に 1 つずつテストでき、個別に機能する場合は、一緒に機能することもできます。
「似ているコード」という意味で「似ている」という意味である場合は、STRef
/IORef
回答のいずれかを参照してください。
ループを実行する簡単かつ柔軟な方法は、let または where 句で再帰関数を定義することです。
main = loop 0 10
where
loop i n | i < n = putStrLn (show i) >> loop (i+2) n
loop _ _ = return ()
これは、loop
2 つの変数i
との関数を定義しますn
。最初のパターンには、条件 ( ) をチェックするガード( の後に) があります。true の場合、このブランチが選択され、コンソールに出力されてから、自身が再び にバインドされます。それ以外の場合は、デフォルトのブランチが選択されます。これは(= IO Monad で何もしない) だけです。|
i < n
i
loop
i
i+2
returns ()
再帰関数を使用してこのようなループを実装することは、非常に柔軟です。高階関数 ( や など) を使用するのが簡単な場合はfor
、map
ループを命令型設定から変換する方法がわからない場合は、通常、再帰関数を使用するとうまくいきます。