如何在球拍中使用多个返回值执行任何操作?

时间:2013-12-13 00:02:58

标签: racket

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

4 个答案:

答案 0 :(得分:7)

虽然我可能会遗漏一些计划历史和其他细微差别,但我会给你实际答案。

首先,一条经验法则是,如果您需要返回超过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实现中,尽管it could work otherwise)。

然而,当可读性优先于性能时,那么在现代Racket中使用liststruct,恕我直言更为实际。话虽如此,我确实为一次性私人助手功能使用了多个值。

最后,在Racket邮件列表上有一个long, interesting discussion

答案 1 :(得分:2)

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中,凭借其明确实用的,实用的,非金属的,非功能性的方法(他们关注每个额外的缺陷单元分配),它可以做得更多感觉,特别是因为它允许人们以“正常”方式调用这些程序,自动忽略“额外”结果,有点像

(let ([q (quotient/remainder 10 3)])
    (list q))

但是在Racket中这是无效的代码。所以是的,它看起来像一个无关紧要的功能,最好完全避免。

答案 2 :(得分:2)

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

语义上返回一个列表和几个值是相似的,但是在列表工作中返回许多值的地方就是创建cons单元以使列表和解构访问器在另一端获取值。但是,在许多情况下,你不会注意到性能上的差异。

使用多个值时,值会在堆栈中,而(call-with-values (lambda () ... (values x y z)) (lambda (x y z) ...)只会检查数字是否正确。如果没有问题,您只需应用下一个程序,因为堆栈有它的所有参数都是从前一次调用中设定的。

你可以围绕这个制作语法糖,一些流行的let-valuesSRFI-8 receive稍微简单一些。两者都使用call-with-values作为原始。

答案 3 :(得分:2)

values非常方便,因为它

  1. 检查返回的元素数是否正确
  2. destructures
  3. 例如,使用

    (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) vs (values 1 2)是等效的。