Style Question

Hello everybody,

I've got a stylistic question. I know that "the ruby way" to implement
iterations is using .each and similar things, but sometimes I like to use

for i in 0...n
  for j in i...n
    do something with i and for
  end
end

Thats nice.

But if I want to iterate downwards the only way I found is 10.downto(0) do

i | end

That seems not as intuitive as the for notation. Especially if I want ruby
to teach algorithms to people who know how to read pseudo code, then I'd
like something like

for i in 10.downto 0
for i in 0.upto 10

I think this has been discussed before, but I'd like to now which style
you use, or if I've missed the solution.

regards,

Brian

PS: How is for implemented? Can I maybe teach Number.downto to return an
object that
     is used by for to iterate like an inverse range?

···

--
Brian Schröder
http://www.brian-schroeder.de/

Brian Schroeder wrote:

Hello everybody,

Moin!

I've got a stylistic question. I know that "the ruby way" to implement
iterations is using .each and similar things, but sometimes I like to use

for i in 0...n
  for j in i...n
    do something with i and for
  end
end

Thats nice.

But if I want to iterate downwards the only way I found is 10.downto(0) do
> i | end

What about this:

for i in (0...n).to_a.reverse
   for j in (i...n).to_a.reverse
     p [i, j]
   end
end

The problem with that is that it will be slow for huge ranges.

Or maybe this:

require 'enumerator'
for i in (n - 1).enum_for(:downto, 0)
   for j in (n - 1).enum_for(:downto, i)
     p [i, j]
   end
end

Or this one:

class Range
   def reverse_each(&block)
     unless last.respond_to?(:downto)
       raise(TypeError, "cannot reverse iterate from #{last.class}")
     end

     is_first = true
     last.downto(first) do |item|
       if is_first and exclude_end?
         is_first = false
         next
       end

       block.call(item)
     end
   end
end

require 'enumerator'
for i in (0...n).enum_for(:reverse_each)
   for j in (i...n).enum_for(:reverse_each)
     p [i, j]
   end
end

