hamster 0.1.23 → 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.
Files changed (65) hide show
  1. data/History.rdoc +26 -0
  2. data/README.rdoc +1 -1
  3. data/lib/hamster.rb +1 -1
  4. data/lib/hamster/core_ext/io.rb +7 -5
  5. data/lib/hamster/hash.rb +31 -29
  6. data/lib/hamster/list.rb +203 -128
  7. data/lib/hamster/set.rb +39 -25
  8. data/lib/hamster/stack.rb +9 -5
  9. data/lib/hamster/trie.rb +5 -1
  10. data/lib/hamster/version.rb +1 -1
  11. data/spec/hamster/hash/all_spec.rb +29 -25
  12. data/spec/hamster/hash/reduce_spec.rb +1 -1
  13. data/spec/hamster/hash/uniq_spec.rb +23 -0
  14. data/spec/hamster/list/all_spec.rb +55 -55
  15. data/spec/hamster/list/any_spec.rb +4 -8
  16. data/spec/hamster/list/append_spec.rb +2 -20
  17. data/spec/hamster/list/break_spec.rb +22 -121
  18. data/spec/hamster/list/cadr_spec.rb +4 -0
  19. data/spec/hamster/list/chunk_spec.rb +6 -0
  20. data/spec/hamster/list/combinations_spec.rb +51 -0
  21. data/spec/hamster/list/count_spec.rb +4 -8
  22. data/spec/hamster/list/cycle_spec.rb +5 -24
  23. data/spec/hamster/list/drop_spec.rb +2 -14
  24. data/spec/hamster/list/drop_while_spec.rb +2 -14
  25. data/spec/hamster/list/each_spec.rb +4 -8
  26. data/spec/hamster/list/eql_spec.rb +9 -10
  27. data/spec/hamster/list/filter_spec.rb +2 -20
  28. data/spec/hamster/list/find_spec.rb +4 -8
  29. data/spec/hamster/list/grep_spec.rb +2 -20
  30. data/spec/hamster/list/include_spec.rb +4 -8
  31. data/spec/hamster/list/init_spec.rb +4 -0
  32. data/spec/hamster/list/inits_spec.rb +42 -0
  33. data/spec/hamster/list/inspect_spec.rb +4 -8
  34. data/spec/hamster/list/intersperse_spec.rb +2 -14
  35. data/spec/hamster/list/join_spec.rb +4 -8
  36. data/spec/hamster/list/last_spec.rb +4 -8
  37. data/spec/hamster/list/map_spec.rb +2 -14
  38. data/spec/hamster/list/maximum_spec.rb +4 -8
  39. data/spec/hamster/list/minimum_spec.rb +4 -8
  40. data/spec/hamster/list/none_spec.rb +4 -8
  41. data/spec/hamster/list/one_spec.rb +4 -8
  42. data/spec/hamster/list/partition_spec.rb +12 -111
  43. data/spec/hamster/list/product_spec.rb +4 -8
  44. data/spec/hamster/list/reduce_spec.rb +4 -8
  45. data/spec/hamster/list/remove_spec.rb +2 -14
  46. data/spec/hamster/list/reverse_spec.rb +4 -8
  47. data/spec/hamster/list/size_spec.rb +4 -8
  48. data/spec/hamster/list/sorting_spec.rb +2 -14
  49. data/spec/hamster/list/span_spec.rb +22 -121
  50. data/spec/hamster/list/split_at_spec.rb +2 -65
  51. data/spec/hamster/list/sum_spec.rb +4 -8
  52. data/spec/hamster/list/tails_spec.rb +42 -0
  53. data/spec/hamster/list/take_spec.rb +2 -14
  54. data/spec/hamster/list/take_while_spec.rb +2 -14
  55. data/spec/hamster/list/to_a_spec.rb +4 -8
  56. data/spec/hamster/list/to_ary_spec.rb +4 -8
  57. data/spec/hamster/list/union_spec.rb +1 -21
  58. data/spec/hamster/list/uniq_spec.rb +3 -15
  59. data/spec/hamster/list/zip_spec.rb +1 -21
  60. data/spec/hamster/set/all_spec.rb +33 -29
  61. data/spec/hamster/set/product_spec.rb +32 -0
  62. data/spec/hamster/set/sum_spec.rb +32 -0
  63. data/spec/hamster/set/uniq_spec.rb +1 -1
  64. data/spec/spec.opts +0 -1
  65. metadata +8 -2
