From: Colin Bartlett on
On Sat, Apr 10, 2010 at 7:05 PM, Intransition <transfire(a)gmail.com> wrote:
> On Apr 9, 6:38 pm, Colin Bartlett <colin...(a)googlemail.com> wrote:
>> I'm not sure that it is a bug. ...
>
> Thanks, that helped me understand a little better.
>
> After working on this for far too long, I have concluded that a highly
> generalized implementation of recursion for all (or at least most)
> enumerable methods not feasible.
http://www.askoxford.com/concise_oed/feasible?view=uk
feasible: adjective 1 possible and practical to achieve easily or
conveniently. 2 informal likely.
I think that's the precise word: I'd concluded that it was probably possible,
but probably too complicated, and I was having difficulties getting things
to work with "to_enum" in a consistent way, or indeed sometime at all.
(For example, a recursive Array#map! worked, but a recursive Array#map didn't.)

> But there are still many things in common between recursion methods,
> so instead I have created a Recursor class
> to act something like an Enumerator for recursive operations.
The idea of having a Recursor class hadn't occurred to me,
and that seems (at first thoughts) quite possibly fruitful as a solution.

From: Robert Klemme on
On 04/09/2010 08:53 PM, Intransition wrote:
>
> On Apr 9, 2:03 pm, Robert Dober <robert.do...(a)gmail.com> wrote:
>> On Thu, Apr 8, 2010 at 5:51 AM, David Masover <ni...(a)slaphack.com> wrote:
>>> On Wednesday 07 April 2010 04:49:15 pm Intransition wrote:
>> <snip>
>>
>>>> This works for #each and #map but not #sort.
>>> #sort isn't a method of Enumerable, it's a method of Array.
>> ruby-1.9.1-p378 > Enumerable.instance_methods.grep /sort/
>> => [:sort, :sort_by]
>> However you will need to define #<=> on the return value of recursive.

Why that? The whole point would be to sort all elements that would be
recursively returned. The #recursive return value would never be seen then.

> Yep. That's trick b/c comparing and array or other enumerable to a non
> enumerable raises an error. So sorting with this is probably out of
> the question. On a side note, I am not so sure that raising an error
> is best. Why not just assume that too non-comparable things are equal?

See above.

Here are my two solutions with plain old Enumerator approach. I do not
see the benefit of a Functor here.

1. simple

module Enumerable
def recursive(&b)
if b
each do |e|
if Enumerable === e
e.recursive(&b)
else
b[e]
end
end
self
else
Enumerator.new(self, :recursive)
end
end
end

2. catch loops

module Enumerable
def recursive(items = {}, &b)
if b
each do |e|
items.fetch e.object_id do |oid|
items[oid] = e

if Enumerable === e
e.recursive(items, &b)
else
b[e]
end
end
end
self
else
Enumerator.new(self, :recursive)
end
end
end

This one suffers the fragility that callers providing something else
than an empty Hash can break it. That could be fixed with a bit more
effort by using a thread local. Just using the simple approach is
probably better since there is no notification of loops anyway so the
using code has zero chance to handle the case of loops which might be
important to know.

Another note: your original code suffered from the inability to mix
different Enumerables because you use "self.class" for the type check.
I changed that by only testing for Enumerable.

Kind regards

robert

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
From: Robert Dober on
On Sun, Apr 11, 2010 at 10:55 PM, Robert Klemme
<shortcutter(a)googlemail.com> wrote:
> On 04/09/2010 08:53 PM, Intransition wrote:
>>
>> On Apr 9, 2:03 pm, Robert Dober <robert.do...(a)gmail.com> wrote:
eed to define #<=>  on the return value of recursive.
>
> Why that?  The whole point would be to sort all elements that would be
> recursively returned.  The #recursive return value would never be seen then.
#each takes care of structure #<=> takes care of order, IOW
#recursive is called from #each, or did I miss something here?
Cheers
R.


--
Learning without thought is labor lost; thought without learning is perilous.”
--- Confucius

From: Robert Klemme on
2010/4/12 Robert Dober <robert.dober(a)gmail.com>:
> On Sun, Apr 11, 2010 at 10:55 PM, Robert Klemme
> <shortcutter(a)googlemail.com> wrote:
>> On 04/09/2010 08:53 PM, Intransition wrote:
>>>
>>> On Apr 9, 2:03 pm, Robert Dober <robert.do...(a)gmail.com> wrote:
> eed to define #<=>  on the return value of recursive.
>>
>> Why that?  The whole point would be to sort all elements that would be
>> recursively returned.  The #recursive return value would never be seen then.
>  #each takes care of structure #<=> takes care of order, IOW

As far as I understand the initial posting of this thread the whole
point is to create "something" (aka an Enumerator) whose #each method
recursively iterates through nested Enumerables. This implies that
during that iteration only elements inside Enumerables are yielded
which are not Enumerable. Hence there is no need to provide a
generalized <=> because x.recursive.sort only ever sees those non
Enumerable elements. Of course, sort only can work properly if all
elements have the same type, are compare compatible or an explicit
comparison block is passed to #sort.

> #recursive is called from #each, or did I miss something here?

Technically speaking yes, but more correct would be to say that
#recursive is invoked from #recursive. (=> my implementation for an
example)

