hamster 0.1.13 → 0.1.14

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 (50) hide show
  1. data/History.rdoc +14 -0
  2. data/README.rdoc +2 -2
  3. data/lib/hamster/hash.rb +10 -9
  4. data/lib/hamster/list.rb +96 -64
  5. data/lib/hamster/set.rb +12 -5
  6. data/lib/hamster/version.rb +1 -1
  7. data/spec/hamster/hash/all_spec.rb +1 -1
  8. data/spec/hamster/hash/eql_spec.rb +42 -13
  9. data/spec/hamster/hash/filter_spec.rb +1 -1
  10. data/spec/hamster/hash/reduce_spec.rb +1 -1
  11. data/spec/hamster/hash/reject_spec.rb +31 -27
  12. data/spec/hamster/list/all_spec.rb +73 -0
  13. data/spec/hamster/list/any_spec.rb +77 -0
  14. data/spec/hamster/list/cadr_spec.rb +4 -2
  15. data/spec/hamster/list/cons_spec.rb +6 -4
  16. data/spec/hamster/list/copying_spec.rb +4 -2
  17. data/spec/hamster/list/drop_spec.rb +16 -2
  18. data/spec/hamster/list/drop_while_spec.rb +17 -3
  19. data/spec/hamster/list/each_spec.rb +18 -4
  20. data/spec/hamster/list/empty_spec.rb +4 -2
  21. data/spec/hamster/list/eql_spec.rb +41 -7
  22. data/spec/hamster/list/filter_spec.rb +20 -6
  23. data/spec/hamster/list/find_spec.rb +63 -0
  24. data/spec/hamster/list/head_spec.rb +4 -2
  25. data/spec/hamster/list/include_spec.rb +16 -2
  26. data/spec/hamster/list/inspect_spec.rb +16 -2
  27. data/spec/hamster/list/map_spec.rb +18 -4
  28. data/spec/hamster/list/none_spec.rb +73 -0
  29. data/spec/hamster/list/reduce_spec.rb +66 -12
  30. data/spec/hamster/list/reject_spec.rb +39 -21
  31. data/spec/hamster/list/size_spec.rb +16 -2
  32. data/spec/hamster/list/tail_spec.rb +4 -2
  33. data/spec/hamster/list/take_spec.rb +16 -2
  34. data/spec/hamster/list/take_while_spec.rb +18 -4
  35. data/spec/hamster/list/to_a_spec.rb +16 -2
  36. data/spec/hamster/list/to_ary_spec.rb +42 -0
  37. data/spec/hamster/set/eql_spec.rb +35 -5
  38. data/spec/hamster/set/filter_spec.rb +1 -1
  39. data/spec/hamster/set/reduce_spec.rb +1 -1
  40. data/spec/hamster/set/reject_spec.rb +31 -27
  41. data/spec/hamster/set/to_a_spec.rb +29 -0
  42. data/spec/hamster/stack/copying_spec.rb +4 -2
  43. data/spec/hamster/stack/empty_spec.rb +4 -2
  44. data/spec/hamster/stack/eql_spec.rb +27 -7
  45. data/spec/hamster/stack/inspect_spec.rb +4 -2
  46. data/spec/hamster/stack/pop_spec.rb +12 -8
  47. data/spec/hamster/stack/push_spec.rb +6 -4
  48. data/spec/hamster/stack/size_spec.rb +4 -2
  49. data/spec/hamster/stack/top_spec.rb +6 -4
  50. metadata +8 -2
@@ -1,3 +1,17 @@
1
+ === 0.1.14 / 2009-12-14
2
+
3
+ * List#reduce supports optional initial value.
4
+
5
+ * Implemented List#to_ary for implicit conversion to arrays and call parameters.
6
+
7
+ * Implemented Set#to_a.
8
+
9
+ * Alias #filter as #find_all.
10
+
11
+ * Alias #reject as #delete_if.
12
+
13
+ * Alias #reduce as #fold.
14
+
1
15
  === 0.1.13 / 2009-12-10
2
16
 
3
17
  * Stack now fully functional
