水星:决定论和模式匹配

时间:2011-09-19 05:35:30

标签: pattern-matching mercury

我有半自杀的功能。当我重新编写它以使用模式匹配而不是if语句时,Mercury表示它变得不确定。我想了解原因。

原始代码:

:- pred nth(list(T), int, T).
:- mode nth(in,      in,  out) is semidet.
nth([Hd | Tl], N, X) :- (if N = 0 then X = Hd else nth(Tl, N - 1, X)).

修订后的代码:

:- pred nth(list(T), int, T).
:- mode nth(in,      in,  out) is nondet.
nth([Hd | _], 0, Hd).                             % Case A
nth([_ | Tl], N, X) :- N \= 0, nth(Tl, N - 1, X). % Case B

我习惯于考虑SML中的模式匹配,其中案例A中的0将确保在B的情况下,N不是0. Mercury的工作方式不同吗?即使N为0,情况B也可以被调用吗? (我将N \= 0子句添加到案例B中,希望能使谓词半自由,但这不起作用。)

有没有办法用模式匹配编写这个谓词,这也是半决定的?

1 个答案:

答案 0 :(得分:8)

是的,Mercury模式匹配与SML的工作方式不同。水星对其声明性语义非常严格。具有多个子句的谓词相当于所有实体的分离(以子句头中的统一为模的一些并发症),并且分离的含义不受分离的不同分支的编写顺序的影响。事实上,很少有水星的意义受你写东西的影响(状态变量是我能想到的唯一例子)。

因此,如果不将N \= 0置于正文中,则使用模式匹配的两个子句的谓词是非确定性的。没有什么可以阻止nth(Tl, 0 - 1, X)有效减少nth([_ | Tl], 0, X)

使用N \= 0,您正走上正轨。不幸的是,虽然if A then B else C 逻辑等同于(A, B) ; (not A, C),但水星的确定性推断通常不够智能,无法找出相反的结果。

特别是,水星的决定论推理并不试图弄清楚两个条款是相互排斥的。在这种情况下,它知道N = 0可以成功或失败,具体取决于N的值,N \= 0可以成功或失败,具体取决于N的值。由于它看到谓词成功的两种不同方式,并且它可能失败,因此它必须是不确定的。有一些方法可以向编译器承诺,它的推断性并不是它所能推断的,但在这种情况下,更容易坚持使用if / then / else。

可以使用模式匹配而编译器认为您可以拥有多个解决方案的情况下,当您将单个变量与相同类型的多个不同构造函数进行匹配时。例如:

:- pred some_pred(list[T]) is det.
some_pred([]) :- something.
some_pred([H | T]) :- something_else.

这称为开关。编译器知道列表有两个构造函数(空列表[]或cons单元格[|]),并且给定列表只能是其中之一,因此是一个析取(或者是一个多个子句)谓词)对于两个构造函数都有一个臂必须是确定性的。对于某些但不是所有构造函数的案例的分离(多个子句)将被推断为半确定性的。

Mercury还能够为交换机生成非常高效的代码,并且还可以通过添加新案例来通知您需要更改的所有位置,因此在可能的情况下使用交换机被认为是好的方式。但是在像你这样的情况下,你会遇到if / then / else。