Note: For groups that may have a significant amount of programming experience, your PM may suggest trying out the Advanced Tutorial 7

Submitting

Regardless of how you choose to complete the assignment, you MUST submit the file you worked on to Canvas.

If you’re submitting remotely, your submitted file will be graded for completeness and correctness via an autograder. Make sure your functions are named correctly and that all of the built-in check-expects pass.

If you’re submitting in-class, make sure to check-in (there will be one question called CHECK-IN Q only available for the first 7 minutes of class) and check-out (there will be a three question survey called CHECKOUT SURVEY only available for the last 10 minutes of class) using PollEverywhere with your location verified.


Getting Started

Congratulations, you’ve graduated to another version of Racket! Make sure at the top of your RKT files you use #lang htdp/asl.

Learning Objectives: For each of the imperative operations below, make sure you can answer the following questions:

  1. What does it do?
  2. What does it return?
  3. When do you need it?
  • begin
  • set!
  • for-each
  • when and unless

Make sure you’re also clear on when you enter the body of each of these operations. If you don’t enter the body, what is returned?


Tutorial 7 Starter Files



Part 1. Debugging Imperative Programs

For the below two activities, for those completing remotely there’s nothing to submit since they’re just demonstrations. These debugging methods can come in handy as we write more complex ASL programs.


Activity 1.1: Informal Debugging

Now that we have imperatives, it becomes really hard to “track” the value of variables as our programs run. For those of you who have programmed in other languages, one way to track a variables value is to “print its value out” occasionally. In purely functional programming there really wasn’t a concept of this since “printing” is an action with a side-effect (i.e. print to the Interactions Window or Console) rather than a function with an output. That’s why the only way we could “peak” into our variables in ISL+ was to use the Stepper.

Open up the debugging.rkt file and checkout the sum-list function from Monday (and the pre-recorded lecture) with some additional function calls inside of the sequence within begin. Read the comments in the file and try running the file and seeing the values of the variables printed out in the Interactions Window. Once you’ve tried using the three different print function calls (2 prints and one printf). Try adding one other print statement within the begin to experiment (something like printing "Why is my professor making me do this." every recursive step would be appropriate.). Then move on to Activity 1.2.


Activity 1.2: Using the Debugger

Integrated Development Environments (IDEs) like DrRacket include formal debugger tools, often called Debuggers. In DrRacket once you change the language to #htdp/asl, next to the run button you’ll see the debug button – hit it! This will compile the program but not start it yet. It should pop up the pause, go, step, in, and out buttons (with all but pause greyed out), think for a while, and then grey out pause (because it’s now paused) and un-grey-out go and step.

Screenshot of Debugger Debugger Initial Screen

The debugger is now waiting for you to either hit step or go. Before you hit go, find an expression you want to “investigate” and hover your mouse over its opening parenthesis. You should see a little red dot appear in the middle of the paren. Right click and choose “Pause at this point”. Now, click go. It should finish loading the file and give you a prompt in the interaction window. When it reaches the part of the program you set the breakpoint at, it will pause and show you the state of the “stack” or what is essentially the computer’s memory at that point.

Hit go again, and it will run to the next call of your function. Keep pressing go until it finishes running.

Screenshot of Debugger in Action Debugger in Action

To remove a break point right click the same paren you did before and choose “Remove pause at this point.” Using the debugger allows you to get a peak at the state of the computer in between runs of your imperative functions. This will be very useful for the next part of the assignment.


Part 2. Basic Imperatives

Now back to the meat and potatoes for this week: imperatives. Let’s make a simple bank account simulator! Where you can give yourself as much money as you’d like! What a world.

note: At some point in this part, you’re going to run into some weird shenanigans with calls to check-expect. Pay very close attention to the order in which you run the check-expects. Also make note of the fact that running imperatives in your Definitions Window…doesn’t necessarily affect the value of your variables “inside” of your check-expects. Why might this be the case? Why might it be beneficial? How can you write your tests to take this into account?


Activity 2.1

Start by making a global variable, balance, and setting it to some initial value. (For the sake of this example we can pretend we have as much money as we’d like instead of being college students)


Activity 2.2

Now make a function deposit! that takes a number as input and adds that number to the balance. Have deposit! return your resulting balance:

; deposit! : number -> number
; Deposits money into our bank account
; Effect: balance increases by deposit number.

Hint: You will use set! in your implementation. Check the Reference at the end of Part 2 if you need a refresher.

Hint 2: There is a bit of a trick to returning the updated balance here. Remember set! returns a value of (void), so you have to do an extra step to get your deposit function to return a number. Checkout the Reference below if you need help.


Activity 2.3

Nice work! Now, write a function called withdraw! that takes a number and removes it from the balance, returning the new balance in the process. Your function should only decrease the balance if it is greater than or equal to the amount being withdrawn.

; withdraw! : number -> number
; Withdraws money from our bank account!
; Effect: balance decreases by withdraw amount unless
;         balance is less than withdraw number also prints out
;         starting and ending balance.

Inside of your sequenced imperatives you should…


Activity 2.4

Now let’s be a little more fancy. Write a very similar function called withdraw-with-fee!

; withdraw-with-fee! : number -> number
; Withdraws money from our bank account!
; Effect: balance decreases by withdraw amount unless
;         balance is less than withdraw number also prints out
;         starting and ending balance. It also charges a fee of $5
;         if a withdrawal results in an overdraft.

This function should be identical to the earlier one except the highlighted step below.

Note: I’m not endorsing overdraft fees. Also a common question is “can I avoid using unless?” Yes since when and unless are linked by a flipped boolean value, you can avoid it. But for the sake of completitionism, try and get it working here.

