在Prolog中将Bytes列表转换为Integer

时间:2017-03-17 16:04:09

标签: prolog byte-shifting

我试图将字节列表转换为相应的整数。我想绑定到第二个变量,因为文档评论会建议。我把它打印成临时黑客。

?- bytes_int([42, 121, 56], N).
3701034

它正在打印正确的值,但我不确定为什么它没有绑定到第二个变量以供进一步使用。我可以做些什么改进来实现这个目标?

style="@style/Theme.blue"

2 个答案:

答案 0 :(得分:4)

我想通过@WillemVanOnsem对这个很好的答案进行一些扩展。这是他发布的版本:

bytes_int(BytesList, R) :-
    bytes_int(BytesList, 0, 0, R).

bytes_int([], _, R, R).
bytes_int([H|T], S0, R0, R) :-
    R1 is R0 + H << S0,
    S1 is S0 + 8,
    bytes_int(T, S1, R1, R).

所以,让我们尝试一些问题。首先,问题中提供的测试用例:

?- bytes_int([42,121,56], I).
I = 3701034.

很好,很有效。因此,让我们尝试一个更一般的情况,我查询三个字节,但不指定它们的值:

?- bytes_int([A,B,C], I).
ERROR: Arguments are not sufficiently instantiated

不幸的是,这不起作用:使用低级整数算法排除了这种更一般的用例。这种限制延续到所有更一般的查询,即使它们有时可能产生解决方案:

?- bytes_int(Bs, I).
Bs = [],
I = 0 ;
ERROR: Arguments are not sufficiently instantiated

至少 second 参数完全实例化的更具体的情况如何:

?- bytes_int(Bs, 3701034).
ERROR: Arguments are not sufficiently instantiated

不,在这种情况下,谓词也无法提供任何答案。

嗯,这一切都是可以预料的,更迫切需要的程序员会在这一点上说。你觉得我们怎么样? 声明性程序员?

因此,我对以上版本进行了以下直接更改,以使其更具说明性:我将(is)/2替换为(#=)/2。是的,我知道,40年前的这些书不会这样做,但仍然让我们这样做,看看我们得到的回报:

bytes_int(BytesList, R) :-
    bytes_int(BytesList, 0, 0, R).

bytes_int([], _, R, R).
bytes_int([H|T], S0, R0, R) :-
    R1 #= R0 + H << S0,
    S1 #= S0 + 8,
    bytes_int(T, S1, R1, R).

让我们马上开始使用最常见的查询,我们只需要任何答案

?- bytes_int(Bs, I).
Bs = [],
I = 0 ;
Bs = [_15310],
I#=_15310<<0 ;
Bs = [_16050, _16056],
_16086#=_16050<<0,
_16086+_16118#=I,
_16118#=_16056<<8 .

尼斯!所以我们现在得到更多答案。事实上,我们现在可以测试不确定:

?- bytes_int(Bs, I), false.
nontermination

此查询终止的事实令人放心:由于谓词应该适用于任意长度的列表,因此不得普遍终止。

让我们再试几个案例:

?- bytes_int([A,B,C], I).
_3674#=A<<0,
_3674+_3706#=_3700,
_3706#=B<<8,
_3700+_3754#=I,
_3754#=C<<16.

上面就是这种情况:我们现在得到所有答案的符号表示,这有时非常有用。

特别是,原始的测试用例当然自然地作为更通用查询的特例

?- bytes_int([42,121,56], I).
I = 3701034.

这与以前完全一样。

所以还有一个案例需要测试:实例化第二个参数怎么样?

?- bytes_int(Bs, 3701034).
Bs = [_5164],
3701034#=_5164<<0 ;
Bs = [_6056, _6062],
_6080#=_6056<<0,
_6080+_6112#=3701034,
_6112#=_6062<<8 .

这至少会产生答案。我把它留作练习来改善这一点。回想一下,到目前为止,我所做的只是将(is)/2替换为(#=)/2,仅这一点就已经显着增加了这种关系的普遍性。根据您的Prolog系统,您当前可能需要导入库以从此声明性整数算法中受益。

答案 1 :(得分:3)

出现的第一个问题是:

bytes_int(BytesList, _) :-
    bytes_int(BytesList, 0, 0).

不会在调用中将结果(_)与变量绑定。您可能想要使用:

bytes_int(BytesList, R) :-
    bytes_int(BytesList, 0, R).

接下来,您不使用累加器。这里的累加器可以存储你迄今为止获得的累积和。所以我们声明了一个额外的参数:

bytes_int(BytesList, R) :-
    bytes_int(BytesList, 0, 0, R).

现在我们设置了真正的算法。如果我们到达列表的末尾。到目前为止累积的总和是最终的结果:

bytes_int([], _, R, R).

在另一种情况下,我们只需更新累积总和,更新班次大小,并对列表的 tail 执行递归:

bytes_int([H|T], S0, R0, R) :-
    R1 is R0 + H << S0,
    S1 is S0 + 8,
    bytes_int(T, S1, R1, R).

现在把它们放在一起:

bytes_int(BytesList, R) :-
    bytes_int(BytesList, 0, 0, R).

bytes_int([], _, R, R).
bytes_int([H|T], S0, R0, R) :-
    R1 is R0 + H << S0,
    S1 is S0 + 8,
    bytes_int(T, S1, R1, R).

这会产生:

?- bytes_int([42, 121, 56], N).
N = 3701034.