質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.13%

【Ruby】selfの意味が分からない

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 3
  • VIEW 4,465

score 11

秋山智俊『恋するプログラム - Rubyでつくる人工無能』読んでいてわからない箇所がありました。コードの最後に書かれてある「self」という部分です。どこから湧いて出たんですか?どういう意味なんですか?

require "unmo"
require "vr/vruby"
require "vr/vrcontrol"
require "vr/handler"
require "nobycanvas"

class NobyForm < VRForm
 include VRDrawble
 include VRMenuUseable

 def construct
  #ノビィ作成(人工無能のことです)
  @noby = Unmo.new("noby")

  #ウィンドウタイトル
  self.caption = "Unmo System : " + @noby.name #下の画像の部分です


イメージ説明

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+9

まず「レシーバー」という概念について説明します。

メソッドというものは,かならず何らかのオブジェクトに対して呼び出されます。
例えば

str = "Ruby"
str.downcase #=> "ruby"

の場合,downcase というメソッドはローカル変数 str が指している String オブジェクト("RUby")に対して呼び出されています。

この,対象となるオブジェクトを,そのメソッドのレシーバー(receiver)と呼びます。
(なぜ「受け手」などと呼ぶのか,という話は割愛)

メソッド呼び出しのピリオドの前には,変数だけでなく一般に式を書くことができます。式の評価結果をレシーバーとしてメソッドが呼び出されます。
例えば

(1 + 3).even? #=> true

の場合,(1 + 3) という式の評価結果である整数 4 に対して,even? メソッド(偶数かどうかを判定するメソッド)が呼び出されています。

次に暗黙のレシーバーについて説明します。

さきほどのメソッド呼び出しの例では,レシーバーがあらわに書かれていました。
しかし,

puts 3

の場合,puts もメソッドですが,レシーバーが書かれていません。
こんなふうに,レシーバーを省略したメソッド呼び出しも可能です。
レシーバーを略した場合,self というものがレシーバーになるというルールです。
上の例は,

self.puts(3)

とまったく同じです。これを省略して書いただけです。

いろいよ self の説明に入っていきます。

まず,self という四つの文字からなる識別子は「擬似変数」と呼ばれるものの一つです。
擬似変数の仲間には nil や true など,どこで参照しても常に同じオブジェクトを指しているものもありますが,self の場合は文脈によって指しているオブジェクトが変わります。
余談ですが,現在のソースファイル(スクリプトファイル)のファイル名を指している __FILE__ という擬似変数もあります。こちらも,参照する場所(ファイル)によって異なる文字列を指します。

ともかく,self は何らかのオブジェクトを指しています。
以下,話を簡単にするため,「メソッド定義の中における self」に限って説明します。
self はそのメソッドの実行主体,つまりレシーバーを指しています。

具体例を挙げます。以下のコードを見てください。

# source-1
class Hoge
  def hello
    puts "Hello, my object_id is #{self.object_id}."
  end
end

x = Hoge.new
x.hello # => "Hello, my object_id is 43074640."

上の例で,hello メソッドの中で self.object_id という式が出てきますね。
self に対して object_id メソッドを呼び出しています。object_id はオブジェクトの ID(全てのオブジェクトが持つ,オブジェクト固有の整数)を返すメソッドです。
その返り値を文字列に埋め込んでいるのですね。
オブジェクトにどんな ID が振られるかは不定なので,同じプログラムを後日実行したら,出てくる数字は変わりえます。

この self は hello メソッドのレシーバーを指しています。
上の実行例では,ローカル変数 x が指しているオブジェクトがそれです。hello メソッドは,そいつの ID を表示して挨拶するメソッドというわけです。

これで,一応 self の説明ができたことになります。

しかし,こんな疑問が湧いてきます。
レシーバーを省略してメソッド呼び出しを書くと self がレシーバーになるのだったら,上の例でも self. を省いて

# source-2
class Hoge
  def hello
    puts "Hello, my object_id is #{object_id}."
  end
end

と書いてもいいんじゃないの?

そのとおりです。この修正版はうまく働きます。

ただ,たとえ self をレシーバーとするときでも,省略できない場合があります。

例えば,メソッド名と同じローカル変数を使っている場合。

# source-3
class Hoge
  def hello
    object_id = "foo"
    puts "Hello, my object_id is #{object_id}."
  end
end

実は単に object_id とだけ書くと,形の上ではローカル変数の参照なのかメソッド呼び出しなのか区別が付きません。
source-2 の場合,ローカル変数がどこにも定義されていない(ローカル変数への代入がどこにも無い)ので,object_id はメソッド呼び出しだと解釈されます。(そういうルールです)。
source-3 の場合,ローカル変数が定義されたあとなので,object_id はローカル変数の参照だと解釈されてしまいます。

self. が省略できない例をもう一つ挙げます。

# source-4
class Hoge
  def hello
    puts "Hello, I am an instance of #{self.class}."
  end
