( ꒪⌓꒪) ゆるよろ日記

( ゚∀゚)o彡°オパーイ!オパーイ! ( ;゚皿゚)ノシΣ フィンギィィーーッ!!!

mrubyをC拡張でRubyに組み込む

やっつけで適当に書いてみた。

yuroyoro/rmruby · GitHub

一体何がしたいのか……。

  irb(main):001:0> require 'rmruby'
  => true
  irb(main):002:0> Rmruby.eval('3.times do |n| puts "hello #{n}!" end')
  hello 0!
  hello 1!
  hello 2!
  3
  => nil

毎回mrbインスタンス作ったりコンテキスト使い捨てしたりしてるし、mrubyからの返り値やコンテキスト内の変数にはruby側からはアクセスできないのだけど、あんまり真面目に作ってないのでカンベン汁( ꒪⌓꒪)

jQueryの$.map/$.eachでfuncitonに渡す引数順がキモすぎる

.......逆じゃねーか。

var f = function(a,b){ console.log("a:" + a + " b:" + b); return a }
  => undefined

var arr = ['foo','bar','baz']
  => undefined

$.each(arr,f)
  a:0 b:foo
  a:1 b:bar
  a:2 b:baz
  => ["foo", "bar", "baz"]

$.map(arr,f)
  a:foo b:0
  a:bar b:1
  a:baz b:2
  => ["foo", "bar", "baz"]


さらに、

$.map(arr,f)
  a:foo b:0
  a:bar b:1
  a:baz b:2
  => ["foo", "bar", "baz"]

$(arr).map(f)
  a:0 b:foo
  a:1 b:bar
  a:2 b:baz
  => [0, 1, 2]


.......逆じゃねーか。

指定したコミットで修正されたファイルをvimで開く

.gitconfigにこういうalias書いた。'git modified HEAD~'で一つ前のコミットで修正されたファイルをvimで開く

[alias]
  # 指定したコミットで変更されたファイルを編集する
  modified = "!f() { git diff $1..$1\\^ --name-only | xargs sh -c 'vim "$@" < /dev/tty' - ;}; f"

dotfiles/.gitconfig at master · yuroyoro/dotfiles · GitHub


もっとうまい方法ないんか?あ

Railsで今いるブランチによってデータベースを切り替える

ブランチングモデルとしてgit-flowを使っていて、メインラインとして、本番適用中のmasterブランチと、次期リリース用のrelease/9999ブランチと、メイン開発ブランチのdevelopがある。


ところが、開発中は頻繁にブランチを移動するし、ブランチによってDBのスキーマが異なるなんてザラにあるし、ブランチ切り替える度にconfig/database.ymlを書き換えるのもタルいので、こんな風に書いた。

development:
  adapter: postgresql
  database: my_app_<%=
    case `git symbolic-ref --short HEAD`
      when 'master'  then 'master'
      when 'develop' then 'develop'
      when /release\/.*/ then 'release'
      when /hotfix\/.*/  then 'master'
      when /feature\/.*/ then 'develop'
      else 'develop'
    end
  %>
 
  username: yuroyoro
  password: yuroyoro
  host: localhost
  encoding: utf8

これで、masterブランチにいるときはmy_app_masterというDBを使い、release/9999ブランチの時はmy_app_releaseってDBを使うようになる。migrationが入り乱れたり、アプリケーションとスキーマが合わないので500るとかなくなって凄惨性あがる.

「関数型Ruby」という病(5) - Object#tryはMaybeモナドの夢を見るか?

前回からかなり期間を空けてしまったが、今回からRubyにおいてnilといかに闘うかについて書く。


本記事は以下略。わかれ( ゚д゚)。

Object#try

Object#tryはActiveSupportで提供されるメソッド、レシーバーがnilじゃない場合に引数のSymbolのメソッドを呼び出してくれる。Symbolの代わりにblockを渡すことも出来る 。

rails/activesupport/lib/active_support/core_ext/object/try.rb at master · rails/rails · GitHub

[4] pry(main)> "foo".try(:upcase)
=> "FOO"

[5] pry(main)> nil.try(:upcase)
=> nil


tryを使うと、nilに対してメソッド呼び出してNoMethodError( ;゚皿゚)ノシΣ フィンギィィーーッ!!!とかいうのがなくなる。ぬるぽ


Ruby1.9に入れたいという提案は残念ながらrejectされてしまったが。

Feature #1122: request for: Object#try - ruby-trunk - Ruby Issue Tracking System

