tiny_frp 0.0.3 → 1.0.0

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 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