1
0
mirror of https://github.com/janet-lang/janet synced 2024-11-28 11:09:54 +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() {
JanetTable *t1;
JanetTable *t1, *t2;
janet_init();
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("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("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();
return 0;

View File

@ -1,6 +1,6 @@
# A game of life implementation
(def- windows
(def- window
(fora [x :range [-1 2]
y :range [-1 2]
:when (not (and (zero? x) (zero? y)))]
@ -8,38 +8,37 @@
(defn- neighbors
[[x y]]
(mapa (fn [[x1 y1]] (tuple (+ x x1) (+ y y1))) windows))
(mapa (fn [[x1 y1]] (tuple (+ x x1) (+ y y1))) window))
(defn tick
"Get the next state in the Game Of Life."
[state]
(def neighbor-set (frequencies (mapcat neighbors (keys state))))
(def next-state @{})
(loop [coord :keys neighbor-set
:let [count (get neighbor-set coord)]]
(if (if (get state coord)
(or (= count 2) (= count 3))
(= count 3))
(put next-state coord true)))
next-state)
(def cell-set (frequencies state))
(def neighbor-set (frequencies (mapcat neighbors state)))
(fora [coord :keys neighbor-set
:let [count (get neighbor-set coord)]
:when (or (= count 3) (and (get cell-set coord) (= count 2)))]
coord))
(defn draw
"Draw cells in the game of life from (x1, y1) to (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))) "+")
:after (print "+" (string.repeat "--" (inc (- y2 y1))) "+")
x :range [x1 (+ 1 x2)]
:before (file.write stdout "|")
:after (file.write stdout "|\n")
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))
#
# 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]]
(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
and end value. The range is half open, [start, end).\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
the form:\n\n
\t:modifier argument\n\n
@ -294,11 +296,9 @@
(if (>= i len)
(tuple.prepend body 'do)
(do
(def {
i bindings
(def {i bindings
(+ i 1) verb
(+ i 2) object
} head)
(+ i 2) object} head)
(if (keyword? bindings)
(case bindings
:while (do
@ -371,8 +371,25 @@
(tuple 'def bindings (tuple get $indexed $i))
subloop
(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)))))))
(doone 0 nil))
(tuple 'do (doone 0 nil) nil))
(defmacro fora
"Similar to loop, but accumulates the loop body into an array and returns that.
@ -398,6 +415,13 @@
(tuple.prepend body 'do)))
(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]
(var accum 0)
(loop [x :in xs] (+= accum x))
@ -1146,7 +1170,6 @@ value, one key will be ignored."
(if (bytes? x) x (string.pretty x))
"\n")
(when f
(def st (fiber.stack f))
(loop
[{:function func
:tail tail
@ -1155,7 +1178,7 @@ value, one key will be ignored."
:name name
:source source
:line source-line
:column source-col} :in st]
:column source-col} :in (fiber.stack f)]
(file.write stderr " in")
(when c (file.write stderr " cfunction"))
(if name
@ -1329,7 +1352,7 @@ value, one key will be ignored."
(def buf @"")
(default onvalue (fn [x]
(put newenv '_ @{:value x})
(print (string.pretty x 8 buf))
(print (string.pretty x 20 buf))
(buffer.clear buf)))
(default onerr default-error-handler)
(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")
# 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
(def x1 100)
(assert (= x1 100) "x1 as symbol")