Scheme:使用fold实现n-argument compose

Pau*_*rth 9 scheme functional-programming

我试图在Scheme中找到多参数"compose"的"最佳"实现(我知道它在某些实现中是内置的,但我假设目前我正在使用没有这个的那个).

对于2参数的组合函数,我有这个:

(define compose
  (lambda (f g)
    (lambda x
      (f (apply g x)))))
Run Code Online (Sandbox Code Playgroud)

这样做的好处是,如果最右边的函数需要额外的参数,那么它们仍然可以通过组合函数传递.这具有令人满意的特性,即在某事物之上组成身份功能不会改变功能.

例如:

(define identity
  (lambda (x) x))

(define list1
  (compose identity list))

(define list2
  (compose identity list1))

(list2 1 2 3)
> (1 2 3)
Run Code Online (Sandbox Code Playgroud)

现在做一个"n-argument"compose我可以这样做:

(define compose-n
  (lambda args
    (foldr compose identity args)))

((compose-n car cdr cdr) '(1 2 3))
> 3
Run Code Online (Sandbox Code Playgroud)

但这不再保留那个漂亮的"身份"属性:

((compose-n identity list) 1 2 3)
> procedure identity: expects 1 argument, given 3: 1 2 3
Run Code Online (Sandbox Code Playgroud)

问题是用于foldr命令的"初始"函数.它已建成:

(compose identity (compose list identity))
Run Code Online (Sandbox Code Playgroud)

所以......我不确定解决这个问题的最佳方法."foldl"似乎是自然更好的选择,因为我想从左边的 "身份"开始而不是正确 ...

但是一个天真的实现:

(define compose-n
  (lambda args
    (foldl compose identity args)))
Run Code Online (Sandbox Code Playgroud)

哪个有效(必须颠倒功能应用程序的顺序):

((compose-n cdr cdr car) '(1 2 3))
> 3
Run Code Online (Sandbox Code Playgroud)

没有解决问题,因为现在我最终不得不把身份功能放在左边!

((compose-n cdr cdr car) '(1 2 3))
> procedure identity: expects 1 argument, given 3: 1 2 3
Run Code Online (Sandbox Code Playgroud)

就像,我需要使用"foldr"但需要一些不同于身份功能的"初始"值...或更好的身份功能?显然我在这里很困惑!

我想实现它不必编写一个明确的尾递归"循环"...似乎应该有一个优雅的方法来做到这一点,我只是卡住了.

Chr*_*ung 4

您可能想尝试这个版本(来自reduceSRFI 1):

(define (compose . fns)
  (define (make-chain fn chain)
    (lambda args
      (call-with-values (lambda () (apply fn args)) chain)))
  (reduce make-chain values fns))
Run Code Online (Sandbox Code Playgroud)

这不是火箭科学:当我在 #scheme IRC 频道上发布此内容时,Eli指出这是compose. :-)(作为奖励,它也适用于您的示例。)