【问题标题】:List of integers and infinite loop in Prolog CLPFDProlog CLPFD 中的整数列表和无限循环
【发布时间】:2016-04-21 17:32:39
【问题描述】:

假设我想像这样表示整数: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],但如果我们要求另一个答案,Prolog 会因为我不明白的原因进入无限循环(它应该说错误,或者已经知道没有其他答案)。

“相反”查询integer_value(integer:negative:[1,9],Z). 不会发生此问题,它返回Z = 19 然后返回false,当两个参数都是变量时也不会发生(它正确枚举数字,没有前导零),这令人惊讶对我来说。

知道这个无限循环是怎么发生的吗?是否有一种简单的方法可以修复它?

【问题讨论】:

  • +1 是一个非常有趣且合适的 CLP(FD) 约束用例!关于单引号,我有一个小评论:对于所有不需要此类引号的原子,您可以省略 ',例如 positivenegativeinteger 等。您可以直接将所有这些原子写下来,例如Sign = positiveSign = negativeinteger:Sign:[I|T]
  • @mat 我知道,但由于我不是 Prolog 程序员,我发现拥有这样的原子非常难看:p

标签: prolog infinite-loop clpfd failure-slice


【解决方案1】:

要查看问题,只需查看程序的一小部分即可。事实上,下面的 就足够了:

integer_value('integer':Sign:[H],E) :- false, abs(E) #, abs(E) #> -1, integer_value_('integer':Sign:[H],E)。 integer_value('integer':Sign:[H,I|T],E) :- 1..9 中的 H, 长度([I|T],L),( 符号 = '阳性', E #= F + H * 10^L ; Sign = '否定', E #= F - H * 10^L ), integer_value_('integer':Sign:[I|T],F)

L 第一次出现在这里,所以任何长度都是可能的。 您将不得不以某种方式修改长度目标。

【讨论】:

  • @Fatalize:请参阅this 了解相关问题。
【解决方案2】:

我设法使用@false 指出的this other answer 解决了我的问题

其中一个问题是将数字的符号确定为最后一步,这样当迭代可能的整数时,我们会在正数和负数之间得到交替的答案:在达到 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).

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多