Increasing counter whithin loop?

[...]

There's really no difference between:

for counter in 0...element_size
   ...
end

and

(0...element_size).each do |counter|
   ...
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.

90% of the time you don't need counters or while loops, though.
Even here, there's nothing preventing you from doing e.g.:

skip = false
sequence.each do |element|
   if skip
     skip = false
     next
   end
   ...
   # 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

Hi Kevin,

[for i in ..... end ]

No, you can manipulate the counter just fine, just that it won't persist for
the next iteration of the count. This is because ruby is providing the i for
you, but not actually checking it to know where it is, or when it's done,
unlike similarly worded constructs in C etc.

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

Patrick

Quoting Patrick Gundlach <clr9.10.randomuser@spamgourmet.com>:

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 Gundlach <clr9.10.randomuser@spamgourmet.com>:

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

[...]
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).

> 90% of the time you don't need counters or while loops, though.
> Even here, there's nothing preventing you from doing e.g.:
>
> skip = false
> sequence.each do |element|
> if skip
> skip = false
> next
> end
> ...
> # 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:

···

On Wednesday 07 December 2005 18:07, Patrick Gundlach wrote:

Patrick

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, mental@rydia.net <mental@rydia.net> 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.

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

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)

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?

[...]

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

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

+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

···

On 12/7/05, mental@rydia.net <mental@rydia.net> 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.

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

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

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 Thu, 2005-12-08 at 18:07 +0900, Patrick Gundlach 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(R) 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:

···

On Thursday 08 December 2005 02:07, Patrick Gundlach wrote:

Patrick

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

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

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'

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

···

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

Hi mental,

[...]

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