Class summary: Function Evaluation and Practice
Copyright (c) 2017 Kathi Fisler
1 WarmUp: Ticket Prices
A sports stadium offers special ticket pricing for large groups. The first 10 tickets cost $15 each, and additional tickets cost $10 each. Develop a function ticketprice that takes a number of tickets and produces the total cost of that many tickets.
Identify the name, parameters, body, and header of your definition.
2 How Functions Evaluate
We have talked about how expressions evaluate: from the innermostexpression out, with definitions extending the dictionary. But what happens when we add our own functions? How do programs with functions evaluate? We’ll work this first with a small function, then revisit on the ticketprice function.
2.1 Information Tracked During Evaluation
As a reminder, Pyret tracks two pieces of information at all times:
The dictionary of known names
(While evaluating) the current (sub) expression being evaluated.
As we look at how functions affect evaluation, our goal is to be clear on how each of defining and using functions affects these two pieces of information.
2.2 Substitute Arguments into Function Bodies
Assume that we have the following definition, and want to evaluate the expression 5 + cube(2).
# contents of definitions window 
fun cube(n :: Number): 
n * n * n 
where: 
cube(1) is 1 
cube(4) is 64 
end 
Evaluation still goes inside out (and lefttoright), but when we get to call to a userdefined function, we substitute the argument into the body of the function, then continue evaluating. Here’s what the evaluation looks like stepbystep:
5 + cube(2) 

5 + (2 * 2 * 2) 

5 + 8 

13 
In the 2nd line, we replaced the call to cube{2} with the body of cube, but with each use of n replaced with the argument 2. Once the substitution occurs, evaluation continues using the same "insideout" rule that we had before.
2.3 Handling Expressions as Arguments
What if we had the program cube(3 + 1). Would the first three steps look like:
cube(3 + 1) 

cube(4) 

4 * 4 * 4 
or
cube(3 + 1) 

(3 + 1) * (3 + 1) * (3 + 1) 

4 * 4 * 4 
or
cube(3 + 1) 

(3 + 1) * (3 + 1) * (3 + 1) 

4 * (3 + 1) * (3 + 1) 
The first sequence is correct. By the insideout rule, Pyret will evaluate the argument (3+1) before it substitutes in the body of the function.
3 Evaluating Functions and IfExpressions
Combining functions and ifexpressions doesn’t introduce anything new, but it does reinforce the rules we’ve already learned.
Go back to the ticketprice function and show the evaluation steps for the expression that follows the function definition.
fun ticketprice(numtix): 
if (numtix > 10): 
(15 * 10) + ((numtix  10) * 10) 
else: 
numtix * 15 
end 
where: 
ticketprice(10) is 150 
ticketprice(2) is 30 
ticketprice(12) is 170 
end 

ticketprice(12) 
ticketprice(12)
if (12 > 10): (15 * 10) + ((12  10) * 10) else: 12 * 15 end
if true: (15 * 10) + ((12  10) * 10) else: 12 * 15 end
(15 * 10) + ((12  10) * 10)
150 + ((12  10) * 10)
150 + (2 * 10)
150 + 20
170
4 How Functions Affect the Dictionary
We have talked about how definitions impact the dictionary of known names within Pyret. How do functions affect the dictionary? Consider the following definitions window contents:
outside = 100 

fun coloredsquare(color): 
square(outside, "solid", color) 
end 
Running this program adds two names to the dictionary: outside and coloredsquare. The fun construct creates dictionary entries, just as = does.
5 Defining Versus Calling Functions
Does a red square get created when we run the definitions window from the previous section? No. All the fun construct does is augment the dictionary with a new operator.
We only get a red square when we use or call the function. For example, if we type
coloredsquare("red") 
in the Interactions window, Pyret evaluates the expression with the following sequence:
coloredsquare("red") 

square(outside, "solid", "red") 

# which produces the image 
It is essential to understand that defining and calling functions are two separate actions. Defining a function augments the dictionary, but the function body does not get evaluated when a function is defined. Calling a function evaluates its body.
A function gets defined only once, but it may be called many times, which means its body may be evaluated many times. To see this, consider the following sequence:
cube(2) + cube(3) 

(2 * 2 * 2) + cube(3) 

8 + cube(3) 

8 + (3 * 3 * 3) 

8 + 27 

35 
The distinction between defining and calling functions is a common stumbling block when people learn programming. Keep the dictionary in mind: we define functions in order to name computations that we’ll use multiple times. Naming is separate from using.
6 More Functions Practice
Here are several problems with which to practice both defining and evaluating functions. Make sure to use the Design Recipe while designing each one, thinking about a good set of examples.
Develop the function imageclassify, which consumes an image and conditionally produces "tall" if the image is taller than wide, "wide" if it is wider than tall, or "square" if its width and height are the same.
[Problem credit: How to Design Programs, 2nd edition]
Develop the function custommessage, which takes the name of a person and a discount to offer to that person. The function produces a string consisting of a message telling the person what discount they can get by shopping today. Here’s an example:
custommessage("Kathi", 10) is 
"Hi Kathi! Shop today and get a 10% discount" 
Pyret has a builtin function called tostring that converts any value into a string. You may find that helpful here.
You want to split the cost of a pizza with friends. Each of you will pay for the number of slices you ate, as well as your share of the tip on those slices. Assume a pizza costs $12 and has 8 slices. Write a function pizzashare that consumes the number of slices that you ate and returns your share of the cost of the pizza including a 10% tip.
fun pizzacost(slices): 
doc: "compute cost of given number of pizza slices, plus 10% tip" 
(12 * (slices / 8)) * 1.10 
where: 
pizzacost(8) is 12 * 1.10 
pizzacost(4) is 6 * 1.10 
end 
This function actually has two tasks in it —
fun tip(amt): 
amt * 1.10 
where: 
tip(1) is 1.10 
tip(2) is 2.20 
tip(5) is 5.50 
end 

fun pizzacost(slices): 
doc: "compute cost of given number of pizza slices, plus tip" 
tip(16 * (slices / 8)) 
where: 
pizzacost(8) is 13.20 
pizzacost(4) is 6.60 
end 
Note that we also write examples for helper functions such as tip. Why? Two reasons: (1) we still want examples to confirm that the function is working properly, and (2) if we get the wrong answer from pizzacost and have not checked tip, we won’t know which function contains the error.
Recall the gradebook programs we wrote on tables last week. Develop a function called trajectory that takes in a student’s grades on the first two exams and produces a string that summarizes the direction of the student’s exam grades. If the two grades are within 5 points of each other, the direction is "steady". Otherwise, if the second exam is higher, the direction is "improving"; if the first exam is higher, the direction is "declining".
Load up the gradebook table from last week, and use the trajectory function to extend the gradebook with a new column that reports the trajectory.
How good were your test cases for trajectory? Open this Pyret file, which contains three solutions to this problem (submitted by students in a previous course). Did your examples pick up that there were errors in these solutions? To find out, paste your where clause into each of these definitions (changing the name of the trajectory function to match which function you are testing). Do any of your examples fail? If not, then your examples didn’t cover the situation in which the solution was wrong. Add examples until you find one that shows the error in the solution, then try to describe more generally what situation had the error.
spring: Mar 20  Jun 19
summer: Jun 20  Sept 21
fall: Sept 22  Dec 20
winter: Dec 21  Mar 19
Develop a function called season that consumes a month (as a String) and a day (a Natural). The function produces the season (a String) for that date.
Note: there are different ways to break this problem down into functions and helper functions. Spend some time thinking about how you might do this, rather than diving in to start coding.
[Problem credit: Glynis Hamel, WPI CS]