用 Racket 构建地图

Building map with Racket

我在练习构建地图功能。

我想出了以下代码:

#lang racket

(define (map procedure items)
  (if (null? items)
      empty
      (cons (procedure (car items)) (map procedure (cdr items)))))

我试过了,效果很好:

(map add1 '(1 2 3))

>> '(2 3 4)

然后我试了一下,效果也不错:

(define (scale-by-3 item)
  (* 3 item))

(map scale-by-3 '(1 2 3))

>> '(3 6 9)

在那之后,我决定推广比例程序:

(define (scale-by-factor factor item)
  (* factor item))

这个按比例缩放的函数有效:

(scale-by-factor 3 4)

>> 12

但是当我尝试将它与地图一起使用时:

(map (scale-by-factor 2 item) '(1 2 3))

我收到以下错误:

item: unbound identifier in module in: item

我该如何解决这个问题?有没有不用lambda就能解决的方法?

失败是因为 item 在您调用它时不存在 - 它在遍历列表时由 map 作为参数传递。要解决此问题,请执行以下操作:

(map (lambda (item) (scale-by-factor 2 item))
     '(1 2 3))

或者我们可以使用 curry 编写一个更好的替代方案,它创建一个 lambda 期望缺少的 item 参数:

(map (curry scale-by-factor 2)
     '(1 2 3))

首先,item确实没有绑定。你没有在任何地方定义它。

你想要的是 scale-by-factor 的部分应用。此函数采用两个参数并计算结果。但是,如果您仅将它部分应用于一个参数,它将计算为一个函数,该函数采用另一个参数并计算最终结果。

您可以在 Racket 中使用 curry 实现此目的,如 here.

(define (map procedure items)
  (if (null? items)
    empty
    (cons (procedure (car items)) (map procedure (cdr items)))))

(define (scale-by-factor factor item)
  (* factor item))

(map (curry scale-by-factor 5) '(1 2 3))

之所以叫curry是因为this

这里有很棒的解决方案。我将提供一些替代方案,以便您可以看到更多的方法来做同样的事情

您可以用柯里化形式定义 scale-by-factor 函数

(define ((scale-by-factor x) y)
  (* x y))

; note it needs two applications to get the computed result now
((scale-by-factor 3) 4)
; => 12

您可以使用 在您的其他问题之一上定义尾递归映射

(define (map f xs)
  (let iter ([xs xs] [k identity])
    (if (empty? xs)
        (k empty)
        (let ([v (f (car xs))])
          (iter (cdr xs) (λ (rest) (k (cons v rest))))))))

(map (scale-by-factor 2) '(1 2 3))
; => '(2 4 6)

在Racket中for/list可以用来创建地图函数:

(define (mymap proc lst)
  (for/list ((item lst))
    (proc item)))

(mymap add1 '(1 2 3))
; =>'(2 3 4)