Increasing counter whithin loop?

Hi,

a very basic question...

I'd like to output the sequence "a b d e", by testing if the current
element is == "b" then skip the next element and continue the loop. The
obvious solution doesn't look rubyish to me, how could I use the first
or second attempt to get the desired solution?

Patrick

···

--------------------------------------------------
a=%w( a b c d e )

# incorrect, outputs "a b c d e"
0.upto(a.size - 1) do |i|
  puts a[i]
  if a[i]=="b"
    # skip next element
    # but i won't get affected
    i += 1
  end
end

# incorrect, outputs "a b c d e"
for i in 0...a.size
  puts a[i]
  if a[i]=="b"
    # skip next element
    # but i won't get affected
    i += 1
  end
end

# incorrect, outputs nothing... is there a next_next ?
a.each do |elt|
  puts elt
  if elt=="b"
    # skip next element
    # ??
  end
end

# this one works, but is ugly
i=0
while i < a.size
  puts a[i]
  if a[i]=="b"
    # skip next element
    i += 1
  end
  i += 1
end

a=%w( a b c d e )

seen = false

a.each {|elem|

unless seen
#do stuff
puts elem
seen = true if elem == "b"
else
seen = false
end
}

cheers,
Pavel

···

--
Posted via http://www.ruby-forum.com/.

Patrick Gundlach wrote:

Hi,

a very basic question...

I'd like to output the sequence "a b d e", by testing if the current
element is == "b" then skip the next element and continue the loop.
The obvious solution doesn't look rubyish to me, how could I use the
first or second attempt to get the desired solution?

Patrick
--------------------------------------------------
a=%w( a b c d e )

a=%w( a b c d e )

=> ["a", "b", "c", "d", "e"]

a.each do |elt|

?> next if elt=="b"

  puts elt
end

a
c
d
e
=> ["a", "b", "c", "d", "e"]

Kind regards

    robert

Patrick Gundlach:

I'd like to output the sequence "a b d e", by testing if the current
element is == "b" then skip the next element and continue the loop.

I do not know whether I understood correctly what you want, but you could
try this:

array = [ :a, :b, :c, :d, :e ]
array.each_with_index do |x, i|
  next if array[i-1] == :b
  print x, ' '
end

Malte

Patrick Gundlach wrote:

Hi,

a very basic question...

I'd like to output the sequence "a b d e", by testing if the current
element is == "b" then skip the next element and continue the loop. The
obvious solution doesn't look rubyish to me, how could I use the first
or second attempt to get the desired solution?

array = [1, :skip, :hidden, 2, :skip, :hidden, 3, :skip]

# Zipped to ensure i is uniquely index()ed
# Also makes sure use array non-destructively
zip = array.zip
for i in zip
  if i[0] == :skip
    zip.delete_at(zip.index(i) + 1)
  else
    puts i[0]
  end
end

Output:

1
2
3

···

--
Neil Stevens - neil@hakubi.us

'A republic, if you can keep it.' -- Benjamin Franklin

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

temp = nil

p a.select {|x| if temp == "b" then temp = nil; next end; temp = x;true}

outputs

["a", "b", "d", "e"]

Probably could be cleaner.

Patrick Gundlach wrote:

···

Hi,

a very basic question...

I'd like to output the sequence "a b d e", by testing if the current
element is == "b" then skip the next element and continue the loop. The
obvious solution doesn't look rubyish to me, how could I use the first
or second attempt to get the desired solution?

Patrick
--------------------------------------------------
a=%w( a b c d e )

# incorrect, outputs "a b c d e"
0.upto(a.size - 1) do |i|
  puts a[i]
  if a[i]=="b"
    # skip next element
    # but i won't get affected
    i += 1
  end
end

# incorrect, outputs "a b c d e"
for i in 0...a.size
  puts a[i]
  if a[i]=="b"
    # skip next element
    # but i won't get affected
    i += 1
  end
end

# incorrect, outputs nothing... is there a next_next ?
a.each do |elt|
  puts elt
  if elt=="b"
    # skip next element
    # ??
  end
end

# this one works, but is ugly
i=0
while i < a.size
  puts a[i]
  if a[i]=="b"
    # skip next element
    i += 1
  end
  i += 1
end

Maybe I'm just being thick, but wouldn't it just be easier (and much
closer to what you are actually trying to do) to say

["a", "b", "c", "d", "e"].each do |x|
     puts x unless x == "c"
end

I'll admit I'm also new to Ruby but isn't the above the more "rubyish"
way to achieve what you're trying to do?

