如何检查变量是否在 Mercury 中实例化
How to check if a variable is instantiated in Mercury
我是 Mercury 语言的完全初学者,尽管我以前学过 Prolog。 Mercury 的新方面之一是 dererminism。 main
函数必须是确定性的。为了做到这一点,我必须检查一个变量是否是 unified/bound 到一个值,但我找不到如何做到这一点。具体看代码:
main(!IO) :-
mother(X,"john"),
( if bound(X) <-----this is my failed try; how to check if X is unified?
then
io.format("%s\n", [s(X)], !IO)
else
io.write_string("Not available\n",!IO)
).
这样的 main
不会失败,即它(我猜)会满足确定性约束。所以问题是如何检查变量是否绑定。
我从这边找到的 Prolog example 翻译了家谱以供比较。
我已经指定了所有事实(人)和他们之间的关系以及一些辅助谓词。
请注意,此键入版本确实找到了您在最佳答案中看到的错误:female(jane).
主谓词不是必须是确定性的,它也可以是cc_multi
,这意味着Mercury将提交(不尝试其他)选择它有;
您可以通过将 mother
替换为 parent
.
来验证这一点
您也不必检查变量的边界,而只需在 if 子句中使用任何不确定的术语,然后在随后的 then 部分将保证绑定变量,或在 else 部分解除绑定。
如果您希望此示例更加动态,则必须使用 lexer
或 term
模块将输入解析为 person
原子。
如果你想要所有的解决方案,你应该检查 solution
模块。
%-------------------------------%
% vim: ft=mercury ff=unix ts=4 sw=4 et
%-------------------------------%
% File: relationship.m
%-------------------------------%
% Classical example of family relationship representation,
% based on:
%-------------------------------%
:- module relationship.
:- interface.
:- import_module io.
%-------------------------------%
:- pred main(io::di, io::uo) is cc_multi.
%-------------------------------%
%-------------------------------%
:- implementation.
:- type person
---> john
; bob
; bill
; ron
; jeff
; mary
; sue
; nancy
; jane
.
:- pred person(person::out) is multi.
person(Person) :- male(Person).
person(Person) :- female(Person).
:- pred male(person).
:- mode male(in) is semidet.
:- mode male(out) is multi.
male(john).
male(bob).
male(bill).
male(ron).
male(jeff).
:- pred female(person).
:- mode female(in) is semidet.
:- mode female(out) is multi.
female(mary).
female(sue).
female(nancy).
female(jane).
:- pred parent(person, person).
:- mode parent(in, in) is semidet.
:- mode parent(in, out) is nondet.
:- mode parent(out, in) is nondet.
:- mode parent(out, out) is multi.
parent(mary, sue).
parent(mary, bill).
parent(sue, nancy).
parent(sue, jeff).
parent(jane, ron).
parent(john, bob).
parent(john, bill).
parent(bob, nancy).
parent(bob, jeff).
parent(bill, ron).
:- pred mother(person, person).
:- mode mother(in, in) is semidet.
:- mode mother(in, out) is nondet.
:- mode mother(out, in) is nondet.
:- mode mother(out, out) is nondet.
mother(Mother, Child) :-
female(Mother),
parent(Mother, Child).
:- pred father(person, person).
:- mode father(in, in) is semidet.
:- mode father(in, out) is nondet.
:- mode father(out, in) is nondet.
:- mode father(out, out) is nondet.
father(Father, Child) :-
male(Father),
parent(Father, Child).
%-------------------------------%
main(!IO) :-
Child = john, % try sue or whatever for the first answer
( if mother(Mother, Child) then
io.write(Mother, !IO),
io.print(" is ", !IO),
io.write(Child, !IO),
io.print_line("'s mother", !IO)
else
io.write(Child, !IO),
io.print_line("'s mother is unknown", !IO)
).
%-------------------------------%
:- end_module relationship.
%-------------------------------%
您不需要检查变量的实例化状态。在每天使用 Mercury 近 10 年的时间里,我从未这样做过。对于每个谓词和谓词的模式,Mercury 知道静态 程序中每个点的每个变量的实例化。所以使用你的例子:
% I'm making some assumptions here.
:- pred mother(string::out, string::in) is det.
main(!IO) :-
mother(X,"john"),
io.format("%s\n", [s(X)], !IO).
mother 的声明说它的第一个参数是一个 output 参数,这意味着在调用 mother 之后它的值将被设置。所以它可以被打印出来。如果您确实使用了 var 谓词(标准库中有一个),它总是会失败。
要做到这一点 Mercury 对程序员提出了其他要求。例如。
(
X = a,
Y = b
;
X = c
)
io.write(Y, !IO)
以上代码不合法。因为 Y 在第一种情况下产生,但在第二种情况下不产生,所以它的基础性没有明确定义。编译器还知道析取是 switch(当 X 已经接地时),因为只有一个析取可能为真。所以它只产生一个答案。
当你有一个多模式谓词时,这种静态基础可能变得棘手。 Mercury 可能需要重新排序您的连词以使程序模式正确:例如,在变量生成后使用它。尽管如此,变量的使用和实例化状态将始终是静态已知的。
我知道这与 Prolog 确实有很大不同,可能需要大量的学习。
希望对你有所帮助,祝一切顺利。
main(!IO) :-
mother(X,"john"),
( if bound(X) <-----this is my failed try; how to check if X is unified?
then
io.format("%s\n", [s(X)], !IO)
else
io.write_string("Not available\n",!IO)
).
此代码在 Mercury 中没有多大意义。如果 X
是来自 mother
的标准输出变量,它将永远不会成功 X
未绑定。
如果 mother
在此模式下是确定性的 (det
),它将 始终 为您提供 X
的单个值。在那种情况下,就不需要检查任何东西; mother(X, "john")
给你约翰的母亲,故事结束,所以不需要 "Not available case"。
由于您正在尝试编写案例来处理 mother
给您的东西和没有给您的东西,我假设 mother
不是确定性的。如果是 semidet
那么有两种可能;它成功 X
绑定到某物,或者它 失败 。 X
未绑定不会成功。
您的代码没有说明如果 mother
失败,main
(必须始终成功)如何成功。请记住,逗号是逻辑连词 (and)。您的代码说“如果 mother(X, "john")
成功且 (if .. then ... else ...) succeeds
),main
成功”。但是如果 mother
失败 ,那么呢?编译器会因此拒绝您的代码,即使您的其余代码有效。
但是 if ... then ... else ...
构造正是为了让您检查可能成功或失败的目标,并指定目标成功和失败的情况,这样整个if/then/else 总是 成功。所以你需要做的就是把 mother(X, "john")
放在 if/then/else.
的 condition 中
在 Mercury 中,您不会打开绑定或未绑定的变量。相反,只需检查可能绑定变量的目标是成功还是失败。
我是 Mercury 语言的完全初学者,尽管我以前学过 Prolog。 Mercury 的新方面之一是 dererminism。 main
函数必须是确定性的。为了做到这一点,我必须检查一个变量是否是 unified/bound 到一个值,但我找不到如何做到这一点。具体看代码:
main(!IO) :-
mother(X,"john"),
( if bound(X) <-----this is my failed try; how to check if X is unified?
then
io.format("%s\n", [s(X)], !IO)
else
io.write_string("Not available\n",!IO)
).
这样的 main
不会失败,即它(我猜)会满足确定性约束。所以问题是如何检查变量是否绑定。
我从这边找到的 Prolog example 翻译了家谱以供比较。
我已经指定了所有事实(人)和他们之间的关系以及一些辅助谓词。
请注意,此键入版本确实找到了您在最佳答案中看到的错误:female(jane).
主谓词不是必须是确定性的,它也可以是cc_multi
,这意味着Mercury将提交(不尝试其他)选择它有;
您可以通过将 mother
替换为 parent
.
您也不必检查变量的边界,而只需在 if 子句中使用任何不确定的术语,然后在随后的 then 部分将保证绑定变量,或在 else 部分解除绑定。
如果您希望此示例更加动态,则必须使用 lexer
或 term
模块将输入解析为 person
原子。
如果你想要所有的解决方案,你应该检查 solution
模块。
%-------------------------------%
% vim: ft=mercury ff=unix ts=4 sw=4 et
%-------------------------------%
% File: relationship.m
%-------------------------------%
% Classical example of family relationship representation,
% based on:
%-------------------------------%
:- module relationship.
:- interface.
:- import_module io.
%-------------------------------%
:- pred main(io::di, io::uo) is cc_multi.
%-------------------------------%
%-------------------------------%
:- implementation.
:- type person
---> john
; bob
; bill
; ron
; jeff
; mary
; sue
; nancy
; jane
.
:- pred person(person::out) is multi.
person(Person) :- male(Person).
person(Person) :- female(Person).
:- pred male(person).
:- mode male(in) is semidet.
:- mode male(out) is multi.
male(john).
male(bob).
male(bill).
male(ron).
male(jeff).
:- pred female(person).
:- mode female(in) is semidet.
:- mode female(out) is multi.
female(mary).
female(sue).
female(nancy).
female(jane).
:- pred parent(person, person).
:- mode parent(in, in) is semidet.
:- mode parent(in, out) is nondet.
:- mode parent(out, in) is nondet.
:- mode parent(out, out) is multi.
parent(mary, sue).
parent(mary, bill).
parent(sue, nancy).
parent(sue, jeff).
parent(jane, ron).
parent(john, bob).
parent(john, bill).
parent(bob, nancy).
parent(bob, jeff).
parent(bill, ron).
:- pred mother(person, person).
:- mode mother(in, in) is semidet.
:- mode mother(in, out) is nondet.
:- mode mother(out, in) is nondet.
:- mode mother(out, out) is nondet.
mother(Mother, Child) :-
female(Mother),
parent(Mother, Child).
:- pred father(person, person).
:- mode father(in, in) is semidet.
:- mode father(in, out) is nondet.
:- mode father(out, in) is nondet.
:- mode father(out, out) is nondet.
father(Father, Child) :-
male(Father),
parent(Father, Child).
%-------------------------------%
main(!IO) :-
Child = john, % try sue or whatever for the first answer
( if mother(Mother, Child) then
io.write(Mother, !IO),
io.print(" is ", !IO),
io.write(Child, !IO),
io.print_line("'s mother", !IO)
else
io.write(Child, !IO),
io.print_line("'s mother is unknown", !IO)
).
%-------------------------------%
:- end_module relationship.
%-------------------------------%
您不需要检查变量的实例化状态。在每天使用 Mercury 近 10 年的时间里,我从未这样做过。对于每个谓词和谓词的模式,Mercury 知道静态 程序中每个点的每个变量的实例化。所以使用你的例子:
% I'm making some assumptions here.
:- pred mother(string::out, string::in) is det.
main(!IO) :-
mother(X,"john"),
io.format("%s\n", [s(X)], !IO).
mother 的声明说它的第一个参数是一个 output 参数,这意味着在调用 mother 之后它的值将被设置。所以它可以被打印出来。如果您确实使用了 var 谓词(标准库中有一个),它总是会失败。
要做到这一点 Mercury 对程序员提出了其他要求。例如。
(
X = a,
Y = b
;
X = c
)
io.write(Y, !IO)
以上代码不合法。因为 Y 在第一种情况下产生,但在第二种情况下不产生,所以它的基础性没有明确定义。编译器还知道析取是 switch(当 X 已经接地时),因为只有一个析取可能为真。所以它只产生一个答案。
当你有一个多模式谓词时,这种静态基础可能变得棘手。 Mercury 可能需要重新排序您的连词以使程序模式正确:例如,在变量生成后使用它。尽管如此,变量的使用和实例化状态将始终是静态已知的。
我知道这与 Prolog 确实有很大不同,可能需要大量的学习。
希望对你有所帮助,祝一切顺利。
main(!IO) :-
mother(X,"john"),
( if bound(X) <-----this is my failed try; how to check if X is unified?
then
io.format("%s\n", [s(X)], !IO)
else
io.write_string("Not available\n",!IO)
).
此代码在 Mercury 中没有多大意义。如果 X
是来自 mother
的标准输出变量,它将永远不会成功 X
未绑定。
如果 mother
在此模式下是确定性的 (det
),它将 始终 为您提供 X
的单个值。在那种情况下,就不需要检查任何东西; mother(X, "john")
给你约翰的母亲,故事结束,所以不需要 "Not available case"。
由于您正在尝试编写案例来处理 mother
给您的东西和没有给您的东西,我假设 mother
不是确定性的。如果是 semidet
那么有两种可能;它成功 X
绑定到某物,或者它 失败 。 X
未绑定不会成功。
您的代码没有说明如果 mother
失败,main
(必须始终成功)如何成功。请记住,逗号是逻辑连词 (and)。您的代码说“如果 mother(X, "john")
成功且 (if .. then ... else ...) succeeds
),main
成功”。但是如果 mother
失败 ,那么呢?编译器会因此拒绝您的代码,即使您的其余代码有效。
但是 if ... then ... else ...
构造正是为了让您检查可能成功或失败的目标,并指定目标成功和失败的情况,这样整个if/then/else 总是 成功。所以你需要做的就是把 mother(X, "john")
放在 if/then/else.
在 Mercury 中,您不会打开绑定或未绑定的变量。相反,只需检查可能绑定变量的目标是成功还是失败。