home · archive · links · projects

邪道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))

© Licensed under CC BY-NC-SA 4.0 if not specified otherwise.
Email: dzshy [at] outlook [dot] com