basic question: passing a modifiable argument to a routine

Discussion in 'Ruby' started by lalawawa, Jan 18, 2010.

  1. lalawawa

    lalawawa Guest

    Arguments to subroutines seem to be passed by value in ruby

    $ irb
    irb(main):001:0> def woof(a)
    irb(main):002:1> a += 2
    irb(main):003:1> end
    => nil
    irb(main):004:0> b = 5
    => 5
    irb(main):005:0> woof(b)
    => 7
    irb(main):006:0> b
    => 5
    irb(main):007:0>

    so how do I pass a variable to a routine if I want the routine to
    modify the variable. In C I would say:

    void woof(int *a) {
    *a += 2;
    }
    int b = 5;
    woof(&b);
    printf("%d\n", b);

    would output 7.
     
    lalawawa, Jan 18, 2010
    #1
    1. Advertisements

  2. They're called functions or methods in Ruby.
    Nope. They're passed by reference -- or rather, they're object
    references passed by value, like in Java.
    Yes, because += makes a reference to a different object.

    Don't. Instead, assign the return value to the variable, or use a bang!
    method on an object.

    b = woof(b)

    Best,
    -- 
    Marnen Laibow-Koser
    http://www.marnen.org
     
    Marnen Laibow-Koser, Jan 18, 2010
    #2
    1. Advertisements

  3. lalawawa

    Josh Cheek Guest

    [Note: parts of this message were removed to make it a legal post.]

    They are actually pass by reference, as in Java and C++, or arrays in C.

    For example
    def woof(a)
    a << "e"
    end

    a = "bcd"
    woof(a)
    a # => "bcde"

    Your example does not reflect this, because of two issues.

    The first is that numbers are immutable, so even though you can modify the
    objects themselves, you can't modify numbers.
    The second is that += does not modify the object itself, it modifies the
    label. It is the same as saying a=a+1
    This is because of the first reason, since they are immutable you must do
    a=a+1 instead of a++

    Honestly, I don't ever really find myself in situations where I need to do
    this, you may need to rethink your approach.
    Regardless, I suppose I can think of three alternatives.

    Alternative 1 is to use an instance variable, don't do this trivially, they
    will pollute your namespace.
    def woof
    @a+=2
    end

    @a = 2
    woof
    @a # => 4


    Alternative 2 is to have the method return the new value ie, it seems a
    little messy, though, since it requires code outside to know that it needs
    to assign results in some given manner.

    def woof(a)
    a+2
    end

    a = 2
    a = woof(a)
    a # => 4


    Alternative 3 is to wrap the variable in a mutable object, like Java's
    Integer (except that isn't mutable either :p), but I can't seem to come up
    with a way of doing this that I don't hate.



    If you care to show your example, we might be able to spot a more Ruby
    approach.
     
    Josh Cheek, Jan 18, 2010
    #3
  4. lalawawa

    lalawawa Guest

    When you say that += creates a different object, then are you saying
    that when I do

    a = 0
    while a < 1000000
    a += 1
    end

    it creates a million different objects and garbage collects most of
    them? Isn't that horribly inefficient for just doing integer
    arithmetic?
     
    lalawawa, Jan 18, 2010
    #4
  5. BTW, the more idiomatic way of doing this would be something like
    1.upto(1_000_000).each do |n|
    # something
    end
    Actually no. For performance reasons, Fixnums are implemented as
    singletons in all Ruby environments that I'm aware of.

    But yes, try playing around with the object_id method and you'll see
    interesting things.

    Best,
    -- 
    Marnen Laibow-Koser
    http://www.marnen.org
     
    Marnen Laibow-Koser, Jan 18, 2010
    #5
  6. In this case, those integers are all Fixnums which are represented by
    immediate values, so no allocation or GC is involved.

    Of course if you are really concerned about efficiency you can always
    do that calculation as:

    a =3D (1000000*1000000 + 1000000) / 2

    --=20
    Rick DeNatale

    Blog: http://talklikeaduck.denhaven2.com/
    Twitter: http://twitter.com/RickDeNatale
    WWR: http://www.workingwithrails.com/person/9021-rick-denatale
    LinkedIn: http://www.linkedin.com/in/rickdenatale
     
    Rick DeNatale, Jan 18, 2010
    #6
  7. lalawawa

    lalawawa Guest

    Well, I'm trying to understand. If I do

    a = 0
    while a < 1_000_000
    a += 1
    end

    a.object_id at the end is 2000001. Does this mean a 2 million
    instances of fixnum are in existance, just not garbage collected?
     
    lalawawa, Jan 18, 2010
    #7
  8. Apart from what Rick already said - the funny thing is: the fact that
    Ruby works this way (i.e. has a uniform object model as opposed to
    Java which does have non object types) makes a lot of things easier.

    And with the optimization Rick mentioned it even isn't too inefficient
    - something that Java programmer haunts when they use object types
    like java.lang.Integer and friends. I remember an article which
    elaborated on that design flaw of Java but I can't seem to find it
    right now. Maybe Charly has a link handy.

    Kind regards

    robert
     
    Robert Klemme, Jan 18, 2010
    #8
  9. No, Fixnums are "immediate values" which means there is just the
    object reference. So there is no object allocated on the heap where
    the reference points to. We had recent discussions about this and
    there is a nice article on Wikipedia that explains how this works:

    http://en.wikipedia.org/wiki/Tagged_pointer

    Kind regards

    robert

    --=20
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
     
    Robert Klemme, Jan 18, 2010
    #9
  10. You can think of it that way (1 million, actually -- Fixnums only use
    odd object_ids), but in fact Fixnums are implemented more efficiently
    than that, at least in MRI. You shouldn't run into performance issues
    with Fixnums.

    Best,
    -- 
    Marnen Laibow-Koser
    http://www.marnen.org
     
    Marnen Laibow-Koser, Jan 18, 2010
    #10
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.