サブクラス

クラスの親子関係

Rubyのクラスには親子関係があります。 あるクラスの子をサブクラスといいます。 サブクラスは複数作ることができますが、親クラス(スーパークラス)を子から作ることはできません。 したがって、この親子関係はツリー状になります。

すべてのクラスの祖先はBasicObjectというクラスです。 あるクラスがどういう祖先を持っているかはancestorsメソッドで分かります。

p String.ancestors

これを実行すると

[String, Comparable, Object, Kernel, BasicObject]

と表示されます。 左から右へ「子から親」の順に並んでいます。 このうちComparableとKernelはクラスではなくモジュールです。 モジュールについては別の記事で説明します。 クラスだけの親子で言えば

String => Object => BasicObject

ということになります。 以下では子クラスを「サブクラス」、親クラスを「スーパークラス」と呼びます。

サブクラスはスーパークラスの性質、例えばメソッド、インスタンス変数を受け継ぎます。

  • サブクラスで使えるメソッドは、「スーパークラスのメソッド」「サブクラス独自のメソッド」
  • サブクラスのメソッドでは「スーパークラスのメソッドで定義されたインスタンス変数」「サブクラスのメソッドで定義されたインスタンス変数」を参照できる

親から子になるにつれ、より個別的、具体的なクラスになっていきます。

サブクラスの定義

サブクラスの定義は<をクラス名の後につけ、さらにスーパークラスを置きます。

class A < B
end

この例ではAがBのサブクラス、BがAのスーパークラスです。 BはRubyが既に定義しているクラスでも、ユーザが新たに定義したクラスでも構いません。

サンプルデータの作成

ここではサブクラスの例として統計のサンプルデータの集合を表すクラスを作ってみましょう。

今回は都道府県別人口のデータを処理するクラスを作ってみます。 データは独立行政法人統計センターのSSDSE(教育用標準データセット)から取ってきました。

https://www.nstac.go.jp/use/literacy/ssdse/

2022/9/22の時点では「SSDSE-E-2022v2」(2022年第2版)が最新です。

データの中から都道府県名と人口をセットにして、ヒア・ドキュメントの文字列にしました。

北海道 5224614
青森県 1237984
... ...

都道府県と人口の間はタブで区切られています。 このデータをハッシュにして取り出すには

  • 改行を区切りとして各行を要素とする配列にする
  • 各要素をタブを区切りとして、都道府県名と人口の2要素の配列にする
  • その2要素の配列を文字列からシンボル、整数に変更する
  • 配列をハッシュに変換する

それぞれの項目はメソッドで実現できます。 全体として連続したメソッドになりますが、これを「メソッド・チェーン」ということもあります。 ここまでをプログラムにしましょう。 少し長くなりますが、全都道府県の人口データもプログラムリストに入れておきます。

d = <<EOS
北海道	5224614
青森県	1237984
岩手県	1210534
宮城県	2301996
秋田県	959502
山形県	1068027
福島県	1833152
茨城県	2867009
栃木県	1933146
群馬県	1939110
埼玉県	7344765
千葉県	6284480
東京都	14047594
神奈川県	9237337
新潟県	2201272
富山県	1034814
石川県	1132526
福井県	766863
山梨県	809974
長野県	2048011
岐阜県	1978742
静岡県	3633202
愛知県	7542415
三重県	1770254
滋賀県	1413610
京都府	2578087
大阪府	8837685
兵庫県	5465002
奈良県	1324473
和歌山県	922584
鳥取県	553407
島根県	671126
岡山県	1888432
広島県	2799702
山口県	1342059
徳島県	719559
香川県	950244
愛媛県	1334841
高知県	691527
福岡県	5135214
佐賀県	811442
長崎県	1312317
熊本県	1738301
大分県	1123852
宮崎県	1069576
鹿児島県	1588256
沖縄県	1467480
EOS

d = d.split(/\n/)\
     .map{|s| s.split(/\t/)}\
     .map{|a| [a[0].to_sym, a[1].to_i]}\
     .to_h

最後から2〜4行目の最後にバックスラッシュがあります。 これは改行を取り消すためのもので、次の行も同じ一行にくっつけるという意味です。 Rubyでは改行が空白よりも強い区切り(文の区切り)であるので、これをつけておきます。 ただし、これは一般論であり、上記のようなメソッドチェーンではバックスラッシュを取り去ってもRubyは同じように解釈してくれます。 好みの問題ですが、バックスラッシュをつけるほうが文のつながりを意識できて良いと思います。 to_hメソッドは配列をハッシュに変換します。 変換するには配列の内容がハッシュに対応するものでなければなりません。 詳細はRubyのドキュメントのArrayを参照してください。

