Range 클래스
Range 클래스
Range 객체는 주어진 시작(begin) 값과 끝(end) 값 사이에 있는 값들의 모임을 나타내요. Array, 문자열 등 여러 타입의 연속 값을 나타내는 데 쓰여요.
출처: Ruby 3.3 API
본문
Range 만들기
리터럴로 만들 수 있어요. ..는 끝 값을 포함하고, ...는 끝 값을 제외해요.
# Ranges that use '..' to include the given end value.
(1..4).to_a # => [1, 2, 3, 4]
('a'..'d').to_a # => ["a", "b", "c", "d"]
# Ranges that use '...' to exclude the given end value.
(1...4).to_a # => [1, 2, 3]
('a'...'d').to_a # => ["a", "b", "c"]
Range.new 메서드로도 만들 수 있어요. 세 번째 인자 exclude_end로 끝 값을 제외하도록 정해요.
# Ranges that by default include the given end value.
Range.new(1, 4).to_a # => [1, 2, 3, 4]
Range.new('a', 'd').to_a # => ["a", "b", "c", "d"]
# Ranges that use third argument +exclude_end+ to exclude the given end value.
Range.new(1, 4, true).to_a # => [1, 2, 3]
Range.new('a', 'd', true).to_a # => ["a", "b", "c"]
시작 없는 범위 (Beginless Ranges)
beginless range는 명확한 끝 값은 있지만 시작 값이 nil이에요. 그러한 범위는 끝 값까지 모든 값을 포함해요.
r = (..4) # => nil..4
r.begin # => nil
r.include?(-50) # => true
r.include?(4) # => true
r = (...4) # => nil...4
r.include?(4) # => false
Range.new(nil, 4) # => nil..4
Range.new(nil, 4, true) # => nil...4
시작 없는 범위로 배열을 슬라이스할 수도 있어요:
a = [1, 2, 3, 4]
r = (..2) # => nil...2
a[r] # => [1, 2]
시작 없는 범위의 each 메서드는 예외를 발생시켜요.
끝 없는 범위 (Endless Ranges)
endless range는 명확한 시작 값은 있지만 끝 값이 nil이에요. 그러한 범위는 시작 값부터 모든 값을 포함해요.
r = (1..) # => 1..
r.end # => nil
r.include?(50) # => true
Range.new(1, nil) # => 1..
끝 없는 범위 리터럴은 두 점이나 세 점으로 쓸 수 있어요. 어느 쪽이든 같은 요소를 갖지만, 둘은 같지 않아요(not equal).
r0 = (1..) # => 1..
r1 = (1...) # => 1...
r0.begin == r1.begin # => true
r0.end == r1.end # => true
r0 == r1 # => false
끝 없는 범위로 배열을 슬라이스할 수도 있어요:
a = [1, 2, 3, 4]
r = (2..) # => 2..
a[r] # => [3, 4]
끝 없는 범위의 each 메서드는 주어진 블록을 무한히 호출해요:
a = []
r = (1..)
r.each do |i|
a.push(i) if i.even?
break if i > 10
end
a # => [2, 4, 6, 8, 10]
범위는 시작과 끝이 모두 없을 수도 있어요. 리터럴로 시작도 끝도 없는 범위를 쓸 때는 시작이나 끝 중 적어도 하나를 명시적 nil로 줘야 해요. Ruby가 Range#inspect에 쓰는 형태인, 명시적 nil 시작과 암묵적 nil 끝을 권장해요:
(nil..) # => (nil..)
(..nil) # => (nil..)
(nil..nil) # => (nil..)
Range와 다른 클래스 (Ranges and Other Classes)
객체의 클래스가 인스턴스 메서드 <=>를 구현하면 그 객체를 범위에 넣을 수 있어요. 그렇게 하는 Ruby 코어 클래스로 Array, Complex, File::Stat, Float, Integer, Kernel, Module, Numeric, Rational, String, Symbol, Time이 있어요.
t0 = Time.now # => 2021-09-19 09:22:48.4854986 -0500
t1 = Time.now # => 2021-09-19 09:22:56.0365079 -0500
t2 = Time.now # => 2021-09-19 09:23:08.5263283 -0500
(t0..t2).include?(t1) # => true
(t0..t1).include?(t2) # => false
범위의 요소들이 인스턴스 메서드 succ를 구현할 때만 범위를 반복(iterate)할 수 있어요. 그렇게 하는 Ruby 코어 클래스로 Integer, String, Symbol이 있어요(위에서 언급한 다른 클래스는 아님). 반복자 메서드에는 Range 자체의 each, step, %와, Enumerable 모듈에서 포함된 each_entry, each_with_index, each_with_object, each_slice, each_cons, reverse_each가 있어요.
a = []
(1..4).each {|i| a.push(i) }
a # => [1, 2, 3, 4]
Range와 사용자 정의 클래스 (Ranges and User-Defined Classes)
범위에 쓸 사용자 정의 클래스는 인스턴스 <=>를 구현해야 해요(Integer#<=> 참고). 반복을 가능하게 하려면 인스턴스 메서드 succ도 구현해야 해요(Integer#succ 참고). 아래 클래스는 <=>와 succ를 모두 구현해서, 범위를 만들고 반복하는 데 모두 쓸 수 있어요. Comparable 모듈이 포함되어 == 메서드가 <=>의 관점에서 정의된다는 점에 주의하세요.
# Represent a string of 'X' characters.
class Xs
include Comparable
attr_accessor :length
def initialize(n)
@length = n
end
def succ
Xs.new(@length + 1)
end
def <=>(other)
@length <=> other.length
end
def to_s
sprintf "%2d #{inspect}", @length
end
def inspect
'X' * @length
end
end
r = Xs.new(3)..Xs.new(6) #=> XXX..XXXXXX
r.to_a #=> [XXX, XXXX, XXXXX, XXXXXX]
r.include?(Xs.new(5)) #=> true
r.include?(Xs.new(7)) #=> false
Public Class Methods
json_create(object)
as_json을 참고하세요. JSON에서 Range 객체를 복원해요.
new(begin, end, exclude_end = false) → new_range
주어진 begin과 end 객체를 기반으로 새 범위를 반환해요. 선택 인자 exclude_end는 객체 end를 범위의 마지막 객체로 포함할지 정해요:
Range.new(2, 5).to_a # => [2, 3, 4, 5]
Range.new(2, 5, true).to_a # => [2, 3, 4]
Range.new('a', 'd').to_a # => ["a", "b", "c", "d"]
Range.new('a', 'd', true).to_a # => ["a", "b", "c"]
Public Instance Methods
%(n) {|element| ... } → self, %(n) → enumerator
self의 요소들을 반복해요. 블록이 주어지면 범위의 선택된 요소로 블록을 호출하고 self를 반환해요. 블록이 없으면 enumerator를 반환하는데, self가 숫자면 Enumerator::ArithmeticSequence 클래스, 아니면 Enumerator 클래스예요. Range#step과 관련돼요.
a = []
(1..5).%(2) {|element| a.push(element) } # => 1..5
a # => [1, 3, 5]
a = []
('a'..'e').%(2) {|element| a.push(element) } # => "a".."e"
a # => ["a", "c", "e"]
e = (1..5) % 2 # => ((1..5).%(2))
e.class # => Enumerator::ArithmeticSequence
self == other → true or false
다음 조건을 모두 만족할 때만 true를 반환해요:
other가 range이다.other.begin == self.beginother.end == self.endother.exclude_end? == self.exclude_end?
그 외에는 false를 반환해요.
r = (1..5)
r == (1..5) # => true
r = Range.new(1, 5)
r == 'foo' # => false
r == (2..5) # => false
r == (1...5) # => false
r == Range.new(1, 5, true) # => false
같은 인자라도 ==와 eql?의 반환값이 다를 수 있어요:
(1..2) == (1..2.0) # => true
(1..2).eql? (1..2.0) # => false
self === object → true or false
object가 self.begin과 self.end 사이에 있으면 true, 그렇지 않으면 false를 반환해요.
(1..4) === 2 # => true
(1..4) === 5 # => false
(1..4) === 'a' # => false
(1..4) === 4 # => true
(1...4) === 4 # => false
('a'..'d') === 'c' # => true
('a'..'d') === 'e' # => false
case 문은 메서드 ===를 사용해요:
case 79
when (1..50)
"low"
when (51..75)
"medium"
when (76..100)
"high"
end # => "high"
as_json(*)
Range#as_json과 Range.json_create 메서드는 Range 객체를 직렬화/역직렬화하는 데 쓸 수 있어요. Range#as_json은 self를 직렬화해서 2-요소 해시를 반환해요:
require 'json/add/range'
x = (1..4).as_json # => {"json_class"=>"Range", "a"=>[1, 4, false]}
y = (1...4).as_json # => {"json_class"=>"Range", "a"=>[1, 4, true]}
z = ('a'..'d').as_json # => {"json_class"=>"Range", "a"=>["a", "d", false]}
JSON.create는 그런 해시를 역직렬화해서 Range 객체를 반환해요:
Range.json_create(x) # => 1..4
Range.json_create(y) # => 1...4
Range.json_create(z) # => "a".."d"
begin → object
self의 시작을 정의하는 객체를 반환해요. Range#first, Range#end와 관련돼요.
(1..4).begin # => 1
(..2).begin # => nil
bsearch {|obj| block } → value
이진 탐색(binary search)으로 self에서 요소를 선택해 반환해요.
count → integer, count(object) → integer, count {|element| ... } → integer
인자나 블록 기준(주어진 경우)에 따라 요소의 개수를 반환해요.
인자도 블록도 없으면 요소의 수를 반환해요:
(1..4).count # => 4
(1...4).count # => 3
('a'..'d').count # => 4
(1..).count # => Infinity
(..4).count # => Infinity
인자 object가 주어지면 self에서 찾은 object의 수를 반환해요(보통 0 또는 1):
(1..4).count(2) # => 1
(1..4).count(5) # => 0
블록이 주어지면 각 요소로 블록을 호출하고, 블록이 truthy 값을 반환한 요소의 수를 반환해요:
(1..4).count {|element| element < 3 } # => 2
cover?(object) → true or false, cover?(range) → true or false
주어진 인자가 self 안에 있으면 true, 그렇지 않으면 false를 반환해요.
범위가 아닌 인자 object는 <=와 <로 평가해요. 끝 값을 포함하는 범위(#exclude_end? == false)는 self.begin <= object <= self.end로, 끝 값을 제외하는 범위(#exclude_end? == true)는 r.begin <= object < r.end로 평가해요.
r = (1..4)
r.cover?(1) # => true
r.cover?(4) # => true
r.cover?(0) # => false
r.cover?(5) # => false
r = (1...4)
r.cover?(1) # => true
r.cover?(3) # => true
r.cover?(0) # => false
r.cover?(4) # => false
범위 인자 range가 주어지면 self와 range의 첫 번째·마지막 요소를 비교해요:
r = (1..4)
r.cover?(1..4) # => true
r.cover?(0..4) # => false
r.cover?(1..5) # => false
r.cover?('a'..'d') # => false
r = (1...4)
r.cover?(1..3) # => true
r.cover?(1..4) # => false
시작과 끝이 숫자면 cover?는 include?처럼 동작해요. 하지만 숫자가 아니면 두 메서드는 다를 수 있어요:
(1..3).cover?(1.5) # => true
(1..3).include?(1.5) # => true
('a'..'d').cover?('cc') # => true
('a'..'d').include?('cc') # => false
다음 중 하나면 false를 반환해요: self의 시작 값이 끝 값보다 큰 경우, 또는 내부 <=> 호출이 nil을 반환해 비교할 수 없는 경우. Range#include?와 관련돼요.
each {|element| ... } → self, each → an_enumerator
블록이 주어지면 self의 각 요소를 블록으로 전달해요. self.first.respond_to?(:succ)가 아니면 예외를 발생시켜요. 블록이 없으면 enumerator를 반환해요.
a = []
(1..4).each {|element| a.push(element) } # => 1..4
a # => [1, 2, 3, 4]
end → object
self의 끝을 정의하는 객체를 반환해요. Range#begin, Range#last와 관련돼요.
(1..4).end # => 4
(1...4).end # => 4
(1..).end # => nil
entries
to_a의 별칭이에요.
eql?(other) → true or false
다음 조건을 모두 만족할 때만 true를 반환해요:
other가 range이다.other.begin eql? self.beginother.end eql? self.endother.exclude_end? == self.exclude_end?
r = (1..5)
r.eql?(1..5) # => true
r = Range.new(1, 5)
r.eql?('foo') # => false
r.eql?(2..5) # => false
r.eql?(1...5) # => false
exclude_end? → true or false
self가 자신의 끝 값을 제외하면 true, 아니면 false를 반환해요.
Range.new(2, 5).exclude_end? # => false
Range.new(2, 5, true).exclude_end? # => true
(2..5).exclude_end? # => false
(2...5).exclude_end? # => true
first → object, first(n) → array
인자 없이 호출하면 self의 첫 번째 요소를 반환해요(있을 때). 음이 아닌 정수 n이 주어지면 배열에서 첫 n개 요소를 반환해요. 첫 요소가 없으면 예외를 발생시켜요.
(1..4).first # => 1
('a'..'d').first # => "a"
(1..10).first(3) # => [1, 2, 3]
(1..4).first(50) # => [1, 2, 3, 4]
(..4).first # Raises RangeError
hash → integer
self의 정수 해시 값을 반환해요. 두 range 객체 r0와 r1은 r0.eql?(r1)일 때만 같은 해시 값을 가져요.
include?(object) → true or false (member?의 별칭)
object가 self의 요소면 true, 아니면 false를 반환해요:
(1..4).include?(2) # => true
(1..4).include?(5) # => false
(1..4).include?(4) # => true
(1...4).include?(4) # => false
('a'..'d').include?('b') # => true
('a'..'d').include?('e') # => false
('a'...'d').include?('d') # => false
시작과 끝이 숫자면 include?는 cover?처럼 동작하지만, 숫자가 아니면 두 메서드는 다를 수 있어요.
inspect → string
begin.inspect와 end.inspect를 포함해 self의 문자열 표현을 반환해요:
(1..4).inspect # => "1..4"
(1...4).inspect # => "1...4"
(1..).inspect # => "1.."
(..4).inspect # => "..4"
to_s와 inspect의 반환은 다를 수 있어요:
('a'..'d').to_s # => "a..d"
('a'..'d').inspect # => "\"a\"..\"d\""
last → object, last(n) → array
인자 없이 호출하면 self의 마지막 요소를 반환해요(있을 때). exclude_end?가 true여도 인자 없이 last는 self의 끝 요소를 반환해요:
(1..4).last # => 4
(1...4).last # => 4
음이 아닌 정수 n이 주어지면 배열에서 마지막 n개 요소를 반환해요. exclude_end?가 true이면 인자와 함께 쓴 last는 끝 요소를 반환하지 않아요:
(1..10).last(3) # => [8, 9, 10]
(1...4).last(3) # => [1, 2, 3]
마지막 요소가 없으면 (1..).last처럼 예외를 발생시켜요.
max → object, max(n) → array, max {|a, b| ... } → object, max(n) {|a, b| ... } → array
메서드 <=> 또는 주어진 블록을 비교에 사용해 self의 최댓값을 반환해요. 인자도 블록도 없으면 self의 최대값 요소를 반환해요:
(1..4).max # => 4
('a'..'d').max # => "d"
(-4..-1).max # => -1
음이 아닌 정수 n이 주어지고 블록이 없으면, 배열에서 n개의 최대값 요소를 반환해요:
(1..4).max(2) # => [4, 3]
('a'..'d').max(2) # => ["d", "c"]
(1..4).max(50) # => [4, 3, 2, 1]
블록이 주어지면 먼저 self의 처음 두 요소로, 그다음에는 지금까지의 최대값과 다음 요소로 순차적으로 호출돼요. n이 0이면 빈 배열을 반환해요. self가 endless 범위((1..))이거나, 블록이 주어졌는데 beginless 범위면 예외를 발생시켜요. Range#min, Range#minmax와 관련돼요.
member?
include?의 별칭이에요.
min → object, min(n) → array, min {|a, b| ... } → object, min(n) {|a, b| ... } → array
메서드 <=> 또는 주어진 블록을 비교에 사용해 self의 최솟값을 반환해요. max와 대칭적으로 동작해요:
(1..4).min # => 1
('a'..'d').min # => "a"
(-4..-1).min # => -4
(1..4).min(2) # => [1, 2]
('a'..'d').min(2) # => ["a", "b"]
(1..4).min(50) # => [1, 2, 3, 4]
self가 beginless 범위((..4))이거나, 블록이 주어졌는데 endless 범위면 예외를 발생시켜요. Range#max, Range#minmax와 관련돼요.
minmax → [object, object], minmax {|a, b| ... } → [object, object]
비교 메서드 <=> 또는 주어진 블록에 따라 self의 최솟값과 최댓값을 담은 2-요소 배열을 반환해요.
(1..4).minmax # => [1, 4]
(1...4).minmax # => [1, 3]
('a'..'d').minmax # => ["a", "d"]
(-4..-1).minmax # => [-4, -1]
(1..4).minmax {|a, b| -(a <=> b) } # => [4, 1]
self가 beginless 또는 endless 범위면 예외를 발생시켜요. 비어 있을 땐 [nil, nil]을 반환해요. Range#min, Range#max와 관련돼요.
overlap?(range) → true or false
range가 self와 겹치면 true, 아니면 false를 반환해요:
(0..2).overlap?(1..3) #=> true
(0..2).overlap?(3..4) #=> false
(0..).overlap?(..0) #=> true
범위가 아닌 인자에는 TypeError를 발생시켜요. 내부 <=> 호출이 nil을 반환하면 false를 반환해요:
(1..3).overlap?('a'..'d') # => false
self나 range가 비어 있으면 false를 반환해요. "빈 범위"란 시작 값이 끝 값보다 크거나(또는 exclusive 범위일 때 같거나) 한 경우를 뜻해요. Range#cover?와 관련돼요.
reverse_each {|element| ... } → self, reverse_each → an_enumerator
블록이 주어지면 self의 각 요소를 역순으로 블록으로 전달해요. 블록이 없으면 enumerator를 반환해요.
a = []
(1..4).reverse_each {|element| a.push(element) } # => 1..4
a # => [4, 3, 2, 1]
a = []
(1...4).reverse_each {|element| a.push(element) } # => 1...4
a # => [3, 2, 1]
size → non_negative_integer or Infinity or nil
시작과 끝 값이 모두 숫자면 self의 요소 수를 반환하고, 아니면 nil을 반환해요:
(1..4).size # => 4
(1...4).size # => 3
(1..).size # => Infinity
('a'..'z').size #=> nil
Range#count와 관련돼요.
step(n = 1) {|element| ... } → self, step(n = 1) → enumerator
self의 요소들을 반복해요. 블록이 주어지고 인자가 없으면 범위의 각 요소로 블록을 호출하고 self를 반환해요. 양의 정수 인자 n이 주어지면 요소 0, 요소 n, 요소 2n 식으로 블록을 호출해요. 블록이 없으면 enumerator를 반환하는데, self가 숫자면 Enumerator::ArithmeticSequence 클래스, 아니면 Enumerator 클래스예요.
a = []
(1..5).step {|element| a.push(element) } # => 1..5
a # => [1, 2, 3, 4, 5]
a = []
(1..5).step(2) {|element| a.push(element) } # => 1..5
a # => [1, 3, 5]
e = (1..5).step(2) # => ((1..5).step(2))
e.class # => Enumerator::ArithmeticSequence
to_a → array (entries의 원본)
self의 요소들을 배열로 반환해요. entries로도 불려요.
to_s → string
self의 문자열 표현을 반환해요(to_s 사용). inspect와 다를 수 있어요.
to_json(*args)
self를 나타내는 JSON 문자열을 반환해요:
require 'json/add/range'
(1..4).to_json # => "{\"json_class\":\"Range\",\"a\":[1,4,false]}"