如何在球拍中有多个返回值?

Posted

技术标签:

【中文标题】如何在球拍中有多个返回值?【英文标题】:How do I do anything with multiple return values in racket? 【发布时间】:2013-12-31 15:30:12 【问题描述】:

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

【问题讨论】:

docs.racket-lang.org/reference/values.html ...? 是的,我提到了身体中的那些形式,但我的问题是那些真的是我们所拥有的最好的吗?如果是这样,当一个普通的旧列表可以更好、更轻松地完成相同的事情时,为什么我们甚至多个返回值?而且我仍然不知道如何定义一个过程接受返回多个值的结果。 您将此过程定义为带有多个参数的简单 lambda,并通过 call-with-values 调用它:(call-with-values (lambda() (values 1 2)) (lambda(a b) ....)) 哇...这就像....正是我在谷歌上搜索的内容。好工作!另外,整洁,我不知道thunk 函数。我一直在制作自己的 lambdas 【参考方案1】:

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

【讨论】:

这样我们就不用把它们打包成一个列表了。 那么我的问题是,多个返回值比列表有什么优势?似乎列表可以做同样的事情,除了以更符合语言的方式,并且更易于使用。例如,(编辑:这里有一个实现,无法格式化......哎呀!另外,这个评论是在威尔的前面,但由于技术困难,我不得不删除它并重新发布它) quotient/remainder 不返回列表。它返回两个整数。试试(list (quotient/reminder 10 3))(call-with-values (lambda () (quotient/reminder 10 3)) list) 在 lisp 中抱怨太多的 cons 单元格,在我看来,有点像抱怨沙漠中的一桶沙子太多。在极其抽象的球拍世界中,原语被装箱、拆箱、调整大小、包装,并且通常以所有其他方式“它只是工作”,这似乎很奇怪,这本质上是一个实现细节,不仅是完全可见,但是标准库经常使用的一个重要概念,您也必须使用它。但是,我现在正在玩肥皂盒。感谢您提供信息。 您的答案是唯一直接且中肯的答案。如何从函数中解压缩多个值?就是这样。谢谢!【参考方案2】:

虽然我可能遗漏了一些 Scheme 历史和其他细微差别,但我会给出我实际的答案。

首先,一条经验法则是,如果您需要返回 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。

【讨论】:

【参考方案3】:

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

【讨论】:

values 保证创建结构,如list,还是可以通过 Sylwester 的答案中详述的堆栈机制实现? @WillNess 这是一个实现细节。从语义上讲,它是一个结构,因为它一直保持完整。 “语义上在一起”不是一个结构。 :) “结构”有一个非常具体的含义 - 一个缺点单元分配。语义是正交的。当然,这两个值在语义上是相连的,说它们导致结构的创建是完全不同的。 :) @WillNess 结构是数据的表示。它不仅限于 cons 单元;结构也可以在堆栈上表示。见en.wikipedia.org/wiki/Data_structure。 我说的是 Lisp 的说法。 :) 而且我们正在比较valueslist... :) 当然,如果一个实现执行使用分析并在堆栈上分配临时conses,那确实是一个非常好的实现。跨度> 【参考方案4】:

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

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

对于多个值,这些值在堆栈上,(call-with-values (lambda () ... (values x y z)) (lambda (x y z) ...) 只检查数字是否正确。如果没问题,您只需应用下一个过程,因为堆栈的参数都是从上一次调用中设置的。

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

【讨论】:

以上是关于如何在球拍中有多个返回值?的主要内容,如果未能解决你的问题,请参考以下文章

如何在球拍中本地更改阅读规则?

如何根据多个单元格中的值自动填充

如何通过命令行将文件加载到球拍中?

如何修正当AI打开左侧球拍时右侧球拍不起作用的问题?在canvs javascript的乒乓球比赛

如何在另一个函数中使用嵌套函数作为球拍语言的参数?

如何在球拍中将左右箭头写为字符?