(wat-aro)

無職から有職者にランクアップしました

OSXにGaucheのHEADをインストール

GaucheのHEADをインストールするにはリリースされた最新のGaucheが必要です.
ここにはまりました.
なぜか最新版の0.9.4でもビルドできなかったのでbrewでインストールしてから後で消しています.
make install の後にbrew uninstall gaucheをすると必要なusr/local/share以下のファイルなどが消されてしまうので 必ずmake install の前にbrew uninstall gaucheしましょう.

$ brew install gauche
$ git clone git@github.com:shirok/Gauche.git
$ cd Gauche
$ ./DIST gen
$ ./configure --enable-threads=pthreads
$ make

ここで

warning: unrecognized encoding name `utf-8’

と表示されますが無視.

$ make check
$ brew uninstall gauche
$ make install

これでインストールできました.

$ gosh -V
Gauche scheme shell, version 0.9.5_pre2 [utf-8,pthreads], x86_64-apple-darwin15.6.0
$ gosh
gosh> (+ 1 1)
2

無事動いていますね.これで完了です.

今後HEADをビルドするにはダウンロードしたGaucheディレクトリで

$ git pull
$ gauche-config --reconfigure | sh && make && make check && make install

するだけで済みます.

MacでSticky ShiftにするためのKarabinerの設定

SKKを使いはじめました.
そうするとシフトキーを多用するのでもっと楽に入力したくなります.
そこでSticky Shiftです.
「シフトキーを押したまま他のキーを入力する」のではなく,「一度シフトキーを押して離した直後に押したキーが大文字になってくれます.
SKKを使っていなくてもCamelCaseなどの入力がとても楽になります.
左手小指はControlキーのためにありますからね.
全国一千万人のEmacs愛好家にとっては譲れませんよね.

Karabinerに標準でSticky Shiftの設定項目はありませんが,private.xmlを編集することで設定できるようになります.
ただ private.xml の記述方法がわかりづらかったので書いておきます.
僕はセミコロンをSticky Shiftにして右シフトをセミコロンにしています.
そのかわりに右シフトをセミコロンに当てています.

~/Library/Application\ Support/Karabiner/private.xmlを編集します.

<?xml version="1.0"?>
<root>
  <item>
    <name>Common</name>
    <item>
      <name>Sticky Shift</name>
      <appendix>Use semicolon to Sticky Shift_L</appendix>
      <identifier>private.semicolon_to_sticky_shift_l</identifier>
      <autogen>
        --KeyToKey-- KeyCode::SEMICOLON, ModifierFlag::NONE,
        KeyCode::VK_STICKY_SHIFT_L
      </autogen>
    </item>
    <item>
      <name>Change Shift_R2Semicoron</name>
      <appendix>Use Shift_R to Semicolon</appendix>
      <identifier>private.dhift_r_to_semicolon</identifier>
      <autogen>--KeyToKey-- KeyCode::SHIFT_R, KeyCode::SEMICOLON</autogen>
    </item>
  </item>
</root>

これでKarabinerでSticky Shiftをチェックできるようになります.
SKKを使っていなくてもSticky Shiftは便利なので是非設定しましょう.

Railsのnew/build/createの違い

build - リファレンス - - Railsドキュメント

new

モデルオブジェクトを生成する.
生成するだけで,保存はされていないためsaveメソッドなどを使用して保存する.

build

new の alias

create

モデルオブジェクトを生成して保存する.

リモートのブランチにローカルでチェックアウトする

まずリモートブランチをfetchします.

$ git fetch

次にリモートブランチを確認します.

 $ git branch -a
* master
  remotes/origin/01_untested
  remotes/origin/02_setup
  remotes/origin/03_models
  remotes/origin/04_factories
  remotes/origin/05_controller_basics
  remotes/origin/06_advanced_controllers
  remotes/origin/07_controller_cleanup
  remotes/origin/08_features
  remotes/origin/09_speedup
  remotes/origin/11_tdd
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

ここでチェックアウトしたいのは02_setupです.
ローカルブランチ名を指定してリモートブランチをチェックアウトします.

 $ git co -b 02_setup origin/02_setup
Branch 02_setup set up to track remote branch 02_setup from origin.
Switched to a new branch '02_setup'

これでリモートブランチにチェックアウトすることができました.

Scheme修行のtryについて

Scheme修行

Scheme修行

p89の欄外で補足されているtryについて.
これが出てきたのは rember1* の実装の中です.
rember1*atom aとリストlを引数に取ります.
lの中で最初に出てきたaと同じアトムを削除して新しいリストを返す手続きです.

tryを使う前の実装は以下になります.

(define rember1*
  (lambda (a l)
    (if (atom? (let/cc oh (rm a l oh)))
        l
        (rm a l (quote ())))))

(define rm
  (lambda (a l oh)
    (cond
     ((null? l) (oh (quote no)))
     ((atom? (car l))
      (if (eq? (car l) a)
          (cdr l)
          (cons (car l)
                (rm a (cdr l) oh))))
     (else
      (let ((new-car
             (let/cc oh
               (rm a (car l) oh))))
        (if (atom? new-car)
            (cons (car l)
                  (rm a (cdr l) oh))
            (cons new-car (cdr l))))))))

リストの中で最後まで探し終わってlがnullになれば継続に(quote no)を渡します.
atomであればcarにリストはないのでcdrを探します. 再帰的に探して,aと同じものがあれば,それを取り除いた残りのリストを返します.
取り除くのは最初に見つかったものだけです.
このコードをtryを使うとこうなります.

(define rember1*
  (lambda (a l)
    (try oh (rm a l oh) l)))

(define rm
  (lambda (a l oh)
    (cond
     ((null? l) (oh (quote no)))
     ((atom? (car l))
      (if (eq? (car l) a)
          (cdr l)
          (cons (car l)
                (rm a (cdr l) oh))))
     (else
      (try oh2
           (cons (rm a (car l) oh2)
                 (cdr l))
           (cons (car l)
                 (rm a (cdr l) oh)))))))

tryについてはここでページ欄外に

(try x α β)
=
(let/cc success
  (let/cc x
    (success a))
  b)

と書かれています.
ここがなかなかわかりませんでした.

まず中のlet/ccから考えます.

(let/cc x
  (success α))

α内で継続xが使われているはずです.
継続xに値γが渡されると,(let/cc x γ)となり,次の計算βに進みます.

継続xに値が渡されない場合はαの値が継続successに渡され,そこで計算が終了しこの式の値はαとなります.
つまり,tryはα内で継続xに値が渡されればβの値が返り,
渡されなければαの値が返るわけです.
元の式で継続に値が渡されたのを判別するために(quote no)を継続に渡してatom?で判別していたものを
継続が返ってくるかこないかで判別できるようになっています.

継続難しいです.
でもScheme修行で少しずつわかってきた気がします.

Schemeでクイックソート

先日の納会でソートの話が少し出たのでクイックソートを書いてみました.
書きやすいのでGaucheで.

まず普通に書いてみます.

(define (quick lst)
  (if (null? lst)
      '()
      (let ((first (car lst)))
        (append (quick (filter (lambda (x) (< x first)) lst))
                (filter (lambda (x) (= x first)) lst)
                (quick (filter (lambda (x) (< first x)) lst))))))
gosh> (quick '( 4 7 8 3 9 2 7 3 92 7 1))
(1 2 3 3 4 7 7 7 8 9 92)

普通ですね.
ただfilterで何度もリストの中身を舐めているのが嫌です. ここでstreamを使ってみます.

(use util.stream)
(define (quick lst)
  (stream->list
   (let recur ((s (list->stream lst)))
     (if (stream-null? s)
         stream-null
         (let ((first (stream-car s)))
           (stream-append (recur (stream-filter (lambda (x) (< x first)) s))
                          (stream-filter (lambda (x) (= x first)) s)
                          (recur (stream-filter (lambda (x) (< first x)) s))))))))
gosh> (quick '( 4 7 8 3 9 2 7 3 92 7 1))
(1 2 3 3 4 7 7 7 8 9 92)

リストからストリームへの変換とストリームからリストへの変換が入っているので
効率的になったのかどうか怪しいですが一応期待通りに動いていますね.
どうするのが正解なんでしょう?

once-onlyマクロの解読

実践Common Lisp p100にあるonce-onlyマクロの解読に挑戦.

実践Common Lisp

実践Common Lisp

 
 
 

マクロのコードは以下のとおり.

(defmacro onece-only ((&rest names) &body body)
  (let ((gensyms (loop for n in names collect (gensym))))
    `(let (,@ (loop for g in gensyms collect `(,g (gensym))))
       `(let (,,@ (loop for g in gensyms for n in names collect ``(,,g ,,n)))
          ,(let (,@ (loop for n in names for g in gensyms collect `(,n ,g)))
                ,@body)))))

一行ずつ解読していく

まずは

(let ((gensyms (loop for n in names collect (gensym))))

の部分から.
バッククォートがないので何がgensymsに束縛されるかをREPLで確かめる.

CL-USER> (let ((gensyms (loop for n in '(a b c) collect (gensym))))
           gensyms)
(#:G884 #:G885 #:G886)

namesの数と同じだけのユニークなシンボルを作成している.  
 
次の行は

`(let (,@ (loop for g in gensyms collect `(,g (gensym))))

gensymsは一行目の処理でユニークなシンボルのリストになっている.
gensymsのそれぞれの要素と(gensym)をペアにしていく.
ここまでを実行してみる.

CL-USER> (let ((gensyms (loop for n in '(a b c) collect (gensym))))
           `(let (,@ (loop for g in gensyms collect `(,g (gensym))))))
(LET ((#:G887 (GENSYM)) (#:G888 (GENSYM)) (#:G889 (GENSYM)))
  )

 
三行目.

`(let (,,@ (loop for g in gensyms for n in names collect ``(,,g ,,n)))

とうとう`,が二重に.
1つずつ見ていく. 二行目の`(let の式の中で `(letとなっているのでここは出力後の形が`(letとなってほしいはず.
,,@となっているのは二行目のバッククォート,三行目頭のバッククォートと二回バッククォートされているので 二度展開しなといloopが展開されない. これでloop内は展開されるようになった. 次は``(,,g ,,n).二重にバッククォートするのは先ほどと同じように`(foo bar) という形のリストにしたいから.
(,,g ,,n)になっているのはloopでgensymsの要素をgに,namesの要素をnに対応付けているから.
`(,gensymsの要素 ,nameの要素)という形に変換しようとしている.
  
ここまでを展開するとこうなる

CL-USER> (let ((names '(a b c)))
           (let ((gensyms (loop for n in names collect (gensym))))
             `(let (,@(loop for g in gensyms collect `(,g (gensym))))
                `(let (,,@ (loop for g in gensyms for n in names collect ``(,,g ,,n)))
                   ))))
(LET ((#:G937 (GENSYM)) (#:G938 (GENSYM)) (#:G939 (GENSYM)))
  `(LET (,`(,#:G937 ,A) ,`(,#:G938 ,B) ,`(,#:G939 ,C))
     ))

コンパイル時には新たに(gensym)で作られたユニークなシンボルにnamesの値が束縛されるようになる.
 
 
最後に4行目.

,(let (,@ (loop for n in names for g in gensyms collect `(,n ,g)))

二行目と三行目でバッククォートされてるので,先頭のカンマは展開されず(let ...という形になる.
,@の部分は既に先頭で一度カンマがあった後なのでそのまま展開出来る.
`(,n ,g)の部分で実際にAにAの値を束縛するという部分を作る.
なのでここではバッククォートが一つ.
ここのgには3行目で値に束縛したユニークなシンボルが入る.
実際に展開する.
最後なのですべて展開するとこうなる.

CL-USER> (let ((names '(a b c))
               (body '(body)))
           (let ((gensyms (loop for n in names collect (gensym))))
             `(let (,@(loop for g in gensyms collect `(,g (gensym))))
                `(let (,,@ (loop for g in gensyms for n in names collect ``(,,g ,,n)))
                   ,(let (,@ (loop for n in names for g in gensyms collect `(,n ,g)))
                         ,@body)))))
(LET ((#:G934 (GENSYM)) (#:G935 (GENSYM)) (#:G936 (GENSYM)))
  `(LET (,`(,#:G934 ,A) ,`(,#:G935 ,B) ,`(,#:G936 ,C))
     ,(LET ((A #:G934) (B #:G935) (C #:G936))
        BODY)))

まとめ

まずnamesと同じ数だけ(gensym)でユニークなシンボルを作り,それをgensymsというリストにする.
gensymsの各要素を新たに(gensym)に束縛するlet式を作る.
この(gensym)はonce-onlyを使うマクロの展開時に新しくユニークなシンボルを作る.
gensymsの各要素を評価すると新しく作られるユニークなシンボルを返すようになる. このユニークなシンボルにnamesの各値を束縛するようにする.
それが本体の三行目に当たる. 四行目ではnamesのシンボルにgensymsの各要素を対応付ける.
gensymsの各要素は新たに作られたユニークなシンボルに束縛され,そのユニークなシンボルはnameの値に束縛される.
以上で終わり.  
 
高階マクロで名前の衝突を回避して,評価順序を保つのはこんなに大変なんですね.