Functions and Procedures



Often, we need to use or call the same set of code-statements that execute a common task from different locations in our script. We could copy and paste these statements everywhere we need them. But this will bloat our code and if we need to change any of these statements, we will have to make the same changes all over the place. There must be a better solution.


Well, some smart people solved this issue by introducing the ability to create Functions/Procedures/Methods in programming languages.
A function (procedure or method) is a way to group a set of code-statement together and give them a name that is referenced every time we want to execute them. In another word, a function is a series of relatively independent programming statement performing a specific task encapsulated into a reusable package with a unique name that may or may not take data inputs and may or may not return data as output.
So a function or a procedure is like a juicer that takes only vegetables and fruits as input and produces a refreshing juice as an output. Its functionality and usage is generally understood by all and we refer to it by its self-explanatory name. It will not work if we feed it wood.


There are four types of functions or procedures:

  1. A function that takes no inputs, options or parameters and has no output or returns any data (like a generic greeting function).
  2. A function that takes a number of inputs, options or parameters and has no output or returns any data (like a customized greeting message).
  3. A function that takes no input, options or parameters but returns some data (like a random or a dice function).
  4. A function that takes a number of inputs, options or parameters and returns some data (like a square root function or sorting function).



Functions and procedures must have a unique name and a unique signature. A function signature refers to the type and number of inputs or parameters it takes, whether it returns some data and the type of the output data.
Functions can define local variables within its scope or manipulate global variables that were defined elsewhere.
Once functions and procedures are defined and sourced (loaded in memory) they can be called from anywhere where they are visible (whether they are globally or locally defined).

Defining Procedures / Function:


Functions and procedures are constructed using the following parts:











Maya MEL:


In Maya MEL functions are called procedures and can be globally or locally defined. Locally scoped procedure definitions must appear before they are called. In general, all procedures and commands from plug-ins should have unique names. If you use the same name, for both a procedure and a plug-in command, the procedure takes precedence.

Local procedures:


If you leave the global keyword off the beginning of a procedure declaration, the procedure is local to the file in which it is defined. You can expose just one or two global procedures, hiding the helper code from other people using your scripts.
You cannot define a local procedure in the Script Editor. They are only available in external script files. (Maya documentation)

Maya MEL:

/*
Example of a local function that can only be called from within the script file and has no input/arguments or return
*/
proc displayMyMessage()
{
    print "hello there!";
}

Global procedures:


Use the keyword global in front of proc to create a global procedure accessible from anywhere in Maya.

Maya MEL:

//example of a global function with input/arguments and return nothing
global proc setMyLocation(float $x, float $y, float $z)
{
    move -a $x $y $z;
}
//example of a global function with input/arguments and a return data
global proc float getSphereVolume ( float $sphereRadius)
{
     float $pi = 3.14159265; //this is a local variable not visible outside this procedure
     return ( (4.0/3.0) * $pi * pow($sphereRadius, 3) );
}
//example of a global function without inputs/arguments but returns data
global proc int rollTheDice ()
{
     int $diceResults = (int) ( rand(1) * 6 ) + 1; //adding 1 so we don't get 0
     return $diceResults;
}


Global Variable:


A global variable is a persistent variable and visible everywhere in a single Maya instance. Variables you define outside of procedures are visible (able to be accessed and changed) to all other top level MEL code.
Occasionally, we need to have a variable accessible by all functions in Maya, like a common variable holing the current time of day for that we use global variable. Once a global variable is defined and executed once it become available to all and could be manipulated by any function within a single instance of Maya. Caution, the memory allocations for global variables and procedures don't get released until Maya closes which makes them memory hogs. So use them sparingly.
A local variable will override a global variable with the same name within the procedure.
All this allows you to write procedures without worrying whether the variable names you choose will conflict with Maya or other procedures.

Maya MEL:

global float $timeOfDay;

//example of a global variable to be used in a global function
global proc updateTimeOfDay ()
{
     global float $timeOfDay;
     if($timeOfDay < 0.9)
     {
         $timeOfDay += 0.1;
     }
     else
     {
         $timeOfDay = 0;
     }
     print $timeOfDay;
}



You could use the Maya MEL command whatIs followed by the procedure or variable name to get some information about where the procedure was defined or the type of variable.

Maya MEL:


whatIs "sphere"; //Result: Command
whatIs "initToolBox"; //Result: Mel procedure found in: …/maya/scripts/initToolBox.mel
whatIs "kaosFieldTest"; //Result: Script found in: …/maya/scripts/kaosFieldTest.mel


