2007-12-01
■ [javascript] JSONをevalするときは括弧で囲まないといけないらしい?
eval("{}")
これはundefinedだが
eval("({})")
これは Object のインスタンスを返す。
JSONをevalするときは
result = eval("("+json+")");
とするのがセオリーなのかな?(prototype.jsはそうなっている)
■ [ruby][scheme] S式をRubyで手軽に生成する
ちょっと気の利いたWebAPIを作ったとき、フォーマットはJSONかXMLが代表的なんだけど、 ことSchemeから使うのならS式の方が使いやすいのは容易に想像できると思われます。 が、JSONは gem install json すれば JSON.generate(obj) で手軽に生成できるのに対し、 S式はRubyから手軽に生成する方法がありません。困りましたね。
というわけで、S式をRubyで手軽に生成するコードを書いてみましたよ。
sexp.rb:
class Array def to_sexp items = self.map{|item| case item when true "#t" when false "#f" when Array item.to_sexp when Symbol item == :dot ? "." : "'#{item}" when Float if (i = item.infinite?) i == 1 ? "+inf.0" : "-inf.0" else item.to_s end else item.inspect end }.join(" ") ["(", items , ")"].join end end
こんな感じで使えます。
[1, [2, 3], 4].to_sexp #=> "(1 (2 3) 4)"
ドット対の表現がちょっと格好悪いんですけど、手軽さのためと割り切って我慢してください。
[1, :dot, 4].to_sexp #=> '(1 . 4)'
余談
ちなみにS式はリストなので、Rubyの配列とそのまま対応するものではありませんよ。
'(1 2 3) というリストは、無理やり配列で表現すると[1, [2, [3, nil]]] のようになります。 '(1 (2 3) 4)というリストは、[1, [ [2, [3, nil]], [4, nil]] のようになります。 実際にセルの絵を書いてみるとよく分かると思います。
これを手書きするのはかなり辛いので、上のコードではそのへんを割り切った仕様にしてみました (だいたいSchemeの「()」を「[]」に直せばOKなように)。
テスト
RSpecによるテストです。
require 'sexp' describe "sexp" do context "nil" do [].to_sexp.should == "()" end context "single" do [1].to_sexp.should == "(1)" end context "double" do [1, 2].to_sexp.should == "(1 2)" end context "triple" do [1, 2, 3].to_sexp.should == "(1 2 3)" end context "string" do [1, "asdf", 3].to_sexp.should == '(1 "asdf" 3)' end context "nest" do [1, [2, 3], 4].to_sexp.should == '(1 (2 3) 4)' end context "dot pair" do [1, :dot, 4].to_sexp.should == '(1 . 4)' end context "symbol" do [:a, :b, :c].to_sexp.should == "('a 'b 'c)" end context "boolean" do [true, false].to_sexp.should == "(#t #f)" end context "infinity" do [1.0/0, -1.0/0].to_sexp.should == "(+inf.0 -inf.0)" end end
2007-12-02
■ [event][prog] LiveCoding#5に出演しました
土曜日に大阪で行われたイベントLiveCoding#5に、 LiveCoderとして出演してきました。
事前に、LivePromoter(※主催者のことらしい)であるujihisaさんから できるだけマニアックな方がいいというオーダーを受けていたので、 Ruby + Javascript + Scheme + Zu という無茶な構成にしてみました(笑)。 *1 BiwaSchemeで 「ず」のインタプリタを作るというテーマだったのですが、 予想外に手間取ってしまい、パース結果をRubyのCGI経由で取ってくるとこまでしか行きませんでした。 解説のnaoya_tさん、Abeさんごめんなさい(><)
敗因
- Ajaxのクロスドメイン制約を忘れていた
- CGIはサーバに、htmlはローカルに置くつもりだったんですが、どちらかに統一しなければならなかったんですね。
- 会場では、慣れている環境ということで全てローカルに統一しました。
- このせいでWEBrickのリファレンスをLiveに参照することに…^^;
- httpのPOSTを行う関数がBiwaSchemeに存在しないことを忘れていた (GETはあった)
- 直前にあわてて用意したんですが、バグってましたね。
はまったところ
- textareaはinnerHTMLではなく、valueでないと中身が取れない
というわけで
define_libfunc("get-content", 1, 1, function(ar){ return ar[0].innerHTML; });
というライブラリ関数を
return ar[0].value;
のように書き換えようとしたら、観客の皆様に怒られました(笑) ちゃんと
return ar[0].value || ar[0].innerHTML;
みたいにしないといけないですね。(と思ったんですが実はこれでも駄目で、
return ar[0].value || ar[0].innerHTML.unescapeHTML();
が正解。)
- alistを取るAPIにしたつもりだったのに、(cons "src" zu) みたいなペアを渡していた(a"list"なんだから、ペアのリストにしなければならない)
- quasiquoteをquoteにしていた。 '("src" . zu) だと、 '("src" . 'zu) という意味になってしまう
- innerHTMLを使うと、中の「>」や「<」がエスケープされてしまう(終了後数十分してやっと気付いた)。
教訓
- 「ここまでは5分あれば行けるやろ」と思うところまでしか辿りつかない。
- 調理はLiveだとしても、素材の仕込みはちゃんとやっておくべし。
他の人のとか
色んなところで既に書かれていますが、oxy君 のコーディング速度があり得なかったですね(笑)。 TGMのGM認定動画を見たときくらいの衝撃を受けました。 どっちも、操作の早さに注目しがちだけど、本当に凄いのは思考の方であるのも同じですね。
あとFlyMake凄いとか、Flash面白い(開発環境が)とか。
ともあれ、発表者の皆さん、参加者の皆さん、会場を提供してくださったグッデイの 皆さん、そして主催のujihisaさん、どうもお疲れ様でした。
*1 実は「Railsで画像ギャラリーを作る」みたいな普通の案も考えていた
■ [javascript] prototype.jsでラジオボタンの値を取得する
$A($('form_name')['input_name']).find(function(v){return v.checked}).value
長 す ぎ る 。$F('input_name')で取れてほしいなぁ。
参考:
■ [biwascheme] サーバ側のCGIにpostする例
LiveCodingで書いたのを、いろいろ修正してちゃんと動くようにしたやつを以下に置きました。
textareaにZuを書いてボタンを押すと、サーバ側のCGIを使って図をパースします。 あとはパース結果を使ってインタプリタを書くだけですが、 そのソースを載せるにはこのブログの余白は狭すぎるので、あとは読者への宿題とします(常套句)。
ソースを見れば分かりますが、HTML内にはJavascriptは一切書かれていません:-) 通信からイベントハンドラまで、全てSchemeで記述されています。*1
*1 もちろん、各APIはJavascriptで実装されているわけですが
■ [rubiskell] RubyからHaskellを呼び出すライブラリ、Rubiskellを作ってみた
Rubyでは、簡単なことは簡単に、複雑なことはそれなりにできる。
Haskellでは、簡単なことは複雑に、複雑なことは簡単にできる。
[ujihisa (2007)より引用]
だとしたら、複雑な部分をHaskellで書いて、簡単なところをRubyで書けば最強なんじゃね?
require 'rubiskell.rb' FIB = <<EOD fib :: Int -> Int fib 0 = 0 fib 1 = 1 fib n = fib (n-2) + fib (n-1) EOD def fib(n) fib = Haskell.new(FIB) fib[n] end puts "fib 5 is #{fib(5)}."
実行結果:
C:\proj\rubiskell>./sample.rb fib 5 is 5.
というわけでRubiskellは、Rubyスクリプト中にHaskellのコードを埋め込むためのライブラリです。
Haskellがあまり得意でない入出力やユーザーインターフェイスをRubyで補うことにより、 LightWeightで信頼性の高いHaskellプログラミングを手軽に体験できるようになるとかなんとか。
動作原理
単純に、テンポラリファイルを作ってrunghcに*1食わせているだけです。
Haskellプログラム中の型宣言部分をパーズして、自動的に入力・出力をエンコードします。
制限
- 渡せる引数は任意個数の整数および文字列のみです。リストやタプルはまだ実装してません。
- 現在はコマンドライン経由で引数を渡しているため、あまり長い文字列は渡せません。win32-open3というgemを使うとwindowsでもopen3できるようになるらしいので、そのうち標準入出力を使うように直します。
感想
以前からネタだけは考えていたんですが、実装したら意外とあっさり動いてびっくり。
呼出し毎にHaskellインタプリタを呼ぶので、短い計算を何度も行うような用途には向いてないかも知れません。 オセロのCPUの思考ルーチンなど、時間のかかる計算を時間を置いて呼ぶような用途を想定しています。
GHCが使えるなら、最初の実行時にコンパイルしてしまうというのもアリかもなぁ。 あと副作用がないHaskellプログラムなら、一度実行した結果はキャッシュできるとか(Rubyレベルでのメモ化)。
ともかく、僕自身はあまりHaskellに詳しくないので^^; Haskellerな皆様の感想をお待ちしています。 ぜひ一度お試しください。
*1 オプションでhugs等別の処理系も指定可
2007-12-04
■ [event][scheme] 生駒読書会#1
日曜日に生駒に行ってきましたよ。てか関東からも人が来るとかwww生駒すげえwww *1
Scheme使いとSchemeビギナーが半々だったので、3章からやると意味不明になるかな…と思って 2章の最後から始めることを提案したんですが、別に良かったのかなぁ。
3impのために初めて書いたマクロのコードをここに置いときますね。
;; (record (a b c) ls (+ a b c)) (define-macro (record vars val . exps) `(apply (lambda ,vars ,@exps) ,val)) ;; (record-case exp1 ;; [key vars exp2..] ;; ... ;; [else exp3..] (define-syntax record-case (syntax-rules (else) ((record-case exp1 [else exp3 exp4 ...]) ; for else (begin exp3 exp4 ...)) ((record-case exp1 [key vars exp2 ...]) ; only one case (record vars (cdr exp1) exp2 ...)) ((record-case exp1 [key vars exp2 ...] case2 ...) ; more than one case (let ([r exp1]) (if (eq? (car r) (quote key)) (record vars (cdr r) exp2 ...) (record-case exp1 case2 ...)))))) (define (main args) (print (record-case '(hoge 1 2 3) (hoge (x y z) (+ x y z)) (else #f))))
今日気付いたんですが、Gaucheの場合recordはmatch-let1で代用可能です((use util.match)が必要)。
ともあれ、初主催お疲れ様でした:-)
*1 実家は関西だそうですが
■ [prog] CodeReposのアカウントを頂きました
初コミット。 テストがてらに、この前のRubyでS式を作るやつを上げてみました。
チェックアウトは
svn co http://svn.coderepos.org/share/lang/ruby/misc/sexp
からどうぞ!
■ [event] 2007年度未踏I期 畑PM採択プロジェクト 最終成果報告会
sandbox(仮) と 「子猫の手」 がおもしろかったお! あとafrousはUI(Ajax)の作りこみがやばい。
Objec[t]ankはなんかアカウントがうまく作れなくてよくわからん。 concier.orgはなんか動いてるのはわかるけどよくわからん。
2007-12-06
■ [biwascheme] Re:Re:biwascheme でXHR
POSTはLiveCodingのときに作ったので、
(define result (http-post "textgraph.cgi" `(("text" . ,zu) ("format" . "sexp"))))
みたいにURLとalistを渡すことでPOSTできるようになりました (注:quasiquoteはまだ実装していないので、実際にはlistとconsでどうぞ…(´・ω・`))
というわけで、今は
- (http-request url) : GET
- (http-post url params) : POST
という風になっています。 コールバックは今のところonSuccessとonFailureしか見てなくて、前者の場合は結果の文字列、後者の場合は#fを返すようにしています。
あとイベントハンドラにSchemeのクロージャを設定できるようになったので、
(define $ getelem)
(set-handler! ($ "preview-btn") "click" (lambda (e) (let ((text (get-content ($ "source")))) (set-content! ($ "preview-div") (or (http-post "wikiformatter.cgi" (list (cons "text" . text))) "error: connection failed")))))
とか書けます :-)
リクエストがタイムアウトしたとき急に切れたとき(transport.responceText === nullのとき)
とかタイムアウトしたとき(検出方法未調査)はどうするのが良いのかなぁ。
2007-12-07
■ [javascript] サーバが一定時間応答しなかったら処理を打ち切る
探していたものとは違うものだけど、一応メモ。
ネタ元:
インデントが崩れているので直したものを貼っておきますね。
function callInProgress (xmlhttp) { switch (xmlhttp.readyState) { case 1: case 2: case 3: return true; break; // Case 4 and 0 default: return false; break; } } function showFailureMessage() { alert('uh oh, it looks like the network is down. Try again shortly'); } // Register global responders that will occur on all AJAX requests Ajax.Responders.register({ onCreate: function(request) { request['timeoutId'] = window.setTimeout( function() { // If we have hit the timeout and the AJAX request is active, abort it and let the user know if (callInProgress(request.transport)) { request.transport.abort(); showFailureMessage(); // Run the onFailure method if we set one up when creating the AJAX object if (request.options['onFailure']) { request.options['onFailure'](request.transport, request.json); } } }, 5000 // Five seconds ); }, onComplete: function(request) { // Clear the timeout, the request completed ok window.clearTimeout(request['timeoutId']); } });
要するに、setTimeoutで「5秒たったら通信が完了したかどうかチェックする処理」を登録する、と。
■ [scheme] quasiquote(`)をRubyist的に説明してみる
quasiquote == #{}
quasiquoteは、Rubyでいう「#{}」みたいなもんです。
ふつーに '(a b c) と書くと、これは ('a 'b 'c) みたいにシンボルのリストになるんだけど、 バッククオート(`)を使って `(a b ,c) と書くと、,c のところに c の値が入る。 例えば c == 3 なら ('a 'b 3) というリストになるわけです。
Ruby:
c = 3 "a b #{c}" #=> "a b 3"
Scheme:
(define c 3) `(a b ,c) ;=> '('a 'b 3)
ちなみに「`」とか「,」は別名があって、`(a b ,c) は (quasiquote a b (unquote c)) の省略記法であるとされています。
unquote-splicing == *ary
「,」は変数の値を埋め込みましたが、「,@」というのもあって、これは Rubyの「*」みたいにリストの中身を展開して埋め込みます。
Scheme:
(define d '(4 5)) `(a b ,c) ;=> '('a 'b '(4 5)) `(a b ,@c) ;=> '('a 'b 4 5)
Ruby:
d = [4, 5] a, b = *d #=> a=4, b=5
でっていう
OnLisp読書会で上のような比喩が発案されたらしいので、書いてみたかっただけ。
てか実装上は上のような単純な例はどうでも良くて、ネストしたquasiquoteとかがヤバげ。
とりあえず仕様書おいときますね [あとで読む]
■ [scheme] R6RSでは、「変数参照のようなマクロ」を定義できるらしい
ふつーのマクロは (somemacro a b c) みたいに、関数適用の形で呼び出すんですが、 R6RSでは identifier-syntax というものを使って「変数参照だと思ったら実はマクロ」 みたいなことができるらしい。
まずdefine-syntaxと組み合わせて、マクロを定義します。ここでは「p-car」という名前のマクロを定義してみます。 identifier-syntaxでは、普通の参照のときと、代入されるとき(set!の左辺に来るとき)の2種類のケースを定義できます。
(define-syntax p-car (identifier-syntax (_ (car p)) ((set! _ e) (set-car! p e))))
このとき、p-carの参照や代入時にマクロ展開が起きて、こうなります。
(define p (cons 4 5)) p-car ⇒ 4 (set! p-car 15) p.car ⇒ 15 p ⇒ (15 5)
うん、面白いけど…何に使うんだろ…w
set!の動作がいじれるってのが肝なのかな。
2007-12-12
■ [biwascheme] BiwaScheme 0.2を公開しました
ああ、tar内のbiwascheme.jsはwebscheme_lib.jsを読み込んでなかったかも…。
いい機会なのでアーカイブを作りなおしてみました。(ダウンロード) よろしければこっちでお試しください>ひげぽんさん
0.1からの変更点:
- 関数が増えたりなんだり
2007-12-14
■ [biwascheme] Syntaxを導入した
マクロ展開を実装した。
例えば (and a b c) を (if a (if b c #f)) に展開するとか。
実装方法は、評価前に式を再帰的に調べて、(and ...) のような呼び出しがあれば 「and」に関連付けられた展開器*1を使ってif文に展開する…という感じ。
このとき注意しないといけないのは、展開結果にさらにマクロが含まれる可能性があるということ(例えばletの中でandを使うとか)。 なので、結果が変化しなくなるまで結果に対してもうマクロ展開を試み続ける必要がある。
関数的にやるなら「展開前の式と展開後の式の構造を再帰的に調べ、同一かどうか判定する」とか、 「展開が起こったかどうかを表すフラグを式といっしょに返す」とかいう手段がありそうだけど、 前者は重そうだし、後者は書くのがめんどくさかったので(笑) 展開が起こったら引数で渡されたフラグオブジェクトを 破壊的に変更することにした。これだと、expand関数に引数を一つ増やすだけなので楽ちん。
この方法でandとorとletを定義した。あとはcaseとかcondとかか。
いずれはSchemeのコードでも展開器を定義できるようにしたい。 それができればdefine-macroが実装できるはず。
*1 今はJavascriptの関数で書いている
2007-12-15
■ [ruby][event] Ruby/Rails勉強会@関西-21
行ってきた。
久しぶりの神戸で、まつもとさんが来られるということもあって、 初めての方がいつもより多かった模様。また京都や大阪会場にもいらしてくださいね。
最初のセッションのロガーをしたので、ログを以下に貼っておきます。編集中 編集終わりました。長文警報発令中。
■ [ruby] Rubyでtwitterの新着を読み上げさせてみる
編集中につき小ネタでもひとつ。
Macにはsayという音声合成で喋ってくれるコマンドがあるのだが、gem install win32-sapi でWindowsでも似たようなことが できるとのこと。ema++
で、ついでなので声を変えられるようにしてみた。 手元では-n 0 はデフォルトの声(日本人)、-n 1 はちょっと高い声(日本人)、-n 2 は外国人の声になった。
say.bat
@echo off ruby -x "%~f0" %* goto :EOF #!ruby #---------------ruby script starts here require 'rubygems' require 'win32/sapi5' require 'optparse' $sp = Win32::SpVoice.new opt = OptionParser.new{|o| o.on('-n n'){|n| n = n.to_i count = $sp.GetVoices.Count if (0...count).include? n $sp.Voice = $sp.GetVoices.Item(n) else raise "-n: out of range; must be < #{count}" end } }.parse!(ARGV) $sp.speak( ARGV.join( ' ' ) ) #---------------ruby script ends here __END__ :EOF rem vim:set ft=ruby: see CALL /?
さらに、twitter gemと組み合わせて friends_timelineを喋らせる例。(上のsay.batをパスの通ったところに置いてください)
require 'rubygems' require 'twitter' require 'kconv' def decode_utf(str) str.gsub(/&#([\d]{4,5});/){ [$1.to_i].pack "U" } end twit = Twitter::Base.new("mail@example.jp", "password") twit.timeline(:friends).each do |s| `say #{s.user.screen_name.gsub(/_/,' ')} 曰く #{decode_utf(s.text).tosjis}` end
これはたのしいwww
■ [ruby][event] 「Ruby 1.9 を語る」 by まつもとゆきひろ - Ruby/Rails勉強会@関西-21
- 島根の方から来ました
- Ruby1.9について語る
- 新ロゴ
- エッジが効いている
- 昨年のRubyKaigi2006で、2007年のクリスマスリリースを発表
- あと1年もあるじゃん(無限の未来?)
- 年々、歳を取るのが加速度的に早くなる
- あと10日…
- 大丈夫か
- 連日、かなりでかいパッチが
- まあ、1.8のように出たら安定しているものではないので
- 「1.9をクリスマスにリリースするというお祭り」だと思ってもらえると…(笑)
基本方針
- {1.8 => 安定, 1.9 => 革新, 2.0 => 人参}
- 1.8
- 日常利用するもの、安定していて、過去のRubyとの互換性も高い (バグは直すが、仕様は変更されない)
- 1.8のメンテナはまつもとさんではない (1.8全体:knu(musha)さん、1.8.x : mput(卜部)さん)
- 2人ともきちんとした人です
- ということで、1.9が出たからといって1.8がなくなったりはしない
- 1.9
- より高速、より強力、より正しい仕様(希望)
- 「間違った仕様」を直すので、1.8とはいくつか非互換がある(後述)
- 2.0
- 馬につるす人参(笑)
- コミュニティは鮫(止まると死んでしまう)
- ほとんどのOSSコミュニティは「楽しいから」続く
- Ruby(を作る方の)コミュニティのために、常に変化がないといけない
- 1.9.1が出ると、1.9であまり無茶ができなくなる →そのための新しい遊び場
- 重要な非互換は1.9でだいたい導入したので、2.0ではコードスケーラビリティとかを考えていく予定
- 100万行書けるように?
- パフォーマンスとの兼ね合い
- AOP, selector namespace, traitsとかについて考えていきたい
非互換性
- 1.9は1.8と非互換がある
- 重大な非互換性は3つ:ブロックパラメータ、文字列、M17N, Unicode
ブロックパラメータ
- ブロックパラメータにはローカル変数しか書けなくなる
- 1.8では foo{|$a| ..} のような書き方が許されていた → これからは $a = foo{|a| .. a} で
- × ary.each{|$var|
- × ary.each{|obj.attr|
- × ary.each{|ar[2]|
- ブロックパラメータはブロック外の変数を上書きしない
x = 15 loop{|x| x = 10; break} p x #=> 15
- 内側と外側のxは違うものになりました
- -wオプションを付けると警告が出る (warning: shadowing outer local variable - x)
- こっちの方が正しい(昔の私は間違っていました(笑))
- 昔はブロックはC言語でいうfor文の代わりだったので、$aを許すのも自然だった。しかし段階的にRubyの仕様を積み上げていく過程で、ブロックはむしろ無名関数としての正確が強くなっていった (自分の中でのRubyの性格が変わった)
- 内側と外側のxは違うものになりました
- 行儀のいいプログラムなら引っかからないはず(笑)
String
- String#eachが廃止された。つまり、String自身はもうEnumerableではない。
- なぜか?
- String#eachは、何について繰り返すべきか自明でない(行?文字?バイト?)
- 1.9では、明示的にいずれかを選んでもらうようにした
- str.lines.each (or each_line)
- str.chars.each (or each_char)
- str.bytes.each (or each_byte)
- str.each とかしてるプログラムはほとんどないので影響は少ないと思う
M17N, Unicode
- 文字列をバイトベースではなく、文字ベースで扱うようにした
- それぞれの文字はエンコーディングに対応
"ABCあいう"[0] #=> ?A(数値)から"A"(文字列)に "あ"[0] #=> "あ" "ABCあいう"[3,1] #=> "あ"
- エンコーディングの指定
- # -* coding: UTF-8 -*- というコメントを先頭に入れるとRuby(とかEmacsとか)がそれ以下をUTF8として扱う
- Windowsな人なら # -* coding: shift-jis -*- とか
- 日本語の変数名も使える :-)
- けどその場合、UTF-8の"太郎"とShift-JISの"太郎"は別モノとして扱われるので、ソースコードのエンコーディングはそろえておきましょう(笑)
- openもエンコーディングに対応
- open(path, "r:EUC-JP"){|f| f.read} #デフォルトは、Rubyスクリプトのソースコードと同じ
- こうするとEUC-JPの文字列を得る (RubyスクリプトがUTF-8だからといって、それに自動的に変換されたりはしない)
小さな非互換
- たくさん
- でも小さいので普通は問題にならないはず
- なくなったもの(ほとんどは1.8でも警告が出る)
- VERSION, VERSION_DATEとか (RUBY_VERSION, RUBY_VERSION_DATEだけに)
- Kernel.to_a (obj.to_a) 配列に変換する意味のあるものだけto_aが定義されるように
- Kernel#getc (Kernel#getsは残る)
- Object#type (Object#classを使う : 考えてみると、Rubyに明示的なtype(型)ってないし)
- File.exists? (File.exist?は残る : Rubyは三単元の「s」は付けない方針 : Railsは使いまくってるけど(笑))
- Hash#index, ENV.index
- Symbol#to_int
- Array/Hash #indices, #indexes (警告がでるやつ)
新機能
- もっとたくさん!
- いくつかを紹介
->
- 無名関数 (proc{|a, b| ...}) が 「-> a, b {...}」 と書けるように
- 評判がわるいw
- C#とかは引数が前に来るが、Rubyでは間に来る
- λと->は似ている!?
- -> を左斜めに傾けるとλに見えるwww
.()
- 1.8では無名関数を実行するのに func.call(1,2) のようにcallが必要だった(またはfunc[1,2])
- 1.9では func.(1,2) のように書けるように
複数のsplat
- puts(*a, *b) のように、配列の展開(splat)を複数書けるようになった
- ただし呼び出しのときのみ (メソッドを定義する側では今までどおり)
末尾の必須引数
- 1.8では、引数の数が決まっていないメソッドは def f(a, b, *c) のように定義する
- けど、最後の引数だけ必須にしたい時がある
- 例:a[1,2,3] = obj のように[]=を再定義したいが、[]内の引数がいくつあるか分からない場合
- 1.9では def []=(a, b, *c, value) のようにして、最後の引数に名前を付けられるようになった
Enumeratorが組み込みに
- 1.8では require 'enumerator' が必要だが、1.9ではrequire不要
- あとEnumeratorが連鎖できるようになったり
- ary.map.with_index{|x, i ... #待望の!
- ary.find.with_index{|x, i ...
- ary.conut.with_index{|x, i ...
- って、with_indexだけ…?(いいじゃん、楽しいから)
- 外部イテレータとして使うこともできるように
e1 = [1, 2, 3, 4].each e2 = [10,11,4].each loop{ p e1.next + e2.next } # 11, 13, 7 が出力される
- 要素がなくなったら nextがStopIterationを投げる → 1.9のloopはこれを受け取る → ループ終了!!
- Sather風らしい (ちなみにSatherは「せいざー」と読むらしい)
- 複数の配列を並行にイテレートするのが簡単に!(zipいらず)
- 無限の列だったり、やたらでかい配列だとzipは無駄が大きいよね
- 実際1.9では、zipもこの外部イテレータを使って定義されている
「!」や「!=」が再定義可能に
- def !=(other)
- def !
- 駆け込み変更^^;
- 1.8では、!=は必ず「==の反転」と定義されていたが、!=を再定義したいという要望も一部にある
- 例:Ambition というSQL生成ライブラリ (Rubyのブロックを解析してSQLに直す)
User.select { |m| m.name != 'macgyver' } "SELECT * FROM users WHERE users.`name` <> 'macgyver'"
- 1.8では!=が再定義できないので、ParseTreeという拡張ライブラリを使ってRubyの構文木を解析している
- でも1.9では構文木が取れなくなるかも知れないので、代わりに != の再定義を許すように
正規表現name captureからローカル変数への代入?
- (頭があがらないという)akrさんからの提案
- 1.8では、Emacsの正規表現エンジンを改造したものを使っている
- 中身の動きを理解せずに改造したので、動いてるのが不思議らしい^^;
- 1.9では鬼車を使うようになった
- この関係で、1.9では正規表現のカッコに名前を付けて参照できるように
- これを、matchdata[:foo] ではなく、ローカル変数の foo で取りたい、という話
- 昨日から(パッチつきで)説得されている
- 10日前だけど心が揺れている(笑)
新しい実装
- YARV - Yet Another Ruby VM! (Ruby本体に取り込まれたのでもうYet Anotherではないのだが、まだまだYARVと呼ばれそう)
- VMはとっかかるのは簡単なのだが、「ちゃんとしたRuby」を動かすのは大変 (ささださんがやり遂げたのは奇跡的なことだそう(笑))
- 1.8では構文木のポインタをトラバースしていたところが、バイトコードだとアドレスをインクリメントするだけで良いので速い
- その他いろんな最適化
- Rubyは基本的に最適化に向かない言語 (1+1すら動的にオーバーライドできる)
- でも柔軟性のためには犠牲にできなかったんです
- コア性能「最大50倍」ベンチマーク「2〜10倍」
- とはいえ、実際のアプリケーションはコア機能よりライブラリ関数の実行の方が多いので、本番アプリではそこまで差はないかも
- でもベンチマーク上は「圧倒的じゃないか!」
- 体感性能はそこそこ速い(気分よくプログラムが書ける) さすがに10倍ではないけど
- グラフでの比較
- Linuxのほうが性能が良い
- fibの計算みんな好きですよね (笑)
- 例外、evalがちょっと遅くなった
- VMという構造上、バイトコードへのコンパイルが入る分evalにオーバーヘッドが発生する
- evalを何回もくりかえすのは行儀が悪いプログラム。全てのプログラムをまんべんなく速くすることは難しいので、切り捨てるなら行儀が悪いものを
- メモリダイエット(akrさん他)
- Rubyのデータ表現を最適化して、メモリ消費量が減った (短いStringとか、空のハッシュ({})とか、2^96以下のBignumとか)
- メモリ消費が減るとGC回数が減るので、結果として速度の向上にもつながる
質疑応答
(藤田さん) ブロック変数のスコープが変更になるとのことだが、ブロック外の変数が書き換えられるのが問題なら、「見えるだけ」にするというのはどうか?
- そういう文法を付けてもいいけど…
- スコープ演算子(「2個外側のxを参照する」とか) を一瞬考えたことはあるが、イヤですよね(笑)
(emaさん) nilの定義は?(nilってどういう意味?)
- nilはなにもない、という意味。値がないときに使う
- もとはドイツ語らしい (英語のnullに当たる)
- Rubyの「nil」はLisp由来
(後藤さん) 1.9の開発中にHashの順番を保存するパッチがあったようだが、それは1.9.1でも残るのか
- 残ってます (つまり、1.9.1ではHashは値を追加した順序を覚えている)
- この変更をするとメモリ消費量が増えるので導入をためらっていたのだが、例のメモリ最適化でだいぶメモリ消費量が減ったので導入することにした (トータルとしてはあまり影響がないだろう)
ではパラメータ付き引数は?
- 1.9.1では導入されない。
- その代わり呼び出し側で name: val のような書き方が許されるようになった
- 2.0では受け取り側も変更されて、それで「パラメータ付き引数が導入された」ということになるだろう
(sheepmanさん) 標準添付ライブラリの削除はどうなっている?
- 減らす予定はある (nahiさんがSOAPを削る作業中だとか)
- 削るのは責任者がいるもの (自分が責任を取れないものを勝手に削ると怒られそうなので、それはやめとこうかなあと)
(小波先生) Arrayの範囲外の参照がnilを返すのはイヤなのだが…
- どうなってほしい?(例外を出す?)
- 試しに例外を出すようにしたら意外とたくさん引っかかって、rescueを付けまくらないといけない
- これは自分にとってハッピーでないのでやりたくない(笑)
(サイロスさん) Rubyの拡張ライブラリを作っているのだが、このCのソースは1.9でもそのまま動くか?
- ほとんどはそのまま動くはず
注意することは?
- あったかもしれないけど思い出せないなあ
- きっと大したことないよ(笑)
- 例のメモリ最適化の影響で、配列の中身を直接参照することができなくなった(必ずマクロ経由で参照しないといけない)
(ujihisaさん) Rubyのバグを利用したBinding.of_callerという便利なメソッドがあったのだが、同様の機構を用意するつもりはないか?
- それを入れるとJRubyのCharles Nutterが怒り狂うので… 彼のご機嫌をあまり損ねたくない(笑)
- ささださんによれば、YARV限定ならそんなに難しくないとのこと
- 少なくとも1.9.1には入らないが、そういう拡張ライブラリを書くのは可能かも?
(cuzicさん) .() というメソッドはどのように定義するのか?
- .()はcallを使うので、callというメソッドを定義してください。
(ujihisaさん) 1.8でも.()の代わりに[]で呼び出せるが、それでは駄目なのか?
- Mathematicaじゃないので、角カッコ([])は使いたくない…という人もいる
- どうしても()で呼び出したいらしい(笑)
(hakobeさん) PerlやPythonのように、内部の文字コードをUnicodeに統一しないのはなぜ?
- そのアプローチには3つ問題がある
- (1) 変換することで落ちる情報がいや
- Rubyの多言語化作業を始めたのは7,8年前で、その当時はまだUnicodeで扱えない文字が結構あった
- (2) 変換コスト
- 文字コードの変換には時間がかかるので、なるべく変換したくない
- バックスラッシュ問題:これらを保障しつつ変換するのは大変、なるべく変換したくない
- (3) 特殊な文字コード体系への対応
- 文学の研究者など、Unicodeよりもっと大きいコード体系を利用する分野がある
- Unicodeを介す処理系はこれらの研究に使うことができない。Ruby1.9の方式なら、これをサポートする余地がある
- Unicodeも進化しているので、Unicodeを介さない理由は昔よりは少なくなっているが、せっかく始めた作業をわざわざ捨てるのも勿体ないので
- 各種の文字コードが混在する日本発の発想はこういうものだよというのを世界に知らしめたい、というのもある:-)
(novさん) immediate valueのfreezeはエラーにならないのに、frozen?がfalseを返すのはなぜ?
- freezeがエラーを返すのが正しいかも。immediate valueをfreezeできるようにするのは実装上大変なので、frozen?は必ずfalseを返すことにしている。
ちなみにRubyではFixnumにもインスタンス変数を追加したりできるので、freezeする余地はあるといえる (参考:質問者novさんの日記)。
(yuyaさん) コンパイル済みのバイトコードを入出力する仕組みはないか?
- Cレベルでは存在する。
構文木をごにょごにょしたいことが多くて…
- それについては、開発陣とユーザ間の長きに渡る綱引きが…(笑)
- 構文木をユーザに見せるようにすると、構文木を変更するのが難しくなってしまう(ノードが減った、増えたでギャーと言われてしまう)
- 構文木を変更することはそう多くないが、ときどきある
- 10年くらいの綱引き
rbcompileがstaticじゃなくなるだけでも遊べて楽しいのですが…
- 気持ちは分かります(笑)
- とりあえず1.8ではParseTreeという拡張ライブラリから構文木をいじれる
(yhara) RubyKaigiの時に話が出ていたFiberやcallccはどうなった?
- Fiberやcallccは、 枠組み自体は残っている。実際、外部イテレータはFiberをつかって書かれている(!)
- Rubyレベルから使うにはrequireが必要 (require 'fiber', require 'continuation')
- これで、「callccでRubyがSEGVします」と言われても「それはRubyのバグではなく、拡張ライブラリのバグです」と言えるように(笑)
- 安全性を高めたい場合はrequireしないことが可能
2007-12-18
■ [softs] いちいち「svk なんちゃら」って打つのが面倒な人のためのSVKシェル
svk使ってると「svk st」→「svk di」→「svk ci ファイル名 -m ほげほげ」とかよくやるんですが、 「svk」の部分がDRYじゃないと思ったので超簡易SVKシェルを作ってみましたよ。
(1/24追記:いろいろ機能強化したものをReposhとして公開しています)
使用例。
C:\proj\_3svk>./svksh.rb Welcome to svk shell svk > st M todo.hd ? .svksh.rb.swp ? _webscheme_lib.js ? svksh.rb svk >
コマンド名を入れると、それをそのままsvkに渡して実行します。終了はexitとかquitとか^D/^Zなどお好みで。
何も入力せずにEnterを押すとstatusが表示されます。
なんとファイル名のタブ補完が使えます!!readlineは神。
以下ソース。
require 'readline' SVK = "c:/prog/svk/bin/svk.bat" PROMPT = "svk > " puts "Welcome to svk shell" loop do cmd = Readline.readline(PROMPT, true) cmd = "status" if cmd == "" case cmd when "reload" load __FILE__ when nil, "exit", "quit" puts "" exit else system "#{SVK} #{cmd}" end end
「svk」を「hg」に置換すると Mercurialシェルとしても使えます(笑)
2007-12-19
■ [biwascheme] compile、executeを再帰からループに変更した
もとの3impの実装はScheme in Schemeなので、当然コンパイルも実行も再帰で書かれているのだが、 id:gnarl版がループでやってたので真似してループにしてみた。
テストが全く問題なく動いて感動した。:-)
というか、テストがなかったらこんな大規模な変更できないよね。TDD(BDD)とJSSpecに++。
でもって、iPod Touchで動かなかったテストケースも通るようになった。 デフォルトのiTouchで使える開発言語はJavascriptとSchemeだけ!(あとJSRubyとか?)
2007-12-22
■ [reposh] Reposh : 「svk なんちゃら」って打つのが面倒な人のためのsvkシェルをsvnやhgにも対応させた
この前のSVKシェルを他のVCSにも対応させたものをリリースしました。
使い方
reposh.rb[Enter] で起動。.hgというディレクトリがあればMercurialモードで、.svnというディレクトリがあれば Subversionモードで起動します。どちらもなければSVKモード。
c:\proj\_3svk>reposh Welcome to reposh 0.1.0 (mode: svk) svk > M todo.hd svk > di todo.hd === todo.hd ================================================================== --- todo.hd (revision 602) +++ todo.hd (local) @@ -376,7 +376,40 @@ (<item> <item> <item> . (another template)) #(<item> <item> <item>) ((<item> ...) (<item> <item> ...)) +!! 11.19 Macro transformers (中略) svk > c:\proj\_3svk>
「di」や「ci」などと打つと、それをsvk等のコマンドに渡して実行します。 空Enterで「st」を実行します(設定で変更可)。
終了は exit か quit か ^D(^Z) で。
あと「:ls」のようにコロンから始めると、通常のシェルのコマンドを実行できます。
ダウンロード
インストール
- reposh.rbをパスの通ったところに置く
- Windowsならreposh.batもダウンロードして編集して使うといいかも
- sample.reposh.yamlを ~/.reposh.yaml にコピー(先頭の「.」を忘れずに)
リポジトリとか
CodeReposに置かせてもらいました。ありがとうございます。
TODO
- darcsとかgitとかに対応してくれる人募集
- シェルのコマンド実行はsystemではなくパイプ(Open3)を使った方が良さげ(実行が終わるまで何も表示されないので)。
2007-12-25
2007-12-26
■ [reposh] プロンプトにsystem_nameを入れたいとしたら
結構、いろんな反応があって驚いているReposhですが。
- eval方式 - prompt: "#{@system_name} >"
- erb方式 - prompt: <%=@system_name%> >"
- DSL方式 - prompt: {system_name} >
ということで、適当に「{system_name}と書くとシステム名に置き換わるよ」とかのルールを決めるのが良いかなぁと 思いました。DSLというほどのもんじゃないですけど。
monotoneは、そういえば、名前は聞いたことあるような…。
結構たくさんあるんですね。
■ [biwascheme] 命令群を列にしよう - Scheme VM を書く - ひげぽん OSとか作っちゃうかMona-
あー、単にペアからベクタにするって話じゃなくて、再帰的な構造を止めるって話だったのですね。
確かに、クロージャを普通にシリアライズしたらif文以降が重複しますよねぇ。気付いてなかった(笑)。
例:
(begin (if #t 1 2) (display "hoge"))
これをコンパイルすると以下のようになる。(メモリ上は共有されてるんだけど、こうやって出力すると重複してしまう)
[constant true [test [constant 1 [frame [constant 'hoge' [argument [refer-global 'display' [apply 1]]]] [halt]]] [constant 2 [frame [constant 'hoge' [argument [refer-global 'display' [apply 1]]]] [halt]]]]]
moshではLOCAL_JMPという命令を増やすなどしてこれを解決するとのこと。アセンブラっぽいな。
biwaschemeにはまだ大きなサンプルコードがないので実行速度が問題になってないけど、いずれは考えないといけないのかな。
■ [biwascheme] arguments.callee.callerを使ってエラーメッセージの表示を簡素化してみた
BiwaSchemeではassert_*という関数を使って型チェックをしている。
define_libfunc("abs", 1, 1, function(ar){ var n = ar[0]; assert_number(ar[0]); return (n<0 ? -n : n); });
なので、例えば (abs "hoge") のような式を評価すると
Error: abs: number required, but got "hoge"
というエラーになる。
ここで注目してほしいのがエラーメッセージに関数名が表示されている点で、以前はこれを実現するために
assert_number(ar[0], "abs");
とか関数名をいちいち書いていたのだけど、assert_* の側で arguments.callee.caller を使って 呼び出し元オブジェクトを参照することで*1、よりDRYな書き方ができるようになった。
callerは便利ですね。
ただ、Operaにはarguments.callee.callerがないんで関数名が表示できないんだけど…。デバッグの時はOpera以外を使ってくださいということで。
*1 define_libfuncで、関数オブジェクトのfnameというプロパティに関数名を設定している
■ [prog] コード読み
昨日、るりまのパッケージ版で概要でなく全文が表示されてしまうバグがあって、 某lingrチャネルをデバッグのメモ代わりに使わせてもらった(ごめんなさいw)。
せっかくなので記録として以下に貼り付けておく。
8:06pm (December 25)
template/library-indexがrhtmlらしい <td class="description"><%= compile_rd(lib.synopsis_source) %></td> でsynopsisじゃなく全体が表示されるのが問題 LiveReading
8:09pm (December 25)
libは@db.libraries.sortからとってきてる
8:10pm (December 25)
db = BitClust::Database.new(dbpath)
8:11pm (December 25)
@db.librariesは@db.librarymap().valuesとおなじ てか@librariesがなくて@dirty_librariesしかなくて dirtyの定義について全くコメントがないwww
librarymapはload_extent(LibraryEntry)でよみこむ
8:14pm (December 25)
Entryというクラスが各記事(エントリ)を表すのだと想像 extentってなんだろうなぁ
8:18pm (December 25)
load_extent -> id_extent -> entries(:library.to_s) みたいな流れらしい entriesの中でDir.entriesを読んでファイルパスをごりごり
8:20pm (December 25)
えっと、librarymap().valuesだからハッシュの値の方全部で それぞれはLibraryEntry.new(db, id)みたいなの
8:22pm (December 25)
idはdecodeid(path) メソッドをたどるスタックが深すぎて俺の脳容量はとっくに0よ
8:23pm (December 25)
ujihisa
確かに人間はすぐStackOverflowするよね 図示すれば一気に楽勝
yhara
decodeidはファイル名の-a-b-cをABCになおすらしい
hakobe
人間の脳のスタックはすごいちっさくて6個とかでもかなりすごい人とか聞いた
yhara
つまりidはString つまりlibrariesはLibraryEntry.new(db, "yaml")とかの配列らしいとわかった。 library-index <- screen.rb <- requesthandler.rb <- (以下略 @entries :: [LibraryEntry] (Haskell風)
8:29pm (December 25)
compile_rd(lib.synopsis_source) なわけだが synopsis_sourceの定義がsource().split(/\n\n/, 2).first || '' で この\n\nがじつにあやしいwwwうえwww
windows上でデータベースを作ると改行がCRLFになってて、それをunixに持っていくとうまくsplitできなくて 全文が表示されてしまう、というバグだったらしい。windows上でもopen(fname,"wb")で書き出すことで 解決した模様。
分かったこと:
- 変数の型がさっぱり分からなくて困った。
- これからは型が分かりやすいネーミングをするように注意しよう、と自戒
- メソッドコールのネストがすげー深くて難しかった。
- けっこう大規模なシステムだからしょうがないのかなぁ。
2007-12-29
■ [prog][idea] ambってCでも書けるのかな
継続の効果的な例として取り上げられたりするambだけど、C + setjmp + longjmp でも書けたりするんかな。 どんな感じになるんだろ。
■ [lisp] lispとschemeとか
SANO no Diaryのlisp話がとても面白かった。いくつか引用。
それでも実際に実装依存でなく用意されているプリミティブだけを見ても
schemeと他のlispとのスタイルとは違う。
再帰の多用はlispがschemeになってやっとこ得たスタイルでlispのスタイルではない。
[宗教戦争より引用]
それだけ特別なスタイルで書きかたが違うのだから
Common Lispの中でschemeの書きかたをしては駄目。
[宗教戦争より引用]
コードジェネレーションの効果的な作用を考えると
面倒からの開放という割と守りの部分ではなく、
shiroさんが過去に言っていたような最適化オプションの切り替え方法といった
攻めの方向にも伸ばせるのだと思う。
んでもそこに辿りつくまでのレベル上げはやっぱり守りを主体にしたものになるんだろうとも思う。
[帰りにプロゴルファーとした話より引用]
マクロの話。% loop for i from 0 to 10 collect i | filter (lambda (x) (evenp x))
10
8
6
4
2
0
%
こんなかんじのシェルができたよ。
[やったより引用]
Lispシェル。授業資料らしい。
http://prog.vub.ac.be/~pcostanza/cl-06-07.html
恐らく極めて高いレベルではないけれども、授業としてCommon Lispをやるとしたらこれくらいの
トピックには触れて欲しい(と私が思う)部分には触れている。
[Pascal costanzaのより引用]
おもろそうなのであとで目を通す。■ [lisp] ANSI Common Lisp
最近ANSI Common Lispを読んでいる。 Lispを学ぶというより、CLの仕様のまとめとして。
雑感:
- Schemeに「こんなんあったらいいなぁ」というのを全部突っ込むとCLになりそう
- 関数名がカオス (prin1とprincとか)
- CLOSのメソッドはクラスに属さないが、これはオブジェクト指向のより一般的な実装になっているらしい
- クラスに属さないことで、例えば複数のクラスに特定化したメソッドを書ける
- lisperって「より一般的」な方を好みそうだよね
- あとAOPっぽいやつとかメソッドコンビネーション(わりと謎機能)がある
- loopマクロはCLerにとってもカオスらしい。
読み終わったらもう一エントリ書くかも。
「Common LispとSchemeの違いは、TGMで言うところのワールドとクラシック」というフレーズを思いついたが、 (いろんな意味で)マニアックすぎて聞かせる相手がいない(笑)。
2007-12-31
■ [misc] 2007年を振り返る
もう三が日も終わったじゃんとかいうツッコミは無しで。
リリースしたもの
ソフトウェア(プロジェクトページがあるもののみ):
- (1/4) Ruby/SDLスターターキット 1.2.0b
- (5/2) hd.vim
- (5/2) SDL用BMFontMaker
- (9/27) プログラミング言語「ず」
- (11/19) BiwaScheme
- (12/2) Rubiskell
- (12/22) Reposh
それ以外:
- (1/6) Ruby/SDL Users
- (5/20) るびま記事
- (10/9) RubyScraping
- (12/25) 「るりま」のバージョン1.9.0がリリースされました (リリースだけ^^;)
大きいのはrskitとbiwaschemeかなぁ。Ruby/SDL方面は最近放置してて申し訳ない。
発表
- (2/20) Plagger meets Ruby
- (6/10) RubyKaigi2007 : Ruby/SDLとその周辺
- (6/20) Ruby勉強会@関西-16「30分でわかるcallccの使い方」
- (11/11) 関西オープンソース2007でMechanizeとHpricotの発表してきました
- (12/2) LiveCoding#5に出演しました
よく見たら全部Ruby関係だなぁ。 あと部内では春合宿で「音で遊ぼう」、例会で「最近のプログラミング言語事情」「Ruby on Railsのデモ」「Javascriptでゲームを書こう?」という発表をしたらしい。
イベント参加
- (2/24) LiveCoding第3回に行ってきた
- (3/24) Haskell勉強会 #3
- (4/16) Livecoding#3.1
- (5/10) gauche.night
- (5/25) るびま読書会#2 (ログ)
- (5/19) Haskell勉強会#4
- (6/4) Erlang勉強会#1
- (6/10) RubyKaigi2007\(^o^)/オワタ
- (7/14) Ruby勉強会@関西-17
- (7/3) びわこで開発合宿をしました
- (8/5) 関東にいます (LLSpirit、未踏の報告会見学)
- (10/24) 関西LifeHack研究会 Vol.3
- (12/4) 生駒読書会#1
- (12/15) Ruby/Rails勉強会@関西-21
行きすぎワロタ。日記に書いてないけどRuby勉強会とか、あと3つ以上あるはず。
おまけ:ゲームとか
- (4/13) blocksumランカーへの道
- その2を書こうと思ったけど書いてねーな。
- STG全般
- 引退した。ふーじんろくはnormalを全機体クリアしたところで満足した。Extraは長くてクリアする根気が…。
- Ti
- 認定m1(だっけ)から認定m6に!
- SHIRASEは進展なし(上手くなってる気はする)
- 来年の目標:SHIRASE500突破
- IIDX
- GOLDか。SPはかろうじて九段、DPは十段。引退気味。金と時間があればやるんだけどなぁ(と言ってる時点でやる気が不足していますね)。
- Pop'n music
- BAD補正でLv41が埋まったので引退。Lv42はどうも越したいという気分にならなくて困る
- Drummania
- V3は名作でした(個人的に)。初めてSPが1000を突破。V4は
- 変愚
- 匠盗賊で70Fくらいまで行ったんだが、うっかり王家の墓とか挑戦したら死んだ。アホー
- 来年の目標:勝利する
全体的に音ゲーのプレイ回数が減って、テトリス(Ti)が増えた。あとDTDwww
来年の目標
- 今年よりすごいプログラマになる
- 就職してもブログを更新する
- リア充
□ ささだ [Fiberをsemi coroutine として使う場合はrequire不要です.]
□ yhara [なるほど、Fiber#transfer、Fiber#alive?、Fiber.currentを使う場合だけrequi..]