Генерация последовательности високосных лет с использованием clpfd - PullRequest
1 голос
/ 26 мая 2019

Код ниже проверяет, является ли данный год високосным или нет.Арифметика формулируется с использованием декларативной целочисленной арифметики из библиотеки clpfd , чтобы запрос можно было выполнить в «обратном порядке» для генерации последовательности високосных лет.Есть два варианта кода, изменяющие тест на то, является ли год столетием или нет, но поведение каждого варианта отличается, и ни один из них не работает правильно.

Я хочу создать последовательность високосных летс 1990 по 2005 год. Для этого я ввожу запрос Year in 1990..2005,leap_year1(Year),indomain(Year). (или leap_year2 для проверки второго варианта).

Код, который я написал (с использованием SWI-Prolog 8.0.2):

:- use_module(library(clpfd)).

leap_year1(Year) :-
  Year #> 0,
  Year mod 4 #= 0,
  (
    Year mod 100 #= 0 ->
      Year mod 400 #= 0      % 2000 is a leap year, 1900 is not
    ;
      true                   % Not a century
  ).

leap_year2(Year) :-
  Year #> 0,
  Year mod 4 #= 0,
  (
    Year mod 100 #\= 0 ->
      true                   % Not a century
    ;
      Year mod 400 #= 0      % 2000 is a leap year, 1900 is not
  ).

Результаты запросов (SWI-Prolog 8.0.2):

?- Year in 1990..2005,leap_year1(Year),indomain(Year).
Year = 2000.

?- Year in 1990..2005,leap_year2(Year),indomain(Year).
Year = 1992 ;
Year = 1996 ;
Year = 2004.

Как видите, ни один запрос правильно не генерирует полную последовательность скачкагоды (1992, 1996, 2000, 2004).

Кроме того, следующие запросы примерно в 1900 году также дают аномальные результаты (комментарии добавлены):

% Verify that 1900 is not a leap year
?- not(leap_year1(1900)), not(leap_year2(1900)).
true.

% Verify that 1892, 1896, 1904 are leap years
?- leap_year1(1892), leap_year1(1896), leap_year1(1904).
true.

?- leap_year2(1892), leap_year2(1896), leap_year2(1904).
true.

% No solutions found (!)
?- Year in 1890..1905,leap_year1(Year),indomain(Year).
false.

% Solutions found as expected (complete result)
?- Year in 1890..1905,leap_year2(Year),indomain(Year).
Year = 1892 ;
Year = 1896 ;
Year = 1904.

Есть ли объяснение этомуповедение?Как мне изменить leap_year(), чтобы он генерировал полную последовательность?

Обновление: (больше?) Завершено, но не понимаю, почему (!)

Разделение чека на века улучшает полноту запроса:

leap_year_century_fix(Year) :-
  Year mod 100 #\= 0.

leap_year_century_fix(Year) :-
  Year mod 100 #= 0,
  Year mod 400 #= 0.      % 2000 is a leap year, 1900 is not

leap_year3(Year) :-
  Year #> 0,
  Year mod 4 #= 0,
  leap_year_century_fix(Year).

Теперь:

?- Year in 1890..1905,leap_year3(Year),indomain(Year).
Year = 1892 ;
Year = 1896 ;
Year = 1904 ;
false.

?- Year in 1990..2015,leap_year3(Year),indomain(Year).
Year = 1992 ;
Year = 1996 ;
Year = 2004 ;
Year = 2008 ;
Year = 2012 ;
Year = 2000.

1 Ответ

3 голосов
/ 26 мая 2019

Выразите это просто, используя и и или , и используйте reification для или :

leap(Y) :-
    Y #> 0,
    Y mod 4 #= 0,
    (Y mod 100 #\= 0) #\/ (Y mod 400 #= 0).
...