空の配列に対するRubyのメソッドの挙動（数学寄り）

以下の結果は何なのかという話。空の配列でバグったら格好悪いので復習する。

```ruby:quiz.rb
p [].any?
p [].all?

p [].combination(0).to_a
p [].combination(1).to_a

p [].cycle.size

p [[], [], []].transpose.transpose
```

## any? と all?
`any?` は「要素にひとつ以上真なものがあるか」なので、空の配列に対して false を返すのは納得しやすい（真逆である `none?` も簡単）。それに対して `all?` は「要素が全て真なものか」なので、空の配列に対してだと**何も無いのに全て**とは何なのか混乱する。

そこで以下のように考える。簡単のため `ary` はbooleanの配列とする。

```ruby:ary.all?&#x20;の実装
# イメージとしては
init && ary[0] && ary[1] && ... && ary[-1]

# より形式的には
ary.inject(init) { |res,elem| res && elem }
```

このとき `ary.size > 0` に対して妥当な結果になる `init` を決められれば、それは空の配列に対する戻り値としても妥当である。 false だとダメだが true ならうまくいく。

あるいは、[ド・モルガンの法則](https://ja.wikipedia.org/wiki/ド・モルガンの法則)を使って `any?` に書き換えても理解できる。一般に `ary.all? == !ary.any?(&:!)` なので、空の配列に対してだと `all?` は `any?` と逆の値を返す。

以上より、**空の配列に対して `all?` は true を返す**。

---

大抵の場合はこの性質で真偽判定が単純になるはずだが、 `all?` は与えられた評価が何であっても空の配列だと true を返してしまうことには注意。成り立つわけがない評価ほど見逃しやすい。

```ruby:vacuous_truth.rb
puts "文字数が'負'な英単語のみをカンマ区切りで入力してください"
ary = gets.chomp.split(',', -1)
p ary

if ary.all? { |str| str.length < 0 }
	puts "成功"
else
	puts "失敗"
end
```

## 順列・組み合わせ
配列の要素に対して指定した個数での選び方を列挙するメソッド。並び順を区別するか、同じものを選んでもいい（重複を許す）か、によって 2×2 = 4種類ある。

* どの場合においても、「0個のものの中から0個を選ぶ方法」は**1通りだけある**。その結果、「0個を選んだことを表す空の配列」を1つ持つリスト `[[]]` が作られる[^rv]。
* それに対して、「0個のものの中から1個以上を選ぶ方法」は存在しない。結果として、選び方を1つも持たないリスト `[]` が作られる[^rv]。

```ruby
# [[]] を返す
ary = []; r = 0
ary.permutation(r).to_a
ary.combination(r).to_a
ary.repeated_permutation(r).to_a
ary.repeated_combination(r).to_a

# [] を返す
ary = []; r = 1
ary.permutation(r).to_a
ary.combination(r).to_a
ary.repeated_permutation(r).to_a
ary.repeated_combination(r).to_a
```

[^rv]: 正確には、ブロックを与えれば順次実行され、与えなければ Enumerator が返される。

## cycle
`each` を無限に繰り返すようなメソッド。（引数で回数指定もできる）

```ruby
ary = [1, 2, 3]
ary.cycle.size     #=> Infinity
ary.cycle.take(10) #=> [1, 2, 3, 1, 2, 3, 1, 2, 3, 1]
```

では**0個を無限回繰り返す**とどうなるのだろう？と思ったが、これは0になった。取り出せるものが無いのだから当然か。

```ruby
[].cycle.size #=> 0
[].cycle.to_a #=> []
```

無限が有限になるぶんには問題は起きそうにない。空の配列だとループが実行されないのは `each` でも何でも同じなので `cycle` 特有の問題ではない。

## transpose
2次元配列を行列とみなして、行と列を入れ替える（転置する）メソッド。

空の配列なら0行であり、0行0列は転置しても同じく空の配列になる。

```ruby
[].transpose #=> []
```

…と思ったら罠があった。本当は**0行なら何列なのかわからない**ので、上の動作はごく稀に都合が悪い[^case]。わかりやすいところでは、「転置を2回すれば元に戻る」が成り立たない事例がある。

```ruby
ary = [[], [], []]             # 3行0列
ary.transpose           #=> [] # 0行3列?
ary.transpose.transpose #=> [] # != ary
```

なお[ Matrix クラス](https://docs.ruby-lang.org/ja/latest/class/Matrix.html)なら行列の大きさを保持するので空行列でもうまく扱える。空の配列の転置のためだけに使うくらいなら場合分けなどするが。

```ruby
require 'matrix'

m = Matrix.build(3, 0) {}  #=> Matrix.empty(3, 0)
m.to_a                     #=> [[], [], []]
m.transpose.to_a           #=> []
m.transpose.transpose.to_a #=> [[], [], []]
```

[^case]: 実際にこれのため0の場合だけアルゴリズムを適用できないことがあった。

## その他
「[self時々nilを返す破壊的メソッド](15504596ed6f0373b10e)」を空の配列に作用させると nil が返る。配列に対する変更が無いことを意味する。

```ruby
[].keep_if {} #=> []  # 常にself
[].select! {} #=> nil # 普通はselfだが、変更が無いとnil
```

何か1個を返すつもりのメソッドだとだいたい nil が返る。個人的には `min` や `max` で対処を忘れてしまいやすい。

```ruby
[].sample #=> nil
[].minmax #=> [nil, nil]
```

`all?` の例でも使ったが、 `inject` で空の配列を扱うなら初期値を指定しておく必要がある（それを決めるのが難しい…）。例えば「与えられた全ての数の積」を計算するなら、0個のときは1を返すのが望ましい。

```ruby
[].inject(:*)    #=> nil
[].inject(1, :*) #=> 1
```