1

Prolog で非常に簡単な問題を解く: 1 から 100 までのすべての数字を出力しますが、数字の代わりに、数字が 3 の倍数の場合は 'Fuzz'、5 の倍数の場合は 'Buzz'、両方の場合は 'FizzBu​​zz' を出力します。

私は最終的に次のことをしました:

fizzbuzz :- forall( between(1, 100, X), fizzbuzz(X) ).
fizzbuzz(X) :- ( write_fb(X) ; write_n(X) ), nl.

write_fb(X) :- bagof(_, fb(X), _).
fb(X) :- X rem 3 =:= 0, write('Fizz').
fb(X) :- X rem 5 =:= 0, write('Buzz').

write_n(X) :- write(X).

しかし、その副作用のためだけに bagof/3 を使用することを回避する述語または制御構造はありませんか? (副作用のためだけに述語を使用することに、私はいつも少し確信が持てません)。

4

11 に答える 11

3

Complementing the existing answers, I would like to show a more relational solution that I hope illustrates some unique benefits of applying a declarative programming paradigm such as logic programming to this question.

First, let us recapitulate the task:

print all numbers from 1 to 100, but instead of the number, print...

  • 'Fuzz' if number is a multiple of 3
  • 'Buzz' if multiple of 5
  • and 'FizzBuzz' if both.

The tacit assumption, I presume, is that the numbers are limited to integers.

For simplicity, let us first restrict ourselves to a single integer, and let us describe the relation between such integer and the required output.

The three cases mentioned above can be quite directly translated to Prolog, using your Prolog ystem's CLP(FD) constraints for declarative integer arithmetic:

integer_output(N, 'Fuzz')     :- N #= 3*_.
integer_output(N, 'Buzz')     :- N #= 5*_.
integer_output(N, 'FizzBuzz') :- N #= 3*_, N #= 5*_.

That's not all though, because this yields for example:

?- integer_output(4, N).
false.

Hence, we need one more case, which we can for example formulate as:

integer_output(N, N)          :- N mod 3 #\= 0, N mod 5 #\= 0.

This simply states that in the event that none of the other cases applies, we output the number as is. The resulting relation is very general. For example, we can use it for concrete integers:

?- integer_output(1, O).
O = 1.

?- integer_output(3, O).
O = 'Fuzz' ;
false.

And we can also use it to write unit tests, for example:

?- integer_output(5, 'Buzz').
true .

Here, the intended output is already specified, and we can use the same relation to ask whether the output will be as required. That's a quite nice property of relations, and would not be so easy if we only wrote the output on the system terminal instead of making it explicit as a predicate argument as we did above.

But there's even more! We can also use the same relation in the other direction, where we ask for example: "Which integers result in the output Buzz?" Here it is:

?- integer_output(I, 'Buzz').
5*_680#=I.

That's a massive generalization of the earlier test case, and can serve as an additional assurance that we have covered all cases. In fact, we can even generalize this further, resulting in the most general query which asks how answers look like in general:

?- integer_output(I, O).
O = 'Fuzz',
3*_742#=I ;
O = 'Buzz',
5*_742#=I ;
O = 'FizzBuzz',
5*_1014#=I,
3*_1038#=I.

Let us reason more about the output. Obviously, we expect that the output is uniquely determined for each possible integer, right? Let us ask Prolog whether this is so, by asking for counterexamples of this property:

?- dif(O1, O2),
   integer_output(I, O1),
   integer_output(I, O2).
O1 = 'Fuzz',
O2 = 'Buzz',
5*_1046#=I,
3*_1070#=I ;
O1 = 'Fuzz',
O2 = 'FizzBuzz',
5*_1318#=I,
3*_1342#=I,
3*_1366#=I .

Now that doesn't look good : From the above, we already suspect that there may be cases of the same integer I yielding two different, equally justifiable, outputs O1 and O2.

And in fact, here's a concrete integer where this problem arises:

?- integer_output(15, O).
O = 'Fuzz' ;
O = 'Buzz' ;
O = 'FizzBuzz' ;
false.

So, it turns out, that the output is not uniquely determined! Let us follow our natural instinct and ask right away:

WHOSE FAULT IS THIS?

CLP(FD) CONSTRAINTS TO BLAME?

In fact, it turns out that using a declarative formulation has simply exposed an ambiguity in the task formulation. Prematurely committing to one of the solutions does not expose this problem.

