Struct 클래스
Struct 클래스
값을 저장하고 꺼내는 간단한 클래스를 만들 때 아주 편한 게 Struct예요. "이름 붙은 필드 몇 개 가진 값 객체"를 한 줄로 만들어 주죠.
출처: Ruby 4.0 API
본문
클래스 Struct는 값을 저장하고 가져올 수 있는 간단한 클래스를 만드는 편리한 방법을 제공해요.
이 예시는 Struct의 하위 클래스 Struct::Customer를 만들어요. 첫 인자인 문자열은 하위 클래스의 이름이고, 나머지 심볼 인자들이 새 하위 클래스의 멤버(member) 를 결정해요.
Customer = Struct.new('Customer', :name, :address, :zip)
Customer.name # => "Struct::Customer"
Customer.class # => Class
Customer.superclass # => Struct
각 멤버에는 값 저장·조회를 위한 writer, reader 두 메서드가 대응돼요:
methods = Customer.instance_methods false
methods # => [:zip, :address=, :zip=, :address, :name, :name=]
하위 클래스의 인스턴스는 ::new 메서드로 만들고 멤버에 값을 할당할 수 있어요:
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
joe # => #<struct Struct::Customer name="Joe Smith", address="123 Maple, Anytown NC", zip=12345>
멤버 값은 이렇게 다룰 수 있어요:
joe.name # => "Joe Smith"
joe.name = 'Joseph Smith'
joe.name # => "Joseph Smith"
이렇게도 되는데, 멤버 이름을 문자열로 쓰든 심볼로 쓰든 상관없는 걸 볼 수 있어요:
joe[:name] # => "Joseph Smith"
joe[:name] = 'Joseph Smith, Jr.'
joe['name'] # => "Joseph Smith, Jr."
Struct::new를 참고하세요.
여기 뭐가 있나 (What's Here)
먼저 다른 데 있는 것부터요. 클래스 Struct는:
- 클래스
Object에서 상속받아요. - 수십 개의 추가 메서드를 제공하는
Enumerable모듈을 포함해요.
불변(immutable) 값 객체를 정의하는 데 쓰이는 Data도 함께 보세요. Data는 비슷하지만 더 엄격한 개념이에요.
여기서 Struct 클래스가 제공하는 메서드는 이렇게 나뉘어요:
- Struct 하위 클래스 생성
- 조회
- 비교
- 가져오기
- 할당
- 반복
- 변환
Struct 하위 클래스 생성 메서드
::new:Struct의 새 하위 클래스를 반환해요.
조회 메서드
hash: 정수 해시 코드를 반환해요.size(length의 별칭): 멤버의 개수를 반환해요.
비교 메서드
==: 주어진 객체가self와 같은지 여부를 반환해요. 멤버 값 비교는==를 써요.eql?: 주어진 객체가self와 같은지 여부를 반환해요. 멤버 값 비교는eql?을 써요.
가져오기 메서드
[]: 주어진 멤버 이름에 연결된 값을 반환해요.to_a(values,deconstruct의 별칭):self의 멤버 값을 배열로 반환해요.deconstruct_keys: 주어진 멤버 이름들에 대한 이름/값 쌍의 해시를 반환해요.dig: 중첩 객체 안에서 주어진 멤버 이름과 추가 인자로 지정된 객체를 반환해요.members: 멤버 이름의 배열을 반환해요.select(filter의 별칭): 주어진 블록이 선택한self의 멤버 값 배열을 반환해요.values_at: 주어진 멤버 이름에 대한 값들을 담은 배열을 반환해요.
할당 메서드
[]=: 주어진 멤버 이름에 주어진 값을 할당해요.
반복 메서드
each: 주어진 블록을 각 멤버 이름과 함께 호출해요.each_pair: 주어진 블록을 각 멤버 이름/값 쌍과 함께 호출해요.
변환 메서드
inspect(to_s의 별칭):self의 문자열 표현을 반환해요.to_h:self의 멤버 이름/값 쌍의 해시를 반환해요.
Public Class Methods
StructClass::keyword_init? → true or falsy value
클래스가 keyword_init: true로 초기화됐다면 true를 반환해요. 그렇지 않으면 nil 또는 false를 반환해요.
예시:
Foo = Struct.new(:a)
Foo.keyword_init? # => nil
Bar = Struct.new(:a, keyword_init: true)
Bar.keyword_init? # => true
Baz = Struct.new(:a, keyword_init: false)
Baz.keyword_init? # => false
StructClass::members → array_of_symbols
Struct 자손의 멤버 이름을 배열로 반환해요:
Customer = Struct.new(:name, :address, :zip)
Customer.members # => [:name, :address, :zip]
new(*member_names, keyword_init: nil) { |Struct_subclass| ... } → Struct_subclass
Struct.new는 Struct의 새 하위 클래스를 반환해요. 새 하위 클래스는:
- 익명일 수도 있고,
class_name으로 주어진 이름을 가질 수도 있어요. member_names로 주어진 멤버들을 가질 수 있어요.- 일반 인자 또는 키워드 인자로 초기화할 수 있어요.
새 하위 클래스는 자기 자신만의 ::new 메서드를 가져요:
Foo = Struct.new('Foo', :foo, :bar) # => Struct::Foo
f = Foo.new(0, 1) # => #<struct Struct::Foo foo=0, bar=1>
클래스 이름 (Class Name)
문자열 인자 class_name을 주면 Struct::class_name이라는 이름의 새 하위 클래스를 반환해요:
Foo = Struct.new('Foo', :foo, :bar) # => Struct::Foo
Foo.name # => "Struct::Foo"
Foo.superclass # => Struct
문자열 인자 class_name이 없으면 익명 하위 클래스를 반환해요:
Struct.new(:foo, :bar).name # => nil
블록 (Block)
블록을 주면 만들어진 하위 클래스를 블록에 넘겨줘요:
Customer = Struct.new('Customer', :name, :address) do |new_class|
p "The new subclass is #{new_class}"
def greeting
"Hello #{name} at #{address}"
end
end # => Struct::Customer
dave = Customer.new('Dave', '123 Main')
dave # => #<struct Struct::Customer name="Dave", address="123 Main">
dave.greeting # => "Hello Dave at 123 Main"
Struct.new에서 나온 출력:
"The new subclass is Struct::Customer"
멤버 이름 (Member Names)
심볼 인자 member_names가 새 하위 클래스의 멤버를 결정해요:
Struct.new(:foo, :bar).members # => [:foo, :bar]
Struct.new('Foo', :foo, :bar).members # => [:foo, :bar]
새 하위 클래스에는 member_names에 대응하는 인스턴스 메서드가 있어요:
Foo = Struct.new('Foo', :foo, :bar)
Foo.instance_methods(false) # => [:foo, :bar, :foo=, :bar=]
f = Foo.new # => #<struct Struct::Foo foo=nil, bar=nil>
f.foo # => nil
f.foo = 0 # => 0
f.bar # => nil
f.bar = 1 # => 1
f # => #<struct Struct::Foo foo=0, bar=1>
싱글턴 메서드 (Singleton Methods)
Struct.new가 반환한 하위 클래스에는 이런 싱글턴 메서드가 있어요:
::new가 하위 클래스의 인스턴스를 만들어요:
Foo.new # => #<struct Struct::Foo foo=nil, bar=nil>
Foo.new(0) # => #<struct Struct::Foo foo=0, bar=nil>
Foo.new(0, 1) # => #<struct Struct::Foo foo=0, bar=1>
Foo.new(0, 1, 2) # Raises ArgumentError: struct size differs
키워드 인자로 초기화:
Foo.new(foo: 0) # => #<struct Struct::Foo foo=0, bar=nil>
Foo.new(foo: 0, bar: 1) # => #<struct Struct::Foo foo=0, bar=1>
Foo.new(foo: 0, bar: 1, baz: 2)
# Raises ArgumentError: unknown keywords: baz
:inspect가 하위 클래스의 문자열 표현을 반환해요:
Foo.inspect
# => "Struct::Foo"
::members가 멤버 이름의 배열을 반환해요:
Foo.members # => [:foo, :bar]
키워드 인자 (Keyword Argument)
기본적으로 새 하위 클래스의 인스턴스 초기화 인자는 위치 인자와 키워드 인자 모두 쓸 수 있어요.
선택적 키워드 인자 keyword_init:로 한 종류의 인자만 받도록 강제할 수 있어요:
KeywordsOnly = Struct.new(:foo, :bar, keyword_init: true)
KeywordsOnly.new(bar: 1, foo: 0)
# => #<struct KeywordsOnly foo=0, bar=1>
KeywordsOnly.new(0, 1)
# Raises ArgumentError: wrong number of arguments
PositionalOnly = Struct.new(:foo, :bar, keyword_init: false)
PositionalOnly.new(0, 1)
# => #<struct PositionalOnly foo=0, bar=1>
PositionalOnly.new(bar: 1, foo: 0)
# => #<struct PositionalOnly foo={:foo=>1, :bar=>2}, bar=nil>
# Note that no error is raised, but arguments treated as one hash value
# Same as not providing keyword_init:
Any = Struct.new(:foo, :bar, keyword_init: nil)
Any.new(foo: 1, bar: 2)
# => #<struct Any foo=1, bar=2>
Any.new(1, 2)
# => #<struct Any foo=1, bar=2>
Public Instance Methods
self == other → true or false
다음이 모두 참일 때만 true를, 그렇지 않으면 false를 반환해요:
other.class == self.class.- 각 멤버 이름
name에 대해other.name == self.name.
예시:
Customer = Struct.new(:name, :address, :zip)
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
joe_jr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
joe_jr == joe # => true
joe_jr[:name] = 'Joe Smith, Jr.'
# => "Joe Smith, Jr."
joe_jr == joe # => false
struct[name] → object
self에서 값을 반환해요.
심볼 또는 문자열 인자 name을 주면 명명된 멤버의 값을 반환해요:
Customer = Struct.new(:name, :address, :zip)
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
joe[:zip] # => 12345
name이 멤버 이름이 아니면 NameError를 발생시켜요.
정수 인자 n을 주면, n이 범위 안일 때 self.values[n]을 반환해요. Array의 Array Indexes를 참고하세요:
joe[2] # => 12345
joe[-2] # => "123 Maple, Anytown NC"
n이 범위를 벗어나면 IndexError를 발생시켜요.
struct[name] = value → value
멤버에 값을 할당해요.
심볼 또는 문자열 인자 name을 주면 명명된 멤버에 주어진 value를 할당하고 value를 반환해요:
Customer = Struct.new(:name, :address, :zip)
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
joe[:zip] = 54321 # => 54321
joe # => #<struct Customer name="Joe Smith", address="123 Maple, Anytown NC", zip=54321>
name이 멤버 이름이 아니면 NameError를 발생시켜요.
정수 인자 n을 주면, n이 범위 안일 때 n 번째 멤버에 주어진 value를 할당해요. Array의 Array Indexes를 참고하세요:
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
joe[2] = 54321 # => 54321
joe[-3] = 'Joseph Smith' # => "Joseph Smith"
joe # => #<struct Customer name="Joseph Smith", address="123 Maple, Anytown NC", zip=54321>
n이 범위를 벗어나면 IndexError를 발생시켜요.
deconstruct_keys(array_of_names) → hash
주어진 멤버 이름들에 대한 이름/값 쌍의 해시를 반환해요.
Customer = Struct.new(:name, :address, :zip)
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
h = joe.deconstruct_keys([:zip, :address])
h # => {:zip=>12345, :address=>"123 Maple, Anytown NC"}
array_of_names가 nil이면 모든 이름과 값을 반환해요:
h = joe.deconstruct_keys(nil)
h # => {:name=>"Joseph Smith, Jr.", :address=>"123 Maple, Anytown NC", :zip=>12345}
dig(name, *identifiers) → object
중첩 객체들 사이에서 객체를 찾아 반환해요. 중첩 객체는 여러 클래스의 인스턴스일 수 있어요. Dig Methods를 참고하세요.
심볼 또는 문자열 인자 name을 주면 name과 identifiers로 지정된 객체를 반환해요:
Foo = Struct.new(:a)
f = Foo.new(Foo.new({b: [1, 2, 3]}))
f.dig(:a) # => #<struct Foo a={:b=>[1, 2, 3]}>
f.dig(:a, :a) # => {:b=>[1, 2, 3]}
f.dig(:a, :a, :b) # => [1, 2, 3]
f.dig(:a, :a, :b, 0) # => 1
f.dig(:b, 0) # => nil
정수 인자 n을 주면 n과 identifiers로 지정된 객체를 반환해요:
f.dig(0) # => #<struct Foo a={:b=>[1, 2, 3]}>
f.dig(0, 0) # => {:b=>[1, 2, 3]}
f.dig(0, 0, :b) # => [1, 2, 3]
f.dig(0, 0, :b, 0) # => 1
f.dig(:b, 0) # => nil
each { |value| ... } → self
주어진 블록을 각 멤버의 값과 함께 호출하고 self를 반환해요:
Customer = Struct.new(:name, :address, :zip)
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
joe.each {|value| p value }
Output:
"Joe Smith"
"123 Maple, Anytown NC"
12345
블록이 없으면 Enumerator를 반환해요. 관련: each_pair.
each_pair { |(name, value)| ... } → self
주어진 블록을 각 멤버 이름/값 쌍과 함께 호출하고 self를 반환해요:
Customer = Struct.new(:name, :address, :zip) # => Customer
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
joe.each_pair {|(name, value)| p "#{name} => #{value}" }
Output:
"name => Joe Smith"
"address => 123 Maple, Anytown NC"
"zip => 12345"
블록이 없으면 Enumerator를 반환해요. 관련: each.
eql?(other) → true or false
다음이 모두 참일 때만 true를, 그렇지 않으면 false를 반환해요:
other.class == self.class.- 각 멤버 이름
name에 대해other.name.eql?(self.name).
Customer = Struct.new(:name, :address, :zip)
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
joe_jr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
joe_jr.eql?(joe) # => true
joe_jr[:name] = 'Joe Smith, Jr.'
joe_jr.eql?(joe) # => false
관련: Object#==.
hash → integer
self의 정수 해시 값을 반환해요.
같은 클래스의 같은 내용을 가진 두 struct는 같은 해시 코드를 가져요(Struct#eql?로도 비교돼요):
Customer = Struct.new(:name, :address, :zip)
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
joe_jr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
joe.hash == joe_jr.hash # => true
joe_jr[:name] = 'Joe Smith, Jr.'
joe.hash == joe_jr.hash # => false
관련: Object#hash.
inspect → string
self의 문자열 표현을 반환해요:
Customer = Struct.new(:name, :address, :zip) # => Customer
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
joe.inspect # => "#<struct Customer name=\"Joe Smith\", address=\"123 Maple, Anytown NC\", zip=12345>"
members → array_of_symbols
self에서 멤버 이름을 배열로 반환해요:
Customer = Struct.new(:name, :address, :zip)
Customer.new.members # => [:name, :address, :zip]
관련: to_a.
select { |value| ... } → array
블록을 주면 블록이 truthy 값을 반환하는 self의 값 배열을 반환해요:
Customer = Struct.new(:name, :address, :zip)
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
a = joe.select {|value| value.is_a?(String) }
a # => ["Joe Smith", "123 Maple, Anytown NC"]
a = joe.select {|value| value.is_a?(Integer) }
a # => [12345]
블록이 없으면 Enumerator를 반환해요.
size → integer
멤버의 개수를 반환해요.
Customer = Struct.new(:name, :address, :zip)
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
joe.size #=> 3
to_a → array
self의 값을 배열로 반환해요:
Customer = Struct.new(:name, :address, :zip)
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
joe.to_a # => ["Joe Smith", "123 Maple, Anytown NC", 12345]
관련: members.
to_h → hash
각 멤버의 이름과 값을 담은 해시를 반환해요:
Customer = Struct.new(:name, :address, :zip)
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
h = joe.to_h
h # => {:name=>"Joe Smith", :address=>"123 Maple, Anytown NC", :zip=>12345}
블록을 주면 각 이름/값 쌍마다 호출돼요. 블록은 반환 해시에서 키/값 쌍이 되는 2-요소 배열을 반환해야 해요:
h = joe.to_h{|name, value| [name.upcase, value.to_s.upcase]}
h # => {:NAME=>"JOE SMITH", :ADDRESS=>"123 MAPLE, ANYTOWN NC", :ZIP=>"12345"}
블록이 적절하지 않은 값을 반환하면 ArgumentError를 발생시켜요.
values_at(*integers) → array
self에서 값들의 배열을 반환해요.
정수 인자 integers를 주면 integers 중 하나로 주어지는 각 값을 담은 배열을 반환해요:
Customer = Struct.new(:name, :address, :zip)
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
joe.values_at(0, 2) # => ["Joe Smith", 12345]
joe.values_at(2, 0) # => [12345, "Joe Smith"]
joe.values_at(2, 1, 0) # => [12345, "123 Maple, Anytown NC", "Joe Smith"]
joe.values_at(0, -3) # => ["Joe Smith", "Joe Smith"]
integers 중 하나라도 범위를 벗어나면 IndexError를 발생시켜요. Array의 Array Indexes를 참고하세요.
정수 범위 인자 integer_range를 주면 범위의 각 원소가 주는 각 값을 담은 배열을 반환해요. 구조체보다 큰 범위 원소는 nil로 채워요:
joe.values_at(0..2)
# => ["Joe Smith", "123 Maple, Anytown NC", 12345]
joe.values_at(-3..-1)
# => ["Joe Smith", "123 Maple, Anytown NC", 12345]
joe.values_at(1..4) # => ["123 Maple, Anytown NC", 12345, nil, nil]
범위 원소 중 음수이면서 범위를 벗어난 것이 있으면 RangeError를 발생시켜요. Array의 Array Indexes를 참고하세요.