我正在尝试实现一个函数composex
,它给出一个函数列表funcs
返回一个函数,该函数是funcs
中所有函数的组合
所以,
输入 - > [f1 f2 f3 ...]
输出 - > f'(x)= f1(f2(f3(...(x))))
我的代码片段是:
(define (reducex initial f arr)
(if (null? arr)
initial
(reducex (f initial (car arr))
f
(cdr arr)
)
)
)
(define (abst x)
(if (< x 0)
(- x)
x
)
)
(define (mult2 x)
(* x 2))
(define (composex funcs)
(lambda (x)
(reducex '()
(lambda (ini, f) (f x))
funcs)
)
)
(define absmult2 (composex (cons abst (cons mult2 '()))))
(absmult2 2)
(absmult2 -2)
我得到的错误是composex
;The object (unquote f), passed as an argument to identifier->symbol, is not an identifier.
;To continue, call RESTART with an option number:
; (RESTART 1) => Return to read-eval-print level 1.
我正在使用mit-scheme执行。
答案 0 :(得分:3)
轻松修复,IIUC:您在内部lambda
的参数列表中添加了逗号。拿出来。
逗号在Scheme中具有特殊含义;它允许你从quasiquote“逃脱”。在这种情况下,你不使用quasiquote,所以这里的逗号在Scheme中没有任何意义。
但这是一个非常糟糕的错误信息。
答案 1 :(得分:3)
潜伏着问题
正如@John所说,您需要移除,
中的(ini, f)
并更改为(ini f)
。这产生了一个语法正确的程序,但其他问题仍然存在。
我建议您更改compose
程序。一,初始'()
是错误的,因为它与函数的类型不匹配。如果您尝试将参数应用于空组合
(define (composex funcs)
(lambda (x)
(reducex '()
(lambda (ini f) (f x))
funcs)))
(define foo (composex '()))
(foo 'x) ; => '()
其次,您的缩减器(lambda (ini f) (f x))
不正确,因此您的答案不正确
(absmult2 2) ; 4
(absmult2 -2) ; -4
这是因为您的reducer忽略ini
。这实际上是我们致电(absmult2 -2)
(let ((ini '()))
(set! ini (abst -2))
(set! ini (mult2 -2))
ini) ;; -4
我们真正想要的行为是
(let ((ini -2))
(set! ini (abst ini))
(set! ini (mult2 ini))
ini) ;; 4
我们可以通过重新处理composex
(define (composex funcs)
(lambda (ini)
(reducex ini
(lambda (x f) (f x))
funcs)))
(absmult2 2) ; 4
(absmult2 -2) ; 4
它现在也适用于空作品
(define foo (composex '()))
(foo 'z) ; 'z
帮助自己
具有讽刺意味的是,您最初所犯的错误可以在其他地方使用,以提高代码的可读性。您可以使用取消引号逗号,
代替cons
来定义合成。请注意使用quasiquote `
(define absmult2 (composex `(,abst ,mult2)))
(absmult2 2) ; 4
(absmult2 -2) ; 4
或者,您可以更改composex
,以便接受可变数量的输入
(define (composex . funcs)
(lambda (ini)
(reducex ini
(lambda (x f) (f x))
funcs)))
(define absmult2 (composex abst mult2))
(absmult2 2) ; 4
(absmult2 -2) ; 4
横向思维
将composex
的其他实现视为搔痒大脑的一种方式
(define (composex . fs)
;; simplified composition of two functions
(define (comp f g)
(lambda (x) (g (f x))))
;; results in simplified reduce
(reducex identity comp fs))
(define absmult2 (composex abst mult2))
(absmult2 2) ; 4
(absmult2 -2) ; 4
上面,这个composex
如何处理我们谈到的空组合示例?
一个甚至没有使用reduce
的人 - 注意最后一个阻止程序员创建一个空组合
(define (composex f . fs)
(lambda (x)
(if (null? fs)
(f x)
((apply composex fs) (f x)))))
(define absmult2 (composex abst mult2))
(absmult2 2) ; 4
(absmult2 -2) ; 4
答案 2 :(得分:1)
在这部分:
(lambda (ini, f) (f x))
现在我不确定您是否错误地将参数分隔为,
或某些疯狂的重合符ini,
作为参数的名称,但是,
用于quasiquote表达式:
(define a 10)
(define b 20)
`(list with ,a and ,(+ a b) inserted)
; ==> (list with 10 and 30 inserted)
就像'
这些由读者宏处理。因此,就像'x
(quote x)
一样,`(,x)
与(quasiquote ((unquote x)))
和quote
相同,quoasiquote
和unquote
是特殊形式。你的lamba最终看起来像:
(lambda (ini (unquote f)) (f x))
MIT Scheme不支持可选参数,只有像ini
和f
这样的符号可以在lamba列表中而不是列表(unquote f)
。因此,您需要将其更改为:
(lambda (ini f) (f x))
修改强>
您的reducex
与foldl
相同,除了它中的参数顺序和所需的函数。例如
(fold cons '() '(1 2 3)) ; ==> (3 2 1)
(reducex '() (lambda (a e) (cons e a)) '(1 2 3)) ; ==> (3 2 1)
您的compose
以错误的顺序执行应用程序,并且此类函数采用可变数量的参数是常见的。例如。
((compose - +) 3 5)
; ==> -8
以下是我认为您需要做的事情:
(define (compose . funcs)
(case (length funcs)
((0) (lambda (v) v)) ; identity
((1) (car funcs)) ; return the argument
(else ; return a composition
(let* ((rfuncs (reverse funcs))
(initial-func (car rfuncs))
(rest-funcs (cdr rfuncs))
(reduce (lambda (fn a) (fn a))))
(lambda args
(foldl reduce
(apply initial-func args)
rest-funcs))))))
所以,让我们用秩序很重要的东西进行测试:
(define (add100 v) (+ v 100))
(define (double v) (* v 2))
(define (manual-compose v)
(double (add100 v)))
(define test-compose (compose double add100))
(manual-compose 50)
; ==> 300
(test-compose 50)
; ==> 300
如果合成是按照composex
的顺序完成的,那么答案就是200
。 compose
支持最正确的函数的arity,因此首先应用它。如果它是一个thunk,那么结果函数是零参数和多个arity,它就变成了一个多元函数:
(define double+ (compose double +))
(double+ 3 8 9 2)
; ==> 44