範囲は、2つの値の間の区間を表します。
範囲をサポートするもっとも一般的な型は、IntegerとStringです。
範囲は、コレクションをすばやく作成したり、文字列を切り出したり、ある値が範囲に含まれるかを確認したり、繰り返し処理を行ったりと、さまざまなことに使えます。
範囲は、範囲演算子..または...を使って作成します(それぞれ、終端を含む場合と含まない場合に対応します)。
1..5 # => 1..5
1...5 # => 1...5
(1..5).to_a # => [1, 2, 3, 4, 5]
(1...5).to_a # => [1, 2, 3, 4]
範囲演算子が2つあるのは、終端の値を含む範囲と含まない範囲を作成できるようにするためです。これは、たとえば0から始まるインデックスを扱うときに便利です。
範囲は、Rangeのコンストラクターであるnewを使って作成することもできます。
Range.new(1, 5) # A range containing 1, 2, 3, 4, 5
範囲演算子..や...を使ってRubyで範囲を作成し、その範囲に対してメソッドを呼び出したいときは、範囲を括弧(())で囲む必要があります。
そうしないと、範囲演算子の2番目の引数に対してメソッドが呼び出されてしまうからです。
(1..5).sum # => 15
1..5.sum # => Error: undefined method `sum' for 5:Integer (NoMethodError)
文字列を切り出したいときは、範囲演算子を使って部分文字列を取得できます。 つまり、部分文字列の開始インデックスと終了インデックスで範囲を作成します。
"Hello World"[0..4] # => "Hello"
"Hello World"[6..10] # => "World"
負のインデックスを使うと、文字列の末尾から部分文字列を取得することもできます。
"Hello World"[-5..-1] # => "World"
"Hello World"[6..-4] # => "Wo"
範囲には、それを扱うためのメソッドがいくつか用意されています。 たとえば、範囲内のすべての値の合計を求めたり、範囲にある値が含まれるかを確認したりするのに使えます。
| メソッド | 説明 | 例 |
|---|---|---|
sum |
範囲内のすべての値の合計を返します | (1..5).sum # => 15 |
size |
範囲のサイズを返します | (1..5).size # => 5 |
include? |
範囲に指定した値が含まれていればtrueを、そうでなければfalseを返します |
(1..5).include?(3) # => true |
範囲は、終端がないものや始端がないものにもできます。
終端や始端のない範囲では、開始値または終了値がnilになりますが、範囲を定義するときにはnilを省略できます。
始端や終端のない範囲は、たとえば文字列を先頭から、または末尾まで切り出したいときに便利です。
"Hello World"[0..] # => "Hello World"
"Hello World"[4..] # => "o World"
"Hello World"[..5] # => "Hello "
コレクションに対して使わない場合、終端のない範囲は、注意して使わないと無限の繰り返しを引き起こすことがあります。
範囲には文字列も使え、2つの文字列の間にある文字列の区間を取得できます。 特定の文字列を使うと、その動作は少し予想外になることがあるので、注意して使ってください。
("aa".."az").to_a # => ["aa", "ab", "ac", ..., "az"]
チェスの愛好家なら、自分だけのチェスを作ってみたいと思うでしょう。 インターネット上にはすでにたくさんのチェスの実装がありますが、自分で作るものは世界に一つだけです!
まずは、駒の基本的な動きの仕組みを実装するところから始めます。
チェスは、縦8マス、横8マスの盤の上で行われます。 各マスは、アルファベット1文字と数字で表されます。
ゲームでは、盤のランクを保持する必要があります。 ランクは盤の行にあたり、1から8までの数字で表されます。
盤のファイルも保持する必要があります。 ファイルは盤の列にあたり、AからHまでの文字で表されます。
ランクとファイルの範囲をそれぞれ保持するChess::RANKSとChess::FILES定数を定義しましょう。
Chess::RANKS
# => 1..8
Chess::FILES
# => 'A'..'H'
ゲームでは、マスが有効かどうかを判定する必要があります。 ランクとファイルが、それぞれランクとファイルの範囲内にあれば、そのマスは有効です。
ランクを表す整数を保持する引数rankと、ファイルを表す文字を保持する引数fileを取るChess.valid_square?メソッドを定義しましょう。
このメソッドは、ランクとファイルがそれぞれの範囲内にあればtrueを返し、そうでなければfalseを返すようにします。
Chess.valid_square?(1, 'A')
# => true
ゲームでは、プレイヤーのニックネームを取得する必要があります。 ニックネームは、プレイヤーの名の最初の2文字と、姓の最後の2文字を組み合わせたものです。 ニックネームは大文字にします。
プレイヤーの名を表す文字列を保持する引数first_nameと、姓を表す文字列を保持する引数last_nameを取るChess.nicknameメソッドを定義しましょう。
このメソッドは、プレイヤーのニックネームを大文字の文字列として返すようにします。
Chess.nickname("John", "Doe")
# => "JOOE"
ゲームでは、どのプレイヤーがどのマスに移動したかを伝えるメッセージを作成する必要があります。 メッセージには、プレイヤーのニックネームと、移動先のマスを使います。 また、マスのファイルとランクがそれぞれの範囲内にあるかを調べて、移動が有効かどうかを判定する必要があります。
移動が有効であれば、メッセージは"{nickname} moved to {square}"とします。
移動が無効であれば、メッセージは"{nickname} attempted to move to {square}, but that is not a valid square"とします。
プレイヤーの名を表す文字列を保持する引数first_name、姓を表す文字列を保持する引数last_name、そして移動先のマスを表す文字列を保持する引数squareを取るChess.move_messageメソッドを定義しましょう。
このメソッドは、移動のメッセージを文字列として返すようにします。
Chess.move_message("John", "Doe", "A1")
# => "JOOE moved to A1"