Предположим, я хочу представить целые числа так: integer:Sign:[FirstDigit,SecondDigit,...]. Например, 42 будет представлен как integer:positive:[4,2].

Мне нужен предикат, который генерирует значение целого числа на основе этого представления и наоборот.

Вот что я придумал:

integer_value_('integer':Sign:[H],E) :-
    H in 0..9,
    (
        Sign = 'positive',
        E #= H
        ;
        Sign = 'negative',
        E #= -H
    ).
integer_value_('integer':Sign:[H,I|T],E) :-
    H in 0..9,
    length([I|T],L),
    (
        Sign = 'positive',
        E #= F + H * 10^L
        ;
        Sign = 'negative',
        E #= F - H * 10^L
    ),
    integer_value_('integer':Sign:[I|T],F).

Это работает, как ожидалось. Однако у него есть неудачное свойство принимать такие вещи, как integer:positive:[0,1], то есть ведущие нули в начале списка. Это особенно проблематично, когда я перечисляю все возможные целые числа с помощью integer_value_(I,J), label([J]).: также появляются числа с ведущими нулями.

Затем я попытался исправить это, используя integer_value_ только для всех цифр, кроме первой, и используя integer_value для первой (имея в виду, что нам нужно учесть, что 0 представлен списком, содержащим только 0):

integer_value('integer':Sign:[H],E) :-
    abs(E) #< 10,
    abs(E) #> -1,
    integer_value_('integer':Sign:[H],E).
integer_value('integer':Sign:[H,I|T],E) :-
    H in 1..9,
    length([I|T],L),
    (
        Sign = 'positive',
        E #= F + H * 10^L
        ;
        Sign = 'negative',
        E #= F - H * 10^L
    ),
    integer_value_('integer':Sign:[I|T],F).

Однако теперь он не ведет себя должным образом. Например, integer_value(I,-19). возвращает I = integer:negative:[1, 9], но если мы запрашиваем другой ответ, Пролог переходит в бесконечный цикл по непонятным мне причинам (он должен сказать false или уже знаю, что других ответов нет) .

Эта проблема не возникает с "противоположным" запросом integer_value(integer:negative:[1,9],Z)., который возвращает Z = 19, а затем false, и не возникает, когда оба аргумента являются переменными (он перечисляет числа правильно, без начальных нулей), то есть удивительно для меня.

Есть идеи, что происходит за бесконечный цикл, и есть ли простой способ его исправить?

5
Fatalize 21 Апр 2016 в 20:32

2 ответа

Лучший ответ

Чтобы увидеть проблему, достаточно взглянуть на крошечную часть вашей программы. Фактически, следующий достаточно:

integer_value('integer':Sign:[H],E) :- false,
    abs(E) #< 10,
    abs(E) #> -1,
    integer_value_('integer':Sign:[H],E).
integer_value('integer':Sign:[H,I|T],E) :-
    H in 1..9,
    length([I|T],L), false,
    (   Sign = 'positive',
        E #= F + H * 10^L
        ;
        Sign = 'negative',
        E #= F - H * 10^L
    ),
    integer_value_('integer':Sign:[I|T],F).

L встречается здесь впервые, поэтому возможна любая длина. Вам придется как-то изменить целевую длину.

5
false 21 Апр 2016 в 18:07

Мне удалось решить мою проблему, используя этот другой ответ, на который указал @ ложный

Одна из уловок состоит в том, чтобы выбрать знак числа на последнем шаге, чтобы при переборе возможных целых чисел мы получали чередующиеся ответы между положительными и отрицательными числами: после достижения 9 (1 цифра) оно будет объединено с -9, затем -8 и т. д. После -1 оно объединится с 10, 11 и т. д. После 99 оно объединится с -99, -98 и т. д. Вы поняли.

integer_value('integer':Sign:I,E) :-
    integer_value('integer':Sign:I,0,E,E).

integer_value('integer':Sign:[H],N0,N,M) :-
    H in 0..9,
    N1 #= H + N0 * 10,
    abs(M) #>= abs(N1),
    integer_value_('integer':Sign:[],N1,N,M).
integer_value('integer':Sign:[H,I|T],N0,N,M) :-
    H in 1..9,
    N1 #= H + N0 * 10,
    abs(M) #>= abs(N1),
    integer_value_('integer':Sign:[I|T],N1,N,M).

integer_value_('integer':Sign:[],N0,N,_) :-
    (
        Sign = 'positive',
        N #= N0
        ;
        Sign = 'negative',
        N #\= 0,
        N #= - N0
    ).
integer_value_('integer':Sign:[H],N0,N,M) :-
    H in 0..9,
    N1 #= H + N0 * 10,
    abs(M) #>= abs(N1),
    integer_value_('integer':Sign:[],N1,N,M).
integer_value_('integer':Sign:[H,I|T],N0,N,M) :-
    H in 0..9,
    N1 #= H + N0 * 10,
    abs(M) #>= abs(N1),
    integer_value_('integer':Sign:[I|T],N1,N,M).
3
Community 23 Май 2017 в 11:58