构建列表(错误 - 需要一个过程)球拍/方案



试图创建一个产生 n x n 板的函数

(new-board 2)

应该生产

(list (make-posn 0 0) (make-posn 0 1) (make-posn 1 0) (make-posn 1 1))

我的代码的当前演绎如下:

(define (new-board y)
    (build-list y (lambda (x) (build-list x (make-posn y x))))
      )

我很确定它会起作用,但鉴于我目前在球拍方面的知识和经验,我找不到错误。

我输入:

> (new-board 3)

并得到错误:

build-list: expects a procedure (arity 1); given (make-posn 3 0)

我是否通过在构建列表中调用构建列表来犯下令人发指的罪行?请让我知道。谢谢!

关于此过程:

(define (new-board y)
    (build-list y (lambda (x) (build-list x 
                                          (make-posn y x))))) ;error!

让我们看看build-list接收的参数是什么。第一个参数是 y ,一个数字,第二个参数是一个过程,但您传递的是计算结果 make-posn ,这不是一个过程,而是一个值。这就是您遇到错误的原因。

编辑 1 :

现在我明白你的意图了。我能想到一个解决方案,但它比你想象的要详细一些:

(define (new-board n)
  (flatten
   (map (lambda (x)
          (map (lambda (y)
                 (make-posn x y))
               (build-list n identity)))
        (build-list n identity))))
(define (flatten lst)
  (if (not (list? lst))
      (list lst)
      (apply append (map flatten lst))))

以下是它的工作原理:

  • build-list 仅用于生成从 0n-1 的数字,我将identity作为过程传递,因为每个数字不需要进一步处理
  • 对于列表中的每个数字,我们还想生成另一个列表,同样从0n-1,因为板中的所有坐标都是必需的。例如,如果n为 3,则坐标'((0 0) (0 1) (0 2) (1 0) (1 1) (1 2) (2 0) (2 1) (2 2))
  • 我正在使用map中的map来构建嵌套列表,这是从这里借来的技术(请参阅:"嵌套映射"(
  • 最后,我不得不扁平化生成的列表,这就是flatten所做的(否则,我们将以列表列表结束(

编辑 2 :

想想看,我找到了一种更简单的方法,更接近你的想法。请注意,flatten过程是不可避免的:

(define (new-board n)
  (flatten
   (build-list n
               (lambda (x)
                 (build-list n
                             (lambda (y)
                               (make-posn x y)))))))

现在,当您键入以下内容时:

(new-board 2)

结果符合预期:

(#(struct:posn 0 0) #(struct:posn 0 1) #(struct:posn 1 0) #(struct:posn 1 1))

如果您查找 build-list 1 的签名(合约(,您会看到它是

 build-list : Nat (Nat -> X) -> (listof X)

因此,它需要一个(自然(数字,然后是一个函数,该函数期望一个自然数并返回您希望包含在列表中的类型 (X( 的元素。因此,在您的情况下,您希望 X 对于您向build-list进行的每个调用的特定类型(每种情况下可能不同(。在内build-list的情况下,看起来您正在尝试列出posn。但是,(make-posn y x)会立即进行单个posn,并且不是build-list期望的功能。因此,正如你提供一个(lambda (x) ...)外部函数build-list一样,你也应该提供一个(lambda (...) ...)到内部函数的函数。

为第一个lambda的参数选择名称x可能会有点令人困惑。我可能会做的是将new-board函数参数的名称更改为 N,因为您似乎想要创建一个包含 N 行(和列(的板。第一个build-list的目的是创建这些行中的每一行(或列,具体取决于您希望如何看待它(。因此,如果您有:

 (define (new-board N)
   (build-list N (lambda (x) ...)))

然后你像这样使用它:

 (new-board 5)

它将减少/简化/评估如下:

 ==> (build-list 5 (lambda (x) ...))
 ==> (list ( (lambda (x) (build-list ... x ...))  0 )
           ( (lambda (x) (build-list ... x ...))  1 )
           ( (lambda (x) (build-list ... x ...))  2 )
           ( (lambda (x) (build-list ... x ...))  3 )
           ( (lambda (x) (build-list ... x ...))  4 )
 ==> (list (build-list ... 0 ...)
           (build-list ... 1 ...)
           (build-list ... 2 ...)
           (build-list ... 3 ...)
           (build-list ... 4 ...))

所以,嵌套build-list没有错。看看你现在是否可以弄清楚如何在当前行固定为特定x值后让内部构建列表生成 posn 列表。

顺便说一下,如果你被允许使用完整的 Racket,有一种很好的方法可以用for循环来表达计算:

(define (new-board n)
  (for*/list ([i n]
              [j n])
    (make-posn i j)))

获得相同结果但采用不同方法的另一种方法是使用带有 quotientremainder 的算术技巧。

(define (new-board n)
  (build-list (* n n)
              (lambda (k)
                (make-posn (quotient k n)
                           (remainder k n)))))

最新更新