What was probably meant was a task description that induces the following relation between an integer and the output:

integer_output(N, 'Fuzz')     :- N #= 3*_, N mod 5 #\= 0.
integer_output(N, 'Buzz')     :- N #= 5*_, N mod 3 #\= 0.
integer_output(N, 'FizzBuzz') :- N #= 3*_, N #= 5*_.
integer_output(N, N)          :- N mod 3 #\= 0, N mod 5 #\= 0.

This yields:

?- integer_output(15, O).
O = 'FizzBuzz' ;
false.

The other test cases still work as expected.

Now, using this relation as a building block, it is easy to lift it to lists of integers, using the meta-predicate maplist/3:

fizz_buzz(Ls) :-
        numlist(1, 100, Ls0),
        maplist(integer_output, Ls0, Ls).

Sample query and answer:

?- fizz_buzz(Ls).
Ls = [1, 2, 'Fuzz', 4, 'Buzz', 'Fuzz', 7, 8, 'Fuzz'|...] ;
false.

Note that we are not writing anything ourselves: We are using the Prolog toplevel to do the writing for us, and reason about arguments.

The advantage is clear: We can again write test cases for such a predicate. For example, we expect the following to hold, and it does:

?- Ls = [1,2|_], fizz_buzz(Ls).
Ls = [1, 2, 'Fuzz', 4, 'Buzz', 'Fuzz', 7, 8, 'Fuzz'|...] .

So far, everything is completely pure and usable in all directions. I leave formatting such solutions as you want as an easy exercise.

If your Prolog system does not provide numlist/3, you can use bagof/3 to obtain the list of integers from 1 to 100 like this:

?- bagof(L, (L in 1..100,indomain(L)), Ls).
Ls = [1, 2, 3, 4, 5, 6, 7, 8, 9|...].

Thus, bagof/3 can be useful for this task, but I cannot recommend to use it for side-effects.

于 2017-04-02T23:05:25.300 に答える
1

aggregate(count, fb(X), C) は解をカウントできますが、bagof に基づいているため、要素をカウントするためだけにリストを作成します。次に、この@falseの回答から、call_nth/2より前の再利用可能な「ビルディングブロック」を作成しました

:- meta_predicate count_solutions(0, ?).

count_solutions(Goal, C) :-
    State = count(0, _), % note the extra argument which remains a variable
    (   Goal,
        arg(1, State, C1),
        C2 is C1 + 1,
        nb_setarg(1, State, C2),
        fail
    ;   arg(1, State, C)
    ).

「適用可能な」コードは

:- use_module(uty, [count_solutions/2]).

fizzbuzz :- forall( between(1, 100, X), fizzbuzz(X) ).
fizzbuzz(X) :-
    ( count_solutions(fb(X), 0) -> write(X) ; true ), nl.

fb(X) :- X rem 3 =:= 0, write('Fizz').
fb(X) :- X rem 5 =:= 0, write('Buzz').
于 2013-01-11T14:29:29.253 に答える
1

一種のパターンマッチングを使用できます:

fizzbuzz :-
    forall( between(1, 100, X), fizzbuzz(X) ).
fizzbuzz(X) :-
    0 is X rem 15,
    format('~w FizzBuzz~n', [X]).

fizzbuzz(X) :-
    0 is X rem 5,
    format('~w Buzz~n', [X]).

fizzbuzz(X) :-
    0 is X mod 3,
    format('~w Fizz~n', [X]).

fizzbuzz(X) :-
    write(X), nl.
于 2013-01-11T08:15:11.160 に答える
0

bagof/3副作用のために乱用して、そこでやめるのはなぜですか?循環リストを悪用することもできます:

fizzbuzz :-
        Fizz = [fail,fail,write('Fizz')|Fizz],
        Buzz = [fail,fail,fail,fail,write('Buzz')|Buzz],
        fb(1, 100, Fizz, Buzz).

fb(N, N, _, _) :- !.
fb(N, Last, [F|Fs], [B|Bs]) :-
        (       bagof(_, ( F ; B ), _)
        ->      true
        ;       write(N)
        ),
        nl,
        succ(N, N1),
        fb(N1, Last, Fs, Bs).
于 2016-12-01T13:09:44.330 に答える
0

ええと、あなたはすでにそれを使用しています。forall/2:

