結構類別
結構類別提供一個便利的方式來建立一個簡單的類別,可以儲存和擷取值。
此範例建立結構的子類別,結構::客戶;第一個引數,字串,是子類別的名稱;其他引數,符號,決定新子類別的「成員」。
Customer = Struct.new('Customer', :name, :address, :zip) Customer.name # => "Struct::Customer" Customer.class # => Class Customer.superclass # => Struct
對應到每個成員有兩個方法,一個寫入器和一個讀取器,用來儲存和擷取值
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."
請參閱結構::new
。
此處內容¶ ↑
首先,其他地方是什麼。結構類別
另請參閱資料
,這是一個有點類似但更嚴格的概念,用於定義不可變值物件。
在這裡,結構類別提供對下列項目有用的方法
建立結構
子類別的方法¶ ↑
-
::new
:傳回結構的新子類別。
查詢方法¶ ↑
比較方法¶ ↑
擷取方法¶ ↑
-
[]
:傳回與給定成員名稱關聯的值。 -
to_a
、values
、deconstruct
:傳回self
中的成員值作為陣列。 -
deconstruct_keys
:傳回給定成員名稱的名稱/值配對雜湊。 -
dig
:傳回巢狀物件中由給定成員名稱和額外引數指定的物件。 -
members
:傳回成員名稱的陣列。 -
values_at
:傳回包含給定成員名稱值的陣列。
指定方法¶ ↑
-
[]=
:將給定值指定給給定成員名稱。
反覆方法¶ ↑
轉換方法¶ ↑
公開類別方法
如果類別使用 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
static VALUE rb_struct_s_keyword_init_p(VALUE obj) { }
傳回 Struct
後代的成員名稱作為陣列
Customer = Struct.new(:name, :address, :zip) Customer.members # => [:name, :address, :zip]
static VALUE rb_struct_s_members_m(VALUE klass) { VALUE members = rb_struct_s_members(klass); return rb_ary_dup(members); }
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
,會傳回名為 Struct::class_name
的 Struct
新子類別
Foo = Struct.new('Foo', :foo, :bar) # => Struct::Foo Foo.name # => "Struct::Foo" Foo.superclass # => Struct
若沒有字串引數 class_name
,會傳回 Struct
的新匿名子類別
Struct.new(:foo, :bar).name # => nil
區塊
若有提供區塊,建立的子類別會傳遞給區塊
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"
成員名稱
Symbol
引數 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>
單例方法
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 # Initialization with keyword arguments: 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_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>
static VALUE rb_struct_s_def(int argc, VALUE *argv, VALUE klass) { VALUE name = Qnil, rest, keyword_init = Qnil; long i; VALUE st; VALUE opt; argc = rb_scan_args(argc, argv, "0*:", NULL, &opt); if (argc >= 1 && !SYMBOL_P(argv[0])) { name = argv[0]; --argc; ++argv; } if (!NIL_P(opt)) { static ID keyword_ids[1]; if (!keyword_ids[0]) { keyword_ids[0] = rb_intern("keyword_init"); } rb_get_kwargs(opt, keyword_ids, 0, 1, &keyword_init); if (UNDEF_P(keyword_init)) { keyword_init = Qnil; } else if (RTEST(keyword_init)) { keyword_init = Qtrue; } } rest = rb_ident_hash_new(); RBASIC_CLEAR_CLASS(rest); for (i=0; i<argc; i++) { VALUE mem = rb_to_symbol(argv[i]); if (rb_is_attrset_sym(mem)) { rb_raise(rb_eArgError, "invalid struct member: %"PRIsVALUE, mem); } if (RTEST(rb_hash_has_key(rest, mem))) { rb_raise(rb_eArgError, "duplicate member: %"PRIsVALUE, mem); } rb_hash_aset(rest, mem, Qtrue); } rest = rb_hash_keys(rest); RBASIC_CLEAR_CLASS(rest); OBJ_FREEZE_RAW(rest); if (NIL_P(name)) { st = anonymous_struct(klass); } else { st = new_struct(name, klass); } setup_struct(st, rest); rb_ivar_set(st, id_keyword_init, keyword_init); if (rb_block_given_p()) { rb_mod_module_eval(0, 0, st); } return st; }
公開執行個體方法
僅在下列情況傳回 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
static VALUE rb_struct_equal(VALUE s, VALUE s2) { if (s == s2) return Qtrue; if (!RB_TYPE_P(s2, T_STRUCT)) return Qfalse; if (rb_obj_class(s) != rb_obj_class(s2)) return Qfalse; if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) { rb_bug("inconsistent struct"); /* should never happen */ } return rb_exec_recursive_paired(recursive_equal, s, s2, s2); }
傳回來自 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
中的陣列索引
joe[2] # => 12345 joe[-2] # => "123 Maple, Anytown NC"
如果 n
超出範圍,會引發 IndexError
。
VALUE rb_struct_aref(VALUE s, VALUE idx) { int i = rb_struct_pos(s, &idx); if (i < 0) invalid_struct_pos(s, idx); return RSTRUCT_GET(s, i); }
將值指定給成員。
如果給定符號或字串引數 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
在範圍內,將給定的 value
指定給第 n
個成員;請參閱 Array
中的陣列索引
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
。
VALUE rb_struct_aset(VALUE s, VALUE idx, VALUE val) { int i = rb_struct_pos(s, &idx); if (i < 0) invalid_struct_pos(s, idx); rb_struct_modify(s); RSTRUCT_SET(s, i, val); return val; }
以陣列形式傳回 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
。
傳回給定成員名稱的名稱/值配對的雜湊。
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}
static VALUE rb_struct_deconstruct_keys(VALUE s, VALUE keys) { VALUE h; long i; if (NIL_P(keys)) { return rb_struct_to_h(s); } if (UNLIKELY(!RB_TYPE_P(keys, T_ARRAY))) { rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array or nil)", rb_obj_class(keys)); } if (RSTRUCT_LEN(s) < RARRAY_LEN(keys)) { return rb_hash_new_with_size(0); } h = rb_hash_new_with_size(RARRAY_LEN(keys)); for (i=0; i<RARRAY_LEN(keys); i++) { VALUE key = RARRAY_AREF(keys, i); int i = rb_struct_pos(s, &key); if (i < 0) { return h; } rb_hash_aset(h, key, RSTRUCT_GET(s, i)); } return h; }
在巢狀物件中尋找並傳回物件。巢狀物件可能是各種類別的執行個體。請參閱 Dig 方法。
如果給定符號或字串引數 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
static VALUE rb_struct_dig(int argc, VALUE *argv, VALUE self) { rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS); self = rb_struct_lookup(self, *argv); if (!--argc) return self; ++argv; return rb_obj_dig(argc, argv, self, Qnil); }
呼叫給定的區塊,並傳入每個成員的值;傳回 self
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.each {|value| p value }
輸出
"Joe Smith" "123 Maple, Anytown NC" 12345
如果未給定區塊,傳回 Enumerator
。
相關:each_pair
。
static VALUE rb_struct_each(VALUE s) { long i; RETURN_SIZED_ENUMERATOR(s, 0, 0, struct_enum_size); for (i=0; i<RSTRUCT_LEN(s); i++) { rb_yield(RSTRUCT_GET(s, i)); } return s; }
呼叫給定的區塊,並傳入每個成員名稱/值配對;傳回 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}" }
輸出
"name => Joe Smith" "address => 123 Maple, Anytown NC" "zip => 12345"
如果未給定區塊,傳回 Enumerator
。
相關:each
。
static VALUE rb_struct_each_pair(VALUE s) { VALUE members; long i; RETURN_SIZED_ENUMERATOR(s, 0, 0, struct_enum_size); members = rb_struct_members(s); if (rb_block_pair_yield_optimizable()) { for (i=0; i<RSTRUCT_LEN(s); i++) { VALUE key = rb_ary_entry(members, i); VALUE value = RSTRUCT_GET(s, i); rb_yield_values(2, key, value); } } else { for (i=0; i<RSTRUCT_LEN(s); i++) { VALUE key = rb_ary_entry(members, i); VALUE value = RSTRUCT_GET(s, i); rb_yield(rb_assoc_new(key, value)); } } return s; }
僅在下列情況傳回 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#==
。
static VALUE rb_struct_eql(VALUE s, VALUE s2) { if (s == s2) return Qtrue; if (!RB_TYPE_P(s2, T_STRUCT)) return Qfalse; if (rb_obj_class(s) != rb_obj_class(s2)) return Qfalse; if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) { rb_bug("inconsistent struct"); /* should never happen */ } return rb_exec_recursive_paired(recursive_eql, s, s2, s2); }
如果給定區塊,傳回 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
。
傳回 self
的整數雜湊值。
同一個類別且內容相同的兩個結構將具有相同的雜湊碼(並且會使用 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
。
static VALUE rb_struct_hash(VALUE s) { long i, len; st_index_t h; VALUE n; h = rb_hash_start(rb_hash(rb_obj_class(s))); len = RSTRUCT_LEN(s); for (i = 0; i < len; i++) { n = rb_hash(RSTRUCT_GET(s, i)); h = rb_hash_uint(h, NUM2LONG(n)); } h = rb_hash_end(h); return ST2FIX(h); }
傳回 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>"
static VALUE rb_struct_inspect(VALUE s) { return rb_exec_recursive(inspect_struct, s, rb_str_new2("#<struct ")); }
傳回成員數目。
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.size #=> 3
傳回 self
的成員名稱,以陣列表示。
Customer = Struct.new(:name, :address, :zip) Customer.new.members # => [:name, :address, :zip]
相關:to_a
。
static VALUE rb_struct_members_m(VALUE obj) { return rb_struct_s_members_m(rb_obj_class(obj)); }
如果給定區塊,傳回 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
。
static VALUE rb_struct_select(int argc, VALUE *argv, VALUE s) { VALUE result; long i; rb_check_arity(argc, 0, 0); RETURN_SIZED_ENUMERATOR(s, 0, 0, struct_enum_size); result = rb_ary_new(); for (i = 0; i < RSTRUCT_LEN(s); i++) { if (RTEST(rb_yield(RSTRUCT_GET(s, i)))) { rb_ary_push(result, RSTRUCT_GET(s, i)); } } return result; }
傳回成員數目。
Customer = Struct.new(:name, :address, :zip) joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe.size #=> 3
VALUE rb_struct_size(VALUE s) { return LONG2FIX(RSTRUCT_LEN(s)); }
以陣列形式傳回 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
。
static VALUE rb_struct_to_a(VALUE s) { return rb_ary_new4(RSTRUCT_LEN(s), RSTRUCT_CONST_PTR(s)); }
傳回包含每個成員名稱和值的 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 元素陣列,其元素將成為傳回 hash 中的鍵/值對
h = joe.to_h{|name, value| [name.upcase, value.to_s.upcase]} h # => {:NAME=>"JOE SMITH", :ADDRESS=>"123 MAPLE, ANYTOWN NC", :ZIP=>"12345"}
如果區塊傳回不適當的值,則會引發 ArgumentError
。
static VALUE rb_struct_to_h(VALUE s) { VALUE h = rb_hash_new_with_size(RSTRUCT_LEN(s)); VALUE members = rb_struct_members(s); long i; int block_given = rb_block_given_p(); for (i=0; i<RSTRUCT_LEN(s); i++) { VALUE k = rb_ary_entry(members, i), v = RSTRUCT_GET(s, i); if (block_given) rb_hash_set_pair(h, rb_yield_values(2, k, v)); else rb_hash_aset(h, k, v); } return h; }
傳回 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>"
以陣列形式傳回 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
。
傳回 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。
static VALUE rb_struct_values_at(int argc, VALUE *argv, VALUE s) { return rb_get_values_at(s, RSTRUCT_LEN(s), argc, argv, struct_entry); }