lazylist 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,7 +1,27 @@
1
+ 2007-04-03 * 0.3.0 * Major work on the implementation:
2
+ - Supports list comprehensions for infinite list like
3
+ Haskell does by offering a simple DSL.
4
+ - Added cartesian products to LazyList.
5
+ Many thanks to Reginald Braithwaite <reg@braythwayt.com>
6
+ for his interesting ideas & email discussion about this
7
+ topic. Be sure to check out his blog under
8
+ http://weblog.raganwald.com/, where he writes about all
9
+ kinds of other interesting stuff.
10
+ * A nice side effect of the changes necessary for the
11
+ new implementation is that the first value of a list is
12
+ not forced until requested. This also causes a bit of
13
+ space&time waste, but hardware is getting better and
14
+ cheaper, isn't it?
15
+ * Now there is more than one Empty object, be careful not to
16
+ use equal? to compare them. It's better to use LazyList#empty?
17
+ for this.
18
+ * Switched from the continuation based ReadQueue to a Thread
19
+ based one, that should be a bit faster.
20
+ * Better organization of the file structure.
21
+ * Added version information.
1
22
  2005-12-23 * 0.2.2 * Added append and + (the binary append case) to append lazy
2
23
  lists to each other.
3
- 2005-12-05 * 0.2.1 * Fixed a documentation bug, reported by Gary Wright
4
- <at2002@mac.com>.
24
+ 2005-12-05 * 0.2.1 * Fixed a documentation bug, reported by Gary Wright <at2002@mac.com>.
5
25
  * enable/disable index reference cache added.
6
26
  * LazyList#sublist added to generate sublists that
7
27
  share elements with their "super" lists.
data/Rakefile CHANGED
@@ -5,15 +5,16 @@ include Config
5
5
 
6
6
  PKG_NAME = 'lazylist'
7
7
  PKG_VERSION = File.read('VERSION').chomp
8
- PKG_FILES = FileList['**/*'].exclude(/^doc/).exclude(/^CVS/).exclude(/^pkg/)
9
-
10
- task :default => :test
8
+ PKG_FILES = FileList['**/*'].exclude(/^(doc|CVS|pkg|coverage)/)
11
9
 
12
10
  desc "Run unit tests"
13
11
  task :test do
14
- cd 'tests' do
15
- ruby %{-I../ext runner.rb}
16
- end
12
+ ruby %{-Ilib tests/runner.rb}
13
+ end
14
+
15
+ desc "Testing library with coverage"
16
+ task :coverage do
17
+ sh 'rcov -x tests -Ilib tests/runner.rb'
17
18
  end
18
19
 
19
20
  desc "Installing library"
@@ -33,45 +34,25 @@ task :clean do
33
34
  end
34
35
 
35
36
  spec = Gem::Specification.new do |s|
36
- #### Basic information.
37
-
38
37
  s.name = 'lazylist'
39
38
  s.version = PKG_VERSION
40
39
  s.summary = "Implementation of lazy lists for Ruby"
41
40
  s.description = ""
42
41
 
43
- #### Dependencies and requirements.
44
-
45
- #s.add_dependency('log4r', '> 1.0.4')
46
- #s.requirements << ""
42
+ s.add_dependency('dslkit', '>= 0.2.2')
47
43
 
48
44
  s.files = PKG_FILES
49
45
 
50
- #### C code extensions.
51
-
52
- #s.extensions << "ext/extconf.rb"
53
-
54
- #### Load-time details: library and application (you will need one or both).
55
-
56
- s.require_path = 'lib' # Use these for libraries.
57
- s.autorequire = 'lazylist'
58
-
59
- #s.bindir = "bin" # Use these for applications.
60
- #s.executables = ["bla.rb"]
61
- #s.default_executable = "bla.rb"
62
-
63
- #### Documentation and testing.
46
+ s.require_path = 'lib'
64
47
 
65
48
  s.has_rdoc = true
66
- s.extra_rdoc_files = [ 'lib/lazylist.rb' ]
49
+ s.extra_rdoc_files = Dir['lib/**/*.rb']
67
50
  s.rdoc_options <<
68
51
  '--title' << 'LazyList -- Infinite lists in Ruby' <<
69
52
  '--main' << 'LazyList' <<
70
53
  '--line-numbers'
71
54
  s.test_files << 'tests/test.rb'
72
55
 
73
- #### Author and project details.
74
-
75
56
  s.author = "Florian Frank"
76
57
  s.email = "flori@ping.de"
