Two questions:
1) Are there equivalents for iteration/enumeration functions like map
that return iterator/enumeration objects (in a Python sense)?
An example:
def read_files(files)
files.each {|file|
# blah
}
end
read_file(['file1', 'file2', 'file3'].map_iter {|x| open(x)})
# map_iter would return an iterator object immediately instead of
opening every file and storing them into an array
There is one issue with this design: the IO object is not closed properly.
I know that I could do this instead:
['file1', 'file2', 'file3'].each {|x|
open(x) {
# blah
}
}
but sometimes it's inconvenient to do that if there's already a
function that accepts an Enumerable such as read_files above.
So read_files expects an enumeration of opened IO objects, I guess. And you want to make sure that files are only opened on demand, correct? You could do something like this
module Enumerable
FileIter = Struct.new :enum do
include Enumerable
def each(&b)
enum.each do |file_name|
File.open(file_name, &b)
end
self
end
end
def file_iter
FileIter.new self
end
end
Robert@babelfish2 ~
$ echo 1 > a
Robert@babelfish2 ~
$ echo 2 > b
irb(main):016:0> def read_files(files)
irb(main):017:1> files.each {|io| p io.read}
irb(main):018:1> end
=> nil
irb(main):019:0> read_files %w{a b}.file_iter
"1\n"
"2\n"
=> #<struct Enumerable::FileIter enum=["a", "b"]>
irb(main):020:0>
Note that there is ARGF which basically does a similar thing: it opens and closes all files named in ARGV and presents them as a single IO object.
Your approach feels a bit off the usual Ruby way and I am suspecting that you are trying to force foreign patterns into the language.
I'd also say there is a design issue with your version of read_files: basically it iterates over a collection of file handles and does something to each one of them. Given the elegance and shortness of Ruby's iteration idiom the function read_files would rather be called read_file and process a single IO only.
2) Is there some lazy evaluation library that can recalculate lazy
expression when values change? Something with the following semantics
(or something like it):
x = lazy {a + b}
a = 1
b = 2
p x # 3 (calculates a + b)
This cannot work IMHO since local variables a and b are defined *after* the block.
p x # 3 (memoized value)
a = 3
p x # 5 (recalculates a + b)
a = [1,2]
b = [3,4]
p x # [1,2,3,4] (recalculates a + b)
p x # [1,2,3,4] (memoized value)
a[1] = '.'
p x # [1,'.',3,4] (recalculates a + b)
I know there's a library called lazy.rb, but it's not exactly what I'm
looking for as it doesn't implement this functionality.
IMHI you better explicitly provide arguments to whatever you use to lazily calculate values. One way is memoize and another option is to use a Hash for this if there is just one argument:
irb(main):023:0> square = Hash.new {|h,k| puts "called"; h[k] = k * k}
=> {}
irb(main):024:0> square[1000000]
called
=> 1000000000000
irb(main):025:0> square[1000000]
=> 1000000000000
irb(main):026:0> square[1000000]
=> 1000000000000
irb(main):027:0>
If you have more arguments, you can do
def lazy(&b)
cache = {}
lambda {|*a| cache[a] ||= b[*a]}
end
irb(main):006:0> plus2 = lazy {|a,b| puts "called #{a} + #{b}"; a+b}
=> #<Proc:0x7ff8c2d8@(irb):3>
irb(main):007:0> plus2[1,2]
called 1 + 2
=> 3
irb(main):008:0> plus2[1,2]
=> 3
irb(main):009:0> plus2[2,1]
called 2 + 1
=> 3
irb(main):010:0> plus2[2,1]
=> 3
irb(main):011:0>
Kind regards
robert
···
On 01.11.2008 09:58, Yuh-Ruey Chen wrote: