为何SML中柯里化函数g的递归部分应用不会引发无限循环?
SML柯里化函数递归中部分应用的疑问解答
先看问题中的代码:
datatype pattern = WildcardP | VariableP of string | UnitP | ConstantP of int | ConstructorP of string * pattern | TupleP of pattern list fun g f1 f2 p = let val r = g f1 f2 (* 为什么这里不会触发无限循环? *) in case p of WildcardP => f1 () | VariableP x => f2 x | ConstructorP(_,p) => r p | TupleP ps => List.foldl (fn (p,i) => (r p) + i) 0 ps | _ => 0 end
关键原因:部分应用是创建函数,而非立即执行函数体
g是一个柯里化函数,它的类型可理解为:(unit -> int) -> (string -> int) -> pattern -> int,也就是分三次接收参数。
当执行val r = g f1 f2时,我们只给g传入了前两个参数f1和f2,并没有传入第三个参数p——这属于部分应用,此时SML并不会执行g的函数体(也就是let块里的递归逻辑),而是直接返回一个新的函数:这个函数只需要接收一个pattern类型的参数,就能完成g f1 f2 p的完整调用。
只有当后续调用r p的时候(比如处理ConstructorP的子模式、TupleP里的元素),才会真正触发g的函数体执行。而此时的递归是针对更小的pattern结构,最终会遇到WildcardP、VariableP这类基础 case 终止递归,完全不会出现无限循环的问题。
举个简单的类比:如果定义fun add x y = x + y,然后写val add5 = add 5,这里add5只是一个等待接收第二个参数的函数,并不会立刻计算出某个值,只有调用add5 3时才会执行加法逻辑。g f1 f2的行为和add 5完全一致,都是生成绑定了部分参数的新函数,而非触发函数体执行。
内容的提问来源于stack exchange,提问作者lamc
相关产品推荐
相关产品推荐

