2016/08/17
kyobashi.swift #2
@gomi_ningen
この実装には問題がある!
自己紹介
某ラビットハウス社で働く社畜 新3年生
・ Java, Scala, Swift, C#, PHP, F-BASIC など...
・ iOS開発初めて1年くらい
・ 日本ごちうさユーザーグループ(JGUG)
・ ごちうさ関連のエンジニアリングのお仕事依頼お待ちしております
🐰 時間がないので自己紹介は割愛します🐰
@gomi_ningen
今日話す内容
末尾呼び出しとその周辺のお話
1. 末尾呼び出し・末尾再帰とは?(3分)
簡単な関数の実装について考えながら、末尾再帰とは何かをおさらい
2. 再帰っぽいけど実はループな実装(3分)
ループならスタックの問題は発生しないので、再帰風にアルゴリズム
を実装できるが、実際はループになるようなコードを書いてみる
3. Swiftと末尾呼び出しのちょっと未来の話(3分)
Swift 4 とか
末尾呼び出し・末尾再帰とは
簡単な関数について考えていく
1 から n までの整数(0, 1, 2 ...)の和を返す関数 tri を実装したい
tri(n) = 0 + 1 + 2 + ... + (n -1) + n
簡単な関数について考えていく
1 から n までの整数(0, 1, 2 ...)の和を返す関数 tri を実装する
tri(n) = 0 + 1 + 2 + ... + (n -1) + n
だいたいこんなかんじ?
簡単な関数について考えていく
1 から n までの整数(0, 1, 2 ...)の和を返す関数 tri を実装する
tri(n) = 0 + 1 + 2 + ... + (n -1) + n
デカい数を引数にして呼び出すと死ぬ
➡︎ いわゆる スタックオーバーフロー というもの
簡単な関数について考えていく
1 から n までの整数(0, 1, 2 ...)の和を返す関数 tri を実装する
tri(n) = 0 + 1 + 2 + ... + (n -1) + n
実行中のサブルーチンに関する情報をスタックに積んでいくため、
関数から関数を呼び出しまくるとスタックから溢れてしまう
簡単な関数について考えていく
サブルーチン内で計算が完結していればスタックフレームは生成されな
いので、単純に while ループで書けばよい
コードとしてはちょっと見通しが悪い?
再帰でこれを実現する方法はないのか?
末尾呼び出し
あるルーチンが、他のルーチンを呼び出して戻り値を受け取ったとして、
それを返す以外の操作をしないとき、末尾呼び出しであるという
⬆︎ 言葉にするとややこしいがコードを見れば単純
末尾呼び出し
Swiftの処理系は、最適化オプションを有効にすると
ほぼほぼ末尾呼び出しをスタックフレームを生成しない形の
アセンブリコードに変換してくれる
(これを末尾呼び出しの除去とよんだりするらしい)
最適化あり: swiftc –O /path/to/file.swift
最適化なし: swiftc –Onone /path/to/file.swift
つまり先ほどの例の関数も、
末尾呼び出しの形にしてあげれば
再帰を使った形かつ大きな引数で
関数を呼び出すことができるはず!
簡単な関数を末尾呼び出しにする
else 句のほうが、末尾呼び出しになっている。特に自分自身を再帰的に
末尾呼び出しするとき、末尾再帰呼び出しという。
もちろん playground 上では最適化がかからないので、
でかい引数で呼び出すと死ぬ
この実装には問題がある!
iOS/OSXプロダクト開発上、問題が発生することは稀だと思うが
一応難癖をつけると、以下のような問題がある(ことにしておく)
・ 実際に末尾呼び出し除去がかかるという保証はない
・ playgroundでデカい引数で呼び出したいとしても無理
実際、前者は抽象的なライブラリを設計する開発者にとっては若干悩ま
しい部分があるかもしれない...
再帰っぽいけど実はループな実装
を作れば問題が解決する
準備: フレームワークの実装
再帰っぽいコードをループに変換するための下準備
関数の実装部
普通の再帰っぽい雰囲気
引数を 100000 で呼び出しても死なない!!!!!!!!!
ちょっと未来のSwiftの話
@tailrecアノテーションの導入
・ Scalaと同様の @tailrec アノテーションの導入提案 issue
・ Swift3 では、ほぼ間違いなく入らない
・ しかし、Swift4 では入る可能性はある
https://github.com/apple/swift-evolution/pull/103/files
まとめ
・ Swiftには末尾再帰除去を想定したコードであることを
明示的に宣言するための言語機能が存在しない
・ ほぼ末尾再帰的な記述でスタックフリーな実装をする
フレームワークは簡単に自作できる
・ とはいえ日常のSwiftプログラミングでこれを考慮しなければ
ならないケースは非常にまれである
・ コンビネータを大量に含むライブラリを書くときは考慮が必要
・ Swift4 に向けて Scala の @tailrec 的なアノテーションが
入るかどうかの検討が進む見通し

この実装には問題がある!

Editor's Notes

  • #3 時間がないので飛ばす
  • #4 読み上げ