Ruby 25 周年記念パーティでデバッグ出力を行う gem つくってきた
今日 2018年2月24日は Ruby が誕生してちょうど 25年目!
ということで記念パーティに行ってきました。
まあなんとなく行ってきたんですが、別に偉い人の話を聞いてもしょうがないので会場ではひたすら Ruby のコードを書いていました。人の話を聞いているよりもコードを書いている方が有意義なのじゃ
そして、ついこの間 2.5 がリリースされたと思ったらもう Ruby 2.6.0-preview1 が出ていますね…早い…。
ちなみにこのバージョンではわたしが投げたパッチも取り込まれているようでよかったよかった。
そんなわけで Ruby 25周年記念でつくったデバッグに便利そうな gem を公開しました。
インストール
$ gem install binding-debug
binding-debug とは
例えばデバッグ出力をする際に 変数名 : 値
みたいな形で出力されてほしい事があると思います。
しかし、このような形式で出力したい場合、毎回自分で変数名を記述する必要があります。
hoge = 42 # 自前で毎回こんな感じの出力フォーマットを定義する必要がある puts "hoge : #{hoge}"
これをどうにかしたかったのでいい感じにデバッグ出力できる gem をつくりました。
使い方
デバッグ出力を行いたいコンテキスト情報(変数名とか)を取得するために Kernel.#binding
を利用します。
以下のようにして Binding#puts
に対して評価したい変数(式)を渡すことで 変数名(式) : 評価した値
みたいな感じで出力されます。
require "binding/debug" # 要 refinements using BindingDebug hoge = 42 binding.puts "hoge" # output: "hoge : 42" # Binding#p だと Kernel.#p で出力 binding.p "hoge" # => "hoge : 42"
また、複数の式を評価したい場合は、%{}
記法を利用することが出来ます。
# %{} を利用すれば以下のように書くことも def plus a, b a + b end foo = "homu" binding.puts %{ foo.to_s.upcase plus 1, 2 (0..20).to_a foo.class.name } # output: # foo.to_s.upcase : HOMU # plus 1, 2 : 3 # (0..20).to_a : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] # foo.class.name : String
思ったよりもいい感じに書けますね。
経緯
元々は instance_eval
+ method_missing
で以下のように『ブロック内で呼び出されたメソッドに対してフォーマット出力する』ことを考えていました。
hoge = 42 # method_missing でいい感じに hoge の呼び出しに対してフックしていい感じに出力する binding.puts { hoge }
しかし、上記の場合、hoge
という呼び出しが『メソッド呼び出し』ではなくて『キャプチャした変数』を優先して呼び出してしまうため意図した呼び出しが出来ませんでした。
hoge = 42 # 明示的にレシーバを付けて呼べばメソッド呼び出しとして評価されるので method_missing で呼び出せるが… binding.puts { self.hoge }
会場でつくっている途中でこの問題に気づいて「うーん、どうしようかな…」と悩んでいたら偶然にも会場に来ていた強い Rubyist の人に「%{}
記法を使うのはどうだろうか、ブロックっぽいし」と助言を貰ってシンプルに文字列を渡して評価する実装にしました。
一応、
homu = "homu" binding.puts { self.homu.upcase }
みたいにチェーン呼び出しにも対応させる実装は書いたんですが、文字列を評価するようにするほうが実装もずっとシンプルになりとてもよい。
ぶっちゃけ勢いで雑に公開したので実際に使ってみて問題がいろいろと治す予定ではあります。
っていうか、探せば類似 gem はありそうだけどそんなの気にしない!
所感
と、言うことで 25周年記念パーティ会場でせっせとつくった gem でしたー。
わたしは Ruby 歴 3年ぐらいのにわかですが、みなさん Ruby 愛が強くてすごいですね。
今後は mruby なんかもいい感じに盛り上がってほしいと思っています(その前に cruby と mruby で仕様がぶれているのをどうにか…
わたしは Rails が全く出来ませんし Ruby = Rails みたいな世の中は本当にクソだなと心の底から思っていますが、Ruby 自体は書いていてとても楽しい言語なので、これからも役に立たない面白いコードやライブラリなんかを書いて行きたいです!!!
Ruby は書いていて楽しいのでみんな Ruby 沼んいハマろう!!
ちなみに関係ありませんが今年は C++ 35周年の年でもあります!
みんな C++ ちゃんも祝ってあげよう!!!
そんなわけで Ruby パパの Matz、Ruby コミッタ、関係者のみなさん、楽しい Ruby をありがとうございます!
C++ の std::tuple で動的にループする
たまに C++ を書かないと忘れるので…。
[コード]
#include <tuple> #include <iostream> #include <string> template<typename F, typename T, std::size_t I> void apply_impl(F f, T const& t){ f(std::get<I>(t)); } template<typename F, typename T, std::size_t ...Indices> void apply_impl(F f, T const& t, std::size_t n, std::index_sequence<Indices...>){ ((void (*[sizeof...(Indices)])(F, T const&)){apply_impl<F, T, Indices>...})[n](f, t); } template<typename F, typename ...Args> void apply(F f, std::tuple<Args...> const& t, std::size_t n){ apply_impl(f, t, n, std::index_sequence_for<Args...>()); } template<typename F, typename ...Args> void for_each(std::tuple<Args...> const& t, F f){ for(std::size_t i = 0 ; i < sizeof...(Args) ; ++i){ apply(f, t, i); } } int main(){ using namespace std::literals; auto data = std::tuple(1, 3.14, "homu"s); auto print = [](auto it){ std::cout << it << std::endl; }; apply(print, data, 0); apply(print, data, 1); apply(print, data, 2); for_each(data, [](auto it){ std::cout << it + it << std::endl; }); return 0; }
[出力]
1 3.14 homu 2 6.28 homuhomu
参考にしたコードはこのあたり。
動的に要素にはアクセスしているけど、値を返すことは出来ないので多相ラムダを渡して、それに値を渡すようにしています。
まー、実際 for_each
だけなら std::apply
とか使ったほうがよさそう。
#include <tuple> #include <iostream> #include <string> template<typename F, typename ...Args> void for_each(std::tuple<Args...> const& t, F f){ std::apply([&](auto... args) constexpr{ (f(args), ...); }, t); } int main(){ using namespace std::literals; auto data = std::tuple(1, 3.14, "homu"s); for_each(data, [](auto it){ std::cout << it + it << std::endl; }); return 0; } /* 2 6.28 homuhomu */
第8回 学生エンジニア限定LT大会!!! でエモい話をしてきた
前回に懲りずに学生に混ざってLTしてきました。
9年間ブログを書き続けて得たもの
https://osyo-manga.github.io/slide-gakusei_LT-08-9years-blog/#/
内容は『アウトプットすることで自分にもプラスになるんやで』みたいな事を話してきました。
まあ、アウトプットすることは大事なんですけど、自分にも役に立つのでいいですよねー。
皆さん、どんどんアウトプットしていきましょう。
本日のハイライト
懇親会で LT をしているあいだにピザと寿司が全部食べれられてしまっていた。
ゆるさん。
今度参加したときは2倍食べてやる!!!
表参道.rb #31 で LINE でお天気bot をつくった話をしてきた
LT してきました。
今回もご飯がとても美味しすぎた…。
LINE でお天気bot をつくった話
https://osyo-manga.github.io/slide-omotesandorb-31-otenki-bot/index.html#/
と、いうことで今回は珍しく生産性のある話をしてきました。
年末に LINE でお天気 bot を作ったので、それで利用した API や Ruboty の紹介なんかをしてきました。
Ruboty のプラグインとして LINE のアダプタとお天気 bot のハンドラをつくったので API キーさえ用意してもらえればすぐに使える(はず…。
何か問題があれば Issues まで教えてください。
そんな感じで今回で5回目のLTをしてきましたー。
運営の方々、発表を聞いてくださった皆様、ありがとうございました。
今回はちょっと参加人数が少なくていつもとは違う趣向でしたが、これぐらいの人数のほうがみんなで色々と意見をいいながら話せるのでこれはこれでよかったかな?と思いました。
みんなでワイワイいいながらコードを見るのは楽しいですね!
そろそろ話すのにも慣れていきたい。
Ruby で (a ==1 && a== 2 && a==3) の結果を真にする
と、いうのが JavaScript 界隈で流行っているので Ruby でもやってみた
==
メソッドを定義する
多分一番簡単なやり方。 比較演算子そのものの結果を変えます。
a = Object.new def a.== other true end p a == 1 && a == 2 && a == 3 # => true
a
メソッドを定義する
Ruby ではメソッド呼び出しの際に ()
を省略する事が出来るので a()
というメソッド呼び出しを a
とだけで呼び出すことが出来ます。
def a @a = (@a || 0) + 1 end p a == 1 && a == 2 && a == 3 # => true
こっちは Ruby らしいですね。
おまけ:==
ではなくて ===
を使う
主題からは外れますが Ruby では ===
演算子は特別な呼び出しなので ==
の変わりに ===
を使うといろいろな手段が使えます。
Proc#===
を使う
Proc#===
は Proc#call
と同じなのでブロックの中身をそのまま返します。
a = proc { true } # a.call 1 と同じ p a === 1 && a === 2 && a === 3 # => true
Method#===
を使う
Method#===
も Proc#===
と同様に #call
を呼び出します。
a = [1, 2, 3].method(:include?) # [1, 2, 3].include? 1 を呼び出しているのと同じ p a === 1 && a === 2 && a === 3 # => true
Range#===
を使う
Range#===
は Range#include?
を呼び出します。
a = (1..3) # (1..3).include? 1 を呼び出しているのと同じ p a === 1 && a === 2 && a === 3 # => true
Set#===
を使う
Range
と同様に Set#===
も Set#include?
を呼び出します。
require "set" a = Set[1, 2, 3] # Set[1, 2, 3].include? 1 を呼び出しているのと同じ p a === 1 && a === 2 && a === 3 # => true
Ruby たのしい