77
58
  s.homepage = "http://lazylist.rubyforge.org"
@@ -83,5 +64,24 @@ Rake::GemPackageTask.new(spec) do |pkg|
83
64
  pkg.package_files += PKG_FILES
84
65
  end
85
66
 
86
- task :release => [ :clean, :package ]
67
+ desc m = "Writing version information for #{PKG_VERSION}"
68
+ task :version do
69
+ puts m
70
+ File.open(File.join('lib', 'lazylist', 'version.rb'), 'w') do |v|
71
+ v.puts <<EOT
72
+ class LazyList
73
+ # LazyList version
74
+ VERSION = '#{PKG_VERSION}'
75
+ VERSION_ARRAY = VERSION.split(/\\./).map { |x| x.to_i } # :nodoc:
76
+ VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
77
+ VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
78
+ VERSION_BUILD = VERSION_ARRAY[2] # :nodoc:
79
+ end
80
+ EOT
81
+ end
82
+ end
83
+
84
+ task :default => [ :version, :test ]
85
+
86
+ task :release => [ :version, :clean, :package ]
87
87
  # vim: set et sw=2 ts=2:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.2
1
+ 0.3.0
@@ -45,8 +45,22 @@ fib.each(10) { |x| print x, " " } ; puts
45
45
  p fib[100]
46
46
  puts
47
47
 
48
+ fib = nil
49
+ puts "Fibonacci lazy list with zip"
50
+ fib = list(1, 1) { fib.zip(fib.drop) { |a, b| a + b } }
51
+ fib.each(10) { |x| print x, " " } ; puts
52
+ p fib[100]
53
+ puts
54
+
55
+ fib = nil
56
+ puts "Fibonacci lazy list with a list and a build call"
57
+ fib = list(1, 1) { build { a + b }.where(:a => fib, :b => fib.drop) }
58
+ fib.each(10) { |x| print x, " " } ; puts
59
+ p fib[100]
60
+ puts
61
+
48
62
  puts "Sum up odd numbers lazylist to get a squares stream"
49
- odd = LazyList.tabulate(1) { |x| 2 * x - 1 }
63
+ odd = LazyList[1..Infinity].select { |x| x % 2 == 1 }
50
64
  puts odd
51
65
  squares = LazyList.tabulate(0) do |x|
52
66
  (0..x).inject(0) { |s, i| s + odd[i] }
@@ -25,7 +25,7 @@ if $0 == __FILE__
25
25
  puts "Sum of the first 1000 digitis of pi: #{sum}"
26
26
  puts "500th digit using memoized computation: #{PI[499]}"
27
27
 
28
- puts "Printing #{max ? "the first #{max}" : "all the "} digits of pi:"
28
+ puts "Printing #{max ? "the first #{max}" : "all the "} digits of pi:" # vim-uff: "
29
29
  PI.each!(max) do |x|
30
30
  STDOUT.print x
31
31
  STDOUT.flush
data/install.rb CHANGED
@@ -6,6 +6,9 @@ require 'fileutils'
6
6
  include FileUtils::Verbose
7
7
 
8
8
  libdir = CONFIG["sitelibdir"]
9
- file = "lib/lazylist.rb"
10
- install(file, libdir)
9
+ install("lib/lazylist.rb", libdir)
10
+ mkdir_p subdir = File.join(libdir, 'lazylist')
11
+ for f in Dir['lib/lazylist/*.rb']
12
+ install(f, subdir)
13
+ end
11
14
  # vim: set et sw=2 ts=2:
@@ -35,6 +35,10 @@
35
35
  #
36
36
  # sq = LazyList.tabulate(1) { |x| x * x }
37
37
  #
38
+ # or in the much nicer list builder syntax:
39
+ #
40
+ # sq = list { x * x }.where :x => 1..Infinity
41
+ #
38
42
  # Now it's possible to get the first 10 square numbers by calling
39
43
  # LazyList#take
40
44
  #
@@ -77,6 +81,14 @@
77
81
  # to compute this result. That's a very transparent way to get memoization for
78
82
  # sequences that require heavy computation.
79
83
  #
84
+ # You can also use the zip method to create fib:
85
+ #
86
+ # fib = list(1, 1) { fib.zip(fib.drop) { |a, b| a + b } }
87
+ #
88
+ # Another way to create the Fibonacci sequence with the build method is this:
89
+ #
90
+ # fib = list(1, 1) { build { a + b }.where(:a => fib, :b => fib.drop(1)) }
91
+ #
80
92
  # You can create lazy lists that are based on arbitrary Enumerables, so can for
