_O_>Во, я как раз работаю над template-based кодогенератором (для UML 2.0) и заинтересовался функциональными языками. Проблема в том, что мне надо взаимодействовать с кодом на С++. Вопрос, есть ли возможность вызова external функций на С++ из Лисп-программы (или программы на ином ФЯ) ? Если нет, то есть ли возможность вызывать просто внешний тул из лисп-программы ? (Т.е. нужен аналог С-шной функции system() или т.п.)
А как на основе этого строить проверку грамматики?
E>Вот, сегодня наткнулся на проектик xml-mapping для Ruby, так там в документации такой пример приведен. Для xml-я:
Это все совсем не то, это и для дотнета есть. Видишь ли, если решать описанную задачу в лоб, как тут многие пытаются, парся xml и генерируя в императивном стиле текст, параллельно при этом проверяя корректность заглатываемых данных, о результат получается одинаков, будь то лисп, руби или C#. Увы, хоть написать такой код просто и не требует особого напряжения ума, но поддерживать подобное очень тяжело. На реальных задачах все это превращается в огромные простыни пестрого кода. Всякими фокусами на базе ООП бывает удается немного ситуацию упростить, например так,
но все равно получается не очень. Вариант с XSLT уже несколько лучше, поскольку удается выкинуть из структуры программы хотя бы обход основной иерархии, но жуткий синтаксис и усложнение понимания в том случае, когда по дереву приходится проходить несколько раз, делают его тоже довольно далеким от идеала. Вариант с универсальным функциональным языком (я пробовал OCaml, в сообщении Re: Задачка: придумать язык
есть пример на Erlang-подобии) тоже не очень. С одной стороны синтаксис проще, с другой нет возможности писать просто конечный код, без того чтобы не заворачивать его в строковые константы. После заверений о крутости лиспа стало интересно посмотреть, а как на нем решить такую задачу. Но, увы, все приведенные решения являются по большей части вариациями на ту же тему.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, fionbio, Вы писали:
F>>Этот код — генератор, а не валидатор (исправленные функции оригинала)!!! F>>Валидацию я сделал по ходу дела. Любой нормальный лиспер в этом коде разберётся F>>без проблем за 0,5 секунды. И поддерживать — никаких проблем.
AVK>Не, ты мне покажи валидацию. Это важно. А если валидация совмещена с генерацией, то это еще хуже.
В условиях задачи валидация не была оговорена. Я позже сделаю отдельный валидатор,
это не сложно, сейчас по просту некогда. Вообще, для многих задач приведённого
подхода вполне достаточно.
F>>Генерация, блин. Оно лучше гораздо, чем XSLT, поверьте. Удобоваримее и проще.
AVK>Не, верить не буду. Потому и прошу показать.
Это проще всего понять, попробовав. Преимуществ с точки зрения
удобства программирования много, например:
1) Возможность бысто и эффективно оттестировать каждый маленький
кусок в REPL. Упрощает отладку в сотни раз. По-хорошему, это не
отменяет юнит-тестов, их можно использовать для более крупных компонент.
2) Компактность кода. Работа с последовательностями и с деревьями/иерархиями
на C#/C++/etc. по сравнению с Лиспом — PITA. Lisp позволяет за секунды в пару
строчек описать то, что выльется в десятки-сотни строк кода на других языках.
Это связано со своеобразным подходом — имеется набор недеструктивных
функций типа remove, remove-if, mapcar и пр., чем-то напоминающих STL'евые
аналоги, но возвращающих результат в виде новой последовательности.
Конечно, это не бесплатно — написаный "с лёту" может быть вполне красив, но
не очень производителен. Но тут идея такая — сначала сделать, чтобы _всё_ работало,
затем оптимизировать. Преждевременная оптимизация — зло.
Пример компактности — транспонирование матрицы (как раз в стиле прототипа —
неоптимально по производительности, зато для лиспера — более чем компактно,
просто и ясно):
(apply #'mapcar #'list
'((1 2 3)
(4 5 6)
(7 8 9))) -->
((1 4 7)
(2 5 8)
(3 6 9))
3) Компактная и простая запись исходных данных и промежуточных результатов
в виде s-expressions. С возможностью в любой момент без проблем посмотреть
*любые* промежуточные результаты в том же REPL.
AVK>А как на основе этого строить проверку грамматики?
Андрей, здесь фокус в том, что это императив, который за счет фокусов Ruby выглядит как декларация.
На самом деле здесь происходит следующее:
# Вызов метода root с двумя параметрами:
# имя "objects" и блок кода в фигурных скобках.
# Так же это можно было бы записать в виде:
root( "objects" ) do
# Вот этот блок кода может быть вызван внутри метода root
# для какого-нибудь скрытого объекта. И следующий метод
# element будет вызыватся для этого объекта с тремя параметрами:
# именем "object", константой ONE_OR_MORE и блоком кода.
element( "object", ONE_OR_MORE ) do
# И здесь все то же самое.
...
end
end
При этом Ruby не дает вызывать неизвестных методов и использовать неизвестные константы. Поэтому, если написать, что-то типа:
root "object" {
Element "object", one_or_more { ... }
}
то сам Ruby будет ругаться на невозможность работы с Element и неопределенность one_or_more.
На таких фокусах у меня весь mxx_ru построен. Вот, пример якобы декларативности:
мне кажется более понятным. Я конечно не "нормальный лиспер". Но таких еще поискать нужно. Все же понятность и модифицируемость — это очень важные вещи.
... << RSDN@Home 1.2.0 alpha rev. 557>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, eao197, Вы писали:
AVK>>А как на основе этого строить проверку грамматики?
E>Андрей, здесь фокус в том, что это императив, который за счет фокусов Ruby выглядит как декларация.
Фокусов мне не надо. Я думал что там что то большее за этим стоит, чего я не понимаю. Прелесть декларации не в том, как это выглядит, а в том что декларация не позволяет изменить логику, вся логика прошита в интерпретаторе. Проще говоря мы заменяем подход "как?" подходом "что?".
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, eao197, Вы писали:
AVK>>>А как на основе этого строить проверку грамматики?
E>>Андрей, здесь фокус в том, что это императив, который за счет фокусов Ruby выглядит как декларация.
AVK>Фокусов мне не надо. Я думал что там что то большее за этим стоит, чего я не понимаю. Прелесть декларации не в том, как это выглядит, а в том что декларация не позволяет изменить логику, вся логика прошита в интерпретаторе. Проще говоря мы заменяем подход "как?" подходом "что?".
Так на самом деле это ничему не противоречит. Ведь такое описание может просто формировать в памяти какое-то представление, которое затем будет доступно интерпритатору. А уже интерпритатор можно менять как угодно -- декларации останутся неизменными. То же самое возможно и в обратном направлении.
... << RSDN@Home 1.1.4 stable rev. 510>>
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
CP>>Из objects выбираются только те элементы, в которых первый символ — object.
AVK>А мне нужно, чтобы если там было что то, начинающееся не с object, то была бы ошибка, причем с сообщением, понятным пользователю. Наконец, правила, по которым определяется корректность, должны быть декларативными.
CP>> Если в object нет :name будет ошибка, также если в property не будет :type или :name будет ошибка.
AVK>Какая?
Понятно, но это делается добавлением валидаторов, но я вы сами написали что это не то что вам нужно. Вы хотите иметь возможность декларативно задавать методы генерации, но вас не польностью устраивает XSLT, в случае использовании лиспа ИМХО правельный путь решения ваших задач — это создание декларативного языка на основе лиспа(с помощью тех же макросов), аналогичного XSLT, но лишённого некоторых недостатков. Но это уже достаточно сложная задача, нужно поискать, возможно что-то аналогичное уже есть.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, cranky, Вы писали:
C>>Ясно. Значит нужны правила типа таких:
[...]
AVK>Нет. Нужно декларативное задание схемы исходного файла. Что то вроде: AVK>
AVK>тег objects
AVK>(
AVK> тег object[один или больше раз]
AVK> (
AVK> атрибут name
AVK> тег property[один или более раз]
AVK> (
AVK> атрибут name
AVK> атрибут type
AVK> )
AVK> )
AVK>)
AVK>
Здравствуйте, fionbio, Вы писали:
AVK>>Не, верить не буду. Потому и прошу показать.
F>Это проще всего понять, попробовав.
Вот я и хочу попробовать. Но прежде чем тратить уйму времени на то, чтобы начать делать концептуально кривые программки, я прошу показать мне пример правильного решения простенькой задачи. Пока что вижу все то же, что есть и в других языках.
F>1) Возможность бысто и эффективно оттестировать каждый маленький F>кусок в REPL.
Что такое REPL? Отладчик местный?
F>2) Компактность кода.
Пока что даже XSLT на конкретной задаче оказался не сильно хуже. А уж у него избыточность синтаксиса жуткая.
F> Работа с последовательностями и с деревьями/иерархиями F>на C#/C++/etc. по сравнению с Лиспом — PITA. Lisp позволяет за секунды в пару F>строчек описать то, что выльется в десятки-сотни строк кода на других языках.
Например?
F>Это связано со своеобразным подходом — имеется набор недеструктивных F>функций типа remove, remove-if, mapcar и пр., чем-то напоминающих STL'евые F>аналоги, но возвращающих результат в виде новой последовательности.
Просто функциональный стиль? Это много где есть. Есть масса функциональных языков, при помощи шаблонов в C++ и анонимных методов в C# функциональный стиль в определенной мере можно эмулимровать.
F>Пример компактности — транспонирование матрицы
Это по твоему работа со списками? Трансформирование матрицы это конечно здорово, но это не та задача, которая лично мне интересна.
F>(как раз в стиле прототипа - F>неоптимально по производительности, зато для лиспера — более чем компактно, F>просто и ясно): F>(apply #'mapcar #'list F> '((1 2 3) F> (4 5 6) F> (7 8 9))) -->> F>((1 4 7) F> (2 5 8) F> (3 6 9))
Ага, а за этим стоит какой нибудь чудовищный макрос. Ну и зачем?
F>3) Компактная и простая запись исходных данных и промежуточных результатов F>в виде s-expressions.
Здравствуйте, eao197, Вы писали:
E>Так на самом деле это ничему не противоречит. Ведь такое описание может просто формировать в памяти какое-то представление, которое затем будет доступно интерпритатору. А уже интерпритатор можно менять как угодно -- декларации останутся неизменными. То же самое возможно и в обратном направлении.
Можно. Но тот же вопрос — зачем тогда Руби? То же самое я могу сделать, к примеру, на шарпе.