@@ -120,7 +120,7 @@ Lists are, where possible, lazy. That is, they try to defer processing items unt
120
120
  true
121
121
  end
122
122
 
123
- The following code will only call <tt>#prime?</tt> as many times as necessary to generate the first 3 prime numbers between 10000 and 1000000:
123
+ The following code will only call <tt>prime?</tt> as many times as necessary to generate the first 3 prime numbers between 10000 and 1000000:
124
124
 
125
125
  Hamster.interval(10000, 1000000).filter { |i| prime?(i) }.take(3) # => 0.0009s
126
126
 
@@ -142,6 +142,6 @@ Besides <tt>Hamster.list</tt> there is <tt>Hamster.interval(from, to)</tt> and <
142
142
 
143
143
  Hamster started out as a spike to prove a point and has since morphed into something I actually use. My primary concern has been to round out the functionality with good test coverage and clean, readable code.
144
144
 
145
- Performance is pretty good--especially with lazy lists--but there are some things which need to be converted from recursive to iterative due to a lack of Tail-Call-Optimisation in Ruby. It's on the list ;-)
145
+ Performance is pretty good--especially with lazy lists--but there are some things which unfortunately had to be converted from recursive to iterative due to a lack of Tail-Call-Optimisation in Ruby, making them a little less readable than I would otherwise have preferred.
146
146
 
147
147
  Documentation is sparse but I've tried as best I can to write specs that reads as documentation. I've also tried to alias methods as their <tt>Enumerable</tt> equivalents where possible to make it easier for people to migrate code.
@@ -51,13 +51,13 @@ module Hamster
51
51
  end
52
52
 
53
53
  def each
54
- block_given? or return self
54
+ return self unless block_given?
55
55
  @trie.each { |entry| yield(entry.key, entry.value) }
56
56
  self
57
57
  end
58
58
 
59
59
  def map
60
- block_given? or return self
60
+ return self unless block_given?
61
61
  if empty?
62
62
  self
63
63
  else
@@ -67,13 +67,14 @@ module Hamster
67
67
  alias_method :collect, :map
68
68
 
69
69
  def reduce(memo)
70
- block_given? or return memo
70
+ return memo unless block_given?
71
71
  @trie.reduce(memo) { |memo, entry| yield(memo, entry.key, entry.value) }
72
72
  end
73
73
  alias_method :inject, :reduce
74
+ alias_method :fold, :reduce
74
75
 
75
76
  def filter
76
- block_given? or return self
77
+ return self unless block_given?
77
78
  trie = @trie.filter { |entry| yield(entry.key, entry.value) }
78
79
  if !trie.equal?(@trie)
79
80
  self.class.new(trie)
@@ -82,17 +83,19 @@ module Hamster
82
83
  end
83
84
  end
84
85
  alias_method :select, :filter
86
+ alias_method :find_all, :filter
85
87
 
86
88
  def reject
87
- block_given? or return self
89
+ return self unless block_given?
88
90
  select { |key, value| !yield(key, value) }
89
91
  end
92
+ alias_method :delete_if, :reject
90
93
 
91
94
  def any?
92
95
  if block_given?
93
96
  each { |key, value| return true if yield(key, value) }
94
97
  else
95
- each { |pair| return true if pair }
98
+ return !empty?
96
99
  end
97
100
  false
98
101
  end
@@ -102,8 +105,6 @@ module Hamster
102
105
  def all?
103
106
  if block_given?
104
107
  each { |key, value| return false unless yield(key, value) }
105
- else
106
- each { |pair| return false unless pair }
107
108
  end
108
109
  true
109
110
  end
@@ -112,7 +113,7 @@ module Hamster
112
113
  if block_given?
113
114
  each { |key, value| return false if yield(key, value) }
114
115
  else
115
- each { |pair| return false if pair }
116
+ return empty?
116
117
  end
117
118
  true
118
119
  end
@@ -1,5 +1,7 @@
1
1
  module Hamster
2
2
 
3
+ Undefined = Object.new
4
+
3
5
  class << self
4
6
 
5
7
  def list(*items)
@@ -7,7 +9,7 @@ module Hamster
7
9
  end
8
10
 
9
11
  def stream(&block)
