2

私は SML に 2 つのリストを持っています。リスト A[(a,b,c),(d,e,f)]とリスト B としましょう[b,e]。A の各トリプルの 2 番目の要素と一致する B の各項目の出現回数を数えたいと思います。出力は 2 になるはずbですe

これはこれまでのコードですが、B のある要素から別の要素に移動すると、カウンターは常に 0 に設定されます。Java では、これは単純な double for ループになることがわかっています。

fun number_in_months (d : (int * int * int ) list, m : (int) list) = 
    if null m then 0 
    else if null d then number_in_months(d, tl m)
    else if (#2(hd d)) = (hd m) then 1 + number_in_months (tl d, m)
    else number_in_months(tl d, m)
4

2 に答える 2

5

コードは、再帰呼び出し間で値を累積していません。他の論理エラーもあるかもしれません。

再帰と関数を使用して値を累積することは、ここで詳細を読むことができる一般的なパターンです。その本質は、リストが空になるまでheadandを使用してリストを分解し、呼び出しごとに値を蓄積することです。tail以下のsum関数は、これを示す簡単な例です。これは、またはが で見つかったaccときに蓄積するように例に適合させることができます。belist A

fun sum(numbers: (int) list) =
  let fun sumR(numbers: (int) list, acc: int) =
    if null numbers
    then acc
    else
      sumR(tl numbers, hd numbers + acc)
  in
    sumR(numbers, 0)
  end

で実行すると、次のようになり[1,2,3]ます。

val sum = fn : int list -> int
- sum([1,2,3]);
val it = 6 : int

注: これはプログラミング言語クラスの Coursera の宿題に関する質問であるため、この回答は意図的に曖昧にしています。

于 2013-01-15T23:43:28.963 に答える
2

あなたが言及したように、それは命令型プログラミング言語のネスト/ダブルループになります。実際に欠けているのは、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 では、「外側の」ループが のすべての要素を通過し、m0 (加算の中立要素) を返します。ケース 3 では、実際の作業を行います。ここでは、引数の型を強制する必要がなく、トリプルの 2 番目の要素を引き出す必要がないように、パターン マッチングが使用されていますx2。必要なのは、計算を行い、 and を使用して再帰呼び出しを行うことだけxsですyss

このようにする場合、内部 (ヘルパー) 関数は元の入力リストの「コピー」を使用し、dその要素をステップ実行します (変更する可能性があります) が、使用できる元の入力リストへの参照を常に取得します。必要な場合。

于 2013-01-16T01:28:29.127 に答える