* 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的な用語を盗用してしまってごめんなさい。



3 Responses to “Ruby Facets(Core)でおしゃれRuby!”

  1. Tweets that mention [書いた] Ruby Facets(Core)でおしゃれRuby! | 沙石院ブログ -- Topsy.com Says:

    [...] This post was mentioned on Twitter by Tomoya Hirano, svc09. svc09 said: Ruby Facets(Core)でおしゃれRuby!: (この記事はRuby Advent Calendar jp: 2009の24日目です. 前日はursmさん、明日はtechnohippyさんです。 普段、技術的なこ.. http://bit.ly/83NVCa [...]

  2. Anonymous Says:

    class << obj; include M; end ≡ obj.extend(M)

Trackback URI | Comments RSS

Leave a Reply