あなたが言及したように、それは命令型プログラミング言語のネスト/ダブルループになります。実際に欠けているのは、2 番目のループです。
コードの次の行からわかるように、「内側」ループは のすべての要素を通過しd
、これが完了すると、「外側」ループは の一番上の要素をポップして最初からやり直そうとします。m
else if null d then number_in_months(d, tl m)
ただし、ご覧のとおり、リストd
が空であることをテストしたばかりで、これ (まったく同じリスト) を の末尾の再帰呼び出しに指定すると、が空になるm
まで、連続する呼び出しごとに同じケースになります。m
あなたは0を返します。
したがって、あなたが見逃しているのは、元の入力リストの「コピーを保持する」ことm
です。これはさまざまな方法で実行できますが、内部 (ヘルパー) 関数が適切に最も使用される関数であり、ネストされたループのように「見える」ことさえあります。
fun number_in_months (d, m) =
let
fun nim' ([], y::ys) = nim (d, ys) (* 1 *)
| nim' (_, []) = 0 (* 2 *)
| nim' ((_, x2, _) :: xs, yss as (y::ys)) = ... (* 3 *)
in
nim'(d, m)
end
上記のコードに一致するパターンを使用すると、はるかに単純になり、エラーが発生しにくくなります。ケース 1 では、「内側」のループが のすべての要素を通過したため、いつでも変更されない外側の関数をd
使用して再帰呼び出しが行われます。d
ケース 2 では、「外側の」ループが のすべての要素を通過し、m
0 (加算の中立要素) を返します。ケース 3 では、実際の作業を行います。ここでは、引数の型を強制する必要がなく、トリプルの 2 番目の要素を引き出す必要がないように、パターン マッチングが使用されていますx2
。必要なのは、計算を行い、 and を使用して再帰呼び出しを行うことだけxs
ですyss
。
このようにする場合、内部 (ヘルパー) 関数は元の入力リストの「コピー」を使用し、d
その要素をステップ実行します (変更する可能性があります) が、使用できる元の入力リストへの参照を常に取得します。必要な場合。