···

On 12/5/05, Patrick Gundlach <clr9.10.randomuser@spamgourmet.com> wrote:

Hi,

a very basic question...

I'd like to output the sequence "a b d e", by testing if the current
element is == "b" then skip the next element and continue the loop. The
obvious solution doesn't look rubyish to me, how could I use the first
or second attempt to get the desired solution?

Patrick
--------------------------------------------------
a=%w( a b c d e )

# incorrect, outputs "a b c d e"
0.upto(a.size - 1) do |i|
  puts a[i]
  if a[i]=="b"
    # skip next element
    # but i won't get affected
    i += 1
  end
end

# incorrect, outputs "a b c d e"
for i in 0...a.size
  puts a[i]
  if a[i]=="b"
    # skip next element
    # but i won't get affected
    i += 1
  end
end

# incorrect, outputs nothing... is there a next_next ?
a.each do |elt|
  puts elt
  if elt=="b"
    # skip next element
    # ??
  end
end

# this one works, but is ugly
i=0
while i < a.size
  puts a[i]
  if a[i]=="b"
    # skip next element
    i += 1
  end
  i += 1
end

Hello again,

thanks for the answers. I have been very unclear what I wanted, but
Malte, Guy and JEGII seemed to have read my mind.

a=%w( a b b d b e )

a.each_with_index do |x, i|
  next if x == "b" and a[i - 1] == "b"
  if x=="b" and a[i+1]=="b"
    puts "double b"
  else
    puts x
  end
end

(slightly different code example, but closer to what I wanted).

I wonder why I cannot change the variable in

for i in 0...x
  
end

Or am I missing some magic?

Patrick

try it, with

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

array.each_with_index do |x, i|
  next if array[i-1] == :b
  print x, ' '
end

:slight_smile:

Guy Decoux

I'd use Gary Watson's method, and cache the last seen element, rather
than rely upon the index (e.g. if you use a stream rather than an array,
you no longer have access to a[i-i]).

martin

···

Patrick Gundlach <clr8.10.randomuser@spamgourmet.com> wrote:

Hello again,

thanks for the answers. I have been very unclear what I wanted, but
Malte, Guy and JEGII seemed to have read my mind.

a=%w( a b b d b e )

a.each_with_index do |x, i|
  next if x == "b" and a[i - 1] == "b"
  if x=="b" and a[i+1]=="b"
    puts "double b"
  else
    puts x
  end
end

ts:

try it, with

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

Huh. Right. So it becomes

array.each_with_index do |x, i|
   next if array[i-1] == :b unless i == 0
   print x, ' '
end

Nasty.

Malte

Maybe I missed something.

  last = nil
  %w( a b c d e ).select { |e| r = last != 'b' ; last = e ; r }

T.

ts:

try it, with

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

Huh. Right. So it becomes

array.each_with_index do |x, i|
   next if array[i-1] == :b unless i == 0

next if i > 0 and array[i - 1] == :b

   print x, ' '
end

Nasty.

James Edward Gray II

···

On Dec 5, 2005, at 9:52 AM, Malte Milatz wrote:

Maybe I missed something.

You missed to read my mind; my explanation was very unclear. I wanted
to have something like

"if two 'b' are consecutive, output 'double b' and continue with the
element behind the second".

'a' 'b' 'b' 'c' 'b' 'd'

->

'a' 'double b' 'c' 'b' 'd'

This is why I thought

for i in sequence
  if sequence[i]=='b' and sequence[i+1]=='b'
   output 'double b'
   # increase i, so that the second 'b' won't be seen by the for-loop
   # but ruby won't let me!?
   # i += 1 does nothing
  else
   output sequence[i]
  end
end

would work, but I can't change the i in the for-loop. This is a pity!

Patrick

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

This is why I thought

for i in sequence
  if sequence[i]=='b' and sequence[i+1]=='b'
   output 'double b'
   # increase i, so that the second 'b' won't be seen by the
for-loop
   # but ruby won't let me!?
   # i += 1 does nothing
  else
   output sequence[i]
  end
end

would work, but I can't change the i in the for-loop. This is a
pity!

It's probably helpful to realize that Ruby has no for-loop in the
traditional sense. The above is equivalent to:

sequence.each do |i|
   if sequence[i]=='b' and sequence[i+1]=='b'
    output 'double b'
    # increase i, so that the second 'b' won't be seen by the
for-loop
    # but ruby won't let me!?
    # i += 1 does nothing
   else
    output sequence[i]
   end
end

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.

···

