for/list 是否做了不必要的反转?

Does for/list do an unnecessary reverse?

我第一次在 Macro Stepper 中四处闲逛,注意到 for/list 扩展为涉及名为 alt-reverse 的代码。 for/list 是否将每个项目都放在空列表的前面,然后将其反转?这看起来效率很低。

我写了个小测试:

(define (test n)
  (time
    (for/list ([x (in-range n)])
      (list x x)))
  (time
    (for/fold ([result '()])
              ([x (in-range n)])
      (cons (list x x) result)))
  (void))

确实,for/list 版本的运行时间大约是 for/fold 没有 reverse 的时间的 150%,差异显然完全是由于额外的垃圾收集:

> (test 500000)
cpu time: 1059 real time: 2079 gc time: 940
cpu time: 614 real time: 1231 gc time: 550
> (test 500000)
cpu time: 1060 real time: 3889 gc time: 907
cpu time: 770 real time: 1363 gc time: 699
> (test 500000)
cpu time: 1035 real time: 2479 gc time: 917
cpu time: 736 real time: 2535 gc time: 651

听起来我不应该养成打电话的习惯for/list。是否有更有效的方法以 "forward" 顺序制作列表(即最后评估的项目是列表中的最后一项)?

查看 Git 历史,我看到 for/list 避免 reverse 的更改是 committed, but it didn't work,因为与延续的微妙交互可能触发四边形-时间行为。 (我确实想知道即将转向 Chez Scheme 后端是否意味着 "when Racket one day gets a better implementation of continuations" 实际上可能很快就会出现。)

您可以按 "forward" 顺序构建列表,正如第一条提交消息所说,“cons 进入递归调用。” Tail Recursion and Recursion versus Iteration 上的球拍指南部分实际上详细讨论了 map 风格和 for/fold 风格方法之间的权衡。

此外,为了将来参考,Racket 社区更倾向于生活在非常活跃和友好的 racket-users mailing list 上,在某种程度上,Slack 频道比这里的 Stack Overflow 更活跃。