Пролог, дайте N и найдите все числа, не делимые на 3 и 5, и эти числа должны быть меньше N - PullRequest
1 голос
/ 16 марта 2020

У меня проблема с поиском решения проблемы.
Предикат Divisible / 2 проверяет, делится ли число N на одно из чисел в списке

divisible([H|_],N) :- N mod H =:= 0.
divisible([H|T],N) :- N mod H =\= 0, divisible(T,N).

Мне нужно построить предикат, который найдет число пример ввода / вывода:

?- find(5, [3,5],Num).
output is : 
Num = 4; Num = 2; Num = 1. False
Here N is 5 and list of number is [3,5]

Текущий код:

findNum(1, LN, Num) :- \+ divisible(LN,1),
                        Num is 1.

findNum(Rank, LN, Num) :- Rank > 1,
                        Num1 is Rank - 1,
                        ( \+ divisible(LN,Num1) -> Num is Num1;
                        findNum(Num1,LN, Num) ).

Печатает только Num = 4; Он никогда не печатает 2 и 1 по некоторым причинам
И я не уверен, где что идет не так .. Любая помощь приветствуется ...

Ответы [ 2 ]

2 голосов
/ 16 марта 2020

Выполнено тремя различными способами


  1. Использование рекурсии
find_rec(0,_,[]) :- !.
find_rec(N0,Possible_divisors,[N0|Successful_divisors]) :-
    divisible(Possible_divisors,N0),
    N is N0 - 1,
    find_rec(N,Possible_divisors,Successful_divisors).
find_rec(N0,Possible_divisors,Successful_divisors) :-
    \+ divisible(Possible_divisors,N0),
    N is N0 - 1,
    find_rec(N,Possible_divisors,Successful_divisors).

Пример выполнения

?- find_rec(5,[3,5],Num).
Num = [5, 3] ;
false.

Использование раздела
find_par(N,Possible_divisors,Successful_divisors) :-
    findall(Ns,between(1,N,Ns),List),
    partition(partition_predicate(Possible_divisors),List,Successful_divisors,_).

partition_predicate(L,N) :-
    divisible(L,N).

Пример выполнения

?- find_par(5,[3,5],Num).
Num = [3, 5].

Использование условного (->;)
find_con(0,_,[]) :- !.
find_con(N0,Possible_divisors,Result) :-
    (
        divisible(Possible_divisors,N0)
    ->
        Result = [N0|Successful_divisors]
    ;
        Result = Successful_divisors
    ),
    N is N0 - 1,
    find_con(N,Possible_divisors,Successful_divisors).

Пример выполнения

?- find_con(5,[3,5],Num).
Num = [5, 3].

Было бы неплохо увидеть некоторые тестовые случаи для делимых / 2 к быстро понять, как это работает.

:- begin_tests(divisible).

divisible_test_case_generator([13,1],13).
divisible_test_case_generator([20,10,5,4,2,1],20).
divisible_test_case_generator([72,36,24,18,12,9,8,6,4,3,2,1],72).
divisible_test_case_generator([97,1],97).
divisible_test_case_generator([99,33,11,9,3,1],99).

test(1,[nondet,forall(divisible_test_case_generator(List,N))]) :-
    divisible(List,N).

:- end_tests(divisible).

Запуск тестов

?- make.
% c:/users/groot/documents/projects/prolog/so_question_177 compiled 0.00 sec, 0 clauses
% PL-Unit: divisible ..... done
% All 5 tests passed
true.

Некоторые отзывы о вашем коде.

  1. Обычно форматирование предикат начинает новую строку после :-
  2. При использовании оператора ; лучше поместить его в строку отдельно, чтобы было очевидно, что многие программисты часами искали ошибки, потому что ; был замечен как , и неправильно понят.
findNum(1, LN, Num) :-
    \+ divisible(LN,1),
    Num is 1.

findNum(Rank, LN, Num) :-
    Rank > 1,
    Num1 is Rank - 1,
    (
        \+ divisible(LN,Num1)
    ->
        Num is Num1
    ;
        findNum(Num1,LN, Num)
    ).

Где ошибка в вашем коде здесь для <true case>

    ->
        Num is Num1

вы не вернулись к следующему значению, как вы сделали для <false case>

    ;
        findNum(Num1,LN, Num)
1 голос
/ 16 марта 2020

Попробуйте изменить предикат findNum на:

findNum(Rank, LN, Num) :- Rank > 1,
                          Num1 is Rank - 1,
                          \+ divisible(LN,Num1) -> Num is Num1.
findNum(Rank, LN, Num) :-  Rank > 1, 
                           Num1 is Rank - 1, 
                           findNum(Num1,LN, Num).

Для меня это дает запрошенный ответ.

...