data/History.rdoc CHANGED
@@ -1,3 +1,29 @@
1
+ === 0.2.0 / 2010-01-14
2
+
3
+ * List methods that return lists are now all lazy. Not sure what, if any, negative impact this will have but the positives have so far been well worth it.
4
+
5
+ * Ensure List#eql? (and #==) doesn't accidentally consider an empty list to be equal to an empty array!
6
+
7
+ * Ensure List responds_to? cadr and friends.
8
+
9
+ * Stream now releases the block for garbage collection once called.
10
+
11
+ * Implement List#combinations.
12
+
13
+ * Implement List#inits.
14
+
15
+ * Implement List#tails.
16
+
17
+ * Implement Hash#uniq.
18
+
19
+ * Alias #uniq as #remove_duplicates.
20
+
21
+ * Alias #all? as #forall?
22
+
23
+ * Implement Set#product.
24
+
25
+ * Implement Set#sum.
26
+
1
27
  === 0.1.23 / 2010-01-11
2
28
 
3
29
  * Implement List#product.
data/README.rdoc CHANGED
@@ -170,7 +170,7 @@ How is this even possible? It's possible because <tt>IO#to_list</tt> creates a l
170
170
 
171
171
  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.
172
172
 
173
- 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, and a little more memory hungry, than I would otherwise have preferred.
173
+ Performance is pretty good--especially with lazy lists--but there are some things which may blow the stack due to a lack of Tail-Call-Optimisation in Ruby.
174
174
 
175
175
  Documentation is sparse but I've tried as best I can to write specs that read 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.
176
176
 
data/lib/hamster.rb CHANGED
@@ -1,6 +1,6 @@
1
+ require 'hamster/core_ext'
1
2
  require 'hamster/list'
2
3
  require 'hamster/stack'
3
4
  require 'hamster/set'
4
5
  require 'hamster/hash'
5
- require 'hamster/core_ext'
6
6
  require 'hamster/version'
@@ -7,11 +7,13 @@ module Hamster
7
7
  module IO
8
8
 
9
9
  def to_list(sep = $/)
10
- line = gets(sep)
11
- if line
12
- Stream.new(line) { to_list }
13
- else
14
- EmptyList
10
+ Stream.new do
11
+ line = gets(sep)
12
+ if line
13
+ Sequence.new(line, to_list)
14
+ else
15
+ EmptyList
16
+ end
15
17
  end
16
18
  end
17
19
 
data/lib/hamster/hash.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'forwardable'
2
+
1
3
  require 'hamster/trie'
2
4
 
3
5
  module Hamster
@@ -8,6 +10,8 @@ module Hamster
8
10
 
9
11
  class Hash
10
12
 
13
+ extend Forwardable
14
+
11
15
  def initialize(trie = Trie.new)
12
16
  @trie = trie
13
17
  end
@@ -15,19 +19,19 @@ module Hamster
15
19
  def size
16
20
  @trie.size
17
21
  end
18
- alias_method :length, :size
22
+ def_delegator :self, :size, :length
19
23
 
20
24
  def empty?
21
25
  @trie.empty?
22
26
  end
23
- alias_method :null?, :empty?
27
+ def_delegator :self, :empty?, :null?
24
28
 
25
29
  def has_key?(key)
26
30
  @trie.has_key?(key)
27
31
  end
28
- alias_method :key?, :has_key?
29
- alias_method :include?, :has_key?
30
- alias_method :member?, :has_key?
32
+ def_delegator :self, :has_key?, :key?
33
+ def_delegator :self, :has_key?, :include?
34
+ def_delegator :self, :has_key?, :member?
31
35
 
32
36
  def get(key)
33
37
  entry = @trie.get(key)
@@ -35,12 +39,12 @@ module Hamster
35
39
  entry.value
36
40
  end
37
41
  end
38
- alias_method :[], :get
42
+ def_delegator :self, :get, :[]
39
43
 
40
44
  def put(key, value)
41
45
  self.class.new(@trie.put(key, value))
42
46
  end
43
- alias_method :[]=, :put
47
+ def_delegator :self, :put, :[]=
44
48
 
45
49
  def delete(key)
46
50
  trie = @trie.delete(key)
@@ -55,7 +59,7 @@ module Hamster
55
59
  return self unless block_given?
56
60
  @trie.each { |entry| yield(entry.key, entry.value) }
57
61
  end
58
- alias_method :foreach, :each
62
+ def_delegator :self, :each, :foreach
59
63
 
60
64
  def map
61
65
  return self unless block_given?
@@ -65,14 +69,14 @@ module Hamster
65
69
  self.class.new(@trie.reduce(Trie.new) { |trie, entry| trie.put(*yield(entry.key, entry.value)) })
66
70
  end
67
71
  end
68
- alias_method :collect, :map
72
+ def_delegator :self, :map, :collect
69
73
 
70
74
  def reduce(memo)
71
75
  return memo unless block_given?
72
76
  @trie.reduce(memo) { |memo, entry| yield(memo, entry.key, entry.value) }
73
77
  end
74
- alias_method :inject, :reduce
75
- alias_method :fold, :reduce
78
+ def_delegator :self, :reduce, :inject
79
+ def_delegator :self, :reduce, :fold
76
80
 
77
81
  def filter
78
82
  return self unless block_given?
@@ -83,26 +87,23 @@ module Hamster
83
87
  self.class.new(trie)
84
88
  end
85
89
  end
86
- alias_method :select, :filter
87
- alias_method :find_all, :filter
90
+ def_delegator :self, :filter, :select
91
+ def_delegator :self, :filter, :find_all
88
92
 
89
93
  def remove
90
94
  return self unless block_given?
91
95
  filter { |key, value| !yield(key, value) }
92
96
  end
93
- alias_method :reject, :remove
94
- alias_method :delete_if, :remove
97
+ def_delegator :self, :remove, :reject
98
+ def_delegator :self, :remove, :delete_if
95
99
 
96
100
  def any?
97
- if block_given?
98
- each { |key, value| return true if yield(key, value) }
99
- else
100
- return !empty?
101
- end
101
+ return !empty? unless block_given?
102
+ each { |key, value| return true if yield(key, value) }
102
103
  false
103
104
  end
104
- alias_method :exist?, :any?
105
- alias_method :exists?, :any?
105
+ def_delegator :self, :any?, :exist?
106
+ def_delegator :self, :any?, :exists?
106
107
 
107
108
  def all?
108
109
  if block_given?
@@ -110,25 +111,26 @@ module Hamster
110
111
  end
111
112
  true
112
113
  end
114
+ def_delegator :self, :all?, :forall?
113
115
 
114
116
  def none?
115
- if block_given?
116
- each { |key, value| return false if yield(key, value) }
117
- else
118
- return empty?
119
- end
117
+ return empty? unless block_given?
118
+ each { |key, value| return false if yield(key, value) }
120
119
  true
121
120
  end
122
121
 
123
122
  def eql?(other)
124
123
  other.is_a?(self.class) && @trie.eql?(other.instance_eval{@trie})
125
124
  end
126
- alias_method :==, :eql?
125
+ def_delegator :self, :eql?, :==
127
126
 
128
127
  def dup
129
128
  self
130
129
  end
131
- alias_method :clone, :dup
130
+ def_delegator :self, :dup, :clone
131
+ def_delegator :self, :dup, :uniq
132
+ def_delegator :self, :dup, :nub
133
+ def_delegator :self, :dup, :remove_duplicates
132
134
 
133
135
  end
134
136
 
data/lib/hamster/list.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'forwardable'
1
2
  require 'monitor'
2
3
 
3
4
  require 'hamster/set'
@@ -6,23 +7,25 @@ module Hamster
6
7
 
7
8
  class << self
8
9
 
10
+ extend Forwardable
11
+
9
12
  def list(*items)
10
13
  items.reverse.reduce(EmptyList) { |list, item| list.cons(item) }
11
14
  end
12
15
 
13
16
  def stream(&block)
14
17
  return EmptyList unless block_given?
15
- Stream.new(yield) { stream(&block) }
18
+ Stream.new { Sequence.new(yield, stream(&block)) }
16
19
  end
17
20
 
18
21
  def interval(from, to)
19
22
  return EmptyList if from > to
20
- Stream.new(from) { interval(from.succ, to) }
23
+ Stream.new { Sequence.new(from, interval(from.succ, to)) }
21
24
  end
