You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于《Racket指南》中rotate与shift-to宏的技术疑问

Racket中rotate和shift-to宏的逻辑解析

1. rotate宏的模式匹配与展开

rotate宏的模板是(rotate a c ...),其中...是Racket宏的可变参数匹配符:

  • a匹配调用时的第一个参数
  • c ...匹配调用时剩下的所有参数(支持0个或多个)

以(rotate 1 2 3)为例:

  • a绑定为1
  • c ...绑定为2 3(是参数序列,而非列表)

展开阶段,(c ... a)会将c的序列拼接在a之前,生成(2 3 1);(a c ...)则是1拼接c的序列,生成(1 2 3)。最终rotate展开为:

(shift-to (2 3 1) (1 2 3))

2. shift-to宏的模式匹配与映射

shift-to的模板是(shift-to (from0 from ...) (to0 to ...)),同样依赖...处理可变序列:

  • 第一个参数列表(from0 from ...)匹配传入的第一个列表,比如(2 3 1):
    • from0绑定为2
    • from ...绑定为3 1
  • 第二个参数列表(to0 to ...)匹配传入的第二个列表(1 2 3):
    • to0绑定为1
    • to ...绑定为2 3

模板中的(set! to from) ...是重复展开逻辑:会将from ...和to ...中的元素一一对应,生成多个set!表达式。这里from ...是3 1,to ...是2 3,所以展开为(set! 2 3) (set! 3 1)。

最终shift-to展开后的完整代码是:

(let ([tmp 2])
  (set! 2 3)
  (set! 3 1)
  (set! 1 tmp))

3. 为什么传入shift-to的顺序是(c ... a)在前?

这个顺序是为了避免赋值时的值覆盖问题:
我们要实现左旋转效果:1的值移到3,2移到1,3移到2。如果直接用(a c ...)作为from列表,先把1的值赋给2,会导致原来的2值丢失,无法再传递给3。

而(c ... a)作为from列表的逻辑是:

  1. 先保存from0(原始2的值)到临时变量tmp
  2. 依次用3覆盖2、1覆盖3(这一步用的都是未被修改的原始值)
  3. 最后用tmp(原始2的值)覆盖1

所有赋值都基于初始值,不会出现值丢失,最终实现正确的左旋转。

完整手动推导流程(以(rotate 1 2 3)为例)

  1. (rotate 1 2 3)匹配(rotate a c ...),a=1,c...=2 3
  2. 展开为(shift-to (2 3 1) (1 2 3))
  3. (shift-to (2 3 1) (1 2 3))匹配(shift-to (from0 from ...) (to0 to ...)),from0=2,from...=3 1,to0=1,to...=2 3
  4. 展开为(let ([tmp 2]) (set! 2 3) (set! 3 1) (set! 1 tmp))
  5. 执行后:1=2,2=3,3=1,完成左旋转

内容的提问来源于stack exchange,提问作者not_Huyn_Bin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 21:20:55