もう6年も記事を書いてないんだなぁ
最近は何でもかんでもやる人になってて、技術としてこれ!ってのがない。
昔はプログラミング言語にこだわってましたが、最近は少し勉強すればどれも一緒という感じで深く踏み入ってないところがある。
深く踏み入れば実際には言語でも得手不得手が違うのと、それぞれ言語の仕組みとしての違いは面白いんだけどそれだとお金貰えないのでつらい。
なんでも拾って自分でやってしまった結果全部浅く広くになって来たので、ここいらで何か集中できるものを探したいし、それができる環境がほしい。
clang3.2(trunk) on MinGW(MSYS) with GCC 4.7.0
clangのコンパイル方法は日本語でもあちこちにあるので省略。
現時点のsvn trunkからcheak outしたソースを使用した場合、
MinGWでGCC4.7.0を使っていると色々と問題が出るのでその修正方法を書きます。
1.Include Pathの解決(既に知っている人が多いと思いますが)
MinGWがc\mingw以外にインストールされている場合Include Pathが解決できずにコンパイルに失敗します。
たとえばMinGWをd\mingwにインストールした場合は下記のように修正します。
clang/lib/Frontend/InitHeaderSearch.cpp
InitHeaderSearch.cpp(line:323)
AddPath("/mingw/include", System, true, false, false); #if defined(_WIN32) AddPath("c:/mingw/include", System, true, false, false); AddPath("d:/mingw/include", System, true, false, false); // 追加 #endif
InitHeaderSearch.cpp(line:401)
// mingw.org C++ include paths AddMinGWCPlusPlusIncludePaths("/mingw/lib/gcc", "mingw32", "4.5.2"); //MSYS #if defined(_WIN32) AddMinGWCPlusPlusIncludePaths("d:/MinGW/lib/gcc", "mingw32", "4.7.0"); // 追加 AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.6.2"); AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.6.1"); AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.5.2"); AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.5.0"); AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.4.0"); AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.3.0"); #endif
2.ABI修正
GCC 4.7 changelogに以下の文があります。
Windows x86 targets are using the __thiscall calling convention for C++ class-member functions.
GCC4.7ではメンバ関数コールを__thiscallにする変更が入ってます。これをclang側にも適応します。
clang/lib/AST/ItaniumCXXABI.cpp(line:42)下記を
CallingConv getDefaultMethodCallConv(bool isVariadic) const { return CC_C; }
このように修正します。
CallingConv getDefaultMethodCallConv(bool isVariadic) const { if (!isVariadic && Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86) return CC_X86ThisCall; else return CC_C; }
3.memoryヘッダの修正
memoryヘッダをインクルードするとコンパイルエラーになります。
これは4.6.2でも下記のようにエラーになるようです。
c:/MinGW/lib/gcc/mingw32/4.6.2/include/c++\ext/concurrence.h:228:2: error: no matching function for call to '_S_destroy' _S_destroy(&_M_mutex); ^~~~~~~~~~ c:/MinGW/lib/gcc/mingw32/4.6.2/include/c++\ext/concurrence.h:273:7: note: candidate template ignored: substitution failure [with _Rm = __gthread_recursive_mutex_t]: non-type template argument evaluates to 4, which cannot be narrowed to type 'bool' _S_destroy(_Rm* __mx) ^ c:/MinGW/lib/gcc/mingw32/4.6.2/include/c++\ext/concurrence.h:282:7: note: candidate template ignored: substitution failure [with _Rm = __gthread_recursive_mutex_t]: no member named 'actual' in '__gthread_recursive_mutex_t' _S_destroy(_Rm* __mx) ^ c:/MinGW/lib/gcc/mingw32/4.6.2/include/c++\ext/concurrence.h:290:7: note: candidate template ignored: substitution failure [with _Rm = __gthread_recursive_mutex_t]: no type named '__type' in '__gnu_cxx::__enable_if' _S_destroy(_Rm* __mx) ^
これはlibstdc++のバグで、gccのヘッダを修正します。
ext/concurrence.h(line:273)
下記コードを
template<typename _Rm> static typename __enable_if<sizeof(&_Rm::sema), void>::__type _S_destroy(_Rm* __mx)
以下のように修正します。
template<typename _Rm> // static typename __enable_if<sizeof(&_Rm::sema), void>::__type static typename __enable_if<(bool)sizeof(&_Rm::sema), void>::__type _S_destroy(_Rm* __mx)
Rubyでflip
関数合成できた。ポイントフリーができた。カリー化もある。
なら次はflipで部分適応の順序を入れかえてやりたい。
まずは下記のProc#flipを見て試す。
http://yuroyoro.hatenablog.com/entry/2012/08/10/232443
div = lambda{|x,y| x.to_f / y } div.(10,2) #=> 5.0 div.flip.(10,2) #=> 0.2
確かにできてる。でも3つ引数をとる場合、第2引数と第3引数を部分適応することはできないらしい。
やってみる。
f = ->a,b,s { s.to_s.gsub(a, b) } p f.(/oo/,"aa","foooo") #=> "faaaa" p f.flip.("aa", /oo/,"foooo") #=> "faaaa" p f.flip.curry.("aa").flip.("foooo").(/oo/) #=> "(?-mix:oo)" わけのわからん物が返ってきた。
確かに一度カリー化されたProcに対してflipしても効果がない。当然だけど…
でもあきらめちゃだめだ。
上記のアプローチはカリー化されてないものに対してのflipしかない
ならカリー化されてるものに対してのflipを書けばいい。
実装は以下
def flip ->f, x, y { f[y][x] }.curry end
このflipを使って第2引数と第3引数を先に部分適応できる形に変換してやるとこうなる
f1 = (flip << flip.(f.curry)) p f1.("aa").("foooo").(/oo/) #元の呼び出しはこう⇒ f.(/oo/,"aa","foooo") #=> "faaaa"
変換の途中経過は以下
# 第1引数と第2引数をひっくり返す p flip.(f.curry).("aa").(/oo/).("foooo") #=> "faaaa" # 元の第1引数と第3引数をひっくり返す # つまりひとつ前の第2引数と第3引数をひっくり返す p flip.(flip.(f.curry).("aa")).("foooo").(/oo/) #=> "faaaa" # 最後に関数合成をして元の第2引数を外に追いやる # ひとつ前で関数の中に埋ってる"aa"を追いやる f1 = (flip << flip.(f.curry)) p f1.("aa").("foooo").(/oo/) #元の呼び出しはこう⇒ f.(/oo/,"aa","foooo") #=> "faaaa"
カリー化されてるProcに対してもflipできた。
Rubyでポイントフリー続きの補足
def g ->x, y { x * y }.curry end def f ->y { y * 4 } end def foo(x, y) f.(g.(x).(y)) end
あんなわけのわからんことしなくてもこれでいけた。
def foo # f.(g.(x).(y)) # (f << g.(x)).(y) # (f.method(:<<) << g).(x).(y) (f.method(:<<) << g) # haskelの(f.).gと大体同じになった end foo.(2).(2) #=> 16
関数合成のほうもλにしたりとかわけのわからないことする必要なかった。
Rubyでポイントフリー続き
さっきとは少し変わってgとf関数はλにしてます。
def g ->x, y { x * y }.curry end def f ->y { y * 4 } end def foo(x, y) f.(g.(x).(y)) end
さて、このfoo(x,y)からxとyを取り除きます。
結果…
def foo ((f.<<).<<.(g)) # f(g(x,y)) end foo.(2).(2) #=> 16
もう何をやっているのか分からないよ・・・。
関数合成のほうもλにしないと自分のRuby力じゃできなかったのでこうしました。
module ComposableFunction # 元コード # def >>(g) # -> *args { g.to_proc.(self.to_proc.(*args)) } # end # # def <<(g) # g.to_proc >> self # end def >> ->g { -> *args { g.to_proc.(self.to_proc.(*args)) } } end def << ->g { g.to_proc.>>.(self) } end end [Proc, Method, Symbol].each do |klass| klass.send(:include, ComposableFunction) end
Rubyでポイントフリー
まずは普通に関数定義するとこんなんですよね?
def g(x) end def f(y) end def foo(x) f(g(x)) end
foo関数をポイントフリーにしてみます。
まずは、関数合成
def foo(x) (method(:f) << method(:g).(x) #もしくは ->x { f (g x) } end
合成したので(method(:f) << method(:g)が1この関数として見れるようになりました。
あとは(x)を消すだけです。
def foo (method(:f) << method(:g) end
fooから引数である(x)が消えた!!?
これだけです。(誰得
まぁ呼び元で
foo.(1) #もしくは foo[1]
みたいになるのでrubyだとちょっと意味分からなくなりそうです。
RubyでMaybeモナド
まずは、データとlookup関数を定義
db = {:alice => {:title => "Ms.", :job => "sales"}, :bob => {:title => "Mr.", :job => "engineer"}} def lookup(key) ->data { data.key?(key) ? Just.return(data[key]) : Nothing } end
lookupのテスト
p return!(db) >= lookup(:bob) #=> <Just:0x882b588 @value={:title=>"Mr.", :job=>"engineer"}> p return!(db) >= lookup(:bob) >= lookup(:job) #=> <Just:0x882b04c @value="engineer"> p return!(db) >= lookup(:chris) >= lookup(:job) #=> <Nothing:0x8814504 @value=nil>
モナド則を満たしているかやってみる。
p "(return x) >>= f == f x" a = Maybe.return(db) >= lookup(:bob) b = lookup(:bob).(db) p a == b #=> true p "m >>= return == m" a = Maybe.return(db) >= method(:Maybe.return) b = Maybe.return(db) p a == b #=> true p "(m >>= f) >>= g == m >>= (\\x -> f x >>= g)" a = (Maybe.return(db) >= lookup(:bob)) >= lookup(:job) b = Maybe.return(db) >= (->x { lookup(:bob).(x) >= lookup(:job) }) p a == b #=> true
ちゃんと満たしてる。
実装は以下
class Monad m where (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b return :: a -> m a fail :: String -> m a m >> k = m >>= \_ -> k fail s = error s
上の定義を見ながらrubyならこんな感じだろうと
class Monad attr_reader :value private_class_method :new def initialize(value = nil) @value = value end def >=(f = Proc.new) case f when Proc f[@value] else f.to_proc[@value] end rescue fail("error") end def >(f = Proc.new) self >= ->_ { case f when Proc f[] else f.to_proc[] end rescue fail("error") } end def self.return(value) new value end def fail(s) Monad.new s end def ==(other) self.class == other.class && @value == other.value end end
モナドの定義ができたので次はMaybeの定義
haskellではこう
instance Monad Maybe where (Just x) >>= k = k x Nothing >>= k = Nothing return = Just fail s = Nothing
上の定義を見ながらrubyなら…(略
class Maybe < Monad def self.return(value) Just.return value end def fail(s) Nothing end def >=(f = Proc.new) case self when Nothing; return Nothing when Just case f when Proc f[@value] else f.to_proc[@value] end rescue fail(nil) end end end
あとはJustとNothingを定義
特異メソッドのself.returnを定義してるのは単に
Just.newと書くと間抜けだなと思いJust.returnと書くようにしてみたかっただけ。
class Just < Maybe def self.return(value) new value end end require 'singleton' class Nothing < Maybe include Singleton def self.return(value) Nothing end end Nothing = Nothing.instance
ちなみに、>=のバインドメソッドはprocかblockを受け取れるようになっているが
a >= &:to_sとかやるとシンタックスエラーになるので渡せない。なんて残念…
ただし、 Procでないものはto_procしてから呼ぶようにしたので
a >= :to_sは可
どうしても&:to_sを渡したいんだけど!!!って場合は、
a.>=(&:to_s)とかすればシンタックスエラーを回避できる。
>=は引数を必ず一つとるが、引数なしのProcを渡したい場合は>で実現できる。
応用として
MaybeのJustかNothingを返さない関数でも
関数合成をしてやればつなげられる。
def something(x) x end def mreturn(value) Maybe.return value end Maybe.return(db) >= method(:Maybe.return) << method(:something) >= method(:mreturn) << method(:something) #=> <Just:0x8b472bc @value={:alice=>{:title=>"Ms.", :job=>"sales"}, :bob=> # {:title=>"Mr.", :job=>"engineer"}}> #ホントは一行
この場合結果はかならずJustなので失敗しないわけだが。
関数合成の実相は以下で昨日も書いた下記からの借り物
http://yuroyoro.hatenablog.com/entry/2012/08/09/203959
module ComposableFunction def >>(g) -> *args { g.to_proc.(self.to_proc.(*args)) } end def <<(g) g.to_proc >> self end end [Proc, Method, Symbol].each do |klass| klass.send(:include, ComposableFunction) end