10
- block_given? or return EmptyList
12
+ return EmptyList unless block_given?
11
13
  Stream.new(yield) { stream(&block) }
12
14
  end
13
15
 
@@ -29,7 +31,7 @@ module Hamster
29
31
  end
30
32
 
31
33
  def size
32
- tail.size + 1
34
+ reduce(0) { |memo, item| memo + 1 }
33
35
  end
34
36
  alias_method :length, :size
35
37
 
@@ -38,46 +40,56 @@ module Hamster
38
40
  end
39
41
  alias_method :>>, :cons
40
42
 
41
- def each(&block)
42
- block_given? or return self
43
- yield(head)
44
- tail.each(&block)
43
+ def each
44
+ return self unless block_given?
45
+ list = self
46
+ while !list.empty?
47
+ yield(list.head)
48
+ list = list.tail
49
+ end
45
50
  nil
46
51
  end
47
52
 
48
53
  def map(&block)
49
- block_given? or return self
54
+ return self unless block_given?
50
55
  Stream.new(yield(head)) { tail.map(&block) }
51
56
  end
52
57
  alias_method :collect, :map
53
58
 
54
- def reduce(memo, &block)
55
- block_given? or return memo
56
- tail.reduce(yield(memo, head), &block)
59
+ def reduce(memo = Undefined, &block)
60
+ return tail.reduce(head, &block) if memo.equal?(Undefined)
61
+ return memo unless block_given?
62
+ each { |item| memo = yield(memo, item) }
63
+ memo
57
64
  end
58
65
  alias_method :inject, :reduce
66
+ alias_method :fold, :reduce
59
67
 
60
68
  def filter(&block)
61
- block_given? or return self
62
- if yield(head)
63
- Stream.new(head) { tail.filter(&block) }
64
- else
65
- tail.filter(&block)
69
+ return self unless block_given?
70
+ list = self
71
+ while !yield(list.head)
72
+ list = list.tail
73
+ return list if list.empty?
66
74
  end
75
+ Stream.new(list.head) { list.tail.filter(&block) }
67
76
  end
68
77
  alias_method :select, :filter
78
+ alias_method :find_all, :filter
69
79
 
70
80
  def reject(&block)
71
- block_given? or return self
72
- if yield(head)
73
- tail.reject(&block)
74
- else
75
- Stream.new(head) { tail.reject(&block) }
81
+ return self unless block_given?
82
+ list = self
83
+ while yield(list.head)
84
+ list = list.tail
85
+ return list if list.empty?
76
86
  end
87
+ Stream.new(list.head) { list.tail.reject(&block) }
77
88
  end
89
+ alias_method :delete_if, :reject
78
90
 
79
91
  def take_while(&block)
80
- block_given? or return self
92
+ return self unless block_given?
81
93
  if yield(head)
82
94
  Stream.new(head) { tail.take_while(&block) }
83
95
  else
@@ -85,13 +97,13 @@ module Hamster
85
97
  end
86
98
  end
87
99
 
88
- def drop_while(&block)
89
- block_given? or return self
90
- if yield(head)
91
- tail.drop_while(&block)
92
- else
93
- self
100
+ def drop_while
101
+ return self unless block_given?
102
+ list = self
103
+ while !list.empty? && yield(list.head)
104
+ list = list.tail
94
105
  end
106
+ list
95
107
  end
96
108
 
97
109
  def take(number)
@@ -103,22 +115,67 @@ module Hamster
103
115
  end
104
116
 
105
117
  def drop(number)
106
- if number > 0
107
- tail.drop(number - 1)
108
- else
109
- self
118
+ list = self
119
+ while !list.empty? && number > 0
120
+ number -= 1
121
+ list = list.tail
110
122
  end
123
+ list
111
124
  end
112
125
 
113
- def include?(item)
114
- item == head || tail.include?(item)
126
+ def include?(object)
127
+ each { |item| return true if object == item }
128
+ false
115
129
  end
116
130
  alias_method :member?, :include?
117
131
 
