組込ライブラリ(Struct)
構造体。
成果物
情報源
Struct
構造体クラス。Struct.new はこのクラスのサブクラスを新たに生成します。
個々の構造体はサブクラスから Struct.new を使って生成します。個々の構造体サブクラスでは構造体のメンバに対するアクセスメソッドが定義されています。
メンバ抜粋
特異メソッド
[] members new
インスタンスメソッド
== [] []= dig each each_pair eql? equal? filter hash inspect length members select size to_a to_h to_s values values_at
new
new(*args, keyword_init: false) -> Class new(*args, keyword_init: false) {|Class| block } -> Class
Struct クラスに新しいサブクラスを作って、それを返します。
え、Class
と同じなの?
サブクラスでは構造体のメンバに対するアクセスメソッドが定義されています。
dog = Struct.new("Dog", :name, :age) fred = dog.new("fred", 5) fred.age = 6 printf "name:%s age:%d", fred.name, fred.age #=> "name:fred age:6" を出力します
実装の都合により、クラス名の省略は後づけの機能でした。メンバ名に String を指定できるのは後方互換性のためだと考えた方が良いでしょう。したがって、メンバ名は Symbol で指定するのが無難です。
引数 | 概要 |
---|---|
args |
構造体を定義するための可変長引数。String または Symbol を指定する |
keyword_init |
true を指定するとキーワード引数で初期化する構造体を定義する |
第一引数が String の場合
args[0] が String の場合、クラス名になるので、大文字で始まる必要があります。つまり、以下のような指定はエラーになります。
p Struct.new('foo', 'bar') # => -:1:in `new': identifier foo needs to be constant (NameError)
また args[1..-1] は、Symbol か String で指定します。
p Struct.new("Foo", :foo, :bar) # => Struct::Foo
同じ名前の構造体を生成したら以下警告が出た。
warning: redefining constant Struct::Foo
第一引数が Symbol の場合
args[0] が Symbol の場合、生成した構造体クラスは名前の無いクラスになります。名前の無いクラスは最初に名前を求める際に代入されている定数名を検索し、見つかった定数名をクラス名とします。
Foo = Struct.new(:foo, :bar) p Foo # => Foo
わかりにくい。第一引数は必ず構造体名にしてくれたらいいのに。なんだよ名前のない構造体って。
ブロックを指定した場合
Struct.new にブロックを指定した場合は定義した Struct をコンテキストにブロックを評価します。また、定義した Struct はブロックパラメータにも渡されます。
Customer = Struct.new(:name, :address) do def greeting "Hello #{name}!" end end Customer.new("Dave", "123 Main").greeting # => "Hello Dave!"
メソッドも実装できるらしい。Struct
はattr_accessor
を省略できるClass
定義みたいなもんか。
Structをカスタマイズする場合はこの方法が推奨されます。無名クラスのサブクラスを作成する方法でカスタマイズする場合は無名クラスが使用されなくなってしまうことがあるためです。
意味わからん。
keyword_init: true を指定した場合
キーワード引数で初期化することを想定した構造体になります。
Point = Struct.new(:x, :y, keyword_init: true) # => Point(keyword_init: true) Point.new(x: 1, y: 2) # => #<struct Point x=1, y=2> Point.new(x: 1) # => #<struct Point x=1, y=nil> Point.new(y: 2) # => #<struct Point x=nil, y=2> Point.new(z: 3) # ArgumentError (unknown keywords: z)
new(*args)
new(*args) -> Struct self[*args] -> Struct
(このメソッドは Struct の下位クラスにのみ定義されています) 構造体オブジェクトを生成して返します。
構造体のメンバの数よりも多くの引数を指定した場合に発生します。
Foo = Struct.new(:foo, :bar) foo = Foo.new(1) p foo.values # => [1, nil]
members
members -> [Symbol]
(このメソッドは Struct の下位クラスにのみ定義されています) 構造体のメンバの名前(Symbol)の配列を返します。
Foo = Struct.new(:foo, :bar) p Foo.members # => [:foo, :bar]
each_pair
メンバと値の組を返す。
Foo = Struct.new(:foo, :bar) Foo.new('FOO', 'BAR').each_pair {|m, v| p [m,v]} # => [:foo, "FOO"] # [:bar, "BAR"]
所感
メンバ変数だけならClass
定義より少しだけ楽になるかも? でも大して変わらないからClass定義だけ知っていれば良さそう。
対象環境
- Raspbierry pi 4 Model B
- Raspberry Pi OS buster 10.0 2020-08-20 ※
- bash 5.0.3(1)-release
- Ruby 3.0.2
$ uname -a Linux raspberrypi 5.10.52-v7l+ #1441 SMP Tue Aug 3 18:11:56 BST 2021 armv7l GNU/Linux