結果としてdは

d = {:北海道=>5224614, :青森県=>1237984, ... ... ... , :沖縄県=>1467480}

となります。

Statクラス

ハッシュのサブクラスStatを統計処理のために作ります。 Statの中身はハッシュと同じですが、次のようなメソッドを作ります。

  • 値の検索(キーと値の組からなるハッシュを返す)
  • 最大の値をもつ組の検索
  • 最小の値をもつ組の検索
  • 平均値の算出
  • 昇順ソート
  • 並びを逆順にする

最初の3つは検索結果をStatオブジェクトで返します。 平均値は小数点以下第1位までに丸めた実数を返します。 最後の2つは並び替えられたStatオブジェクトを返します。

これらの機能はハッシュにはないものです。

class Stat < Hash
  def initialize h={}
    super()
    update(h)
  end
  def find_by_value(v)
    Stat.new(select{|key, value| value == v})
  end
  def max
    m = map{|k,v| v}.max
    find_by_value(m)
  end
  def min
    m = map{|k,v| v}.min
    find_by_value(m)
  end
  def average
    (map{|k,v| v}.sum.to_f/size).round(1)
  end
  def sort
    Stat.new(super{|a,b| a[1]<=>b[1]}.to_h)
  end
  def reverse
    Stat.new(to_a.reverse.to_h)
  end
end

このプログラムで注意真ければならないのはselfという特別な変数(疑似変数)です。 selfは「その」オブジェクト自身を表します。

def abc
  self.clear
  clear
end

メソッドabcの定義の中のselfとは、次のようなオブジェクトです。 例えばそのオブジェクトが変数xxに代入されていて、xx.abcが実行されたとき、selfは変数xxの指すオブジェクトになります。 同じクラスの別のオブジェクトが変数yyに代入されていて、yy.abcが実行されたときはselfyyの指すオブジェクトになります。

ですので、selfは固定されたオブジェクトではなく、メソッドabcが呼ばれたときのabcが属するオブジェクトになります。 このように、メソッドにはかならずそのメソッドとセットになったオブジェクトがあり、そのオブジェクトをメソッドのレシーバーとも言います。 ですから、「selfはメソッドabcのレシーバーを表す」とも言えます。

1行目はselfを空のハッシュにします。 2行目はレシーバーが書かれていません。 メソッド名だけです。 「メソッド名だけでメソッドが呼ばれたときのレシーバーは、selfとする」という約束があります。 ですから2行目と1行目は同じ結果をもたらします。

それでは、Statクラスの定義を説明しましょう。 クラス定義の中にはレシーバが省略されたメソッドが多数出てきますので注意してください。

  • StatはHashのサブクラス(class Stat < Hashの部分がそれを表す)
  • インスタンスを生成するStat.newが呼ばれたとき、initializeメソッドが初期化をする。 h={}というパラメータがあるので、newメソッドには引数をつけることができる。 引数が与えられなかったときのデフォルト値は{}、つまり空のハッシュ。 super()はこのメソッドのスーパークラスにおける同名のメソッドを呼び出す。 つまりHashのinitializeメソッドが呼び出される。 したがって、super()は空のハッシュ・インスタンスを生成、初期化する。 updateはレシーバが省略されたメソッドなので、selfをレシーバとして実行される。 updateはHashのメソッドで、引数を破壊的に付け加える。 以上から、Stat.newにハッシュが引数で与えられたときは、そのハッシュを中身とするStatオブジェクトが生成される。 引数がなければ空のStatオブジェクトが生成される
  • find_by_valueにはパラメータvがある。 vはハッシュの値(都道府県データでは人口を表す整数)のどれかであることが期待されている。 その値をもつキーと値の対からなるStatオブジェクトを返す。 selectメソッドはブロックが真になるような対だけからなるハッシュを返す
  • maxは、まず値だけの配列をmapメソッドで作り、その最大値を(配列の)maxオブジェクトで取り出す。 最後にfind_by_valueでStatオブジェクトを返す。
  • minは同じことを最小値について行う
  • averageでは、Statの値だけからなる配列を作り、その合計を計算し、実数に変換、要素数で割って平均を求め、最後にroundで小数点以下一位に丸める
  • sortでは、Hashのsortメソッドをsuperで呼び出し、大小をブロックで評価してソートする。
  • reverseでは、ハッシュを配列に変換(各「キーと値」はその2つを要素とする配列に変換される)、reverseメソッドで逆順にし、ハッシュに戻す

