如何在球拍中执行具有多个返回值的操作?

问题描述 投票:0回答:5

似乎为了在 Racket 中使用多个返回值,我必须使用

define-values
或使用
(call-with-values (thunk (values-expr)) list)
将它们收集到列表中。在后一种情况下,如果只是必须将它们收集到一个列表中,为什么有人会选择返回多个值而不是列表呢?此外,这两种方法在大多数代码中都非常冗长且笨拙。我觉得我一定误解了有关多重返回值的一些非常基本的东西。就此而言,我如何编写一个过程接受多个返回值?

racket
5个回答
11
投票

虽然我可能错过了一些计划历史和其他细微差别,但我会给你我实用的答案。

首先,一个经验法则是,如果您需要返回 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 邮件列表上有一个长而有趣的讨论。


5
投票

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 中这是无效代码。所以是的,它看起来像是一个无关的功能,最好完全避免。


4
投票
使用

list

 作为消费者会破坏多个值的目的,因此在这种情况下,您可以一开始就使用列表。多个值实际上是一种优化方式。

在语义上返回一个列表和多个值是相似的,但是如果您在列表中返回许多值,则需要创建 cons 单元格来创建列表,并解构访问器以获取另一端的值。然而,在许多情况下,您不会注意到性能差异。

对于多个值,值位于堆栈上,

(call-with-values (lambda () ... (values x y z)) (lambda (x y z) ...)

仅检查数字以查看其是否正确。如果没问题,您只需应用下一个过程,因为堆栈的参数都是从上一次调用中设置的。

您可以围绕此制作语法糖,一些流行的语法糖是

let-values

SRFI-8 receive 是稍微简单的一个。两者都使用 call-with-values
 作为原语。


4
投票

values

 很方便,因为它

    检查返回的元素数量是否正确
  1. 破坏结构
例如,使用

(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)
 是等效的。


0
投票
这里的许多答案展示了如何使用

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
    
© www.soinside.com 2019 - 2024. All rights reserved.