Skip to content

Instantly share code, notes, and snippets.

@dander
Last active August 27, 2020 07:29
Show Gist options
  • Save dander/66ad65555b30286115cee675b16bf1d7 to your computer and use it in GitHub Desktop.
Save dander/66ad65555b30286115cee675b16bf1d7 to your computer and use it in GitHub Desktop.
An experiment to make a general purpose generator-creating function
Red [
Title: "generator function experiments"
Author: "Dave Andersen"
Date: 27-Sep-2017
]
reload: does [do system/options/script]
; @JacobGood's closure func
closure1: func [
vars [block!]
spec [block!]
body [block!]
][
; Can't use `function` here, because it will collect set-words
; in the body, which may be closure vars.
func spec compose [(bind body context vars)]
]
; @Nenad's closure func
closure2: func [vars spec body][
bind (body-of spec: func spec body) (context vars)
:spec
]
closure2: func [vars spec body][bind body-of spec: func spec body context vars :spec]
; @9214's closure modification
closure3: func [vars spec body][func spec bind body context vars]
closure: :closure3
enclose1: func [args /local w][
collect [foreach w args [
if word? w [keep to-set-word w keep/only to-paren w]
]]
]
enclose2: func [args /local w][
context compose/only collect [foreach w args [
if word? w [keep to-set-word w keep/only to-paren w]
]]
]
enclose3: func [args /local w][
collect [foreach w args [
if word? w [keep to-set-word w keep 'quote keep/only to-paren reduce ['get to-lit-word w]]
]]
]
enclose: :enclose3
generator1: func [
{Defines a generator with the given spec}
spec [block!] "generator parameters"
body [block!] "body of the function returned by the generator"
][
closure
compose/only enclose [spec body]
spec
compose/only [
closure compose/only (enclose spec) [] (body)
]
]
generator2: func [
{Defines a generator with the given spec}
spec [block!] "generic spec for the defined generator"
body [block!] "body of the function returned by the generator"
][
closure
enclose2 [spec body]
spec
compose/only [closure (enclose spec) [] body]
]
generator: :generator1
++: func [
"Increment a value by 1"
'value [word! path!] "thing to be incremented"
/after "return the current value, then increment"
][
either after [
also get value
set value (get value) + 1
][
set value (get value) + 1
]
]
ticker: generator [
"Generate numbers counting up from i indefinitely"
i [number!]
][
++/after i
]
range: generator [
"Generate numbers from start to finish, inclusive"
start [number!] finish [number!]
][
if start <= finish [++/after start]
]
..: make op! :range
iter: generator [
"Iterate over the items in a series"
series [series!]
][
also series/1
series: next series
]
blockify: function [
"Produce a block from the output of a generator"
f [function!]
][
collect [while [value: f] [keep value]]
]
cycler: generator [
"Loop over the items in a series"
series [series!]
][
if tail? series [series: head series]
also :series/1
series: next series
]
limit1: function [
"Generate up to 'n' items from a series or generator function"
sequence [series! function!]
n [integer!]
][
gen: generator [i n][
if n < 1 [return none]
n: n - 1
i
]
gen either function? :sequence [:sequence][iter sequence] n
]
limit-gen: generator [
"Generate up to 'n' items from a generator function"
f [function!]
n [integer!]
][
if n < 1 [return none]
n: n - 1
f
]
limit: func [f n][
either series? :f [
limit-gen iter :f n
][
limit-gen :f n
]
]
zip: function [
"Generate the outputs from two functions interleaved together"
f1 [function!] f2 [function!]
][
select context [
cyc: cycler reduce [:f1 :f2]
gen: func [/local f][f: cyc f]
]
'gen
]
apply: generator [
"Apply a function to the items of a generator"
g [function!] "generator"
f [function!] "function to apply"
/local x
][
x: g
if x [f x]
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment