

Prologでは再帰的に処理する方法と、findallでリストを生成する方法があると聞きました。  
下に、再帰的な処理のプログラムがありますが、 findallを使うプログラムの書き方を教えて下さい。

[code]

    %  
    %  <<問題>>  
    %  初心者向きのPrologの問題。100個の乱数値(0-99)をリストに生成してください。  
    %  乱数値(0-99)は _乱数値 is random(100) で取れることとしてください。  
    %  
    %  初心者でない人には、「あなたは、何通りのプログラムパターンが脳裏を過ぎりますか？」  
    %  
    
    
    '100個の乱数値(0-99)をリストに生成します'(_乱数リスト) :-  
        '100個の'(_乱数リスト),  
        '乱数値(0-99)をリストに生成します'(_乱数リスト).  
    
    '100個の'(_乱数リスト) :-  
        length(_乱数リスト,100).  
    
    '乱数値(0-99)をリストに生成します'([]).  
    '乱数値(0-99)をリストに生成します'([_乱数値|R]) :-  
        _乱数値 is random(100),  
        '乱数値(0-99)をリストに生成します'(R).  
    
[/code]


CC BY-SA 3.0
2014-12-19T08:13:09.353
2119
2014-12-21T11:51:42.347
2014-12-21T11:47:43.503
728
5175
question
prolog
Prologでは再帰的に処理する方法と、findallでリストを生成する方法があると聞きました。findallの使い方を教えてください。


指示してばかりも気が引ける部分があったので、ちょっとprologに挑戦してみました。

環境は以下の通り:

  * windows 8.1 pro x64
  * SWI-Prolog 6.6.6 for Windows XP/Vista/7/8 64-bit edition

初心者なので用語などが異なるかもしれませんがご容赦ください。

まず個人的な好みで英字変数名が好きなのでまずご提示のコードをを書き換えました（内容は同じです。並びが違うぐらいです）。

sample.plg

[code]

    set_random_value_to_each_item_of_list([]).
    set_random_value_to_each_item_of_list([_r|R]) :-
      _r is random(100),
      set_random_value_to_each_item_of_list(R).
    
    gen_100_random_list(R) :-
      create_list_have_100_items(R),
      set_random_value_to_each_item_of_list(R).
    
    create_list_have_100_items(R) :-
      length(R, 100).  % この部分適用において、Rとはこれがtrueになるようなリスト
                       %    -> 100個の任意のtermを持つリスト
    
[/code]

実行するには(`?-` がSWI-Prologのプロンプト)

[code]

    ?- ['X:/path/to/file/sample.plg'].
    ?- gen_100_random_list(X).
    X = [34, 63, 65, 82, 56, 75, 17, 0, 87|...].
    
[/code]

* * *

で、これをfindall/3でということですが、

[code]

    ?- findall(X, (length(ELst,100), member(_X, ELst), X is random(100)), L).
    
[/code]

または

[code]

    ?- findall(X, (numlist(1, 100, NLst), member(_X, NLst), X is random(100)), L).
    
[/code]

で取ることができました。

プロシージャでは

[code]

    gen_100_random_with_findall(L) :-
      findall(X, (length(ELst,100), member(_X, ELst), X is random(100)), L).
    
    gen_100_random_with_findall2(L) :-
      findall(X, (numlist(1, 100, NLst), member(_X, NLst), X is random(100)), L).
    
[/code]

として再読み込みして実行

[code]

    ?- ['X:/path/to/file/sample.plg'].
    
    ?- gen_100_random_with_findall(R).
    R = [70, 73, 47, 53, 63, 56, 64, 82, 29|...].
    
    ?- gen_100_random_with_findall2(R).
    R = [52, 39, 66, 39, 16, 94, 52, 56, 88|...].
    
[/code]

期待通り動作していることを確認しています。

* * *

必要かどうかわかりませんが、解説としては、 たとえば、

[code]

    a(1).
    a(2).
    a(3).
    
[/code]

という定義があれば、

[code]

    ?- a(X).
    X = 1 ;
    X = 2 ;
    X = 3.
    
[/code]

なので、

[code]

    ?- findall(X, a(X), L).
    L=[1,2,3]
    
[/code]

が取れます。ここで Xを aに適用せずに Xは2であると定義すれば Xは常に2になる一方で aは何を渡していても3回結果を返すので、

[code]

    ?- findall(X, (a(_), X is 2), L).
    L=[2,2,2]
    
[/code]

という事になります。 random(100)で乱数が取れることはわかっているので、 あとはaの代わりに100個返す方法をさえあればよく、それは
lengthとnumlistを使って実現して、回答に示した形になったというわけです。


CC BY-SA 3.0
2014-12-21T11:44:28.130
2323
2014-12-21T11:51:42.347
2014-12-21T11:51:42.347
728
728
2119
answer