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 +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:
|