Описание чисел палиндрома на самом деле хорошая задача для CLP (FD) и DCG. Сначала давайте опишем, как выглядят цифры числа палиндрома:
:- use_module(library(clpfd)).
palindromedigits(Digits) :- % Digits are palindrome digits if
Digits ins 0..9, % they are between 0 and 9
Digits = [H|_], % and the first digit...
H #\= 0, % ... is different from 0
phrase(palindrome, Digits). % and they form a palindrome
palindrome --> % a palindrome is
[]. % an empty list
palindrome --> % or
[_]. % a list with a single element
palindrome --> % or
[A], % an element A
palindrome, % followed by a palindrome
[A]. % followed by an element A
Чтобы проверить, является ли число палиндромом, вы можете превратить его в список цифр, и palindromedigits/1
должен удерживать этот список. Чтобы сгенерировать такие числа, вы могли бы использовать length/2
для описания списков всех возможных длин, для этих списков снова должно быть palindromedigits/1
, а цифры должны быть умножены на соответствующие им степени десяти и суммированы. Поскольку начальные нули исключаются palindromedigits/1
, вам нужно добавить факт для 0
, если вы хотите включить его в числа палиндрома. Это может выглядеть примерно так:
palindromenumber(0). % 0 is a palindromenumber
palindromenumber(PN) :- % rule for testing numbers
number(PN), % succeeds if PN is a number
number_codes(PN,C), % C is a list of codes corresponding to the digits
maplist(plus(48),Digits,C), % codes and digits are off by 48
palindromedigits(Digits). % Digits is a palindrome
palindromenumber(PN) :- % rule for generating numbers
var(PN), % succeeds if PN is a variable
length(Digits,_), % Digits is a list of length 0,1,2,...
palindromedigits(Digits), % Digits is a palindrome
digits_number_(Digits,PN,1,0), % Digits correspond to the number PN
label(Digits). % labels the list Digits with actual numbers
Обратите внимание, что коды, соответствующие цифрам, выключены на 48, следовательно, цель с maplist/3
, например ::
?- number_codes(123,C), maplist(plus(48),Digits,C).
C = [49, 50, 51], % <- the codes
Digits = [1, 2, 3]. % <- the actual digits
Предикат digits_number_/4
довольно прост. Он вызывается с 1
как начальная степень десяти и 0
как начальный накопитель для числа. Цифры умножаются на степень десяти, соответствующую их положению в числе, и затем добавляются в аккумулятор. Если список цифр пуст, аккумулятор содержит номер, соответствующий списку цифр.
digits_number_([],PN,_,PN).
digits_number_([D|Ds],PN,P,Acc) :-
Acc1 #= Acc + D*B,
P10 #= P*10,
digits_number_(Ds,PN,P10,Acc1).
Обратите внимание, что не имеет значения, что цифры умножаются на степени в обратном порядке, потому что это число палиндрома.
Теперь вы можете запросить номера палиндрома:
?- palindromenumber(PN).
PN = 0 ;
PN = 1 ;
PN = 2 ;
.
.
.
PN = 33 ;
PN = 44 ;
PN = 55 ;
.
.
.
PN = 666 ;
PN = 676 ;
PN = 686 ;
.
.
.
PN = 7667 ;
PN = 7777 ;
PN = 7887
.
.
.
Или вы можете проверить, является ли число палиндромом:
?- palindromenumber(121).
true ;
false.
?- palindromenumber(123).
false.
?- palindromenumber(12321).
true ;
false.
EDIT
Чтобы ответить на вопрос в своем комментарии, вы можете сделать это, описав отношение между такой последовательностью и ее длиной. Таким образом, у вас будет предикат с арностью два вместо арности один. Давайте дадим ему хорошее описательное имя, скажем firstN_palindromicnumbers/2
. Фактическая реализация описывается предикатом с дополнительным аргументом, который содержит текущего кандидата, подлежащего проверке. Поскольку вы хотите начать последовательность с 1, это будет аргумент firstN_palindromicnumbers_ / 3 будет вызываться с:
firstN_palindromicnumbers(N,PNs) :-
firstN_palindromicnumbers_(N,PNs,1). % sequence starts with 1
Аргумент, содержащий кандидатов, будет увеличен на 1 посредством рекурсий, тогда как первый аргумент, N
, будет уменьшаться каждый раз, когда кандидат оказывается действительным палиндромным числом. Таким образом, предикат в конечном итоге заканчивается N
, равным 0
, пустым списком и кандидатом, который нас не волнует. Это будет базовый случай. В противном случае заголовок списка - это наименьшее число палиндромов в (оставшейся части) последовательности. Вы можете повторно использовать цели number_codes/2
и maplist/3
сверху, чтобы описать список цифр, соответствующий текущему кандидату, и DCG palindrome//0
, чтобы указать, что цифры должны быть палиндромом. Другие цели из предиката palindromedigits/1
не понадобятся, так как кандидаты будут 1,2,3, ..., таким образом, состоящие из (по крайней мере) одной цифры (цифр) от 0 до 9 без начальных нулей. Вы можете выразить это в Прологе так:
firstN_palindromicnumbers_(0,[],_C). % base case
firstN_palindromicnumbers_(N1,[C0|PNs],C0) :- % case: C0 is a palindrome
N1 #> 0, % sequence is not of desired length yet
number_codes(C0,Codes),
maplist(plus(48),Digits,Codes),
phrase(palindrome, Digits), % digits form a palindrome
N0 #= N1-1, % sequence of length N1-1 has to be described yet
C1 #= C0+1, % C1 is the next candidate
firstN_palindromicnumbers_(N0,PNs,C1). % PNs is the rest of the sequence
firstN_palindromicnumbers_(N1,PNs,C0) :- % case: C0 ain't a palindrome
N1 #> 0, % sequence is not of desired length yet
number_codes(C0,Codes),
maplist(plus(48),Digits,Codes),
\+ phrase(palindrome, Digits), % digits don't form a palindrome
C1 #= C0+1, % C1 is the next candidate
firstN_palindromicnumbers_(N1,PNs,C1). % PNs is the rest of the sequence
Теперь вы можете запросить предикат для последовательности палиндромных чисел заданной длины (обратите внимание, что с SWI-Prolog вам, возможно, придется нажать w , чтобы увидеть весь список):
?- firstN_palindromicnumbers(15,PNs).
PNs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] [write] % <- hit the w key
PNs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66] ;
false.
?- firstN_palindromicnumbers(25,PNs).
PNs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161] ;
false.
Вы также можете использовать предикат, чтобы проверить, является ли данный список последовательностью первых N
палиндромных чисел:
?- firstN_palindromicnumbers(N,[1,2,3,4,5]).
N = 5 ;
false.
?- firstN_palindromicnumbers(N,[0|_]).
false.
?- firstN_palindromicnumbers(N,[1,2,3,4,11]).
false.
И самый общий запрос также дает ожидаемые ответы:
?- firstN_palindromicnumbers(N,PNs).
N = 0,
PNs = [] ;
N = 1,
PNs = [1] ;
N = 2,
PNs = [1, 2] ;
N = 3,
PNs = [1, 2, 3] ;
.
.
.