mirror of
https://github.com/kanaka/mal.git
synced 2024-09-11 13:55:55 +03:00
It is more interesting to ask an implementation of count from empty? than the reverse. Ask for nth, map, concat and conj. Allow core.mal in answers. Currently, requires the branch with foldr.
71 lines
2.2 KiB
Plaintext
71 lines
2.2 KiB
Plaintext
;; These are the answers to the questions in ../docs/exercise.md.
|
|
|
|
(load-file "../core.mal")
|
|
|
|
(def! nil? (fn* [x] (= x nil )))
|
|
(def! true? (fn* [x] (= x true )))
|
|
(def! false? (fn* [x] (= x false)))
|
|
|
|
(def! sequential? (fn* [x] (if (list? x) true (if (vector? x) true false))))
|
|
|
|
(def! > (fn* [a b] (< b a) ))
|
|
(def! <= (fn* [a b] (if (< b a) false true)))
|
|
(def! >= (fn* [a b] (if (< a b) false true)))
|
|
|
|
(def! hash-map (fn* [& xs] (apply assoc {} xs)))
|
|
(def! list (fn* [& xs] xs))
|
|
(def! prn (fn* [& xs] (println (apply pr-str xs))))
|
|
(def! swap! (fn* [a f & xs] (reset! a (apply f (deref a) xs))))
|
|
|
|
(def! count
|
|
(let* [inc_left (fn* [acc _] (inc acc))]
|
|
(fn* [xs] (if (nil? xs) 0 (reduce inc_left 0 xs)))))
|
|
(def! nth
|
|
(fn* [xs index]
|
|
(if (empty? xs)
|
|
(throw "nth: index out of range")
|
|
(if (< index 0)
|
|
(throw "nth: index out of range")
|
|
(if (zero? index)
|
|
(first xs)
|
|
(nth (rest xs) (dec index)))))))
|
|
(def! map
|
|
(fn* [f xs]
|
|
(let* [iter (fn* [x acc] (cons (f x) acc))]
|
|
(foldr iter () xs))))
|
|
(def! concat
|
|
(let* [concat2 (fn* [xs ys] (foldr cons ys xs))]
|
|
(fn* [& xs] (foldr concat2 () xs))))
|
|
(def! conj
|
|
(let* [flip_cons (fn* [xs x] (cons x xs))]
|
|
(fn* [xs & ys]
|
|
(if (vector? xs)
|
|
(apply vector (concat xs ys))
|
|
(reduce flip_cons xs ys)))))
|
|
|
|
(def! do2 (fn* [& xs] (nth xs (dec (count xs)))))
|
|
|
|
(defmacro! let2
|
|
;; Must be a macro because the first argument must not be evaluated.
|
|
(fn* [binds form]
|
|
(if (empty? binds)
|
|
form
|
|
;; This let* increases the readability, but the values could
|
|
;; easily be replaced below.
|
|
(let* [key (first 0)
|
|
val (nth binds 1)
|
|
more (rest (rest binds))]
|
|
`((fn* [~key] (let2 ~more ~form)) ~val)))))
|
|
|
|
(def! apply
|
|
;; Replace (f a b [c d]) with ('f 'a 'b 'c 'd) then evaluate the
|
|
;; resulting function call (the surrounding environment does not
|
|
;; matter when evaluating a function call).
|
|
;; Use nil as marker to detect deepest recursive call.
|
|
(let* [q (fn* [x] (list 'quote x))
|
|
iter (fn* [x acc]
|
|
(if (nil? acc) ; x is the last element (a sequence)
|
|
(map q x)
|
|
(cons (q x) acc)))]
|
|
(fn* [& xs] (eval (foldr iter nil xs)))))
|