flor 0.15.0 → 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +14 -1
- data/CREDITS.md +1 -0
- data/LICENSE.txt +1 -1
- data/Makefile +6 -2
- data/README.md +2 -1
- data/flor.gemspec +12 -2
- data/lib/flor.rb +3 -3
- data/lib/flor/colours.rb +1 -1
- data/lib/flor/conf.rb +3 -4
- data/lib/flor/core/executor.rb +31 -61
- data/lib/flor/core/node.rb +213 -96
- data/lib/flor/core/procedure.rb +194 -75
- data/lib/flor/core/texecutor.rb +6 -7
- data/lib/flor/djan.rb +41 -22
- data/lib/flor/flor.rb +137 -42
- data/lib/flor/id.rb +77 -59
- data/lib/flor/log.rb +43 -22
- data/lib/flor/migrations/0001_tables.rb +7 -7
- data/lib/flor/parser.rb +271 -74
- data/lib/flor/pcore/_apply.rb +108 -0
- data/lib/flor/pcore/_atom.rb +2 -4
- data/lib/flor/pcore/_att.rb +54 -37
- data/lib/flor/pcore/_dmute.rb +18 -0
- data/lib/flor/pcore/_dol.rb +17 -0
- data/lib/flor/pcore/_dqs.rb +35 -0
- data/lib/flor/pcore/_head.rb +25 -0
- data/lib/flor/pcore/_obj.rb +1 -3
- data/lib/flor/pcore/_pat_guard.rb +1 -1
- data/lib/flor/pcore/_pat_obj.rb +11 -3
- data/lib/flor/pcore/_pat_regex.rb +16 -2
- data/lib/flor/pcore/_ref.rb +51 -0
- data/lib/flor/pcore/_rxs.rb +27 -0
- data/lib/flor/pcore/_val.rb +11 -6
- data/lib/flor/pcore/{logo.rb → andor.rb} +4 -6
- data/lib/flor/pcore/apply.rb +72 -2
- data/lib/flor/pcore/arith.rb +16 -4
- data/lib/flor/pcore/array_qmark.rb +100 -0
- data/lib/flor/pcore/break.rb +1 -2
- data/lib/flor/pcore/case.rb +1 -1
- data/lib/flor/pcore/cmp.rb +3 -2
- data/lib/flor/pcore/collect.rb +2 -2
- data/lib/flor/pcore/cond.rb +19 -1
- data/lib/flor/pcore/cursor.rb +12 -11
- data/lib/flor/pcore/define.rb +30 -4
- data/lib/flor/pcore/do_return.rb +3 -0
- data/lib/flor/pcore/flatten.rb +39 -0
- data/lib/flor/pcore/if.rb +15 -5
- data/lib/flor/pcore/includes.rb +5 -2
- data/lib/flor/pcore/inject.rb +1 -1
- data/lib/flor/pcore/iterator.rb +28 -18
- data/lib/flor/pcore/keys.rb +2 -2
- data/lib/flor/pcore/map.rb +19 -1
- data/lib/flor/pcore/match.rb +2 -2
- data/lib/flor/pcore/matchr.rb +18 -5
- data/lib/flor/pcore/max.rb +51 -0
- data/lib/flor/pcore/merge.rb +134 -0
- data/lib/flor/pcore/move.rb +1 -1
- data/lib/flor/pcore/noret.rb +1 -1
- data/lib/flor/pcore/not.rb +15 -1
- data/lib/flor/pcore/on.rb +11 -0
- data/lib/flor/pcore/on_cancel.rb +5 -1
- data/lib/flor/pcore/on_error.rb +69 -4
- data/lib/flor/pcore/push.rb +4 -9
- data/lib/flor/pcore/range.rb +5 -5
- data/lib/flor/pcore/reduce.rb +5 -18
- data/lib/flor/pcore/return.rb +26 -0
- data/lib/flor/pcore/reverse.rb +4 -0
- data/lib/flor/pcore/sequence.rb +8 -1
- data/lib/flor/pcore/set.rb +74 -15
- data/lib/flor/pcore/shuffle.rb +71 -0
- data/lib/flor/pcore/slice.rb +137 -0
- data/lib/flor/pcore/sort.rb +244 -0
- data/lib/flor/pcore/sort_by.rb +67 -0
- data/lib/flor/pcore/split.rb +39 -0
- data/lib/flor/pcore/stall.rb +1 -1
- data/lib/flor/pcore/strings.rb +123 -0
- data/lib/flor/pcore/timestamp.rb +34 -0
- data/lib/flor/pcore/to_array.rb +2 -3
- data/lib/flor/pcore/twig.rb +1 -1
- data/lib/flor/pcore/type_of.rb +37 -0
- data/lib/flor/pcore/until.rb +3 -3
- data/lib/flor/punit/cancel.rb +3 -3
- data/lib/flor/punit/ccollect.rb +29 -0
- data/lib/flor/punit/cmap.rb +76 -20
- data/lib/flor/punit/concurrence.rb +440 -33
- data/lib/flor/punit/cron.rb +1 -1
- data/lib/flor/punit/every.rb +1 -1
- data/lib/flor/punit/graft.rb +2 -3
- data/lib/flor/punit/on_timeout.rb +5 -1
- data/lib/flor/punit/part.rb +63 -0
- data/lib/flor/punit/schedule.rb +1 -1
- data/lib/flor/punit/task.rb +52 -10
- data/lib/flor/punit/trap.rb +4 -5
- data/lib/flor/tools/shell.rb +37 -18
- data/lib/flor/unit/caller.rb +23 -11
- data/lib/flor/unit/executor.rb +33 -12
- data/lib/flor/unit/ganger.rb +10 -1
- data/lib/flor/unit/hook.rb +2 -1
- data/lib/flor/unit/hooker.rb +13 -2
- data/lib/flor/unit/loader.rb +7 -7
- data/lib/flor/unit/logger.rb +15 -17
- data/lib/flor/unit/models.rb +4 -2
- data/lib/flor/unit/models/execution.rb +83 -38
- data/lib/flor/unit/models/message.rb +16 -0
- data/lib/flor/unit/models/pointer.rb +24 -0
- data/lib/flor/unit/models/timer.rb +25 -4
- data/lib/flor/unit/models/trace.rb +14 -0
- data/lib/flor/unit/models/trap.rb +39 -14
- data/lib/flor/unit/scheduler.rb +11 -7
- data/lib/flor/unit/storage.rb +55 -39
- data/lib/flor/unit/taskers.rb +17 -14
- data/lib/flor/unit/waiter.rb +4 -3
- metadata +40 -10
- data/lib/flor/changes.rb +0 -26
- data/lib/flor/dollar.rb +0 -224
- data/lib/flor/unit/hooks.rb +0 -37
data/lib/flor/pcore/reverse.rb
CHANGED
data/lib/flor/pcore/sequence.rb
CHANGED
@@ -10,6 +10,13 @@ class Flor::Pro::Sequence < Flor::Procedure
|
|
10
10
|
# task 'charly'
|
11
11
|
# ```
|
12
12
|
|
13
|
-
names %w[ sequence
|
13
|
+
names %w[ sequence begin ]
|
14
|
+
|
15
|
+
def cancel_when_closed
|
16
|
+
|
17
|
+
return cancel if node_status_flavour == 'on-error'
|
18
|
+
|
19
|
+
[]
|
20
|
+
end
|
14
21
|
end
|
15
22
|
|
data/lib/flor/pcore/set.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
class Flor::Pro::Set < Flor::Procedure
|
3
3
|
#
|
4
|
-
#
|
4
|
+
# Sets a field or a variable.
|
5
5
|
#
|
6
6
|
# ```
|
7
7
|
# sequence
|
@@ -67,12 +67,11 @@ class Flor::Pro::Set < Flor::Procedure
|
|
67
67
|
def pre_execute
|
68
68
|
|
69
69
|
unatt_unkeyed_children
|
70
|
+
reref_children
|
70
71
|
|
71
|
-
|
72
|
-
sc = @node['single_child'] = nacn.size == 1
|
73
|
-
nacn = non_att_children[0..-2] unless sc
|
72
|
+
@node['single_child'] = (non_att_children.count == 1)
|
74
73
|
|
75
|
-
|
74
|
+
rep_children
|
76
75
|
|
77
76
|
@node['refs'] = []
|
78
77
|
end
|
@@ -87,32 +86,92 @@ class Flor::Pro::Set < Flor::Procedure
|
|
87
86
|
|
88
87
|
def receive_non_att
|
89
88
|
|
90
|
-
|
91
|
-
last = ! @node['single_child'] && (@fcid + 1) == children.size
|
89
|
+
ft = tree[1][@fcid] || []
|
92
90
|
|
93
|
-
|
91
|
+
if ft[0] == '_rep' || (Flor.is_string_tree?(ft) && ! last_receive?)
|
92
|
+
@node['refs'] << payload['ret']
|
93
|
+
elsif ft[0] == '_ref' &&
|
94
|
+
ft[1].size == 2 &&
|
95
|
+
ft[1][0][0, 2] == [ '_sqs', 'f' ] && ft[1][1][0, 2] == [ '_sqs', 'ret' ]
|
96
|
+
then
|
97
|
+
payload['ret'] = node_payload_ret
|
98
|
+
end
|
94
99
|
|
95
100
|
super
|
96
101
|
end
|
97
102
|
|
98
103
|
def receive_last
|
99
104
|
|
100
|
-
ret =
|
105
|
+
ret =
|
106
|
+
if @node['single_child']
|
107
|
+
node_payload_ret
|
108
|
+
else
|
109
|
+
payload['ret']
|
110
|
+
end
|
111
|
+
|
112
|
+
refs = @node['refs']
|
101
113
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
114
|
+
case refs.size
|
115
|
+
when 0 then 0
|
116
|
+
when 1 then set_value(refs.first, ret)
|
117
|
+
else splat_value(refs, ret)
|
106
118
|
end
|
107
119
|
|
108
120
|
payload['ret'] =
|
109
|
-
if tree[0] == 'setr' ||
|
121
|
+
if tree[0] == 'setr' || refs_include_f_ret?
|
110
122
|
ret
|
111
123
|
else
|
112
124
|
node_payload_ret
|
113
125
|
end
|
114
126
|
|
115
|
-
|
127
|
+
wrap
|
128
|
+
end
|
129
|
+
|
130
|
+
protected
|
131
|
+
|
132
|
+
def refs_include_f_ret?
|
133
|
+
|
134
|
+
!! @node['refs']
|
135
|
+
.find { |ref|
|
136
|
+
ref.length == 2 &&
|
137
|
+
ref[1] == 'ret' &&
|
138
|
+
ref[0].match(/\Af(ld|ield)?\z/) }
|
139
|
+
end
|
140
|
+
|
141
|
+
def reref_children
|
142
|
+
|
143
|
+
t = tree
|
144
|
+
|
145
|
+
cn = t[1]
|
146
|
+
.collect { |ct|
|
147
|
+
hd, cn, ln = ct
|
148
|
+
if hd == '_dqs'
|
149
|
+
[ '_ref', [ ct ], ln ]
|
150
|
+
elsif Flor.is_single_ref_tree?(ct)
|
151
|
+
[ '_ref', [ [ '_sqs', hd, ln ] ], ln ]
|
152
|
+
else
|
153
|
+
ct
|
154
|
+
end }
|
155
|
+
|
156
|
+
@node['tree'] = [ t[0], cn, t[2] ] if cn != t[1]
|
157
|
+
end
|
158
|
+
|
159
|
+
def rep_children
|
160
|
+
|
161
|
+
t = tree
|
162
|
+
li = t[1].length - 1
|
163
|
+
|
164
|
+
cn = t[1]
|
165
|
+
.each_with_index
|
166
|
+
.collect { |ct, i|
|
167
|
+
hd, cn, ln = ct
|
168
|
+
if hd == '_ref' && (@node['single_child'] || li != i)
|
169
|
+
[ '_rep', cn, ln ]
|
170
|
+
else
|
171
|
+
ct
|
172
|
+
end }
|
173
|
+
|
174
|
+
@node['tree'] = [ t[0], cn, t[2] ] if cn != t[1]
|
116
175
|
end
|
117
176
|
end
|
118
177
|
|
@@ -0,0 +1,71 @@
|
|
1
|
+
|
2
|
+
class Flor::Pro::Shuffle < Flor::Procedure
|
3
|
+
#
|
4
|
+
# Returns a shuffled version of an array.
|
5
|
+
#
|
6
|
+
# ## shuffle
|
7
|
+
#
|
8
|
+
# ```
|
9
|
+
# shuffle [ 0 1 2 3 4 ]
|
10
|
+
# # might set [ 3 2 0 1 4 ] in f.ret
|
11
|
+
# shuffle [ 0 1 2 3 4 ] 2
|
12
|
+
# # might set [ 4 2 ] in f.ret
|
13
|
+
# shuffle [ 0 1 2 3 4 ] count: 2
|
14
|
+
# # might set [ 4 2 ] in f.ret
|
15
|
+
#
|
16
|
+
# [ 0 1 2 3 4 ]
|
17
|
+
# shuffle _
|
18
|
+
# # might set [ 4 0 2 1 3 ] in f.ret
|
19
|
+
# ```
|
20
|
+
#
|
21
|
+
# ## sample
|
22
|
+
#
|
23
|
+
# When given a count integer, "sample" behaves exactly like "shuffle".
|
24
|
+
# When not given a count, it returns a single, random, element of the given
|
25
|
+
# array.
|
26
|
+
#
|
27
|
+
# ```
|
28
|
+
# sample [ 'a' 'b' 'c' ]
|
29
|
+
# # might set 'b' in f.ret
|
30
|
+
#
|
31
|
+
# [ 'a' 'b' 'c' ]
|
32
|
+
# sample _
|
33
|
+
# # might set 'b' in f.ret
|
34
|
+
#
|
35
|
+
# sample [ 'a' 'b' 'c' ] 2
|
36
|
+
# # might set [ 'c', 'b' ] in f.ret
|
37
|
+
# ```
|
38
|
+
#
|
39
|
+
# ## see also
|
40
|
+
#
|
41
|
+
# slice, index, reverse, and length
|
42
|
+
|
43
|
+
names %w[ shuffle sample ]
|
44
|
+
|
45
|
+
def pre_execute
|
46
|
+
|
47
|
+
unatt_unkeyed_children
|
48
|
+
|
49
|
+
@node['atts'] = []
|
50
|
+
@node['rets'] = []
|
51
|
+
end
|
52
|
+
|
53
|
+
def receive_last
|
54
|
+
|
55
|
+
arr =
|
56
|
+
(@node['rets'] + [ node_payload_ret ])
|
57
|
+
.find { |r| r.is_a?(Array) }
|
58
|
+
|
59
|
+
fail Flor::FlorError.new("no array to #{heap}") unless arr
|
60
|
+
|
61
|
+
cnt =
|
62
|
+
att('count', nil) ||
|
63
|
+
@node['rets'].find { |r| r.is_a?(Integer) }
|
64
|
+
|
65
|
+
ret = arr.sample(cnt || arr.size)
|
66
|
+
ret = ret.first if cnt == nil && heap == 'sample'
|
67
|
+
|
68
|
+
wrap('ret' => ret)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
@@ -0,0 +1,137 @@
|
|
1
|
+
|
2
|
+
class Flor::Pro::Slice < Flor::Procedure
|
3
|
+
#
|
4
|
+
# Takes an array or a string and returns a slice of it (a new
|
5
|
+
# array or a new string).
|
6
|
+
#
|
7
|
+
# "index" takes an array or a string and returns the element (character)
|
8
|
+
# at the given index.
|
9
|
+
#
|
10
|
+
# ## slice
|
11
|
+
#
|
12
|
+
# ```
|
13
|
+
# set a [ 'alpha' 'bravo' 'charly' ]
|
14
|
+
#
|
15
|
+
# slice a 1 -1 # sets [ 'bravo', 'charly' ] into the field 'ret'
|
16
|
+
# slice a from: 1 to: -1 # same as above
|
17
|
+
#
|
18
|
+
# a
|
19
|
+
# slice 1 -1 # sets [ 'bravo', 'charly' ] into the field 'ret'
|
20
|
+
# slice 1 -1 # sets [ 'charly' ] into the field 'ret'
|
21
|
+
# ```
|
22
|
+
#
|
23
|
+
# It slices the content of `f.ret` by default:
|
24
|
+
# ```
|
25
|
+
# set a [ 0 1 2 3 ]
|
26
|
+
# # ...
|
27
|
+
# a # (copy content of a into f.ret)
|
28
|
+
# slice 1 count: 2 # sets [ 1, 2 ] into the field 'ret'
|
29
|
+
# ```
|
30
|
+
#
|
31
|
+
# ## index
|
32
|
+
#
|
33
|
+
# ```
|
34
|
+
# set a [ 'alpha' 'bravo' 'charly' ]
|
35
|
+
#
|
36
|
+
# index a (-2) # sets 'bravo' into the field 'ret'
|
37
|
+
# index a at: -2 # sets 'bravo' into the field 'ret'
|
38
|
+
# ```
|
39
|
+
#
|
40
|
+
# It indexes the content of `f.ret` by default:
|
41
|
+
# ```
|
42
|
+
# set a [ 0 1 2 3 4 ]
|
43
|
+
# # ...
|
44
|
+
# a # (copy content of a into f.ret)
|
45
|
+
# index (-2) # sets 3 into the field 'ret'
|
46
|
+
# ```
|
47
|
+
#
|
48
|
+
# ## see also
|
49
|
+
#
|
50
|
+
# length
|
51
|
+
|
52
|
+
names 'slice', 'index'
|
53
|
+
|
54
|
+
# three cases: [ start, count ], [ start, end ], and [ start, end, step ]
|
55
|
+
|
56
|
+
def pre_execute
|
57
|
+
|
58
|
+
unatt_unkeyed_children
|
59
|
+
|
60
|
+
@node['atts'] = []
|
61
|
+
@node['rets'] = []
|
62
|
+
end
|
63
|
+
|
64
|
+
def receive_last
|
65
|
+
|
66
|
+
send(heap)
|
67
|
+
end
|
68
|
+
|
69
|
+
protected
|
70
|
+
|
71
|
+
def collection
|
72
|
+
|
73
|
+
coll =
|
74
|
+
@node['rets'].find { |e| e.respond_to?(:slice) } ||
|
75
|
+
node_payload_ret
|
76
|
+
|
77
|
+
fail Flor::FlorError.new(
|
78
|
+
"cannot slice instance of #{coll.class}", self
|
79
|
+
) unless coll.respond_to?(:slice)
|
80
|
+
|
81
|
+
coll
|
82
|
+
end
|
83
|
+
|
84
|
+
def slice
|
85
|
+
|
86
|
+
coll = collection
|
87
|
+
|
88
|
+
ints = @node['rets'].select { |e| e.is_a?(Integer) }
|
89
|
+
|
90
|
+
st = att('from', 'start') || ints.shift || 0
|
91
|
+
en = att('to', 'end') || ints.shift
|
92
|
+
co = att('count')
|
93
|
+
sp = att('step') || 1
|
94
|
+
|
95
|
+
en ||= (st + co - 1)
|
96
|
+
en = coll.length + en if en < 0
|
97
|
+
|
98
|
+
ret =
|
99
|
+
case coll
|
100
|
+
when String
|
101
|
+
do_slice(coll.chars, st, en, sp, true)
|
102
|
+
when Array
|
103
|
+
do_slice(coll, st, en, sp)
|
104
|
+
else
|
105
|
+
fail Flor::FlorError.new("cannot slice instance of #{coll.class}", self)
|
106
|
+
end
|
107
|
+
|
108
|
+
ret ||= ''
|
109
|
+
|
110
|
+
wrap('ret' => ret)
|
111
|
+
end
|
112
|
+
|
113
|
+
def do_slice(coll, st, en, sp, join=false)
|
114
|
+
|
115
|
+
l = coll.length
|
116
|
+
|
117
|
+
return nil if st >= l
|
118
|
+
|
119
|
+
r = []
|
120
|
+
|
121
|
+
while st <= en && st < l
|
122
|
+
r << coll[st]
|
123
|
+
st = st + sp
|
124
|
+
end
|
125
|
+
|
126
|
+
join ? r.join : r
|
127
|
+
end
|
128
|
+
|
129
|
+
def index
|
130
|
+
|
131
|
+
coll = collection
|
132
|
+
index = att('at') || @node['rets'].find { |e| e.is_a?(Integer) }
|
133
|
+
|
134
|
+
wrap('ret' => coll[index] || '')
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
@@ -0,0 +1,244 @@
|
|
1
|
+
|
2
|
+
class Flor::Pro::Sort < Flor::Procedure
|
3
|
+
#
|
4
|
+
# Sorts an array or an object.
|
5
|
+
#
|
6
|
+
# "sort" takes an array or an object and sorts its content.
|
7
|
+
#
|
8
|
+
# ```
|
9
|
+
# sort [ 0 7 1 5 3 4 2 6 ]
|
10
|
+
# # => [ 0 1 2 3 4 5 6 7 ]
|
11
|
+
# ```
|
12
|
+
#
|
13
|
+
# Without a function, "sort" uses the underlying (Ruby) sort methods.
|
14
|
+
#
|
15
|
+
# One can use a function to sort in specific ways:
|
16
|
+
# ```
|
17
|
+
# [ { name: 'Alice', age: 33, function: 'ceo' }
|
18
|
+
# { name: 'Bob', age: 44, function: 'cfo' }
|
19
|
+
# { name: 'Charly', age: 27, function: 'cto' } ]
|
20
|
+
# sort (def a b \ - a.age b.age)
|
21
|
+
# ```
|
22
|
+
#
|
23
|
+
# The function should return a boolean or a number. `true` or a negative
|
24
|
+
# number indicates `a` comes before `b`, anything else indicates `a`
|
25
|
+
# comes after `b`.
|
26
|
+
#
|
27
|
+
# ## behind the scenes
|
28
|
+
#
|
29
|
+
# Sorting an array results in a sorted array stored in `f.ret`, sorting
|
30
|
+
# an object results in a sorted (entries) object stored in `f.ret`.
|
31
|
+
#
|
32
|
+
# Using a function to sort is quite slow. Behind the scene a quicksort is
|
33
|
+
# used, to lower the number of calls to the sort function, but since
|
34
|
+
# the function is a flor function, calls are quite costly.
|
35
|
+
# By default, "sort" will cache the call results. For example, upon
|
36
|
+
# comparing 1 with 7, the results will be cached (the 7 vs 1 will be cached
|
37
|
+
# as well).
|
38
|
+
#
|
39
|
+
# It's OK to disable this caching:
|
40
|
+
# ```
|
41
|
+
# sort memo: false a (def a b \ < a b)
|
42
|
+
# ```
|
43
|
+
# (but why should we need that?)
|
44
|
+
#
|
45
|
+
# ## see also
|
46
|
+
#
|
47
|
+
# sort_by, reverse, and shuffle
|
48
|
+
|
49
|
+
name 'sort'
|
50
|
+
|
51
|
+
def pre_execute
|
52
|
+
|
53
|
+
@node['atts'] = []
|
54
|
+
|
55
|
+
@node['col'] = nil # collection
|
56
|
+
@node['fun'] = nil # function
|
57
|
+
|
58
|
+
unatt_unkeyed_children
|
59
|
+
end
|
60
|
+
|
61
|
+
def receive_non_att
|
62
|
+
|
63
|
+
if @node['ranges']
|
64
|
+
|
65
|
+
quick_partition_receive
|
66
|
+
|
67
|
+
else
|
68
|
+
|
69
|
+
r = payload['ret']
|
70
|
+
|
71
|
+
if Flor.is_func_tree?(r)
|
72
|
+
@node['fun'] ||= r
|
73
|
+
elsif Flor.is_collection?(r)
|
74
|
+
@node['col'] ||= r
|
75
|
+
end
|
76
|
+
|
77
|
+
super
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def receive_last
|
82
|
+
|
83
|
+
@node['col'] ||= node_payload_ret
|
84
|
+
|
85
|
+
if @node['col'].empty?
|
86
|
+
wrap('ret' => @node['col'])
|
87
|
+
elsif @node['fun']
|
88
|
+
quick_sort
|
89
|
+
else
|
90
|
+
default_sort
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
protected
|
95
|
+
|
96
|
+
def receive_argument
|
97
|
+
|
98
|
+
@node['args'] << payload['ret']
|
99
|
+
|
100
|
+
if children[@ncid]
|
101
|
+
execute_child(@ncid)
|
102
|
+
else
|
103
|
+
iterate
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
# default_sort
|
109
|
+
#
|
110
|
+
# When no function is given, turns to a collection of string
|
111
|
+
# (or JSON representations) and sorts
|
112
|
+
|
113
|
+
def default_sort
|
114
|
+
|
115
|
+
col =
|
116
|
+
@node['col']
|
117
|
+
classes =
|
118
|
+
col.collect(&:class).uniq
|
119
|
+
f =
|
120
|
+
(classes.count > 1 || [ Hash ].include?(classes[0])) ?
|
121
|
+
lambda { |e| e.is_a?(String) ? e : JSON.dump(e) } :
|
122
|
+
lambda { |e| e }
|
123
|
+
|
124
|
+
ret = col.sort_by(&f)
|
125
|
+
ret = col.inject({}) { |h, (k, v)| h[k] = v; h } if col.is_a?(Hash)
|
126
|
+
|
127
|
+
wrap('ret' => ret)
|
128
|
+
end
|
129
|
+
|
130
|
+
#
|
131
|
+
# quick_sort
|
132
|
+
#
|
133
|
+
# A quicksort drives the game
|
134
|
+
|
135
|
+
def quick_sort
|
136
|
+
|
137
|
+
col = @node['col']
|
138
|
+
@node['colk'] = col.is_a?(Hash) ? 'object' : 'array'
|
139
|
+
@node['col'] = col.to_a
|
140
|
+
|
141
|
+
@node['ranges'] = {}
|
142
|
+
|
143
|
+
@node['memo'] =
|
144
|
+
att('memo', 'cache') == false ?
|
145
|
+
nil :
|
146
|
+
{}
|
147
|
+
|
148
|
+
quick_partition_execute(0, col.length - 1)
|
149
|
+
end
|
150
|
+
|
151
|
+
def quick_swap(a, b)
|
152
|
+
|
153
|
+
return if a == b
|
154
|
+
|
155
|
+
col = @node['col']
|
156
|
+
col[a], col[b] = col[b], col[a]
|
157
|
+
end
|
158
|
+
|
159
|
+
def quick_kab(va, vb)
|
160
|
+
|
161
|
+
return [] unless @node['memo']
|
162
|
+
|
163
|
+
kab = [ va, vb ]
|
164
|
+
.collect { |e|
|
165
|
+
case e
|
166
|
+
when Array, Hash, String then Digest::SHA256.hexdigest(JSON.dump(e))
|
167
|
+
else JSON.dump(e)
|
168
|
+
end }
|
169
|
+
|
170
|
+
[ kab.join('__'), kab.reverse.join('__') ]
|
171
|
+
end
|
172
|
+
|
173
|
+
def quick_compare(ra, a, b)
|
174
|
+
|
175
|
+
col = @node['col']
|
176
|
+
va, vb = col[a], col[b]
|
177
|
+
|
178
|
+
ra['kab'], ra['kba'] = quick_kab(va, vb)
|
179
|
+
kab = ra['kab']
|
180
|
+
|
181
|
+
if kab && @node['memo'].has_key?(kab)
|
182
|
+
quick_partition_receive(ra['sub'], @node['memo'][kab])
|
183
|
+
else
|
184
|
+
apply(@node['fun'], [ [ 'a', va ], [ 'b', vb ] ], tree[2])
|
185
|
+
.tap { |ms| ra['sub'] = Flor.sub_nid(ms.first['nid']) }
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def quick_partition_execute(lo, hi)
|
190
|
+
|
191
|
+
return [] if lo >= hi
|
192
|
+
|
193
|
+
ra = @node['ranges']["#{lo}_#{hi}"] ||= { 'i' => lo, 'j' => lo }
|
194
|
+
|
195
|
+
quick_compare(ra, ra['j'], hi)
|
196
|
+
# compare element at j with pivot (element at hi)
|
197
|
+
end
|
198
|
+
|
199
|
+
def quick_partition_receive(sn=from_sub_nid, r=:none)
|
200
|
+
|
201
|
+
rk, ra = @node['ranges'].find { |_, v| v['sub'] == sn }
|
202
|
+
lo, hi = rk.split('_').collect(&:to_i)
|
203
|
+
i, j = ra['i'], ra['j']
|
204
|
+
|
205
|
+
if r == :none
|
206
|
+
ret = payload_ret
|
207
|
+
r = ret == true || (ret.is_a?(Numeric) && ret < 0)
|
208
|
+
if m = @node['memo']
|
209
|
+
m[ra['kab']], m[ra['kba']] = r, ! r
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
if r
|
214
|
+
quick_swap(i, j)
|
215
|
+
ra['i'] = i = (i + 1)
|
216
|
+
end
|
217
|
+
|
218
|
+
ra['j'] = j = (j + 1)
|
219
|
+
|
220
|
+
return quick_partition_execute(lo, hi) if j < hi # loop on
|
221
|
+
|
222
|
+
# partition loop is over...
|
223
|
+
|
224
|
+
quick_swap(i, hi)
|
225
|
+
|
226
|
+
@node['ranges'].delete(rk)
|
227
|
+
|
228
|
+
# partition at i...
|
229
|
+
|
230
|
+
ms =
|
231
|
+
quick_partition_execute(lo, i - 1) +
|
232
|
+
quick_partition_execute(i + 1, hi)
|
233
|
+
|
234
|
+
if ms.any?
|
235
|
+
ms
|
236
|
+
elsif @node['ranges'].any?
|
237
|
+
[]
|
238
|
+
else # quicksort is over
|
239
|
+
col = @node['col']
|
240
|
+
wrap('ret' => (@node['colk'] === 'object' ? Hash[col] : col))
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|