如何在Racket中以normal order实现自定义if(不切换lazy语言)
在标准Racket中实现遵循正常序的
new-if 方法一:使用宏(最贴近正常序行为)
Racket的宏在代码展开阶段处理逻辑,不会提前求值参数,完美契合正常序「先展开、再按需求值」的特性。将new-if定义为宏即可解决问题:
#lang racket (define (p) (p)) (define-syntax new-if (syntax-rules () [(new-if test-expr then-expr else-expr) (cond (test-expr then-expr) (else else-expr))]))
执行(new-if (= 2 2) 5 (p))时,宏会先展开为(cond ((= 2 2) 5) (else (p)))。由于Racket的cond是特殊形式,仅会在测试条件为真时求值对应分支,因此(p)永远不会被执行,直接返回5。
方法二:手动延迟求值(Thunk)
若不想使用宏,可将需要延迟的分支包装为无参数函数(即Thunk),在需要时再调用。修改new-if的定义,让它接收Thunk作为分支参数:
#lang racket (define (p) (p)) (define (new-if test-expr then-thunk else-thunk) (cond (test-expr (then-thunk)) (else (else-thunk))))
调用时需将分支表达式包装为lambda:
(new-if (= 2 2) (lambda () 5) (lambda () (p)))
此时传递的是lambda对象,不会提前求值(p);当测试条件为真时,仅会调用(then-thunk)得到5,else-thunk永远不会被执行。
原代码问题分析
原代码中的new-if是普通函数,而Racket作为应用序语言,会在调用函数前先求值所有参数。执行(new-if (= 2 2) 5 (p))时,会先求值(p),而(p)是递归调用自身,直接陷入死循环。
Racket内置的if是特殊形式,遵循正常序求值规则:先测试条件,仅在条件为真时求值then分支、为假时求值else分支,不会提前求值所有分支。上述两种实现正是模拟了这种行为。
内容的提问来源于stack exchange,提问作者Mitchell Xu
相关产品推荐
相关产品推荐

