Эмуляция лиспов в клетках - PullRequest
       26

Эмуляция лиспов в клетках

5 голосов
/ 10 сентября 2010

Список в 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)

Существует ли пакет Tcl, обеспечивающий лучшую эмуляцию списков lisp в Tcl? Предоставляет ли такой пакет простое преобразование в обычные списки Tcl?

Как может выглядеть приведенный выше код в Tcl при использовании такого пакета?

1 Ответ

7 голосов
/ 10 сентября 2010

Недостатки 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 разные представления о том, как должен форматироваться список).

...