tiny_frp 0.0.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/tiny_frp/version.rb +1 -1
- data/lib/tiny_frp.rb +81 -104
- data/unit_test/test_tiny_frp.rb +23 -61
- 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: 95461bc9e9bcaec52bd9d693befbab7f761c4596
|
4
|
+
data.tar.gz: 864a0e822365a039c315192d02c509b8a15ddeef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68ee1322327823da24345bbd919030bec4e33990d2379612c1d3efa09d982215c73c39d4171489edda8786a90aa22a4c29334e64e7220fbdd9b47fbf6e64e719
|
7
|
+
data.tar.gz: 34f5f5cdd816f06c61a22a07ff5625d24b43370a0784e48127d4b281bef255cebb18dceb478e1e66f41f611508ed2a8586145921632e772e0a16402b4e241344
|
data/lib/tiny_frp/version.rb
CHANGED
data/lib/tiny_frp.rb
CHANGED
@@ -1,144 +1,121 @@
|
|
1
1
|
require "tiny_frp/version"
|
2
2
|
|
3
3
|
module TinyFRP
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
def self.process(node, n)
|
5
|
+
n.times.inject(res: [], last_memo: {}){|acc|
|
6
|
+
memo = node.call({}, acc[:last_memo])
|
7
|
+
{res: acc[:res] + [memo[node]], last_memo: memo}
|
8
|
+
}[:res]
|
9
|
+
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
def self.loop(node, &proc)
|
12
|
+
last_memo = {}
|
13
|
+
loop {
|
14
|
+
memo = node.call({}, last_memo)
|
15
|
+
proc.call(*memo[node])
|
16
|
+
last_memo = memo
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
class Node
|
21
|
+
def >>(node)
|
22
|
+
Composite.new(self, node)
|
15
23
|
end
|
16
24
|
|
17
|
-
def
|
18
|
-
|
19
|
-
s, i = *a
|
20
|
-
edges = traverse(s).merge(s.node => [[i, signal.node]]){|k, old, new| (old + new).uniq}
|
21
|
-
h.merge(edges){|k, old, new| (old + new).uniq}
|
22
|
-
end
|
25
|
+
def <<(node)
|
26
|
+
Composite.new(node, self)
|
23
27
|
end
|
24
28
|
|
25
|
-
def
|
26
|
-
|
27
|
-
queue = Array.new(@inputs)
|
28
|
-
until queue.empty?
|
29
|
-
node = queue.shift
|
30
|
-
val = node.call(*received[node].sort{|a, b| a[0] <=> b[0]}.map{|idx, val| val})
|
31
|
-
@children[node].each do |idx, ch_node|
|
32
|
-
received[ch_node] = received[ch_node] + [[idx, val]]
|
33
|
-
if received[ch_node].size == @parents[ch_node].size
|
34
|
-
queue << ch_node
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
return val
|
29
|
+
def +(node)
|
30
|
+
Bundle.new(self, node)
|
39
31
|
end
|
40
32
|
|
41
|
-
def
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
proc.call(v) if proc
|
47
|
-
}
|
48
|
-
when 0
|
49
|
-
[]
|
50
|
-
when 1
|
51
|
-
[conduct]
|
52
|
-
when Integer
|
53
|
-
[conduct] + run(mode - 1)
|
33
|
+
def call(memo, last_memo, *args)
|
34
|
+
if memo.has_key?(self)
|
35
|
+
memo
|
36
|
+
else
|
37
|
+
calc(memo, last_memo, *args)
|
54
38
|
end
|
55
39
|
end
|
56
40
|
end
|
57
41
|
|
58
|
-
class
|
59
|
-
|
60
|
-
|
61
|
-
@node, @assigned_signals = node, assigned_signals
|
42
|
+
class Lift < Node
|
43
|
+
def initialize(&proc)
|
44
|
+
@proc = proc
|
62
45
|
end
|
63
|
-
end
|
64
|
-
|
65
|
-
class Node
|
66
|
-
attr_reader :fixed_argc
|
67
46
|
|
68
|
-
def
|
69
|
-
|
47
|
+
def argc
|
48
|
+
@argc ||= @proc.parameters.select{|t, n| t == :opt}.count
|
70
49
|
end
|
71
50
|
|
72
|
-
def
|
73
|
-
|
74
|
-
if signals.any?{|s| !s.is_a?(Signal)}
|
75
|
-
raise "Error: cannot apply SignalFunction to Non-Signal Object."
|
76
|
-
end
|
77
|
-
if signals.size < @fixed_argc || (!@rest_arg && signals.size != @fixed_argc)
|
78
|
-
raise "Error: cannot apply SignalFunction to Signals."
|
79
|
-
end
|
80
|
-
Signal.new(self, signals)
|
51
|
+
def calc(memo, last_memo, *args)
|
52
|
+
memo.merge(self => [@proc.call(*args)])
|
81
53
|
end
|
82
54
|
end
|
83
55
|
|
84
56
|
class Foldp < Node
|
85
|
-
def initialize(initial_state, proc
|
86
|
-
@
|
87
|
-
@fixed_argc, @rest_arg = fixed_argc, rest_arg
|
57
|
+
def initialize(initial_state, &proc)
|
58
|
+
@initial_state, @proc = initial_state, proc
|
88
59
|
end
|
89
60
|
|
90
|
-
def
|
91
|
-
@
|
92
|
-
if diff.has_key?(k)
|
93
|
-
[k, diff[k]]
|
94
|
-
else
|
95
|
-
[k, @state[k]]
|
96
|
-
end
|
97
|
-
}.to_h
|
61
|
+
def argc
|
62
|
+
@argc ||= @proc.parameters.select{|t, n| t == :opt}.count - 1
|
98
63
|
end
|
99
64
|
|
100
|
-
def
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
65
|
+
def update(last_state, diff)
|
66
|
+
case diff
|
67
|
+
when Hash
|
68
|
+
last_state.keys.map{|k|
|
69
|
+
if diff.has_key?(k)
|
70
|
+
[k, diff[k]]
|
71
|
+
else
|
72
|
+
[k, last_state[k]]
|
73
|
+
end
|
74
|
+
}.to_h
|
75
|
+
else
|
76
|
+
diff
|
77
|
+
end
|
108
78
|
end
|
109
79
|
|
110
|
-
def call(*
|
111
|
-
|
80
|
+
def call(memo, last_memo, *args)
|
81
|
+
last_state = last_memo.has_key?(self) ? last_memo[self].first : @initial_state
|
82
|
+
memo.merge(self => [update(last_state, @proc.call(last_state, *args))])
|
112
83
|
end
|
113
84
|
end
|
114
|
-
|
115
|
-
class
|
116
|
-
def initialize(
|
117
|
-
@
|
85
|
+
|
86
|
+
class Composite < Node
|
87
|
+
def initialize(pnode, cnode)
|
88
|
+
@pnode, @cnode = pnode, cnode
|
118
89
|
end
|
119
90
|
|
120
|
-
def
|
121
|
-
|
91
|
+
def argc
|
92
|
+
@pnode.argc
|
122
93
|
end
|
123
|
-
end
|
124
94
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
95
|
+
def calc(memo, last_memo, *args)
|
96
|
+
p_res = @pnode.call(memo, last_memo, *args)
|
97
|
+
n_res = @cnode.call(p_res, last_memo, *p_res[@pnode])
|
98
|
+
n_res.merge(self => n_res[@cnode])
|
99
|
+
end
|
129
100
|
end
|
130
101
|
|
131
|
-
|
132
|
-
|
133
|
-
|
102
|
+
class Bundle < Node
|
103
|
+
def initialize(*nodes)
|
104
|
+
@nodes = nodes
|
105
|
+
end
|
134
106
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
Lift.new(proc, fixed_argc, rest_arg)
|
139
|
-
end
|
107
|
+
def argc
|
108
|
+
@argc ||= @nodes.inject(0){|acc, n| acc + n.argc}
|
109
|
+
end
|
140
110
|
|
141
|
-
|
142
|
-
|
111
|
+
def calc(memo, last_memo, *args)
|
112
|
+
res = @nodes.inject(memo: memo, rest_args: args){|acc, n|
|
113
|
+
{
|
114
|
+
memo: n.call(acc[:memo], last_memo, *acc[:rest_args].take(n.argc)),
|
115
|
+
rest_args: acc[:rest_args].drop(n.argc)
|
116
|
+
}
|
117
|
+
}[:memo]
|
118
|
+
res.merge(self => @nodes.inject([]){|acc, n| acc + res[n]})
|
119
|
+
end
|
143
120
|
end
|
144
121
|
end
|
data/unit_test/test_tiny_frp.rb
CHANGED
@@ -5,79 +5,41 @@ module TinyFRP
|
|
5
5
|
require 'test/unit'
|
6
6
|
require $LIBRARY_ROOT_PATH + '/lib/tiny_frp.rb'
|
7
7
|
|
8
|
-
|
9
|
-
def
|
10
|
-
|
11
|
-
s2 = Signal.new(:node_b, [])
|
12
|
-
s3 = Signal.new(:node_c, [s1, s2])
|
13
|
-
s4 = Signal.new(:node_d, [s3])
|
14
|
-
s5 = Signal.new(:node_e, [s3])
|
15
|
-
s6 = Signal.new(:node_f, [s4, s5])
|
16
|
-
ps = Runtime.make_map(s6)
|
17
|
-
|
18
|
-
assert_equal([], ps[:node_a])
|
19
|
-
assert_equal([], ps[:node_b])
|
20
|
-
assert_equal([:node_a, :node_b], ps[:node_c])
|
21
|
-
assert_equal([:node_c], ps[:node_d])
|
22
|
-
assert_equal([:node_c], ps[:node_e])
|
23
|
-
assert_equal([:node_d, :node_e], ps[:node_f])
|
8
|
+
module Util
|
9
|
+
def lift(&proc)
|
10
|
+
TinyFRP::Lift.new(&proc)
|
24
11
|
end
|
25
12
|
|
26
|
-
def
|
27
|
-
|
28
|
-
s2 = Signal.new(:node_b, [])
|
29
|
-
s3 = Signal.new(:node_c, [s1, s2])
|
30
|
-
s4 = Signal.new(:node_d, [s3])
|
31
|
-
s5 = Signal.new(:node_e, [s3])
|
32
|
-
s6 = Signal.new(:node_f, [s4, s5])
|
33
|
-
cs = Runtime.traverse(s6)
|
34
|
-
|
35
|
-
assert_equal(1, cs[:node_a].size)
|
36
|
-
assert(cs[:node_a].include? [0, :node_c])
|
37
|
-
|
38
|
-
assert_equal(1, cs[:node_b].size)
|
39
|
-
assert(cs[:node_b].include? [1, :node_c])
|
40
|
-
|
41
|
-
assert_equal(2, cs[:node_c].size)
|
42
|
-
assert(cs[:node_c].include? [0, :node_d])
|
43
|
-
assert(cs[:node_c].include? [0, :node_e])
|
44
|
-
|
45
|
-
assert_equal(1, cs[:node_d].size)
|
46
|
-
assert(cs[:node_d].include? [0, :node_f])
|
47
|
-
|
48
|
-
assert_equal(1, cs[:node_e].size)
|
49
|
-
assert(cs[:node_e].include? [1, :node_f])
|
50
|
-
|
51
|
-
assert_equal(0, cs[:node_f].size)
|
13
|
+
def foldp(init_state, &proc)
|
14
|
+
TinyFRP::Foldp.new(init_state, &proc)
|
52
15
|
end
|
16
|
+
end
|
53
17
|
|
54
|
-
|
55
|
-
|
56
|
-
s1 = Signal.new(proc{ v1 = v1 + 1}, [])
|
57
|
-
s2 = Signal.new(proc{ v2 = v2 * 2}, [])
|
58
|
-
s3 = Signal.new(proc{|a, b| a + b}, [s1, s2])
|
59
|
-
|
60
|
-
assert_equal([1+2, 2+4, 3+8, 4+16, 5+32], Runtime.new(s3).run(5))
|
61
|
-
end
|
18
|
+
class TinyFRPTest < Test::Unit::TestCase
|
19
|
+
include Util
|
62
20
|
|
63
21
|
def test_lift
|
64
22
|
v1, v2 = 0, 1
|
65
|
-
|
66
|
-
|
67
|
-
signal = TinyFRP.bundle(l1, l2) >> TinyFRP.lift{|a, b| a + b}
|
68
|
-
|
69
|
-
assert_equal([1+2, 2+4, 3+8, 4+16, 5+32], Runtime.new(signal).run(5))
|
23
|
+
node = lift{|a, b| a + b} << lift{v1 = v1 + 1} + lift{v2 = v2 * 2}
|
24
|
+
assert_equal([[1+2], [2+4], [3+8], [4+16], [5+32]], TinyFRP.process(node, 5))
|
70
25
|
end
|
71
26
|
|
72
27
|
def test_foldp
|
73
|
-
|
74
|
-
|
75
|
-
|
28
|
+
node = lift{|a, b| a + b} << foldp(0){|acc| acc + 1} + foldp(1){|acc| acc * 2}
|
29
|
+
assert_equal([[1+2], [2+4], [3+8], [4+16], [5+32]], TinyFRP.process(node, 5))
|
30
|
+
end
|
76
31
|
|
77
|
-
|
32
|
+
def test_composite
|
33
|
+
node1 = foldp(0){|a| a + 1} >> lift{|a| a + 1}
|
34
|
+
node2 = foldp(0){|a| a + 1} >> lift{|a| a - 1}
|
35
|
+
node3 = lift{|a, b| a * b} << node1 + node2
|
36
|
+
assert_equal([[1 * 1 - 1], [2 * 2 - 1], [3 * 3 - 1]], TinyFRP.process(node3, 3))
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_bundle
|
40
|
+
node = foldp(0){|a| a + 1} + foldp(1){|a| a + 1} + foldp(2){|a| a + 1}
|
41
|
+
assert_equal([[1, 2, 3], [2, 3, 4], [3, 4, 5]], TinyFRP.process(node, 3))
|
78
42
|
end
|
79
43
|
end
|
80
44
|
end
|
81
45
|
end
|
82
|
-
|
83
|
-
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tiny_frp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- sawaken
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|