在scheme中自己实现list comprehension
scheme并不提供list comprehension特性,但是
在stackoverflow上可以看到几个提供list comprehension的库(Swindle已经是个语言了):
- Swindle is primarily a CLOS emulator library, but it has list comprehensions too. I've used them, they're convenient, but the version I used was buggy and incomplete. (I just needed generic functions.)
- However, you probably want SRFI-42. I haven't used it, but it HAS to have fewer bugs than the Swindle list comprehensions.
可是两个库提供的list comprehension所用的语法我看着都不爽......肿木办?自己写个呗:scheme is a programable programming language!
(define (flat-map f xs)
(apply append (map f xs)))
(define-syntax list-of
(syntax-rules (<- :=)
[(_ expr (v <- a-list) rule ...);folk
(flat-map (lambda (v) (list-of expr rule ...)) a-list)]
[(_ expr (v := v-expr) rule ...)
(let ([v v-expr])
(list-of expr rule ...))]
[(_ expr P rule ...);filter(prune)
(if P
(list-of expr rule ...)
'())]
[(_ expr);base case
(cons expr '())]))
(apply append (map f xs)))
(define-syntax list-of
(syntax-rules (<- :=)
[(_ expr (v <- a-list) rule ...);folk
(flat-map (lambda (v) (list-of expr rule ...)) a-list)]
[(_ expr (v := v-expr) rule ...)
(let ([v v-expr])
(list-of expr rule ...))]
[(_ expr P rule ...);filter(prune)
(if P
(list-of expr rule ...)
'())]
[(_ expr);base case
(cons expr '())]))
这个list-of隐含了对表达式从左到右的处理顺序.
来个辅助函数range,生成从low到up的整数list:
(define (range low up)
(if (> low up) '() (cons low (range (+ 1 low) up))))
(if (> low up) '() (cons low (range (+ 1 low) up))))
>(range 1 10)
=>(1 2 3 4 5 6 7 8 9 10)
比如求边长整数且最大边长不超过100的所有直角三角形 :
[list-of (list x y z) [x <- (range 1 100)] [y <- (range 1 100)] [z <- (range 1 100)]
(< x y z) (= (* z z) (+ (* x x) (* y y)))]
(< x y z) (= (* z z) (+ (* x x) (* y y)))]
结果:
{{3 4 5}
{5 12 13}
{6 8 10}
{7 24 25}
{8 15 17}
{9 12 15}
{9 40 41}
{10 24 26}
{11 60 61}
{12 16 20}
{12 35 37}
{13 84 85}
{14 48 50}
{15 20 25}
{15 36 39}
{16 30 34}
{16 63 65}
{18 24 30}
{18 80 82}
{20 21 29}
{20 48 52}
{21 28 35}
{21 72 75}
{24 32 40}
{24 45 51}
{24 70 74}
{25 60 65}
{27 36 45}
{28 45 53}
{28 96 100}
{30 40 50}
{30 72 78}
{32 60 68}
{33 44 55}
{33 56 65}
{35 84 91}
{36 48 60}
{36 77 85}
{39 52 65}
{39 80 89}
{40 42 58}
{40 75 85}
{42 56 70}
{45 60 75}
{48 55 73}
{48 64 80}
{51 68 85}
{54 72 90}
{57 76 95}
{60 63 87}
{60 80 100}
{65 72 97}}
但是这样做是先生成100*100*100的list然后在检查过滤,效率太低,可以调整一下表达式顺序:
(list-of (list x y z)
[x <- (range 1 100)]
[y <- (range 1 100)]
(< x y);prune,z is not bound yet
[z := (+ (* x x) (* y y))];a more effect but less decarative way
(<= z 100));filte
[x <- (range 1 100)]
[y <- (range 1 100)]
(< x y);prune,z is not bound yet
[z := (+ (* x x) (* y y))];a more effect but less decarative way
(<= z 100));filte
这下快多了,但是这些顺序细节不应该暴露出来,应该再写个partial evaluator根据变量出现的先后自动调整表达式顺序,然后在交给list-of去计算.以后蛋疼的时候再弄吧......
还有如果再蛋疼,可以把它搞成兼容惰性求值的,[x <- a-generator]酱紫.