Programming with Robots by Albert W. Schueller - HTML preview

PLEASE NOTE: This is an HTML preview only and some elements such as links or page numbers may be incorrect.
Download the book in PDF, ePub, Kindle for a complete version.

variables pass out of scope when their declaring functions end.

4.5.2

Pass-by-value vs. pass-by-reference

By default, the variables declared in the argument list of a function definition are local

variables to that function. When a function is called, its arguments are copied into the

argument variables. The original variables, in the calling program, are not affected by any

changes made by the function to its argument variables. This style of argument passing is

pass-by-value.

Alternatively, the programmer may wish for changes made by the function to one or more

of its arguments to be reflected in the corresponding variables of the calling program. To

accomplish this, when the variable is passed into the function through one of its arguments,

the copy process is skipped and the function has access to the actual variable as it exists

in the calling program. This style of argument passing is pass-by-reference. The syntax

to inform the compiler what style of argument passing is desired is to simply precede the

argument name with a ’&’ character.

Pass-by-reference is particularly useful when the programmer wishes to have a function

return more than one piece of information to the calling program. For example, suppose in

our Mean() function we wished, not only to have the mean returned to the calling program,

but also to have the function give us the sum of the two arguments.

A simple return

statement is unable to return more than one value. To get around this, consider the function

in Listing 4.8.

Compare this to the function in Listing 4.7. The return type is now void and there are two pass-by-reference arguments. Observe the use of the ’&’ character in the second two

arguments to indicate that these arguments are pass-by-reference. Listing 4.9 shows how to use this function to get both the sum and the mean of the first two arguments.

The main program declares 4 float-type variables. The x and y variables hold the numbers

to be summed and averaged. Notice that we also need variables, s and m, to hold the results of

the MeanSum() function. As the instructions of the main program are carried out from top to

bottom, before the call to MeanSum(), the variables s and m are empty (uninitialized). After

the call to MeanSum(), they contain the sum and mean respectively. Only after MeanSum()

has been allowed to do its work, can we display the contents of s and m. If we mistakenly try

to display the contents of s and m before the call to MeanSum(), the results are unpredictable–

we may just see a zero, or we may see some random number.

4.5. FUNCTIONS

25

// computes the sum and the mean of its

// first two arguments , the sum is placed

// in the third argument , the mean is

// placed in the fourth argument

void M e a n S u m ( f l o a t a , f l o a t b , f l o a t & sum ,

f l o a t & mean ) {

sum = a + b ;

mean = ( a + b )/2;

r e t u r n ;

}

Listing 4.8: A function that accepts two pass-by-value float-type arguments and two pass-

by-reference arguments, computes the sum and mean, and puts the results into the pass-by-

reference arguments.

task main () {

f l o a t x , y , s , m ;

x = 3 . 4 ; y = 2 . 8 ;

M e a n S u m ( x , y , s , m );

n x t D i s p l a y S t r i n g (0 , " Sum = %5.2 f " , s );

n x t D i s p l a y S t r i n g (1 , " Mean = %5.2 f " , m );

w a i t 1 0 M s e c ( 2 0 0 ) ;

}

Listing 4.9: A main program that uses the MeanSum() function and displays the results.

26

CHAPTER 4. SENSORS AND FUNCTIONS

More on RobotC function syntax can be found here2.

2http://carrot.whitman.edu/Robots/PDF/Functions.pdf

4.6. EXERCISES

27

4.6

Exercises

1. Which of the following variable names are valid?

n, 2n, tax2, SpeciesType, sales tax, dog#, ?89

If invalid, why?

2. Suppose a programmer wrote a program that, when executed, displayed a message

on the screen indicating whether the touch sensor was depressed or not. What is the

indeterminate in the program?

3. Suppose we have an integer-type variable, count. Give two different RobotC expres-

sions that decrement count by 2.

4. Do some experiments (run little test programs), to determine what happens when a

character-type variable is decremented. How about incremented? Give your observa-

tions and speculate about what might be going on.

5. Write a program that reads a value from the sonar sensor and displays the value

graphically as a horizontal line across the center of the screen that starts at (0,31)

and ends at (x,31) where x is the value of the sonar sensor. The idea is that the

length of the line indicates the magnitude of the sonar reading.

6. Write a program that reads a value from the light sensor and displays the value graph-

ically as a filled circle with center (50,32) and radius x, where x is the value of the

light sensor. The idea is that the size of the circle indicates the magnitude of the light

reading.

7. Write a program that starts, waits 1 second, reads the light sensor, displays the value

for 1 second, and exits.

8. Write a program that starts, waits 1 second, reads the sonar sensor, displays the value

for 1 second, and exits.

9. Write a program that implements a function called