81
93
  # example wrap your passwd file in one pretty easily:
82
94
  #
@@ -90,6 +102,18 @@
90
102
  # pw.find { |x| x =~ /^root:/ } => "root:x:0:0:root:/root:/bin/bash\n"
91
103
  # instead, only every line until the root line is loaded into the memory.
92
104
  #
105
+ # To create more complex lazy lists, you can build them from already existing
106
+ # lazy lists.
107
+ #
108
+ # Natural numbers:
109
+ # naturals = LazyList.from(1)
110
+ #
111
+ # Odd Numbers > 100:
112
+ # odds = list { x }.where(:x => naturals) { x % 2 == 1 && x > 100 }
113
+ #
114
+ # Alternative definition of square numbers:
115
+ # squares = build { odds[0, x].inject(0) { |s, y| s + y } }.where :x => naturals
116
+ #
93
117
  # == References
94
118
  #
95
119
  # A very good introduction into lazy lists can be found in the scheme bible
@@ -97,181 +121,173 @@
97
121
  # [http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-24.html#%25_sec_3.5]
98
122
  #
99
123
  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
- return select(&block), reject(&block)
108
- end
124
+ require 'dslkit'
125
+ require 'lazylist/list_builder'
126
+ require 'lazylist/version'
109
127
 
128
+ require 'lazylist/enumerable'
129
+ include LazyList::Enumerable
110
130
 
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
131
+ # Exceptions raised by the LazyList implementation.
132
+ class Exception < ::StandardError; end
124
133
 
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
+ # ReadQueue is the implementation of an read-only queue that only supports
135
+ # #shift and #empty? methods. It's used as a wrapper to encapsulate
136
+ # enumerables in lazy lists.
137
+ class ReadQueue
138
+ # Creates an ReadQueue object from an enumerable.
139
+ def initialize(enumerable)
140
+ @data = []
141
+ @producer = Thread.new do
142
+ Thread.stop
143
+ begin
144
+ enumerable.each do |value|
145
+ old, Thread.critical = Thread.critical, true
146
+ begin
147
+ @data << value
148
+ @consumer.wakeup
149
+ Thread.stop
150
+ ensure
151
+ Thread.critical = old
152
+ end
153
+ end
154
+ rescue => e
155
+ @consumer.raise e
156
+ ensure
157
+ @consumer.wakeup
158
+ end
134
159
  end
160
+ Thread.pass until @producer.stop?
135
161
  end
136
162
 
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
163
+ # Extracts the top element from the queue or nil if the queue is
164
+ # empty.
165
+ def shift
166
+ if empty?
167
+ nil
147
168
  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
169
+ @data.shift
152
170
  end
153
171
  end
154
172
 
155
- # obsoleted by #zip
156
- def combine(other, &operator)
157
- warn "method 'combine' is obsolete - use 'zip'"
158
- zip(other, &operator)
159
- end
173
+ alias pop shift # for backwards compatibility
160
174
 
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
175
+ # Returns true if the queue is empty.
176
+ def empty?
177
+ if @data.empty?
178
+ old, Thread.critical = Thread.critical, true
179
+ begin
180
+ @consumer = Thread.current
181
+ @producer.wakeup
182
+ Thread.stop
183
+ rescue ThreadError
184
+ ;
185
+ ensure
186
+ @consumer = nil
187
+ Thread.critical = old
188
+ end
189
+ end
190
+ @data.empty?
169
191
  end
192
+ end
170
193
 
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) }
194
+ Promise = DSLKit::BlankSlate.with :inspect, :to_s # :nodoc:
195
+ # A promise that can be evaluated on demand (if forced).
196
+ class Promise
197
+ def initialize(&block)
198
+ @block = block
175
199
  end
176
200
 
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
201
+ # Return the value of this Promise.
202
+ def __value__
203
+ case v = @block.call
204
+ when Promise
205
+ v.__value__
206
+ else
207
+ v
184
208
  end
185
- return Empty if s.empty?
186
- self.class.new(s.head) { s.tail.select(&block) }
187
209
  end
188
- alias find_all select
189
210
 
190
- # obsoleted by #select
191
- def filter(&p)
192
- warn "method 'filter' is obsolete - use 'select'"
193
- select(&p)
211
+ # Redirect all missing methods to the value of this Promise.
212
+ def method_missing( *args, &block )
213
+ __value__.__send__( *args, &block )
194
214
  end