Hint: Because in this new step you need to sequence two primitives, what special form will we see twice in this function?


Part 2 Reference

Modifying variables

set! allows us to modify variables, changing their values after we define them. It is a crucial part of imperative programming. set! takes two arguments, a variable and an expression and sets the variable to that expression. So for example:

(define hi "hello") ; define some variable
; the variable hi is now "hello"
(set! hi "hola") ; set our variable with a string
; the variable hi is now "hola"
(set! hi (string-append "bon" "jour")) ; a more complex expression
; the variable hi is now "bonjour"

Sequencing imperatives

begin allows us to sequence imperatives. It takes an unlimited number of valid sub-expressions, executes them in order, and then the begin’s value will be the value of the final sub-expression. For example, the following expression would result in a value of 2 with a side-effect of x now containing the number 1.

(define x 0)
(+ 1 (begin (set! x (+ x 1))
            x))

Imperative conditionals

when is similar to if but an imperative variant. The signature for when is (when question-expression then-expression), note there is no else expression here.

If the question-expression evaluates to true then when executes the given then-expression and returns the result. Otherwise, if the question-expression evaluates to false, then when does nothing and returns (void) (this is different than how if behaves).

(define hi "ni hao") ; define some variable (define bye "goodbye") ; define some other variable
(when (string=? hi "hola") (set! bye "so long"))
; hi is set to "ni hao" so this will do nothing and return void
; bye is still set to goodbye
(set! hi "hola") ; set our variable with a string (when (string=? hi "hola")
(set! bye "so long"))
; hi is now set to hola, so we will set bye to "so long"
; bye is now set to "so long" from the when statement

unless is a companion to when. Instead of executing the then-expression when the question-expression evaluates to true, it executes it when the question-expression evaluates to false. Otherwise all behavior is identical to when.


Imperative iterators

for-each is an imperative iterator. It has the same inputs as map but only returns void as output. It’s meant to be used to perform some imperative for-each item in a list. If you want it to return a value, you’ll need to combine it with a sequencer!


Part 3. Imperative Loops

Now that we’ve got some basic imperatives down. We need to also use some imperative loops! These are like our iterators from earlier in the quarter, but they behave imperatively.


Activity 3.1

Now write list-max using for-each and set!

; list-max : (listof number) -> number
; Returns the largest number in a list assuming the list is non-empty.

Hint: You’ll want to use local to create a variable that you can modify. Just like local can be used to define functions meant to only be used within a function’s definition, we can use it to define variables accessible and modifiable only from within that function.

Notice that we don’t have an effect statement here because list-max, while using imperatives internally, won’t change any global variables or object fields and so won’t have any effects that will be visible outside the function.

This is very similar to the sum-list function so checkout that as an example if you’re stuck.


Activity 3.2

Now rewrite it using iterative recursion (but still using set!):

; list-max/iter : (listof number) -> number
; Returns the largest number in a list assuming the list is non-empty.

You can write this quite “concisely” if you use for-each.


Part 4. Simple GUIs

Most of the time in real programming projects, we don’t leave our programs in a purely text-based form for someone to interact with. Imagine if you asked a person at an ATM for instance, to interact with your program by typing in specific commands with specific syntax. Most of the time, we separately develop a front-end for our programs called a Graphical User Interface (GUI) (like the snake game you’re working on in Exercise 6!).

At the bottom of the starter file we’ve included driver code for the simplest possible text editor (think Microsoft Word but scaled WAY down). You call the editor by running (edit-text) in the REPL (Interactions Window). It will display the text in the string variable the-text, which is initially just the empty string (so you won’t see any text to begin with).

Each time you press a key, edit-text will call the procedure key-pressed with the key you typed as an argument, e.g. “a”, “b”, etc..

Your job will be to define edit-text so that it can do three different actions (one per activity). I highly recommend making one work and then testing it by actually running the editor before moving to the next one!


Activity 4.1: Adding characters

Modify key-pressed so that it updates the-text with the new character that was typed. You can join two strings using string-append, which is just like append, but works on strings rather than lists. You can then store that result back in the-text.


Activity 4.2: Removing characters

The current version would be great if humans were infallible, but in practice we make typos. So we need to modify the editor to support backspace. In particular, when the backspace key is pressed, it should remove the character at the end of the-text.

When you press the backspace key (delete on a Mac), the key that gets reported to key-pressed is the magic backspace character, which you can’t actually type into a string in your source code because as soon as your try, the editor will think you want to erase part of your source code, not that you want to type the backspace character as part of a string. So most programming languages provide a mechanism for typing un-typable characters inside of string constants by using something called an escape character, which in most languages is a backslash (\). The escape character tells the system that the following character should be treated differently than usual. In Racket, a backslash followed by the letter b means a backspace character. So if you want to check whether a key is the backspace key, just say (string=? key "\b")

Now we need some way of removing characters from a string. The simplest way to do that is to use the substring function. If you say (substring string start end), it will return to you the section of string starting at character number start (with 0 meaning the first character), up to (but not including) character number end.

You want to delete the last character, so you want a substring starting at position 0 and ending at the position of the last character in the string (i.e. the (- length 1). That means you need to know the length of the original string, and you can get that with the string-length procedure which takes in a string and returns the number of characters in the string (like length does with lists).


Activity 4.3: Quitting

Finally, you’d like to have some way of signaling when you’re done typing. So modify key-pressed to quit the editor when the user presses return/enter. The return key is reported to key-pressed as "\r", and you can tell the editor to quit just by setting the variable quit? to true.