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.
- data/README.md +0 -0
- data/Rakefile +81 -0
- data/lib/fr.rb +44 -0
- data/lib/fr/applicative.rb +0 -0
- data/lib/fr/array.rb +168 -0
- data/lib/fr/boolean.rb +15 -0
- data/lib/fr/either.rb +94 -0
- data/lib/fr/errors.rb +5 -0
- data/lib/fr/errors/zipper_error.rb +8 -0
- data/lib/fr/functor.rb +11 -0
- data/lib/fr/functor/array.rb +15 -0
- data/lib/fr/functor/either.rb +17 -0
- data/lib/fr/functor/maybe.rb +17 -0
- data/lib/fr/maybe.rb +76 -0
- data/lib/fr/monad.rb +236 -0
- data/lib/fr/monad/array.rb +21 -0
- data/lib/fr/monad/either.rb +18 -0
- data/lib/fr/monad/maybe.rb +24 -0
- data/lib/fr/monad/random.rb +50 -0
- data/lib/fr/monad/reader.rb +45 -0
- data/lib/fr/monad/state.rb +62 -0
- data/lib/fr/monad/writer.rb +49 -0
- data/lib/fr/monoid.rb +52 -0
- data/lib/fr/monoid/array.rb +13 -0
- data/lib/fr/monoid/boolean.rb +23 -0
- data/lib/fr/monoid/either.rb +13 -0
- data/lib/fr/monoid/maybe.rb +15 -0
- data/lib/fr/monoid/numeric.rb +31 -0
- data/lib/fr/monoid/string.rb +11 -0
- data/lib/fr/numeric.rb +1 -0
- data/lib/fr/object.rb +45 -0
- data/lib/fr/string.rb +1 -0
- data/lib/fr/szipper.rb +36 -0
- data/lib/fr/szipper/abstract_cursor.rb +63 -0
- data/lib/fr/szipper/edited_cursor.rb +62 -0
- data/lib/fr/szipper/enumerable.rb +266 -0
- data/lib/fr/szipper/head_cursor.rb +71 -0
- data/lib/fr/szipper/head_prev_cursor.rb +112 -0
- data/lib/fr/szipper/memoized_cursor.rb +59 -0
- data/lib/fr/szipper/node.rb +67 -0
- data/lib/fr/szipper/tail_next_cursor.rb +68 -0
- data/lib/fr/thunk.rb +73 -0
- data/lib/fr/tzipper.rb +38 -0
- data/lib/fr/tzipper/abstract_cursor.rb +351 -0
- data/lib/fr/tzipper/dangling_cursor.rb +107 -0
- data/lib/fr/tzipper/edited_cursor.rb +161 -0
- data/lib/fr/tzipper/memoized_cursor.rb +129 -0
- data/lib/fr/tzipper/node.rb +57 -0
- data/lib/fr/tzipper/path.rb +125 -0
- data/lib/fr/tzipper/root_cursor.rb +124 -0
- data/lib/fr/unfold.rb +93 -0
- data/spec/examples/array.example +0 -0
- data/spec/examples/monads/array.example +0 -0
- data/spec/examples/monads/maybe.example +0 -0
- data/spec/examples/monads/random.example +0 -0
- data/spec/examples/monads/state.example +0 -0
- data/spec/examples/szipper.example +652 -0
- data/spec/examples/thunk.example +0 -0
- data/spec/examples/tzipper.example +0 -0
- data/spec/examples/unfold.example +90 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/szipper/model.rb +225 -0
- data/spec/support/szipper/nodel.rb +144 -0
- 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
|