tryのチェーンと関数合成

tryの結果はnilになる可能性があるので、メソッドチェーンする場合は、tryを重ねる必要がある。

[12] pry(main)> nil.try(:pluralize).try(:camelize)
=> nil

[13] pry(main)> 'undefined_method'.try(:pluralize).try(:camelize)
=> "UndefinedMethods"


以前の記事 で、 Procに対して、関数gを引数に取って、自身との合成関数を返すメソッドcomposeを以下のように定義することで、この手のメソッドチェーンはSymbol#to_procと関数合成に変換できることを示した。

module ComposableFunction
  def compose(g)
    lambda{|*args| self.to_proc.call(g.to_proc.call(*args)) }
  end

  alias_method :<<, :compse

  def >>(g)
    g << self
  end
end

[Proc, Method, Symbol].each do |klass|
  klass.send(:include, ComposableFunction)
end


ならば、tryに対してpluralizeとcamelizeを合成した関数を渡せばよいのでは...?

[9] pry(main)> 'undefined_method'.try(&:pluralize >> :camelize)
=> "UndefinedMethods"

[10] pry(main)> nil.try(&:pluralize >> :camelize)
=> nil

[11] pry(main)> 'undefined_method'.try(&:pluralize >> :camelize)
=> "UndefinedMethods"


解決したように見えるが、実はそうではない。次のような例ではこのアプローチはうまくいかない。nilを含むArrayの中からランダムに一つ取り出して、upcaseしてto_sする場合を考える。

[20] pry(main)> arr = [:foo,:bar, nil, :baz]
=> [:foo, :bar, nil, :baz]

[21] pry(main)> arr.try(:sample).try(:upcase).try(:to_s)
=> "BAZ"

[22] pry(main)> arr.try(:sample).try(:upcase)
=> nil


これを、先ほどのアプローチと同様に合成した関数をtryに渡すようにしてみる。

[42] pry(main)> arr.try(&:sample >> :upcase >> :to_s)
=> "FOO"

[43] pry(main)> arr.try(&:sample >> :upcase >> :to_s)
NoMethodError: undefined method `upcase' for nil:NilClass
from /Users/ozaki/dev/Project/sandbox/ruby/functionally/lib/functionally/composable.rb:3:in `call'


sampleで選択される要素がnilだった場合、うまくいかない。上記のアプローチは、実は以下のようなコードと等価だ。

[50] pry(main)> f = lambda{|x| x.sample}
=> #<Proc:0x007ffea4db6b68@(pry):43 (lambda)>

[51] pry(main)> g = lambda{|y| y.upcase }
=> #<Proc:0x007ffea4da2f00@(pry):44 (lambda)>

[52] pry(main)> h = lambda{|z| z.to_s }
=> #<Proc:0x007ffea5bc6708@(pry):76 (lambda)>

[53] pry(main)> arr.try{|x| h.call(g.call(f.call(x))) }
=> "BAR"

[53] pry(main)> arr.try{|x| h.call(g.call(f.call(x))) }
NoMethodError: undefined method `upcase' for nil:NilClass
from (pry):44:in `block in __pry__'

f.call(x)がnilを返したにも関わらず、そのままnilをgに渡している。これを回避するためには、合成されるf, g, hそれぞれの結果に対してtryを適用させる必要がある。


たとえば、fとgをtryでwrapするlambdaを合成させる

[63] pry(main)> arr.try(&:sample >> lambda{|x| x.try(:upcase) } >> lambda{|x| x.try(:to_s)})
=> "FOO"
[64] pry(main)> arr.try(&:sample >> lambda{|x| x.try(:upcase) } >> lambda{|x| x.try(:to_s)})
=> nil


これは、以下のようなコードと等しい。

[65] pry(main)> arr.try{|x| f.call(x).try{|y| g.call(y) }.try{z| h.call(z)} }
=> "BAR"


よくみるとtryが入れ子になっている。ここで、前回の記事でApplicativeっぽいなにかを導入して、入れ子になったflat_mapを変換したことを思い出してほしい。以前は、Procの配列に対して適用すべき引数を配列で渡すと、結果を配列で返す挙動を追加した。


これは、Arrayに対してlift関数を定義し、関数適用を を集合(配列)という文脈上で行うように拡張した、ということだ。


同様の考えた方で、今回は「tryという文脈上で関数合成する」ように、関数合成を拡張する。Procにlift_try関数を定義し、lift_tryされたProcはtryしながら関数合成するようになる。

