Prolog:单引号和双引号的不同行为

Prolog: Different behaviour of single and double quotes

我是 Prolog 的新手,我偶然发现了一些我不明白的东西。

这是我的代码:

:- dynamic user/3.
user('id', 'Name', 20).

changeAge(Id, NewAge) :-
   user(Id, Name, _),
   retract(user(Id,_,_)),
   assert(user(Id,Name,NewAge)).

要更新数据库中的用户信息, changeAge/2 执行这三个步骤:

  1. 查找正确的记录,使用user/3
  2. 从数据库中删除一条匹配记录,使用 retract/1
  3. 使用 assert/1.
  4. 将新的更新记录插入数据库

这是我的控制台输出:

1 ?- user('id', _, Age).
Age = 20.

2 ?- changeAge('id', 25).
true.

3 ?- user('id', _, Age).
Age = 25.

4 ?- changeAge("id", 30).
false.

5 ?- user('id', _, Age).
Age = 25.

为什么单引号给我 true(第 2 行) 当双引号给我 false (第 4 行)?

TL;DR1: 阅读 this answer 问题 “What is the difference between ' and " in Prolog?”。

TL;DR2: 目标'id'<a href="https://sicstus.sics.se/sicstus/docs/latest4/html/sicstus.html/mpg_002dref_002dunify.html" rel="nofollow noreferrer"> = </a>"id" succeeds iff the Prolog flag double_quotes 设置为 atom.

Prolog flag double_quotes can be set at runtime using <a href="https://sicstus.sics.se/sicstus/docs/latest4/html/sicstus.html/mpg_002dref_002dset_005fprolog_005fflag.html" rel="nofollow noreferrer">set_prolog_flag/2</a>:

  • ?- set_prolog_flag(double_quotes, <i>chars</i>).

    ?- 'id' = "id".
    false.
    
  • ?- set_prolog_flag(double_quotes, <i>codes</i>).

    ?- 'id' = "id".
    false.
    
  • ?- set_prolog_flag(double_quotes, <b>atom</b>).

    ?- 'id' = "id".
    true.
    

有关详细信息,请阅读 SICStus Prolog manual page on "Strings as lists"!

假设一些用户在数据库中获得了同一个 ID 的多条记录;可能有潜在用途的东西,比如说,如果人们可能有多个名字……不管怎样,这个答案不是关于正确建模部分,而是关于 的技术方面! YMMV.

:- dynamic(user/3).

init_db :-
   retractall(user(_,_,_)),
   maplist(assert, [user(i,n,1),user(i,n,2),user(i,m,1),user(i,m,2),
                    user(j,n,1),user(j,n,2),user(j,m,1),user(j,m,2)]).

changeAge(Id, NewAge) :-
   user(Id, Name, _),
   retract(user(Id,_,_)),
   assert(user(Id,Name,NewAge)).

让我们初始化数据库然后"change some ages" :-)

?- init_db, (changeAge(i,6) ; changeAge(j,7)), false.
false.

?- findall(user(Id,Name,Age), user(Id,Name,Age), DB).
DB = [user(i,m,6),user(i,m,6),user(i,m,6),user(i,m,6),
      user(j,m,7),user(j,m,7),user(j,m,7),user(j,m,7)].

不好! 在:8 个不同的记录。输出:2 个不同的记录,每个记录的重数为 4。

让我们用上面的原始事实恢复然后使用 changeAge/2 略有不同:

?- init_db, changeAge(_,_), false.
false.

?- findall(user(Id,Name,Age), user(Id,Name,Age), DB).
DB = [user(i,m,_),user(i,m,_),user(i,m,_),user(i,m,_),
      user(j,m,_),user(j,m,_),user(j,m,_),user(j,m,_)].

更糟! 在:8 个不同的地面记录。输出:2 个不同的非地面记录。

底线: 请注意附加到 !

的 "handle with care" 警告标志