方案出错 "The object (unquote f), passed as an argument to identifier->symbol, is not an identifier."

Getting error in scheme "The object (unquote f), passed as an argument to identifier->symbol, is not an identifier."

我正在尝试实现一个函数 composex 给定一个函数列表 funcs returns 一个函数是 funcs 中所有函数的组合 所以,

input -> [f1 f2 f3 ...]
output -> 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 执行。

轻松修复,IIUC:您在内部 lambda 的参数列表中放置了一个逗号。拿出来。

逗号在Scheme中有特殊含义;它允许您从准报价中 "escape"。在这种情况下,您没有使用准引号,因此此处的逗号在 Scheme 中没有任何意义。

不过,这是一条非常糟糕的错误消息。

本部分:

(lambda (ini, f) (f x))

现在我不确定你是否错误地用 , 分隔了参数,或者一些疯狂的巧合想要 ini, 作为参数的名称,但是 ,用于准引用表达式:

(define a 10)
(define b 20)
`(list with ,a and ,(+ a b) inserted) 
; ==> (list with 10 and 30 inserted)

就像 ' 一样,这些由 reader 宏处理。因此就像 'x(quote x)`(,x)(quasiquote ((unquote x)))quote 相同,quoasiquoteunquote 是特殊形式.你的兰巴最终看起来像:

(lambda (ini (unquote f)) (f x))

MIT Scheme 不支持可选参数,只有像 inif 这样的符号可以出现在 lamba 列表中,而不是列表 (unquote f)。因此,您需要将其更改为:

(lambda (ini f) (f x))

编辑

您的 reducexfoldl 相同,除了其中参数的顺序和它所采用的函数。例如

(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 的顺序进行作文,答案应该是 200compose 支持最右边函数的数量,因此最先应用的函数。如果它是一个 thunk,那么生成的函数可以使用零参数,并且使用多个 arity 一个它变成一个 multi arity 函数:

(define double+ (compose double +))
(double+ 3 8 9 2)
; ==> 44 

潜在问题

如@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

这是因为你的减速器忽略了 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