В учебнике HtDP, посвященному программированию на языке Scheme (Lisp) в среде drRacket, особое внимание уделяется вопросам, касающимся работы с изображениями и анимацией. Поводом написать данную статью послужили мои попытки разобрать примеры, изложенные в этом учебнике.

drRacket можно скачать с сайта.

Вводная часть учебника содержит описание функции empty-scene, предназначенной для работы с изображениями. Например, эта программа создает пустую сцену

#lang racket  
(require 2htdp/image)      ;библиотека для работы с изображениями 
(empty-scene 100 60)     ;сцена (канвас) размером 100х60 


А эта помещает на сцену зеленый круг.

#lang racket  
(require 2htdp/image)
(place-image (circle 5 "solid" "green") ;поместить изображение 
                         50 80         ;координаты изображения
             (empty-scene 100 100))        ;пустая сцена (канвас)

Для создания анимации используется функция animate. Следующая программа создает анимацию (движение объекта).

#lang racket  
(require 2htdp/image)  ;библиотека для работы с изображением
(require 2htdp/universe) ;библиотека для работы с анимацией
(define (picture-of-circle height)
(place-image (circle 5 "solid" "green") 50 height (empty-scene 100 60)))
(animate picture-of-circle)

1. Функция animate запускает таймер и посчитывает количество тактов.
2. Таймер срабатывает 28 раз в секунду.
3. Каждый раз, когда таймер «тикает», animane отправляет в picture-of-circle текущий такт; и
4. сцена отображается на канвасе.

Это означает, что круг сначала появляется в кооринате 0, затем 1, затем 2 и т. д. Поэтому круг опускается сверху вниз. Т.е. эта программа за 3,5 секунд создает около 100 изображений, что и позволяет создать эффект движения.

Кстати, в этой статье обсуждается вопрос объектного программирования на языке Scheme.

В первой части учебника приводится описание функции big-bang, а все программы, использующие big-bang, называются в дальнейшем world-программами. Эта программа рисует красный квадрат 100х100 (Параграф 2.5).

#lang racket
(require 2htdp/image)
(require 2htdp/universe)
(define (number->square s)
  (square s "solid" "red"))
(big-bang 100 [to-draw number->square])

Если изменить значение параметра, функция нарисует квадрат другого размера.

(big-bang 50 [to-draw number->square]) ; квадрат 50х50
(big-bang 100 [to-draw number->square]) ; квадрат 100х100
(big-bang 150 [to-draw number->square]) ; квадрат 150х150

Чтобы в общих чертах описать функцию big-bang, надо рассмотреть алгоритм
реализации функций CONS,CAR,CDR из учебника SICP.

(define (f-cons x y)
(define (dispatch m)
(cond ((= m 0) x)
((= m 1) y)
(else (error "Аргумент не 0 или 1 -- CONS" m))))
dispatch)
(define (f-car z) (z 0))
(define (f-cdr z) (z 1))

Проверим результат работы этих функций.

(define a 1)
(define b 2)
(display (f-car (f-cons a b))) ; получаем 1
(display (f-cdr (f-cons a b))) ; получаем 2

Создадим функцию f-cr:

(define (f-cr x y) (y x)) 

Теперь функция f-cr может передавать любые значения.

Изображение
image

По аналогии создадим функцию big-bang-1:

(define (big-bang-1 x y) (y x))

и функцию to-draw-1, инкапсулирующую render-1:

(define (to-draw-1 x)
(define (render-1 y)              
(place-image (x y) y y (empty-scene (* 2 y) (* 2 y))) 
  ) render-1)

Реализуем алгоритм передачи параметров (значений):

Изображение
image

Далее в учебнике рассматривается конструкция вида:

(big-bang cw0
  [on-tick tock]
  [on-key ke-h]
  [on-mouse me-h]
  [to-draw render]
  [stop-when end?]
  ...)

которая позволяет обрабатывать нажатие клавиш, таймер, координаты движения объекта. Далее (Figure 13) приведен листинг программы, использующей эту конструкцию.

(define BACKGROUND (empty-scene 100 100))
(define DOT (circle 3 "solid" "red"))

(define (main y)
  (big-bang y
    [on-tick sub1]
    [stop-when zero?]
    [to-draw place-dot-at]
    [on-key stop]))
(define (place-dot-at y)
  (place-image DOT 50 y BACKGROUND))
(define (stop y ke)  0)

Запустить программу можно в режиме «Начинающий студент» (Верхнее меню > Язык > Выбрать язык… > Начинающий студент), добавив пакеты (Верхнее меню > Язык > Добавить учебный пакет...) для работы с изображениями (image.rkt) и анимацией (universe.rkt).

Параграф 3 называется «Как проектировать программы» (How to Design Programs). Здесь автор говорит о том, что программы представляют информацию в виде данных. Действительно, числа могут представлять из себя как количество тактов, отсчитываемых таймером, так и координаты объекта, его цвет, размер и т.п. Далее программа должна преобразовать данные обратно в информацию.

На рис. 14 изображена диаграмма, иллюстрирующая эту идею.
Параграф 3.5 (On Testing) посвящен разработке с помощью тестов.
Параграф 3.6 посвящен проектированию world-программ. На рисунке 17 представлена упрощенная схема библиотеки 2htdp/universe.

