Intermezzo: Quote, Unquote
Lists play a central role in our book and in Racket, which spawned our teaching languages. Be sure to use BSL+ or higher for working through this intermezzo. For the design of programs, it is critical to understand how lists are constructed from first principles; it informs the organizations of the functions in our programs. Routine work with lists calls for a compact notation, however, like the list function introduced in The list function.
Since the late 1950s Lisp-style languages have come with an even more powerful pair of list-creation tools: quotation and anti-quotation. Many programming languages support them now, and the PHP web page design language injected the idea into the commercial world.
This intermezzo give you a taste of this quotation mechanism. It also introduces symbols, a new form of data that is intimately tied to quotation. While this introduction is informal and uses simplistic examples, latter parts of the book illustrate how powerful the idea is with near-realistic sample problems. Come back to this intermezzo if any of these examples cause any trouble.
Quote
Quotation is a short-hand mechanism for writing down a large list easily. Roughly speaking, a list constructed with the list function can be constructed even more concisely by quoting lists. Conversely, a quoted list abbreviates a construction with list.
> '(1 2 3) (list 1 2 3)
> '("a" "b" "c") (list "a" "b" "c")
> '(#t "hello world" 42) (list true "hello world" 42)
> '(("a" 1) ("b" 2) ("d" 4)) (list (list "a" 1) (list "b" 2) (list "d" 4))
| is short for |
|
| is short for |
|
'(1 "a" 2 #f 3 "c")
'()
and this table-like shape:
'(("alan" 1000) ("barb" 2000) ("carl" 1500) ("dawn" 2300)) Now eliminate list in favor of cons where needed.
Quasiquote And Unquote
The preceding section should convince you of the advantages of ' and quote. You may even wonder why the book introduces quote only now and not right from the start. It seems to greatly facilitate the formulation of test cases that involve lists as well as for keeping track of large collections of data. But all good things come with surprises, and that includes quote.
When it comes to program design, it is misleading for beginners to think of lists as quoted or even list-constructed values. The construction of lists with cons is far more illuminating for the step-wise creation of programs than short-hands such as quote, which hide the underlying construction. So don’t forget to think of cons whenever you are stuck during the design of a list-processing function.
(define x 42)
'(40 41 x 43 44)
> '(40 41 x 43 44) (list 40 41 'x 43 44)
'(1 (+ 1 1) 3)
| is short for |
|
In some cases, you do not want to create a nested list. You actually want a true expression in a quoted list and you want to evaluate the expression during the construction of the list. For such cases, you want to use quasiquote. Like quote, quasiquote is a keyword for a compound sentence: (quasiquote (1 2 3)). And like quote, quasiquote comes with a shorthand, namely the ` character, which is the “other” single-quote key on your keyboard.
> `(1 2 3) (list 1 2 3)
> `("a" "b" "c") (list "a" "b" "c")
> `(#t "hello world" 42) (list true "hello world" 42)
> `(40 41 ,x 43 44) (list 40 41 42 43 44)
> `(1 ,(+ 1 1) 3) (list 1 2 3)
(quasiquote (40 41 (unquote x) 43 44)) (quasiquote (1 (unquote (+ 1 1)) 3))
; String String -> ... Deeply Nested List ... ; produce a (representation of) a web page with given author and title (define (my-first-web-page author title) `(html (head (title ,title) (meta ((http-equiv "content-type") (content "text-html")))) (body (h1 ,title) (p "I, " ,author ", made this page."))))
A second look also shows that the title parameter shows up twice in the function body: once nested in a nested list labeled with 'head and once nested in the nested list labeled with 'body. The other parameter shows up only once. We consider the nested list a page template, and the parameters are holes in the template, to be filled by useful values. As you can imagine, this template-driven style of creating web pages is particular style of programming is most useful when you wish to create many similar pages for a site.
(my-first-web-page "Matthias" "Hello World")
Nested List Representation | Web Page Code (HTML) | ||||||||||||||||||||||||||||||||||||
|
|
If this were 1993, you could now sell the above function as a Dot Com company that generates people’s first web page with a simple function call. Alas, in this day and age, it is only an exercise.
Exercise 195. Eliminate quasiquote and unquote from the following expressions so that they are written with list instead:
`(1 "a" 2 #f 3 "c")
this table-like shape:
`(("alan" ,(* 2 500)) ("barb" 2000) (,(string-append "carl" " , the great") 1500) ("dawn" 2300)) and this second web page:
`(html (head (title ,title)) (body (h1 ,title) (p "A second web page"))) Also write down the nested lists that the expressions produce.
Unquote Splice
| is short for |
|
(list 'tr "1" "2" "3" "4" "5")
(cons 'tr (make-row '(1 2 3 4 5)))
`(tr ,@(make-row '(1 2 3 4 5)))
(cons 'tr (make-row '(1 2 3 4 5)))
'(table ((border "1")) |
(tr (td "1") (td "2") (td "3") (td "4")) |
(tr (td "2.8") (td "-1.1") (td "3.4") (td "1.3"))) |
; List-of-numbers -> ... Nested List ... ; create a row for an HTML table from a list of numbers (define (make-row l) (cond [(empty? l) '()] [else (cons (make-cell (first l)) (make-row (rest l)))])) ; Number -> ... Nested List ... ; create a cell for an HTML table from a number (define (make-cell n) `(td ,(number->string n)))
> (make-cell 2) (list 'td "2")
> (make-row '(1 2)) (list (list 'td "1") (list 'td "2"))
; List-of-numbers List-of-numbers -> ... Nested List ... ; create an HTML table from two lists of numbers (define (make-table row1 row2) `(table ((border "1")) (tr ,@(make-row row1)) (tr ,@(make-row row2))))
> (make-table '(1 2 3 4 5) '(3.5 2.8 -1.1 3.4 1.3))
(list
'table
(list (list 'border "1"))
(list
'tr
(list 'td "1")
(list 'td "2")
(list 'td "3")
(list 'td "4")
(list 'td "5"))
(list
'tr
(list 'td "3.5")
(list 'td "2.8")
(list 'td "-1.1")
(list 'td "3.4")
(list 'td "1.3")))
Exercise 196. Eliminate quasiquote, unquote, and unquote-splicing from the following expressions so that they are written with list instead:
`(0 ,@'(1 2 3) 4)
this table-like shape:
`(("alan" ,(* 2 500)) ("barb" 2000) (,@'(list "carl" " , the great") 1500) ("dawn" 2300)) and this third web page:
`(html (body (table ((border "1")) (tr ((width "200")) ,@(make-row '( 1 2))) (tr ((width "200")) ,@(make-row '(99 65)))))) where make-row is the function from above.Also write down the nested lists that the expressions produce.
Exercise 197. Create the function make-ranking, which consumes a list of ranked song titles and produces a list representation of an HTML table. Consider this example:
(define one-list '("Asia: Heat of the Moment" "U2: One" "The White Stripes: Seven Nation Army")) If you apply make-ranking to one-list and display the result in a browser, you would see something like that:Hint: Although you could design a function that determines the rankings from a list of strings, we wish you to focus on the creation of tables instead. Thus we supply the following functions:
(define (ranking los) (reverse (add-ranks (reverse los)))) (define (add-ranks los) (cond [(empty? los) '()] [else (cons (list (length los) (first los)) (add-ranks (rest los)))])) Before you use these functions, equip them with signatures and purpose statements. Then explore their workings with interactions in DrRacket. Accumulators expands the design recipe with a way to create simpler functions for computing rankings than ranking and add-ranks.