条件式では特殊な動作をするRubyリテラル

Rubyにおいて一部のクラスのオブジェクトは、`new` などを使用せずに[**リテラル**](http://docs.ruby-lang.org/ja/2.0.0/doc/spec=2fliteral.html)として直接記述できる。ただし**条件式の文脈で書いた場合**は、必ずしも通常のオブジェクト扱い（`false`, `nil` 以外は真と評価）となるわけではないらしい。

もちろん、変数やメソッドなどを条件式にする分には問題無い。その結果がnilかどうかで条件分岐することはよく行われている。また、リテラルを条件式にすることはふつう無いので、動作を知らなくてもバグを生む可能性は少ない。この記事は注意書きではなく、単に個人的に驚いた仕様を少し触って調べたものである。

## 警告が出たりするリテラル
確認できた分だけ列挙。

### 正規表現
マニュアルの[if文の説明](http://docs.ruby-lang.org/ja/2.0.0/doc/spec=2fcontrol.html#if)にさらっと書いてある。組み込み変数 [`$_`](http://docs.ruby-lang.org/ja/2.0.0/class/Kernel.html#V__)（`Kernel.#gets` や `Kernel.#readline` で最後に読み込んだ文字列）とのマッチを評価するとのこと。

```irb
irb(main):001:0> puts 'true' if /regexp/
(irb):1: warning: regex literal in condition
=> nil
irb(main):002:0> gets   # $_に値を入れるため
geregexpxe
=> "geregexpxe\n"
irb(main):003:0> puts 'true' if /regexp/
(irb):3: warning: regex literal in condition
true
=> nil
```

### 範囲式
通常は両端に比較可能なオブジェクトを指定して `Range` オブジェクトを作るが、「[条件式としての範囲式](http://docs.ruby-lang.org/ja/1.9.3/doc/spec=2foperator.html#range_cond)」では両端に条件式を指定する。

```irb
irb(main):001:0> puts 'true' if true..false
(irb):1: warning: range literal in condition
true
=> nil
```

基本的な使い方については、記事「[Ruby でフリップフロップ](http://qiita.com/Ping/items/acb7fbb9aa141c2c46df)」で詳しく説明されている。実用的なテキスト処理を例にしていて、前節の正規表現リテラルも使われている上に、AWKやPerlの場合も載っていて、これらの機能の価値が分かりやすい。

### (文字列)
警告が出るだけで問題は無い？

```irb
irb(main):001:0> puts 'true' if 'string'
(irb):1: warning: string literal in condition
true
=> nil
```

### (整数)
「条件式としての範囲式」の中で指定すると警告が出る。`true` に判定されない理由がよく分からなかった。また、リテラルを括弧で囲むだけで警告はなくなり評価結果が変わる。

```irb
irb(main):001:0> puts 'true' if 0..9
(irb):1: warning: integer literal in conditional range
(irb):1: warning: integer literal in conditional range
=> nil
irb(main):002:0> puts 'true' if (0..9)
true
=> nil
```

## 条件式として扱われる場所
試した結果からの推測なので、抜け漏れや誤解があるかもしれない。

- if, while, 条件演算子など
- not
- 条件式中のandなど
- 条件式としての範囲式

```irb
irb(main):001:0> puts 'true' while /(?!)/
(irb):1: warning: regex literal in condition
=> nil

irb(main):002:0> puts !/regexp/
(irb):2: warning: regex literal in condition
true
=> nil

irb(main):003:0> puts /a/ && /b/   # これだと単なる正規表現オブジェクト同士の&&
(?-mix:b)
=> nil
irb(main):004:0> puts 'true' if /a/ && /b/   # これなら$_とのマッチ評価の論理積
(irb):4: warning: regex literal in condition
(irb):4: warning: regex literal in condition
=> nil

irb(main):005:0> puts 'true' if /a/../b/
(irb):5: warning: regex literal in condition
(irb):5: warning: regex literal in condition
=> nil
```

## 小技？
いま使いたいわけではないが、何かあったときに選択肢のひとつとなるかもしれないので、できそうなことを考えてみる。

### 評価結果の受け取り
すぐに条件分岐させるのではなく、評価結果を一旦変数に記憶したいことがあるはず。しかしリテラルを普通に代入すればただのオブジェクトになってしまう。

対策は簡単で、if文や条件演算子を使ってtrue/falseを返せば結果を受け取れるし、もっと短くnotを2回かけてもできる。

```irb
irb(main):001:0> result = true..false   # これだと条件式にならない
ArgumentError: bad value for range
        from (irb):1
        from C:/Ruby/Ruby-2.1.1/bin/irb.bat:18:in `<main>'
irb(main):002:0> result = if true..false then true else false end
(irb):2: warning: range literal in condition
=> true
irb(main):003:0> result = true..false ? true : false
(irb):3: warning: range literal in condition
=> true
irb(main):004:0> result = !!(true..false)
(irb):4: warning: range literal in condition
=> true
```

### ループ文の条件式で使用
通常はif文の条件式でしか使わなそうだが、whileなどのループ文でも何かできないかなと。

```irb
irb(main):001:0> gets; gets until /\Ayes\Z/   # yesと入力するまでループ
(irb):1: warning: regex literal in condition
no
no
yes
=> nil
```

### 条件式のネスト
範囲式や論理演算子は入れ子にすることができる。何か面白いことができそうな気はするのだが、評価順序に頭が追いつかなくて全く思いつかない。

```irb
irb(main):001:0> (1..10).each { |i| puts ((i%3==0)..(i%2==0))..((i%4==0)..(i%7==0)) ? -i : i }
1
2
-3
-4
-5
-6
7
8
-9
-10
=> 1..10
```

個々の評価順序と結果は以下の通り。

| i | i%3==0 | i%2==0 | i%4==0 | i%7==0 | 結果 |
|:-:|:-:|:-:|:-:|:-:|:-:|
|  1 | false |       |       |       | false |
|  2 | false |       |       |       | false |
|  3 | true  | false | false |       | true  |
|  4 |       |       | true  | false | true  |
|  5 |       | false |       | false | true  |
|  6 |       | true  |       | false | true  |
|  7 | false |       |       |       | false |
|  8 | false |       |       |       | false |
|  9 | true  | false |       | false | true  |
| 10 |       | true  |       | false | true  |