

`Prolog` にて `ローンパターン(Loan Pattern)` を後述のように実装しましたが、より簡潔に書けないものでしょうか？

ここでの `ローンパターンは、 リソースの解放を忘れないようにする仕組み` を意図しています。 ( [C#でいう using
ステートメント](https://msdn.microsoft.com/ja-jp/library/yh598w02.aspx) 、 [Javaでいう
try-with-resources
ステートメント](http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html)
が近いかもしれません。 )

* * *

特に以下の点について、疑問が残りました。

  * 毎度 `print_each` のような定義をしない方法はないか
  * `repeat` の部分を簡潔に書けないか？
  * (そもそもこれはローンパターンになっているのか？)

[code]

    main :-
        writeln('# Open!'),
    
        % 'test.txt' を開き、print_each 述語で処理する。
        open_each('test.txt', print_each),
    
        writeln('# Closed!').
    
    % ファイルを読み込むごとに呼ばれる述語
    % 【疑問点】毎度 print_each の定義しなければならないものか？
    print_each(Term) :-
        % some process here...
        % 
        printf('%t', [Term]), nl.
    
    open_each(FileName, Do) :-
        open(FileName, read, Stream),
    
        % 【疑問点】とくに以下は簡潔に書けないか？
        repeat,
        ( at_end_of_stream(Stream)
        ->  !
        ;   read(Stream, Term),
            call(Do, Term), 
            fail
        ),
    
        close(Stream).
    
    :- main, halt.
    
[/code]

以下は test.txt の中身です。

[code]

    text(foobar).
    text(fuba).
    text(abafu).
    text(syzzy).
    
[/code]

実行例は以下のようになります。

[code]

    # Open!
    text(foobar).
    text(fuba).
    text(abafu).
    text(syzzy).
    # Closed!
    
[/code]

環境: SWI-Prolog version 5.10.4 for amd64 です。

※ より Prolog らしいコードや用語の誤りなどありましたらコメントなどでご指摘お願いします


CC BY-SA 3.0
2015-01-28T06:35:39.483
5498
2015-02-07T06:04:48.007
5021
question
prolog
Prolog にて、ローンパターン(Loan Pattern) を簡潔に実装したい


`throw` and `catch` はいかがでしょうか。これは、Java などの `try 〜 catch 〜 finally 〜`
と同じ様なものだと思います。

[code]

    main :-
      writeln('# Open!'),
      open_each('test.txt', read_and_print_term),
      writeln('# Closed!').
    
    read_and_print_term(Stream) :-
      at_end_of_stream(Stream) -> !;
      read(Stream, Term),
      writef('%t', [Term]), nl,
      read_and_print_term(Stream).
    
    %% 追記: 以下は必要ありませんでした    
    %%read_and_print_term :- throw(_).
    
    open_each(FileName, Do) :-
      open(FileName, read, Stream),
      catch(call(Do, Stream), _, (write('# Error!'), nl)),
      close(Stream).
    
    :- main, halt.
    
[/code]

例えば、`text.txt` が以下の様になっている場合、

[code]

    text(foobar).
    text(fuba).
    Hello World!
    text(abafu).
    text(syzzy).
    
[/code]

実行結果は以下の様になります。

[code]

    # Open!
    text(foobar)
    text(fuba)
    # Error!
    # Closed!
    
[/code]

ただ、質問者様のプログラムではエラーはスキップしてファイルを最後まで読み込む様な仕様に見えるので、これでは期待に添わないかもしれません。ご参考程度にどうぞ。


CC BY-SA 3.0
2015-01-28T12:58:14.473
5521
2015-01-28T23:41:40.993
2015-01-28T23:41:40.993
5498
answer


クロージャのある言語であれば、Loan
Patternにおけるリソース利用処理をクロージャ渡しで表現できて非常に簡潔に記述できます。私の知る限り、Prologにはクロージャはありません。ですが`copy_term/2`を利用することで関数型言語におけるλ式のような機能を実装できます。

[code]

    :- op(1050, xfx, '=>').
    '=>'(Head, Body,       A) :- copy_term(Head => Body,  A        => X), call(X).
    '=>'(Head, Body,    A, B) :- copy_term(Head => Body, (A, B)    => X), call(X).
    '=>'(Head, Body, A, B, C) :- copy_term(Head => Body, (A, B, C) => X), call(X).
    % とりあえず3引数まで対応
    
[/code]

↑このような定義を予め用意しておきます。すると、以下の様に`call/*`によってλ式を呼び出すことができるようになります。

[code]

    ?- F = ((A, B, Out) => Out is A+B), call(F, 1, 2, X).
    % ==> X = 3.
    
[/code]

次に、回答となる実際のコードです。  
（要点を明確にするため`catch`の利用は省略させていただきました。）

[code]

    main :-
      with_io('test.txt', read, (IO) =>
        forall(terms_in_io(IO, Term), writeln(next :- Term))
      ),
      halt.
    
    with_io(Path, Mode, Call) :-
      open(Path, Mode, IO), call(Call, IO), !, close(IO).
    
    terms_in_io(IO, Term) :- repeat
        , (at_end_of_stream(IO), !, fail; read(IO, Term)).
    
[/code]


CC BY-SA 3.0
2015-02-07T06:04:48.007
6085
2015-02-07T06:04:48.007
4313
5498
answer