# Most elegant way to do this?

Discussion in 'Ruby' started by rbysamppi@gmail.com, Nov 27, 2007.

1. ### Guest

Are there any more elegant, concise, pithy, and more Rubyish ways of
doing this?

def roll(number_of_dice)
sum = 0
number_of_dice.times do
sum += rand(5).next
end
sum
end

, Nov 27, 2007

2. ### Alex YoungGuest

wrote:
> Are there any more elegant, concise, pithy, and more Rubyish ways of
> doing this?
>
> def roll(number_of_dice)
> sum = 0
> number_of_dice.times do
> sum += rand(5).next
> end
> sum
> end
>
> Thanks in advance!
>

The Incredible Inevitable Inject:

def roll(number_of_dice)
(0...number_of_dice).inject(0){|m,r| rand(5)+m}
end

--
Alex

Alex Young, Nov 27, 2007

3. ### JCGuest

wrote:
> Are there any more elegant, concise, pithy, and more Rubyish ways of
> doing this?
>
> def roll(number_of_dice)
> sum = 0
> number_of_dice.times do
> sum += rand(5).next
> end
> sum
> end
>
> Thanks in advance!

I don't think there's really anything you can do to it, but I would
suggest that you add a parameter to set the sides of the dice and also
add one to the random output (if it's the random number gen I'm thinking
of, it'll give you 0-5 which means your dice have a blank side )

-JC

JC, Nov 27, 2007
4. ### PhrogzGuest

On Nov 26, 5:24 pm, Alex Young <> wrote:
> wrote:
> > Are there any more elegant, concise, pithy, and more Rubyish ways of
> > doing this?

>
> > def roll(number_of_dice)
> > sum = 0
> > number_of_dice.times do
> > sum += rand(5).next
> > end
> > sum
> > end

>
> The Incredible Inevitable Inject:
>
> def roll(number_of_dice)
> (0...number_of_dice).inject(0){|m,r| rand(5)+m}
> end

You missed the +1 needed to take rand(5) to 1..6 instead of 0..5.

And I personally like 1..num_dice instead of 0...num_dice. And,
finally, I sum things so often I usually have this lying around:

module Enumerable
def sum
if block_given?
inject(0){ |sum,obj| sum + yield(obj) }
else
inject(0){ |sum,obj| sum+obj }
end
end
end

which makes the solution simply:
def roll(number_of_dice)
(1..number_of_dice).sum{ rand(5)+1 }
end

In the vein of DRY code and unix tools, I strongly encourage everyone
to be on constant vigil looking for bits of code that can be
abstracted out to little atomic re-usable bits. After a while, coding
is less like carving entire models from styrofoam, and more like
snapping little Lego blocks together.

Phrogz, Nov 27, 2007
5. ### Robert KlemmeGuest

2007/11/27, Phrogz <>:
> On Nov 26, 5:24 pm, Alex Young <> wrote:
> > wrote:
> > > Are there any more elegant, concise, pithy, and more Rubyish ways of
> > > doing this?

> >
> > > def roll(number_of_dice)
> > > sum = 0
> > > number_of_dice.times do
> > > sum += rand(5).next
> > > end
> > > sum
> > > end

> >
> > The Incredible Inevitable Inject:
> >
> > def roll(number_of_dice)
> > (0...number_of_dice).inject(0){|m,r| rand(5)+m}
> > end

>
> You missed the +1 needed to take rand(5) to 1..6 instead of 0..5.

And everybody apparently missed 6 because rand(5) will yield *5*
values ranging in 0..4. )

> And I personally like 1..num_dice instead of 0...num_dice. And,
> finally, I sum things so often I usually have this lying around:
>
> module Enumerable
> def sum
> if block_given?
> inject(0){ |sum,obj| sum + yield(obj) }
> else
> inject(0){ |sum,obj| sum+obj }
> end
> end
> end
>
> which makes the solution simply:
> def roll(number_of_dice)
> (1..number_of_dice).sum{ rand(5)+1 }
> end

There is another one, that - at least theoretically - saves some

require 'enumerator'

def roll(number)
raise ArgumentError, "Negative!" if number < 0
number.to_enumtimes).inject(number) {|s,| s + rand(6)}
end

I also threw in to_enum just for the fun of it.

> In the vein of DRY code and unix tools, I strongly encourage everyone
> to be on constant vigil looking for bits of code that can be
> abstracted out to little atomic re-usable bits. After a while, coding
> is less like carving entire models from styrofoam, and more like
> snapping little Lego blocks together.

