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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6a0da64a0deadc5d35504fb31fa7c0bfc93a3b7a
4
- data.tar.gz: 1ee140957c306bdc7d6d08fc8a80b464e56d1e2c
3
+ metadata.gz: 95461bc9e9bcaec52bd9d693befbab7f761c4596
4
+ data.tar.gz: 864a0e822365a039c315192d02c509b8a15ddeef
5
5
  SHA512:
6
- metadata.gz: 255e6f5b217918fe1e1ffc7228a8662d063119317a73d1e7ac9e5479267d953b561ea8d638bee75ed9345a27149d8ed8a95f198de4b7dd2a00d898d123406073
7
- data.tar.gz: 3bc5920a8e340e1b02d1df34c33c95a09cb388110dd7bc50826dbad65f3db44a46027135c278cad21d7c86e6edfc543b2b2c3cd4fa3ceb8fb169e548d5ae0e45
6
+ metadata.gz: 68ee1322327823da24345bbd919030bec4e33990d2379612c1d3efa09d982215c73c39d4171489edda8786a90aa22a4c29334e64e7220fbdd9b47fbf6e64e719
7
+ data.tar.gz: 34f5f5cdd816f06c61a22a07ff5625d24b43370a0784e48127d4b281bef255cebb18dceb478e1e66f41f611508ed2a8586145921632e772e0a16402b4e241344
@@ -1,3 +1,3 @@
1
1
  module TinyFRP
2
- VERSION = "0.0.3"
2
+ VERSION = "1.0.0"
3
3
  end
data/lib/tiny_frp.rb CHANGED
@@ -1,144 +1,121 @@
1
1
  require "tiny_frp/version"
2
2
 
3
3
  module TinyFRP
4
- class Runtime
5
- def initialize(signal)
6
- @parents = Runtime.make_map(signal)
7
- @children = Runtime.traverse(signal)
8
- @inputs = @parents.keys.select{|node| @parents[node] == []}
9
- end
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
- def self.make_map(signal)
12
- signal.assigned_signals.inject({}){|h, s|
13
- h.merge(make_map(s))
14
- }.merge(signal.node => signal.assigned_signals.map{|s| s.node})
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 self.traverse(signal)
18
- signal.assigned_signals.each_with_index.inject({signal.node =>[]}) do |h, a|
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 conduct
26
- received = Hash.new([])
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 run(mode=:loop, &proc)
42
- case mode
43
- when :loop
44
- loop{
45
- v = conduct
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 Signal
59
- attr_reader :node, :assigned_signals
60
- def initialize(node, assigned_signals)
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 >>(node)
69
- node.apply([self])
47
+ def argc
48
+ @argc ||= @proc.parameters.select{|t, n| t == :opt}.count
70
49
  end
71
50
 
72
- def apply(args)
73
- signals = args.map{|s| s.is_a?(Node) && s.fixed_argc == 0 ? Signal.new(s, []) : s}
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, fixed_argc, rest_arg)
86
- @state, @proc = initial_state, proc
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 update(diff)
91
- @state.keys.map{|k|
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 call(*raw_args)
101
- @state = update(@proc.call(@state, *raw_args))
102
- end
103
- end
104
-
105
- class Lift < Node
106
- def initialize(proc, fixed_argc, rest_arg=false)
107
- @proc, @fixed_argc, @rest_arg = proc, fixed_argc, rest_arg
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(*raw_args)
111
- @proc.call(*raw_args)
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 Bundler
116
- def initialize(signals)
117
- @signals = signals
85
+
86
+ class Composite < Node
87
+ def initialize(pnode, cnode)
88
+ @pnode, @cnode = pnode, cnode
118
89
  end
119
90
 
120
- def >>(node)
121
- node.apply(@signals)
91
+ def argc
92
+ @pnode.argc
122
93
  end
123
- end
124
94
 
125
- def self.foldp(initial_state, &proc)
126
- fixed_argc = proc.parameters.select{|t, n| t == :opt}.count - 1
127
- rest_arg = proc.parameters.any?{|t, n| t == :rest}
128
- Foldp.new(initial_state, proc, fixed_argc, rest_arg)
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
- def self.bundle(*signals)
132
- Bundler.new(signals)
133
- end
102
+ class Bundle < Node
103
+ def initialize(*nodes)
104
+ @nodes = nodes
105
+ end
134
106
 
135
- def self.lift(&proc)
136
- fixed_argc = proc.parameters.select{|t, n| t == :opt}.count
137
- rest_arg = proc.parameters.any?{|t, n| t == :rest}
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
- def self.bottom
142
- Lift.new(proc{|*_| nil}, 0, true)
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
@@ -5,79 +5,41 @@ module TinyFRP
5
5
  require 'test/unit'
6
6
  require $LIBRARY_ROOT_PATH + '/lib/tiny_frp.rb'
7
7
 
8
- class RuntimeTest < Test::Unit::TestCase
9
- def test_make_map
10
- s1 = Signal.new(:node_a, [])
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 test_traverse
27
- s1 = Signal.new(:node_a, [])
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
- def test_conduct
55
- v1, v2 = 0, 1
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
- l1 = TinyFRP.lift{v1 = v1 + 1}
66
- l2 = TinyFRP.lift{v2 = v2 * 2}
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
- f1 = TinyFRP.foldp(val: 0){|acc| {val: acc[:val] + 1}}
74
- f2 = TinyFRP.foldp(val: 1){|acc| {val: acc[:val] * 2}}
75
- signal = TinyFRP.bundle(f1, f2) >> TinyFRP.lift{|a, b| a[:val] + b[:val]}
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
- assert_equal([1+2, 2+4, 3+8, 4+16, 5+32], Runtime.new(signal).run(5))
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.3
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-04 00:00:00.000000000 Z
11
+ date: 2015-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler