fzip 0.1.0 → 0.2.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,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- OTg0MWE1NTE4M2IyYmVjN2UxZjQ1NGM4YTdjNTQwZTAxNzkzOWQ5NQ==
4
+ ZDhiOWFhY2M0MTBiNGZiZjRjZThiYmE4OTg4ZTc3OGZlODkzZjA0ZA==
5
5
  data.tar.gz: !binary |-
6
- ZTg4YWE1NWIxMDViZDA3YThiZDliYTI3MjY0MTQ2YTU5Nzc2MmI0YQ==
6
+ MzE5MWUwYjVjNTU0YmFjZmE1MjNkZjdiMDBiZWI2MDc3OWE2MWVjZQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MmRiYzljMTVhNzhmNDk0MDdlZTZmOTViNTIyZmZjM2FhZTBkZjZmYmUzNTlm
10
- ZTA0NWU5ZDYyNDhjNmY0YWNhYjA5MzBhNjc1NzJhZTA0MzgxZTc0NzVhMzRm
11
- MDE0NjcyYjI0ZDAyZjVkMWJiZmU1ZThjNWZiNjFmZDU5NjViNDg=
9
+ ODBiYmZjNTE2NzAzNmQwY2JkMGU0Njk3MTAyYTMxOTY0ZTJkYzQ1MWE2YWEx
10
+ NjQzZjU1ZTE0Mjg2ZjE2MGY5Yjc0Yjk3YzhjNjEyNWQ2MmRhZGI3NmMxYWU5
11
+ NDI2ODBmZjUzYjI3NDczZjczYzg1NmQxM2U4OTkzZjJmZjNhMDY=
12
12
  data.tar.gz: !binary |-
13
- NTQwZDkzMzVmYWYzMzRjYWI1OGIwMzUzODEyN2U4MzFlZmJhNGJiODBmMDg2
14
- YmNlNWIyYzM4ZjA5M2VkMzhjODA5MzYxZmM1MWMxZDAwNTZkNjg3ODRkM2Iw
15
- MzAyYmVlYjhiNWY3ZTMzYmVjMjI2YjAyMmNhNDQ0ZmM2OWY1ZDg=
13
+ YjM4NzdhNjFkNThkYWUwNTMxMjVlMDBhMmM3N2NkMTRhN2YxYjM3M2MyMDAy
14
+ N2M0NTVhMDdkZDdlNjNkZDEwMGJkOTY2MTZhMWJhYzkyYmVlMGRlNjg0OTE3
15
+ ZTY5YzQxZmQwYWYyZGM5YTc4ZDM2ZjgyZjg3YjM2YzA4MmU1NWY=
data/.gitignore CHANGED
@@ -1 +1,2 @@
1
1
  coverage
2
+ pkg
@@ -2,7 +2,7 @@ require File.expand_path('../lib/fzip/version', __FILE__)
2
2
 
3
3
  Gem::Specification.new do |gem|
4
4
  gem.name = 'fzip'
5
- gem.version = '0.1.0'
5
+ gem.version = Fzip::VERSION
6
6
  gem.authors = [ 'Arne Brasseur' ]
7
7
  gem.email = [ 'arne@arnebrasseur.net' ]
8
8
  gem.description = 'Functional zipper class'
@@ -1,11 +1,19 @@
1
1
  module FZip
2
2
  class Adapter
3
+ # Can the node have children
4
+ #
5
+ # Returns true if the node can have even if it currently doesn't.
3
6
  def branch?(node)
4
7
  end
5
8
 
9
+ # The children of a node
10
+ #
11
+ # Return an array, or an object that responds to first, drop and +
6
12
  def children(node)
7
13
  end
8
14
 
15
+ # Given a node and an array of children, returns a new branch node of the
16
+ # same type with the supplied children.
9
17
  def make_node(node, children)
10
18
  end
11
19
  end
@@ -1,3 +1,3 @@
1
1
  module Fzip
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -1,14 +1,22 @@
1
1
  module Fzip
2
2
  class Zipper
3
- attr_reader :adapter, :parent, :path, :node, :lefts, :rights, :at_end
3
+ attr_reader :adapter
4
+ attr_reader :parent
5
+ attr_reader :path
6
+ attr_reader :node
7
+ attr_reader :lefts
8
+ attr_reader :rights
9
+ attr_reader :at_end
4
10
 
