On Fri, Nov 28, 2008 at 03:41:40AM +0900, Francoys wrote:
> -------------------------------------------------------------------------
> technic 1 (bad)
> 
> a_list = # a big link list
> b_list = # another big link list
> c_list = # result of operation on a_list and b_link
> 
> def foo1(x,y,z)
>    .....
> end
> 
> def foo2(x,y,z)
>    ......
> end
> 
> def foo3(x,y,z)
>    .....
> end
> 
> # le content of a_lst, b_lst, c_lst are will be modify by foo1, foo2, foo3
> foo(x,y,z)
>    a_lst, b_lst, c_lst = foo1(x,y,z)
>    a_lst, b_lst, c_lst = foo2(a_lst, b_lst, c_lst)
>    a_lst, b_lst, c_lst = foo3(a_lst, b_lst, c_lst)
>    return a_lst, b_lst, c_lst
> end
> 
> p( foo(a_list, b_list, c_list))
> # in this case scenario: 6 lists ares needed !

Perhaps you've misunderstood how assignments work in Ruby. If you do

  a = [:an, :array, :of, :stuff]
  b = a

then the second line does *not* create any new object. So what you say is
true only if function foo1 creates and returns three new lists.

Now, if foo1, foo2 and foo3 each modify the lists (as you say), then they
don't even need to return them. You could just do:

  def foo(x,y,z)
    foo1(x,y,z)
    foo2(x,y,z)
    foo3(x,y,z)
  end

  foo(a_list, b_list, c_list)
  p a_list, b_list, c_list

> -------------------------------------------------------------------------
> technic 2 (faster and better because it's create less list, but i do not
> like globals variables)
> 
> $a_list = # a big link list
> $b_list = # another big link list
> $c_list = # result of operation on a_list and b_link
> 
> def foo1()
>   op1($a_list,$b_list,$c_list)
> end
> 
> def foo2()
>    op2($a_list,$b_list,$c_list)
> end
> 
> def foo3()
>    op2($a_list,$b_list,$c_list)
> end
> 
> foo()
>    foo1()
>    foo2()
>    foo3()
> end
> 
> foo()
> p $a_list, $b_list, $c_list
> 
> # in this case scenario: 3 lists ares needed !

That ends up being pretty much the same as my modified example above, except
that I didn't use any globals.

As you're probably aware, after execution of

   op1($a_list, $b_list, $c_list)

those three global variables *must* still be pointing to the same objects -
the references are passed by value. However op1 can mutate those objects.

> -------------------------------------------------------------------------
> technic 3 (faster, and smarter because lists cease to exist with the end
> of execution of p )
> 
> foo()
>    a_list = # a big link list
>    b_list = # another big link list
>    c_list = # result of operation on a_list and b_link
> 
>    def foo1()
>      op1(a_list,b_list,c_list)
>     end
> 
>     def foo2()
>        op2(a_list,b_list,c_list)
>     end
> 
>     def foo3()
>       op2(a_list,b_list,c_list)
>     end
> 
> foo1()
> foo2()
> return foo3()
> end
> 
> p foo()

No, not faster or necessarily smarter. Objects are not destroyed when they
drop out of scope; they are destroyed after they no longer have any live
references, when the next garbage collection is done.

If you really want to organise your code like this, you can using lambdas:

  def foo
    a_list = ...
    b_list = ...
    c_list = ...

    foo1 = lambda {
      op1(a_list, b_list, c_list)
    }

    ...
    foo1[]
  end

This just avoids having to pass a_list, b_list, c_list to foo, because they
are visible in the enclosing scope.

But in many cases a more Rubyesque way would be to create a container class
to hold those three lists and to group together the operations on them.

class A
  class B
    attr_accessor :a, :b, :c
    def initialize(a,b,c)
      @a, @b, @c = a,b,c
    end
    def foo1
      op1(@a, @b, @c)
      # or perhaps: @a, @b, @c = op1(@a, @b, @c)
    end
    ...
  end

  def foo
    a_list = ...
    b_list = ...
    c_list = ...

    b = B.new(a_list, b_list, c_list)

    b = b.foo1
    ...
    return b.a, b.b, b.c
  end
end

Notice that a 'throwaway' instance of class B is created inside method
A#foo, and is garbage-collected after it returns. That doesn't matter -
objects are cheap to create in Ruby, and it's a small object as it just has
three instance variables (which only hold a reference to the list objects,
which already exist).

With this approach you need to decide where it makes most sense for op1 to
live. If it makes sense to live in class B then it gets even simpler, as it
can access the instance variables @a, @b, @c directly without having to have
them passed as arguments.

I reiterate - objects are created and destroyed often in Ruby. You may not
realise it, but even

   10.times { puts "hello" }

creates 10 distinct String objects, and garbage collects them.

Regards,

Brian.