Increasing counter whithin loop?

On Wednesday 07 December 2005 11:51, [email protected] wrote:

else
sequence.each do |i|

Written this way, it’s probably more obvious why incrementing i
doesn’t do what you had expected.

-mental

If you’re really tied to the traditional for loop, you can use a while
loop
and do the increment yourself at the end of the loop, but usually you
find a
better way to do it in Ruby that doesn’t involve going through the chars
one
at a time, and that’s why we rarely use the for construct.

[…]

end
OK, then it makes sense that one cannot manipulate counter. But IMO it
is not intuitive (POLS ;-)) that the counter in the for-loop can’t be
changed.

set skip to true to skip the next iteration


end

Yes, sure, but now it is getting ugly again. I am not looking for any
way to work, but to find a readable, beautiful and ruby-like piece of
code.

Patrick

Quoting Patrick G. [email protected]:

That (last part of the sentence) is exactly what confused me.
Nobody else thinks that this persistance would be useful?

Well, let’s assume we’ve got persistence. What would you expect
each of the following to do?

for i in 1…3
i = 3
end

for i in [ 1, 2, 3 ]
i = 3
end

for s in [ “a”, “b”, “c” ]
s = “c”
end

for s in $stdin
s = “hello”
end

-mental

Quoting Patrick G. [email protected]:

OK, then it makes sense that one cannot manipulate counter. But
IMO it is not intuitive (POLS ;-)) that the counter in the
for-loop can’t be changed.

Maybe think of it like foreach in TCL [ foreach i $things { … } ],
or the one for in Javascript [ for (var i in things) { … } ]
rather than for in C.

As a rule, I think Ruby’s going to be extremely counterintuitive
whenever you expect C-like behavior.

Yes, sure, but now it is getting ugly again. I am not looking for
any way to work, but to find a readable, beautiful and ruby-like
piece of code.

That will probably require thinking about it from a different angle.
The “skip the next (future) element based on the current one” is an
inherently messy concept.

Notching up the counter in a C-style loop is a concise way to do
that, but it’s still not particularly easy to reason about if your
loop is nontrivial.

It seems like the cleaner and more Ruby-esque solutions offered so
far involve attacking it from the other end – “skip the current
element based on the previous one”. It’s clearer to remember
things from the past than it is to reach forward into the future.

Along those lines (this is basically Trans’ suggestion):

a = %w(a b c d e)

prev = nil
a.each do |elt|
puts elt unless prev == “b”
prev = elt
end

Now, we’ll imagine for the moment that Ruby has C-like for loops:

a = %w(a b c d e)

for ( i = 0 ; i < a.size ; i += 1 )
puts a[i]
i += 1 if a[i] == “b”
end

I don’t know. I’m not sure I would find the C version clearer if I
weren’t a grizzled old C veteran.

Out of curiousity, do you think either of these would be clearer?

a.each_with_prev do |elt, prev|
next if prev == “b”
puts elt
end

or

a.each_with_prev do |elt, prev|
puts elt unless prev == “b”
end

Don’t forget that you’re allowed to define convenience methods if
that would clarify things elsewhere:

module Enumerable
def each_with_prev( initial=nil )
prev = initial
each do |elt|
begin
yield elt, prev
ensure # in case of ‘next’
prev = elt
end
end
end
end

A lot of Ruby’s clarity comes not from being able to write things
clearly in the “raw” language, but from being able to easily
customize the language for your needs.

-mental

On 12/7/05, [email protected] [email protected] wrote:

A lot of Ruby’s clarity comes not from being able to write things
clearly in the “raw” language, but from being able to easily
customize the language for your needs.

Beautifully said, and most true. I think your each_with_prev is the
best way to tackle this problem in a clean Ruby-esque way.

Regards,
Ryan

On 12/7/05, [email protected] [email protected] wrote:

A lot of Ruby’s clarity comes not from being able to write things
clearly in the “raw” language, but from being able to easily
customize the language for your needs.

+1

You can also try something like this:

module Enumerable
def each_with_gathered_dups
group = []
each do |elem|
if elem == group.last
group << elem
else
begin
yield group unless group.empty?
ensure
group = [elem]
end
end
end
yield group
end
end

which might work something like this

