【问题标题】:Getting error in scheme "The object (unquote f), passed as an argument to identifier->symbol, is not an identifier."在方案“对象(取消引用 f),作为参数传递给标识符 - > 符号,不是标识符。”
【发布时间】:2018-10-27 23:53:36
【问题描述】:

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

输入 -> [f1 f2 f3 ...]
输出 -> f'(x) = f1(f2(f3( ... (x))))

我的代码sn-p是:

(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 执行。

【问题讨论】:

    标签: lambda functional-programming scheme function-composition mit-scheme


    【解决方案1】:

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

    逗号在Scheme中有特殊的含义;它允许您从准引用中“逃脱”。在这种情况下,您没有使用 quasiquote,因此这里的逗号在 Scheme 中没有任何意义。

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

    【讨论】:

    • 错误消息实际上是让我绊倒的原因。 Google 也没有帮助解决此错误消息。
    【解决方案2】:

    在这部分:

    (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)
    

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

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

    MIT 方案不支持可选参数,只有像 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 1,它就变成了一个 multi arity 函数:

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

    【讨论】:

    • 很好地解释了quoteunquote。虽然语法正确,(lambda (ini f) (f x)) 还有其他看不见的问题 - 我的答案中有详细信息:D
    • MIT 方案确实支持可选参数;不过,似乎没有办法指定它们的默认值,这可能就是您的意思。 /not-a-nitpick :)
    • @WillNess 剩余参数和可选参数是有区别的。
    • 确实,the documentation 两者都有。
    【解决方案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
    

    【讨论】:

    • 感谢您提供如此详细的回答。在检查了第一个答案之后,这正是我在回到这里之前在我的代码中修复的内容。
    • @AyushGoel 不客气。我做了一些额外的cmets。我希望您能愉快地学习有史以来最好的语言之一。如果您需要更多帮助,请告诉我们:)
    • 我喜欢set! 的两个说明 - 第一个显示 Reader 和第二个 - 状态 monad 的计算模型(没有变化与变化)。 OP 的 text 似乎表明预期的构图方向是通常的从右到左。最后一个composex 可以调整以适应它,并且还允许空组合:(composex . fs) | null? fs = (\x.x) | null? (cdr fs) = (car fs) | else = (\x.(f (`(composex . ,(cdr fs)) x)))。剩下的问题是,如何使用从左到右的reducex 来制作从右到左的构图?
    猜你喜欢
    • 2020-08-04
    • 1970-01-01
    • 2011-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-10
    相关资源
    最近更新 更多