I don't really understand why this thread seems to be so complicated.
I suspect that either I am missing something or not understanding
properly, or other contributors to this thread are missing something.
:-)

Kind regards

robert

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

From: Intransition on
On Apr 11, 4:55 pm, Robert Klemme <shortcut...(a)googlemail.com> wrote:

> >> However you will need to define #<=>  on the return value of recursive.
>
> Why that?  The whole point would be to sort all elements that would be
> recursively returned.  The #recursive return value would never be seen then.

Do you run into the issue of sorting [1,2,3, [:a,:b,:c]] where by an
error is raised when it tries, 1 <=> [:a,:b,:c] ?

> > Yep. That's trick b/c comparing and array or other enumerable to a non
> > enumerable raises an error. So sorting with this is probably out of
> > the question. On a side note, I am not so sure that raising an error
> > is best. Why not just assume that too non-comparable things are equal?
>
> See above.
>
> Here are my two solutions with plain old Enumerator approach.  I do not
> see the benefit of a Functor here.
>
> 1. simple
>
> module Enumerable
>    def recursive(&b)
>      if b
>        each do |e|
>          if Enumerable === e
>            e.recursive(&b)
>          else
>            b[e]
>          end
>        end
>        self
>      else
>        Enumerator.new(self, :recursive)
>      end
>    end
> end

Yes, that's the basic definition. It does have issues though. For one,
the Enumerator isn't very useful, as it doesn't seem able to handle
the recursion very will (compare #with_index and #map, etc.). Another
issue is hashes, they won't iterate well here. In Ruby 1.8.7 or less
you also get an infinite loop b/c String's are Enumerable, so an
exception really needs to be made for them. Other than all that it
works ;-)

> 2. catch loops
>
> module Enumerable
>    def recursive(items = {}, &b)
>      if b
>        each do |e|
>          items.fetch e.object_id do |oid|
>            items[oid] = e
>
>            if Enumerable === e
>              e.recursive(items, &b)
>            else
>              b[e]
>            end
>          end
>        end
>        self
>      else
>        Enumerator.new(self, :recursive)
>      end
>    end
> end

Nice use of #fetch, btw.

> This one suffers the fragility that callers providing something else
> than an empty Hash can break it.  That could be fixed with a bit more
> effort by using a thread local.  Just using the simple approach is
> probably better since there is no notification of loops anyway so the
> using code has zero chance to handle the case of loops which might be
> important to know.

Is it important? recursive graphs seem so rare anyway, and if your
doing an recursive iteration wouldn't an infinite loop be expected?
Just as if one were iterating over an infinite sequence?

> Another note: your original code suffered from the inability to mix
> different Enumerables because you use "self.class" for the type check.
> I changed that by only testing for Enumerable.

I have two versions actually, one testing for Enumerable and one
testing for self.class, I think both are useful. But you are right, to
address the original question (of the previous thread on this topic)
testing for Enumerable is the one needed..

First  |  Prev  |  Next  |  Last
Pages: 1 2 3 4 5 6
Prev: what is the meaning of \A and \Z
Next: win32ole question