 Application Center - Maplesoft

# Maple Programming: 5.4: Conditional statements

You can switch back to the summary page by clicking here.

5.04.mws

Programming in Maple

Roger Kraft
Department of Mathematics, Computer Science, and Statistics
Purdue University Calumet

roger@calumet.purdue.edu

5.4. Conditional statements

In the previous two sections of this chapter we learned how to make Maple repeat a block of statements. In this section we learn how to make Maple skip over certain blocks of statements.

Here is a simple procedure. It takes in two numbers as parameters and it returns the larger of the two numbers.

 > bigger := proc( a, b )

 > if a >= b then a else b fi;

 > end; Try it out.

 > bigger(3, 5); > bigger(-3, -5); >

The procedure bigger  introduces another important element of programming, the conditional statement  (also called a if-then-else-fi statement ) . The conditional statement allows Maple to make a choice when it is computing. A conditional statement is also sometimes called a branching statement since it gives Maple a choice between two possible branches of calculations to make. Here is Maple's syntax for a conditional statement. The part of the if-then-else-fi statement between the if  and the then  is called the conditional-part  and it is either true or false. If the conditional-part is true, then Maple executes the statements between the then  and the else . These statements are called the body of the then-part . If the conditional-part is false, then the Maple executes the statements between the else  and the fi . These statements are called the body of the else-part . The bodies of either the then or else part can contain any number of Maple commands.  The body of the else-part is optional and if it is left off, then the conditional statement has the following simpler form, which is referred to as an if-statement . If the conditional-part of an if-statement is true, then Maple executes the body of the then-part. If the conditional-part of an if-statement is false, then Maple does not execute any statements, and it appears as if the if-statement did nothing.

The formatting of the conditional statement, with the bodies of the then and else parts indented slightly and the words else  and fi  on their own lines, is not part of the syntax. But the formatting makes it a lot easier to read a conditional statement and should be used most of the time. For some practical tips on how to work with multiple lines in a conditional statement, see the last section of the previous chapter.

Here are a few examples of simple conditional statements. The next command randomly generates a zero or one and if the random number is zero, the statement outputs heads , otherwise it outputs tails . Try executing this statement several times.

 > if rand(0..1)() = 0 then

 > else

 > tails

 > fi; >

If we remove the else-part of the if-then-else statement, so that it becomes an if-statement, then the statement will produce no output about half of the time. Try executing this statement several times.

 > if rand(0..1)() = 0 then heads fi; >

The following execution group generates a long list of random integers between 1 and 10 and then it uses an if-statement inside of a for-in-loop to determine what percentage of the random integers were 10's. What answer do you expect to get?

 > N := 1000:

 > counter := 0:

 > seq( rand(1..10)(), i=1..N ):

 > for i in % do  # Check for 10's.

 > if i = 10 then counter := counter+1 fi

 > od;

 > counter/N;

 > evalf( % );  Try executing the execution group several times. Try changing the value of N  to 100 or 10 or 10000.

 >

In the last section of this chapter we saw that loop statements can be very useful commands to use at the Maple command prompt. The conditional statement however is of pretty limited use at the Maple prompt. Instead it is almost always used in the body of a procedure definition or in the body of a loop. Almost all of our examples of conditional statements will be in procedure bodies.

One common use of conditional statements in mathematics textbooks is in the definition of piecewise defined functions, that is, functions defined by different formulas on different parts of the domain. Here are a few examples.

Suppose we wanted to represent in Maple the mathematical function defined by for and by for . Here is how we can do it using a procedure containing a conditional statement.

 > f := proc( x )

 > if x < 0 then x^2+1 else sin(Pi*x) fi

 > end; Notice how there is only one boolean expression, even though there are two pieces to the function. The second piece of the function, the part, should apply whenever . But we only have whenever the boolean expression x<0  is false, which automatically puts us in the else-part of the conditional statement. So only one boolean expression is needed for a piecewise defined function with two pieces. But this is not how traditional mathematics books would typeset the definition of this function. Mathematics books almost always write out a boolean expression for each piece of the function, like this. What appears in a mathematics book is more like the following Maple procedure.

 > f := proc(x)

 > if x < 0 then x^2+1 else

 > if x >= 0 then sin(Pi*x) fi;

 > fi;

 > end; This version has two boolean expressions because it has two if-statements. This is not the preferred way of defining our function in Maple for several reasons. First of all, it is not as easy to read as the previous version. With only one conditional statement we know that the else-part is mutually exclusive of the then-part. With two conditional statements it is not obvious that the two statements are mutually exclusive. A reader must carefully examine the boolean expressions to determine if they are meant to be mutually exclusive or not. Secondly, the version with one conditional is computationally more efficient than the version with two conditional statements. Every call to the second version must evaluate two boolean expressions while every call to the first version only needs to evaluate one boolean expression. This can make a difference if the function is going to be called thousands (or millions or even billions ) of times.

