mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-31 15:43:01 +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:
		| @@ -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; | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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")) | ||||
|   | ||||
| @@ -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") | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Calvin Rose
					Calvin Rose