邪道Scheme
因爲Scheme和Lisp系的其他語言一樣,過於靈活,因此可以往語言裏面加入很多奇怪的東西。甚至,如果想用命令式的方式,像Python一樣編寫Scheme代碼,也是可以的。
這篇文章中的代碼都是爲GNU Guile和TinyScheme而寫的,這兩個解釋器都支持老式的Lisp宏。這種宏在Common Lisp當中更多見,並不衛生,也不屬於任何Scheme標準。但是大多數解釋器和編譯器都支持。
例如,假如想在Racket中運行,只需要在代碼前面加入下面這段語法就可以了:
#lang racket
(define-syntax define-macro
(lambda (x)
(syntax-case x ()
((_ (macro . args) body ...)
#'(define-macro macro (lambda args body ...)))
((_ macro transformer)
#'(define-syntax macro
(lambda (y)
(syntax-case y ()
((_ . args)
(let ((v (syntax->datum #'args)))
(datum->syntax y (apply transformer v)))))))))))
hello, world
Python代碼:
print('hello, world')
宏定義:
(define println
(lambda x
(apply display x)
(newline)))
最終效果:
(println "hello world")
Def
Python代碼:
def is_even(x):
if x % 2 == 0:
return True
return False
宏定義:
(define-macro (def form . body)
`(define ,form
(call/cc (lambda (return)
,@body))))
最終效果:
(def (is-even x)
(cond ((= 0 (modulo x))
(return #t)))
(return #f))
While
Python代碼:
i = 0
while True:
i = i + 1
if x % 2 == 0:
continue
print(i)
if (i > 10):
break
宏定義:
(define-macro (while condition . body)
(let ((loop (gensym)))
`(call/cc (lambda (break)
(letrec ((,loop (lambda ()
(cond (,condition
(call/cc (lambda (continue)
,@body))
(,loop))))))
(,loop))))))
最終效果:
(let ((i 0))
(while #t
(set! i (+ i 1))
(cond ((= (modulo i 2) 0)
(continue)))
(cond ((> i 10)
(break)))
(println i)))
For
Python代碼:
for i in range(0, 10):
print(i)
宏和工具函數定義:
(define (iter-get iter)
(cond ((list? iter)
(car iter))
(else
(iter 'get))))
(define (iter-next iter)
(cond ((list? iter)
(cdr iter))
(else
(iter 'next))))
(define (range start end)
(lambda (method)
(cond ((eq? 'get method)
(if (>= start end)
'()
start))
((eq? 'next method)
(range (+ 1 start) end)))))
(define-macro (for i range . body)
(let ((loop (gensym))
(iter (gensym)))
`(call/cc (lambda (break)
(letrec
((,loop (lambda (,iter)
(if (eq? (iter-get ,iter) '())
'()
(let ((,i (iter-get ,iter)))
(call/cc (lambda (continue)
,@body))
(,loop (iter-next ,iter)))))))
(,loop ,range))))))
最終效果:
(for i (range 0 10)
(println i))
Goto
這個還是算了吧!
Fizz Buzz!
Python寫出來是這樣:
for i in range(35):
if i % 15 == 0:
print("FizzBuzz")
continue
if i % 3 == 0:
print("Fizz")
continue
if i % 5 == 0:
print("Buzz")
continue
print(i)
用Scheme的話,加上上面的宏,幾乎可以一一對應:
(for i (range 1 35)
(cond ((= 0 (modulo i 15))
(println "FizzBuzz")
(continue)))
(cond ((= 0 (modulo i 3))
(println "Fizz")
(continue)))
(cond ((= 0 (modulo i 5))
(println "Buzz")
(continue)))
(println i))