Swift Advent Calendar 2015 で勉強してみる(2日目)
Swift プログラミング
Swift Advent Calendar 2015 が暖かい感じに盛り上がっていて好きだったので、自分もこれを眺めながら Swift をあれこれ勉強してみることにしました。
その2日目です。
そこで、開催されている4つの Swift Advent Calendar の内容を拝見して、そこからいろいろ浮かんだことを綴ってみようと思います。
- Swift Advent Calendar 2015
- Swift その2 Advent Calendar 2015
- Swift その3 Advent Calendar 2015
- Swift(一人) Advent Calendar 2015
Swift Advent Calendar
Swift Advent Calendar の2日目は mo_to_44さん の Swiftのmap, filter, reduce(などなど)はこんな時に使う! というお話でした。
きっと3種の神器
そんな話題の中心になることが窺える map
, filter
, reduce
といえば Swift でよく使われるメソッドな印象がします。
Advent Calendar 記事内のように、自分も最初にこれらの関数を見て驚き、そして 使いどころのずいぶん難しい関数だなー と感じて、それがいつしか すごくいい! 関数という認識になっていたものだから、今回の記事内に描かれている こういう時にはこれを使う! という趣旨がとっても、良い着眼点だなーと早くも感心しながら拝見してました。
ちなみにこの Advent Calendar 記事内では map
, filter
, reduce
の他にも次の関数の使い方が紹介されていて盛りだくさん!です。
forEach
enumerate
minElement
maxElement
dropFirst
dropLast
flatMap
メリット
そして 次の項 で挙げられていたメリットを読んで、これもいい着眼点だなって思いました。
特に『無駄な空配列を用意しなくて済む』というのと『コードの書き方が変わる』というのが良かったです。
これらの2つって、意識して使い出して初めて感じる良さな気がして、これが地味に、そしてかなり、効いてくる感じがするんですよね。何気ないところをこうやって感じて言葉で表せるって、すごいです。
let 縛り
そしてこの2つを拝見していて思い出したのが、Swift で let
縛りをして遊んだ頃の記憶です。
Swift には、従来の変数 var
に加えて、値を扱うのに最適な let
という変数が登場しました。
そしてこの2種類ある変数のうち、この let
だけしか使ってはいけないみたいな独自ルールを勝手に作ってコードをいろいろ書いてみた時、従来の考え方だけだと暗礁に乗り上げたり、無理やり再帰呼び出ししないといけなかったりします。
そんな時、この map
, filter
, reduce
を思い出して使ってみたら、変数 let
だけでコーディング出来る場面がかなり広がったのを覚えています。
つまり、無用な空配列(配列を使った計算で、新しい結果を入れるために空の配列を用意する = ここに新しい値を順次入れていく)が不要になり、コードの書き方が変わる(変数 let
だけでも組み立てられるようになる)みたいなことが、自分も体感として得られました。
わかりやすさも大事
そして Advent Calendar 記事内の 書きやすさや分かりやすさの点で、目的に適した関数を使うことが大事
というお話も好感が持てました。
そう、ちょっと話が脱線しますけど、たぶん map
は map
意外にない気がするんですよね。
これがたとえば、
numbers.map(toString)
みたいになって初めて読めるコードになると思います。これくらいの規模なら断然 map
の方が見やすいと思うんですけど、これが長く連なりすぎたりすると、読みづらいコードになりそうに思います。
あまり長く map
を連ねるよりは、意味ごとにひとつメソッドを作った方がいいんじゃないかなと、今のところは想像してます。
話が逸れましたが、話を戻して先ほど map
を見てもわからないみたいなことを書きましたけど、map
そのものには あるインスタンスが扱う内容物に任意の関数を適用し、新たなインスタンスを作り出す
という意味があると思うんですよね。
そのため map
の戻り値を無視するというのは、その意味を汲み取る流れを突然断ち切るように感じます。
そういう面でも、最後が打ち切る場面であるなら 内容物それぞれに対して処理を適用する
的な意味合いと思われる forEach
を使うのが自然な在り方のように感じます。
@warn_unused_result
map
や filter
の使い道は Advent Calendar 記事内を見ていただくとして、この中で特にいいなと思ったのは @warn_unused_result
属性のお話でした。
この @warn_unused_result
属性は、記事内にも説明がある通り 戻り値が使われなかった時にコンパイラーが警告する
ことを意味するものです。
これが指定されていない普通の関数の場合、戻り値を返す関数を実行した時に、その戻り値を変数に受けずに無視しても何も言われません。それが、この属性を指定することで、変数に受けないと警告が発生するようになります。
@warn_unused_result の使いどころ
つまり、これを使えば コンセプト的に、戻り値の値を使ってもらわなければ困る ことをコードに明記することができるようになります。
使ってみた例としては例えば あるメソッドを呼ぶとハンドルを返して、それを使用中は保持して最後に release しなければいけない みたいな時に @warn_unused_result
をしておくことで、ハンドルをうっかりキープし忘れるみたいなことを、コンパイラー警告を使って予防できます。
警告時に自由にメッセージをそれる
しかもこの時 @warn_unused_result
属性に message
を添えると、警告発生時にそのメッセージを表示させることができます。
@warn_unused_result(message="Need to keep while using and release after use.")
こうしておけば、なぜ戻り値を使わなければいけないのかが明確になって、さらに適切なコード記載を促せそうです。
flatMap
この Advent Calendar 記事内でひとつだけ 説明が難しい
として紹介されていた flatMap
が印象的でした。
たしかに、慣れてくるとなんとなく便利に使え始める flatMap
いざそれが どういうコンセプトで作られているか
を考えてみると、うまく説明をまとめることって難しそうです。
感想
この辺りはきっと Haskell みたいな関数型プログラミングを修得する上で身につきそうなきがするし、そうすることで世界が広がることは間違いなさそうですけど、もし使い道も理屈も分からないなら、そしてそれだけ難しい概念なのなら、まずは『概念はわかるけど使えない』よりは『概念は分からないけど使いどころはわかる』みたいな方を目標に置いてもバチは当たらないような気がします。
上記は極論なので言い過ぎかもしれないですけど、ともあれ今のところの Swift の世界観で見た flatMap
はとりあえず 専用のプロトコル上に定義されたもの(flatMap という概念)
ではなく、単純に SequenceType
や Optional
に備えられたメソッドのひとつな印象なので、それらにクローズアップして動きを理解するというだけでも、今のところは手に負えるのかな?というのが理由です。
自分も flatMap
についてはぜんぜん なんのために在るか
を説明できる知識がないので、その辺りをそろそろちゃんと学ばないといけないなと、この Advent Calendar 記事を拝見していて思わされました。
Swift その2 Advent Calendar 2015
Swift その2 Advent Calendar 2015 の2日目は mito_logさん の socket.ioをネタにJavascriptCoreに入門してみる with Swift というお話でした。
Socket.IO ってなんだろう?という思いと、JavaScriptCore 大好き!という思いが交錯する、個人的にとても興味深い Advent Calendar 記事です。
タイトルだけ見ると何やら難しそうなことをしているようにも捉えてしまいますけど、実際に読んでみるとむしろ JavaScriptCore 実践のためのイントロダクション みたいな感じで、気軽に読める内容でした。
試みについて
そもそもの試みとして挙げられていた内容は、なるほど『かつて Objective-C 用に提供されていた MegaBits/SIOSocket の実装は、内部で JavaScriptCore を活用して JavaScript を上手に活かしたものになっていたから、その実装技術の可能性を Swift でも確かめてみたい』みたいな感じみたいです。
たしかに JavaScriptCore を使って JavaScript コードと Swift コードを垣根無くブリッジできたら嬉しそうですよね。もっとも、それが JavaScriptCore のメインの役割なのですけれど、それの汎用的なプラクティスを模索する試みはあまりされてない気がして、興味がそそられます。
使うための準備から
この Advent Calendar 記事内では JavaScriptCore を使う上での準備から、そこで使われる各種機能の説明までがかなりしっかり行われているのが見所に感じました。
そして自分もだいぶ前になりますけれど Swift 1 の頃に JavaScriptCore の基本的なところをまとめたスライド JavaScriptCore.framework の普通な使い方 #cocoa_kansai をアップしてあるので、それとこの記事をとりあえず眺めれば JavaScriptCore の基本的なところはおさえられるんじゃないかなと思います。
自分のスライドは古いので、少しばかりコードを読み替えないといけないところも出てきそうですけど、とりあえず感覚的には書いた通りに使えているので、Advent Calendar 記事と合わせて見てもらえればなんとかなるでしょう。
所感
ひととおり JavaScriptCore の説明が終わった後に SIOSocketをSwift製にする上で気づいた点など として、実際に試してみての所感がまとめられていて、なかなかお目にかかれない JavaScriptCore の実践話というのもあって良い感じでした。
特に JavaScriptCore で UIWebView
を扱うところとか self キャプチャ時の癖みたいなところが、個人的には面白く感じたところでした。
他にもたくさん興味深い体験談が記されているので、実際に JavaScriptCore を導入したいと思ったときに是非読んでおきたい記事だと感じました。
Swift その3 Advent Calendar 2015
Swift その3 Advent Calendar 2015 は初日が空席で、この2日目が最初の回になりますけれど、この日は krimpedanceさん の IllustratorCC で描いたオブジェクトを UIBezierPathに書き出す というお話でした。
趣向としては『Illustrator 用の拡張機能 Drawscript は便利だけれどちょっと惜しいところがある。だったら自分で作っちゃおう!』といった感じ。コードで語る感じが印象的でした。
Illustrator 拡張機能
自分は Illustrator に触れたことがないのですけど、ともあれ Adobe Extension Builder というのを使うと、その拡張機能を作ることができるみたいです。
HTML と CSS、それに JavaScript を使って作成できるそうで、記事内に掲載されているコードも全て JavaScript で記されていました。実際に使えるコードみたいですし、ボリューム的にもコンパクトなので 自分も Illustrator 機能拡張を作ってみたいな という人にちょうど良さそうな内容です。
ここで作成した機能拡張を使うと、Illustrator から Swift ですぐに使える UIBezierPath
コードを作成できるとのことでした。
アイコンセット
実際にこの機能拡張を使って作成したアイコンセット krimpedance/KRIcon で公開されているとのことなので、興味がある人は眺めてみるといいかもしれません、というか自分自身がちょっと興味ありです。
このプロジェクトにはライセンス表記が置かれてないので、まだ自由には使えなそうな感じですけど。
Swift(一人) Advent Calendar 2015
Swift(一人) Advent Calendar 2015 は全日を shimesabaさん が埋め尽くすという素晴らしさですけれど、その第2日目は autoclosureというマニアックで少し面白い機能 というお話でした。
autoclosure
面白いですよね。この Advent Calendar 記事内では、そんな autoclosure
とはどんなものかから、それが実際に使われている事例の紹介と、それについての考察が記載されています。
もともとイメージを掴みづらい機能なので、この記事を1、2回軽く読んだくらいでは理解できないかもしれないですけど、この記事は順を追って様子観察してくれてるので、実際に Playground でコードの動きを確かめながら学んでみると、少しずつ掴めてくると思います。
具体例(その2)
せっかくなので Advent Calendar 記事内の例とは別の例を挙げておきます。
まず、Advent Calendar 記事内とダブりますけど autoclosure
のメリットとしては 引数として渡した値を自動的に関数で包んで、その値が必要になった時に初めてその関数を使って取得する
みたいな感じなので、つまり 引数に渡した値を実際に使うか使わないかが状況によって変わる
みたいなときに使うと効果的です。
真偽に応じて返す値を切り替える
たとえば、次のような関数を作ってみます。
func selectValue(condition:Bool, trueValue: Int, falseValue: Int) -> Int {
if condition {
return trueValue
}
else {
return falseValue
}
}
こうしたときに、次のようにしてこの関数を利用できます。
let c = selectValue(a > 0, b * 100, b * -100)
これで、変数 a
が 0 より大きいときは b を 100 倍した値
が、そうでないときは b を -100 倍した値
が結果として得られます。
ただ、今のこのコードの書き方の場合、関数 selectValue
に3つの引数を渡そうとするタイミングでそれぞれが計算されて、その計算済みの値を使って selectValue
の内容が処理されます。
計算を、必要になるタイミングまで遅らせる
今回の関数であれば、第1引数に渡した条件式が真のときには第1引数だけが使われて、偽のときには第2引数だけが使われるので、両方を計算するのは処理の無駄が増えてもったいないです。
今回の例であればちょっとした無駄で済むのでまだいいですけど、これがそれぞれ計算に5分かかるみたいな場合は無駄が無視できなくなってきます。
そんなときに autoclosure
が威力を発揮します。
関数定義の書き方は 引数で受け取りたい型を戻り値に返す引数を取らないクロージャーを受け取り、そのクロージャーの前に @autoclosure を付与する
形になります。
つまり第2引数であれば trueValue:Int
だったのが
@autoclosure trueValue: () -> Int
という書き方になります。
具体的には次のようなイメージの変数になります。ここの _trueValue
が、実際に引数に渡した『式』になると思ってください。
let trueValue = { () -> Int in
return _trueValue
}
第3引数も同様にして書き換えると、関数全体は次のように書き換わります。
この時、これまで普通の Int
型の変数として受け取っていた trueValue
と falseValue
は、これからは
@autoclosure trueValue: () -> Int
になるので、使うときには包まれた値を取り出すために 変数名に引数リストを添えて実行
します。
func selectValue(condition:Bool, @autoclosure trueValue: () -> Int, @autoclosure falseValue: () -> Int) -> Int {
if condition {
return trueValue()
}
else {
return falseValue()
}
}
こうすることで、条件式 condition
が真のときに初めて trueValue
で渡した式が実行されて、偽のときに初めて falseValue
で渡した式が実行されます。
今回の例であれば、使わない方の式はそのまま使わず終わるので、両方の式が計算されることはありません。
もちろん、条件によってはどちらとも使わないみたいな処理を記述することも可能です。
func selectValue(condition:Int, @autoclosure trueValue: () -> Int, @autoclosure falseValue: () -> Int) -> Int {
if condition > 0 {
return trueValue()
}
else if condition < 0 {
return falseValue()
}
else {
return 0
}
}