On Nov 1, 6:36 am, Robert Klemme <shortcut... / googlemail.com> wrote:
> On 01.11.2008 09:58, Yuh-Ruey Chen wrote:
>
> > 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?

That was really just a random example that I thought up quickly to
explain my question. Others have already posted better examples.

> 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.

Per my previous reply, I'm trying to find a way to chain iterators
without nesting blocks (so they can be passed freely into other
functions expecting enumerables) and without intermediate arrays.

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

Hmm, what I'm looking for is not just a simple memoization technique.
Suppose I have a function that computes the union of two arrays
whenever each array is changed. Using a perhaps more possible syntax:

attr_accessor :array_a

def foo
   lazy_union = lazy_expr { lazy_depends(:array_a) +
lazy_depends(:array_b) }
   @array_a = [1,2]
   @array_b = [3,4]
   p lazy_union # [1,2,3,4]
   @array_a[1] = 5
   p lazy_union # [1,5,3,4]
end

This is a case your memoization technique doesn't address. Yet I'm
pretty sure this is a common use case, so I was thinking that there
should be some library out there that provides this functionality.