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.
- data/History.rdoc +26 -0
- data/README.rdoc +1 -1
- data/lib/hamster.rb +1 -1
- data/lib/hamster/core_ext/io.rb +7 -5
- data/lib/hamster/hash.rb +31 -29
- data/lib/hamster/list.rb +203 -128
- data/lib/hamster/set.rb +39 -25
- data/lib/hamster/stack.rb +9 -5
- data/lib/hamster/trie.rb +5 -1
- data/lib/hamster/version.rb +1 -1
- data/spec/hamster/hash/all_spec.rb +29 -25
- data/spec/hamster/hash/reduce_spec.rb +1 -1
- data/spec/hamster/hash/uniq_spec.rb +23 -0
- data/spec/hamster/list/all_spec.rb +55 -55
- data/spec/hamster/list/any_spec.rb +4 -8
- data/spec/hamster/list/append_spec.rb +2 -20
- data/spec/hamster/list/break_spec.rb +22 -121
- data/spec/hamster/list/cadr_spec.rb +4 -0
- data/spec/hamster/list/chunk_spec.rb +6 -0
- data/spec/hamster/list/combinations_spec.rb +51 -0
- data/spec/hamster/list/count_spec.rb +4 -8
- data/spec/hamster/list/cycle_spec.rb +5 -24
- data/spec/hamster/list/drop_spec.rb +2 -14
- data/spec/hamster/list/drop_while_spec.rb +2 -14
- data/spec/hamster/list/each_spec.rb +4 -8
- data/spec/hamster/list/eql_spec.rb +9 -10
- data/spec/hamster/list/filter_spec.rb +2 -20
- data/spec/hamster/list/find_spec.rb +4 -8
- data/spec/hamster/list/grep_spec.rb +2 -20
- data/spec/hamster/list/include_spec.rb +4 -8
- data/spec/hamster/list/init_spec.rb +4 -0
- data/spec/hamster/list/inits_spec.rb +42 -0
- data/spec/hamster/list/inspect_spec.rb +4 -8
- data/spec/hamster/list/intersperse_spec.rb +2 -14
- data/spec/hamster/list/join_spec.rb +4 -8
- data/spec/hamster/list/last_spec.rb +4 -8
- data/spec/hamster/list/map_spec.rb +2 -14
- data/spec/hamster/list/maximum_spec.rb +4 -8
- data/spec/hamster/list/minimum_spec.rb +4 -8
- data/spec/hamster/list/none_spec.rb +4 -8
- data/spec/hamster/list/one_spec.rb +4 -8
- data/spec/hamster/list/partition_spec.rb +12 -111
- data/spec/hamster/list/product_spec.rb +4 -8
- data/spec/hamster/list/reduce_spec.rb +4 -8
- data/spec/hamster/list/remove_spec.rb +2 -14
- data/spec/hamster/list/reverse_spec.rb +4 -8
- data/spec/hamster/list/size_spec.rb +4 -8
- data/spec/hamster/list/sorting_spec.rb +2 -14
- data/spec/hamster/list/span_spec.rb +22 -121
- data/spec/hamster/list/split_at_spec.rb +2 -65
- data/spec/hamster/list/sum_spec.rb +4 -8
- data/spec/hamster/list/tails_spec.rb +42 -0
- data/spec/hamster/list/take_spec.rb +2 -14
- data/spec/hamster/list/take_while_spec.rb +2 -14
- data/spec/hamster/list/to_a_spec.rb +4 -8
- data/spec/hamster/list/to_ary_spec.rb +4 -8
- data/spec/hamster/list/union_spec.rb +1 -21
- data/spec/hamster/list/uniq_spec.rb +3 -15
- data/spec/hamster/list/zip_spec.rb +1 -21
- data/spec/hamster/set/all_spec.rb +33 -29
- data/spec/hamster/set/product_spec.rb +32 -0
- data/spec/hamster/set/sum_spec.rb +32 -0
- data/spec/hamster/set/uniq_spec.rb +1 -1
- data/spec/spec.opts +0 -1
- 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
|
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
data/lib/hamster/core_ext/io.rb
CHANGED
@@ -7,11 +7,13 @@ module Hamster
|
|
7
7
|
module IO
|
8
8
|
|
9
9
|
def to_list(sep = $/)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
22
|
+
def_delegator :self, :size, :length
|
19
23
|
|
20
24
|
def empty?
|
21
25
|
@trie.empty?
|
22
26
|
end
|
23
|
-
|
27
|
+
def_delegator :self, :empty?, :null?
|
24
28
|
|
25
29
|
def has_key?(key)
|
26
30
|
@trie.has_key?(key)
|
27
31
|
end
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
75
|
-
|
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
|
-
|
87
|
-
|
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
|
-
|
94
|
-
|
97
|
+
def_delegator :self, :remove, :reject
|
98
|
+
def_delegator :self, :remove, :delete_if
|
95
99
|
|
96
100
|
def any?
|
97
|
-
|
98
|
-
|
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
|
-
|
105
|
-
|
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
|
-
|
116
|
-
|
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
|
-
|
125
|
+
def_delegator :self, :eql?, :==
|
127
126
|
|
128
127
|
def dup
|
129
128
|
self
|
130
129
|
end
|
131
|
-
|
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
|
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
|
23
|
+
Stream.new { Sequence.new(from, interval(from.succ, to)) }
|
21
24
|
end
|
22
|
-
|
25
|
+
def_delegator :self, :interval, :range
|
23
26
|
|
24
27
|
def repeat(item)
|
25
|
-
Stream.new(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
|
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
|
-
|
56
|
+
def_delegator :self, :empty?, :null?
|
46
57
|
|
47
58
|
def size
|
48
59
|
reduce(0) { |memo, item| memo.succ }
|
49
60
|
end
|
50
|
-
|
61
|
+
def_delegator :self, :size, :length
|
51
62
|
|
52
63
|
def cons(item)
|
53
64
|
Sequence.new(item, self)
|
54
65
|
end
|
55
|
-
|
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
|
-
|
76
|
+
def_delegator :self, :each, :foreach
|
66
77
|
|
67
78
|
def map(&block)
|
68
79
|
return self unless block_given?
|
69
|
-
Stream.new
|
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
|
-
|
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
|
-
|
80
|
-
|
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
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
92
|
-
|
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
|
-
|
99
|
-
|
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
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
-
|
113
|
-
|
114
|
-
|
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
|
-
|
121
|
-
|
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
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
137
|
-
|
138
|
-
|
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
|
-
|
146
|
-
|
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
|
-
|
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)
|
217
|
+
Stream.new { Sequence.new(filter(&block), Sequence.new(remove(&block))) }
|
179
218
|
end
|
180
219
|
|
181
220
|
def append(other)
|
182
|
-
Stream.new
|
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
|
-
|
185
|
-
|
186
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
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(
|
274
|
+
Sequence.new(take(number), Sequence.new(drop(number)))
|
218
275
|
end
|
219
276
|
|
220
277
|
def span(&block)
|
221
|
-
return Sequence.new(
|
222
|
-
|
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(
|
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
|
-
|
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
|
-
|
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
|
-
|
254
|
-
|
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
|
-
|
259
|
-
|
260
|
-
|
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
|
-
|
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
|
-
|
341
|
+
def_delegator :self, :union, :|
|
271
342
|
|
272
343
|
def init
|
273
344
|
return EmptyList if tail.empty?
|
274
|
-
Stream.new(head
|
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
|
-
|
407
|
+
|
408
|
+
other.empty? && list.empty?
|
305
409
|
end
|
306
|
-
|
410
|
+
def_delegator :self, :eql?, :==
|
307
411
|
|
308
412
|
def dup
|
309
413
|
self
|
310
414
|
end
|
311
|
-
|
415
|
+
def_delegator :self, :dup, :clone
|
312
416
|
|
313
417
|
def to_a
|
314
418
|
reduce([]) { |a, item| a << item }
|
315
419
|
end
|
316
|
-
|
317
|
-
|
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
|
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
|
-
|
371
|
-
|
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?(@
|
382
|
-
@
|
498
|
+
unless defined?(@target)
|
499
|
+
@target = @block.call
|
500
|
+
@block = nil
|
383
501
|
end
|
384
502
|
end
|
385
|
-
@
|
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
|
|