displayLightSensor()

that accepts one argument called wait. The function will read the light sensor and

display its value for wait seconds before exiting. Use the function to write a program

that reads the light sensor and displays its value three separate times, pausing 1.5

second between reads.

10. Write a program that displays an uninitialized variable. What happens when you run

the program?

11. Modify the DisplaySmiley() function in Listing 4.6 so that it shows a frown instead.

Use it to animate a bouncing frowny face.

28

CHAPTER 4. SENSORS AND FUNCTIONS

12. Write a function that displays an equilateral triangle with center (x, y), side length a,

and orientation θ. You may assume that the side length is in pixels and that orientation

is in degrees.

Chapter 5

Decisions

In Section 4.1, we discussed the notion of indeterminacy–values in programs that are not known to the programmer when they write the program. When dealing with indeterminate information, we must have a method of making decisions. In real life, decisions are

complicated, based on incomplete information and thousands of variables. In computer sci-

ence, decisions are more straightforward and ultimately result in one of two possible choices.

Because of this clear dichotomy, programming languages base decision making on Boolean

algebra–the study of true and false statements. This explains why one of the basic datatypes

summarized in Table 4.1 is Boolean.

5.1

Boolean Algebra

In ordinary algebra, we study variables that take on any real value. We study the behavior of

these variables as they interact using the ordinary operations of arithmetic, namely addition,

subtraction, multiplication and division. In Boolean algebra, the variables can have only two

values, true or false. Further, the operations are no longer those of arithmetic, but rather

those of symbolic logic and are called conjunction, disjunction and negation.

In RobotC, we can declare a pair of Boolean variables as

bool p,q;

and we can assign values to them as

p=false; q=true;

In much the same way that we can combine integer- and float-type variables using the ordi-

nary arithmetic operations, we can combine boolean-type variables with Boolean operations.

The Boolean operations are defined as follows.

5.1.1

Conjunction

Also known as the “and operator”, conjunction in RobotC is represented by ’&&’. Two

Boolean values are combined syntactically as

p && q;

29

30

CHAPTER 5. DECISIONS

&&

true

false

true

true

false

false

false

false

Table 5.1: Conjunction, the “and operator”.

||

true

false

true

true

true

false

true

false

Table 5.2: Disjunction, the “or operator”.

and the result is a new Boolean value. The result depends on the values of p and q and

follows the rules outlined in Table 5.1.

5.1.2

Disjunction

Also known as the “or operator”, disjunction in RobotC is represented by ’||’. Two

Boolean values are combined syntactically as

p || q;

and the result is a new Boolean value. The result depends on the values of p and q and

follows the rules outlined in Table 5.2.

5.1.3

Negation

Also known as the “not operator”, negation in RobotC is represented by ’~’. Unlike the

conjunction and disjunction operators, the negation operator only acts on a single boolean

value as

~p;

and the result is a new Boolean value that is simply the opposite of the value of p. If p

is false, then ~p is true. If p is true, then ~p is false. Additional information about

Boolean algebra in RobotC is available here1.

5.1.4

Boolean Expressions

It is surprising how complicated Boolean algebra can get given the simple and limited nature

of its variables and operations. A Boolean expression is any combination of Boolean

variables and operations that can be evaluated to a Boolean value if all the values of the

1http://carrot.whitman.edu/Robots/PDF/Boolean%20Algebra.pdf

5.2. COMPARISON OPERATORS

31

Syntax

Description

>=

greater than or equal, evaluates to true if the left-hand

side is greater than or equal to the right-hand side, false

otherwise.

>

greater than, evaluates to true if the left-hand side is

greater than the right-hand side, false otherwise.

==

equal to, evaluates to true if the left-hand side is equal

to the right-hand side, false otherwise.

<=

less than or equal, evaluates to true if the left-hand side

is less than or equal to the right-hand side, false other-

wise.

<

less than, evaluates to true if the left-hand side is less

than the right-hand side, false otherwise.

!=

not equal to, evaluates to true if the left-hand side is not

equal to the right-hand side, false otherwise.

Table 5.3: The comparison operators.

variables are known. For convenience, Boolean expressions may also include parentheses to

control the order of evaluation. Negation takes precedence over conjunction and disjunction.

In the case of ties, expressions are evaluated from left to right. Consider the snippet of

instruction in Listing 5.1.

bool p , q , r , s ;

p = true ; q = f a l s e ; r = true ;

s =~( p || q ) && ( q || r ) && ( r && p );

Listing 5.1: A compound Boolean expression. At the end of the block, s has the value false.

5.2

Comparison Operators

Now that we have an understanding of Boolean algebra, it is important to note that in

