これは、この質問に対する既に削除された回答によって引き起こされた質問です。この問題は次のように要約できます。
折りたたみ中に生成されたリストの末尾を使用して、リストを折りたたむことは可能ですか?
これが私の言いたいことです。階乗を計算したいとします (これはばかげた例ですが、デモンストレーション用です)。次のようにすることにします。
fac_a(N, F) :-
must_be(nonneg, N),
( N =< 1
-> F = 1
; numlist(2, N, [H|T]),
foldl(multiplication, T, H, F)
).
multiplication(X, Y, Z) :-
Z is Y * X.
ここで、 に与えるリストを生成する必要がありfoldl
ます。ただし、定数メモリで同じことを行うことができます (リストを生成せず、 を使用せずにfoldl
):
fac_b(N, F) :-
must_be(nonneg, N),
( N =< 1
-> F = 1
; fac_b_1(2, N, 2, F)
).
fac_b_1(X, N, Acc, F) :-
( X < N
-> succ(X, X1),
Acc1 is X1 * Acc,
fac_b_1(X1, N, Acc1, F)
; Acc = F
).
ここでのポイントは、 を使用するソリューションとは異なりfoldl
、これは定数メモリを使用することです。すべての値を含むリストを生成する必要はありません!
階乗の計算は最良の例ではありませんが、次に来るばかげたことを理解するのは簡単です。
私がループ (および再帰) を本当に恐れているとしましょう。それでもリストは必要です。だからここに私が試すかもしれないものがあります:
fac_c(N, F) :-
must_be(nonneg, N),
( N =< 1
-> F = 1
; foldl(fac_foldl(N), [2|Back], 2-Back, F-[])
).
fac_foldl(N, X, Acc-Back, F-Rest) :-
( X < N
-> succ(X, X1),
F is Acc * X1,
Back = [X1|Rest]
; Acc = F,
Back = []
).
驚いたことに、これは意図したとおりに機能します。部分リストの先頭にある初期値で折り畳みを「シード」し、現在の先頭を消費しながら次の要素を追加し続けることができます。の定義は、上記fac_foldl/4
の定義とほとんど同じですfac_b_1/4
。唯一の違いは、状態の維持方法が異なることです。ここでの私の仮定は、これは定数メモリを使用する必要があるということです:その仮定は間違っていますか?
これがばかげていることはわかっていますが、折り畳みがいつ開始されるかがわからないリストを折り畳むのに役立つ可能性があります。元の質問では、xy 座標のリストを指定して、接続された領域を見つける必要がありました。xy 座標のリストを 1 回折りたたむだけでは十分ではありません (ただし、2 つのパスで実行できます。同じウィキペディアの記事で参照されているより良い方法が少なくとも 1 つあることに注意してください。ただし、これも複数のパスを使用します。全体として、マルチパス アルゴリズムは、隣接するピクセルへの一定時間のアクセスを前提としています!)。
元の「地域」の質問に対する私自身の解決策は次のようになります。
set_region_rest([A|As], Region, Rest) :-
sort([A|As], [B|Bs]),
open_set_closed_rest([B], Bs, Region0, Rest),
sort(Region0, Region).
open_set_closed_rest([], Rest, [], Rest).
open_set_closed_rest([X-Y|As], Set, [X-Y|Closed0], Rest) :-
X0 is X-1, X1 is X + 1,
Y0 is Y-1, Y1 is Y + 1,
ord_intersection([X0-Y,X-Y0,X-Y1,X1-Y], Set, New, Set0),
append(New, As, Open),
open_set_closed_rest(Open, Set0, Closed0, Rest).
上記と同じ「テクニック」を使用して、これをひねって折り畳むことができます。
set_region_rest_foldl([A|As], Region, Rest) :-
sort([A|As], [B|Bs]),
foldl(region_foldl, [B|Back],
closed_rest(Region0, Bs)-Back,
closed_rest([], Rest)-[]),
!,
sort(Region0, Region).
region_foldl(X-Y,
closed_rest([X-Y|Closed0], Set)-Back,
closed_rest(Closed0, Set0)-Back0) :-
X0 is X-1, X1 is X + 1,
Y0 is Y-1, Y1 is Y + 1,
ord_intersection([X0-Y,X-Y0,X-Y1,X1-Y], Set, New, Set0),
append(New, Back0, Back).
これも「効く」。上記のように終了条件を明示していないため、折り目は選択ポイントを残します。そのfac_foldl/4
ため、その直後にカットが必要です (醜い)。
質問
- リストを閉じてカットを削除するきれいな方法はありますか? 階乗の例では、追加情報があるため、いつ停止するかがわかります。しかし、2 番目の例では、リストの後ろが空のリストであることをどのように確認すればよいでしょうか?
- 私が見逃している隠れた問題はありますか?
- これは、DCG を使用した Implicit State とどこか似ているように見えますが、それがどのように機能するのかまったく理解できなかったことを認めなければなりません。これらは接続されていますか?