Prolog简洁的foreach

时间:2019-03-29 01:00:18

标签: scope prolog

我正在尝试以简洁的方式使用Prolog的foreach。例如,假设我有一个列表,并且想查看它是否对称。考虑以下代码:

isSymmetricList1(Size, List) :-
  foreach(between(1, Size, I1), (
    I2 is Size - I1 + 1,
    nth1(I1, List, Elem),
    nth1(I2, List, Elem)
  )).

isSymmetricList2Sub1(Size, List, I1) :-
  I2 is Size - I1 + 1,
  nth1(I1, List, Elem),
  nth1(I2, List, Elem).

isSymmetricList2(Size, List) :-
  foreach(between(1, Size, I1), (
    isSymmetricList2Sub1(Size, List, I1)
  )).

考虑一些测试用例:

?- isSymmetricList1(4,[0,0,0,0]).
false.

?- isSymmetricList1(4,[0,0,0,1]).
false.

?- isSymmetricList1(4,[1,2,2,1]).
false.

?- isSymmetricList1(4,List).
false.

?- isSymmetricList2(4,[0,0,0,0]).
true.

?- isSymmetricList2(4,[0,0,0,1]).
false.

?- isSymmetricList2(4,[1,2,2,1]).
true.

isSymmetricList1失败,即使对于对称列表也仅返回false。我对这是为什么的理解是,I2实际上在isSymmetricList1的范围内,因此在所有4个迭代中只能有一个值,这与它的目的不一致。在isSymmetricList2中,我将I2绑定到isSymmetricList2Sub1的范围内,将isSymmetricList2绑定到foreach的范围之外,从而有效地将其绑定到多个值(因为它们是不同的实例) ,或者说)。

这有效。但是,这会导致混乱。我真的不希望在我的命名空间周围出现一堆子规则。 I know there's a lambda module,也许这只是我必须使用的,但是我很想知道是否严格要求使用它。 (而且,它的语法有些棘手。)是否可以使用 module.exports = () => ({ settings: require('./mockData/settings.json'), options: require('./mockData/options.json'), filter: require('./mockData/filter.json'), }); 来执行比单个目标更复杂的操作,而无需在名称空间中添加额外的规则,也无需导入额外的模块?例如,如果您可以声明本地规则,或限制绑定的范围。

2 个答案:

答案 0 :(得分:2)

请注意,未解决您对foreach/2使用情况的担忧。

您对对称列表的定义不只是证明一个列表及其反向列表是相同的吗?

symmetric_list(List) :-
    reverse(List, List).

通话示例:

|?- symmetric_list([0,0,0,0]).
yes

| ?- symmetric_list([0,0,0,1]).
no

| ?- symmetric_list([1,2,2,1]).
yes

您还可以生成对称列表:

| ?- symmetric_list(List).
List = [] ;
List = [_16114] ;
List = [_16114, _16114] ;
List = [_16114, _16126, _16114] ;
List = [_16114, _16126, _16126, _16114]
...

关于注释中提到的foreach/2forall/2谓词以及其他答案之一,它们在不同的场景中使用。 forall/2谓词是实现 generate and test 循环的事实上的标准谓词。对于第一个参数的所有解决方案,第二个参数为true时,forall/2目标为true。这就隐式地需要回溯到生成器参数中,因此需要使用否定来确定谓词的标准定义:

forall(Generate, Test) :- \+ (Generate, \+ Test).

使用否定会导致forall/2不返回任何绑定。当这些绑定是必需的时,foreach/2谓词是一个可能的选择。不想在此重新表述有关谓词的文档,建议您参考Prolog系统的文档(例如SWI-Prolog和SICStus Prolog)来提供它。

答案 1 :(得分:1)

最明显的问题是foreach/2并不是您想要的。首先,让我们对您的isSymmetricList1/2进行最小修复,以确保它不会在您的示例中失败:

isSymmetricList1(Size, List) :-
  forall(between(1, Size, I1), (
    I2 is Size - I1 + 1,
    nth1(I1, List, Elem),
    nth1(I2, List, Elem)
  )).

您能发现差异吗?

有了这个定义,我得到:

?- isSymmetricList1(4,[0,0,0,0]).
true.

?- isSymmetricList1(4,[0,0,0,1]).
false.

?- isSymmetricList1(4,[1,2,2,1]).
true.

您似乎确切地理解了foreach/2为何做的事​​情以及失败的原因。我怀疑您也知道使用forall/2可以解决您的“问题”。难题是,我们如何知道何时需要一个,何时需要另一个?我认为我真的不知道答案,所以我们必须等待专家们来向我们俩解释。

相反,我将提提您的问题提出的更易于访问的主题。

您意识到这是一种真正的绕行方式,对吧?首先,您遍历整个列表,比较每对元素两次。然后,nth1/3必须在每次调用列表时从列表的开头开始遍历。

关于“在您的命名空间中浮动的子规则”,这毕竟不是一件坏事。辅助谓词可帮助您考虑关系。它们还为您提供了一个很好的机会,可通过谓词名称记录您的意图。

这有点让我想开始特别谈论命名和camelCase以及您不应该这样做的话题,但这太离题了。