2008-05-01
■ [event][scheme] プログラミングGauche&SICP読書会 #1
SICPの方の司会をさせて頂いたのですが、進行がgdgdになった。申し訳ない。
次回までにはちゃんとcondとcaseを実装しておきます^^;
2008-05-02
■ [web] 京大ホームページリニューアル
先代のウェブサイトの時に、UIの授業で「京大のホームページが使いにくい理由を挙げよ」という課題が出されたのを思い出した(笑)。
リニューアルしたけど、全体の構成はあんまり変わってないな。 というか在学生に当てはまる「◯◯の方」という項目がないあたりが酷い(例えば生協の営業時間が知りたいときはどうすればいいんだ?)
■ [mac][linux] Ubuntu in VirtualBox on Mac OS Xで、左commandをAltに割り当てる
~/.xmodmapに以下のように書く(optionが左Alt, commandが左Superと認識されるので、それらを入れ替えるだけ)。
remove mod1 = Alt_L remove mod4 = Super_L keysym Super_L = Alt_L keysym Alt_L = Super_L add mod1 = Alt_L add mod4 = Super_L
しかし「かな」と「英数」がどっちもZenkaku_Hankakuに見えてしまう(xev調べ)のは何とかならんかな。 レイアウトをJapanese-106にしてるのがまずいのだろうか。
2008-05-08
■ [tDiary] accesskeyを潰した
どうも、ボタンにaccesskey属性が指定されてるかららしい。
<span class="button append"> <input type="submit" tabindex="999" accesskey="A" name="<%%= submit_command %>" value=" <%%= submit_label %> ">
まあキーボードからボタンが押せるのは便利だけど、、、C-aはないよC-aは。
とりあえず、skel/edit.rhtml, skel/preview.rhtmlを書き換えて対処した(very 場当たり)。
http://d.hatena.ne.jp/kwatch/20080413/1208098168 のパッチが採用されるといいな。
■ [event][scala] Scala勉強会@岡山-1 行ってきた
帰省コースどまんなかだったんで寄ってきました。
Scalaの感想とか
variance指定(いまだによく分かってない)とかマニアックな機能が入ってるわりに、実用指向なんですよねこれ多分。Javaの巨大な標準ライブラリがそのまま使えるわけだし。 XMLリテラルとか入ってるし(笑) C系文法だけどパターンマッチがあるとか、 そのへんのギャップが面白いなと思いました。Scalaがバリバリに実用される世界では、みんなどんなコードを書くんだろうか。
懇親会とか
ふだんJavaな人と話さないので面白かった。Javaクラスタ。
moriqさんからRailsのテストの話を聞いた。規模が大きくなるとテストの実行時間が伸びて辛い。解決方法は2つ。(1) Rspec server(?)みたいなものを使って、 テストの起動時間を短くする。 (2) fixtureとか使うといちいちDBへのアクセスが入るので遅い。DBにアクセスしないように、モデルのモックを作って、 コントローラのテストのときはそれを使う。 (←Scala関係ないぞ)
以下、ログです。
■ [event][scala] Scala勉強会@岡山-1 ログ
- 原因はJava-ja
自己紹介
- keisuken
- Java, Ruby, JS, ASなど
- jspit
- GB単位のファイルの分割ツール(by Scala)
- Ruby/PureImage
概要
- http://www.scala-lang.org/
- 静的型付け(型推論あり)
- コンパイル型
- OOP+関数型
- 頻繁にバージョンアップされている
- Javaと.NET上で動作する(が、.NET版は停滞中)
- リーダーはスイスのローザンヌ工科大学に (javacのGenericsに関わっていた人物)
- Javaとおなじところ:
- Erasureな(コンパイル時には型がわかっているが、実行時には分からない(??))Generics
- Javaと同等のパフォーマンス
- 型システム(Javaから継承)
- クラスファイル
- Javaとちがうところ:
- すべてがオブジェクト(数値も)
- 型推論、クロージャ、パターンマッチ
- Implicit Conversions(Views) (あとで)
- scala = インタプリタ兼REPL
- scalac = コンパイラ
object Hello extends Application{ println("Hello.") }
- javap Hello.class →JVM上でのクラス(?)が見える
- scaladoc
- 標準ライブラリ: 豊富なコレクション、Actor、パーザコンビネータなどなど
文法
リテラル
- 文字列 : "Foo\nBar"
- ながい文字列(ヒアドキュメント代わり) : """ほげほげ(改行もOK)"""
"""Foo |Bar """.stripMargin ←「|」までの空白を取り除いてくれるメソッド
- 数値 : 1234, 1234l (long), 1.2 (Double), 1.2f (Float), 1.2e10 (Double)
コメント
- 行コメント : // ほげほげ
- ブロックコメント : /*ほげほげ*/
- ScalaDoc用コメント : /** ... @param foo value */
代入
- var i = 1234 //再代入可能
- val i = 1234 //再代入不可(関数型的なかんじ)
- var i: Int = 1234 //型を明示することもできる
分岐
- if : Javaのif文相当。ただし式である(値を持つ)。
- 例: val length = if (str != null) str.length else 0
- match : パターンマッチ。switch文より柔軟
ループ
- for文 : Javaの拡張for文のような感じだが、filter/map用途にも使える
for (i <- 0 until 10) { //foreach的な例 printf("{0} ", i) } val strs = for (v <- values) yield { v.toString } //map的な例
- while, do while文 : Javaとほぼ同じだが、continue/breakがない
クロージャ
//例: foreachメソッド (メソッド呼び出しの「.」は、曖昧でなければ省略できる) 0 until 10 foreach {i => printf("{0} ", i) }
XMLリテラル
- きもーい(笑)
- val h1 = <h1>Hello, world!</h1>
- 中括弧を使うと値が埋め込める
val html = <html><body>{ for (value <- Array(1,2,3)) yield <p>{value}</p> }</body></html>
メソッド
- オーバーロード可能
- 一行でも書ける
def foo(str) = "Foo" + str
クラス
- コンストラクタはthis //def this() = this("", "")
- 暗黙にScalaObjectを継承する
- override def toString = ... //と書かないとコンパイルエラー
object
- シングルトン(インスタンスが一つだけ)なクラス。mainメソッドはここに書く
trait
- Javaのinterfaceのようで違うもの (Rubyのmoduleに近い?) 実装が書ける
- 実装を省略すると抽象メソッドになる
パッケージ
- 名前空間。Javaとほぼ同じ。
- package com.examples.foo で Fooを定義→com.examples.foo.Foo
- 取り込みは import で (import java.util.{List,Map} とか, import java.util._とか)
- 変数もimportできる(jsのwithみたいなものか?) val foo = new Foo → import foo
例外
- try{ .. } catch { case e: FooException => .. } finally { .. }
- throw new FooException(..)
パターンマッチ
- Haskellのような
- オブジェクトに対する正規表現のような
val foo: Any = 233 //AnyはJavaのObject型みたいなもの case class Name(name: String) //case classはmatchで使う用のクラス
foo match { case v: Int => .. case v: String => .. case _ => .. case Name(v) => .. }
Implicit conversions
- 型変換とか、型の拡張に使われる機構
implicit def int2str(value: Int): String = { //このメソッド名は何でもいいっぽ(型が重要) value.toString } val value: String = 1234 //うまくいく!
- 型の拡張の例
implicit def person2father(p: Person) : Father = new Father(p)
val person = new Person() val children = person.children //自動的にPersonからFatherが作られる
デモ
- ベンチマーク
- ツール
- Ant Scala task, Maven Scala plug-in
- ライブラリ
- Spec, Lift(webフレームワーク)
第一部まとめ
- Javaとの親和性大
- パフォーマンスもそこそこ
- 便利な文法やAPI→LightWeightな開発
- JavaでできることはScalaでできる!(Web、GUI、マルチメディア…)
余談
- Javaだと軽量に書けない…というわけではないよね
- 言語の改良、Eclipse、軽量指向なフレームワーク
- Javaには実績がある
- Scalaは楽しい
質疑応答
- Scalaのキラーなメリットってない?他の言語で絶対できないような
- ないと言ってもいい。今のところは。誰も発見/発明していない
- DSLとか?
- Java APIでできることはJavaでも出来るわけだから、実績がない分Scalaが不利
- Perl/Python/Rubyに対してなら、静的型(かつ軽量指向であること)がメリット。あと速度
- Lift -> Apache, Tomcat,
- 基本的にはJavaの環境をそのままつかう
- O/RマッパーはLift独自実装(ActiveRecordぽい) 中身はJDBCかも
応用
もっと濃い文法 : 遅延評価
- lazy val okButton = new JButton("OK") //okButtonが使われたときに生成される!
- 相互参照
lazy val a = (bを参照するなにか) lazy val b = (aを参照するなにか)
演算子の再定義
- 単項演算子 : + - ! ~
- def unary_+ : Num = new Num(value)
- 二項演算子 (優先順位は変えられない)
- def +(v: Num) = ...
scala.Predef
「_」(Perlの$_みたいな)
- 1 until 10 foreach { println(_) }
カリー化されたメソッドの定義
- def open(file: String) (p: InputStream => Unit) { // fileは文字列型、pはクロージャ
- 使うときは open("foo.txt") {in => ... }
- openに "foo.txt"を渡し、その結果にクロージャを渡している
高階関数
- def calc(f: (Int,Int) => Int, x:Int, y:Int) .. // fは2つの整数を受け取ってIntを返す関数
- Javaではメソッドを型につかえないので、内部でいろいろしている
Generics
- val array = new Array[String](10)
- array(0) = "foo" //配列の添字は()で表す
- 定義側は class Foo[T] (var value: T) とか (Tは型。型は使うときに指定する)
アノテーション
- Javaのとはいろいろ異なる
- @throws(classOf[IOException]) def open(...)
Actor(軽量プロセス)
- 標準添付
- Erlang由来? 非同期メッセージパッシング
- 10Actorくらいを1スレッドが担当→JVによりマルチコア対応!
- actorというメソッドがある
actor { for (i <- i1 to 10) { println("a: " + i) Thread.sleep(1000); } }
- 送信: foo ! i + 1
- 受信: receive{ case i: Int => .. }
これ以降のログは取れていません。資料公開に期待しましょう:-)
2008-05-09
■ [mac] Mac OS X Leopardでrubygemsがrequireできない
発端:
/Users/yhara/proj/bookmarklets % ./script/generate Rails requires RubyGems >= 0.9.4. Please install RubyGems and try again: http://rubygems.rubyforge.org
いやgemはあるんだが。
/Users/yhara/proj/bookmarklets % gem --version 1.1.1
LOAD_PATHは?
/Users/yhara/proj/bookmarklets % ruby -e 'p $LOAD_PATH' ["/opt/local/lib/ruby/site_ruby/1.8", "/opt/local/lib/ruby/site_ruby/1.8/i686-darwin9.2.2", "/opt/local/lib/ruby/site_ruby", "/opt/local/lib/ruby/vendor_ruby/1.8", "/opt/local/lib/ruby/vendor_ruby/1.8/i686-darwin9.2.2", "/opt/local/lib/ruby/vendor_ruby", "/opt/local/lib/ruby/1.8", "/opt/local/lib/ruby/1.8/i686-darwin9.2.2", "."]
普通だなぁ。
あ、いや、rubygems.rbってどこにあるんだろう?
/Users/yhara/proj/bookmarklets % locate rubygems.rb /Library/Ruby/Gems/1.8/gems/rubygems-update-1.1.1/lib/rubygems.rb /Library/Ruby/Site/1.8/rubygems.rb /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems.rb
なんじゃこりゃ。Mac変態すぎるだろ…。
そういやgemコマンドの実体はどこにあるんだろ。
/Users/yhara/proj/bookmarklets % which gem /usr/bin/gem /Users/yhara/proj/bookmarklets % ls -l /usr/bin/gem lrwxr-xr-x 1 root wheel 71 2 22 01:29 /usr/bin/gem -> ../../System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/gem
ふむ。じゃあ、ここに揃えるか。
/Users/yhara/proj/bookmarklets % export RUBYLIB=$RUBYLIB:/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8
/Users/yhara/proj/bookmarklets % ./script/generate Missing the Rails 2.0.2 gem. Please `gem install -v=2.0.2 rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.
えーー。
結論
めんどくなったので、自分でtgzをダウンロードしてインストールするなどした。
mv /usr/bin/gem /usr/bin/gem_leopard
追記
ブクマコメントで指摘してもらったのだが(ありがとうございます)、デフォルトのgemとMacPortsのrubyを組み合わせて使ってるのがいけなかったっぽい。 そりゃそうか。
というわけで、MacPortsでrubyを入れる人は、rb-rubygemsも忘れずにインストールしよう。
2008-05-12
■ [scheme] Guy Steele Jr.の論文
http://library.readscheme.org/page1.html より。
- "Scheme: An Interpreter for Extended Lambda Calculus"
- "Lambda: The Ultimate Imperative"
- "Lambda: The Ultimate Declarative"
- "Debunking the 'Expensive Procedure Call' Myth, or, Procedure Call Implementations Considered Harmful, or, Lambda: The Ultimate GOTO"
- "The Art of the Interpreter of, the Modularity Complex (Parts Zero, One, and Two)"
- "RABBIT: A Compiler for SCHEME"
- "Design of LISP-based Processors, or SCHEME: A Dielectric LISP, or Finite Memories Considered Harmful, or LAMBDA: The Ultimate Opcode"
「Lambda: The Ultimate ○○」シリーズがおもしろそげ。
2008-05-14
■ [ruby] Rubyで「ナベアツカレンダー」を作ってみた (iCalendar gem)
前の日記で
■ [junk] 3の倍数と3の付く日だけ猛烈に開発して、5の倍数の日は一切PCに触りません
という開発スタイルはどうか。NDD(Nabeatsu Driven Development)。
メリハリのあるプログラミングライフをあなたに!
[20080512#p02より引用]
というネタを書いたのですが、実際に「3の日」と「5の日」ってどれくらいの割合になるんでしょうか?例えば、Googleカレンダーで「3の日」「5の日」を表示できたら、とても分かりやすそうですよね。 そんなわけで、RubyからiCal形式のファイル(*.ics)を作る方法を調べてみました。
iCalendar gemの使い方
RubyGemsの icalendar というライブラリを使うと、iCal形式のファイルを簡単に作れるようです。
例えば、イベントが1つだけ登録されたカレンダーを作るならこんな感じ。
require 'rubygems' require 'icalendar' require 'date' # Rubyの標準添付ライブラリ。Dateクラス、DateTimeクラスを提供する。 cal = Icalendar::Calendar.new cal.event do dtstart Date.new(2005, 04, 29) dtend Date.new(2005, 04, 28) summary "Meeting with the man." description "Have a long lunch meeting and decide nothing..." klass "PRIVATE" end puts cal.to_ical
とても簡単ですね。:-) インストールはいつもの「gem install icalendar」で。
ナベアツカレンダーを作ろう
というわけで、「3の日」と「5の日」が登録されたカレンダーはこんな感じになりました。 だいたい週2〜3の割合で「3の日」が、週1〜2の割合で「5の日」があるみたいですね。 この微妙なランダム性がいい感じです。
作成に使ったスクリプトは以下です。
require 'rubygems' require 'icalendar' require 'date' # 新しいカレンダーを作る cal = Icalendar::Calendar.new # 今年。(とりあえず年末まであれば良いだろう) y = Time.now.year # 各月の、 (1..12).each do |m| # 各日について、 (1..31).each do |d| # 有効な日付なら (つまり、2月31日みたいな存在しない日でなければ) if Date.valid_date?(y, m, d) # 5の倍数の日は if d % 5 == 0 # 「5」という名前の終日イベントを登録 cal.event do dtstart Date.new(y, m, d) # その日 dtend Date.new(y, m, d) + 1 # 次の日 summary "5" # イベントのタイトル description "5" # イベントの説明 end # そうでなくて、3の倍数か「3」がつく日は elsif d % 3 == 0 or /3/ =~ d.to_s # 「3」という名前のイベント(0時〜1時)を登録 cal.event do dtstart DateTime.new(y, m, d, 0, 0) dtend DateTime.new(y, m, d, 1, 0) summary "3" description "3" end end end end end # iCal形式で出力 puts cal.to_ical
Googleカレンダーにも「ナベアツカレンダー」という名前で登録してみましたので、どうぞご利用ください。 よいナベアツライフを!
■ [scheme] R6RSからテストケースを抜き出したい (1)
r6rs.tar.gzにはtexファイルが含まれてるので、それの\begin{scheme}...\end{scheme} を抜き出せばいいんじゃね?
途中経過。
(bytevector-sint-ref b 0 (endianness little) 16)=> -3 (bytevector->u8-list b)=> (253 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) (bytevector-uint-set! b 0 (- (expt 2 128) 3) (endianness big) 16) (bytevector-uint-ref b 0 (endianness big) 16) => #xfffffffffffffffffffffffffffffffd
問題点:
- &assertionとかどうしようかな
- undefined と unspecified はどうしよう?
- 単にそのテストケースを無視することにする?
- 無視しないとすると、#<undef>をどう表すか? (define *the-undefined* (display)) とかか。
- expectedに「0もしくはNaN」とか書いてあったりするんだがw
何にせよ「; two return values」とか「; approximately」とか「; prints: error opening file」とかあるので、 多少は手作業で修正しないといかんかも。
抜きだしスクリプトは以下。
;;; extract test data from r6rs/documents/*.tex (use file.util) (use gauche.collection) (define *re-scheme-block* #/\\begin\{scheme\}(.*?)\\end\{scheme\}/) (define (test-case? str) (and (not (#/\\hyper/ str)) (not (#/\\vari?\{.*\}/ str)) )) ;(#/\\[lx]?ev/ str))) (define (tex->scheme-source str) (regexp-replace-all* str #/\\[lx]?ev/ "=>" #/\%\s*$/ "" #/\\exception\{\\&(.*?)\}/ "&\\1" #/\\sharpsign/ "#" #/\\backwhack\{\}/ "\\" #/\\schtrue(\{\})?/ "#t" #/\\schfalse(\{\})?/ "#f" #/\\sharpfoo\{(.)\}/ "#\\1" #/\\(ide|textrm)\{(.*?)\}/ "\\2" #/\\#/ "#" #/\\_/ "_" #/\\&/ "&" #/\\unspecified/ "#<unspecified>" #/\\theunspecified/ "#<undef>")) (define (tex->test-data-list str) (let1 match (*re-scheme-block* str) (if (and match (test-case? (match 1))) (cons (tex->scheme-source (match 1)) (tex->test-data-list (rxmatch-after match))) '()))) (define (extract-file path) (print ";;; ---- " path) (for-each print (tex->test-data-list (file->string path)))) (define (main args) (case (length args) ((1) (print "usage: extract-r6rs-test foo.tex")) (else (for-each extract-file (cdr args)))))
2008-05-15
■ [Ruby/SDL][mac] Ruby/SDLで作ったゲームをバイナリ配布したい
Ruby/SDLでゲームを作ったとき、他人に遊んでもらうにはどうしたらいいか。
- WIndows → exerbでexeにして配布
- Unix系 → aptとかでRuby/SDL入れてね
- Mac OS X → まず付属cd-romからXCodeをインストールして、macportsをインストールして、SDLとrsdlとRuby/SDLと…
明らかにMacがめんどい。
対策を考える
- finkでSDL、SDL_*, rsdl、Ruby/SDLをインストールできるようにする
- バイナリ配布すれば、XCodeが要らない。ステップがいっこ減る。
- と思ったらLeopard用のfink自体のバイナリ配布が無くて涙目www
- コンパイル済みのSDL_*, rsdl, Ruby/SDLをバイナリ配布する
- やりかたが分からん…けどfinkがやってるんだからできるはずだよな
2008-05-19
■ [linux] Ubuntu 7.10から8.04に上げたらキーボードがおかしくなりました(><)
具体的にはログイン時とかに「Error activating XKB configuration」とか言われて、 あと「変換」キーを押してもSCIMが起動しなくなった。どうも日本語キーボードとして認識できなくなったらしい。
[システム]->[設定]->[キーボード]の"レイアウト"の[追加]で、「レイアウト:日本」の キーボードを追加し、既存のキーボードレイアウトを削除してください。
という手順で直った。よかった。
2008-05-25
■ [softs] TubePlayer
日記の更新が滞っている原因1。
YouTubeとニコニコ動画を見るためのブラウザ。検索結果を文字だけでどばっと一覧表示してくれて、あとは各タイトルをクリックするだけで動画が見られる。
検索結果にサムネイルをいっぱい並べるのは分かり易いといえば分かり易いんだけど、一覧性が下がるのが困りもの。 クリック回数も増えるし、ブラウザのタブの枚数も増えるし。 TubePlayerの、「とりあえずタイトルだけ100件並べますね」というアプローチはなかなか快適だった。タイトルだけでも、わりと良い精度で取捨選択できるもんだな。 Jane Style(2chブラウザ)と同じ作者ってのがまた凄い。
ともかく、インターフェイスは重要だなーと再認識した。「大量の情報を少ない操作で効率よく閲覧できるようにする」という点ではLivedoor Readerと似てる。
■ [idea] メーリングリストを読むのに良い方法ないですかね?
インターフェイスの話だと、最近はメーリングリストのメールを読むのにいい方法を探している。
LDRに流し込むと全部のメールが1次元に並んでしまうので話を追いづらい。その点ではbladeのツリー表示が好きなんだけど、クリック回数が多いのがどうも。 Gmailのツリー一括表示はかなり理想に近いけど、アーカイブするのが面倒なんだよね(笑)。というか普通のメールと混ざっちゃうし。 自動でアーカイブすることもできるけど、そうすると今度は読まなくなる、という。
MLのログを1ツリー1エントリなフィードに変換するスクリプトを書けばいいのかな。そしてLDRで読む、と。ただ3日なり一週間なり遅延しないと綺麗にツリーにまとまってくれなさそうだけど。
■ [gadget] HORI コンパクトジョイスティック クリアブルー
日記の更新が滞っている原因2。久しぶりに繋いだらこれがなかなか。3000円にしては良くできてるよなぁ。
…とは思えど、やっぱり8方向なのが惜しいなーと思わずにいられない(笑)。どっかに4方向のジョイスティックとか売ってませんかね? もしくは4方向に改造するとか。
■ [ruby] PHP::SessionのRuby版を誰か
このまえYAPCで話題になったPHP::Session、誰かRubyに移植しないかなぁ。ちょっと所用で使いたいんですが。
■ [ruby] ruby-users.jp / rails-users.jp の使い方を考えてみた
同じくこのまえのYAPC以来、世間では *-users ブームなわけですが、誰かruby-users.jp取りませんか?と 書こうとしたんだけど、今調べたら既にあったわ。
んで、rails-users.jpともども、使い方をちょっと考えてみたんで書いてみるよ。
定番ライブラリの紹介がほしい
Railsで開発するときに困るのが、「今」○○をするならどのプラグインを使えばいいの?ってこと。 例えば認証ならこれとか、日本語対応ならこれとか、タグ付けならこれとか。 プラグインは多数の開発者の手によって日々進化するものだから、ハウツー本でお薦めされているものが「今」必ずしも一番いいとは限らない。
じゃあ、Railsでなんかしたいときにどうするのがいいか。「Googleで調べる」でもいいけど、しかし一番効率がいいのは「詳しい人に聞く」ってやつで、 じゃあ近くに詳しい人がいない場合は誰に聞けばいいの?
そこで rails-users.jp ですよ。
Rubyのライブラリについても、rubygemsのおかげで前よりずっと多くのライブラリを簡単に入手できるようになったわけで。 XMLならこれとか、ゲーム作るならこれとか、GUIやりたいならこれとか、別に解説記事じゃなくていいんで、ライブラリを探すための指針がどっかあればいいなと。
蛇足1
そろそろ日本のIT系ハブサイトのハブサイトを……って、あれ?
蛇足2
そういや俺も*-usersな名前のサイトを作ってたな…と思い出す。rubysdl-users.jpとか取得すべきか?w それとも http://sdl.ruby-users.jp/ をこっちに向けてもらうとか…
蛇足3
vim-users と lisp-users マダー? (functional-usersでもいいな。いやusersはおかしいか。functional-programmers.jp。長いな。)
2008-05-27
■ [tetris] tetrisonline.jpの登録が酷い
- 既知の認証局によって署名されていません
- パスワードは「予想しやすいものはやめましょう」と書いてるわりに記号は使えない
- ニックネームが半角6文字から(何故?)、仕方ないのでyhara2に
- しかも登録後ニックネームは変更不可 (ユーザーIDが変更不可なのは分かるんだが…)
- メールアドレスが長すぎるとはねられる、例えば yutaka.hara+tetrisonlineあっとgmail.comとかは不可
- というか「+」が使えない
- なぜか血液型の登録が必須、しかも全体に公開
んでもって、頑張って全部埋めたと思ったらこの仕打ちですよ。
どうしろと…(´・ω・`)
*1 ちなみに僕の血液型はABではなくOです
2008-05-29
■ [biwascheme] JavaScriptでS式を記述するいい方法はないものか
方法1: new Pairのベタ書き。
new Pair(1, new Pair(2, new Pair(3, nil)));
このように、リストの各要素ごとにインデントし、最後の行にnilを書くのが読みやすいです。
方法2: Array#to_list。
[1, 2, 3].to_list();
ベタ書きはさすがに面倒なので、最近はこんな感じで書けるようにしていました。 でも、リストがネストしたときに読みにくいのがちょっと困る。
//例: (let ((x 1)) y) [Sym("let"), [[Sym("x"), 1].to_list()].to_list(), Sym("y")].to_list();
これを解決するために考えたのが:
方法3: BiwaScheme.List。
//例: (let ((x 1)) y) List(Sym("let"), List(List(Sym("x"), 1)), Sym("y"));
どうでしょうか?
「もっとこう書けるよ」というのがありましたらぜひ教えてください m(__)m
■ [biwascheme] condとcaseを実装しました
最近関西で始まったGauche本読書会やSICP読書会でBiwaSchemeを試していただいたのが、 そこで困ったのが「condとcaseがない」こと。
っていうかSICPってわりと初めの方からcondとか出てくるんだなー(一章はifとかだけだろと思い込んでいた^^;)というのは置いといて、 せっかく使ってもらえるのにcondが無いのは悲しいねってことで実装してみました。
■ [biwascheme] cond
以下、これからScheme処理系を実装する人のためにメモを残しておきます。
condはRubyでいう「caseの引数がないやつ」というか(←わかりにくいな)、 if〜elseを順に並べたようなものです。
(define m 12) (print (cond ((<= 3 m 5) "spring") ((<= 6 m 8) "summer") ((<= 9 m 11) "autumn") (else "winter")))
で、R6RSの仕様書によるとそれぞれの節(clause)は以下のような形をしているようです。
まずは条件とそれが成り立ったときの処理がかかれてい普通の節。 これはifに展開すればOKでしょう。
// pattern 1: (test expr) // -> (if test expr ret)
次に、exprがなくて条件だけの場合。この場合は、条件式の結果が返り値になるそうです。 じゃあorで。
// pattern 2: (test) // -> (or test ret)
さらに、「=>」を使うことで条件式の結果を関数に渡すこともできます。 これってR6RSからかと思ってたけど、 んですね。
この場合は、条件式の結果を保存するための一時変数が必要になります。 ここで問題になるのが一時変数の名前で、下手なものを選ぶとユーザが使っている変数名と バッティングして大変なことに…。
BiwaSchemeではとりあえず、__gensym_1 のような「被らなさそうなシンボル」を 生成することにしました。本当は、ユーザが '__gensym_1 のようなシンボルを使っても 大丈夫なように実装しないといけないんですが、まずは「そういうシンボルは使わないで ください」という方針で行きましょう。
// pattern 3: (test => expr) // -> (let ((#<gensym1> test)) // (if test (expr #<gensym1>) ret))
さて、condの最後の節にはelse節を置くことができます。 これもいくつかのパターンに分けられます。
まずは普通のelse節。基本的にはbeginを使えばOKですが、展開後の式をコンパクトにするため、 式が一つだけだった場合はbeginを省略することにしましょう。*1
// pattern A: (else expr ...) // -> (begin expr ...) // pattern B: (else expr) // -> expr
次に、exprが省略されたelse節。このとき何を返すかはR5/6RSでは未定義っぽいんですが、 Gaucheが#fを返してたので#fを返すことにしました。
// pattern A: (else) // -> #f ; not specified in R6RS...?
最後に、else節がない場合。つまりどの節の条件も成り立たなかったとき、 R6RSでは「unspecified」な値を返すことになっています。
// pattern D: no else clause // -> #<undef>
と、ここまで書いてしまえば、実装するのは比較的簡単でした。このエントリの最後に貼っておきます。
実装
define_syntax("cond", function(x){ var clauses = x.cdr; if(!(clauses instanceof Pair) || clauses === nil){ throw new Error("malformed cond: cond needs list but got " + to_write_ss(clauses)); } // TODO: assert that clauses is a proper list var ret = null; clauses.to_array().reverse().each(function(clause){ if(!(clause instanceof Pair)){ throw new Error("bad clause in cond: " + to_write_ss(clause)); } if(clause.car === Sym("else")){ if(ret !== null){ throw new Error("'else' clause of cond followed by more clauses: " + to_write_ss(clauses)); } else if(clause.cdr === nil){ // pattern A: (else) // -> #f ; not specified in R6RS...? ret = false; } else if(clause.cdr.cdr === nil){ // pattern B: (else expr) // -> expr ret = clause.cdr.car; } else{ // pattern C: (else expr ...) // -> (begin expr ...) ret = new Pair(Sym("begin"), clause.cdr); } } else if(ret === null){ // pattern D: no else clause // -> #<undef> ret = undefined; } else{ var test = clause.car; if(clause.cdr === nil){ // pattern 1: (test) // -> (or test ret) ret = [Sym("or"), test, ret].to_list(); } else if (clause.cdr.cdr === nil){ // pattern 2: (test expr) // -> (if test expr ret) ret = [Sym("if"), test, clause.cdr.car, ret].to_list(); } else if(clause.cdr.car === Sym("=>")){ // pattern 3: (test => expr) // -> (let ((#<gensym1> test)) // (if test (expr #<gensym1>) ret)) var test = clause.car, expr = clause.cdr.cdr.car; var tmp_sym = BiwaScheme.gensym(); ret = List(Sym("let"), List( List(tmp_sym, test) ), List(Sym("if"), test, List(expr, tmp_sym), ret)); } else{ // pattern 4: (test expr ...) // -> (if test (begin expr ...) ret) ret = [Sym("if"), test, new Pair(Sym("begin"), clause.cdr), ret].to_list(); } } }); return ret; });
*1 これは「なんとなく無駄が嫌」という理由によるもので、この最適化にどのくらい効果があるのかは不明です^^;; 良い子のみんなはちゃんとボトルネックを測ってから最適化しようね!
■ [biwascheme] case
caseも、condと考え方はおなじ。
まず、keyを一時変数に代入しておきます。
// (case key clauses ....) // -> (let ((#<gensym1> key))
で、普通の節だったら、eqv?に展開。
// pattern A: ((datum ...) expr ...) // -> (if (or (eqv? key (quote d1)) ...) (begin expr ...) ret)
elseだったら単純にbeginに展開。
// pattern B: (else expr ...) // -> (begin expr ...)
■ [biwascheme] macroexpand族
caseのデバッグで欲しくなったので、macroexpand一族も実装してみた。
最初は何をしたらいいのかが分かってなくて困ったけど、できてみれば簡単だった。
- %macroexpandはマクロで、与えられた式をマクロでなくなるまで展開する。これを行う関数はもうある(Interpreter#expand)ので簡単。
- %macroexpand-1はマクロを1段だけ展開する。要するにTopEnvからマクロ変換器を取ってきて、一回だけ実行してやればいい。
- %がついてない方 (macroexpand, macroexpand-1) は、単にマクロから関数になっただけ(quoteされたSchemeプログラムを受け取る)。
var macroexpand_1 = function(x){ if(x instanceof Pair){ if(x.car instanceof Symbol && TopEnv[x.car.name] instanceof Syntax){ var transformer = TopEnv[x.car.name]; x = transformer.transform(x); } else throw new Error("macroexpand-1: `" + to_write_ss(x) + "' is not a macro"); } return x; } define_syntax("%macroexpand", function(x){ var expanded = (new Interpreter).expand(x.cdr.car); return [Sym("quote"), expanded].to_list(); }); define_syntax("%macroexpand-1", function(x){ var expanded = macroexpand_1(x.cdr.car); return [Sym("quote"), expanded].to_list(); }); define_libfunc("macroexpand", 1, 1, function(ar){ return (new Interpreter).expand(ar[0]); }); define_libfunc("macroexpand-1", 1, 1, function(ar){ return macroexpand_1(ar[0]); });
■ [biwascheme][idea] with-output-to-elementとかで
printの出力結果がぜんぶdiv要素に入るとか良くね?
;; <div id="foo"></div> (with-output-to-element ($ "foo") (print "hello") (print "world")) (get-content ($ "foo")) ;; → "hello\nworld\n"
2008-05-30
■ [mac][Ruby/SDL] Mac OS X と Ruby/SDL 2.0.1 と Ruby 1.9
MacでもRuby/SDL+Fiberしたい。したいですよね。
というわけでやってみた。
手順
まずRuby/SDLを普通にコンパイルする。rubyとかSDLとかSDL_imageとかSDL_ttfとかSDL_soundはMacPortsで入れておこう。
次にRuby 1.9.0-1のソースを取ってきてコンパイルする。
- ./configure --prefix=/Users/yhara/bin/rubys/ruby-1.9.0-1
- make && make install
- ln -s ~/bin/rubys/ruby-1.9.0-1/ ~/bin/ruby-1.9.0-1
次にrsdlを取ってきてコンパイルする…んだが、timespecが再定義されているというエラーが。
以下適当すぎるパッチ。
/Users/yhara/research/Ruby/rubys/ruby-1.9.0-1/include/ruby % diff missing.h ~/bin/rubys/ruby-1.9.0-1/include/ruby-1.9.0/ruby/missing.h 34,39c34,39 < #if !defined(HAVE_STRUCT_TIMESPEC) < struct timespec { < time_t tv_sec; /* seconds */ < long tv_nsec; /* nanoseconds */ < }; < #endif --- > //#if !defined(HAVE_STRUCT_TIMESPEC) > //struct timespec { > // time_t tv_sec; /* seconds */ > // long tv_nsec; /* nanoseconds */ > //}; > //#endif
さらに、Makefileをちょっと修正する。
/Users/yhara/research/sdl/rsdl % diff Makefile Makefile-1.9 4c4 < LIBS = -lpthread -ldl -lobjc `sdl-config --libs` --- > LIBS = -L/Users/yhara/bin/rubys/ruby-1.9.0-1/lib -lpthread -ldl -lobjc `sdl-config --libs`
これでmakeが通るはず。rsdlを~/bin/rsdl-1.9にコピー。あ、~/binは「どこかパスの通ってるとこ」ってことね。
うごきましたー
ばばーん
ソースは 20080210 を参照。
Before...
□ yhara [ありがとうございます。 tDiaryにはいつもお世話になっていますm(__)m]
□ tnobuhito [冷蔵庫が無いような気が。。。]
□ yhara [冷蔵庫は書き忘れですねw]