java, scalaをやってきてgoに思うこと
- golangで末尾再帰最適化は使えるか?-
2017/6/23 株式会社AJA 森 拓真
階乗
6! = 6 * 5 * 4 * 3 * 2 * 1 = 720
5! = 5 * 4 * 3 * 2 * 1 = 120
4! = 4 * 3 * 2 * 1 = 24
3! = 3 * 2 * 1 = 6
2! = 2 * 1 = 2
1! = 1 = 1
末尾再帰最適化
どんな風に書けばいいの?
末尾再帰 -> 関数の最後の呼び出しが自身の呼び出しになっていれば良い
再帰関数(スタックオーバーフローが起きる)
末尾再帰関数 (スタックオーバーフローが起きないように最適化可能)
再帰の場合
5!
コールスタック
4!
3!
2!
1!
return 1
return 2 * 1 = 1
return 3 * 2 = 6
return 4 * 6 = 24
func fact (5)
return 5 * 24 = 120
末尾再帰の場合
fact (4, 5)
コールスタック
fact(3, 20)
fact(2, 60)
fact(1, 120)
fact(0, 120)
return 120
return 120
return 120
return 120
func fact (5, 1)
return 120
n = 5 - 1 = 4
result = 5 * 1 = 5
n = 4 - 1 = 3
result = 5 * 4 = 20
n = 3 - 1 = 2
result = 20 *3 = 60
n = 2 - 1 = 1
result = 60 * 2 = 120
n = 1 - 1 = 0
result = 120
Scalaコンパイラの最適化って実際何してるの?
classファイルをjadで逆コンパイルした結果
階乗を求める末尾再帰な関数
末尾再帰最適化の対応状況
言語 対応状況 備考
java × 非対応
scala ◯ 対応 デフォルトで有効。
@tailrecアノテーションで、非末尾
再帰関数をコンパイルエラーにでき
る
ruby △ 対応 デフォルトでは無効。
tailcall_optimizationオプションを有効にすると利用
可能
javascript △ 対応 ES6からは、実装仕様として要求
Babelではトランスパイル時に最適化するようになっ
ている
Golangで末尾再帰最適化は使えるの?(1/5)
Golangで末尾再帰最適化は使えるの?(2/5)
特定の数までの足し上げる(ex: 5 + 4 + 3 + 2 + 1)末尾再帰関数
StackoverFlow
Golangで末尾再帰最適化は使えるの?(3/5)
BenchmarkRecursive-4 5000000 349 ns/op
BenchmarkRecursiveIteration-4 30000000 56.0 ns/op
BenchmarkTailRecursive-4 5000000 316 ns/op
パフォーマンス計測してみた
Golangで末尾再帰最適化は使えるの?(4/5)
go build -gcflags -S recursive.go &> assembly_code.txt
Golangで末尾再帰最適化は使えるの?(5/5)
● 使えない
まとめ
● golangでは末尾再帰は最適化されない。末尾再帰はfor, goto
を利用した記述方法に変更する。
● 今後も追加される気配は無い。

goで末尾再帰最適化は使えるか?