Недостатки Lisp не могут быть напрямую смоделированы как значения Tcl из-за принципиально разных семантических моделей.Лисп использует модель, в которой значения могут быть непосредственно обновлены;значение ячейки памяти.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]
Это приводит к ожидаемому выводу (учитывая, что у Lisp и Tcl разные представления о том, как должен форматироваться список).