10
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Ruby のフリップフロップの闇感と Enumerable#flipflop

Last updated at Posted at 2016-03-13

以前,「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 に代入される。

nilfalse 以外のすべてのオブジェクトは真と評価されるので,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 でもフリップフロップは生き残っているので,結局廃止されないことになったのだろう。

10
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?