自分勝手なgoshコマンドの作成

個人用のスクリプトを書く際には、便利なライブラリが全てuseされていると嬉しい。
(gauche.experimental.*は、これをuseするのが億劫になり使わないことが度々)
今までは逐一利用したいライブラリをuseしてきたけれど…面倒くさい。
特に他人と共有しようとするスクリプトでなければ、もっとわがままに振る舞っても良いのではないかと思った。
幸いgoshは-lオプションで事前に読み込むファイルを設定できる。

個人的な処理を行う際に起動するgoshコマンドをgosh-exとして作ってみた。
行う手順は以下のとおり

  • ~/.gosh-ex作成
  • gosh-ex コマンドを作成

.gosh-ex

useしておきたいライブラリなどをuseするなどする。

(use gauche.experimental.lamb)
(use gauche.experimental.ref)
(use gauche.experimental.app)
(use srfi-1)
(use util.match)
(use util.list)

gosh-exコマンド

以下のようなシェルスクリプトを作成

#!/bin/sh
gosh -I ~ -l ~/.gosh-ex $@

これでgosh-exで立ち上げたreplでは便利なライブラリがデフォルトでつかえるようになる。

clojureのカスケードマクロを導入

昨日書いていたコードの中でassoce-default相当の関数を書こうとした際に、
clojureの-?>が使いたくなった。-?>は結果をみて偽なら実行を途中で打ち切るという点ではand-let*と同様、
でもand-let*とは異なり結果に特に名前をつけたくないような場合に使いたくなる。
これがあるとassoc-defaultは以下のように書ける。

(define (assoc-default e alist) ;;elispなどにある
  (-?> (assoc e alist) cadr))

(assoc-default 'x '((x 10) (y 20) (z 30))) ; => 10
(assoc-default 'i '((x 10) (y 20) (z 30))) ; => #f

code

(use util.match)
(define-macro (-> init . rest)
  (fold (lambda (xs acc)
          (match xs
            [(fn . args) `(,fn ,acc ,@args)]
            [fn `(,fn ,acc)]))
        init rest))

(define-macro (->> init . rest)
  (fold (lambda (xs acc)
          (match xs
            [(fn . args) `(,fn ,@(append args (list acc)))]
            [fn `(,fn ,acc)]))
        init rest))

(define-macro (-?> init . rest)
  (let1 tmp (gensym)
    (fold (lambda (xs acc)
            (match xs
              [(fn . args) 
               `(let1 ,tmp ,acc
                  (and ,tmp (,fn ,tmp ,@args)))]
              [ fn `(let1 ,tmp ,acc (and ,tmp (,fn ,tmp)))]))
          init rest)))

;; (macroexpand '(-> 10))
;; (macroexpand '(->> 10))
;; (macroexpand '(-?> 10))
;; (macroexpand '(-> 10 (+ 2) (- 3)))
;; (display (unwrap-syntax (macroexpand '(-?> 10 (+ 2) (- 3)))))
;; (macroexpand '(->> 10 (+ 2) (- 3)))

listを一連の流れの連鎖ではなく候補からの選択と捉えてみると…

listを一連の流れの連鎖ではなく候補からの選択と捉えてみると、別な形のマクロが思い浮かぶ。
(やり過ぎ良くない…ただ、実験ということで)
例えば、こんな感じ。clojureの->,->>にならい、初期値を取りそれをもとに選択を行うようになっている。

(define (f x p val) (and (p x) val))
(define (QandA Q)
  (@maybeL Q (f symbol? 'symbol)
           (f integer? 'int)
           (f list? 'list)
           (f identity 'unknown)))
(map QandA '(10 aa (aa) #(aa))) ; => (int symbol list unknown)

他の構文とは異質なものなので、それをはっきりさせるために@をつけている。

code

(use util.match)
(define-macro (@maybeL init . candidates)
  (let ((tmp (gensym)))
    `(let1 ,tmp ,init
       (or ,@(map (match-lambda
                      [(fn . args) `(,fn ,tmp ,@args)]
                      [fn `(,fn ,tmp)])
                  candidates)))))

(define-macro (@maybeR init . candidates)
  (let ((tmp (gensym)))
    `(let1 ,tmp ,init
       (or ,@(map (match-lambda
                   [(fn . args) `(,fn ,@(append args (list tmp)))]
                   [fn `(,fn ,tmp)])
                  candidates)))))

(define-macro (@allL init . candidates)
  (let ((tmp (gensym)))
    `(let1 ,tmp ,init
       (and ,@(map (match-lambda
                      [(fn . args) `(,fn ,tmp ,@args)]
                      [fn `(,fn ,tmp)])
                  candidates)))))

(define-macro (@allR init . candidates)
  (let ((tmp (gensym)))
    `(let1 ,tmp ,init
       (and ,@(map (match-lambda
                   [(fn . args) `(,fn ,@(append args (list tmp)))]
                   [fn `(,fn ,tmp)])
                  candidates)))))

@maybeLがあるとfizzbuzzが以下のように書ける。(意味があるかは知らない)

fizzbuzz

(define (f n x val) (and (zero? (modulo n x)) val))
(dotimes (i 100)
  (print
   (@maybeL k (f 15 'fizzbuzz) (f 3 'fizz) (f 5 'buzz) identity)))