IBM®
Перейти к тексту
    в России и странах СНГ [изменить]    Условия использования
 
 
   
    Главная страница    Продукты    Услуги и решения    Поддержка и загрузка    Мой профиль    
Перейти к тексту

developerWorks Россия  >  Linux  >

Создание скриптов при помощи Guile

Увеличиваем возможности C и Scheme

developerWorks
Опции документа
PDF format - Fits A4 and Letter

PDF - Fits A4 and Letter
51KB (11 страница)

Загрузить Adobe® Reader®

Опции документа, требующие включения JavaScript, не отображаются

Обсудить


Выскажите мнение об этой странице

Помогите нам улучшить содержание


Уровень сложности: средний

M. Тим Джонс, инженер-консультант, Emulex Corp.

21.05.2009

Scheme – это язык программирования, а Guile – интерпретатор и библиотека для Scheme, преобразующие его во встроенный язык сценариев, что делает его идеальным средством вдохнуть новую динамичную жизнь в ваши старые статичные приложения. Познакомьтесь с кратким обзором Guile и изучите его широкие возможности для создания гибких приложений.

Guile появился в 1995 году в качестве интерпретатора для языка Scheme, упрощенного потомка языка Lisp, созданного Джоном МакКарти в 1958 году. Однако Guile делает Scheme встраиваемым, что позволяет использовать этот интерпретатор для встроенных скриптов. Guile – это не просто одно из расширений языка, это официальное расширение языка в рамках проекта GNU. Скрипты на Guile можно обнаружить во множестве приложений с открытым исходным кодом – от утилит gEDA CAD до Scheme Constraints Window Manager (Scwm), который обеспечивает гибкость настройки при помощи скриптов Scheme (ссылки приведены в разделе Ресурсы). У Guile очень успешная история использования в качестве встроенного языка скриптов таких приложений, как GNU Emacs, GIMP и Apache Web Server.

Ключевое свойство Guile - это расширяемость (см. рисунок 1). При помощи Guile вы можете обрабатывать скрипты Scheme, динамически связывать скрипты со скомпилированными приложениями на C и даже интегрировать скомпилированные функции C в скрипты Scheme. Это означает, что пользователи смогут перекраивать или изменять ваши приложения, добавляя в них свои параметры.


Рисунок 1. Модель использования скриптов Guile
Рисунок 1. Модель использования скриптов Guile

Одним из лучших примеров индивидуальной настройки приложений является индустрия видеоигр. Видеоигры предоставляют громадные возможности для индивидуальной настройки при помощи скриптов. Многие разработчики игр используют скрипты даже для создания основного кода, реализуя с их помощью отдельные аспекты (такие как системные параметры).

Простой пример

Теперь давайте взглянем на простой пример интеграции Guile в приложение на языке C. В данном случае я использую программу на языке C, которая вызывает скрипт Scheme. Исходный код первого примера приведен в листингах 1 и 2.

Использование скриптов в играх

Современные игры обычно включают в себя скриптовые языки, от традиционных, вроде Python и Ruby, до специализированных, как UnrealScript (ссылки приведены в разделе Ресурсы). В играх эти скриптовые языки могут использоваться для настройки системных характеристик и даже для настройки параметров объектов, использующихся в игре. Скрипты наиболее подходят для разработки игр, поскольку для введения новых свойств отпадает необходимость длительной компиляции. Если вы просмотрите подкаталоги вашей любимой игры для PC, скорее всего вы обнаружите скрипты.

В листинге 1 показано приложение на языке C, которое вызывает скрипт Scheme. Для начала обратите внимание на включение файла заголовка libguile, которое делает доступными необходимые символы Guile. Затем обратите внимание на определение нового типа: SCM. Это абстрактный тип данных языка C, который представляет все объекты Scheme, содержащиеся внутри Guile. Здесь объявляется функция Scheme, вызываемая позже.

Первое, что нужно сделать для любого потока, использующего Guile, - это выполнить вызов scm_init_guile. Эта функция инициализирует глобальный статус Guile и должна быть выполнена раньше любой другой функции Scheme. Затем, до вызова функции Scheme, должен быть загружен файл, в котором находится эта функция. Сделайте это при помощи функции scm_c_primitive_load. Обратите внимание на название: _c_ в названии обозначает, что функция содержит переменную языка C (в отличие от переменной Scheme).

Далее выполняется поиск и возврат символьного имени переменной (модель функций Scheme) с помощью scm_c_lookup, затем оно разыменовывается при помощи scm_variable_ref и сохраняется в переменной Scheme func. Затем при помощи scm_call_0вызывается функция Scheme. Эта функция Guile вызывает ранее объявленную функцию Scheme без аргументов.


