raskell 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +72 -0
- data/ROADMAP.md +15 -0
- data/lib/raskell.rb +11 -0
- data/lib/raskell/array.rb +209 -0
- data/lib/raskell/f.rb +1448 -0
- data/lib/raskell/folds.rb +188 -0
- data/lib/raskell/identity.rb +34 -0
- data/lib/raskell/integer.rb +23 -0
- data/lib/raskell/nothing.rb +5 -0
- data/lib/raskell/object.rb +27 -0
- data/lib/raskell/proc.rb +111 -0
- data/lib/raskell/streams.rb +440 -0
- data/lib/version.rb +3 -0
- metadata +58 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4795026fb8eba55958eb0ae8b7188e8e50e20cbe
|
4
|
+
data.tar.gz: c8ce90fb43aea46b81f1360bf5664e3fb164bf1b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 58fdef50dd65ad6bb1eea500941787b1c84b6da7f61e948e43962dc92f7eb14c8352efdfc1f4ccef2783d95524b306fa887eaa802c107e815d68c36d088bbd84
|
7
|
+
data.tar.gz: 19b932ac37c062a5b94682ce5252838935e5c970a99101baf465a20a1b08dcae2bd9accb4486f2a2870d7061b431cec60fc982a0c70693f87e6afa6b4dbc7dad
|
data/README.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# raskell
|
2
|
+
Making Ruby a Joy to Work With
|
3
|
+
=======
|
4
|
+
|
5
|
+
|
6
|
+
Usage:
|
7
|
+
|
8
|
+
Reminder: Lambdas can be applied with [\*args] notation and .() is syntactic sugar for .call()
|
9
|
+
```f = ->(x) { x + 2 } ```
|
10
|
+
```f[1]``` evaluates as ```3```
|
11
|
+
|
12
|
+
|
13
|
+
Lambdas can be partially applied, yielding a new lambda, with call
|
14
|
+
```plus = ->(x,y) { x + y }```
|
15
|
+
```plus10 = plus.(10) ## or plus[10]```
|
16
|
+
```plus10.(20)``` evaluates as ```30```
|
17
|
+
|
18
|
+
Lambda Composition with \* (right-associative) ```( (f * g * h).(x) == f.(g.(h.(x))))```
|
19
|
+
```times10 = ->(x) { x * 10 }```
|
20
|
+
```minus3 = ->(x) { x - 3 }```
|
21
|
+
```double = ->(x) { x * 2 }```
|
22
|
+
|
23
|
+
```(times10 * minus3 * double).(5)``` evaluates as ```70```
|
24
|
+
```(double * minus3 * times10).(5)``` evaluates as ```94```
|
25
|
+
|
26
|
+
|
27
|
+
Lambda Pipelining with \| (left-associative) ``` (f | g | h).(x) == h.(g.(f.(x))) ```
|
28
|
+
```(times10 | minus3 | double).(5)``` evaluates as ```94```
|
29
|
+
```(double | minus3 | times10).(5)``` evaluates as ```70```
|
30
|
+
|
31
|
+
Lambda Tupling with + (associative)
|
32
|
+
```(times10 + minus3 + double).(5)``` evaluates as ```[50, 2, 10]```
|
33
|
+
|
34
|
+
Objects, when called, act like constant functions that throw away any values applied to them
|
35
|
+
```5.call(1,2,3,[4,7])``` evaluates to ```5```
|
36
|
+
|
37
|
+
Arrays, when called, map across themselves calling each element with the arguments it was called with
|
38
|
+
```[times10, minus3, double].(5)``` evaluates to ```5```
|
39
|
+
```[plus, times10, 3].(0,1)``` evaluates to ```[1, 0, 3]```
|
40
|
+
Note that ```[plus,times10,3][0,1]``` evaluates to ```[plus]```, not ```[1, 0, 3]```, so be careful where you use ```func[]``` as shorthand ```func.()``` or ```func.call()```!
|
41
|
+
|
42
|
+
Streams, when called, map across themselves calling each element with the arguments it was called with
|
43
|
+
```[times10, minus3, double].to_stream.(5)``` evaluates to ```5```
|
44
|
+
```[plus, times10, 3].to_stream.(0,1)``` evaluates to ```[1, 0, 3].to_stream```
|
45
|
+
Note that ```[plus,times10,3].to_stream[0,1]``` evaluates to ```[plus].to_stream```, not ```[1, 0, 3].to_stream```, so be careful where you use ```func[]``` as shorthand ```func.()``` or ```func.call()```!
|
46
|
+
|
47
|
+
Preface any collection function with F. to call that particular function
|
48
|
+
```F.map(times10 * plus10).([1,2,3])``` evaluates as ```[100, 200, 300]```
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
Available Operators to Overload in Ruby
|
54
|
+
|
55
|
+
|
56
|
+
(unary)
|
57
|
+
!, ~, +, \-
|
58
|
+
|
59
|
+
(binary)
|
60
|
+
\*\*, \*, /, %, +, \-, <<, >>, &, \|, ^, ||, &&
|
61
|
+
=, +=, \*=, -=,
|
62
|
+
<, <=, >=, >, ==, ===, !=, =~, !~, <=>
|
63
|
+
[],[]=
|
64
|
+
|
65
|
+
Using in Raskell so far
|
66
|
+
[], \*, \*\*, ^, \|, &, +, %, <<, >>
|
67
|
+
|
68
|
+
=~ and !~ will be good for later when I have 'regular expressions' over arbitrary asterated semirings - then i can match if the data, path, whatever matches a regex of allowable types - and this gives us a powerful form of type constraint for free
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
data/ROADMAP.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Making Ruby a "Joy" to Work With In X (Easy?) Steps
|
2
|
+
|
3
|
+
\*I) partial application for lambdas, treat [] as .call() for lambdas, and add .call(\*args) to objects (a no-op) and arrays (map across calling element.call(\*args) for each element)
|
4
|
+
\*II) function tupling, composition, and pipelining in the form of +,\*, and \|
|
5
|
+
\*III) a very lightweight testing framework, DoubleCheck, built using the above as a demo, along with tests for everything previously built
|
6
|
+
\*IV) a standard collections library based around fusable stream transducers - (flat)map, filter, fold\*, zip, append, scanl - Scala/Haskell eqv.
|
7
|
+
\*V) add instances of from_stream(ClassName=Array) and to_stream for Dictionary, (Multi?)Set, Array, Range, String, Integer, Object, and Enumerable
|
8
|
+
\*VI) add tupling for foldl, and (map / scanl with each other, really anything that produces as many outputs as it takes in - so might need a scanl1 and a foldl1, so we can combine any/all of them together and still succeed in only consuming a stream once) as well, so that multiple transducers can run in parallel across the same stream in a single pass
|
9
|
+
\*VII) Implement Applicative instances for Array and Stream
|
10
|
+
VIII) organize code so that it is clear what consumes the entire stream strictly (foldl-based), what consumes the entire stream lazily (scanl, everything else pretty much), and what can be combined via + to avoid multiple passes over a single stream when computing multiple functions of the stream (foldl, scanl) - example, ```(F.length + F.sum + F.product + F.sum_of_squares).([1,2,3,4].to_stream)``` computes all of those statistics in a single pass, yielding ```[4, 10, 24, 30]```, and make sure that all functions are as optimized and elegantly defined as possible. -- probably need a specialized one for very large lists, one for infinite, and one for the rest
|
11
|
+
IX) modularize for easy addition into other's projects, optimize automatically in the common cases (use array-based functions when possible instead of stream)
|
12
|
+
X) Make everything that isn't a function a 'constant' function that implicitly pushes to an ArgumentStack once lifted, and then returns the ArgumentStack. Modify lambda calling to properly use argument stack if it is passed in, and to place unused arguments onto after calling - forking lambdas - i.e. a [fn, fn2,fn3,fn4].(), duplicate argument stacks and create unique new ones in their locations, rather than 'sharing' a common argument stack - turning Raskell into a concatenative language - a "Joy" to *work* with
|
13
|
+
|
14
|
+
|
15
|
+
get rid of ^ for cartesian product, use for * instance instead
|
data/lib/raskell.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require './lib/raskell/nothing'
|
2
|
+
require './lib/raskell/identity'
|
3
|
+
require './lib/raskell/integer'
|
4
|
+
require './lib/raskell/object'
|
5
|
+
require './lib/raskell/streams'
|
6
|
+
require './lib/raskell/proc'
|
7
|
+
require './lib/raskell/array'
|
8
|
+
require './lib/raskell/folds'
|
9
|
+
require './lib/raskell/f'
|
10
|
+
require './lib/version'
|
11
|
+
|
@@ -0,0 +1,209 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
class Array
|
4
|
+
|
5
|
+
def deep_clone
|
6
|
+
map(&:deep_clone)
|
7
|
+
end
|
8
|
+
|
9
|
+
def empty
|
10
|
+
[]
|
11
|
+
end
|
12
|
+
|
13
|
+
def fmap(fn)
|
14
|
+
map {|x| fn.(x) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(*args)
|
18
|
+
#functions = self.map { |x| x.kind_of?(Proc) ? self : ->() { x } }
|
19
|
+
self.any? {|x| x.kind_of? Proc } ? fmap(->(f) { f.(*args)}) : self
|
20
|
+
end
|
21
|
+
|
22
|
+
def take(n)
|
23
|
+
if n == 0
|
24
|
+
[]
|
25
|
+
elsif n >= self.length
|
26
|
+
self
|
27
|
+
else
|
28
|
+
self.slice(0, n)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def drop(n)
|
33
|
+
if n == 0
|
34
|
+
self
|
35
|
+
elsif n >= self.length
|
36
|
+
[]
|
37
|
+
else
|
38
|
+
self.slice(n, self.length)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def foldr(func, unit)
|
43
|
+
(self.length-1).foldr(->(idx, acc) { func.(self[idx], acc) }, unit)
|
44
|
+
end
|
45
|
+
|
46
|
+
def foldl(func, unit)
|
47
|
+
(self.length-1).foldl(->(acc, idx) { func.(acc, self[idx]) }, unit)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.next_item
|
51
|
+
next_fn = ->(state) {
|
52
|
+
i = state.first
|
53
|
+
arr = state.last
|
54
|
+
if i == arr.length
|
55
|
+
[:done]
|
56
|
+
else
|
57
|
+
[:yield, arr[i], Stream.new(next_fn, [i+1, arr])]
|
58
|
+
end
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.to_stream(xs)
|
63
|
+
Stream.new(self.next_item, [0, xs.deep_clone])
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_stream
|
67
|
+
self.class.to_stream(self)
|
68
|
+
end
|
69
|
+
|
70
|
+
alias_method :standard_equals, :==
|
71
|
+
def ==(obj)
|
72
|
+
obj.kind_of?(Stream) ? self.to_stream == obj : standard_equals(obj)
|
73
|
+
end
|
74
|
+
|
75
|
+
alias_method :standard_triple_equals, :===
|
76
|
+
def ===(obj)
|
77
|
+
obj.kind_of?(Stream) ? self.to_stream === obj : standard_triple_equals(obj)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
class String
|
83
|
+
|
84
|
+
def self.empty
|
85
|
+
""
|
86
|
+
end
|
87
|
+
|
88
|
+
def empty?
|
89
|
+
self == ""
|
90
|
+
end
|
91
|
+
|
92
|
+
def first
|
93
|
+
self[0]
|
94
|
+
end
|
95
|
+
|
96
|
+
def take(n)
|
97
|
+
if n == 0
|
98
|
+
""
|
99
|
+
elsif n >= self.length
|
100
|
+
self
|
101
|
+
else
|
102
|
+
self.slice(0, n)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def drop(n)
|
107
|
+
if n == 0
|
108
|
+
self
|
109
|
+
elsif n >= self.length
|
110
|
+
""
|
111
|
+
else
|
112
|
+
self.slice(n, self.length)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.next_item
|
117
|
+
next_fn = ->(xs) { xs.empty? ? [:done] : [:yield, xs.first, Stream.new(next_fn, xs.drop(1))] }
|
118
|
+
next_fn
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.to_stream(xs)
|
122
|
+
Stream.new(self.next_item, xs)
|
123
|
+
end
|
124
|
+
|
125
|
+
def to_stream
|
126
|
+
self.class.to_stream(self)
|
127
|
+
end
|
128
|
+
|
129
|
+
alias_method :standard_equals, :==
|
130
|
+
def ==(obj)
|
131
|
+
obj.kind_of?(Stream) ? self.to_stream == obj : standard_equals(obj)
|
132
|
+
end
|
133
|
+
|
134
|
+
alias_method :standard_triple_equals, :===
|
135
|
+
def ===(obj)
|
136
|
+
obj.kind_of?(Stream) ? self.to_stream === obj : standard_triple_equals(obj)
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
require 'set'
|
142
|
+
|
143
|
+
class Set
|
144
|
+
|
145
|
+
def self.to_stream(set)
|
146
|
+
set.to_a.sort.to_stream
|
147
|
+
end
|
148
|
+
|
149
|
+
def to_stream
|
150
|
+
self.class.to_stream(self)
|
151
|
+
end
|
152
|
+
|
153
|
+
def push(item)
|
154
|
+
self << item
|
155
|
+
self
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
|
162
|
+
class Hash
|
163
|
+
|
164
|
+
def <<(keyval)
|
165
|
+
raise("Can only push pairs into a dictionary") unless (keyval.kind_of?(Array) || keyval.kind_of?(Stream))
|
166
|
+
self[keyval.first]=keyval.last
|
167
|
+
self
|
168
|
+
end
|
169
|
+
|
170
|
+
def push(pair)
|
171
|
+
self <<(pair)
|
172
|
+
self
|
173
|
+
end
|
174
|
+
|
175
|
+
def to_stream
|
176
|
+
## have to sort to ensure stream == works
|
177
|
+
self.class.to_stream(self)
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.to_stream(xs)
|
181
|
+
## have to sort to ensure steream == works
|
182
|
+
xs.to_a.sort {|x,y| x.first <=> y.first}.to_stream
|
183
|
+
end
|
184
|
+
|
185
|
+
alias_method :standard_equals, :==
|
186
|
+
def ==(obj)
|
187
|
+
obj.kind_of?(Stream) ? self.sort.to_stream == obj : standard_equals(obj)
|
188
|
+
end
|
189
|
+
|
190
|
+
alias_method :standard_triple_equals, :===
|
191
|
+
def ===(obj)
|
192
|
+
obj.kind_of?(Stream) ? self.to_stream === obj : standard_triple_equals(obj)
|
193
|
+
end
|
194
|
+
|
195
|
+
def deep_clone
|
196
|
+
Hash.new(self.map{|k,v| [k.deep_clone, v.deep_clone]})
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
# class Range
|
202
|
+
# def to_stream
|
203
|
+
# F.range(self.min, self.max)
|
204
|
+
# end
|
205
|
+
|
206
|
+
# def self.to_stream(range)
|
207
|
+
# F.range.(range.min, range.max)
|
208
|
+
# end
|
209
|
+
# end
|
data/lib/raskell/f.rb
ADDED
@@ -0,0 +1,1448 @@
|
|
1
|
+
|
2
|
+
## module System
|
3
|
+
class F ## @@System||=:F to use 3;) --- or, you can just extend with System, and get the F for free.
|
4
|
+
# stream combinators
|
5
|
+
@@to_stream = ->(xs) { xs.to_stream }
|
6
|
+
|
7
|
+
def self.to_stream
|
8
|
+
@@to_stream
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.from_stream(*args)
|
12
|
+
if args.length > 0
|
13
|
+
FromStream.new().(*args)
|
14
|
+
else
|
15
|
+
FromStream.new()
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.to_array
|
20
|
+
FromStream.new(Array)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.to_hash
|
24
|
+
FromStream.new(Hash)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.to_set
|
28
|
+
FromStream.new(Set)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.to_a
|
32
|
+
self.to_array
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.to_h
|
36
|
+
self.to_hash
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.infinity
|
40
|
+
Float::INFINITY
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.negative_infinity
|
44
|
+
-Float::INFINITY
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.inf
|
48
|
+
Float::INFINITY
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.ninf
|
52
|
+
-Float::INFINITY
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.id
|
56
|
+
Identity.new # ->(x) { x }
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.apply_with2
|
60
|
+
@@apply_with2||= ->(y, f, x) { f.(x,y)}
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.apply
|
64
|
+
@@apply||= ->(f, *xs) { f.(*xs) }
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.apply_with
|
68
|
+
@@apply_with||= ->(x,f) { f.(x) } # flip * apply
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.compose
|
72
|
+
@@compose||= ->(f,g) { f * g }
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.flip
|
76
|
+
@@flip||= -> (f,x,y) { f.(y,x) }
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.slf
|
80
|
+
@@slf||= -> (f, x) { f.(x,x) } ## square = slf * times
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.fix
|
84
|
+
@@fix||= ->(f, x) {
|
85
|
+
result = x
|
86
|
+
next_result = f.(x)
|
87
|
+
while result != next_result
|
88
|
+
result = next_result
|
89
|
+
next_result = f.(result)
|
90
|
+
end
|
91
|
+
result
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
## next_stream_fn yields Nothing for stop, and the next stream to go to otherwise
|
96
|
+
## yield_fn yields a Nothing for skip, and the next value to yield otherwise
|
97
|
+
def self.cstep
|
98
|
+
@@cstep||= ->(next_stream_fn, yield_fn) {
|
99
|
+
## next_stream_fn :: state -> next_fn -> Maybe(stream)
|
100
|
+
## yield_fn :: state -> Maybe(item)
|
101
|
+
next_fn = ->(state) {
|
102
|
+
next_stream = next_stream_fn.(state)
|
103
|
+
to_yield = yield_fn.(state) if to_skip != Nothing
|
104
|
+
if next_stream == Nothing
|
105
|
+
[:done]
|
106
|
+
elsif to_yield == Nothing
|
107
|
+
[:skip, next_stream]
|
108
|
+
else
|
109
|
+
[:yield, to_yield, next_stream]
|
110
|
+
end
|
111
|
+
}
|
112
|
+
next_fn
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.step
|
117
|
+
@@step||= ->(transition_fn, yield_fn) {
|
118
|
+
## transition_fn :: state -> Maybe(state)
|
119
|
+
## yield_fn :: state -> Maybe(item)
|
120
|
+
next_fn = ->(state) {
|
121
|
+
next_state = transition_fn.(state)
|
122
|
+
to_yield = yield_fn.(state) if next_state != Nothing
|
123
|
+
if next_state == Nothing
|
124
|
+
[:done]
|
125
|
+
elsif to_yield == Nothing
|
126
|
+
[:skip, Stream.new(next_fn, next_state)]
|
127
|
+
else
|
128
|
+
[:yield, to_yield, Stream.new(next_fn, next_state)]
|
129
|
+
end
|
130
|
+
}
|
131
|
+
next_fn
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
135
|
+
## booleans
|
136
|
+
def self.not
|
137
|
+
@@not||= ->(x) { !x }
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.and
|
141
|
+
@@and||= ->(x,y) { x && y }
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.nand
|
145
|
+
@@nand||= ->(x,y) { !(x && y) }
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.or
|
149
|
+
@@or||= ->(x,y) { x || y }
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.nor
|
153
|
+
@@nor||= ->(x,y) { !x && !y }
|
154
|
+
end
|
155
|
+
|
156
|
+
def self.xor
|
157
|
+
@@xor||= ->(x,y) { !(x && y) && (x || y) }
|
158
|
+
end
|
159
|
+
|
160
|
+
## numbers
|
161
|
+
def self.inc
|
162
|
+
@@inc||= ->(x) { x + 1 }
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.dec
|
166
|
+
@@dec||= ->(x) { x - 1 }
|
167
|
+
end
|
168
|
+
|
169
|
+
def self.plus
|
170
|
+
@@plus||= ->(x,y) { x + y }
|
171
|
+
end
|
172
|
+
|
173
|
+
def self.times
|
174
|
+
@@times||= ->(x,y) { x * y }
|
175
|
+
end
|
176
|
+
|
177
|
+
def self.sub_from
|
178
|
+
@@sub_from||= ->(x,y) { x - y }
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.div_from
|
182
|
+
@@div_from||= ->(x,y) { x / y }
|
183
|
+
end
|
184
|
+
|
185
|
+
def self.div_by
|
186
|
+
@@div_by||= ->(y,x) { x / y}
|
187
|
+
end
|
188
|
+
|
189
|
+
def self.sub_by
|
190
|
+
@@sub_by||= ->(y,x) { x - y}
|
191
|
+
end
|
192
|
+
|
193
|
+
def self.equals
|
194
|
+
@@equals||= ->(x,y) { x == y }
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.equal
|
198
|
+
@@equal||= ->(x,y) { x == y }
|
199
|
+
end
|
200
|
+
|
201
|
+
def self.eq
|
202
|
+
@@eq||= ->(x,y) { x === y }
|
203
|
+
end
|
204
|
+
|
205
|
+
def self.is_gt
|
206
|
+
@@is_gt||= ->(y,x) { x > y }
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.is_lt
|
210
|
+
@@is_lt||= ->(y,x) { x < y }
|
211
|
+
end
|
212
|
+
|
213
|
+
def self.is_gte
|
214
|
+
@@is_gte||= ->(y,x) { x >= y }
|
215
|
+
end
|
216
|
+
|
217
|
+
def self.is_lte
|
218
|
+
@@is_lte||= ->(y,x) { x <= y }
|
219
|
+
end
|
220
|
+
|
221
|
+
def self.gt
|
222
|
+
@@gt||= ->(x,y) { x > y }
|
223
|
+
end
|
224
|
+
|
225
|
+
def self.lt
|
226
|
+
@@lt||= ->(x,y) { x < y }
|
227
|
+
end
|
228
|
+
|
229
|
+
def self.gte
|
230
|
+
@@gte||= ->(x,y) { x >= y }
|
231
|
+
end
|
232
|
+
|
233
|
+
def self.lte
|
234
|
+
@@lte||= ->(x,y) { x <= y }
|
235
|
+
end
|
236
|
+
|
237
|
+
#insert ln, lg, log, log_base, e, pi, exp/pow, square, cube, nth_root, sqrt here later
|
238
|
+
def self.max
|
239
|
+
@@max||= ->(x,y) { x >= y ? x : y}
|
240
|
+
end
|
241
|
+
|
242
|
+
def self.min
|
243
|
+
@@min||= ->(x,y) { x <= y ? x : y}
|
244
|
+
end
|
245
|
+
|
246
|
+
## streams
|
247
|
+
def self.empty
|
248
|
+
@@empty||= Stream.new(->(x) { [:done] }, Nothing)
|
249
|
+
end
|
250
|
+
|
251
|
+
def self.wrap
|
252
|
+
@@wrap||= ->(x) {
|
253
|
+
next_fn = ->(bool) { bool ? [:yield, x, Stream.new(next_fn, false)] : [:done]}
|
254
|
+
Stream.new(next_fn, true)
|
255
|
+
}
|
256
|
+
end
|
257
|
+
|
258
|
+
def self.cons
|
259
|
+
@@cons||= ->(el) {
|
260
|
+
->(stream) {
|
261
|
+
Stream.new(->(x) { [:yield, el, stream] } , Nothing)
|
262
|
+
} * to_stream
|
263
|
+
}
|
264
|
+
end
|
265
|
+
|
266
|
+
|
267
|
+
def self.first
|
268
|
+
@@first||= -> (stream) { ## should offer an equivalent that returns a stream with a single element
|
269
|
+
next_item = stream.next_item
|
270
|
+
while next_item.first == :skip
|
271
|
+
next_item = next_item.last.next_item
|
272
|
+
end
|
273
|
+
next_item.first == :yield ? next_item[1] : Nothing
|
274
|
+
} * to_stream
|
275
|
+
end
|
276
|
+
|
277
|
+
def self.rest
|
278
|
+
@@rest||= -> (stream) {
|
279
|
+
next_item = stream.next_item
|
280
|
+
next_item == [:done] ? Nothing : next_item.last
|
281
|
+
} * to_stream
|
282
|
+
end
|
283
|
+
|
284
|
+
def self.take
|
285
|
+
@@take||= ->(n) {
|
286
|
+
raise("#{n} must be a positive number") if n < 0
|
287
|
+
->(stream) {
|
288
|
+
next_fn = step.(->(state){
|
289
|
+
if state.first == 0
|
290
|
+
Nothing
|
291
|
+
else
|
292
|
+
next_item = state.last.next_item
|
293
|
+
count = next_item.first == :skip ? state.first : state.first-1
|
294
|
+
next_item == [:done] ? Nothing : [count, next_item.last]
|
295
|
+
end
|
296
|
+
},
|
297
|
+
->(state) {
|
298
|
+
next_item = state.last.next_item
|
299
|
+
next_item.first == :yield ? next_item[1] : Nothing
|
300
|
+
})
|
301
|
+
Stream.new(next_fn, [n, stream])
|
302
|
+
} * to_stream
|
303
|
+
}
|
304
|
+
end
|
305
|
+
|
306
|
+
def self.drop
|
307
|
+
@@drop||= ->(n) {
|
308
|
+
raise("#{n} must be a positive number") if n < 0
|
309
|
+
->(stream) {
|
310
|
+
next_fn = step.(->(state){
|
311
|
+
next_item = state.last.next_item
|
312
|
+
count = next_item.first == :skip || state.first == 0 ? state.first : state.first-1
|
313
|
+
next_item == [:done] ? Nothing : [count, next_item.last]
|
314
|
+
},
|
315
|
+
->(state) {
|
316
|
+
count = state.first
|
317
|
+
next_item = state.last.next_item
|
318
|
+
next_item.first == :yield && count == 0 ? next_item[1] : Nothing
|
319
|
+
})
|
320
|
+
Stream.new(next_fn, [n, stream])
|
321
|
+
} * to_stream
|
322
|
+
}
|
323
|
+
end
|
324
|
+
|
325
|
+
def self.drop_except
|
326
|
+
@@drop_except||= ->(n) {
|
327
|
+
raise("#{n} must be a positive number") if n < 0
|
328
|
+
->(stream) {
|
329
|
+
next_fn = ->(strm) {
|
330
|
+
potential_stream = take.(n+1) << strm
|
331
|
+
if (length.(potential_stream)) < n+1
|
332
|
+
[:skip, take.(n) << strm]
|
333
|
+
else
|
334
|
+
next_item = strm.next_item
|
335
|
+
tag = next_item.first
|
336
|
+
val = next_item[1]
|
337
|
+
next_strm = next_item.last
|
338
|
+
if tag == :done
|
339
|
+
[:done]
|
340
|
+
elsif tag == :skip
|
341
|
+
[:skip, next_strm]
|
342
|
+
elsif tag == :yield
|
343
|
+
[:skip, next_strm]
|
344
|
+
else
|
345
|
+
raise "#{next_item} is a malformed stream response!"
|
346
|
+
end
|
347
|
+
end
|
348
|
+
}
|
349
|
+
Stream.new(next_fn, stream)
|
350
|
+
} * to_stream
|
351
|
+
}
|
352
|
+
end
|
353
|
+
|
354
|
+
def self.init
|
355
|
+
@@init||= ->(stream) {
|
356
|
+
next_fn = ->(state) {
|
357
|
+
strm = state.last
|
358
|
+
next_item = strm.next_item
|
359
|
+
if next_item == [:done] && state.first == Nothing
|
360
|
+
raise "init requires a stream length of at least 1"
|
361
|
+
elsif next_item == [:done]
|
362
|
+
[:done]
|
363
|
+
elsif next_item.first == :skip
|
364
|
+
[:skip, Stream.new(next_fn, [state.first, next_item.last])]
|
365
|
+
elsif next_item.first == :yield && state.first == Nothing
|
366
|
+
[:skip, Stream.new(next_fn, [next_item[1], next_item.last])]
|
367
|
+
elsif next_item.first == :yield
|
368
|
+
[:yield, state.first, Stream.new(next_fn, [next_item[1], next_item.last])]
|
369
|
+
else
|
370
|
+
raise "#{next_item} is a malformed stream response"
|
371
|
+
end
|
372
|
+
}
|
373
|
+
Stream.new(next_fn, [Nothing, stream])
|
374
|
+
} * to_stream
|
375
|
+
end
|
376
|
+
|
377
|
+
def self.take_while
|
378
|
+
@@take_while||= ->(fn) {
|
379
|
+
raise("take_while requires a function") unless fn.kind_of?(Proc)
|
380
|
+
->(stream) {
|
381
|
+
next_fn = ->(state) {
|
382
|
+
next_item = state.next_item
|
383
|
+
tag = next_item.first
|
384
|
+
val = next_item[1]
|
385
|
+
next_stream = next_item.last
|
386
|
+
if tag == :done || (tag == :yield && !fn.(val))
|
387
|
+
[:done]
|
388
|
+
elsif tag == :skip
|
389
|
+
[:skip, Stream.new(next_fn, next_stream)]
|
390
|
+
elsif tag == :yield
|
391
|
+
[:yield, val, Stream.new(next_fn, next_stream)]
|
392
|
+
else
|
393
|
+
raise("#{next_item} is a malformed stream response!")
|
394
|
+
end
|
395
|
+
}
|
396
|
+
Stream.new(next_fn, stream)
|
397
|
+
} * to_stream
|
398
|
+
}
|
399
|
+
end
|
400
|
+
|
401
|
+
def self.drop_while
|
402
|
+
@@drop_while||= ->(fn) {
|
403
|
+
raise("drop_while requires a function") unless fn.kind_of?(Proc)
|
404
|
+
->(stream) {
|
405
|
+
next_fn = ->(state) {
|
406
|
+
next_item = state.next_item
|
407
|
+
tag = next_item.first
|
408
|
+
val = next_item[1]
|
409
|
+
next_strm = next_item.last
|
410
|
+
if tag == :done
|
411
|
+
[:done]
|
412
|
+
elsif tag == :skip || (tag == :yield && fn.(val))
|
413
|
+
[:skip, Stream.new(next_fn, next_strm)]
|
414
|
+
elsif tag == :yield
|
415
|
+
[:yield, val, next_strm]
|
416
|
+
else
|
417
|
+
raise("#{next_item} is a malformed stream response!")
|
418
|
+
end
|
419
|
+
}
|
420
|
+
Stream.new(next_fn, stream)
|
421
|
+
} * to_stream
|
422
|
+
}
|
423
|
+
end
|
424
|
+
|
425
|
+
def self.take_until
|
426
|
+
@@take_until||= ->(fn) {
|
427
|
+
raise("take_while requires a function") unless fn.kind_of?(Proc)
|
428
|
+
->(stream) {
|
429
|
+
next_fn = ->(state) {
|
430
|
+
next_item = state.next_item
|
431
|
+
tag = next_item.first
|
432
|
+
val = next_item[1]
|
433
|
+
next_stream = next_item.last
|
434
|
+
if tag == :done || (tag == :yield && fn.(val))
|
435
|
+
[:done]
|
436
|
+
elsif tag == :skip
|
437
|
+
[:skip, Stream.new(next_fn, next_stream)]
|
438
|
+
elsif tag == :yield
|
439
|
+
[:yield, val, Stream.new(next_fn, next_stream)]
|
440
|
+
else
|
441
|
+
raise("#{next_item} is a malformed stream response!")
|
442
|
+
end
|
443
|
+
}
|
444
|
+
Stream.new(next_fn, stream)
|
445
|
+
} * to_stream
|
446
|
+
}
|
447
|
+
end
|
448
|
+
|
449
|
+
def self.drop_until
|
450
|
+
@@drop_until||= ->(fn) {
|
451
|
+
raise("drop_while requires a function") unless fn.kind_of?(Proc)
|
452
|
+
->(stream) {
|
453
|
+
next_fn = ->(state) {
|
454
|
+
next_item = state.next_item
|
455
|
+
tag = next_item.first
|
456
|
+
val = next_item[1]
|
457
|
+
next_strm = next_item.last
|
458
|
+
if tag == :done
|
459
|
+
[:done]
|
460
|
+
elsif tag == :skip || (tag == :yield && !fn.(val))
|
461
|
+
[:skip, Stream.new(next_fn, next_strm)]
|
462
|
+
elsif tag == :yield
|
463
|
+
[:yield, val, next_strm]
|
464
|
+
else
|
465
|
+
raise("#{next_item} is a malformed stream response!")
|
466
|
+
end
|
467
|
+
}
|
468
|
+
Stream.new(next_fn, stream)
|
469
|
+
} * to_stream
|
470
|
+
}
|
471
|
+
end
|
472
|
+
|
473
|
+
def self.zip_with
|
474
|
+
@@zip_with||= ->(fn) {
|
475
|
+
->(left_stream, right_stream, *streams) {
|
476
|
+
streams = ([left_stream, right_stream] + streams).map(&:to_stream)
|
477
|
+
next_fn = ->(state) {
|
478
|
+
val_so_far = state.first
|
479
|
+
strms = state.drop(1)
|
480
|
+
next_stream = strms.first
|
481
|
+
next_item = next_stream.next_item
|
482
|
+
new_streams = strms.drop(1) + [next_stream.next_item.last == :done ? empty : next_item.last]
|
483
|
+
tag = next_item.first
|
484
|
+
val = tag == :done ? nil : next_item[1]
|
485
|
+
if tag == :done
|
486
|
+
[:done]
|
487
|
+
elsif tag == :skip
|
488
|
+
[:skip, Stream.new(next_fn, [val_so_far] + new_streams)]
|
489
|
+
elsif tag == :yield && val_so_far.length == streams.length - 1
|
490
|
+
[:yield, fn.(*(val_so_far + [val])), Stream.new(next_fn, [[]] + new_streams)]
|
491
|
+
elsif tag == :yield
|
492
|
+
[:skip, Stream.new(next_fn, [val_so_far + [val]] + new_streams)]
|
493
|
+
else
|
494
|
+
raise "#{next_item} is a malformed stream response!"
|
495
|
+
end
|
496
|
+
}
|
497
|
+
|
498
|
+
Stream.new(next_fn, [[]] + streams)
|
499
|
+
}
|
500
|
+
}
|
501
|
+
end
|
502
|
+
|
503
|
+
## lists
|
504
|
+
def self.list
|
505
|
+
@@list||= ->(*items) { items }
|
506
|
+
end
|
507
|
+
|
508
|
+
def self.empty?
|
509
|
+
@@empty_p||= F.equal.(empty)
|
510
|
+
end
|
511
|
+
|
512
|
+
def self.null?
|
513
|
+
@@null_p||= F.equal.(empty)
|
514
|
+
end
|
515
|
+
|
516
|
+
def self.double
|
517
|
+
@@double||= slf.(plus)
|
518
|
+
end
|
519
|
+
|
520
|
+
def self.square
|
521
|
+
@@square||= slf.(times)
|
522
|
+
end
|
523
|
+
|
524
|
+
def self.app
|
525
|
+
@@app||= ->(*fs) { apply.(fs.first, fs.drop(1)) }
|
526
|
+
end
|
527
|
+
|
528
|
+
def self.snoc
|
529
|
+
@@snoc||= ->(el) {
|
530
|
+
|
531
|
+
->(stream) {
|
532
|
+
# next_fn = step.(->(stream) {
|
533
|
+
# next_item = stream.next_item
|
534
|
+
# next_item == [:done] ? wrap.(el) : next_item.last
|
535
|
+
# },
|
536
|
+
# ->(stream) {
|
537
|
+
# next_item = stream.next_item
|
538
|
+
# next_item.first == :skip ? Nothing : next_item[1]
|
539
|
+
# })
|
540
|
+
next_fn = ->(s) {
|
541
|
+
next_item = s.next_item
|
542
|
+
if next_item == [:done]
|
543
|
+
[:skip, wrap.(el)]
|
544
|
+
elsif next_item.first == :skip
|
545
|
+
[:skip, Stream.new(next_fn, next_item.last)]
|
546
|
+
elsif next_item.first == :yield
|
547
|
+
[:yield, next_item[1], Stream.new(next_fn, next_item.last)]
|
548
|
+
else
|
549
|
+
raise "#{next_item} is a malformed stream result"
|
550
|
+
end
|
551
|
+
}
|
552
|
+
Stream.new(next_fn, stream)
|
553
|
+
} * to_stream
|
554
|
+
}
|
555
|
+
end
|
556
|
+
|
557
|
+
def self.zip
|
558
|
+
@@zip||= zip_with.(list)
|
559
|
+
end
|
560
|
+
|
561
|
+
def self.unfoldl
|
562
|
+
@@unfoldl||= -> (next_fn, stop_fn, seed) {
|
563
|
+
stream_next_fn = ->x { x }
|
564
|
+
Stream.new(stream_next_fn, seed)
|
565
|
+
}
|
566
|
+
end
|
567
|
+
|
568
|
+
|
569
|
+
def self.transducer
|
570
|
+
@@transducer||= ->(next_val_fn, next_state_fn, stop_fn) {
|
571
|
+
->(stream) {
|
572
|
+
next_fn = ->(state) {
|
573
|
+
if stop_fn.(state)
|
574
|
+
[:done]
|
575
|
+
elsif (next_val = next_val_fn.(state)) == Nothing
|
576
|
+
[:skip, next_state_fn.(state)]
|
577
|
+
else
|
578
|
+
[:yield, next_val_fn.(state), next_state_fn.(state)]
|
579
|
+
end
|
580
|
+
}
|
581
|
+
Stream.new(next_fn, stream)
|
582
|
+
} * to_stream
|
583
|
+
}
|
584
|
+
end
|
585
|
+
|
586
|
+
def self.mapleft
|
587
|
+
@@mapleft||= ->(fn) {
|
588
|
+
->(stream) {
|
589
|
+
next_fn = ->(state) {
|
590
|
+
next_el = state.next_item
|
591
|
+
if next_el == [:done]
|
592
|
+
[:done]
|
593
|
+
elsif next_el.first == :skip
|
594
|
+
[:skip, Stream.new(next_fn, next_el.last)]
|
595
|
+
elsif next_el.first == :yield
|
596
|
+
[:yield, fn.(next_el[1]), Stream.new(next_fn, next_el.last)]
|
597
|
+
else
|
598
|
+
raise "#{next_el.inspect} is not a valid stream state!"
|
599
|
+
end
|
600
|
+
}
|
601
|
+
Stream.new(next_fn, stream)
|
602
|
+
} * to_stream
|
603
|
+
}
|
604
|
+
end
|
605
|
+
|
606
|
+
def self.foldleft
|
607
|
+
@@foldleft||= ->(f,u) {
|
608
|
+
|
609
|
+
->(stream) {
|
610
|
+
next_item = stream.next_item
|
611
|
+
result = u
|
612
|
+
while next_item != [:done]
|
613
|
+
if next_item.first == :skip
|
614
|
+
|
615
|
+
elsif next_item.first == :yield
|
616
|
+
result = f.(result, next_item[1])
|
617
|
+
else
|
618
|
+
raise "#{next_item} is a malformed stream response"
|
619
|
+
end
|
620
|
+
next_item = next_item.last.next_item
|
621
|
+
end
|
622
|
+
result
|
623
|
+
} * to_stream
|
624
|
+
|
625
|
+
}
|
626
|
+
end
|
627
|
+
|
628
|
+
def self.scanleft
|
629
|
+
@@scanleft||= ->(f,u) {
|
630
|
+
->(stream) {
|
631
|
+
next_fn = ->(state) {
|
632
|
+
result_so_far = state.first
|
633
|
+
strm = state.last
|
634
|
+
next_item = strm.next_item
|
635
|
+
tag = next_item[0]
|
636
|
+
val = next_item[1]
|
637
|
+
next_stream = next_item.last
|
638
|
+
if tag == :done
|
639
|
+
[:done]
|
640
|
+
elsif tag == :skip
|
641
|
+
[:skip, Stream.new(next_fn, [result_so_far, next_stream])]
|
642
|
+
elsif tag == :yield
|
643
|
+
new_result = f.(result_so_far, val)
|
644
|
+
[:yield, new_result, Stream.new(next_fn, [new_result, next_stream]) ]
|
645
|
+
else
|
646
|
+
raise "#{next_item} is a malformed stream response"
|
647
|
+
end
|
648
|
+
}
|
649
|
+
Stream.new(next_fn, [u, stream])
|
650
|
+
} * to_stream
|
651
|
+
|
652
|
+
}
|
653
|
+
end
|
654
|
+
|
655
|
+
def self.zip_with_index
|
656
|
+
@@zip_with_index||= F.zip_with.(F.list).(F.range.(0, F.infinity))
|
657
|
+
end
|
658
|
+
|
659
|
+
def self.apply_fn
|
660
|
+
@@apply_fn||= -> (f, *args) { f.(*args)}
|
661
|
+
end
|
662
|
+
|
663
|
+
## functional combinators - higher-order functions generic over their container
|
664
|
+
|
665
|
+
## implement this and foldl instead as first implementing scanl and scanr, and then choosing the very last value of the stream
|
666
|
+
## so that we can have an abort-fold-when that gives the value collected so far like a loop that terminates early
|
667
|
+
def self.foldr
|
668
|
+
@@foldr||= ->(f,u) {
|
669
|
+
->(stream) {
|
670
|
+
next_item = stream.next_item
|
671
|
+
if next_item == [:done]
|
672
|
+
u
|
673
|
+
elsif next_item.first == :skip
|
674
|
+
foldr.(f, u, next_item.last)
|
675
|
+
elsif next_item.first == :yield
|
676
|
+
f.(next_item[1], foldr.(f, u, next_item.last))
|
677
|
+
else
|
678
|
+
raise "#{next_item} is improperly formed for a Stream"
|
679
|
+
end
|
680
|
+
} * to_stream
|
681
|
+
|
682
|
+
}
|
683
|
+
end
|
684
|
+
|
685
|
+
def self.filter
|
686
|
+
@@filter||= ->(fn) {
|
687
|
+
->(stream) {
|
688
|
+
next_fn = ->(state) {
|
689
|
+
next_el = state.next_item
|
690
|
+
if next_el == [:done]
|
691
|
+
[:done]
|
692
|
+
elsif next_el.first == :skip || (next_el.first == :yield && !fn.(next_el[1]))
|
693
|
+
[:skip, Stream.new(next_fn, next_el.last)]
|
694
|
+
elsif next_el.first == :yield && fn.(next_el[1])
|
695
|
+
[next_el.first, next_el[1], Stream.new(next_fn, next_el.last)]
|
696
|
+
else
|
697
|
+
raise "#{next_el.inspect} is not a valid stream state!"
|
698
|
+
end
|
699
|
+
}
|
700
|
+
Stream.new(next_fn, stream)
|
701
|
+
} * to_stream
|
702
|
+
}
|
703
|
+
end
|
704
|
+
|
705
|
+
def self.flatmap
|
706
|
+
@@flatmap||= ->(fn) {
|
707
|
+
|
708
|
+
->(stream) {
|
709
|
+
next_fn = ->(next_el) {
|
710
|
+
state = next_el.first
|
711
|
+
potential_stream = next_el.last
|
712
|
+
if potential_stream == Nothing
|
713
|
+
next_el = state.next_item
|
714
|
+
if next_el == [:done]
|
715
|
+
[:done]
|
716
|
+
elsif next_el.first == :skip
|
717
|
+
[:skip, Stream.new(next_fn, [next_el.last, Nothing])]
|
718
|
+
elsif next_el.first == :yield
|
719
|
+
[:skip, Stream.new(next_fn, [next_el.last, fn.(next_el[1])])]
|
720
|
+
else
|
721
|
+
raise "#{next_el.inspect} is not a valid stream state!"
|
722
|
+
end
|
723
|
+
else
|
724
|
+
next_el = potential_stream.next_item
|
725
|
+
if next_el == [:done]
|
726
|
+
[:skip, Stream.new(next_fn, [state, Nothing])]
|
727
|
+
elsif next_el.first == :skip
|
728
|
+
[:skip, Stream.new(next_fn, [state, next_el.last])]
|
729
|
+
elsif next_el.first == :yield
|
730
|
+
[:yield, next_el[1], Stream.new(next_fn, [state, next_el.last])]
|
731
|
+
else
|
732
|
+
raise "#{next_el.inspect} is not a valid stream state!"
|
733
|
+
end
|
734
|
+
end
|
735
|
+
}
|
736
|
+
Stream.new(next_fn, [stream, Nothing])
|
737
|
+
} * to_stream
|
738
|
+
|
739
|
+
}
|
740
|
+
end
|
741
|
+
|
742
|
+
def self.range
|
743
|
+
@@range||= ->(begin_with, end_with) {
|
744
|
+
(if begin_with <= end_with
|
745
|
+
stream_next_fn = ->(n) { n > end_with ? [:done] : [:yield, n, Stream.new(stream_next_fn, n + 1)] }
|
746
|
+
Stream.new(stream_next_fn, begin_with)
|
747
|
+
else
|
748
|
+
stream_next_fn = ->(n) { n < end_with ? [:done] : [:yield, n, Stream.new(stream_next_fn, n - 1)] }
|
749
|
+
Stream.new(stream_next_fn, begin_with)
|
750
|
+
end)
|
751
|
+
}
|
752
|
+
end
|
753
|
+
|
754
|
+
def self.append
|
755
|
+
@@append||= ->(left_stream) {
|
756
|
+
->(right_stream) {
|
757
|
+
left_next_fn = ->(stream) {
|
758
|
+
next_el = stream.next_item
|
759
|
+
if next_el == [:done]
|
760
|
+
[:skip, right_stream]
|
761
|
+
elsif next_el.first == :skip
|
762
|
+
[:skip, Stream.new(left_next_fn, next_el.last)]
|
763
|
+
elsif next_el.first == :yield
|
764
|
+
[next_el.first, next_el[1], Stream.new(left_next_fn, next_el.last)]
|
765
|
+
else
|
766
|
+
raise "#{next_el.inspect} is not a valid stream state!"
|
767
|
+
end
|
768
|
+
}
|
769
|
+
|
770
|
+
Stream.new(left_next_fn, left_stream)
|
771
|
+
} * to_stream
|
772
|
+
|
773
|
+
} * to_stream
|
774
|
+
end
|
775
|
+
|
776
|
+
def self.initial
|
777
|
+
@@initial||= ->(stream) {
|
778
|
+
next_fn = ->(strm) {
|
779
|
+
next_item = strm.next_item
|
780
|
+
if next_item.first == :done
|
781
|
+
raise "Must have at least one item inside of the stream!"
|
782
|
+
elsif next_item.first == :yield
|
783
|
+
[:yield, next_item[1], empty]
|
784
|
+
elsif next_item.first == :skip
|
785
|
+
[:skip, next_item.last]
|
786
|
+
else
|
787
|
+
raise("#{next_item} is a malformed stream response!")
|
788
|
+
end
|
789
|
+
}
|
790
|
+
Stream.new(next_fn, stream)
|
791
|
+
}
|
792
|
+
end
|
793
|
+
|
794
|
+
def self.final
|
795
|
+
@@final||= -> (stream) {
|
796
|
+
next_fn = ->(state) {
|
797
|
+
prev_step = state.first
|
798
|
+
strm = state.last
|
799
|
+
raise("Must have at least one item inside of the stream!") if prev_step == [:done]
|
800
|
+
next_item = strm.next_item
|
801
|
+
if prev_step.first == :skip
|
802
|
+
[:skip, Stream.new(next_fn, [next_item, next_item.last])]
|
803
|
+
elsif next_item == [:done]
|
804
|
+
[:yield, prev_step[1], empty]
|
805
|
+
elsif next_item.first == :yield
|
806
|
+
[:skip, Stream.new(next_fn, [next_item, next_item.last])]
|
807
|
+
elsif next_item.first == :skip
|
808
|
+
[:skip, Stream.new(next_fn, [prev_step, next_item.last])]
|
809
|
+
else
|
810
|
+
raise "#{next_item} is a malformed stream result"
|
811
|
+
end
|
812
|
+
}
|
813
|
+
next_item = stream.next_item
|
814
|
+
Stream.new(next_fn, [next_item, next_item.last])
|
815
|
+
} * to_stream
|
816
|
+
end
|
817
|
+
|
818
|
+
|
819
|
+
|
820
|
+
def self.suffixes
|
821
|
+
@@suffixes||= ->(stream) {
|
822
|
+
next_fn = ->(strm) {
|
823
|
+
next_item = strm.next_item
|
824
|
+
if next_item.first == :done
|
825
|
+
[:yield, strm, empty]
|
826
|
+
elsif next_item.first == :yield
|
827
|
+
[:yield, strm, Stream.new(next_fn, next_item.last)]
|
828
|
+
elsif next_item.first == :skip
|
829
|
+
[:skip, Stream.new(next_fn, next_item.last)]
|
830
|
+
else
|
831
|
+
raise("#{next_item} is a malformed stream response!")
|
832
|
+
end
|
833
|
+
}
|
834
|
+
Stream.new(next_fn, stream)
|
835
|
+
} * to_stream
|
836
|
+
end
|
837
|
+
|
838
|
+
|
839
|
+
def self.prefixes
|
840
|
+
@@prefixes||= foldr.(->(el, acc) { cons.(empty, map.(cons.(el), acc)) }, wrap.(empty))
|
841
|
+
end
|
842
|
+
|
843
|
+
def self.naturals
|
844
|
+
@@naturals||= range.(1, infinity)
|
845
|
+
end
|
846
|
+
|
847
|
+
## stream functions
|
848
|
+
def self.last
|
849
|
+
@@last||= first * final
|
850
|
+
end
|
851
|
+
|
852
|
+
def self.uncons
|
853
|
+
@@uncons||= ->(s) { append(first.(l), wrap(rest.(l))) } * to_stream
|
854
|
+
end
|
855
|
+
|
856
|
+
def self.unsnoc
|
857
|
+
@@unsnoc||= ->(s) { append(wrap(init.(s)), last.(s)) } * to_stream
|
858
|
+
end
|
859
|
+
|
860
|
+
def self.reverse
|
861
|
+
@@reverse||= foldl.(->(acc, el) { cons.(el, acc) }, []) ## or foldr.(->(el,acc) { snoc.(acc, el) }
|
862
|
+
end
|
863
|
+
|
864
|
+
def self.length
|
865
|
+
@@length||= foldl.(inc, 0)
|
866
|
+
end
|
867
|
+
|
868
|
+
def self.length_at_least
|
869
|
+
@@length_at_least||= ->(n) { ->(x) { x != Nothing } * find_where.(equals.(n)) * scanl.(inc, 0) }
|
870
|
+
end
|
871
|
+
|
872
|
+
def self.concat
|
873
|
+
@@concat||= flatmap.(to_stream)
|
874
|
+
end
|
875
|
+
|
876
|
+
def self.enconcat
|
877
|
+
@@enconcat||= ->(left_stream, el) { append.(left_stream.to_stream) * cons.(el) * to_stream }
|
878
|
+
end
|
879
|
+
|
880
|
+
def self.replace
|
881
|
+
@@replace||= ->(to_replace, to_replace_with) {map.(->(x) { x == to_replace ? to_replace_with : x })}
|
882
|
+
end
|
883
|
+
|
884
|
+
def self.replace_with
|
885
|
+
@@replace_with||= ->(to_replace_with, to_replace) { map.(->(x) { x == to_replace ? to_replace_with : x }) }
|
886
|
+
end
|
887
|
+
|
888
|
+
def self.find_where
|
889
|
+
@@find_where||= ->(fn){ first * filter.(fn) }
|
890
|
+
end
|
891
|
+
|
892
|
+
def self.find_last_where
|
893
|
+
@@find_last_where||= ->(fn){ last * filter.(fn) }
|
894
|
+
end
|
895
|
+
|
896
|
+
def self.transpose
|
897
|
+
@@transpose||= ->(stream_of_streams) { zip.( *(stream_of_streams.to_stream) ) }
|
898
|
+
end
|
899
|
+
|
900
|
+
def self.tail
|
901
|
+
@@tail||= rest
|
902
|
+
end
|
903
|
+
|
904
|
+
def self.prefix
|
905
|
+
@@prefix||= init
|
906
|
+
end
|
907
|
+
|
908
|
+
def self.suffix
|
909
|
+
@@suffix||= rest
|
910
|
+
end
|
911
|
+
|
912
|
+
def self.head
|
913
|
+
@@head||= first
|
914
|
+
end
|
915
|
+
|
916
|
+
def self.inits
|
917
|
+
@@inits||= prefixes
|
918
|
+
end
|
919
|
+
|
920
|
+
def self.tails
|
921
|
+
@@tails||= suffixes
|
922
|
+
end
|
923
|
+
|
924
|
+
def self.starts_with
|
925
|
+
@@starts_with||= ->(slice, xs) { }
|
926
|
+
end
|
927
|
+
|
928
|
+
def self.ends_with
|
929
|
+
@@ends_with||= ->(slice, xs) { }
|
930
|
+
end
|
931
|
+
|
932
|
+
def self.intersperse
|
933
|
+
@@intersperse||= ->(x, xs) { rest * flatmap.(->(y) { [x, y] }) << xs.to_stream }
|
934
|
+
end
|
935
|
+
|
936
|
+
# stream folds useful for containers of numbers (core statistics algorithms)
|
937
|
+
|
938
|
+
def self.maximum
|
939
|
+
@@maximum||= foldl.(max, negative_infinity)
|
940
|
+
end
|
941
|
+
|
942
|
+
def self.minimum
|
943
|
+
@@minimum||= foldl.(min, infinity)
|
944
|
+
end
|
945
|
+
|
946
|
+
def self.maximum_by
|
947
|
+
@@maximum_by||= ->(fn) { foldl.(->(max_so_far, el) { max_so_far == Nothing || fn.(el) > fn.(max_so_far) ? el : max_so_far}, Nothing) }
|
948
|
+
end
|
949
|
+
|
950
|
+
def self.minimum_by
|
951
|
+
@@minimum_by||= ->(fn) { foldl.(->(min_so_far, el) { min_so_far == Nothing || fn.(el) < fn.(min_so_far) ? el : min_so_far}, Nothing) }
|
952
|
+
end
|
953
|
+
|
954
|
+
def self.sum
|
955
|
+
@@sum||= foldl.(plus, 0)
|
956
|
+
end
|
957
|
+
|
958
|
+
def self.product
|
959
|
+
@@product||= foldl.(times, 1)
|
960
|
+
end
|
961
|
+
|
962
|
+
## this is a one-pass algorithm, but only an estimate
|
963
|
+
#sum_of_squares_of_differences_from_mean_iterative
|
964
|
+
## - need length, sum, sum_of_squares, M1,M2,M3, deltas, etc... see @@https||=//en.wikipedia.org/wiki/Algorithms_for_calculating_variance
|
965
|
+
## population_variance
|
966
|
+
## sample_variance
|
967
|
+
#->(l) {
|
968
|
+
# len, sm, sm_sqrs = (length + sum + sum_of_squares).(l)
|
969
|
+
#}
|
970
|
+
|
971
|
+
|
972
|
+
def self.repeat
|
973
|
+
@@repeat||= ->(n, fn, x) {
|
974
|
+
result = x
|
975
|
+
count = n
|
976
|
+
while count > 0
|
977
|
+
result = fn.(result)
|
978
|
+
end
|
979
|
+
result
|
980
|
+
}
|
981
|
+
end
|
982
|
+
|
983
|
+
def self.rotate
|
984
|
+
@@rotate||= ->(s) { append.(tail.(s), initial.(s)) }
|
985
|
+
end
|
986
|
+
|
987
|
+
def self.first_index_where
|
988
|
+
@@first_index_where||= ->(fn) { first * find_where.( fn * last ) * zip_with_index }
|
989
|
+
end
|
990
|
+
|
991
|
+
def self.last_index_where
|
992
|
+
@@last_index_where||= ->(fn) { first * last * filter.( fn * last ) * zip_with_index }
|
993
|
+
end
|
994
|
+
|
995
|
+
def self.mean
|
996
|
+
@@mean||= ->(l) { div_from.(*( (sum + length).(l) )) }
|
997
|
+
end ## this works because (sum + length).(l)== [sum.(l), length.(l)]
|
998
|
+
|
999
|
+
def self.sum_of_squares
|
1000
|
+
@@sum_of_squares||= sum * map.(square)
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
## this is a two-pass algorithm
|
1004
|
+
|
1005
|
+
def self.sum_of_differences_from_estimated_mean
|
1006
|
+
@@sum_of_differences_from_estimated_mean||= ->(xs) {
|
1007
|
+
sum * map.(square * sub_by.())
|
1008
|
+
}
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
# stream folds useful for containers of booleans
|
1012
|
+
def self.ands
|
1013
|
+
@@ands||= ->(x) { x == Nothing } * find_where.(F.equals.(false))
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
def self.ors
|
1017
|
+
@@ors||= ->(x) { x != Nothing } * find_where.(F.equals.(true))
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
|
1021
|
+
## stream functionals
|
1022
|
+
def self.contains?
|
1023
|
+
@@contains_p||= ->(el) { F.not * F.equal.(Nothing) * find_where.(equals.(el)) }
|
1024
|
+
end
|
1025
|
+
|
1026
|
+
def self.does_not_contain?
|
1027
|
+
@@does_not_contain_p||= ->(el) { F.equal.(Nothing) * find_where.(equals.(el)) }
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
def self.contains_slice?
|
1031
|
+
@@contains_slice_p||= ->(slice) { any?.(starts_with.(slice)) * tails }
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
def self.all?
|
1035
|
+
@@all_p||= ->(f) { ->(x) { x == Nothing } * find_where.(->(x) { !f.(x)})}
|
1036
|
+
end ## this is going to become equal.(Nothing) * first * find_where.(F.not.(f))
|
1037
|
+
|
1038
|
+
def self.any?
|
1039
|
+
@@any_p||= ->(f) { ->(x) { x != Nothing } * find_where.(f)}
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
def self.fold
|
1043
|
+
@@fold||= ->(fn, u) { final * scanl.(fn, u) }
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
def self.slice_by
|
1047
|
+
@@slice_by||= ->(starts_with_fn, ends_with_fn) { take_until.(ends_with_fn) * drop_until.(starts_with_fn) }
|
1048
|
+
end
|
1049
|
+
|
1050
|
+
def self.interleave
|
1051
|
+
@@interleave||= ->(xs, *ys) { ys.length > 0 ? (concat * zip).(*([xs]+ys)) : ->(zs, *ys) { concat << zip.(*([xs,zs]+ys)) } }
|
1052
|
+
end ## you can undo this with % n, where n is the number of streams
|
1053
|
+
|
1054
|
+
def self.intercalate
|
1055
|
+
@@intercalate||= ->(xs, xss) { concat.intersperse.(xs, xss) }
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
def self.partition_by
|
1059
|
+
@@partition_by||= ->(fn) {
|
1060
|
+
folded_filter = ->(f) { foldl.(->(acc, el) { f.(el) ? acc << el : acc }, []) }
|
1061
|
+
(folded_filter.(fn) + folded_filter.(->(x) { !fn.(x) })) * to_stream
|
1062
|
+
}
|
1063
|
+
end
|
1064
|
+
|
1065
|
+
def self.partition_at
|
1066
|
+
@@partition_at||= ->(n) { take.(n) + drop.(n) }
|
1067
|
+
end
|
1068
|
+
|
1069
|
+
def self.split_by
|
1070
|
+
@@split_by = ->(fn) { take_while.(fn) + drop_while.(fn) }
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
def self.sum_of_square_difference_from_mean_two_pass
|
1074
|
+
@@sum_of_square_difference_from_mean_two_pass||= ->(l) { sum * map.(square * sub_by.(mean.(l))) << l }
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
def self.last_index_of
|
1078
|
+
@@last_index_of||= ->(x) { last_index_where.(F.equal.(x)) }
|
1079
|
+
end
|
1080
|
+
|
1081
|
+
def self.first_index_of
|
1082
|
+
@@first_index_of||= ->(x) { first_index_where.( F.equal.(x) ) }
|
1083
|
+
end
|
1084
|
+
|
1085
|
+
def self.union
|
1086
|
+
@@union||= ->(xs) {
|
1087
|
+
->(ys) {
|
1088
|
+
to_stream * to_set << append.(xs, ys)
|
1089
|
+
} * to_stream
|
1090
|
+
} * to_stream
|
1091
|
+
end
|
1092
|
+
|
1093
|
+
def self.interesect
|
1094
|
+
@@intersect||= ->(xs) {
|
1095
|
+
->(ys) {
|
1096
|
+
to_stream << (to_set.(xs) & to_set.(ys))
|
1097
|
+
} * to_stream
|
1098
|
+
} * to_stream
|
1099
|
+
end
|
1100
|
+
|
1101
|
+
def self.difference
|
1102
|
+
@@difference||= ->(xs) {
|
1103
|
+
->(ys) {
|
1104
|
+
to_remove = to_set.(ys)
|
1105
|
+
filter.(->(x) { !ys.include?(x)}) << xs
|
1106
|
+
} * to_stream
|
1107
|
+
} * to_stream
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
def self.cartesian_product
|
1111
|
+
@@cartesian_product||= ->(xs) {
|
1112
|
+
->(ys) {
|
1113
|
+
flatmap.(->(x) { map.(->(y) { [x,y] }, ys) }, xs)
|
1114
|
+
} * to_stream
|
1115
|
+
} * to_stream
|
1116
|
+
end
|
1117
|
+
|
1118
|
+
def self.unzip
|
1119
|
+
@@unzip||= ->(xs) {
|
1120
|
+
map.(first) + map.(last) << xs
|
1121
|
+
} * to_stream
|
1122
|
+
end
|
1123
|
+
|
1124
|
+
def self.bucket_by
|
1125
|
+
@@bucket_by||= ->(fn, xs) {
|
1126
|
+
foldl.(->(acc, el) {
|
1127
|
+
key = fn.(el)
|
1128
|
+
acc[key] ||= []
|
1129
|
+
acc[key] << el
|
1130
|
+
acc
|
1131
|
+
}, {}).(to_stream.(xs))
|
1132
|
+
}
|
1133
|
+
def self.bucket_by_and_summarize
|
1134
|
+
@@bucket_by_and_summarize||= ->(group_fn, summary_fn) {
|
1135
|
+
map.(summary_fn) * bucket_by.(group_fn)
|
1136
|
+
}
|
1137
|
+
end
|
1138
|
+
|
1139
|
+
def self.window
|
1140
|
+
@@window||= ->(n) {
|
1141
|
+
map.(take.(n)) * suffixes
|
1142
|
+
}
|
1143
|
+
end
|
1144
|
+
|
1145
|
+
def self.subsequences
|
1146
|
+
@@subsequences||= ->() { ## Using the applicative instance for <**>, which is ^ in Raskell
|
1147
|
+
subs = ->(ys) { ys == empty ? wrap.(empty) : subs.(rest.(ys)) ^ [F.id, F.cons.(first.(ys))].to_stream }
|
1148
|
+
}.() #wrap.(empty)
|
1149
|
+
end
|
1150
|
+
## Using Control.Applicative
|
1151
|
+
# subs [] = [[]]
|
1152
|
+
# subs (@@x||=xs) = subs xs <**> [id, (x :)]
|
1153
|
+
## subsequences is useful for fuzzy sequence matching style algorithms a la command tab in sublime,
|
1154
|
+
### or figuring out if some melody or progressionis an elaboration of another (same thing), etc...
|
1155
|
+
## is there any way to do this with a left fold instead of a right one?
|
1156
|
+
|
1157
|
+
|
1158
|
+
def self.continuous_subsequences
|
1159
|
+
@@continuous_subsequences||= filter.(F.not * empty?) * flatmap.(prefixes) * suffixes
|
1160
|
+
end
|
1161
|
+
## continuous subsequences is useful for exact inside sequence matching a la find in sublime
|
1162
|
+
|
1163
|
+
def self.quicksort
|
1164
|
+
->(xs) {
|
1165
|
+
if empty?.(xs)
|
1166
|
+
empty
|
1167
|
+
else
|
1168
|
+
pivot = head.(xs)
|
1169
|
+
partitions = (F.map.(self.quicksort) * partition_by.(F.is_lte.(pivot)) << tail.(xs)).to_a
|
1170
|
+
append.(partitions[0],cons.(pivot, partitions[1]))
|
1171
|
+
end
|
1172
|
+
} * to_stream ### if only this wasn't infinitely recursing...
|
1173
|
+
end
|
1174
|
+
|
1175
|
+
def self.group_by
|
1176
|
+
@@group_by||= ->(fn) {
|
1177
|
+
next_fn = ->(state) {
|
1178
|
+
strm = state.last
|
1179
|
+
group = state.first
|
1180
|
+
next_item = strm.next_item
|
1181
|
+
tag = next_item.first
|
1182
|
+
val = next_item[1]
|
1183
|
+
next_stream = next_item.last
|
1184
|
+
if tag == :done && group == []
|
1185
|
+
[:done]
|
1186
|
+
elsif tag == :done
|
1187
|
+
[:yield, group, empty]
|
1188
|
+
elsif tag == :skip
|
1189
|
+
[:skip, Stream.new(next_fn, [group, next_stream])]
|
1190
|
+
elsif tag == :yield && (group.length == 0 || fn.(val) == fn.(group.last))
|
1191
|
+
[:skip, Stream.new(next_fn, [group + [val], next_stream])]
|
1192
|
+
elsif tag == :yield
|
1193
|
+
[:yield, group, Stream.new([[], next_stream])]
|
1194
|
+
else
|
1195
|
+
raise "#{next_item} is a malformed stream response!"
|
1196
|
+
end
|
1197
|
+
}
|
1198
|
+
Stream.new(next_fn, [[], state])
|
1199
|
+
} * to_stream
|
1200
|
+
end
|
1201
|
+
|
1202
|
+
def self.lines_from_file
|
1203
|
+
@@lines_from_file||= ->(filepath, options={}) {
|
1204
|
+
(options['separator'] ? IO.foreach(filepath, options['separator']) : IO.foreach(filepath) ).to_stream
|
1205
|
+
}
|
1206
|
+
end
|
1207
|
+
|
1208
|
+
def self.group
|
1209
|
+
@@group||= ->(xs) { group_by.(id) }
|
1210
|
+
end
|
1211
|
+
end
|
1212
|
+
#std
|
1213
|
+
|
1214
|
+
class Stream
|
1215
|
+
|
1216
|
+
def deep_clone
|
1217
|
+
Stream.new(self.next_item_function, self.state.deep_clone)
|
1218
|
+
end
|
1219
|
+
|
1220
|
+
def [](first, last=-1)
|
1221
|
+
if last == -1
|
1222
|
+
i = first
|
1223
|
+
F.first * F.drop.(i)
|
1224
|
+
elsif first < 0 && last < 0
|
1225
|
+
F.drop_except.(last.abs - 1) * F.drop_except.(first.abs) << self
|
1226
|
+
##todo
|
1227
|
+
else
|
1228
|
+
raise ""
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
end
|
1232
|
+
|
1233
|
+
=begin ## ruby doesn't allow you to override the return value on []=, which means we'd have to destructively alter this Stream
|
1234
|
+
## but for some reason that isn't working very well - probably due to the need for self-reference. Guess my deep_clone isn't working 100%?
|
1235
|
+
def []=(index, item)
|
1236
|
+
next_fn = ->(state) {
|
1237
|
+
i = state.first
|
1238
|
+
strm = state.last
|
1239
|
+
if
|
1240
|
+
[:yield, item, strm.next_item.last]
|
1241
|
+
else
|
1242
|
+
next_item = strm.next_item
|
1243
|
+
tag = next_item.first
|
1244
|
+
val = next_item[1]
|
1245
|
+
next_stream = next_item.last
|
1246
|
+
if tag == :done && i < index
|
1247
|
+
[:skip, F.snoc(item, Array.new(index - i-1, Nothing).to_stream)]
|
1248
|
+
elsif tag == :skip
|
1249
|
+
[:skip, Stream.new(next_fn, next_stream)]
|
1250
|
+
elsif tag == :yield && i == index
|
1251
|
+
[:yield, item, next_stream]
|
1252
|
+
elsif tag == :yield
|
1253
|
+
[:yield, val, Stream.new(next_fn, next_stream)]
|
1254
|
+
else
|
1255
|
+
raise "#{next_item} is a malformed stream response"
|
1256
|
+
end
|
1257
|
+
end
|
1258
|
+
}
|
1259
|
+
state = [0, self.deep_clone]
|
1260
|
+
self.next_item_function = next_fn
|
1261
|
+
self.state = state
|
1262
|
+
end
|
1263
|
+
=end
|
1264
|
+
|
1265
|
+
def first
|
1266
|
+
F.first.(self)
|
1267
|
+
end
|
1268
|
+
|
1269
|
+
def last
|
1270
|
+
F.last.(self)
|
1271
|
+
end
|
1272
|
+
|
1273
|
+
def rest
|
1274
|
+
F.rest.(self)
|
1275
|
+
end
|
1276
|
+
|
1277
|
+
def any?(fn=F.id)
|
1278
|
+
F.any?(fn) << self
|
1279
|
+
end
|
1280
|
+
|
1281
|
+
def all?(fn=F.id)
|
1282
|
+
F.all?(fn) << self
|
1283
|
+
end
|
1284
|
+
|
1285
|
+
## Applicative <**>
|
1286
|
+
## [1,2,3] ** [id, double]
|
1287
|
+
## [1,2,2,4,3,6]
|
1288
|
+
## defaults to cartesian product if you haven't got any procs in there
|
1289
|
+
def ^(stream_of_fns, is_cartesian=false)
|
1290
|
+
is_cartesian || !F.any?.(->(x) { x.kind_of? Proc }) ? self.cartesian_product.(stream_of_fns) : F.flatmap.(->(x) { stream_of_fns.(x).to_stream }).(self)
|
1291
|
+
end
|
1292
|
+
|
1293
|
+
## zip or Applicative <*> depending on if there any function values in the array/stream ## or should it be interleave to go with % below?
|
1294
|
+
#[id, double] * [1,2,3]
|
1295
|
+
#[1,2,3,2,4,6]
|
1296
|
+
def **(stream, is_zip=false)
|
1297
|
+
is_zip || !F.any?.(->(x){ x.kind_of? Proc }) ? F.zip.(self, stream) : F.flatmap.(->(f) { F.map.(f) << stream.to_stream }).(self.to_stream)
|
1298
|
+
end
|
1299
|
+
|
1300
|
+
def *(stream) ## * and % are opposites
|
1301
|
+
F.interleave.(self, stream)
|
1302
|
+
end
|
1303
|
+
|
1304
|
+
## %2 is odds, rest %2 is evens. doing % 3 breaks into every third item. % n does every nth as a stream
|
1305
|
+
## [odds, evens].(interleave.(xs,ys)) == [xs,ys]
|
1306
|
+
def %(n=2)
|
1307
|
+
F.filter.(->(x) { x % n == 0}).(self)
|
1308
|
+
end
|
1309
|
+
|
1310
|
+
def +(stream)
|
1311
|
+
F.append.(self, stream)
|
1312
|
+
end
|
1313
|
+
|
1314
|
+
def -(stream)
|
1315
|
+
F.difference.(self, stream)
|
1316
|
+
end
|
1317
|
+
|
1318
|
+
## this kind of conflicts with | being function pipelining - maybe change that to use < and > instead of * and | so we can keep ruby-ishness
|
1319
|
+
## but we'll still need something for >>=, <**>, <$> and <.> later...
|
1320
|
+
|
1321
|
+
def |(stream) #
|
1322
|
+
F.union.(self, stream)
|
1323
|
+
end
|
1324
|
+
|
1325
|
+
def &(stream)
|
1326
|
+
F.intersect.(self, stream)
|
1327
|
+
end
|
1328
|
+
end
|
1329
|
+
|
1330
|
+
class Array
|
1331
|
+
|
1332
|
+
## Applicative <**>
|
1333
|
+
## [1,2,3] ** [id, double]
|
1334
|
+
## [1,2,2,4,3,6]
|
1335
|
+
def ^(arr, is_cartesian=false)
|
1336
|
+
is_cartesian || !arr.any?{|x| x.kind_of? Proc } ? self.cartesian_product(arr) : F.flatmap.(->(x) { arr.to_stream.(x) }).(self).to_a
|
1337
|
+
end
|
1338
|
+
|
1339
|
+
## cartesian product
|
1340
|
+
|
1341
|
+
def cartesian_product(arr)
|
1342
|
+
self.map {|x| arr.to_a.map { |y| [x,y] } }.foldl(->(acc,el) { acc.push(el) }, [])
|
1343
|
+
end
|
1344
|
+
|
1345
|
+
## zip or Applicative <*> depending on if there any function values in the array/stream
|
1346
|
+
#[id, double] * [1,2,3]
|
1347
|
+
#[1,2,3,2,4,6]
|
1348
|
+
def **(arr, is_zip=false)
|
1349
|
+
is_zip || !self.any?{|x| x.kind_of? Proc } ? self.zip(arr.to_a) : F.flatmap.(->(f) { F.map.(f) << arr.to_stream }).(self.to_stream).to_a
|
1350
|
+
end
|
1351
|
+
|
1352
|
+
|
1353
|
+
end
|
1354
|
+
|
1355
|
+
class Enumerator
|
1356
|
+
|
1357
|
+
def self.to_stream(enum)
|
1358
|
+
enumerator_to_use = enum.clone.lazy
|
1359
|
+
enumerator_to_store = enumerator_to_use.clone
|
1360
|
+
|
1361
|
+
next_fn = ->(state) {
|
1362
|
+
idx = state[0]
|
1363
|
+
enum_frozen = state[1]
|
1364
|
+
enum_next = state[2]
|
1365
|
+
|
1366
|
+
next_state = [idx+1, enum_frozen, enum_next]
|
1367
|
+
next_tag = :yield
|
1368
|
+
begin
|
1369
|
+
begin
|
1370
|
+
next_item = enum_next.next
|
1371
|
+
|
1372
|
+
rescue StopIteration => e
|
1373
|
+
next_tag = :done
|
1374
|
+
end
|
1375
|
+
rescue IOError => e
|
1376
|
+
next_state = [idx, enum_frozen, enum_frozen.clone.drop(i)]
|
1377
|
+
next_tag = :skip
|
1378
|
+
end
|
1379
|
+
## this {@@type||= "enumerator" is a dirty hack - there has to be a better way to restore state after calling next_item}
|
1380
|
+
next_tag == :done ? [:done] : [next_tag] + (next_tag == :yield ? [next_item] : []) + [Stream.new(next_fn, next_state, {type: "enumerator"})]
|
1381
|
+
|
1382
|
+
}
|
1383
|
+
Stream.new(next_fn, [0, enumerator_to_store, enumerator_to_use], {type: "enumerator"})
|
1384
|
+
end
|
1385
|
+
|
1386
|
+
def to_stream
|
1387
|
+
self.class.to_stream(self)
|
1388
|
+
end
|
1389
|
+
|
1390
|
+
end
|
1391
|
+
|
1392
|
+
=begin
|
1393
|
+
## functionals useful for lists ( but which work on anything supporting .each, and will yield a list )
|
1394
|
+
|
1395
|
+
|
1396
|
+
|
1397
|
+
updated
|
1398
|
+
|
1399
|
+
last_index_of_slice
|
1400
|
+
first_index_of_slice
|
1401
|
+
nth
|
1402
|
+
index_of
|
1403
|
+
index_where
|
1404
|
+
findIndex?
|
1405
|
+
|
1406
|
+
random
|
1407
|
+
randomN
|
1408
|
+
segmentLength
|
1409
|
+
|
1410
|
+
|
1411
|
+
|
1412
|
+
splice
|
1413
|
+
patch
|
1414
|
+
span
|
1415
|
+
|
1416
|
+
permutations
|
1417
|
+
combinations
|
1418
|
+
|
1419
|
+
sort_by ## insertionSort
|
1420
|
+
scanr
|
1421
|
+
|
1422
|
+
## functionals useful in untyped languages like ruby - arbitrarily deep map, zip, and merge - and deep_clone
|
1423
|
+
|
1424
|
+
deep_map ## for a dictionary, maps across all its values deep until not-a-dictionary
|
1425
|
+
## for a list, maps across all its values deep until not-a-list
|
1426
|
+
## in general, it just deeply applies fmap with a particular kind it should only apply to
|
1427
|
+
deep_map_ ## keeps going no matter what the kind is, as long as it supports fmap
|
1428
|
+
deep_zip
|
1429
|
+
deep_merge ## implement with deep_zip and deep_map
|
1430
|
+
deep_diff ## implement with deep_zip and deep_map
|
1431
|
+
|
1432
|
+
=end
|
1433
|
+
|
1434
|
+
|
1435
|
+
=begin The below is for later implementation
|
1436
|
+
|
1437
|
+
|
1438
|
+
quicksort ## use stream fusion == quicksort
|
1439
|
+
mergesort ## use stream fusion
|
1440
|
+
|
1441
|
+
deepMap
|
1442
|
+
deepZip
|
1443
|
+
deepMerge
|
1444
|
+
|
1445
|
+
hylo # unfoldl . foldl
|
1446
|
+
meta # foldl . unfoldl
|
1447
|
+
|
1448
|
+
=end
|