初心者向け
Scheme学習に関して
(Scheme:初心者の質問箱:log00より移動)
Schemerの皆様が考える、Scheme学習の最適なパスは、どのようなものでしょうか。是非、ご教授下さい。
- 良いプログラムをたくさん読む。悪いブログラムも読んでみる。そして自分で楽しめる物/必要な物をどんどん書く。 初心者のうちは車輪の再発明を恐れずにどんどん書く。 簡単な再発明ぐらいできないでどうする。そして自分が頑張って作ったもののよりエレガントな解を見つけて orz となる。そうやってセンスを身につける。すごいプログラムを読んで、どうやってそこにたどり着くのかさっぱり分からないのは仕方ない。悪いプログラムのどこが駄目なのか、どうすれば少しはマシなるか考えるのは少しだけやさしい。Lisp:読み物も参照。
- ちょっとした小物が出来たらこの WiLiKi で話題をふってみよう。いろいろアドバイスがもらえること間違い無し。
- そして、 あわてるな。
- skr: Shiro氏を始めとして、Schemerの人々が歩まれた過程に興味があります。WiLiKiや2chのサイトを見ていると、Scheme使いの多さにいつも驚きます。工学系の学科を出て重工業な職場にいると、Scheme使いはちっともいません。
- (Lisper? ばかりです。っておちじゃないですよね? ) 設計検証ツールなんかは実は LISP で書かれていたりしませんか? リスプマシンは航空会社の予約システムで活躍してたって話を聞いたことがあります。(米国の複雑な国内線網をまんべんなく稼働させるため高度なアルゴリズムをつかっていた?) LSI の分野だと、Cadence のツールは SKILL と呼ばれる Common Lisp? 以前の伝統的な Lisp で書かれていてリスナーウィンドウが開いて、Emacs? みたいに Lisp でマクロが書けたり好みにカスタマイズ出来ます。
- 自分で楽しめる物/必要な物を書いてみようとするとライブラリ不足の壁にぶち当たってしまいます。上に書かれている http-get もこの一例ですよね。くれくれ言うのは気が引けますが上級者様にはがんがんライブラリ増やして欲しいです。
- Shiro (2005/09/07 20:26:32 PDT):ライブラリは重要なんですが、時間のかかるプロセスでも あるんで、少し長い目で見ていただけたらと。contribution歓迎。
- skimu (2005/09/07 21:36:31 PDT): 「上級者様には...」なんていってないで、欲しい物があればとにかく自分で作ってみなよ。その上で困ったことがあったらその都度、こことか ML で相談すればいいじゃん。上の http-get の例だって〓〓使い方が分からないと質問したからこそ、機能不足が明らかになったり、改良点のアイデアが生またんでしょ? 自分で始めないとなにも始まらないよ。
- び (2005/09/07 21:49:26 PDT) 昨今の言語処理系って山のようなライブラリとともに提供されているせいか、「プログラミング==ライブラリを組み合わせてつなぐこと」と思い込んでいる人も少なくないような気がします。そういうグルー言語としてGaucheを使うのも悪くないですが、自分で作ってみるのも楽しいんですがね。特にhttp-getみたいなプロトコルハンドラを自分で作成/拡張すると、HTTPそのものを勉強するいいチャンスにもなりますよ。それに、自分が欲しいライブラリが何かを知っているのは自分だけなんですから。
- skimu (2005/09/07 22:07:16 PDT): (うっ、ちょっと頭に血がのぼっちゃってました。ここはヌルくいくところでしたね。) ライブラリが少ないというのは初心者でもみんなに使ってもらえる便利なライブラリが書ける可能性大ということにもなりますね。チャンスですよ!
- Shiro: 上の「Schemerの人々が歩まれた過程に興味があります」を受けて→Scheme:RoadToScheme
- skr: Scheme:RoadToSchemeを興味深く読ませて頂いております。調子に乗って、初心者が読むのにおすすめのSchemeコードを教えて下さい、とお願いしたら怒られますか?
- び: 今目の前で動いている(使っている)コードを読む。例えばGaucheだったら lib の下のコードを読む。ライブラリとして使ったことのあるものなら、動きは理解できるはず。何も逐一頭から読む必要はなくて、「ここはどうなってるんだろう」と思ったら思ったところを読む。そのうち読む前に「こうやってんのかな?」と自然に予測してしまうようになる。予測が当たったり外れたりするけど、外れたら「何でこう書いてるんだろう」とか考えてみる。自分の予測したコードを書いてみて比べてみる。
- skimu (2005/09/13 20:51:21 PDT): srfi-1 はどうでしょう? ひとつひとつの関数は小さく独立しているし、 Lisp のノリを感じるには良いかもしれません。 あと、僕の lisp.scm はギャグとしても 500 行以下のミニリスプは探せばいくつも見つかると思うので、比べてみるのも楽しいですよ。他に鑑賞用 Scheme コードっ〓〓どれくらいあるかなぁ。バズル好きなひとには Olin Shivers の maze も楽しいのだけど。
- skimu WiLiKi 内の初心者お役立ちページをピックアップしてみました。 Gauche:gdumpfs Gauche:LogScanner Scheme:Brainfuck Scheme:アンカーパチンコ Scheme:イラストロジック Scheme:末尾再帰で木をトラバース Scheme:テキスト処理 初心者の方の感想をぜひ聞きたいです。役にたったとか、難し過ぎとか。
- Gauche:CGI:スケジュール予定表:Shiro版がスゲェ勉強になりました。 とりあえず、この手順をそのままなぞってみてから、次に自分なりの CGI を書いてみる、 みたいな。
読むテクニック
(Scheme:初心者の質問箱:log00より移動)
結城さんのサイトの YukiWiki の「読むテクニック」ページ:
http://www.hyuki.com/yukiwiki/wiki.cgi?%c6%c9%a4%e0%a5%c6%a5%af%a5%cb%a5%c3%a5%af
上のページと同じ事を聞きたいのですが、他の人が書いた Lisp コードを読むとき、どのようにして読んでいますか?
- skimu (2006/01/20 18:12:45 PST): つい先日、試してみたのですが、「破壊的に読む」というのが意外と早道かもしれないと思いました。「破壊的に読む」とは、インデントや変数名等を自分好みに片っ端から変更しながら読み進めるやり方です。自分のスタイルとはかけ離れた慣れないスタイルのコードだと何処になにがあってそれらどういうふうに使われているのか覚えておくのが大変だけど、編集しながら自分のスタイルに持ってゆくと覚えておきやすい。先日 1 万行ほどの C のソースコードをこのやり方で読んでいったら 3000 行も変換しないうちに、オリジナルのスタイルから自分のスタイルへの変換公式みたいのが身に付いて、そこから先は変換しなくてもそのまま読み進めてゆく事ができました。
- び (2006/01/20 18:57:52 PST): わたしの場合、漫然と読むのはだめですね。よほど面白いコードでなければ寝ちゃいます(笑)。何らかの目的意識なり問題意識なり(バグ臭い挙動を追うとか、イケてるAPIがどうやって実装されてか見るとか)があって、そこから芋蔓式に追って行くのが通常パターンでしょうか。Lisp系言語の場合、show-paren-modeとfont-lockを有効にしたEmacs、それにgrepがあればかなり楽に読めます。昔はetagsなんかも使っていたのですが、いつの間にか忘れました。
- Shiro(2006/01/21 23:12:26 PST): 一般的なコードの読み方ではなく、Lisp/Scheme特有の話 ということなら、コードから読み取った関数の型をコメントでメモしてゆく、というのを よくやります。高階関数使いまくりのコードだと特に、 引数や返り値が何を意図しているのかが見えにくいので。
- ありがとうございます。コードをいじりながら読んでいくのはいいですね。今度やってみます。
分かってしまえばなんてこと無いけど難しかったこと。
(Scheme:初心者の質問箱:log00から移動)
- skimu (2005/09/15 19:38:20 PDT): 僕は REPL? が難しかった。 UNIX のシェルはいわば REL なので、いちいち返り値が表示されてくるのに違和感があった。最初に読んだチュートリアルに入出力がほとんど出てこなかったせいもあるかもしれないし、表示される返り値を UNIX コマンドの標準出力に対応つけようとしてたことしたことも敗因。 REPL? の P は echo $? だと気づいてすっきり。 あと、Gauche はエラーがあるとトップレベルに戻ってくるけど、他の Lisp の処理系では、エラーのあったその環境で REPL? のプロンプトが出たりしてこれも難しかった。
- び (2005/09/15 23:53:27 PDT): named letとかcondのtest => expr形式とか。もっと言うとlambdaでクロージャを作るということがきちんと理解できるまでにしばらくかかった。Schemeを使うまでべったりだったCとかPerl4の枠をなかなか取っ払えなかったんだと思う。慣れちゃうと逆にクロージャが作れて末尾呼び出しが最適化されないような言語で書くのが非常に苦痛になった。ちなみに未だにdoの構文が憶えられなくて、無限ループを作る時にイディオム的につかっているだけ。call/ccも意味は理解できるけど自分のコードでは大域脱出でしか使ったことがない。ひょっとして一番最初に憶えるのがSchemeだったらこういう引っかかり方はしないんだろうか。
デバッグの方法について
(Scheme:初心者の質問箱:log01から移動しました。)
最近 The little schemer を読みはじめ、このサイトの文書を色々参考に させてもらっている者です(もちろんGausheを使ってます)。デバッグするときに みなさんはどうされているのでしょうか? Cでprintf()でデバッグするように ちょこっとソース内にアウトプットをはさむとかしてみたいのですが、schemeの デバッグではそういうことはするのでしょうか? あとgdbのようなものとかが あれば教えてもらえませんでしょうか?
- Shiro(2007/08/22 13:02:03 PDT): printfデバッグみたいなのは私もよくやります。
Ruiさんの記事が親切です→ http://d.hatena.ne.jp/rui314/20070628/p1
ブレークポイントを指定したりローカル変数の中身を調べたりするデバッガは Gaucheにはまだ無いのですが、誰かが以前portable scheme debugger http://www.cs.tut.fi/staff/pk/scheme/psd/article/article.html を 動かそうと試みていたような記憶があります。 - PSDはcut-sea:log3で紹介してますが、少し動かしてみたことはあります。
当時ソース読まなきゃって書いてるのに、読んでないなー(wcut-sea:2007/08/22 17:51:38 PDT - あとはskimuさんところにあるggcってのが結構以前からあるdebug/traceのライブラリですね。 最近は動かしてないから、今現在そのまま素直に動作するかどうかは分かりません。cut-sea:2007/08/22 18:23:14 PDT
- 質問者です。色々と教えていただきありがとうございます。とりあえず、リーダー マクロ#?=がとっかかり良さそうなので、そこから始めてみます。デバッガは使えるよう になるのが目標ですが、、道は遠そう。
- 質問者です。リーダーマクロ使わせてもらってます。前回解答をいただいてから、Gausheユーザリファレンスにばっちり書いてあるのがわかりました。それに関する質問
なのですが、例えば以下のようにしたとき、(stdin)":458:...の行の458というのはどう
いう意味なのでしょうか?
(define func #?=(lambda (x) (number? x))) #?="(stdin)":458:(lambda (x) (number? x)) #?- #<closure func> func
ソースを見てみたのですが、read_item()から先が分かりませんでした。 - Shiro(2007/08/29 17:58:05 PDT): 標準入力からの読み込みの、プロセス開始時から数えて 458行目ってことです。標準入力から読んでいる場合は行数はほとんど意味がありませんが、 ファイルから読み込んでいる場合はファイル内での行数になるので、どこの式が表示されている かを知る手がかりになります。
- 質問者です。遅くなりましたが、丁寧な返答ありがとうございました。それにしてもデバッグが難しいです。C/C++だと、再現するバグなら間違っている箇所を見付けるのは難しくない(もちろん例外あり) つもりだったですが、 Schemeはソースの動きを追うのが一苦労なうえに、間違っている箇所を見付けるのも難しいです。
- Shiro(2007/09/05 13:39:49 PDT): 慣れが半分、後は「バグを出しにくいコードの書き方」が 半分でしょうか。出来る限り副作用無しで書いておくとバグの切り分けが非常に容易に なります。たぶん、「ソースの動きを追う」という考え方自体、C/C++とSchemeで 差があるんではないかと思います。
- その「差」についてもうちょっと詳しく教えてください。(tko)
- Shiro(2007/09/06 02:15:03 PDT): Cなどの手続き型中心の言語では、「プログラムの
動作を時系列で追う」というのがプログラムを理解することの中心になると思います。
プログラムの状態が次々と書き換わってゆくので、ある変数の内容がどの時点で
いくつであり、どの時点で変更されるのか、を把握してゆくことが重要ですね。
従って最終的にはデバッガでステップ実行してゆくことになります。
関数型言語では、時系列での状態変化よりも、それぞれのコンポーネントで 「どういう入力があったらどういう出力が得られるか」という性質を追うことが プログラムの理解の中心になります。問題を生じる入力が複雑で巨大であっても、 関数のふるまいは入力を分割して与え、それぞれ出てくる出力を合成したものに なるので、理解できるまで入力データを分割して行き、予想と通りの出力が 得られるか、得られているとしたら入力を合成した場合に出力も予想通りに 合成されるか、といった点を調べてゆきます。デバッガでステップ実行するよりも、 関数単位でちょこちょこ入力を与えて出力を見るっていうふうに進めることが 多いですね。
Schemeの場合は手続き型でも書けるので、アプローチとしては両者を併用することに なりますが、私の経験ではなるべく部品は関数的に書いておき、アプリケーションの レベルで状態を持たせるようにしておくと問題が追いやすくなります。 - デバッグとはちょっと話が違うのですが、既存のSchemeのコード、しかも大量のコードを理解するにはどうすればいいでしょうか? ソースコードの読み方のようなデバッガを使うやり方はあまり効果的ではないのでしょうか? (tko)
- Schemeの処理系ではプログラムの実行ステップとソースコードの対応を表示しにくいせいかいわゆるステップ実行ができる処理系なりデバッガが少いです.効果的かどうかよくわかりませんが,ソースコードを動的に解読するためのツールがなあまりないので,そのような解読手法が一般的にはおこなわれないのではないかと思います.(nobsun)
- 色々と返答(と質問も)をありがとうございました。ステップ実行ができるデバッガ があれば非常に便利とは思いますが、Schemeの実装からはそういう対応がとりにくい のですね。それはそれで面白いですね。(最初の質問者 2007/09/15 23:00)
リソース
- Scheme:RoadToScheme Road to Scheme, または私は如何にして心配するのを止めてSchemeを愛するようになったか