fr 0.9.1

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.
Files changed (64) hide show
  1. data/README.md +0 -0
  2. data/Rakefile +81 -0
  3. data/lib/fr.rb +44 -0
  4. data/lib/fr/applicative.rb +0 -0
  5. data/lib/fr/array.rb +168 -0
  6. data/lib/fr/boolean.rb +15 -0
  7. data/lib/fr/either.rb +94 -0
  8. data/lib/fr/errors.rb +5 -0
  9. data/lib/fr/errors/zipper_error.rb +8 -0
  10. data/lib/fr/functor.rb +11 -0
  11. data/lib/fr/functor/array.rb +15 -0
  12. data/lib/fr/functor/either.rb +17 -0
  13. data/lib/fr/functor/maybe.rb +17 -0
  14. data/lib/fr/maybe.rb +76 -0
  15. data/lib/fr/monad.rb +236 -0
  16. data/lib/fr/monad/array.rb +21 -0
  17. data/lib/fr/monad/either.rb +18 -0
  18. data/lib/fr/monad/maybe.rb +24 -0
  19. data/lib/fr/monad/random.rb +50 -0
  20. data/lib/fr/monad/reader.rb +45 -0
  21. data/lib/fr/monad/state.rb +62 -0
  22. data/lib/fr/monad/writer.rb +49 -0
  23. data/lib/fr/monoid.rb +52 -0
  24. data/lib/fr/monoid/array.rb +13 -0
  25. data/lib/fr/monoid/boolean.rb +23 -0
  26. data/lib/fr/monoid/either.rb +13 -0
  27. data/lib/fr/monoid/maybe.rb +15 -0
  28. data/lib/fr/monoid/numeric.rb +31 -0
  29. data/lib/fr/monoid/string.rb +11 -0
  30. data/lib/fr/numeric.rb +1 -0
  31. data/lib/fr/object.rb +45 -0
  32. data/lib/fr/string.rb +1 -0
  33. data/lib/fr/szipper.rb +36 -0
  34. data/lib/fr/szipper/abstract_cursor.rb +63 -0
  35. data/lib/fr/szipper/edited_cursor.rb +62 -0
  36. data/lib/fr/szipper/enumerable.rb +266 -0
  37. data/lib/fr/szipper/head_cursor.rb +71 -0
  38. data/lib/fr/szipper/head_prev_cursor.rb +112 -0
  39. data/lib/fr/szipper/memoized_cursor.rb +59 -0
  40. data/lib/fr/szipper/node.rb +67 -0
  41. data/lib/fr/szipper/tail_next_cursor.rb +68 -0
  42. data/lib/fr/thunk.rb +73 -0
  43. data/lib/fr/tzipper.rb +38 -0
  44. data/lib/fr/tzipper/abstract_cursor.rb +351 -0
  45. data/lib/fr/tzipper/dangling_cursor.rb +107 -0
  46. data/lib/fr/tzipper/edited_cursor.rb +161 -0
  47. data/lib/fr/tzipper/memoized_cursor.rb +129 -0
  48. data/lib/fr/tzipper/node.rb +57 -0
  49. data/lib/fr/tzipper/path.rb +125 -0
  50. data/lib/fr/tzipper/root_cursor.rb +124 -0
  51. data/lib/fr/unfold.rb +93 -0
  52. data/spec/examples/array.example +0 -0
  53. data/spec/examples/monads/array.example +0 -0
  54. data/spec/examples/monads/maybe.example +0 -0
  55. data/spec/examples/monads/random.example +0 -0
  56. data/spec/examples/monads/state.example +0 -0
  57. data/spec/examples/szipper.example +652 -0
  58. data/spec/examples/thunk.example +0 -0
  59. data/spec/examples/tzipper.example +0 -0
  60. data/spec/examples/unfold.example +90 -0
  61. data/spec/spec_helper.rb +23 -0
  62. data/spec/support/szipper/model.rb +225 -0
  63. data/spec/support/szipper/nodel.rb +144 -0
  64. metadata +116 -0
