第2回:超!Gauche入門に取り組んだ

Gauche入門の第2回が公開されたようですので、取り組んでみました。
なにはともあれ、実際にやってみよう!!というノリで(^^;

【第2回:超!Gauche入門】
http://www.thinkit.co.jp/article/74/2/

サンプルコードから文法の解説をしていくという形で進んでいく形でした。

まずはじゃんけんを表示するプログラムでJavaSchemeのコードを比較するというものです。

[じゃんけんコードサンプルの引用]

;;; じゃんけんプログラム
(use srfi-27)

(define (guu-choki-pa r)
  (cond ((< r 0.33) "Guu")
		((< r 0.66) "Choki")
		(else "Pa")))

(define (jyanken)
  (print (guu-choki-pa (random-real))))

(dotimes (i 10) (jyanken))

ぱっと見て知らなかった手続きとしては「dotimes」と「cond」です。

cond

ifとcondの違いは、elseif(clause節)とelseが使えるか使えないかという感じなのでしょうか?

defineやcond関数は厳密にいうと関数ではなくスペシャルフォームです。

という解説があったのですが、スペシャルフォームの意味がわからなかったので後で調べます。

ヘルプに簡単な説明がありました。

Function Scheme手続き
Special Form 特殊形式 (R5RSでは「構文」)
Macro マクロ

なるほど、構文という意味なんですね・・・
これって言語に関わらず常識だったりするのでしょうか?

dottimes

dotimesに関してはヘルプを見てみたらこんな記述が。

-- Special Form: do ((variable init [step]) ...) (test expr ...) body
-- Macro: dotimes (variable limit [result]) body ...
-- Macro: dolist (variable lexpr [result]) body ...
Common Lispからの輸入です。それぞれ以下のように展開されます。

同じ繰り返しを行う処理でも doとdotimesとdolistがあると。
doはcondと同様に特殊形式だけど、dotimesとdolistはマクロらしい。

マクロとか特殊形式(構文)などの定義については、追々プログラミングGauche本等で学んでいきたいと思います。

ヘルプを見てわかったこと

  • dotimesはwhileと同等
  • dolistはforeachと同等

感想

こうやって、実際に普段仕事で使っているJavaSchemeのコードの比較はわかりやすいです。
特徴やアプローチの方法も違うし。

どうだったでしょうか? Javaと比べると短く、シンプルですよね。Schemeには式と区別される文はなく、また演算子もありません。

Javaは普段から見慣れているはずなのに、1行1行が全て明示的に記述されているので長く感じました。
Schemeのコードは括弧が気にならなければ、シンプルに感じました。
直感的な印象なので、何とも言えませんが、上記コードの記述に余分(明示的)な情報がないからかなぁ。
S式の仕組みがシンプルだからなのかなぁ?

Gaucheの使いかた

次のページに進むとGaucheの使い方の解説で「ふむふむ」と読み進めました。

実行方法の解説を要約です。

  1. プログラムファイルを作ってShell上で実行
  2. REPL(対話プログラム)で実行
  3. エディタ上(Emacsvim)上で実行

後に説明した方法ほど、能率良く開発が行えます。

なるほど。

Emacs上での実行はすごく便利なのは前回の取り組みで実感できましたので、この機会に入門講座での説明にはないvimで自分がどのようにエディタ上で実行しているかをメモを兼ねて紹介したいと思います。

vim上での実行

4つウインドウが縦に割れているのですが、各ウインドウは下記の通りです。

  1. ヘルプ画面
  2. コードを書く画面
  3. 記録や感想を書く画面
  4. コマンドウインドウ画面

vim上でschemeコードを実行する為に下記プラグインを利用させて頂いています。
http://d.hatena.ne.jp/ns9tks/20080115/1200416097

あと、Gaucheのヘルプも活用したいのでこちらのプラグインも導入しています。
http://www.vim.org/scripts/script.php?script_id=2199

基本的にはひとつのウインドウで「2」のコードを書く画面だけなのですが、メモをとりながら実行したい場合はさらに「3」のウインドウを表示して分割します。
選択範囲のコードを実行したり、カレント行を実行したり、ファイル全体を実行したりできます。
vim上で実行するにはファイルタイプは予め指定しておきます。*1

:setf scheme

結果は、「4」コマンドウインドウ画面に表示され、表示結果はレジスタに格納されるので後でペーストして、日記や記録にも利用することができます。簡単に言うと、作業をしながらメモをするという流れがスムーズにできるという感じでしょうか。

Emacs上では対応した括弧内をRELPに送信してくれますが、vimでは選択がひと手間ですがvim7のテキストオブジェクトという機能を使うと面倒には感じません。括弧を含んで選択したいなら、括弧の上にカーソルをおいて、「va)」で選択できます。
実行は、*2で行うことができます。

