【问题标题】:How to let a self referential binding?如何让自引用绑定?
【发布时间】:2016-06-02 19:46:49
【问题描述】:

我有一个let,我需要其中一个绑定来在正文中引用它自己。即我需要这样的东西:

(let ((my-value (make-value :some-data 2784 :next-value my-value)))
  ; ...)

这会失败,因为定义处的my-value 未绑定。我想我也许可以使用setf 来分配值,但这对我来说不是一个好的通用解决方案(例如,由于副作用)。

我该如何处理这种情况?也许有办法进行前向声明?

【问题讨论】:

  • 您需要某种形式的基本案例,因为评估(如果可能的话)仍然很急切。
  • @user2864740 即我必须使用setf?
  • 引用一个不存在的对象是没有意义的。首先创建对象。
  • 别忘了将*print-circle*设置为T;只是说...

标签: common-lisp


【解决方案1】:

我想我也许可以使用 setf 来分配值,但这对我来说不是一个好的通用解决方案(例如,由于副作用)。

某些语言旨在支持自引用变量,例如代数系统,您可以在其中编写 x = x * 4 - 2,系统会求解您的方程为 x。 在 Common Lisp 中,评估规则说您必须能够在 make-value 的参数中评估 x,然后才能将有意义的值分配给 x

您可以使用lambda 延迟操作。例如,您可以添加一个 lazy-let 宏来转换:

(lazy-let ((x (make-value :some-data d :next-value x)))
  ...)

...进入那个:

(let* ((x0 nil)
       (x (make-value :some-data d :next-value (lambda () x0))))
  (setf x0 x)
  ...)

但是你需要强制使用funcall来计算惰性值。 Haskell 等惰性语言为您隐藏了这种行为。

如果你用 Prolog 编写,你会说:

make_value(X,D) :- X = value{data: D, next: X}.

这要归功于统一,它可以用来执行一种有趣的副作用,即最多设置一个变量。 然而,这在 Common Lisp 中是不可能的(你可以在 Lisp 中实现统一,但如果唯一的目的是避免setf,你可能不应该这样做)。

以上示例中发生的情况是副作用被隐藏了。 我的观点是,没有什么魔法:不知何故,必须有一个副作用来建立一个对象和它自己之间的联系,即使它不可见。

您可以这样做,并在本地执行副作用的实现上提供无副作用的功能。这是完全可以接受的。 首先,定义你的类型:

(defstruct (value (:constructor make-value%))
  some-data
  next-value)

基本构造函数被命名为make-value%,这个名字可能不应该被你的包导出。然后,定义面向用户的构造函数:

(defun make-value (&key some-data (next nil nextp))
  (let ((value (make-value% :some-data some-data)))
    (setf (value-next-value value) (if nextp next value))
    value))

该实现小心地将本地副作用包装到一个函数中,从外部角度来看,该函数不会改变其环境(备注:分配内存也是一个副作用)。它允许用户提供下一个元素,但默认情况下它将结构链接到自身。 这是一个示例用法:

(let ((x (make-value :some-data 1234)))
  (assert (eq x (value-next-value x))))

【讨论】:

    【解决方案2】:

    您应该遵循评论者的建议并创建对象然后对其进行修改:

    (setq *print-circle* t)
    (let ((x (list 1 2 3)))
      (setf (second x) x)
      x)
    ==> #1=(1 #1# 3)
    

    如果你真的想发疯,可以使用阅读器魔法:

    (let ((x '#1=(1 #1# 3)))
      x)
    ==> #1=(1 #1# 3)
    

    但是,这会创建一个 引用 对象,您以后应该不要对其进行修改。

    它还将创建一个唯一的对象,即,如果不是一个新的对象,即上述代码的每次调用都将返回相同的对象:

    (defun unsafe ()
      '#1=(1 #1# 3))
    (eq (unsafe) (unsafe))
    ==> T
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-12
      • 2020-01-14
      • 1970-01-01
      • 2012-01-07
      • 1970-01-01
      相关资源
      最近更新 更多