411 REFERENCE · Wikibooks
The Sway Reference Manual/Functions
Reference material by Wikibooks contributors. This retains its source identity and is not 411 news reporting.
Recall, the series of expressions we evaluated to find
the y-value of a point on the line
y = 5x - 3
given an x-value:
sway> var m = 5;
INTEGER: 5
sway> var x = 9;
INTEGER: 9
sway> var b = -3;
INTEGER: -3
sway> var y = m * x + b;
INTEGER: 42
sway> y;
INTEGER: 42
Now, suppose we wished to find the y-value corresponding to
a different x-value or, worse yet, for a different x-value
on a different line. All the work we did would have to be
repeated. A function is a way to encapsulate all these operations
so we can repeat them with a minimum of effort.
Encapsulating a series of operations
First, we will define a not too useful function that
calculates y give a slope of 5,
a y-intercept of -3, and an
x-value of 9 (exactly
as above). We do this by wrapping a function around
the sequence of operations above.
The return value of a function is the value of the
last thing evaluated.
function y()
{
var m = 5;
var x = 9;
var b = -3;
m * x + b; //this quantity is returned
}
There are a few things to note. The keyword function indicates
that a function definition is occurring. The name of this
particular function is y. The stuff between the curly braces
is the code that will be evaluated (or executed) when the
function is called. This code is not evaluated until then.
You can copy and paste this function into the Sway interpreter.
If you do, you'll see something like:
sway> function y()
more> {
more> var m = 5;
more> var x = 9;
more> var b = -3;
more> m * x + b; //this quantity is returned
more> }
FUNCTION: <function y()>
Notice that the interpreter prompt changes to more> when
the input is incomplete. In the case of the function definition
above, that occurs when the curly close brace is enteredTyping a long construct into the interpreter is tedious.
In a later chapter,
we will learn how to store our code in a file and have
the interpreter execute the code in that file. If we need to
make changes to our code, we simply edit the file. That
way, we do not need to type in the modified code into the
interpreter from scratch..
Once the function is defined, we can find the value of y repeatedly:
sway> y();
INTEGER: 42
sway> y();
INTEGER: 42
The parentheses after the y indicate that we wish to call
the y function and get its value.
The y function, as written,
is not too useful in that we cannot use it to compute
similar things, such as the y-value for a different value of
x. But before we improve our function,
let's modify it so that it displays the current environmentSway lets you redefine variables and functions..
This may help you to understand what happens in a function call.
while the body of the function is executing:
function y()
{
var m = 5;
var x = 9;
var b = -3;
pp(this);
m * x + b; //this quantity is returned
}
When we call the new version of y, we see its current environment,
which has bindings for b, x, and m.
sway> y();
<OBJECT 2566>:
context: <OBJECT 749>
dynamicContext: <OBJECT 749>
callDepth: 1
constructor: <function y()>
this: <OBJECT 2566>
b: -3
x: 9
m: 5
INTEGER: 42
The variables b, x, and m are known as local variables since
they are not directly visible outside the neighborhood of the function
body.
Passing arguments
A hallmark of a good function is that it lets you compute
more than one thing. We can modify our function to take in the
value of x in which we are interested.
In this way,
we can compute more than one value of y.
We do this by passing in
an argument, in this case, the value of x.
function y(x)
{
var slope = 5;
var intercept = -3;
return slope * x + intercept;
}
We give names to the values being passed in by placing variable
names between the function definition parentheses.
In this case, we chose x as the name.
Notice that since we are passing in x, we no longer need (or want)
the definition of x, so we delete it.
Now we can compute y for an infinite number of x'''s:
sway> y(9);
INTEGER: 42
sway> y(0);
INTEGER: -3
sway> y(-2);
INTEGER: -13
What if we wish to
compute a y-value for a given x for a different
line? One approach would be to pass in the slope and interceptas well as x:
function y(x,slope,intercept)
{
return slope * x + intercept;
}
sway> y(9,5,-3);
INTEGER: 42
sway> y(0,5,-3);
INTEGER: -3
If we wish to calculate using a different line, we just pass in the
new slope and intercept along with our value of x.
This certainly works as intended, but is not the best way. One problem
is that we keep on having to type in the slope and intercept even if
we are computing y-values on the same line. Anytime you
find yourself doing the same tedious thing over and over,
be assured that
someone has thought of a way to avoid that particular tedium.
So assuming that is true, how do we
customize our function so that we only have to enter the slope
and intercept once per particular line? We will explore
three different ways for doing this. In reading further,
it is not important if you understand all that is going on.
What is important is that you know other approaches exist
and understand the pros and cons of each approach:
Creating functions on the fly
Since creating functions is hard work (lots of typing) and
Computer Scientists avoid hard work like the plague, somebody
early on got the idea of writing a function that itself
creates functions! Brilliant! We can do this for our line problem.
We will tell our creative function to create a y function
for a particular slope and intercept! While we are at it,
let's change the variable names m and b to slopeand intercept, respectively:
function makeLine(slope,intercept)
{
function y(x)
{
slope * x + intercept;
}
y;
}
The makeLine function creates a local y function
and then returns it. This next version is equivalent:
function makeLine(slope,intercept)
{
function y(x)
{
slope * x + intercept;
}
}
Since the last thing makeLine does is to define the yfunction, the y function is returned by a call tomakeLine.
So our creative function simply defines a y function and then
returns it. Now we can create a bunch of different lines:
sway> var a = makeLine(5,-3);
FUNCTION: <function y(x)>
sway> var b = makeLine(6,2);
FUNCTION: <function y(x)>
sway> a(9);
INTEGER: 42
sway> b(9);
INTEGER: 56
Notice how lines a and b remember
the slope and intercept supplied
when they were createdThe local function y has, as its context, the local environment
of the makeLine function. This environment
holds the bindings for slope and intercept..
While this is decidedly cool, the problem is many languages (C and Java
included) do not allow you to define functions that create other functions.
Fortunately, Sway does allow this.
Using objects
Another approach to our line problem is
to use something called an object.
In Sway,
an object is simply an environment and we have seen those
before.
So there is nothing new here except in how to use objects
to achieve our goal.
Here, we define a function
that creates and returns a line object.
A function that creates and returns an object is
known as a constructor.
function line(slope,intercept)
{
this;
}
The this variable always points to the current environment,
which in this case includes the bindings
of the formal parameters slope and intercept.
By returning this,
we return the environment of line,
and we can look up the values ofslope and intercept at our leisure.
To prove that slope and intercept
exist, we can use the built-in pretty printing function,pp:
sway> m = line(5,-3);
OBJECT: <OBJECT 231>
sway> pp(m);
<OBJECT 231>:
context : <object 145>
dynamicContext: <object 145>
constructor: <function line(slope,intercept)>
this: <object 231>
intercept: -3
slope : 5
OBJECT: <OBJECT 231>
We access the variables in an object with the '.' (dot) operator:
sway> m . slope;
INTEGER: -3
sway> m . constructor;
FUNCTION: <function line(slope,intercept)>
Now we modify our y function to take in a line object as
well as x and use the dot operator to extract the line's slope
and intercept:
function y(line,x)
{
line . slope * x + line . intercept;
}
In this scenario, we create different lines, then pass each
line to our new y function:
sway> var m = line(5,-3);
OBJECT: <object 231>
sway> var n = line(6,2);
OBJECT: <object 256>
sway> y(m,9);
INTEGER: 42
sway> y(n,9);
INTEGER: 56
The problem with this approach is we have separated line objects from
finding y values, yet these two concepts are closely related. As
an example, suppose we have parabola objects as well as line objects.
Our y function would fail miserably for parabola objects
even though the concept of (x,y) points on a parabola is just as
valid as points on a lineHere is a concrete example of trying to generalize, so that
our function works for all objects for which the concept of
the function is valid..
In the
object-oriented world, we solve this problem by
bundling the object and functions that work specifically on
that object together. In our case,
we
make the y function part of the line object:
function line(slope,intercept)
{
function y(x)
{
slope * x + intercept;
}
this;
}
This is very similar to the functions-on-the-fly approach, but we
return this instead of the function bound to y.
Now we call the y function
via the line object.
sway> var m = line(5,-3);
OBJECT: <object 231>
sway> var n = line(6,2);
OBJECT: <object 256>
sway> m . y(9);
INTEGER: 42
sway> n . y(9);
INTEGER: 56
Should we have a parabola object,
it would have its own y function
with a different implementation.
We would call it just the same, however:
sway> var p = parabola(2,0,0);
OBJECT: <object 453>
sway> p . y(7);
INTEGER: 49
This approach is supported in object oriented languages such as Java.
The earlier approach (where the function was separated from the object)
is supported in procedural languages such as C.
Functions versus operator
All operators are functions and can be called using operator
syntax. For example, the following expressions both sum the
values of a and b:
var sum = a + b;
var sum = +(a,b);
Conversely, any function of two arguments can be called using operator
syntax.
Sometimes using operator syntax makes your code more clear.
Let's make a function that increments a variable by a given amount,
similar to the C, C++, and Java operator of the same name:
function +=($v,amount)
{
$v = force($v) + amount;
}
Don't worry about how the code works; just note that
the += function has two formal parameters ($vand amount'') and thus takes two arguments. We can
call += to increment a variable using function
call syntax:
var x = 2;
+=(x,1);
inspect(x);
or we can use operator
syntax:
var x = 2;
x += 1;
inspect(x);
In both cases, the output of the code fragments is the same:
x is 3
Functions that are called using operator syntax have the
same precedence level as the mathematical operators and
are left associative.
Footnotes
Assignment Storing Code in Files