単項マイナスと構文解析
単項マイナスとは 単項マイナスと括弧 括弧なし単項マイナスを許容する場合のBNF calcの場合
今回はメソッドの呼び出し制限ついて説明します。 呼び出し制限にはpublic、private、protectedの3つがあります。
英語のpublicとprivateは「公的な」「私的な」という形容詞ですが、同時に「公開の」「非公開の」という意味も持っています。 プログラム上では後者の意味でよく使われる単語です。 Rubyではprivateを「メソッドをクラス定義の外に非公開にする」ために良く用います。
# プライベートメソッド(外から参照できないメソッド)の例
class Statistic
def initialize array
@array = array
end
def show
print "合計は #{@array.sum}\n"
print "平均は #{average(@array)}\n"
print "標準偏差は #{stdev(@array)}\n"
end
private
def average(array)
(array.sum.to_f/array.size).round(1)
end
def stdev(array)
mean = average(array)
v = array.inject(0){|a,b| a+(b-mean)*(b-mean)}/array.size
Math.sqrt(v).round(1)
end
end
d = Statistic.new([5,6,7,8])
d.show
#=> 合計は 26
#=> 平均は 6.5
#=> 標準偏差は 1.1
p d.average([5,6,7,8]) #=> エラー、Privateメソッドを非関数形式で呼び出したため
p average([5,6,7,8]) #=> エラー、averageはトップレベルで未定義なため
この例ではStatisticクラスを定義しています。 このクラスは配列を引数にインスタンスを生成、初期化し、showメソッドで配列の統計量(合計、平均、標準偏差)を表示します。 クラスには3つのインスタンスメソッドshow、average、stdevが定義されていますが、クラス外部で使えるのはshowのみになっています。 最後の2行で、外部からaverageを呼び出していますが(片方はドットつき、他方はドットなし)いずれもエラーになります。 これはaverageがクラス定義の外部では呼び出せないことを示しています。 stdevも同様に外部からは呼び出せません。
これはクラス定義の中ほどにあるprivate
メソッドによって、以下の行で定義されたクラス定義内のメソッドが「private呼び出し制限」のメソッドになるためです。
「private呼び出し制限のメソッド」を短く「privateメソッド」といいますが、「メソッドの呼び出し制限を設定するためのprivateメソッド」と混乱するおそれがありますので、
文脈から注意深く判断するようにしてください。
showメソッドはprivate
メソッドの前なのでpublicメソッドになります。
クラス定義におけるデフォルトはpublicです。
averageやstdevのようにクラス定義内では使うが、外からは使うことのないメソッドは、プログラム上で良く発生します。 そのようなメソッドを外部から呼び出すと問題が発生することが予想される場合は、privateメソッドにしておくべきです。 (上記の例ではそのような問題発生はありませんが、privateメソッドの例示のために設定しました)。
さて、privateの良くある使い方を説明しましたが、実はprivateの本当の意味は「非公開」ではなく、「関数形式でしか呼び出せない」なのです。 そこで、以下では呼び出し制限の定義とメソッドの振る舞いについて解説します。
まず、話の前提として「メソッドはレシーバとセットで呼び出される」ということを確認しておきたいと思います。 次の例を見てください
class A
def intro
print "私はクラスAのインスタンスです\n"
end
end
a = A.new
a.intro #=> 私はクラスAのインスタンスです
メソッドintroがクラスAのインスタンス・メソッドとして定義されました。 そのメソッドを呼び出すには
a.intro
の形でメソッドを呼び出すこのときのインスタンスを「メソッドのレシーバ」と呼びます。
もうひとつ大事なことは「レシーバのクラスでそのメソッドを定義済みである」ということがメソッド呼び出しの前提です。 上記の例ではクラスAでintroを定義しているのでa(の指すインスタンス)をレシーバとしてintroを呼び出せるわけです。
※ 以下では「a(の指すインスタンス)」を単に「a」と書くことにします。
もし、定義されてなければエラーになります。
class A
# introを定義しない
end
a = A.new
a.intro #=> エラー(NoMethodError)
Aのサブクラス(直接の子でなくても孫から先でも良い)はインスタンス・メソッドを受け継ぐので、メソッド呼び出しが可能です。
class A
def intro
print "私はクラスAのインスタンスです\n"
end
end
class B < A
end
b = B.new
b.intro #=> 私はクラスAのインスタンスです
以上をまとめると
ということになります。
呼び出し制限にはpublic、private、protectedの3つがあり、メソッドはこの3つのいずれかを持っています。
publicメソッドは制限なしに呼び出せます。 つまり、プログラムのどこでもそのメソッドを呼び出せます。 また、クラスでメソッドを定義するとき、そのメソッドの呼び出し制限のデフォルトはpublicです。 最初の例をもう一度見てみましょう。
class A
def intro
print "私はクラスAのインスタンスです\n"
end
end
a = A.new
a.intro #=> 私はクラスAのインスタンスです
メソッドintroの呼び出し制限はデフォルトのpublicです。 introはレシーバaとセットで、クラス定義の外で呼び出すことができます。 また、クラス定義の内部で呼び出すことも可能です。
class C
def calc(x)
print "合計は#{sum(x)}、平均は#{average(x)}\n"
end
def sum(x)
x.sum
end
def average(x)
x.sum.to_f/x.size
end
end
c = C.new
c.calc([1,2,3,4]) #=> 合計は10、平均は2.5
c2 = C.new
c2.calc([1,2,3,4]) #=> 合計は10、平均は2.5
クラスCの内部では3つのメソッドcalc、sum、averageが定義されています。 そして、メソッドcalcの中では、sumとaverageの2つのメソッドが呼ばれています。 ここで注意したいのは、これらのメソッドは「インスタンス、ドット」の部分が無いということです。 つまりレシーバが指定されていません。 このように、レシーバが省略されている呼び出し形式を「関数形式」といいます。 関数形式の呼び出しでは、レシーバはあるのですが、それが書かれていないだけなのです。 そのレシーバを「デフォルトのレシーバ」と、ここでは言うことにします。
では、デフォルトのレシーバは何かと言うと、それは(外部から)calcを呼び出したときのレシーバです。
c=C.new
でクラスCのインスタンスが変数cに代入され、c.calc([1,2,3,4])
でそのインスタンスをレシーバとしてcalcが呼ばれた時点でレシーバが確定します。
つまり、レシーバは変数cの指すインスタンスです。
同様に、最終行のc2.calc([1,2,3,4])
ではc2の指すインスタンスがレシーバです。
calcメソッドを実行中にsumとaverageを関数形式で呼び出すとき、そのレシーバはcalcのレシーバを用います。
メソッド定義の中では「self」という疑似変数がデフォルトのレシーバを指します。 selfの指すインスタンスは、calcが呼ばれた時点で確定します。
プログラムの3行目は
print "合計は#{self.sum(x)}、平均は#{self.average(x)}\n"
とレシーバselfを明示して書いても同じことになります。 ですが、特に何か意図するところがなければ、selfは省略するのが普通です。
ここでの説明をまとめると
ということになります。
今までのselfの説明はメソッド定義の中に限った話でした。 selfは他の場所では何を指しているのでしょうか? 一般にselfは次のようになっています。
となります。
※ mainについてはRubyのドキュメントに記載があります。
注意が必要なのはメソッド定義の部分です。 「メソッドの定義」と「メソッドの呼び出し(または実行)」は別のことだと理解してください。 「メソッドを定義」しているときのselfと「メソッドを実行」しているときのselfは別物です。 メソッド定義には、メソッドを実行するときの振る舞いを書くのですから、selfも実行時の振る舞いが想定されなければなりません。 すなわち「selfには定義中のメソッドが実行されたときのレシーバが代入される」ということを想定してメソッド定義を書かなければなりません。
次の例で、selfが何を指すか確認してください。 表中央のselfはRubyがその行を実行中のselfです。 (「実行」には「クラス定義を実行」「メソッド定義を実行」などもあることに注意してください)。
プログラム | self | メソッド実行中のself |
---|---|---|
class A | ※ | |
def intro | A | |
print “私はクラスAのインスタンスです\n” | A | introを呼び出したときのレシーバ |
end | A | |
end | ※ | |
a = A.new | main | |
a.intro #=> 私はクラスAのインスタンスです | main |
※のところはselfはmainだと思われるが未確認。
トップレベルとクラス、モジュール定義はselfが分かりやすいですけれども、メソッド定義の中のselfを考えるのは面倒な場合があります。 次の例を考えてみましょう。
def average2(array)
(array.sum.to_f/array.size).round(1)
end
def stdev2(array)
mean = average2(array)
v = array.inject(0){|a,b| a+(b-mean)*(b-mean)}/array.size
Math.sqrt(v).round(1)
end
class Statistic2
def initialize array
@array = array
end
def show
print "合計は #{@array.sum}\n"
print "平均は #{average2(@array)}\n"
print "標準偏差は #{stdev2(@array)}\n"
end
end
d = Statistic2.new([2,4,3,9])
d.show
#=> 合計は 18
#=> 平均は 4.5
#=> 標準偏差は 2.7
さて、このとき、selfはそれぞれの場所で何を表しているでしょうか?
細かい分析と検討の結果、最後に?のついた疑問が出てきました。
この疑問に答えるには、トップクラスで定義されたメソッドの扱いを知る必要があります。 Rubyのドキュメントには次のように書かれています。
トップレベルで定義したメソッドは Object の private インスタンスメソッドとして定義されます。
このことから、average2とstdev2はObjectクラスのメソッドです。
このように、selfがどのインスタンスを指していようと、そのインスタンスのクラスはObjectのサブクラスなので、トップレベルで定義されたメソッドを継承しています。 よって、selfはそのメソッドのレシーバになれるので、関数形式でそのメソッドを呼ぶことができるのです。
長い考察でしたが、簡潔にいうと
「トップレベルで定義したメソッドは、プログラムのどこでも関数形式で呼び出すことができる」
ということになります。
privateメソッドは関数形式(メソッド名だけでの呼び出し)でのみ呼び出せます(ただし「self.メソッド名」ならば呼び出すことが可能)。 ということは、privateメソッドは「selfをレシーバとしてのみ呼び出せる」ということです。
また、一般にメソッド呼び出しにおけるレシーバは次の条件を満たしていなければなりません。
この2つを組み合わせると、privateメソッドを呼び出すにはselfの指すオブジェクトのクラスが上記の2条件のいずれかを満たすことが必要です。 説明が複雑になるので、例をあげて説明します。
トップクラスではselfはmainを指していて、mailはObjectクラスのインスタンスで、Objectではprvメソッドが定義されていないので、prvを関数形式で(つまりselfであるmainをレシーバとして)呼ぶことはできません。
同様にトップクラスでpubも関数形式では呼べませんが、pubは「インスタンス、ドット、メソッド名」の形式でも呼び出せるので、c.pub
として呼び出すことができます。
c.pub
が実行されている間は常にselfとcは同じオブジェクトを指しています。
ですから、この実行の最中はprvを関数形式で呼び出すことが可能です。
それはどのようなときでしょうか? それはpubの定義の中で直接または間接にprvを呼び出すときです。
以上の考察をまとめると
ということになります。 「直接または間接」というのは後ほど説明します。
ただし、ひとつ注意が必要で、メソッドが定義されたクラスのサブクラスはメソッドを継承できるということから、サブクラス中のメソッド定義でもprivateメソッドを呼び出せます。
privateの本来の意味は「呼び出し形式が関数形式に限る」ということなのですが、その結果として「クラスとサブクラスの定義外では(通常は)呼び出せない」ということになります。 それがprivate(非公開)という名前になっている理由だと思われます。
実際問題、クラス定義の中だけで使われる関数は良くでてきます。 そのような関数にはprivateの設定をしましょう。
さて、クラス定義の中のメソッドはデフォルトでpublicでした。
privateにするためにはprivate
メソッドを使います。
このことは冒頭の例でも示しました。
privateメソッドを引数なしで呼び出すとその後のメソッドはすべてprivateメソッドになります。
また、private
メソッドを引数つきで使うと、その引数のメソッドがprivateメソッドになります。
このときはprivateメソッドに先立って対象となるメソッドを定義しておかなければなりません。
引数のメソッド名にはシンボルまたは文字列を使いますが、シンボルを使う方が普通です。
private :average
これにより、averageメソッドだけがprivateメソッドになります。 引数なしのprivateメソッドと異なり、引数付きのprivateメソッドでは以後のメソッドは引き続きデフォルトがpublicであるとして定義されます。
privateメソッドに対応するpublicメソッドとprotectedメソッドがあります。 これらはメソッド名を引数にとり、そのメソッドの呼び出し制限を設定します。 また、引数なしで実行されると、それ以後に定義されるメソッドすべての呼び出し制限を設定します。
最後に「直接または間接」を詳しく説明しましょう。 「直接呼び出す」の意味は明らかですから、「間接的に呼び出す」例を示します。
class D
def a
print "privateメソッドaです\n"
end
def b
c #トップレベルのメソッドcを呼び出す
end
private :a
end
def c
a # privateメソッドaを呼び出す
end
d = D.new
d.b #=> privateメソッドaです
private :a
によって、メソッドaの呼び出し制限がprivateになってるd.b
でpublicメソッドbを呼び出すこの例ではprivateメソッドaがクラス定義の外で正常に呼び出されました。 それは、メソッドcがDのメソッドbから呼び出されたため、selfがdのままの状態でメソッドcが実行できたからです。 この場合privateメソッドaはDのメソッドbによって、cを通して間接に呼ばれています。 これが「間接的な呼び出し」の意味です。
bとは独立にcがaを呼び出すことはできません。
これは、privateメソッドがクラス外で呼び出せる例外です。 通常はprivateメソッドはクラス外から呼び出せないと考えても問題ありません。
protectedメソッドはprivateメソッドに似た使い方ができますが、定義は違います。 protectedメソッドは「そのメソッドを持つオブジェクトが selfであるコンテキストでのみ」呼び出せます。
ここで難しいのが「コンテキスト」だと思います。 私も正確な定義は分かっていないのですが、実行時の「状態」をデータとしてまとめたものを一般にコンテキストというので、Rubyでも同様だと思います。 protectedの理解自体にはコンテキスト全体の理解までは必要ありません。 selfの状態だけを理解するだけで十分です。
さて、実行状態において、そのメソッドを持つオブジェクトがselfというのはどういうときでしょうか。 privateの最後の例では、メソッドがb=>c=>aと呼ばれる間ずっとselfとdは同じオブジェクトを指していました。 この状態が「そのメソッドを持つオブジェクトがself」です。 そのときにはprotectedメソッドは(関数形式でも関数形式でなくても)呼び出すことができます。
class E
def initialize(name)
@name = name
end
def a
print "protectedメソッドaです\n"
print "レシーバは#{@name}です\n"
end
def b(e)
e.a # selfはdになっているのでprotectedメソッドを使える
end
protected :a
end
d = E.new("d")
e = E.new("e")
d.b(e) #=> privateメソッドaです レシーバはeです
この例ではpublicメソッドbを呼び出した時に、selfはdと同じものを指しています。 「そのメソッドを持つオブジェクト(d)が selfであるコンテキスト」になっています。 そこで、protectedメソッドaが使えますが、ここではeをレシーバにして呼び出しています。 このように、関数形式ではない形でも呼び出せるのはprivateとの違いです。
この呼び出し制限から、privateメソッドとprotectedメソッドはクラス定義外では(一部例外を除いて)呼び出すことができません。 メソッドを「非公開」にするにはprivateでもprotectedでも同様の効果が期待できますが、privateを使っている例がほとんどです。 privateという名前が「非公開」をイメージしやすいからでしょうか。
単項マイナスとは 単項マイナスと括弧 括弧なし単項マイナスを許容する場合のBNF calcの場合
パーサ・ジェネレータとは 少し複雑な文法 四則(加減乗除)計算のBNF Racc で実装 クラス定義、BNFの記述部分 ヘッダー、インナー、フッター コンパイルと実行 演算子の優先順位と結合における左右の優先順位 まとめ
StrScanライブラリのドキュメント 字句解析とは StrScanライブラリ StrScanライブラリを使った字句解析 実例
lbtというgemを作って公開してみた lbtはどんなgemか ファイルの配置 lbt.gemspec Rakefile gemのビルド RubyGems.orgへのアップロード 補足・・rake/gempackagetaskサブライブラリについて
文字列のエンコーディングに頭を悩ませることはほとんどなくなりました。 なぜなら、どのアプリ、システムもUTF-8を使うようになったからです。 Rubyでもエンコーディングの問題が起こることはまず無いでしょう。 ですが、今回はエンコーディングの考え方を整理してみたいと思います。
Fiberを書いたときから、次はスレッドを書こうと思っていましたが、時間がかかってしまいました。 その理由は、期待したとおりのスレッドの効果がなかったためです。 今回はそのことを書きますが、これはRubyのスレッドの抱えている問題なのか、自分のやり方が悪いのかははっきりしていません。
Fiberは「ノンプリエンプティブな軽量スレッド」とRubyのマニュアルに記載されています。
今回はRubyプログラムから自動的にドキュメントを作成するRDocについて書きたいと思います。 私はこのことについて、エキスパートではありません。 この記事も、初心者の体験談だと考えてください。
Ruby/Gtkの記事を先日書いたときに、「これはかなり使える」という手応えを感じたので、WordBook(Railsで作った単語帳プログラム)のGTK 4版を作りました。 プログラムは「徒然なるままにRuby」のGitHubレポジトリに置いてあります。 レポジトリをダウンロードし、ディレクトリ_example/...
今回はGTK 3とGTK 4をRubyで使うライブラリについて書きたいと思います。
今回もRubyとGUIのトピックです。 Glimmerを取り上げます。
Rubyはグラフィックについて弱い印象があります。 しかし、グラフィックはデバイスに関することなので、言語そのものには直接の関係はないはずで、あるとすればライブラリです。 今後グラフィック関係のgemが開発されることに期待しましょう。
Rails7におけるシステムテストについて書きます。
前回作ったWordbook(リソースフル)のテストを書いてみます。 RailsのテストはminitestをRails用に拡張したものです。
今回はRailsの慣例に沿った形でWordbookを作り直します。
今回はWordBookの検索と削除についてです。
今回はRailsにおけるデータの作成と保存、そして変更について説明します。 そのベースになるモデルとデータベースの話から始め、appendとchangeの動作について詳しく説明します。
一般に、HTMLは文書の構造を表し、CSSはその体裁(見栄え)を表します。 Railsは最終的にCSSを含むHTML文書を出力するので、この2つについての理解が必須です。 この記事ではとくにCSSの人気ライブラリであるBootstrapを紹介します。 BootstrapはJavascriptも含んでいます。
Rubyの最も人気のあるアプリケーションであるRuby on Railsを取り上げようと思い、書き始めました。 予想してはいましたが、相当な分量になってしまいました。 そのため、何回かに分けて記事にすることにします。 また、対象となる読者のレベルをどうしようかと考えましたが、「徒然Ruby」が基礎的な内容から始ま...
Rubyのライブラリ管理システムのRubygemsとコマンドgemおよびbundlerについて説明します。
minitestについて連続して2回書いてきました。 「minitestはドキュメントが少ない」という人がいますが、私も同感です。 例えば、モックとスタブの説明も少ないです。 そこで、今回はmock.rbのソースコードを参考に、モックの私的ドキュメントを書いてみました。 あくまで私個人の考えであり、minites...
今回もminitestの話です。 mockとstubに焦点をあて説明します。
アプリ作成の記事でminitestを使いました。 今回はminitestについて、また一般にテストについて、私の考えを書こうと思います。
今回はメソッドの呼び出し制限ついて説明します。 呼び出し制限にはpublic、private、protectedの3つがあります。
今回は特異メソッド、特異クラス定義、名前空間、モジュール関数について説明します。
2023/10/29 追記:この記事は新しく書き直しました。 古い記事で使っていたGitHubのCalcが大幅にアップデートされたためです。 そこで、この記事に合うようなプログラムsimple_calcを新たに作りました。 このプログラムは本レポジトリの_example/simple_calcにあります。
if〜elsif〜・・・〜else〜endは皆さん良く使うでしょうか? これは場合分けで良く使われる方法です。 これと同様の制御構造にcase文があります。 Cのswitch文に似ていますが、より強力な機能を持っています。 if-else-endよりも高い能力があるといえます。
Procオブジェクトを生成するメソッドlambdaについて説明します。
今回はブロックを一般化したオブジェクトProcを説明します。
ブロック付きメソッドの作り方を説明します。
モジュールには名前空間とミックスイン(Mix-in)の2つの機能があります。 ここではミックスインについて説明します。
クラスの親子関係
Rubyの演算子とその再定義について書きます。
今回からクラスとインスタンスを定義、生成する方法を説明します
Kernelモジュールのメソッドはどこでも使うことができます。 そのメソッドの中には便利で有用なものが多いです。
ここでは私が便利だと思ったメソッドを紹介します。
実数
今回はシンボルとハッシュについて説明します。
文字列は最も使うオブジェクトのひとつです。 特にウェブ・アプリケーションでは、コンテンツだけでなくHTMLのタグやCSSを含めすべてが文字列です。 Rubyは文字列オブジェクトのメソッドが充実しており、またパターンマッチのための正規表現も充実しています。
配列は、どのプログラミング言語にもあると思います。 複数の要素を一括して扱うことができるのが配列です。 Rubyの配列はメソッドが充実しているので、プログラムを効率的、機能的に書くのに役立ちます。
今回の目標はインスタンスです。 インスタンスを説明するために、ローカル変数と文字列オブジェクトを事前に扱います。
今回はメソッド定義です。 メソッド定義はRubyの核心ですが、今回はトップレベルに限って説明します。 この限定によって、内容はかなり易しくなっています。
ブロックはRubyの特長です。 ブロックのおかげで記述が非常にすっきりと分かりやすくなります。 今回はブロックをイテレータの本体として使う方法を説明します。
ここではRubyの最も基本的なオブジェクトである整数について説明します。
「徒然なるままに」をネットで調べてみると、「することもなく、手持無沙汰なのにまかせてという意味」とありました。 まさに、自分の現状を言い当てた言葉。 しかも、ブログに書くネタもなかなか思いつかない日々。