5

我会马上承认以下是对我想做的事情的非常糟糕的描述。提前道歉。请提出问题以帮助我解释。:-)

我用其他语言编写了 ETL(提取、转换、加载),其中包含看起来像这样的单个操作:

// 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,它负责调用 Operations 并在它们之间推送数据。

我正在尝试在 Common Lisp 中做类似的事情,并且我想使用相同的基本结构,即每个操作都被定义为一个输入列表和输出列表的普通函数,但是很懒惰。

我可以define-condition使用条件 ( have-value) 来使用类似 foryield的行为,并且可以在单个循环中运行它,并且效果很好。我以相同的方式定义操作,循环输入:

(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 对象,用于存储函数的状态:它是您在处理程序块中按名称(符号)调用的东西,而不是您可以存储以供以后使用的延续。

可以在这里做我想做的事情吗?或者我最好让每个操作函数明确地查看它的输入队列,并明确地将值放在输出队列上?

4

2 回答 2

3

Plain Common Lisp 不支持协程或向下延续。您不能跳出某些计算然后再跳回。有一些库(例如cl-cont)可以为延续提供“一些”支持。

我会使用类似“流”的(参见SICP)抽象(使用 FORCE 和 DELAY)或类似SERIES(实现高效的惰性计算工具)的东西。

于 2010-06-01T19:49:44.780 回答
0

我不认为条件系统在这里使用是正确的。有续集会更好。此外,C# 编译器将您呈现的方法转换为类似延续的对象。

在 Common Lisp 中,您可以使用 cl-cont 库进行延续。

于 2010-06-01T03:44:45.163 回答