mirror of
https://github.com/kanaka/mal.git
synced 2024-08-16 17:20:23 +03:00
Prepare move of reusable code to lib/.
Changes creating huge diffs, like file splits, have been delayed for readability . Also fix description of `and`.
This commit is contained in:
parent
d0ca46a2e4
commit
3e9b89d4b5
@ -5,9 +5,7 @@ Then refresh the .png files.
|
||||
|
||||
General:
|
||||
- add chat bot for #mal
|
||||
- move tokenizer.mal and reader.mal from malc along with
|
||||
./examples/{equality,memoize,pprint,protocols}.mal and
|
||||
./core.mal to ./lib directory
|
||||
- move tokenizer.mal and reader.mal from malc to ./lib directory
|
||||
|
||||
- Finish guide.md
|
||||
- mention that identifier names are suggested. some have run
|
||||
|
@ -1,16 +1,14 @@
|
||||
;;
|
||||
;; memoize.mal
|
||||
;;
|
||||
;; FIXME lib/memoize.mal
|
||||
;; Memoize any function.
|
||||
|
||||
;; Implement `memoize` using an atom (`mem`) which holds the memoized results
|
||||
;; (hash-map from the arguments to the result). When the function is called,
|
||||
;; the hash-map is checked to see if the result for the given argument was already
|
||||
;; calculated and stored. If this is the case, it is returned immediately;
|
||||
;; otherwise, it is calculated and stored in `mem`.
|
||||
;;
|
||||
;; Adapted from http://clojure.org/atoms
|
||||
;;
|
||||
|
||||
;; Memoize any function
|
||||
;; Adapted from http://clojure.org/atoms
|
||||
|
||||
(def! memoize
|
||||
(fn* [f]
|
||||
(let* [mem (atom {})]
|
||||
@ -23,18 +21,14 @@
|
||||
(swap! mem assoc key ret)
|
||||
ret))))))))
|
||||
|
||||
;; Naive (non-memoized) Fibonacci function
|
||||
(def! fib
|
||||
(fn* [n]
|
||||
(if (<= n 1)
|
||||
n
|
||||
(+ (fib (- n 1)) (fib (- n 2))))))
|
||||
nil
|
||||
|
||||
;; Benchmarks for memoize.mal
|
||||
|
||||
;; -----------------------------------------------
|
||||
;; Benchmarks
|
||||
(load-file "../lib/heavy_computations.mal") ; fib
|
||||
;; FIXME (load-file "../lib/memoize.mal")
|
||||
(load-file "../lib/perf.mal") ; time
|
||||
|
||||
(load-file "../perf.mal") ; for the 'time' macro
|
||||
(def! N 32)
|
||||
|
||||
;; Benchmark naive 'fib'
|
||||
|
@ -1,3 +1,7 @@
|
||||
;; FIXME lib/pprint.mal
|
||||
;; Pretty printer a MAL object.
|
||||
|
||||
;; FIXME: hide these private routines in a private environment.
|
||||
|
||||
(def! spaces- (fn* [indent]
|
||||
(if (> indent 0)
|
||||
@ -35,6 +39,11 @@
|
||||
(def! pprint (fn* [obj]
|
||||
(println (pp- obj 0))))
|
||||
|
||||
nil
|
||||
|
||||
;; Examples of the pretty printer.
|
||||
|
||||
;; FIXME (load-file "../lib/pprint.mal") and uncomment
|
||||
|
||||
;;(pprint '(7 8 9 "ten" [11 12 [13 14]] 15 16))
|
||||
;;(pprint '{:abc 123 :def {:ghi 456 :jkl [789 "ten eleven twelve"]}})
|
||||
|
@ -1,3 +1,4 @@
|
||||
;; FIXME lib/memoize.mal
|
||||
;; A sketch of Clojure-like protocols, implemented in Mal
|
||||
;; By chouser (Chris Houser)
|
||||
;; Original: https://gist.github.com/Chouser/6081ea66d144d13e56fc
|
||||
@ -40,8 +41,11 @@
|
||||
(if (first more)
|
||||
(apply extend type more)))))
|
||||
|
||||
;;----
|
||||
;; Example:
|
||||
nil
|
||||
|
||||
;; Examples for protocols.
|
||||
|
||||
;; FIXME (load-file "../lib/protocols.mal")
|
||||
|
||||
(def! make-triangle (fn* [o a]
|
||||
^{:type :shape/triangle} {:opposite o, :adjacent a}))
|
||||
|
21
lib/README.md
Normal file
21
lib/README.md
Normal file
@ -0,0 +1,21 @@
|
||||
This directory contains general-purpose reusable code that does not
|
||||
fit in the process like `cond`, `gensym`, `inc`, `not` and `or`.
|
||||
|
||||
The split in small files is motivated by implementations too limited
|
||||
to load a single big file, but MAL has no proper module management.
|
||||
|
||||
However, here are some guidelines.
|
||||
|
||||
- Begin with an one-line ;; short description
|
||||
|
||||
- End with `nil`, so that the result of `load-file` is conveniently
|
||||
short when loading manually and predictilbe for automatic testing
|
||||
|
||||
- Describe the restrictions on each parameter in comments.
|
||||
|
||||
- Define private symbols in hidden environments when possible. If this
|
||||
is not possible, for example for macros, give them a name starting
|
||||
with an underscore.
|
||||
|
||||
- Support successive imports safely by giving the same definitions
|
||||
again.
|
@ -1,7 +1,5 @@
|
||||
;; Kind of standard library for MAL for tests and examples.
|
||||
|
||||
;; `cond` `gensym` `inc` `not` `or` would make sense here but are
|
||||
;; already defined in the step files as part of the process.
|
||||
;; FIXME: trivial.mal
|
||||
;; Trivial but convenient functions.
|
||||
|
||||
;; Integer predecessor (number -> number)
|
||||
(def! dec (fn* (a) (- a 1)))
|
||||
@ -9,6 +7,9 @@
|
||||
;; Integer nullity test (number -> boolean)
|
||||
(def! zero? (fn* (n) (= 0 n)))
|
||||
|
||||
;; FIXME: folds.mal
|
||||
;; Left and right folds.
|
||||
|
||||
;; Left fold (f (.. (f (f init x1) x2) ..) xn)
|
||||
(def! reduce
|
||||
(fn* (f init xs)
|
||||
@ -29,15 +30,23 @@
|
||||
;; init : Accumulator
|
||||
;; xs : sequence of Elements x1 x2 .. xn
|
||||
;; return : Accumulator
|
||||
;; FIXME: pass f and xs and build this only once in a private env
|
||||
(let* [rec (fn* [acc index]
|
||||
(if (< index 0)
|
||||
acc
|
||||
(rec (f (nth xs index) acc) (dec index))))]
|
||||
;; FIXME stop using dec or load trivial.mal
|
||||
(rec init (dec (count xs))))))
|
||||
|
||||
nil
|
||||
|
||||
;; FIXME: lib/trivial.mal
|
||||
;; Returns the unchanged argument.
|
||||
(def! identity (fn* (x) x))
|
||||
|
||||
;; FIXME: test_cascade.mal
|
||||
;; Iteration on evaluations interpreted as boolean values.
|
||||
|
||||
;; Conjonction of predicate values (pred x1) and .. and (pred xn)
|
||||
;; Evaluate `pred x` for each `x` in turn. Return `false` if a result
|
||||
;; is `nil` or `false`, without evaluating the predicate for the
|
||||
@ -47,6 +56,7 @@
|
||||
;; pred : Element -> interpreted as a logical value
|
||||
;; xs : sequence of Elements x1 x2 .. xn
|
||||
;; return : boolean
|
||||
;; FIXME: use cond
|
||||
(if (empty? xs)
|
||||
true
|
||||
(if (pred (first xs))
|
||||
@ -64,23 +74,27 @@
|
||||
;; return : boolean
|
||||
(if (empty? xs)
|
||||
nil
|
||||
;; FIXME use or
|
||||
(let* (res (pred (first xs)))
|
||||
(if res
|
||||
res
|
||||
(some pred (rest xs)))))))
|
||||
|
||||
;; Search for first successful evaluation.
|
||||
;; Search for first evaluation returning `nil` or `false`.
|
||||
;; Rewrite `x1 x2 .. xn x` as
|
||||
;; (let* [r1 x1]
|
||||
;; (if r1 test1
|
||||
;; (let* [r2 x2]
|
||||
;; ..
|
||||
;; (if rn
|
||||
;; rn
|
||||
;; x) ..)))
|
||||
;; x
|
||||
;; rn) ..)
|
||||
;; r1))
|
||||
;; Without arguments, returns `true`.
|
||||
(defmacro! and
|
||||
(fn* (& xs)
|
||||
;; Arguments and the result are interpreted as boolean values.
|
||||
;; FIXME: use cond
|
||||
(if (empty? xs)
|
||||
true
|
||||
(if (= 1 (count xs))
|
||||
@ -89,7 +103,11 @@
|
||||
`(let* (~condvar ~(first xs))
|
||||
(if ~condvar (and ~@(rest xs)) ~condvar)))))))
|
||||
|
||||
;; FIXME: composition.mal
|
||||
;; Composition of partially applied functions.
|
||||
|
||||
;; FIXME (load-file "../lib/folds.mal") ; reduce
|
||||
|
||||
;; Rewrite x (a a1 a2) .. (b b1 b2) as
|
||||
;; (b (.. (a x a1 a2) ..) b1 b2)
|
||||
;; If anything else than a list is found were `(a a1 a2)` is expected,
|
||||
@ -97,6 +115,7 @@
|
||||
;; equivalent to `-> x (list a)`.
|
||||
(defmacro! ->
|
||||
(fn* (x & xs)
|
||||
;; FIXME define this only once
|
||||
(let* [f (fn* [acc form]
|
||||
(if (list? form)
|
||||
`(~(first form) ~acc ~@(rest form))
|
||||
@ -110,12 +129,11 @@
|
||||
;; (b b1 b2 (.. (a a1 a2 x) ..)).
|
||||
(defmacro! ->>
|
||||
(fn* (x & xs)
|
||||
;; FIXME define this only once
|
||||
(let* [f (fn* [acc form]
|
||||
(if (list? form)
|
||||
`(~(first form) ~@(rest form) ~acc)
|
||||
(list form acc)))]
|
||||
(reduce f x xs))))
|
||||
|
||||
;; This `nil` is intentional so that the result of doing `load-file` is
|
||||
;; `nil` instead of whatever happens to be at the end of `core.mal`.
|
||||
nil
|
@ -1,22 +1,30 @@
|
||||
;;
|
||||
;; equality.mal
|
||||
;;
|
||||
|
||||
;; This file checks whether the `=` function correctly implements equality of
|
||||
;; hash-maps and sequences (lists and vectors). If not, it redefines the `=`
|
||||
;; function with a pure mal (recursive) implementation that only relies on the
|
||||
;; native original `=` function for comparing scalars (integers, booleans,
|
||||
;; symbols, strings).
|
||||
;;
|
||||
|
||||
;; Save the original (native) `=` as scalar-equal?
|
||||
(def! scalar-equal? =)
|
||||
|
||||
;; FIXME: test this
|
||||
;; A faster `and` macro which doesn't use `=` internally.
|
||||
;; (defmacro! and2 ; `true` or `nil`
|
||||
;; (fn* [& xs] ; interpreted as logical values
|
||||
;; (if (empty? xs)
|
||||
;; true
|
||||
;; `(if ~(first xs) (and2 ~(rest xs))))))
|
||||
;; A simple `and` macro for two argument which doesn't use `=` internally
|
||||
(defmacro! and2
|
||||
(fn* [a b]
|
||||
`(let* (and2_FIXME ~a)
|
||||
(if and2_FIXME ~b and2_FIXME))))
|
||||
|
||||
;; FIXME: in the following, replace (and2 a (and2 b c)) with (and2 a b c),
|
||||
;; replace (if a b false) with (and2 a b).
|
||||
|
||||
;; Implement `=` for two sequential arguments
|
||||
(def! sequential-equal?
|
||||
(fn* [a b]
|
||||
@ -76,3 +84,5 @@
|
||||
(do
|
||||
(def! = mal-equal?)
|
||||
(println "equality.mal: Replaced = with pure mal implementation")))
|
||||
|
||||
nil
|
19
lib/heavy_computations.mal
Normal file
19
lib/heavy_computations.mal
Normal file
@ -0,0 +1,19 @@
|
||||
;; Some inefficient arithmetic computations for benchmarking.
|
||||
|
||||
;; Unfortunately not yet available in tests of steps 4 and 5.
|
||||
|
||||
;; Compute n(n+1)/2 with a non tail-recursive call.
|
||||
(def! sumdown
|
||||
(fn* [n] ; non-negative number
|
||||
(if (= n 0)
|
||||
0
|
||||
(+ n (sumdown (- n 1))))))
|
||||
|
||||
;; Compute a Fibonacci number with two recursions.
|
||||
(def! fib
|
||||
(fn* [n] ; non-negative number
|
||||
(if (<= n 1)
|
||||
n
|
||||
(+ (fib (- n 1)) (fib (- n 2))))))
|
||||
|
||||
nil
|
@ -1,3 +1,6 @@
|
||||
;; Mesure performances.
|
||||
|
||||
;; Evaluate an expression, but report the time spent
|
||||
(defmacro! time
|
||||
(fn* (exp)
|
||||
`(let* (start_FIXME (time-ms)
|
||||
@ -6,6 +9,7 @@
|
||||
(prn (str "Elapsed time: " (- (time-ms) start_FIXME) " msecs"))
|
||||
ret_FIXME))))
|
||||
|
||||
;; FIXME: hide this private routine in a private environment
|
||||
(def! run-fn-for*
|
||||
(fn* [fn max-ms acc-ms last-iters]
|
||||
(let* [start (time-ms)
|
||||
@ -18,6 +22,8 @@
|
||||
last-iters
|
||||
(run-fn-for* fn max-ms new-acc-ms iters)))))
|
||||
|
||||
;; Count evaluations of a function during a given time frame.
|
||||
;; FIXME: max-secs is a number.
|
||||
(def! run-fn-for
|
||||
(fn* [fn max-secs]
|
||||
(do
|
||||
@ -25,3 +31,5 @@
|
||||
(run-fn-for* fn 1000 0 0)
|
||||
;; Now do the test
|
||||
(run-fn-for* fn (* 1000 max-secs) 0 0))))
|
||||
|
||||
nil
|
@ -1,5 +1,5 @@
|
||||
(load-file "../core.mal")
|
||||
(load-file "../perf.mal")
|
||||
(load-file "../lib/core.mal") ; or ->
|
||||
(load-file "../lib/perf.mal") ; time
|
||||
|
||||
;;(prn "Start: basic macros performance test")
|
||||
|
||||
|
@ -1,11 +1,8 @@
|
||||
(load-file "../core.mal")
|
||||
(load-file "../perf.mal")
|
||||
(load-file "../lib/heavy_computations.mal") ; fib sumdown
|
||||
(load-file "../lib/perf.mal") ; time
|
||||
|
||||
;;(prn "Start: basic math/recursion test")
|
||||
|
||||
(def! sumdown (fn* (N) (if (> N 0) (+ N (sumdown (- N 1))) 0)))
|
||||
(def! fib (fn* (N) (if (= N 0) 1 (if (= N 1) 1 (+ (fib (- N 1)) (fib (- N 2)))))))
|
||||
|
||||
(time (do
|
||||
(sumdown 10)
|
||||
(fib 12)))
|
||||
|
@ -1,5 +1,5 @@
|
||||
(load-file "../core.mal")
|
||||
(load-file "../perf.mal")
|
||||
(load-file "../lib/core.mal") ; or ->
|
||||
(load-file "../lib/perf.mal") ; run-fn-for
|
||||
|
||||
;;(prn "Start: basic macros/atom test")
|
||||
|
||||
|
@ -150,8 +150,7 @@ x
|
||||
;=>"yes"
|
||||
|
||||
;;
|
||||
;; Loading core.mal
|
||||
(load-file "../core.mal")
|
||||
(load-file "../lib/core.mal")
|
||||
|
||||
;; Testing reduce
|
||||
(reduce + 7 [])
|
||||
|
@ -286,12 +286,14 @@
|
||||
(let* [or_FIXME 23] (or false (+ or_FIXME 100)))
|
||||
;=>123
|
||||
|
||||
|
||||
;;
|
||||
;; Testing time-ms function
|
||||
(load-file "../lib/heavy_computations.mal")
|
||||
(def! start-time (time-ms))
|
||||
(= start-time 0)
|
||||
;=>false
|
||||
(let* [sumdown (fn* (N) (if (> N 0) (+ N (sumdown (- N 1))) 0))] (sumdown 10)) ; Waste some time
|
||||
(sumdown 10) ; Waste some time
|
||||
;=>55
|
||||
(> (time-ms) start-time)
|
||||
;=>true
|
||||
|
Loading…
Reference in New Issue
Block a user