2 Instructions, Functions, and Frameworks
A program is a list of instructions, executed in order from top to bottom. You can usually think of each line of code as a separate instruction, like an assignment statement that calculates a value and stores the result in memory as the value of a variable. Conditional constructions like if / then / else allow us to execute different lists of instructions based on values already calculated and iteration constructions like for loops allow us to repeat a set of instructions multiple times. Functions allow us to group sets of instructions together so that we can call them from multiple points in a program to perform the same operations on different sets of values.
Functions
As in algebra, functions in programming take some arguments and return a value. defines the function and shows how to apply it to any argument. The variable is just a place holder. In Python we could say the same thing with
def f(x): # define f(x) as all the indented stuff after the : a = x**2 # first term is assigned to a a = a + 2 * x # add on the second term a = a + 1 # and then the third return a # return the value to the calling expression
so that any time we called the function in an expression like
y = 5 + f(27 + z)
we will get back a value calculated according to the statements in the function definition f( )
. The value 27+z
will be evaluated and passed to the function, where that value will be used in place of the variable x
. We will get into a little more detail about variables and number format in the following chapters. Functions can be as simple as this straight through calculation, or much more complex with multiple decisions to make and instructions that may be doing much more than just calculations. All functions have three things in common:
- They take some number of arguments (including the special case where that number is zero)
- They execute some instructions to achieve a goal (including the special case where they do nothing)
- They return something as a result when they are finished (including the special case where that something is just the null set, nothing, or “
void
” in C)
It’s possible, but not very interesting for a function to take 0 arguments, execute 0 statements, and/or return an empty result. Before defining a function in code, you need to decide what inputs it needs as its arguments, what it will do before returning, and what output it will return.
Calling Functions
A function can be called as part of an expression in an assignment statement like this
y = 5 + f(27 + z)
and the result returned by the function will be used in the expression to calculate the result eventually assigned to y
, or a function can simply be called on its own as a standalone statement
f(27 + z)
When called this way, the return value, if any, will be discarded after the function completes. This is only useful if the code in the function is doing something besides just calculating a return value. A function could also be used as an argument to another function:
print(f(27 + z))
would print the value returned by the function f()
, and the value returned by print()
would be discarded.
Traditional and Looping Programs
Traditional computer programs are typically run from a command line where the user types the name of the program and any command line arguments, hits return and waits for the program to end. The program starts running, executes a predetermined set of instructions, maybe modified by the command line arguments, then ends, returning the user to the command line.
The modern applications you are familiar with usually start from some Graphical User Interface (GUI) when you double click with a mouse. The application program sets up all the resources it needs to function, then goes into a loop, checking for user input and responding to user instructions, maybe checking for new data from the internet and responding to it, then repeating the loop over and over until either the power is switched off, or the user instructs the application to close. Even when nothing is changing, the application is going through this loop, checking to see if there has been any input from the user.
Embedded systems use a similar looping strategy on microcontrollers, first setting up all the needed resources, then going through the same loop over and over to check for changes in conditions and to respond to those changes.
You can use the same languages to write either style of program and you need to think about which approach best suits the task you are trying to accomplish.
Frameworks
Every programming environment has its own framework within which we can define functions and execute statements to achieve our objectives. The framework usually includes a text file of code you have composed, as well as a library of additional code that extends on the core capabilities of the base language. If everything is assembled in a well crafted Integrated Development Environment (IDE) you can concentrate on your own code and the rest of the framework will fill in and support from the background, without your needing to pay it too much attention. That framework in the background defines what you need to do in the foreground to get your code to do something interesting.
Python
Python has the simplest framework to get started with. Your code goes in a text file with the extension .py or in a cell in a Jupyter Notebook. Import the libraries you want to use, define any functions of your own, then list the executable statements, or instructions, you want to run in order from top to bottom. The program will run in traditional mode and end when it runs out of executable statements.
# Comments start with # and are ignored by the system. # Import all the functionality from the numpy library. from numpy import *
def f(x): # way shorter than the version above return x**2 + 2 * x + 1 z = sin(25.0) # use the sin() function from numpy y = 5 + f(27 + z) print(y)
This framework requires almost no background just to get started, as you can do some useful things without even importing libraries or defining functions.
Line Breaks and Indentation are important in Python. Only the line right after the def statement is part of the function and that’s shown by the indenting. The same approach will apply later with other Python language structures.
C
Code goes in a text file with the extension .c and some declarations go in text files with the extension .h to list the contents of libraries and other source code files. The source file hello.c might contain the classic hello world program from section 1.1 of Kernighan and Ritchie:
// Comments start with // and are ignored by the system. // Include the header file for the standard input/output library // with functions including printf() #include <stdio.h> main() // every C program needs a main function { // curly braces begin and end blocks of code // Call the function printf with a string as the single argument. // The \n is translated into a 'newline' character. printf("hello, world \n"); } // this is the end of the function definition
When a C program starts running, it automatically calls a special function named main()
and that function must be defined in one of the source code files. main()
then executes some code and calls some functions, then when it returns, the program ends. If you want a looping program structure, then you can build that into your main()
function.
All executable statements must be inside a function and an error message will pop up if you try compiling code with an executable statement outside a function. The compiler simply doesn’t know what to do with it.
Line Breaks and Indentation mean nothing to the compiler in C. That’s why we need curly braces {}
to identify the code that is part of a function and semicolons ;
to indicate the ends of individual instructions. That doesn’t mean you shouldn’t use line breaks and indentation to make your code readable!
Arduino
The Arduino IDE hides some more of the framework to make your life easier and then calls a C compiler for you, so all the same rules still apply. In that hidden portion is the main()
function required by all C programs and #include
files for important libraries like Serial
. The Arduino main()
function calls the function setup()
once, then uses a while
loop to call the loop()
function repeatedly until the power is switched off or you push the hardware reset button. This framework is aimed at developing looping programs for embedded systems that just keep on going. The same example might look like this in the Arduino IDE:
void setup() { Serial.begin(115200); Serial.print("hello, world\n"); } void loop() { // put your main code here, to run repeatedly: }
The function setup()
takes no arguments, as seen by the empty parentheses. It is declared as returning type void
meaning it returns nothing. setup()
calls the Serial
library function begin()
to set the speed of the serial port so it can communicate with the serial monitor in the IDE. The speed at which the program is talking, must match the speed the monitor is listening for, so set the speed to 115200 baud or bits per second in the IDE’s Serial Monitor window.
The Serial library is written in C++, defining the Serial
‘object’ that has ‘methods’ like begin()
and print()
that are called just like functions with the Serial.print()
syntax where the period indicates you want the print()
method from the Serial
object. The most important thing, and maybe the only general thing you need to know about these libraries is just about every library has a begin()
method that must be called before you can use the library. C++ is an extended version of C that provides object oriented programming capabilities that are especially useful for writing reusable objects like Serial
. You don’t need to know C++ in order to make use of these libraries.
This program, or sketch in Arduino terminology, prints ‘hello world’ and seems to end, while in fact it keeps calling the loop()
function over and over. loop()
takes no arguments, returns type void
(nothing), and contains no executable statements, yet it needs to be there because the hidden main()
function is going to call it.
MATLAB
The MATLAB framework is embedded in an IDE that looks after managing most libraries automatically. Your code goes in files with a .m extension, usually created inside the IDE. The code is executed in order from top to bottom, similar to Python.
Defining Functions in C and Arduino
Every function you call must be defined once and only once. In the discussion above you can see examples defining the function f()
in Python and main()
, setup()
, and loop()
in C and Arduino. You can probably figure out Python by inference, but C functions will take a little more description.
Function return type
To use a function, the compiler needs to know what type of value it will return, an integer, a float, a double, or maybe something even more complicated. The function declaration starts out with the type, just like a variable declaration.
int f(){ return 5; }
f()
is a function that takes no arguments and returns an integer value. In this case it is a pretty boring value, always 5
Return type void
Some functions don’t return anything and they are declared as type void
which would not make any sense for a variable.
void loop() { // put your main code here, to run repeatedly: }
declares a function called loop()
that takes no arguments and returns nothing (void
). In this case it also does nothing, but we would fill in some statements to make things happen in our application or sketch. If you catch yourself talking about “the void loop” during labs, you probably haven’t understood the ideas around functions and function declaration. The main, setup, and loop functions have special meaning in the frameworks where they are used, but they are otherwise just the same as the functions you would define yourself to simplify the structure of your program code.
Function arguments and types
To use a function, the compiler also needs to know exactly how many and what types of arguments it takes. The function argument declarations starts out with the type, just like a variable declaration.
float g(float x, int j){ return x * j; }
g()
is a function that takes two arguments, a float
named x
locally and an int
named j
locally, and returns a float value. In this case it is only a slightly less boring value, the product of x
and j
.