This request has already been treated.

  1. kts_h

    exec.rb で a.funcA の補足

    kts_h
Changes in body
Source | HTML | Preview
@@ -1,103 +1,104 @@
# デフォルト引数の評価される順番
redmineのソースコードを読んでいたら、デフォルト引数の右辺に関数を使っていたので、気になって調べてみました。
個人的には発見した感がありますが、実際にはデフォルト引数の右辺に関数持ってくる実装は可読性を下げる可能性が高そうなので、やめておいた方がいいかと思います。
# Pythonの場合
以下のコードを実行した時、標準出力されるのはどういう順番かと言う話です。
```python:exec.py
def func1():
print("begin func1")
return "func1"
def func2(a=func1()):
print("begin func2")
return "func2"
class ClassA():
print("begin classA")
def funcA(self, a=func2()):
print("begin funcA")
print("main")
print(func2())
a = ClassA()
a.funcA()
```
正解は、以下の通り。
```shell-session
begin func1
begin classA
begin func2
main
begin func2
func2
begin funcA
```
関数の宣言が読み込まれた時点で評価されています。
ちなみにclass直下のところで呼び出される関数もclassの宣言を読み込んだ時点で評価されるので、このような順番になっています。
# Rubyの場合
同じ形でrubyも書いてみます。
```ruby:exec.rb
def func1
p "begin func1"
"func1"
end
def func2 a=func1
p "begin func2"
"func2"
end
class ClassA
p "begin classA"
def funcA a=func2
p "begin funcA"
end
end
p "main"
p func2
a = ClassA.new
+a.funcA
```
この実行結果は以下の通り。
```shell-session
"begin classA"
"main"
"begin func1"
"begin func2"
"func2"
"begin func1"
"begin func2"
"begin funcA"
```
pythonと違いデフォルト引数の右辺に来ている関数は、関数が実際に呼び出されたタイミングで評価されます。
それ以外の違いはないのですが、だからこそ見つけづらい言語の違いかなとおもいました。
# まとめ
書き出すと単純ですが以下のことが言えるかなと思います。
* rubyとpythonではデフォルト引数の右辺の評価タイミングが違う。
* class直下の関数はpython、ruby共にclassの宣言を読み込んだ時点で実行される。
この違いによりシンタックスエラーの見つかり方も若干変わってくるかなと思います。
pythonだと先に実行されるので、アプリケーション実行後すぐにエラーになるのに対して、rubyは関数が実行されるまでエラーにならないことになりますし、普通にエラーになりにくいのではないかと思います。
そして、この結果からさらに言えるのは、pythonではこの書き方は基本NGだと思います。moduleの読み込まれる順序に依存して関数の結果が変わる可能性があるため、実行結果が場合によって変わってしまいます。
一方で、rubyは可読性は下がるにせよ、それなりに安全にこの書き方を許容できるのではないかと思います。