[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Putting some code out to DRY

T. Onoma

10/10/2004 7:26:00 PM


Notice the three lines of duplication. Any ideas on DRYing this up?

def each
seed = @start
if @step
@step.times do
yield(seed)
@skip.times { seed = seed.succ }
break if @halt.call(seed) if @halt
end
else
loop do
yield(seed)
@skip.times { seed = seed.succ }
break if @halt.call(seed) if @halt
end
end
end

Thanks,
T.


[CO2D] or [WET] ?



12 Answers

Alexey Verkhovsky

10/10/2004 7:51:00 PM

0

On Sun, 2004-10-10 at 22:25, trans. (T. Onoma) wrote:
> Notice the three lines of duplication. Any ideas on DRYing this up?

I would extract them into a method.

Another alternative, at a price of checking @step on every iteration,
and much less readable than your original:

# UNTESTED

def each
seed = @start
counter = 0
loop do
break if @step and (counter += 1) > step
yield(seed)
@skip.times { seed = seed.succ }
break if @halt.call(seed) if @halt
end
end





Florian Gross

10/10/2004 7:55:00 PM

0

trans. (T. Onoma) wrote:

> Notice the three lines of duplication. Any ideas on DRYing this up?
>
> def each
> seed = @start
@step ||= 1.0 / 0.0 # Infinity
@step.times do
yield(seed)
@skip.times { seed = seed.succ }
break if @halt and @halt.call(seed)
> end
> end

And maybe this: (in case @start is numeric)

def each
(@start .. (1.0 / 0.0)).step(@skip) do |seed|
yield(seed)
break if @halt and @halt.call(seed)
end
end

> Thanks,
> T.

Regards,
Florian Gross

Randy W. Sims

10/10/2004 8:00:00 PM

0

loop do
break if @step && (@step -= 1) < 0
yield(seed)
@skip.times { seed = seed.succ }
break if @halt.call(seed) if @halt
end



Mikael Brockman

10/10/2004 8:03:00 PM

0

"trans. (T. Onoma)" <transami@runbox.com> writes:

> Notice the three lines of duplication. Any ideas on DRYing this up?
>
> def each
> seed = @start
> if @step
> @step.times do
> yield(seed)
> @skip.times { seed = seed.succ }
> break if @halt.call(seed) if @halt
> end
> else
> loop do
> yield(seed)
> @skip.times { seed = seed.succ }
> break if @halt.call(seed) if @halt
> end
> end
> end

You could let @step default to $infinity, where $infinity is something
like this:

| $infinity = Object.new
| class << $infinity
| def times; loop { yield }; end
| # other infinity methods omitted because YAGNI
| end

That would let you define #each as simply

| def each
| seed = @start
| @step.times do
| yield(seed)
| @skip.times { seed = seed.succ }
| break if @halt.call(seed) if @halt
| end
| end



Markus

10/10/2004 9:37:00 PM

0

On Sun, 2004-10-10 at 12:25, trans. (T. Onoma) wrote:
> Notice the three lines of duplication. Any ideas on DRYing this up?
>
> def each
> seed = @start
> if @step
> @step.times do
> yield(seed)
> @skip.times { seed = seed.succ }
> break if @halt.call(seed) if @halt
> end
> else
> loop do
> yield(seed)
> @skip.times { seed = seed.succ }
> break if @halt.call(seed) if @halt
> end
> end
> end
>

How about:

Forever = Object.new
def Forever.times
loop {yield}
end

def each
seed = @start
(@step || Forever).times do
yield(seed)
@skip.times { seed = seed.succ }
break if @halt.call(seed) if @halt
end
end


-- Markus




Mauricio Fernández

10/10/2004 9:55:00 PM

0

On Mon, Oct 11, 2004 at 04:25:30AM +0900, trans. (T. Onoma) wrote:
>
> Notice the three lines of duplication. Any ideas on DRYing this up?
>
> def each
> seed = @start

Am I the only one who sometimes does things resembling
whatever = lambda do
yield(seed)
@skip.times { seed = seed.succ }
break if @halt.call(seed) if @halt
end
and then whatever[] ??


> if @step
> @step.times do
> yield(seed)
> @skip.times { seed = seed.succ }
> break if @halt.call(seed) if @halt
> end
> else
> loop do
> yield(seed)
> @skip.times { seed = seed.succ }
> break if @halt.call(seed) if @halt
> end
> end
> end
>
> Thanks,
> T.
>
>
> [CO2D] or [WET] ?
>
>

--
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com



Jamis Buck

10/10/2004 10:14:00 PM

0

Mauricio Fernández wrote:
> On Mon, Oct 11, 2004 at 04:25:30AM +0900, trans. (T. Onoma) wrote:
>
>>Notice the three lines of duplication. Any ideas on DRYing this up?
>>
>> def each
>> seed = @start
>
>
> Am I the only one who sometimes does things resembling
> whatever = lambda do
> yield(seed)
> @skip.times { seed = seed.succ }
> break if @halt.call(seed) if @halt
> end
> and then whatever[] ??

Glad you mentioned this, batsman. I was just about to. Let me fill in
the blank at the end of your example, though, since I'm the type of guy
that just can't stand a song only partially sung...

@step ? @step.times(&whatever) : loop(&whatever)

- Jamis

>
>
>
>> if @step
>> @step.times do
>> yield(seed)
>> @skip.times { seed = seed.succ }
>> break if @halt.call(seed) if @halt
>> end
>> else
>> loop do
>> yield(seed)
>> @skip.times { seed = seed.succ }
>> break if @halt.call(seed) if @halt
>> end
>> end
>> end
>>
>>Thanks,
>>T.
>>
>>
>>[CO2D] or [WET] ?
>>
>>
>
>


--
Jamis Buck
jgb3@email.byu.edu
http://www.jamisbuck...


Mikael Brockman

10/10/2004 10:31:00 PM

0

Mauricio Fernández <batsman.geo@yahoo.com> writes:

> On Mon, Oct 11, 2004 at 04:25:30AM +0900, trans. (T. Onoma) wrote:
> >
> > Notice the three lines of duplication. Any ideas on DRYing this up?
> >
> > def each
> > seed = @start
>
> Am I the only one who sometimes does things resembling
> whatever = lambda do
> yield(seed)
> @skip.times { seed = seed.succ }
> break if @halt.call(seed) if @halt
> end
> and then whatever[] ??

Nope. I do that all the time. I also often do stuff like

class Foo
define_thing = lambda do |id, y|
define_method x do |z|
do_some_stuff z, y
do_some_other_stuff z, y
end
end

define_thing.call :foo
define_thing.call :bar
define_thing.call :baz
end




T. Onoma

10/11/2004 1:34:00 AM

0

Multiple responses below:

On Sunday 10 October 2004 03:59 pm, Florian Gross wrote:
| > def each
| > seed = @start
|
| @step ||= 1.0 / 0.0 # Infinity
| @step.times do
| yield(seed)
| @skip.times { seed = seed.succ }
| break if @halt and @halt.call(seed)
|
| > end
| > end

This one blew-up on 1.8.2, no Float#times :(

| And maybe this: (in case @start is numeric)
|
| def each
| (@start .. (1.0 / 0.0)).step(@skip) do |seed|
| yield(seed)
| break if @halt and @halt.call(seed)
| end
| end

Oh the irony here! It sort of uses what I'm rewriting ;)

Thanks.

---

On Sunday 10 October 2004 04:03 pm, Mikael Brockman wrote:
| You could let @step default to $infinity, where $infinity is something
|
| like this:
| | $infinity = Object.new
| | class << $infinity
| | def times; loop { yield }; end
| | # other infinity methods omitted because YAGNI
| | end
|
| That would let you define #each as simply
|
| | def each
| | seed = @start
| | @step.times do
| | yield(seed)
| | @skip.times { seed = seed.succ }
| | break if @halt.call(seed) if @halt
| | end
| | end

and

On Sunday 10 October 2004 05:36 pm, Markus wrote:
| > end
|
| How about:
|
| Forever = Object.new
| def Forever.times
| loop {yield}
| end
|
| def each
| seed = @start
| (@step || Forever).times do
| yield(seed)
| @skip.times { seed = seed.succ }
| break if @halt.call(seed) if @halt
| end
| end

These two are similar. And I'm sure I'd go this way if Infinite/Forever were a
standard class, and yet I may go this route anyway.

Hmm... may be an interesting place to use one of those method procs:

forever = proc(:times){|&blk| blk.call}

(1.9 only I think) Just a thought.

Thanks.

---

On Sunday 10 October 2004 03:51 pm, Alexey Verkhovsky wrote:
| On Sun, 2004-10-10 at 22:25, trans. (T. Onoma) wrote:
| > Notice the three lines of duplication. Any ideas on DRYing this up?
|
| I would extract them into a method.
|
| Another alternative, at a price of checking @step on every iteration,
| and much less readable than your original:
|
| # UNTESTED
|
| def each
| seed = @start
| counter = 0
| loop do
| break if @step and (counter += 1) > step
| yield(seed)
| @skip.times { seed = seed.succ }
| break if @halt.call(seed) if @halt
| end
| end

and

On Sunday 10 October 2004 03:59 pm, Randy W. Sims wrote:
| loop do
| break if @step && (@step -= 1) < 0
| yield(seed)
| @skip.times { seed = seed.succ }
| break if @halt.call(seed) if @halt
| end

Extra break. I bet this would be the thing to do if using a lower level
language. Using Ruby though, I'm guessing other means are more efficient. You
concur?

Thanks.

---

On Sunday 10 October 2004 05:55 pm, Mauricio Fernández wrote:
| On Mon, Oct 11, 2004 at 04:25:30AM +0900, trans. (T. Onoma) wrote:
| > Notice the three lines of duplication. Any ideas on DRYing this up?
| >
| > def each
| > seed = @start
|
| Am I the only one who sometimes does things resembling
| whatever = lambda do
| yield(seed)
| @skip.times { seed = seed.succ }
| break if @halt.call(seed) if @halt
| end
| and then whatever[] ??
|

and

On Sunday 10 October 2004 06:14 pm, Jamis Buck wrote:
| Glad you mentioned this, batsman. I was just about to. Let me fill in
| the blank at the end of your example, though, since I'm the type of guy
| that just can't stand a song only partially sung...
|
| @step ? @step.times(&whatever) : loop(&whatever)

This was my thought too. But when I did it that way I thought maybe I was
loosing a little efficiency assigning and calling the proc, and wondered if
there were any better ways, but maybe not.

---

Thanks everyone. Let you know what I settle on.

T.



Jamis Buck

10/11/2004 1:58:00 AM

0

trans. (T. Onoma) wrote:

> On Sunday 10 October 2004 05:55 pm, Mauricio Fernández wrote:
> | On Mon, Oct 11, 2004 at 04:25:30AM +0900, trans. (T. Onoma) wrote:
> | > Notice the three lines of duplication. Any ideas on DRYing this up?
> | >
> | > def each
> | > seed = @start
> |
> | Am I the only one who sometimes does things resembling
> | whatever = lambda do
> | yield(seed)
> | @skip.times { seed = seed.succ }
> | break if @halt.call(seed) if @halt
> | end
> | and then whatever[] ??
> |
>
> and
>
> On Sunday 10 October 2004 06:14 pm, Jamis Buck wrote:
> | Glad you mentioned this, batsman. I was just about to. Let me fill in
> | the blank at the end of your example, though, since I'm the type of guy
> | that just can't stand a song only partially sung...
> |
> | @step ? @step.times(&whatever) : loop(&whatever)
>
> This was my thought too. But when I did it that way I thought maybe I was
> loosing a little efficiency assigning and calling the proc, and wondered if
> there were any better ways, but maybe not.

Try some benchmarks. I've found I'm nearly always wrong in what I assume
to efficient vs. inefficient in Ruby, and benchmarks are usually trivial
to write:

require 'benchmark'

class Test
def initialize( start, step, skip, &halt )
@start = start
@step = step
@skip = skip
@halt = halt
end

def each1
seed = @start
if @step
@step.times do
yield(seed)
@skip.times { seed = seed.succ }
break if @halt.call(seed) if @halt
end
else
loop do
yield(seed)
@skip.times { seed = seed.succ }
break if @halt.call(seed) if @halt
end
end
end

def each2
seed = @start
whatever = lambda do
yield(seed)
@skip.times { seed = seed.succ }
return if @halt.call(seed) if @halt
end
@step ? @step.times(&whatever) : loop(&whatever)
end
end

Benchmark.bm do |x|
with_step = Test.new( 1, 100000, 1 )
without_step = Test.new( 1, nil, 1 ) { |s| s > 100000 }

x.report { with_step.each1 { |s| } }
x.report { without_step.each1 { |s| } }
x.report { with_step.each2 { |s| } }
x.report { without_step.each2 { |s| } }
end

Turns out, the two approaches are virtually identical in execution times:

user system total real
0.200000 0.000000 0.200000 ( 0.233535)
0.370000 0.000000 0.370000 ( 0.428697)
0.200000 0.000000 0.200000 ( 0.239925)
0.370000 0.000000 0.370000 ( 0.405779)

- Jamis

--
Jamis Buck
jgb3@email.byu.edu
http://www.jamisbuck...