Mini-Project: 

Procedural Abstraction

13

Functions (Procedures that Return a Value)

A function is a procedure that returns a value (result). You've used lots of built-in functions (or predefined functions) in your apps, although they may not have been called 'functions'. Here are some examples:







Notice how each of these blocks has a nub on its left hand side, just like our value blocks:

All of these blocks are function-call blocks, meaning they are used to call a function to compute a certain value. Some functions return a string: upcase(), join(). Some return a number: squareRoot(), lengthOfList(). Some return a boolean: isListEmpty(), isInList(). And some return a list: makeAList()

What this means is that a function-call block can be plugged into any slot that can take a value. For example, they can be plugged into setters. Notice that all three of the following statements would set global X to the value 2, and that two of them involve using functions:

Notice also, that in the third example, we've plugged two function blocks together to get 2: lengthOfList(makeAList("one", "two")) 

Here's another example. Both of these statements would set global X to "HELLO" but the second one uses a function to first convert "hello" to "HELLO":

Procedures (Don't return a Value)

Procedures that don't return a value cannot be used in this way. Calls to procedures can only be plugged into do slots. For example, the sequence of procedure calls here can be plugged into the when_Button1.Click's do-slot:

Just as we can define our own procedures we can define our own functions. Consider a real simple example: a function to add two values: add(X,Y). We wouldn't ordinarily define such a procedure because we already have the '+' operator and could just say 'X + Y'. But it serves as a simple example. 

Suppose we wanted to use a procedure or a function to add two variables, x and y and assigning the the resulting sum to z. The following table shows the difference between a procedure and a function. Looking at the top purple code blocks, in the left column we have a procedure definition and in the right column we have a function definition. They both take 2 parameters, n1 and n2(instead of x an y), the two values that will be added together. But notice that the procedure needs a global variable z to store the result, whereas the function just returns the sum.

Now look at the button click event handlers. On the left we have a procedure call: call addProcedure(), but it's not completely clear what's happening there. You'd have to look at the procedure definition to figure out that global z was being set to the sum of the two inputs. It's not clear from the call that that's what happens to the sum. This is less than ideal. Ideally, we want our code to be easily readable. 

On the right we have a function: call addFunction(), and it is completely obvious that the sum is being assigned to z. So that's a much better design.

Also, notice that the procedure can only store the sum in the global variable z. But the function can do all kinds of interesting things with the value that is returned by the function call. The following example shows three different ways to use the result from addFunction():

So, the big difference between a procedure and a function is that a function returns a value and that value can be plugged into any slot that takes a value. 


The result can be any type of data: a number, a list, a text, a color, etc. 

Functions that Just Do A Computation

Many functions, such as the addFunction(), simply perform a computation. In those cases we can implement the function by just plugging the entire computation into the result slot.

Functions that Make A Choice

But sometimes that can't be done. For example consider the function max(x,y) which returns the larger of two numbers, x or y. In this case we need to compare x and y before choosing one or the other to return.


In the following drills, whenever you are asked to define a function, it will one or the other of these two types of functions. 

Getting Started

A. Download the Starter Code

B. In AppInventor, click on the Projects drop down menu 


For each of the following problems, code the solution in the Blocks Editor. Then run and test your solution. Keep running and testing until you are certain that it is correct. Each problem should have its solution displayed in the specific label for it. 

Procedure Basics

Procedure

Goals & Requirements

0. Rename the Screen1.Title to your name. 

1. Procedural Abstraction. One good use of a procedure is to eliminate duplicate code. In addition to improving the readability of the app, the use of a procedure will make it easier to modify that chunk of code because it only occurs once in the program.

Notice the duplicate code in Screen1.Initialize and ButtonNewList.Click. Define a procedure named makeNewList that encapsulates that code and then call the procedure from Screen1.Initialize and ButtonNewList.Click

2. Error Message Procedure. Notice in the ButtonTestIt.Click block that the else clause displays an error message in the LabelOutput. Convert that task to a procedure named displayOutofBoundsMsg and call the procedure from the else clause. This also makes the app more easily readable. The else clause should look as shown here when you're done:

3. Is a Valid Index. In the current code, testing whether the user's input is a valid index or not requires a big ugly expression: 

Define a function named isValidIndex and replace this expression with a call to your function.

When you're done, the ButtonTestIt.Click procedure should look like this. See how much more readable it is.

4. Use a Parameter. It's not clear in the above block what is a valid index? In other words, what value is being tested to see if it is a valid index. Revise your function definition so that it takes a parameter where the value to be tested can be passed to the function. Then call the function and pass it the global index, which is the value we want to test for validity. In the function itself, you'll want to use the parameter's value rather than the global index in the expression in the result slot. The revised ButtonTestIt should look like this when you're done, even more readable: 

5. Advanced Example. SumTheList should be a Function. The task of summing a list should really be a function because it returns a value: namely, the sum of the list or the sum of a sublist of the list. Notice in the code above that you can't tell what list is being summed and you can't see a relationship between the index that's being input and the summing of the list. What's the point of inputting the index? A function definition would make this a lot clearer. For example, if we had a function such as sumListToIndex(list, index), then this would be much easier to follow. So let's define and use that function. 

This is an advanced concept. So we'll just provide an example here. The sumListToIndex(list, finalIndex) function should take two parameters, one for the list being summed and one for the final index that we want to sum up to. If finalIndex is 2 we sum the first two elements of the list. If finalIndex is lengthOfList() we sum all elements of the list. When it's done, our sumListToIndex(list, finalIndex) will sort of resemble how we use other list functions such as selectListItem(list, index) or removeListItem(list, index)

Here's the definition we want:

If you compare that to the original definition of sumList [shown below] you can see that we've places the entire loop inside a do-then-result block and when the loop finishes we return runningSum.

Given this definition, our entire program looks like this. The procedure and function definitions have been minimized (collapsed) to make the whole program readable at a glance: 

Modularity

Another big advantage of procedural abstraction is that once you've correctly defined the procedure, you can close it up and just use wherever it's needed. This is the principle of modularity: by organizing our program into modules (procedures and functions) we reduce its complexity and we hide the details. If you think about it, that's all complex objects are designed: your car contains tons of modules.

(Note, you should not generally collapse blocks, they are simply collapsed so that the solutions are not visible and to make the point of modularity)

6. Compute and Display the Sum. Let's make one final change to this app. Let's get rid of the big green block in the ButtonTestIt.Click procedure by defining a procedure named computeAndDisplaySumToIndex. When you're done with that, the whole program should look like this, with your own procedure and function blocks expanded (not collapsed):

Show TClark your completed app, then turn in all the files in the form.