Absolutely!

Kind regards

robert

--
use.inject do |as, often| as.you_can - without end

Robert Klemme, Nov 27, 2007
6. ### Robert KlemmeGuest

2007/11/27, Robert Klemme <>:
> There is another one, that - at least theoretically - saves some

Of course I wanted to say that it /practically/ saves addition efforts
and /theoretically/ it will also save time.

Cheers

robert

Robert Klemme, Nov 27, 2007
7. ### Alex YoungGuest

Phrogz wrote:
> On Nov 26, 5:24 pm, Alex Young <> wrote:
>> wrote:
>>> Are there any more elegant, concise, pithy, and more Rubyish ways of
>>> doing this?
>>> def roll(number_of_dice)
>>> sum = 0
>>> number_of_dice.times do
>>> sum += rand(5).next
>>> end
>>> sum
>>> end

>> The Incredible Inevitable Inject:
>>
>> def roll(number_of_dice)
>> (0...number_of_dice).inject(0){|m,r| rand(5)+m}
>> end

>
> You missed the +1 needed to take rand(5) to 1..6 instead of 0..5.

Oops

--
Alex

Alex Young, Nov 27, 2007
8. ### Alex YoungGuest

Robert Klemme wrote:
> 2007/11/27, Phrogz <>:
>> On Nov 26, 5:24 pm, Alex Young <> wrote:
>>> wrote:
>>>> Are there any more elegant, concise, pithy, and more Rubyish ways of
>>>> doing this?
>>>> def roll(number_of_dice)
>>>> sum = 0
>>>> number_of_dice.times do
>>>> sum += rand(5).next
>>>> end
>>>> sum
>>>> end
>>> The Incredible Inevitable Inject:
>>>
>>> def roll(number_of_dice)
>>> (0...number_of_dice).inject(0){|m,r| rand(5)+m}
>>> end

>> You missed the +1 needed to take rand(5) to 1..6 instead of 0..5.

>
> And everybody apparently missed 6 because rand(5) will yield *5*
> values ranging in 0..4. )

http://hometown.aol.com/dicetalk/polymor2.htm

Nobody said dice have to be cubic...

--
Alex

Alex Young, Nov 27, 2007
9. ### Heesob ParkGuest

Hi,
unknown wrote:
> Are there any more elegant, concise, pithy, and more Rubyish ways of
> doing this?
>
> def roll(number_of_dice)
> sum = 0
> number_of_dice.times do
> sum += rand(5).next
> end
> sum
> end
>
> Thanks in advance!

If you want the dice number, you should use rand(6).

def roll(n)
eval('+rand(6)+1'*n)
end

Regards,

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

Heesob Park, Nov 27, 2007
10. ### William JamesGuest

On Nov 26, 6:17 pm, wrote:
> Are there any more elegant, concise, pithy, and more Rubyish ways of
> doing this?
>
> def roll(number_of_dice)
> sum = 0
> number_of_dice.times do
> sum += rand(5).next
> end
> sum
> end
>
> Thanks in advance!

def sum *x
x.inject{|a,b| a+b}
end
def roll n
sum( *(1..n).map{rand(6)+1} )
end

William James, Nov 27, 2007
11. ### Robert DoberGuest

On Nov 27, 2007 7:40 AM, Phrogz <> wrote:
> On Nov 26, 5:24 pm, Alex Young <> wrote:
> > wrote:
> > > Are there any more elegant, concise, pithy, and more Rubyish ways of
> > > doing this?

> >
> > > def roll(number_of_dice)
> > > sum = 0
> > > number_of_dice.times do
> > > sum += rand(5).next
> > > end
> > > sum
> > > end

> >
> > The Incredible Inevitable Inject:
> >
> > def roll(number_of_dice)
> > (0...number_of_dice).inject(0){|m,r| rand(5)+m}
> > end

>
> You missed the +1 needed to take rand(5) to 1..6 instead of 0..5.
>
> And I personally like 1..num_dice instead of 0...num_dice. And,
> finally, I sum things so often I usually have this lying around:
>
> module Enumerable
> def sum
> if block_given?
> inject(0){ |sum,obj| sum + yield(obj) }
> else
> inject(0){ |sum,obj| sum+obj }
> end
> end
> end

I agree with this need, in Labrador you can do
an_enum.inject+)
and as we were nitpicking
inject(0){ |m,| m + rand(5) } #sic

