Shiro:UnixUser0307
Unix User 7月号に 「使って遊ぶ! GaucheによるSchemeスクリプトプログラミング」という記事を 書かせて頂きました。
単なるSchemeの紹介なら他にもあるので、ちょっと欲張って 「最短距離でマクロまで解説する」という無謀な試みをしました。 そのためわかりにくくなってしまったかもしれません。 コメント等、書き込んで頂ければ幸いです。
記事中に出てきたURL
- ハッカーになろう
- 普通のやつらの上を行け
http://practical-scheme.net/trans/beating-the-averages-j.html
- R5RS
- R5RS日本語訳
http://www.math.toyama-u.ac.jp/~iwao/Scheme/r5rsj/html/r5rsj_toc.html
- Gaucheのダウンロード
- Gaucheのホームページ
- Gauche:Platform
http://practical-scheme.net/wiliki/wiliki.cgi/Gauche:Platform
- vi for schemers
- The Origin of CAR and CDR in LISP
- SRFI
- なんでもλ
- なんでも再帰
- マクロの例(リストの内包表記)
- マクロの例(Perl風マクロ)
http://www.geocities.co.jp/SiliconValley-PaloAlto/7043/#perl.scm
- Schemeの情報
他サイトのコメント
- すきー間だらけの(寒)Gauche体験記:
のに子さんによる、
詳細な読解レポート。著者としては、ああこういうところに理解の山があるのか、
ということがわかって、大変参考になりました。
- Setu?: ここに置いてあるサンプルのコードをコピペして動かしたら、動かないので焦った。
リストを見ても絶対に間違いはなさそうだし、テストするのに行をコメントアウトしたり、print して見たり、最後にこれを見た時には、うちの gauche を疑った(ごめん):.... yearは2004が入っている。 (print (quotient year 4)) .... *** ERROR: unbound variable: print
え?で、これを更に削るの?って削ろうとしたら、マウスで空白を選択しようとした時に、変な物が見えた気がした。そうだったのだ、このリストでは空白が全角の空白だったのでした。サンプルコードが<pre>で囲んでなくて、うまくページに出来なかったので空白を全角にしてごまかしたんじゃないかと思います。
関連して思ったのは、gaucheでの全角空白の扱いを半角の空白と一緒にしたら良いんじゃないかなと思いました。これは、バグか要望に書いた方が良いのかな?-- 2004/08/21 00:34:06 PDT
- Setu?: ここに置いてあるサンプルのコードをコピペして動かしたら、動かないので焦った。
- Shiro: Gauche:WishListへ移動します。
コメント、質問等どうぞ。
(適当に下に追加して頂ければ、表題等、小人さんが整形してくれるでしょう)
密度濃過ぎ?
一応一通り読みましたよ〜。 ちょっと分量から行くと内容が詰め込みすぎって感は否めない。 ただ、 Scheme を知らない人からすると簡単そうな印象は与えられたかも。 たったあれだけだし。 一方で Perl や Ruby をすでに使ってる人からすると、是非使ってみようとまでは いかないかなぁ。(個人的にはあんまりメジャーになってほしいとは思ってないけど) あの量に納めるために、かなりの文章を捨て去ったりもしたんでしょうね。cut-sea
- Shiro: うーむ、やはり濃過ぎたか。 順当に導入するには、リスト処理のところでappendやreverseを 実装してみせるというのが定番なんでしょうが、 そういう解説は他にもあるし、ということで思い切ってすっとばしてしまいました。
ハードコアLisperの作業風景は
こんばんは。わたしはまだまだ Scheme の入門者ですが、とても面白く読ませて頂きました。 別の機会にでも、「ハードコアLisper」の実際の作業風景をもっと描写して頂けると、 Scheme の魅力がより伝わるかと思いました。それでは。
- Shiro (2003/06/11 13:32:43 PDT): なるほど。実は、私自身は
「ハードコアLisper」ではないですし、
スクリプト的な使い方は伝統的なLispのプログラミングスタイルからはちょっと
離れているようにも思います。
私が「ハードコアLisper」だと思って見ている方々は Lispマシンをいじってた方々で、そこではLispは統合環境でしたから、 プログラムの動作中にバグが出るとデバッガが立ち上がって(インタフェースは Emacs)、その場で修正してプログラムを続行する、というようなことを普通に やっていた模様です。
柔軟性と制限とのバランス
システムというのは柔軟な所と制限されて引きしまった所のメリハリが必要と ならったことがありますが、 Lisp マシンと聞くと柔軟さの塊というイメージがあります。 Lisp 系の言語の、新しい言語を作れる程の柔軟性はとても魅力的なのですが、 型チェックなどのバグを入れないように制限する手段は自分で作らなくては ならないのでしょうか? バグのない Lisp コードを書くためには、たとえ Lisper だとてなんらかの テクニックを使っていると思うのですが。
- Shiro (2003/06/14 20:08:06 PDT): Lispのアプローチは、基本的に制約の無い状態から、 オプショナルに制約を入れてゆくという方向です。しかし、コンパイラに 整合性を厳密に検査させるというよりは、コードの最適化のヒントだとか、 うっかりミスを防ぐ安全網のような方向です。 例えばCommon Lispでは型宣言を書くことができて、コンパイラはそれを もとに最適化したり、データフロー解析が可能な範囲で 整合性チェックをします。ただ、例えば関数に型情報があるわけじゃないので クロージャを渡されたら追えなくなります。多くの整合性チェックはランタイムに 行われることになります。安全網としては、例えばパッケージ(名前空間)を ロックすることによって、不用意に束縛が上書きされることを防いだりすることが できる処理系もあります。
- 個人的な印象では、何十人ものプログラマが関わるようなプロジェクトでは Lispの柔軟性をフルに活かすのは難しいと思います。敢えてやるとすれば 舵取りをするリーダー格の人間が基本的なマクロやコーディングディシプリンを 制定して、皆がそれに自主的に従うようなやり方でしょうか。 データ型を間違えて渡してしまうミスをコンパイラで検出することは出来ませんが、 例えばCLOSのMOPを使って、contractとなる条件をチェックするコードを 動的に挿入したり外したりするフレームワークを作っておき、 単体テストと組み合わせることで対応するという方法でしょうね。 今でいうAOPの考え方に近いと思います。
- しかし、Lispらしさが出てくるのは、10人以下、できれば2〜3人の小チームで、 各人が大きなモジュールを任されて、その中は基本的に一人で書いてゆく、 というスタイルだと思います。他のプログラミング言語でもそういう仕事分担は しますが、Lispの場合、基本的に分担しておいても、緊急時にはそれを オーバライドできる仕組みがあるので、〆切前のストレスは低いような 気がします (他人のモジュールにバグを発見、しかし担当プログラマは ダウンしている、とりあえずwrapperメソッドで回避だ、みたいな。)
AOP、Java
戯 2003/06/14 23:10:48 PDT 職場では、責任とか保証とか言い出すもんだから、 柔軟性を活かす事を制限されてしまったりして、悲しい目に遇いますね。 実は自分を縛ってるって気付かないのが、ほげ上司/ほげ企業なのかも。
Overrideの考え方を、Classとかに縛られず好きな個所(手続き?)で使えるならば、 AOPに近いものになるような気がします。つまり、Classがこんなにも働き過ぎでなければ、 AOP用の処理系をわざわざ作るまでもなかったんじゃないかと>AspectJ(藁
Javaは、良くも悪くも、Cの改良というか後継なんだろうな。 C勢力(communityかも)を取り込み、かつてC勢力が楽に実現できなくて困ってた夢を実現してあげてるような気が。 だから思考が、Cに(古典的発想で)OOPを追加したモノ、に留まる。
- Shiro (2003/06/15 13:54:37 PDT): AOPは一種のメタプログラミングなので、 コンパイラの前に一段かませるというのは間違っちゃいないかなと思います。 Lispではマクロがメタプログラミングに使える、というだけなんで、 別の方向の進化もありでしょう。
単体テスト
Lisp 用の単体テストとして LispUnit(xUnit) のようなサポートツールはあるのでしょうか?(q)
コラム3の「Scheme プログラムの開発は...あまり本格的なデバッガの必要性を感じることはない」という点はなにかひっかかりを感じました。Lisper ではないわたしの推測としては、 「関数型言語では、そもそもバグが入りこまないように副作用なく記述することがコモンセンスとして あるために、自然と品質が高いソースを書くことができる」?(q)
さほど複雑ではないプロダクトコードを書く身としては、高度でスキルが必要なソースより、 Unit Test と Pair Programming (XP)によって誰でも編集できるソースのほうが嬉しいです。 OOP というのは決してベストでも何でもないですが、保守性は上げられるかと思います。(q)
- Shiro (2003/06/15 13:54:37 PDT): unit testツールとしてこれが定番と言えるものは
無さそう(私が知らないだけかも)なんですが、Ward Cunninghamの
FITはLispでも使えるようです。
私の見た範囲では、特にテストフレームワークみたいなものは
作らずに、簡単なマクロだけを使ってテストをベタに書き下すことが
多いようです。例えばGaucheでは
こんな感じ
でテストしています。
- Makoto (2003/06/15 17:06:31 PDT): Schematicsに、SchemeUnitというユニットテストのためのツールがあります。これ自体は、PLT Scheme用ですが。
- SchemeUnit のペーパーざっと読んで(眺めて)みました(schemeunit-schemeql.ps)。 SchemeUnit versus JUnit に指摘されているように、 Scheme の方が簡潔に記述 できてますね。
test.scm, xUnit よりも Perl の Test ライクのフレームワークですね(q)
- Shiro (2003/06/27 23:51:58 PDT): 現在のtest.scmに対して、 「こういう機能があったら便利」というものはありますか?
- Lisp の特徴を生かすようなものがあまり思いつかないのですが、 まだまだテストが足りない、とかこれだけ書けば安心して Refactoring できる、とか判断するために、 地味ですがテストの数と結果がサマリーとして出ると嬉しいです。(q)
- 特に Emacs と合わせて使う場合、(test* "test msg" ...) の特定のテスト行だけインタラクティブに実行できるので、全体のテストは後で行いたい時にとても便利だと思います。(q)
dow.scmに1・2月の日付を指定すると...
いまさらながら、バックナンバーを手に入れて勉強させてもらってます。ところで、サンプルプログラムの「dow.scm」なんですが、引数の月を1・2月に指定すると、ありえない日付が表示されちゃってます。(例を挙げれば、$ dow.scm 0 1 1 を入力すると、-1/13/1(日)と表示されます。) 1・2月は周期をもとめる都合上13・14月に、そして西暦をデクリメントされているように見受けるのですが、その値をそのままprintで表示しちゃっているようです。ただ、これをバグというには単純すぎる気がしてしまって、これはもしや、なんらかの意図をもった故意なのでは? とも勘ぐってしまうのですが、真相はどうなんでしょうか? -- emeitch (2005/07/27 09:56:44 PDT)
- Shiro(2005/07/27 12:24:51 PDT): あらほんとだ。これは副作用を使うときは注意しましょう、 という反面教師なのです。じゃだめか。C言語から入りやすいようにinc!, dec!を 早目に出しておこう、という考えがあったのですがはまりましたね。 この時点ではletが未出なので、やるとしたら (- year 1), (+ month 12) を 別の変数にdefineしてそれを曜日計算に使いましょうか。
(define year* (if (<= month 2) (- year 1) year)) (define month* (if (<= month 2) (+ month 12) month)) ;; 曜日計算で year, monthのかわりにyear*, month*を使用
- emeitch(2005/07/28 09:36:02 PDT): >これは副作用を使うときは注意しましょう、
という反面教師なのです。
その線があるかなと、勘ぐってました:-)
コード修正に関しては、あの場での想定読者の知識を考慮すると、上記コードが最良と思われます。
余談ですが、dow.scmの、(quotient (+ (* 13 month) 8) 5)
には面食らいました。こんな最適化した記述をさくっとできるようになりたいものです。
- emeitch(2005/07/28 10:45:54 PDT): よくよく調べたら、曜日を求める一連のアルゴリズムは有名な公式(ツェラーの公式) だったんですね。不勉強でした。おはずかしい。
それにしても、おもしろい公式だぁ。