説明は長くなってしまいましたが、これはメソッドチェーンでたくさんのことをコンパクトにプログラムしたからです。 逆にいえば、「Rubyでは短いプログラムで多くのことを実行できる」ということになります。

プログラムの実行

データの定義、Statクラスの定義に続けて、次のプログラムを書き加えます。

s = Stat.new(d)
p s.find_by_value(7344765)
p s.find_by_value(0)
p s.max
p s.min
p s.average
p s.sort
p s.sort.reverse

実行してみましょう。

{:埼玉県=>7344765}
{}
{:東京都=>14047594}
{:鳥取県=>553407}
2683959.6
{:鳥取県=>553407, :島根県=>671126, ... ... ... , :東京都=>14047594}
{:東京都=>14047594, :神奈川県=>9237337, ... ... ... , :鳥取県=>553407}
  • 都道府県別人口のデータをハッシュにしたdを用いてStatオブジェクトsを生成
  • 人口7344765の県を検索すると、埼玉県であった
  • 人口0の県を検索すると、そのような県はないので、空のStatオブジェクトが返される
  • 人口最大は東京都
  • 人口最小は鳥取県
  • 人口の平均値は2683959.6人
  • 人口の少ない順に並べると鳥取県・・・・東京都
  • それを逆順にし、人口の多い順に並べると東京都・・・鳥取県

クラスを作ったことにより、これらの統計処理がメソッドひとつで実行できるようになりました。 このクラスは都道府県人口以外の統計処理にも使えます。

また、標準偏差やヒストグラム(グラフィックの手段が必要)などもメソッドで用意すればもっと実用的になるでしょう。

今回はサブクラスを取り上げました。 StatクラスはHashのすべてのメソッドを使うことができるので、一から作るよりも少ない労力で作成することができました。 このようにサブクラスの仕組み(これを継承ともいう)はプログラムの資産を活かすことにつながるわけです。

2023

単項マイナスと構文解析

1 minute read

単項マイナスとは 単項マイナスと括弧 括弧なし単項マイナスを許容する場合のBNF calcの場合

Raccライブラリと構文解析

3 minute read

パーサ・ジェネレータとは 少し複雑な文法 四則(加減乗除)計算のBNF Racc で実装 クラス定義、BNFの記述部分 ヘッダー、インナー、フッター コンパイルと実行 演算子の優先順位と結合における左右の優先順位 まとめ

StrScanライブラリと字句解析

less than 1 minute read

StrScanライブラリのドキュメント 字句解析とは StrScanライブラリ StrScanライブラリを使った字句解析 実例

Gem

1 minute read

lbtというgemを作って公開してみた lbtはどんなgemか ファイルの配置 lbt.gemspec Rakefile gemのビルド RubyGems.orgへのアップロード 補足・・rake/gempackagetaskサブライブラリについて

Encoding

1 minute read

文字列のエンコーディングに頭を悩ませることはほとんどなくなりました。 なぜなら、どのアプリ、システムもUTF-8を使うようになったからです。 Rubyでもエンコーディングの問題が起こることはまず無いでしょう。 ですが、今回はエンコーディングの考え方を整理してみたいと思います。

Thread

less than 1 minute read

Fiberを書いたときから、次はスレッドを書こうと思っていましたが、時間がかかってしまいました。 その理由は、期待したとおりのスレッドの効果がなかったためです。 今回はそのことを書きますが、これはRubyのスレッドの抱えている問題なのか、自分のやり方が悪いのかははっきりしていません。

Fiber

1 minute read

Fiberは「ノンプリエンプティブな軽量スレッド」とRubyのマニュアルに記載されています。

RDoc

less than 1 minute read

今回はRubyプログラムから自動的にドキュメントを作成するRDocについて書きたいと思います。 私はこのことについて、エキスパートではありません。 この記事も、初心者の体験談だと考えてください。

Back to Top ↑

2022

Ruby/GTK4

5 minute read

Ruby/Gtkの記事を先日書いたときに、「これはかなり使える」という手応えを感じたので、WordBook(Railsで作った単語帳プログラム)のGTK 4版を作りました。 プログラムは「徒然なるままにRuby」のGitHubレポジトリに置いてあります。 レポジトリをダウンロードし、ディレクトリ_example/...

Shoes – Rubyとグラフィック

5 minute read

Rubyはグラフィックについて弱い印象があります。 しかし、グラフィックはデバイスに関することなので、言語そのものには直接の関係はないはずで、あるとすればライブラリです。 今後グラフィック関係のgemが開発されることに期待しましょう。

Rails7 テスト