programming we rarely construct expressions comprised solely of Boolean variables like that

of Listing 5.1. Instead, we usually construct Boolean expressions that arise by comparing variables of the other types. In RobotC there are 6 operators designed to compare values

and return Boolean values. They are: greater than or equal, greater than, equal, less than,

less than or equal, and not equal. Each has its own syntax summarized in Table 5.3

Be wary of the == operator! A common programming error is to use the assignment

operator, =, to compare values. This error is exacerbated by the fact that, because the

mistaken syntax actually makes sense to the compiler (a fact that we will discuss later), it

will not cause a compiler error.

32

CHAPTER 5. DECISIONS

Listing 5.2 shows how to use comparison operators. It shows the common task of testing whether a variable lies inside a certain range. The expressions in parentheses evaluate to

either true or false depending on the values of the variables x, y, and z.

f l o a t x =5.2 , y =0.0 , z = 1 0 . 0 ;

bool s ;

s = ( x >= y ) && ( x <= z );

Listing 5.2: An example of using comparison operators. The value of s at the end of the

snippet is true. This shows how a programmer would test if y ≤ x ≤ z.

It is interesting to note that the comparison operators also work on string and character

values using alphabetical order. When comparing two strings/characters, whichever comes

first in the dictionary is the smaller of the two. String/character comparisons are case-

sensitive with the rule that capital letters are less than their corresponding lower-case letters.

5.3

Conditional Statements

Now that we have the ability to create Boolean expressions, we introduce conditional state-

ments. A conditional statement allows a block of instruction to be executed depending

on the value of a Boolean expression.

5.3.1

If-statements

An if-statement is a block of instruction that is executed only if its predicate is true. The

predicate is the Boolean expression that controls whether or not the block of an if-statement

is executed. If the predicate is true, then the block will be executed, if it is false then the

block will be skipped and program execution will resume after the closing brace of the block.

The syntax, given in Listing 5.3 is simple and quite readable.

if ( [ p r e d i c a t e ] ) {

// c o n d i t i o n a l block

}

Listing 5.3: The syntax of an if-statement. The predicate, a Boolean expression, determines

whether the succeeding block of instruction is executed.

5.3.2

If-else statements

A straightforward extension of the if-statement is the if-else-statement. In an if-else-statement

the value of the predicate determines which of two blocks of instructions is executed. The

syntax is summarized in Listing 5.4.

5.3. CONDITIONAL STATEMENTS

33

if ( [ p r e d i c a t e ] ) {

// c o n d i t i o n a l block executed if

// the pr edic ate is true

}

else {

// c o n d i t i o n a l block executed if

// the pr edic ate is false

}

Listing 5.4: The syntax of an if-else-statement. If the predicate is true, the first block is

executed, otherwise the second block is executed.

More on the if-else statement can be found here2.

In Listing 5.5, we test the value of the sonar sensor and display different messages depending on the distance measured by the sensor at run time.

# p r a g m a c o n f i g ( Sensor , S1 , Sonar , s e n s o r S O N A R )

task main () {

int d i s t a n c e = 0;

d i s t a n c e = S e n s o r V a l u e [ S o n a r ];

n x t D i s p l a y S t r i n g (3 , " S o n a r : % d " , d i s t a n c e );

if ( d i s t a n c e > 50) {

n x t D i s p l a y S t r i n g (4 , " Come closer , " );

n x t D i s p l a y S t r i n g (5 , " I can ’ t see you ! " );

}

else {

n x t D i s p l a y S t r i n g (4 , " Back off man ! , " );

}

w a i t 1 0 M s e c ( 3 0 0 ) ;

}

Listing 5.5: An example of an if-else-statement. If the distance measured by the sonar is

greater than 50cm, then the first message is displayed. If it is less than or equal to 50cm,

then the second message is displayed. The instruction at the top “declares” the sonar sensor

and was inserted by RobotC.

The current value of the sensor is an integer and is always available in SensorValue[Sonar]

(this is actually an array element, we will discuss arrays a little later). For convenience and

readability, we copy the current value of the sonar sensor into the integer-type variable,

distance. For the sake of the example, we arbitrarily decide that if the sonar reading is

more than 50cm, then the target is too far away and if it is less than or equal to 50cm, then

it is too close. We use the if-else-statement to display different messages depending on this

2http://carrot.whitman.edu/Robots/PDF/Decision%20Making.pdf

34

CHAPTER 5. DECISIONS

condition. The predicate in this case is (distance>50). The value of the predicate depends

on the value of the indeterminate, distance.

5.4

Mathematical Expressions

Numeric variables and literals can be combined using the infix style of mathematical ex-

pressions. Infix is the method of expression in which the mathematical operation is placed