Листинг 1. Программа на языке C, вызывающая скрипт Scheme

#include <stdio.h>
#include <libguile.h>

int main( int argc, char **arg )
{
  SCM func;

  scm_init_guile();

  scm_c_primitive_load( "script.scm" );

  func = scm_variable_ref( scm_c_lookup( "simple-script" ) );

  scm_call_0( func );

  return 0;
}

В листинге 2 приведен скрипт Scheme, вызываемый внутри приложения на языке C. Скрипт использует процедуру display для вывода на экран строки. После этой функции следует вызов процедуры newline, которая выполняет возврат каретки.


Листинг 2. Скрипт Scheme, вызываемый приложением на языке C (script.scm)

(define simple-script
  (lambda ()
    (display "script called") (newline)))

Интересно то, что здесь скрипт связан с программой на языке C не статически, а динамически. Скрипт Scheme можно изменить, и тогда при выполнении ранее скомпилированной программы на языке C будут выполняться новые команды из скрипта. В этом заключается мощь встроенных скриптов: вы сочетаете скорость скомпилированных приложений и гибкость динамических скриптов.

Теперь, когда мы разобрались с простым примером, давайте копнем немного глубже, чтобы исследовать некоторые другие элементы использования скриптов Scheme в приложениях на языке C.



В начало


Краткое введение в Scheme

Поскольку многим Scheme может показаться непонятным, давайте рассмотрим несколько примеров, иллюстрирующих мощь этого языка. В этих примерах показаны переменные, условные операторы и циклы, а также некоторые основные особенности Scheme. Полное описание Scheme выходит за пределы данной статьи, но ссылки на необходимую информацию можно найти в разделе Ресурсы.

Для этих примеров я использовал интерпретатор Guile, который позволяет работать с Scheme в режиме реального времени, обеспечивает работу с кодом Scheme и отображает результат немедленно.

Переменные

Scheme является языком, использующим динамические типы данных, следовательно, тип переменной, как правило, неизвестен до момента исполнения. Переменные Scheme - это просто контейнеры, тип которых может быть задан позже.

Переменные создаются при помощи примитива define, а затем изменяются при помощи примитива set!. Здесь это сделано вот так:

guile> (define my-var 3)
guile> (begin (display my-var) (newline))
guile> (set! my-var (* my-var my-var))

Процедуры

Способ создания процедур в Scheme также не будет неожиданным: при помощи примитива define. Процедуры могут быть анонимными (лямбда-процедуры) или именованными. Именованные процедуры хранятся в переменной, как показано ниже:

(define (square val) (* val val))

Эта форма отличается от традиционного синтаксиса Lisp, если вы с ним знакомы, но она проще для чтения. Теперь можно использовать эту новую процедуру так же, как и другие примитивы:

guile> (square 5)
25

Условные операторы

В Scheme существует много вариантов работы с условными операторами. Самый простой и распространенный - это условный оператор if. Он определяет проверку условия, истинное выражение и необязательное ложное выражение. В следующем примере вы можете видеть, как в Scheme делается обработка списков. Список начинается с if и заканчивается (display "less"). Вспомните, что Scheme является потомком Lisp и, соответственно, в его основе лежат списки. Scheme обрабатывает код и данные в виде списков, что размывает границы между данными и кодом.

guile> (define my-var 3)
guile> (if (> my-var 20) (display "more") (display "less"))
less

Циклы

В Scheme циклы реализуются при помощи рекурсии, что требует определенной осмотрительности при использовании. Тем не менее это естественный способ итерирования. В следующем примере показан скрипт, который выполняет итерирование от 0 до 9, а затем выводит сообщение done (готово). В этом примере используется то, что в Scheme называется концевая рекурсия. Обратите внимание, что в конце цикла, который многократно вызывается с одной и той же функцией и аргументом, увеличивающимся с каждым повтором на единицу, выполняется итерация цикла. В традиционных языках программирования такая рекурсия обращается за пределы стека для обработки истории вызовов, но в Scheme это делается иначе. Последний вызов (tail) просто вызывает функцию без дополнительных вызовов или издержек на обслуживание стека.

(let countup ((i 0))
  (if (= i 10) (begin (display "done") (newline))
    (begin
      (display i)
      (newline)
      (countup (+ i 1)))))

Другой интересный способ использования циклов в Scheme - это процедура map. Эта команда просто применяет (или привязывает (maps)) процедуру к списку, как показано в следующем примере. Такой подход одновременно является простым и наглядным.