5 minute read

前回作ったWordbook(リソースフル)のテストを書いてみます。 RailsのテストはminitestをRails用に拡張したものです。

Rails7 モデルとデータベース

2 minute read

今回はRailsにおけるデータの作成と保存、そして変更について説明します。 そのベースになるモデルとデータベースの話から始め、appendとchangeの動作について詳しく説明します。

Rails7とBootstrap

2 minute read

一般に、HTMLは文書の構造を表し、CSSはその体裁(見栄え)を表します。 Railsは最終的にCSSを含むHTML文書を出力するので、この2つについての理解が必須です。 この記事ではとくにCSSの人気ライブラリであるBootstrapを紹介します。 BootstrapはJavascriptも含んでいます。

Rails7のインストール

2 minute read

Rubyの最も人気のあるアプリケーションであるRuby on Railsを取り上げようと思い、書き始めました。 予想してはいましたが、相当な分量になってしまいました。 そのため、何回かに分けて記事にすることにします。 また、対象となる読者のレベルをどうしようかと考えましたが、「徒然Ruby」が基礎的な内容から始ま...

GemとBundler

1 minute read

Rubyのライブラリ管理システムのRubygemsとコマンドgemおよびbundlerについて説明します。

minitest(3)モックの詳細

2 minute read

minitestについて連続して2回書いてきました。 「minitestはドキュメントが少ない」という人がいますが、私も同感です。 例えば、モックとスタブの説明も少ないです。 そこで、今回はmock.rbのソースコードを参考に、モックの私的ドキュメントを書いてみました。 あくまで私個人の考えであり、minites...

minitest(1)テストとは

2 minute read

アプリ作成の記事でminitestを使いました。 今回はminitestについて、また一般にテストについて、私の考えを書こうと思います。

public、private、protected

2 minute read

今回はメソッドの呼び出し制限ついて説明します。 呼び出し制限にはpublic、private、protectedの3つがあります。

アプリ制作、インストール、テスト

1 minute read

2023/10/29 追記:この記事は新しく書き直しました。 古い記事で使っていたGitHubのCalcが大幅にアップデートされたためです。 そこで、この記事に合うようなプログラムsimple_calcを新たに作りました。 このプログラムは本レポジトリの_example/simple_calcにあります。

case文

2 minute read

if〜elsif〜・・・〜else〜endは皆さん良く使うでしょうか? これは場合分けで良く使われる方法です。 これと同様の制御構造にcase文があります。 Cのswitch文に似ていますが、より強力な機能を持っています。 if-else-endよりも高い能力があるといえます。

Lambda

1 minute read

Procオブジェクトを生成するメソッドlambdaについて説明します。

Proc オブジェクト

2 minute read

今回はブロックを一般化したオブジェクトProcを説明します。

モジュール

less than 1 minute read

モジュールには名前空間とミックスイン(Mix-in)の2つの機能があります。 ここではミックスインについて説明します。

Kernelモジュール

less than 1 minute read

Kernelモジュールのメソッドはどこでも使うことができます。 そのメソッドの中には便利で有用なものが多いです。

便利なメソッド

1 minute read

ここでは私が便利だと思ったメソッドを紹介します。

文字列と正規表現

3 minute read

文字列は最も使うオブジェクトのひとつです。 特にウェブ・アプリケーションでは、コンテンツだけでなくHTMLのタグやCSSを含めすべてが文字列です。 Rubyは文字列オブジェクトのメソッドが充実しており、またパターンマッチのための正規表現も充実しています。

配列

2 minute read

配列は、どのプログラミング言語にもあると思います。 複数の要素を一括して扱うことができるのが配列です。 Rubyの配列はメソッドが充実しているので、プログラムを効率的、機能的に書くのに役立ちます。

トップレベルのメソッド

1 minute read

今回はメソッド定義です。 メソッド定義はRubyの核心ですが、今回はトップレベルに限って説明します。 この限定によって、内容はかなり易しくなっています。

ブロックとイテレータ

less than 1 minute read

ブロックはRubyの特長です。 ブロックのおかげで記述が非常にすっきりと分かりやすくなります。 今回はブロックをイテレータの本体として使う方法を説明します。

整数

less than 1 minute read

ここではRubyの最も基本的なオブジェクトである整数について説明します。

Hello world

less than 1 minute read

「徒然なるままに」をネットで調べてみると、「することもなく、手持無沙汰なのにまかせてという意味」とありました。 まさに、自分の現状を言い当てた言葉。 しかも、ブログに書くネタもなかなか思いつかない日々。

Back to Top ↑