[NUBY] Ruby Way code for shuffling an array doesn't behave asexpected

Discussion in 'Ruby' started by Alexey Verkhovsky, Jul 17, 2004.

  1. One example in The Ruby Way claims that in the following snippet second
    line shuffles the a (i.e., makes an array consisting of the same
    elements as a, but in a different order).

    a = [1, 2, 3, 4, 5]
    puts a.collect { a.slice!(rand(a.length)) }.inspect

    The claim sounds reasonable, but in reality Ruby 1.9 (CVS HEAD) always
    returns an array of three random elements, not of five as expected.

    Why is that so?

    Best regards,
    Alexey Verkhovsky
     
    Alexey Verkhovsky, Jul 17, 2004
    #1
    1. Advertising

  2. Alexey Verkhovsky wrote:

    >One example in The Ruby Way claims that in the following snippet second
    >line shuffles the a (i.e., makes an array consisting of the same
    >elements as a, but in a different order).
    >
    >a = [1, 2, 3, 4, 5]
    >puts a.collect { a.slice!(rand(a.length)) }.inspect
    >
    >The claim sounds reasonable, but in reality Ruby 1.9 (CVS HEAD) always
    >returns an array of three random elements, not of five as expected.
    >
    >Why is that so?
    >
    >
    >

    This is due to changes in Ruby between v1.6.8 and 1.8

    It's because the block passed to collect is changing the contents of a,
    so it doesn't reach all the elements of a.

    Changing the line to

    puts a.dup.collect {a.slice!(rand(a.length)) }.inspect

    fixes it

    HTH

    --
    Mark Sparshatt
     
    Mark Sparshatt, Jul 17, 2004
    #2
    1. Advertising

  3. Alexey Verkhovsky

    Mark Hubbart Guest

    Re: [NUBY] Ruby Way code for shuffling an array doesn't behave as expected

    On Jul 17, 2004, at 3:12 PM, Alexey Verkhovsky wrote:

    > One example in The Ruby Way claims that in the following snippet second
    > line shuffles the a (i.e., makes an array consisting of the same
    > elements as a, but in a different order).
    >
    > a = [1, 2, 3, 4, 5]
    > puts a.collect { a.slice!(rand(a.length)) }.inspect
    >
    > The claim sounds reasonable, but in reality Ruby 1.9 (CVS HEAD) always
    > returns an array of three random elements, not of five as expected.
    >
    > Why is that so?


    It appears that #each is being affected by the slicing of the array
    that it is acting on. By the time it gets to the third element, it's
    the last one. So it stops.

    It doesn't seem to do that in 1.6.x; I assume #each worked on a copy of
    the array back then...

    RUBY_VERSION
    ==>"1.6.8"
    a=[1,2,3,4,5]
    ==>[1, 2, 3, 4, 5]
    p a.collect{a.slice!(rand(a.length))}
    [4, 3, 5, 2, 1]
    ==>nil

    To fix it in later versions, use a.dup.collect instead. That should fix
    the odd behavior.

    HTH,
    Mark
     
    Mark Hubbart, Jul 17, 2004
    #3
  4. On Sun, 2004-07-18 at 01:43, Mark Sparshatt wrote:
    > It's because the block passed to collect is changing the contents of a,
    > so it doesn't reach all the elements of a.


    So, Ruby iterators operate on the original collection and let you modify
    the collection in the process. Another fact useful to remember.

    This is, by the way, one thing I like about Java iterators: they are
    fail-safe in such scenarios. E.g.

    List list = new ArrayList(2);
    list.add(new Object());
    list.add(new Object());
    Iterator i = list.iterator();
    i.next(); // this is OK
    list.add(new Object()); // list gets changed
    i.next(); // throws ConcurrentModificationException

    IMHO, an exception here is The Right Thing (TM). At least, it saved me
    from my own stupid mistakes more than once.

    Best regards,
    Alex
     
    Alexey Verkhovsky, Jul 18, 2004
    #4
  5. Alexey Verkhovsky

    Mark Hubbart Guest

    Re: [NUBY] Ruby Way code for shuffling an array doesn't behave as expected

    On Jul 17, 2004, at 4:12 PM, Alexey Verkhovsky wrote:

    > On Sun, 2004-07-18 at 01:43, Mark Sparshatt wrote:
    >> It's because the block passed to collect is changing the contents of
    >> a,
    >> so it doesn't reach all the elements of a.

    >
    > So, Ruby iterators operate on the original collection and let you
    > modify
    > the collection in the process. Another fact useful to remember.
    >
    > This is, by the way, one thing I like about Java iterators: they are
    > fail-safe in such scenarios. E.g.
    >
    > List list = new ArrayList(2);
    > list.add(new Object());
    > list.add(new Object());
    > Iterator i = list.iterator();
    > i.next(); // this is OK
    > list.add(new Object()); // list gets changed
    > i.next(); // throws ConcurrentModificationException
    >
    > IMHO, an exception here is The Right Thing (TM). At least, it saved me
    > from my own stupid mistakes more than once.


    I'm not sure that an exception would be The Right Thing, but I do
    wonder why it no longer works on a copy of the array? Allowing
    modification of the actual array as you iterate over it seems
    dangerous.

    But there must have been a reason for abandoning the original
    behavior... Perhaps it was a performance issue?

    cheers,
    Mark
     
    Mark Hubbart, Jul 18, 2004
    #5
  6. On Sun, 2004-07-18 at 03:04, Mark Hubbart wrote:
    > I'm not sure that an exception would be The Right Thing

    A smilie was inadvertently omitted in my earlier post. It should have
    said "The Right Thing (TM) :)"

    :)

    Life is full of compromises, and on the issue of what to do with stale
    iterators, there are at least three choices:

    1. Nothing (fast and dangerous)
    2. Iterate over a copy (safe and slow)
    3. Iterate over the original, but detect stale iterators (the middle
    way, much safer than 1 and somewhat faster than 2).

    Each of the three choices has pros and cons.

    All I'm saying is that in my personal experience Java's
    ConcurrentModificationException has prevented or exposed many bugs that
    otherwise would be subtle and probably very costly affairs. Which is why
    I like it.

    But then, the most important difference between Ruby and Java is that
    the former lets you do anything, and that includes shooting yourself in
    the foot, too. The latter, meantime, does not believe that you REALLY
    know what you are doing. :)

    Best regards,
    Alex
     
    Alexey Verkhovsky, Jul 18, 2004
    #6
  7. Alexey Verkhovsky

    Gully Foyle Guest

    Alexey Verkhovsky wrote:
    > On Sun, 2004-07-18 at 03:04, Mark Hubbart wrote:
    >
    >>I'm not sure that an exception would be The Right Thing

    >
    > A smilie was inadvertently omitted in my earlier post. It should have
    > said "The Right Thing (TM) :)"
    >
    > :)
    >
    > Life is full of compromises, and on the issue of what to do with stale
    > iterators, there are at least three choices:
    >
    > 1. Nothing (fast and dangerous)
    > 2. Iterate over a copy (safe and slow)
    > 3. Iterate over the original, but detect stale iterators (the middle
    > way, much safer than 1 and somewhat faster than 2).
    >
    > Each of the three choices has pros and cons.
    >
    > All I'm saying is that in my personal experience Java's
    > ConcurrentModificationException has prevented or exposed many bugs that
    > otherwise would be subtle and probably very costly affairs. Which is why
    > I like it.
    >
    > But then, the most important difference between Ruby and Java is that
    > the former lets you do anything, and that includes shooting yourself in
    > the foot, too. The latter, meantime, does not believe that you REALLY
    > know what you are doing. :)
    >
    > Best regards,
    > Alex
    >
    >
    >
    >


    One alternative solution is to provide for all three options using a
    resettable flag. Kinda like $SAFE for security.
     
    Gully Foyle, Jul 18, 2004
    #7
  8. Alexey Verkhovsky wrote:

    > One example in The Ruby Way claims that in the following snippet second
    > line shuffles the a (i.e., makes an array consisting of the same
    > elements as a, but in a different order).


    By the way, if you just want to shuffle an Array you can also use the
    much simpler ary.sort_by { rand }. (It will get minimally biased with
    containers containing millions of elements, but that can be fixed by
    using Array.new(32) { rand } instead of rand.)

    > Best regards,
    > Alexey Verkhovsky


    More regards,
    Florian Gross
     
    Florian Gross, Jul 18, 2004
    #8
  9. Alexey Verkhovsky

    Hal Fulton Guest

    Florian Gross wrote:
    > Alexey Verkhovsky wrote:
    >
    >> One example in The Ruby Way claims that in the following snippet second
    >> line shuffles the a (i.e., makes an array consisting of the same
    >> elements as a, but in a different order).

    >
    >
    > By the way, if you just want to shuffle an Array you can also use the
    > much simpler ary.sort_by { rand }. (It will get minimally biased with
    > containers containing millions of elements, but that can be fixed by
    > using Array.new(32) { rand } instead of rand.)


    And by the way, the code which modified the array while iterating
    was probably ill-advised from the beginning, although it did work
    in 1.6.8.


    Hal
     
    Hal Fulton, Jul 18, 2004
    #9
    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. Joe Burnett
    Replies:
    0
    Views:
    2,063
    Joe Burnett
    Sep 7, 2003
  2. SilverWolf

    sorting and shuffling array

    SilverWolf, Oct 13, 2003, in forum: C Programming
    Replies:
    3
    Views:
    379
    Richard Heathfield
    Oct 14, 2003
  3. Stephan Aspridis

    Loop doesn't behave the way it's supposed to.

    Stephan Aspridis, Jan 3, 2004, in forum: C Programming
    Replies:
    19
    Views:
    535
    Stephan Aspridis
    Jan 11, 2004
  4. Rasika WIJAYARATNE
    Replies:
    0
    Views:
    628
    Rasika WIJAYARATNE
    Dec 14, 2007
  5. Asfand Yar Qazi
    Replies:
    2
    Views:
    120
    Asfand Yar Qazi
    Jan 19, 2006
Loading...

Share This Page