邪道Scheme

2022-12-13

因为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 2))
        (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))


Email: i (at) mistivia (dot) com