Help us understand the problem. What is going on with this article?

Swift3.0では使い慣れたfor文が使えなくなります

More than 1 year has passed since last update.

Swift3.0ではC言語のようなfor文が使えなくなります

Swiftがオープンソースになり、現在のSwift2系からSwift3系にバージョンアップする際にどのようなアップデートがあるかを把握できるようになりました。

バージョンアップに盛り込まれる機能、削除される機能はどのように決められるかというと、以下のような流れで決められているようです。

  1. 提案
  2. レビュー
  3. 採択

これらの提案はApple内部の方だけでなく、Apple社外のユーザーからも提案が可能になっています。

これらの提案の中で特に象徴的なことの一つとして、C-Styleのfor文が削除というものがあったのですが、Swift3.0で削除されることが正式に決まっています。

このproposal(提案)はApple社外のErica Sadun氏からの提案であり、具体的には以下のような構文が使えなくなるということを意味しています。

for var i = 0; i < 100; i++ {
    print("\(i)")
}

Objective-Cでもよく使われる構文であり、これがなくなるのはかなりの衝撃だと思います。

おそらくSwiftで書かれているプロダクトコード内にもこのスタイルで書かれた構文がいくつかあると思います。

あまりにも使い慣れた構文であり、これがなくなることによって書けないものもあるんじゃないのか?という疑問も当然浮かんでくるかと思います。

安心してください!書けますよ。

そして、これからSwiftで書くソースコードでこのことを考慮して書いておくだけでSwift3.0が出た際にそれに伴う修正を行う必要がなくなります。

今まではWWDCで発表されるまでどのような変更がなされるか分かりませんでしたが、オープンソース化によって今からでも対策を練ることができます。

サンプル

それではいくつかのサンプルを見てみましょう。

単純なfor文

非常に単純なfor文です。Swift3.0では以下の構文が書けなくなります。

for var i = 0; i < 100; i++ {
    print("\(i)")
}

これはfor-inを使うことで解決できます。以下のコードは上記のコードと同じ意味になります。

for i in 0..<100 {
    print("\(i)")
}

また、このような書き方もできます。forEachについてはこちらの記事にも記載しています。

Array(0..<100).forEach {print("\($0)")}

このような非常に単純なものであれば、従来のfor文よりもより簡潔に書くことができるようになっています。

インデックスを利用したい場合

配列内のインデックスが大きな意味を持つ場合があります。例えば奇数のインデックスの要素のみを取得したい場合はどうでしょうか?

Swift3.0から使えなくなるインデックスの取得

従来の書き方であればこのように書いていたでしょう。

let array = ["a", "b", "c", "d"]
for var i = 0; i < array.count; i++ {
    if (i % 2) != 0 {
        print("\(array[i])") // b, d
    }
}

これまで述べてきたとおり、この文法はSwift3.0では使用できなくなります。しかしSwiftにはenumerateという便利なメソッドがあります。

enumerateを使用したインデックスの取得

enumerateはインデックスと要素のタプルの配列を生成してくれるSequenceTypeに定義されたメソッドです。

enumerateを使用すると、以下のように書くことができます。

let array = ["a", "b", "c", "d"]
for (index, element) in array.enumerate() {
    if (index % 2) != 0 {
        print("\(element)") // b, d
    }
}

for-inを使用せずに以下のように書くことも可能です。

let array = ["a", "b", "c", "d"]
array.enumerate().filter { ($0.0 % 2 != 0)} .map { $0.1 }
array.enumerate().flatMap { ($0.0 % 2 != 0) ? $0.1 : nil }

mapfilterなどについてはこちらの投稿で詳しく解説していますのでご参照ください。enumerateについても触れています。

strideを利用した方法

インデックスに応じた要素を取得するには、以下のようにstrideを利用する方法も良いかと思います(@uasiのコメントより)。

let array = ["a", "b", "c", "d"]
for i in 1.stride(to: array.count, by: 2) {
    print("\(array[i])") // b, d
}

まとめ

あのfor文がなくなるなんて!という衝撃はあるものの、mapfilterenumerateなど見慣れなくてとっつきにくいのですが、これらのSwiftの標準ライブラリに用意されているものを利用すれば実はあまり困ることがありません。

従来のfor文を標準ライブラリに用意されているメソッドで書き換えてみようとすると、従来の記述が冗長であることもなんとなくわかってきます。

Swift3.0に向けて今までのfor文を使わないように書き換えていきましょう!

その他参考資料

第2回shibuya.swiftでは、この内容についてLTを行いました。

mercari
フリマアプリ「メルカリ」を、グローバルで開発しています。
https://tech.mercari.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away