hamster 0.1.13 → 0.1.14

Sign up to get free protection for your applications and to get access to all the features.
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