2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012-02-08
Rubyで、OGNLっぽい文字列表現でオブジェクトグラフを辿るアレを書いた
Ruby | |
![]()
Java使いはおなじみのOGNL(Object Notation Graph Language)っぽい記法で、Rubyのオブジェクトグラフを辿るアレを書いてみた。evalすればってツッコミはなしで、な。
厳密にOGNLになってなくて、どっちかっていうとRubyっぽい表現になってるけど、違和感ないはず。
文字列をparseしてオブジェクトグラフを辿る処理を、ループでも再帰でもなくinject(fold)でさらっと書けるようになったところに、関数型言語を勉強した成果が現れている、と思いたい。
RubyでOGNLっぽく、文字列で表現されたプロパティにアクセスするための適当なナニか — Gist
#!/usr/bin/env ruby # RubyでOGNL(Object Notation Graph Language)っぽく、 # 文字列で表現されたプロパティにアクセスするための # 適当なナニか。 # # # Ognl.new('foo_string').apply(foo) # => object notation graph language!! # # Ognl.new('foo_array[1]').apply(foo) # => 2 # # Ognl.new('foo_hash[:a]').apply(foo) # => ooo # # Ognl.new('nested_obj.foo_string').apply(foo) # => ( ゚∀゚)o彡°おっぱい!おっぱい! # # Ognl.new('nested_obj.foo_array[1]').apply(foo) # => 456 # # Ognl.new('nested_obj.foo_hash[:a]').apply(foo) # => oppai!! # # ognl.new('foo_array[3]').apply(foo) # => #<Foo:0x10e0366a0> # # Ognl.new('foo_array[3].foo_string').apply(foo) # => ( ゚∀゚)o彡°おっぱい!おっぱい! # # Ognl.new('foo_array[3].foo_array[1]').apply(foo) # => 456 # # Ognl.new('foo_array[3].foo_hash[:a]').apply(foo) # => oppai!! # # Ognl.new('foo_hash[:foo2]').apply(foo) # => #<Foo:0x10e0366a0> # # Ognl.new('foo_hash[:foo2].foo_string').apply(foo) # => ( ゚∀゚)o彡°おっぱい!おっぱい! # # Ognl.new('foo_hash[:foo2].foo_array[1]').apply(foo) # => 456 # # Ognl.new('foo_hash[:foo2].foo_hash[:a]').apply(foo) # => oppai!! # class Ognl attr_accessor :expression def initialize(expression) @expression = expression end def parse(e) e.split(/(\.|\[[^\]]+\])/).reject{|exp| exp == "." || exp.nil? || exp.empty?} end def apply(target) exps = parse(expression) exps.inject(target){|obj, exp| if exp =~ /\[([^\]]+)\]/ key = $1 key = case key when /^\d+$/ then key.to_i when /^:(.+)/ then $1.to_sym else key.to_s end obj.send("[]", key) if obj.respond_to? "[]" else obj.send(exp) if obj.respond_to? exp end } end end class Foo attr_accessor :foo_array, :foo_hash, :nested_obj, :foo_int, :foo_string def initialize @foo_array = [1, 2, 3] @foo_hash = {:a => "ooo", :b => "pai"} @foo_int = 123 @foo_string = "object notation graph language!!" end end foo = Foo.new puts "Ognl.new('foo_string').apply(foo)" puts "=> #{Ognl.new('foo_string').apply(foo)}" puts "" puts "Ognl.new('foo_array[1]').apply(foo)" puts "=> #{Ognl.new('foo_array[1]').apply(foo)}" puts "" puts "Ognl.new('foo_hash[:a]').apply(foo)" puts "=> #{Ognl.new('foo_hash[:a]').apply(foo)}" puts "" foo2 = Foo.new foo2.foo_int = 999 foo2.foo_string = "( ゚∀゚)o彡°おっぱい!おっぱい!" foo2.foo_hash[:a] = "oppai!!" foo2.foo_array[1] = 456 foo.nested_obj = foo2 foo.foo_array << foo2 foo.foo_hash[:foo2] = foo2 puts "Ognl.new('nested_obj.foo_string').apply(foo)" puts "=> #{Ognl.new('nested_obj.foo_string').apply(foo)}" puts "" puts "Ognl.new('nested_obj.foo_array[1]').apply(foo)" puts "=> #{Ognl.new('nested_obj.foo_array[1]').apply(foo)}" puts "" puts "Ognl.new('nested_obj.foo_hash[:a]').apply(foo)" puts "=> #{Ognl.new('nested_obj.foo_hash[:a]').apply(foo)}" puts "" puts "Ognl.new('foo_array[3]').apply(foo)" puts "=> #{Ognl.new('foo_array[3]').apply(foo)}" puts "" puts "Ognl.new('foo_array[3].foo_string').apply(foo)" puts "=> #{Ognl.new('foo_array[3].foo_string').apply(foo)}" puts "" puts "Ognl.new('foo_array[3].foo_array[1]').apply(foo)" puts "=> #{Ognl.new('foo_array[3].foo_array[1]').apply(foo)}" puts "" puts "Ognl.new('foo_array[3].foo_hash[:a]').apply(foo)" puts "=> #{Ognl.new('foo_array[3].foo_hash[:a]').apply(foo)}" puts "" puts "Ognl.new('foo_hash[:foo2]').apply(foo)" puts "=> #{Ognl.new('foo_hash[:foo2]').apply(foo)}" puts "" puts "Ognl.new('foo_hash[:foo2].foo_string').apply(foo)" puts "=> #{Ognl.new('foo_hash[:foo2].foo_string').apply(foo)}" puts "" puts "Ognl.new('foo_hash[:foo2].foo_array[1]').apply(foo)" puts "=> #{Ognl.new('foo_hash[:foo2].foo_array[1]').apply(foo)}" puts "" puts "Ognl.new('foo_hash[:foo2].foo_hash[:a]').apply(foo)" puts "=> #{Ognl.new('foo_hash[:foo2].foo_hash[:a]').apply(foo)}" puts ""
2012-02-06
関数適用のススメ 〜 オブジェクト指向プログラマへ捧げる関数型言語への導入その2
こんにちわ。今日は、関数適用の話をします。
前回のエントリで、「メソッドチェーンと違いがよくわからない」という指摘をもらったり、なんかスゴイSmalltakerから黒魔術を駆使したトラックバックをもらったりしたので、もう少し前回のエントリの意図するところを掘り下げたい、と思います。
ちなみに、このシリーズは、自分が学習して考えた結果をまとめたもので、タイトルにある「オブジェクト指向プログラマ」というのは俺自信のことです(キリッ
ごめんなさい。
前回まで
前回は、このような入れ子になった関数呼び出しを、
// かっこがいっぱい……。
putStr(unlines(sort(lines(in))))
関数合成を駆使して、このように変換するところまで説明しました。
putStr << unlines << sort << lines <*> in
オブジェクトが無い世界
本題に入る前に、「オブジェクト指向プログラマのこと何ぞいっさい顧みてねーじゃねーか」という指摘もあったので、今回のお話の前提をまず設定します。
今説明している世界は、「オブジェクトが無い世界」です。あるのは、関数だけです。
前回の、「標準入力から何行か読み込んで、ソートして返す」処理ですが、実はScalaでもこのようなメソッドチェーンを利用して書くことが可能です。というか、通常はこう書きます。
println( in.lines.toSeq.sorted.mkString("\n") )
これは、StringオブジェクトやSeqオブジェクトが持つメソッドを利用していますが、今回は「オブジェクトが無い世界」のお話なので、この方法はいったん忘れてください。そもそもの、パラダイムが違うのでゲソ。
記事の後半で、クラス作ったりimplicit conversionしたりしてますが、あくまでコードの意図を実現するための小細工ですので、そこはご容赦を。
しかし、関数型言語はオブジェクト指向なプログラミング言語より非力ではないか、というと、実はそうでもなくて、代わりに関数が第一級の値(ファーストクラス)であり、合成して新しい関数をアドホックに作成することで、柔軟な表現力を獲得している、と思っています。
今回の例では、「関数」というオブジェクトだけが存在して(オブジェクトあるじゃねーか、というツッコミはなしでゲソ)、その「関数」が「合成する」というメソッドや、「値を適用する」というメソッドを持っている、という想定で話をします。
では、本題に入ります。
関数適用じゃないイカ!
前回は、関数適用(ようは、呼び出し)に、<*>という別名をつけました。最初の、かっこが多いバージョンのコードに、この<*>を明示的に書いてみると、イカのような形になります。
// putStr(unlines(sort(lines(in))))
putStr <*> (unlines <*> (sort <*> (lines <*> in)))
全部で、4箇所に<*>が登場しているので、4回の関数適用が行われている事がわかります。逆に、カッコを無くしたバージョンでは、
putStr << unlines << sort << lines <*> in
<*>なので、関数適用は1回です。つまり、putStr/unlines/sort/linesを合成した「大きな関数」に一度だけ関数適用を行っている、と言う形です。
もっと合成しなイカ!
では、putStr/unlines/sort/linesを合成した「大きな関数」を、sortingという変数に束縛(代入でもいいです)しておきましょう。関数はファーストクラスなので、適当な変数に束縛したり、別な関数に渡すことが可能だからです。
// 引数に文字列を受け取って標準出力にソート結果を表示する関数オブジェクト val sorting = putStr << unlines << sort << lines
このsorting関数は、引数に文字列を受け取って標準出力にソート結果を表示します。
次に、ファイル名を引数にとって読み込み、文字列を返すreadFile関数を定義しましょう。
val readFile = (filename:String) => scala.io.Source.fromFile(new java.io.File(filename)).getLines.mkString("\n")
先ほどのsorting関数と合成することで、ファイルから読み込んだ内容をソートする関数を新しく作り出すことができます。
val fileSorting = sorting << readFile fileSorting <*> "test.txt"
オブジェクト指向では、継承や委譲を利用してメソッドを組み合わせることでプログラミングを行いますが、関数型の世界では、この例のように、ある関数に新しい関数を合成したり、関数に関数を渡したりしてプログラミングしていく、という感覚です。
左から読まなイカ?!
通常のプログラマは、このようにカッコがネストしているコードを詠むときに、無意識にカッコの内側から処理が行われる、と読んでいるはずです。
putStr(unlines(sort(lines(in))))
上記のコードだと、「linesにinを渡して、その結果をsortにながして、さらにunlinesに……。」といった具合です。いわば、右から左へコードを詠んでいる、とも言えます。
ですが、我々は文章を左から右に読むことに慣れ親しんでいるので、プログラムだけ右から左に読まなければいけない、というのはちょっとどうかと思うわけです。みなさんは、訓練のたまものによって、違和感なくコードを読めているだけだと言えます。
そこで、関数の合成順を逆にできれば、左から右に処理を行わせるような書き方を実現できます。前回、Fuction1型へのimplicit conversionで、関数を合成する<<メソッドを追加しました。このメソッドは、「引数の関数gの結果を自分(自分自身も関数オブジェクト)に適用する関数」を合成する、ということを行います。この順番を、逆にしたらどうでしょうか?
つまり、「自分自身に値を適用した結果を、引数の関数gに渡す関数」へ合成できるようなメソッドを用意するのです。実は、もう用意してあって、>>メソッドがコレにあたります。
trait ComposableFunction1[-T1, +R] { val f: T1 => R def >>[A](g:R => A):T1 => A = f andThen g def <<[A](g:A => T1):A => R = f compose g def <*>(v:T1) = f apply(v) }
この<<と>>の違いは、イカの例を見てもらえれば分かるかと思います。
scala> val addFoo = (s:String) => s + "Foo " addFoo: String => java.lang.String = <function1> scala> val addBar = (s:String) => s + "Bar " addBar: String => java.lang.String = <function1> scala> (addFoo << addBar) <*> "おっぱい" res19: java.lang.String = "おっぱいBar Foo " scala> (addFoo >> addBar) <*> "おっぱい" res20: java.lang.String = "おっぱいFoo Bar "
では、この>>を使って、左から読めるコードにしてみましょう。こうなります。
(readFile >> lines >> sort >> unlines >> putStr) <*> "test.txt"
「readFileして、linesして、sortして……」というように、左から読んだ順と処理の順番が一致しています。実は、このようなコードの方が、自然に近いのではないでしょうか?
もっと左から読まなイカ?!
さきほど、>>を利用してコードを左から読めるように改修しましたが、ひとつ問題点があります。
(readFile >> lines >> sort >> unlines >> putStr) <*> "test.txt"
readFileに渡す引数"test.txt"が、依然として一番右に置かれていて、readFileと離れた位置にある点です。文章として読むなら、「"test.txt"を、readFileに渡して、lineを……。」というように、引数が一番左に来るのがいいでしょう。
では、どうすればいいのかというと、"test.txt"に、「関数を引数にとって、その関数に自信を値として渡して評価する機能」があればいいわけです。このような機能をもつコンテナを、ここでは仮にApと呼んでおきます。
class Ap[A](v:A){ def apply[B](f: A => B) = f(v) }
このApクラスは、コンストラクタに受け取った値を保持しておいて、applyメソッドで「関数を引数に取って自信を適用する」機能を持ちます。
なお、このApクラスは、一般的に言うApplicativeとは全く違うものなので、誤解無きようお願いします。
では、Apクラスを使ってみましょう。
(new Ap("test.txt")) apply readFile >> lines >> sort >> unlines >> putStr
引数"test.txt"が一番左に来たので、より自然に近い順番になりました。ここで、applyメソッドの別名を、前回と同じく<*>とし、StringからApクラスへのimplict conversionを定義してやると、
class Ap[A](v:A){ def apply[B](f: A => B) = f(v) def <*>[B](f:A => B) = f(v) } implicit def toAp(v:String) = new Ap(v)
このように書けてしまいます。
"test.txt" <*> (readFile >> lines >> sort >> unlines >> putStr)
今回は、この<*>という「関数適用を行う演算子」をApクラスとimplicit convesionによって実現していますが、言語仕様としてこのような演算子を持つプログラミング言語が存在してもおかしくはありません。一般的なプログラミング言語では、()によって関数適用が表現されているだけ、とも言えます。
ようはなにが言いたいかというと
- 小さな関数を合成して大きな関数を作るのが、関数型のやり方
- 合成と適用を分けて考えると、合成した関数自体を保存しておいて、別な関数と合成したり、引数に渡したりできる
- その結果、カッコがネストしたコードを、左から読めるようなコードに書き換えることができる
参考資料とか?
2012-02-03
関数合成のススメ 〜 オブジェクト指向プログラマへ捧げる関数型言語への導入その1
こんにちわ。今日は、関数合成の話をします。
標準入力から何行か読み込んで、ソートして返す以下のようなScalaのコードを例にします。
例なので、あえて冗長なコードを書いてます。
このコードでは、4つの関数が用意されています(unlines/putstr/sort/lines)。
この4つの関数を組み合わせて、読み込んだ文字列を行に分解して、ソートして、
また改行コードをつけて文字列にもどして表示、ということをやっています。
object Main extends App { val in = scala.io.Source.stdin.getLines.mkString("\n") // 改行をつけて結合する関数 val unlines = (s:Seq[String]) => s.mkString("\n") // 文字列を表示する関数 val putStr = (s:String) => println(s) // ソートする関数 val sort = (s:Seq[String]) => s.sorted // 文字列を改行コードで分割する関数 val lines = (s:String) => s.lines.toSeq // かっこがいっぱい……。 putStr( unlines( sort( lines(in)))) }
問題は、最後の行です。
// かっこがいっぱい……。
putStr( unlines( sort( lines(in))))
このカッコを無くすことが、今回のテーマです。
合成しなイカ
(g ∘ f)(x) = g(f(x))
関数gと関数fの合成関数g ∘ fに引数xを渡した結果は、関数gにf(x)の結果を渡したものと等しい、と言うことです。
先ほどのコードの例に戻りましょう。関数gをputStr, fをunlines(...)に置き換えると
(putStr ∘ unlines)(sort(lines(in)) = putStr( unlines( sort( lines(in))))
となります。この調子で、sortやlinesも合成していくと、このように変形できます。
(putStr ∘ unlines ∘ sort ∘ line)(in)
さて、Scalaにおける関数の合成は、Function1トレイトにcomposeというメソッドとして定義されています。これを利用すると、
(putStr compose unlines compose sort compose lines)(in)
このようになりました。カッコが減っていますね。
読みにくくなイカ?!
composeを使って関数を合成できることが分かりましたが、そもそもcomposeって長いのでもうヤ。
そこで、Groovyのように<<で関数を合成できるようにします。Function1に対して、このようなimplicit conversionを
定義します。
trait ComposableFunction1[-T1, +R] { val f: T1 => R def >>[A](g:R => A):T1 => A = f andThen g def <<[A](g:A => T1):A => R = f compose g } implicit def toComposableFunction1[T1, R](func:T1 => R) = new ComposableFunction1[T1, R]{ val f = func }
その結果、先ほどのcomposeは<<で置換できるので、
(putStr << unlines << sort << lines)(in)
おー、だいぶシンプルになったじゃなイカ!
適用しなイカ?!
まだちょっと不満な点があります。それは、(合成関数)(引数)のように、まだカッコが残っているからです。
「全てのカッコを、書かれる前に消し去りたい。全てのコード、過去と未来の全てのカッコを、この手で…!」
そこで、composeに<<という別名を付けたように、関数適用applyにも別名を付けます。ここでは、<*>をapplyの別名として、
先ほど用意したimplicit conversionで変換されるComposableFunction1に追加します。
trait ComposableFunction1[-T1, +R] { val f: T1 => R def >>[A](g:R => A):T1 => A = f andThen g def <<[A](g:A => T1):A => R = f compose g // applyの別名 def <*>(v:T1) = f apply(v) }
さぁ、さっきのコードを変形してみようじゃなイカ?
putStr << unlines << sort << lines <*> in
やったー、カッコが消えたよ!!。
さて、これをもう少し推し進めると、Applicativeスタイルになるんじゃなイカ?と思ったのですが、今回はここまで。
ちなみにHaskellでは
import List main = do cs <- getContents sorting cs sorting :: String -> IO () sorting cs = putStr (unlines (sort (lines cs)))
が
sorting :: String -> IO () sorting cs = putStr $ unlines $ sort $ lines cs
こうなって
sorting :: String -> IO () sorting = putStr . unlines . sort . lines
こうなります。
2012-01-10
ちょっとの手間でRSpecの出力をキレイにするためにnamed_letというのを書いてみた
RSpecの話です。
RSpecは、テストコードがそのまま仕様を記述するドキュメントになる、というのが大きな利点の一つです。
しかし、rspecコマンドに-dオプションを渡して出力されるドキュメントは、必ずしも読める文章になっているとは限りません。
require 'spec_helper' require "cancan/matchers" describe Ability do context 'an user' do let(:user) { Factory.create(:user) } let(:article) { Factory.create(:article) } let(:own_article) { Factory.create(:article, :user => user) } subject { Ability.new(user) } it { should be_able_to(:read, article) } it { should be_able_to(:update, own_article) } end end
Userは、ある記事をreadする権限を持っていて、自分が書いた記事はupdateすることができる、という記述です。
'should be_able_to(:read, article)'や' should be_able_to(:update, own_article) 'は、そのままの文章になってます。
では、これをrspec -dで実行してみます。
アルェ?
'should be able to :read #<Article id:38 ...'とか全然文章っぽくないじゃないですかー!?ヤダーっ!!
これは、RSpecのほぼ全てのMatcherの実装で、引数のObjectのinspectメソッドを利用して出力を構築するようになっているからです。えー……。
そこで、letでなにかを定義するときに使った名前をそのまま出力するようにした、named_letというのを作ってみました。
これは、rspecのlet(:name)で定義したオブジェクトのto_sとinspectの値を、:nameに変更することで、少しの手間でキレイな出力を得られるようにしたものです。
rspecのlet(:name)で定義したオブジェクトのto_sとinspectの値を、:nameに変更する — Gist
このように使います。
describe Ability do context 'an user' do let(:user) { Factory.create(:user) } named_let(:article) { Factory.create(:article) } named_let(:own_article, "own article") { Factory.create(:article, :user => user) } subject { Ability.new(user) } it { should be_able_to(:read, article) } it { should be_able_to(:update, own_article) } end end
さっきのspecで、letで定義されていたarticleを、named_letを利用するように変更しました。named_letでは、第一引数のSymbolを表示の際に利用します。第二引数に別名を渡すことも出来ます。
named_let(:article) { Factory.create(:article) }
named_let(:own_article, "own article") { Factory.create(:article, :user => user) }
あらためて実行してみます。
ちゃんと意図した文章になって出力されていますね。やったー。
2012-01-05
MySQL Workbenchで日本語を表示させる方法
~/Library/Application Support/MySQL/Workbench/wb_options.xml 内でFontを指定してる箇所をHelveticaからIPAフォントなどに変更すればよい。
<value type="string" key="workbench.model.Layer:Color">#F0F1FE</value>
<value type="string" key="workbench.model.NoteFigure:Color">#FEFDED</value>
<value type="string" key="workbench.model.NoteFigure:TextFont">M+1P+IPAG 11</value>
<value type="string" key="workbench.model.ObjectFigure:ColorList">#98BFDA
しかし、なんでGUIで設定出来ないの?





ひとつ質問なのですが、
> 合成して新しい関数をアドホックに作成する
> 小さな関数を合成して大きな関数を作るのが、関数型のやり方
この辺りは、オブジェクト指向のDecoratorパターンに似ているなあと思いました。
GoFに載っているGUIツールキットの例でも、コンストラクタでGUIを構築してDraw()でGUIを走らせますが、これも合成と適用の関係に似ているような気がします。
どうなんでしょうか。あんまり質問になっていなくてすみません。