guile> (define my-list '(1 2 3 4 5))
guile> (define (square val) (* val val))
guile> (map square my-list)
(1 4 9 16 25)



В начало


Использование скриптов Scheme в приложениях на языке C

Как вы видели в листинге 1, добавление скрипта Scheme в приложение на языке C не составляет особой проблемы. Теперь рассмотрим пример, в котором для переноса кода Scheme в C используются другие программные интерфейсы приложений (application programming interfaces, API). В большинстве приложений вам необходимо не только выполнять вызовы элементов Scheme, но и передавать аргументы функций Scheme, получать возвращенные значения и использовать переменные в обеих средах. Для этого в Guile содержится богатый набор средств.

Guile пытается преодолеть границу между двумя средами и придать языку C мощь Scheme. Этой цели служат динамические типы, продолжения, сборка мусора и другие концепции Scheme, расширяющие возможности языка C при помощи API-интерфейсов Guile.

Одним из примеров использования концепций Scheme в языке C является возможность динамического создания новых переменных Scheme из среды C. Для создания переменных Scheme используется функция C scm_c_define. Напоминаю, _c_ обозначает, что в качестве аргумента используется тип данных языка C. Если у вас уже есть переменная Scheme (полученная при помощи функции scm_c_lookup), вместо этого вы можете использовать scm_define. Наряду с созданием переменных Scheme в среде C вы можете разыменовывать переменные Scheme и конвертировать значения между двумя средами. Соответствующий пример приведен в листинге 3.

В листингах 3 и 4 приведены два примера взаимодействия между C и Scheme. В первом примере показан вызов функции Scheme из C, передача аргумента и перехват возвращенного значения. Во втором примере создается переменная Scheme для передачи аргументу. В листинге 4 представлены одинаковые функции Scheme, но первая из них использует аргумент, а вторая – статическую переменную.

Ограничения вызова scm_call

В Guile возможно пять вариантов вызова scm_call. Функцию Scheme можно вызвать без аргументов (scm_call_0) или с несколькими аргументам (не более четырех, scm_call_4), ограниченными количеством переменных Scheme, передаваемых при помощи Guile (четыре). Также не поддерживаются функции с переменным числом аргументов. Если необходимо передать более четырех аргументов или использовать переменное количество аргументов, можно использовать список объектов Scheme с необходимым количеством аргументов.

В первом примере, в листинге 3, для вызова функции Scheme с единственным аргументом просто используется функция scm_call_1. Обратите внимание, что здесь вы должны передать Scheme значения функции. Функция scm_int2num используется для конвертирования целого числа C в числовой тип данных Scheme. Для обратного преобразования переменной Scheme ret_val в целое число C используйте scm_num2int.

Второй пример в листинге 3 начинается с создания новой переменной Scheme при помощи scm_c_define, определенной в строковой переменной C (sc_arg). Эта переменная назначается автоматически при помощи функции преобразования типов scm_int2num. Теперь, когда создана переменная Scheme, вы можете просто вызвать функцию Scheme square2 (на этот раз без аргументов) и использовать тот же метод для получения и разыменования возвращенного значения.


Листинг 3. Обращение к функциям и переменным Scheme при помощи C

#include <stdio.h>
#include <libguile.h>

int main( int argc, char *argv[] )
{
  SCM func;
  SCM ret_val;
  int sqr_result;

  scm_init_guile();

  /* Вызов скрипта square с переданным аргументом */

  scm_c_primitive_load( "script.scm" );

  func = scm_variable_ref( scm_c_lookup( "square" ) );

  ret_val = scm_call_1( func, scm_int2num(7) );

  sqr_result = scm_num2int( ret_val, 0, NULL );

  printf( "result of square is %d\n", sqr_result );

  /* Вызов скрипта square2 с использованием переменной Scheme */

  scm_c_define( "sc_arg", scm_int2num(9) );

  func = scm_variable_ref( scm_c_lookup( "square2" ) );

  ret_val = scm_call_0( func );

  sqr_result = scm_num2int( ret_val, 0, NULL );

  printf( "result of square2 is %d\n", sqr_result );

  return 0;
}

В листинге 4 показаны две процедуры Scheme, использующиеся приложением на языке C, приведенным в листинге 3. Первая процедура square – это обычная функция Scheme, которая получает один аргумент и возвращает результат. Вторая процедура - square2 - не получает аргументов, но зато обрабатывает переменную Scheme (sc_arg). Как и в предыдущем случае, эта переменная также возвращает результат.


Листинг 4. Скрипты Scheme, вызываемые в листинге 3 (script.scm)

(define square
  (lambda (x) (* x x)))

(define square2
  (lambda () (* sc_arg sc_arg)))



В начало


Использование функций C в скриптах Scheme

В последнем примере рассмотрим процесс вызова функций C из скриптов Scheme. Начнем с функции, вызываемой при помощи Scheme, в листинге 5. Для начала вы должны запомнить, что хотя это функция языка C, она получает и возвращает объекты Scheme (тип SCM). Начнем с создания переменной языка C, которая используется для получения аргумента SCM при помощи функции scm_num2int (конвертирует числовой тип данных Scheme в int языка С). Полученный аргумент возводится в квадрат и возвращает результат при помощи другого вызова в scm_from_int.

Оставшийся код программы в листинге 5 определяет параметры среды для загрузки в Scheme. После инициализации среды Guile при помощи вызова scm_c_define_gsubr в Scheme экспортируется функция языка C, которая принимает в качестве аргументов имя функции в Scheme, количество аргументов (обязательные, дополнительные, другие) и собственно экспортируемую функцию C. Остальное вы видели ранее. Загружается скрипт Scheme, получается ссылка на соответствующую функцию Scheme, и она вызывается без аргументов.


Listing 5. C program for setting up the environment for Scheme

#include <stdio.h>
#include <libguile.h>

SCM c_square( SCM arg)
{
  int c_arg = scm_num2int( arg, 0, NULL );

  return scm_from_int( c_arg * c_arg );
}

int main( int argc, char *argv[] )
{
  SCM func;

  scm_init_guile();

  scm_c_define_gsubr( "c_square", 1, 0, 0, c_square );

  scm_c_primitive_load( "script.scm" );

  func = scm_variable_ref( scm_c_lookup("main-script") );

  scm_call_0( func );

  return 0;
}

В листинге 6 содержится скрипт Scheme. Этот скрипт отображает ответ на вызов c_square, являющийся функцией, экспортированной в приложение на языке C из листинга 5.


Listing 6. Scheme script that calls the C function (script.scm)

(define main-script
  (lambda ()
    (begin (display (c_square 8))
           (newline))))

Как ни прост этот пример, он тем не менее демонстрирует легкость, с которой вы можете совместно использовать код и переменные в двух различных программных средах.



В начало


Эпилог

Время создания и распространения статических программ ушло. Сегодня пользователи ожидают от программ гибкости и удобной индивидуальной настройки. Хотя такая эволюция сопряжена с новыми сложностями, она в конечном счете позволяет пользователям раскрыть нам самим новые возможности наших приложений. Надеюсь, что эта статья поможет вам ощутить всю силу Guile. Scheme, возможно, один из самых старых используемых ныне языков программирования, но он остается одним из самых мощных. Guile делает его еще мощнее и удобнее.



Ресурсы

Научиться

Получить продукты и технологии

Обсудить


Об авторе

М. Тим Джонс

М. Тим Джонс - архитектор встроенного ПО и, кроме того, автор книг Artificial Intelligence: A Systems Approach, GNU/Linux Application Programming (выдержавшей на данный момент второе издание), AI Application Programming (второе издание) и BSD Sockets Programming from a Multilanguage Perspective. Он имеет обширный опыт разработки ПО в самых разных предметных областях - от ядер специальных ОС для геосинхронных космических аппаратов до архитектур встраиваемых систем и сетевых протоколов. Тим - инженер-консультант Emulex Corp., Лонгмонт, Колорадо.




Выскажите мнение об этой странице


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



 


 


 


Поделиться этой статьей:

забобрить забобрить memori сохранить в memori




В начало


IBM, эмблема IBM, ibm.com, DB2, developerWorks, Lotus, Rational, Tivoli и WebSphere являются зарегистрированными товарными знаками корпорации International Business Machines Corporation в США и/или других странах. Эти и другие зарегистрированные IBM выражения отмечены в этой статье соответсвующим знаком (® или ™) при первом упоминании, указывающим на зарегистрированные или незарегистрированные в США товарные знаки, принадлежащие корпорации IBM. Эти товарные знаки также могут быть зарегистрированными или незарегистрированными товарными знаками в других странах. Обратитесь к списку товарных знаков корпорации IBM. Linux является зарегистрированным товарным знаком Линуса Торвальдса в США и/или других странах. Другая компания, продукт или название услуги могут быть торговыми марками или знаками обслуживания, принадлежащими иным физическим или юридическим лицам.

IBM обладает всеми авторскими правами касательно информации, расположенной на developerWorks. Использование информации приведенной на этом ресурсе без явного письменного разрешения от IBM или первоначального автора запрещены. Если Вы желаете использовать информацию с developerWorks, пожалуйста воспользуйтесь регистрационной формой для того, чтобы связаться с нами запрос на использование материалов developerWorks Россия.
    IBM в России Конфиденциальность Контакты