132
+ def any?
133
+ if block_given?
134
+ each { |item| return true if yield(item) }
135
+ else
136
+ each { |item| return true if item }
137
+ end
138
+ false
139
+ end
140
+ alias_method :exist?, :any?
141
+ alias_method :exists?, :any?
142
+
143
+ def all?
144
+ if block_given?
145
+ each { |item| return false unless yield(item) }
146
+ else
147
+ each { |item| return false unless item }
148
+ end
149
+ true
150
+ end
151
+
152
+ def none?
153
+ if block_given?
154
+ each { |item| return false if yield(item) }
155
+ else
156
+ each { |item| return false if item }
157
+ end
158
+ true
159
+ end
160
+
161
+ def find
162
+ return nil unless block_given?
163
+ each { |item| return item if yield(item) }
164
+ end
165
+ alias_method :detect, :find
166
+
118
167
  def eql?(other)
119
- return true if other.equal?(self)
120
168
  return false unless other.is_a?(List)
121
- other.head == head && other.tail.eql?(tail)
169
+
170
+ list = self
171
+ while !list.empty? && !other.empty?
172
+ return true if other.equal?(list)
173
+ return false unless other.is_a?(List)
174
+ return false unless other.head.eql?(list.head)
175
+ list = list.tail
176
+ other = other.tail
177
+ end
178
+ other.equal?(list)
122
179
  end
123
180
  alias_method :==, :eql?
124
181
 
@@ -128,8 +185,9 @@ module Hamster
128
185
  alias_method :clone, :dup
129
186
 
130
187
  def to_a
131
- reduce([]) { |ary, item| ary << item }
188
+ reduce([]) { |a, item| a << item }
132
189
  end
190
+ alias_method :to_ary, :to_a
133
191
 
134
192
  def inspect
135
193
  to_a.inspect
@@ -208,56 +266,30 @@ module Hamster
208
266
  true
209
267
  end
210
268
 
211
- def size
212
- 0
213
- end
214
- alias_method :length, :size
215
-
216
- def each
217
- block_given? or return self
218
- nil
219
- end
220
-
221
269
  def map
222
270
  self
223
271
  end
224
272
  alias_method :collect, :map
225
273
 
226
- def reduce(memo)
227
- memo
228
- end
229
- alias_method :inject, :reduce
230
-
231
274
  def filter
232
275
  self
233
276
  end
234
277
  alias_method :select, :filter
278
+ alias_method :find_all, :filter
235
279
 
236
280
  def reject
237
281
  self
238
282
  end
283
+ alias_method :delete_if, :reject
239
284
 
240
285
  def take_while
241
286
  self
242
287
  end
243
288
 
244
- def drop_while
245
- self
246
- end
247
-
248
289
  def take(number)
249
290
  self
250
291
  end
251
292
 
252
- def drop(number)
253
- self
254
- end
255
-
256
- def include?(item)
257
- false
258
- end
259
- alias_method :member?, :include?
260
-
261
293
  end
262
294
 
263
295
  end
@@ -45,13 +45,13 @@ module Hamster
45
45
  end
46
46
 
47
47
  def each
48
- block_given? or return self
48
+ return self unless block_given?
49
49
  @trie.each { |entry| yield(entry.key) }
50
50
  self
51
51
  end
52
52
 
53
53
  def map
54
- block_given? or return self
54
+ return self unless block_given?
55
55
  if empty?
56
56
  self
57
57
  else
@@ -61,13 +61,14 @@ module Hamster
61
61
  alias_method :collect, :map
62
62
 
63
63
  def reduce(memo)
64
- block_given? or return memo
64
+ return memo unless block_given?
65
65
  @trie.reduce(memo) { |memo, entry| yield(memo, entry.key) }
66
66
  end
67
67
  alias_method :inject, :reduce
68
+ alias_method :fold, :reduce
68
69
 
69
70
  def filter
70
- block_given? or return self
71
+ return self unless block_given?
71
72
  trie = @trie.filter { |entry| yield(entry.key) }
72
73
  if !trie.equal?(@trie)
73
74
  self.class.new(trie)
@@ -76,11 +77,13 @@ module Hamster
76
77
  end
77
78
  end
78
79
  alias_method :select, :filter
