2

宿題は、0 から 10,000 までの数値と、1 からその数値までの間に循環素数がいくつあるかという 2 つの変数を取り入れることです。

再帰を介して変数を元に戻すのに問題があります(バックトレースと呼ばれるものだと思います)。正しい数値を取得し、コンセプトがダウンしていると確信しています。問題は、それがスローされることです。変数を再割り当てしようとするとエラー (?!)

コードは次のとおりです。

circPrimeCompare(Below, NumCirc):-
    RealNum is 0,
    circPrimeCompare(1, Below, RealNum, []),
    print('R: '),
    print(RealNum),
    nl,
    (NumCirc =:= RealNum).

circPrimeCompare(_, 0, _, _).     
circPrimeCompare(N, 1, _, _):- prime(N), print('aaa').
circPrimeCompare(X, Below, RealNum, L):-
    ( prime(X), X<Below ->
        print(X),
        nl,
        numDigits(X, Y),
        rotate(X, Y, N2),
        (   prime(N2) 
            ->  RealNum2 is RealNum + 1 
            ;   RealNum2 is RealNum
            ),
        X2 is X + 1,
        (   not(circPrimeCompare(X2, Below, RealNum2, L)) 
            ->  RealNum = RealNum2, print(RealNum), nl 
            ;   RealNum = RealNum2, print(RealNum), nl
            ),
        print('RealNum2: '),
        print(RealNum),
        nl
    ;
    ( X<Below ->
        X2 is X + 1,
        RealNumPass is RealNum,
        (   not(circPrimeCompare(X2, Below, RealNumPass, L)) 
            ->  RealNum = RealNumPass, print(RealNum), nl 
            ;   RealNum = RealNumPass, print(RealNum), nl
            ),
        print('RealNum: '),
        print(RealNum),
        nl
        )
    ).

トレースは次のとおりです。

   Fail: (26) circPrimeCompare(10, 10, 4, []) ? creep
^  Exit: (25) not(user:circPrimeCompare(10, 10, 4, [])) ? creep
   Call: (25) 4=4 ? creep
   Exit: (25) 4=4 ? creep
...
   Exit: (24) circPrimeCompare(9, 10, 4, []) ? creep
^  Fail: (23) not(user:circPrimeCompare(9, 10, 4, [])) ? creep
   Call: (23) 4=4 ? creep
   Exit: (23) 4=4 ? creep
...
   Exit: (22) circPrimeCompare(8, 10, 4, []) ? creep
^  Fail: (21) not(user:circPrimeCompare(8, 10, 4, [])) ? creep
   **Call: (21) 3=4 ? creep
   Fail: (21) 3=4 ? creep**
   Redo: (21) numDigits(7, _G589) ? creep

太字の部分は私を投げているものです。なぜこのように振る舞うのか、私にはよくわかりません。変数は基本的に 1 回しか使用できないためですか。それを修正する方法についてのアイデアはありますか?

(そして、はい、これは本当にひどいコードだと思います。この課題の前に、Prolog で何も書いたことがありませんでした。)

4

3 に答える 3

1

一般的なプロローグのイディオムは、既に述べたように、作業を完了するためにアキュムレータでシードされた「ヘルパー」述語を使用することです。さらに、問題をより小さく単純な部分に分解するのに役立ちます。多くの場合、私が「公開述語」と呼んでいるものは、制約を強制し、すべての作業を行う「プライベート」ヘルパー述語を呼び出すこと以外はあまり機能しません。多くの場合、そのヘルパー述語は、パブリック述語と同じ名前で、アリティが異なります (たとえばfoo/3、パブリック述語の とは対照的に)。foo/2このようなもの:

%
% count the number of circular primes below the specified
% upper limit.
%    
count_circular_primes( Max , Cnt ) :-
  integer(Max) ,
  Max >=     1 ,
  Max =< 10000 ,
  count_circular_primes( Max , 0 , Cnt )
  .

ここでのヘルパー述語はcount_circular_primes/3、0 としてシードされたアキュムレータ値を使用します。次のようになります。

count_circular_primes( 0 , Cnt , Cnt ) .
count_circular_primes( N , T , Cnt ) :-
  N > 0 ,
  is_circular_prime(N) ,
  T1 is T + 1 ,
  N1 is N - 1 ,
  count_circular_primes(N1,T1,Cnt)
  .

ここですべてのカウントが行われることに気付くでしょう。あとは、特定の整数が循環素数かどうかを判断するだけです。簡単!

Cntまた、再帰操作全体が完了するまで、結果変数は何にも統合されないことにも注意してください。Nゼロに達すると、アキュムレータはT結果と統合されます。

フレームワークが整ったので、単一の整数が循環素数かどうかを判断する方法を考えてみましょう。それはかなり簡単なはずです:

  1. 数は素数ですか?
  2. 循環バッファーとして扱い、数字を 1 桁左に回転させます。
  3. 最初に戻るまで繰り返します。

リストのローテーションを行う方法は次のとおりです。バックトラックすると、リスト ローテーションのセット全体が生成されます。

rotate(Xs,Ys) :-
  append(A,[B|C],Xs) , % get the prefix (A) , suffix (C) and current item (B) from Xs
  append(C,A,D)      , % append the suffic (C) to the prefix (A) to get the leftmost bit (B)
  append([B],D,Ys)     % append the current item (B) to the leftmost bit (D) to get the rotation
  .

仕組みは次のappend/3とおりです。指定されたリストのすべての可能なプレフィックス/サフィックスの組み合わせを生成するために使用できます。append(Prefix,Suffix,[1,2,3,4])バックトラッキングによって返されるソリューションの完全なセットは

Prefix    Suffix
========= =========
[]        [1,2,3,4]
[1]         [2,3,4]
[1,2]         [3,4]
[1,2,3]         [4]
[1,2,3,4]        []

ローテーションを生成findall/3できるようになると、特定の入力を指定して、ソリューション セット全体をリストとして取得するために使用できます。したがって、上記のrotate/2述語を考えると、次のように言えます。

findall( X , rotate( [1,2,3,4] , X ) , Rotations ).

Rotationsこのリストのリストと統合されます:

[ [1,2,3,4] , [2,3,4,1] , [3,4,1,2] , [4,1,2,3] ]

回転のセットを取得したら、あとはそれが素数を表しているかどうかを評価するだけです。forall/2次の行に沿って、でそれを行うことができます。

forall( member(Rotation,Rotations), is_prime(Rotation) )

findall/3との使用が許可されていない場合、状況は少し複雑になりますforall/2が、これで問題が解決するはずです。

于 2013-04-29T21:01:26.333 に答える