215
+ end
195
216
 
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) }
217
+ # A promise that can be evaluated on demand (if forced), that caches its
218
+ # value.
219
+ class MemoPromise < Promise
220
+ # Return the value of this Promise and memoize it for later access.
221
+ def __value__
222
+ @value ||= super
202
223
  end
203
- alias collect map
224
+ end
204
225
 
205
- # obsoleted by #map
206
- def mapper(&f)
207
- warn "method 'mapper' is obsolete - use 'map'"
208
- map(&f)
226
+ # This module contains module functions, that are added to LazyList and it's
227
+ # instances.
228
+ module ModuleFunctions
229
+ # Delay the value of _block_ to a Promise.
230
+ def delay(&block)
231
+ MemoPromise.new(&block)
209
232
  end
210
- end
211
- include LazyList::Enumerable
212
233
 
213
- # Exceptions raised by the LazyList implementation.
214
- class Exception < ::Exception; end
215
-
216
- # ReadQueue is the implementation of an read-only queue that only supports
217
- # #pop and #empty? methods. It's used as a wrapper to encapsulate
218
- # enumerables in lazy lists.
219
- class ReadQueue
220
- # Creates an ReadQueue object from an enumerable.
221
- def initialize(enumerable)
222
- @enumerable = enumerable
223
- @break = proc {}
224
- @enumerable.each do |x|
225
- @current = x
226
- callcc do |@continue|
227
- @break.call
228
- return
229
- end
234
+ # Force the delayed _obj_ to evaluate to its value.
235
+ def force(obj)
236
+ case obj
237
+ when Promise
238
+ force obj.__value__
239
+ else
240
+ obj
230
241
  end
231
- @continue = false
232
- @break.call
233
242
  end
234
243
 
235
- # Extracts the top element from the queue or nil if the queue is
236
- # empty.
237
- def pop
238
- top = @current
239
- callcc { |@break| @continue.call } unless empty?
240
- top
244
+ # Returns a LazyList, that consists of the mixed elements of the lists
245
+ # _lists without any special order.
246
+ def mix(*lists)
247
+ return empty if lists.empty?
248
+ first = lists.shift
249
+ if lists.empty?
250
+ first
251
+ elsif first.empty?
252
+ mix(*lists)
253
+ else
254
+ LazyList.new(first.head) do
255
+ t = first.tail
256
+ lists = lists.push delay { t } unless t.empty?
257
+ mix(*lists)
258
+ end
259
+ end
241
260
  end
261
+ end
262
+ extend ModuleFunctions
263
+ include ModuleFunctions
242
264
 
243
- # Returns true if the queue is empty.
244
- def empty?
245
- @continue == false
246
- end
265
+ # Returns an empty list.
266
+ def self.empty
267
+ new(nil, nil)
247
268
  end
248
269
 
249
- # Returns a new lazy list, unless head and tail are nil. In the latter case
250
- # LazyList::Empty is returned.
251
- def self.new(head, tail = nil, &promise)
252
- if head.nil? and tail.nil?
253
- if LazyList.const_defined?(:Empty)
254
- Empty
255
- else
256
- super
257
- end
258
- else
259
- super
260
- end
270
+ # Returns an empty list.
271
+ def empty
272
+ @klass ||= self.class
273
+ @klass.new(nil, nil)
261
274
  end
262
275
 
263
276
  # Creates a new LazyList element. The tail can be given either as
264
277
  # second argument or as block.
265
- def initialize(head, tail = nil, &promise)
278
+ def initialize(head = nil, tail = nil, &block)
266
279
  @cached = true
267
280
  @ref_cache = {}
268
281
  if tail
269
- promise and
282
+ if block
270
283
  raise LazyList::Exception,
271
284
  "Use block xor second argument for constructor"
285
+ end
272
286
  @head, @tail = head, tail
273
- elsif promise
274
- @head, @tail = head, promise
287
+ elsif block
288
+ @head, @tail = head, delay(&block)
289
+ else
290
+ @head, @tail = nil, nil
275
291
  end
276
292
  end
277
293
 
@@ -281,32 +297,45 @@ class LazyList
281
297
  attr_writer :cached
282
298
 
283
299
  # Returns true, if index references into the lazy list are cached for fast
284
- # access to the referenceѕ elements.
300
+ # access to the referenced elements.
285
301
  def cached?
286
302
  !!@cached
287
303
  end
288
304
 
