Scheme中为何无法如此使用values?有无不依赖call-with-values的多值绑定方法?
报错核心原因
Scheme对表达式返回值的接收有严格的上下文规则:
- 普通函数调用的实参位、变量定义/赋值的右值这类场景属于单值上下文,仅允许表达式返回0或1个值,一旦返回多个值就会直接抛出运行时错误。
- 只有专门为多值设计的语法位置(比如多值绑定结构的接收位、call-with-values的消费者函数入口)属于多值上下文,才能正确接收
values返回的多个结果。
你写的(avg (delist '(1 2 3)))中,(delist '(1 2 3))只是avg的第一个实参表达式,这个位置是单值上下文,根本不会把返回的1、2、3三个值当成avg的三个参数传入——运行时只会尝试取返回的第一个值作为avg的第一个实参,多余的返回值会直接触发异常,和显式写(avg 1 2 3)传三个离散参数的调用逻辑完全不同。很多刚接触Scheme多值返回的开发者都会误以为values会自动把多值展开成多个实参,实际上从R5RS到最新的R7RS标准都没有定义这种隐式行为,单值上下文下传入多值本身就是非法操作。
不使用call-with-values的实现方式
有两种成熟方案,都不需要显式调用call-with-values:
- 最简洁的方案:完全不需要通过values中转。你的核心需求是把列表元素作为离散参数传入可变参数函数avg,这本来就是
apply的原生能力,连自定义的delist函数都不需要:
(apply avg '(1 2 3)) ;; 直接返回2,和(avg 1 2 3)运行效果完全一致
- 如果确实需要先接收
values返回的固定个数多值,可以用Scheme标准的多值绑定语法let-values先把多值绑定到独立变量,再传入目标函数:
;; 适配固定3个返回值的场景 (let-values ([(a b c) (delist '(1 2 3))]) (avg a b c)) ;; 正常返回2
如果是变长多值的场景,没有比直接用apply处理原列表更简洁的方案,刻意绕路走values多值返回反而会增加不必要的逻辑复杂度。
内容的提问来源于stack exchange,提问作者WEI_CAO
相关产品推荐
相关产品推荐