ヘルプで手続きを引きたい場合は、で「1」ヘルプ画面が表示できます。
閉じるときは「ZZ」で。

もし、メモ中のコードを(はてなダイアリー)実行したい場合はファイルタイプを変更してあげればできるので、都度ファイルタイプを交互に切り替えます。

:setf scheme
:setf hatena

そんな感じでvimを一度も抜けることなく、評価・ヘルプ・メモ*3を繰り返すことができます。

最終お題

最終ページです。

「S式を与えると対応するHTMLを出力するプログラム」を書こうということです。
ぱっと見た感じでは綺麗なコードというのはわかるのですが、解説を見ないとどういう風にデータ(処理)が流れているかわからず、JavaとかRubyとかのノリでいきなりコードだけで理解しようとするとつまづくかなー。と思いました。

解説を読んだ上で理解できているかどうか、自分の言葉を交えて述べてみます。

コードの引用+コメント付け*4

;; 開始タグと終了タグと属性ごとに処理を行う
(define (print-html e)
 (cond ((list? e)
        (print-open-tag (car e))
        (print-html-list (cdr e))
        (print-close-tag (car e)))
        (else (display e))))

;; リストが空になるまで再帰的にパースする。
(define (print-html-list s)
 (cond ((null? s) #f)
       (else
        (print-html (car s))
        (print-html-list (cdr s))
		)))
;; 開始タグを処理する
(define (print-open-tag tag)
 (display "<")
 (display tag)
 (display ">"))

;; 終了タグを処理する
(define (print-close-tag tag)
 (display "</")
 (display tag)
 (display ">"))

;; データ定義
(define gauche-page
 '(html
   (head (title "Gauche Web"))
   (body (h1 "Gauche Web Page")
         (body (h1 "Gauche Web Page")
         (table (tr (td 1)(td "lisp"))
         (table (tr (td 1)(td "lisp"))
                (tr (td 2)(td "scheme"))))))

;; メイン処理
(print-html gauche-page)

処理の流れ

処理を追いかけてみたいと思います。

データ定義とメイン処理

データ定義リストをリスト毎に処理を行う手続きに引き渡す。

この時、データ定義されているデータは「ネストされたリストデータ」でリストの中身としてタグ部分と要素になっています。
空要素タグは対象外で。

print-html手続きで

最初にprint-htmlで引き渡されたネストせれたリストデータを「タグ名」と「タグ要素」に処理を分けます。
先頭にタグ名が記述されているのがポイントで、carとcdrでリストの要素を分割します。
リストでなければ、渡されたデータをそのまま表示します。cdr部分はリストの可能性があるのでprint-html-list手続きで処理をしてもらいます。

print-open-tagとprint-close-tag

開始タグと終了タグを表示するだけです。

print-html-list

print-html-listの手続きがこのhtml生成プログラムの面白いと思った部分でした。


引き渡されたパラメータがあれば、再帰処理を行っているのですが、ここでもcarとcdrを使っています。
一つのタグの中に、複数のタグが存在している可能性があるので、print-html手続きへ再帰的に式を渡している。
最終的にprint-html-list手続きへパラメータが渡ってこなくなった時に、再帰終了となる。

まとめ

  • carとcdrの使い方がリストを扱う基本となる。
  • 再帰便利。
  • アプローチの方法で煩雑なプログラムにはならないよ。

感想

じゃんけんの例題、HTMLの変換ともに面白かったです。
ただ入門記事だけで「おぉっなるほど」とまではいかず、最後に記載があるように他の書籍等で補完しながらだと効果的と思いました。

次回が楽しみだなぁ。

*1:拡張子がscmなら自動的に判断してくれます

*2:設定次第で変更可能

*3:hatena.vimを使えばはてなへ投稿もできます

*4:コードの引用掲載に問題があったら指摘してください。消します。