3,4月で気になった Ruby のパッチ、提案

書こう書こうと思っていて結局書いてなかったシリーズ。
とりあえず、現状まとめていたやつだけ放出。

Feature #14579: Hash value omission - Ruby trunk - Ruby Issue Tracking System

JavaScript みたいに Hash を定義する時に『値を省略してキーだけ渡して Hash を定義する』みたいな記法の提案。
これ、JavaScript を書いていた時に結構多用していたので Ruby にもめっちゃほしかったんですが、Reject されてしまった模様…。

Feature #14473: Add Range#subrange? - Ruby trunk - Ruby Issue Tracking System

任意の Range が任意の Range の範囲内かどうかを判定するメソッドの提案。
あれば便利そう。

Feature #4824: Provide method Kernel#executed? - Ruby trunk - Ruby Issue Tracking System

Ruby だと『実行したファイルがコマンドラインから呼ばれたかどうか』を判定する場合に

if __FILE__ == $0
  my_main()          # call any method or execute any code
end

みたいに書くんですが、この __FILE__ == $0 をメソッド化してわかりやすくしたいよねーみたいな提案。
かなり前から議論されているんですが、いいメソッド名がないらしくてまだ決まってない模様。

Feature #14585: Array#each_pair - Ruby trunk - Ruby Issue Tracking System

ほしいのはわかるけど名前がいまいち。

Feature #14593: Add Enumerator#concat - Ruby trunk - Ruby Issue Tracking System

今までなんでなかったのか理由が気になる。

Feature #14594: Rethink yield_self's name - Ruby trunk - Ruby Issue Tracking System

yield_self の別名を考える、ということで色々と議論されていたみたいですが、ほぼほぼ then に決まったぽい。

Feature #14022: String#surround - Ruby trunk - Ruby Issue Tracking System

これ、稀によく欲しくなるのでちょっと期待していたんですが、Reject されてしまった模様。

Feature #13383: [PATCH] Module#source_location - Ruby trunk - Ruby Issue Tracking System

Module#source_location の提案 文字通りクラスの定義場所を取得するメソッドだけど、Ruby の場合はオープンクラスをいろんな場所で呼び出す事が出来るので複数ヶ所を取得する必要があって止まってるみたい。
ただ、最後の定義場所を取得できるだけでも便利そう

Feature #14197: Enumerable#{select,reject accept a pattern argument - Ruby trunk - Ruby Issue Tracking System}

collection.reject(/re/) みたいに『#reject にも値を渡したいよねー』という提案。
まあわからなくはない。

第9回 学生エンジニア限定LT大会!!!でスライドの作り方の LT してきた

もう1週間以上も経ってしまいましたが第9回 学生エンジニア限定LT大会!!!に遊びに行ってきました。
自分でブログ書けと言って書いてなかった 今回は寿司とピザを食べることが出来たぞ!!!

[reveal.js + markdown でスライドをつくろう!!]

と、言うことでわたしもスライドをつくる時に利用している reveal.js の紹介なんかをしてきました。
markdown でスライドを書くことが出来るのでそんなに凝らないスライドだとサクッと書くことが出来るのでかなり重宝しています。
スライドにも載せていますがここにテンプレートを用意していたりするので気になる人は使ってみるとよいと思います。
使い方はここ ってか、最近学生じゃないのに学生LT関連でLTしまくっとるな…。

C++ で std::tuple を使ってバージョン番号を比較する

元ネタ:C++ バージョン番号を比較する方法 - イネマルのプログラミング備忘録

#include <tuple>
#include <iostream>

int
main(){
    // 適当なバージョンデータ
    auto appA = std::tuple{ 2, 0, 101, 2 };
    auto appB = std::tuple{ 2, 0, 100, 3 };
    // C++11 で使いたいなら std::make_tuple を使う
//     auto appA = std::make_tuple(2, 0, 101, 2);
//     auto appB = std::make_tuple(2, 0, 100, 3);

    // 比較(appA < appB)
    bool result = appA < appB;

    // 結果
    if (result) {
        std::cout << "appAの方が古い" << std::endl;
    }
    else {
        std::cout << "appBの方が古い" << std::endl;
    }

    return 0;
}
/*
output:
appBの方が古い
*/

こういう場合は std::tuple 便利だよね!!!といいたかっただけの記事。

参照

1,2月で気になった Ruby のパッチ、提案

去年、Ruby のパッチを書き始めた頃から開発者向けの ML に参加しているんですが、その中で面白そうなパッチや提案が結構目に付くので定期的にまとめてみようかと。

Feature #13784: Add Enumerable#filter as an alias of Enumerable#select - Ruby trunk - Ruby Issue Tracking System

Enumerable#filterEnumerable#select の alias にする提案。 提案自体は去年されていたんですが、最近取り込まれました。 先日リリースされた Ruby 2.6 preview 版にもすでに実装されています。 確かに名前は filter の方が直感的だと思うんですが、これにより find_allselectfilter と同等のメソッドが3つ存在することに…。

Feature #14371: New option "recursive: true"; for Hash#transform_keys! - Ruby trunk - Ruby Issue Tracking System

Ruby 2.5 で追加された Hash#transform_keys!再帰的に適用させたい、みたいな提案だったんですが、残念ながら reject されました。 これに関しては matz がコメントしているのでそっちを参照してください。

Feature #14390: UnboundMethod#to_proc - Ruby trunk - Ruby Issue Tracking System

UnboundMethod#to_proc を追加して、第一引数をレシーバとして扱うような提案…かな? 多分以下のような挙動を想定。

to_s = Integer.instance_method(:to_s)

# 従来の使い方
p to_s.bind(42).call 2
# => "101010"

# 提案された使い方
# 第一引数を bind する
p to_s.to_proc.call 42, 2
# => "101010"

確かにこれがあるといろいろとできそうな気がする。

Feature #13581: Syntax sugar for method reference - CommonRuby - Ruby Issue Tracking System

これ、スレッド内でかなり議論されているんですが、やりたいことは単純で X.method(:hoge)シンタックスシュガーがほしいという要求です。 わたしもこれがめちゃくちゃ欲しいんですが、どういうシンタックスシュガーがいいのか…。 あと instance_methodシンタックスシュガーがほしい〜〜〜〜〜〜。

Feature #5120: String#split needs to be logical - Ruby trunk - Ruby Issue Tracking System

これもかなり昔から提案されていたみたいなんですが、最近ちょっと話題になって正式に reject されました。 確かに "".split('') # => [''] になったほうが自然な気がしますが、まあいまから仕様を変えるのも大変そう。

Feature #14498: Class#to_proc - Ruby trunk - Ruby Issue Tracking System

Class#to_procClass#new を呼び出す提案。 ぱっとみ便利そうなんですが、to_proc を潰すのはもったいないので、これぐらいなら Dog.method(:new) 使えばいいかなーという感じ。 可読性の観点からもよくないということで reject されています。

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 が全く出来ませんし RubyRails みたいな世の中は本当にクソだなと心の底から思っていますが、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
*/

C++ たのしー

Vim script でいい感じにデバッグ出力する

Vim script でデバッグ出力を行いたい場合、以下のようなスクリプトを定義しておくことで『変数名:値』みたいな出力をすることが出来ます。

command! -nargs=*
\   Debug
\   try
\|      echom <q-args> ":" string(<args>)
\|  catch
\|      echom <q-args>
\|  endtry

使い方

let hoge = 42
" 変数名 : 値
" を出力
Debug hoge
" hoge : 42 と出力

" 変数名がない場合はその名前だけ出力
Debug foo
" foo と出力

とても便利。