289
- # Denotes the empty LazyList which is a guard at the end of finite
290
- # lazy lists.
291
- Empty = new(nil, nil)
305
+ # If the constant Empty is requested return a new empty list object.
306
+ def self.const_missing(id)
307
+ if id == :Empty
308
+ new(nil, nil)
309
+ else
310
+ super
311
+ end
312
+ end
292
313
 
293
314
  # Returns the value of this element.
294
- attr_accessor :head
315
+ attr_writer :head
295
316
  protected :head=
296
317
 
318
+ # Returns the head of this list by computing its value.
319
+ def head
320
+ @head = force @head
321
+ end
322
+
323
+ # Returns the head of this list without forcing it.
324
+ def peek_head
325
+ @head
326
+ end
327
+ protected :peek_head
328
+
297
329
  # Writes a tail value.
298
330
  attr_writer :tail
299
331
  protected :tail=
300
332
 
301
333
  # Returns the next element by computing its value if necessary.
302
334
  def tail
303
- if @tail.is_a? Proc
304
- @tail = @tail[@head] || Empty
305
- end
306
- @tail
335
+ @tail = force @tail
307
336
  end
308
337
 
309
- # Returns the tail of this list without evaluating.
338
+ # Returns the tail of this list without forcing it.
310
339
  def peek_tail
311
340
  @tail
312
341
  end
@@ -315,32 +344,67 @@ class LazyList
315
344
  # Identity lambda expression, mostly used as a default.
316
345
  Identity = lambda { |x| x }
317
346
 
347
+ # This block returns true.
348
+ All = lambda { |x| true }
349
+
350
+ # Create an array tuple from argument list.
351
+ Tuple = lambda { |*t| t }
352
+
353
+ # Returns true, if a less than b.
354
+ LessThan = lambda { |a, b| a < b }
355
+
356
+ # Swaps _you_ and _me_ and returns an Array tuple of both.
357
+ SwapTuple = lambda { |you, me| [ me, you ] }
358
+
318
359
  # Returns a lazy list which is generated from the Enumerable a or
319
360
  # LazyList.span(a, n), if n was given as an argument.
320
361
  def self.[](a, n = nil)
321
362
  case
322
363
  when n
323
364
  span(a, n)
324
- when IO === a
325
- io(a)
326
- else
365
+ when a.respond_to?(:to_io)
366
+ io(a.to_io)
367
+ when a.respond_to?(:to_ary)
368
+ from_queue(a.to_ary)
369
+ when Range === a
370
+ from_range(a)
371
+ when a.respond_to?(:each)
327
372
  from_enum(a)
373
+ else
374
+ list(a)
328
375
  end
329
376
  end
330
377
 
331
378
  # Generates a lazy list from any data structure e which
332
379
  # responds to the #each method.
333
380
  def self.from_enum(e)
334
- oq = ReadQueue.new(e)
335
- return Empty if oq.empty?
336
- next_top = proc do
337
- if oq.empty?
338
- Empty
381
+ from_queue ReadQueue.new(e)
382
+ end
383
+
384
+ # Generates a lazyx list by popping elements from a queue.
385
+ def self.from_queue(rq)
386
+ return empty if rq.empty?
387
+ new(delay { rq.shift }) { from_queue(rq) }
388
+ end
389
+
390
+ # Generates a lazy list from a Range instance _r_.
391
+ def self.from_range(r)
392
+ if r.exclude_end?
393
+ if r.begin >= r.end
394
+ empty
395
+ else
396
+ new(delay { r.begin }) { from_range(r.begin.succ...r.end) }
397
+ end
398
+ else
399
+ case
400
+ when r.begin > r.end
401
+ empty
402
+ when r.begin == r.end
403
+ new(delay { r.begin }) { empty }
339
404
  else
340
- new(oq.pop, next_top)
405
+ new(delay { r.begin }) { from_range(r.begin.succ..r.end) }
341
406
  end
342
407
  end
343
- new(oq.pop, next_top)
344
408
  end
345
409
 
346
410
  # Generates a finite lazy list beginning with element a and spanning
@@ -348,9 +412,9 @@ class LazyList
348
412
  # successor method succ.
349
413
  def self.span(a, n)
350
414
  if n > 0
351
- new(a) { span(a.succ, n - 1) }
415
+ new(delay { a }) { span(a.succ, n - 1) }
352
416
  else
353
- Empty
417
+ empty
354
418
  end
355
419
  end
356
420
 
@@ -359,10 +423,11 @@ class LazyList
359
423
  # If none is given the identity function is computed instead.
360
424
  def self.tabulate(n = 0, &f)
361
425
  f = Identity unless f
