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しないことが可能
Fiberをsemi coroutine として使う場合はrequire不要です.
なるほど、Fiber#transfer、Fiber#alive?、Fiber.currentを使う場合だけrequireが必要と。