Практика мультипарадигмального программирования, 04 лекция (от 19 марта)
Материал из eSyr's wiki.
(Содержимое страницы заменено на «== From Ebaums Inc to MurkLoar. == We at EbaumsWorld consider you as disgrace of human race. Your faggotry level exceeded any imaginab...») |
(Отмена правки № 1300 участника 85.25.141.60 (обсуждение)) |
||
Строка 1: | Строка 1: | ||
- | == | + | [[Практика мультипарадигмального программирования, 03 лекция (от 12 марта)|Предыдущая лекция]] | [[Практика мультипарадигмального программирования, 05 лекция (от 26 марта)|Следующая лекция]] |
- | + | ||
- | + | = Встраиваемый пример ЛИСПа = | |
- | + | ||
+ | Существует один пример встраемого Common LISP: ecl версия 0.6, документирован паршиво. | ||
+ | |||
+ | Есть несколько диалектов, которые ни Common, ни Схема, ни ведома зверушка. Например, есть librep, который Схема, но маскируется под ЛИСп, например, есть () и T — истина, но на самом деле это Схема. Есть тьма-тьмущая своих диалектов, икто не объясняет зачем. | ||
+ | |||
+ | Что показать в качестве примера встраемого ЛИСПа? Легче встраивать Схему, она часто встраивается. Но в emacs встроен eLISP, в AutoCAD встроен AutoLISP. Но AutoCAD закрытый, emacs — отдельное проишествие. В остальных случаях чаще схема. У любого приложения, ориентированного на Схему, предполагается интеграция с Си. Посему будет рассмотрена интеграция с MZ Scheme. | ||
+ | |||
+ | = Scheme = | ||
+ | == Отличия Scheme == | ||
+ | |||
+ | * В Схеме списки, а не S-выражения. | ||
+ | * Список верхнего уровня S-выражением не является | ||
+ | * ... | ||
+ | * В Схеме не различается понятия связанного с символом значения и функции | ||
+ | Пример: | ||
+ | Common Lisp | ||
+ | > car | ||
+ | Error: symbol has no value | ||
+ | >(setq car 25) | ||
+ | 25 | ||
+ | >car | ||
+ | 25 | ||
+ | при этом это не мешает иметь функцию car | ||
+ | Scheme | ||
+ | >car | ||
+ | #<system_function car> | ||
+ | При этом если мы зададим значение, мы функцию потеряем. | ||
+ | Что это позволяет: | ||
+ | (lambda (x y) (cons x y)) | ||
+ | В Лиспе это вызовет ошибку, надо | ||
+ | #'(lambda (x y) (cons x y)) | ||
+ | но это немного другое. В Схеме это будет вычислено. | ||
+ | |||
+ | Когда мы говорим (cons a b), то в Схеме все три параметра вычисляются, а в Лиспе первым элементом должна быть функция. Как следствие, в Схеме не нужен funcall. | ||
+ | |||
+ | === Именование === | ||
+ | {| | ||
+ | !Lisp | ||
+ | !Scheme | ||
+ | |- | ||
+ | |eq | ||
+ | |eq? | ||
+ | |- | ||
+ | |eql | ||
+ | |eql? | ||
+ | |- | ||
+ | |equal | ||
+ | |equal? | ||
+ | |- | ||
+ | | | ||
+ | |- | ||
+ | |stringp | ||
+ | |string? | ||
+ | |} | ||
+ | |||
+ | <div class="comment">Стандарты пишутся не для того, чтобы было понгятно, я для того, чтобы у двух людей, которым было понятно, не было разногласия. | ||
+ | |||
+ | Но есть исключения: был человек Пастел, и его RFC читать одно удовольствие.</div> | ||
+ | |||
+ | == Как определяются символы в Схеме, как ввести функцию в схеме == | ||
+ | Есть define, он ничем не является, встречаться должен на первом уровне, он не является S-выражением. | ||
+ | * Сопоставление символу значениея | ||
+ | (define twenty-five 25) | ||
+ | * Сопоставление функции | ||
+ | ** Схемеры обычно используют такой вариант: | ||
+ | (define (myfun arg1 arg2 ...) ...) | ||
+ | ** Предыдущий вариант является сокращённой формой записи для | ||
+ | (define myfun (lambda (arg1 arg2 ...) ...)) | ||
+ | |||
+ | <div class="comment">Однажды лектор спросил у программистов на Схеме, есть ли в ней динамическое связывание, через три дня они ответили, что они нашли его. Обычно используется лексическое связывание</div> | ||
+ | |||
+ | == Реализация Scheme == | ||
+ | Существует достаточно много реализаций Схемы, соответствующих стандарту. В итоге лектор выбрал две: | ||
+ | * guile — декларируется, что Схема там побочный эффект, а вообще это могучая система программирования, где можно будет делать вообще на всём. Но в документации там швах. А те потроха, в которых приходится копаться, ... . Причём есть два API, одно deprecated, разработчики сказали, что они его больше не будут поддерживать, но по документации оно понятно. Есть новое API, но в качестве примера его использовать нельзя. Этот интерпретатор можно и встраивать, и расширять. | ||
+ | * mzscheme — не намного лучше, но удалось разобраться практически сразу. Написана явно на Си, с расчётом и расширения, и встраивания в Си. И те же проблемы, что и в Си, например, garbage collected. А нам нужно, чтобы S-выражения были в локальных переменных ... . Проблема в том, что чтобы запустить garbage collector, нужно знать пределы стека. Посему, там есть два способа запуска: функция, которая не возвращаем значения, и мы запускаем её навсегда. Пишем вторую функцию main, void на вход, void на выход. И если хотим возвращать значения, то делаем: | ||
+ | main → scheme → real_main() | ||
+ | Есть второй вариант, но он не портабельный. | ||
+ | |||
+ | Когда лектор добрался до примера, как его встроить, то это был экран текста, довольно запутанный. | ||
+ | |||
+ | Не получается так хорошо, как с Tcl, в две строчки. Дело, вероятно, в том, что Tcl широко используется, а Scheme — нет, и тем более не в качестве встроенного языка. | ||
+ | |||
+ | == Как расширить интерпретатор == | ||
+ | Обычно встраивание требуется для того, чтобы пользователь что-то написал, а чтобы он писал что-то применительно к программе, нужно, чтобы были проблемно-ориентированные функции. Оказалось, что в mzscheme это продумано достаточно хорошо. | ||
+ | |||
+ | * Есть тип Scheme_Object *, без звёздочки не употребляется никогда. Можно считать, что это S-выражение и есть | ||
+ | ** Чтобы это появилось, есть два варианта: | ||
+ | *** #include <scheme.h> | ||
+ | *** #include <escheme.h> | ||
+ | *** Различие в способе сборки мусора | ||
+ | * Когда возникает написать схемовскую функцию, то она должна возвращать Scheme_Object, и получать int argc, Scheme_Object ** argv | ||
+ | Scheme_Object * func(int argc, Scheme_Object ** argv) | ||
+ | |||
+ | Пример: | ||
+ | Функция triple: делает из (a) (a a a). Или (a . (a . (a .()))). | ||
+ | Scheme_Object * func(int argc, Scheme_Object ** argv) | ||
+ | { | ||
+ | /* | ||
+ | * проверяем количество параметров, | ||
+ | * должно быть 1, иначе ошибка | ||
+ | */ | ||
+ | if (argc != 1) | ||
+ | /* | ||
+ | * правильным будет в случае не одного | ||
+ | * параметра сообщать ошибки интерпретатору схемы | ||
+ | */ | ||
+ | { | ||
+ | scheme_null; //искать пустой список пришлось в исходниках | ||
+ | } | ||
+ | |||
+ | return scheme_make_pair(argv[0], | ||
+ | scheme_make_pair(argv[0], | ||
+ | scheme_make_pair(argv[0], scheme_null))) | ||
+ | } | ||
+ | |||
+ | == Список простых функций == | ||
+ | === Значения === | ||
+ | * scheme_null () | ||
+ | * scheme_true #t | ||
+ | * scheme_false #f | ||
+ | * scheme_void — в Схеме вроде нет, нужно для того, чтобы не возвращать значения | ||
+ | |||
+ | === Как создавать S-выражения === | ||
+ | * Scheme_Object * scheme_make_pair(Scheme_Object * a, Scheme_Object * b) — создание точечной пары (обычно такие вещи называются pair) | ||
+ | * Пусть есть строка и хочется сделать атом, который строка. Как это сделать: Scheme_Object * scheme_make_string(const char * str); Есть ещё Scheme_Object * scheme_make_string_no_copy(const char * str). В первом случае происходит копирование, во втором — просто устанавливается ссылка на существующий объект | ||
+ | * Scheme_Object * scheme_make_integer_value(int i) — есть несколько модификаций (from_long_long, from unsigned, &hellip) | ||
+ | * Scheme_Object * scheme_make_char(int c) — int для поддержки unicode | ||
+ | * Scheme_Object * scheme_make_double(double d) | ||
+ | * Scheme_Object * scheme_make_symbol(...) — пытается найти имя (char *) | ||
+ | |||
+ | === Как извдечь данные из Scheme_Object * === | ||
+ | * double scheme_real_to_double(Scheme_Object *) — единственная простая ничем не примечательная функция | ||
+ | * int scheme_get_int_val(Scheme_Object *, long * v) — возврат значения производится через v | ||
+ | |||
+ | Ожидаемо, если бы были функции для извлечения строки и т. д. Но их не нашлось. Нашлось следующее: | ||
+ | * SCHEME_STR_VAL(...) — макрос, нужно передать объект, возвращает char * | ||
+ | * SCHEME_FLOAT_VAL(...) | ||
+ | * SCHEME_DOUBLE_VAL(...) | ||
+ | * SCHEME_INT_VAL(...) | ||
+ | Предварительно надо проверить тип: | ||
+ | * SCHEME_DOUBLEP | ||
+ | * SCHEME_DOUBLEP | ||
+ | * SCHEME_INTP | ||
+ | * SCHEME_NULLP | ||
+ | * SCHEME_STRINGP | ||
+ | * SCHEME_PAIRP | ||
+ | Макросы возвращают 0 в случае "нет" и 1 в случае "да" | ||
+ | |||
+ | === Работа со списками === | ||
+ | * SCHEME_CAR | ||
+ | * SCHEME_CDR | ||
+ | * SCHEME_CAAR | ||
+ | * SCHEME_CDDR | ||
+ | * SCHEME_CADR | ||
+ | Других нет. | ||
+ | |||
+ | == Другой пример: nple == | ||
+ | nple(int quan, char c) — делает список из quan символов c | ||
+ | Scheme_Object * nple(int argc, Scheme_Object ** argv) | ||
+ | { | ||
+ | Scheme_Object * res = scheme_null; | ||
+ | int i, n; | ||
+ | |||
+ | if (argc != 2) | ||
+ | { | ||
+ | return res; | ||
+ | } | ||
+ | |||
+ | if (!SCHEME_INTP(argv[0])) | ||
+ | { | ||
+ | scheme_wrong_type("nple", "integer number", 0, argc, argv); //очень похоже на то, что это longjump и return не требуется | ||
+ | return scheme_null; | ||
+ | } | ||
+ | |||
+ | scheme_get_int_val(argv[0], &n); | ||
+ | |||
+ | for (i = 0; i < n; i++) | ||
+ | { | ||
+ | res = scheme_make_pair(argv[1], res); | ||
+ | } | ||
+ | |||
+ | return res; | ||
+ | } | ||
+ | |||
+ | == Как расширить интерпретатор == | ||
+ | Надо сделать сошку. | ||
+ | |||
+ | Сошка будет состоять из одного модуля, в ней будет три функции: | ||
+ | * scheme_initialize | ||
+ | * scheme_reload | ||
+ | * scheme_module_name | ||
+ | |||
+ | Одна из них (scheme_initialize) даётся для инициализации интерпретатора схема и своих собственных данных. | ||
+ | |||
+ | scheme_reload инициализирует только интепретатор (Scheme_Env). Для начала необходима инициализация примитивов — функций, у которых есть C-обёртки. Мы сделаем обёртки и погрузим их в интерпретатор. | ||
+ | Scheme_Object * scheme_reload(Scheme_Env * env) | ||
+ | { | ||
+ | Scheme_Object * proc; | ||
+ | |||
+ | proc = scheme_make_prim(triple); //лучше функции объявлять как статики | ||
+ | scheme_add_global("triple", proc, env); //не обязан быть именно примитив | ||
+ | |||
+ | //со второй функцие поступим иначе — есть вариант создания примитива, при котором делается проверка аргументов | ||
+ | proc = scheme_make_prim(nple, "nple", 2, 2); //последние два параметра — минимальное и максимальное количество параметров | ||
+ | scheme_add_global("nple", proc, env); //не обязан быть именно примитив | ||
+ | return scheme_void; //обычно делают так, но можно попытаться что-то создать и вернуть, проявится оно при вызове scheme_load_extension() | ||
+ | } | ||
+ | |||
+ | Scheme_Object * scheme_initialize(Scheme_Env * env) | ||
+ | { | ||
+ | return Scheme_Reload(env); | ||
+ | } | ||
+ | |||
+ | Различие: initialize вызывается только в первый раз, в остальные — reload. | ||
+ | |||
+ | Scheme_Object * scheme_module_name() | ||
+ | { | ||
+ | return scheme_false; | ||
+ | } | ||
+ | |||
+ | === Как это скомпилировать === | ||
+ | Можно использовать gcc, | ||
+ | |||
+ | Можно использовать их программу, которая есть враппер для разных C-компиляторов. Сделано, вероятно, для унификации. Называется mzc. | ||
+ | $mzc --cc sample.c ; # компиляция модуля, получим sample.o | ||
+ | $mzc --ld sample.so sample.o | ||
+ | |||
+ | === Запуск === | ||
+ | $ mzscheme | ||
+ | > (load-extension "sample.so") | ||
+ | > (triple 'a) | ||
+ | (a a a) | ||
+ | > (nple 10 2) | ||
+ | (2 2 2 2 2 2 2 2 2 2) | ||
+ | |||
+ | Тема следующей лекции: компиляция из одного языка в другой | ||
+ | |||
+ | {{Практика мультипарадигмального программирования}} | ||
+ | {{Lection-stub}} |
Текущая версия
Предыдущая лекция | Следующая лекция
Содержание |
[править] Встраиваемый пример ЛИСПа
Существует один пример встраемого Common LISP: ecl версия 0.6, документирован паршиво.
Есть несколько диалектов, которые ни Common, ни Схема, ни ведома зверушка. Например, есть librep, который Схема, но маскируется под ЛИСп, например, есть () и T — истина, но на самом деле это Схема. Есть тьма-тьмущая своих диалектов, икто не объясняет зачем.
Что показать в качестве примера встраемого ЛИСПа? Легче встраивать Схему, она часто встраивается. Но в emacs встроен eLISP, в AutoCAD встроен AutoLISP. Но AutoCAD закрытый, emacs — отдельное проишествие. В остальных случаях чаще схема. У любого приложения, ориентированного на Схему, предполагается интеграция с Си. Посему будет рассмотрена интеграция с MZ Scheme.
[править] Scheme
[править] Отличия Scheme
- В Схеме списки, а не S-выражения.
- Список верхнего уровня S-выражением не является
- ...
- В Схеме не различается понятия связанного с символом значения и функции
Пример:
Common Lisp > car Error: symbol has no value >(setq car 25) 25 >car 25
при этом это не мешает иметь функцию car
Scheme >car #<system_function car>
При этом если мы зададим значение, мы функцию потеряем. Что это позволяет:
(lambda (x y) (cons x y))
В Лиспе это вызовет ошибку, надо
#'(lambda (x y) (cons x y))
но это немного другое. В Схеме это будет вычислено.
Когда мы говорим (cons a b), то в Схеме все три параметра вычисляются, а в Лиспе первым элементом должна быть функция. Как следствие, в Схеме не нужен funcall.
[править] Именование
Lisp | Scheme |
---|---|
eq | eq? |
eql | eql? |
equal | equal? |
stringp | string? |
[править] Как определяются символы в Схеме, как ввести функцию в схеме
Есть define, он ничем не является, встречаться должен на первом уровне, он не является S-выражением.
- Сопоставление символу значениея
(define twenty-five 25)
- Сопоставление функции
- Схемеры обычно используют такой вариант:
(define (myfun arg1 arg2 ...) ...)
- Предыдущий вариант является сокращённой формой записи для
(define myfun (lambda (arg1 arg2 ...) ...))
[править] Реализация Scheme
Существует достаточно много реализаций Схемы, соответствующих стандарту. В итоге лектор выбрал две:
- guile — декларируется, что Схема там побочный эффект, а вообще это могучая система программирования, где можно будет делать вообще на всём. Но в документации там швах. А те потроха, в которых приходится копаться, ... . Причём есть два API, одно deprecated, разработчики сказали, что они его больше не будут поддерживать, но по документации оно понятно. Есть новое API, но в качестве примера его использовать нельзя. Этот интерпретатор можно и встраивать, и расширять.
- mzscheme — не намного лучше, но удалось разобраться практически сразу. Написана явно на Си, с расчётом и расширения, и встраивания в Си. И те же проблемы, что и в Си, например, garbage collected. А нам нужно, чтобы S-выражения были в локальных переменных ... . Проблема в том, что чтобы запустить garbage collector, нужно знать пределы стека. Посему, там есть два способа запуска: функция, которая не возвращаем значения, и мы запускаем её навсегда. Пишем вторую функцию main, void на вход, void на выход. И если хотим возвращать значения, то делаем:
main → scheme → real_main()
Есть второй вариант, но он не портабельный.
Когда лектор добрался до примера, как его встроить, то это был экран текста, довольно запутанный.
Не получается так хорошо, как с Tcl, в две строчки. Дело, вероятно, в том, что Tcl широко используется, а Scheme — нет, и тем более не в качестве встроенного языка.
[править] Как расширить интерпретатор
Обычно встраивание требуется для того, чтобы пользователь что-то написал, а чтобы он писал что-то применительно к программе, нужно, чтобы были проблемно-ориентированные функции. Оказалось, что в mzscheme это продумано достаточно хорошо.
- Есть тип Scheme_Object *, без звёздочки не употребляется никогда. Можно считать, что это S-выражение и есть
- Чтобы это появилось, есть два варианта:
- #include <scheme.h>
- #include <escheme.h>
- Различие в способе сборки мусора
- Чтобы это появилось, есть два варианта:
- Когда возникает написать схемовскую функцию, то она должна возвращать Scheme_Object, и получать int argc, Scheme_Object ** argv
Scheme_Object * func(int argc, Scheme_Object ** argv)
Пример: Функция triple: делает из (a) (a a a). Или (a . (a . (a .()))).
Scheme_Object * func(int argc, Scheme_Object ** argv) { /* * проверяем количество параметров, * должно быть 1, иначе ошибка */ if (argc != 1) /* * правильным будет в случае не одного * параметра сообщать ошибки интерпретатору схемы */ { scheme_null; //искать пустой список пришлось в исходниках } return scheme_make_pair(argv[0], scheme_make_pair(argv[0], scheme_make_pair(argv[0], scheme_null))) }
[править] Список простых функций
[править] Значения
- scheme_null ()
- scheme_true #t
- scheme_false #f
- scheme_void — в Схеме вроде нет, нужно для того, чтобы не возвращать значения
[править] Как создавать S-выражения
- Scheme_Object * scheme_make_pair(Scheme_Object * a, Scheme_Object * b) — создание точечной пары (обычно такие вещи называются pair)
- Пусть есть строка и хочется сделать атом, который строка. Как это сделать: Scheme_Object * scheme_make_string(const char * str); Есть ещё Scheme_Object * scheme_make_string_no_copy(const char * str). В первом случае происходит копирование, во втором — просто устанавливается ссылка на существующий объект
- Scheme_Object * scheme_make_integer_value(int i) — есть несколько модификаций (from_long_long, from unsigned, &hellip)
- Scheme_Object * scheme_make_char(int c) — int для поддержки unicode
- Scheme_Object * scheme_make_double(double d)
- Scheme_Object * scheme_make_symbol(...) — пытается найти имя (char *)
[править] Как извдечь данные из Scheme_Object *
- double scheme_real_to_double(Scheme_Object *) — единственная простая ничем не примечательная функция
- int scheme_get_int_val(Scheme_Object *, long * v) — возврат значения производится через v
Ожидаемо, если бы были функции для извлечения строки и т. д. Но их не нашлось. Нашлось следующее:
- SCHEME_STR_VAL(...) — макрос, нужно передать объект, возвращает char *
- SCHEME_FLOAT_VAL(...)
- SCHEME_DOUBLE_VAL(...)
- SCHEME_INT_VAL(...)
Предварительно надо проверить тип:
- SCHEME_DOUBLEP
- SCHEME_DOUBLEP
- SCHEME_INTP
- SCHEME_NULLP
- SCHEME_STRINGP
- SCHEME_PAIRP
Макросы возвращают 0 в случае "нет" и 1 в случае "да"
[править] Работа со списками
- SCHEME_CAR
- SCHEME_CDR
- SCHEME_CAAR
- SCHEME_CDDR
- SCHEME_CADR
Других нет.
[править] Другой пример: nple
nple(int quan, char c) — делает список из quan символов c
Scheme_Object * nple(int argc, Scheme_Object ** argv) { Scheme_Object * res = scheme_null; int i, n; if (argc != 2) { return res; } if (!SCHEME_INTP(argv[0])) { scheme_wrong_type("nple", "integer number", 0, argc, argv); //очень похоже на то, что это longjump и return не требуется return scheme_null; } scheme_get_int_val(argv[0], &n); for (i = 0; i < n; i++) { res = scheme_make_pair(argv[1], res); } return res; }
[править] Как расширить интерпретатор
Надо сделать сошку.
Сошка будет состоять из одного модуля, в ней будет три функции:
- scheme_initialize
- scheme_reload
- scheme_module_name
Одна из них (scheme_initialize) даётся для инициализации интерпретатора схема и своих собственных данных.
scheme_reload инициализирует только интепретатор (Scheme_Env). Для начала необходима инициализация примитивов — функций, у которых есть C-обёртки. Мы сделаем обёртки и погрузим их в интерпретатор.
Scheme_Object * scheme_reload(Scheme_Env * env) { Scheme_Object * proc; proc = scheme_make_prim(triple); //лучше функции объявлять как статики scheme_add_global("triple", proc, env); //не обязан быть именно примитив //со второй функцие поступим иначе — есть вариант создания примитива, при котором делается проверка аргументов proc = scheme_make_prim(nple, "nple", 2, 2); //последние два параметра — минимальное и максимальное количество параметров scheme_add_global("nple", proc, env); //не обязан быть именно примитив return scheme_void; //обычно делают так, но можно попытаться что-то создать и вернуть, проявится оно при вызове scheme_load_extension() }
Scheme_Object * scheme_initialize(Scheme_Env * env) { return Scheme_Reload(env); }
Различие: initialize вызывается только в первый раз, в остальные — reload.
Scheme_Object * scheme_module_name() { return scheme_false; }
[править] Как это скомпилировать
Можно использовать gcc,
Можно использовать их программу, которая есть враппер для разных C-компиляторов. Сделано, вероятно, для унификации. Называется mzc.
$mzc --cc sample.c ; # компиляция модуля, получим sample.o $mzc --ld sample.so sample.o
[править] Запуск
$ mzscheme > (load-extension "sample.so") > (triple 'a) (a a a) > (nple 10 2) (2 2 2 2 2 2 2 2 2 2)
Тема следующей лекции: компиляция из одного языка в другой
Практика мультипарадигмального программирования
Календарь
пн | пн | пн | пн | пн | |
Февраль
| 12 | 19 | |||
Март
| 12 | 19 | 26 | ||
Апрель
| 02 | 09 | 16 |