@@ -0,0 +1,125 @@
1
+ module Fr
2
+ module TZipper
3
+
4
+ class AbstractPath
5
+
6
+ # @return [AbstractPath]
7
+ # abstract :parent
8
+
9
+ # Contains the node's leftward siblings, sorted nearest to furthest
10
+ #
11
+ # @return [Array<#leaf?, #children, #copy>]
12
+ # abstract :left
13
+
14
+ # Contains the node's rightward siblings, sorted nearest to furthest
15
+ #
16
+ # @return [Array<#leaf?, #children, #copy>]
17
+ # abstract :right
18
+
19
+ # True when the node has no rightward siblings
20
+ # abstract :last?
21
+
22
+ # True when the node has no leftward siblings
23
+ # abstract :first?
24
+
25
+ # Distance from the root node
26
+ #
27
+ # @return [Integer]
28
+ # abstract :depth
29
+ end
30
+
31
+ # @private
32
+ Root = Class.new(AbstractPath) do
33
+
34
+ # @return self
35
+ def parent
36
+ self
37
+ end
38
+
39
+ # (see AbstractPath#left)
40
+ def left
41
+ []
42
+ end
43
+
44
+ # (see AbstractPath#right)
45
+ def right
46
+ []
47
+ end
48
+
49
+ # (see AbstractPath#last?)
50
+ # @return true
51
+ def last?
52
+ true
53
+ end
54
+
55
+ # (see AbstractPath#first?)
56
+ # @return true
57
+ def first?
58
+ true
59
+ end
60
+
61
+ # (see AbstractPath#depth)
62
+ def depth
63
+ 0
64
+ end
65
+
66
+ def position
67
+ nil
68
+ end
69
+
70
+ # @return [String]
71
+ def inspect
72
+ "root"
73
+ end
74
+ end.new
75
+
76
+ class Hole < AbstractPath
77
+
78
+ # (see AbstractPath#right)
79
+ attr_reader :left
80
+
81
+ # @return [AbstractPath]
82
+ attr_reader :parent
83
+
84
+ # (see AbstractPath#left)
85
+ attr_reader :right
86
+
87
+ def initialize(left, parent, right)
88
+ @left, @parent, @right =
89
+ left, parent, right
90
+ end
91
+
92
+ # (see AbstractPath#last?)
93
+ def last?
94
+ @right.empty?
95
+ end
96
+
97
+ # (see AbstractPath#first?)
98
+ def first?
99
+ @left.empty?
100
+ end
101
+
102
+ # (see AbstractPath#depth)
103
+ def depth
104
+ 1 + @parent.depth
105
+ end
106
+
107
+ def position
108
+ @left.length
109
+ end
110
+
111
+ # @return [String]
112
+ def inspect
113
+ "#{@parent.inspect}/#{@left.length}"
114
+ end
115
+
116
+ # @return [Boolean]
117
+ def ==(other)
118
+ AbstractCursor === other and
119
+ depth == other.depth and
120
+ position == other.position
121
+ end
122
+ end
123
+
124
+ end
125
+ end
@@ -0,0 +1,124 @@
1
+ module Fr
2
+ module TZipper
3
+
4
+ class RootCursor < AbstractCursor
5
+
6
+ # @return [AbstractNode]
7
+ attr_reader :node
8
+
9
+ # @return [AbstractPath]
10
+ attr_reader :path
11
+
12
+ def initialize(node)
13
+ @node, @path =
14
+ node, Root
15
+ end
16
+
17
+ # @group Query the Tree Location
18
+ #########################################################################
19
+
20
+ # (see AbstractCursor#depth)
21
+ def depth
22
+ 0
23
+ end
24
+
25
+ # (see AbstractCursor#first?)
26
+ def first?
27
+ true
28
+ end
29
+
30
+ # (see AbstractCursor#last?)
31
+ def last?
32
+ true
33
+ end
34
+
35
+ # (see AbstractCursor#leaf?)
36
+ def leaf?
37
+ @node.leaf? or @node.children.empty?
38
+ end
39
+
40
+ # (see AbstractCursor#root?)
41
+ def root?
42
+ true
43
+ end
44
+
45
+ # @group Traversing the Tree
46
+ #########################################################################
47
+
48
+ # (see AbstractCursor#first)
49
+ def first
50
+ self
51
+ end
52
+
53
+ # (see AbstractCursor#last)
54
+ def last
55
+ self
56
+ end
57
+
58
+ # (see AbstractCursor#next)
59
+ # @return [void]
60
+ def next
61
+ raise Errors::ZipperError,
62
+ "root node has no siblings"
63
+ end
64
+
65
+ # (see AbstractCursor#prev)
66
+ # @return [void]
67
+ def prev
68
+ raise Errors::ZipperError,
69
+ "root node has no siblings"
70
+ end
71
+
72
+ # (see AbstractCursor#root)
73
+ # @return [RootCursor]
74
+ def root
75
+ self
76
+ end
77
+
78
+ # (see AbstractCursor#up)
79
+ # @return [void]
80
+ def up
81
+ raise Errors::ZipperError,
82
+ "root node has no parent"
83
+ end
84
+
85
+ # @group Editing the Tree
86
+ #########################################################################
87
+
88
+ # (see AbstractCursor#append)
89
+ # @return [void]
90
+ def append(node)
91
+ raise Errors::ZipperError,
92
+ "root node has no siblings"
93
+ end
94
+
95
+ # (see AbstractCursor#prepend)
96
+ # @return [void]
97
+ def prepend(node)
98
+ raise Errors::ZipperError,
99
+ "root node has no siblings"
100
+ end
101
+
102
+ # (see AbstractCursor#replace)
103
+ # @return [RootCursor]
104
+ def replace(node)
105
+ RootCursor.new(node)
106
+ end
107
+
108
+ # (see AbstractCursor#delete)
109
+ # @return [void]
110
+ def delete
111
+ raise Errors::ZipperError,
112
+ "cannot delete root node"
113
+ end
114
+
115
+ # @endgroup
116
+ #########################################################################
117
+
118
+ def inspect
119
+ "Cursor(Root, #{@node.inspect})"
120
+ end
121
+ end
122
+
123
+ end
124
+ end
data/lib/fr/unfold.rb ADDED
@@ -0,0 +1,93 @@
1
+ class << Array
2
+ # Recursive version
3
+ def unfold(seed, &block)
4
+ m = yield(seed)
5
+ m.fold([]){|(item, seed)| [item] + unfold(seed, &block) }
6
+ end
7
+
8
+ # First generated element is first in Array
9
+ def unfold(seed)
10
+ [].tap do |xs|
11
+ while true
12
+ yield(seed).fold(false) do |(item,seed_)|
13
+ xs << item
14
+ seed = seed_
15
+ end || break
16
+ end
17
+ end
18
+ end
19
+
20
+ # First generated element is last in Array
21
+ def unfoldR(seed, &block)
22
+ m = yield(seed)
23
+ m.fold([]){|(item, seed)| unfold(seed, &block) + [item] }
24
+ end
25
+
26
+ # First generated element is last in Array
27
+ def unfoldR(seed)
28
+ [].tap do |xs|
29
+ while true
30
+ yield(seed).fold(false) do |(item,seed_)|
31
+ xs.unshift(item)
32
+ seed = seed_
33
+ end || break
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ class << Hash
40
+ # Recursive version
41
+ def unfold(seed, &block)
42
+ m = yield(seed)
43
+ m.fold({}){|(item, seed)| unfold(seed, &block).merge(item) }
44
+ end
45
+
46
+ # Iterative version
47
+ def unfold(seed)
48
+ {}.tap do |xs|
49
+ while true
50
+ yield(seed).fold(false) do |(item,seed_)|
51
+ xs.update(item)
52
+ seed = seed_
53
+ end || break
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ class Set; end
60
+ class << Set
61
+ # Recursive version
62
+ def unfold(seed, &block)
63
+ m = yield(seed)
64
+ m.fold(Set.new){|(item, seed)| unfold(seed, &block) << item }
65
+ end
66
+
67
+ # Iterative version
68
+ def unfold(seed)
69
+ Set.new.tap do |xs|
70
+ while true
71
+ yield(seed).fold(false) do |(item,seed_)|
72
+ xs << item
73
+ seed = seed_
74
+ end || break
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ class << Enumerator
81
+ # First generated element is first in Enumerator
82
+ def unfold(seed)
83
+ Enumerator.new do |yielder|
84
+ while true
85
+ yield(seed).fold(false) do |(item,seed_)|
86
+ yielder.yield item
87
+ seed = seed_
88
+ true
89
+ end || break
90
+ end
91
+ end
92
+ end
93
+ end
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,652 @@
1
+ require "spec_helper"
2
+
3
+ describe Fr::SZipper do
4
+
5
+ let(:z) { Fr::SZipper }
6
+ let(:n) { Fr::SZipper::Node }
7
+
8
+ describe ".build(enum)" do
9
+ context "empty" do
10
+ subject { z.build }
11
+
12
+ it { should be_head }
13
+ it { should be_tail }
14
+ it { should be_empty }
15
+ it { should_not respond_to(:node) }
16
+ end
17
+
18
+ context "non-empty" do
19
+ subject { z.build(n.new("x")) }
20
+
21
+ it { should be_head }
22
+ it { should_not be_empty }
23
+ it { should respond_to(:node) }
24
+ end
25
+ end
26
+
27
+ describe "#head?" do
28
+ subject { z.build(n.build(%w(x y z))) }
29
+
30
+ # The first element (or empty zipper)
31
+ specify { subject.should be_head }
32
+ specify { z.build.should be_head }
33
+ specify { z.build.prev(true).should be_head }
34
+ specify { z.build.next(true).should be_head }
35
+
36
+ # Other elements
37
+ specify { subject.prev(true).should_not be_head }
38
+ specify { subject.next.should_not be_head }
39
+ specify { subject.next.next.should_not be_head }
40
+ specify { subject.next.next.next(true).should_not be_head }
41
+ end
42
+
43
+ describe "#head" do
44
+ let(:x) { n.build(%w(x y z)) }
45
+ subject { z.build(x) }
46
+
47
+ # Empty sequence has no head element
48
+ specify { expect { n.build.head }.to raise_error }
49
+ specify { expect { n.build.prev(true).head }.to raise_error }
50
+ specify { expect { n.build.next(true).head }.to raise_error }
51
+
52
+ # The first element
53
+ specify { subject.head.should eql(subject) }
54
+ specify { subject.head.node.should eql(x) }
55
+
56
+ # Other elements
57
+ specify { subject.prev(true).head.node.should eql(x) }
58
+ specify { subject.next.head.node.should eql(x) }
59
+ specify { subject.next.next.head.node.should eql(x) }
60
+ specify { subject.next.next.next(true).head.node.should eql(x) }
61
+ end
62
+
63
+ describe "#tail?" do
64
+ subject { z.build(n.build(%w(x y z))) }
65
+
66
+ # The last element
67
+ specify { subject.next.next.should be_tail }
68
+ specify { z.build.should be_tail }
69
+ specify { z.build.prev(true).should be_tail }
70
+ specify { z.build.next(true).should be_tail }
71
+
72
+ # Other elements
73
+ specify { subject.should_not be_tail }
74
+ specify { subject.prev(true).should_not be_tail }
75
+ specify { subject.next.should_not be_tail }
76
+ specify { subject.next.next.next(true).should_not be_tail }
77
+ end
78
+
79
+ describe "#tail" do
80
+ let(:x) { n.build(%w(x y z)) }
81
+ subject { z.build(x) }
82
+
83
+ # Empty sequence has no tail element
84
+ specify { expect { z.build.tail }.to raise_error }
85
+
86
+ # Tail element
87
+ specify { subject.tail.should be_tail }
88
+ specify { subject.next.next.tail.node.should eql(x.next.next) }
89
+ specify { subject.next.next.tail.should eql(subject.next.next) }
90
+
91
+ # Other elements
92
+ specify { subject.tail.node.should eql(x.next.next) }
93
+ specify { subject.prev(true).tail.node.should eql(x.next.next) }
94
+ specify { subject.next.tail.node.should eql(x.next.next) }
95
+ specify { subject.next.next.next(true).tail.node.should eql(x.next.next) }
96
+ end
97
+
98
+ describe "#position" do
99
+ subject { z.build(n.build(%w(x y z))) }
100
+
101
+ # Empty sequence
102
+ specify { z.build.position.should == -1 }
103
+
104
+ # Non-empty sequence
105
+ specify { subject.prev(true).position.should == -1 }
106
+ specify { subject.position.should == 0 }
107
+ specify { subject.next.position.should == 1 }
108
+ specify { subject.next.next.position.should == 2 }
109
+ specify { subject.next.next.next(true).position.should == 3 }
110
+ end
111
+
112
+ describe "#empty?" do
113
+ subject { z.build(n.build(%w(x y z))) }
114
+
115
+ # True if no current or following elements
116
+ specify { z.build.should be_empty }
117
+ specify { subject.tail.next(true).should be_empty }
118
+
119
+ # False when on an element or elements follow
120
+ specify { subject.should_not be_empty }
121
+ specify { subject.next.should_not be_empty }
122
+ specify { subject.next.next.should_not be_empty }
123
+ specify { subject.prev(true).should_not be_empty }
124
+ end
125
+
126
+ describe "#prev" do
127
+ let(:x) { n.build(%w(x y z)) }
128
+ subject { z.build(x) }
129
+
130
+ # head.prev is ill-defined
131
+ specify { expect { z.build.prev }.to raise_error }
132
+ specify { expect { subject.prev }.to raise_error }
133
+
134
+ # Empty sequence has no previous element
135
+ specify { z.build.prev(true).should == z.build }
136
+
137
+ # Non-empty sequence
138
+ specify { subject.prev(true).should_not respond_to(:node) }
139
+ specify { subject.tail.prev.node.should eql(x.next) }
140
+ specify { subject.tail.prev.prev.node.should eql(x) }
141
+ end
142
+
143
+ describe "#next" do
144
+ let(:x) { n.build(%w(x y z)) }
145
+ subject { z.build(x) }
146
+
147
+ # tail.next is ill-defined
148
+ specify { expect { z.build.next }.to raise_error }
149
+ specify { expect { subject.tail.next }.to raise_error }
150
+
151
+ # Empty sequence has no next element
152
+ specify { z.build.next(true).should == z.build }
153
+
154
+ # Non-empty sequence
155
+ specify { subject.next.node.should eql(x.next) }
156
+ specify { subject.prev(true).next.node.should eql(x) }
157
+ specify { subject.next.next.node.should eql(x.next.next) }
158
+ specify { subject.next.next.next(true).should_not respond_to(:node) }
159
+ specify { subject.tail.next(true).next(true).should_not respond_to(:node) }
160
+ end
161
+
162
+ describe "#append(node)" do
163
+ let(:x) { n.build(%w(x y z)) }
164
+ subject { z.build(x) }
165
+
166
+ context "on empty stream" do
167
+ # Positioned at newly inserted node
168
+ specify { z.build.append(n.new("_")).node.value.should == "_" }
169
+ specify { z.build.append(n.new("_")).should be_head }
170
+ specify { z.build.append(n.new("_")).should be_tail }
171
+ end
172
+
173
+ context "at head" do
174
+ # Positioned at newly inserted node
175
+ specify { subject.append(n.new("_")).should_not be_head }
176
+ specify { subject.append(n.new("_")).node.value.should == "_" }
177
+ specify { subject.append(n.new("_")).prev.node.value.should == "x" }
178
+ specify { subject.append(n.new("_")).next.node.value.should == "y" }
179
+
180
+ # Previous node's `next` is updated
181
+ specify { subject.append(n.new("_")).prev.next.node.value.should == "_" }
182
+ specify { subject.append(n.new("_")).prev.node.next.value.should == "_" }
183
+ end
184
+
185
+ context "at head.next" do
186
+ # Positioned at newly inserted node
187
+ specify { subject.next.append(n.new("_")).node.value.should == "_" }
188
+ specify { subject.next.append(n.new("_")).prev.node.value.should == "y" }
189
+ specify { subject.next.append(n.new("_")).next.node.value.should == "z" }
190
+
191
+ # Previous node's `next` is updated
192
+ specify { subject.next.append(n.new("_")).prev.next.node.value.should == "_" }
193
+ specify { subject.next.append(n.new("_")).prev.node.next.value.should == "_" }
194
+ end
195
+
196
+ context "at tail" do
197
+ # Positioned at newly inserted node
198
+ specify { subject.tail.append(n.new("_")).node.value.should == "_" }
199
+ specify { subject.tail.append(n.new("_")).prev.node.value.should == "z" }
200
+ specify { subject.tail.append(n.new("_")).should be_tail }
201
+
202
+ # Previous node's `next` is updated
203
+ specify { subject.tail.append(n.new("_")).prev.next.node.value.should == "_" }
204
+ specify { subject.tail.append(n.new("_")).prev.node.next.value.should == "_" }
205
+ end
206
+
207
+ context "at head.prev" do
208
+ # Positioned at newly inserted node
209
+ specify { subject.prev(true).append(n.new("_")).should be_head }
210
+ specify { subject.prev(true).append(n.new("_")).node.value.should == "_" }
211
+ specify { subject.prev(true).append(n.new("_")).next.node.value.should == "x" }
212
+ specify { subject.prev(true).append(n.new("_")).prev(true).position.should == -1 }
213
+ end
214
+
215
+ context "at tail.next" do
216
+ # Positioned at newly inserted node
217
+ specify { subject.tail.next(true).append(n.new("_")).should be_tail }
218
+ specify { subject.tail.next(true).append(n.new("_")).node.value.should == "_" }
219
+ specify { subject.tail.next(true).append(n.new("_")).prev.node.value.should == "z" }
220
+
221
+ # Previous node's `next` is updated
222
+ specify { subject.tail.next(true).append(n.new("_")).prev.next.node.value.should == "_" }
223
+ specify { subject.tail.next(true).append(n.new("_")).prev.node.next.value.should == "_" }
224
+ end
225
+
226
+ context "append(y).prev.append(x)" do
227
+ # todo
228
+ # <x> <y> x y z
229
+ # a <x> <y> b c
230
+ # a b <x> <y> c
231
+ # x y z <x> <y>
232
+ end
233
+ end
234
+
235
+ describe "#prepend(node)" do
236
+ let(:x) { n.build(%w(x y z)) }
237
+ subject { z.build(x) }
238
+
239
+ context "on empty stream" do
240
+ # Positioned at newly inserted node
241
+ specify { z.build.prepend(n.new("_")).node.value.should == "_" }
242
+ specify { z.build.prepend(n.new("_")).should be_head }
243
+ specify { z.build.prepend(n.new("_")).should be_tail }
244
+ end
245
+
246
+ context "at head" do
247
+ # Positioned at newly inserted node
248
+ specify { subject.prepend(n.new("_")).should be_head }
249
+ specify { subject.prepend(n.new("_")).node.value.should == "_" }
250
+ specify { subject.prepend(n.new("_")).next.node.value.should == "x" }
251
+ end
252
+
253
+ context "at head.next" do
254
+ # Positioned at newly inserted node
255
+ specify { subject.next.prepend(n.new("_")).node.value.should == "_" }
256
+ specify { subject.next.prepend(n.new("_")).prev.node.value.should == "x" }
257
+ specify { subject.next.prepend(n.new("_")).next.node.value.should == "y" }
258
+
259
+ # Previous node's `next` is updated
260
+ specify { subject.next.prepend(n.new("_")).prev.next.node.value.should == "_" }
261
+ specify { subject.next.prepend(n.new("_")).prev.node.next.value.should == "_" }
262
+ end
263
+
264
+ context "at tail" do
265
+ # Positioned at newly inserted node
266
+ specify { subject.tail.prepend(n.new("_")).should_not be_tail }
267
+ specify { subject.tail.prepend(n.new("_")).node.value.should == "_" }
268
+ specify { subject.tail.prepend(n.new("_")).prev.node.value.should == "y" }
269
+ specify { subject.tail.prepend(n.new("_")).next.node.value.should == "z" }
270
+
271
+ # Previous node's `next` is updated
272
+ specify { subject.tail.prepend(n.new("_")).prev.next.node.value.should == "_" }
273
+ specify { subject.tail.prepend(n.new("_")).prev.node.next.value.should == "_" }
274
+ end
275
+
276
+ context "at head.prev" do
277
+ # Positioned at newly inserted node
278
+ specify { subject.prev(true).prepend(n.new("_")).should be_head }
279
+ specify { subject.prev(true).prepend(n.new("_")).node.value.should == "_" }
280
+ specify { subject.prev(true).prepend(n.new("_")).next.node.value.should == "x" }
281
+ specify { subject.prev(true).prepend(n.new("_")).prev(true).position.should == -1 }
282
+ end
283
+
284
+ context "at tail.next" do
285
+ # Positioned at newly inserted node
286
+ specify { subject.tail.next(true).prepend(n.new("_")).should be_tail }
287
+ specify { subject.tail.next(true).prepend(n.new("_")).node.value.should == "_" }
288
+ specify { subject.tail.next(true).prepend(n.new("_")).prev.node.value.should == "z" }
289
+
290
+ # Previous node's `next` is updated
291
+ specify { subject.tail.next(true).prepend(n.new("_")).prev.next.node.value.should == "_" }
292
+ specify { subject.tail.next(true).prepend(n.new("_")).prev.node.next.value.should == "_" }
293
+ end
294
+
295
+ context "prepend(y).prev.prepend(x)" do
296
+ # todo
297
+ # <x> <y> x y z
298
+ # <x> a <y> b c
299
+ # a <x> b <y> c
300
+ # x y z <x> <y>
301
+ end
302
+ end
303
+
304
+ describe "#update(node)" do
305
+ let(:x) { n.build(%w(x y z)) }
306
+ let(:m) { n.build(%w(m n o)) }
307
+ subject { z.build(x) }
308
+
309
+ context "on empty stream" do
310
+ # Positioned at newly updated (inserted) node
311
+ specify { z.build.update(m).position.should == 0 }
312
+ specify { z.build.update(m).node.value.should == "m" }
313
+
314
+ # Doesn't copy m's entire chain
315
+ specify { z.build.update(m).should be_head }
316
+ specify { z.build.update(m).should be_tail }
317
+ end
318
+
319
+ context "at head" do
320
+ # Positioned at newly updated node
321
+ specify { subject.update(m).node.value.should == "m" }
322
+
323
+ # Replaces existing node
324
+ specify { subject.update(m).position.should == subject.position }
325
+
326
+ # Doesn't copy m's entire chain
327
+ specify { subject.update(m).node.next.value.should == "y" }
328
+ specify { subject.update(m).next.node.value.should == "y" }
329
+
330
+ # todo: Explain
331
+ specify { subject.update(m).node.next.should eql(x.next) }
332
+ specify { subject.update(m).next.node.should eql(x.next) }
333
+ end
334
+
335
+ context "at head.next" do
336
+ # Positioned at newly updated node
337
+ specify { subject.next.update(m).node.value.should == "m" }
338
+
339
+ # Replaces existing node
340
+ specify { subject.next.update(m).position.should == subject.next.position }
341
+
342
+ # Doesn't copy m's entire chain
343
+ specify { subject.next.update(m).node.next.value.should == "z" }
344
+ specify { subject.next.update(m).next.node.value.should == "z" }
345
+
346
+ # todo: Explain
347
+ specify { subject.next.update(m).node.next.should eql(x.next.next) }
348
+ specify { subject.next.update(m).next.node.should eql(x.next.next) }
349
+ end
350
+
351
+ context "at tail" do
352
+ # Positioned at newly updated node
353
+ specify { subject.tail.update(m).node.value.should == "m" }
354
+
355
+ # Replaces existing node
356
+ specify { subject.tail.update(m).position.should == subject.tail.position }
357
+
358
+ # Doesn't copy m's entire chain
359
+ specify { subject.tail.update(m).should be_tail }
360
+
361
+ # todo: Explain
362
+ specify { subject.tail.update(m).node.next.should be_nil }
363
+ end
364
+
365
+ context "at head.prev" do
366
+ # Positioned at newly updated node
367
+ specify { z.build.prev(true).update(m).node.value.should == "m" }
368
+
369
+ # Creates a new head
370
+ specify { subject.prev(true).update(m).should be_head }
371
+ specify { subject.prev(true).update(m).position.should == subject.position }
372
+
373
+ # Doesn't copy m's entire chain
374
+ specify { subject.prev(true).update(m).node.next.value.should == "x" }
375
+ specify { subject.prev(true).update(m).next.node.value.should == "x" }
376
+
377
+ # todo: Explain
378
+ specify { subject.prev(true).update(m).node.next.should eql(x) }
379
+ specify { subject.prev(true).update(m).next.node.should eql(x) }
380
+ end
381
+
382
+ context "at tail.next" do
383
+ # Positioned at newly updated (inserted) node
384
+ specify { subject.tail.next(true).update(m).node.value.should == "m" }
385
+
386
+ # Creates a new tail
387
+ specify { subject.tail.next(true).update(m).should be_tail }
388
+ specify { subject.tail.next(true).update(m).position.should == subject.tail.position + 1 }
389
+
390
+ # Doesn't copy m's entire chain
391
+ specify { subject.tail.next(true).update(m).node.next.should be_nil }
392
+ end
393
+ end
394
+
395
+ describe "#update{|node| ... }" do
396
+ let(:x) { n.build(%w(x y z)) }
397
+ let(:m) { n.build(%w(m n o)) }
398
+ subject { z.build(x) }
399
+
400
+ context "on empty stream" do
401
+ # Positioned at newly updated (inserted) node
402
+ specify { z.build.update{|_| m }.position.should == 0 }
403
+ specify { z.build.update{|_| m }.node.value.should == "m" }
404
+
405
+ # Doesn't copy m's entire chain
406
+ specify { z.build.update{|_| m }.should be_head }
407
+ specify { z.build.update{|_| m }.should be_tail }
408
+
409
+ # Yields nil
410
+ specify { z.build.update{|_| _.should be_nil; m }}
411
+ end
412
+
413
+ context "at head" do
414
+ # Positioned at newly updated node
415
+ specify { subject.update{|x| m }.node.value.should == "m" }
416
+
417
+ # Replaces existing node
418
+ specify { subject.update{|x| m }.position.should == subject.position }
419
+
420
+ # Doesn't copy m's entire chain
421
+ specify { subject.update{|x| m }.node.next.value.should == "y" }
422
+ specify { subject.update{|x| m }.next.node.value.should == "y" }
423
+
424
+ # todo: Explain
425
+ specify { subject.update{|x| m }.node.next.should eql(x.next) }
426
+ specify { subject.update{|x| m }.next.node.should eql(x.next) }
427
+
428
+ # Yields current node
429
+ specify { subject.update{|o| o.should eql(x); o }}
430
+ end
431
+
432
+ context "at head.next" do
433
+ # Positioned at newly updated node
434
+ specify { subject.next.update{|y| m }.node.value.should == "m" }
435
+
436
+ # Replaces existing node
437
+ specify { subject.next.update{|y| m }.position.should == subject.next.position }
438
+
439
+ # Doesn't copy m's entire chain
440
+ specify { subject.next.update{|y| m }.node.next.value.should == "z" }
441
+ specify { subject.next.update{|y| m }.next.node.value.should == "z" }
442
+
443
+ # todo: Explain
444
+ specify { subject.next.update{|y| m }.node.next.should eql(x.next.next) }
445
+ specify { subject.next.update{|y| m }.next.node.should eql(x.next.next) }
446
+
447
+ # Yields current node
448
+ specify { subject.next.update{|o| o.should eql(x.next); o }}
449
+ end
450
+
451
+ context "at tail" do
452
+ # Positioned at newly updated node
453
+ specify { subject.tail.update{|z| m }.node.value.should == "m" }
454
+
455
+ # Replaces existing node
456
+ specify { subject.tail.update{|z| m }.position.should == subject.tail.position }
457
+
458
+ # Doesn't copy m's entire chain
459
+ specify { subject.tail.update{|z| m }.should be_tail }
460
+
461
+ # todo: Explain
462
+ specify { subject.tail.update{|z| m }.node.next.should be_nil }
463
+
464
+ # Yields current node
465
+ specify { subject.tail.update{|o| o.should eql(x.next.next); o }}
466
+ end
467
+
468
+ context "at head.prev" do
469
+ # Positioned at newly updated node
470
+ specify { z.build.prev(true).update{|_| m }.node.value.should == "m" }
471
+
472
+ # Creates a new head
473
+ specify { subject.prev(true).update{|_| m }.should be_head }
474
+ specify { subject.prev(true).update{|_| m }.position.should == subject.position }
475
+
476
+ # Doesn't copy m's entire chain
477
+ specify { subject.prev(true).update{|_| m }.node.next.value.should == "x" }
478
+ specify { subject.prev(true).update{|_| m }.next.node.value.should == "x" }
479
+
480
+ # todo: Explain
481
+ specify { subject.prev(true).update{|_| m }.node.next.should eql(x) }
482
+ specify { subject.prev(true).update{|_| m }.next.node.should eql(x) }
483
+
484
+ # Yields nil
485
+ specify { subject.prev(true).update{|o| o.should be_nil; m }}
486
+ end
487
+
488
+ context "at tail.next" do
489
+ # Positioned at newly updated (inserted) node
490
+ specify { subject.tail.next(true).update{|_| m }.node.value.should == "m" }
491
+
492
+ # Creates a new tail
493
+ specify { subject.tail.next(true).update{|_| m }.should be_tail }
494
+ specify { subject.tail.next(true).update{|_| m }.position.should == subject.tail.position + 1 }
495
+
496
+ # Doesn't copy m's entire chain
497
+ specify { subject.tail.next(true).update{|_| m }.node.next.should be_nil }
498
+
499
+ # Yields nil
500
+ specify { subject.tail.next(true).update{|o| o.should be_nil; m }}
501
+ end
502
+ end
503
+
504
+ describe "#replace(node)" do
505
+ let(:x) { n.build(%w(x y z)) }
506
+ subject { z.build(x) }
507
+
508
+ context "on empty stream" do
509
+ # todo
510
+ end
511
+
512
+ context "at head" do
513
+ # todo
514
+ end
515
+
516
+ context "at head.next" do
517
+ # todo
518
+ end
519
+
520
+ context "at tail" do
521
+ # todo
522
+ end
523
+
524
+ context "at head.prev" do
525
+ # todo
526
+ end
527
+
528
+ context "at tail.next" do
529
+ # todo
530
+ end
531
+ end
532
+
533
+ describe "#delete" do
534
+ let(:x) { n.build(%w(x y z)) }
535
+ subject { z.build(x) }
536
+
537
+ context "on empty stream" do
538
+ # todo
539
+ end
540
+
541
+ context "at head" do
542
+ # todo
543
+ end
544
+
545
+ context "at head.next" do
546
+ # todo
547
+ end
548
+
549
+ context "at tail" do
550
+ # todo
551
+ end
552
+
553
+ context "at head.prev" do
554
+ # todo
555
+ end
556
+
557
+ context "at tail.next" do
558
+ # todo
559
+ end
560
+ end
561
+
562
+ describe "#truncate" do
563
+ let(:x) { n.build(%w(x y z)) }
564
+ subject { z.build(x) }
565
+
566
+ context "on empty stream" do
567
+ # todo
568
+ end
569
+
570
+ context "at head" do
571
+ # todo
572
+ end
573
+
574
+ context "at head.next" do
575
+ # todo
576
+ end
577
+
578
+ context "at tail" do
579
+ # todo
580
+ end
581
+
582
+ context "at head.prev" do
583
+ # todo
584
+ end
585
+
586
+ context "at tail.next" do
587
+ # todo
588
+ end
589
+ end
590
+
591
+ end
592
+
593
+ describe Fr::SZipper::Enumerable do
594
+
595
+ describe "#each{|cursor| ... }" do
596
+ end
597
+
598
+ describe "#all?{|node| predicate }" do
599
+ end
600
+
601
+ describe "#any?{|node| predicate }" do
602
+ end
603
+
604
+ describe "#none?{|node| predicate }" do
605
+ end
606
+
607
+ describe "#count{|node| predicate }" do
608
+ end
609
+
610
+ describe "#find{|node| predicate }" do
611
+ end
612
+
613
+ describe "#drop(n)" do
614
+ end
615
+
616
+ describe "#entries" do
617
+ end
618
+
619
+ describe "#map{|node| ... }" do
620
+ end
621
+
622
+ describe "#filter{|node| predicate }" do
623
+ end
624
+
625
+ describe "#reject{|node| predicate }" do
626
+ end
627
+
628
+ describe "#index{|node| predicate }" do
629
+ end
630
+
631
+ describe "#grep(pattern)" do
632
+ end
633
+
634
+ describe "#include?(node)" do
635
+ end
636
+
637
+ describe "#foldl(seed){|seed, cursor| ... }" do
638
+ end
639
+
640
+ describe "#reduce{|acursor, bcursor| ... }" do
641
+ end
642
+
643
+ describe "#max_by" do
644
+ end
645
+
646
+ describe "#min_by" do
647
+ end
648
+
649
+ describe "#minmax_by" do
650
+ end
651
+
652
+ end