mirror of
https://github.com/kanaka/mal.git
synced 2024-08-16 17:20:23 +03:00
exercices: fix apply again. It must be a function, not a macro.
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.
This commit is contained in:
parent
304930e749
commit
1ca3ee3dcd
@ -15,21 +15,20 @@ pass a command like 'load-file' before running the testsuite.
|
||||
Some solutions are given in the `examples` directory. Feel free to
|
||||
submit new solutions, or new exercises.
|
||||
|
||||
|
||||
- Implement the following functions with other built-in functions.
|
||||
- `nil?`, `true?` and `false?`
|
||||
- `empty?`
|
||||
- `sequential?`
|
||||
- Implement `nil?`, `true?`, `false?` and `sequential?` with other
|
||||
built-in functions.
|
||||
|
||||
- Implement `>`, `<=` and `>=` with `<`.
|
||||
|
||||
- Implement the following non-recursive functions.
|
||||
- `hash-map`
|
||||
- `list`
|
||||
- `prn`
|
||||
- `swap!`
|
||||
- Implement `hash-map`, `list`, `prn` and `swap!` as non-recursive
|
||||
functions.
|
||||
|
||||
- Implement `map` with a recursion.
|
||||
- Implement `count`, `nth`, `map`, `concat` and `conj` with the empty
|
||||
constructor `()`, `empty?`, `cons`, `first` and `rest`.
|
||||
|
||||
Let `count` and `nth` benefit from tail call optimization.
|
||||
|
||||
Try to replace explicit recursions with calls to `reduce` and `foldr`.
|
||||
|
||||
- Implement the `do` special as a non-recursive function. The special
|
||||
form will hide your implementation, so in order to test it, you will
|
||||
@ -38,7 +37,7 @@ submit new solutions, or new exercises.
|
||||
- Implement `let*` as a macro that uses `fn*` and recursion.
|
||||
The same remark applies.
|
||||
|
||||
- Implement `apply` as a macro.
|
||||
- Implement `apply`.
|
||||
|
||||
- Implement maps using lists.
|
||||
- Recall how maps must be evaluated.
|
||||
|
@ -1,11 +1,11 @@
|
||||
;; 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! empty? (fn* [xs] (= 0 (count xs))))
|
||||
|
||||
(def! sequential? (fn* [x] (if (list? x) true (if (vector? x) true false))))
|
||||
|
||||
(def! > (fn* [a b] (< b a) ))
|
||||
@ -17,13 +17,33 @@
|
||||
(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]
|
||||
(if (empty? xs)
|
||||
()
|
||||
(cons (f (first xs)) (map f (rest 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 (- (count xs) 1))))
|
||||
(def! do2 (fn* [& xs] (nth xs (dec (count xs)))))
|
||||
|
||||
(defmacro! let2
|
||||
;; Must be a macro because the first argument must not be evaluated.
|
||||
@ -32,20 +52,19 @@
|
||||
form
|
||||
;; This let* increases the readability, but the values could
|
||||
;; easily be replaced below.
|
||||
(let* [key (nth binds 0)
|
||||
(let* [key (first 0)
|
||||
val (nth binds 1)
|
||||
more (rest (rest binds))]
|
||||
`((fn* [~key] (let2 ~more ~form)) ~val)))))
|
||||
|
||||
(def! apply
|
||||
(let* [
|
||||
;; (a b [c d]) -> (a b c d)
|
||||
flat_end (fn* [xs]
|
||||
(if (= 1 (count xs))
|
||||
(first xs) ; [c d] above
|
||||
(cons (first xs) (flat_end (rest xs)))))
|
||||
;; x -> 'x to protect the already-evaluated arguments.
|
||||
quote_elt (fn* [x] `(quote ~x))
|
||||
]
|
||||
(fn* [& xs]
|
||||
(eval (map quote_elt (flat_end xs))))))
|
||||
;; 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)))))
|
||||
|
Loading…
Reference in New Issue
Block a user