80
+ alias_method :find_all, :filter
79
81
 
80
82
  def reject
81
- block_given? or return self
83
+ return self unless block_given?
82
84
  select { |item| !yield(item) }
83
85
  end
86
+ alias_method :delete_if, :reject
84
87
 
85
88
  def any?
86
89
  if block_given?
@@ -121,6 +124,10 @@ module Hamster
121
124
  end
122
125
  alias_method :clone, :dup
123
126
 
127
+ def to_a
128
+ reduce([]) { |a, item| a << item }
129
+ end
130
+
124
131
  end
125
132
 
126
133
  end
@@ -1,5 +1,5 @@
1
1
  module Hamster
2
2
 
3
- VERSION = "0.1.13".freeze
3
+ VERSION = "0.1.14".freeze
4
4
 
5
5
  end
@@ -29,7 +29,7 @@ describe Hamster::Hash do
29
29
  describe "with a block" do
30
30
 
31
31
  it "returns true if the block always returns true" do
32
- @hash.all? { |item| true }.should be_true
32
+ @hash.all? { |key, value| true }.should be_true
33
33
  end
34
34
 
35
35
  it "returns false if the block ever returns false" do
@@ -6,20 +6,49 @@ describe Hamster::Hash do
6
6
 
7
7
  describe "##{method}" do
8
8
 
9
+ describe "returns false when comparing with" do
10
+
11
+ before do
12
+ @hash = Hamster.hash("A" => "aye", "B" => "bee", "C" => "see")
13
+ end
14
+
15
+ it "a standard hash" do
16
+ @hash.send(method, "A" => "aye", "B" => "bee", "C" => "see").should be_false
17
+ end
18
+
19
+ it "an aribtrary object" do
20
+ @hash.send(method, Object.new).should be_false
21
+ end
22
+
23
+ end
24
+
9
25
  [
10
- [[], [], true],
11
- [["A" => "aye"], [], false],
12
- [[], ["A" => "aye"], false],
13
- [["A" => "aye"], ["A" => "aye"], true],
14
- [["A" => "aye"], ["B" => "bee"], false],
15
- [["A" => "aye", "B" => "bee"], ["A" => "aye"], false],
16
- [["A" => "aye"], ["A" => "aye", "B" => "bee"], false],
17
- [["A" => "aye", "B" => "bee", "C" => "see"], ["A" => "aye", "B" => "bee", "C" => "see"], true],
18
- [["C" => "see", "A" => "aye", "B" => "bee"], ["A" => "aye", "B" => "bee", "C" => "see"], true],
19
- ].each do |a, b, result|
20
-
21
- it "returns #{result} for #{a.inspect} and #{b.inspect}" do
22
- Hamster.hash(*a).send(method, Hamster.hash(*b)).should == result
26
+ [{}, {}, true],
27
+ [{"A" => "aye"}, {}, false],
28
+ [{}, {"A" => "aye"}, false],
29
+ [{"A" => "aye"}, {"A" => "aye"}, true],
30
+ [{"A" => "aye"}, {"B" => "bee"}, false],
31
+ [{"A" => "aye", "B" => "bee"}, {"A" => "aye"}, false],
32
+ [{"A" => "aye"}, {"A" => "aye", "B" => "bee"}, false],
33
+ [{"A" => "aye", "B" => "bee", "C" => "see"}, {"A" => "aye", "B" => "bee", "C" => "see"}, true],
34
+ [{"C" => "see", "A" => "aye", "B" => "bee"}, {"A" => "aye", "B" => "bee", "C" => "see"}, true],
35
+ ].each do |a, b, expected|
36
+
37
+ describe "returns #{expected}" do
38
+
39
+ before do
40
+ @a = Hamster.hash(a)
41
+ @b = Hamster.hash(b)
42
+ end
43
+
44
+ it "for #{a.inspect} and #{b.inspect}" do
45
+ @a.send(method, @b).should == expected
46
+ end
47
+
48
+ it "for #{b.inspect} and #{a.inspect}" do
49
+ @b.send(method, @a).should == expected
50
+ end
51
+
23
52
  end
24
53
 
25
54
  end