序言: 怎么编程
当你是一个小孩子时,你爸妈教你用手指头数数,Consider a quick look at On Teaching Part I. 然后做一些简单的计算:“1加1等于2”;“1加2等于3”,等等。然后他们将会问你“2加3”等于多少?你将会数一只手的全部手指头。 他们编程,你计算。在某种意义上,那就是编程和计算的全部。
Download DrRacket from its web site.
(+ 1 1)
DrRacket的顶部叫做定义区。 在这个区域,你创建程序,叫编辑。只要你在定义区添加一个字或者或者修改一些内容,保存 按钮出现在左上角。当你首次点击保存时, DrRacket会询问你文件的名字,以便它能保存好你的程序。一旦定义区和一个文件关联了,点击保存确保定义区的内容安全地存储在文件里。
程序 包括 一系列的表达式。你已经看到了数学中的表达式。目前,一个表达式是一个普通数字或者 是开始于一个左括号“(”,并且结束于匹配的右括号“)”的内容—
DrRacket提倡 屏蔽括号对之间的区域。 - 当你点击运行, DrRacket对定义区内的表达式求值,在交互区 显示结果。然后,DrRacket,你的忠实的保姆,在提示符处 (>),等着你的命令。 提示符的出现表示DrRacket正等着你输入其他的表达式,然后它就对这些表达式求值,就像定义区的那样:
> (+ 1 1) 2
咋提示符处,输入一个表达式,在你的键盘上点击“返回”或者 or “回车”键 ,然后观察DrRacket响应的结果。喜欢的话,你可以经常这样做:
通常,当DrRacket替你计算时,他使用你熟悉和喜欢的数学规则,像你一样,只有当所有的操作数是普通数字时,它才能决定一个加法的结果。
如果一个操作数是一个带括号的操作符表达式—
> (+ (1) (2)) function call:expected a function after the open parenthesis, found a number
在这部分内容中,编程就是写可理解的算术表达式,计算就是确定它们的值。 借助DrRacket,可以轻松探索这类编程和计算。
算法还是算法
如果编程仅仅是数字和算术,它将会像数学一样令人烦恼。幸运的是,比数字更多的东西可以编程:文本,定理,图像,还有更多。 Just kidding: mathematics is a fascinating subject, but you won’t need much of it for now.
> "hello world" "hello world"
> (string-append "hello" "world") "helloworld"
> (string-append "hello " "world") "hello world"
(string-append "hello" " " "world")
你可以用字符串做更多的事情,而不仅仅是追加它们。 You can do more with strings than append them. You can extract pieces from a string, reverse them, render all letters uppercase (or lowercase), strip blank spaces from the left and right, and so on. And best of all, you don’t have to memorize any of that. If you need to know what you can do with strings, look up the term in HelpDesk.Use F1 or the drop-down menu on the right to open HelpDesk. Look at the manuals for BSL and its section on pre-defined operations, especially those for strings.
> (+ (string-length "hello world") 20) 31
> (number->string 42) "42"
> (string->number "42") 42
> (string->number "hello world") #false
> (and #true #true) #true
> (and #true #false) #false
> (or #true #false) #true
> (or #false #false) #false
> (not #false) #true
(and (or (= (string-length "hello world") (string->number "11")) (string=? "hello world" "good morning")) (>= (+ (string-length "hello world") 60) 80))
>
![]()
Add (require 2htdp/image) to the definitions area, or select Add Teachpack from the Language menu and choose image from the Preinstalled HtDP/2e Teachpack menu.
(* (image-width
) (image-height
))
> (image-width (square 10 "solid" "red")) 10
> (image-width (overlay (rectangle 20 20 "solid" "blue") (circle 5 "solid" "red"))) 20
(place-image (circle 5 "solid" "green") 50 80 (empty-scene 100 100))
Let’s summarize again. To program is to write down an
arithmetic expression, but you’re no longer restricted to
boring numbers. In BSL, arithmetic is the arithmetic of numbers,
strings, Booleans, and even images. To compute, though, still means
to determine the value of an expression—
And now you’re ready to write programs that make rockets fly.
Inputs and Output
The programs you have written so far are pretty boring. You write down an expression or several expressions; you click RUN; you see some results. If you click RUN again, you see the exact same results. As a matter of fact, you can click RUN as often as you want, and the same results show up. In short, your programs really are like calculations on a pocket calculator, except that DrRacket calculates with all kinds of data, not just numbers.
That’s good news and bad news. It is good because programming and computing ought to be a natural generalization of using a calculator. It is bad because the purpose of programming is to deal with lots of data and to get lots of different results, with more or less the same calculations. (It should also compute these results quickly, at least faster than we can.) That is, you need to learn more still before you know how to program. No need to worry though: with all your knowledge about arithmetic of numbers, strings, Boolean values, and images, you’re almost ready to write a program that creates movies, not just some silly program for displaying “hello world” somewhere. And that’s what we’re going to do next.
Just in case you didn’t know, a movie is a sequence of images that are rapidly displayed in order. If your algebra teachers had known about the “arithmetic of images” that you saw in the preceding section, you could have produced movies in algebra instead of boring number sequences. Well, here is one more such table:
x =
1
2
3
4
5
6
7
8
9
10
y =
1
4
9
16
25
36
49
64
81
?
It turns out that making a movie is no more complicated than completing a table of numbers like that. Indeed, it is all about such tables:
x =
1
2
3
4
y =
![]()
![]()
![]()
![]()
y = the image that contains a dot x2 pixels below the top.
This second part means you must supply one number—
(y 1)
(y 2)
- First,
(define (FunctionName InputName) BodyExpression)
is a function definition. You recognize it as such because it starts with the “define” keyword. It essentially consists of three pieces: two names and an expression. The first name is the name of the function; you need it to apply the function as often as you wish. The second name—called a parameter— represents the input of the function, which is unknown until you apply the function. The expression, dubbed body, computes the output of the function for a specific input. - Second,
(FunctionName ArgumentExpression)
is a function application. The first part tells DrRacket which function you wish to use. The second part is the input to which you want to apply the function. If you were reading a Windows or a Mac manual, it might tell you that this expression “launches” the “application” called FunctionName and that it is going to process ArgumentExpression as the input. Like all expressions, the latter is possibly a plain piece of data or a deeply nested expression.
> (empty-scene 100 60)
![]()
> (place-image 50 23 (empty-scene 100 60))
![]()
> (place-image 50 20 (empty-scene 100 60))
![]()
> (place-image 50 30 (empty-scene 100 60))
![]()
> (place-image 50 40 (empty-scene 100 60))
![]()
(define (picture-of-rocket height) (place-image 50 height (empty-scene 100 60)))
(picture-of-rocket 0) (picture-of-rocket 10) (picture-of-rocket 20) (picture-of-rocket 30)
> (animate picture-of-rocket)
As soon as you hit the “return” key, DrRacket evaluates
the expression; but it does not display a result, not even a prompt.
It opens another window—
So here is what you learned in this section. Functions are useful because they can process lots of data in a short time. You can launch a function by hand on a few select inputs to ensure that it produces the proper outputs. This is called testing a function. Or, DrRacket can launch a function on lots of inputs with the help of some libraries; when you do that, you are running the function. Naturally, DrRacket can launch functions when you press a key on your keyboard or when you manipulate the mouse of your computer. To find out how, keep reading. Whatever triggers a function application isn’t important, but do keep in mind that (simple) programs are functions.
Many Ways to Compute
When you evaluate (animate picture-of-rocket), the rocket eventually disappears into the ground. That’s plain silly. Rockets in old science fiction movies don’t sink into the ground; they gracefully land on their bottoms, and the movie should end right there.
This idea suggests that computations should proceed differently, depending on the situation. In our example, the picture-of-rocket program should work “as is” while the rocket is in flight. When the rocket’s bottom touches the bottom of the canvas, however, it should stop the rocket from descending any farther.
In a sense, the idea shouldn’t be new to you. Even your mathematics teachers define functions that distinguish various situations:
> (sign 10) 1
> (sign -5) -1
> (sign 0) 0
This is a good time to explore what the STEP button does. Add (sign -5) to the definitions area and click STEP for the above sign program. When the new window comes up, click the right and left arrows there.
(cond [ConditionExpression1 ResultExpression1] [ConditionExpression2 ResultExpression2] ... [ConditionExpressionN ResultExpressionN])
(define (picture-of-rocket.v2 height) (cond [(<= height 60) (place-image 50 height
(empty-scene 100 60))] [(> height 60) (place-image 50 60
(empty-scene 100 60))]))
With this knowledge, you can now change the course of the simulation. The goal is to not let the rocket descend below the ground level of a 100-by-60 scene. Since the picture-of-rocket function consumes the height where it should place the rocket in the scene, a simple test comparing the given height to the maximum height appears to suffice.
See figure 5 for the revised function definition. The definition uses the name picture-of-rocket.v2 to distinguish the two versions. Using distinct names also allows us to use both functions in the interactions area and to compare the results. Here is how the original version works:
> (picture-of-rocket 5555)
![]()
> (picture-of-rocket.v2 5555)
![]()
> (animate picture-of-rocket.v2)
Stop! What do you think we want to see?
Landing the rocket this far down is ugly. Then again, you know how to fix this aspect of the program. As you have seen, BSL knows an arithmetic of images. When place-image adds an image to a scene, it uses its center point as if it were the whole image, even though the image has a real height and a real width. As you may recall, you can measure the height of an image with the operation image-height. This function comes in handy here because you really want to fly the rocket only until its bottom touches the ground.
(- 60 (/ (image-height
) 2))
(place-image 50 (- 60 (image-height
))
(empty-scene 100 60))
(- 60 (/ (image-height
) 2))
(define (picture-of-rocket.v3 height) (cond [(<= height (- 60 (/ (image-height ) 2)))
(place-image 50 height
(empty-scene 100 60))] [(> height (- 60 (/ (image-height ) 2)))
(place-image 50 (- 60 (/ (image-height
) 2))
(empty-scene 100 60))]))
When you think and experiment along these lines, you eventually get to the program in figure 6. Given some number, which represents the height of the rocket, it first tests whether the rocket’s bottom is above the ground. If it is, it places the rocket into the scene as before. If it isn’t, it places the rocket’s image so that its bottom touches the ground.
One Program, Many Definitions
Now suppose your friends watch the animation but don’t like
the size of your canvas. They might request a version that uses 200-by-400
scenes. This simple request forces you to replace 100 with 400 in
five places in the program and 60 with 200 in two other places—
Stop! Before you read on, try to do just that so that you get an idea of how difficult it is to execute this request for a five-line program. As you read on, keep in mind that programs in the world consist of 50,000 or 500,000 or even 5,000,000 or more lines of program code.
(define Name Expression)
(define HEIGHT 60)
(define (picture-of-rocket.v4 h) (cond [(<= h (- HEIGHT (/ (image-height ROCKET) 2))) (place-image ROCKET 50 h (empty-scene WIDTH HEIGHT))] [(> h (- HEIGHT (/ (image-height ROCKET) 2))) (place-image ROCKET 50 (- HEIGHT (/ (image-height ROCKET) 2)) (empty-scene WIDTH HEIGHT))])) (define WIDTH 100) (define HEIGHT 60) (define ROCKET )
> (animate picture-of-rocket.v4)
The program in figure 7
consists of four definitions: one function definition and three
constant definitions. The numbers 100
and 60 occur only twice—
When DrRacket evaluates (animate picture-of-rocket.v4), it replaces HEIGHT with 60, WIDTH with 100, and ROCKET with the image every time it encounters these names. To experience the joys of real programmers, change the 60 next to HEIGHT into a 400 and click RUN. You see a rocket descending and landing in a 100 by 400 scene. One small change did it all.
(- HEIGHT (/ (image-height ROCKET) 2))
(define ROCKET-CENTER-TO-TOP (- HEIGHT (/ (image-height ROCKET) 2)))
While the order of constant definitions matters, it does not matter where you place constant definitions relative to function definitions. Indeed, if your program consists of many function definitions, their order doesn’t matter either, though it is good to introduce all constant definitions first, followed by the definitions of functions in decreasing order of importance. When you start writing your own multi-definition programs, you will see why this ordering matters.
; constants (define WIDTH 100) (define HEIGHT 60) (define MTSCN (empty-scene WIDTH HEIGHT)) (define ROCKET )
(define ROCKET-CENTER-TO-TOP (- HEIGHT (/ (image-height ROCKET) 2))) ; functions (define (picture-of-rocket.v5 h) (cond [(<= h ROCKET-CENTER-TO-TOP) (place-image ROCKET 50 h MTSCN)] [(> h ROCKET-CENTER-TO-TOP) (place-image ROCKET 50 ROCKET-CENTER-TO-TOP MTSCN)]))
The program also contains two line comments, introduced with semicolons (“;”). While DrRacket ignores such comments, people who read programs should not because comments are intended for human readers. It is a “back channel” of communication between the author of the program and all of its future readers to convey information about the program.
Once you eliminate all repeated expressions, you get the program in figure 8. It consists of one function definition and five constant definitions. Beyond the placement of the rocket’s center, these constant definitions also factor out the image itself as well as the creation of the empty scene.
How would you change the program to create a 200-by-400 scene?
How would you change the program so that it depicts the landing of a green UFO (unidentified flying object)? Drawing the UFO is easy:
(overlay (circle 10 "solid" "green") (rectangle 40 4 "solid" "green")) How would you change the program so that the background is always blue?
How would you change the program so that the rocket lands on a flat rock bed that is 10 pixels higher than the bottom of the scene? Don’t forget to change the scenery, too.
Magic Numbers Take another look at picture-of-rocket.v5. Because we eliminated all repeated expressions, all but one number disappeared from this function definition. In the world of programming, these numbers are called magic numbers, and nobody likes them. Before you know it, you forget what role the number plays and what changes are legitimate. It is best to name such numbers in a definition.
Here we actually know that 50 is our choice for an x-coordinate for the rocket. Even though 50 doesn’t look like much of an expression, it really is a repeated expression, too. Thus, we have two reasons to eliminate 50 from the function definition, and we leave it to you to do so.
One More Definition
(define (picture-of-rocket t) (cond [(<= t ROCKET-CENTER-TO-TOP) (place-image ROCKET 50 t MTSCN)] [(> t ROCKET-CENTER-TO-TOP) (place-image ROCKET 50 ROCKET-CENTER-TO-TOP MTSCN)]))
Even if you have never taken a physics course, you know that a time is not a distance. So somehow our program worked by accident. Don’t worry, though; it is all easy to fix. All you need to know is a bit of rocket science, which people like us call physics.
You might wonder why V is 3 here. There is no special reason. We consider 3 pixels per clock tick a good velocity. You may not. Play with this number and see what happens with the animation.
; properties of the "world" and the descending rocket (define WIDTH 100) (define HEIGHT 60) (define V 3) (define X 50) ; graphical constants (define MTSCN (empty-scene WIDTH HEIGHT)) (define ROCKET )
(define ROCKET-CENTER-TO-TOP (- HEIGHT (/ (image-height ROCKET) 2))) ; functions (define (picture-of-rocket.v6 t) (cond [(<= (distance t) ROCKET-CENTER-TO-TOP) (place-image ROCKET X (distance t) MTSCN)] [(> (distance t) ROCKET-CENTER-TO-TOP) (place-image ROCKET X ROCKET-CENTER-TO-TOP MTSCN)])) (define (distance t) (* V t))
> (animate picture-of-rocket.v6)
In comparison to the previous versions of picture-of-rocket,
this one shows that a program may consist of several function
definitions that refer to each other. Then again, even the first
version used + and /—
As you become a true-blue programmer, you will find out that
programs consist of many function definitions and many constant
definitions. You will also see that functions refer to each other
all the time. What you really need to practice is to organize them
so that you can read them easily, even months after completion.
After all, an older version of you—
You Are a Programmer Now
The claim that you are a programmer may have come as a surprise to you at the end of the preceding section, but it is true. You know all the mechanics that there are to know about BSL. You know that programming uses the arithmetic of numbers, strings, images, and whatever other data your chosen programming languages support. You know that programs consist of function and constant definitions. You know, because we have told you, that in the end, it’s all about organizing these definitions properly. Last but not least, you know that DrRacket and the teachpacks support lots of other functions and that DrRacket’s HelpDesk explains what these functions do.
You might think that you still don’t know enough to write programs that react to keystrokes, mouse clicks, and so on. As it turns out, you do. In addition to the animate function, the 2htdp/universe library provides other functions that hook up your programs to the keyboard, the mouse, the clock, and other moving parts in your computer. Indeed, it even supports writing programs that connect your computer with anybody else’s computer around the world. So this isn’t really a problem.
In short, you have seen almost all the mechanics of putting together programs. If you read up on all the functions that are available, you can write programs that play interesting computer games, run simulations, or keep track of business accounts. The question is whether this really means you are a programmer. Are you?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Not!
When you look at the “programming” bookshelves in a random bookstore, you will see loads of books that promise to turn you into a programmer on the spot. Now that you have worked your way through some first examples, however, you probably realize that this cannot possibly happen.
Acquiring the mechanical skills of programming—
Good programming is far more than the mechanics of acquiring a
language. Most importantly, it is about keeping in mind that
programmers create programs for other people to read them in the
future. A good program reflects the problem statements and its
important concepts. It comes with a concise self-description.
Examples illustrate this description and relate it back to the
problem. The examples make sure that the future reader knows why and
how your code works. In short, good programming is about solving
problems systematically and conveying the system within the code.
Best of all, this approach to programming actually makes programming
accessible to everyone—
The rest of this book is all about these things; very little of the book’s content is actually about the mechanics of DrRacket, BSL, or libraries. The book shows you how good programmers think about problems. And, you will even learn that this way of solving problems applies to other situations in life, such as the work of doctors, journalists, lawyers, and engineers.
Oh, and by the way, the rest of the book uses a tone that is more appropriate for a serious text than this Prologue. Enjoy!
Note on What This Book Is Not About Many introductory books on programming contain a lot of material about the authors’ favorite application discipline for programming: puzzles, mathematics, physics, music, and so on. To some extent, including such material is natural because programming is obviously useful in all these areas. Then again, this kind of material distracts from proper programming and usually fails to tease apart the incidental from the essential. Hence this book focuses on programming and problem solving and what computer science can teach you in this regard. We have made every attempt to minimize the use of knowledge from other areas; for those few occasions when we went too far, we apologize.