Style Question

  • Thread starter Brian Schroeder
  • Start date
B

Brian Schroeder

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

Florian Gross

Brian said:
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
 
D

David A. Black

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

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 :)
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
 
R

Robert Klemme

Brian Schroeder said:
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

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


Kind regards

robert
 
F

Florian Gross

Robert said:
Moin!

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 said:
Kind regards
robert

More regards,
Florian Gross
 
R

Robert Klemme

Florian Gross said:
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.


What about String Ranges?

That was just meant as an example - implementing integer ranges. I probably
should have pointed that out more clearly.
This is a problem -- there are Ranges that will never satisfy this
condition. (Float ranges come to mind)

Yep, the int check is missing.
def sign(x); x <=> 0; end

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

def initialize(from said:
More regards,
Florian Gross

EMR (even more regards)

robert
 

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

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,139
Latest member
JamaalCald
Top