write_fb(X) :-
    forall(fb(X), true).

または、問題の表現を変更できます。

write_fb(X) :-
  (X rem 3 =:= 0 -> write('Fizz') ; true),
  (X rem 5 =:= 0 -> write('Buzz') ; true).

もちろん、この場合、bagof/3生成されるリストは非常に小さいため、 and friends を使用しても問題ありません。

于 2013-01-11T11:09:18.337 に答える
0

私...私は次のようなことをします:

fizzbuzz( X , Y ) :-
  X =< Y ,
  R3 is X % 3 ,
  R5 is X % 5 ,
  map( R3 , R5 , X , V ) ,
  write(V) ,
  nl ,
  X1 is X+1 ,
  fizzbuzz( X1 , Y )
  .

map( 0 , 0 , _ , fizzbuzz ) :- ! .
map( 0 , _ , _ , fizz     ) :- ! .
map( _ , 0 , _ , buzz     ) :- ! .
map( _ , _ , X , X        ) :- ! .
于 2013-01-15T18:46:34.207 に答える
0

私の尊敬する同僚が答えましたが、「間」が必要です。

ソリューションを収集する必要はありません。その点で、あなたの直感は正しいです。あなたは次のようなものから始めたと思います

fizzbuzz :-  between(1, 100, N),
             fb(N).


fb(N) :-  N rem 5 =:= 0,  
          N rem 3 =:= 0,
          write(fizzbuzz).

fb(N) :-  N rem 5 =:= 0,    
          write(buzz).

fb(N) :-  N rem 3 =:= 0,
          write(fizz).

fb(N) :-  write(N).

これに関する問題は、fb が「不動」ではないことです。複数のソリューションを提供するとは思わないでしょうが、実際にはそうです。たとえば、fb(15) はすべての fb ルールと統合されます。

解決策は、カットを使用して、強制的に固定することです。

fizzbuzz :-  between(1, 100, N),
             fb(N).


fb(N) :-  N rem 5 =:= 0,  
          N rem 3 =:= 0,
          !,
          write(fizzbuzz).

fb(N) :-  N rem 5 =:= 0,
          !,    
          write(buzz).

fb(N) :-  N rem 3 =:= 0,
          !,
          write(fizz).

fb(N) :-  write(N).
于 2013-01-12T18:37:39.170 に答える
-1

/*

問題文

すべての各数値について解く:

1 から 100 までの各数値を生成し、 しかし、各数値を各メッセージに変換し、 各数値が 3 の倍数の場合、各メッセージに 'Fuzz' 、 各数値が 5 の倍数の場合、各メッセージ「Buzz」に 各番号が両方の場合、各メッセージ「FizzBu​​zz」に、 次に、各メッセージを出力します。

*/

/*

プログラム

*/

    :- use_module(library(clpfd)) .

    :- op(10'1,'yfx','forall') .
    :- op(10'1,'fy','once') .

    (
        program
    )
    :-
    (
        (
            between(1,100,NUMBER)
        )
        forall
        (
            once
            (
                (
                    MESSAGE='FizzBuzz'
                    ,
                    NUMBER rem 10'3 #= 10'0
                    ,
                    NUMBER rem 10'5 #= 10'0
                )
                |
                (
                    MESSAGE='Buzz'
                    ,
                    NUMBER rem 10'5 #= 10'0
                )
                |
                (
                    MESSAGE='Fuzz'
                    ,
                    NUMBER rem 10'3 #= 10'0
                )
                |
                (
                    MESSAGE=_
                )
            )
            ,
            once
            (
                (
                    nonvar(MESSAGE)
                    ,
                    writeq(NUMBER)
                    ,
                    nl
                    ,
                    writeq(MESSAGE)
                    ,
                    nl
                )
                |
                (
                    true
                )
            )
        )
    )
    .

/*

テスト

swi prolog でテスト済み。*/

    ?- program .

    3
    'Fuzz'
    5
    'Buzz'
    6
    'Fuzz'
    9
    'Fuzz'
    10
    'Buzz'
    12
    'Fuzz'
    15
    'FizzBuzz'

    { .e.t.c. | ... as expected ... |  .e.t.c. }

    99
    'Fuzz'
    100
    'Buzz'
    true.

    ?- 

*/

于 2017-04-02T18:12:13.000 に答える