R.

--

http://ruby-smalltalk.blogspot.com/

---
All truth passes through three stages. First, it is ridiculed. Second,
it is violently opposed. Third, it is accepted as being self-evident.
Schopenhauer (attr.)

Robert Dober, Nov 27, 2007
12. ### Brian AdkinsGuest

On Nov 27, 6:26 am, William James <> wrote:
> On Nov 26, 6:17 pm, wrote:
>
> > Are there any more elegant, concise, pithy, and more Rubyish ways of
> > doing this?

>
> > def roll(number_of_dice)
> > sum = 0
> > number_of_dice.times do
> > sum += rand(5).next
> > end
> > sum
> > end

>
> > Thanks in advance!

>
> def sum *x
> x.inject{|a,b| a+b}
> end
> def roll n
> sum( *(1..n).map{rand(6)+1} )
> end

Clever, but 3 times slower than the original (after correcting it).

Brian Adkins, Nov 27, 2007
13. ### Brian AdkinsGuest

On Nov 26, 7:17 pm, wrote:
> Are there any more elegant, concise, pithy, and more Rubyish ways of
> doing this?
>
> def roll(number_of_dice)
> sum = 0
> number_of_dice.times do
> sum += rand(5).next
> end
> sum
> end
>
> Thanks in advance!

As others have pointed out, you need rand(6).next to get (1..6);
otherwise, it's hard to improve on what you have here. This is a
simple, iterative mathematical function. I personally don't think
using map, inject, etc. is more "Rubyish" in this context, just 2 to 3
times slower.

The interface is "roll n" regardless of the underlying implementation,
so you might as well make it fast.

One style improvement might be to use a one line block:

def roll num_dice
sum = 0
num_dice.times { sum += rand(6).next }
sum
end

You can make it ~10% faster (with the loss of some readability) by not
invoking next each time and just summing at the end:

def roll num_dice
sum = 0
num_dice.times { sum += rand(6) }
sum + num_dice
end

Brian Adkins, Nov 27, 2007
14. ### Brian AdkinsGuest

On Nov 27, 8:37 am, Brian Adkins <> wrote:
> On Nov 26, 7:17 pm, wrote:
>
> > Are there any more elegant, concise, pithy, and more Rubyish ways of
> > doing this?

>
> > def roll(number_of_dice)
> > sum = 0
> > number_of_dice.times do
> > sum += rand(5).next
> > end
> > sum
> > end

>
> > Thanks in advance!

>
> As others have pointed out, you need rand(6).next to get (1..6);
> otherwise, it's hard to improve on what you have here. This is a
> simple, iterative mathematical function. I personally don't think
> using map, inject, etc. is more "Rubyish" in this context, just 2 to 3
> times slower.
>
> The interface is "roll n" regardless of the underlying implementation,
> so you might as well make it fast.
>
> One style improvement might be to use a one line block:
>
> def roll num_dice
> sum = 0
> num_dice.times { sum += rand(6).next }
> sum
> end
>
> You can make it ~10% faster (with the loss of some readability) by not
> invoking next each time and just summing at the end:
>
> def roll num_dice
> sum = 0
> num_dice.times { sum += rand(6) }
> sum + num_dice
> end

Actually make that ~12% faster with the following - just explain with

def roll num_dice
sum = num_dice
num_dice.times { sum += rand(6) }
sum
end

Brian Adkins, Nov 27, 2007
15. ### PhrogzGuest

On Nov 27, 2:06 am, Robert Klemme <> wrote:
> 2007/11/27, Phrogz <>:
>
> > You missed the +1 needed to take rand(5) to 1..6 instead of 0..5.

>
> And everybody apparently missed 6 because rand(5) will yield *5*
> values ranging in 0..4. )

Oh crimeny...oops.

> There is another one, that - at least theoretically - saves some
>
> require 'enumerator'
>
> def roll(number)
> raise ArgumentError, "Negative!" if number < 0
> number.to_enumtimes).inject(number) {|s,| s + rand(6)}
> end

You know, I _want_ to embrace to_enum, because it's certainly
excellently powerful and abstract...but somehow I just don't grok it.
Thanks for the reminder. Sometime I'll have to think about it.