362
- new(f[n]) { tabulate(n.succ, &f) }
426
+ new(delay { f[n] }) { tabulate(n.succ, &f) }
363
427
  end
364
428
 
365
- # Returns a list of all elements succeeding _n_ and starting from _n_.
429
+ # Returns a list of all elements succeeding _n_ (that is created by calling
430
+ # the #succ method) and starting from _n_.
366
431
  def self.from(n = 0)
367
432
  tabulate(n)
368
433
  end
@@ -370,16 +435,16 @@ class LazyList
370
435
  # Generates a lazy list which iterates over its previous values
371
436
  # computing something like: f(i), f(f(i)), f(f(f(i))), ...
372
437
  def self.iterate(i = 0, &f)
373
- new(i) { iterate(f[i], &f) }
438
+ new(delay { i }) { iterate(f[i], &f) }
374
439
  end
375
440
 
376
441
  # Generates a lazy list of a give IO-object using a given
377
442
  # block or Proc object to read from this object.
378
443
  def self.io(input, &f)
379
444
  if f
380
- input.eof? ? Empty : new(f[input]) { io(input, &f) }
445
+ input.eof? ? empty : new(delay { f[input] }) { io(input, &f) }
381
446
  else
382
- input.eof? ? Empty : new(input.readline) { io(input) }
447
+ input.eof? ? empty : new(delay { input.readline }) { io(input) }
383
448
  end
384
449
  end
385
450
 
@@ -403,9 +468,9 @@ class LazyList
403
468
  sublist_span(0, n)
404
469
  elsif m > 0
405
470
  l = ref(n)
406
- self.class.new(l.head) { l.tail.sublist_span(0, m - 1) }
471
+ self.class.new(delay { l.head }) { l.tail.sublist_span(0, m - 1) }
407
472
  else
408
- Empty
473
+ empty
409
474
  end
410
475
  end
411
476
 
@@ -436,23 +501,25 @@ class LazyList
436
501
  if s.empty?
437
502
  return set_ref(n, self)
438
503
  end
504
+ s.head # force the head
439
505
  s = s.tail
440
506
  i -= 1
441
507
  end
442
508
  set_ref(n, s)
443
509
  end
444
- private :ref
510
+ protected :ref
445
511
 
446
512
  # If n is a Range every element in this range is returned.
447
513
  # If n isn't a Range object the element at index n is returned.
448
514
  # If m is given the next m elements beginning the n-th element are
449
515
  # returned.
450
516
  def [](n, m = nil)
451
- if n.is_a? Range
517
+ case
518
+ when Range === n
452
519
  sublist(n)
453
- elsif n < 0
520
+ when n < 0
454
521
  nil
455
- elsif m
522
+ when m
456
523
  sublist(n, m)
457
524
  else
458
525
  ref(n).head
@@ -464,23 +531,40 @@ class LazyList
464
531
  # elements to iterate over.
465
532
  def each(n = nil)
466
533
  s = self
467
- while (n.nil? or n > 0) and not s.empty? do
468
- yield s.head
469
- s = s.tail
470
- n -= 1 unless n.nil?
534
+ if n
535
+ until n <= 0 or s.empty?
536
+ yield s.head
537
+ s = s.tail
538
+ n -= 1 unless n.nil?
539
+ end
540
+ else
541
+ until s.empty?
542
+ yield s.head
543
+ s = s.tail
544
+ n -= 1 unless n.nil?
545
+ end
471
546
  end
472
547
  s
473
548
  end
474
549
 
475
- # Similar to LazyList#each but destroys elements from past
476
- # iterations perhaps saving some memory.
550
+ # Similar to LazyList#each but destroys elements from past iterations perhaps
551
+ # saving some memory. Try to call GC.start from time to time in your block.
477
552
  def each!(n = nil)
478
553
  s = self
479
- while (n.nil? or n > 0) and not s.empty? do
480
- yield s.head
481
- s = s.tail
482
- n -= 1 unless n.nil?
483
- @head, @tail = s.head, s.tail
554
+ if n
555
+ until n <= 0 or s.empty?
556
+ yield s.head
557
+ s = s.tail
558
+ n -= 1 unless n.nil?
559
+ @head, @tail = s.head, s.tail
560
+ end
561
+ else
562
+ until s.empty?
563
+ yield s.head
564
+ s = s.tail
565
+ n -= 1 unless n.nil?
566
+ @head, @tail = s.head, s.tail
567
+ end
484
568
  end
485
569
  self
486
570
  end
