【问题标题】:using foldr which are implemented (with lambda - foldr1) vs. (withinout lambda - foldr2) in Scheme使用在 Scheme 中实现的 foldr(使用 lambda - foldr1)与(没有 lambda - foldr2)
【发布时间】:2014-05-13 20:52:46
【问题描述】:

我有两个版本的 foldr 实现

1) 使用 lambda 实现 foldr1

[foldr1 implementation]
(define foldr1
  (lambda (func lst end)
    (if (null? lst)
    end
    (func (car lst)
        (foldr1 func (cdr lst) end)))))

2) 没有 lambda 的 foldr2 实现

[foldr2 implementation]
 (define (foldr2 func end lst)
   (if (null? lst)
       end
       (func (car lst) 
         (foldr2 func end (cdr lst)))))

基本相同的行为,但是当使用 foldr 实现 myFunc 时,我有两个版本的 myFunc

... myFunc2 using foldr2(foldr implemented without lambda)...
 (define (myFunc2 proced lst)                            
  (foldr2                                                     
   (lambda (acc x) (cons (proced acc) x))   
   '() 
   lst)
 )

=> 工作正常。

但 myFunc1(应该与 myFunc2 行为相同):

...myFunc1 using foldr1 (foldr implemented with lambda)...
 (define (myFunc1 proced lst)
  (foldr1 
    (let ((acc x) x) (cons (proced acc)))
    '()
    lst)
  )

使用 lambda 实现的 foldr1 时,myFunc1 到底出了什么问题?

【问题讨论】:

    标签: lambda scheme fold


    【解决方案1】:

    实际上,foldr"with lambda" 和 "without lambda" 的两个版本是相同的,版本 "without" 只是版本 "with" 的语法糖,它们是完全等价的 (除了参数的顺序发生了变化,但这没什么)。

    现在,myFunc1 过程是另一回事。您以错误的顺序传递最后两个参数,但这是您的问题中最少的 - 您编写的 等效,let 只是返回最后一个表达式的值,并且将是当foldr 被调用时,只计算一次,而lambda 是一个匿名过程,它可以接收参数并将在foldr 中执行多次。更甚者,let 中的语法错误,无法编译。换句话说:这两行意味着完全不同的东西,let 版本(在修复语法错误之后)永远不会作为foldr 的函数参数:

    (lambda (acc x)  (cons (proced acc) x))    
    (let ((acc x) x) (cons (proced acc)))
    

    您可能会感到困惑,因为let 形式可以用lambda 表示,如here 所示。但事实并非如此!你不能用let替换lambda...除非它碰巧返回lambda,但这正是你要避免的。

    【讨论】:

    • 现在我知道我做错了什么,但我仍然想不出如何使用 foldr1 修复 myFunc1(使用 lambda 实现的 foldr)。让它工作......
    • @user1915570 没有什么可修复的,因为如果不隐式或显式传递lambda,就无法使用foldr。在 Scheme 中,所有过程(匿名或命名)都是 lambdas,foldr 需要一个过程作为参数。只需将lambda 作为参数传递给foldr1,就像您在foldr2 中所做的那样。
    • 在这个问题之前,因为 foldr1 , foldr2 是等价的,所以我想我可以直接在 myFunc1 中使用 foldr2 ,但是结果不是我想要的。所以我尝试了 myFunc2 ......但现在我明白了 myFunc1 是完全不同的故事......然后,直接使用 foldr1 回到 myFunc2 ......我可能会改变语法的顺序。但我想不出如何..
    • @user1915570 foldr1foldr2 等价的,但请注意您更改了参数的顺序...
    【解决方案2】:

    那么什么是程序?我们知道foldr 是一个过程,但是是什么让它成为一个过程?答案是lambda 表单。

    (define x 10)             ; second argument to define is 10, so x is 10
    x                         ; ==> 10 (surprised?)
    
    (define y (lambda () 10)) ; second argument to define is a lambda form
    y                         ; ==> #<procedure:y> (it might vary from implementation to implementation)
    (y)                       ; ==> 10
    

    x 和 y 都是变量,但 y 是 procedure。这意味着你不能像(x) 那样调用x,因为这会产生错误,但你可以调用(y),它会运行lambda 主体中的任何内容。

    现在Scheme 有一个编写过程y 的简单方法。你可以写:

    (define (y) 10)           ; it's the same as the y above
    y                         ; ==> #<procedure:y> 
    

    将其视为英语的简化。我们说“它是”而不是“它是”,但它们的意思是一样的。两种编写程序的方式也是如此。我再给你 3 个替代语法:

    (define sqrt (lambda (x) (* x x)))
    (define (sqrt x) (* x x))
    (sqrt 10)                  ; ==> 100
    
    (define list (lambda l l))
    (define (list . l) l)
    (list 1 2 3 4)             ; ==> (1 2 3 4)
    
    ;; notice its fold1 as in number 1, not lowercase L
    (define fold1 
      (lambda (fun acc lst)
        (let loop ((acc acc) (lst lst))
          (if (null? lst)
              acc
              (loop (fun (car lst) acc) (cdr lst))))))
    
    (define (fold1 fun acc lst)
        (let loop ((acc acc) (lst lst))
          (if (null? lst)
              acc
              (loop (fun (car lst) acc) (cdr lst)))))
    
    (fold1 cons '() '(1 2 3 4))    ; ==> (4 3 2 1)
    

    现在foldr 就像fold1 一样接受一个过程,因为它是第一个参数。这意味着你可以写:

    (foldr (let ()
             (define (my-add a b) (+ a b)) ; remember this is  too
             my-add) ; return my-add
           0 
           '(1 2 3 4 5)) ; ==> 15
    

    但是,如果您只是要给出过程名称,以便您可以按名称返回它。既然my-add 的函数部分是(lambda (a b) (+ a b))(你看到了吗?)为什么不直接说:

    (foldr (lambda (a b) (+ a b)) ; PS: just + would suffice
           0 
           '(1 2 3 4 5)) ; ==> 15
    

    记住。发送到foldr 过程的不是lambda 表单,因为它已经被评估并且是一个过程。就像你在定义一个代表一个过程的符号之后评估它一样。它与fold1 几乎相同,不要在表达式(fold1 + 0 (cons 1 (cons 2 (cons 3 '())))) 中获取带有cons 的表达式,因为在应用fold1+ 被评估为#,0 被评估为0 (数字自我评估)和(cons 1 (cons 2 (cons 3 '()))) 被评估为(1 2 3)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-11
      • 1970-01-01
      • 2011-06-30
      相关资源
      最近更新 更多