raskell 0.1.2 → 0.1.3
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 +4 -4
- data/README.md +8 -8
- data/ROADMAP.md +5 -8
- data/lib/raskell/f.rb +1178 -1178
- data/lib/raskell/folds.rb +181 -180
- data/lib/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e133ee451e2be310983090109ed99d82b8fa1ef
|
4
|
+
data.tar.gz: 9ce3847f6ebe54b79faf505f35f867c65a8e3ea3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 66bef77ff5951dab5c044da8bedcea9b089ad883aa3391e3d6236cdf75b2aab21881895a90150b740211bc015d90682d3e957f1100ab4d8dfd0031a8d27fc5ee
|
7
|
+
data.tar.gz: d5321834069742a61bbcd293e1522922677da1b55ff7e2cba70d2d3b5b677907aebddaf1e030ee0974dde2d8846207ca9b2f553117916d376e3f25dd549e6860
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# raskell
|
2
|
-
|
2
|
+
Functional and Concatenative Stream Programming in Ruby
|
3
3
|
=======
|
4
4
|
|
5
5
|
|
@@ -7,7 +7,7 @@ Usage:
|
|
7
7
|
|
8
8
|
Reminder: Lambdas can be applied with [\*args] notation and .() is syntactic sugar for .call()
|
9
9
|
```f = ->(x) { x + 2 } ```
|
10
|
-
```f[1]```
|
10
|
+
```f[1]``` , ```f.(1)```, and ```f.call(1)``` all evaluate as ```3```
|
11
11
|
|
12
12
|
|
13
13
|
Lambdas can be partially applied, yielding a new lambda, with call
|
@@ -24,7 +24,7 @@ Lambda Composition with \* (right-associative) ```( (f * g * h).(x) == f.(g.(h.(
|
|
24
24
|
```(double * minus3 * times10).(5)``` evaluates as ```94```
|
25
25
|
|
26
26
|
|
27
|
-
Lambda Pipelining with \| (left-associative) ```
|
27
|
+
Lambda Pipelining with \| (left-associative) ```(f | g | h).(x) == h.(g.(f.(x)))```
|
28
28
|
```(times10 | minus3 | double).(5)``` evaluates as ```94```
|
29
29
|
```(double | minus3 | times10).(5)``` evaluates as ```70```
|
30
30
|
|
@@ -32,20 +32,20 @@ Lambda Tupling with + (associative)
|
|
32
32
|
```(times10 + minus3 + double).(5)``` evaluates as ```[50, 2, 10]```
|
33
33
|
|
34
34
|
Objects, when called, act like constant functions that throw away any values applied to them
|
35
|
-
```5.
|
35
|
+
```5.(1,2,3,[4,7])``` evaluates to ```5```
|
36
36
|
|
37
37
|
Arrays, when called, map across themselves calling each element with the arguments it was called with
|
38
|
-
```[times10, minus3, double].(5)``` evaluates to ```
|
38
|
+
```[times10, minus3, double].(5)``` evaluates to ```[50, -15, 10]```
|
39
39
|
```[plus, times10, 3].(0,1)``` evaluates to ```[1, 0, 3]```
|
40
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
41
|
|
42
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 ```
|
43
|
+
```[times10, minus3, double].to_stream.(5).to_a``` evaluates to ```[50, -15, 10]```
|
44
44
|
```[plus, times10, 3].to_stream.(0,1)``` evaluates to ```[1, 0, 3].to_stream```
|
45
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
46
|
|
47
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]```
|
48
|
+
```F.map.(times10 * plus10).([1,2,3])``` evaluates as ```[100, 200, 300]```
|
49
49
|
|
50
50
|
|
51
51
|
|
@@ -65,7 +65,7 @@ Available Operators to Overload in Ruby
|
|
65
65
|
Using in Raskell so far
|
66
66
|
[], \*, \*\*, ^, \|, &, +, %, <<, >>
|
67
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 -
|
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 - this gives us a powerful form of type constraint for free
|
69
69
|
|
70
70
|
|
71
71
|
|
data/ROADMAP.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Making Ruby a
|
1
|
+
Making Ruby a Joy to Work With In X (Easy?) Steps
|
2
2
|
|
3
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
4
|
\*II) function tupling, composition, and pipelining in the form of +,\*, and \|
|
@@ -6,10 +6,7 @@ Making Ruby a "Joy" to Work With In X (Easy?) Steps
|
|
6
6
|
\*IV) a standard collections library based around fusable stream transducers - (flat)map, filter, fold\*, zip, append, scanl - Scala/Haskell eqv.
|
7
7
|
\*V) add instances of from_stream(ClassName=Array) and to_stream for Dictionary, (Multi?)Set, Array, Range, String, Integer, Object, and Enumerable
|
8
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
|
-
|
11
|
-
IX)
|
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
|
9
|
+
\*VII) Implement Applicative instances for Proc, Array, and Stream
|
10
|
+
VII) modularize for easy addition into other's projects, organize code and test coverage (Procish), reduce code duplication
|
11
|
+
IX) Document, let simmer with experience, and optimize
|
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
|
data/lib/raskell/f.rb
CHANGED
@@ -1,1217 +1,1249 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
1
|
+
module System
|
2
|
+
module Collections
|
3
|
+
module ObjectLambdas
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def id
|
7
|
+
Identity.new # ->(x) { x }
|
8
|
+
end
|
9
|
+
|
10
|
+
def equals
|
11
|
+
@@equals||= ->(x,y) { x == y }
|
12
|
+
end
|
13
|
+
|
14
|
+
def equal
|
15
|
+
@@equal||= ->(x,y) { x == y }
|
16
|
+
end
|
17
|
+
|
18
|
+
def eq
|
19
|
+
@@eq||= ->(x,y) { x === y }
|
20
|
+
end
|
21
|
+
|
22
|
+
def list
|
23
|
+
@@list||= ->(*items) { items }
|
24
|
+
end
|
16
25
|
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
26
|
|
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
27
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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)
|
28
|
+
module ProcishLambdas
|
29
|
+
extend self
|
30
|
+
|
31
|
+
def apply_with2
|
32
|
+
@@apply_with2||= ->(y, f, x) { f.(x,y)}
|
90
33
|
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
34
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
35
|
+
def apply
|
36
|
+
@@apply||= ->(f, *xs) { f.(*xs) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def apply_fn
|
40
|
+
@@apply_fn||= -> (f, *args) { f.(*args)}
|
41
|
+
end
|
42
|
+
|
43
|
+
def apply_with
|
44
|
+
@@apply_with||= ->(x,f) { f.(x) } # flip * apply
|
45
|
+
end
|
46
|
+
|
47
|
+
def compose
|
48
|
+
@@compose||= ->(f,g) { f * g }
|
49
|
+
end
|
50
|
+
|
51
|
+
def flip
|
52
|
+
@@flip||= -> (f,x,y) { f.(y,x) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def slf
|
56
|
+
@@slf||= -> (f, x) { f.(x,x) } ## square = slf * times
|
57
|
+
end
|
58
|
+
|
59
|
+
def repeat
|
60
|
+
@@repeat||= ->(n, fn, x) {
|
61
|
+
result = x
|
62
|
+
count = n
|
63
|
+
while count > 0
|
64
|
+
result = fn.(result)
|
65
|
+
end
|
66
|
+
result
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
def fix
|
71
|
+
@@fix||= ->(f, x) {
|
72
|
+
result = x
|
73
|
+
next_result = f.(x)
|
74
|
+
while result != next_result
|
75
|
+
result = next_result
|
76
|
+
next_result = f.(result)
|
77
|
+
end
|
78
|
+
result
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
## next_stream_fn yields Nothing for stop, and the next stream to go to otherwise
|
83
|
+
## yield_fn yields a Nothing for skip, and the next value to yield otherwise
|
84
|
+
def cstep
|
85
|
+
@@cstep||= ->(next_stream_fn, yield_fn) {
|
86
|
+
## next_stream_fn :: state -> next_fn -> Maybe(stream)
|
87
|
+
## yield_fn :: state -> Maybe(item)
|
88
|
+
next_fn = ->(state) {
|
89
|
+
next_stream = next_stream_fn.(state)
|
90
|
+
to_yield = yield_fn.(state)
|
91
|
+
if next_stream == Nothing
|
92
|
+
[:done]
|
93
|
+
elsif to_yield == Nothing
|
94
|
+
[:skip, next_stream]
|
95
|
+
else
|
96
|
+
[:yield, to_yield, next_stream]
|
97
|
+
end
|
98
|
+
}
|
99
|
+
next_fn
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
def step
|
104
|
+
@@step||= ->(transition_fn, yield_fn) {
|
105
|
+
## transition_fn :: state -> Maybe(state)
|
106
|
+
## yield_fn :: state -> Maybe(item)
|
107
|
+
next_fn = ->(state) {
|
108
|
+
next_state = transition_fn.(state)
|
109
|
+
to_yield = yield_fn.(state) if next_state != Nothing
|
110
|
+
if next_state == Nothing
|
111
|
+
[:done]
|
112
|
+
elsif to_yield == Nothing
|
113
|
+
[:skip, Stream.new(next_fn, next_state)]
|
114
|
+
else
|
115
|
+
[:yield, to_yield, Stream.new(next_fn, next_state)]
|
116
|
+
end
|
117
|
+
}
|
118
|
+
next_fn
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
def unfoldl
|
123
|
+
@@unfoldl||= -> (next_fn, stop_fn, seed) {
|
124
|
+
Stream.new(step.(->(state) { stop_fn.(state) ? Nothing : next_fn.(state) }, next_fn ), seed)
|
125
|
+
}
|
126
|
+
end
|
127
|
+
end
|
168
128
|
|
169
|
-
|
170
|
-
|
171
|
-
|
129
|
+
module BooleanLambdas
|
130
|
+
extend self
|
131
|
+
|
132
|
+
## booleans
|
133
|
+
def not
|
134
|
+
@@not||= ->(x) { !x }
|
135
|
+
end
|
136
|
+
|
137
|
+
def and
|
138
|
+
@@and||= ->(x,y) { x && y }
|
139
|
+
end
|
140
|
+
|
141
|
+
def nand
|
142
|
+
@@nand||= ->(x,y) { !(x && y) }
|
143
|
+
end
|
144
|
+
|
145
|
+
def or
|
146
|
+
@@or||= ->(x,y) { x || y }
|
147
|
+
end
|
148
|
+
|
149
|
+
def nor
|
150
|
+
@@nor||= ->(x,y) { !x && !y }
|
151
|
+
end
|
152
|
+
|
153
|
+
def xor
|
154
|
+
@@xor||= ->(x,y) { !(x && y) && (x || y) }
|
155
|
+
end
|
156
|
+
|
157
|
+
def ands
|
158
|
+
@@ands||= ->(x) { x == Nothing } * find_where.(F.equals.(false))
|
159
|
+
end
|
160
|
+
|
161
|
+
def ors
|
162
|
+
@@ors||= ->(x) { x != Nothing } * find_where.(F.equals.(true))
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
module NumericLambdas
|
168
|
+
extend self
|
169
|
+
|
170
|
+
def infinity
|
171
|
+
Float::INFINITY
|
172
|
+
end
|
173
|
+
|
174
|
+
def negative_infinity
|
175
|
+
-Float::INFINITY
|
176
|
+
end
|
177
|
+
|
178
|
+
def inf
|
179
|
+
Float::INFINITY
|
180
|
+
end
|
181
|
+
|
182
|
+
def ninf
|
183
|
+
-Float::INFINITY
|
184
|
+
end
|
185
|
+
|
186
|
+
## numbers
|
187
|
+
def inc
|
188
|
+
@@inc||= ->(x) { x + 1 }
|
189
|
+
end
|
190
|
+
|
191
|
+
def dec
|
192
|
+
@@dec||= ->(x) { x - 1 }
|
193
|
+
end
|
194
|
+
|
195
|
+
def plus
|
196
|
+
@@plus||= ->(x,y) { x + y }
|
197
|
+
end
|
198
|
+
|
199
|
+
def times
|
200
|
+
@@times||= ->(x,y) { x * y }
|
201
|
+
end
|
202
|
+
|
203
|
+
def sub_from
|
204
|
+
@@sub_from||= ->(x,y) { x - y }
|
205
|
+
end
|
206
|
+
|
207
|
+
def div_from
|
208
|
+
@@div_from||= ->(x,y) { x / y }
|
209
|
+
end
|
210
|
+
|
211
|
+
def div_by
|
212
|
+
@@div_by||= ->(y,x) { x / y}
|
213
|
+
end
|
214
|
+
|
215
|
+
def sub_by
|
216
|
+
@@sub_by||= ->(y,x) { x - y}
|
217
|
+
end
|
218
|
+
|
219
|
+
def is_gt
|
220
|
+
@@is_gt||= ->(y,x) { x > y }
|
221
|
+
end
|
222
|
+
|
223
|
+
def is_lt
|
224
|
+
@@is_lt||= ->(y,x) { x < y }
|
225
|
+
end
|
226
|
+
|
227
|
+
def is_gte
|
228
|
+
@@is_gte||= ->(y,x) { x >= y }
|
229
|
+
end
|
230
|
+
|
231
|
+
def is_lte
|
232
|
+
@@is_lte||= ->(y,x) { x <= y }
|
233
|
+
end
|
234
|
+
|
235
|
+
def gt
|
236
|
+
@@gt||= ->(x,y) { x > y }
|
237
|
+
end
|
238
|
+
|
239
|
+
def lt
|
240
|
+
@@lt||= ->(x,y) { x < y }
|
241
|
+
end
|
242
|
+
|
243
|
+
def gte
|
244
|
+
@@gte||= ->(x,y) { x >= y }
|
245
|
+
end
|
246
|
+
|
247
|
+
def lte
|
248
|
+
@@lte||= ->(x,y) { x <= y }
|
249
|
+
end
|
250
|
+
|
251
|
+
#insert ln, lg, log, log_base, e, pi, exp/pow, square, cube, nth_root, sqrt here later
|
252
|
+
def max
|
253
|
+
@@max||= ->(x,y) { x >= y ? x : y}
|
254
|
+
end
|
255
|
+
|
256
|
+
def min
|
257
|
+
@@min||= ->(x,y) { x <= y ? x : y}
|
258
|
+
end
|
259
|
+
|
260
|
+
def double
|
261
|
+
@@double||= slf.(plus)
|
262
|
+
end
|
263
|
+
|
264
|
+
def square
|
265
|
+
@@square||= slf.(times)
|
266
|
+
end
|
267
|
+
|
268
|
+
|
269
|
+
end
|
270
|
+
|
271
|
+
module FoldableStreamLambdas
|
272
|
+
def foldleft
|
273
|
+
@@foldleft||= ->(f,u) {
|
274
|
+
|
275
|
+
->(stream) {
|
276
|
+
next_item = stream.next_item
|
277
|
+
result = u
|
278
|
+
while next_item != [:done]
|
279
|
+
if next_item.first == :skip
|
280
|
+
|
281
|
+
elsif next_item.first == :yield
|
282
|
+
result = f.(result, next_item[1])
|
283
|
+
else
|
284
|
+
raise "#{next_item} is a malformed stream response"
|
285
|
+
end
|
286
|
+
next_item = next_item.last.next_item
|
287
|
+
end
|
288
|
+
result
|
289
|
+
} * to_stream
|
290
|
+
|
291
|
+
}
|
292
|
+
end
|
172
293
|
|
173
|
-
|
174
|
-
|
175
|
-
|
294
|
+
def foldr
|
295
|
+
@@foldr||= ->(f,u) {
|
296
|
+
->(stream) {
|
297
|
+
next_item = stream.next_item
|
298
|
+
if next_item == [:done]
|
299
|
+
u
|
300
|
+
elsif next_item.first == :skip
|
301
|
+
foldr.(f, u, next_item.last)
|
302
|
+
elsif next_item.first == :yield
|
303
|
+
f.(next_item[1], foldr.(f, u, next_item.last))
|
304
|
+
else
|
305
|
+
raise "#{next_item} is improperly formed for a Stream"
|
306
|
+
end
|
307
|
+
} * to_stream
|
308
|
+
|
309
|
+
}
|
310
|
+
end
|
176
311
|
|
177
|
-
|
178
|
-
|
179
|
-
|
312
|
+
def partition_by
|
313
|
+
@@partition_by||= ->(fn) {
|
314
|
+
folded_filter = ->(f) { foldl.(->(acc, el) { f.(el) ? acc << el : acc }, []) }
|
315
|
+
(folded_filter.(fn) + folded_filter.(->(x) { !fn.(x) })) * to_stream
|
316
|
+
}
|
317
|
+
end
|
318
|
+
|
319
|
+
def bucket_by
|
320
|
+
@@bucket_by||= ->(fn, xs) {
|
321
|
+
foldl.(->(acc, el) {
|
322
|
+
key = fn.(el)
|
323
|
+
acc[key] ||= []
|
324
|
+
acc[key] << el
|
325
|
+
acc
|
326
|
+
}, {}).(to_stream.(xs))
|
327
|
+
}
|
328
|
+
end
|
329
|
+
|
330
|
+
def bucket_by_and_summarize
|
331
|
+
@@bucket_by_and_summarize||= ->(group_fn, summary_fn) {
|
332
|
+
map.(summary_fn) * bucket_by.(group_fn)
|
333
|
+
}
|
334
|
+
end
|
180
335
|
|
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
336
|
|
189
|
-
|
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
|
337
|
+
end
|
245
338
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
339
|
+
module OneForOneStreamLambdas
|
340
|
+
extend self
|
341
|
+
|
342
|
+
def mapleft
|
343
|
+
@@mapleft||= ->(fn) {
|
344
|
+
->(stream) {
|
345
|
+
next_fn = ->(state) {
|
346
|
+
next_el = state.next_item
|
347
|
+
if next_el == [:done]
|
348
|
+
[:done]
|
349
|
+
elsif next_el.first == :skip
|
350
|
+
[:skip, Stream.new(next_fn, next_el.last)]
|
351
|
+
elsif next_el.first == :yield
|
352
|
+
[:yield, fn.(next_el[1]), Stream.new(next_fn, next_el.last)]
|
353
|
+
else
|
354
|
+
raise "#{next_el.inspect} is not a valid stream state!"
|
355
|
+
end
|
356
|
+
}
|
357
|
+
Stream.new(next_fn, stream)
|
358
|
+
} * to_stream
|
359
|
+
}
|
360
|
+
end
|
250
361
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
362
|
+
def scanleft
|
363
|
+
@@scanleft||= ->(f,u) {
|
364
|
+
->(stream) {
|
365
|
+
next_fn = ->(state) {
|
366
|
+
result_so_far = state.first
|
367
|
+
strm = state.last
|
368
|
+
next_item = strm.next_item
|
369
|
+
tag = next_item[0]
|
370
|
+
val = next_item[1]
|
371
|
+
next_stream = next_item.last
|
372
|
+
if tag == :done
|
373
|
+
[:done]
|
374
|
+
elsif tag == :skip
|
375
|
+
[:skip, Stream.new(next_fn, [result_so_far, next_stream])]
|
376
|
+
elsif tag == :yield
|
377
|
+
new_result = f.(result_so_far, val)
|
378
|
+
[:yield, new_result, Stream.new(next_fn, [new_result, next_stream]) ]
|
379
|
+
else
|
380
|
+
raise "#{next_item} is a malformed stream response"
|
381
|
+
end
|
382
|
+
}
|
383
|
+
Stream.new(next_fn, [u, stream])
|
384
|
+
} * to_stream
|
385
|
+
|
386
|
+
}
|
387
|
+
end
|
257
388
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
389
|
+
def zip_with
|
390
|
+
@@zip_with||= ->(fn) {
|
391
|
+
->(left_stream, right_stream, *streams) {
|
392
|
+
streams = ([left_stream, right_stream] + streams).map(&:to_stream)
|
393
|
+
next_fn = ->(state) {
|
394
|
+
val_so_far = state.first
|
395
|
+
strms = state.drop(1)
|
396
|
+
next_stream = strms.first
|
397
|
+
next_item = next_stream.next_item
|
398
|
+
new_streams = strms.drop(1) + [next_stream.next_item.last == :done ? empty : next_item.last]
|
399
|
+
tag = next_item.first
|
400
|
+
val = tag == :done ? nil : next_item[1]
|
401
|
+
if tag == :done
|
402
|
+
[:done]
|
403
|
+
elsif tag == :skip
|
404
|
+
[:skip, Stream.new(next_fn, [val_so_far] + new_streams)]
|
405
|
+
elsif tag == :yield && val_so_far.length == streams.length - 1
|
406
|
+
[:yield, fn.(*(val_so_far + [val])), Stream.new(next_fn, [[]] + new_streams)]
|
407
|
+
elsif tag == :yield
|
408
|
+
[:skip, Stream.new(next_fn, [val_so_far + [val]] + new_streams)]
|
409
|
+
else
|
410
|
+
raise "#{next_item} is a malformed stream response!"
|
411
|
+
end
|
412
|
+
}
|
413
|
+
|
414
|
+
Stream.new(next_fn, [[]] + streams)
|
415
|
+
}
|
416
|
+
}
|
417
|
+
end
|
265
418
|
|
419
|
+
def replace_with
|
420
|
+
@@replace_with||= ->(to_replace_with, to_replace) { map.(->(x) { x == to_replace ? to_replace_with : x }) }
|
421
|
+
end
|
266
422
|
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
423
|
+
end
|
424
|
+
|
425
|
+
module HigherOrderStreamLambdas
|
426
|
+
extend self
|
427
|
+
|
428
|
+
def flatmap
|
429
|
+
@@flatmap||= ->(fn) {
|
430
|
+
|
431
|
+
->(stream) {
|
432
|
+
next_fn = ->(next_el) {
|
433
|
+
state = next_el.first
|
434
|
+
potential_stream = next_el.last
|
435
|
+
if potential_stream == Nothing
|
436
|
+
next_el = state.next_item
|
437
|
+
if next_el == [:done]
|
438
|
+
[:done]
|
439
|
+
elsif next_el.first == :skip
|
440
|
+
[:skip, Stream.new(next_fn, [next_el.last, Nothing])]
|
441
|
+
elsif next_el.first == :yield
|
442
|
+
[:skip, Stream.new(next_fn, [next_el.last, fn.(next_el[1])])]
|
443
|
+
else
|
444
|
+
raise "#{next_el.inspect} is not a valid stream state!"
|
445
|
+
end
|
446
|
+
else
|
447
|
+
next_el = potential_stream.next_item
|
448
|
+
if next_el == [:done]
|
449
|
+
[:skip, Stream.new(next_fn, [state, Nothing])]
|
450
|
+
elsif next_el.first == :skip
|
451
|
+
[:skip, Stream.new(next_fn, [state, next_el.last])]
|
452
|
+
elsif next_el.first == :yield
|
453
|
+
[:yield, next_el[1], Stream.new(next_fn, [state, next_el.last])]
|
454
|
+
else
|
455
|
+
raise "#{next_el.inspect} is not a valid stream state!"
|
456
|
+
end
|
457
|
+
end
|
458
|
+
}
|
459
|
+
Stream.new(next_fn, [stream, Nothing])
|
460
|
+
} * to_stream
|
461
|
+
|
462
|
+
}
|
463
|
+
end
|
464
|
+
|
465
|
+
def filter
|
466
|
+
@@filter||= ->(fn) {
|
467
|
+
->(stream) {
|
468
|
+
next_fn = ->(state) {
|
469
|
+
next_el = state.next_item
|
470
|
+
if next_el == [:done]
|
471
|
+
[:done]
|
472
|
+
elsif next_el.first == :skip || (next_el.first == :yield && !fn.(next_el[1]))
|
473
|
+
[:skip, Stream.new(next_fn, next_el.last)]
|
474
|
+
elsif next_el.first == :yield && fn.(next_el[1])
|
475
|
+
[next_el.first, next_el[1], Stream.new(next_fn, next_el.last)]
|
476
|
+
else
|
477
|
+
raise "#{next_el.inspect} is not a valid stream state!"
|
478
|
+
end
|
479
|
+
}
|
480
|
+
Stream.new(next_fn, stream)
|
481
|
+
} * to_stream
|
482
|
+
}
|
483
|
+
end
|
484
|
+
|
485
|
+
def find_where
|
486
|
+
@@find_where||= ->(fn){ first * filter.(fn) }
|
487
|
+
end
|
488
|
+
|
489
|
+
def find_last_where
|
490
|
+
@@find_last_where||= ->(fn){ last * filter.(fn) }
|
491
|
+
end
|
492
|
+
|
493
|
+
def first_index_where
|
494
|
+
@@first_index_where||= ->(fn) { first * find_where.( fn * last ) * zip_with_index }
|
495
|
+
end
|
496
|
+
|
497
|
+
def last_index_where
|
498
|
+
@@last_index_where||= ->(fn) { first * find_last_where.( fn * last ) * zip_with_index }
|
499
|
+
end
|
500
|
+
|
501
|
+
def all?
|
502
|
+
@@all_p||= ->(f) { ->(x) { x == Nothing } * find_where.(->(x) { !f.(x)})}
|
503
|
+
end ## this is going to become equal.(Nothing) * first * find_where.(F.not.(f))
|
504
|
+
|
505
|
+
def any?
|
506
|
+
@@any_p||= ->(f) { ->(x) { x != Nothing } * find_where.(f)}
|
272
507
|
end
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
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
|
508
|
+
|
509
|
+
def slice_by
|
510
|
+
@@slice_by||= ->(starts_with_fn, ends_with_fn) { take_until.(ends_with_fn) * drop_until.(starts_with_fn) }
|
511
|
+
end
|
512
|
+
|
513
|
+
def split_by
|
514
|
+
@@split_by = ->(fn) { take_while.(fn) + drop_while.(fn) }
|
515
|
+
end
|
516
|
+
|
517
|
+
def group_by
|
518
|
+
@@group_by||= ->(fn) {
|
519
|
+
next_fn = ->(state) {
|
520
|
+
strm = state.last
|
521
|
+
group = state.first
|
334
522
|
next_item = strm.next_item
|
335
523
|
tag = next_item.first
|
336
524
|
val = next_item[1]
|
337
|
-
|
338
|
-
if tag == :done
|
525
|
+
next_stream = next_item.last
|
526
|
+
if tag == :done && group == []
|
339
527
|
[:done]
|
528
|
+
elsif tag == :done
|
529
|
+
[:yield, group, empty]
|
340
530
|
elsif tag == :skip
|
341
|
-
[:skip,
|
531
|
+
[:skip, Stream.new(next_fn, [group, next_stream])]
|
532
|
+
elsif tag == :yield && (group.length == 0 || fn.(val) == fn.(group.last))
|
533
|
+
[:skip, Stream.new(next_fn, [group + [val], next_stream])]
|
342
534
|
elsif tag == :yield
|
343
|
-
[:
|
535
|
+
[:yield, group, Stream.new([[], next_stream])]
|
344
536
|
else
|
345
537
|
raise "#{next_item} is a malformed stream response!"
|
346
538
|
end
|
539
|
+
}
|
540
|
+
Stream.new(next_fn, [[], state])
|
541
|
+
} * to_stream
|
542
|
+
end
|
543
|
+
|
544
|
+
def window_by
|
545
|
+
@@window_by||= self.group_by ## basically, window_by is group_by, but where you pass it a relation (a true/false function) instead of an arbitrary function
|
546
|
+
end
|
547
|
+
|
548
|
+
end
|
549
|
+
|
550
|
+
module StreamLambdas
|
551
|
+
extend self
|
552
|
+
|
553
|
+
|
554
|
+
def empty?
|
555
|
+
@@empty_p||= F.equal.(empty)
|
556
|
+
end
|
557
|
+
|
558
|
+
def null?
|
559
|
+
@@null_p||= F.equal.(empty)
|
560
|
+
end
|
561
|
+
|
562
|
+
def empty
|
563
|
+
@@empty||= Stream.new(->(x) { [:done] }, Nothing)
|
564
|
+
end
|
565
|
+
|
566
|
+
def wrap
|
567
|
+
@@wrap||= ->(x) {
|
568
|
+
next_fn = ->(bool) { bool ? [:yield, x, Stream.new(next_fn, false)] : [:done]}
|
569
|
+
Stream.new(next_fn, true)
|
570
|
+
}
|
571
|
+
end
|
572
|
+
|
573
|
+
def cons
|
574
|
+
@@cons||= ->(el) {
|
575
|
+
->(stream) {
|
576
|
+
Stream.new(->(x) { [:yield, el, stream] } , Nothing)
|
577
|
+
} * to_stream
|
578
|
+
}
|
579
|
+
end
|
580
|
+
|
581
|
+
|
582
|
+
def first
|
583
|
+
@@first||= -> (stream) { ## should offer an equivalent that returns a stream with a single element
|
584
|
+
next_item = stream.next_item
|
585
|
+
while next_item.first == :skip
|
586
|
+
next_item = next_item.last.next_item
|
347
587
|
end
|
588
|
+
next_item.first == :yield ? next_item[1] : Nothing
|
589
|
+
} * to_stream
|
590
|
+
end
|
591
|
+
|
592
|
+
def rest
|
593
|
+
@@rest||= -> (stream) {
|
594
|
+
next_item = stream.next_item
|
595
|
+
next_item == [:done] ? Nothing : next_item.last
|
596
|
+
} * to_stream
|
597
|
+
end
|
598
|
+
|
599
|
+
def snoc
|
600
|
+
@@snoc||= ->(el) {
|
601
|
+
|
602
|
+
->(stream) {
|
603
|
+
# next_fn = step.(->(stream) {
|
604
|
+
# next_item = stream.next_item
|
605
|
+
# next_item == [:done] ? wrap.(el) : next_item.last
|
606
|
+
# },
|
607
|
+
# ->(stream) {
|
608
|
+
# next_item = stream.next_item
|
609
|
+
# next_item.first == :skip ? Nothing : next_item[1]
|
610
|
+
# })
|
611
|
+
next_fn = ->(s) {
|
612
|
+
next_item = s.next_item
|
613
|
+
if next_item == [:done]
|
614
|
+
[:skip, wrap.(el)]
|
615
|
+
elsif next_item.first == :skip
|
616
|
+
[:skip, Stream.new(next_fn, next_item.last)]
|
617
|
+
elsif next_item.first == :yield
|
618
|
+
[:yield, next_item[1], Stream.new(next_fn, next_item.last)]
|
619
|
+
else
|
620
|
+
raise "#{next_item} is a malformed stream result"
|
621
|
+
end
|
622
|
+
}
|
623
|
+
Stream.new(next_fn, stream)
|
624
|
+
} * to_stream
|
348
625
|
}
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
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))
|
626
|
+
end
|
627
|
+
|
628
|
+
def init
|
629
|
+
@@init||= ->(stream) {
|
630
|
+
next_fn = ->(state) {
|
631
|
+
strm = state.last
|
632
|
+
next_item = strm.next_item
|
633
|
+
if next_item == [:done] && state.first == Nothing
|
634
|
+
raise "init requires a stream length of at least 1"
|
635
|
+
elsif next_item == [:done]
|
387
636
|
[:done]
|
388
|
-
elsif
|
389
|
-
[:skip, Stream.new(next_fn,
|
390
|
-
elsif
|
391
|
-
[:
|
637
|
+
elsif next_item.first == :skip
|
638
|
+
[:skip, Stream.new(next_fn, [state.first, next_item.last])]
|
639
|
+
elsif next_item.first == :yield && state.first == Nothing
|
640
|
+
[:skip, Stream.new(next_fn, [next_item[1], next_item.last])]
|
641
|
+
elsif next_item.first == :yield
|
642
|
+
[:yield, state.first, Stream.new(next_fn, [next_item[1], next_item.last])]
|
392
643
|
else
|
393
|
-
raise
|
644
|
+
raise "#{next_item} is a malformed stream response"
|
394
645
|
end
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
646
|
+
}
|
647
|
+
Stream.new(next_fn, [Nothing, stream])
|
648
|
+
} * to_stream
|
649
|
+
end
|
650
|
+
|
651
|
+
def append
|
652
|
+
@@append||= ->(left_stream) {
|
653
|
+
->(right_stream) {
|
654
|
+
left_next_fn = ->(stream) {
|
655
|
+
next_el = stream.next_item
|
656
|
+
if next_el == [:done]
|
657
|
+
[:skip, right_stream]
|
658
|
+
elsif next_el.first == :skip
|
659
|
+
[:skip, Stream.new(left_next_fn, next_el.last)]
|
660
|
+
elsif next_el.first == :yield
|
661
|
+
[next_el.first, next_el[1], Stream.new(left_next_fn, next_el.last)]
|
662
|
+
else
|
663
|
+
raise "#{next_el.inspect} is not a valid stream state!"
|
664
|
+
end
|
665
|
+
}
|
666
|
+
|
667
|
+
Stream.new(left_next_fn, left_stream)
|
668
|
+
} * to_stream
|
669
|
+
|
670
|
+
} * to_stream
|
671
|
+
end
|
672
|
+
|
673
|
+
def concat
|
674
|
+
@@concat||= flatmap.(to_stream)
|
675
|
+
end
|
676
|
+
|
677
|
+
def enconcat
|
678
|
+
@@enconcat||= ->(left_stream, el) { append.(left_stream.to_stream) * cons.(el) * to_stream }
|
679
|
+
end
|
680
|
+
|
681
|
+
def initial
|
682
|
+
@@initial||= ->(stream) {
|
683
|
+
next_fn = ->(strm) {
|
684
|
+
next_item = strm.next_item
|
685
|
+
if next_item.first == :done
|
686
|
+
raise "Must have at least one item inside of the stream!"
|
687
|
+
elsif next_item.first == :yield
|
688
|
+
[:yield, next_item[1], empty]
|
689
|
+
elsif next_item.first == :skip
|
690
|
+
[:skip, next_item.last]
|
416
691
|
else
|
417
692
|
raise("#{next_item} is a malformed stream response!")
|
418
693
|
end
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
next_item =
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
[:yield, val, Stream.new(next_fn, next_stream)]
|
694
|
+
}
|
695
|
+
Stream.new(next_fn, stream)
|
696
|
+
}
|
697
|
+
end
|
698
|
+
|
699
|
+
def final
|
700
|
+
@@final||= -> (stream) {
|
701
|
+
next_fn = ->(state) {
|
702
|
+
prev_step = state.first
|
703
|
+
strm = state.last
|
704
|
+
raise("Must have at least one item inside of the stream!") if prev_step == [:done]
|
705
|
+
next_item = strm.next_item
|
706
|
+
if prev_step.first == :skip
|
707
|
+
[:skip, Stream.new(next_fn, [next_item, next_item.last])]
|
708
|
+
elsif next_item == [:done]
|
709
|
+
[:yield, prev_step[1], empty]
|
710
|
+
elsif next_item.first == :yield
|
711
|
+
[:skip, Stream.new(next_fn, [next_item, next_item.last])]
|
712
|
+
elsif next_item.first == :skip
|
713
|
+
[:skip, Stream.new(next_fn, [prev_step, next_item.last])]
|
440
714
|
else
|
441
|
-
raise
|
715
|
+
raise "#{next_item} is a malformed stream result"
|
442
716
|
end
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
717
|
+
}
|
718
|
+
next_item = stream.next_item
|
719
|
+
Stream.new(next_fn, [next_item, next_item.last])
|
720
|
+
} * to_stream
|
721
|
+
end
|
722
|
+
|
723
|
+
|
724
|
+
def rotate
|
725
|
+
@@rotate||= ->(s) { append.(tail.(s), initial.(s)) }
|
726
|
+
end
|
727
|
+
|
728
|
+
|
729
|
+
def interleave
|
730
|
+
@@interleave||= ->(xs, *ys) { ys.length > 0 ? (concat * zip).(*([xs]+ys)) : ->(zs, *ys) { concat << zip.(*([xs,zs]+ys)) } }
|
731
|
+
end ## you can undo this with % n, where n is the number of streams
|
732
|
+
|
733
|
+
def intercalate
|
734
|
+
@@intercalate||= ->(xs, xss) { concat.intersperse.(xs, xss) }
|
735
|
+
end
|
736
|
+
|
737
|
+
def suffixes
|
738
|
+
@@suffixes||= ->(stream) {
|
739
|
+
next_fn = ->(strm) {
|
740
|
+
next_item = strm.next_item
|
741
|
+
if next_item.first == :done
|
742
|
+
[:yield, strm, empty]
|
743
|
+
elsif next_item.first == :yield
|
744
|
+
[:yield, strm, Stream.new(next_fn, next_item.last)]
|
745
|
+
elsif next_item.first == :skip
|
746
|
+
[:skip, Stream.new(next_fn, next_item.last)]
|
464
747
|
else
|
465
748
|
raise("#{next_item} is a malformed stream response!")
|
466
749
|
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
750
|
}
|
751
|
+
Stream.new(next_fn, stream)
|
752
|
+
} * to_stream
|
753
|
+
end
|
754
|
+
|
755
|
+
|
756
|
+
def prefixes
|
757
|
+
@@prefixes||= foldr.(->(el, acc) { cons.(empty, map.(cons.(el), acc)) }, wrap.(empty))
|
758
|
+
end
|
759
|
+
|
760
|
+
## stream functions
|
761
|
+
def last
|
762
|
+
@@last||= first * final
|
763
|
+
end
|
764
|
+
|
765
|
+
def uncons
|
766
|
+
@@uncons||= ->(s) { append(first.(l), wrap(rest.(l))) } * to_stream
|
767
|
+
end
|
768
|
+
|
769
|
+
def unsnoc
|
770
|
+
@@unsnoc||= ->(s) { append(wrap(init.(s)), last.(s)) } * to_stream
|
771
|
+
end
|
772
|
+
|
773
|
+
def reverse
|
774
|
+
@@reverse||= foldl.(->(acc, el) { cons.(el, acc) }, []) ## or foldr.(->(el,acc) { snoc.(acc, el) }
|
775
|
+
end
|
776
|
+
|
777
|
+
def length
|
778
|
+
@@length||= foldl.(inc, 0)
|
779
|
+
end
|
780
|
+
|
781
|
+
def length_at_least
|
782
|
+
@@length_at_least||= ->(n) { ->(x) { x != Nothing } * find_where.(equals.(n)) * scanl.(inc, 0) }
|
783
|
+
end
|
784
|
+
|
785
|
+
def replace
|
786
|
+
@@replace||= ->(to_replace, to_replace_with) {map.(->(x) { x == to_replace ? to_replace_with : x })}
|
787
|
+
end
|
788
|
+
|
789
|
+
def zip
|
790
|
+
@@zip||= zip_with.(list)
|
791
|
+
end
|
792
|
+
|
793
|
+
def zip_with_index
|
794
|
+
@@zip_with_index||= F.zip_with.(F.list).(F.range.(0, F.infinity))
|
795
|
+
end
|
796
|
+
|
797
|
+
def transpose
|
798
|
+
@@transpose||= ->(stream_of_streams) { zip.( *(stream_of_streams.to_stream) ) }
|
799
|
+
end
|
800
|
+
|
801
|
+
def tail
|
802
|
+
@@tail||= rest
|
803
|
+
end
|
804
|
+
|
805
|
+
def prefix
|
806
|
+
@@prefix||= init
|
807
|
+
end
|
808
|
+
|
809
|
+
def suffix
|
810
|
+
@@suffix||= rest
|
811
|
+
end
|
812
|
+
|
813
|
+
def head
|
814
|
+
@@head||= first
|
815
|
+
end
|
816
|
+
|
817
|
+
def inits
|
818
|
+
@@inits||= prefixes
|
819
|
+
end
|
820
|
+
|
821
|
+
def tails
|
822
|
+
@@tails||= suffixes
|
823
|
+
end
|
824
|
+
|
825
|
+
def starts_with?
|
826
|
+
@@starts_with||= ->(prefix, stream) { F.ands << zip_with.(equals, prefix, stream) }
|
827
|
+
end
|
828
|
+
|
829
|
+
def ends_with?
|
830
|
+
@@ends_with||= ->(slice) { F.equal.(slice) * drop_except.(length.(slice)) }
|
831
|
+
end
|
832
|
+
|
833
|
+
def intersperse
|
834
|
+
@@intersperse||= ->(x, xs) { rest * flatmap.(->(y) { [x, y].to_stream }) << xs.to_stream }
|
835
|
+
end
|
836
|
+
|
837
|
+
def contains?
|
838
|
+
@@contains_p||= ->(el) { F.not * F.equal.(Nothing) * find_where.(equals.(el)) }
|
839
|
+
end
|
840
|
+
|
841
|
+
def does_not_contain?
|
842
|
+
@@does_not_contain_p||= ->(el) { F.equal.(Nothing) * find_where.(equals.(el)) }
|
843
|
+
end
|
844
|
+
|
845
|
+
def contains_slice?
|
846
|
+
@@contains_slice_p||= ->(slice) { any?.(starts_with.(slice)) * tails }
|
847
|
+
end
|
848
|
+
|
849
|
+
def partition_at
|
850
|
+
@@partition_at||= ->(n) { take.(n) + drop.(n) }
|
851
|
+
end
|
852
|
+
|
853
|
+
def last_index_of
|
854
|
+
@@last_index_of||= ->(x) { last_index_where.(F.equal.(x)) }
|
855
|
+
end
|
856
|
+
|
857
|
+
def first_index_of
|
858
|
+
@@first_index_of||= ->(x) { first_index_where.( F.equal.(x) ) }
|
859
|
+
end
|
860
|
+
|
861
|
+
def union
|
862
|
+
@@union||= ->(xs) {
|
863
|
+
->(ys) {
|
864
|
+
to_stream * to_set << append.(xs, ys)
|
865
|
+
} * to_stream
|
866
|
+
} * to_stream
|
867
|
+
end
|
497
868
|
|
498
|
-
|
869
|
+
def intersect
|
870
|
+
@@intersect||= ->(xs) {
|
871
|
+
->(ys) {
|
872
|
+
to_stream << (to_set.(xs) & to_set.(ys))
|
873
|
+
} * to_stream
|
874
|
+
} * to_stream
|
875
|
+
end
|
876
|
+
|
877
|
+
def difference
|
878
|
+
@@difference||= ->(xs) {
|
879
|
+
->(ys) {
|
880
|
+
to_remove = to_set.(ys)
|
881
|
+
filter.(->(x) { !ys.include?(x)}) << xs
|
882
|
+
} * to_stream
|
883
|
+
} * to_stream
|
884
|
+
end
|
885
|
+
|
886
|
+
def cartesian_product
|
887
|
+
@@cartesian_product||= ->(xs) {
|
888
|
+
->(ys) {
|
889
|
+
flatmap.(->(x) { map.(->(y) { [x,y] }, ys) }, xs)
|
890
|
+
} * to_stream
|
891
|
+
} * to_stream
|
892
|
+
end
|
893
|
+
|
894
|
+
def unzip
|
895
|
+
@@unzip||= ->(xs) {
|
896
|
+
map.(first) + map.(last) << xs
|
897
|
+
} * to_stream
|
898
|
+
end
|
899
|
+
|
900
|
+
def window
|
901
|
+
@@window||= ->(n) {
|
902
|
+
map.(take.(n)) * suffixes
|
499
903
|
}
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
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"
|
904
|
+
end
|
905
|
+
|
906
|
+
def subsequences
|
907
|
+
@@subsequences||= ->() { ## Using the applicative instance for <**>, which is ^ in Raskell
|
908
|
+
subs = ->(ys) { ys == empty ? wrap.(empty) : subs.(rest.(ys)) ^ [F.id, F.cons.(first.(ys))].to_stream }
|
909
|
+
}.() #wrap.(empty)
|
910
|
+
end
|
911
|
+
## Using Control.Applicative
|
912
|
+
# subs [] = [[]]
|
913
|
+
# subs (@@x||=xs) = subs xs <**> [id, (x :)]
|
914
|
+
## subsequences is useful for fuzzy sequence matching style algorithms a la command tab in sublime,
|
915
|
+
### or figuring out if some melody or progressionis an elaboration of another (same thing), etc...
|
916
|
+
## is there any way to do this with a left fold instead of a right one?
|
917
|
+
|
918
|
+
|
919
|
+
def continuous_subsequences
|
920
|
+
@@continuous_subsequences||= filter.(F.not * empty?) * flatmap.(prefixes) * suffixes
|
921
|
+
end
|
922
|
+
## continuous subsequences is useful for exact inside sequence matching a la find in sublime
|
923
|
+
|
924
|
+
def quicksort
|
925
|
+
->(xs) {
|
926
|
+
if empty?.(xs)
|
927
|
+
empty
|
928
|
+
else
|
929
|
+
pivot = head.(xs)
|
930
|
+
partitions = (F.map.(self.quicksort) * partition_by.(F.is_lte.(pivot)) << tail.(xs)).to_a
|
931
|
+
append.(partitions[0],cons.(pivot, partitions[1]))
|
550
932
|
end
|
933
|
+
} * to_stream ### if only this wasn't infinitely recursing...
|
934
|
+
end
|
935
|
+
|
936
|
+
def group
|
937
|
+
@@group||= ->(xs) { group_by.(id) }
|
938
|
+
end
|
939
|
+
|
940
|
+
end
|
941
|
+
|
942
|
+
module NumericStreamLambdas
|
943
|
+
extend self
|
944
|
+
|
945
|
+
def range ## count from the first to the second by increments of one
|
946
|
+
@@range||= ->(begin_with, end_with) {
|
947
|
+
(if begin_with <= end_with
|
948
|
+
stream_next_fn = ->(n) { n > end_with ? [:done] : [:yield, n, Stream.new(stream_next_fn, n + 1)] }
|
949
|
+
Stream.new(stream_next_fn, begin_with)
|
950
|
+
else
|
951
|
+
stream_next_fn = ->(n) { n < end_with ? [:done] : [:yield, n, Stream.new(stream_next_fn, n - 1)] }
|
952
|
+
Stream.new(stream_next_fn, begin_with)
|
953
|
+
end)
|
551
954
|
}
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
955
|
+
end
|
956
|
+
|
957
|
+
def naturals
|
958
|
+
@@naturals||= range.(1, infinity)
|
959
|
+
end
|
960
|
+
|
961
|
+
def maximum
|
962
|
+
@@maximum||= foldl.(max, negative_infinity)
|
963
|
+
end
|
964
|
+
|
965
|
+
def minimum
|
966
|
+
@@minimum||= foldl.(min, infinity)
|
967
|
+
end
|
968
|
+
|
969
|
+
def maximum_by
|
970
|
+
@@maximum_by||= ->(fn) { foldl.(->(max_so_far, el) { max_so_far == Nothing || fn.(el) > fn.(max_so_far) ? el : max_so_far}, Nothing) }
|
971
|
+
end
|
972
|
+
|
973
|
+
def minimum_by
|
974
|
+
@@minimum_by||= ->(fn) { foldl.(->(min_so_far, el) { min_so_far == Nothing || fn.(el) < fn.(min_so_far) ? el : min_so_far}, Nothing) }
|
975
|
+
end
|
976
|
+
|
977
|
+
def sum
|
978
|
+
@@sum||= foldl.(plus, 0)
|
979
|
+
end
|
980
|
+
|
981
|
+
def product
|
982
|
+
@@product||= foldl.(times, 1)
|
983
|
+
end
|
984
|
+
|
985
|
+
def mean
|
986
|
+
@@mean||= ->(l) { div_from.(*( (sum + length).(l) )) }
|
987
|
+
end ## this works because (sum + length).(l)== [sum.(l), length.(l)]
|
988
|
+
|
989
|
+
def sum_of_squares
|
990
|
+
@@sum_of_squares||= sum * map.(square)
|
991
|
+
end
|
992
|
+
|
993
|
+
## this is a one-pass algorithm, but only an estimate
|
994
|
+
#sum_of_squares_of_differences_from_mean_iterative
|
995
|
+
## - need length, sum, sum_of_squares, M1,M2,M3, deltas, etc... see @@https||=//en.wikipedia.org/wiki/Algorithms_for_calculating_variance
|
996
|
+
## population_variance
|
997
|
+
## sample_variance
|
998
|
+
#->(l) {
|
999
|
+
# len, sm, sm_sqrs = (length + sum + sum_of_squares).(l)
|
1000
|
+
#}
|
1001
|
+
|
1002
|
+
## this is a two-pass algorithm
|
1003
|
+
|
1004
|
+
def sum_of_differences_from_estimated_mean_two_pass
|
1005
|
+
@@sum_of_differences_from_estimated_mean||= ->(xs) { sum * map.(square * sub_by.(mean.(xs))) << xs }
|
1006
|
+
end
|
567
1007
|
|
1008
|
+
## one pass might involve a scanl for (length, sum, flip * const), which is then mapped across to produce (mean, difference_from_mean, square_difference_from_mean, M2, ....), which is then scanl'd to summarize in some way, doing it all in a single pass.... I wonder if grouping the data and then transposing, and concatenating, then running the same query against it after to see how the values change as a stability measure.
|
1009
|
+
|
1010
|
+
#so to produce quicksort - foldl across the stream producing a tree, where inserting each element takes worst-case log(n), then call to_stream, which to produce all elements takes o(n)
|
568
1011
|
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
1012
|
+
|
1013
|
+
end
|
1014
|
+
|
1015
|
+
module StreamDropAndTakeLambdas
|
1016
|
+
extend self
|
1017
|
+
|
1018
|
+
## there are so many of these I pulled them into their own
|
1019
|
+
def drop
|
1020
|
+
@@drop||= ->(n) {
|
1021
|
+
raise("#{n} must be a positive number") if n < 0
|
1022
|
+
->(stream) {
|
1023
|
+
next_fn = step.(->(state){
|
1024
|
+
next_item = state.last.next_item
|
1025
|
+
count = next_item.first == :skip || state.first == 0 ? state.first : state.first-1
|
1026
|
+
next_item == [:done] ? Nothing : [count, next_item.last]
|
1027
|
+
},
|
1028
|
+
->(state) {
|
1029
|
+
count = state.first
|
1030
|
+
next_item = state.last.next_item
|
1031
|
+
next_item.first == :yield && count == 0 ? next_item[1] : Nothing
|
1032
|
+
})
|
1033
|
+
Stream.new(next_fn, [n, stream])
|
1034
|
+
} * to_stream
|
580
1035
|
}
|
581
|
-
|
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) {
|
1036
|
+
end
|
608
1037
|
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
1038
|
+
def take
|
1039
|
+
@@take||= ->(n) {
|
1040
|
+
raise("#{n} must be a positive number") if n < 0
|
1041
|
+
->(stream) {
|
1042
|
+
next_fn = step.(->(state){
|
1043
|
+
if state.first == 0
|
1044
|
+
Nothing
|
1045
|
+
else
|
1046
|
+
next_item = state.last.next_item
|
1047
|
+
count = next_item.first == :skip ? state.first : state.first-1
|
1048
|
+
next_item == [:done] ? Nothing : [count, next_item.last]
|
1049
|
+
end
|
1050
|
+
},
|
1051
|
+
->(state) {
|
1052
|
+
next_item = state.last.next_item
|
1053
|
+
next_item.first == :yield ? next_item[1] : Nothing
|
1054
|
+
})
|
1055
|
+
Stream.new(next_fn, [n, stream])
|
1056
|
+
} * to_stream
|
1057
|
+
}
|
1058
|
+
end
|
624
1059
|
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
[
|
640
|
-
|
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
|
1060
|
+
def drop_except
|
1061
|
+
@@drop_except||= ->(n) {
|
1062
|
+
raise("#{n} must be a positive number") if n < 0
|
1063
|
+
->(stream) {
|
1064
|
+
next_fn = cstep.(
|
1065
|
+
->(state) {
|
1066
|
+
strm = state.last
|
1067
|
+
accumulated_items = state.first
|
1068
|
+
next_el = strm.next_item
|
1069
|
+
accumulated_items = (state.first.length < n ? state.first : state.first[1..-1]) + [next_el[1]] if next_el.first == :yield
|
1070
|
+
next_el == [:done] ? accumulated_items.to_stream : Stream.new(next_fn, [accumulated_items, next_el.last])
|
1071
|
+
},
|
1072
|
+
->(state) { Nothing }
|
1073
|
+
)
|
1074
|
+
Stream.new(next_fn, [[], stream])
|
1075
|
+
} * to_stream
|
648
1076
|
}
|
649
|
-
|
650
|
-
} * to_stream
|
1077
|
+
end
|
651
1078
|
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
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
|
1079
|
+
def take_while
|
1080
|
+
@@take_while||= ->(fn) {
|
1081
|
+
raise("take_while requires a function") unless fn.kind_of?(Proc)
|
1082
|
+
->(stream) {
|
1083
|
+
next_fn = ->(state) {
|
1084
|
+
next_item = state.next_item
|
1085
|
+
tag = next_item.first
|
1086
|
+
val = next_item[1]
|
1087
|
+
next_stream = next_item.last
|
1088
|
+
if tag == :done || (tag == :yield && !fn.(val))
|
1089
|
+
[:done]
|
1090
|
+
elsif tag == :skip
|
1091
|
+
[:skip, Stream.new(next_fn, next_stream)]
|
1092
|
+
elsif tag == :yield
|
1093
|
+
[:yield, val, Stream.new(next_fn, next_stream)]
|
1094
|
+
else
|
1095
|
+
raise("#{next_item} is a malformed stream response!")
|
1096
|
+
end
|
1097
|
+
}
|
1098
|
+
Stream.new(next_fn, stream)
|
1099
|
+
} * to_stream
|
699
1100
|
}
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
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
|
1101
|
+
end
|
1102
|
+
|
1103
|
+
def drop_while
|
1104
|
+
@@drop_while||= ->(fn) {
|
1105
|
+
raise("drop_while requires a function") unless fn.kind_of?(Proc)
|
1106
|
+
->(stream) {
|
1107
|
+
next_fn = ->(state) {
|
1108
|
+
next_item = state.next_item
|
1109
|
+
tag = next_item.first
|
1110
|
+
val = next_item[1]
|
1111
|
+
next_strm = next_item.last
|
1112
|
+
if tag == :done
|
1113
|
+
[:done]
|
1114
|
+
elsif tag == :skip || (tag == :yield && fn.(val))
|
1115
|
+
[:skip, Stream.new(next_fn, next_strm)]
|
1116
|
+
elsif tag == :yield
|
1117
|
+
[:yield, val, next_strm]
|
1118
|
+
else
|
1119
|
+
raise("#{next_item} is a malformed stream response!")
|
1120
|
+
end
|
1121
|
+
}
|
1122
|
+
Stream.new(next_fn, stream)
|
1123
|
+
} * to_stream
|
735
1124
|
}
|
736
|
-
|
737
|
-
} * to_stream
|
1125
|
+
end
|
738
1126
|
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
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
|
1127
|
+
def take_until
|
1128
|
+
@@take_until||= ->(fn) {
|
1129
|
+
raise("take_while requires a function") unless fn.kind_of?(Proc)
|
1130
|
+
->(stream) {
|
1131
|
+
next_fn = ->(state) {
|
1132
|
+
next_item = state.next_item
|
1133
|
+
tag = next_item.first
|
1134
|
+
val = next_item[1]
|
1135
|
+
next_stream = next_item.last
|
1136
|
+
if tag == :done || (tag == :yield && fn.(val))
|
1137
|
+
[:done]
|
1138
|
+
elsif tag == :skip
|
1139
|
+
[:skip, Stream.new(next_fn, next_stream)]
|
1140
|
+
elsif tag == :yield
|
1141
|
+
[:yield, val, Stream.new(next_fn, next_stream)]
|
1142
|
+
else
|
1143
|
+
raise("#{next_item} is a malformed stream response!")
|
1144
|
+
end
|
1145
|
+
}
|
1146
|
+
Stream.new(next_fn, stream)
|
1147
|
+
} * to_stream
|
768
1148
|
}
|
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
|
-
|
1149
|
+
end
|
818
1150
|
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
1151
|
+
def drop_until
|
1152
|
+
@@drop_until||= ->(fn) {
|
1153
|
+
raise("drop_while requires a function") unless fn.kind_of?(Proc)
|
1154
|
+
->(stream) {
|
1155
|
+
next_fn = ->(state) {
|
1156
|
+
next_item = state.next_item
|
1157
|
+
tag = next_item.first
|
1158
|
+
val = next_item[1]
|
1159
|
+
next_strm = next_item.last
|
1160
|
+
if tag == :done
|
1161
|
+
[:done]
|
1162
|
+
elsif tag == :skip || (tag == :yield && !fn.(val))
|
1163
|
+
[:skip, Stream.new(next_fn, next_strm)]
|
1164
|
+
elsif tag == :yield
|
1165
|
+
[:yield, val, next_strm]
|
1166
|
+
else
|
1167
|
+
raise("#{next_item} is a malformed stream response!")
|
1168
|
+
end
|
1169
|
+
}
|
1170
|
+
Stream.new(next_fn, stream)
|
1171
|
+
} * to_stream
|
1172
|
+
}
|
1173
|
+
end
|
1174
|
+
end
|
1175
|
+
|
1176
|
+
module ConverterLambdas
|
1177
|
+
extend self
|
1178
|
+
|
1179
|
+
def to_stream
|
1180
|
+
@@to_stream||= ->(xs) { xs.to_stream }
|
1181
|
+
end
|
1182
|
+
|
1183
|
+
def from_stream(*args)
|
1184
|
+
if args.length > 0
|
1185
|
+
FromStream.new().(*args)
|
830
1186
|
else
|
831
|
-
|
1187
|
+
FromStream.new()
|
832
1188
|
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
|
1189
|
+
end
|
879
1190
|
|
880
|
-
|
881
|
-
|
882
|
-
|
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)
|
1191
|
+
def lines_from_file
|
1192
|
+
@@lines_from_file||= ->(filepath, options={}) {
|
1193
|
+
(options['separator'] ? IO.foreach(filepath, options['separator']) : IO.foreach(filepath) ).to_stream
|
1194
|
+
}
|
978
1195
|
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
1196
|
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
def self.sum_of_squares
|
1000
|
-
@@sum_of_squares||= sum * map.(square)
|
1001
|
-
end
|
1002
|
-
|
1003
|
-
## this is a two-pass algorithm
|
1197
|
+
def to_array
|
1198
|
+
FromStream.new(Array)
|
1199
|
+
end
|
1004
1200
|
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
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
|
-
|
1201
|
+
def to_hash
|
1202
|
+
FromStream.new(Hash)
|
1203
|
+
end
|
1020
1204
|
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
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) }
|
1205
|
+
def to_set
|
1206
|
+
FromStream.new(Set)
|
1207
|
+
end
|
1208
|
+
|
1209
|
+
def to_a
|
1210
|
+
self.to_array
|
1211
|
+
end
|
1212
|
+
|
1213
|
+
def to_h
|
1214
|
+
self.to_hash
|
1215
|
+
end
|
1216
|
+
end
|
1056
1217
|
end
|
1057
1218
|
|
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
1219
|
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1220
|
+
## module System
|
1221
|
+
class F
|
1222
|
+
extend Collections::ConverterLambdas
|
1223
|
+
extend Collections::ObjectLambdas
|
1224
|
+
extend Collections::ProcishLambdas
|
1225
|
+
extend Collections::BooleanLambdas
|
1226
|
+
extend Collections::NumericLambdas
|
1227
|
+
extend Collections::StreamDropAndTakeLambdas
|
1228
|
+
extend Collections::OneForOneStreamLambdas
|
1229
|
+
extend Collections::FoldableStreamLambdas
|
1230
|
+
extend Collections::HigherOrderStreamLambdas
|
1231
|
+
extend Collections::NumericStreamLambdas
|
1232
|
+
extend Collections::StreamLambdas
|
1080
1233
|
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
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
|
-
end
|
1234
|
+
def self.app
|
1235
|
+
@@app||= ->(*fs) { apply.(fs.first, fs.drop(1)) }
|
1236
|
+
end
|
1134
1237
|
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
end
|
1140
|
-
|
1141
|
-
def self.window
|
1142
|
-
@@window||= ->(n) {
|
1143
|
-
map.(take.(n)) * suffixes
|
1144
|
-
}
|
1145
|
-
end
|
1146
|
-
|
1147
|
-
def self.subsequences
|
1148
|
-
@@subsequences||= ->() { ## Using the applicative instance for <**>, which is ^ in Raskell
|
1149
|
-
subs = ->(ys) { ys == empty ? wrap.(empty) : subs.(rest.(ys)) ^ [F.id, F.cons.(first.(ys))].to_stream }
|
1150
|
-
}.() #wrap.(empty)
|
1151
|
-
end
|
1152
|
-
## Using Control.Applicative
|
1153
|
-
# subs [] = [[]]
|
1154
|
-
# subs (@@x||=xs) = subs xs <**> [id, (x :)]
|
1155
|
-
## subsequences is useful for fuzzy sequence matching style algorithms a la command tab in sublime,
|
1156
|
-
### or figuring out if some melody or progressionis an elaboration of another (same thing), etc...
|
1157
|
-
## is there any way to do this with a left fold instead of a right one?
|
1158
|
-
|
1159
|
-
|
1160
|
-
def self.continuous_subsequences
|
1161
|
-
@@continuous_subsequences||= filter.(F.not * empty?) * flatmap.(prefixes) * suffixes
|
1162
|
-
end
|
1163
|
-
## continuous subsequences is useful for exact inside sequence matching a la find in sublime
|
1164
|
-
|
1165
|
-
def self.quicksort
|
1166
|
-
->(xs) {
|
1167
|
-
if empty?.(xs)
|
1168
|
-
empty
|
1169
|
-
else
|
1170
|
-
pivot = head.(xs)
|
1171
|
-
partitions = (F.map.(self.quicksort) * partition_by.(F.is_lte.(pivot)) << tail.(xs)).to_a
|
1172
|
-
append.(partitions[0],cons.(pivot, partitions[1]))
|
1173
|
-
end
|
1174
|
-
} * to_stream ### if only this wasn't infinitely recursing...
|
1175
|
-
end
|
1176
|
-
|
1177
|
-
def self.group_by
|
1178
|
-
@@group_by||= ->(fn) {
|
1179
|
-
next_fn = ->(state) {
|
1180
|
-
strm = state.last
|
1181
|
-
group = state.first
|
1182
|
-
next_item = strm.next_item
|
1183
|
-
tag = next_item.first
|
1184
|
-
val = next_item[1]
|
1185
|
-
next_stream = next_item.last
|
1186
|
-
if tag == :done && group == []
|
1187
|
-
[:done]
|
1188
|
-
elsif tag == :done
|
1189
|
-
[:yield, group, empty]
|
1190
|
-
elsif tag == :skip
|
1191
|
-
[:skip, Stream.new(next_fn, [group, next_stream])]
|
1192
|
-
elsif tag == :yield && (group.length == 0 || fn.(val) == fn.(group.last))
|
1193
|
-
[:skip, Stream.new(next_fn, [group + [val], next_stream])]
|
1194
|
-
elsif tag == :yield
|
1195
|
-
[:yield, group, Stream.new([[], next_stream])]
|
1196
|
-
else
|
1197
|
-
raise "#{next_item} is a malformed stream response!"
|
1198
|
-
end
|
1199
|
-
}
|
1200
|
-
Stream.new(next_fn, [[], state])
|
1201
|
-
} * to_stream
|
1238
|
+
def self.fold
|
1239
|
+
@@fold||= ->(fn, u) { final * scanl.(fn, u) }
|
1240
|
+
end
|
1241
|
+
|
1202
1242
|
end
|
1243
|
+
end
|
1203
1244
|
|
1204
|
-
def self.lines_from_file
|
1205
|
-
@@lines_from_file||= ->(filepath, options={}) {
|
1206
|
-
(options['separator'] ? IO.foreach(filepath, options['separator']) : IO.foreach(filepath) ).to_stream
|
1207
|
-
}
|
1208
|
-
end
|
1209
1245
|
|
1210
|
-
|
1211
|
-
@@group||= ->(xs) { group_by.(id) }
|
1212
|
-
end
|
1213
|
-
end
|
1214
|
-
#std
|
1246
|
+
class F < System::F;end;
|
1215
1247
|
|
1216
1248
|
class Stream
|
1217
1249
|
|
@@ -1232,38 +1264,6 @@ class Stream
|
|
1232
1264
|
|
1233
1265
|
end
|
1234
1266
|
|
1235
|
-
=begin ## ruby doesn't allow you to override the return value on []=, which means we'd have to destructively alter this Stream
|
1236
|
-
## 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%?
|
1237
|
-
def []=(index, item)
|
1238
|
-
next_fn = ->(state) {
|
1239
|
-
i = state.first
|
1240
|
-
strm = state.last
|
1241
|
-
if
|
1242
|
-
[:yield, item, strm.next_item.last]
|
1243
|
-
else
|
1244
|
-
next_item = strm.next_item
|
1245
|
-
tag = next_item.first
|
1246
|
-
val = next_item[1]
|
1247
|
-
next_stream = next_item.last
|
1248
|
-
if tag == :done && i < index
|
1249
|
-
[:skip, F.snoc(item, Array.new(index - i-1, Nothing).to_stream)]
|
1250
|
-
elsif tag == :skip
|
1251
|
-
[:skip, Stream.new(next_fn, next_stream)]
|
1252
|
-
elsif tag == :yield && i == index
|
1253
|
-
[:yield, item, next_stream]
|
1254
|
-
elsif tag == :yield
|
1255
|
-
[:yield, val, Stream.new(next_fn, next_stream)]
|
1256
|
-
else
|
1257
|
-
raise "#{next_item} is a malformed stream response"
|
1258
|
-
end
|
1259
|
-
end
|
1260
|
-
}
|
1261
|
-
state = [0, self.deep_clone]
|
1262
|
-
self.next_item_function = next_fn
|
1263
|
-
self.state = state
|
1264
|
-
end
|
1265
|
-
=end
|
1266
|
-
|
1267
1267
|
def first
|
1268
1268
|
F.first.(self)
|
1269
1269
|
end
|
@@ -1332,7 +1332,7 @@ end
|
|
1332
1332
|
class Array
|
1333
1333
|
|
1334
1334
|
## Applicative <**>
|
1335
|
-
## [1,2,3]
|
1335
|
+
## [1,2,3] ^ [id, double] == [1,2,3].flatmap([id, double])
|
1336
1336
|
## [1,2,2,4,3,6]
|
1337
1337
|
def ^(arr, is_cartesian=false)
|
1338
1338
|
is_cartesian || !arr.any?{|x| x.kind_of? Proc } ? self.cartesian_product(arr) : F.flatmap.(->(x) { arr.to_stream.(x) }).(self).to_a
|
@@ -1345,7 +1345,7 @@ class Array
|
|
1345
1345
|
end
|
1346
1346
|
|
1347
1347
|
## zip or Applicative <*> depending on if there any function values in the array/stream
|
1348
|
-
#[id, double]
|
1348
|
+
#[id, double] ** [1,2,3]
|
1349
1349
|
#[1,2,3,2,4,6]
|
1350
1350
|
def **(arr, is_zip=false)
|
1351
1351
|
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
|