Is it possible, a fully general Enumerable#recursive?

On 04/12/2010 07:00 PM, Caleb C. wrote:

On Apr 12, 11:08 am, Robert K. [email protected] wrote:

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

I find this subject extremely confusing. Colin’s answer to my last
email is still causing me puzzlement.

:slight_smile:

[3,2,1,[6,5,4,[9,8,7]].recursive.sort #=> [1,2,3,4,5,6,7,8,9]

The former proves much more difficult.

Yeah, Intransition’s way is what I understood to be the point of this
thread…

Probably because he is the OP. :slight_smile:

Intransition, how would you handle this:
[9,8,7,[6,5,4,[3,2,1]].recursive.sort #=> [1,2,3,[4,5,6,[7,8,9]]

Clearly possible, but I have trouble seeing how you’re going to make
that happen easily.

The question is: is this desirable? How do you compare a collection
with individual values etc.? You also cannot easily provide a block to
#sort because then you need might have to do complex type
differentiations etc.

I have often found that too much recursion is confusing. This whole
problem is going to be a lot easier if it’s structured as an external
iterator first and foremost. I have an extern iterator library
(sequence) maybe I can figure out how to do this within that. (I don’t
really have a good grip on how Enumerator works, myself…)

I also start getting doubts whether all this is a good idea - either my
way or Tom’s way. My way suffers from loss of structure information as
well as it requires compatible types as leaves on all levels. His way
makes sorting difficult since you do not have a uniform type any more.
It’s probably better to provide classes for the nesting on a case by
case basis.

Kind regards

robert

On Fri, Apr 16, 2010 at 11:20 PM, Colin B.
[email protected] wrote:

“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.”)
I beg to differ
module Enumerable
def recursive_each &blk
each do | ele |
Enumerable === ele ? ele.recursive_each(&blk) : blk[ele]
end
end

def rec_enum; enum_for :recursive_each end
end

x=[ :a, 42, [:b, :c], {:a=> 42, :b => [1,2]}]

rx = x.rec_enum

p rx.to_a
p rx.any?{ |x| x > 41 rescue false }
p rx.map{ |x| “<#{x}>” }.join(", ")

Cheers
R.

On 4/16/10, Colin B. [email protected] wrote:

a highly generalized implementation of recursion for all

(2) Irrespective of the internal results from #visit, Enumerable#map
collects the successive results of the block for Enumerable#map
into a new (flat) array.

It’s more like:
(3) Too much recursion just makes my brain hurt.

It’s not really your failing… I just have difficulty understanding
these things. It’s not like your code was particularly complicated,
but I couldn’t keep all the implications straight in my head.

Does the following reduce the puzzlement?
[snip]
Does that help in any way?

Yes, some. Thanks for the explanation

I have often found that too much recursion is confusing.
Yes - me too! My test version of a “general” recursive enumerator
looks more like C (or Fortran!) code than Ruby.
I sort of instinctively think in terms of what’s underlying the recursion,
which can get in the way of me just using recursion,
when just using recursion might well be easier!

Ah, good. I’m glad I’m not the only one. There’s a good reason why I
refuse outright to look seriously at functional languages.

This whole problem is going to be a lot easier if it’s structured
as an external iterator first and foremost.
I have an extern iterator library (sequence)
maybe I can figure out how to do this within that.
(I don’t really have a good grip on how Enumerator works, myself…)

Provided I understand more or less correctly what you mean by
an “extern iterator library”, then it might be something that
I could understand!

Basically, external iterator == no recursion. Enumerator is an
external iterator.

I’m still trying to work out (in my copious free time, hah!) how this
would work and if it’s worth it. An external iterator has to keep an
‘index’ internally so it knows where it is in the data structure. In
this case, the ‘index’ is not a simple integer, its something more
like an xpath.

On 4/16/10, Robert D. [email protected] wrote:

  Enumerable === ele ? ele.recursive_each(&blk) : blk[ele]

p rx.to_a
p rx.any?{ |x| x > 41 rescue false }
p rx.map{ |x| “<#{x}>” }.join(", ")

Sorry, but:

y=[9,8,7,[6,5,4,[1,2,3]]]
ry=y.rec_enum
ry.sort #=> [1, 2, 3, 4, 5, 6, 7, 8, 9], should be
[1,2,3,[4,5,6,[7,8,9]]]

On Apr 16, 5:44 pm, Robert D. [email protected] wrote:

  Enumerable === ele ? ele.recursive_each(&blk) : blk[ele]

p rx.to_a
p rx.any?{ |x| x > 41 rescue false }
p rx.map{ |x| “<#{x}>” }.join(", ")

Yes and no. See my previous post about preserving structure.

On Mon, Apr 12, 2010 at 6:00 PM, Caleb C. [email protected]
wrote:

On Apr 12, 11:08 am, Robert K. [email protected] wrote:

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.
:slight_smile:
Colin’s comment: Or possibly both apply? (I think that’s true for me!
:-))

I find this subject extremely confusing.
Colin’s answer to my last email is still causing me puzzlement.
Two apologies: first, for not replying sooner to this.
(I was going to do a combined response to this and to what Tom and
Robert
have said, hence the delay: I then decided that it would be better
to make a reply to you on this specific point, and follow up later
with more general observations. I think I’ve nearly got a proof of
concept
thing working. That said, I - for now - still agree with Tom’s comment:
“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.”)

Second, sorry for causing puzzlement. I (mostly) try not to be
deliberately obfuscatory. Was it (1) or (2) (or both!)
of my reply to your post that was/is causing puzzlement?

(1) Using #map inside #visit combined with using Enumerable#map
seems to (within #visit) produce the structure of the original
array,
but with nil values instead of the original values.
(Changing #map inside #visit to #each gets back the original
values.)

(2) Irrespective of the internal results from #visit, Enumerable#map
collects the successive results of the block for Enumerable#map
into a new (flat) array.

Does the following reduce the puzzlement?

Taking point (2) of my “puzzling” post first, I was trying to say
that I didn’t think that there was a bug
for two - or rather one and a half - reasons.

The full reason, rewording (2): I think (at least with hindsight:
I’m not sure that I would think this if I was looking at
the documentation and code without this thread prompting it)
Tom’s code was producing the behaviour expected according to
the Ruby 1.9 documentation: that is Enumerable:map returns
a “flat” array built up from successive elements yielded
from an iteration, and that the Enumerable:map returned array
will be “flat” (and is intended to be “flat”)
even if the underlying iteration is recursive.

The half reason is that as I understand it, the basic behavior
of Enumerator and Enumerable assumes that the underlying
iteration stream is “flat”.
You can set up a recursive iteration, and use that with Enumerator
and Enumerable, but the recursion is hidden from Enumerator/Enumerable.
So the “expected” behaviour from an Enumerator/Enumerable method
is similar to what you’d expect from running the method against
a flat array. (If anyone knows different, please correct that.)

So, turning to (1), I think the question is not:
why is array.visit.map flattening the result?
but rather:
why is array.visit{block} preserving the nesting in the array?

What I was trying to say was that because Tom (I think correctly
for what he was wanting to do) was using Array#map inside
his recursive iterator, then internally to the iterator
the nesting structure of the arrays was preserved (but that
for reasons I didn’t understand with values changed to nil),
but that also internally a “flat” array of the iterated elements
(possibly with different values from the returned value from
the yield) was being built up, and that for Enumerable#map
it is that “flat” array that is returned.

Does that help in any way?

I have often found that too much recursion is confusing.
Yes - me too! My test version of a “general” recursive enumerator
looks more like C (or Fortran!) code than Ruby.
I sort of instinctively think in terms of what’s underlying the
recursion,
which can get in the way of me just using recursion,
when just using recursion might well be easier!

This whole problem is going to be a lot easier if it’s structured
as an external iterator first and foremost.
I have an extern iterator library (sequence)
maybe I can figure out how to do this within that.
(I don’t really have a good grip on how Enumerator works, myself…)

Provided I understand more or less correctly what you mean by
an “extern iterator library”, then it might be something that
I could understand!

Am 12.04.2010 um 17:51 schrieb Intransition:

On Apr 11, 4:55 pm, Robert K. [email protected] 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] ?

How does recursively sorting [1,2,3, [:a,:b,:c]] make any sense? That’s
just as broken as trying to sort [:a, “b”, 3, Time.now] - those values
are not comparable and sort will raise.
I can see sorting e.g. [[2,5,4],[3,2,1]] recursively making sense,
though, the result being [[1,2,3],[2,4,5]]. But the above should just
raise, anything else makes IMO no sense.

Regards
Stefan

On Sat, Apr 17, 2010 at 1:17 AM, Caleb C. [email protected]
wrote:

"After working on this for far too long, I have concluded that
(Changing #map inside #visit to #each gets back the original values.)
but I couldn’t keep all the implications straight in my head.
looks more like C (or Fortran!) code than Ruby.

maybe I can figure out how to do this within that.
would work and if it’s worth it. An external iterator has to keep an
(or at least most) enumerable methods not feasible.")

y=[9,8,7,[6,5,4,[1,2,3]]]
ry=y.rec_enum
ry.sort #=> [1, 2, 3, 4, 5, 6, 7, 8, 9], should be [1,2,3,[4,5,6,[7,8,9]]]

Why? What is the purpose of rec_enum then?
R.

Am 17.04.2010 um 12:48 schrieb apeiros:

Am 12.04.2010 um 17:51 schrieb Intransition:

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

How does recursively sorting [1,2,3, [:a,:b,:c]] make any sense? That’s just as broken as trying to sort [:a, “b”, 3, Time.now] - those values are not comparable and sort will raise.
I can see sorting e.g. [[2,5,4],[3,2,1]] recursively making sense, though, the result being [[1,2,3],[2,4,5]]. But the above should just raise, anything else makes IMO no sense.

Addendum: of course I’m talking about sort without a block. If you
supply a block to sort, it’d be entirely possible:
[3,1,2, [“b”,“c”,“a”]].recursively.sort { |a,b| a.class == b.class ? a
<=> b : a.class.to_s <=> b.class.to_s } # => [[“a”,“b”,“c”],1,2,3]

Regards
Stefan