有没有办法在lisp或scheme中构建自引用数据结构(比如带循环的图形)?我之前从未想过它,但是由于缺乏进行破坏性修改的方法,我可以找不到任何简单的方法来制作它。这只是函数式语言的一个重要缺陷,如果是这样,那么像haskell这样的懒函数语言呢?
答案 0 :(得分:14)
在Common Lisp中,您可以修改列表内容,数组内容,CLOS实例的插槽等。
Common Lisp还允许读写循环数据结构。使用
? (setf *print-circle* t)
T
; a list of two symbols: (foo bar)
? (defvar *ex1* (list 'foo 'bar))
*EX1*
; now let the first list element point to the list,
; Common Lisp prints the circular list
? (setf (first *ex1*) *ex1*)
#1=(#1# BAR)
; one can also read such a list
? '#1=(#1# BAR)
#1=(#1# BAR)
; What is the first element? The list itself
? (first '#1=(#1# BAR))
#1=(#1# BAR)
?
所谓的纯功能编程语言不允许副作用。大多数Lisp方言不纯。它们允许副作用,并允许修改数据结构。
有关详细信息,请参阅Lisp介绍书。
答案 1 :(得分:9)
在Scheme中,您可以使用set!
,set-car!
和set-cdr!
(以及以爆炸('!'
)结尾的任何其他内容(表示修改)轻松完成此操作:
(let ((x '(1 2 3)))
(set-car! x x)
; x is now the list (x 2 3), with the first element referring to itself
)
答案 2 :(得分:9)
Common Lisp支持使用setf
修改数据结构。
您可以通过tying the knot在Haskell中构建循环数据结构。
答案 3 :(得分:4)
您不需要“破坏性修改”来构建自引用数据结构;例如,在Common Lisp中,'#1=(#1#)
是一个包含自身的cons-cell。
Scheme和Lisp能够进行破坏性修改:您可以像上面这样构建循环缺点:
(let ((x (cons nil nil)))
(rplaca x x) x)
在学习Lisp / Scheme时,您能告诉我们您正在使用的材料吗?我正在编制黑色直升机的目标清单;关于Lisp和Scheme的错误信息的传播必须停止。
答案 4 :(得分:3)
是的,它们可能很有用。我的一位大学教授创建了一种名为Medusa Numbers的Scheme类型。它们是任意精度浮点数,可能包括重复小数。他有一个功能:
(create-medusa numerator denominator) ; or some such
创造了代表理性的美杜莎数字。结果:
(define one-third (create-medusa 1 3))
one-third => ; scheme hangs - when you look at a medusa number you turn to stone
(add-medusa one-third (add-medusa one-third one-third)) => 1
如前所述,这是通过明智的应用集合车来完成的!和set-cdr!
答案 5 :(得分:3)
它不仅可行,它还是Common Lisp对象系统的核心:标准类是它自己的一个实例!
答案 6 :(得分:3)
我赞成了明显的Scheme技术;这个答案只涉及Haskell。
在Haskell中,您可以使用let
完全在功能上执行此操作,这被认为是好的风格。一个很好的例子是regexp到NFA的转换。您也可以使用IORef
命令性地执行此操作,这被认为是糟糕的风格,因为它会强制所有代码进入IO monad。
一般来说,Haskell的懒惰评估适用于循环和无限数据结构的可爱功能实现。在任何复杂的let
绑定中,所有绑定的东西都可以用在所有定义中。例如,将一个特定的有限状态机转换为Haskell非常简单,无论它有多少个循环。
答案 7 :(得分:1)
CLOS示例:
(defclass node () ((child :accessor node-child :initarg :child))) (defun make-node-cycle () (let* ((node1 (make-instance 'node)) (node2 (make-instance 'node :child node1))) (setf (node-child node1) node2)))
答案 8 :(得分:1)
Tying the Knot (circular data structures in Haskell)
另见Haskell Wiki页面:Tying the Knot
答案 9 :(得分:0)
嗯,Lisp / Scheme中的自引用数据结构和SICP流没有提到?好吧,总结一下,流= =懒惰评估列表。它可能正是你想要的那种自我引用,但它是一种自我引用。
因此,SICP中的cons-stream
是一种延迟评估其参数的语法。 (cons-stream a b)
会在不评估a或b的情况下立即返回,并且仅在您调用car-stream
或cdr-stream
来自SICP,http://mitpress.mit.edu/sicp/full-text/sicp/book/node71.html: >
(define fibs (cons-stream 0 (cons-stream 1 (add-streams (stream-cdr fibs) fibs))))
这个定义说纤维是一种 以0和1开头的流,例如 其余的流可以 通过向自身添加纤维来生成 转移一个地方:
在这种情况下,'fibs'被赋予一个对象,其值根据'fibs'懒惰地定义
几乎忘了提及,懒惰的流存在于常用的SRFI-40或SRFI-41库中。其中一个应该在大多数流行的方案中都可用,我认为
答案 10 :(得分:0)
在搜索“CIRCULAR LISTS LISP SCHEME”时,我偶然发现了这个问题。
这是我如何制作一个(在STk方案中):
首先,列一个清单
(define a '(1 2 3))
此时,STk认为a是一个列表。
(list? a)
> #t
接下来,转到最后一个元素(在这种情况下为3
)并将当前包含cdr
的{{1}}替换为指向自身的指针。
nil
现在,STk认为a不是列表。
(set-cdr! (cdr ( cdr a)) a)
(它如何解决这个问题?)
现在,如果您打印(list? a)
> #f
,您将找到一个无限长的a
列表,您将需要终止该程序。在Stk中,您可以(1 2 3 1 2 3 1 2 ...
或control-z
退出。
但循环列表有什么用呢?
我可以想到使用模运算的模糊示例,例如星期几control-\
的循环列表,或者由3位(M T W T F S S M T W ...)
表示的整数循环列表。
有没有真实的例子?