Is it possible, a fully general Enumerable#recursive ?

R

Robert Klemme

I find this subject extremely confusing. Colin's answer to my last
email is still causing me puzzlement.
:)
I think I see what the confusion is. Your version of recursive looses
all the nested structure of the original. And so works fine for most
cases, as long that is what one wants/expects. I've been trying to do
it such that the nested structure stays intact. e.g.

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

Your method produces:

[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. :)
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
 
C

Colin Bartlett

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

Robert Dober

On Fri, Apr 16, 2010 at 11:20 PM, Colin Bartlett
=A0 "After working on this for far too long, I have concluded that
=A0 =A0a highly generalized implementation of recursion for all
=A0 =A0(or at least most) enumerable methods not feasible.")
I beg to differ
module Enumerable
def recursive_each &blk
each do | ele |
Enumerable =3D=3D=3D ele ? ele.recursive_each(&blk) : blk[ele]
end
end

def rec_enum; enum_for :recursive_each end
end

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

rx =3D x.rec_enum

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

Cheers
R.
--=20
The best way to predict the future is to invent it.
-- Alan Kay
 
C

Caleb Clausen

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.

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
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.
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 Fri, Apr 16, 2010 at 11:20 PM, Colin Bartlett
"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(", ")

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

Intransition

<snip>> =A0 "After working on this for far too long, I have concluded tha= t
=A0 =A0a highly generalized implementation of recursion for all
=A0 =A0(or at least most) enumerable methods not feasible.")

I beg to differ
module Enumerable
=A0 def recursive_each &blk
=A0 =A0 each do | ele |
=A0 =A0 =A0 Enumerable =3D=3D=3D ele ? ele.recursive_each(&blk) : blk[ele= ]
=A0 =A0 end
=A0 end

=A0 def rec_enum; enum_for :recursive_each end
end

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

rx =3D x.rec_enum

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

Robert Dober

Two apologies: first, for not replying sooner to this.
(I was going to do a combined response to this and to what Tom and Rober= t
=A0have said, hence the delay: I then decided that it would be better
=A0to make a reply to you on this specific point, and follow up later
=A0with more general observations. I think I've nearly got a proof of co= ncept
=A0thing working. That said, I - for now - still agree with Tom's commen= t:
=A0 =A0"After working on this for far too long, I have concluded that
=A0 =A0 a highly generalized implementation of recursion for all
=A0 =A0 (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
=A0 =A0 seems to (within #visit) produce the structure of the original a= rray,
=A0 =A0 but with nil values instead of the original values.
=A0 =A0 (Changing #map inside #visit to #each gets back the original val= ues.)

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

It's more like:
=A0(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
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 recursio= n,
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.
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 =3D=3D 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 Fri, Apr 16, 2010 at 11:20 PM, Colin Bartlett
=A0 "After working on this for far too long, I have concluded that
=A0 =A0a highly generalized implementation of recursion for all
=A0 =A0(or at least most) enumerable methods not feasible.")
I beg to differ
module Enumerable
=A0 def recursive_each &blk
=A0 =A0 each do | ele |
=A0 =A0 =A0 Enumerable =3D=3D=3D ele ? ele.recursive_each(&blk) : blk[el= e]
=A0 =A0 end
=A0 end

=A0 def rec_enum; enum_for :recursive_each end
end

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

rx =3D x.rec_enum

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

Sorry, but:

y=3D[9,8,7,[6,5,4,[1,2,3]]]
ry=3Dy.rec_enum
ry.sort =A0#=3D> [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.



--=20
The best way to predict the future is to invent it.
-- Alan Kay
 
A

apeiros

Am 12.04.2010 um 17:51 schrieb Intransition:
recursive.
=20
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.
=20
Do you run into the issue of sorting [1,2,3, [:a,:b,:c]] where by an
error is raised when it tries, 1 <=3D> [: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=
 
A

apeiros

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 <=3D> [:a,:b,:c] ?
=20
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 =3D=3D =
b.class ? a <=3D> b : a.class.to_s <=3D> b.class.to_s } # =3D> =
[["a","b","c"],1,2,3]

Regards
Stefan=
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,156
Messages
2,570,878
Members
47,413
Latest member
KeiraLight

Latest Threads

Top