Система условий Common Lisp для передачи контроля
Я сразу признаю, что следующее - довольно ужасное описание того, что я хочу сделать. Извиняюсь заранее. Пожалуйста, задавайте вопросы, чтобы помочь мне объяснить.:-)
Я написал ETL (Extract, Transform, Load) на других языках, которые состоят из отдельных операций, которые выглядят примерно так:
// in class CountOperation
IEnumerable<Row> Execute(IEnumerable<Row> rows) {
var count = 0;
foreach (var row in rows) {
row["record number"] = count++;
yield return row;
}
}
Затем вы объединяете несколько этих операций вместе и вызываете Dispatcher, который отвечает за вызов операций и передачу данных между ними.
Я пытаюсь сделать нечто подобное в Common Lisp, и я хочу использовать одну и ту же базовую структуру, то есть каждая операция определяется как обычная функция, которая вводит список и выводит список, но лениво.
Я могу define-condition
условие (have-value
) использовать для yield
-подобное поведение, и я могу запустить его в один цикл, и он прекрасно работает. Я определяю операции таким же образом, просматривая входные данные:
(defun count-records (rows)
(loop for count from 0
for row in rows
do (signal 'have-value :value `(:count ,count @,row))))
Проблема в том, что я хочу связать воедино несколько операций и запустить их. Моя первая попытка написать диспетчер для этих выглядит примерно так:
(let ((next-op ...)) ;; pick an op from the set of all ops
(loop
(handler-bind
((have-value (...))) ;; records output from operation
(setq next-op ...) ;; pick a new next-op
(call next-op)))
Но перезапуски имеют только динамический экстент: у каждой операции будут одинаковые имена перезапусков. Перезапуск - это не объект Lisp, который я могу сохранить для хранения состояния функции: это то, что вы вызываете по имени (символу) внутри блока обработчика, а не продолжение, которое вы можете сохранить для дальнейшего использования.
Можно ли сделать что-то, как я хочу здесь? Или мне лучше просто заставить каждую функцию-функцию явно просматривать свою входную очередь и явно помещать значения в выходную очередь?
2 ответа
Обычный Лисп не поддерживает сопрограммы или нисходящие продолжения. Вы не можете выпрыгнуть из некоторых вычислений, а затем вернуться обратно. Существуют библиотеки (например, cl-cont) для обеспечения некоторой поддержки продолжений.
Я бы использовал абстракции, подобные "потоку" (см. SICP) (используя FORCE и DELAY) или что-то вроде SERIES (который реализует эффективные средства для отложенных вычислений).
Я не думаю, что система условий - это то, что нужно использовать здесь. Будет лучше с продолжениями. Кроме того, компилятор C# превращает представленный вами метод в объект, подобный продолжению.
В Common Lisp вы можете продолжить работу с библиотекой cl-cont.