On Wednesday 07 December 2005 11:51, mental@rydia.net wrote:

Quoting Patrick Gundlach <clr9.10.randomuser@spamgourmet.com>:
> This is why I thought
>
> for i in sequence
> if sequence[i]=='b' and sequence[i+1]=='b'
> output 'double b'
> # increase i, so that the second 'b' won't be seen by the
> for-loop
> # but ruby won't let me!?
> # i += 1 does nothing
> else
> output sequence[i]
> end
> end
>
> would work, but I can't change the i in the for-loop. This is a
> pity!

It's probably helpful to realize that Ruby has no for-loop in the
traditional sense. The above is equivalent to:

sequence.each do |i|
   if sequence[i]=='b' and sequence[i+1]=='b'
    output 'double b'
    # increase i, so that the second 'b' won't be seen by the
for-loop
    # but ruby won't let me!?
    # i += 1 does nothing
   else
    output sequence[i]
   end
end

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

-mental

It's probably helpful to realize that Ruby has no for-loop in the
traditional sense. The above is equivalent to:

sequence.each do |i|
[...]
end

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

Yes, but when writing 'for i in x ... end' I'd expect a for-loop :slight_smile:
It would be really nice to be able to increase the counter from within
the loop. I somewhat expected that to work, I can't tell you why.
Perhaps that is what I was used to in other languages?

Patrick

[...]

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.

That is exactly what I am trying to find. I have a list (Array) of
different elements, which I want to render below each other, except
when there are two elements of type 'b', they can be put next to each
other. So I think I need a check like 'if this element is == 'b' and
next element is also == 'b', then render them next to each other. This
rendering has to be known in advance, so I can't use information if
the last element is of type 'b' (with the second occurance of 'b').

Of course, I can write a while loop, but this would be

a) setting some counter to 0
b) accessing the elements via (index)
c) checking on counter <=> sequence.length

all which are acceptable, but don't look like the nice ruby builtins
that I am used to. The

sequence.each do |element|
....
end

would be nice, but I understand that there is no
'skip_the_next_element'-method. So the next nicer attempt would be

for counter in 0...element.size
  # ...
  increase_counter_by_one_to_skip_one_interation
end

But - contradicting my intuition - doesn't seem to work/exist. So I
have to stick to an ugly while loop.... :wink: So my question is: did I
miss something? Is there any reason why we can't manipulate the
counter within the loop?

Patrick

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.

How about something like:

irb(main):008:0> "abbcdeef".gsub(/(\w)\1/) { |match|
irb(main):009:1* " double #{match[0, 1]} "
irb(main):010:1> }
=> "a double b cd double e f"

···

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

[...]

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

That is exactly what I am trying to find. I have a list (Array) of
different elements, which I want to render below each other, except
when there are two elements of type 'b', they can be put next to each
other. So I think I need a check like 'if this element is == 'b' and
next element is also == 'b', then render them next to each other. This
rendering has to be known in advance, so I can't use information if
the last element is of type 'b' (with the second occurance of 'b').

Of course, I can write a while loop, but this would be

a) setting some counter to 0
b) accessing the elements via (index)
c) checking on counter <=> sequence.length

all which are acceptable, but don't look like the nice ruby builtins
that I am used to. The

sequence.each do |element|
....
end

would be nice, but I understand that there is no
'skip_the_next_element'-method. So the next nicer attempt would be

for counter in 0...element.size
  # ...
  increase_counter_by_one_to_skip_one_interation
end

But - contradicting my intuition - doesn't seem to work/exist. So I
have to stick to an ugly while loop.... :wink: So my question is: did I
miss something? Is there any reason why we can't manipulate the
counter within the loop?

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

sequence.each do |element|
....
end

would be nice, but I understand that there is no
'skip_the_next_element'-method. So the next nicer attempt would
be

for counter in 0...element.size
  # ...
  increase_counter_by_one_to_skip_one_interation
end

But - contradicting my intuition - doesn't seem to work/exist. So
I have to stick to an ugly while loop.... :wink: So my question is:
did I miss something?

There's really no difference between:

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

and

(0...element_size).each do |counter|
   ...
end

Both call Range#each with the given block.

Is there any reason why we can't manipulate the counter within
the loop?

'counter' isn't actually a counter. It's just a parameter of the
block given to Range#each. While there's probably a real counter
behind the scenes somewhere, it's not exposed to you.

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

or alternately:

skip = false
sequence.each do |element|
   unless skip
     ...
     # set skip to true to skip the next iteration
     ...
   end
   skip = false
end

-mental