@@ -489,12 +573,15 @@ class LazyList
489
573
  # which elements to place first in the result lazy list. If no compare block
490
574
  # is given lambda { |a,b| a < b } is used as a default value.
491
575
  def merge(other, &compare)
492
- compare = lambda { |a, b| a < b } unless compare
493
- return other if empty?
494
- return self if other.empty?
495
- if compare[head, other.head]
576
+ compare ||= LessThan
577
+ case
578
+ when empty?
579
+ other
580
+ when other.empty?
581
+ self
582
+ when compare[head, other.head]
496
583
  self.class.new(head) { tail.merge(other, &compare) }
497
- elsif compare[other.head, head]
584
+ when compare[other.head, head]
498
585
  self.class.new(other.head) { merge(other.tail, &compare) }
499
586
  else
500
587
  self.class.new(head) { tail.merge(other.tail, &compare) }
@@ -508,12 +595,12 @@ class LazyList
508
595
  def append(*other)
509
596
  if empty?
510
597
  if other.empty?
511
- Empty
598
+ empty
512
599
  else
513
600
  other.first.append(*other[1..-1])
514
601
  end
515
602
  else
516
- list(head) { tail.append(*other) }
603
+ self.class.new(delay { head }) { tail.append(*other) }
517
604
  end
518
605
  end
519
606
 
@@ -569,7 +656,7 @@ class LazyList
569
656
 
570
657
  # Returns true if this is the empty lazy list.
571
658
  def empty?
572
- self.equal? Empty
659
+ self.peek_head == nil && self.peek_tail == nil
573
660
  end
574
661
 
575
662
  # Returns true, if this lazy list and the other lazy list consist of only
@@ -586,61 +673,156 @@ class LazyList
586
673
  # Inspects the list as far as it has been computed by returning
587
674
  # a string of the form [1, 2, 3,... ].
588
675
  def inspect
589
- result = "["
676
+ return '[]' if empty?
677
+ result = '['
678
+ first = true
590
679
  s = self
591
- until s.empty?
592
- pt = s.peek_tail
593
- case
594
- when pt.equal?(Empty) # to avoid calling == on pt
595
- result << s.head.inspect
596
- break
597
- when Proc === pt
598
- result << s.head.inspect << ",... "
599
- break
680
+ seen = {}
681
+ until s.empty? or Promise === s.peek_head or seen[s.__id__]
682
+ seen[s.__id__] = true
683
+ if first
684
+ first = false
600
685
  else
601
- t = s.tail
602
- if t.equal? self
603
- result << s.head.inspect << ",... "
604
- break
686
+ result << ', '
687
+ end
688
+ result << s.head.inspect
689
+ Promise === s.peek_tail and break
690
+ s = s.tail
691
+ end
692
+ unless empty?
693
+ if first
694
+ result << '... '
695
+ elsif !s.empty?
696
+ result << ',... '
697
+ end
698
+ end
699
+ result << ']'
700
+ end
701
+
702
+ alias to_s inspect
703
+
704
+ # Returns one "half" of the product of this LazyList and the _other_:
705
+ # list(1,2,3).half_product(list(1,2)).to_a # => [[1, 1], [2, 1], [2, 2], [3, 1], [3, 2]]
706
+ # _block_ can be used to yield to every pair generated and create a new list
707
+ # element out of it. It defaults to Tuple.
708
+ def half_product(other, &block)
709
+ block ||= Tuple
710
+ if empty? or other.empty?
711
+ empty
712
+ else
713
+ mix(
714
+ delay { zip(other, &block) },
715
+ delay { tail.half_product(other, &block) }
716
+ )
717
+ end
718
+ end
719
+
720
+ def swap(block) # :nodoc:
721
+ lambda { |you, me| block[me, you] }
722
+ end
723
+ private :swap
724
+
725
+ # Returns the (cartesian) product of this LazyList instance and the _other_.
726
+ # _block_ can be used to yield to every pair generated and create a new list
727
+ # element out of it, but it's useful to at least return the default Tuple
728
+ # from the block.
729
+ def product(other, &block)
730
+ if empty? or other.empty?
731
+ empty
732
+ else
733
+ other_block =
734
+ if block
735
+ swap block
605
736
  else