And I think that Range should provide reverse_each in Standard Ruby... (There's no problem in it when using #downto -- it is only defined for Integers, not for Strings.)

Regards,
Florian Gross

Hi --

Hello everybody,

I've got a stylistic question. I know that "the ruby way" to implement
iterations is using .each and similar things, but sometimes I like to use

for i in 0...n
  for j in i...n
    do something with i and for
  end
end

Thats nice.

But if I want to iterate downwards the only way I found is 10.downto(0) do
> i | end

That seems not as intuitive as the for notation. Especially if I want ruby
to teach algorithms to people who know how to read pseudo code, then I'd
like something like
> for i in 10.downto 0
> for i in 0.upto 10

I think this has been discussed before, but I'd like to now which style
you use, or if I've missed the solution.

I would have thought that

  10.downto(0).each do |i|

would be very pseudo-coder friendly. Have you determined for certain
that the people you're teaching can't grasp this? You wouldn't even
have to explain range notation :slight_smile:

PS: How is for implemented? Can I maybe teach Number.downto to return an
object that
     is used by for to iterate like an inverse range?

You could wrap it, like this:

  class Integer
    alias olddownto downto
    def downto(x,&b)
      olddownto(x,&b) if b
      return(x..self).to_a.reverse
    end
  end

David

···

On Sun, 5 Sep 2004, Brian Schroeder wrote:

--
David A. Black
dblack@wobblini.net

"Brian Schroeder" <spam0504@bssoftware.de> schrieb im Newsbeitrag news:pan.2004.09.05.11.20.28.668820@bssoftware.de...

Hello everybody,

I've got a stylistic question. I know that "the ruby way" to implement
iterations is using .each and similar things, but sometimes I like to use

for i in 0...n
for j in i...n
do something with i and for
end

Thats nice.

But if I want to iterate downwards the only way I found is 10.downto(0) do
> i | end

That seems not as intuitive as the for notation. Especially if I want ruby
to teach algorithms to people who know how to read pseudo code, then I'd
like something like

for i in 10.downto 0
for i in 0.upto 10

I think this has been discussed before, but I'd like to now which style
you use, or if I've missed the solution.

regards,

Brian

PS: How is for implemented? Can I maybe teach Number.downto to return an
object that
   is used by for to iterate like an inverse range?

Even more straightforward: you can implement a range that supports both directions yourself plus arbitrary stepping like this:

class Rg
  include Enumerable

  def initialize(from, to, step = sign(to - from))
    raise ArgumentError, "Invalid step #{step}" if (to - from) * step <= 0
    @from, @to, @step = from, to, step
    @to += @step - ((@to - @from) % @step)
  end

  def each
    x = @from
    until x == @to
      yield x
      x += @step
    end
    self
  end

  def sign(x)
    case
      when x > 0
        1
      when x == 0
        0
      else
        -1
    end
  end
end

module Kernel
private
  def Rg(from, to, *step) Rg.new(from, to, *step) end
end

Then you can do this:

for i in Rg 0, 10, 2
  puts i
end

0
2
4
6
8
10
=> #<Rg:0x10186da0 @from=0, @step=2, @to=12>

for i in Rg( 0, -10, -3 )
  puts i
end

0
-3
-6
-9
=> #<Rg:0x101801a0 @from=0, @step=-3, @to=-12>

for i in Rg 0, -10
puts i
end

0
-1
-2
-3
-4
-5
-6
-7
-8
-9
-10
=> #<Rg:0x10199ce8 @step=-1, @to=-11, @from=0>

Kind regards

    robert

Robert Klemme wrote:

Hello everybody,

Moin!

PS: How is for implemented? Can I maybe teach Number.downto to return an
object that
   is used by for to iterate like an inverse range?

Even more straightforward: you can implement a range that supports both directions yourself plus arbitrary stepping like this:

I'd prefer using an enumerator around Range#step or Fixnum#step instead.

Here are a few small comments and suggestion about your code. Maybe they are useful.

class Rg
include Enumerable

def initialize(from, to, step = sign(to - from))

What about String Ranges?

   raise ArgumentError, "Invalid step #{step}" if (to - from) * step <= 0
   @from, @to, @step = from, to, step
   @to += @step - ((@to - @from) % @step)
end

def each
   x = @from
   until x == @to

This is a problem -- there are Ranges that will never satisfy this condition. (Float ranges come to mind)

     yield x
     x += @step
   end
   self
end

def sign(x)
   case
     when x > 0
       1
     when x == 0
       0
     else
       -1
   end
end

def sign(x); x <=> 0; end

end

Kind regards
   robert

More regards,
Florian Gross

"Florian Gross" <flgr@ccan.de> schrieb im Newsbeitrag news:2q18quFqav8gU1@uni-berlin.de...

Robert Klemme wrote:

Hello everybody,

Moin!

PS: How is for implemented? Can I maybe teach Number.downto to return an
object that
   is used by for to iterate like an inverse range?

Even more straightforward: you can implement a range that supports both directions yourself plus arbitrary stepping like this:

I'd prefer using an enumerator around Range#step or Fixnum#step instead.

Here are a few small comments and suggestion about your code. Maybe they are useful.

class Rg
include Enumerable

def initialize(from, to, step = sign(to - from))

What about String Ranges?

That was just meant as an example - implementing integer ranges. I probably should have pointed that out more clearly.

   raise ArgumentError, "Invalid step #{step}" if (to - from) * step <= 0
   @from, @to, @step = from, to, step
   @to += @step - ((@to - @from) % @step)
end

def each
   x = @from
   until x == @to

This is a problem -- there are Ranges that will never satisfy this condition. (Float ranges come to mind)

Yep, the int check is missing.

     yield x
     x += @step
   end
   self
end

def sign(x)
   case
     when x > 0
       1
     when x == 0
       0
     else
       -1
   end
end

def sign(x); x <=> 0; end

Very nice! Then we don't need sign():

def initialize(from, to, step = ((to - from) <=> 0))

end

Kind regards
   robert

More regards,
Florian Gross

EMR (even more regards)

    robert