Racket 中多个值和普通元组之间的区别?

Difference between multiple values and plain tuples in Racket?

Racket 或 Scheme 中的 valueslistcons 有什么区别?什么时候使用一个比另一个更好?例如,如果 quotient/remainder returns (cons _ _) 而不是 (values _ _) 会有什么缺点?

这也与你的编程风格有关。如果您使用 values,那么它通常意味着您想要显式 return n 值。使用 conslistvector 通常意味着您想要 return 一个包含某些内容的值。

总有pros/cons。对于 values:它可能在某些实现上使用较少的内存。调用者需要使用 let-values 或其他多值特定语法。 (我希望我可以像 CL 一样使用 let。)

对于 cons 或其他类型:您可以使用 letlambda 来接收 returning 值。您需要使用 car 或其他程序显式解构它以获得您想要的值。

何时使用哪个?再次取决于您的编程风格和具体情况,但如果 returning 值不能在一个对象中表示(例如商和余数),那么最好使用 values 来制作程序的含义更清楚。如果 returning 值是一个对象(例如一个人的姓名和年龄),那么最好使用 cons 或其他构造函数(例如 record)。

它们在 Scheme 和 Racket 中语义相同。在两者中,您都需要知道 return 如何使用它。

values 连接到 call-with-values 并且 let-values 等特殊形式只是此过程调用的语法糖。用户需要知道结果的形式才能使用 call-with-values 来使用结果。 return 通常在堆栈上完成,调用也在堆栈上。在 Scheme 中支持 values 的唯一原因是生产者 return 和消费者调用之间没有开销。

使用cons(或list)用户需要知道return的数据结构是什么样的。与 values 一样,您可以使用 apply 而不是 call-with-values 来做同样的事情。作为 let-values(以及更多)的替代品,制作 destructuring-bind 宏很容易。

在 Common Lisp 中则完全不同。如果您有更多信息要提供,您可以始终使用值,如果用户只想使用第一个值,则仍然可以将其用作正常过程。因此,对于 CL,您不需要提供 quotient 作为变体,因为 quotient/remainder 也可以正常工作。只有当您使用采用多个值的特殊形式或过程时,该过程才会 return 更多值以与 Scheme 相同的方式工作。这使得 values 在 CL 中成为比 Scheme 更好的选择,因为您可以编写一个而不是更多的过程。

在 CL 中,您可以像这样访问哈希:

(gethash 'key *hash* 't)
; ==> T; NIL

如果您不使用第二个值 returned,您将不知道 T 是默认值还是找到的实际值。在这里,您会看到第二个值,表明在散列中找不到密钥。如果您知道只有数字,通常您不会使用该值,默认值已经表明未找到密钥。在球拍中:

(hash-ref hash 'key #t)
; ==> #t

在 racket 中 failure-result 可以是一个 thunk,所以你可以通过,但我敢打赌它会 return 多个值,而不是如果值确实像 CL 中那样工作。我假设 CL 版本和 Scheme 有更多的内部管理,作为一种极简语言,可能不想给实现者额外的工作。

2002 年,George Caswell 在 comp.lang.scheme 中提出了这个问题。 接下来的话题很长,但有很多见解。讨论 显示意见分歧。

https://groups.google.com/d/msg/comp.lang.scheme/ruhDvI9utVc/786ztruIUNYJ

我当时的回答:

>    What are the motivations behind Scheme's multiple return values feature?
> Is it meant to reflect the difference in intent, or is there a
> runtime-practical reason?

I imagine the reason being this.

Let's say that need f is called by g. g needs several values from f.
Without multiple value return, f packs the values in a list (or vector),
which is passed to g. g then immediately unpacks the list.

With multple values, the values are just pushed on the stack. Thus no
packing and unpacking is done.

Whether this should be called an optimization hack or not, is up to you.

--
Jens Axel Søgaard

We don't need no side-effecting         We don't need no allocation
We don't need no flow control           We don't need no special-nodes
No global variables for execution       No dark bit-flipping for debugging
Hey! did you leave the args alone?      Hey! did you leave those bits alone?
(Chorus)                  -- "Another Glitch in the Call", a la Pink Floyd

编辑:发帖前错过了 Alexis 对同一主题的评论

在列表中使用多个 return 值的一个经常被忽视的实际优势是 Racket 的 compose "just works" 具有 return 多个值的功能:

(define (hello-goodbye name)
  (values (format "Hello ~a! " name)
          (format "Goodbye ~a." name)))

(define short-conversation (compose string-append hello-goodbye))

> (short-conversation "John")
"Hello John! Goodbye John."

compose 生成的函数会将 hello-goodbye 编辑的两个值 return 作为两个参数传递给 string-append。如果您正在编写具有大量组合的函数式代码,这非常方便,并且比使用 call-with-values 等在您周围显式传递值要自然得多。