[devel] I: alterator internals - 3
Stanislav Ievlev
=?iso-8859-1?q?inger_=CE=C1_altlinux=2Eorg?=
Пн Июн 6 13:15:28 MSD 2005
Третья часть рассказа. Наверное самая сложная, но если что-то будет неясно
- ничего страшного, в следующих частях всё постепенно прояснится.
2.6 Несколько замечаний про функций.
В прошлый раз было подробно рассказано как описываются функции.
Несколько полезных замечаний:
0. У длинного определения
(define f (lambda (x y) ... ))
Есть более короткая форма
(define (f x y) ... )
Если процедура без аргументов, то определение в упрощённой форме будет выглядить как:
(define (f) ... )
1. Процедура является таким же полноправным типом данных как и числа и
строки, поэтому их можно как передавать в качестве аргумента, так и
возвращать в качестве ответа.
Вот например как можно было бы определить функцию "модуль числа":
(define (abs x) ((if (< x 0) - +) x))
2. Дополнение к предыдущему.
Если вас не смущает сведение выражения:
(define a (+ 1 2))
(* a 5)
к:
(* (+ 1 2) 5)
То не должно смущать и сведение:
(define f (lambda (x) (+ x x))
(g f)
к:
(g (lambda (x) (+ x x)))
То есть когда вы передаёте функцию в качестве аргумента, то можно сначала
сконструировать, дать ей имя и передать указывая имя, а можно и не давать имени, а сразу
сконструировать и передать.
2.7 О символах
Из чего сделана переменная? Переменная это некоторое "имя" и "связь" между
этим именем и каким-то объектом, и прежде всего именно "связь" (связи может не быть, а имена есть всегда). То есть можно рассматривать по-отдельности: отдельно имя, и отдельно некоторая таблица которая ставит соответствие между именами и объектами на которые они ссылаются.
(define a 4)
Это значит есть, имя "a" и оно ссылается на объект - число 4.
Когда интерпретатор видит выражение (define b a), он обнаруживает наличие имени
"a", а потом производит поиск в своей таблице имён, отмечает что "a"
ссылается на объект - число 4 и производит подстановку, после которой
выражение приобретает вид (define b 4).
А что если мы хотим получить просто "имя"? Тогда мы говорим
интерпретатору, пожайлуста не надо размышлять над следующим выражением -
дай мне его "как есть" - эта процедура носит имя quote.
(define b (quote a)) - назначит "имени" b, уже не не 4, а просто "имя" a.
"имена" являются одним из типов данных схемы и называются символами.
У длинного варианта записи:
(quote объект)
Существует сокращенный вариант записи:
'объект.
То есть можно написать так:
(define b 'a).
Чтобы ещё лучше прочувствовать символы, запустим
интерпретатор. Мы будем вводить выражения, а он будет писать во что они
проинтерпретировались:
--
$ gambsc
Gambit Version 4.0 beta 12
> (define a 5)
> 5
5
> a
5
> 'a
a
>
> b
*** ERROR IN (console)@5.1 -- Unbound variable: b
1>
> 'b
b
>
--
Обратите внимание на то что переменной b нет (точнее связи нет), но "имя" b без связи с
чем-либо замечательно интерпретируется.
Не сильно огорчайтесь, если чего-то сейчас не ясно, всё прояснится
когда мы чуть глубже поймём как работает интерпретатор.
2.8 Унивесальный клей.
Практически нет программ которые работали бы с базовыми типами
предоставляемыми языком (строки, числа, символы),
каждый язык предоставляет возможность разработчику создавать свои, новые типы,
составленные из других типов (из базовых или других составных).
Scheme предлагает только один способ сделать составной тип - объединение
двух объектов в пару.
Делается это так:
(cons 3 4) ; "создать пару из 3 и 4"
(cons "aaa" "bbb")
Доступ к первому и второму элементу пар обеспечивают функции car и cdr:
(define a (car 'first 'second))
(car a) ; получить первый элемент пары, то есть "имя" "first"
(cdr a) ; получить второй элемент пары, то есть "имя" "second"
Пары вполне достаточно чтобы создать такую известную структуру как односвязный список.
Для этого первый элемент пары будет ссылаться на содержимое элемента
списка, а второй - на следующий элемент списка.
Последний элемент списка должен ссылаться на "никого нет", мы
для этого введём специальное имя "()".
Вот например так создаётся список из двух элементов, содержащих 3 и 4:
(define elem2 (cons 4 '() )) ; второй элемент содержит 4 и ссылается на "никого больше нет"
(define elem1 (cons 3 elem2)) ; первый эелемент содержит 3 и ссылается на второй
В "развернутом виде" это выглядит так:
(define elem1 (cons 3 (cons 4 '())))
Если захотим сделать список из трёх елементов 1 2 и 3, то надо написать:
(cons 1 (cons 2 (cons 3 '())))
Как видно конструкция очень громоздкая, поэтому есть более короткий
вариант:
(list 1 2 3)
Поскольку список склеен из пар, то и работать с ним можно при помощи при
помощи тех же car и cdr.
(define a (list 1 2 3))
(car a) ; получить содержимое первого элемента
(cdr a) ; получить "хвост", то есть ссылку на второй элемент
(car (cdr a)) ; получить содержимое второго элемента
(cdr (cdr a)) ; получить "хвост" после второго элемента, то есть третий элемент
Опять-таки для веера из "car" и "cdr" существует сокращенные варианты
записи:
вместо (car (car .. - (caar ..
вместо (car (cdr .. - (cadr ..
вместо (car (cdr (cdr ... - (caddr ..
вместо (car (car (cdr ... - (caadr ..
вместо (car (cdr (cdr (cdr .. - (cadddr ..
Надеюсь что уловили закономерность? Впрочем пользоваться этим скорее всего
не придётся. В нашем примере:
(cadr a) ; получить содержимое второго элемента
(caddr a) ; получить содержимое третьего элемента
(cdddr a) ; получить "хвост" третьего элемента, то есть "никого нет" или символ "()".
2.9 Снова про alterator
На конструкции языка можно смотреть как на последовательный вызов функций,
а можно как на фразу на каком-то другом языке более высокого уровня
(vbox (button "aaa") (button "bbb"))
Можно прочитать как "вызвать функцию которая вернёт вертикальную группу с
двумя параметрами, результатами вызова функций одна из которых создаст
кнопку с именем 'aaa', а другая - с именем 'bbb'".
А можно как "вертикальная группа из кнопкок 'aaa' и 'bbb'".
На alterator можно смотреть как на сборище функций, а можно как на
словарь.
Когда вы решаете какую-нибудь задачу на alterator, вы просто формулируете
проблему на некотором языке, а он её решает. Именно формулируете, а не
программируете последовательный вызов функций.
Давайте сделаем последовательность из двух диалогов, один спросит наше
имя, а второй напечатает приглашение.
Для того чтобы связать диалоги, надо присвоить им имена. В alterator
каждый диалог имеет уникальный URL, и имеется таблица соответствий между
этими URL и файлами которые содержат описания диалогов.
Основной диалог, с которым происходит запуск приложения всегда должен
иметь имя "/"
Пусть описание диалога с вопросом будет жить в файле "q.scm", а
описание диалога с приглашением "w.scm", назовём первый как требуется
правилами "/", а
второй "/welcome".
Описание будет выглядить так:
" начинаем с файла 'q.scm' (или / - это в файле 'q.scm)'
/welcome - это в файле 'w.scm'"
Что на языке mapper записывается как:
--
(/ file "q.scm")
(/welcome file "w.scm")
--
Сохраним это в файле "simple_map.scm"
Теперь создадим описание диалога с приветствием:
"вертикальная группа в которую вставили:
- метку "My first alterator dialog"
- горизонтальная группа с меткой "Your name:" и полем редактирования по
имени username, по умолчанию пустым.
- кнопка "OK", при нажатии на которую происходит перeключение на URL "/welcome"
и передаётся параметр по имени name со значением взятом из поля редактирования
username."
Сначала разберём конструкции, с которыми ещё не сталкивались:
0. Поле редактирования, по умолчанию пустое, описывается как:
(edit "")
1. Сказать, поле с именем username, это значит:
(id 'username (edit ""))
2. Сказать что взять содержимое из поля по имени username, это:
(username text)
3. Сказать, что "при нажатии перейти ...", это значит:
(on-click (goto "/welcome" 'name (username text)))
Переключение диалога означает что вы просто заменяете содержимое окна, то
есть остаётесь на самом деле в том же самом диалоге, просто с другим
содержимым.
Обратите внимание на использование символов, когда мы обрабатываем фразы,
содержащие "имя". В конструкции с "id" мы имеем просто имя, когда уже
забираем текст, у нас в наличие уже не просто имя, а имя связанное с
конкретным виджетом, поэтому обращаться к нему можно уже без использования
quote.
Соединим всё это теперь вместе:
--
(vbox
(label "My first alterator dialog")
(hbox
(label "Your name:")
(id 'username (edit "")))
(button "OK"
(on-click (goto "/welcome" 'name (username text)))))
--
И сохраним в файл "q.scm"
Аргументы, которые передавались диалогу доступны через функцию global.
Описание диалога приветствия:
"
Вертикальная группа из:
- метки, текст которой есть соединение "Welcome " и значения переданного
параметра по имени name.
- кнопки "OK", при нажатие на которую происходит завершение работы
диалога (и соотв. завершение работы вообще)
"
Вот содержимое файла "w.scm":
--
(vbox
(label (string-append "Welcome "
(global 'name)))
(button "OK"
(on-click (end-dialog))))
--
Теперь произведём запуск одного комнонеты alterator которая собственно
занимается диалогом с пользователем "lookout -m simple_map.scm".
Всё - ваше первое приложение под alterator готово. Согласитесь, что за три
лекции мы уже кое-чему научились.
Продолжение будет ...
Подробная информация о списке рассылки Devel