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 +22 -2
- data/Rakefile +30 -30
- data/VERSION +1 -1
- data/examples/examples.rb +15 -1
- data/examples/pi.rb +1 -1
- data/install.rb +5 -2
- data/lib/lazylist.rb +413 -231
- data/lib/lazylist/enumerable.rb +112 -0
- data/lib/lazylist/list_builder.rb +137 -0
- data/lib/lazylist/version.rb +8 -0
- data/make_doc.rb +1 -1
- data/tests/runner.rb +1 -6
- data/tests/test.rb +61 -0
- metadata +54 -37
- data/TODO +0 -2
@@ -0,0 +1,112 @@
|
|
1
|
+
class LazyList
|
2
|
+
module Enumerable
|
3
|
+
include ::Enumerable
|
4
|
+
|
5
|
+
# Returns two lazy lists, the first containing the elements of this lazy
|
6
|
+
# list for which the block evaluates to true, the second containing the
|
7
|
+
# rest.
|
8
|
+
def partition(&block)
|
9
|
+
return select(&block), reject(&block)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns a sorted version of this lazy list. This method should only be
|
13
|
+
# called on finite lazy lists or it will never return. Also see
|
14
|
+
# Enumerable#sort.
|
15
|
+
def sort # :yields: a, b
|
16
|
+
LazyList.from_enum(super)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns a sorted version of this lazy list. This method should only be
|
20
|
+
# called on finite lazy lists or it will never return. Also see
|
21
|
+
# Enumerable#sort_by.
|
22
|
+
def sort_by # :yields: obj
|
23
|
+
LazyList.from_enum(super)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Calls _block_ with two arguments, the element and its index, for each
|
27
|
+
# element of this lazy list. If _block_ isn't given, the method returns a
|
28
|
+
# lazy list that consists of [ element, index ] pairs instead.
|
29
|
+
def each_with_index(&block)
|
30
|
+
if block
|
31
|
+
each_with_index.each { |x| block[x] }
|
32
|
+
else
|
33
|
+
i = -1
|
34
|
+
map { |x| [ x, i += 1 ] }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the lazy list, that contains all the given _block_'s return
|
39
|
+
# values, if it was called on every
|
40
|
+
# self[i], others[0][i], others[1][i],... others[others.size - 1][i]
|
41
|
+
# for i in 0..Infinity. If _block_ wasn't given
|
42
|
+
# this result will be the array
|
43
|
+
# [self[i], others[0][i], others[1][i],... ]
|
44
|
+
# and a lazy list of those arrays is returned.
|
45
|
+
def zip(*others, &block)
|
46
|
+
if empty? or others.any? { |o| o.empty? }
|
47
|
+
empty
|
48
|
+
else
|
49
|
+
block ||= Tuple
|
50
|
+
self.class.new(delay { block[head, *others.map { |o| o.head }] }) do
|
51
|
+
tail.zip(*others.map { |o| o.tail }, &block)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# obsoleted by #zip
|
57
|
+
def combine(other, &operator)
|
58
|
+
warn "method 'combine' is obsolete - use 'zip'"
|
59
|
+
zip(other, &operator)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns a lazy list every element of this lazy list for which
|
63
|
+
# pattern === element is true. If the optional _block_ is supplied,
|
64
|
+
# each matching element is passed to it, and the block's result becomes
|
65
|
+
# an element of the returned lazy list.
|
66
|
+
def grep(pattern, &block)
|
67
|
+
result = select { |x| pattern === x }
|
68
|
+
block and result = result.map(&block)
|
69
|
+
result
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns a lazy list of all elements of this lazy list for which the block
|
73
|
+
# is false (see also +Lazylist#select+).
|
74
|
+
def reject
|
75
|
+
select { |obj| !yield(obj) }
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns a lazy list of all elements of this lazy list for which _block_
|
79
|
+
# is true.
|
80
|
+
def select(&block)
|
81
|
+
block = All unless block
|
82
|
+
s = self
|
83
|
+
until s.empty? or block[s.head]
|
84
|
+
s = s.tail
|
85
|
+
end
|
86
|
+
return empty if s.empty?
|
87
|
+
self.class.new(delay { s.head }) { s.tail.select(&block) }
|
88
|
+
end
|
89
|
+
alias find_all select
|
90
|
+
|
91
|
+
# obsoleted by #select
|
92
|
+
def filter(&p)
|
93
|
+
warn "method 'filter' is obsolete - use 'select'"
|
94
|
+
select(&p)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Creates a new Lazylist that maps the block or Proc object f to every
|
98
|
+
# element in the old list.
|
99
|
+
def map(&f)
|
100
|
+
return empty if empty?
|
101
|
+
f = Identity unless f
|
102
|
+
self.class.new(delay { f[head] }) { tail.map(&f) }
|
103
|
+
end
|
104
|
+
alias collect map
|
105
|
+
|
106
|
+
# obsoleted by #map
|
107
|
+
def mapper(&f)
|
108
|
+
warn "method 'mapper' is obsolete - use 'map'"
|
109
|
+
map(&f)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'dslkit'
|
2
|
+
|
3
|
+
class LazyList
|
4
|
+
def do_build(lb, others) # :nodoc:
|
5
|
+
if empty? or others.any? { |o| o.empty? }
|
6
|
+
Empty
|
7
|
+
else
|
8
|
+
variables = [ head ].concat others.map { |o| o.head }
|
9
|
+
if lb.filter
|
10
|
+
if lb.filter[*variables]
|
11
|
+
self.class.new(lb.transform[*variables]) do
|
12
|
+
tail.do_build(lb, others.map { |o| o.tail })
|
13
|
+
end
|
14
|
+
else
|
15
|
+
tail.do_build(lb, others.map { |o| o.tail })
|
16
|
+
end
|
17
|
+
else
|
18
|
+
self.class.new(lb.transform[*variables]) do
|
19
|
+
tail.do_build(lb, others.map { |o| o.tail })
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def do_comprehend(lb, others) # :nodoc:
|
26
|
+
if lb.filter
|
27
|
+
cartesian_product(*others).select do |variables|
|
28
|
+
lb.filter[*variables]
|
29
|
+
end.map do |variables|
|
30
|
+
lb.transform[*variables]
|
31
|
+
end
|
32
|
+
else
|
33
|
+
cartesian_product(*others).map do |variables|
|
34
|
+
lb.transform[*variables]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# This class encapsulates a list builder (ListBuilder), that can be transformed
|
40
|
+
# into a LazyList, by calling LazyList::ListBuilder#where.
|
41
|
+
class ListBuilder
|
42
|
+
# This class is a special kind of Proc object, that uses instance_eval to
|
43
|
+
# execute a code block.
|
44
|
+
class ListBuilderProc < Proc
|
45
|
+
include DSLKit::MethodMissingDelegator
|
46
|
+
include DSLKit::BlockSelf
|
47
|
+
|
48
|
+
# Creates a ListBuilderProc working on the list builder _lb_ using the Proc
|
49
|
+
# returned by lb.#{name}. _name_ has to be either :filter or :transform.
|
50
|
+
def initialize(lb, name, &block)
|
51
|
+
@name = name
|
52
|
+
@lb = lb
|
53
|
+
@method_missing_delegator = block_self(&block)
|
54
|
+
super(&@lb.__send__(@name))
|
55
|
+
end
|
56
|
+
|
57
|
+
# Call this ListBuilderProc instance with the arguments _args_, which have to be
|
58
|
+
# the ordered values of the variables.
|
59
|
+
def call(*args)
|
60
|
+
prepare_variables
|
61
|
+
@lb.variables.each_with_index do |var, i|
|
62
|
+
instance_variable_set "@#{var}", args[i]
|
63
|
+
end
|
64
|
+
instance_eval(&@lb.__send__(@name))
|
65
|
+
end
|
66
|
+
alias [] call
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def prepare_variables
|
71
|
+
@variables_prepared and return
|
72
|
+
variables = @lb.variables
|
73
|
+
class << self; self; end.instance_eval do
|
74
|
+
attr_reader(*variables)
|
75
|
+
end
|
76
|
+
@variables_prepared = true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Creates LazyList::ListBuilder instance. _mode_ has to be either :do_build
|
81
|
+
# or :do_comprehend.
|
82
|
+
def initialize(mode, &block)
|
83
|
+
@mode = mode
|
84
|
+
@transform = ListBuilderProc.new(self, :transform, &block)
|
85
|
+
end
|
86
|
+
|
87
|
+
# The variable names defined in this list builder.
|
88
|
+
attr_reader :variables
|
89
|
+
|
90
|
+
# The transform ListBuilderProc instance of this list builder.
|
91
|
+
attr_reader :transform
|
92
|
+
|
93
|
+
# The filter ListBuilderProc of this list builder or nil.
|
94
|
+
attr_reader :filter
|
95
|
+
|
96
|
+
# This method creates a LazyList instance from this list builder,
|
97
|
+
# using the _sources_ hash to fetch the variables from. _sources_ consists
|
98
|
+
# of the variable name and the values, that can be LazyList instances or
|
99
|
+
# otherwise they will be transformed into a LazyList with LazyList.[].
|
100
|
+
#
|
101
|
+
# It also takes a block, to filter the results by a boolean expression.
|
102
|
+
def where(sources = {}, &block)
|
103
|
+
@variables = []
|
104
|
+
generators = []
|
105
|
+
sources.each do |var, src|
|
106
|
+
@variables << var
|
107
|
+
generators << (src.is_a?(LazyList) ? src : LazyList[src])
|
108
|
+
end
|
109
|
+
if block_given?
|
110
|
+
@filter = ListBuilderProc.new(self, :filter, &block)
|
111
|
+
else
|
112
|
+
@filter = nil
|
113
|
+
end
|
114
|
+
generators.first.__send__(@mode, self, generators[1..-1])
|
115
|
+
end
|
116
|
+
|
117
|
+
# Return a (not much) nicer string representation of the list
|
118
|
+
# builder.
|
119
|
+
def to_s
|
120
|
+
"#<LazyList::ListBuilder>"
|
121
|
+
end
|
122
|
+
alias inspect to_s
|
123
|
+
|
124
|
+
class << self
|
125
|
+
# Used to support the build method
|
126
|
+
def create_build(&block)
|
127
|
+
new(:do_build, &block)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Used to evaluate a list comprehension, usually if calling the list
|
131
|
+
# method with only a block.
|
132
|
+
def create_comprehend(&block)
|
133
|
+
new(:do_comprehend, &block)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
data/make_doc.rb
CHANGED
data/tests/runner.rb
CHANGED
@@ -3,12 +3,7 @@
|
|
3
3
|
require 'test/unit/ui/console/testrunner'
|
4
4
|
require 'test/unit/testsuite'
|
5
5
|
$:.unshift File.expand_path(File.dirname($0))
|
6
|
-
$:.unshift '
|
7
|
-
$:.unshift '../lib'
|
8
|
-
begin
|
9
|
-
require 'coverage'
|
10
|
-
rescue LoadError
|
11
|
-
end
|
6
|
+
$:.unshift 'tests'
|
12
7
|
require 'test'
|
13
8
|
require 'test_enumerable'
|
14
9
|
|
data/tests/test.rb
CHANGED
@@ -67,6 +67,7 @@ class TC_LazyList < Test::Unit::TestCase
|
|
67
67
|
assert_equal @finite_inner1.to_a, @finite1.to_a
|
68
68
|
assert_equal @finite_inner10.to_a, @finite10.to_a
|
69
69
|
assert_equal @finite_span_generated, @finite_span.to_a
|
70
|
+
assert_equal @finite_span_generated, LazyList["A"..."K"].to_a
|
70
71
|
end
|
71
72
|
|
72
73
|
def test_size
|
@@ -84,6 +85,8 @@ class TC_LazyList < Test::Unit::TestCase
|
|
84
85
|
assert_equal 1, @odd[0]
|
85
86
|
assert_equal 3, @odd[1]
|
86
87
|
assert_equal 5, @odd[2]
|
88
|
+
assert_equal [3, 5, 7], @odd.take_range(1..3)
|
89
|
+
assert_equal [3, 5, 7, 9], @odd.take_span(1, 4)
|
87
90
|
assert_equal (1..19).select(&@oddp), @odd[0, 10].to_a
|
88
91
|
assert_equal (1..10).to_a, @natural[0, 10].to_a
|
89
92
|
assert_equal [ 1 ] * 10, @ones[0, 10].to_a
|
@@ -244,11 +247,16 @@ class TC_LazyList < Test::Unit::TestCase
|
|
244
247
|
assert_equal 5, f[0]
|
245
248
|
assert_equal 26, f[1]
|
246
249
|
assert_equal [5, 26, 13, 66, 33, 166, 83, 416], f[0, 8].to_a
|
250
|
+
a196 = LazyList.iterate(35) { |x| x + x.to_s.reverse.to_i }
|
251
|
+
assert_equal [35, 88, 176, 847, 1595, 7546, 14003, 44044, 88088, 176176],
|
252
|
+
a196.take(10)
|
247
253
|
end
|
248
254
|
|
249
255
|
def test_inspect
|
250
256
|
l = LazyList[1..11]
|
251
257
|
assert_equal "[]", Empty.inspect
|
258
|
+
assert_equal "[... ]", l.inspect
|
259
|
+
l[0]
|
252
260
|
assert_equal "[1,... ]", l.inspect
|
253
261
|
l[1]
|
254
262
|
assert_equal "[1, 2,... ]", l.inspect
|
@@ -284,10 +292,12 @@ class TC_LazyList < Test::Unit::TestCase
|
|
284
292
|
assert_equal [1, 3, 5, 7, 9, 11, 13, 15, 17, 19], o.take(10)
|
285
293
|
assert_equal @odd.take(10), o.take(10)
|
286
294
|
ones = list(1) { ones }
|
295
|
+
assert_equal '[... ]', ones.inspect
|
287
296
|
assert_equal 1, ones.head
|
288
297
|
assert_equal '[1,... ]', ones.inspect
|
289
298
|
assert_equal [ 1 ] * 10 , ones.take(10)
|
290
299
|
assert_equal '[1,... ]', ones.inspect
|
300
|
+
assert_equal [:foo], LazyList[:foo].to_a
|
291
301
|
end
|
292
302
|
|
293
303
|
def test_sublist
|
@@ -324,5 +334,56 @@ class TC_LazyList < Test::Unit::TestCase
|
|
324
334
|
assert_equal [1, 2, 3] + [5, 6, 7] + [9, 10, 11, 12],
|
325
335
|
l1.append(l2, l3).take(10)
|
326
336
|
end
|
337
|
+
|
338
|
+
def test_list_builder
|
339
|
+
lb = build { y * x }
|
340
|
+
assert_kind_of LazyList::ListBuilder, lb
|
341
|
+
assert_equal "#<LazyList::ListBuilder>", lb.to_s
|
342
|
+
l = lb.where :x => 1..10, :y => 'a'..'j' do
|
343
|
+
x % 2 == 0 && y < 'j'
|
344
|
+
end
|
345
|
+
assert_kind_of LazyList, l
|
346
|
+
assert_equal ["bb", "dddd", "ffffff", "hhhhhhhh"], l.to_a
|
347
|
+
l = build { y * x }.where :x => 1..10, :y => 'a'..'j'
|
348
|
+
assert_equal ["a", "bb", "ccc", "dddd", "eeeee", "ffffff", "ggggggg",
|
349
|
+
"hhhhhhhh", "iiiiiiiii", "jjjjjjjjjj"], l.to_a
|
350
|
+
end
|
351
|
+
|
352
|
+
def twice(x)
|
353
|
+
2 * x
|
354
|
+
end
|
355
|
+
|
356
|
+
def test_list_comprehend
|
357
|
+
f = LazyList[1..3]
|
358
|
+
g = LazyList[1..2]
|
359
|
+
assert_equal [ ], LazyList.mix(LazyList::Empty).to_a
|
360
|
+
assert_equal [ ], LazyList.mix(LazyList::Empty, LazyList::Empty).to_a
|
361
|
+
assert_equal [ 1 ], LazyList.mix(list(1), LazyList::Empty).to_a
|
362
|
+
assert_equal [ 1 ], LazyList.mix(LazyList::Empty, list(1)).to_a
|
363
|
+
assert_equal [ 1, 1, 2, 2, 3 ], LazyList.mix(f, g).to_a
|
364
|
+
assert_equal list, list * list
|
365
|
+
assert_equal list, list * list(1)
|
366
|
+
assert_equal list, list(1) * list
|
367
|
+
assert_equal list, list.cartesian_product
|
368
|
+
assert_equal [ [1, 1], [1, 2], [2, 1], [2, 2], [3, 1], [3, 2] ],
|
369
|
+
f.cartesian_product(g).to_a
|
370
|
+
assert_equal [[1, 1, 1], [1, 1, 2], [1, 2, 1], [1, 2, 2], [2, 1, 1], [2, 1,
|
371
|
+
2], [2, 2, 1], [2, 2, 2]], g.cartesian_product(g, g).to_a
|
372
|
+
assert_equal [1, 2, 2, 4, 2, 4, 4, 8],
|
373
|
+
g.cartesian_product(g, g, &lambda { |x,y,z| x * y * z }).to_a
|
374
|
+
assert_equal g.map { |x| x * x }, g.cartesian_product { |x| x * x }
|
375
|
+
l = list { [ x, y ] }.where :x => g, :y => g
|
376
|
+
assert_equal [ [ 1, 1 ], [ 1, 2 ], [ 2, 1 ], [ 2, 2 ] ], l.to_a.sort
|
377
|
+
l = list { [ x, y ] }.where :x => f, :y => g
|
378
|
+
assert_equal [ [ 1, 1 ], [ 1, 2 ], [ 2, 1 ], [ 2, 2 ], [ 3, 1 ], [ 3, 2 ] ], l.to_a.sort
|
379
|
+
l = list { [ x, y ] }.where(:x => f, :y => f) { x > y }
|
380
|
+
assert_equal [ [ 2, 1 ], [ 3, 1 ], [ 3, 2 ] ], l.to_a.sort
|
381
|
+
l = list { [ x, y ] }.where(:x => f, :y => f) { x <= y }
|
382
|
+
assert_equal [ [1, 1], [1, 2], [1, 3], [2, 2], [2, 3], [3, 3] ], l.to_a.sort
|
383
|
+
test = list { x * y }.where :x => 1..4, :y => list(3, 5, 7, 9)
|
384
|
+
assert_equal [ 3, 5, 6, 7, 10, 14, 9, 9, 21, 27, 15, 18, 36, 12, 28, 20 ].sort, test.to_a.sort
|
385
|
+
test = list { x + twice(y) }.where :x => 1..4, :y => list(3, 5, 7, 9)
|
386
|
+
assert_equal [ 7, 11, 8, 15, 12, 16, 9, 19, 17, 21, 13, 20, 22, 10, 18, 14 ].sort, test.to_a.sort
|
387
|
+
end
|
327
388
|
end
|
328
389
|
# vim: set et sw=2 ts=2:
|
metadata
CHANGED
@@ -3,62 +3,79 @@ 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:
|
6
|
+
version: 0.3.0
|
7
|
+
date: 2007-05-05 00:00:00 +02:00
|
8
8
|
summary: Implementation of lazy lists for Ruby
|
9
9
|
require_paths:
|
10
|
-
|
10
|
+
- lib
|
11
11
|
email: flori@ping.de
|
12
12
|
homepage: http://lazylist.rubyforge.org
|
13
13
|
rubyforge_project: lazylist
|
14
|
-
description:
|
15
|
-
autorequire:
|
14
|
+
description: ""
|
15
|
+
autorequire:
|
16
16
|
default_executable:
|
17
17
|
bindir: bin
|
18
18
|
has_rdoc: true
|
19
19
|
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
20
|
requirements:
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
version: 0.0.0
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
25
24
|
version:
|
26
25
|
platform: ruby
|
27
26
|
signing_key:
|
28
27
|
cert_chain:
|
29
28
|
authors:
|
30
|
-
|
29
|
+
- Florian Frank
|
31
30
|
files:
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
31
|
+
- VERSION
|
32
|
+
- tests
|
33
|
+
- GPL
|
34
|
+
- README.en
|
35
|
+
- install.rb
|
36
|
+
- Rakefile
|
37
|
+
- examples
|
38
|
+
- lib
|
39
|
+
- make_doc.rb
|
40
|
+
- CHANGES
|
41
|
+
- tests/runner.rb
|
42
|
+
- tests/test.rb
|
43
|
+
- tests/test_enumerable.rb
|
44
|
+
- examples/pi.rb
|
45
|
+
- examples/hamming.rb
|
46
|
+
- examples/examples.rb
|
47
|
+
- examples/sieve.rb
|
48
|
+
- lib/lazylist.rb
|
49
|
+
- lib/lazylist
|
50
|
+
- lib/lazylist/list_builder.rb
|
51
|
+
- lib/lazylist/enumerable.rb
|
52
|
+
- lib/lazylist/version.rb
|
51
53
|
test_files:
|
52
|
-
|
54
|
+
- tests/test.rb
|
53
55
|
rdoc_options:
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
- --title
|
57
|
+
- LazyList -- Infinite lists in Ruby
|
58
|
+
- --main
|
59
|
+
- LazyList
|
60
|
+
- --line-numbers
|
59
61
|
extra_rdoc_files:
|
60
|
-
|
62
|
+
- lib/lazylist.rb
|
63
|
+
- lib/lazylist/list_builder.rb
|
64
|
+
- lib/lazylist/enumerable.rb
|
65
|
+
- lib/lazylist/version.rb
|
61
66
|
executables: []
|
67
|
+
|
62
68
|
extensions: []
|
69
|
+
|
63
70
|
requirements: []
|
64
|
-
|
71
|
+
|
72
|
+
dependencies:
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: dslkit
|
75
|
+
version_requirement:
|
76
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 0.2.2
|
81
|
+
version:
|