scope question

Discussion in 'Ruby' started by Harry Kakueki, Apr 2, 2010.

  1. Maybe I'm missing something obvious here, but...
    Does this result surprise anyone else or just me?
    I was expecting a_hash and an_arr to be local to the method.
    Did I just do something stupid?


    def scope_q(a_hash,an_arr,a_num)
    a_hash["b"] = 3
    an_arr[1] = 5
    a_num += 1
    end

    h = {"a"=>1,"b"=>2,"c"=>3,"d"=>4}
    a = [1,2,3,4]
    n = 7

    scope_q(h,a,n)

    p h #> {"a"=>1, "b"=>3, "c"=>3, "d"=>4}
    p a #> [1, 5, 3, 4]
    p n #> 7


    #ruby 1.9.1p243


    Harry
     
    Harry Kakueki, Apr 2, 2010
    #1
    1. Advertisements

  2. Harry Kakueki

    Ryan Davis Guest

    nope. that's how it works. Every reference (variable, name, whatever you =
    want to call it) to an object (with some exceptions) is a pointer to the =
    actual object. When you call scope_q(h,a,n) you are passing those =
    references into the method and they're modified directly.
    The obvious counter-example is here with a_num/n. Small integers =
    (fixnums) are optimized so they're not a pointer to a 32 bit number and =
    are instead a 30 bit number (2 bits used for flags for special cases). =
    so 7 gets copied in instead of passing a reference to it. As a result, =
    the copy gets modified, not the original value at 'n'.
     
    Ryan Davis, Apr 2, 2010
    #2
    1. Advertisements

  3. I am afraid, you are. Hopefully not for long ;-)
    a_hash, an_arr and a_num are local to the method indeed. However, a_hash an=
    d an_array hold references to hash and array object respectively, not copie=
    s. So you are changing the objects passed to this method via those referenc=
    es. On the other hand, a_num is passed a Fixnum instance which are implemen=
    ted as immediate objects and hence passed as copy.
    Here where you pass references to Hash h and Array a, and n as an immediate=
    object 7.
    Changed in the method
    Changed in the method
    Not changed as the methods incremented a copy.
    All ruby versions work identically in this area.
     
    Gennady Bystritsky, Apr 2, 2010
    #3
  4. and an_array hold references to hash and array object respectively, not cop=
    ies. So you are changing the objects passed to this method via those refere=
    nces. On the other hand, a_num is passed a Fixnum instance which are implem=
    ented as immediate objects and hence passed as copy.
    def scope_q(an_arr)
    #an_arr[1] =3D 5 #This is a reference.
    an_arr =3D [6,6] #This is a local an_arr ?
    end

    a =3D [1,2,3,4]

    scope_q(a)

    p a #> [1, 2, 3, 4]



    Harry
     
    Harry Kakueki, Apr 2, 2010
    #4
  5. want to call it) to an object (with some exceptions) is a pointer to the ac=
    tual object. When you call scope_q(h,a,n) you are passing those references =
    into the method and they're modified directly.
    ) are optimized so they're not a pointer to a 32 bit
    cases). so 7 gets copied in instead of passing a
    lue at 'n'.

    I would say that in order to explain this is more relevant to say that
    Fixnums are inmutable, rather that the fact that they are implemented
    as inmediate values. In his method he is not calling a mutating method
    on Fixnum, just assigning a new object to the local variable:

    def scope_q(a_hash,an_arr,a_num)
    a_num +=3D 1 # this is equivalent to a_num =3D a_num + 1
    end

    So my point is that, independently on the internal implementation of
    Fixnums, the important consideration for his question is the fact that
    they are inmutable, and so it's impossible to call a method that would
    modify the object. So it's not possible to mimic the same examples as
    with hashes and arrays.

    Jesus.
     
    Jesús Gabriel y Galán, Apr 2, 2010
    #5
  6. and an_array hold references to hash and array
    o this method via those references. On the other hand,
    To clarify this: variables are references to objects. When you say this:

    an_array[1]=3D 5

    you are calling method "[]=3D" of the object referenced by an_array.
    This object happens to be referenced also in the outer scope of the
    method scope_q, in your example by variable a. So, if the method "[]=3D"
    modifies the object, that modification is seen outside when you
    reference the object with variable a. On the other hand, when you do
    this:

    an_arr =3D [6,6]

    you are not calling any method on the object previously referenced by
    an_arr. You are assigning a new object to the local variable an_arr.
    This does not affect in any way variable "a" in the outer scope, or
    the object referenced by a.

    Jesus.
     
    Jesús Gabriel y Galán, Apr 2, 2010
    #6
  7. Harry Kakueki

    Josh Cheek Guest

    You can get the array and hash to mimic the number, though, by calling non
    mutating methods on them.


    def scope( old_hash , old_array , old_num )
    puts "in scope"
    p (old_hash.merge Hash[ 'e',5 , 'f',6 , 'g',7 ]) # =3D> {"a"=3D>1, "b"=
    =3D>2,
    "c"=3D>3, "d"=3D>4, "e"=3D>5, "f"=3D>6, "g"=3D>7}
    p (old_array +=3D [5,6,7]) # =3D> [1, 2, 3, 4, 5=
    , 6,
    7]
    p (old_num +=3D 1) # =3D> 8
    end



    hash =3D {"a"=3D>1,"b"=3D>2,"c"=3D>3,"d"=3D>4}
    array =3D [1,2,3,4]
    num =3D 7

    scope( hash , array , num )

    puts
    puts "after scope"
    p hash # =3D> {"a"=3D>1, "b"=3D>2, "c"=3D>3, "d"=3D>4}
    p array # =3D> [1, 2, 3, 4]
    p num # =3D> 7
     
    Josh Cheek, Apr 2, 2010
    #7
  8. Thanks for all the explanations, everybody.
    I could pretty much understand what was happening when I saw it happen
    but it just surprised me that it worked that way.
    I'm sure I have read that method parameters are local variables in that method.
    Either that is not quite right or I have misinterpreted what that means.
    Apparently, the latter. I need to read up on this.

    Anyway, thanks again.

    Harry
     
    Harry Kakueki, Apr 4, 2010
    #8
  9. That is true: method parameters are local variables in that method
    that are initially bound to what the caller passes as arguments to the
    method.

    The rest of your example illustrates what happens when you call
    mutating methods on objects, or reassign those variables.

    Jesus.
     
    Jesús Gabriel y Galán, Apr 4, 2010
    #9
  10. Harry Kakueki

    rlf Guest

    Coming from a C background, I had similar difficulties; worrying about
    pointer versus copy parameter semantics. The answer to my concerns
    turned out to be... Why should I have to care?

    With Ruby, you don't. It takes a different tact altogether.
    This is how I reconciled it.
    Try this in IRB:
    Set up your outer variables

    h = {"a"=>1,"b"=>2,"c"=>3,"d"=>4}
    a = [1,2,3,4]
    n = 7

    But instead of creating your method, just do this:

    a_hash, an_arr, a_num = h , a , n # thru parallel assignment

    Now, if you execute some of those statements you had in your method,
    you should still get the same outcomes, but hopefully not as
    surprising in this context.

    A quick digression regarding assignment semantics:
    The 'local' * Variables *( on the left hand side ) are getting their
    initial values
    By evaluating the * Expresssions * on the right hand side of the
    assignment, which evaluate to a list of Objects. i.e. h, a and n
    offered up their objects to initialize the variables on the left.


    When you invoke a method, Ruby essentailly performs this kind of
    parallel assignment:

    - The Arguments in the Invocation are treated as a Right Hand Side
    list of expressions that evaluate to Objects.

    - The Parameters specified in the method Definition act as the
    (local) variables on the Left Hand Side.

    To me, that's one of the great incites in Ruby. It lets us write
    incredibly flexible methods, courtesy of the parallel assignment
    "splatters".
     
    rlf, Apr 4, 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.