lazylist 0.2.2 → 0.3.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 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: