On this page:
Arithmetic And Arithmetic
Inputs And Output
Many Ways To Compute
One Program, Many Definitions
One More Definition
You Are a Programmer Now
Not!

Вступление: умение программировать

Когда ты был дитям, твои родители наверняка учили тебя считать, а позже — считать "на пальцах": "1 + 1 будет 2"; "1 + 2 будет 3"; и так далее. Потом они спрашивали "сколько будет 3 + 2?", и ты считал пальцы на руке. Они программировали, а ты вычислял.

Запусти DrRacket и выбери “Beginning Student Language” ("Начинающий студент") из субменю “How to Design Programs” в меню “Language” (Язык).

Теперь роли меняются. Ты программируешь, а компьютер — твой ребёнок. Конечно, ты начинаешь с самых простых вычислений. Ты набираешь:

(+ 1 1)

в верхнюю часть DrRacket, нажимаешь RUN (ПУСК), и результат появляется в нижней части: 2

Вот и всё! Ты задаёшь вопросы, будто DrRacket -- ребёнок, и он делает вычисления за тебя. Люди часто говорят: "компьютер вычисляет за тебя", но в реальности компьютер сам по себе абсолютно бесполезен для большинства людей. Именно программы делают компьютеры полезными, просто к фразе "программы вычисляют за тебя" сложно привыкнуть.” Ты также можешь попросить DrRacket вычислить сразу несколько запросов:
(+ 2 2)
(* 3 3)
(- 4 2)
(/ 6 2)
После нажатия RUN (ПУСК), ты увидишь 4 9 2 3 внизу окна DrRacket, это и есть правильные ответы. Это значит, что DrRacket многое знает об арифметике, намного больше, чем маленький ребёнок. Более того, DrRacket умеет возводить в квадрат, вычислять экспоненту, и даже кое-что из тригонометрии:
(sqr 3)
(expt 2 3)
(sin 0)
(cos pi)
Если теперь нажать "RUN (ПУСК)", нижняя часть окна DrRacket отобразит четыре числа: 9 8 0 -1.0 Просмотрись к последнему числу. Его префикс “#i” -- сокращение для "я не знаю точной цифры, потому пока покажу вот это". В отличии от твоего калькулятора или большинства других сред программирования, DrRacket невероятно честен. Если он не знает точный ответ -- он предупредит тебя об этом при помощи специального префикса. Позже мы покажем тебе действительно странные факты о "компьютерных числах", и ты будешь по-настоящему благодарен предупреждениям DrRacket.

1 Терминология

Сейчас мы остановимся ненадолго для того, чтоб ввести несколько новых понятий:
  • Вернхяя половина DrRacket называется областью определений. В этой области ты будешь создавать программы, этот процесс называется редактированием. Как только ты что-либо меняешь в области определений, кнопка SAVE (СОХРАНИТЬ) тут же появится в верхнем правом углу. Когда ты нажмёшь SAVE (СОХРАНИТЬ) впервые, DrRacket попросит тебя ввести название файла, то есть указать место, где будет находиться твоя программа на компьютере (технически говоря, на диске). Однажды сохранив, при каждом следующем нажатии на SAVE (СОХРАНИТЬ) DrRacket будет убеждаться, что содержимое области определений безопасно сохранено в файле.

  • Программы состоят из выражений. Пока что, будем считать, что программы являются выражениями.

    Ты видел выражения в математике. Пока что будем считать, что выражение -- это либо число, либо что-то, начинающееся с открывающейся круглой скобки “(” и заканчивающееся закрывающейся круглой скобкой “)”— DrRacket будет вознаграждать тебя за закрытие скобок, отмечая выражение серым.

  • Если ты нажмёшь RUN (ЗАПУСК), DrRacket вычислит выражения одно за другим и отобразит результат в области взаимодействия. Затем, DrRacket, твой верный слуга, ждёт твоих команд в приглашении снизу и ты можешь вводить дополнительные выражения, несмотря на то, что они исчезнут при следующем нажатии RUN (ЗАПУСК):

    > (+ 1 1)

    2

    Введи свою первую программу в приглашении (строка, начинающаяся с ">"), нажми "Enter" (Ввод) и смотри, как DrRacket отвечает тебе результатом. Ты можешь делать это так часто, как тебе того хочется:

    > (+ 2 2)

    4

    > (* 3 3)

    9

    > (- 4 2)

    2

    > (/ 6 2)

    3

    > (sqr 3)

    9

    > (expt 2 3)

    8

    > (sin 0)

    0

    > (cos pi)

    #i#i#i#i#i#i#i#i#i#i#i#i#i#i#i#i#i#i#i#i#i#i#i#i#i#i#i#i-1.0

Достаточно запоминаний.

Возможно тебя интересует, может ли DrRacket складывать больше двух чисел одновременно, и да, он может! Более того, для этого есть два разных способа:
> (+ 2 (+ 3 4))

9

> (+ 2 3 4)

9

Первый из них -- вложенная арифметика, знакомая тебе со школы. Второй метод -- арифметика Racket и это будет естественно, если ты всегда будешь использовать скобки для группирования операции и чисел. Пришло время обсудить нашу нотацию— Язык Начинающего Студента (Beginning Student Language) или просто BSL.

