Conditionals and Booleans
1 Conditionals:   Computations with Decisions
1.1 Asking Multiple Questions

Conditionals and Booleans

Copyright (c) 2017 Kathi Fisler

This material goes with Booleans (section 2.5) from the textbook

1 Conditionals: Computations with Decisions

Continuing with our program to order pens: we’ve computed the cost of producing the pens, but we should also account for shipping costs. Shipping costs can be based on many things (weight of order, cost of order, shipping destination, etc). Let’s work with cost of order.

We’ll write a function add-shipping to compute the total cost of an order including shipping. Assume an order valued at $10 or less ships for $4, while an order valued above $10 ships for $8.

Let’s concretize this into some examples:

  a $10 order with shipping costs 10 + 4

  a $3.95 order with shipping costs 3.95 + 4

  a $20 order with shipping costs 20 + 8

  a $10.01 order with shipping costs 10.01 + 8

Last class, we learned to circle differences across examples to help us identify parameters of a function. The order cost is definitely a different that appears in both a scenario and its answer, but the shipping costs also differ across examples. Should we circle those too?

We’ll circle them (since they are differences), but we won’t name that circle. The shipping charges depend on the order cost – we can compute that value from the cost – it isn’t an input that we need to take as a parameter to our function. For this problem, we only need the cost of the order as an input.

Let’s write out the function header (function name, parameters, and types) for our function along with the examples in Pyret:

  fun add-shipping(order-amt :: Number) -> Number:

    doc: "increase order price by costs for shipping"

    ... # we'll fill in the body shortly.

  where:

    add-shipping(10) is 10 + 4

    add-shipping(3.95) is 3.95 + 4

    add-shipping(20) is 20 + 8

    add-shipping(10.01) is 10.01 + 8

  end

NOTE: The version that Shriram worked in class used pen-cost in the examples, which left you with an (odd) empty slogan. To avoid that, these notes instead take the cost of the order so far, and just add the shipping. We will see how to combine pen-cost and add-shipping in the next lecture.

To fill in the body of this function, we need to ask a question about the input: specifically, is it less than $10? We do this with a construct called if:

  fun add-shipping(order-amt :: Number) -> Number:

    doc: "increase order price by costs for shipping"

    if order-amt <= 10:

      order-amt + 4

    else:

      order-amt + 8

    end

  where:

    add-shipping(10) is 10 + 4

    add-shipping(3.95) is 3.95 + 4

    add-shipping(20) is 20 + 8

    add-shipping(10.01) is 10.01 + 8

  end

In an if expression, we ask a question (here order-amt <= 10), provide the result of the computation when the answer to the question is positive (order-amt + 4), and provide the answer when the result is negative (else, followed by order-amt + 8. We also need end to tell Pyret we’re done asking the question.

Side Note: You might wonder what should happen if we give a nonsensical number, such as a negative number, or zero. For now, we’re focusing on writing programs that produce answers for meaningful inputs. We’ll deal with unexpected inputs in a couple of weeks.

An if-expression could also have been used to handle bulk discounts, different prices for different fonts, and other ideas that came up when we discussed the pens problems in the last lecture.

Friday’s lecture only got up to this point. We will cover the material that follows on Monday (it’s in this file to keep the parts of the example together).

1.1 Asking Multiple Questions

Shipping costs are rising, so we want to modify the program to include a third shipping level: orders between $10 and $30 ship for $8, but orders over 30 ship for $12.

How might we capture "orders between $10 and $30" in code? There is no "between" operator in Pyret, so we have to make two comparisons to the order-amt:

  order-amt > 10

  order-amt < 30

If-statements need single yes/no questions though, not two. So we have to combine these into one yes/no answer. Before we do that, let’s look at what types of data these expressions return. If we type 20 <= 10 at the prompt, what do you expect Pyret return? } If we type this into Pyret, we get true. Is true a string? No, it doesn’t have quotation marks around it. It isn’t a number (though some of you with prior programming experience may have seen true be a number; that isn’t how Pyret works). Instead, this expression returns a new type of value called Boolean.

Boolean has two values: true and false.

So back to our original problem: if order-amt > 10 and order-amt < 30 both return booleans, how can we combine those Booleans to require that both conditions be true? Most programming languages follow English here, and combine such expressions with and.

  order-amt > 10 and order-amt < 30

Try this with an order-amt of 40. Oops – Pyret complains. Note this is the same error we got when we wrote code like 3 + 8 * 2. We need to include parentheses to tell Pyret which operation(s) to do first:

  (40 > 10) and (40 < 30)

With this in hand, now we can extend our add-shipping program to include the new range of shipping costs. When the decision you are making has more than one possible answer (not just yes/no to one question), you can use else if to ask subsequent questions:

  fun add-shipping(order-amt :: Number) -> Number:

    doc: "increase order price by costs for shipping"

    if order-amt <= 10:

      order-amt + 4

    else if (order-amt > 10) and (order-amt < 30):

      order-amt + 8

    else:

      order-amt + 12

    end

  where:

    add-shipping(10) is 10 + 4

    add-shipping(3.95) is 3.95 + 4

    add-shipping(20) is 20 + 8

    add-shipping(10.01) is 10.01 + 8

    add-shipping(30) is 30 + 12

  end