使用foldr,它在Scheme中实现(使用lambda - foldr1)和(within lambda - foldr2)

时间:2014-05-13 20:52:47

标签: lambda scheme fold

我有两个版本的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究竟出了什么问题?

2 个答案:

答案 0 :(得分:2)

实际上,两个版本的foldr"与lambda"和#34;没有lambda"是相同的,版本"没有"对于版本"只是语法糖,",它们完全等效(除了参数的顺序发生变化,但没有什么)。

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

foldr

也许您感到困惑,因为(lambda (acc x) (cons (proced acc) x)) (let ((acc x) x) (cons (proced acc))) 表单可以let表示,如here所示。但事实恰恰相反!您无法将lambda替换为lambda ...除非它恰好返回let,但这正是您要避免的。< / p>

答案 1 :(得分:1)

那么什么是程序?我们知道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> 

将其视为英语的简化。我们说&#34;它&#34;&#34;而不是&#34;它是&#34;但他们的意思是一样的。这也是两种编写程序的方法。我将为您提供另外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

记住。它不是发送到lambda过程的foldr表单,因为它已经过评估并且是一个过程。就像在定义过程后评估表示过程的符号一样。它与fold1中的cons表达式(fold1 + 0 (cons 1 (cons 2 (cons 3 '()))))几乎完全相同,因为在应用fold1时{ {1}}评估为#,0评估为+(数字自我评估),0评估为(cons 1 (cons 2 (cons 3 '())))