さて、あなたはほとんどそこにいます。すでにこの問題にかなりの時間を費やしたので、有効なコードをいくつか示してコメントします。
最初に、引数としてX
andを運ぶワーカー述語を呼び出し、それらを次のように初期化します。Y
0
validPair(Result) :-
validPair(0, 0, Result).
次に、基本ケースを処理します。から始めたので0
、基本ケースが上限です。逆の方向に進むこともできました。それは単なる選択です。ここでのカットは、次の節Y
よりも優れていることを心配する必要がないことを意味することに注意してください。100
validPair(_X, 101, []) :- !.
X
これは、合計が 未満になる正しい制限に適合する場合100
です。最初にすべてが問題ないことを確認してから、!/0
述語を使用して実行が最後の句に到達するのをもう一度防ぎます。これは意味がないためです。それが終わったら、興味深い値を計算してリストに追加するだけです。
validPair(X, Y, [[X, Y, Sum, Product]|R]) :-
Limit is min(100 - Y, Y),
X =< Limit,
!,
Sum is X + Y,
Product is X * Y,
NextX is X + 1,
validPair(NextX, Y, R).
処理する必要がある唯一のケースはX
、合計が 未満になるように修正した制限を超えた場合100
です。それが起こったら、次からやり直し、 にY
リセットX
し0
ます。
validPair(_X, Y, R) :-
NextY is Y + 1,
validPair(0, NextY, R).
何か問題がある場合は、コメントで説明を求めてください。
注: ここで使用されているカットは赤いカットです。つまり、述語の正しさは節の順序に完全に依存します。それは悪い習慣です。適切なガードでそれらを補完してみてください(X =< 100
たとえば)、それは良い追加になるでしょう:)
編集:
コードを監査してみましょう :D スタイルについてコメントすることから始めましょう: この節 (本体がないためファクトと呼ばれます) では、and を 1 回だけ使用N
しX
ます。つまり、それらの値を保存する必要はありません。この場合、名前の前に a を付ける_
か、単に匿名変数を使用します_
。
genList(100,N, X,[]).
になる
genList(100, _N, _X, []).
また
genList(100, _, _, []).
ここでも同じですS
。この句では使用されません。初回のみ使用です。_
または_Sum
、ここでも他の節での使用を文書化したい場合は、それを置き換えることができます (良い習慣です)。次に、2 つの変数を使用してまったく同じ値を保持します。ここには興味がありません。そのためだけに新しい変数を宣言するのではなく、最初の引数としてnextgenList/4
を呼び出すだけです。と もSum
同じです。この句の適切なバージョンは次のようになります。Y
N1
genList(S,N, X,[[X,Y,Sum,Product]|Xs]):-
Y is N+1,
Sum is X+Y,
NewS is Sum,
Sum<101,
Product is X*Y,
N1 is N+1,
genList(NewS,N1, X,Xs).
の中へ
genList(_PreviousSum, N, X,[[X, Y, Sum, Product]|Xs]):-
Y is N+1,
Sum is X + Y,
Sum<101,
Product is X * Y,
genList(Sum, Y, X, Xs).
あなたの最後の節には算術の問題があります: N + X
is just the term +(N, X)
. 値ではありませんN + X
。is/2
他の句と同じように述語を使用する必要があります。の 2 番目の句と同じ問題ですS
。これらの小さな編集は次のようになります。
genList(S,N,X,Q):-
N+X < 101,
NextX is X + 1,
genList(0,NextX,NextX,Q).
の中へ
genList(_PreviousSum, N, X, Q) :-
Sum is N + X,
Sum < 101,
NextX is X + 1,
genList(0, NextX, NextX, Q).
したがって、現時点では、修正されたプログラムは次のようになります。
genList(100, _N, _X, []).
genList(_PreviousSum, N, X,[[X, Y, Sum, Product]|Xs]):-
Y is N+1,
Sum is X + Y,
Sum<101,
Product is X * Y,
genList(Sum, Y, X, Xs).
genList(_PreviousSum, N, X, Q) :-
Sum is N + X,
Sum < 101,
NextX is X + 1,
genList(0, NextX, NextX, Q).
スタイルの編集だけだったので、動作は変わりません。
それでは、スタイルではなくロジックのどこが間違っていたのかを見てみましょう。まずはベースケース。ここではすべて問題ありません。合計が上限であるかどうか、およびそれが return であるかどうかを確認します[]
。完全!
genList(100, _N, _X, []).
さて、あなたの「内部再帰」。ほぼ大丈夫です。私を悩ませている詳細を見てみましょう: 以前の合計を保持する値がありますが、新しい値を計算し、上限 + 1 に対して再テストします。より良いアイデアは、テストPreviousSum
に対してテストし、テスト< 100
を削除することSum < 101
です。そのためだけに議論があるという事実を正当化する方がよいでしょう! さらに、その制限の場合に句の実行を防ぐためにガードが使用されていることはより理解できます。そう、
genList(_PreviousSum, N, X,[[X, Y, Sum, Product]|Xs]):-
Y is N+1,
Sum is X + Y,
Sum<101,
Product is X * Y,
genList(Sum, Y, X, Xs).
に変わるだろう
genList(PreviousSum, N, X,[[X, Y, Sum, Product]|Xs]):-
PreviousSum < 100,
Y is N+1,
Sum is X + Y,
Product is X * Y,
genList(Sum, Y, X, Xs).
この変更はちょっと文体的なものであり、プログラムの動作を変更するものではないことに注意してください。それでも読みやすくなります!
さて、大きな悪いオオカミ: genList(_PreviousSum, N, X, Q) :- Sum は N + X、Sum < 101、NextX は X + 1、genList(0, NextX, NextX, Q) です。ここで言いたいことはたくさんあります。Sum
最初に繰り返しますが、PreviousSum
すでに値を保持しているため、計算する必要はありません。< 100
次に、代わりにテストする必要があり< 101
ます。次に、それをテストする必要がありX >= N
ます。これは、2 番目の句ではなく、代わりにこの句を通過したい場合に限られるためです。最後になりましたが、新しい反復を開始する代わりに、genList(0, NextX, NextX, Q)
genList(NextX, 0, NextX, Q) で開始する必要があります。ここでは、値を適切にリセットしていません。結果の句は次のとおりです。
genList(PreviousSum, N, X, Q) :-
PreviousSum < 100,
N >= X,
NextX is X + 1,
genList(NextX, 0, NextX, Q).
ご覧のとおり、2 番目の節 if を通過できないことは定式化されていますN >= X
。それが正しいことを保証するために、適切なテストを追加する必要があります。
genList(PreviousSum, N, X,[[X, Y, Sum, Product]|Xs]):-
PreviousSum < 100,
N < X,
Y is N+1,
Sum is X + Y,
Product is X * Y,
genList(Sum, Y, X, Xs).
これで完了です。プログラムは正しいです。
最終版は次のとおりです。
genList(100, _N, _X, []).
genList(PreviousSum, N, X,[[X, Y, Sum, Product]|Xs]):-
PreviousSum < 100,
N < X,
Y is N+1,
Sum is X + Y,
Product is X * Y,
genList(Sum, Y, X, Xs).
genList(PreviousSum, N, X, Q) :-
PreviousSum < 100,
N >= X,
NextX is X + 1,
genList(NextX, 0, NextX, Q).
変数の命名はまだ不十分です。より明確なバージョンは次のようになります。
genList(100, _X, _Y, []).
genList(PreviousSum, X, Y,[[X, Y, Sum, Product]|Xs]):-
PreviousSum < 100,
X < Y,
NewX is X + 1,
Sum is X + Y,
Product is X * Y,
genList(Sum, NewX, Y, Xs).
genList(PreviousSum, X, Y, Q) :-
PreviousSum < 100,
X >= Y,
NextY is Y + 1,
genList(NextY, 0, NextY, Q).
ここでまだ問題があります (ええ、終わりはありません :D): 積の合計などを計算する前に変数をインクリメントするという事実は、いくつかの値をスキップすることを意味します。後にインクリメントしてみてください。それは練習としてしましょう:)