试图创建一个产生 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
仅用于生成从0
到n-1
的数字,我将identity
作为过程传递,因为每个数字不需要进一步处理 - 对于列表中的每个数字,我们还想生成另一个列表,同样从
0
到n-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)))
获得相同结果但采用不同方法的另一种方法是使用带有 quotient
和 remainder
的算术技巧。
(define (new-board n)
(build-list (* n n)
(lambda (k)
(make-posn (quotient k n)
(remainder k n)))))