如何在Lisp中创建一个从右到左实现lambda的函数

时间:2014-11-17 02:09:48

标签: lisp

我试图通过尝试不同的语言实现来理解LIsp。

我目前正在尝试创建一个函数,它接受一个lambda函数列表,并从右到左,从左到右组合它们。

这是一个我不清楚的例子

(apply-right-left '((lambda (x) (+ x 1)) (lambda (x) (* x 2))) 1)
-->  3

感谢...

2 个答案:

答案 0 :(得分:4)

请参阅PORT/ext.lisp中的compose

(defun compose (&rest functions)
  "Return the composition of all the arguments.
All FUNCTIONS should take one argument, except for
the last one, which can take several."
  (reduce (lambda (f0 f1)
            (lambda (&rest args) (funcall f0 (apply f1 args))))
          functions :initial-value #'identity))

因此,

(funcall (compose (lambda (x) (+ x 1)) (lambda (x) (* x 2))) 1)
==> 3

如果您想从左到右撰写,则需要将:from-end传递给reduce

如果您不想使用上述compose创建中间函数,可以直接使用reduce

(reduce #'funcall
        (list (lambda (x) (+ x 1)) (lambda (x) (* x 2)))
        :initial-value 1
        :from-end t)
==> 3

答案 1 :(得分:1)

对于Common Lisp,sds的答案是正确的。我对Scheme更熟悉,所以我决定为Scheme编写一个版本,增加了对部分应用程序的支持。基本上,它是一个通用的组合,如果你传递一个函数,它就会被调用,如果你传递一个非函数,它会被一个函数包装器包装起来并被调用。这是一个满口的,所以我将在下面的例子中解释。

这里的功能(需要SRFI 26):

(define (wrap x)
  (cut values x <...>))

(define (call-compose . args)
  (cond ((null? args) (values))
        ((procedure? (car args))
         (call-with-values (cut apply call-compose (cdr args)) (car args)))
        (else
         (apply call-compose (wrap (car args)) (cdr args)))))

果然,如果你使用测试功能调用它,你会得到:

> (call-compose (lambda (x) (+ x 1)) (lambda (x) (* x 2)) 1)
3

你甚至可以做类似的事情(除了+*的参数被交换之外,就像你上面的例子一样):

> (call-compose + 1 * 2 1)
3

(这与(+ 1 (* 2 1))相同。)


这篇文章的其余部分不再深入了解。如果您是Scheme的新手,可能没有意义。 : - )

魔术的关键部分是函数包装器wrap。它接受任何值并返回一个带有任意数量参数的函数,并返回添加了这些参数的值(作为多个值)。例如:

> (define wrap1 (wrap 1))
> (wrap1 2 3 4)
1
2
3
4

因此,使用(call-compose + 1 * 2 1)之类的表达式,与(call-compose + (wrap 1) * (wrap 2) (wrap 1))相同,后者转换为(mv + (mv (wrap 1) (mv * (mv (wrap 2) (mv (wrap 1))))))(其中mva macro that provides the same functionality as Common Lisp's multiple-value-call)。

您也可以将wrap与函数对象一起使用。这允许您将函数对象传递给高阶函数。如,

> (call-compose map (wrap +) '(1 2 3) '(4 5 6))
(5 7 9)

为了获得更多乐趣,我还将compose扩展为具有相同的行为:

(define (compose . args)
  (cond ((null? args) values)
        ((procedure? (car args))
         (lambda xs
           (call-with-values (cut apply (apply compose (cdr args)) xs) (car args))))
        (else
         (apply compose (wrap (car args)) (cdr args)))))