-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtransducers.lua
163 lines (148 loc) · 3.36 KB
/
transducers.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
local f = require 'functional'
local tr = require 'transformers'
-- canonical uri: http://clojure.org/transducers
--
-- a transformer 'step' is a reducing function:
-- type Reducer a r = r -> a -> r
-- or loosely, r -> a -> r
-- a transducer is a transformation from one reducing function to another:
-- type Transducer a b = forall r . Reducer a r -> Reducer b r
-- or loosely, (r -> a -> r) -> (r -> a -> r)
local function wrap(xf)
return {
init = function() return {} end,
step = xf or error("no step function."),
complete = function(result) return result end
}
end
local function map(f)
return function(xf)
return {
init = xf.init,
step = function(result, item)
local mapped = f(item)
return xf.step(result, mapped)
end,
complete = xf.complete
}
end
end
local function filter(predicate)
return function(xf)
return {
init = xf.init,
step = function(value, item)
local allow = predicate(item)
if allow then
value = xf.step(value, item)
end
return value
end,
complete = xf.complete
}
end
end
local function remove(predicate)
local not_ = function(pred)
return function(x)
return not pred(x)
end
end
return filter(not_(predicate))
end
local function drop(n)
return function(xf)
local left = n
return {
init = xf.init,
step = function(value, item)
if(left > 0) then
left = left - 1
else
value = xf.step(value, item)
end
return value
end,
complete = xf.complete
}
end
end
-- early termination of reduce (see clojure spec)
local function reduced(value)
return {
value = value,
__reduced__ = true
}
end
local function take(n)
return function(xf)
local left = n
return {
init = xf.init,
step = function(value, item)
value = xf.step(value, item)
left = left - 1
if(left <= 0) then
value = reduced(value);
end
return value
end,
complete = xf.complete
}
end
end
local function is_reduced(value)
if type(value) == 'table' then
return value.__reduced__
end
return false
end
local function deref(v)
return v.value
end
local function reduce(xf, input)
-- we do not use f.reduce here because we need to account for early
-- termination of reduce.
--
-- local result = f.reduce(xf.step, input, init)
-- return xf.complete(result)
local result = xf.init()
for i in f.iter(input) do
result = xf.step(result, i)
if is_reduced(result) then
result = deref(result)
break
end
end
return xf.complete(result)
end
local function transduce(transducer, f, seq)
local transformer = f
if type(transformer) == 'function' then
-- we have a function; convert to a transformer
transformer = wrap(transformer)
end
local xf = transducer(transformer)
return reduce(xf, seq)
end
local function into(transducer, init, seq)
if type(init) == 'table' then
local xf = transducer(tr.append)
return reduce(xf, seq)
elseif type(init) == 'string' then
local xf = transducer(tr.concat)
return reduce(xf, seq)
else
error('unknown type', type(init))
end
end
return {
map = map,
drop = drop,
take = take,
into = into,
remove = remove,
filter = filter,
reduce = reduce,
transduce = transduce,
}