end

x = Hoge.new
x.hello # => "Hello, I am an instance of Hoge."

今度は,自身が何のクラスのインスタンスかを表示して挨拶します。
source-4 で self. を省略すると,class の部分がメソッド呼び出しではなくクラス定義の冒頭かと勘違いされ,(直後にクラス名が書いてないので)SyntaxError となります。

ここからがいよいよ核心です。

まずは代入メソッドと呼ばれるものを説明します。

代入メソッドはメソッド名の末尾が = であるようなメソッドですが,インスタンス変数に外部から値をセットするのが典型的な使い方です。
以下の例をご覧ください。

class Person
  def name=(name)
    @name = name
  end

  def name
    return @name # return は省略可
  end
end

x = Person.new
x.name = "Richard"
puts x.name #=> "Richard"

代入メソッドの面白いところは,上の例のように,使うときに = の前にスペースを入れても構わないことです。
ふつう,メソッド名の途中にスペースなんか入れたらダメですが,代入メソッドは文法上特別扱いなんですね。
でもメソッドであることは変わりません。

この

x.name = "Richard"

は,いかにも(名前のとおり)代入ぽいですが,れっきとしたメソッド呼び出しです。
ローカル変数 x が指す Person オブジェクトをレシーバーとし,文字列 "Richard" を引数とした,メソッド name= の呼び出しなんです。

ところで,この代入メソッドを,Person クラスの他のメソッドから利用するとします。

たとえば,他人と同じ名前をつける named_after というメソッドを考えましょうか。(誰かの名にちなんで命名すること)

こんなふうに使います。

person1 = Person.new
person1 = "Eaves"

person2 = Person.new
person2.named_after person1

puts person2.name #=> "Eaves"

このメソッドはこんなふうに書けますね:

def named_after(other)
  self.name = other.name
end

named_after メソッドの中で,自分自身の name= メソッドを呼び出しているのです。

えー,実は今の場合,代入メソッドを呼び出さなくてもインスタンス変数 @name に直接代入すればいいのですが,説明のためにあえてこうしました。
(現実の仕事では,代入メソッドを使うべきシチュエーションがよくあるのですが,本題と関係ないので割愛)

このメソッドでは self. が省略できません。なぜなら

def named_after(other)
  name = other.name
end

と書くと,ローカル変数への代入と解釈されてしまうからです。

これがご質問の答えとなる self の正体です。

(力尽きた・・・)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/07 14:55

    scivolaさん

    回答ありがとうございます。こんなに詳しく分かりやすく説明してくださって感激しています。selfの使い方や@変数の外部からの操作など、勉強になる点がたくさんありました。教科書よりもわかりやすかったのでノートに取らせていただきました。

    キャンセル

+3

selfが分からないと言う事は、オブジェクト指向プログラミングについて学んでいないと言う事だと思いますが、
この本をAmazonで情報を見ると、出版社からのコメントとして、

Rubyプログラミングの実践的入門書としても優れています。 

また、レビューとして、

Rubyの入門書としても利用できる。

と書いてあるので、本の中でオブジェクト指向について説明した部分があるかと思います。
そこを読んでください。
ここの回答欄でオブジェクト指向についてゼロから説明するのは難しいです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/07 14:31

    otnさん

    回答ありがとうございます。残念ながら本書はコードを書いて覚える本なので、オブジェクト指向について詳しくは説明されていません。

    キャンセル

  • 2018/03/07 16:00 編集

    そうなんですね。ということは、レビューの文言がおかしいのか。あるいは、他の言語の中上級者向けのRuby入門書なのか。
    であれば、適当なRuby入門書をまず読むのが良いかと思います。

    キャンセル

+1

Rubyはやっていませんからわかりませんが。

まず、ソースコードが読みづらいです。

Ruby インデント

インデントがないと読みにくいです。

teratailには ソースコード用のものがあるのでそれを利用してください。

"</>" ボタンを押して、出てきたやつの中にソースコードを記述してください。

次に質問内容について。

たぶん、このselfっていうやつは 他の言語だと this に相当するやつだと思います。

自分オブジェクトのメンバを指定するときに使う。

よって、

self.caption = "hello"

だとしたら、そのオブジェクト(自分自身)のcaptionメンバ ( フィールドかもしれないし、メソッドかもしれないが。 ) に "hello" を代入している

ってことになる。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/07 14:25

    BeatStarさん

    回答ありがとうございます。selfは自分自身のオブジェクトを指しているのですね。例文をつかったご説明は大変分かりやすいものでした。
    また、インデントの不備に関しては申し訳ありませんでした。イラッとさせないように今後は気をつけようと思います。

    キャンセル

  • 2018/03/07 14:33

    イラっとはしていませんが、読みにくかったので。
    ( 上記の "イラッと..."は私の言葉じゃないですが。 )

    selfについての
    詳しい話は scivolaさんのアンサー等を参照してください。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.13%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る