Wil*_*mKF 5 lisp tcl dynamic-scope
lisp中的列表是一系列cons单元格,但在Tcl中,列表是一个用空格分隔元素的字符串.为了将代码从lisp转换为tcl,可以简单地使用lisp列表并将它们转换为Tcl列表.然而,这会导致副作用缺陷单元没有遇到Tcl代码.例如,在lisp中考虑以下代码:
(setq a (list 1 2 3 4))
(let ((b a)
(a (cddr a)))
(declare (special a b))
(setf (cadr b) ‘b)
(setf (cadr a) ‘d)
(print a))
(print a)
;; Results in:
(3 d)
(1 b 3 d)
Run Code Online (Sandbox Code Playgroud)
是否有一个Tcl包可以在Tcl中更好地模拟lisp列表?这样的软件包是否可以轻松转换为常规Tcl列表?
上面的代码在Tcl中使用这样的包可能是什么样的?
由于语义模型根本不同,Lisp cons单元不能直接建模为Tcl值.Lisp使用一个模型,可以直接更新值; 值是内存单元格.Tcl使用不同的模型,其值在概念上是不可变的,并且在任何"1 2 3 4"之间原则上没有任何差别,它们恰好位于任何其他地方; Tcl中的可变实体是变量带有名称(名称字符串本身是不可变的,当然......)这种不变性在简单值的层面上是有意义的,但它也扩展到Tcl的列表和字典; 变异操作都返回一个新值或更新变量.(实现比这更有效,使用写时复制策略来保留不变性的语义模型,同时能够实现具有值本身变异的事物,而实际上已知它们在语义上是等效的.)
因此,您必须将可更新的cons单元构造为变量.这是你如何做到这一点:
proc cons {a b} {
global gensym cons
set handle G[incr gensym]
set cons($handle) [list $a $b]
return $handle
}
proc car {handle} {
global cons
return [lindex $cons($handle) 0]
}
proc cdr {handle} {
global cons
return [lindex $cons($handle) 1]
}
proc setCar {handle value} {
global cons
lset cons($handle) 0 $value
}
# Convenience procedures
proc makeFromList args {
set result "!nil"
foreach value [lreverse $args] {
set result [cons $value $result]
}
return $result
}
proc getAsList {handle} {
set result {}
while {$handle ne "!nil"} {
lappend result [car $handle]
set handle [cdr $handle]
}
return $result
}
set a [makeFromList 1 2 3 4]
# Use some local context; Tcl doesn't have anything exactly like Lisp's "let"
apply {a {
set b $a
set a [cdr [cdr $a]]
setCar [cdr $b] "b"
setCar [cdr $a] "d"
puts [getAsList $a]
}} $a
puts [getAsList $a]
Run Code Online (Sandbox Code Playgroud)
这产生了预期的输出(假设Lisp和Tcl对于如何格式化列表有不同的想法).