CS2a/Spr09: Introduction to Computers
More on Servlets

More on servlets

The case expression

The echo servlet will behave poorly however if you don't give it any parameter. It will usually just cause the browser to hang. To rectify this problem, we can use the following nicer echo servlet:

; niceEcho.servlet (servlet (a) (case a ((#null){Please add "?a=Hi" to the URL and hit return}) (("Hi") {Good job. Now try "?a=Hello"}) (("Hello") {Great. Now try "?a=ANYTHING"}) (("ANYTHING") {OK, OK. Now try something more original}) (("original" "something") {Are you trying to be funny?}) (else {You just typed [a]}))) If one visits this page without an "a" parameter, then the value assigned to "a" by the servlet will be the special value "#null" and it will return some instructions about how it should be called. Otherwise, it will look at the value passed in for "a" and if it matches one of the quoted expressions "Hi", "Hello" etc. it will return the corresponding expression. Note that "original" and "something" both trigger the same response "Are you trying to be funny?". If no match is found, then the servlet just echoes back what the user entered.

The case expression in general has the form

(case a ((value1) result1) ((value2 value3) result2) ... (else lastresult)) When the case expression is evaluated, the Scheme system tries to find the first match of a with one of the values. When a match is found, the corresponding result expression is evaluated. If no match is found then the else clause applies and the lastresult expression is evaluated. The niceEcho.servlet shows how to use the case to support an interaction between the user and the servlet.

EXERCISE: What is the result of evaluating the following Scheme expressions: (case "b" (("a" "c") "hello") (("b" "d") "goodbye") (("a" "b") "wow") (else "darn")) (case (* 2 2) ((1 3 5 7 9) "odd digit") ((0 2 4 6 8) "even digit"))

Next we give an example of a servlet that handles many parameters:

;multiEcho.servlet (servlet (color name age) { <html><head><title>multiEcho</title></head> <body style="background:[color]"> <h1> Hi [name], so you are [age] years old!</h1> </body> </html> } ) If this servlet is accessed by visiting the "multiEcho.servlet" URL with the following parameter string: ?name=Tim&age=46&color=lightgreen Then the browser will return a lightgreen page with an h1 heading containing the text Hi Tim, so you are 46 years old! Note that the order in which the parameters appear in the URL or in the parameter list of the servlet does not matter.

Processing numbers using servlets

Let us now revisit and extend some of the first servlets we considered above. For example, lets generalize the hypotenuse servlet to handle any triangle provided we enter the lengths of the two other sides.

One way to write this servlet is the following:

; hypotenuse.servlet (servlet (a1 b1) (let* ( (a (Double. a1)) (b (Double. b1)) ) (case (a) ((#null) {Enter data by adding "?a=3&b=4" to the URL}) (else {The right triangle with sides of length [a] and [b], has a hypotenuse of length [(sqrt (+ (* a a) (* b b)))]})))) The (Double. a1) expression converts the input parameter a1 into a decimal number. The let* syntax is a way of giving temporary names to calculated values. The names are only valide "within" the let* expression. The servlet parameters a1 and b1 must be strings that represent numbers or else the servlet will generate an error! We will discuss methods for dealing with user input errors in a later section on "error checking". For now, we assume that the user will follow instructions and enter numbers when they are expected.

Another way is to use the "define" statement to give values to the variables "a" and "b" as follows: ; hypotenuse.servlet (servlet (a1 b1) (define a (Double. a1)) (define b (Double. b1)) (case (a) ((#null) {Enter data by adding "?a=3&b=4" to the URL}) (else {The right triangle with sides of length [a] and [b], has a hypotenuse of length [(sqrt (+ (* a a) (* b b)))]}))))
EXERCISE: Write a servlet "sumtoN.servlet" which finds the sum of the numbers from 1 to N using the formula [ 1+2+3+ldots+N = N*(N+1)/2 ] and use it to find the sum from 1 to 100 (You should get 5050).
EXERCISE: Write a servlet "tip.servlet" which calculates the tip required for a meal assuming that you tip at the 15% rate.

Giving names to values using let*

When processing complex formulas it is often helpful to break the formula into pieces and to give names to each individual piece. For example, the body mass index provides a rough measure of whether your are overweight. The formula consists of dividing your weight in kilograms by the square of your height in meters. The following servlet computes your BMI by first converting your weight in pounds to your weight in kilograms, and your height in inches to your height in meters, then it applies the formula:

; BMI.servlet (servlet (w1 h1) (case w1 ((#null) {Enter data using ?w=170&h=68}) (else (let* ( (w (Double. w1)) (h (Double. h1)) (k (/ w 2.2)) ; weight in kilograms (m (/ h 39.36)) ; height in meters (bmi (/ k (* m m))) ) {If your weight is [w] pounds <br> and your height is [h] inches, <br> Then your BMI is [bmi]. <br> Note: a BMI over 30 is considered medically dangerous})))) The let* expression above provides temporary names k, m, and bmi for the computed values. The general form of a let* statement is (let* ( (var1 Expr1) (var2 Expr2) ... (varn Exprn) ) Expr) where the Exprs are evaluated and stored in the corresponding variables var1,.... These variables are then used in the final Expr to compute the value returned by the let* expression. The key points to note here are that each variable can be used to define the values of the following variables, but none of the variables has a value outside of the let*. Note: there is also a let expression which is almost exactly the same, except that the defined variables cannot depend on each other.

You can also do this with define and begin:

; BMI.servlet (servlet (w1 h1) (case w1 ((#null) {Enter data using ?w=170&h=68}) (else (begin (define w (Double. w1)) (define h (Double. h1)) (define k (/ w 2.2)) ; weight in kilograms (define m (/ h 39.36)) ; height in meters (define bmi (/ k (* m m))) {If your weight is [w] pounds <br> and your height is [h] inches, <br> Then your BMI is [bmi]. <br> Note: a BMI over 30 is considered medically dangerous}))))

EXERCISE: Write a servlet that uses a let* expression to compute the area of a triangle whose sides are a, b, and c using the following formula:
A = sqrt((s-a)(s-b)(s-c)s)
where s=(a+b+c)/2 is the semiperimeter. You should try this with a=3, b=4, and c=5. These are the sides of a 3-4-5 right triangle and so the areas should be half of base times width, which is 0.5*3*4= 6.

The if form and conditional execution

As a final example of these simple servlets, we now consider a liquor test servlet which determines whether you can buy liquor in Massachusetts.

; liquorTest.servlet (case (age1) ((#null) {Enter your age by appending ?age=19 to the URL}) (else (let* ((age (Double. age1))) (if (< age 21) {Sorry, you have to wait [(- 21 age)] years before you can buy alcohol here.} {You've been able to buy liquor here for [(- age 21)] years}))))

This servlet uses the (if TEST THEN ELSE form to test whether the buyer's age is 21 or higher. This form first evaluates the TEST. If the result is false, it then evaluates the ELSE code, otherwise it evaluates the THEN code. This example also introduces the comparison operator <. Scheme has a rich set of operators for creating tests. The numeric comparison operators you can use are

(< a b) -- true if a is less than b (<= a b) -- true if a is less than or equal to b (> a b) -- true if a is greater than b (>= a b) -- true if a is greater than or equal to b (= a b) -- true if a is equal to b (!= a b) -- true if a is not equal to b In addition, the arguments a and b can be arithmetic expressions (like (/ age 10)).

You can also combine tests using the and, or, and not operators, e.g. to test if someone is "college age" you could use the following expression:

(and (<= age 22) (>= age 16)) To test whether someone is "not" college age, you could use either (not (and (<= age 22) (>= age 16))) or (or (> age 22) (< age 16)) which are equivalent for any numeric value of age.

EXERCISE: What is the result of evaluating the following Scheme expressions: (if (< 5 (* 2 4)) (* 23 20) (- 18 5)) (if (> (+ 23 9) (* 6 5)) {Cool! [(+ 23 9)] rules!} {Hmmm, [(* 6 5)] is biggest})
EXERCISE: Write a servlet that determines whether someone is a senior citizen by testing if their age is at least 60.

The cond form and multiple tests

Sometimes one wants to combine several tests and do something different for each of the possible test results. For example, one might want to classify someone as a child, teenager, adult, or senior citizen based on their age. The simplest way to do this is using the cond form, as in the following servlet

; ageClassifier.servlet (servlet (age1) (let* ((age (Double. age1))) (cond ((< age 13) "child") ((< age 20) "teenager") ((< age 60) "adult") (else "senior citizen")))) The cond expression consists of several "clauses" each of which begins with a test. If the test is true, then the rest of the clause is evaluated. Otherwise, testing continues with the next clause. The very last clause should always be an "else" clause which applies to all remaining cases. The tests can be simple comparisons as shown above, or can be very complex expressions.

EXERCISE: Write a servlet which computes the tip given the cost of the meal and a number between 0.0 and 10.0 representing the quality of service. You should use a cond to determine whether to leave no tip, a 5%, 10%, 15%, or 20% tip based on the service number.

Summary of Scheme Syntax

The first figure below shows all of the Scheme expressions we have seen in this chapter and the next figure shows the Scheme procedures we have encountered thus far.
S-expressions
where the Ei are numbers, strings, or S-expressions. (E0 E1 ... Ek) Quasi-strings
where Ei are S-expressions {....[E1]....[E2].... ....[En]....} Servlets
where E is an S-expression (servlet (a b ...) E) Case-based execution
where the Ci are constants and the Ei are expressions. (case E0 ((C1) E1) ((C2 C3 C4 C5) E2) ... (else E)) Simple tests
where TEST, THEN, ELSE are expressions. (if TEST THEN ELSE) Multiple tests
where Ti are tests and the Ei are expressions. (cond (T1 E1) (T2 E2) ... (else E)) Local Variable Binding
where the Vi are variables and the Ei are expressions. (let* ((V1 E1) (V2 E2) ... (Vn En)) E)

Summary of Scheme Syntax

Logical operators:
(and T1 T2 ...) (or T1 T2 ...) (not T) Arithmetic operators:
(+ a b ...) (- a b) (* a b ...) (/ a b) (sqrt a) (exp a) (log a) (sin a) (cos a) (tan a) (asin a) (acos a) (atan a) (round a) Comparison operators:
(< a b) (> a b) (<- a b) (>- a b) (= a b) (!= a b) (equal? a b) Java procedures:
(Date.) (Math.random) (.getHeader request "....") (.getRemoteAddr request) (.sendRedirect response URL)

Scheme procedures seen so far

Creative Commons License
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 2.0 Generic License