随時更新.
Common Lisp | Scheme | 備考 |
---|---|---|
atom | なし | (not (pair? obj))を使う |
consp | pair? | |
dolist | for-each | 記法は異なる |
mapcan | append-map! | SRFI 1 |
mapcar | map | |
multiple-value-bind | receive | SRFI 8, library syntax |
null | null? | |
nth | list-ref | |
nthcdr | list-tail | SRFI 1ではdrop |
progn | begin | |
rplaca | set-car! | |
rplacd | set-cdr! | |
setq | set! |
mutexやcondition variableのような同期機構ではなく、チャネル通信などの機構を使えということなのだと思うが、ロックを使ったほうが素直に実装できる場合もある。
幸いセマフォはあるようなので、セマフォでmutexやcondition variableを次のように書いた。
;;; mutex
(define (make-mutex)
(make-semaphore 1))
(define (mutex-lock! m)
(semaphore-wait m))
(define (mutex-unlock-primitive! m)
(semaphore-post m))
(define (mutex-unlock! m . rest)
(let ((condvar (if (null? rest) #f (car rest))))
(mutex-unlock-primitive! m)
(when condvar
(condition-variable-wait! condvar))))
;;; gate
(define (make-gate)
(make-semaphore 0))
(define (gate-wait! g)
(semaphore-wait g))
(define (gate-signal! g)
(semaphore-post g))
;;; condition variable
(define (make-condition-variable)
(list 'condition-variable '() (make-mutex)))
(define (condition-variable-gates cv)
(list-ref cv 1))
(define (condition-variable-mutex cv)
(list-ref cv 2))
(define (condition-variable-clear-gates! cv)
(set-car! (cdr cv) '() ))
(define (condition-variable-put-gates! cv val)
(set-car! (cdr cv)
(cons val (condition-variable-gates cv))))
(define (condition-variable-wait! cv)
(let ((mutex (condition-variable-mutex cv))
(gates (condition-variable-gates cv))
(new-gate (make-gate)))
(mutex-lock! mutex)
(condition-variable-put-gates! cv new-gate)
(mutex-unlock-primitive! mutex)
(gate-wait! new-gate)))
(define (condition-variable-signal! cv)
(let ((mutex (condition-variable-mutex cv))
(gates (condition-variable-gates cv)))
(mutex-lock! mutex)
(for-each (lambda (g) (gate-signal! g))
gates)
(condition-variable-clear-gates! cv)
(mutex-unlock-primitive! mutex)))
condition variableは使い捨てのmutexを用いて実装している。
これで、同期に関してSRFI 18で定められた手続きが使える。
調べてみると Racket ではペアへの破壊的な代入を許さないらしい。
代わりに mcons というデータ構造が用意されていてこちらは set-mcar! や set-mcdr! といった手続きで代入ができる。
R5RS に基づいて書かれた Scheme のプログラムを動かすにはペアを扱う手続きを mcons を使うものに定義し直す必要があるが、すでにそれをするパッケージが用意されている。
> (require r5rs)
とすると、set-car! や set-cdr! が定義され、cons で作ったペアに再代入できる。
]]>
(define (simple exp)
(define (flat lst op)
(append-map! (lambda (x)
(if (and (pair? x) (eq? (car x) op))
(flat (cdr x) op)
(list x)))
lst))
(define (constant-fold args init op)
(define (cf args num acc)
(cond ((null? args)
(values acc num))
((number? (car args))
(cf (cdr args) (op num (car args)) acc))
(else
(cf (cdr args) num (append! acc (list (car args)))))))
(cf args init '() ))
(define (simple-+ args)
(set! args (map simple args))
(set! args (flat args '+))
(receive (exps num) (constant-fold args 0 +)
(cond ((null? exps) num)
(else `(+ ,@args num)))))
(define (simple-* args)
(call/cc
(lambda (c)
(set! args (map simple args))
(set! args (flat args '*))
(map (lambda (x)
(when (and (number? x) (= x 0))
(c 0)))
args)
(set! args (constant-fold args 1 *))
(set! args (remove (lambda (x)
(and (number? x)(= x 1)))
args))
(if (= 1 (length args))
(car args)
`(* ,@args)))))
(define (simple-expt base ex)
(set! base (simple base))
(set! ex (simple ex))
(cond ((or (and (number? base) (= base 1))
(and (number? ex) (= ex 0)))
1)
((or (and (number? base) (= base 0))
(and (number? ex) (= ex 1)))
base)
(else
`(expt ,base ,ex))))
(define (simple-- arg)
(cond ((number? arg) (- arg))
((and (pair? arg)
(eq? (car arg) '+))
(simple-+ (map (lambda (x) (list '- x))
(cdr arg))))
((and (pair? arg)
(eq? (car arg) '*))
(simple-* (map (lambda (x) (list '- x))
(cdr arg))))
(else
`(- ,arg))))
(cond ((not (pair? exp)) exp)
((eq? (car exp) '-)
(simple-- (cadr exp)))
((eq? (car exp) '+)
(simple-+ (cdr exp)))
((eq? (car exp) '*)
(simple-* (cdr exp)))
((eq? (car exp) 'expt)
(simple-expt (cadr exp) (caddr exp)))
(else
`(,(car exp) ,@(map simple (cdr exp))))))
> (simple (deriv '(expt (* (sin x) (cos x)) 1/2) 'x)) => (* (expt (* (sin x) (cos x)) -1/2) (+ (* (cos x) (cos x)) (* (sin x) (- (sin x)))) 1/2)
`-‘ 演算子は単項演算子としてのみ使える。
]]>Scheme で微分するには次のようにする。
(define (deriv exp var)
(cond ((eq? exp var) 1)
((not (pair? exp)) 0)
((eq? (car exp) '-)
`(- ,(deriv (cadr exp) var)))
((eq? (car exp) '+)
`(+ ,(deriv (cadr exp) var) ,(deriv (caddr exp) var)))
((eq? (car exp) '*)
`(+ (* ,(deriv (cadr exp) var)
,(caddr exp))
(* ,(cadr exp)
,(deriv (caddr exp) var))))
((eq? (car exp) 'expt)
(let ((g (cadr exp))
(n (caddr exp)))
`(* (* ,n (expt ,g ,(- n 1))) ,(deriv g var))))
((eq? (car exp) 'sin)
`(* (cos ,(cadr exp)) ,(deriv (cadr exp) var)))
((eq? (car exp) 'cos)
`(* (- (sin ,(cadr exp))) ,(deriv (cadr exp) var)))
((eq? (car exp) 'tan)
`(* (+ 1 (expt ,exp 2)) ,(deriv (cadr exp) var)))
(else (error "not yet."))))
> (deriv '(expt (* (sin x) (cos x)) 1/2)) 'x) => (* (* 1/2 (expt (* (sin x) (cos x)) -1/2)) (+ (* (* (cos x) 1) (cos x)) (* (sin x) (* (- (sin x)) 1))))
`-‘ 演算子は単項演算子としてのみ使える。
]]>load で相対パスを使った場合は current-directory (デフォルトでは実行パス)を基点にロードパスが決まる。
ただ、”./filename” や “../filename” といった形式は使えなかった。
current-directory と結合してOSに丸投げするだけでいいのに、信じられない。
ロードされているファイルからそのファイルのあるディレクトリを基点に相対パスでロードする場合は load-relative を用いる。
(load-relative "relative-path")
その際、基点となるディレクトリは current-load-relative-directory 関数で取得できる。
]]>
CL-USER> (let (a b c) c)
NIL
のように let で束縛する値を省略できる.
省略された変数は nil に束縛される.
しかし,Scheme にはそのような省略記法はない.
そこで,マクロを使い Common Lisp のような略記法を導入する.
次のように書く.
(define-syntax old-let let)
(define-syntax %let
(syntax-rules ()
((_ (te ...) () body ...)
(old-let (te ...)
body ...))
((_ (te ...) ((id e) be ...) body ...)
(%let (te ... (id e)) (be ...) body ...))
((_ (te ...) (id be ...) body ...)
(%let (te ... (id '())) (be ...) body ...))))
(define-syntax let
(syntax-rules ()
((_ e1 e2 ...)
(%let () e1 e2 ...))))
Common Lisp と同様に束縛する値を省略した変数は空リストに束縛されるようにしている.
]]>
(define (copy-graph-sub ht graph)
(cond ((not (pair? graph)) graph)
((hash-table-exists? ht graph)
(hash-table-ref ht graph))
(else
(let ((p (cons #f #f)))
(hash-table-set! ht graph p)
(set-car! p (copy-graph-sub ht (car graph)))
(set-cdr! p (copy-graph-sub ht (cdr graph)))
p))))
(define (copy-graph graph)
(copy-graph-sub (make-hash-table) graph))
ハッシュテーブルの操作はSRFI-69に沿っている。
Gauche では hash-table-ref は hash-table-get, hash-table-set! は hash-table-put! と書くみたい。
(use gauche.threads)
として、 gauche.threads モジュールをロードする必要がある。
]]>