1
0
mirror of https://github.com/janet-lang/janet synced 2024-11-25 01:37:19 +00:00

Add generator expressions for easier iteration.

Similar to python generator, but with the same
syntax as the loop macro.
This commit is contained in:
Calvin Rose 2018-11-20 21:48:06 -05:00
parent 1e87b01e02
commit 184fe31e0c
4 changed files with 62 additions and 23 deletions

View File

@ -25,11 +25,12 @@
int main() { int main() {
JanetTable *t1; JanetTable *t1, *t2;
janet_init(); janet_init();
t1 = janet_table(10); t1 = janet_table(10);
t2 = janet_table(0);
janet_table_put(t1, janet_cstringv("hello"), janet_wrap_integer(2)); janet_table_put(t1, janet_cstringv("hello"), janet_wrap_integer(2));
janet_table_put(t1, janet_cstringv("akey"), janet_wrap_integer(5)); janet_table_put(t1, janet_cstringv("akey"), janet_wrap_integer(5));
@ -52,6 +53,14 @@ int main() {
assert(janet_equals(janet_table_get(t1, janet_cstringv("hello")), janet_wrap_nil())); assert(janet_equals(janet_table_get(t1, janet_cstringv("hello")), janet_wrap_nil()));
assert(janet_equals(janet_table_get(t1, janet_cstringv("box")), janet_wrap_nil())); assert(janet_equals(janet_table_get(t1, janet_cstringv("box")), janet_wrap_nil()));
janet_table_put(t2, janet_csymbolv("t2key1"), janet_wrap_integer(10));
janet_table_put(t2, janet_csymbolv("t2key2"), janet_wrap_integer(100));
janet_table_put(t2, janet_csymbolv("some key "), janet_wrap_integer(-2));
janet_table_put(t2, janet_csymbolv("a thing"), janet_wrap_integer(10));
assert(janet_equals(janet_table_get(t2, janet_csymbolv("t2key1")), janet_wrap_integer(10)));
assert(janet_equals(janet_table_get(t2, janet_csymbolv("t2key2")), janet_wrap_integer(100)));
janet_deinit(); janet_deinit();
return 0; return 0;

View File

@ -1,6 +1,6 @@
# A game of life implementation # A game of life implementation
(def- windows (def- window
(fora [x :range [-1 2] (fora [x :range [-1 2]
y :range [-1 2] y :range [-1 2]
:when (not (and (zero? x) (zero? y)))] :when (not (and (zero? x) (zero? y)))]
@ -8,38 +8,37 @@
(defn- neighbors (defn- neighbors
[[x y]] [[x y]]
(mapa (fn [[x1 y1]] (tuple (+ x x1) (+ y y1))) windows)) (mapa (fn [[x1 y1]] (tuple (+ x x1) (+ y y1))) window))
(defn tick (defn tick
"Get the next state in the Game Of Life." "Get the next state in the Game Of Life."
[state] [state]
(def neighbor-set (frequencies (mapcat neighbors (keys state)))) (def cell-set (frequencies state))
(def next-state @{}) (def neighbor-set (frequencies (mapcat neighbors state)))
(loop [coord :keys neighbor-set (fora [coord :keys neighbor-set
:let [count (get neighbor-set coord)]] :let [count (get neighbor-set coord)]
(if (if (get state coord) :when (or (= count 3) (and (get cell-set coord) (= count 2)))]
(or (= count 2) (= count 3)) coord))
(= count 3))
(put next-state coord true)))
next-state)
(defn draw (defn draw
"Draw cells in the game of life from (x1, y1) to (x2, y2)" "Draw cells in the game of life from (x1, y1) to (x2, y2)"
[state x1 y1 x2 y2] [state x1 y1 x2 y2]
(def cellset @{})
(loop [cell :in state] (put cellset cell true))
(loop [:before (print "+" (string.repeat "--" (inc (- y2 y1))) "+") (loop [:before (print "+" (string.repeat "--" (inc (- y2 y1))) "+")
:after (print "+" (string.repeat "--" (inc (- y2 y1))) "+") :after (print "+" (string.repeat "--" (inc (- y2 y1))) "+")
x :range [x1 (+ 1 x2)] x :range [x1 (+ 1 x2)]
:before (file.write stdout "|") :before (file.write stdout "|")
:after (file.write stdout "|\n") :after (file.write stdout "|\n")
y :range [y1 (+ 1 y2)]] y :range [y1 (+ 1 y2)]]
(file.write stdout (if (get state (tuple x y)) "X " ". "))) (file.write stdout (if (get cellset (tuple x y)) "X " ". ")))
(print)) (print))
# #
# Run the example # Run the example
# #
(var *state* {'(0 0) true '(-1 0) true '(1 0) true '(1 1) true '(0 2) true}) (var *state* '[(0 0) (-1 0) (1 0) (1 1) (0 2)])
(loop [i :range [0 20]] (loop [i :range [0 20]]
(print "generation " i) (print "generation " i)

View File

@ -271,7 +271,9 @@
\t:range - loop over a range. The object should be two element tuple with a start \t:range - loop over a range. The object should be two element tuple with a start
and end value. The range is half open, [start, end).\n and end value. The range is half open, [start, end).\n
\t:keys - Iterate over the keys in a data structure.\n \t:keys - Iterate over the keys in a data structure.\n
\t:in - Iterate over the values in an indexed data structure or byte sequence.\n\n \t:in - Iterate over the values in an indexed data structure or byte sequence.\n
\t:generate - Iterate over values yielded from a fiber. Can be paired with the generator
function for the producer/consumer pattern.\n\n
loop also accepts conditionals to refine the looping further. Conditionals are of loop also accepts conditionals to refine the looping further. Conditionals are of
the form:\n\n the form:\n\n
\t:modifier argument\n\n \t:modifier argument\n\n
@ -294,11 +296,9 @@
(if (>= i len) (if (>= i len)
(tuple.prepend body 'do) (tuple.prepend body 'do)
(do (do
(def { (def {i bindings
i bindings
(+ i 1) verb (+ i 1) verb
(+ i 2) object (+ i 2) object} head)
} head)
(if (keyword? bindings) (if (keyword? bindings)
(case bindings (case bindings
:while (do :while (do
@ -371,8 +371,25 @@
(tuple 'def bindings (tuple get $indexed $i)) (tuple 'def bindings (tuple get $indexed $i))
subloop subloop
(tuple ':= $i (tuple + 1 $i))))) (tuple ':= $i (tuple + 1 $i)))))
:generate (do
(def $fiber (gensym))
(def $yieldval (gensym))
(def preds @['and
(do
(def s (gensym))
(tuple 'do
(tuple 'def s (tuple fiber.status $fiber))
(tuple 'or (tuple = s :pending) (tuple = s :new))))])
(def subloop (doone (+ i 3) preds))
(tuple 'do
(tuple 'def $fiber object)
(tuple 'var $yieldval (tuple resume $fiber))
(tuple 'while (tuple.slice preds 0)
(tuple 'def bindings $yieldval)
subloop
(tuple := $yieldval (tuple resume $fiber)))))
(error (string "unexpected loop verb: " verb))))))) (error (string "unexpected loop verb: " verb)))))))
(doone 0 nil)) (tuple 'do (doone 0 nil) nil))
(defmacro fora (defmacro fora
"Similar to loop, but accumulates the loop body into an array and returns that. "Similar to loop, but accumulates the loop body into an array and returns that.
@ -398,6 +415,13 @@
(tuple.prepend body 'do))) (tuple.prepend body 'do)))
(tuple tuple.slice $accum 0))) (tuple tuple.slice $accum 0)))
(defmacro generate
"Create a generator expression using the loop syntax. Returns a fiber
that yields all values inside the loop in order. See loop for details."
[head & body]
(tuple fiber.new
(tuple 'fn @[] (tuple 'loop head (tuple yield (tuple.prepend body 'do))))))
(defn sum [xs] (defn sum [xs]
(var accum 0) (var accum 0)
(loop [x :in xs] (+= accum x)) (loop [x :in xs] (+= accum x))
@ -1146,7 +1170,6 @@ value, one key will be ignored."
(if (bytes? x) x (string.pretty x)) (if (bytes? x) x (string.pretty x))
"\n") "\n")
(when f (when f
(def st (fiber.stack f))
(loop (loop
[{:function func [{:function func
:tail tail :tail tail
@ -1155,7 +1178,7 @@ value, one key will be ignored."
:name name :name name
:source source :source source
:line source-line :line source-line
:column source-col} :in st] :column source-col} :in (fiber.stack f)]
(file.write stderr " in") (file.write stderr " in")
(when c (file.write stderr " cfunction")) (when c (file.write stderr " cfunction"))
(if name (if name
@ -1329,7 +1352,7 @@ value, one key will be ignored."
(def buf @"") (def buf @"")
(default onvalue (fn [x] (default onvalue (fn [x]
(put newenv '_ @{:value x}) (put newenv '_ @{:value x})
(print (string.pretty x 8 buf)) (print (string.pretty x 20 buf))
(buffer.clear buf))) (buffer.clear buf)))
(default onerr default-error-handler) (default onerr default-error-handler)
(run-context newenv getchunk onvalue onerr "repl")) (run-context newenv getchunk onvalue onerr "repl"))

View File

@ -46,6 +46,14 @@
(assert (= txs '[[-1 -1] [-1 0] [-1 1] [0 -1] [0 1] [1 -1] [1 0] [1 1]]) "nested for") (assert (= txs '[[-1 -1] [-1 0] [-1 1] [0 -1] [0 1] [1 -1] [1 0] [1 1]]) "nested for")
# Generators
(def gen (generate [x :range [0 100] :when (pos? (% x 4))] x))
(var gencount 0)
(loop [x :generate gen]
(++ gencount)
(assert (pos? (% x 4)) "generate in loop"))
(assert (= gencount 75) "generate loop count")
# Check x:digits: works as symbol and not a hex number # Check x:digits: works as symbol and not a hex number
(def x1 100) (def x1 100)
(assert (= x1 100) "x1 as symbol") (assert (= x1 100) "x1 as symbol")