Ruby でゴーストメソッド
Ruby でゴーストメソッドを書いてみた。
class X def initialize value @value = value end def method_missing name, *args @value.send name, *args end end n = X.new 42 p n + 3 p n - 4 ary = X.new [1, 2, 3] p ary.include? 2
ゴーストメソッドとは『存在しないメソッドを存在するかのように振る舞うメソッド』のことをさす。
Ruby では method_missing
を使用して定義する事ができる。
Ruby のメタプログラミングでは割とポピュラーな技法になる。
RSpec の have_attributes マッチャ
RSpec の have_attributes
マッチャは『オブジェクトの属性(プロパティ)を検証する』マッチャになります。
例えば、
it { expect(10.to_s).to eq("10") } it { expect(10.positive?).to eq(true) }
というようなテストが
# メソッド名と戻り値の Hash を渡す it { expect(10).to have_attributes(to_s: "10", positive?: true) }
という風に書くことが出来ます。
[subject と組み合わせる]
subject
と is_expected
を使用して、subject
対象のプロパティのテストを行ないたい場合、
subject { 10 } it { expect(subject.to_s).to eq("10") } # こういう風に書くことは出来ない # it { is_expected.to_s.to eq("10") }
のように is_expected
を使用することは出来ません。
このような場合に have_attributes
マッチャを利用することが出来ます。
it { is_expected.to have_attributes(to_s: "10") }
べんり。
[参照]
https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/have-attributes-matcher
C++ の range-based for 内で iterator を進めたい
通常 range-based for を使用する場合は iterator ではなくて実体を参照するのでユーザから iterator の操作を行うことは出来ません。 ので、range-based for 内でも間接的に iterator を操作できるようなラッパーを書いてみました。
[コード]
#include <vector> #include <string> #include <iostream> template<typename T> struct iteratorable{ using iterator_type = typename T::iterator; struct iterator_wrapper{ iterator_wrapper(iterator_type itr) : itr(itr) {} iterator_type& operator * (){ return itr; } iterator_type& operator ++ (){ return ++itr; } bool operator != (const iterator_wrapper& o){ return itr < o.itr; } iterator_type itr; }; iterator_wrapper begin(){ return { list.begin() }; } iterator_wrapper end(){ return { list.end() }; } T& list; }; template<typename T> iteratorable<T> as_iterator(T& t){ return { t }; } int main(){ std::vector<std::string> table = { "homu", "mami", "mado", "saya", "an" }; for(auto&& it : as_iterator(table)){ std::cout << "name :" << *it << std::endl; it++; } return 0; }
[出力]
name :homu name :mado name :an
まあそもそも range-based for は『iterator を操作しないで安全なループを行う』って役割もあると思うのでこういうアプローチをしたいなら直接 for 文 + iterator でループしたほうがよい気はする。
C++ の range-based for でループカウンタを使用する
Twitter でみかけたので書いてみた。
一番簡単なのは boost::adaptors:indexed
使うのが楽っぽい。
[コード]
#include <vector> #include <iostream> #include <string> #include <boost/range/adaptor/indexed.hpp> int main(){ std::vector<std::string> table = { "homu", "mami", "mado"}; for(auto&& it : table | boost::adaptors::indexed()){ std::cout << it.index() << ":" << it.value() << std::endl; } return 0; } /* output: 0:homu 1:mami 2:mado */
標準ライブラリに Range ライブラリはよ
clang-check で -std=c++14 を使う
C++14 に対して clang-check を使おうと思ったんですが
$ clang-check -std=c++14 main.cpp -- clang-check: Unknown command line argument '-std=c++14'. Try: 'clang-check -help' clang-check: Did you mean '-h=c++14'?
みたいに単純に -std=c++14
をつけたらエラーになりました。
[-extra-arg
を使用する]
clang のコンパイラオプションを使用したい場合は -extra-arg
を返せばいいみたいです。
$ clang-check -std=c++14 main.cpp --
Ruby の &. と #try の違い
Ruby 2.3 で Safe Navigation Operator(&.
)という新しい演算子が追加されました。
これは obj&.to_i
という風にメソッド呼び出しのように使い、レシーバが nil
の場合に nil
を返します。
10&.to_s # => "10" nil&.to_s # nil
このように nil
チェックすることなく安全にメソッドを呼び出すことが出来ます。
#try
Rails の ActiveSupport にも #try
という似たようなメソッドが定義されています。
これはレシーバがメソッドが呼びだせる場合にそのメソッドを呼び出すというような処理になります。
また、レシーバが nil
の場合は必ず nil
を返します。
10.try(:to_s) # => "10" nil.try(:to_s) # => nil
&.
と #try
の違い
両方とも『レシーバが nil
ではない場合にメソッドを呼び出す』という目的なんですが、具体的な挙動は少し違っています。
&.
が『nilでない場合にメソッドを呼び出す』のに対して、#try
は『メソッドを呼び出すことができるときにメソッドを呼び出す』というような挙動になります。
どういうことかというと
# 10 には #hoge メソッドは定義されていない(呼び出せない)ので nil を返す 10.try(:hoge) # => nil # レシーバは nil ではないので #hoge メソッドを呼びだそうとするが # #hoge メソッドは定義されてないのでエラー 10&.hoge # => Error: undefined method `hoge' for 10:Fixnum (NoMethodError)
というような挙動になります。
これは #try
が respond_to?
を使用して『メソッドを呼びだせるかどうか』をチェックしているためです。
このような違いがあるため &.
を #try
の代替として使用する場合は注意して使用する必要があります。
共通の挙動
#try
も &.
もレシーバが nil
の場合は nil
しか返しません。
nil&.nil? # => nil nil.try(:nil?) # => nil