

下に示したのは、表(或いは行列)形式で与えられたデータをcsvファイル形式で書き出すプログラムです。

ここでappend/3を使うのはfailでの失敗駆動を避けるためですが、少し不自然な感じがします。  
どのように改良したら、もっと分り易いコードになるでしょうか。

[code]

    put_csv_lines(_csvファイル,_区切り符号,LL) :-  
    tell(_csvファイル),  
    一行ずつ表示する(LL),  
    told.  
    
    一行ずつ表示する(LL) :-  
    append(_,[L|R],LL),  
    concat_atom(L,_区切り符号,_行表示),  
    writef('%t\n',[_行表示]),  
    R = [].  
    
[/code]

ここで append/3 が使われている主な理由は最後の副目標 R = [] で全ての行が終了した時点で
述語も真で終わりたいからです。その問題がなければ、member/2 で良い筈です。

[code]

    一行ずつ表示する(Outstream,LL) :-  
    member(L,LL),  
    concat_atom(L,_区切り符号,_行表示),  
    swritef(_改行付き行表示,'%t\n',[_行表示]),  
    write(Outstream,_改行付き行表示),  
    fail.  
    一行ずつ表示する(_,_).  
    
[/code]

このスタイルを嫌ったのが上記の append/3 を使った定義ということになります。さて、他に これに代わる表現はないでしょうか？


CC BY-SA 3.0
2014-12-20T02:49:43.747
2190
2014-12-25T11:23:35.107
2014-12-25T01:38:10.393
5175
5175
question
prolog
Prologで繰り返し処理を行う方法


forall/2とmember/2を組み合わせて繰り返し処理を行うことができます。

[code]

    save_as_csv_file(File, Separator, Table) :-
        open(File, write, Out),
        forall(member(Row, Table),
               write_csv_line(Out, Separator, Row)),
        close(Out),
        !.
    
    write_csv_line(Out, Separator, Row) :-
        concat_atom(Row, Separator, Line),
        swritef(LineNL, '%t\n', [Line]),
        write(Out, LineNL).
    
[/code]

この書き方では、TableのそれぞれのメンバーRowに対してwrite_csv_lineの処理を行うことが、素直に表現できています。

もちろん、これを読む人間はforall/2の使い方を知っていなければ内容を理解できません。しかし失敗駆動を直接使ったループでは、失敗の後どこまでバックトラックするのかをプログラムから読み取るのは容易でなく、プログラムを読む人間に負担を強いることになります。


CC BY-SA 3.0
2014-12-24T02:54:52.277
2462
2014-12-24T02:54:52.277
3890
2190
answer


質問者としてコメントします。  
forall/2 を使ったら良いのではないか？という回答を期待しました。  
期待通りの回答を戴いたということになります。

上で回答者に対するコメントとして書いたことですが、読みにくいので書き直します。  
失敗駆動と呼ばれるコード

[code]

    foo(L) :-  
      member(A,L),
      writef('%t\n',[A]),
      fail.
    foo(_).
    
[/code]

これを forall/2 を使って、

[code]

    foo(L) :-  
      forall(member(A,L),writef('%t\n',[A])).  
    
[/code]

とすっきりと書き直す話です。  
forall(P,Q) はPが真になる全ての場合にQもまた真である。という意味です。  
Pが真である場合にQが偽になると、forall/2 自体がfailしてしまいます。  
forall/2はPとQの間に上記の関係が成立しています。あるいは、関係を検査する述語である  
とも言えます。  
ところが、上記の例では、このように関係を規定する述語を手続き的な連接を表現するのに利用  
しています。  
それで構わないと考えて私も好んでこの表現を使うのですが、留意点があります。それは、  
Q側に分割された部分に上のコードでいうと、failまで到達しないケースがあってはならない。  
ここでは、 writef/2 のみですから、これが真になりfailに到達することは自明ですが、  
Qのコードが大きかったり、複雑だったり、あるいはExceptionを含むコードである場合には  
failまで到達していないケースがありうるのではないかと疑い、これを潰して置く必要が  
あります。


CC BY-SA 3.0
2014-12-25T01:25:09.527
2519
2014-12-25T11:23:35.107
2014-12-25T11:23:35.107
5175
5175
2190
answer