between the values upon which it acts (as opposed to prefix or postfix). This is the style

common in most TI calculators.

5.4.1

Basic arithmetic

RobotC recognizes the usual mathematical symbols: + (addition), - (subtraction), * (mul-

tiplication), and / (division). In addition, RobotC recognizes the use of parentheses for

grouping in mathematical expressions. RobotC also recognizes a number of more advanced

mathematical functions like sine, cosine, logarithms and the exponential function. Some

of those additional functions are summarized in the RobotC On-line Support on the left side-bar under the NXT Functions → Math section.

5.4.2

Integer arithmetic

The basic arithmetic operations are straightforward and intuitive in most cases. However,

when working with integer datatypes there is an exception. The / operator (division) au-

tomatically detects when it is operating on a pair of integers and, in that case, switches to

whole number division. Whole number division returns an integer value that represents

the number of times the denominator goes into the numerator, dropping any remainder.

For example, the expression 3/2 in RobotC evaluates to 1 not 1.5. The expression -1/2

evaluates to 0. The expression 33/10 evaluates to 3.

The / (division) operator only performs whole number division if both the numerator

and denominator are integer type variables or literals. In all other cases, ordinary division

is used. To force ordinary division of two integers either include a decimal point if it is a

literal value, e.g. 3/2.0 instead of just 3/2, or convert the integer variable to a float, e.g.

((float)n)/2 instead of n/2.

If we want the remainder after whole number division, there is separate operator for inte-

gers, % (modulus) that returns an integer value that represents the remainder after dividing

the left-hand side by the right-hand side. For example, 3%2 evaluates to 1, 33%10 evaluates

to 3.

Together these operators provide powerful methods of manipulating integer values.

5.4.3

Exponentiation

A curious omission from this collection of mathematical functions is the exponentiation

function for computing quantities like 23 or 100.33. However, we have the tools necessary

to build our own.

The C programming language uses the function, pow(), to perform

5.4. MATHEMATICAL EXPRESSIONS

35

exponentiation. For example, 23 = pow(2,3), and 100.33 = pow(10,0.33). If we have need

of exponentiation, we simply take advantage of the properties of logarithms base e and the

exponential function, ex. Recall that

log (xa) = a log x.

e

e

Also, recall that log (ex) = x. Combining these two properties, we have

e

xa = ea loge(x).

In RobotC, ex = exp(x), and log (x) =

e

log(x). Therefore, the function in Listing 5.6 will

give us the standard C exponentiation function.

f l o a t pow ( f l o a t x , f l o a t a ) {

r e t u r n exp ( a * log ( x ));

}

Listing 5.6: The standard C language exponentiation function built of available RobotC

functions.

5.4.4

Randomness

Another important function is the random number generator, random(). Randomness is

important in computer science for the purpose of running realistic simulations, for security,

and for introducing unpredictability in game play.

Each time the random() function is called, it returns a positive integer between 0 and

its single argument. For example, random(10) will return a number between 0 and 10

inclusively each time it is called. If we only wanted a random number between 1 and 10,

we would use the expression random(9) + 1. The expression random(100)-50 will return

an integer between -50 and 50 inclusively. The maximum range of the random() function

is 32767.

To generate random float type values between 0 and 1 inclusively, we can use the expres-

sion random(32767)/(float)32767. Here we use the maximum possible range so that we

get as many possibilities between 0 and 1 as we can.

Since computers are completely deterministic, getting randomness can be difficult. In

many programming environments (not RobotC) careful analysis of the random() function

will show that it generates the same sequence of random numbers every time you restart your

program. To change the sequence, programmers must seed the random number generator

with some externally obtained (and hopefully) random number. The seed of a random

number generator is the number that the generator starts with when applying its randomness

formula for computing the next random number. To set the seed, we call the srand()

function with an integer argument, just once, at the beginning of the program. Afterwards,

the random() function will generate a sequence of random numbers based on the seed.

Fortunately, robots have lots of external sources for seeds. The programmer could read

the sound sensor and use that value as the seed before proceeding. The sequence of random

36

CHAPTER 5. DECISIONS

numbers would then depend upon the sound level at the time the program was started.

In a noisy room, this would be a good source of randomness. However, in a quiet room,

the reading may be the same each time, which gives the same random sequence each time

the program runs. To get around this, there are lots of possibilities. The programmer

could read numbers from several sensors and mix the values together in a formula. A very

reliable random seed is value of the system clock when the program is executed. With this

method, each time the program runs, a different seed, based on the system clock, will be

used and, in turn, a different sequence of random numbers will be generated. The reserved

variable, nSysTime, contains an ever-changing integer that represents the number of elapsed

milliseconds since the brick was powered up. Mo