限制prolog中的变量范围

时间:2014-11-10 05:45:11

标签: prolog iso-prolog

我想声明一个列表列表,如下所示:

%% example 1
Xs = [
  [[A],[[A]]], 
  [[A],[[A],[A]]], 
  [[A],[[A],[A],[A]]]
].

这里,符号A指的是每个列表中的相同变量。执行maplist(writeln,xs)会产生以下输出:

[[_G1],[[_G1]]]
[[_G1],[[_G1],[_G1]]]
[[_G1],[[_G1],[_G1],[_G1]]]

我想在每个列表中使用相同的符号A,但是为了使每个列表的变量不同,要提供以下输出:

[[_G1],[[_G1]]]
[[_G2],[[_G2],[_G2]]]
[[_G3],[[_G3],[_G3],[_G3]]]

我做这项工作的唯一方法是为每个列表提供自己唯一的变量,如下所示:

%% example 2
Xs = [
  [[A1],[[A1]]], 
  [[A2],[[A2],[A2]]], 
  [[A3],[[A3],[A3],[A3]]]
].

是否有任何Prolog语法,因此不需要为每个变量编号,如示例2所示?我尝试在列表中添加括号,如下所示:

Xs = [
  ([[A],[[A]]]), 
  ([[A],[[A],[A]]]), 
  ([[A],[[A],[A],[A]]])
].

但是这给了我与例1相同的输出。

3 个答案:

答案 0 :(得分:2)

你可以这样做:

首先,使用不同的变量创建所需的列表结构:

?- maplist(length, Lists, [2,3,4]).
Lists = [[X1, X2], [X3, X4, X5], [X6, X7, X8, X9]].

然后,使用以下附加定义:

same_element(Ls) :- maplist(=([_]), Ls).

您可以将同一子列表中的变量统一到同一个词:

?- maplist(same_element, [[X1, X2], [X3, X4, X5], [X6, X7, X8, X9]]).
X1 = X2, X2 = [_G1141],
X3 = X4, X4 = X5, X5 = [_G1149],
X6 = X7, X7 = X8, X8 = X9, X9 = [_G1157].

组合:

?- maplist(length, Lists, [2,3,4]),
   maplist(same_element, Lists),
   maplist(writeln, Lists).

得到以下特性:

[[_G1079],[_G1079]]
[[_G1087],[_G1087],[_G1087]]
[[_G1095],[_G1095],[_G1095],[_G1095]]

现在,使用以下Emacs定义:

(defun nice-variables (start end)
  (interactive "r")
  (goto-char start)
  (let ((n 1)
        (variables nil)
        (m (make-marker)))
    (set-marker m end)
    (while (and (<= (point) (marker-position m))
                (re-search-forward "_G" (marker-position m) t))
      (let* ((from (point))
             (len (skip-chars-forward "0-9"))
             (str (buffer-substring-no-properties from (+ from len)))
             (num (assoc str variables)))
        (delete-backward-char (+ len 2))
        (if num
            (insert (format "X%d" (cdr num)))
          (setq variables (cons (cons str n) variables))
          (insert (format "X%d" n))
          (setq n (1+ n)))))))
在该地区

M-x nice-variables RET,您得到:

[[X1],[X1]]
[[X2],[X2],[X2]]
[[X3],[X3],[X3],[X3]]

这也是我在上面第一个查询的输出中使用的,以使其更具可读性。

因此,您可以通过统一想要相同的变量来生成动态所需的结构,或者复制&amp;粘贴上面的输出并直接在程序中稍作修改即可使用它。

答案 1 :(得分:2)

Prolog中的变量名称具有跨越谓词定义中的单个子句或顶级查询的作用域。所以,这个:

?- List = [A, A, A].

表示具有相同变量A的三倍的列表。如果你把它放到一个谓词中,那就像在一个文件中my_lists.pl(我没有像你一样嵌套列表,只是为了保持简单):

my_list([A, A]).
my_list([A, A, A]).
my_list([A, A, A, A]).

三个子句中的A现在不在同一个词法范围内,因此如果您查阅此谓词,然后使用例如my_list(L)收集findall/3的所有可能实例,你得到你想要的东西:

?- [my_lists].
true.

?- findall(L, my_list(L), Ls).
Ls = [[_G1945, _G1945],
      [_G1933, _G1933, _G1933],
      [_G1918, _G1918, _G1918, _G1918]].

这接近你想要的吗?你想要实现的是什么?

答案 2 :(得分:2)

如果要写出变量并为它们指定精确的名称,则需要写选项variable_names/1This answer解释了如何。或者,您可以使用遗留谓词numbervars/3,使用术语'$VAR'(N)统一不同的变量,然后使用writeq/1或写选项numbervars(true)

但是在您指出的情况下,这两种方法都不起作用。事实上,你的查询真的很幸运

 ?- Xs = [[[A],[A]],[[A],[A],[A]],[[A],[A],[A],[A]]], maplist(writeln,Xs).

为不同的列表生成相同的变量。更糟糕的是,编写相同列表的目标非常相同,可能会为不同的调用生成不同的变量名称:

p(N) :-
   length(_,N),
   length(K,1),
   writeq(K),
   garbage_collect,
   writeq(K).

对于p(100),SICStus写入[_776][_46],SWI写入[_G517][_G3]。简而言之,你在美好的一天抓住了Prolog。这并不奇怪,因为标准只需要依赖于&#34;实现依赖&#34;具有一些限制的名称的值:它以下划线开头,其余字符对于不同的变量是不同的,对于相同的写入目标中的相同变量也是如此。以下是ISO / IEC 13211-1:1995:

  

7.10.5写一个术语

     

使用Term(8.14.2)输出术语write_term/3时   所采取的行动由以下规则界定:

     
    

a)如果Term是变量,则表示字符序列      发送该变量是输出。序列开始了      带_(下划线),其余字符为
     依赖于实现。相同的字符序列
     用于每次出现的特定变量      Term。每个字符使用不同的字符序列      Term中的不同变量。

  

这样做的原因是全局一致的变量命名会在实现中产生大量开销。

总结:如果要为同一个变量使用不同的变量名,请使用带有不同参数的variable_names/1。如果您希望变量实际上不同,请以不同方式命名,或相应地使用copy_term/2