a = [1, 2, 2, 2, 3, 4, 5, 5, 6, 6, 6, 6]
a.each_with_gathered_dups { |g| p g }

outputing

[1]
[2, 2, 2]
[3]
[4]
[5, 5]
[6, 6, 6, 6]

Notice I chose to wrap the dups as arrays, and single elements as
single element arrays, just another approach, but the lesson is don’t
fight the language, grow it to your problem (and learn how to make
better method names than I do :slight_smile:

pth

That (last part of the sentence) is exactly what confused me.
Nobody else thinks that this persistance would be useful?

Well, let’s assume we’ve got persistence. What would you expect
each of the following to do?

for i in 1…3
i = 3
end

infinite loop.

for i in [ 1, 2, 3 ]
i = 3
end

infinite loop.

for s in [ “a”, “b”, “c” ]
s = “c”
end

infinite loop.

for s in $stdin
s = “hello”
end

infinite loop.

Same as

do

while true

But perhaps I don’t get your question.

Patrick

(please no cc: to me)

OK, then it makes sense that one cannot manipulate counter. But IMO it
is not intuitive (POLS ;-)) that the counter in the for-loop can’t be
changed.

Don’t base your sense of “intuitive” on other languages.

? How else should I expect a behaviour, if not from other languages?

[…]

Use RegExps to do your string processing then.

Yes, but as soon as my elements aren’t strings anymore, I need another
solution.

Patrick

On Wednesday 07 December 2005 18:07, Patrick G. wrote:

[…]
OK, then it makes sense that one cannot manipulate counter. But IMO it
is not intuitive (POLS ;-)) that the counter in the for-loop can’t be
changed.

Don’t base your sense of “intuitive” on other languages. It is more
intuitive
that we’re going through each element in an array than checking an
arbitrary
integer against a condition in between two semicolons, IMHO. And my
first
real language was C++.

The generally “normal” way to do string processing in Ruby is with
Regular
Expressions. This is arguable, but at least I’ve seen it more. Play
with my
gsub example and see if you can’t get something in one line that does
what
you’re looking for (unless you haven’t told us the real situation).

set skip to true to skip the next iteration


end

Yes, sure, but now it is getting ugly again. I am not looking for any
way to work, but to find a readable, beautiful and ruby-like piece of
code.

Use RegExps to do your string processing then. I admit, it’s just NOT
the way
you think about string processing when coming from something like C++,
but
it’s so much easier to say “Find all the repeated letters and replace
them
with double ‘letter’” than “loop through every letter in the string. If
the
next letter in the string is the same as the current one, then replace
both
of them with ‘double letter’ by making a new array, copying the current
over,
replacing the letters, copying the end over, and then incrementing the
count
of the for loop to get the language structure to support what I happen
to be
doing.”

When you’re used to the latter, it’s hard to get your head around
regexps, but
it’s much nicer, IMHO, and I’m one that had to do the adjustment. Play
with
it and see if you can’t get something that works, or let us know what
you’re
really doing, and I’m sure that someone will pop out with a golfed
intuitive
one-ish-liner that fixes your problem. :slight_smile:

Hi mental,

Maybe think of it like foreach in TCL [ foreach i $things { … } ],
or the one for in Javascript [ for (var i in things) { … } ]
rather than for in C.

Hmm, I don’t know tcl nor JS well enough.

As a rule, I think Ruby’s going to be extremely counterintuitive
whenever you expect C-like behavior.

It worked quite well before.

That will probably require thinking about it from a different angle.
The “skip the next (future) element based on the current one” is an
inherently messy concept.

Perhaps more messy than a loop without this feature, but a

next_and_skip_next_element

should be perfectly clear to a reader. I mean, if we have
‘complicated’ constructs such as ‘inject’, we will be able to cope
with ‘skip_next_element’, won’t we?

[…]

for ( i = 0 ; i < a.size ; i += 1 )
puts a[i]
i += 1 if a[i] == “b”
end

You know that those are not the same. The first one is looking back,
the second could look forward.

Out of curiousity, do you think either of these would be clearer?

a.each_with_prev do |elt, prev|
next if prev == “b”
puts elt
end

Definitely, but one can do this already with inject. Well, almost.

A lot of Ruby’s clarity comes not from being able to write things
clearly in the “raw” language, but from being able to easily
customize the language for your needs.

