Hatena::ブログ(Diary)

ゆろよろ日記 RSSフィード Twitter

2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
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っぽい文字列表現でオブジェクトグラフを辿るアレを書いた

| 15:03 |  Rubyで、OGNLっぽい文字列表現でオブジェクトグラフを辿るアレを書いたを含むブックマーク  Rubyで、OGNLっぽい文字列表現でオブジェクトグラフを辿るアレを書いたのブックマークコメント

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

| 16:32 |  関数適用のススメ 〜 オブジェクト指向プログラマへ捧げる関数型言語への導入その2を含むブックマーク  関数適用のススメ 〜 オブジェクト指向プログラマへ捧げる関数型言語への導入その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によって実現していますが、言語仕様としてこのような演算子を持つプログラミング言語が存在してもおかしくはありません。一般的なプログラミング言語では、()によって関数適用が表現されているだけ、とも言えます。

ようはなにが言いたいかというと

  • 小さな関数を合成して大きな関数を作るのが、関数型のやり方
  • 合成と適用を分けて考えると、合成した関数自体を保存しておいて、別な関数と合成したり、引数に渡したりできる
  • その結果、カッコがネストしたコードを、左から読めるようなコードに書き換えることができる

次回は、再帰ラムダ計算の話をしようと思うゲソ。

参考資料とか?

mizonmizon 2012/02/08 12:46 とても勉強になります。

ひとつ質問なのですが、

> 合成して新しい関数をアドホックに作成する
> 小さな関数を合成して大きな関数を作るのが、関数型のやり方

この辺りは、オブジェクト指向のDecoratorパターンに似ているなあと思いました。
GoFに載っているGUIツールキットの例でも、コンストラクタでGUIを構築してDraw()でGUIを走らせますが、これも合成と適用の関係に似ているような気がします。

どうなんでしょうか。あんまり質問になっていなくてすみません。

2012-02-03

関数合成のススメ 〜 オブジェクト指向プログラマへ捧げる関数型言語への導入その1

| 14:57 |  関数合成のススメ 〜 オブジェクト指向プログラマへ捧げる関数型言語への導入その1を含むブックマーク  関数合成のススメ 〜 オブジェクト指向プログラマへ捧げる関数型言語への導入その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というのを書いてみた

| 17:46 |  ちょっとの手間でRSpecの出力をキレイにするためにnamed_letというのを書いてみたを含むブックマーク  ちょっとの手間でRSpecの出力をキレイにするためにnamed_letというのを書いてみたのブックマークコメント

RSpecの話です。


RSpecは、テストコードがそのまま仕様を記述するドキュメントになる、というのが大きな利点の一つです。

しかし、rspecコマンドに-dオプションを渡して出力されるドキュメントは、必ずしも読める文章になっているとは限りません。


例として、以下のようなCanCanspecを見てみます。

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で実行してみます。


f:id:yuroyoro:20120110174348p:image:w640


アルェ?

'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) }

あらためて実行してみます。


f:id:yuroyoro:20120110174349p:image:w640


ちゃんと意図した文章になって出力されていますね。やったー。


2012-01-05

MySQL Workbenchで日本語を表示させる方法

18:12 |  MySQL Workbenchで日本語を表示させる方法を含むブックマーク  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で設定出来ないの?