如何在Racket中以编程方式展开let*系列函数
背景
此问题与一项作业略有相关,但我并非寻求他人代做。
我的作业不鼓励过度使用let、let*及类似形式,以此引导我们用“函数式思维”思考。
我知道let表达式可以展开成仅用lambda的形式,示例如下:
#lang racket ; 使用 let (let ([x 1] [y 2]) (+ x y)) ; 改用 lambda ((lambda (x y) (+ x y)) 1 2)
由此我产生了一个疑问。
问题
有没有办法通过编程完成这种展开?只需要处理用到let、let*等的部分即可。
示例效果如下:
> (expand-let '(let ([x 1] [y 2]) (+ x y))) '((lambda (x y) (+ x y)) 1 2)
如果能支持Racket所有“局部绑定”形式:let、let*、letrec、let-values、let*-values、letrec-values等,那就更好了。
正如评论所说,这对我的作业没实际用处,但作为Racket新手,我把它当成个人项目来研究。欢迎任何建议。
已尝试方案
- 有回答讨论过
let和letrec的手动展开方法,但只是手动步骤,没法自动完成。 - 有回答对
let、let*和letrec的作用域做了详细讨论,帮我理解了它们的差异,但没解决自动展开的问题。 - 有一个类似的问题和我的需求接近,但存在几点不同:
- 我不需要执行代码,只需要展开那些属于“语法糖”的局部绑定形式
- 那个问题用的是
#lang scheme,而我用的是#lang racket - 有评论提到沙箱求值,但我不确定这是否符合需求,它看起来是用来控制代码执行的,而我需要的是把局部绑定形式转成
lambda形式 - 该问题的回答提到了
expand函数,这似乎是个方向,但我不知道怎么继续推进。比如执行下面的代码:> (syntax-e (expand '(let ([x 1] [y 2]) (+ x y)))) '(#<syntax:/Applications/Racket v8.11.1/collects/racket/private/qq-and-or.rkt:193:51 let-values> #<syntax (((x) (quote 1)) ((y) (quote 2)))> #<syntax:/Applications/Racket v8.11.1/collects/racket/private/kw.rkt:1263:25 (#%app + x y)>)
自行编写代码(非完整解决方案)
作为新手,我尝试自己写了解决方案,但遇到了瓶颈,欢迎大家提建议。
(define (expand-let atomized-expr) (expand-let-helper (syntax->datum (expand atomized-expr)) (lambda (result) result))) (define (expand-let-helper parse-tree return) (define (expand-let-bindings bindings) (map (lambda (binding) (list (caar binding) (expand-let (cadr binding)))) bindings)) (cond [(null? parse-tree) (return '())] [(not (pair? parse-tree)) (return parse-tree)] [(eq? (car parse-tree) '#%app) (expand-let-helper (cdr parse-tree) (lambda (result) (return result)))] [(eq? (car parse-tree) '#%top) (return (cdr parse-tree))] [(eq? (car parse-tree) 'let-values) (let* ([bindings (cadr parse-tree)] [body (cddr parse-tree)] [expanded-bindings (expand-let-bindings bindings)] [vars (map car expanded-bindings)] [vals (map cadr expanded-bindings)]) (expand-let-helper body (lambda (result) (return `((lambda (,@vars) ,@result) ,@vals)))))] [else (expand-let-helper (cdr parse-tree) (lambda (cdr-result) (expand-let-helper (car parse-tree) (lambda (car-result) (return (cons car-result cdr-result))))))]))
执行结果如下:
; 成功案例 > ((lambda (x y) (let ((a (+ x y)) (b (- x y))) (* a b))) 1 2) -3 > (expand-let '((lambda (x y) (let ((a (+ x y)) (b (- x y))) (* a b))) 1 2)) '((lambda (x y) ((lambda (a b) (* a b)) (+ x y) (- x y))) '1 '2) > (eval (expand-let '((lambda (x y) (let ((a (+ x y)) (b (- x y))) (* a b))) 1 2))) -3 ; 失败案例(评论中指出) > (expand-let '(let ([n (random -10 10)]) (cond [(positive? n) 1] [(negative? n) -1] [else 0]))) '((lambda (n) (if (positive? n) '1 (if (negative? n) '-1 '0))) (random '-10 '10))
内容的提问来源于stack exchange,提问作者Joshua Shew
相关产品推荐
相关产品推荐

