Archive for the ‘書いた’ Category
* Scala – 暗黙の型変換の喜び
Posted on May 22nd, 2010 by tomoya. Filed under 書いた.
くまくま〜のエントリに毒されて、家族や職場のことを考えずして、1週間ほどScalaで遊んでみることにした。
Rubyから入っていく人が多いようだが、実際にテキストを読みつつコードを書いてみると、かなりとっつきやすい。
短く、簡潔に、必要なところだけを書けば、コードが動くというRubyの美点は、Scalaにも見られる。これはたまらない。
Rubyのようなダイナミックな言語に許された大きな特徴の1つは、すでに(どこか別の場所で)定義されているクラスを”開いて”拡張できること。
たとえば、基本クラスであるStringに対して、
class String def to_aster '*' * self.size end end "password".to_aster #=> "********"
といったメソッド追加が簡単にできる。こんなちょっとした変更だけのために、Stringの派生クラスを作らなくてもいいので、これはおいしい。
ところで、静的な型を持ち、コンパイルされるScalaでも、実はこれと同等のことが実現できる。ただし、方法はもう少しだけ込み入っている。
scala > val hidden = "password".toAster hidden: java.lang.String = ********
ScalaでのStringは、java.lang.Stringであり、すでに定義済みのこのクラスを拡張できることはさすがにできない。代わりに、「暗黙の型変換」という仕組みを利用する。
class AsterString(str:String) { def toAster = str.map(_ => '*') } implicit def str2asterStr(str:String) = new AsterString(str)
と拡張したクラスを作り、implicitキーワードをつけて、変換を行うメソッドを定義しておくと、
"aaaaaa".toAster res58: String = ******
と、新しい魔法を使うことができるようになる。
でも、このためだけに拡張クラスを作るのは面倒くさいかな。はい、そうですよね。
でしたら、無名クラスで代用することも可能になっております。
implicit def toAster(str:String) = new { def toAster = str.map(_=>'*') } toAster: (str: String)java.lang.Object{def toAter: String} "aaaaaa".toAster res00: String = ******
うん、同じく、魔法がきいてる。
そして、魔法を、一気に黒魔法へと変えることができる。
implicit def toAster(d:Double) = d.toString.toAster toAster: (d: Double)String val pi:String = 3.14159d pi: String = *********
Double -> Stringの型変換に、toAsterメソッドが使われるようになった。
このため、toStringを使わずに、Stringで宣言された変数にDoubleの値を代入にしても、コンパイラからtype mismatchとは言われなくなる。
定義したあなたがこの変換をいつまでも記憶の何処かにとどめておくことができたならいいけれど、数ヶ月が経ち、自分で定義した暗黙の型変換メソッドをすっかり忘れてしまっていたなら、きっと何が起こったのかわからなくて唖然とすることになるだろう。
それでもよく考え抜き、「適切に」使用されるならば、これまでコンパイラを黙らせるためだけに書いてきたtoString()やInteger.parseInt()などの紋切り型のメソッドやキャストを書く必要がなくなるばかりか、データベースから取り出した値をScalaオブジェクトの自動的に変換させるORMの機能の実装にすこぶる役に立つものになるだろう。
Scalaさんは、はじめてRubyを触ったときを思い出すほど、わくわくさせてくれる言語である。
こんなDSLを量産したくなりますね。
val phrase = onC(4 / 4).with7th {
val melody = Melody('F * 2', G * 2 , 'C * 4)
melody.withHarmony(1 octaveUp)
}
tempo(120).play(phrase)むふふ、これはおいしい。
* Ruby Facets(Core)でおしゃれRuby!
Posted on December 24th, 2009 by tomoya. Filed under 書いた.
(この記事はRuby Advent Calendar jp: 2009の24日目です. 前日はursmさん、明日はtechnohippyさんです。
普段、技術的なことはwww.nicecabbage.comで書いていますが、今回は企画ものなので日本語で書いてみます。)

メリークリスマスイブ!
こんな聖なる夜に、他人のブログを読んでいる人なんていないと知りながら、投稿します。
この記事では、「Ruby Facets」ライブラリを紹介したいと思います。
Rubyは簡単に基本クラスを拡張できることから、より便利に、あるいは暗黒的に使うためのライブラリがたくさんありますね。”2.days.ago”で一躍評判となったRailsのActiveSupportや、”require :merb / :core_ext / :lib”でおなじみのMerbのextlibが有名です。
Ruby Facetsも、そんなライブラリのひとつ。Rubyをほんのりおしゃれに、ちょっぴりスパイシーにしちゃいます。
どんなことができるのか、つまみ食い形式でご紹介しましょう。
Array
Arrayの積を求める(デカルト積というらしい)
%w(Chapman Cleese Gilliam) ** %w(ham egg spam) #=> [["Chapman", "ham"], ["Chapman", "egg"], ["Chapman", "spam"], ["Cleese", "ham"], ["Cleese", "egg"], ["Cleese", "spam"], ["Gilliam", "ham"], ["Gilliam", "egg"], ["Gilliam", "spam"]]
組み合わせを求める
%w(Chapman Cleese Gilliam).combination(2).to_a #=> [["Chapman", "Cleese"], ["Chapman", "Gilliam"], ["Cleese", "Gilliam"]]
Arrayのおしりに詰め物をする
%w(k t k r).pad(10, "!") #=> ["k", "t", "k", "r", "!", "!", "!", "!", "!", "!"]
セパレータを複数指定できるjoin
["Crosby", "Stils", "Nash", "Young"].conjoin(', ', ' & ') #=> "Crosby, Stils, Nash & Young"
Enumerable
EnumerableをごにょごにょするときのECOモード「Denumerable」
(select, mapなどはフィルタのように働き、中間のオブジェクトを生成しない)
arr = (1..100).to_a class << arr include Denumerable end puts arr.select{|n| n%10==0}.map{|n| n**2}.to_a
グループ化
(1970..1990).group_by{|y| y < 1989 ? 'showa' : 'heisei' } #=> {"heisei"=>[1989, 1990], "showa"=>[1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987, 1988]}
MapしてHashしたいから、Mash!
[:year, :month, :day].mash{|sym| [sym, Date.today.send(sym)]} #=> {:day=>24, :month=>12, :year=>2009}
最頻出要素(modeは統計用語だそうです)
%w(ham egg spam egg spam egg).mode #=> ["egg"]
出現する確率
%w(ham egg spam egg spam egg).probability #=> {"egg"=>0.5, "spam"=>0.333333333333333, "ham"=>0.166666666666667}
出現する個数
%w(ham egg spam egg spam egg).frequency #=> {"egg"=>3, "spam"=>2, "ham"=>1}
エントロピー増大の法則
%w(ham egg ham egg ham egg).entropy #=> 1.0 %w(ham egg ham egg ham spam).entropy #=> 1.45914791702724 %w(ham egg spam! egg ham spam).entropy #=> 1.91829583405449 %w(ham egg spam! spam!! ham spam).entropy #=> 2.25162916738782 %w(ham egg spam! spam!! spam!!! spam).entropy #=> 2.58496250072116 %w(spam!!!! egg spam! spam!! spam!!! spam).entropy #=> 2.58496250072116 %w(spam!!!! spam!!!!! spam! spam!! spam!!! spam).entropy #=> 2.58496250072116
Hash
Hashだって、時にはArrayのように振る舞わせたいものです。
{ :john=>'lennon'} + { :ono=>'yoko'} #=> {:john=>"lennon", :ono=>"yoko"} { :john=>'lennon', :ono=>'yoko'} - { :john=>'lennon'} #=> {:ono=>yoko} { :john=>'lennon', :ono=>'yoko'}.join(' ', ', ') #=> "john lennon, ono yoko"
Object
Object#newのProc化
class Monty def initialize(name); @name = name end end %w(Chapman Cleese Gilliam).map &Monty #=> [#<Monty:0x1254a6c @name="Chapman">, #<Monty:0x1254a30 @name="Cleese">, #<Monty:0x12549f4 @name="Gilliam">]
Kernel
ファイル名、行番号つきデバッグ ‘d’(’g'とか’n'とかありますが、’d'もありますよ、ということで)
d variable
#=> [["Chapman", "Cleese", "Gilliam", "Idle", "Jones", "Palin"]] d.rb:5仲間に入ってる?
me = "tomoya55" monties = ["Chapman", "Cleese", "Gilliam", "Idle", "Jones", "Palin"] me.in? monties #=> false
困ったらユーザに聞くといい
ask "Oops, I encoutered unexpected problems. Do you want to debug me?"とまらないかも。。しれない。
loop do break if maybe end
maybeはtrueになる確率を指定することができる。
# Christmas Eve by Tasturo Yamashita maybe(0.01) ? 'she will come' : 'she will not'
だまれ!
silently do warn "It is a Chiristmas, stupid!" puts "We wish a Merry Chiristmas, We wish a Merry Chiristmas" STDERR.puts "****, explode!" end
alias devnull silentlyなどしてみるのもいいかもしれません
Stackable
文字列をスタックのように扱う
str = "0123456" class << str include Stackable end str.pop #=> 6 str.push("9") #=> "0123459"
Time
時の経過をはかる
Time.elapse{require 'active_support'} #=> 0.695170879364014 Time.elapse{require 'extlib'} #=> 0.185311079025269
1年前
Time.now.ago(1, :years)
1年後(引数のパターンが違うのがいやですね。こちらのCREDITはActiveSupportチームらしいです)
Time.now.advance(:years=>1)
Module
ラッパー(いわゆるデコレーター)
class ChristmasObject wrap(:inspect) do |orig, *args| "<We Wish>" + orig.call + "< a Merry Christmas!!>" end end p ChristmasObject.new #=> <We Wish>#<ChristmasObject:0x12535cc>< a Merry Christmas!!>
Symbol
~ではじまるSymbolは否定語
:friend.not? #=> false :'~friend'.not? #=> true
String
Stringにはわさわさとメソッドが追加されています。使いやすそうなものだけ。
"ero".bracket('(') #=> "(ero)" "ero".bracket('x') #=> "xerox" "santa".chars #=> ["s", "a", "n", "t", "a"] "santa clause".cleave #=> ["santa", "clause"] cleaveは「割く」という意味ぽい %q(/usr/bin/ruby).quote(:b) #=> "`/usr/bin/ruby`" %q(/usr/bin/ruby).quote(:s) #=> "'/usr/bin/ruby'" %q(/usr/bin/ruby).quote(:d) #=> ""/usr/bin/ruby"" %q("/usr/bin/ruby").dequote #=> "/usr/bin/ruby" "ham spam".rewrite([[/am/, "i"],[/\s/,'-']]) #=> "hi-spi"
まだまだありますがこのへんで、ふう〜
あまりここでは紹介していないけれど、メタプログラミングのための拡張もずいぶんなされているので、Rubyで黒魔術やるのが好きな向きには一見の価値ありだと思います。
ところで、Facets(Core)自体は、目新しい機能は何も用意しません。当たり前に使いたいメソッドを簡潔に実装したものの集まりにすぎません。たいていのメソッドの実装は数行程度で、多くても20行程度。
おそらくRubyプログラマなら、似た振る舞いをするメソッドを実装した経験も数多くあるでしょう。
このライブラリを使用する目的は、
- 車輪の再発明なしに
- 簡潔に
- 正しいメソッド名で
書けることができることにつきます。つまり、コードが読みやすくなり、メンテナンスしやすくなるのですね。
正しいメソッド名とというのは、かなり重要です。
われわれ、へっぽこ日本語プログラマは、痛いクラス名、痛いメソッド名を量産してしまいがちなので、こういったライブラリから適切な英単語をパクる術を身につけておきましょう!
どれだけRuby文法的にきれいにコードを書いても、命名規則がアレげじゃあ、台無しです。
かっこいいメソッド名を使って、おしゃれRubyにしちゃいましょう。
また、実装自体が短いので、Ruby Wayなコードの書き方の参考にもなるなるです。
(ただし、テストがRSpecじゃないのが残念!)
RDocを見て、気になるメソッドがあればソースもぜひ覗いてください。
今夜さっそく気になるあの子を落とすのに役立つトリビアが待ってるかもしれません。
最後に、、、個人的な趣味で、コード例にPython的な用語を盗用してしまってごめんなさい。
Pages
Categories
Archives
- June 2010 (1)
- May 2010 (1)
- January 2010 (2)
- December 2009 (2)
- September 2009 (1)
- August 2009 (3)
- July 2009 (3)
- June 2009 (10)
- March 2009 (3)
- February 2009 (9)
- January 2009 (7)