Of course, and that is what I am asking in the whole thread! Is there
a nicer way for this loop… Let’s see. I have this each_with_next
now, but still I am completely unstatisfied with my loop. But I am now
convinced that I have the optimal solution, even if it the way it
looks now. And btw., I need a look-ahead for this specific purpose.
The each_with_previous was a very nice hint, thanks, even if I wasn’t
able to use it. I’d probably have to create each_with_prev_and_next.

module Enumerable
def each_with_next(last=nil)
prev=nil
first=true
each do |elt|
begin
if first
first=false
next
else
yield prev,elt
end
ensure # in case of ‘next’
prev = elt
end
end
yield prev,last
end
end

a=[:a, :b, :b, :c, :b, :d, :e]

skip=false

a.each_with_next do |elt,n|
if skip
skip=false
next
end
if elt==:b && n==:b
puts “double_b”
skip=true
else
puts elt
end
end

Patrick

(please, no cc:)

Hi mental,

Well, let’s assume we’ve got persistence. What would you expect
each of the following to do?

[…]

ok, rethinking and withdrawing my answer…

Your examples are pretty good. I think I come to the conclusion that
it is pretty hard to find a way to manipulate the counter in a for-loop.

This would still allow something like ‘skip next n elements’, since we
are walking along a sequence.

Thanks for these good examples,

Patrick

On Thu, 2005-12-08 at 18:07 +0900, Patrick G. wrote:

Perhaps more messy than a loop without this feature, but a

next_and_skip_next_element

should be perfectly clear to a reader. I mean, if we have
‘complicated’ constructs such as ‘inject’, we will be able to cope
with ‘skip_next_element’, won’t we?

I think you could implement something like that via a customized each
and a catch/throw, yes. It’d be a bit involved but certainly doable.

[ snip ]

You know that those are not the same. The first one is looking back,
the second could look forward.

I guess I misunderstood the goal. I was going for “the cleanest way to
skip elements following those satisfying some condition”, rather than
“the cleanest way to skip elements following those satisfying some
condition”. :slight_smile:

But from your new example it’s apparent that you really do want
iteration-with-lookahead (or an equivalent), and the ability to consume
more than one input item at a time. That sounds suspiciously like
parsing a grammar…

      yield prev,elt
    end
  ensure # in case of 'next'
    prev = elt
  end
end
yield prev,last

end
end

It’s a subtle detail, but you’d probably want to wrap that yield
prev,last in a 1.times {} so retry and next work as expected. You’ve
also got to handle break specially, and handle the empty case.

module Enumerable
def each_with_next(last=nil)
prev = nil
first = true
skip_last = true # skip last iteration if empty
each do |elt|
skip_last = true # skip last iteration if ‘break’ used
begin
if first
first = false
next
else
yield prev, elt
end
ensure # in case of ‘next’
prev = elt
end
skip_last = false # ‘break’ wasn’t used this time around
end
1.times { yield prev, last } unless skip_last
end
end

Messing with the future makes messy code. At least it’s all confined in
one place.

Ah, I did just notice that ‘retry’ won’t work with this, as both ‘next’
and ‘retry’ will hit the ensure clause. That might not be a problem for
your purposes, though. I notice a lot of things (like IO#each) don’t
really support retry either.

Just for fun, here is an alternate approach for the lookahead. You’ll
probably recognize it as the C-like solution, dressed up a bit.

a = %w(a b b c b d e).map { |s| s.intern }

ArrayStream.new(a).each do |elt|
if elt == :b && s.la == :b
puts “double_b”
s.skip
else
puts elt
end
end

Given:

class ArrayStream
include Enumerable
def initialize( array )
@array = array
@pos = 0
end
def empty?
@pos >= @array.size
end
def get
pos = @pos
@pos += 1
@array[pos]
end
def la(offset=1)
@array[@pos+offset]
end
def skip(offset=1)
@pos += offset
end
def each
# ‘retry’ won’t retry, but then that
# doesn’t work for IO#each either
yield get until empty?
end
end

You know, there has GOT to be a simpler way to do this… :slight_smile:

What’s the actual problem you’re trying to solve? I’ll bet there’s a
more “outside the box” solution. As I said, you’re basically parsing a
simple grammar:

GRAMMAR := ( DOUBLE_B | LETTER )*

DOUBLE_B := ‘b’ ‘b’

LETTER := ‘a’ | ‘b’ | ‘c’ | ‘d’ | … | ‘z’

If your input is strings, regexps are the tool for this (it’s a regular
grammar). If it’s something else, you still might want to bring some
parsing-related techniques and/or libraries to bear.

Of course, if your real problem is not much more complex than your
example, I’d say go with the index variable and while loop. :stuck_out_tongue:

-mental

On Thursday 08 December 2005 02:07, Patrick G. wrote:

OK, then it makes sense that one cannot manipulate counter. But IMO it
is not intuitive (POLS ;-)) that the counter in the for-loop can’t be
changed.