module LiftTryFunction
  def lift_try
    self.to_proc.tap{|f|
      def f.compose_try(g)
        lambda{|x| x.try(&self)}.compose(g).lift_try
      end

      def f.<<(g)
        self.to_proc.compose_try(g)
      end

      def f.>>(g)
        g.lift_try.compose_try(self)
      end
    }
  end
end

[Proc, Method, Symbol].each do |klass|
  klass.send(:include, LiftTryFunction)
end


上記のlift_try関数は、Proc#compose(:<<)を、tryするlambdaに包んで行う新しいProc(lamdba)を返す。lift_tryされたProc(lambda)に対する関数合成は、つねにtryを挟むような形に拡張される。


先ほどの例の、:sample >> :upcase >> :to_sを、「tryという文脈上」での関数合成に書き換えてみる。

>[225] pry(main)> arr.try(&:sample.lift_try >> :upcase >> :to_s)
=> "BAR"

[226] pry(main)> arr.try(&:sample.lift_try >> :upcase >> :to_s)
=> nil

[227] pry(main)> arr.try(&:sample.lift_try >> :upcase >> :to_s)
=> "BAR"


うまく動作している。

これってMaybeモナ……?

前回の、関数適用を集合の文脈に拡張したように、今回は関数合成をtryという文脈上で行う方法を示した。tryという文脈とはいわば「失敗するかもしれない計算」とも言え、これはMaybeモナ……おっと誰か来たようだ。


Object#tryは、そのそもObjectにおける単位元とかモナ... 自己関手の圏のモノイド対象が満たすべき法則を満足していないのでMaybeモニャーっとは言えないのではあるが、lift_tryによって文脈に持ち上げたあと、関数合成のさいに自動的にtryするような配管的な処理の差し込みを行うような場合に、自己関手の圏のモノイド対象のような考え方を用いることができるということだけ言っておきます。


「あたかも我々がモナドを知らないかの様に言われているのが目立ちますが、理解している事だけはハッキリ言っておきます。」(by @WhitehackerZ1)


次回は、nil.to_i => 0から単位元とモノイドについて書く

ヽ(´・ω・)ノ うるせえエビフライぶつけんぞ

git cherryでブランチ間のコミットのdiffを。色もつけて

git cherryで、ブランチ・ツリー間のコミットのdiffを見ることができる。

git cherry [-v] [<upstream> [<head> [<limit>]]]


デフォルトでは、upsteamに無いコミットは"+"で、逆にheadになくてupstreamにあるコミットは"-"で表示される。それとsha1のみ。味もそっけもない。

f:id:yuroyoro:20121022153203p:plain


"git cherry -v"で、コミットログもでる。

f:id:yuroyoro:20121022153223p:image:w640


これでも充分だけど、もっとdiffっぽく色付けたり、Autherや日付表示したりしたい。のでこんなalias書いた

  # colorized cheery -v
  cch= "!f() { git cherry -v "$@" | awk '{ if($1 == \"+\"){ color = \"green\" } if($1 == \"-\"){ color = \"red\" } cmd = \"git show --date=short --no-notes --pretty=format:\\047%C\" color $1 \" %h %Cgreen%cd %Cblue%cn%x09%Creset%s\\047 --summary \" $2; cmd | getline t; close(cmd); print t }' ;}; f"

( ꒪⌓꒪) Add git cch as colorized git cherry · 859732d · yuroyoro/dotfiles · GitHub


こんな表示になる。

f:id:yuroyoro:20121022153303p:image:w640


awkコワイ。

Rubyのデフォルト引数で再帰

Rubyのデフォルト引数では、他の引数に依存した式を書ける。地味に便利。

[1] pry(main)> def foo(a, b = a * 2)
[1] pry(main)*   puts b
[1] pry(main)* end  
=> nil
[2] pry(main)> foo(3)
6
=> nil

再帰もかける。デフォルト引数で再帰させてフィボナってみる

[3] pry(main)> def fib(n,r = (n <=1 ? n : fib(n-2) + fib(n-1)))
[3] pry(main)*   r
[3] pry(main)* end  
=> nil
[4] pry(main)> 11.times do |n| puts "fib(#{n}) => #{fib(n)}" end
fib(0) => 0
fib(1) => 1
fib(2) => 1
fib(3) => 2
fib(4) => 3
fib(5) => 5
fib(6) => 8
fib(7) => 13
fib(8) => 21
fib(9) => 34
fib(10) => 55

キモイ。