lazylist 0.1.3 → 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/CHANGES +9 -0
- data/Rakefile +1 -2
- data/TODO +35 -0
- data/VERSION +1 -1
- data/examples/examples.rb +6 -6
- data/examples/hamming.rb +5 -5
- data/examples/sieve.rb +2 -7
- data/lib/lazylist.rb +209 -92
- data/make_doc.rb +2 -3
- data/tests/runner.rb +7 -2
- data/tests/test.rb +27 -25
- data/tests/test_enumerable.rb +95 -0
- metadata +4 -2
data/CHANGES
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
2005-10-08 * 0.2.0 * Lots of changes (without backwards compatibility):
|
|
2
|
+
- Now the usual methods from the Ruby Enumerable module
|
|
3
|
+
should be much better supported.
|
|
4
|
+
- In general there are more finite lazy lists returned
|
|
5
|
+
from methods (for example: lazylist[a, b] or
|
|
6
|
+
lazylist[a..b]) instead of arrays.
|
|
7
|
+
- mapper, filter and combine methods give an 'obsoleted'
|
|
8
|
+
warning now, you better use map, select, and zip from
|
|
9
|
+
now on.
|
|
1
10
|
2005-09-26 * 0.1.3 * Fixed some Ruby 1.9 incompatibilities.
|
|
2
11
|
* Added default arguments for drop&take.
|
|
3
12
|
* I've decided to pollute the Kernel module with the #list
|
data/Rakefile
CHANGED
data/TODO
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
* adding better support for finite lazy lists
|
|
2
|
+
* this stuff
|
|
3
|
+
# XXX
|
|
4
|
+
def append(*elements)
|
|
5
|
+
return rest if empty?
|
|
6
|
+
s = self
|
|
7
|
+
loop do
|
|
8
|
+
if s.tail.empty?
|
|
9
|
+
break s
|
|
10
|
+
else
|
|
11
|
+
s = s.tail
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
s.tail = rest
|
|
15
|
+
self
|
|
16
|
+
end
|
|
17
|
+
alias << append
|
|
18
|
+
|
|
19
|
+
# XXX self should be finite
|
|
20
|
+
def +(other)
|
|
21
|
+
other.is_a? self.class or
|
|
22
|
+
raise TypeError, "other has to be a #{self.class}"
|
|
23
|
+
copy = LazyList[to_a]
|
|
24
|
+
s = copy
|
|
25
|
+
loop do
|
|
26
|
+
if s.tail.empty?
|
|
27
|
+
break s
|
|
28
|
+
else
|
|
29
|
+
s = s.tail
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
s.tail = LazyList[other.to_a]
|
|
33
|
+
copy
|
|
34
|
+
end
|
|
35
|
+
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.2.0
|
data/examples/examples.rb
CHANGED
|
@@ -7,21 +7,21 @@ def rand(s = 666)
|
|
|
7
7
|
end
|
|
8
8
|
r = rand(666)
|
|
9
9
|
r.each(10) { |x| print x, " " } ; puts
|
|
10
|
-
dice =
|
|
10
|
+
dice = r.map { |x| 1 + x % 6 }
|
|
11
11
|
dice.each(10) { |x| print x, " " } ; puts
|
|
12
|
-
coin =
|
|
12
|
+
coin = r.map { |x| x % 2 == 0 ? :head : :tail }
|
|
13
13
|
coin.each(10) { |x| print x, " " } ; puts
|
|
14
14
|
|
|
15
|
-
puts "Prime number lazy list with
|
|
16
|
-
prime = LazyList[10000..1000000].
|
|
15
|
+
puts "Prime number lazy list with select (10000..1000000)"
|
|
16
|
+
prime = LazyList[10000..1000000].select do |x|
|
|
17
17
|
not (2..Math.sqrt(x).to_i).find { |d| x % d == 0 }
|
|
18
18
|
end
|
|
19
19
|
prime.each(10) { |x| print x, " " } ; puts
|
|
20
20
|
p prime[1]
|
|
21
21
|
puts
|
|
22
22
|
|
|
23
|
-
puts "Squared prime numbers with
|
|
24
|
-
prime.
|
|
23
|
+
puts "Squared prime numbers with map"
|
|
24
|
+
prime.map { |x| x ** 2 }.each(5) { |x| print x, " " } ; puts
|
|
25
25
|
puts
|
|
26
26
|
|
|
27
27
|
puts "Lazy Lists from mathematical sequences"
|
data/examples/hamming.rb
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
require 'lazylist'
|
|
2
2
|
|
|
3
|
-
# Computes the hamming sequence: that is the sequence of natural numbers,
|
|
4
|
-
#
|
|
3
|
+
# Computes the hamming sequence: that is the sequence of natural numbers, whose
|
|
4
|
+
# prime divisors are all <= 5.
|
|
5
5
|
hamming = list(1) do
|
|
6
|
-
hamming.
|
|
7
|
-
hamming.
|
|
8
|
-
hamming.
|
|
6
|
+
hamming.map { |x| 2 * x }.merge(
|
|
7
|
+
hamming.map { |x| 3 * x }.merge(
|
|
8
|
+
hamming.map { |x| 5 * x }))
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
max = (ARGV.shift || 100).to_i
|
data/examples/sieve.rb
CHANGED
|
@@ -2,15 +2,10 @@ require 'lazylist'
|
|
|
2
2
|
|
|
3
3
|
# Sieve or Eratosthenes with filters on Lazy Lists. It has a very nice
|
|
4
4
|
# notation, but is a real memory and cpu hog. Enjoy!
|
|
5
|
-
|
|
6
|
-
def from(n = 0)
|
|
7
|
-
list(n) { from(n + 1) }
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def primes(l = from(2))
|
|
5
|
+
def primes(l = LazyList.from(2))
|
|
11
6
|
current, rest = l.head, l.tail
|
|
12
7
|
list(current) do
|
|
13
|
-
primes rest.
|
|
8
|
+
primes rest.select { |x| (x % current) != 0 }
|
|
14
9
|
end
|
|
15
10
|
end
|
|
16
11
|
|
data/lib/lazylist.rb
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
#
|
|
26
26
|
# * http://rubyforge.org/frs?group_id=394
|
|
27
27
|
#
|
|
28
|
-
#
|
|
28
|
+
# The homepage of this library is located at
|
|
29
29
|
#
|
|
30
30
|
# * http://lazylist.rubyforge.org
|
|
31
31
|
#
|
|
@@ -41,48 +41,53 @@
|
|
|
41
41
|
# sq.take(10)
|
|
42
42
|
# ==>[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
|
|
43
43
|
#
|
|
44
|
-
# To compute
|
|
45
|
-
#
|
|
44
|
+
# To compute the first 10 square numbers and do something with them you can
|
|
45
|
+
# call the each method with:
|
|
46
46
|
#
|
|
47
|
-
#
|
|
47
|
+
# sq.each(10) { |x| puts x }
|
|
48
48
|
#
|
|
49
|
-
#
|
|
49
|
+
# To compute every square number and do something with them you can call the
|
|
50
|
+
# "each" method without an argument:
|
|
50
51
|
#
|
|
51
|
-
#
|
|
52
|
+
# sq.each { |x| puts x }
|
|
53
|
+
#
|
|
54
|
+
# Notice that calls to each without an argument will not return if applied to
|
|
55
|
+
# infinite lazy lists.
|
|
52
56
|
#
|
|
53
|
-
#
|
|
57
|
+
# You can also use indices on lazy lists to get the values at a certain range:
|
|
54
58
|
#
|
|
59
|
+
# sq[ 0..9 ] or sq[0, 10]
|
|
55
60
|
# ==>[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
|
|
56
61
|
#
|
|
57
62
|
# To spare memory it's possible to throw away every element after it was
|
|
58
63
|
# fetched:
|
|
59
64
|
#
|
|
60
|
-
#
|
|
61
|
-
#
|
|
65
|
+
# sq.take!(1) => [1]
|
|
66
|
+
# sq.take!(1) => [4]
|
|
62
67
|
#
|
|
63
68
|
# Of course it's also possible to compute more complex lists like the Fibonacci
|
|
64
69
|
# sequence:
|
|
65
70
|
#
|
|
66
|
-
#
|
|
71
|
+
# fib = LazyList.tabulate(0) { |x| x < 2 ? 1 : fib[x-2] + fib[x-1] }
|
|
67
72
|
#
|
|
68
|
-
#
|
|
73
|
+
# fib[100] => 573147844013817084101
|
|
69
74
|
# computes the 99th Fibonacci number. (We always start with index 0.)
|
|
70
|
-
#
|
|
75
|
+
# fib[101] => 927372692193078999176
|
|
71
76
|
# computes the 100th Fibonacci number. The already computed values are reused
|
|
72
77
|
# to compute this result. That's a very transparent way to get memoization for
|
|
73
78
|
# sequences that require heavy computation.
|
|
74
79
|
#
|
|
75
|
-
# You create
|
|
76
|
-
#
|
|
80
|
+
# You can create lazy lists that are based on arbitrary Enumerables, so can for
|
|
81
|
+
# example wrap your passwd file in one pretty easily:
|
|
77
82
|
#
|
|
78
|
-
#
|
|
83
|
+
# pw = LazyList[ File.new("/etc/passwd") ]
|
|
79
84
|
#
|
|
80
85
|
# Call grep to find the users root and flori:
|
|
81
|
-
# pw.grep /^(root|flori):/ => ["root:x:0:0:...\n"
|
|
86
|
+
# pw.grep /^(root|flori):/ => ["root:x:0:0:...\n",... ]
|
|
82
87
|
#
|
|
83
88
|
# In this case the whole passwd file is slurped into the memory. If
|
|
84
89
|
# you use
|
|
85
|
-
#
|
|
90
|
+
# pw.find { |x| x =~ /^root:/ } => "root:x:0:0:root:/root:/bin/bash\n"
|
|
86
91
|
# instead, only every line until the root line is loaded into the memory.
|
|
87
92
|
#
|
|
88
93
|
# == References
|
|
@@ -91,12 +96,126 @@
|
|
|
91
96
|
# Structure and Interpretation of Computer Programs (SICP)
|
|
92
97
|
# [http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-24.html#%25_sec_3.5]
|
|
93
98
|
#
|
|
94
|
-
|
|
95
99
|
class LazyList
|
|
100
|
+
module Enumerable
|
|
101
|
+
include ::Enumerable
|
|
102
|
+
|
|
103
|
+
# Returns two lazy lists, the first containing the elements of this lazy
|
|
104
|
+
# list for which the block evaluates to true, the second containing the
|
|
105
|
+
# rest.
|
|
106
|
+
def partition(&block)
|
|
107
|
+
accepted, rejected = to_a.partition(&block)
|
|
108
|
+
return LazyList.from_enum(accepted), LazyList.from_enum(rejected)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Returns a sorted version of this lazy list. This method should only be
|
|
112
|
+
# called on finite lazy lists or it will never return. Also see
|
|
113
|
+
# Enumerable#sort.
|
|
114
|
+
def sort # :yields: a, b
|
|
115
|
+
LazyList.from_enum(super)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Returns a sorted version of this lazy list. This method should only be
|
|
119
|
+
# called on finite lazy lists or it will never return. Also see
|
|
120
|
+
# Enumerable#sort_by.
|
|
121
|
+
def sort_by # :yields: obj
|
|
122
|
+
LazyList.from_enum(super)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Calls _block_ with two arguments, the element and its index, for each
|
|
126
|
+
# element of this lazy list. If _block_ isn't given, the method returns a
|
|
127
|
+
# lazy list that consists of [ element, index ] pairs instead.
|
|
128
|
+
def each_with_index(&block)
|
|
129
|
+
if block
|
|
130
|
+
each_with_index.each { |x| block.call(x) }
|
|
131
|
+
else
|
|
132
|
+
i = -1
|
|
133
|
+
map { |x| [ x, i += 1 ] }
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Returns the lazy list, that contains all the given _block_'s return
|
|
138
|
+
# values, if it was called on every
|
|
139
|
+
# self[i], others[0][i], others[1][i],... others[others.size - 1][i]
|
|
140
|
+
# for i in 0..Infinity. If _block_ wasn't given
|
|
141
|
+
# this result will be the array
|
|
142
|
+
# [self[i], others[0][i], others[1][i],... ]
|
|
143
|
+
# and a lazy list of those arrays is returned.
|
|
144
|
+
def zip(*others, &block)
|
|
145
|
+
if empty? or others.any? { |o| o.empty? }
|
|
146
|
+
Empty
|
|
147
|
+
else
|
|
148
|
+
block ||= lambda { |*all| all }
|
|
149
|
+
list(block.call(head, *others.map { |o| o.head })) do
|
|
150
|
+
tail.zip(*others.map { |o| o.tail}, &block)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# obsoleted by #zip
|
|
156
|
+
def combine(other, &operator)
|
|
157
|
+
warn "method 'combine' is obsolete - use 'zip'"
|
|
158
|
+
zip(other, &operator)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Returns a lazy list every element of this lazy list for which
|
|
162
|
+
# pattern === element is true. If the optional _block_ is supplied,
|
|
163
|
+
# each matching element is passed to it, and the block's result becomes
|
|
164
|
+
# an element of the returned lazy list.
|
|
165
|
+
def grep(pattern, &block)
|
|
166
|
+
result = select { |x| pattern === x }
|
|
167
|
+
block and result = result.map(&block)
|
|
168
|
+
result
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Returns a lazy list of all elements of this lazy list for which the block
|
|
172
|
+
# is false (see also +Lazylist#select+).
|
|
173
|
+
def reject
|
|
174
|
+
select { |obj| !yield(obj) }
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Returns a lazy list of all elements of this lazy list for which _block_
|
|
178
|
+
# is true.
|
|
179
|
+
def select(&block)
|
|
180
|
+
block = Identity unless block
|
|
181
|
+
s = self
|
|
182
|
+
until s.empty? or block[s.head] do
|
|
183
|
+
s = s.tail
|
|
184
|
+
end
|
|
185
|
+
return Empty if s.empty?
|
|
186
|
+
self.class.new(s.head) { s.tail.select(&block) }
|
|
187
|
+
end
|
|
188
|
+
alias find_all select
|
|
189
|
+
|
|
190
|
+
# obsoleted by #select
|
|
191
|
+
def filter(&p)
|
|
192
|
+
warn "method 'filter' is obsolete - use 'select'"
|
|
193
|
+
select(&p)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Creates a new Lazylist that maps the block or Proc object f to every
|
|
197
|
+
# element in the old list.
|
|
198
|
+
def map(&f)
|
|
199
|
+
return Empty if empty?
|
|
200
|
+
f = Identity unless f
|
|
201
|
+
self.class.new(f[head]) { tail.map(&f) }
|
|
202
|
+
end
|
|
203
|
+
alias collect map
|
|
204
|
+
|
|
205
|
+
# obsoleted by #map
|
|
206
|
+
def mapper(&f)
|
|
207
|
+
warn "method 'mapper' is obsolete - use 'map'"
|
|
208
|
+
map(&f)
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
include LazyList::Enumerable
|
|
212
|
+
|
|
213
|
+
# Exceptions raised by the LazyList implementation.
|
|
214
|
+
class Exception < ::Exception; end
|
|
96
215
|
|
|
97
216
|
# ReadQueue is the implementation of an read-only queue that only supports
|
|
98
217
|
# #pop and #empty? methods. It's used as a wrapper to encapsulate
|
|
99
|
-
# enumerables in
|
|
218
|
+
# enumerables in lazy lists.
|
|
100
219
|
class ReadQueue
|
|
101
220
|
# Creates an ReadQueue object from an enumerable.
|
|
102
221
|
def initialize(enumerable)
|
|
@@ -127,12 +246,7 @@ class LazyList
|
|
|
127
246
|
end
|
|
128
247
|
end
|
|
129
248
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
# Exceptions raised by the LazyList implementation.
|
|
133
|
-
class Exception < ::Exception; end
|
|
134
|
-
|
|
135
|
-
# Returns a new LazyList, unless head and tail are nil. In the latter case
|
|
249
|
+
# Returns a new lazy list, unless head and tail are nil. In the latter case
|
|
136
250
|
# LazyList::Empty is returned.
|
|
137
251
|
def self.new(head, tail = nil, &promise)
|
|
138
252
|
if head.nil? and tail.nil?
|
|
@@ -161,7 +275,7 @@ class LazyList
|
|
|
161
275
|
end
|
|
162
276
|
|
|
163
277
|
# Denotes the empty LazyList which is a guard at the end of finite
|
|
164
|
-
#
|
|
278
|
+
# lazy lists.
|
|
165
279
|
Empty = new(nil, nil)
|
|
166
280
|
|
|
167
281
|
# Returns the value of this element.
|
|
@@ -189,17 +303,20 @@ class LazyList
|
|
|
189
303
|
# Identity lambda expression, mostly used as a default.
|
|
190
304
|
Identity = lambda { |x| x }
|
|
191
305
|
|
|
192
|
-
# Returns a
|
|
193
|
-
# LazyList.span.
|
|
306
|
+
# Returns a lazy list which is generated by LazyList.from_enum or
|
|
307
|
+
# LazyList.span.
|
|
194
308
|
def self.[](a, n = nil)
|
|
195
|
-
|
|
309
|
+
case
|
|
310
|
+
when n
|
|
196
311
|
span(a, n)
|
|
312
|
+
when IO === a
|
|
313
|
+
io(a)
|
|
197
314
|
else
|
|
198
315
|
from_enum(a)
|
|
199
316
|
end
|
|
200
317
|
end
|
|
201
318
|
|
|
202
|
-
# Generates a
|
|
319
|
+
# Generates a lazy list from any data structure e which
|
|
203
320
|
# responds to the #each method.
|
|
204
321
|
def self.from_enum(e)
|
|
205
322
|
oq = ReadQueue.new(e)
|
|
@@ -214,7 +331,7 @@ class LazyList
|
|
|
214
331
|
new(oq.pop, next_top)
|
|
215
332
|
end
|
|
216
333
|
|
|
217
|
-
# Generates a finite
|
|
334
|
+
# Generates a finite lazy list beginning with element a and spanning
|
|
218
335
|
# n elements. The data structure members have to support the
|
|
219
336
|
# successor method succ.
|
|
220
337
|
def self.span(a, n)
|
|
@@ -225,7 +342,7 @@ class LazyList
|
|
|
225
342
|
end
|
|
226
343
|
end
|
|
227
344
|
|
|
228
|
-
# Generates a
|
|
345
|
+
# Generates a lazy list which tabulates every element beginning with n
|
|
229
346
|
# and succeding with succ by calling the Proc object f or the given block.
|
|
230
347
|
# If none is given the identity function is computed instead.
|
|
231
348
|
def self.tabulate(n = 0, &f)
|
|
@@ -233,16 +350,25 @@ class LazyList
|
|
|
233
350
|
new(f[n]) { tabulate(n.succ, &f) }
|
|
234
351
|
end
|
|
235
352
|
|
|
236
|
-
#
|
|
353
|
+
# Returns a list of all elements succeeding _n_ and starting from _n_.
|
|
354
|
+
def self.from(n = 0)
|
|
355
|
+
tabulate(n)
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
# Generates a lazy list which iterates over its previous values
|
|
237
359
|
# computing something like: f(i), f(f(i)), f(f(f(i))), ...
|
|
238
360
|
def self.iterate(i = 0, &f)
|
|
239
361
|
new(i) { iterate(f[i], &f) }
|
|
240
362
|
end
|
|
241
363
|
|
|
242
|
-
# Generates a
|
|
364
|
+
# Generates a lazy list of a give IO-object using a given
|
|
243
365
|
# block or Proc object to read from this object.
|
|
244
|
-
def self.io(
|
|
245
|
-
|
|
366
|
+
def self.io(input, &f)
|
|
367
|
+
if f
|
|
368
|
+
input.eof? ? Empty : new(f[input]) { io(input, &f) }
|
|
369
|
+
else
|
|
370
|
+
input.eof? ? Empty : new(input.readline) { io(input) }
|
|
371
|
+
end
|
|
246
372
|
end
|
|
247
373
|
|
|
248
374
|
# Returns the n-th LazyList-Object.
|
|
@@ -269,19 +395,18 @@ class LazyList
|
|
|
269
395
|
# returned.
|
|
270
396
|
def [](n, m = nil)
|
|
271
397
|
if n.is_a? Range
|
|
272
|
-
n
|
|
398
|
+
LazyList[take_range(n)]
|
|
273
399
|
elsif n < 0
|
|
274
400
|
nil
|
|
275
401
|
elsif m
|
|
276
|
-
|
|
277
|
-
s ? s.take(m) : nil
|
|
402
|
+
LazyList[take_span(n, m)]
|
|
278
403
|
else
|
|
279
404
|
ref(n).head rescue nil
|
|
280
405
|
end
|
|
281
406
|
end
|
|
282
407
|
|
|
283
408
|
# Iterates over all elements. If n is given only n iterations are done.
|
|
284
|
-
# If self is a finite
|
|
409
|
+
# If self is a finite lazy list each returns also if there are no more
|
|
285
410
|
# elements to iterate over.
|
|
286
411
|
def each(n = nil)
|
|
287
412
|
s = self
|
|
@@ -306,30 +431,11 @@ class LazyList
|
|
|
306
431
|
self
|
|
307
432
|
end
|
|
308
433
|
|
|
309
|
-
#
|
|
310
|
-
|
|
311
|
-
p = Identity unless p
|
|
312
|
-
s = self
|
|
313
|
-
until s.empty? or p[s.head] do
|
|
314
|
-
s = s.tail
|
|
315
|
-
end
|
|
316
|
-
return Empty if s.empty?
|
|
317
|
-
self.class.new(s.head) { s.tail.filter(&p) }
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
# Creates a new Lazylist that maps the block or Proc object f to every
|
|
321
|
-
# element in the old list.
|
|
322
|
-
def mapper(&f)
|
|
323
|
-
return Empty if empty?
|
|
324
|
-
f = Identity unless f
|
|
325
|
-
self.class.new(f[head]) { tail.mapper(&f) }
|
|
326
|
-
end
|
|
327
|
-
|
|
328
|
-
# Merges this LazyList with the other. It uses the &compare block to decide
|
|
329
|
-
# which elements to place first in the result LazyList. If no compare block
|
|
434
|
+
# Merges this lazy list with the other. It uses the &compare block to decide
|
|
435
|
+
# which elements to place first in the result lazy list. If no compare block
|
|
330
436
|
# is given lambda { |a,b| a < b } is used as a default value.
|
|
331
437
|
def merge(other, &compare)
|
|
332
|
-
compare = lambda { |a,b| a < b } unless compare
|
|
438
|
+
compare = lambda { |a, b| a < b } unless compare
|
|
333
439
|
return other if empty?
|
|
334
440
|
return self if other.empty?
|
|
335
441
|
if compare[head, other.head]
|
|
@@ -341,17 +447,6 @@ class LazyList
|
|
|
341
447
|
end
|
|
342
448
|
end
|
|
343
449
|
|
|
344
|
-
# Combines this LazyList with the other to a new LazyList.
|
|
345
|
-
# The elements of the resulting lists are computed
|
|
346
|
-
# by calling the binary operator block on the two elemnts
|
|
347
|
-
# of the input lists. E.g. lambda { |x, y| x + y } adds
|
|
348
|
-
# the two input lists.
|
|
349
|
-
def combine(other, &operator)
|
|
350
|
-
self.class.new(operator.call(head, other.head)) do
|
|
351
|
-
tail.combine(other.tail, &operator)
|
|
352
|
-
end
|
|
353
|
-
end
|
|
354
|
-
|
|
355
450
|
# Takes the next n elements and returns them as an array.
|
|
356
451
|
def take(n = 1)
|
|
357
452
|
result = []
|
|
@@ -359,27 +454,40 @@ class LazyList
|
|
|
359
454
|
result
|
|
360
455
|
end
|
|
361
456
|
|
|
457
|
+
# Takes the _range_ indexes of elements from this lazylist and returns them
|
|
458
|
+
# as an array.
|
|
459
|
+
def take_range(range)
|
|
460
|
+
range.map { |i| ref(i).head }
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
# Takes the m elements starting at index n of this lazy list and returns them
|
|
464
|
+
# as an array.
|
|
465
|
+
def take_span(n, m)
|
|
466
|
+
s = ref(n)
|
|
467
|
+
s ? s.take(m) : nil
|
|
468
|
+
end
|
|
469
|
+
|
|
362
470
|
# Takes the next n elements and returns them as an array. It destroys these
|
|
363
|
-
# elements in
|
|
471
|
+
# elements in this lazy list. Also see #each! .
|
|
364
472
|
def take!(n = 1)
|
|
365
473
|
result = []
|
|
366
474
|
each!(n) { |x| result << x }
|
|
367
475
|
result
|
|
368
476
|
end
|
|
369
477
|
|
|
370
|
-
# Drops the next n elements and returns the rest of this
|
|
478
|
+
# Drops the next n elements and returns the rest of this lazy list. n
|
|
371
479
|
# defaults to 1.
|
|
372
480
|
def drop(n = 1)
|
|
373
481
|
each(n) { }
|
|
374
482
|
end
|
|
375
483
|
|
|
376
|
-
# Drops the next n elements, destroys them in the
|
|
377
|
-
# returns the rest of this
|
|
484
|
+
# Drops the next n elements, destroys them in the lazy list and
|
|
485
|
+
# returns the rest of this lazy list. Also see #each! .
|
|
378
486
|
def drop!(n = 1)
|
|
379
487
|
each!(n) { }
|
|
380
488
|
end
|
|
381
489
|
|
|
382
|
-
# Returns the size. This is only sensible if the
|
|
490
|
+
# Returns the size. This is only sensible if the lazy list is finite
|
|
383
491
|
# of course.
|
|
384
492
|
def size
|
|
385
493
|
inject(0) { |s,| s += 1 }
|
|
@@ -387,7 +495,7 @@ class LazyList
|
|
|
387
495
|
|
|
388
496
|
alias length size
|
|
389
497
|
|
|
390
|
-
# Returns true if this is the empty
|
|
498
|
+
# Returns true if this is the empty lazy list.
|
|
391
499
|
def empty?
|
|
392
500
|
self.equal? Empty
|
|
393
501
|
end
|
|
@@ -398,7 +506,7 @@ class LazyList
|
|
|
398
506
|
def eql?(other)
|
|
399
507
|
other.is_a? self.class or return false
|
|
400
508
|
size == other.size or return false
|
|
401
|
-
zip(other) { |x,y| x == y or return false }
|
|
509
|
+
to_a.zip(other.to_a) { |x, y| x == y or return false }
|
|
402
510
|
true
|
|
403
511
|
end
|
|
404
512
|
alias == eql?
|
|
@@ -439,19 +547,28 @@ module Kernel
|
|
|
439
547
|
# cannot be described well with LazyList#iterate or LazyList#tabulate.
|
|
440
548
|
#
|
|
441
549
|
# - list without any arguments, returns the empty lazy list LazyList::Empty.
|
|
442
|
-
# - list(x) returns the lazy list with only the element x as a member
|
|
443
|
-
#
|
|
444
|
-
#
|
|
445
|
-
#
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
550
|
+
# - list(x) returns the lazy list with only the element x as a member,
|
|
551
|
+
# list(x,y) returns the lazy list with only the elements x and y as a
|
|
552
|
+
# members, and so on.
|
|
553
|
+
# - list(x) { xs } returns the lazy list with the element x as a head
|
|
554
|
+
# element, and that is continued with the lazy list xs as tail. To define an
|
|
555
|
+
# infinite lazy list of 1s you can do:
|
|
556
|
+
# ones = list(1) { ones } # => [1,... ]
|
|
557
|
+
# To define all even numbers directly, you can do:
|
|
558
|
+
# def even(n = 0) list(n) { even(n + 2) } end
|
|
559
|
+
# and then:
|
|
560
|
+
# e = even # => [0,... ]
|
|
561
|
+
def list(*values, &promise)
|
|
562
|
+
result = LazyList::Empty
|
|
563
|
+
last = nil
|
|
564
|
+
values.reverse_each do |v|
|
|
565
|
+
result = LazyList.new(v, result)
|
|
566
|
+
last ||= result
|
|
567
|
+
end
|
|
568
|
+
if last and block_given?
|
|
569
|
+
last.instance_variable_set :@tail, promise
|
|
453
570
|
end
|
|
571
|
+
result
|
|
454
572
|
end
|
|
455
573
|
end
|
|
456
|
-
|
|
457
574
|
# vim: set et sw=2 ts=2:
|
data/make_doc.rb
CHANGED
data/tests/runner.rb
CHANGED
|
@@ -5,13 +5,18 @@ require 'test/unit/testsuite'
|
|
|
5
5
|
$:.unshift File.expand_path(File.dirname($0))
|
|
6
6
|
$:.unshift 'lib'
|
|
7
7
|
$:.unshift '../lib'
|
|
8
|
-
|
|
8
|
+
begin
|
|
9
|
+
#require 'coverage'
|
|
10
|
+
rescue LoadError
|
|
11
|
+
end
|
|
9
12
|
require 'test'
|
|
13
|
+
require 'test_enumerable'
|
|
10
14
|
|
|
11
15
|
class TS_AllTests
|
|
12
16
|
def self.suite
|
|
13
|
-
suite = Test::Unit::TestSuite.new
|
|
17
|
+
suite = Test::Unit::TestSuite.new 'All tests'
|
|
14
18
|
suite << TC_LazyList.suite
|
|
19
|
+
suite << TC_LazyEnumerable.suite
|
|
15
20
|
end
|
|
16
21
|
end
|
|
17
22
|
Test::Unit::UI::Console::TestRunner.run(TS_AllTests)
|
data/tests/test.rb
CHANGED
|
@@ -22,10 +22,10 @@ class TC_LazyList < Test::Unit::TestCase
|
|
|
22
22
|
@natural = LazyList.tabulate(1)
|
|
23
23
|
@ones = LazyList.iterate(1) { 1 }
|
|
24
24
|
@oddp = lambda { |x| x % 2 == 1 }
|
|
25
|
-
@odd = @natural.
|
|
25
|
+
@odd = @natural.select(&@oddp)
|
|
26
26
|
@evenp = lambda { |x| x % 2 == 0 }
|
|
27
|
-
@boolean = @natural.
|
|
28
|
-
@even = @natural.
|
|
27
|
+
@boolean = @natural.map { |x| x % 2 == 0}
|
|
28
|
+
@even = @natural.select(&@evenp)
|
|
29
29
|
@finite_inner0 = MyEnum.new(0)
|
|
30
30
|
@finite0 = LazyList[@finite_inner0]
|
|
31
31
|
@finite_inner1 = MyEnum.new(1)
|
|
@@ -78,39 +78,39 @@ class TC_LazyList < Test::Unit::TestCase
|
|
|
78
78
|
assert_equal(10, @finite_span.length)
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
-
def
|
|
81
|
+
def test_select
|
|
82
82
|
assert_equal(1, @odd[0])
|
|
83
83
|
assert_equal(3, @odd[1])
|
|
84
84
|
assert_equal(5, @odd[2])
|
|
85
|
-
assert_equal((1..19).select(&@oddp), @odd[0, 10])
|
|
86
|
-
assert_equal((1..10).to_a, @natural[0, 10])
|
|
85
|
+
assert_equal((1..19).select(&@oddp), @odd[0, 10].to_a)
|
|
86
|
+
assert_equal((1..10).to_a, @natural[0, 10].to_a)
|
|
87
87
|
assert_equal([ 1 ] * 10, @ones[0, 10].to_a)
|
|
88
|
-
ends_with_a = @strings.
|
|
89
|
-
assert(ends_with_a[0, 27] ==
|
|
88
|
+
ends_with_a = @strings.select { |x| x[-1] == ?a }
|
|
89
|
+
assert(ends_with_a[0, 27].to_a ==
|
|
90
90
|
[ "a", ("a".."z").map { |x| x + "a" } ].flatten)
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
def test_map
|
|
94
|
-
id = @natural.
|
|
94
|
+
id = @natural.map
|
|
95
95
|
assert_equal(1, id[0])
|
|
96
96
|
assert_equal(2, id[1])
|
|
97
97
|
assert_equal(3, id[2])
|
|
98
|
-
assert_equal((1..10).to_a, id[0, 10])
|
|
99
|
-
assert_equal((1..10).to_a, @natural[0, 10])
|
|
98
|
+
assert_equal((1..10).to_a, id[0, 10].to_a)
|
|
99
|
+
assert_equal((1..10).to_a, @natural[0, 10].to_a)
|
|
100
100
|
squaredf = lambda { |x| x ** 2 }
|
|
101
|
-
squared = @natural.
|
|
101
|
+
squared = @natural.map(&squaredf)
|
|
102
102
|
assert_equal(1, squared[0])
|
|
103
103
|
assert_equal(4, squared[1])
|
|
104
104
|
assert_equal(9, squared[2])
|
|
105
|
-
assert_equal((1..10).map(&squaredf), squared[0, 10])
|
|
106
|
-
assert_equal((1..10).to_a, @natural[0, 10])
|
|
105
|
+
assert_equal((1..10).map(&squaredf), squared[0, 10].to_a)
|
|
106
|
+
assert_equal((1..10).to_a, @natural[0, 10].to_a)
|
|
107
107
|
strangef = lambda { |x| x * (x[0] - ?a + 1) }
|
|
108
|
-
strange = @strings.
|
|
108
|
+
strange = @strings.map(&strangef)
|
|
109
109
|
assert_equal("a", strange[0])
|
|
110
110
|
assert_equal("bb", strange[1])
|
|
111
111
|
assert_equal("ccc", strange[2])
|
|
112
|
-
assert_equal(("a".."z").map(&strangef), strange[0, 26])
|
|
113
|
-
assert_equal(("a".."z").to_a, @strings[0, 26])
|
|
112
|
+
assert_equal(("a".."z").map(&strangef), strange[0, 26].to_a)
|
|
113
|
+
assert_equal(("a".."z").to_a, @strings[0, 26].to_a)
|
|
114
114
|
end
|
|
115
115
|
|
|
116
116
|
def test_index
|
|
@@ -118,7 +118,8 @@ class TC_LazyList < Test::Unit::TestCase
|
|
|
118
118
|
assert_equal(1, @natural[0])
|
|
119
119
|
assert_equal(2, @natural[1])
|
|
120
120
|
assert_equal(nil, @natural[-1, 10])
|
|
121
|
-
assert_equal(
|
|
121
|
+
assert_equal(LazyList[1..10], @natural[0, 10])
|
|
122
|
+
=begin
|
|
122
123
|
assert_equal((6..15).to_a, @natural[5, 10])
|
|
123
124
|
assert_equal((1..1).to_a, @natural[0..0])
|
|
124
125
|
assert_equal((1..0).to_a, @natural[0..-1])
|
|
@@ -128,6 +129,7 @@ class TC_LazyList < Test::Unit::TestCase
|
|
|
128
129
|
assert_equal((6..15).to_a, @natural[5..14])
|
|
129
130
|
assert_equal((1..10).to_a, @natural[0...10])
|
|
130
131
|
assert_equal((6..15).to_a, @natural[5...15])
|
|
132
|
+
=end
|
|
131
133
|
end
|
|
132
134
|
|
|
133
135
|
def test_merge
|
|
@@ -137,7 +139,7 @@ class TC_LazyList < Test::Unit::TestCase
|
|
|
137
139
|
assert_equal(@natural[0, 10].to_a, natural[0, 10].to_a)
|
|
138
140
|
double_list = @natural.merge(@natural) { |a,b| a <= b }
|
|
139
141
|
assert(double_list[0, 10].to_a, (1..5).map { |x| [x, x] }.flatten)
|
|
140
|
-
odd2 = @natural.
|
|
142
|
+
odd2 = @natural.select(&@oddp).drop(1)
|
|
141
143
|
some = @even.merge(odd2)
|
|
142
144
|
assert_equal(@natural[1, 9].to_a, some[0, 9].to_a)
|
|
143
145
|
end
|
|
@@ -207,7 +209,7 @@ class TC_LazyList < Test::Unit::TestCase
|
|
|
207
209
|
assert_equal(nil, f[-1])
|
|
208
210
|
assert_equal(5, f[0])
|
|
209
211
|
assert_equal(26, f[1])
|
|
210
|
-
assert_equal([5, 26, 13, 66, 33, 166, 83, 416], f[0, 8])
|
|
212
|
+
assert_equal([5, 26, 13, 66, 33, 166, 83, 416], f[0, 8].to_a)
|
|
211
213
|
end
|
|
212
214
|
|
|
213
215
|
def test_inpsect
|
|
@@ -224,10 +226,10 @@ class TC_LazyList < Test::Unit::TestCase
|
|
|
224
226
|
assert_equal("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]", l.inspect)
|
|
225
227
|
end
|
|
226
228
|
|
|
227
|
-
def
|
|
228
|
-
combined = @natural.
|
|
229
|
-
assert_equal (12..21).to_a, combined[10,10]
|
|
230
|
-
assert_equal (2..11).to_a, combined[0,10]
|
|
229
|
+
def test_zip
|
|
230
|
+
combined = @natural.zip(@ones) { |x, y| x + y }
|
|
231
|
+
assert_equal (12..21).to_a, combined[10,10].to_a
|
|
232
|
+
assert_equal (2..11).to_a, combined[0,10].to_a
|
|
231
233
|
end
|
|
232
234
|
|
|
233
235
|
def from(n = 0)
|
|
@@ -235,7 +237,7 @@ class TC_LazyList < Test::Unit::TestCase
|
|
|
235
237
|
end
|
|
236
238
|
|
|
237
239
|
def odd
|
|
238
|
-
from(1).
|
|
240
|
+
from(1).select { |x| x % 2 == 1 }
|
|
239
241
|
end
|
|
240
242
|
|
|
241
243
|
def test_list
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'test/unit'
|
|
4
|
+
require 'lazylist'
|
|
5
|
+
|
|
6
|
+
class TC_LazyEnumerable < Test::Unit::TestCase
|
|
7
|
+
def setup
|
|
8
|
+
@a = LazyList[%w[abc cdef aab efgh eefgh hijkl]]
|
|
9
|
+
@o = list(1) { @o }
|
|
10
|
+
@n = LazyList.from(1)
|
|
11
|
+
@l = LazyList[1..5]
|
|
12
|
+
@m = LazyList[5..8]
|
|
13
|
+
@r = [5, -2, 3, 13, 0]
|
|
14
|
+
@s = LazyList[@r]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_zip
|
|
18
|
+
l = @l.zip @m
|
|
19
|
+
assert_kind_of LazyList, l
|
|
20
|
+
assert_equal [[1, 5], [2, 6], [3, 7], [4, 8]], l.to_a
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_zip_and_block
|
|
24
|
+
l = @l.zip(@m) { |x,y| x + y }
|
|
25
|
+
assert_kind_of LazyList, l
|
|
26
|
+
assert_equal [6, 8, 10, 12], l.to_a
|
|
27
|
+
zipped = @n.zip(@o) { |x, y| x + y }
|
|
28
|
+
assert_kind_of LazyList, zipped
|
|
29
|
+
assert_equal (12..21).to_a, zipped[10,10].to_a
|
|
30
|
+
assert_equal (2..11).to_a, zipped[0,10].to_a
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_zip_multiple_and_block
|
|
34
|
+
zipped = @n.zip(@o, @m) { |x, y, z| x * z + y }
|
|
35
|
+
assert_kind_of LazyList, zipped
|
|
36
|
+
assert_equal [6, 13, 22, 33], zipped.to_a
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def test_each_with_index
|
|
40
|
+
l = @m.each_with_index
|
|
41
|
+
assert_kind_of LazyList, l
|
|
42
|
+
assert_equal l.to_a, [ [5, 0], [6, 1], [7, 2], [8, 3] ]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_each_with_index_and_block
|
|
46
|
+
@m.each_with_index { |x, i| assert_equal x - 5, i }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def test_sort
|
|
50
|
+
l = @s.sort
|
|
51
|
+
assert_kind_of LazyList, l
|
|
52
|
+
assert @r.sort, l
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def test_sort_by
|
|
56
|
+
l = @s.sort_by { |x| -x }
|
|
57
|
+
assert_kind_of LazyList, l
|
|
58
|
+
assert @r.sort_by { |x| -x }, l
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_grep
|
|
62
|
+
l = @a.grep /e/
|
|
63
|
+
assert_kind_of LazyList, l
|
|
64
|
+
assert_equal %w[cdef efgh eefgh], l.to_a
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def test_grep_with_block
|
|
68
|
+
expected = %w[cdef efgh eefgh]
|
|
69
|
+
i = 0
|
|
70
|
+
l = @a.grep(/e/) do |x|
|
|
71
|
+
assert_equal expected[i], x
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def test_reject
|
|
76
|
+
odd = @n.select { |x| x % 2 == 1 }
|
|
77
|
+
assert_kind_of LazyList, odd
|
|
78
|
+
assert_equal [1, 3, 5, 7, 9], odd.take(5)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def test_reject
|
|
82
|
+
odd = @n.reject { |x| x % 2 == 0 }
|
|
83
|
+
assert_kind_of LazyList, odd
|
|
84
|
+
assert_equal [1, 3, 5, 7, 9], odd.take(5)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_partition
|
|
88
|
+
even, odd = @l.partition { |x| x % 2 == 0 }
|
|
89
|
+
assert_kind_of LazyList, odd
|
|
90
|
+
assert_kind_of LazyList, even
|
|
91
|
+
assert_equal [2, 4], even.take(5)
|
|
92
|
+
assert_equal [1, 3, 5], odd.take(5)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
# vim: set et sw=2 ts=2:
|
metadata
CHANGED
|
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
|
|
|
3
3
|
specification_version: 1
|
|
4
4
|
name: lazylist
|
|
5
5
|
version: !ruby/object:Gem::Version
|
|
6
|
-
version: 0.
|
|
7
|
-
date: 2005-10-
|
|
6
|
+
version: 0.2.0
|
|
7
|
+
date: 2005-10-08 00:00:00 +02:00
|
|
8
8
|
summary: Implementation of lazy lists for Ruby
|
|
9
9
|
require_paths:
|
|
10
10
|
- lib
|
|
@@ -32,6 +32,7 @@ files:
|
|
|
32
32
|
- examples
|
|
33
33
|
- install.rb
|
|
34
34
|
- GPL
|
|
35
|
+
- TODO
|
|
35
36
|
- Rakefile
|
|
36
37
|
- VERSION
|
|
37
38
|
- tests
|
|
@@ -44,6 +45,7 @@ files:
|
|
|
44
45
|
- examples/pi.rb
|
|
45
46
|
- examples/examples.rb
|
|
46
47
|
- tests/test.rb
|
|
48
|
+
- tests/test_enumerable.rb
|
|
47
49
|
- tests/runner.rb
|
|
48
50
|
- lib/lazylist.rb
|
|
49
51
|
test_files:
|