単項マイナスと構文解析
単項マイナスとは 単項マイナスと括弧 括弧なし単項マイナスを許容する場合のBNF calcの場合
今回は特異メソッド、特異クラス定義、名前空間、モジュール関数について説明します。
特異メソッドは英語ではsingleton method(一人っ子のメソッド)といいます。 英語の方が意味をとりやすいと思います。 特異メソッドは個々のオブジェクト(インスタンス)だけがレシーバになるメソッドのことを言います。
次の例はクラスAのインスタンスを2つ作り、そのうちの一方に特異メソッドを作ったときの実行結果を示すものです。
オブジェクトaに特異メソッドを定義するにはdef a.intro
のように「オブジェクト、ドット、メソッド名」のようにします。
class A
end
a = A.new
b = A.new
def a.intro
print "私はaです\n"
end
a.intro #=> 私はaです
b.intro #=> エラー (NoMethodError)
実行します。
私はaです
example21.rb:12:in `<main>': undefined method `intro' for #<A:0x00007f6f3d737bd0> (NoMethodError)
b.intro
^^^^^^
aにはintroメソッドを定義したので「a.intro」の結果「私はaです」が出力されましたが、bには定義されていないので、「b.intro」はエラーになりました。
通常のメソッドは同じクラスから生成したインスタンスすべてで可能ですが、特異メソッドはそれが定義された個別のインスタンスでのみ可能です。 特異メソッドは様々なケースで使えると思いますが、例えば次のようなケースが考えられます。
Rubyでは個々のクラス(例えばIntegerやString)はClassクラスのインスタンスです。 話がややこしくなりますが、クラスはクラスであると同時にインスタンスでもあるわけです。
クラス名は大文字で始まりますが、この文字は定数を表しています。 クラスを定義すると、そのクラスオブジェクト(Classクラスのインスタンス)が生成され、定数に代入されるのです。 つまり大文字で始まる名前は、クラスを表す名前であると同時に定数の名前でもあります。
クラスはインスタンスですから、変数に代入することもできます。
class A
end
c = A
p c #=> A
p c.class #=> Class
変数cにはクラスA(のオブジェクト)が代入され、「p c」でクラス名Aが、「p c.class」でクラスAのクラスであるClassが表示されます。
このようにクラスを変数に代入することはできますが、通常はそういうことはしません。 それはプログラムを分かりにくくなることに繋がります。 定数を参照すれば、それがクラスだと直感的に分かります。 定数だけをクラスに用いるのが良い習慣です。
さて、クラスはオブジェクトですから、特異メソッドを定義することができます。 クラスの特異メソッドをクラスメソッドといいます。
class A
end
def A.intro
print "私はクラスAです\n"
end
A.intro #=> 私はクラスAです
クラスメソッドの定義はクラスの内側でもできます。
class A
def A.intro
print "私はクラスAです\n"
end
end
A.intro #=> 私はクラスAです
この方法では疑似変数selfを使うことができます。 「class A〜end」では、selfは「ClassクラスのインスタンスであるクラスA」を指します。 したがって、この区間では定数Aとselfは同じオブジェクトを指します。 ですので、
class A
def self.intro
print "私はクラスAです\n"
end
end
A.intro #=> 私はクラスAです
のように、selfを使ってクラスメソッドを定義することができるわけです。
クラスメソッドはそのサブクラスにも継承されます。 上記のプログラムに続き、以下を追加するとBにもAのクラスメソッドintroが継承されます。
class B < A
end
B.intro #=>私はクラスAです
特異メソッドは個々のオブジェクトにメソッドを定義するものでした。 クラスメソッドはクラス(クラスはオブジェクトでもある)の特異メソッドです。 ではモジュールについてはどうでしょうか? モジュールはクラス同様にオブジェクトなのでしょうか? 答えはイエスです。 モジュールはModuleクラスのインスタンスです。 したがって、モジュールの特異メソッドを作ることができます。
module C
end
def C.intro
print "私はモジュールCです\n"
end
C.intro #=> 私はモジュールCです
モジュール定義の中ではselfを使った特異メソッド定義が可能です(クラスの場合と同じ)。
モジュールの場合はクラスと違い、サブモジュールを作ることができませんから、特異メソッドの継承はありません。 また、モジュールをincludeしても特異メソッドの継承はできません。 インクルードは通常のメソッドと定数を引き継ぐだけです。
トップレベルに同じ名前のメソッドを2つ作ることはできません。 それをすると、2番めの定義が1番めの定義の「再定義」になり、以後2番めの定義だけが有効になります。 しかし、異なる動作をする2つのメソッドに同じ名前をつけたいときもあります。 例えば、あるプログラムはMarkdownファイルをHTMLにもPDFにも変換できるとします。 「変換する」メソッドは、convertという名前が良いでしょう。 HTMLに変換するのもconvert、PDFに変換するのもconvertにしたいのです。
このとき、convertにプリフィックスをつけて区別することが可能です。
def html_convert
... ... ...
end
def pdf_convert
... ... ...
end
このようなプリフィックス名により、区別することを「名前空間」を使うといいます。
HTMLに関するメソッドにはすべてhtml_
をつけ、PDFに関するメソッドについてはpdf_
をつけるわけです。
同様のことはmoduleの特異メソッドでも実現できます。
module HTML
end
module PDF
end
def HTML.convert
... ... ...
end
def PDF.convert
... ... ...
end
このようにモジュールの特異メソッドは名前空間を付与することと同一の効果があります。 同じことをクラスでもできますが、クラスには継承があるため、サブクラスを作ると別の名前空間で同じ内容のものが作られてしまいます。 したがって、名前空間としてはモジュールの利用が適しています。
しかし、クラスの特異メソッドも名前空間に似た効果があります。 例えば、IOクラスにはreadという特異メソッドがあります。
s = IO.read(ファイル名)
このメソッドはファイルを読み込み、その文字列を変数sに代入します。
単にreadという名前のメソッドでは名前の衝突が不安になりますが、IO.
がついているので、これはIOクラスの特異メソッドだと分かります。
これは名前空間に似た効果です。
また、IOのサブクラスにFileがあります。
特異メソッドはサブクラスに引き継がれるので
s = File.read(ファイル名)
でも同じreadメソッドが呼ばれます。 これはファイルクラスの特異メソッドと見えますが、実はIOの特異メソッドの継承です。
特異メソッドは一般のオブジェクト(通常のクラスから生成したインスタンス)に定義するよりも、モジュールやクラスに定義する場合が多いように思います。
実際Rubyのドキュメントを見ると、クラスメソッドは数多く定義されています。
最もよく使うのはインスタンスを生成するnew
メソッドでしょう。
これはほとんどのクラスの祖先であるObjectクラスの特異メソッドです。
各クラスはそれを受け継いでいるので、new
が使えるのですね。
特異クラス定義とは、次のようにclass <<(式)〜end
という構文のことをいいます。
ここで定義されるメソッドは(式)の指すオブジェクトの特異メソッドになります。
class A
end
a = A.new
b = A.new
class << a
def intro
print "私はaです\n"
end
end
a.intro #=> 私はaです
b.intro #=> エラー(NoMethodError)
classに<< a
をつけると、aの指すオブジェクトについて定義をすることになります。
そこで定義されるメソッドはすべてaの指すオブジェクトの特異メソッドです。
この記法の良いところは「多くの特異メソッドを少ないタイプ量で記述できる」ことです。
また、selfをこの記法に使い、クラスメソッドを定義することができます。
class A
class << self
def intro
print "私はクラスAです\n"
end
end
end
A.intro #=> 私はクラスAです
これにより、class A〜endの中で、普通のメソッドもクラスメソッドも定義することができ、非常に効率的になります。 この記法が最も良いかもしれません。 モジュールにはこのタイプの記法がないので、個別に作ることになります。 ですが、次に述べるモジュール関数を作る方法を適用すると簡単に特異メソッドと通常のメソッドの2つができます。
ビルトイン・オブジェクトの中にMathモジュールがあります。
数学関数を提供するモジュールです。
例えば正弦関数はMath.sin(角度)
として呼ぶことができます。
また、モジュールをインクルードしておけば、sin(角度)
とモジュール名を省略することができます。
p Math.sin(Math::PI/3) #=> 0.8660254037844386
include Math
p sin(PI/3) #=> 0.8660254037844386
::
を使ってモジュール下の定数であることを示す。
クラス定義の中で定義された定数も「クラス名::定数」となる。
これはMathモジュール外部からの参照で用いられるMath::
を付けなくて良い。
また、sinメソッドはMathモジュールの通常のメソッドであるここで注意が必要なのは、Math.sinとsinメソッドの違いです
この2つがセットになっているので、インクルードを使う/使わないの選択が可能になります。
これは便利なことなので、モジュールの提供するメソッドに名前空間をつける目的であれば、両方のメソッドの定義をするべきです。
(Math.
がつかないsinはインクルードが必要なので、その時点で名前の衝突を避けることができる)。
このようなメソッドを「モジュール関数」といいます。
以前紹介したモジュールのミックス・インは通常のメソッドを複数のクラスに提供する仕組みでした。 ミックス・インのメソッドは、特異メソッドを含まないので、モジュール関数ではありません。
モジュール関数は通常のメソッドと特異メソッドの2つを定義しなければならないのですが、それは非効率です。
それを解決するメソッドがmodule_function
メソッドです。
module_function メソッド名
」によって、そのメソッドがモジュール関数になる。
メソッド名はシンボルで表すmodule X
def intro
print "私はモジュールXのモジュール関数です\n"
end
module_function :intro
end
X.intro #=> 私はモジュールXのモジュール関数です
include X
intro #=> 私はモジュールXのモジュール関数です
有用な関数をプログラム全体で使えるように提供したいときには、このようなモジュール関数として提供するのが良い方法です。 特にライブラリの作成においては、モジュール関数にするのがベストな選択です。 (ライブラリでなければ、トップレベルのメソッドでも十分です)。
前項で述べたように、モジュール内で定義された定数をモジュール外から参照するには「モジュール名::定数名」としなければなりませんでした。 これは「モジュールで、定数にも名前空間をつけることができる」ということを示しています。
定数は変数と同様にオブジェクトを指しますが、変数と違い再代入ができません。 その定数が生きている間はずっと同じオブジェクトを指します。 ユーザが定義する定数は数字や文字列(特にフリーズした文字列)が多いと思いますが、その他にクラス名やモジュール名も定数です。 このことから「クラスやモジュールにも名前空間を適用することができる」ということができます。
module ABC
class A
def intro
print "モジュールABCのAです\n"
end
end
end
module EFG
class A
def intro
print "モジュールEFGのAです\n"
end
end
end
a = ABC::A.new
b = EFG::A.new
a.intro #=> モジュールABCのAです
b.intro #=> モジュールEFGのAです
プログラムが大きくなり、クラス名の名前の衝突が心配であれば(特にライブラリでは)モジュールを使った名前空間の提供が解決手段となるかもしれません。
単項マイナスとは 単項マイナスと括弧 括弧なし単項マイナスを許容する場合の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の最も基本的なオブジェクトである整数について説明します。
「徒然なるままに」をネットで調べてみると、「することもなく、手持無沙汰なのにまかせてという意味」とありました。 まさに、自分の現状を言い当てた言葉。 しかも、ブログに書くネタもなかなか思いつかない日々。