22
- alias_method :range, :interval
25
+ def_delegator :self, :interval, :range
23
26
 
24
27
  def repeat(item)
25
- Stream.new(item) { repeat(item) }
28
+ Stream.new { Sequence.new(item, repeat(item)) }
26
29
  end
27
30
 
28
31
  def replicate(number, item)
@@ -30,29 +33,37 @@ module Hamster
30
33
  end
31
34
 
32
35
  def iterate(item, &block)
33
- Stream.new(item) { iterate(yield(item), &block) }
36
+ Stream.new { Sequence.new(item, iterate(yield(item), &block)) }
34
37
  end
35
38
 
36
39
  end
37
40
 
38
41
  module List
39
42
 
43
+ extend Forwardable
44
+
40
45
  Undefined = Object.new
41
46
 
47
+ CADR = /^c([ad]+)r$/
48
+
49
+ def first
50
+ head
51
+ end
52
+
42
53
  def empty?
43
54
  false
44
55
  end
45
- alias_method :null?, :empty?
56
+ def_delegator :self, :empty?, :null?
46
57
 
47
58
  def size
48
59
  reduce(0) { |memo, item| memo.succ }
49
60
  end
50
- alias_method :length, :size
61
+ def_delegator :self, :size, :length
51
62
 
52
63
  def cons(item)
53
64
  Sequence.new(item, self)
54
65
  end
55
- alias_method :>>, :cons
66
+ def_delegator :self, :cons, :>>
56
67
 
57
68
  def each
58
69
  return self unless block_given?
@@ -62,13 +73,19 @@ module Hamster
62
73
  list = list.tail
63
74
  end
64
75
  end
65
- alias_method :foreach, :each
76
+ def_delegator :self, :each, :foreach
66
77
 
67
78
  def map(&block)
68
79
  return self unless block_given?
69
- Stream.new(yield(head)) { tail.map(&block) }
80
+ Stream.new do
81
+ if empty?
82
+ self
83
+ else
84
+ Sequence.new(yield(head), tail.map(&block))
85
+ end
86
+ end
70
87
  end
71
- alias_method :collect, :map
88
+ def_delegator :self, :map, :collect
72
89
 
73
90
  def reduce(memo = Undefined, &block)
74
91
  return tail.reduce(head, &block) if memo.equal?(Undefined)
@@ -76,80 +93,102 @@ module Hamster
76
93
  each { |item| memo = yield(memo, item) }
77
94
  memo
78
95
  end
79
- alias_method :inject, :reduce
80
- alias_method :fold, :reduce
96
+ def_delegator :self, :reduce, :inject
97
+ def_delegator :self, :reduce, :fold
81
98
 
82
99
  def filter(&block)
83
100
  return self unless block_given?
84
- list = self
85
- while !yield(list.head)
86
- list = list.tail
87
- return list if list.empty?
101
+ Stream.new do
102
+ if empty?
103
+ self
104
+ elsif yield(head)
105
+ Sequence.new(head, tail.filter(&block))
106
+ else
107
+ tail.filter(&block)
108
+ end
88
109
  end
89
- Stream.new(list.head) { list.tail.filter(&block) }
90
110
  end
91
- alias_method :select, :filter
92
- alias_method :find_all, :filter
111
+ def_delegator :self, :filter, :select
112
+ def_delegator :self, :filter, :find_all
93
113
 
94
114
  def remove(&block)
95
115
  return self unless block_given?
96
116
  filter { |item| !yield(item) }
97
117
  end
98
- alias_method :reject, :remove
99
- alias_method :delete_if, :remove
118
+ def_delegator :self, :remove, :reject
119
+ def_delegator :self, :remove, :delete_if
100
120
 
101
121
  def take_while(&block)
102
122
  return self unless block_given?
103
- if yield(head)
104
- Stream.new(head) { tail.take_while(&block) }
105
- else
106
- EmptyList
123
+ Stream.new do
124
+ if empty?
125
+ self
126
+ elsif yield(head)
127
+ Sequence.new(head, tail.take_while(&block))
128
+ else
129
+ EmptyList
130
+ end
107
131
  end
108
132
  end
109
133
 
110
- def drop_while
134
+ def drop_while(&block)
111
135
  return self unless block_given?
