我试图将字节列表转换为相应的整数。我想绑定到第二个变量,因为文档评论会建议。我把它打印成临时黑客。
?- bytes_int([42, 121, 56], N).
3701034
它正在打印正确的值,但我不确定为什么它没有绑定到第二个变量以供进一步使用。我可以做些什么改进来实现这个目标?
style="@style/Theme.blue"
答案 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.