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

    Thanks in advance!
     
    , Nov 27, 2007
    #1
    1. Advertising

  2. Alex Young Guest

    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
    #2
    1. Advertising

  3. JC Guest

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

    -JC
     
    JC, Nov 27, 2007
    #3
  4. Phrogz Guest

    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
    #4
  5. 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
    addition efforts:

    require 'enumerator'

    def roll(number)
    raise ArgumentError, "Negative!" if number < 0
    number.to_enum:)times).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
    #5
  6. 2007/11/27, Robert Klemme <>:
    > There is another one, that - at least theoretically - saves some
    > addition efforts:


    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
    #6
  7. Alex Young Guest

    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
    #7
  8. Alex Young Guest

    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
    #8
  9. Heesob Park Guest

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

    How about this:

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

    Regards,

    Park Heesob
    --
    Posted via http://www.ruby-forum.com/.
     
    Heesob Park, Nov 27, 2007
    #9
  10. 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
    #10
  11. Robert Dober Guest

    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
    #11
  12. Brian Adkins Guest

    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
    #12
  13. Brian Adkins Guest

    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
     
    Brian Adkins, Nov 27, 2007
    #13
  14. Brian Adkins Guest

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

    def roll num_dice
    sum = num_dice
    num_dice.times { sum += rand(6) }
    sum
    end
     
    Brian Adkins, Nov 27, 2007
    #14
  15. Phrogz Guest

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


    > There is another one, that - at least theoretically - saves some
    > addition efforts:
    >
    > require 'enumerator'
    >
    > def roll(number)
    > raise ArgumentError, "Negative!" if number < 0
    > number.to_enum:)times).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
    #15
  16. Brian Adkins Guest

    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
    #16
  17. 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
    #17
  18. 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
    #18
  19. 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
    #19
  20. Brian Adkins Guest

    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
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Rob Cowie
    Replies:
    28
    Views:
    673
    Bruno Desthuilliers
    Jun 13, 2006
  2. Duk Lee
    Replies:
    2
    Views:
    749
    Alexey Smirnov
    Jul 23, 2007
  3. Alex Buell

    What's the most elegant way of doing this?

    Alex Buell, Feb 3, 2009, in forum: C Programming
    Replies:
    3
    Views:
    348
    Alex Buell
    Feb 4, 2009
  4. Kenneth McDonald
    Replies:
    2
    Views:
    119
    Kenneth McDonald
    Sep 2, 2008
  5. Terry Michaels
    Replies:
    4
    Views:
    175
    w_a_x_man
    Oct 16, 2010
Loading...

Share This Page