(I wish I could find some flaw with it, and say "no no no, what it
really should be is ____"; I just somehow basically find it confusing.)

Phrogz, Nov 27, 2007
16. ### Brian AdkinsGuest

Why be satisfied with a 10-12% increase in speed when we can have an
order of magnitude?

sudo gem install rubyinline

--

require 'rubygems'
require 'inline'

module Kernel
inline do |builder|
builder.c "
int roll(int n) {
int sum = n;
while (n-- > 0) {
sum += (rand() % 6);
}
return sum;
}
"
end
end

puts roll(3)

Brian Adkins, Nov 27, 2007
17. ### Eivind EklundGuest

On Nov 27, 2007 3:35 PM, Brian Adkins <> wrote:
> Why be satisfied with a 10-12% increase in speed when we can have an
> order of magnitude?
>
> sudo gem install rubyinline
>
> --
>
> require 'rubygems'
> require 'inline'
>
> module Kernel
> inline do |builder|
> builder.c "
> int roll(int n) {
> int sum = n;
> while (n-- > 0) {
> sum += (rand() % 6);
> }
> return sum;
> }
> "
> end
> end
>
> puts roll(3)

If you're going to do overkill, do overkill. You've got an extra
compare in that main loop, with the check for n-- > 0. And it doesn't
work for negative numbers. Correcting for this (untested):
int roll(int n) {
int sum = n;
if (n > 0) {
do {
sum += rand() % 6;
} while (--n);
} else if (n < 0) {
do {
sum -= rand() % 6;
} while (++n);
}
return sum;
}

Of course, this still has the problem that rand() % 6 typically return
slightly biased numbers, as RAND_MAX is usually a 2^n-1

;-)

Eivind.

Eivind Eklund, Nov 27, 2007
18. ### Cameron McBrideGuest

On 11/27/07, Eivind Eklund <> wrote:
> Of course, this still has the problem that rand() % 6 typically return
> slightly biased numbers, as RAND_MAX is usually a 2^n-1
>
> ;-)
>
> Eivind.

hah! overkill not only on method, but also in worry. biased Nd6?
awesome! ok, that kind of made my morning.

Cameron

Cameron McBride, Nov 27, 2007
19. ### Robert KlemmeGuest

2007/11/27, Brian Adkins <>:
> On Nov 26, 7:17 pm, wrote:
> > Are there any more elegant, concise, pithy, and more Rubyish ways of
> > doing this?
> >
> > def roll(number_of_dice)
> > sum = 0
> > number_of_dice.times do
> > sum += rand(5).next
> > end
> > sum
> > end
> >
> > Thanks in advance!

>
> As others have pointed out, you need rand(6).next to get (1..6);
> otherwise, it's hard to improve on what you have here. This is a
> simple, iterative mathematical function. I personally don't think
> using map, inject, etc. is more "Rubyish" in this context, just 2 to 3
> times slower.
>
> The interface is "roll n" regardless of the underlying implementation,
> so you might as well make it fast.
>
> One style improvement might be to use a one line block:
>
> def roll num_dice
> sum = 0
> num_dice.times { sum += rand(6).next }
> sum
> end
>
> You can make it ~10% faster (with the loss of some readability) by not
> invoking next each time and just summing at the end:
>
> def roll num_dice
> sum = 0
> num_dice.times { sum += rand(6) }
> sum + num_dice
> end

I guess there is even more room for improvement by doing this:

def roll num_dice
sum = roll num_dice
num_dice.times { sum += rand(6) }
sum
end

SCNR ;-)

robert

--
use.inject do |as, often| as.you_can - without end

Robert Klemme, Nov 27, 2007
20. ### Brian AdkinsGuest

On Nov 27, 9:57 am, Eivind Eklund <> wrote:
> If you're going to do overkill, do overkill. You've got an extra
> compare in that main loop, with the check for n-- > 0.

I assume you're just having fun, but even so, if your compiler doesn't
optimize (n-- > 0) to be as efficient as (n--), get a new compiler.

> And it doesn't
> work for negative numbers. Correcting for this (untested):

Yes, rolling a negative number of dice is precluded, it just confuses
people.

> int roll(int n) {
> int sum = n;
> if (n > 0) {
> do {
> sum += rand() % 6;
> } while (--n);
> } else if (n < 0) {
> do {
> sum -= rand() % 6;
> } while (++n);
> }
> return sum;
>
> }

You might want to double check that code...

> Of course, this still has the problem that rand() % 6 typically return
> slightly biased numbers, as RAND_MAX is usually a 2^n-1

Ok, here you have a point. The roll function won't cut it in Las
Vegas.

> ;-)
>
> Eivind.

Brian Adkins, Nov 27, 2007