Pure Clojure solution to the rplaca/rplacd problem.

This commit is contained in:
Simon Brooke 2019-08-23 11:16:27 +01:00
parent ffa3ecd1fe
commit 3d2c524f3f
2 changed files with 127 additions and 95 deletions

View file

@ -58,7 +58,7 @@
[x]
(cond
(= x NIL) NIL
(instance? beowulf.cons_cell.ConsCell x) (.CAR x)
(instance? beowulf.cons_cell.ConsCell x) (.first x)
:else
(throw
(Exception.
@ -70,7 +70,7 @@
[x]
(cond
(= x NIL) NIL
(instance? beowulf.cons_cell.ConsCell x) (.CDR x)
(instance? beowulf.cons_cell.ConsCell x) (.getCdr x)
:else
(throw
(Exception.
@ -85,8 +85,8 @@
(= l NIL) NIL
(empty? path) l
:else (case (last path)
\a (uaf (CAR l) (butlast path))
\d (uaf (CDR l) (butlast path)))))
\a (uaf (.first l) (butlast path))
\d (uaf (.getCdr l) (butlast path)))))
(defn CAAR [x] (uaf x (seq "aa")))
(defn CADR [x] (uaf x (seq "ad")))

View file

@ -3,47 +3,79 @@
Lisp 1.5 lists do not necessarily have a sequence as their CDR, so
cannot be implemented on top of Clojure lists.")
;; (def NIL
;; "The canonical empty list symbol."
;; (symbol "NIL"))
(def NIL
"The canonical empty list symbol."
(symbol "NIL"))
;; (def T
;; "The canonical true value."
;; (symbol "T")) ;; true.
(def T
"The canonical true value."
(symbol "T")) ;; true.
;; (def F
;; "The canonical false value - different from `NIL`, which is not canonically
;; false in Lisp 1.5."
;; (symbol "F")) ;; false as distinct from nil
(def F
"The canonical false value - different from `NIL`, which is not canonically
false in Lisp 1.5."
(symbol "F")) ;; false as distinct from nil
(defprotocol MutableSequence
"Like a sequence, but mutable."
(rplaca
[this value]
"replace the first element of this sequence with this value")
(rplacd
[this value]
"replace the rest (but-first; cdr) of this sequence with this value")
(getCdr
[this]
"like `more`, q.v., but returns List `NIL` not Clojure `nil` when empty." ))
(deftype ConsCell [^:unsynchronized-mutable CAR ^:unsynchronized-mutable CDR]
MutableSequence
(rplaca [this value]
(rplaca [this value]
(if
(or
(instance? beowulf.cons_cell.ConsCell value)
(satisfies? MutableSequence value) ;; can't reference
;; beowulf.cons_cell.ConsCell,
;; because it is not yet
;; defined
(number? value)
(symbol? value))
(do
(set! (. cell CAR) value)
cell)
(set! (. this CAR) value)
this)
(throw (ex-info
(str "Invalid value in RPLACA: `" value "` (" (type value) ")")
{:cause :bad-value
:detail :rplaca}))))
(rplacd [this value]
(if
(or
(satisfies? MutableSequence value)
(number? value)
(symbol? value))
(do
(set! (. this CDR) value)
this)
(throw (ex-info
(str "Invalid value in RPLACD: `" value "` (" (type value) ")")
{:cause :bad-value
:detail :rplaca}))))
(getCdr [this]
(. this CDR))
clojure.lang.ISeq
(cons [this x] (ConsCell. x this))
(first [this] (.CAR this))
;; next and more must return ISeq:
;; https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/ISeq.java
(more [this] (if
(seq? (.CDR this))
(.CDR this)
(seq? (.getCdr this))
(.getCdr this)
clojure.lang.PersistentList/EMPTY))
(next [this] (if
(seq? (.CDR this))
(.CDR this)
(seq? (.getCdr this))
(.getCdr this)
nil ;; next returns nil when empty
))
@ -56,8 +88,8 @@
clojure.lang.IPersistentCollection
(count [this] (if
(coll? (.CDR this))
(inc (.count (.CDR this)))
(coll? (.getCdr this))
(inc (.count (.getCdr this)))
1))
(empty [this] false) ;; a cons cell is by definition not empty.
(equiv [this other] (if
@ -71,83 +103,83 @@
(= (first this) (first other)))
(if
(and
(seq? (rest this))
(seq? (rest other)))
(.equiv (rest this) (rest other))
(= (rest this) (rest other))))
(seq? (.getCdr this))
(seq? (.getCdr other)))
(.equiv (.getCdr this) (.getCdr other))
(= (.getCdr this) (.getCdr other))))
false)))
;; (defn- to-string
;; "Printing ConsCells gave me a *lot* of trouble. This is an internal function
;; used by the print-method override (below) in order that the standard Clojure
;; `print` and `str` functions will print ConsCells correctly. The argument
;; `cell` must, obviously, be an instance of `ConsCell`."
;; [cell]
;; (loop [c cell
;; n 0
;; s "("]
;; (if
;; (instance? beowulf.cons_cell.ConsCell c)
;; (let [car (.CAR c)
;; cdr (.CDR c)
;; cons? (instance? beowulf.cons_cell.ConsCell cdr)
;; ss (str
;; s
;; (to-string car)
;; (cond
;; cons?
;; " "
;; (or (nil? cdr) (= cdr 'NIL))
;; ")"
;; :else
;; (str " . " (to-string cdr) ")")))]
;; (if
;; cons?
;; (recur cdr (inc n) ss)
;; ss))
;; (str c))))
(defn- to-string
"Printing ConsCells gave me a *lot* of trouble. This is an internal function
used by the print-method override (below) in order that the standard Clojure
`print` and `str` functions will print ConsCells correctly. The argument
`cell` must, obviously, be an instance of `ConsCell`."
[cell]
(loop [c cell
n 0
s "("]
(if
(instance? beowulf.cons_cell.ConsCell c)
(let [car (.first c)
cdr (.getCdr c)
cons? (instance? beowulf.cons_cell.ConsCell cdr)
ss (str
s
(to-string car)
(cond
cons?
" "
(or (nil? cdr) (= cdr NIL))
")"
:else
(str " . " (to-string cdr) ")")))]
(if
cons?
(recur cdr (inc n) ss)
ss))
(str c))))
;; (defn pretty-print
;; "This isn't the world's best pretty printer but it sort of works."
;; ([^beowulf.cons_cell.ConsCell cell]
;; (println (pretty-print cell 80 0)))
;; ([^beowulf.cons_cell.ConsCell cell width level]
;; (loop [c cell
;; n (inc level)
;; s "("]
;; (if
;; (instance? beowulf.cons_cell.ConsCell c)
;; (let [car (.CAR c)
;; cdr (.CDR c)
;; cons? (instance? beowulf.cons_cell.ConsCell cdr)
;; print-width (count (print-str c))
;; indent (apply str (repeat n " "))
;; ss (str
;; s
;; (pretty-print car width n)
;; (cond
;; cons?
;; (if
;; (< (+ (count indent) print-width) width)
;; " "
;; (str "\n" indent))
;; (or (nil? cdr) (= cdr 'NIL))
;; ")"
;; :else
;; (str " . " (pretty-print cdr width n) ")")))]
;; (if
;; cons?
;; (recur cdr n ss)
;; ss))
;; (str c)))))
(defn pretty-print
"This isn't the world's best pretty printer but it sort of works."
([^beowulf.cons_cell.ConsCell cell]
(println (pretty-print cell 80 0)))
([^beowulf.cons_cell.ConsCell cell width level]
(loop [c cell
n (inc level)
s "("]
(if
(instance? beowulf.cons_cell.ConsCell c)
(let [car (.first c)
cdr (.getCdr c)
cons? (instance? beowulf.cons_cell.ConsCell cdr)
print-width (count (print-str c))
indent (apply str (repeat n " "))
ss (str
s
(pretty-print car width n)
(cond
cons?
(if
(< (+ (count indent) print-width) width)
" "
(str "\n" indent))
(or (nil? cdr) (= cdr NIL))
")"
:else
(str " . " (pretty-print cdr width n) ")")))]
(if
cons?
(recur cdr n ss)
ss))
(str c)))))
;; (defmethod clojure.core/print-method
;; ;;; I have not worked out how to document defmethod without blowing up the world.
;; beowulf.cons_cell.ConsCell
;; [this writer]
;; (.write writer (to-string this)))
(defmethod clojure.core/print-method
;;; I have not worked out how to document defmethod without blowing up the world.
beowulf.cons_cell.ConsCell
[this writer]
(.write writer (to-string this)))
(defmacro make-cons-cell