On some occasions you might see a code statement enclosed in a string and passed as the argument of Maya MEL command eval like eval "pow 8 2"; to prevent MEL parser from treating pow as an implicitly defined procedure.
A procedure can have an explicit definition (not this kind #@!%* of explicit) as follow proc myProc(){print "HI";} or an implicit definition where you use a procedure name without any previous definition (this is crazy).
You mean if I use an undefined procedure name in Maya MEL, then I am implicitly defined it. Yes, using a name in Maya MEL reserve that name for a procedure which may be defined in some script file or a loaded plug-in command.
If you don't believe me go ahead type and execute your name in Maya MEL window, then run the following command whatIs (type in your name).

Maya MEL:

//Explicit definition of foo
proc foo()
{
     print "explicit";
}
//Implicit definition of foo
proc myProc()
{
     foo;
}
/*
foo is a term frequently used in computer science referring to a generic name that has no special meaning.
Foo origin is not known with certainty. Some attribute Foo usage to the military who adopted it from 1930s cartoons to mean UFO. And some refer to MIT 1959 TMRC Language Dictionary where FOO means "our first obligation is to keep the foo counters turning". While others say it came from original IBM PC segment register containing F000 pointing to ROM basic and/or BIOS code. Some example of IBM usage of F00 published in 1981 (http://ask.slashdot.org/story/99/02/12/1433221/what-is-the-origin-of-foo)
*/


Calling procedures:

Using a defined MEL procedure is the same as using any other MEL command or function. To execute any MEL procedure, enter the name of the MEL procedure in a script, the Script Editor, or the Command Line.

If Maya encounters a command without a definition, it searches through the script paths for a MEL script with the same name as the command (minus the .mel extension on the file name). If it finds the file, it declares all the global MEL procedures within that file, and if the procedure you called exists in the file, it executes.

When you create and save a new MEL script, you must source it before externally calling a procedure coded in your new script. This is because Maya sources scripts at startup and on command, not when you try to call an external procedure from the Command Line or Script Editor. (Maya Online Documentation)

Maxscript:


Defining a function:


When you define a function in Maxscript, the following events occur:


Because the function itself is a value stored in a variable, you can call the function through the variable name. Also you can assign the function value to another variable, store it in an array, pass it as an argument to another function or assign another value to the variable and lose any reference to that function forever.

MAXScript:

/*
Example of a local function with no input/arguments or return
*/
fn displayMyMessage =
(
     format "hello there!"
)
– to call this function type displayMyMessage()

The function is visible in the current MAXScript scope context. The variables used inside a function are local variables, unless they were already defined at a higher scope or were declared as global in the function. The function parameter variables always have a local scope.


When writing scripts, it is good programming practice to explicitly declare your local and global variables. Implicit declaration is provided as a short-hand, typically used when working in the Listener interactively or developing short scripts. When developing extended scripts, explicitly declaring variables can reduce errors and improve readability of the code.

It is also recommend that you declare as local all variables unless you really want them to be global variables. The reasons for this are described in Scope of Variables. (Autodesk® 3ds MAXScript Reference)

Global procedures:


Global variables are visible in all running MAXScript code and hold their values until you exit 3ds Max. If a function variable is assigned to another global variable, a reference to the function value is stored in that variable. Thus while a function variable (name) may no longer be in scope, the function itself continue to be globally visible. The globalVars struct provides methods to collect, create, test and remove global variables.

MAXScript:


global myGlobalDisplayMessage
fn myGlobalDisplayMessage =
(
     format "hello there!"
)
– once executed you can run this command globalVars.isglobal #displayMessage


Function examples:

MAXScript:

--example of a function with input/arguments and return nothing
fn setMyLocation x y z =
(
     $.pos.x = x – the $ refers to selected object
     $.pos.y = y
     $.pos.z = z
)
setMyLocation 100 20 10 – to execute the above function

--example of a function with input/arguments and a return data
fn getSphereVolume sphereRadius =
(
     retVal = (4.0/3.0) * ::pi * (pow sphereRadius 3)
     return retVal
)
getSphereVolume 10 – to execute the above function
--example of a function without inputs/arguments but returns data
fn rollTheDice =
(
     diceResults = (int) (random 1 6)
     return diceResults
)
rollTheDice() – to execute the above function



In 3ds Max 8 and higher, a global variable can be specified by preceding its name with double-colon '::'. Such names are looked for only in the global variable pool, and will be created there if they do not currently exist.

MAXScript:


(
    local pi = "Hello!"
    format "% : %\n" pi ::pi

    fn test x:pi y:::pi = format "% : %\n" x y

    test()
)
/*
Output
Hello! : 3.14159
Hello! : 3.14159
*/




Python:


Defining a function:


Functions in Python are values like other data types and can be passed to other functions/object constructors, and so forth.
You can define functions in Python following these simple rules:







Python:


'''
Example of a local function with no input/arguments or return
'''
def displayMyMessage():
    print "hello there!"
# to call this function type displayMyMessage()

Scope of Variables:


All variables in a program may not be accessible at all locations in that program. This depends on where you have declared a variable. Variables that are defined inside a function body have a local scope and can be accessed only inside that function. While variables defined outside have a global scope and can be accessed throughout the program body by all functions. When you call a function, the variables declared inside it are brought into scope.


Python:

#Global and local variables
def myDisplayGlobalMessage():
    myLocalStr = "Hi local!"
    print myLocalStr
    print myGlobalMessage
'''
myDisplayGlobalMessage() will print both local and global variable
print myLocalStr outside the function will result in NameError: name 'myLocalStr' is not defined
'''



Function examples:

Python:

#example of a function with input/arguments and return nothing
def setMyLocation (x, y, z,):
     print 'your location is set to: {0} {1} {2}'.format(x, y, z)

setMyLocation (100, 20, 10) # to execute the above function


#example of a function with input/arguments and a return data
import math
def getSphereVolume(sphereRadius) :
     retVal = (4.0/3.0) * math.pi * math.pow(sphereRadius, 3)
     return retVal


sVol = getSphereVolume(10) #to execute the above function

print sVol

#example of a function without inputs/arguments but returns data
import random
def rollTheDice():
     diceResults = (int)( random.randrange (1, 6))
     return diceResults


print rollTheDice() # to execute the above function



Default arguments:


A default argument is an argument that assumes a default value if a value is not provided in the function call for that argument.

Python:


def printLocation( x = 0, y = 0, z = 0 ):
     "This prints the incoming arguments"
     print 'X = {0}\nY = {1}\nZ = {2}'.format(x,y,z)
     return

#Now you can call it
printLocation ( 50,20,10 )
printLocation ()


Variable-length arguments:


On occasion, more function arguments are needed but you don't want to include every possible argument thus making them as required arguments or default them to some values. Variable-length arguments are used for such occasions and they are not named in the function definition, unlike required and default arguments. Let say your function could handle multiple processing options while not all these options used together like a function that can print files to screen, save files to desk, or verify files spelling. You could build your function using variable-length arguments and process these arguments as options switching between possible function operations.


Python:

# This function takes one required argument and variable length arguments
def fileProcessor ( directoryPath, *var_tuple ):
     "Usage: fileProcessor (\"c:/tmp/\", \"read\", \"save\", \"spell\")"
     print 'Current operation on files @ \"{0}\" are: '.format(directoryPath)
     operations = ''
     for var in var_tuple:
         operations += var + ', '
         print operations.strip(' ,')
     return
# Now you can call fileProcessor function
fileProcessor( "c:/Autodesk/", "spell", "read" )


Anonymous Functions:


You can create small anonymous functions that are not declared in the standard manner and without using the def keyword. Why? Anonymous functions are one line operations that you may want to use over and over. Anonymous functions use the lambda keyword in their definition. The syntax of lambda functions contains only a single statement like:

lambda arg1 ,arg2,.....arg10: operation on the arguments

Python:


# Function definition is here
dot = lambda vec1, vec2: vec1[0] * vec2[0] + vec1[1] * vec2[1] + vec1[2] * vec2[2]
# Now you can call dot as a function and use it everywhere in your script
print "\nDot product: ", dot([10,20,10], [-20,45,10])



Lua:


Defining a function:

http://www.lua.org/pil/5.1.html
Functions abstract processing statements and expressions. Most function calls of functions that have no arguments include empty list () to indicate the call unless the function has one single argument of literal string or a table constructor, then the parentheses are optional. Functions used by a Lua program can be defined both in Lua and in C (or in any other language used by the host application). In Lua, a function is a value with the same rights as conventional values like numbers and strings. Functions can be stored in variables (both global and local) and in tables, can be passed as arguments, and can be returned by other functions.
You can define functions in Lua following these simple rules:

Lua:


--Example of a local function with no input/arguments or return
function displayMyMessage()
     print "hello there!"
end


A somewhat difficult notion in Lua is that functions, like all other values, are anonymous; they do not have names. When we talk about a function name, say print, we are actually talking about a variable that holds that function. Like any other variable holding any other value, we can manipulate such variables in many ways. The following example, although a little silly, shows the point:

Lua:


a = {p = print}
a.p("Hello World") --> Hello World
print = math.sin – `print' now refers to the sine function
a.p(print(1)) --> 0.841470
sin = a.p – `sin' now refers to the print function
sin(10, 20) --> 10 20


Example of Lua auto adjusts the number of arguments to the number of parameters:

Lua:


function f(a, b)
     return a or b
end
--CALL PARAMETERS
f(3) – a=3, b=nil
f(3, 4) – a=3, b=4
f(3, 4, 5) – a=3, b=4 (5 is discarded)


When you call incCount(), Lua first initializes n with nil. The OR results in its second operand and Lua assigns a default 1 to n.

Lua:


function incCount (n)
     n = n or 1 -- This function has 1 as its default argument;
     count = count + n
end


Example of return of multiple results

Lua:


function foo0 () end – returns no results
function foo1 () return 'a' end – returns 1 result
function foo2 () return 'a','b' end – returns 2 results


In a multiple assignment, a function call as the last (or only) expression produces as many results as needed to match the variables:

Lua:

--calling the above define functions
x,y = foo2() – x='a', y='b'
x = foo2() – x='a', 'b' is discarded
x,y,z = 10,foo2() – x=10, y='a', z='b'
--If a function has no results, or not as many results as we need, Lua produces nils:
x,y = foo0() – x=nil, y=nil
x,y = foo1() – x='a', y=nil
x,y,z = foo2() – x='a', y='b', z=nil


A function call that is not the last element in the list always produces one result:

Lua:

--calling the above define functions
x,y = foo2(), 20 – x='a', y=20
x,y = foo0(), 20, 30 – x='nil', y=20, 30 is discarded


When a function call is the last (or the only) argument to another call, all results from the first call go as arguments. We have seen examples of this construction already, with print:

Lua:

--calling the above define functions
print(foo0()) -->
print(foo1()) --> a
print(foo2()) --> a b
--You can force a call to return exactly one result
by enclosing it in an extra pair of parentheses
print( ( foo0() ) ) --> nil
print( (foo1() ) ) --> a
print( ( foo2() ) ) --> a
--When the call to foo2 appears inside an expression, Lua adjusts the number of results to one;
--so, in the last line, only the "a" is used in the concatenation.
print(foo2(), 1) --> a 1

A constructor also collects all results from a call, without any adjustments:

Lua:

--calling the above define functions
a = {foo0()} – a = {} (an empty table)
a = {foo1()} – a = {'a'}
a = {foo2()} – a = {'a', 'b'}
-As always, this behavior happens only when the call is the last in the list; otherwise, any call
--produces exactly one result:
a = {foo0(), foo2(), 4} – a[1] = nil, a[2] = 'a', a[3] = 4


Variable-length arguments:


The three dots (...) in the parameter list indicate that the function has a variable number of arguments. When this function is called, all its arguments are collected in a single table, which the function accesses as a hidden parameter named arg. Besides those arguments, the arg table has an extra field, n, with the actual number of arguments collected.

Lua:


– This function takes one required argument and variable length arguments
function select (n, ...)
     return arg[n]
end
--Typically string .find returns multiple values indicating the indexes of search pattern
print(string.find("hello hello", " hel")) --> 6 9
--But using the function we defined above to select a specific return from the string.find function
print(select(1, string.find("hello hello", " hel"))) --> 6
print(select(2, string.find("hello hello", " hel"))) --> 9


Named arguments:


The parameter passing mechanism in Lua is positional: The first argument gives the value to the first parameter, and so on. Sometimes, we want to specify the arguments by name. Lua has no direct support for that syntax, but we can pack all arguments into a table and use that table as the only argument to the function.

Lua:


function rename (arg)
     return os.rename(arg.old, arg.new)
end
rename {old="temp.lua", new="temp1.lua"} – {xxx=yyy, zzz=www} creates a table


Closures:


A closure is a function along with all that it needs to access its up-values. A function enclosed in another function has full access to local variables from the enclosing function. This is called Lexical scoping. http://www.lua.org/pil/6.1.html

The function defined below uses an up-value, to keep its counter. However, by the time we call the function, the variable i is already out of scope, because the function that created that variable (newCounter) has returned. Nevertheless, Lua uses the concept of closure to access the function up-values correctly.

Lua:


function newCounter ()
     local i = 0
     return function () – anonymous function
         i = i + 1
         return i
     end
end

c1 = newCounter()
print(c1()) --> 1
print(c1()) --> 2


Function examples:

Lua:

--example of a function with input/arguments and return nothing
function setMyLocation (x, y, z,)
     print string.format("your location is set to: %d, %d, %d", x, y, z)
end
setMyLocation (100, 20, 10) – to execute the above function


--example of a function with input/arguments and a return data
function getSphereVolume(sphereRadius)
     retVal = (4.0/3.0) * math.pi * math.pow(sphereRadius, 3)
    return retVal
end
sVol = getSphereVolume(10) --to execute the above function
print sVol

--Example of a function without inputs/arguments but returns data
function rollTheDice()
     diceResults = math.floor( math.random (1, 6))
     return diceResults
end
print rollTheDice() – to execute the above function