Don’t base your sense of “intuitive” on other languages.

? How else should I expect a behaviour, if not from other languages?

Perhaps how Ruby defines the construct, since it’s not another language?

The general idea here is simply that the Ruby way® isn’t about
skipping
elements when going through one at a time. That’s why this construct
doesn’t
work like you’re expecting, because there are more Ruby-ish ways to
accomplish it, but do please come up with an example where this is the
best
way to do it, and I’ll be more than happy to calm down and stop whining
at
you. :slight_smile:

[…]

Use RegExps to do your string processing then.

Yes, but as soon as my elements aren’t strings anymore, I need another
solution.

Ok, so what are your elements? :wink:

Hi mental,

[…]

Your two solutions are very nice, thank you. I think I’ll go for the
each_with_next.

What’s the actual problem you’re trying to solve? I’ll bet there’s a
more “outside the box” solution.

I have to print out some schedule. I create the parts by having a new
specialized class parse some xml source, do some calculation and then
output each. Let’s say, one part of this schedule is a list of
members. So I have

class ListOfMembers
def parse(src)
# …
end
def render
# …
end
end

There are other items in the schedule that I need to output. I collect
all these parts in an array:

@schedule << ListOfMember.new.parse(the source)

and output all like

@schedule.each { |elt| elt.render }

but in case of two ‘ListOfMembers’ following each other, they should be
output next to each other, so I wrap them in a ‘Group of two
ListOfMembers’

This is why I want to have a lookahead and a skip_next element to skip
the second ‘ListOfMembers’ if the first one is already one of those
and pass both to ‘render two LOM’. It would have been easy in

for i in 0…schedule.size
if schedule[i]==LOM && schedule[i+1]==LOM
# do the two LOM thing
i += 1
else
schedule[i].render
end
end

Your ArrayStream looks very much like that, I begin to like it!

Thanks for your answer,

Patrick

Hi mental,

[…]

output next to each other, so I wrap them in a ‘Group of two ListOfMember=
s’

Hmm. What happens if you have three in a row?

‘this can’t happen’

Seems to me you could do something like this:

collapsed = []
@schedule.each do |elt|

yes, but this involves another step which reduces readability a bit.

Thanks again,

Patrick

On Mon, 2005-12-12 at 21:07 +0900, Patrick G. wrote:

Your two solutions are very nice, thank you. I think I’ll go for the
each_with_next.

Cool. Note that (per the recent thread with Matz) I was wrong about
needing to program the iterator defensively for redo/next. Those
confine their effects to the block, and you’d handle retry/break with
‘ensure’ like you would other exits (e.g. exceptions), but normally you
don’t need to care.

Anyway, this is sufficient for each_with_next:

module Enumerable
def each_with_next(last=nil)
prev = nil
first = true
each do |elt|
if first
first = false
next
else
yield prev, elt
end
prev = elt
end
yield prev, last unless first
end
end

but in case of two ‘ListOfMembers’ following each other, they should be
output next to each other, so I wrap them in a ‘Group of two ListOfMembers’

Hmm. What happens if you have three in a row?

Seems to me you could do something like this:

collapsed = []
@schedule.each do |elt|
prev = collapsed.last
if LOM === prev && LOM === elt
grp = GOLM.new
grp.add( prev )
grp.add( elt )
collapsed[-1] = grp
elsif GOLM === prev && LOM === elt
prev.add( elt )
else
collapsed << elt
end
end
collapsed.each { |elt| elt.render }

Lookbehind + a little mutation. No magic iterators required…

-mental