lazylist 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES ADDED
@@ -0,0 +1,9 @@
1
+ 2004-09-30 * 0.1.2 * Added combine method.
2
+ * The ref method now uses a hash to cache return values.
3
+ This is not what I would still call a list datastructure.
4
+ It increases memory consumption (as if that would matter
5
+ anymore), too. But it speeds up things a great deal, if
6
+ you persist on using indexes into a list.
7
+ * Rakefile added
8
+ * Supports Rubygems now
9
+ 2003-09-12 * 0.1.1 * Initial Release
data/GPL ADDED
@@ -0,0 +1,340 @@
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ Preamble
10
+
11
+ The licenses for most software are designed to take away your
12
+ freedom to share and change it. By contrast, the GNU General Public
13
+ License is intended to guarantee your freedom to share and change free
14
+ software--to make sure the software is free for all its users. This
15
+ General Public License applies to most of the Free Software
16
+ Foundation's software and to any other program whose authors commit to
17
+ using it. (Some other Free Software Foundation software is covered by
18
+ the GNU Library General Public License instead.) You can apply it to
19
+ your programs, too.
20
+
21
+ When we speak of free software, we are referring to freedom, not
22
+ price. Our General Public Licenses are designed to make sure that you
23
+ have the freedom to distribute copies of free software (and charge for
24
+ this service if you wish), that you receive source code or can get it
25
+ if you want it, that you can change the software or use pieces of it
26
+ in new free programs; and that you know you can do these things.
27
+
28
+ To protect your rights, we need to make restrictions that forbid
29
+ anyone to deny you these rights or to ask you to surrender the rights.
30
+ These restrictions translate to certain responsibilities for you if you
31
+ distribute copies of the software, or if you modify it.
32
+
33
+ For example, if you distribute copies of such a program, whether
34
+ gratis or for a fee, you must give the recipients all the rights that
35
+ you have. You must make sure that they, too, receive or can get the
36
+ source code. And you must show them these terms so they know their
37
+ rights.
38
+
39
+ We protect your rights with two steps: (1) copyright the software, and
40
+ (2) offer you this license which gives you legal permission to copy,
41
+ distribute and/or modify the software.
42
+
43
+ Also, for each author's protection and ours, we want to make certain
44
+ that everyone understands that there is no warranty for this free
45
+ software. If the software is modified by someone else and passed on, we
46
+ want its recipients to know that what they have is not the original, so
47
+ that any problems introduced by others will not reflect on the original
48
+ authors' reputations.
49
+
50
+ Finally, any free program is threatened constantly by software
51
+ patents. We wish to avoid the danger that redistributors of a free
52
+ program will individually obtain patent licenses, in effect making the
53
+ program proprietary. To prevent this, we have made it clear that any
54
+ patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ GNU GENERAL PUBLIC LICENSE
60
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+ 0. This License applies to any program or other work which contains
63
+ a notice placed by the copyright holder saying it may be distributed
64
+ under the terms of this General Public License. The "Program", below,
65
+ refers to any such program or work, and a "work based on the Program"
66
+ means either the Program or any derivative work under copyright law:
67
+ that is to say, a work containing the Program or a portion of it,
68
+ either verbatim or with modifications and/or translated into another
69
+ language. (Hereinafter, translation is included without limitation in
70
+ the term "modification".) Each licensee is addressed as "you".
71
+
72
+ Activities other than copying, distribution and modification are not
73
+ covered by this License; they are outside its scope. The act of
74
+ running the Program is not restricted, and the output from the Program
75
+ is covered only if its contents constitute a work based on the
76
+ Program (independent of having been made by running the Program).
77
+ Whether that is true depends on what the Program does.
78
+
79
+ 1. You may copy and distribute verbatim copies of the Program's
80
+ source code as you receive it, in any medium, provided that you
81
+ conspicuously and appropriately publish on each copy an appropriate
82
+ copyright notice and disclaimer of warranty; keep intact all the
83
+ notices that refer to this License and to the absence of any warranty;
84
+ and give any other recipients of the Program a copy of this License
85
+ along with the Program.
86
+
87
+ You may charge a fee for the physical act of transferring a copy, and
88
+ you may at your option offer warranty protection in exchange for a fee.
89
+
90
+ 2. You may modify your copy or copies of the Program or any portion
91
+ of it, thus forming a work based on the Program, and copy and
92
+ distribute such modifications or work under the terms of Section 1
93
+ above, provided that you also meet all of these conditions:
94
+
95
+ a) You must cause the modified files to carry prominent notices
96
+ stating that you changed the files and the date of any change.
97
+
98
+ b) You must cause any work that you distribute or publish, that in
99
+ whole or in part contains or is derived from the Program or any
100
+ part thereof, to be licensed as a whole at no charge to all third
101
+ parties under the terms of this License.
102
+
103
+ c) If the modified program normally reads commands interactively
104
+ when run, you must cause it, when started running for such
105
+ interactive use in the most ordinary way, to print or display an
106
+ announcement including an appropriate copyright notice and a
107
+ notice that there is no warranty (or else, saying that you provide
108
+ a warranty) and that users may redistribute the program under
109
+ these conditions, and telling the user how to view a copy of this
110
+ License. (Exception: if the Program itself is interactive but
111
+ does not normally print such an announcement, your work based on
112
+ the Program is not required to print an announcement.)
113
+
114
+ These requirements apply to the modified work as a whole. If
115
+ identifiable sections of that work are not derived from the Program,
116
+ and can be reasonably considered independent and separate works in
117
+ themselves, then this License, and its terms, do not apply to those
118
+ sections when you distribute them as separate works. But when you
119
+ distribute the same sections as part of a whole which is a work based
120
+ on the Program, the distribution of the whole must be on the terms of
121
+ this License, whose permissions for other licensees extend to the
122
+ entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.
279
+
280
+ END OF TERMS AND CONDITIONS
281
+
282
+ How to Apply These Terms to Your New Programs
283
+
284
+ If you develop a new program, and you want it to be of the greatest
285
+ possible use to the public, the best way to achieve this is to make it
286
+ free software which everyone can redistribute and change under these terms.
287
+
288
+ To do so, attach the following notices to the program. It is safest
289
+ to attach them to the start of each source file to most effectively
290
+ convey the exclusion of warranty; and each file should have at least
291
+ the "copyright" line and a pointer to where the full notice is found.
292
+
293
+ <one line to give the program's name and a brief idea of what it does.>
294
+ Copyright (C) <year> <name of author>
295
+
296
+ This program is free software; you can redistribute it and/or modify
297
+ it under the terms of the GNU General Public License as published by
298
+ the Free Software Foundation; either version 2 of the License, or
299
+ (at your option) any later version.
300
+
301
+ This program is distributed in the hope that it will be useful,
302
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
+ GNU General Public License for more details.
305
+
306
+ You should have received a copy of the GNU General Public License
307
+ along with this program; if not, write to the Free Software
308
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
309
+
310
+
311
+ Also add information on how to contact you by electronic and paper mail.
312
+
313
+ If the program is interactive, make it output a short notice like this
314
+ when it starts in an interactive mode:
315
+
316
+ Gnomovision version 69, Copyright (C) year name of author
317
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
318
+ This is free software, and you are welcome to redistribute it
319
+ under certain conditions; type `show c' for details.
320
+
321
+ The hypothetical commands `show w' and `show c' should show the appropriate
322
+ parts of the General Public License. Of course, the commands you use may
323
+ be called something other than `show w' and `show c'; they could even be
324
+ mouse-clicks or menu items--whatever suits your program.
325
+
326
+ You should also get your employer (if you work as a programmer) or your
327
+ school, if any, to sign a "copyright disclaimer" for the program, if
328
+ necessary. Here is a sample; alter the names:
329
+
330
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
331
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
332
+
333
+ <signature of Ty Coon>, 1 April 1989
334
+ Ty Coon, President of Vice
335
+
336
+ This General Public License does not permit incorporating your program into
337
+ proprietary programs. If your program is a subroutine library, you may
338
+ consider it more useful to permit linking proprietary applications with the
339
+ library. If this is what you want to do, use the GNU Library General
340
+ Public License instead of this License.
data/README.en ADDED
@@ -0,0 +1,27 @@
1
+ Installation
2
+ ============
3
+
4
+ Just type into the command line as root:
5
+
6
+ # ruby install.rb
7
+
8
+ Documentation
9
+ =============
10
+
11
+ The API documentatin of this library is can be produced by typing:
12
+ $ ruby makedoc.rb
13
+
14
+ You should also look into the files in the examples directory to get an idea
15
+ how this library is used. It is also interesting to examine test.rb for this
16
+ reason.
17
+
18
+ Author
19
+ ======
20
+
21
+ Florian Frank <flori@ping.de>
22
+
23
+ License
24
+ =======
25
+
26
+ GNU General Public License (GPL)
27
+
data/Rakefile ADDED
@@ -0,0 +1,71 @@
1
+ # Rakefile for File::Tail -*- ruby -*-
2
+
3
+ require 'rake/gempackagetask'
4
+ require 'rbconfig'
5
+
6
+ include Config
7
+
8
+ PKG_NAME = 'lazylist'
9
+ PKG_VERSION = File.read('VERSION').chomp
10
+ PKG_FILES = Dir.glob("**/*").delete_if { |item|
11
+ item.include?("CVS") or item.include?("pkg")
12
+ }
13
+
14
+ desc "Installing library"
15
+ task :install do
16
+ libdir = CONFIG["sitelibdir"]
17
+ install('lib/lazylist.rb', libdir)
18
+ end
19
+
20
+ spec = Gem::Specification.new do |s|
21
+
22
+ #### Basic information.
23
+
24
+ s.name = 'lazylist'
25
+ s.version = PKG_VERSION
26
+ s.summary = "Implementation of lazy lists for Ruby"
27
+ s.description = ""
28
+
29
+ #### Dependencies and requirements.
30
+
31
+ #s.add_dependency('log4r', '> 1.0.4')
32
+ #s.requirements << ""
33
+
34
+ s.files = PKG_FILES
35
+
36
+ #### C code extensions.
37
+
38
+ #s.extensions << "ext/extconf.rb"
39
+
40
+ #### Load-time details: library and application (you will need one or both).
41
+
42
+ s.require_path = 'lib' # Use these for libraries.
43
+ s.autorequire = 'lazylist'
44
+
45
+ #s.bindir = "bin" # Use these for applications.
46
+ #s.executables = ["bla.rb"]
47
+ #s.default_executable = "bla.rb"
48
+
49
+ #### Documentation and testing.
50
+
51
+ s.has_rdoc = true
52
+ #s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a
53
+ #s.rdoc_options <<
54
+ # '--title' << 'Rake -- Ruby Make' <<
55
+ # '--main' << 'README' <<
56
+ # '--line-numbers'
57
+ s.test_files << 'tests/test.rb'
58
+
59
+ #### Author and project details.
60
+
61
+ s.author = "Florian Frank"
62
+ s.email = "flori@ping.de"
63
+ s.homepage = "http://lazylist.rubyforge.org"
64
+ s.rubyforge_project = "lazylist"
65
+ end
66
+
67
+ Rake::GemPackageTask.new(spec) do |pkg|
68
+ pkg.need_tar = true
69
+ pkg.package_files += PKG_FILES
70
+ end
71
+ # vim: set et sw=4 ts=4:
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.2
@@ -0,0 +1,64 @@
1
+ require 'lazylist'
2
+
3
+ puts "Random number lazy list"
4
+ rand = lambda { |s| ((s * 1103515245 + 12345) / 65536) % 32768 }
5
+ r = LazyList.iterate(rand[666], &rand)
6
+ r.each(10) { |x| print x, " " } ; puts
7
+ r = LazyList.iterate(rand[666], &rand).mapper { |x| 1 + x % 6 }
8
+ r.each(10) { |x| print x, " " } ; puts
9
+
10
+ puts "Prime number lazy list with filter"
11
+ prime = LazyList[10000..1000000].filter do |x|
12
+ not (2..Math.sqrt(x).to_i).find { |d| x % d == 0 }
13
+ end
14
+ prime.each(10) { |x| print x, " " } ; puts
15
+ p prime[1]
16
+ puts
17
+
18
+ puts "Squared prime numbers with mapper"
19
+ prime.mapper { |x| x ** 2 }.each(5) { |x| print x, " " } ; puts
20
+ puts
21
+
22
+ puts "Lazy Lists from mathematical sequences"
23
+ a196 = LazyList.iterate(35) { |x| x + x.to_s.reverse.to_i }
24
+ a196.each(10) { |x| print x, " " } ; puts
25
+ hailstone = LazyList.iterate(7) { |x| x % 2 == 0 ? x / 2 : 3 * x + 1 }
26
+ hailstone.each(20) { |x| print x, " " } ; puts
27
+ terras = LazyList.iterate(7) { |x| x % 2 == 0 ? x / 2 : (3 * x + 1) / 2 }
28
+ terras.each(20) { |x| print x, " " } ; puts
29
+ wolfram = LazyList.iterate(7) do |x|
30
+ ((3.0 / 2) * (x % 2 == 0 ? x : x + 1)).to_i
31
+ end
32
+ wolfram.each(15) { |x| print x, " " } ; puts
33
+ puts
34
+
35
+ puts "Fibonacci lazy list with recursion"
36
+ fib = LazyList.tabulate(0) { |x| x < 2 ? 1 : fib[x-2] + fib[x-1] }
37
+ fib.each(10) { |x| print x, " " } ; puts
38
+ p fib[100]
39
+ puts
40
+
41
+ puts "Sum up odd numbers lazylist to get a squares stream"
42
+ odd = LazyList.tabulate(1) { |x| 2 * x - 1 }
43
+ puts odd
44
+ squares = LazyList.tabulate(0) do |x|
45
+ (0..x).inject(0) { |s, i| s + odd[i] }
46
+ end
47
+ puts squares
48
+ squares.each(10) { |x| print x, " " } ; puts
49
+ puts squares
50
+ puts odd
51
+ puts
52
+
53
+ puts "Lazy lists from io objects"
54
+ me = LazyList.io(File.new($0)) { |io| io.readline }
55
+ me.each(6) { |line| puts line }
56
+ p me[55]
57
+ puts me.length
58
+ puts
59
+
60
+ me = LazyList[File.new($0)]
61
+ me.each(6) { |line| puts line }
62
+ p me[61]
63
+ puts me.length
64
+ # vim: set noet sw=4 ts=4:
@@ -0,0 +1,18 @@
1
+ require 'lazylist'
2
+
3
+ # Computes the hamming sequence: that is the sequence of natural numbers that
4
+ # are dividable only by 1, 2, 3 or 5.
5
+ hamming = LazyList.new(1) do
6
+ hamming.mapper { |x| 2 * x }.merge(
7
+ hamming.mapper { |x| 3 * x }.merge(
8
+ hamming.mapper { |x| 5 * x }))
9
+ end
10
+
11
+ max = (ARGV.shift || 100).to_i
12
+ hamming.each(max) do |x|
13
+ print x, " "
14
+ STDOUT.flush
15
+ end
16
+ puts
17
+ print hamming[1000], ", ", hamming[1001], "\n"
18
+ # vim: set noet sw=4 ts=4:
data/examples/pi.rb ADDED
@@ -0,0 +1,32 @@
1
+ require 'lazylist'
2
+
3
+ # This spigot algorithm computes an unbounded number of digits of Pi and
4
+ # uses a lazy list to save them.
5
+ #
6
+ # References:
7
+ # - Jeremy Gibbons (2003). An Unbounded Spigot Algorithm for the Digits of Pi.
8
+ # http://web.comlab.ox.ac.uk/oucl/work/jeremy.gibbons/publications/spigot.pdf
9
+ #
10
+
11
+ def f(q,r,t, k)
12
+ n = (3 * q + r) / t
13
+ if n == (4 * q + r) / t
14
+ LazyList.new(n) { f(10 * q, 10 * (r - n * t), t, k) }
15
+ else
16
+ f(q * k, q * (4 * k + 2) + r * (2 * k + 1), t * (2 * k + 1), k + 1)
17
+ end
18
+ end
19
+
20
+ max = ARGV.empty? ? nil : ARGV.shift.to_i
21
+ pi = f(1, 0, 1, 1) # Setup my lazy list
22
+ sum = pi[0, 1000].inject(0) do |s, i| s += i end
23
+ puts "Sum of the first 1000 digitis of pi: #{sum}"
24
+ puts "500th digit using memoized computation: #{pi[499]}"
25
+
26
+ puts "Printing #{max ? "the first #{max}" : "all the "} digits of pi:"
27
+ pi.each!(max) do |x|
28
+ STDOUT.print x
29
+ STDOUT.flush
30
+ end
31
+ puts
32
+ # vim: set noet sw=4 ts=4:
data/examples/sieve.rb ADDED
@@ -0,0 +1,22 @@
1
+ require 'lazylist'
2
+
3
+ # Sieve or Eratosthenes with filters on Lazy Lists. It has a very nice
4
+ # notation, but is a real memory and cpu hog. Enjoy!
5
+
6
+ class LazyList
7
+
8
+ def prime_filter
9
+ LazyList.new(head) do
10
+ tail.filter { |x| x % head != 0 }.prime_filter
11
+ end
12
+ end
13
+
14
+ end
15
+
16
+ max = (ARGV.shift || 100).to_i
17
+ LazyList.tabulate(2) { |x| x }.prime_filter.each(max) do |x|
18
+ print x, " "
19
+ STDOUT.flush
20
+ end
21
+ puts
22
+ # vim: set noet sw=4 ts=4:
data/install.rb ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rbconfig'
4
+ include Config
5
+ require 'fileutils'
6
+ include FileUtils::Verbose
7
+
8
+ libdir = CONFIG["sitelibdir"]
9
+ file = "lib/lazylist.rb"
10
+ install(file, libdir)
11
+ # vim: set et sw=2 ts=2:
data/lib/lazylist.rb ADDED
@@ -0,0 +1,396 @@
1
+ # = lazylist.rb - Implementation of lazy lists for Ruby
2
+ #
3
+ # == Author
4
+ #
5
+ # Florian Frank mailto:flori@ping.de
6
+ #
7
+ # == Description
8
+ #
9
+ # This class implements lazy lists (or streams) for Ruby. Such lists avoid the
10
+ # computation of values which aren't needed for some computation. So it's
11
+ # possible to define infinite lists with a limited amount of memory. A value
12
+ # that hasn't been used yet is calculated on the fly and saved into the list.
13
+ # A value which is used for a second time is computed only once and just read
14
+ # out of memory for the second usage.
15
+ #
16
+ # == Example
17
+ #
18
+ # To compute the square numbers with a lazy list you can define one as
19
+ #
20
+ # sq = LazyList.tabulate(1) { |x| x * x }
21
+ #
22
+ # Now it's possible to get the first 10 square numbers by calling
23
+ # LazyList#take
24
+ #
25
+ # sq.take(10)
26
+ # ==>[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
27
+ #
28
+ # To compute every square number and do something with it you can call the
29
+ # "each" method:
30
+ #
31
+ # sq.each { |x| puts x }
32
+ #
33
+ # Notice that calls to each will not return if applied to infinite lazylists.
34
+ #
35
+ # You can also use indices on lazy lists to get the values at a certain range:
36
+ #
37
+ # sq[ 0..9 ] or sq[0, 10]
38
+ #
39
+ # ==>[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
40
+ #
41
+ # To spare memory it's possible to throw away every element after it was
42
+ # fetched:
43
+ #
44
+ # sq.take!(1) => [1]
45
+ # sq.take!(1) => [4]
46
+ #
47
+ # Of course it's also possible to compute more complex lists like the Fibonacci
48
+ # sequence:
49
+ #
50
+ # fib = LazyList.tabulate(0) { |x| x < 2 ? 1 : fib[x-2] + fib[x-1] }
51
+ #
52
+ # fib[100] => 573147844013817084101
53
+ # computes the 99th Fibonacci number. (We always start with index 0.)
54
+ # fib[101] => 927372692193078999176
55
+ # computes the 100th Fibonacci number. The already computed values are reused
56
+ # to compute this result. That's a very transparent way to get memoization for
57
+ # sequences that require heavy computation.
58
+ #
59
+ # You create LazyLists that are based on arbitrary Enumerables, so can
60
+ # for example wrap your passwd file in one pretty easily:
61
+ #
62
+ # pw = LazyList[ File.new("/etc/passwd") ]
63
+ #
64
+ # Call grep to find the users root and flori:
65
+ # pw.grep /^(root|flori):/ => ["root:x:0:0:...\n", "flori:x:500:500...\n"]
66
+ #
67
+ # In this case the whole passwd file is slurped into the memory. If
68
+ # you use
69
+ # pw.find { |x| x =~ /^root:/ } => "root:x:0:0:root:/root:/bin/bash\n"
70
+ # instead, only every line until the root line is loaded into the memory.
71
+ #
72
+ # == References
73
+ #
74
+ # A very good introduction into lazy lists can be found in the scheme bible
75
+ # Structure and Interpretation of Computer Programs (SICP)
76
+ # [http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-24.html#%25_sec_3.5]
77
+ #
78
+
79
+ class LazyList
80
+
81
+ # ReadQueue is the implementation of an read-only queue that only supports
82
+ # #pop and #empty? methods. It's used as a wrapper to encapsulate
83
+ # enumerables in LazyLists.
84
+ class ReadQueue
85
+
86
+ # Creates an ReadQueue object from an enumerable.
87
+ def initialize(enumerable)
88
+ @enumerable = enumerable
89
+ prepare
90
+ end
91
+
92
+ # Extracts the top element from the queue or nil if the queue is
93
+ # empty.
94
+ def pop
95
+ top = @current
96
+ callcc { |@break| @continue.call } unless empty?
97
+ top
98
+ end
99
+
100
+ # Returns true if the queue is empty.
101
+ def empty?
102
+ @continue == false
103
+ end
104
+
105
+ private
106
+
107
+ def prepare
108
+ @break = lambda {}
109
+ @enumerable.each do |x|
110
+ @current = x
111
+ callcc do |@continue|
112
+ @break.call
113
+ return
114
+ end
115
+ end
116
+ @continue = false
117
+ @break.call
118
+ end
119
+
120
+ end
121
+
122
+ include Enumerable # OK. Infinite lazy lists aren't really enumerable...
123
+
124
+ # Exceptions raised by the LazyList implementation.
125
+ class Exception < ::Exception; end
126
+
127
+ # Creates a new LazyList element. The tail can be given either as
128
+ # second argument or as block.
129
+ def initialize(head, tail = nil, &block_tail)
130
+ @ref_cache = {}
131
+ if tail
132
+ raise LazyList::Exception.new(
133
+ "Use block xor second argument for constructor") if block_tail
134
+ @head, @tail = head, tail
135
+ elsif block_tail
136
+ @head, @tail = head, block_tail
137
+ end
138
+ end
139
+
140
+ # Returns the value of this element.
141
+ attr_accessor :head
142
+ protected :head=
143
+
144
+ # Writes a tail value.
145
+ attr_writer :tail
146
+ protected :tail=
147
+
148
+ # Returns the next element by computing its value if necessary.
149
+ def tail
150
+ if @tail.is_a? Proc
151
+ @tail = @tail[@head]
152
+ end
153
+ @tail
154
+ end
155
+
156
+ # Returns the tail of this list without dooing evaluation.
157
+ def peek_tail
158
+ @tail
159
+ end
160
+ protected :peek_tail
161
+
162
+ # Denotes the empty LazyList which is a guard at the end of finite
163
+ # LazyLists.
164
+ Empty = new(nil, nil)
165
+
166
+ # Identity lambda expression, mostly used as a default.
167
+ Identity = lambda { |x| x }
168
+
169
+ # Returns a finite LazyList which is generated by LazyList.from_enum or
170
+ # LazyList.span. (See below.)
171
+ def self.[](a, n = nil)
172
+ if n
173
+ span(a, n)
174
+ else
175
+ from_enum(a)
176
+ end
177
+ end
178
+
179
+ # Generates a finite LazyList from any datastructure e which
180
+ # responds to the #each method.
181
+ def self.from_enum(e)
182
+ oq = ReadQueue.new(e)
183
+ return Empty if oq.empty?
184
+ next_top = lambda do
185
+ if oq.empty?
186
+ Empty
187
+ else
188
+ new(oq.pop, next_top)
189
+ end
190
+ end
191
+ new(oq.pop, next_top)
192
+ end
193
+
194
+ # Generates a finite LazyList beginning with element a and spanning
195
+ # n elements. The datastructure members have to support the
196
+ # successor method succ.
197
+ def self.span(a, n)
198
+ if n > 0
199
+ new(a) { span(a.succ, n - 1) }
200
+ else
201
+ Empty
202
+ end
203
+ end
204
+
205
+ # Generates a LazyList which tabulates every element beginning with n
206
+ # and succeding with succ by calling the Proc object f or the given block.
207
+ # If none is given the identity function is computed instead.
208
+ def self.tabulate(n, &f)
209
+ f = Identity unless f
210
+ new(f[n]) { tabulate(n.succ, &f) }
211
+ end
212
+
213
+ # Generates a LazyList which iterates over its previous values
214
+ # computing something like: f(i), f(f(i)), f(f(f(i))), ...
215
+ def self.iterate(i, &f)
216
+ new(i) { iterate(f[i], &f) }
217
+ end
218
+
219
+ # Generates a LazyList of a give IO-object using a given
220
+ # block or Proc object to read from this object.
221
+ def self.io(io, &f)
222
+ io.eof? ? Empty : new(f[io]) { io(io, &f) }
223
+ end
224
+
225
+ # Returns the n-th LazyList-Object.
226
+ def ref(n)
227
+ if @ref_cache.key?(n)
228
+ return @ref_cache[n]
229
+ end
230
+ s = self
231
+ i = n
232
+ while i > 0 do
233
+ if s.empty?
234
+ return @ref_cache[n] = nil
235
+ end
236
+ s = s.tail
237
+ i -= 1
238
+ end
239
+ @ref_cache[n] = s
240
+ end
241
+ private :ref
242
+
243
+ # If n is a Range every element in this range is returned.
244
+ # If n isn't a Range object the element at index n is returned.
245
+ # If m is given the next m elements beginning the n-th element are
246
+ # returned.
247
+ def [](n, m = nil)
248
+ if n.is_a? Range
249
+ n.map { |i| ref(i).head }
250
+ elsif n < 0
251
+ nil
252
+ elsif m
253
+ s = ref(n)
254
+ s ? s.take(m) : nil
255
+ else
256
+ ref(n).head rescue nil
257
+ end
258
+ end
259
+
260
+ # Iterates over all elements. If n is given only n iterations are done.
261
+ # If self is a finite LazyList each returns also if there are no more
262
+ # elements to iterate over.
263
+ def each(n = nil)
264
+ s = self
265
+ while (n.nil? or n > 0) and not s.empty? do
266
+ yield s.head
267
+ s = s.tail
268
+ n -= 1 unless n.nil?
269
+ end
270
+ s
271
+ end
272
+
273
+ # Similar to LazyList#each but destroys elements from past
274
+ # iterations perhaps saving some memory.
275
+ def each!(n = nil)
276
+ s = self
277
+ while (n.nil? or n > 0) and not s.empty? do
278
+ yield s.head
279
+ s = s.tail
280
+ n -= 1 unless n.nil?
281
+ @head, @tail = s.head, s.tail
282
+ end
283
+ self
284
+ end
285
+
286
+ # Filters only elements for which predicate p is true.
287
+ def filter(&p)
288
+ p = Identity unless p
289
+ s = self
290
+ until s.empty? or p[s.head] do
291
+ s = s.tail
292
+ end
293
+ return Empty if s.empty?
294
+ self.class.new(s.head) { s.tail.filter(&p) }
295
+ end
296
+
297
+ # Creates a new Lazylist that maps the block or Proc object f to every
298
+ # element in the old list.
299
+ def mapper(&f)
300
+ return Empty if empty?
301
+ f = Identity unless f
302
+ self.class.new(f[head]) { tail.mapper(&f) }
303
+ end
304
+
305
+ # Merges this LazyList with the other. It uses the &compare block to decide
306
+ # which elements to place first in the result LazyList. If no compare block
307
+ # is given lambda { |a,b| a < b } is used as a default value.
308
+ def merge(other, &compare)
309
+ compare = lambda { |a,b| a < b } unless compare
310
+ return other if empty?
311
+ return self if other.empty?
312
+ if compare[head, other.head]
313
+ self.class.new(head) { tail.merge(other, &compare) }
314
+ elsif compare[other.head, head]
315
+ self.class.new(other.head) { merge(other.tail, &compare) }
316
+ else
317
+ self.class.new(head) { tail.merge(other.tail, &compare) }
318
+ end
319
+ end
320
+
321
+ # Combines this LazyList with the other to a new LazyList.
322
+ # The elements of the resulting lists are computed
323
+ # by calling the binary operator block on the two elemnts
324
+ # of the input lists. E.g. lambda { |x, y| x + y } adds
325
+ # the two input lists.
326
+ def combine(other, &operator)
327
+ self.class.new(operator.call(head, other.head)) do
328
+ tail.combine(other.tail, &operator)
329
+ end
330
+ end
331
+
332
+ # Takes the next n elements and returns them as an array.
333
+ def take(n)
334
+ result = []
335
+ each(n) { |x| result << x }
336
+ result
337
+ end
338
+
339
+ # Takes the next n elements and returns them as an array. It destroys these
340
+ # elements in the LazyList.
341
+ def take!(n)
342
+ result = []
343
+ each!(n) { |x| result << x }
344
+ result
345
+ end
346
+
347
+ # Drops the next n elements and returns the rest of this LazyList. n
348
+ # defaults to 1.
349
+ def drop(n)
350
+ each(n) { }
351
+ end
352
+
353
+ # Drops the next n elements, destroys them in the LazyList and
354
+ # returns the rest of this LazyList.
355
+ def drop!(n)
356
+ each!(n) { }
357
+ end
358
+
359
+ # Returns the size. This is only sensible if the LazyList is finite
360
+ # of course.
361
+ def size
362
+ inject(0) { |s,| s += 1 }
363
+ end
364
+
365
+ alias length size
366
+
367
+ # Returns true if this is the empty LazyList.
368
+ def empty?
369
+ self == Empty
370
+ end
371
+
372
+ # Inspects the list as far as it has been computed by returning
373
+ # a string of the form [1, 2, 3,... ].
374
+ def inspect
375
+ result = "["
376
+ s = self
377
+ until s.empty?
378
+ case s.peek_tail
379
+ when Empty
380
+ result << s.head.inspect
381
+ break
382
+ when Proc
383
+ result << s.head.inspect << ",... "
384
+ break
385
+ else
386
+ result << s.head.inspect << ", "
387
+ s = s.tail
388
+ end
389
+ end
390
+ result << "]"
391
+ end
392
+
393
+ alias to_s inspect
394
+
395
+ end
396
+ # vim: set et sw=2 ts=2:
data/make_doc.rb ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $outdir = 'doc/'
4
+ puts "Creating documentation in '#$outdir'."
5
+ system "rdoc -o #$outdir lib/lazylist.rb"
6
+ # vim: set et sw=2 ts=2:
data/tests/test.rb ADDED
@@ -0,0 +1,235 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'lazylist'
5
+ require 'tempfile'
6
+
7
+ class MyEnum
8
+
9
+ include Enumerable
10
+
11
+ def initialize(n)
12
+ @n = n
13
+ end
14
+
15
+ def each(&block) (1..@n).each(&block) end
16
+
17
+ end
18
+
19
+ class TC_LazyList < Test::Unit::TestCase
20
+
21
+ def setup
22
+ @strings = LazyList.tabulate("a")
23
+ @natural = LazyList.tabulate(1)
24
+ @ones = LazyList.iterate(1) { 1 }
25
+ @oddp = lambda { |x| x % 2 == 1 }
26
+ @odd = @natural.filter(&@oddp)
27
+ @evenp = lambda { |x| x % 2 == 0 }
28
+ @boolean = @natural.mapper { |x| x % 2 == 0}
29
+ @even = @natural.filter(&@evenp)
30
+ @finite_inner0 = MyEnum.new(0)
31
+ @finite0 = LazyList[@finite_inner0]
32
+ @finite_inner1 = MyEnum.new(1)
33
+ @finite1 = LazyList[@finite_inner1]
34
+ @finite_inner10 = MyEnum.new(10)
35
+ @finite10 = LazyList[@finite_inner10]
36
+ @finite_span = LazyList.span("A", 10)
37
+ @finite_span_generated = ("A".."J").to_a
38
+ end
39
+
40
+ def test_constructor
41
+ ll1 = LazyList.new(:foo, LazyList::Empty)
42
+ assert(!ll1.empty?)
43
+ ll2 = LazyList.new(:foo) { Empty }
44
+ assert(!ll2.empty?)
45
+ assert_raises(LazyList::Exception) do
46
+ ll3 = LazyList.new(:foo, :argh) { Empty }
47
+ end
48
+ end
49
+
50
+ def test_read_queue
51
+ @read_queue0 = LazyList::ReadQueue.new(1..0)
52
+ @read_queue1 = LazyList::ReadQueue.new(1..1)
53
+ @read_queue10 = LazyList::ReadQueue.new(1..10)
54
+ assert(@read_queue0.empty?)
55
+ assert_equal(nil, @read_queue0.pop)
56
+ assert(!@read_queue1.empty?)
57
+ assert_equal(1, @read_queue1.pop)
58
+ assert(!@read_queue10.empty?)
59
+ for i in 1..10 do
60
+ assert_equal(i, @read_queue10.pop)
61
+ end
62
+ end
63
+
64
+ def test_finite
65
+ assert_equal(@finite_inner0.to_a, @finite0.to_a)
66
+ assert_equal(@finite_inner1.to_a, @finite1.to_a)
67
+ assert_equal(@finite_inner10.to_a, @finite10.to_a)
68
+ assert_equal(@finite_span_generated, @finite_span.to_a)
69
+ end
70
+
71
+ def test_size
72
+ assert_equal(0, @finite0.size)
73
+ assert_equal(1, @finite1.size)
74
+ assert_equal(10, @finite10.size)
75
+ assert_equal(10, @finite_span.size)
76
+ assert_equal(0, @finite0.length)
77
+ assert_equal(1, @finite1.length)
78
+ assert_equal(10, @finite10.length)
79
+ assert_equal(10, @finite_span.length)
80
+ end
81
+
82
+ def test_filter
83
+ assert_equal(1, @odd[0])
84
+ assert_equal(3, @odd[1])
85
+ assert_equal(5, @odd[2])
86
+ assert_equal((1..19).select(&@oddp), @odd[0, 10])
87
+ assert_equal((1..10).to_a, @natural[0, 10])
88
+ assert_equal([ 1 ] * 10, @ones[0, 10].to_a)
89
+ ends_with_a = @strings.filter { |x| x[-1] == ?a }
90
+ assert(ends_with_a[0, 27] ==
91
+ [ "a", ("a".."z").map { |x| x + "a" } ].flatten)
92
+ end
93
+
94
+ def test_map
95
+ id = @natural.mapper
96
+ assert_equal(1, id[0])
97
+ assert_equal(2, id[1])
98
+ assert_equal(3, id[2])
99
+ assert_equal((1..10).map, id[0, 10])
100
+ assert_equal((1..10).to_a, @natural[0, 10])
101
+ squaredf = lambda { |x| x ** 2 }
102
+ squared = @natural.mapper(&squaredf)
103
+ assert_equal(1, squared[0])
104
+ assert_equal(4, squared[1])
105
+ assert_equal(9, squared[2])
106
+ assert_equal((1..10).map(&squaredf), squared[0, 10])
107
+ assert_equal((1..10).to_a, @natural[0, 10])
108
+ strangef = lambda { |x| x * (x[0] - ?a + 1) }
109
+ strange = @strings.mapper(&strangef)
110
+ assert_equal("a", strange[0])
111
+ assert_equal("bb", strange[1])
112
+ assert_equal("ccc", strange[2])
113
+ assert_equal(("a".."z").map(&strangef), strange[0, 26])
114
+ assert_equal(("a".."z").to_a, @strings[0, 26])
115
+ end
116
+
117
+ def test_index
118
+ assert_equal(nil, @natural[-1])
119
+ assert_equal(1, @natural[0])
120
+ assert_equal(2, @natural[1])
121
+ assert_equal(nil, @natural[-1, 10])
122
+ assert_equal((1..10).to_a, @natural[0, 10])
123
+ assert_equal((6..15).to_a, @natural[5, 10])
124
+ assert_equal((1..1).to_a, @natural[0..0])
125
+ assert_equal((1..0).to_a, @natural[0..-1])
126
+ assert_equal((1...1).to_a, @natural[0...0])
127
+ assert_equal((1...0).to_a, @natural[0...-1])
128
+ assert_equal((1..10).to_a, @natural[0..9])
129
+ assert_equal((6..15).to_a, @natural[5..14])
130
+ assert_equal((1..10).to_a, @natural[0...10])
131
+ assert_equal((6..15).to_a, @natural[5...15])
132
+ end
133
+
134
+ def test_merge
135
+ natural = @even.merge(@odd)
136
+ assert_equal(@natural[0, 10].to_a, natural[0, 10].to_a)
137
+ natural = @odd.merge(@even)
138
+ assert_equal(@natural[0, 10].to_a, natural[0, 10].to_a)
139
+ double_list = @natural.merge(@natural) { |a,b| a <= b }
140
+ assert(double_list[0, 10].to_a, (1..5).map { |x| [x, x] }.flatten)
141
+ odd2 = @natural.filter(&@oddp).drop(1)
142
+ some = @even.merge(odd2)
143
+ assert_equal(@natural[1, 9].to_a, some[0, 9].to_a)
144
+ end
145
+
146
+ def test_take_drop
147
+ assert_equal([ ], @odd.take(0))
148
+ assert_equal([ 1, 3, 5 ], @odd.take(3))
149
+ assert_equal([ 1, 3, 5 ], @odd.take(3))
150
+ assert_equal([ ], @odd.take!(0))
151
+ assert_equal([ 1 ], @odd.take(1))
152
+ assert_equal([ 1 ], @odd.take!(1))
153
+ assert_equal([ 3, 5, 7 ], @odd.take(3))
154
+ assert_equal([ 3, 5 ], @odd.take!(2))
155
+ assert_equal([ 7, 9, 11 ], @odd.take(3))
156
+ assert_equal([ 7, 9, 11 ], @odd.drop(0).take(3))
157
+ assert_equal([ 7, 9, 11 ], @odd.take(3))
158
+ assert_equal([ 9, 11, 13 ], @odd.drop(1).take(3))
159
+ assert_equal([ 7, 9, 11 ], @odd.take(3))
160
+ assert_equal([ 11, 13, 15 ], @odd.drop(2).take(3))
161
+ assert_equal([ 7, 9, 11 ], @odd.take(3))
162
+ @odd.drop!(0)
163
+ assert_equal([ 7, 9, 11 ], @odd.take(3))
164
+ @odd.drop!(1)
165
+ assert_equal([ 9, 11, 13 ], @odd.take(3))
166
+ @odd.drop!(2)
167
+ assert_equal([ 13, 15, 17 ], @odd.take(3))
168
+ end
169
+
170
+ def test_io
171
+ @tempfile0 = Tempfile.new("test")
172
+ 1.upto(0) do |i|
173
+ @tempfile0.puts i
174
+ end
175
+ @tempfile0.close
176
+ @tempfile0_list = LazyList[File.new(@tempfile0.path)]
177
+ @tempfile10 = Tempfile.new("test")
178
+ 1.upto(10) do |i|
179
+ @tempfile10.puts i
180
+ end
181
+ @tempfile10.close
182
+ @tempfile10_list = LazyList[File.new(@tempfile10.path)]
183
+ assert_equal(0, @tempfile0_list.size)
184
+ assert_equal([], @tempfile0_list.to_a)
185
+ assert_equal(10, @tempfile10_list.size)
186
+ assert_equal((1..10).map { |x| x.to_s + "\n" },
187
+ @tempfile10_list.to_a)
188
+ temp = LazyList.io(File.new(@tempfile0.path)) do |io|
189
+ io.readline
190
+ end
191
+ content = temp.inject([]) { |c, line| c << line }
192
+ assert_equal([], content)
193
+ temp = LazyList.io(File.new(@tempfile10.path)) do |io|
194
+ io.readline
195
+ end
196
+ content = temp.inject([]) { |c, line| c << line }
197
+ assert_equal((1..10).map { |x| x.to_s + "\n" }, content)
198
+ end
199
+
200
+ def test_iterate
201
+ f = LazyList.iterate(5) do |x|
202
+ if x % 2 == 0
203
+ x / 2
204
+ else
205
+ 5 * x + 1
206
+ end
207
+ end
208
+ assert_equal(nil, f[-1])
209
+ assert_equal(5, f[0])
210
+ assert_equal(26, f[1])
211
+ assert_equal([5, 26, 13, 66, 33, 166, 83, 416], f[0, 8])
212
+ end
213
+
214
+ def test_inpsect
215
+ list = LazyList[1..11]
216
+ assert_equal("[]", LazyList::Empty.inspect)
217
+ assert_equal("[1,... ]", list.inspect)
218
+ list[1]
219
+ assert_equal("[1, 2,... ]", list.inspect)
220
+ list[2]
221
+ assert_equal("[1, 2, 3,... ]", list.inspect)
222
+ list[9]
223
+ assert_equal("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10,... ]", list.inspect)
224
+ list.to_a
225
+ assert_equal("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]", list.inspect)
226
+ end
227
+
228
+ def test_combine
229
+ combined = @natural.combine(@ones) { |x, y| x + y }
230
+ assert_equal((12..21).to_a, combined[10,10])
231
+ assert_equal((2..11).to_a, combined[0,10])
232
+ end
233
+
234
+ end
235
+ # vim: set noet sw=4 ts=4:
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.1
3
+ specification_version: 1
4
+ name: lazylist
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.2
7
+ date: 2004-09-30
8
+ summary: Implementation of lazy lists for Ruby
9
+ require_paths:
10
+ - lib
11
+ author: Florian Frank
12
+ email: flori@ping.de
13
+ homepage: http://lazylist.rubyforge.org
14
+ rubyforge_project: lazylist
15
+ description: ''
16
+ autorequire: lazylist
17
+ default_executable:
18
+ bindir: bin
19
+ has_rdoc: true
20
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
21
+ requirements:
22
+ -
23
+ - ">"
24
+ - !ruby/object:Gem::Version
25
+ version: 0.0.0
26
+ version:
27
+ platform: ruby
28
+ files:
29
+ - README.en
30
+ - CHANGES
31
+ - GPL
32
+ - examples
33
+ - Rakefile
34
+ - VERSION
35
+ - install.rb
36
+ - make_doc.rb
37
+ - lib
38
+ - tests
39
+ - examples/examples.rb
40
+ - examples/hamming.rb
41
+ - examples/pi.rb
42
+ - examples/sieve.rb
43
+ - lib/lazylist.rb
44
+ - tests/test.rb
45
+ test_files:
46
+ - tests/test.rb
47
+ rdoc_options: []
48
+ extra_rdoc_files: []
49
+ executables: []
50
+ extensions: []
51
+ requirements: []
52
+ dependencies: []