Tech Tuesday: Functions (Part 3 - Argument Passing)

In the last two Tech Tuesdays we have learned about how functions let us define new words in a programming language and how arguments and other local variables are restricted to the scope of the function.  Today we will take another look at arguments and how exactly information is passed into the function.  Then Tech Tuesday will take the rest of the summer off and resume after Labor Day as I will take my usual late August break from blogging.

To motivate today’s discussion take a look at the following code and ask yourself what the second print will show:

function three(a) {
    a = 3;
    return a;
}

var n = 5;
print(three(n));
print(n);

We learned last week that the argument a is local to the function three and so wouldn’t conflict with a variable a that exists outside the function.  But now we are asking a different question, which is what happens to the variable n when we use it in our call to three?

When we run this code, we see that the first print statement gives us “3” — no surprise there.  The second print yields “5”, which means that the value of n is not affected when we change the value of a inside the function.  We refer to this as “call by value" — the function call first finds the value of n, which is 5, and then passes a copy of that value to the function three.  Using the metaphor of phone books that we introduced in the last post on data types  

What would be the other alternative?  A “call by reference.”  Huh?  Well, it could have been the case that instead of passing the value of n to the function we pass the location that the variable n points to into the function.  In that case a would essentially be an “alias” for n and by assigning to a we would be assigning to n.  The net result would be that after three(n), the value of n has been changed to 3.  As we will see, that is in fact what happens in Javascript when we use more complicated data types such as objects or arrays as arguments to functions.  Other languages have explicit constructs for allowing a call by reference.  

For instance, consider the following PHP code

function three($a) {
    $a = 3;
    return $a;
}

$n = 5;
print(three($n));
print($n);

As you can see this code looks identical to the Javascript code from above just using the $ sign in front of each variable name to denote a variable.  In fact when we run this code we get the same result as before.  Now look at the following code instead:

function three(&$a) {
    $a = 3;
    return $a;
}

$n = 5;
print(three($n));
print($n);

Do you notice the tiny difference?  The argument a inside the function is preceded by an & (ampersand).  In PHP this causes the argument to be passed by “reference.”   That means the argument a inside the function is now an alias for n and when we assign to a we are also assigning to n.  You can see that by running the code where the second print yields “3” (instead of 5 as before).  Put differently, after we exit the function three, the value of n has been changed from 5 to 3.

Now you may ask, doesn’t the defeat the whole purpose of having the arguments be local variables?  At some level it does, which is why some languages don’t support this call by reference at all.  But the story is not so easy.  What if the variable that we are using to call the function contains something very large?  We have not yet encountered complex data types (coming up after the summer break), but consider a very large text as in the following Javascript example:

function firstTwenty(t) {
    return t.substring(0, 20);
}

var book = "Once upon a time there lived ...";    // imagine a very, very long string here
print(firstTwenty(book));

In this case all we are interested in is finding the first twenty characters of the text — we wouldn’t really want to make a copy of the entire string just to do it.  Obviously this is a contrived example but there are many real cases where making a copy of a value would be a very expensive operation (taking up CPU time and memory).  Allowing a pass by reference is one solution to this problem.  There are also two other solutions.  First, we could prevent long or complicated data from being changed at all, so that we could not make a change to the long string even if we wanted to (we would have to make an explicit copy).  Second, we could make a copy of the supplied variable only if the argument is changed inside the function.  This ingenious idea is known as “copy on write.”  Both of these solutions are related to the important concepts of identity and mutability which we will cover in the future once we have introduced more complex data types.

So what should you take away from today?  That it’s not at all obvious what gets passed to a function when you call it.  Depending on the programming language, the data type of the argument and how the function was defined (in those languages that support it), you are either getting a copy of the value or a reference to it.  Those are two very different things with vastly different implications for whether activity inside the function will affect the program execution outside of the function.  Again this should serve as a reminder why getting code to do the right thing can be quite the challenge.

Enhanced by Zemanta

Posted: 14th August 2012Comments
Tags:  tech tuesday programming functions arguments

Newer posts

Older posts

blog comments powered by Disqus
  1. continuations posted this

Newer posts

Older posts