5
11
  def initialize(adapter, node, lefts = nil, path = nil, parent = nil, rights = nil, changed = false, at_end = false)
6
12
  @adapter = adapter
13
+
7
14
  @node = node
8
15
  @lefts = lefts
9
- @path = path
10
- @parent = parent
11
16
  @rights = rights
17
+
18
+ @path = path # Array[Node]
19
+ @parent = parent # Zipper
12
20
  @changed = changed
13
21
  @at_end = at_end
14
22
  end
@@ -48,7 +56,7 @@ module Fzip
48
56
  end
49
57
 
50
58
  def down
51
- if branch? && children
59
+ if branch? && children.any?
52
60
  new(
53
61
  node: children.first,
54
62
  lefts: [],
@@ -64,7 +72,7 @@ module Fzip
64
72
  return parent unless changed?
65
73
  parent_path = path.drop(1)
66
74
  new(
67
- node: make_node(node, lefts + [node] + rights),
75
+ node: make_node(parent.node, lefts + [node] + rights),
68
76
  lefts: parent.lefts,
69
77
  path: parent_path.empty? ? nil : parent_path,
70
78
  parent: parent.parent,
@@ -88,7 +96,14 @@ module Fzip
88
96
  end
89
97
  end
90
98
 
91
- #def rightmost
99
+ def rightmost
100
+ return self unless path && rights && !rights.empty?
101
+ new(
102
+ node: rights.last,
103
+ lefts: (lefts + [node] + rights)[0..-2],
104
+ rights: []
105
+ )
106
+ end
92
107
 
93
108
  def left
94
109
  if path && lefts && !lefts.empty?
@@ -100,7 +115,14 @@ module Fzip
100
115
  end
101
116
  end
102
117
 
103
- # def leftmost
118
+ def leftmost
119
+ return self unless path && lefts && !lefts.empty?
120
+ new(
121
+ node: lefts.first,
122
+ lefts: [],
123
+ rights: (lefts + [node] + rights).drop(1)
124
+ )
125
+ end
104
126
 
105
127
  def insert_left(item)
106
128
  raise "insert at top" unless path
@@ -152,9 +174,38 @@ module Fzip
152
174
  backtrack.(self)
153
175
  end
154
176
 
155
- # def prev
177
+ def prev
178
+ return up unless loc = left
179
+ loop do
180
+ if child = loc.branch? && loc.down
181
+ loc = child.rightmost
182
+ else
183
+ return loc
184
+ end
185
+ end
186
+ end
156
187
 
157
- # def remove
188
+ # Removes the node at loc, returning the loc that would have preceded
189
+ # it in a depth-first walk.
190
+ def remove
191
+ raise "Remove at top" unless path
192
+ if lefts.empty?
193
+ parent.new(
194
+ node: make_node(parent.node, rights),
195
+ changed: true
196
+ )
197
+ else
198
+ loc = new(
199
+ node: lefts.first,
200
+ lefts: lefts.drop(1),
201
+ changed: true
202
+ )
203
+ loop do
204
+ return loc unless child = loc.branch? && loc.down
205
+ loc = child.rightmost
206
+ end
207
+ end
208
+ end
158
209
 
159
210
  def each
160
211
  return to_enum unless block_given?
@@ -165,3 +216,8 @@ module Fzip
165
216
  end
166
217
  end
167
218
  end
219
+
220
+ # Notes on the port from zip.clj :
221
+ # Gave these variables a different name
222
+ # pnodes => path
223
+ # ppath => parent
@@ -1,6 +1,4 @@
1
- $LOAD_PATH.unshift(Pathname(__FILE__).dirname.parent.join('lib'))
2
-
3
- require 'fzip'
1
+ require 'spec_helper'
4
2
 
5
3
  describe Fzip, 'array' do
6
4
  let(:tree) {
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fzip::Zipper, 'next' do
4
+ let(:tree) {
5
+ [
6
+ ['x', '+', 'y'],
7
+ ['a', '*', 'b']
8
+ ]
9
+ }
10
+ let(:zipper) { Fzip.array(tree) }
11
+
12
+ let(:sequence) {
13
+ [
14
+ tree,
15
+ ['x', '+', 'y'],
16
+ 'x',
17
+ '+',
18
+ 'y',
19
+ ['a', '*', 'b'],
20
+ 'a',
21
+ '*',
22
+ 'b'
23
+ ]
24
+ }
25
+
26
+ it 'should go depth first' do
27
+ sequence.each.with_index do |n, idx|
28
+ nth = (0...idx).inject(zipper) {|z| z.next}
29
+ expect(nth.node).to eq n
30
+
31
+ p nth.node
32
+ sequence.first(idx+1).reverse.each.with_index do |p, pidx|
33
+ puts " => #{p.inspect}"
34
+ pth = (0...pidx).inject(nth) {|n| n.prev}
35
+ expect(pth.node).to eq p
36
+ end
37
+ end
38
+ end
39
+
40
+ it 'should go to the end' do
41
+ z = zipper
42
+ 9.times do
43
+ expect(z.end?).to be_false
44
+ z = z.next
45
+ end
46
+ expect(z.end?).to be_true
47
+ end
48
+
49
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fzip::Zipper, 'remove' do
4
+ let(:tree) {
5
+ [
6
+ ['x', '+', 'y'],
7
+ ['a', '*', 'b']
8
+ ]
9
+ }
10
+
11
+ let(:zipper) { Fzip.array(tree) }
12
+
13
+ context 'when removing the top node' do
14
+ it 'should raise an exception' do
15
+ expect { zipper.remove }.to raise_error
16
+ end
17
+ end
18
+
19
+ context 'when removing a node that has a left sibling' do
20
+ let(:zipper) { super().down.down.right }
21
+
22
+ it 'should remove the node' do
23
+ expect(zipper.remove.root).to eq [
24
+ ['x', 'y'],
25
+ ['a', '*', 'b']
26
+ ]
27
+ end
28
+
29
+ it 'should return the zipper pointing to the left sibling' do
30
+ expect(zipper.remove.node).to eq 'x'
31
+ end
32
+ end
33
+
34
+ context 'without left siblings' do
35
+ let(:zipper) { super().down.down }
36
+
37
+ it 'should remove the node' do
38
+ expect(zipper.remove.root).to eq [
39
+ [ '+', 'y'],
40
+ ['a', '*', 'b']
41
+ ]
42
+ end
43
+
44
+ it 'should point to the parent' do
45
+ expect(zipper.remove.node).to eq [ '+', 'y' ]
46
+ end
47
+
48
+ context 'without right siblings' do
49
+ let(:tree) {
50
+ [
51
+ [ 'z' ]
52
+ ]
53
+ }
54
+
55
+ it 'should remove the node' do
56
+ expect(zipper.remove.root).to eq [ [] ]
57
+ end
58
+
59
+ it 'should point to the parent node' do
60
+ expect(zipper.remove.node).to eq []
61
+ end
62
+ end
63
+ end
64
+
65
+ context 'with left cousins' do
66
+ let(:zipper) { super().down.right }
67
+
68
+ it 'should remove the node' do
69
+ expect(zipper.remove.root).to eq [ ['x', '+', 'y'] ]
70
+ end
71
+
72
+ it 'should point to the rightmost left cousin' do
73
+ expect(zipper.remove.root).to eq [ ['x', '+', 'y'] ]
74
+ end
75
+ end
76
+
77
+ end
@@ -0,0 +1,3 @@
1
+ $LOAD_PATH.unshift(Pathname(__FILE__).dirname.parent.join('lib'))
2
+
3
+ require 'fzip'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fzip
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arne Brasseur
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-31 00:00:00.000000000 Z
11
+ date: 2014-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  prerelease: false
@@ -57,6 +57,9 @@ files:
57
57
  - lib/fzip/version.rb
58
58
  - lib/fzip/zipper.rb
59
59
  - spec/fzip_spec.rb
60
+ - spec/next_spec.rb
61
+ - spec/remove_spec.rb
62
+ - spec/spec_helper.rb
60
63
  homepage: https://github.com/plexus/fzip
61
64
  licenses:
62
65
  - MIT
@@ -83,3 +86,6 @@ specification_version: 4
83
86
  summary: Functional zipper class
84
87
  test_files:
85
88
  - spec/fzip_spec.rb
89
+ - spec/next_spec.rb
90
+ - spec/remove_spec.rb
91
+ - spec/spec_helper.rb