Exercise : Define a function g  similar to the last definition of f  in such a way that the two boolean expressions are not mutually exclusive. Draw a graph of g . If the boolean expressions are not mutually exclusive, how does that affect the graph of g ?

 >

Let us graph our function f  to see what it looks like.

 > plot( f, -1..1, discont=true, color=red ); It is worth noting that the following command does not  work, even though it looks perfectly OK. The function f  is converted to an expression by evaluating it at x , and then the form of the plot  command for expressions is used.

 > plot( f(x), x=-1..1 );

```Error, (in f) cannot determine if this expression is true or false: x < 0

```

What went wrong is that Maple is using its rule of full evaluation, so Maple tries to evaluate all of the operands in the plot  command before  actually calling the plot  procedure. When Maple tries to evaluate f(x)  there is an error, since the actual parameter x  is an unassigned variable and the conditional statement in f  has no way to determine if the boolean expression x<0  is true or not.

 > f(x);

```Error, (in f) cannot determine if this expression is true or false: x < 0

```

The following command does work, since it prevents the evaluation of f(x)  until the plot  procedure actually starts sticking numbers as actual parameters into the formal parameter x  in f(x) .

 > plot( 'f(x)', x=-1..1 ); In an optional section later in this chapter we will say more about this situation with f(x) .

 >

Exercise : Here is what seems to be a reasonable definition for the function f .

 > f := proc(x)

 > if x < 0  then   x^2+1   fi;

 > if x >= 0 then sin(Pi*x) fi;

 > end; But it is not correct. Look at the graph of this version of f .

 > plot( f, -1..1 ); What happened to the left half of f , the part for x<0 ? Here is a hint. What is the return value for the procedure call f(-2) ?

 > f(-2);

 >

Exercise : Consider the following two functions.

 > f := proc(x) if x>=0 then x^2 else 0 fi end; > g := proc(x) if x>=0 then x^2 fi end; Their graphs look similar.

 > plot( f, -10..10 ); > plot( g, -10..10 ); But they are not the same function. Explain how they differ.

 >

Here is an interesting variation on the idea of a piecewise defined function. We create a randomly defined piecewise function. The following procedure, like the piecewise defined function above, evaluates f  by choosing between two expressions,   x^2+1  and sin(Pi*x) . But instead of basing the choice of the expression on the value of the input x , this version bases the choice on a random number generated within the procedure.

 > f := proc( x )

 > if rand(0..1)()=0 then x^2+1 else sin(Pi*x) fi

 > end; So the value of the function f  at any point will be one of two randomly chosen numbers. Try executing the following command several times.

 > f(5), f(5), f(5), f(5), f(5); Here is what a graph of this function might look like. Notice that each time the function is graphed, we get a different graph (why?).

 > plot( f, -1..1 ); > plot( f, -1..1 ); >

Exercise : What causes the vertical bands of red in the graph? Here is a hint.

 > plot( f, -1..1, style=point, symbol=circle, numpoints=50, adaptive=false ); >

Suppose that we want to represent in Maple a piecewise defined function with three pieces. For example, suppose we want to represent the function g defined by for , by for 0 < < , and by for ,

or, to use a notation similar to (but not exactly like) standard mathematical notation, Here is a procedure that computes this function.

 > g := proc(x)

 > if x <= 0 then

 > x^2 + x

 > else

 > if x < 3*Pi then

 > sin(x)

 > else

 > x^2-6*x*Pi+9*Pi^2-x+3*Pi

 > fi

 > fi

 > end; >

This procedure uses a conditional statement as the body of the else-part of another conditional statement. We call these nested conditional statements . Notice three things. First, notice how three levels of indentation are used to help show the structure of the procedure body, in particular, the way the second conditional statement is the else-part of the first conditional statement. Second, there are only two boolean expressions even though there are three pieces of the function. The third piece of the function acts as the "default" piece and it applies whenever the first two pieces do not. Third, notice how the second boolean expression does not say 0<x<3*Pi  (as it would probably be written in a mathematics book). There are two reasons for not writing the boolean expression this way. First, it is syntactically incorrect in Maple (see the next section of this chapter for more about the syntax of boolean expressions). Second, it is partially redundant with the first boolean expression. Since we are in the else-part of the first (outer) conditional statement, we know that x<=0  is false, so it must be that x  is positive, so there is no need to have the boolean expression check again if 0<x .

Let us plot this function.

 > plot( g, -3..3*Pi+3 ); >

Exercise : Why was the right hand endpoint of this plot set to 3*Pi+3 ? If we make the left hand endpoint -4 , what would be a good choice for the right hand endpoint? What would the graph of g  look like if we graphed it over a large domain, say from -100 to 100? Why?

 >

Nested conditional statements are fairly common, so Maple has a special abbreviation for them. Here is the definition of g  using this abbreviation.

 > g := proc( x )

 > if x <= 0 then

 > x^2 + x

 > elif x < 3*Pi then

 > sin(x)

 > else

 > x^2-6*x*Pi+9*Pi^2-x+3*Pi

 > fi

 > end; >

