以前,「Ruby のココがダメ」という記事で,「フリップフロップ ワケわかめ」と書いた。
その後,@cuzic さんが「Ruby のオススメの機能7選」という記事で「Flip Flop 演算子」を知名度が低いオススメ機能として挙げられた。
最近,@Ping さんが「Ruby でフリップフロップ」という記事で,「..
」と「...
」の違いなども含め,やや詳しく説明された。
フリップフロップを知ってから十年くらい無視し続けて来たのに,ここ半年でミョーに意識することになったので,ちょっと考えたことを書いてみる。
Ruby のフリップフロップとは何か,ということは,リンク先の記事とかコメントとか公式リファレンスマニュアルの「範囲式」の「条件式としての範囲式」とか見てください。
フリップフロップの闇感
フリップフロップは何か闇な感じがする。なぜなのか。
既に多くの人が書いているとおり,誰が状態を保持しているのか分からないところが闇。何らかのオブジェクトが状態を持っているとかじゃないと落ち着かない。Ruby ぽくない気がする。
それと,範囲式が条件式の中で使われたときに限りフリップフロップとして働く,というのが気持ち悪い。ふつう範囲式を評価すると,その値は Range オブジェクトなのだが,条件式の中では真偽値として評価される。
以下の,コード A と B は等価ではない。
strings = %w(a b begin c d end e)
# コード A
strings.each do |s|
puts s if (s == "begin")..(s == "end")
end
# コード B
strings.each do |s|
x = (s == "begin")..(s == "end")
puts s if x
end
コード A は範囲式がフリップフロップとして働き,標準出力に順に「begin
」「c
」「d
」「end
」が出力される。
コード B では,s
が "a"
,"b"
のときは,範囲式を評価することで false..false
という Range オブジェクトが生成され,それが x
に代入される。
nil
と false
以外のすべてのオブジェクトは真と評価されるので,Range オブジェクトは当然,真だ。よって「a
」「b
」が出力される。
ところが,s
が "begin"
のときは true..false
という Range オブジェクトを作ろうとして,例外が発生する。
Enumerable#flipflop ってどうよ
そういうメソッドは無い。僕の前に Enumerable#flipflop
は無い。
つくったらあーるよーだっ!
大雑把なコードで示すと,こういうメソッドがあってもいいんじゃないか?
module Enumerable
def flipflop(start_criterion, end_criterion)
each do |item|
yield item if (start_criterion === item)..(end_criterion === item)
end
end
end
これなら Ruby 的に違和感が無いんじゃないか?
こんなふうに使う。
"begin"
が来てから "end"
が来るまでを出力するなら:
strings = %w(a b begin c d end e)
strings.flipflop("begin", "end") do |s|
puts s
end
ゼロが来てから nil
が来るまで:
[2, 9, 0, 4, 7, nil, 8].flipflop(:zero?.to_proc, :nil?.to_proc) do |x|
# なんとか
end
Object#===
を使うから,引数には数値,文字列,正規表現,範囲,Proc オブジェクトなどが渡せる。
もちろん実用的には,ブロック無しで Enumerator
を返したり,...
に相当する〈終端を含まない〉オプションを用意したり,といったことが必要だろう。〈始点を含まない〉も必要かもしれない。論理反転も要るような気がする。
そして廃止へ(2018/12/11 追記)
下記の記事の「フリップフロップ構文を使うと警告が出るようになった」によると,フリップフロップは Ruby 3 で廃止されることが決まったそうです(Ruby 2.6 では警告)。
サンプルコードでわかる!Ruby 2.6の主な新機能と変更点 - Qiita
まあいいんじゃない?
そのかわりに Enumerable#flipflop
を入れてくれてもよさそうな気が。
やっぱ廃止やめ?(2019/12/20追記)
Ruby 2.6 でフリップフロップを使うと
warning: flip-flop is deprecated
って警告が出てたんだけど,Ruby 2.6.4 から警告が出なくなった。
以下で経緯が判るはずなのだが,まだよく読んでいない。
https://bugs.ruby-lang.org/issues/5400
2019/7/11 にまつもとさんが
OK, I give up. The warning should be removed.
と書いてあるのだけ読んだ。
Ruby 3 での廃止が撤回されたのかどうかは,今のところ私はわかってない。誰か教えて〜
結局生き残った(2022/03/24追記)
Ruby 3.1.1 でもフリップフロップは生き残っているので,結局廃止されないことになったのだろう。