112
- list = self
113
- while !list.empty? && yield(list.head)
114
- list = list.tail
136
+ Stream.new do
137
+ if empty?
138
+ self
139
+ elsif yield(head)
140
+ tail.drop_while(&block)
141
+ else
142
+ self
143
+ end
115
144
  end
116
- list
117
145
  end
118
146
 
119
147
  def take(number)
120
- return EmptyList unless number > 0
121
- Stream.new(head) { tail.take(number - 1) }
148
+ Stream.new do
149
+ if empty?
150
+ self
151
+ elsif number > 0
152
+ Sequence.new(head, tail.take(number - 1))
153
+ else
154
+ EmptyList
155
+ end
156
+ end
122
157
  end
123
158
 
124
159
  def drop(number)
125
- list = self
126
- while !list.empty? && number > 0
127
- number -= 1
128
- list = list.tail
160
+ Stream.new do
161
+ if empty?
162
+ self
163
+ elsif number > 0
164
+ tail.drop(number - 1)
165
+ else
166
+ self
167
+ end
129
168
  end
130
- list
131
169
  end
132
170
 
133
171
  def include?(object)
134
172
  any? { |item| item == object }
135
173
  end
136
- alias_method :member?, :include?
137
- alias_method :contains?, :include?
138
- alias_method :elem?, :include?
174
+ def_delegator :self, :include?, :member?
175
+ def_delegator :self, :include?, :contains?
176
+ def_delegator :self, :include?, :elem?
139
177
 
140
178
  def any?
141
179
  return any? { |item| item } unless block_given?
142
180
  each { |item| return true if yield(item) }
143
181
  false
144
182
  end
145
- alias_method :exist?, :any?
146
- alias_method :exists?, :any?
183
+ def_delegator :self, :any?, :exist?
184
+ def_delegator :self, :any?, :exists?
147
185
 
148
186
  def all?
149
187
  return all? { |item| item } unless block_given?
150
188
  each { |item| return false unless yield(item) }
151
189
  true
152
190
  end
191
+ def_delegator :self, :all?, :forall?
153
192
 
154
193
  def none?
155
194
  return none? { |item| item } unless block_given?
@@ -171,19 +210,25 @@ module Hamster
171
210
  return nil unless block_given?
172
211
  each { |item| return item if yield(item) }
173
212
  end
174
- alias_method :detect, :find
213
+ def_delegator :self, :find, :detect
175
214
 
176
215
  def partition(&block)
177
216
  return self unless block_given?
178
- Stream.new(filter(&block)) { Sequence.new(remove(&block)) }
217
+ Stream.new { Sequence.new(filter(&block), Sequence.new(remove(&block))) }
179
218
  end
180
219
 
181
220
  def append(other)
182
- Stream.new(head) { tail.append(other) }
221
+ Stream.new do
222
+ if empty?
223
+ other
224
+ else
225
+ Sequence.new(head, tail.append(other))
226
+ end
227
+ end
183
228
  end
184
- alias_method :concat, :append
185
- alias_method :cat, :append
186
- alias_method :+, :append
229
+ def_delegator :self, :append, :concat
230
+ def_delegator :self, :append, :cat
231
+ def_delegator :self, :append, :+
187
232
 
188
233
  def reverse
189
234
  reduce(EmptyList) { |list, item| list.cons(item) }
@@ -193,37 +238,49 @@ module Hamster
193
238
  return minimum { |minimum, item| item <=> minimum } unless block_given?
194
239
  reduce { |minimum, item| yield(minimum, item) < 0 ? item : minimum }
195
240
  end
196
- alias_method :min, :minimum
241
+ def_delegator :self, :minimum, :min
197
242
 
198
243
  def maximum(&block)
199
244
  return maximum { |maximum, item| item <=> maximum } unless block_given?
200
245
  reduce { |maximum, item| yield(maximum, item) > 0 ? item : maximum }
201
246
  end
202
- alias_method :max, :maximum
247
+ def_delegator :self, :maximum, :max
203
248
 
204
249
  def grep(pattern, &block)
205
250
  filter { |item| pattern === item }.map(&block)
206
251
  end
207
252
 
208
253
  def zip(other)
