マクロツイーター

はてダから移行した記事の表示が崩れてますが、そのうちに直せればいいのに(えっ)

Ruby でもっと変態的な if 関数を

結局のところ、既存の if と同等のものを新たに実装しても、新しいことができる訳ではなくあまり面白くない。そこでもっと変態的な if 式を作ることを考えてみた。

平凡な if

まずは、従来の「平凡な if 式」の仕様について確認することで、新しい定義で使う概念について説明を兼ねる。

  • 「これまでの if 節(および他の『条件式を伴う』節)の成立条件の論理和」のことを「累積条件」と呼ぶ。
  • “if (式)” が成立するのは: 「式」が真の時。
  • “elsif (式)” が成立するのは: 「¬累積条件 ∧ 式」が真の時。
  • “else” が成立するのは: 「¬累積条件」が真の時。

「累積条件」について補足しておく。以下のコードを考える。

if n.mult_of?(15) then puts "FizzBuzz"
elsif n.mult_of?(3) then puts "Fizz"
elsif n.mult_of?(5) then puts "Buzz" # (※)
else puts n
end

この中で (※) の節における「累積条件」は

( n.mult_of?(15) ) || ( !n.mult_of?(15) && n.mult_of?(3) )

となる。普通の if 式ではこれは「これまでの『条件式』の論理和」である

( n.mult_of?(15) ) || ( n.mult_of?(3) )

と常に同値になるが、ここで定義する「変態的 if 式」ではこの性質は必ずしも成立しないことに注意。

変態的 if 式

節の種類が if、elsif、else の 3 つしかないのは非常につまらないので、これを拡張してもっと累積条件と現在の条件との多様な組み合わせを指定できるようにしてみよう。

成立条件
.if { 式 }.then
.then直前条件
.thenandif { 式 }.then直前条件 ∧ 式
.thenorif { 式 }.then直前条件 ∨ 式
.ifabove累積条件
.ifaboveandif { 式 }.then累積条件 ∧ 式
.ifaboveorif { 式 }.then累積条件 ∨ 式
.else¬累積条件
.elseandif { 式 }.then¬累積条件 ∧ 式
.elseorif { 式 }.then¬累積条件 ∨ 式
.anyway常に真
※ .elseandif の代わりに .elsif も使用可能。

ここで、「直前条件」は

自身より前で直近の「条件式を伴う節」の成立条件

を指す(これも条件式の値そのものではない)。

この「変態的 if 式」(weird if-expression)を実装したのが Wix モジュールである。


使い方は、まず「Wix」を書いて、その後に上の表にある条件節を繋げていく。(先頭には .if と .anyway のみ指定できる。)

Wix.if { name.empty? }.then { puts "名無しですか?" }
.else { puts "#{name}さん、こんにちは!" }

なお、実際には .if や .thenorif 等の条件の節は「累積条件」と「直前条件」の更新をしている(上掲の表の「成立条件」の値を「直前条件」とする)だけで、その後の .then が「直前条件によるブロックの実行」を行っている。だから .if の後に必ずしも .then を繋げる必要はない。

変態的 if 式の使用例

何故か分岐が 3 つしかない FizzBuzz

require 'Wix'
1.upto(20) do |n|
  Wix.if { n % 3 == 0 }.then {
    print "Fizz"
  }.if { n % 5 == 0 }.then {
    print "Buzz"
  }.else {
    print n
  }
  puts
end

とても不気味なナベアツ。*1

require 'Wix'
1.upto(40) do |n|
  Wix.if { n % 3 == 0 }
   .thenorif { /3/ =~ n.to_s }
   .then { print "<aho>" }
   .anyway { print n }
   .then { print "</aho>" }
   .anyway { print " " }
end
puts

*1:「アホになる」は単に XMLマークアップで表した。