This version of g  has only one  conditional statement in it (notice the single fi  at the end of the procedure body and the use of fewer levels of indentation). There is no nesting of conditional statements in this version of g . Where the previous version of g  had a conditional statement in the else-part of the outer if-then-else-fi statement, the abbreviated version has an elif-part ( elif  is an abbreviation for " el se if ") followed by an else-part. This form of the conditional statement is called an if-then-elif-then-else-fi  statement. There can be as many elif-then  clauses as you want in an if-then-elif-then-else-fi statement. For example, if we want to represent a piecewise defined function with four pieces in its definition, then we can use two elif-then clauses (in a single if-then-elif-then-else statement).

 > h := proc( x )

 > if x <= 1 then

 > x

 > elif x <= 2 then

 > x^2

 > elif x <= 3 then

 > 6-x

 > else

 > x^2-6*x+12

 > fi

 > end; >

When you read a conditional statement like this, it is important to remember that the boolean expressions are "cumulative". So for example, the x^2  part is not used just when x<=2  is true as the elif  clause just before it might seem to imply. The x^2  part is only used when x<=2  is true and   x<=1  is false. The test for x<=2  only comes after the test for x<=1  fails. Similarly, the test for x<=3  only comes after both x<=1  fails and x<=2  fails. And the x^2-6*x+12  part is used only if each of the three tests x<=1 , x<=2 , and x<=3  all fail.

 >

Let us look at graphs of these last two functions.

 > plot( g, -3..3*Pi+3 ); > plot( h, -1..5 ); >

The syntax for a if-then-elif-then-else-fi  statement should be pretty clear by now. Remember that there can be as many elif-then clauses as needed in this form of the conditional statement.

 >

The next example shows how we might need two if-then-else-fi statements nested inside of an if-then-else-fi statement, one in each of the then and else parts. This procedure finds the largest of three numbers.

 > bigger3 := proc( a, b, c )

 > if a >= b then

 > if a >= c then a else b fi;

 > else

 > if b >= c then b else c fi;

 > fi;

 > end; Try this procedure out.

 > bigger3( 1, 2, 3 ); > bigger3( 3, 2, 1 ); > bigger3( 1, 3, 2 ); >

This procedure could use the elif  abbreviation for one of the nested conditional statements, but that probably would not make the procedure any easier to understand.

 >

Exercise : Rewrite the procedure bigger3  using an elif-clause in the outer conditional statement.

 >

There is another way to implement bigger3 . This version uses nested calls to our procedure bigger  instead of nested conditional statements. (Recall that bigger  was defined at the very beginning of this section.)

 > bigger3 := proc(a, b, c)

 > bigger( bigger(a, b), c );

 > end; This is a common technique in programming. Use one procedure as part of the definition of another procedure.  If we compare the two versions of bigger3 , notice how the inner call to bigger  plays the same role as the outer conditional statement, and the outer call to bigger  plays the role of both  the inner conditional statements.

 > bigger3(-3, 4, 2); >

We can also create a procedure bigger4  that finds the largest of four numbers. We can do this two ways, one way using nested conditional statements and another way using nested procedure calls to bigger3  and/or bigger . The nested conditional statement version of bigger4  will be quite messy but you should try writing it. Here are several ways of writing bigger4  using nested procedure calls.

 > bigger4 := proc(a, b, c, d)

 > bigger( bigger(a, b), bigger(c, d) );

 > end; > bigger4 := proc(a, b, c, d)

 > bigger( bigger( bigger(a, b), c ), d);

 > end; > bigger4 := proc(a, b, c, d)

 > bigger( bigger3(a, b, c), d );

 > end; There are still several other ways of making bigger4  out of bigger  and bigger3 . What are they?

 >

Exercise : Write a version of bigger4  that uses only nested conditional statements.

 >

Exercise : Write a version of bigger4  that uses procedure calls nested inside of conditional statements.

 >

For the sake of completeness, what about finding the maximum of an arbitrary number of numbers. This is like our problem of finding the average of an arbitrary number of numbers. We solve it by using a list (which is a data structure) as the input to our procedure.

 > biggest := proc( list::list(numeric) ) # Type check the input.

 > local candidate, i;

 > candidate := list;  # Make an initial guess.

 > for i from 2 to nops(list) do

 > if list[i] > candidate then

 > candidate := list[i]; # Update our guess.

 > fi;

 > od;

 > candidate;  # Return our final answer.

 > end;    Now try it out.

 > biggest( [1,2,3,4,5,100,6] ); Let us test our procedure on a randomly generated list of integers.

 > random_list_of_integers := [ seq( rand(), i=1..12 ) ];  > biggest( random_list_of_integers ); >

Exercise : Change the definition of biggest  to use a call to procedure bigger  in place of the if-statement.

 >

Exercise : If you read the optional section on the special local variables args  and nargs , then write a version of biggest  that does not need the brackets in its procedure call.

 >

 >

 >