209
- Stream.new(Sequence.new(other.head).cons(head)) { tail.zip(other.tail) }
254
+ Stream.new do
255
+ if empty? && other.empty?
256
+ self
257
+ else
258
+ Sequence.new(Sequence.new(head, Sequence.new(other.head)), tail.zip(other.tail))
259
+ end
260
+ end
210
261
  end
211
262
 
212
263
  def cycle
213
- Stream.new(head) { tail.append(self.cycle) }
264
+ Stream.new do
265
+ if empty?
266
+ self
267
+ else
268
+ Sequence.new(head, tail.append(self.cycle))
269
+ end
270
+ end
214
271
  end
215
272
 
216
273
  def split_at(number)
217
- Sequence.new(drop(number)).cons(take(number))
274
+ Sequence.new(take(number), Sequence.new(drop(number)))
218
275
  end
219
276
 
220
277
  def span(&block)
221
- return Sequence.new(EmptyList).cons(self) unless block_given?
222
- Stream.new(take_while(&block)) { Sequence.new(drop_while(&block)) }
278
+ return Sequence.new(self, Sequence.new(EmptyList)) unless block_given?
279
+ Sequence.new(take_while(&block), Sequence.new(drop_while(&block)))
223
280
  end
224
281
 
225
282
  def break(&block)
226
- return Sequence.new(EmptyList).cons(self) unless block_given?
283
+ return Sequence.new(self, Sequence.new(EmptyList)) unless block_given?
227
284
  span { |item| !yield(item) }
228
285
  end
229
286
 
@@ -236,42 +293,56 @@ module Hamster
236
293
  end
237
294
 
238
295
  def sort(&block)
239
- Hamster.list(*to_a.sort(&block))
296
+ Stream.new do
297
+ Hamster.list(*to_a.sort(&block))
298
+ end
240
299
  end
241
300
 
242
301
  def sort_by(&block)
243
302
  return sort unless block_given?
244
- Hamster.list(*to_a.sort_by(&block))
303
+ Stream.new do
304
+ Hamster.list(*to_a.sort_by(&block))
305
+ end
245
306
  end
246
307
 
247
308
  def join(sep = "")
309
+ return "" if empty?
248
310
  sep = sep.to_s
249
311
  tail.reduce(head.to_s) { |string, item| string << sep << item.to_s }
250
312
  end
251
313
 
252
314
  def intersperse(sep)
253
- return self if tail.empty?
254
- Stream.new(head) { Stream.new(sep) { tail.intersperse(sep) } }
315
+ Stream.new do
316
+ if tail.empty?
317
+ self
318
+ else
319
+ Sequence.new(head, Sequence.new(sep, tail.intersperse(sep)))
320
+ end
321
+ end
255
322
  end
256
323
 
257
324
  def uniq(items = Set.new)
258
- list = self
259
- while !list.empty? && items.include?(list.head)
260
- list = list.tail
325
+ Stream.new do
326
+ if empty?
327
+ self
328
+ elsif items.include?(head)
329
+ tail.uniq(items)
330
+ else
331
+ Sequence.new(head, tail.uniq(items.add(head)))
332
+ end
261
333
  end
262
- return list if list.empty?
263
- Stream.new(list.head) { list.tail.uniq(items.add(list.head)) }
264
334
  end
265
- alias_method :nub, :uniq
335
+ def_delegator :self, :uniq, :nub
336
+ def_delegator :self, :uniq, :remove_duplicates
266
337
 
267
338
  def union(other)
268
339
  self.append(other).uniq
269
340
  end
270
- alias_method :|, :union
341
+ def_delegator :self, :union, :|
271
342
 
272
343
  def init
273
344
  return EmptyList if tail.empty?
274
- Stream.new(head) { tail.init }
345
+ Stream.new { Sequence.new(head, tail.init) }
275
346
  end
276
347
 
277
348
  def last
@@ -290,6 +361,38 @@ module Hamster
290
361
  reduce(0, &:+)
291
362
  end
292
363
 
