Ruby入門中です。今日はStructクラスについて調べました。
Structクラスとは
一言で言うと、クラスより簡単に構造体を作成する仕組みです。
docs.ruby-lang.org
Structクラスの特徴
定義が簡単
Struct.newで引数に渡したシンボルに対応するメンバとアクセサメソッドを持つクラスを生成します。
# 気温情報を持つ構造化データ Temperature = Struct.new(:date, :high, :low) temp = Temperature.new('2018/01/01', 8.0, 0.5) temp.high # => 8.0
ブロックでメソッドも定義可能
ブロックを利用することでメソッドを定義することも可能です。
Temperature = Struct.new(:date, :high, :low) do def mean (high + low) / 2.0 end end
Structクラスの使いどころ
構造化データを用意する際、Hash、Class、Structあたりが選択肢となります。
以下のような時にStructクラスを利用すると良さそうです。
- ちょっとした構造化データが必要だが、クラス作るほどではない
- 利用箇所が限定的
- その構造化データが今後拡張される想定がない(継承、ミックスインなどしない)
- Hashのように、メンバが動的に変更されたくない
- 動的に変更する構造化データが欲しい場合、OpenStructクラスも用意されている
HashとStructの比較サンプルソース
以下、Hashを利用した場合とStructを利用した場合の比較サンプルソースです。
require 'csv' # --- Hashを利用した年間気温クラス class AnnualWeatherWithHash def initialize(file_name) @readings = [] CSV.foreach(file_name, headers: true) do |row| @readings << { date: Date.parse(row[2]), high: row[10].to_f, low: row[11].to_f, } end end # 平均気温 def mean return 0.0 if @readings.size.zero? total = @readings.reduce(0.0) do |sum, reading| sum + (reading[:high] + reading[:low]) / 2.0 end total / @readings.size.to_f end end # --- Structを利用した年間気温クラス class AnnualWeatherWithStruct # Structを作成しReading定数に保持 Reading = Struct.new(:date, :high, :low) do def mean # ブロックでメソッド定義することも可能 (high + low) /2.0 end end def initialize(file_name) @readings = [] CSV.foreach(file_name, headers: true) do |row| # Reading定数をクラスのように利用することができる @readings << Reading.new(Date.parse(row[2]), row[10].to_f, row[11].to_f) end end # 平均気温 def mean return 0.0 if @readings.size.zero? total = @readings.reduce(0.0) { |sum, reading| sum + reading.mean } total / @readings.size.to_f end end
参考
- Effective Ruby (項目10: 構造化データにはHashではなくStructを使おう)