Автор пишет, что существует 3 основных шага по созданию big-bang-программ (параграф 3.7):

1. Для всех величин, которые остаются неизменными, надо создать константы.
a. «Физические» константы привязаны к таким атрибутам объекта, как скорость, цвет,
ширина, высота и т.д.
(define WHEEL-RADIUS 5)
b. «Графические» константы.
(define WHEEL
(circle WHEEL-RADIUS «solid» «black»))
2. Те свойства, которые изменяются с течением времени в ответ на нажатие клавиш,
таймер и т.д. надо представить в виде состояний объекта управления.
3. При получении набора состояний всех объектов, надо вызвать big-bang для отображения этих объектов. Далее читателям предлагается сделать упражнения, в которых необходимо использовать функцию big-bang.

В параграфе 4.3 обсуждается вопрос обработки событий, генерируемых клавиатурой и мышкой. На рис. 20 представлена условная конструкция, обрабатывающая клавиши «left» и «right».

(define (keh p k)
  (cond
    [(string=? "left" k)
     (- p 5)]
    [(string=? "right" k)
     (+ p 5)]
    [else p]))

Добавлю, что если также необходимо обрабатывать клавиши «up» и «down», то координаты объекта следует хранить в списке:

(define coord-xy (list 50 50) ) ; x=50 ; y=50

и передавать этот список в big-bang:

(big-bang coord-xy 
[to-draw place-dot-at] 
[on-key keh])

функция place-dot-at «разбирает» список на части:

(define (place-dot-at xy )
  (place-image DOT (car xy) (cdr xy) MTS))    ; (car(cdr xy))

а функция keh «разбирает» список, изменяет одну из координат, и собирает обратно:

(list (-(car p)5) (cdr p) )     ;  (car (cdr p))

Далее предлагается разработать программу-светофор.

(check-expect (traffic-light-next "red") "green")
(define (traffic-light-next s)
  (cond
    [(string=? "red" s) "green"]
    [(string=? "green" s) "yellow"]
    [(string=? "yellow" s) "red"]))

Добавлю, что светофор переключается очень быстро (каждый такт). Чтобы этого избежать, надо изменить время срабатывания таймера.

[on-tick traffic-light-next rate-expr]

Теперь таймер будет срабатывать каждые rate-expr секунд (on-tick tick-expr rate-expr).

Также в тексте дана ссылка на страницу с world-программами. Запустить программы можно в режиме «Начинающий студент», добавив необходимые пакеты.

Параграф 5 (Adding Structure) содержит определение структур данных (Пример)

#lang racket
(define-struct posn (x y))
(define a (make-posn 10 34))
(posn-x a)
(posn-y a)
(posn? (posn 10 34))

В параграфе 5.4 автор дает определение структуры. Он пишет, что структура, фактически, определяет другие функции. В частности:

1. Конструктор, который создает экземпляр структуры.
2. Селектор, который имеет доступ к полям экземпляра структуры.
3. Структурный предикат, который, подобно обычным предикатам, проверяет,
является ли аргумент экземпляром структуры.

make-posn — это конструктор
posn-x и posn-y — селекторы
posn? — предикат

Подробнее об этом написано в Документации (Structures)

Параграф 5.5. Программу из упражнения 71 предлагается запустить по-шагам (step-by-step) в отладчике. Запускать надо в режиме «Начинающий студент», добавив учебные пакеты.

В параграфе 5.6 автор делает поэтапное описание программы для покадровой анимации (перемещение объекта вдоль одной из осей). Теперь в программе используются структуры для определения координат объекта.
Вот эта программа
(require 2htdp/image)
(require 2htdp/universe)
;        (define-struct posn (x y))
(define MTS (empty-scene 100 100))
(define DOT (circle 3 "solid" "red"))
 ; A Posn represents the state of the world.
(define (x+ p)
  (make-posn (+ (posn-x p) 3) (posn-y p))) 

(define (scene+dot p) (place-image DOT (posn-x p) (posn-y p) MTS) )

 (define (reset-dot p x y me)
  (cond
    [(mouse=? me "button-down") (make-posn x y)]
    [else p]))
; Posn -> Posn 
(define (main p0)
  (big-bang p0
    [on-tick x+]
    [on-mouse reset-dot]
    [to-draw scene+dot]))
(main (make-posn 1 20))

В параграфе 5.11 приводится пример обработки изображения.
В параграфе 6.1 делается поэтапное описание игры Space invanders.
В параграфе 6.2 предлагается дополнить программу из параграфа 4.3 (светофор).

В параграфе 7 автор подводит некие итоги и делится с читателями своими размышлениями о том, какими навыками должен обладать хороший программист.
Поделиться с друзьями
-->

Комментарии (3)


  1. Vjatcheslav3345
    18.05.2017 09:06

    Публикации по учебнику и среде планируется продолжать?


    1. demser
      18.05.2017 22:58

      Да, наверное


  1. JIghtuse
    18.05.2017 12:49
    +1

    Статья какая-то сумбурная, больше похожа на отзыв. А вот книга и серия курсов очень нравится, полезные навыки прививает.