Возможно ты считаешь, что учишь язык Racket потому, что программа называется DrRacket ("Доктор Racket"). Это не так. Ты учишь целую серию обучающих языков, созданных для этой книги, начиная с BSL. Но, как только ты освоишь эти языки, ты можешь быстро переключиться и программировать на огромном количестве разных языков, включая Racket, а также JavaScript, Python, Ruby, и многие другие скриптовые языки.

В BSL, каждый раз, когда ты хочешь вычислить что-то подобно калькулятору, тебе необходима открывающая скобка, за ней операция, затем числа, к которым необходимо применить операцию (разделённые пробелами или даже переносами строк), и закрывающая скобка. Элементы операции называются операндами. Вложенная арифметика означает, что ты можешь использовать выражение в качестве операнда, именно поэтому
> (+ 2 (+ 3 4))

9

-- вполне правильная программа. Делай так сколько угодно:
> (+ 2 (+ (* 3 3) 4))

15

> (+ 2 (+ (* 3 (/ 12 4)) 4))

15

> (+ (* 5 5) (+ (* 3 (/ 12 4)) 4))

38

Количеству вложений нет предела, разве что твоё терпение.

Естественно, при вычислениях DrRacket использует правила, известные тебе и любимые тобой со школы. Также как и ты, DrRacket может определить результат сложения только после того, как все операнды являются обычными числами. Если операнд является выражением в скобках—чем-то, что начинается с “(” и операции—сначала он определяет результат вложенного выражения. Unlike you, it never needs to ponder with which expression to calculate first—because this first rule is the only rule there is to it.

Ценой за уверенность DrRacket является тот факт, что скобки имеют значение. Ты, программист, должен ввести все эти скобки, и должен ввести их не слишком много. В то время как твой учитель может пропустить пару лишних скобок, в случае с BSL ты получишь ошибку. Выражение (+ (1) (2)) содержит слишком много скобок, и DrRacket без сомнений сообщит тебе об этом:
> (+ (1) (2))

function call: expected a function after the open parenthesis, but found a number
(вызов функции: после открытой скобки ожидалась функция, но было найдено число)

Тем не менее, как только ты привыкнешь к программированию в BSL, ты поймёшь, что это вовсе не большая цена. Во-первых, делай операции сразу над несколькими операндами, если находишь это естественным: Или помести курсор рядом с операцией и нажми F1. Это действие откроет Справку DrRacket (DrRacket's HelpDesk) и найдёт документацию к операции. Пользуйся результатами, относящимися к Учебным Языкам HtDP (HtDP teaching languages). Как ты уже мог заметить, этот текст тоже ссылается на документацию в Справку (HelpDesk).
> (+ 1 2 3 4 5 6 7 8 9 0)

45

> (* 1 2 3 4 5 6 7 8 9 0)

0

Если не знаешь, что именно делает операция для нескольких операндов -- введи пример в область взаимодействий и нажми "Enter"; DrRacket покажет тебе, как работает операция. Во-вторых, клшжа ты будешь читать чужие программы—а этим ты будешь заниматься примерно половину своей карьеры программиста—тебе никогда не придётся гадать, какое выражение выполняется первым. Скобки и вложения моментально сообщат тебе.

В этом контексте, программирование -- это написание понятных арифметических выражений, а также вычисление их значений. С помощью DrRacket очень просто изучать программирование и вычисления в этом роде.

Arithmetic And Arithmetic

Если бы программирование было бы только о числах и арифметике, оно было бы таким же скучным, как и математика. Шутка! Математика -- интереснейшая наука, но тебе это и так известно. Сейчас тебе потребуется совсем немного математики, но для того, чтобы стать действительно хорошим программистом -- тебе придётся, всё же, познакомиться с ней поближе. К счастью, в программировании есть много интересного по мимо чисел: текст, правда/ложь, изображения и многое другое.

Вот три программы, работающих с текстом:
(string-append "hello" "world")
(string-append "hello " "world")
(string-append "hell" "o world")
После нажатия RUN (ЗАПУСК), DrRacket отобразит такой результат: "helloworld" "hello world" "hello world"

Чтобы понять, что же именно пошло не так, для начала тебе нужно понять, что в BSL текст -- это любая последовательность клавиатурных символов, обёрнутых в двойные кавычки ("). Технически говоря, это называется строкой. Так, "hello world" является вполне нормальной строкой, и когда DrRacket выполняет эту строку, он отображает её в области взаимодействий, так же как он делал с числами. Более того, для большинства людей первой программой является программа, выводящая на экран слова “hello” ("здравствуй") и “world” ("мир")—вообще-то ты уже написал три таких, но самой простой из них была бы та, которая состояла из одной только строки:

"hello world"

Нажми RUN и восхитись выводом программы.

Также, тебе необходимо знать, что в добавок к арифметике над числами, DrRacket также знает об арифметике над строками. Так, string-append является операцией сложения для строк, также как для чисел операция +; он создаёт строку соединяя вторую строку с концом первой. Как показала первая линия, он делает это буквально, не вставляя ничего между двумя строками: ни пробела, ни запятой, ничего. Таким образом, если ты хочешь видеть фразу "hello world", тебе на самом деле необходимо добавить пробел к одной из фраз где-нибудь; это видно на второй и третей линии. Конечно, самым естественным способом создать эту фразу из двух слов -- ввести

(string-append "hello" " " "world")

поскольку string-append, как и +, умеет работать сразу над несколькими операндами.

Ты можешь делать со строками больше, чем просто соединять их. Ты можешь извлекать из них подстроки, переворачивать задом-наперёд, писать все буквы в верхнем регистре (или в нижнем), вырезать пробелы слева или справа и так далее. И самое главное: тебе не нужно этого всего запоминать. Если тебе интересно, что ты можешь делать со строками, посмотри в HelpDesk. Используй F1 или выпадающее меню справа в HelpDesk, ищи языки HtDP (BSL) в справочных пособиях (manuals) и их секции в описаниях примитивов (primitives). Там перечислины все операции в BSL, особенно те, что работают со строками.

Если ты всё же пошёл и посмотрел на список строковых операций BSL, ты мог увидеть примитивную (primitive) (также называемую встроенной (built-in)) операцию, которая может принимать строки и производить числа. То есть, ты можешь вычислить длинну строки, и прибавить к ней 20:
> (+ (string-length "hello world") 20)

31

и DrRacket вычислит это выражение точно так же, как и любое другое. То есть, арифметическое выражение не должно содержать только числа или только строки. Многие смешивают и то и другое по мере необходимости. Есть операции, конвертирующие строки в числа и числа в строки, результат вполне очевиден:
> (number->string 42)

"42"

> (string->number "42")

42

Если ты ожидаешь результат “forty-two” ("сорок-два") или что-то вроде того -- извини, но это немного не то, чего в основном хотят от строкового калькулятора.

Тем не менее, второе выражение вызывает вопрос. Что если string->number будет использован со строкой, не состоящей только из цифер? В этом случае операция производит совершенно другой результат:
> (string->number "hello world")

false

Это ни число ни строка; это Булев (Boolean). В отличии от чисел и строк, Булевые (Boolean) значения принимают всего два состояния: true (правда) и false (ложь). Первый означает правдивый ответ, а второй -- ложный. У DrRacket есть несколько операций для объединения Boolean'ов:
> (and true true)

true

> (and true false)

false

> (or true false)

true

> (or false false)

false

> (not false)

true

, и ты получаешь результаты, соответствующие операции, которую ты вызвал (and -- И, or -- ИЛИ, not -- НЕ). (Не знаешь что возвращает and, or, и not? Проще простого: (and x y) возвращает true если x и y имеют значение true; (or x y) возвращает true если либо x либо y либо оба являются true; и (not x) возвращает true только тогда, когда x имеет значение false.)

Несмотря на то, что конвертировать одно число в Boolean невозможно, определённо полезно “конвертировать” в Boolean сразу два числа:
(> 10 9)
(< -1 0)
(= 42 9)
Прежде чем продолжить, попробуй угадать результаты:

true

true

false

Теперь попробуй вот это: (>= 10 10), (<= -1 0), и (string=? "design" "tinker"). Последнее выражение абсолютно отличается от предыдущих, но не переживай, оно абсолютно нормальное.

Со всеми этими новыми типами данных—да, числа, строки и Boolean'ы являются данными—и операциями над ними очень просто забыть некоторые азы, такие как вложенная арифметика:
(and (or (= (string-length "hello world") (string->number "11"))
         (string=? "hello world" "good morning"))
     (>= (+ (string-length "hello world") 60) 80))
Каков результат этого выражения? Как ты догадался? Сам? Или ты ввёл его в панель взаимодействий DrRacket и нажал "Энтер"? Если второе, как думаешь, ты бы мог вычислить ответ в одиночку? В конце концов, если ты не можешь предугадать, что DrRacket сделает с маленькими выражениями, наверное тебе не захочется ему доверять выполнение гораздо больших задач, чем эта.

Прежде, чем ты узнаешь как заниматься “настоящим” программированием, давай, ради интереса, обсудим еще один тип данных: изображения. Для вставки изображений вроде этой ракеты в DrRacket, используй меню Insert (Вставить) и выбери пункт “Insert image ... (Вставить изображение)”. Или, если ты читаешь эту книгу в он-лайне, скопируй и вставь изображение из своего браузера прямо в DrRacket. Когда ты вставляешь изображение в панель взаимодействий и нажимаешь Энтер, вот так:

> 

DrRacket отвечает изображением. В отличии от многих языков программирования, BSL понимает изображения, и поддерживает арифметическу над ними точно так же, как над числами или строками. Если коротко -- твои программы могут делать вычисления с изображениями, ты можешь делать это в области взаимодействий. Идя дальше, программисты на BSL—как и программисты для других языков—создали библиотеки, которые могут быть полезны другим. Использовать такие библиотеки -- всё равно, что совершенствовать свой словарный запас новыми словами, или свой программный словарь новыми примитивами. Мы называем подобные библиотеки teachpacks (учебными пакетами) поскольку они поманают в обучении.

(require 2htdp/image) указывает, что ты хочешь добавить библиотеку "image" . Либо же, воспользуйся элементом выпадающего меню “Language (Язык)”, выбери “Add Teachpack ... (Добавить учебный пакет)” и выбери "image". Одна важная библиотека—"image"поддерживает операции для вычисления ширины (width) и высоты (height) изображения:
(* (image-width )
   (image-height ))
После добавления библиотеки в программу, нажатие RUN даёт тебе 1176, поскольку это площадь изображения размером 28 на 42.

Тебе не нужно пользоваться Гуглом для того, чтобы искать изображения и вставлять их в свои программы в DrRacket при помощи меню “Insert”. Ты также можешь попросить DrRacket создать тебе простые изображения с нуля:
> (circle 10 "solid" "red")

image

> (rectangle 30 20 "outline" "blue")

image

Самое лучшее состоит в том, что DrRacket не просто рисует изображения, а что он действительно работает с ними как со значениями, точно как с числами. Поэтому естественно, что в BSL есть операции для объединения изображений, также как и операции для объединения чисел или соединения строк (перев.: overlay -- накладывать, перекрывать):
> (overlay (circle 5 "solid" "red")
           (rectangle 20 20 "solid" "blue"))

image

Перекрывая те же изображения в обратном порядке возвращает сплошной синий квадрат:
> (overlay (rectangle 20 20 "solid" "blue")
           (circle 5 "solid" "red"))

image

Остановись и подумай немного над последним результатом.

Как ты можешь увидеть, overlay больше напоминает string-append, нежели +, но он, всё же, “складывает” изображения так же, как string-append “складывает” строки и как + складывает числа. Вот еще одна иллюстрация этой идеи:
> (image-width (square 10 "solid" "red"))

10

> (image-width
    (overlay (rectangle 20 20 "solid" "blue")
             (circle 5 "solid" "red")))

20

Эти взаимодействия с DrRacket ничего не рисуют, они всего лишь вычисляют ширину изображений.

Ты должен знать об еще двух операциях: empty-scene и place-image. Первый из них создаёт сцену, специальный вид прямоугольника. Второй помещает изображение на такую сцену:
(place-image (circle 5 "solid" "green")
             50 80
             (empty-scene 100 100))
and you get this: Not quite. The image comes without a grid. We superimpose the grid on the empty scene so that you can see where exactly the green dot is placed.

As you can see from this image, the origin (or (0,0)) is in the upper-left corner. Unlike in mathematics, the y coordinate is measured downwards, not upwards. Otherwise, the image shows what you should have expected: a solid green disk at the coordinates (50,80) in a 100 by 100 empty rectangle.

Let’s summarize again. To program is to write down an arithmetic expression, but you’re no longer restricted to boring numbers. With BSL, your arithmetic is the arithmetic of numbers, strings, Boolean values, and even images. To compute though still means to determine the value of the expressions(s) except that this value can be a string, a number, a Boolean, or an image.

And now you’re basically 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. Remember those tables that your teachers would show you? Here is one more:

x =

1

2

3

4

5

6

7

8

9

10

y =

1

4

9

16

25

36

49

64

81

?

Your teachers would now ask you to fill in the blank, i.e., replace the “?” mark with a number.

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

5

6

y =

?

To be concrete, your teacher should ask you here to draw the sixth image, the seventh, and the 1273rd one because a movie is just a lot of images, some 20 or 30 of them per second. So you need some 1200 to 1800 of them to make one minute’s worth of it.

You may also recall that your teacher not only asked for the fifth, sixth, or seventh number in some sequence but also for an expression that determines any element of the sequence from a given x. In the numeric example, the teacher wants to see something like this:Write x2 if you want to be fancy.

If you plug in 1, 2, 3, and so on for x, you get 1, 4, 9, and so on for yjust as the table says. For the sequence of images, you could say something like

y = the image that contains a dot x2 pixels below the top.

The key is that these one-liners are not just expressions but functions.

At first glance functions are like expressions, always with a y on the left, followed by an = sign, and an expression. They aren’t expressions, however. And the notation you (usually) learn in school for functions is utterly misleading. In DrRacket, you therefore write functions a bit differently:

(define (y x) (* x x))

The define says “consider y a function”, which like an expression, computes a value. A function’s value, though, depends on the value of something called the input, which we express with (y x). Since we don’t know what this input is, we use a name to represent the input. Following the mathematical tradition, we use x here to stand in for the unknown input but pretty soon, we shall use all kinds of names.

This second part means you must supply one value—a number—for x to determine a specific value for y. When you do, DrRacket plugs in the value for x into the expression associated with the function. Here the expression is (* x x). Once x is replaced with a value, say 1, DrRacket can compute the result of the expressions, which is also called the output of the function.

Click RUN and watch nothing happen. Nothing shows up in the interactions area. Nothing seems to change anywhere else in DrRacket. It is as if you hadn’t accomplished anything. But you did. You actually defined a function and informed DrRacket about its existence. As a matter of fact, the latter is now ready for you to use the function. Enter

(y 1)

at the prompt in the interactions area and watch a 1 appear in response. The (y 1) is called a function application in DrRacket.... and in mathematics, too. Your teachers just forgot to tell you. Try

(y 2)

and see a 4 pop out. Of course, you can also enter all these expressions in the definitions area and click RUN:
(define (y x) (* x x))
 
(y 1)
(y 2)
(y 3)
(y 4)
(y 5)
In response, DrRacket displays: 1 4 9 16 25, which are the numbers from the table. Now determine the missing entry.

What all this means for you is that functions provide a rather economic way of computing lots of interesting values with a single expression. Indeed, programs are functions, and once you understand functions well, you know almost everything there is about programming. Given their importance, let’s recap what we know about functions so far:
  • First,

    (define (FunctionName InputName) BodyExpression)

    is a function definition. You recognize it as such, because it starts with the “define” keyword. A function definition 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—most programmers call it a parameterrepresents 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. As seen, the expression involves the parameter, and it may also consist of many other expressions.

  • 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 wish to apply the function. If you were reading a Windows or Mac manual, it might tell you that this expression “launches” the (software) “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 (number, string, image, Boolean) or a complex, deeply nested expression.

Functions can input more than numbers, and they can output all kinds of data, too. Our next task is to create a function that simulates the second table—the one with images of a gray dot—just like the first function simulated the numeric table. Since the creation of images from expressions isn’t something you know from high school, let’s start simply. Do you remember empty-scene? We quickly mentioned it at the end of the previous section. When you type it into the definitions area, like that:

(empty-scene 100 100)

clicking RUN produces an empty rectangle, also called a scene:

You can add images to a scene with place-image:

(place-image  50 0 (empty-scene 100 100))

produces an scene with a rocket hovering near the center of the top:

Think of the rocket as an object that is like the disk—though more interesting—in the above table from your mathematics class.

Next you should make the rocket descend, just like the disk in the above table. From the preceding section you know how to achieve this effect by increasing the y coordinate that is supplied to place-image:
(place-image  50 10 (empty-scene 100 100))
(place-image  50 20 (empty-scene 100 100))
(place-image  50 30 (empty-scene 100 100))
Clicking RUN yields three scenes:

All that’s needed now is to produce lots of these scenes easily and to display all of them in rapid order.

The first goal can be achieved with a function of course:
(define (create-rocket-scene height)
  (place-image  50 height (empty-scene 100 100)))
Yes, this is a function definition. Instead of y, it uses the name create-rocket-scene, a name that immediately tells you what the function outputs: a scene with a rocket. Instead of x, the function definition uses height for the name of its parameter, a name that suggests that it is a number and that it tells the function where to place the rocket. The body expression of the function is just like the series of expressions with which we just experimented, except that it uses height in place of a number. And we can easily create all of those images with this one function:
(create-rocket-scene 0)
(create-rocket-scene 10)
(create-rocket-scene 20)
(create-rocket-scene 30)
Try this out in the definitions area or the interactions area, both create the expected scenes.

(require 2htdp/universe) The second goal requires knowledge about one additional primitive operation from the "universe" library: animate. So, add the require line to the definitions area, click RUN, and enter the following expression:

> (animate create-rocket-scene)

Stop for a moment and note that the argument expression is a function. Don’t worry for now about using functions as arguments; it works well with animate but don’t try this at home yet.

As soon as you hit the “return” key, DrRacket evaluates the expression but it does not display a result, not even an interactions prompt (for a while). It open another window—a canvasand starts a clock that ticks 28 times per second. Every time the clock ticks, DrRacket applies create-rocket-scene to the number of ticks passed since this function call. The results of these function calls are displayed in the canvas, and it produces the effect of an animated movie. The simulation runs until you click STOP or close the window. At that point, animate returns the number of ticks that have passed.

The question is where the images on the window come from. The short explanation is that animate runs its operand on the numbers 0, 1, 2, etc. and displays the resulting images. The long explanation is this:
  • animate starts a clock, and animate counts the number of clock ticks;

  • the clock ticks 28 times per second;

  • every time the clock ticks, animate applies the function create-rocket-scene to the current clock tick; and

  • the scene that this application creates is displayed on the screen.

This means that the rocket first appears at height 1, then 2, then 3, etc., which explains why the rocket descends from the top of the screen to the bottom. That is, our three-line program creates some 100 pictures in about 3.5 seconds, and displaying these pictures rapidly creates the effect of a rocket descending to the ground.

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 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 just functions.

Many Ways To Compute

When you run the create-rocket-scene program from the preceding section, the rocket eventually disappears in 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 create-rocket-scene program should work “as is” while the rocket is in-flight. When the rocket’s bottom touches the bottom of the screen, however, it should stop the rocket from descending any further.

In a sense, the idea shouldn’t be new to you. Even your mathematics teachers define functions that distinguish various situations:

This sign distinguishes three kinds of inputs: those numbers larger than 0, those equal to 0, and those smaller than 0. Depending on the input, the result of the function—or output as we may occasionally call it—is +1, 0, or -1.

Open a new tab in DrRacket and start with a clean slate.

You can define this function in DrRacket without much ado using a conditional expression:
(define (sign x)
  (cond
    [(> x 0) 1]
    [(= x 0)  0]
    [(< x 0) -1]))
 
(sign 10)
(sign -5)
(sign 0)
To illustrate how sign works, we added three applications of the function to the definitions area. If you click RUN, you see 1, -1, and 0.

In general, a conditional expression has the shape
(cond
  [ConditionExpression1 ResultExpression1]
  [ConditionExpression2 ResultExpression2]
  ....
  [ConditionexpressionN ResultExpressionN])
That is, a conditional expressions consists of many conditional lines. Each line contains two expressions: the left one is often called condition and the right one is called result. This is a good time to explore what the STEP button does. Click STEP for the above sign program. When the new window comes up, click the right and left arrows there. To evaluate a cond expression, DrRacket evaluates the first condition expression, ConditionExpression. If the evaluation of ConditionExpression1 yields true, DrRacket replaces the cond expression with the first result expression (ResultExpression1) and evaluates it. Whatever value DrRacket obtains is the result of the entire cond expression. If the evaluation of ConditionExpression1 yields false, DrRacket drops the first line and moves on to the second line, which is treated just like the first one. In case all condition expressions evaluate to false, DrRacket signals an error.

(define (create-rocket-scene.v2 height)
  (cond
    [(<= height 100)
     (place-image  50 height (empty-scene 100 100))]
    [(> height 100)
     (place-image  50 100 (empty-scene 100 100))]))

Figure 1: Landing a rocket (version 2)

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 100 scene. Since the create-rocket-scene function consumes the height at which it is to place the rocket in the scene, a simple test comparing the given height to the maximum height appears to suffice.

See figure 1 for the revised function definition. In BSL, you can really use all kinds of characters in function names, including “.” or “-” which you have already seen. We call the revised function fig:create-rocket-scene.v2 to distinguish it from the original version. Doing so also allows us to use both functions in the interactions area of DrRacket and to compare their results:
> (create-rocket-scene 5555)

image

> (create-rocket-scene.v2 5555)

image

No matter what number over 100 you give to create-rocket-scene.v2, you get the same scene. In particular, when you run the simulation

> (animate create-rocket-scene.v2)

the rocket descends, sinks half way into the ground, and finally comes to a halt.

Landing the rocket half-way under ground is ugly. Then again, you basically know how to fix this aspect of the program. As you learned from the preceding sections, DrRacket knows an arithmetic of images. Images have a center point and, when place-image adds an image to a scene, it uses this center point as if it were the image. This explains why the rocket is half way under ground at the end: DrRacket thinks of the image as if it were a point, but 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, which is to images like + is to numbers. This function comes in handy here because you really want to fly the rocket only until it bottom touches the ground.

Putting one and one together—also known as playing around—you can now figure out that

(- 100 (/ (image-height ) 2))

is the point at which you want the rocket to stop its descent. Hint: you could figure this out by playing with the program directly. Or you can experiment in the interactions area with your image arithmetic. Enter this expression, which is one natural guess:
(place-image  50 (- 100 (image-height ))
             (empty-scene 100 100))
and then this one:
(place-image  50 (- 100 (/ (image-height ) 2))
             (empty-scene 100 100))
Which result do you like better?

(define (create-rocket-scene.v3 height)
  (cond
    [(<= height (- 100 (/ (image-height ) 2)))
     (place-image  50 height (empty-scene 100 100))]
    [(> height (- 100 (/ (image-height ) 2)))
     (place-image  50 (- 100 (/ (image-height ) 2))
                  (empty-scene 100 100))]))

Figure 2: Landing a rocket (version 3)

When you think and experiment along these lines, you eventually get to the program in figure 2. 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 imagine this. Your manager at your hot game company doesn’t like flight simulations where rockets that sink halfway into the ground. Worse, all of a sudden, this manager requests a version of the game that uses 200 by 400 scenes. This simple request forces you to replace 100 with 400 in five places in the program, which includes the animate line, and to replace 100 with 200 in three other places. 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 real programs consists of 50,000 or 500,000 or 5,000,000 lines of code.

In the ideal program, a small request, such as changing the sizes of the canvas, should require an equally small change. The tool to achieve this simplicity with BSL is define. In addition to defining functions, you can also introduce constant definitions, which assign some name to a constant. The general shape of a constant definition is straightforward:

(define Name Expression)

Thus, for example, if you write down

(define HEIGHT 100)

in your program, you are saying that HEIGHT always represents the number 100. The meaning of such a definition is what you expect. Whenever DrRacket encounters HEIGHT during its calculations, it uses 100 instead. Of course, you can also add

(define WIDTH 100)

to your program to have one single place where you specify the width of the scene.

(define (create-rocket-scene.v4 h)
  (cond
    [(<= h (- HEIGHT (/ (image-height ) 2)))
     (place-image  50 h (empty-scene WIDTH HEIGHT))]
    [(> h (- HEIGHT (/ (image-height ) 2)))
     (place-image  50 (- HEIGHT (/ (image-height ) 2))
                  (empty-scene WIDTH HEIGHT))]))
 
(define WIDTH 100)
(define HEIGHT 100)

Figure 3: Landing a rocket (version 4)

Now take a look at the code in figure 3, which implements this simple change. Copy the program into DrRacket, and after clicking RUN, evaluate the following interaction:

> (animate create-rocket-scene.v4)

Confirm that the program still functions as before.

The program in figure 3 consists of three definitions: one function definition and two constant definitions. The number 100 occurs only twice: once as the value of WIDTH and once as the value of HEIGHT. The last line starts the simulation, just as in version 3 of the program. You may also have noticed that it uses h instead of height for the function parameter of create-rocket-scene.v4. Strictly speaking, this change isn’t necessary because DrRacket doesn’t confuse height with HEIGHT but we did it to avoid confusing you.

When DrRacket evaluates (animate create-rocket-scene.v4), it replaces HEIGHT with 100 and WIDTH with 100 every time it encounters these names. To experience the joys of real programmers, change the 100 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.

In modern parlance, you have just experienced your first program refactoring. Every time you re-organize your program to prepare yourself for likely future change requests, you refactor your program. Put it on your resume. It sounds good, and your future employer probably enjoys reading such buzzwords, even if it doesn’t make you a good programmer. What a good programmer would never live with, however, is that the program contains the same expression three times:

(- HEIGHT (/ (image-height ) 2))

Every time your friends and colleagues read this program, they need to understand what this expression computes, namely, the distance between the bottom of the screen and the center point of a rocket resting on the ground. Every time DrRacket computes the value of the expressions it has to perform three arithmetic operations: (1) determine the height of the image; (2) divide it by 2; and (3) subtract the result from 100. And every time it comes up with the same number.

This observation calls for the introduction of one more definition to your program:
(define ROCKET-CENTER-TO-BOTTOM
  (- HEIGHT (/ (image-height ) 2)))
plus the replacement of every other occurrence of (- HEIGHT (/ (image-height ) 2)) with ROCKET-CENTER-TO-BOTTOM. You might wonder whether the new definition should be placed above or below the definition for HEIGHT. More generally, you should be wondering whether the ordering of definitions matters. The answer is that for constant definitions, the order matters, and for function definitions it doesn’t. As soon as DrRacket encounters a constant definition, it determines the value of the expression and then associates the name with this value. Thus, for example,
(define HEIGHT (* 2 CENTER))
(define CENTER 100)
is meaningless because DrRacket hasn’t seen the definition for CENTER yet when it encounters the definition for HEIGHT. In contrast,
(define CENTER 100)
(define HEIGHT (* 2 CENTER))
works as expected. First, DrRacket associates CENTER with 100. Second, it evaluates (* 2 CENTER), which yields 200. Finally, DrRacket associates 200 with HEIGHT.

While the order of constant definitions matters, it doesn’t matter whether you first define constants and then functions or vice versa. Indeed, if your program consisted of more than one function, it wouldn’t matter in which order you defined those. For pragmatic reasons, it is good to introduce all constant definitions first, followed by the definitions of important functions, with the less important ones bringing up the rear guard. When you start writing your own multi-definition programs, you will soon see why this ordering is a good idea.

; constants
(define WIDTH  100)
(define HEIGHT 100)
(define MTSCN  (empty-scene WIDTH HEIGHT))
(define ROCKET )
(define ROCKET-CENTER-TO-BOTTOM
  (- HEIGHT (/ (image-height ROCKET) 2)))
 
; functions
(define (create-rocket-scene.v5 h)
  (cond
    [(<= h ROCKET-CENTER-TO-BOTTOM)
     (place-image ROCKET 50 h MTSCN)]
    [(> h ROCKET-CENTER-TO-BOTTOM)
     (place-image ROCKET 50 ROCKET-CENTER-TO-BOTTOM MTSCN)]))

Figure 4: Landing a rocket (version 5)

The program also contains two line comments, introduced with semi-colons (“;”). 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 4. 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.

Before you read on, ponder the following changes to your program:
  • 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 per se 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.

Better than pondering is doing. It’s the only way to learn. So don’t let us stop you. Just do it.

2 Magic Numbers

Take another look at the definition of create-rocket-scene.v5. As 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 whimsical choice for an x coordinate for the rocket. Even though 50 doesn’t look like much of an expression, it actually is a repeated expression, too. In other words, we have two reasons to eliminate 50 from the function definition, and we leave it to you to do so.

One More Definition

Danger ahead! This section introduces one piece of knowledge from physics. If physics scares you, skip this section on a first reading; programming doesn’t require physics knowledge. Real rockets don’t descend at a constant speed. Real cars don’t stop on the spot. They decelerate, which is the opposite of accelerate. What this really means is that an object first travels at a constant speed and then the driver hits the brakes or the pilot fires some engines. The effect of using the brakes or firing the engines is to change the speed slowly—decelerate in fancy English.

To understand how this process works precisely, you must think back to your physics courses. If you haven’t because you’re too young or if you can’t remember because you’re too old or if you fell asleep in the course, you’ll need to look in a physics book. Don’t worry. This happens to programmers all the time because they need to help people in music, physics, mechanical engineering, economics, photography, and all kinds of other disciplines and, obviously, not even programmers know everything. So they look it up. Or they are told what deceleration means in terms of distance traveled:

That is, if v is the speed of the object and a is the deceleration—change of speed—then the object travels d miles (or meters or pixels or whatever) in t seconds.

By now you know that a good teacher would have shown you a proper function definition:

because this tells everyone immediately that the computation of d depends on t and that v and a are constants. A programmer goes even further and uses meaningful names for these one-letter abbreviations:
(define VELOCITY 20)
(define DECELERATION 1)
 
(define (distance t)
  (- (*  VELOCITY t) (* 1/2 DECELERATION (sqr t))))
This program consists of three definitions: a function that computes the distance traveled by a decelerating object; its velocity or speed for you; and its change in speed or deceleration. The distance function uses a primitive operator called sqr; if you can’t figure out what it does, play with it in the interactions area or look it up in HelpDesk.

The next and only other thing you need to know is that animate actually applies its functions to the number of clock ticks that have passed since it was first called and not the height of the rocket image. From this revelation it immediately follows that our five versions of create-rocket-scene have thus far used the wrong name for the input. Clearly, tshort for time—would be much better than h, which is short for height:
(define (create-rocket-scene t)
  (cond
    [(<= t ROCKET-CENTER-TO-BOTTOM)
     (place-image ROCKET X t MTSCN)]
    [(> t ROCKET-CENTER-TO-BOTTOM)
     (place-image ROCKET X ROCKET-CENTER-TO-BOTTOM MTSCN)]))
More importantly, however, this small change to the definition also clarifies that this program doesn’t compute how far the rocket has traveled in the given time; it uses the time as if it were a distance.

; properties of the world
(define WIDTH  100)
(define HEIGHT 100)
 
; properties of the descending rocket
(define VELOCITY 20)
(define DECELERATION 1)
 
; various other constants
(define MTSCN  (empty-scene WIDTH HEIGHT))
(define ROCKET )
(define ROCKET-CENTER-TO-BOTTOM
  (- HEIGHT (/ (image-height ROCKET) 2)))
 
(define X 50)
 
; functions
(define (create-rocket-scene.v6 t)
  (cond
    [(<= (distance t) ROCKET-CENTER-TO-BOTTOM)
     (place-image ROCKET X (distance t) MTSCN)]
    [(> (distance t) ROCKET-CENTER-TO-BOTTOM)
     (place-image ROCKET X ROCKET-CENTER-TO-BOTTOM MTSCN)]))
 
(define (distance t)
  (- (* VELOCITY t) (* 1/2 DECELERATION (sqr t))))

Figure 5: Landing a rocket (version 6)

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. Instead of t, we use (distance t), and we add the above definitions to our program. The final program is displayed in figure 5. It consists of two function definitions: create-rocket-scene.v6 and distance. The remaining constant definitions make the function definitions readable and modifiable. As always, you can run the program by supplying one of its functions to animate:

> (animate create-rocket-scene.v6)

In comparison to the previous versions of create-rocket-scene, this final version teaches you that a program may consist of more than one function definition and that one function definition (create-rocket-scene.v6) can refer to other function definitions (distance).

In a way, this revelation shouldn’t surprise you. Even the first version of create-rocket-scene used + and / and other functions. It’s just that you think of those as built into DrRacket. While + and / are indeed an intrinsic part of the programming language, some others are not. For example, most of the “image arithmetic” and a good part of the “string arithmetic” are just function definitions that we created a long time ago and that are added to your definitions area when you click RUN.

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 such collections of definitions so that you can read them easily even months after completion. After all, you or your manager will want to make changes to these programs, and if you don’t know how to read them and if you didn’t organize them well, you will have a difficult time with even the smallest task. Otherwise you mostly know what there is to know and you can program.

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 is to know. You know that programming and computing is about arithmetic of numbers, strings, images, and whatever other data your chosen programming languages supports. 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 teackpacks support lots of other functions and that DrRacket 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 "universe" teachpack provide 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.

From a theoretical perspective, you are missing one piece of the puzzle: the ability to define functions that, when called, compute forever. This may sound useless and difficult to achieve. It is neither. Here is how you define such a program:

(define (run ul)
  (run 42))
 
(run 5)

If you click RUN, you get no result. Actually, you should immediately move the mouse to the STOP button, click, hold the mouse button down, and wait for DrRacket to stop your run-away program.

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.

Stop! Think! Don’t turn the page yet.

Not!

When you look at the “programming” book shelves in any random book store of some unnamed book chain, not to speak of certain parts of college book stores, you will see loads of books that promise to turn lead into gold, that is, make you a programmer in 21 days or faster. There are also books by cautious authors who think you need to stretch the same or similar material over the entire course of a semester. If you have worked through the first six sections of this book, however, you know that neither of these approaches can create a solid understanding of programming.

Acquiring the mechanical skills of programming—learning how to write instructions or expressions that the computer understands, getting to know what functions are available in the libraries, and similar activities—aren’t helping you much with real programming. To make such claims is like saying that a 10-year old who knows how to dribble can play on a professional soccer (football) team. It is also like claiming that memorizing a thousand words from the dictionary and a few rules from a grammar book teaches you a foreign language.

Programming is far more than the mechanics of language acquisition. It is about reading problem statements, extracting the important concepts. It is about figuring out what is really wanted. It is about exploring examples to strengthen your intuitive understanding of the problem. It is about organizing knowledge and it is about knowing what you don’t know yet. It is about filling those last few gaps. It is about making sure that you know how and why your code works, and that you and your readers will do so in the future. In short, it is really about solving problems systematically.

The rest of this book is all about these things; very little of the book’s content is about the mechanics of BSL or other HtDP languages. The book shows you how good computer programmers think about problems, and—promise!—you will even learn to see that these ideas of problem solving apply to other situations in life, e.g., the work of doctors and journalists, lawyers and engineers, or car mechanics and photographers.

Oh, and by the way, the rest of the book uses a tone that is appropriate for a serious text.

What the book is not about: Many early books on programming and even some of today’s books teach you a lot about the authors’ favorite application discipline for programming: mathematics, physics, music, accounting, and so on. To some extent that is natural, because programming is useful in those areas. Then again, it forces you to know a lot (or at least something) about those disciplines. This book really 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.