かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Ruby メソッド定義 (def式) 内でメソッド定義 (def式)した場合のメモ

Rubyのお勉強をしていてメソッド定義内でメソッド定義したら不思議な挙動 (JS脳には予想外だっった) だったのでメモ。

メソッド定義 (def式) 内に メソッド定義 (def式) をした場合

def a
  puts 'A'
  def b
    puts 'B'
  end
  print "self.b > "
  self.b
end

a
# => "A"
# => "self.b > B"
b
# => "B"

えっ? def a 内で定義した def bdef a の外からでも呼び出せるの…!?
JS脳的には下記のようなイメージだったのでびっくりしました

function a() {
  console.log('a')
  function b () {
    console.log('b')
  }
  b()
}
a()
// a
// b
b()
// Error: Uncaught ReferenceError: b is not defined

Ruby のメソッド定義 (def式) は呼び出されるとトップレベルに定義される?

Ruby では def式 の書かれている場所を通れば、そのファイル?のトップレベルにメソッドが定義されるのかなという印象を持ちました。

def a
  puts 'A'
  def b
    puts 'B'
  end
end

a # => "A"
b # => "B"

a を呼び出してないと b は未定義になる

def a
  puts 'A'
  def b
    puts 'B'
  end
end
b
# => undefined local variable or method `b` for main:Object

Ruby は関数ではなくメソッドで、レシーバー.関数名 引数 の形で呼び出されるとあったので、def式で定義すると、そのファイルのトップレベルのオブジェクトがレシーバーになるからトップレベルから呼び出せるのかな〜?とか考えていました。詳しくはないので完全に憶測なのですが…

メソッド定義 (def式) は後から呼び出されたもので上書きされる?

先の実験でメソッド定義内にあるメソッド定義は外側のメソッドを呼び出した際に定義されることがわかりました。
という事は同じ名前のメソッドが外側にもある場合、メソッドの呼び出し順によって、呼び出されるメソッド上書きされが変わってしまう可能性があるのではないかと思い試してみました。

def a
  puts 'A'
  b
  def b
    puts 'B inside A'
  end
  b
end

def b
  puts 'B'
end

b # => "B" … トップレベルのメソッドが呼び出される

a
# => "A"
# => "B" … メソッド内の def b の定義を通る前なので、外側のトップレベルのメソッドが呼び出されている
# => "B inside A" …  a 内の b メソッドが定義され、新しく定義された b が呼び出されている

b # => "B inside A"
# トップレベルの b メソッドは a 内で定義された方に上書きされてしまっている

予想通りでした。
Ruby の場合同じファイル内でメソッド定義する場合はメソッド名が重複しないように気をつける必要がありそうです。
そもそもトップレベルにつらつら定義するのが Ruby 的お作法では良くないのかも? この辺りの文化については詳しくないのでどこで学べば良いのでしょう…?

ポエム

Ruby のメソッド定義内でメソッド定義したらメソッド外で呼び出したらエラーになるよね〜って思って実行したらふつーに呼び出せてしまってびっくりしました。慣れ親しんでる JavaScript の関数定義と違ってRuby の メソッドて定義 (def式) は常にトップレベルに定義されるって感じっぽいのですね。JavaScript感覚で使ってるとハマりそうでした。

Ruby は関数じゃなくてメソッドって呼ぶのなんでだろう?慣習???みたいに思ってたのですが、今回のハマりで読んでたRubyの本

レシーバーに含まれる情報を使用しないメソッドを関数的メソッドと呼ぶ。全てのメソッドはレシーバー情報を持っているので純粋な関数ではない
初めてのRuby 著 Yugui

みたいな感じに書かれていて、オブジェクトに紐付いてて呼び出し元(レシーバー)の情報を持っているとメソッド?
トップレベルに置かれた def式 は mail オブジェクトとかに紐付いててその情報持ってるってこと??
って事は JavaScript はブラウザで実行したらfunction も全部 window オブジェクトに紐づいて window.func() で呼び出せるから関数じゃなくて本当はメソッドなん???
むむむ?????
と解ったよーな、解らんよーな感じになりました。
ナルホドわからん!! 俺は雰囲気でぷろぐらむを描いている!!!!


初めてのRuby

初めてのRuby

プログラミング言語 Ruby

プログラミング言語 Ruby