In this part of the lab, we will describe C's while and for loops. Plus, we'll get more practice in translating Scheme to C by converting Scheme loops to C loops.
In class we have seen C's while statement. Convert the following into C/C++, compile, and run it.
(define steps (lambda (n) ;; expects a positive number (local ((define step 0) (define i n)) (begin (while-fn (lambda () (> i 1)) (lambda () (begin (set! step (+ step 1)) (if (even? i) (set! i (/ i 2)) (set! i (+ (* i 3) 1)))))) step))))
Rather than writing a recursive C function for even?, use the following C condition to test if i is even: (i+1)/2 == i/2. I.e., 4 is even since 5/2 == 4/2 is true because this is integer division, but 3 is odd since 4/2 == 3/2 is false. (Note: There are slicker ways to test for evenness.)
This function is a standard example of a simple integer function which terminates for all known examples, but no one has proven whether it terminates for all inputs. Observe how i gets smaller when it is even, but larger when it is odd.
Here's another example to try. Note that, for variety and brevity, here we use implicit begin's.
(define sum_upto_n (lambda (n) (local ((define total 0) (define i n)) (while-fn (lambda () (positive? i)) (lambda () (set! total (+ total i)) (set! i (- i 1)))) total)))
This iterates the body for each number from n down to 1. The idea of iterating a loop for each number between two values is very common, and has a special form in many languages, generally called a for loop. In C/C++ we could write the following:
int sum(int n) { int total=0; int i; for (i=n; i>0; i=i-1) total=total+i; return total; }
A C for loop consists of four parts, three in the parenthesis, and one after:
Observe that C/C++ doesn't really limit us to only looping for every value in a range, e.g., n down to 1. In the third part, we can change our counter however we want. But the most common and easiest to explain use is to increment or decrement your counter.
That is only one of many ways to use a for loop for this example. One equivalent way, although in poorer style is the following:
int sum(int n) { int total=0; int i=n; for (; i>0;) { total=total+i; i=i-1; } return total; }
Debugging a C program can be more difficult that debugging a Scheme program because we don't have the equivalent of Donkey to step through our programs. In this part of the lab, we will find and fix some program errors. These will be examples of some of the common C programming mistakes. We also demonstrate a common technique for debugging.
What does this program in /home/comp210/public_html/Labs/lab11/error1.cc do? Can you fix it so it does the intended calculation?
For small programs like this, it's often not too difficult to step through the evaluation either in your head or on paper. But errors can be hard to find, especially when you "know" that your program is "obviously" right. One technique for effectively stepping through the interesting bits of the computation is to print some interesting values while your program is running.
Copy, compile, and run /home/comp210/public_html/Labs/lab11/error1print.cc, which is like the previous program, except that we print out some values along the way. Observe that, for readability, we label what we print, so that we don't confuse ourselves with all of this output.
Good places to print out values include
There are ways to extend this technique so that you don't have to keep editing your program, adding and removing these printing statements. (E.g., putting the statements in conditionals with compile-time or run-time tests.) Also, there really are some tools that help you step through C programs (e.g., gdb, adb) -- they just aren't as easy to use as Donkey.
Now try /home/comp210/public_html/Labs/lab11/error2.cc.
Now try /home/comp210/public_html/Labs/lab11/error3.cc.