似乎为了在 Racket 中使用多个返回值,我必须使用
define-values
或使用 (call-with-values (thunk (values-expr)) list)
将它们收集到列表中。在后一种情况下,如果只是必须将它们收集到一个列表中,为什么有人会选择返回多个值而不是列表呢?此外,这两种方法在大多数代码中都非常冗长且笨拙。我觉得我一定误解了有关多重返回值的一些非常基本的东西。就此而言,我如何编写一个过程接受多个返回值?
虽然我可能错过了一些计划历史和其他细微差别,但我会给你我实用的答案。
首先,一个经验法则是,如果您需要返回 2 个或 3 个以上的值,请不要使用多个值,也不要使用列表。使用
struct
。这通常会更容易阅读和维护。
Racket 的
match
形式使得解构列表返回值变得更加容易——就像 define-values
: 一样简单
(define (f)
(list 1 2))
(match-define (list a b) (f))
(do-something-with a b)
;; or
(match (f)
[(list a b) (do-something-with a b)])
如果您有其他函数
g
,需要一个 (list/c a b)
,并且您想将其与 f
组合,那么如果 f
返回一个列表会更简单。如果都使用双元素 struct
也会更简单。 而我认为 call-with-values
是一种尴尬的混乱。
允许多个返回值是一个优雅的想法,因为它使返回值与参数对称。使用多个值也比列表或结构更快(在 Racket 的当前实现中,尽管它可以以其他方式工作)。
但是,当可读性比性能更重要时,那么在现代 Racket 中,使用
list
或 struct
可能更实用,恕我直言。话虽如此,我确实为一次性私有辅助函数使用了多个值。
最后,Racket 邮件列表上有一个长而有趣的讨论。
Racket doc 为我们提供了典型的示例,变相说明原因:
> (let-values ([(q r) (quotient/remainder 10 3)])
(if (zero? r)
q
"3 does *not* divide 10 evenly"))
"3 does *not* divide 10 evenly"
我们直接得到两个值,并在接下来的计算中分别使用它们。
更新: 在 Common Lisp 中,凭借其绝对实用的、彻底的、非功能性的方法(他们关心每个额外的 cons 单元分配),它更有意义,特别是因为它允许一个也以“正常”方式调用此类过程,自动忽略“额外”结果,有点像
(let ([q (quotient/remainder 10 3)])
(list q))
但在 Racket 中这是无效代码。所以是的,它看起来像是一个无关的功能,最好完全避免。
list
作为消费者会破坏多个值的目的,因此在这种情况下,您可以一开始就使用列表。多个值实际上是一种优化方式。在语义上返回一个列表和多个值是相似的,但是如果您在列表中返回许多值,则需要创建 cons 单元格来创建列表,并解构访问器以获取另一端的值。然而,在许多情况下,您不会注意到性能差异。
对于多个值,值位于堆栈上,
(call-with-values (lambda () ... (values x y z)) (lambda (x y z) ...)
仅检查数字以查看其是否正确。如果没问题,您只需应用下一个过程,因为堆栈的参数都是从上一次调用中设置的。您可以围绕此制作语法糖,一些流行的语法糖是
let-values
和SRFI-8 receive 是稍微简单的一个。两者都使用
call-with-values
作为原语。
values
很方便,因为它
(define (out a b) (printf "a=~a b=~a\n" a b))
然后
(let ((lst (list 1 2 3)))
(let ((a (first lst)) (b (second lst))) ; destructure
(out a b)))
即使
lst
有 3 个元素,
也能工作,但是
(let-values (((a b) (values 1 2 3)))
(out a b))
不会。
如果您想要与列表相同的控制和解构,您可以使用
match
:
(let ((lst (list 1 2)))
(match lst ((list a b) (out a b))))
注意结构的创建,例如
(list 1 2)
与
(values 1 2)
是等效的。
match
分解返回的列表 - 但您也可以使用
match/values
和其他一些形式(如 match-define-values
、)对多个返回值执行相同的操作match-let-values
和公司。
; demonstrate returning and matching complex values
(define (f) (values (list 1 2) 3))
(match/values (f)
[((list a b) c) (+ a c)]) ; 4
(match-define-values ((list a b) c) (f))
(println a) ; 1