364
+ def tails
365
+ Stream.new do
366
+ if empty?
367
+ Sequence.new(self)
368
+ else
369
+ Sequence.new(self, tail.tails)
370
+ end
371
+ end
372
+ end
373
+
374
+ def inits
375
+ Stream.new do
376
+ if empty?
377
+ Sequence.new(self)
378
+ else
379
+ Sequence.new(EmptyList, tail.inits.map { |list| list.cons(head) })
380
+ end
381
+ end
382
+ end
383
+
384
+ def combinations(number)
385
+ return Sequence.new(EmptyList) if number == 0
386
+ Stream.new do
387
+ if empty?
388
+ self
389
+ else
390
+ tail.combinations(number - 1).map { |list| list.cons(head) }.append(tail.combinations(number))
391
+ end
392
+ end
393
+ end
394
+ def_delegator :self, :combinations, :combination
395
+
293
396
  def eql?(other)
294
397
  return false unless other.is_a?(List)
295
398
 
@@ -301,20 +404,21 @@ module Hamster
301
404
  list = list.tail
302
405
  other = other.tail
303
406
  end
304
- other.equal?(list)
407
+
408
+ other.empty? && list.empty?
305
409
  end
306
- alias_method :==, :eql?
410
+ def_delegator :self, :eql?, :==
307
411
 
308
412
  def dup
309
413
  self
310
414
  end
311
- alias_method :clone, :dup
415
+ def_delegator :self, :dup, :clone
312
416
 
313
417
  def to_a
314
418
  reduce([]) { |a, item| a << item }
315
419
  end
316
- alias_method :to_ary, :to_a
317
- alias_method :entries, :to_a
420
+ def_delegator :self, :to_a, :to_ary
421
+ def_delegator :self, :to_a, :entries
318
422
 
319
423
  def to_list
320
424
  self
@@ -324,10 +428,14 @@ module Hamster
324
428
  to_a.inspect
325
429
  end
326
430
 
431
+ def respond_to?(name, include_private = false)
432
+ super || CADR === name
433
+ end
434
+
327
435
  private
328
436
 
329
437
  def method_missing(name, *args, &block)
330
- if name.to_s =~ /^c([ad]+)r$/
438
+ if CADR === name
331
439
  accessor($1)
332
440
  else
333
441
  super
@@ -354,7 +462,6 @@ module Hamster
354
462
  include List
355
463
 
356
464
  attr_reader :head, :tail
357
- alias_method :first, :head
358
465
 
359
466
  def initialize(head, tail = EmptyList)
360
467
  @head = head
@@ -367,22 +474,33 @@ module Hamster
367
474
 
368
475
  include List
369
476
 
370
- attr_reader :head
371
- alias_method :first, :head
372
-
373
- def initialize(head, &tail)
374
- @head = head
375
- @tail = tail
477
+ def initialize(&block)
478
+ @block = block
376
479
  @mutex = Mutex.new
377
480
  end
378
481
 
482
+ def head
483
+ target.head
484
+ end
485
+
379
486
  def tail
487
+ target.tail
488
+ end
489
+
490
+ def empty?
491
+ target.empty?
492
+ end
493
+
494
+ private
495
+
496
+ def target
380
497
  @mutex.synchronize do
381
- unless defined?(@value)
382
- @value = @tail.call
498
+ unless defined?(@target)
499
+ @target = @block.call
500
+ @block = nil
383
501
  end
384
502
  end
385
- @value
503
+ @target
386
504
  end
387
505
 
388
506
  end
@@ -396,7 +514,6 @@ module Hamster
396
514
  def head
397
515
  nil
398
516
  end
399
- alias_method :first, :head
400
517
 
401
518
  def tail
402
519
  self
@@ -405,48 +522,6 @@ module Hamster
405
522
  def empty?
406
523
  true
407
524
  end
408
- alias_method :null?, :empty?
409
-
410
- def map
411
- self
412
- end
413
- alias_method :collect, :map
414
-
415
- def filter
416
- self
417
- end
418
- alias_method :select, :filter
419
- alias_method :find_all, :filter
420
-
421
- def remove
422
- self
423
- end
424
- alias_method :reject, :remove
425
- alias_method :delete_if, :remove
426
-
427
- def take_while
428
- self
429
- end
430
-
431
- def take(number)
432
- self
433
- end
434
-
435
- def append(other)
436
- other
437
- end
438
- alias_method :concat, :append
439
- alias_method :cat, :append
440
- alias_method :+, :append
441
-
442
- def zip(other)
443
- return super unless other.empty?
444
- self
445
- end
446
-
447
- def cycle
448
- self
449
- end
450
525
 
451
526
  end
452
527