606
- result << s.head.inspect << ", "
607
- s = t
737
+ block = Tuple
738
+ SwapTuple
739
+ end
740
+ mix(
741
+ delay { half_product(other, &block)},
742
+ delay { other.tail.half_product(self, &other_block) }
743
+ )
744
+ end
745
+ end
746
+ alias * product
747
+
748
+ # Returns the cartesian_product of this LazyList and the others as a LazyList
749
+ # of Array tuples. A block can be given to yield to all the created tuples
750
+ # and create a LazyList out of the block results.
751
+ def cartesian_product(*others) # :yields: tuple
752
+ case
753
+ when empty?
754
+ self
755
+ when others.empty?
756
+ block_given? ? map(&Proc.new) : map
757
+ else
758
+ product = others[1..-1].inject(product(others[0])) do |intermediate, list|
759
+ intermediate.product(list) do |existing, new_element|
760
+ existing + [ new_element ]
608
761
  end
609
762
  end
763
+ if block_given?
764
+ block = Proc.new
765
+ product.map { |tuple| block[*tuple] }
766
+ else
767
+ product
768
+ end
610
769
  end
611
- result << "]"
612
770
  end
613
771
 
614
- alias to_s inspect
615
- end
772
+ # This module contains methods that are included into Ruby's Kernel module.
773
+ module ObjectMethods
774
+ # A method to improve the user friendliness for creating new lazy lists, that
775
+ # cannot be described well with LazyList.iterate or LazyList.tabulate.
776
+ #
777
+ # - list without any arguments, returns the empty lazy list LazyList::Empty.
778
+ # - list { x / y } returns a LazyList::ListBuilder object for a list
779
+ # comprehension, that can be transformed into a LazyList by calling the
780
+ # LazyList::ListBuilder#where method.
781
+ # - list(x) returns the lazy list with only the element x as a member,
782
+ # list(x,y) returns the lazy list with only the elements x and y as a
783
+ # members, and so on.
784
+ # - list(x) { xs } returns the lazy list with the element x as a head
785
+ # element, and that is continued with the lazy list xs as tail. To define an
786
+ # infinite lazy list of 1s you can do:
787
+ # ones = list(1) { ones } # => [1,... ]
788
+ # To define all even numbers directly, you can do:
789
+ # def even(n = 0) list(n) { even(n + 2) } end
790
+ # and then:
791
+ # e = even # => [0,... ]
792
+ def list(*values, &block)
793
+ values_empty = values.empty?
794
+ result = LazyList[values]
795
+ if block_given?
796
+ if values_empty
797
+ result = LazyList::ListBuilder.create_comprehend(&block)
798
+ else
799
+ result.instance_eval do
800
+ ref(values.size - 1)
801
+ end.instance_variable_set(:@tail, LazyList.delay(&block))
802
+ end
803
+ end
804
+ result
805
+ end
616
806
 
617
- module Kernel
618
- # A method to improve the user friendliness for creating new lazy lists, that
619
- # cannot be described well with LazyList#iterate or LazyList#tabulate.
620
- #
621
- # - list without any arguments, returns the empty lazy list LazyList::Empty.
622
- # - list(x) returns the lazy list with only the element x as a member,
623
- # list(x,y) returns the lazy list with only the elements x and y as a
624
- # members, and so on.
625
- # - list(x) { xs } returns the lazy list with the element x as a head
626
- # element, and that is continued with the lazy list xs as tail. To define an
627
- # infinite lazy list of 1s you can do:
628
- # ones = list(1) { ones } # => [1,... ]
629
- # To define all even numbers directly, you can do:
630
- # def even(n = 0) list(n) { even(n + 2) } end
631
- # and then:
632
- # e = even # => [0,... ]
633
- def list(*values, &promise)
634
- result = LazyList::Empty
635
- last = nil
636
- values.reverse_each do |v|
637
- result = LazyList.new(v, result)
638
- last ||= result
639
- end
640
- if last and block_given?
641
- last.instance_variable_set :@tail, promise
807
+ # This method returns a Lazylist::ListBuilder instance for tuplewise building
808
+ # of lists like the zip method does. This method call
809
+ #
810
+ # build { x + y }.where(:x => 1..3, :y => 1..3)
811
+ #
812
+ # returns the same list [ 2, 4, 6 ] as this expression does
813
+ #
814
+ # LazyList[1..3].zip(LazyList[1..3]) { |x, y| x + y }
815
+ def build(&block)
816
+ LazyList::ListBuilder.create_build(&block)
642
817
  end
643
- result
818
+ end
819
+
820
+ class ::Object
821
+ unless const_defined? :Infinity
822
+ Infinity = 1 / 0.0
823
+ end
824
+
825
+ include LazyList::ObjectMethods
644
826
  end
645
827
  end
646
828
  # vim: set et sw=2 ts=2: