Ruby で任意のクラスが特異クラスかどうか判定する
Ruby で任意のクラスが特異クラスか判定しなーと思ってあれこれ考えていたんですが、そもそも Module#singleton_class?
がすでに存在していました。
class X end p X.singleton_class? # => false p X.singleton_class.singleton_class? # => true p X.new.singleton_class.singleton_class? # => true p Class.new.singleton_class? # => false p Class.new.new.singleton_class.singleton_class? # => true
ドキュメントはちゃんと探しましょう。
Boost 1.65.0 がリリース
Boost 1.65.0 がリリースされました。
Boost 1.65.0 では以下の2つのライブラリが新しく追加されました。
Boost.PolyCollection は抽象クラスに特化したコンテナで、Boost.TypeErasure などとも連携して使うことが出来ます。
久しく Boost を使ってないので機会があれば触ってみたいところ。
C++ で std::getline を使った split
前回、std::regex
を使った split を実装しましたが、今回は std::getline
を使った実装です。
#include <iostream> #include <sstream> #include <vector> #include <string> int main(){ auto split = [](auto src, auto del){ std::istringstream stream{src}; std::string output; std::vector<std::string> result; while(std::getline(stream, output, del)){ result.push_back(output); } return result; }; std::string src{"homu,mami,mado"}; for(auto&& str : split(src, ',')){ std::cout << str << std::endl; } return 0; } /* output: homu mami mado */
上のコードは多相ラムダとか使ってるので C++14 以降でしか動作しませんが、実装自体は C++03 でも動くのでレガシーな環境だと std::getline
を使うのが手っ取り早そう。
C++ で std::regex を使った split
C++ で split が話題になっていたのでやってみた。
#include <regex> #include <iostream> #include <string> #include <vector> int main(){ auto split = [](std::string src, auto pat){ std::vector<std::string> result{}; std::regex reg{pat}; std::copy( std::sregex_token_iterator{src.begin(), src.end(), reg, -1}, std::sregex_token_iterator{}, std::back_inserter(result) ); return result; }; std::string src{"homu,mami,mado"}; for(auto&& str : split(src, ",")){ std::cout << str << std::endl; } return 0; } /* output: homu mami mado */
C++ で split のような関数を標準ライブラリにいれる場合、戻り値型をどうするのかがむずかしいよね…。
C++17 で追加された std::conjunction/std::disjunction メタ関数
知らなかったので覚書。
C++17 で std::conjunction
と std::disjunction
という2つのメタ関数が追加されました。
この2つのメタ関数はいずれも可変長テンプレート引数を受け取ります。
具体的にどのようなメタ関数なのかは以下のコードを見てください。
#include <type_traits> int main(){ using true_ = std::true_type; using false_ = std::false_type; // 引数型の ::value が全て true なら true static_assert( std::conjunction<true_, true_, true_>{}, ""); static_assert(!std::conjunction<true_, false_, true_>{}, ""); // 引数型の ::value のいずれかが true なら true static_assert( std::disjunction<true_, false_, false_>{}, ""); static_assert(!std::disjunction<false_, false_, false_>{}, ""); return 0; }
std::conjunction
は全ての型で &&
し、std::disjunction
は全ての型で ||
するって感じですね。
どういう時に役立つの
例えば、『任意の複数の型が同じ型かどうか』を判定するメタ関数を定義するとします。
そういう場合は、std::conjunction
を使えば簡単に処理する事が出来ます。
// 再帰みたいなことをしないでも実装できた!! template<typename T, typename... Args> struct all_of : std::conjunction<std::is_same<T, Args>...>{}; static_assert( all_of<int, int, int>{}, ""); static_assert(!all_of<int, int, float>{}, ""); static_assert(!all_of<int, int, int&>{}, "");
これは便利だなあ…。
ちなみに C++17 では fold expression を使って次のように実装することも出来たりします。
// std::conjunction すらいらない!! template<typename T, typename... Args> struct all_of : std::bool_constant<(... && std::is_same_v<T, Args>)>{}; static_assert( all_of<int, int, int>{}, ""); static_assert(!all_of<int, int, float>{}, ""); static_assert(!all_of<int, int, int&>{}, "");
C++14 で追加されればよかったのに…。
C++ のラムダ式を型推論しながら関数ポインタ型で受け取る
ほぼ使うことはないだろうけど、かなりの知見だったので覚書。
ラムダ式を関数ポインタ型へ変換する
これは割と一般的な知識だと思うんですが、『キャプチャしていない』ラムダ式は関数ポインタ型へとキャストする事が出来ます。
using func_t = int(*)(int); // 関数ポインタ型として受け取る事が出来る func_t twice = [](int n){ return n + n; };
+
単項演算子で明示的に関数ポインタ型に変換する
さて、これを踏まえて次のように『関数ポインタ型を型推論する』ような関数にラムダ式を渡したいとします。
// テンプレートで戻り値型や引数型を推論するよ template<typename R, typename A> void func(R(*f)(A)){ } // error: no matching function for call to 'func' func( [](int n){ return n + n; } );
しかし、この場合は型変換されずにエラーになってしまいます。
こういう場合に +
単項演算子を使うことで明示的に関数ポインタ型に変換することが出来ます。
// テンプレートで戻り値型や引数型を推論するよ template<typename R, typename A> void func(R(*f)(A)){ } // ok func( +[](int n){ return n + n; } );
関数ポインタ型にキャスト出来ることは知っていたけど +
単項演算子で明示的に関数ポインタ型に変換できることは知らなかった…。
まあ実際に使う機会があるかどうかは別ですが…。
参照
ここがすごいよ Hsakell の $ 演算子
最近ちょろっと Haskell を書いてて 『$ 演算子すげー』ってなったので勢いでまとめてみる。
Haskell に明るくないのでゆるふわな感じで書いていくよー。
(ちなみに $
自体の具体的な解説はしない。
関数適用と演算子の優先順位
他言語をやってから Haskell を学び始めると次のようなコードを書いてしまうことが多いと思う。
main = do -- 1 + 2 の結果を出力したいなっ print 1 + 2
しかし、上記のコードでは次のようなエラーになってしまう。
test.hs:3:16: No instance for (Num (IO ())) arising from a use of ‘+’ In the expression: print 1 + 2 In an equation for ‘main’: main = print 1 + 2
これは Haskell では『演算子よりも関数適用の優先順位が高い』ためである。
どういうことかというと括弧がない場合、 Haskell では以下のように解釈してしまうからである。
(print 1) + 2
つまり
1 + 2
の結果をprint
に渡す
のではなくて
print 1
の結果を+ 2
する
という処理になる。
こういう場合は括弧を使用して処理の優先順位を明示化して回避する必要がある。
main = do -- 先に 1 + 2 を処理するよ! print (1 + 2)
括弧の代わりに $ を使う
さて、括弧を使うことで回避することは出来た。
しかし、『いちいち括弧を書きたくない!!』というのが人の性でである。
そういう場合によく利用されているのが $
演算子である。
この $
演算子を使うことで次のようにして括弧を記述することなく適切に処理を行う事が出来る。
main = do -- 1 + 2 が先に処理されるよ! print $ 1 + 2
やったね!!これで括弧を書かなくてよくなったよ!!
$
演算子の仕組みを解説
と、いうだけだと物足りないのでもう少し $
演算子の仕組みについて解説。
先ほど『関数適用よ演算子よりも関数適用の優先順位が高い』とかいた。
なので
f 20 + f 10
みたいな処理は
(f 20) + (f 10)
のように『f
関数を先に適用してから』 +
演算子が呼び出されることになる。
これを別の視点から考えてみると演算子の『左辺』と『右辺』を先に処理するという風に解釈する事が出来る。
同様に +
演算子を $` 演算子に置き換えて考えてみると
f $ g 10
は
(f) $ (g 10)
のように処理される。
つまり $
演算子を使うことで『左辺と右辺を別々に処理する』という事ができるようになるのである。
これにより $
演算子で括弧を使わなくても処理の優先順位を制御することが出来ることになる。
ちなみに $
演算子は演算子の中で優先順位が低いので
print $ 1 + 2
は
(print $ (1 + 2))
となる。
まとめ
$
演算子は処理の優先順位を制御できる- これにより括弧の記述を省略する事が出来る
- Haskell において演算子よりも関数適用のほうが優先順位が高い
f a + g h
は(f a) + (g h)
となる
- 式が複雑になってくるなら素直に括弧を書こう
あと $
が具体的にどういう関数なのかはあえてここではかかないけど、気になったら調べてみてね!!