1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Swift] first last second third

Last updated at Posted at 2023-09-13

first, last はともかくとして、second, third って、何じゃ?

タイトルを見て奇異に感じた方が多いと思います

至極単純な発想で、配列の先頭の要素をfirstで参照できるならば、二番目、三番目の要素をsecond, third で参照できたら、コードが読みやすいかな? って。

今回は、そんな配列に関する内容です。

first, lastComputed property(計算型プロパティ)ですが、ここでは単に first, lastと呼び捨てにします。

また、分かりやすい説明にしたいため、厳密な型やプロトコルの定義に基づく解説とは、少し外れるところもありますが、ご容赦ください。

first, lastの正体

Swiftには、CollectionCollectionプロトコルを継承した型)の代表として、Array, Set, Dictionaryがありますが、lastが在るのは、Arrayだけです。firstはどれも持っていますが。

まず、firstがどこで定義されているかというと、Collectionプロトコルです。
extension Collection
  public var first: Element? {
    let start = startIndex
    if start != endIndex { return self[start] }
    else { return nil }
  }

startIndex == endIndex とは、要素が一つも無い、空のコレクションの場合

Collectionは同じ属性のオブジェクトを「ある規則」により塊(かたまり)として保持するデータ構造(プロトコル)です。「ある規則」の部分が、Array, Set, Dictionaryの特徴そのものです。
Array, Set, Dictionaryは、Collectionプロトコルに準拠しているため、どれもfirstを持っています。

次に、lastがどこで定義されているか調べてみると、BidirectionalCollectionプロトコルです。Collectionにはありません。
extension BidirectionalCollection
  public var last: Element? {
    return isEmpty ? nil : self[index(before: endIndex)]
  }

こちらは isEmpty で空のコレクションを判定しています

BidirectionalCollectionはその名の通り、先頭からと最後からの双方向からアクセスできるコレクションです。
Arrayは、さらに、添え字を使ってランダムにアクセスできますね。

一方、Set, Dictionaryには、何番目といった『順序』の概念がありません1。だから、lastがないのです。
要素を管理する上で「先頭」は当然知っています。「最後」は要素を順にたどっていけば分かるでしょうが、計算量が問題です。lastは$O(1)$が要件のため、$O(N)$とか$O(logN)$かかるlastは認めないということでしょう。

Set, Dictionaryには『順序』はありませんが、順番にアクセスすることはできますね。この場合の順番は、「一つづつ順に取り出せる」という意味の順番です。for-inでイテレートできるということです。これは、Sequenseプロトコルに準拠しているからです。

なお、ここで取り出される要素の順序は、要素を追加した順序とは無関係であることを、付け加えておきます。

プロトコルの継承関係は↓こんなイメージです(イメージのため、本当の継承階層とは違います)。




前置きが長くなりましたが、本題に入ります。

second, thirdを定義する

前述の通り、順序付けの概念がArrayしかないため、Arrayextensionで定義しました。

extension Array {
    var second: Element? { self.count >= 2 ? self[1] : nil }
    var third: Element? { self.count >= 3 ? self[2] : nil }
}

見てのとおり、至ってシンプルです。

配列の先頭と二番目の要素を比較したいときは、
if array.first! > array.second! { ・・・ } みたいに使います。

thirdはおまけで、以降、tenぐらいまで用意してもいいですが、それほど使い道は無いかな?と思います。

想定した使い方

SwiftのタプルがHashableでは無いため、タプルをSetの要素やDictionarykeyにすることができません。そんなとき、タプル内の要素が同じ型の場合2は、Arrayで代替しています。
その場合、タプルの場合だとtuple.0, tuple.1アクセスしますが、配列で代用したときは、array.first, array.secondでアクセスします(C++pair型を真似た)。

必ず要素が存在していることが分かっている(場合に使用する)ので、オプショナル型をやめて単純にします。

extension Array {
    var first: Element { self[0] }
    var second: Element { self[1] }
    var third: Element { self[2] }
    var last: Element { self[self.count - 1]  }
}

最後から二番目

lastは最終要素ですが、最後から二番目(n番目)の要素をアクセスしたい場合もありますね。
そんな時のために、以下のextensionも便利です。

extension Array {
    subscript(last last: Int) -> Element {
        get {
            //precondition(self.indices.contains(last), "out of bounds")
            return self[self.endIndex - last - 1]
        }
        set {
            //precondition(self.indices.contains(last), "out of bounds")
            self[self.endIndex - last - 1] = newValue
        }
    }
}

最終の要素はarray[last: 0]、最後から二番目はarray[last: 1]で、
array[last: array.count - 1]は先頭の要素です。

IMG_0041.jpg

(後ろからの添え字も0〜としたため、pythonのマイナス添え字とは異なります)

使う頻度は少ないですが、有ると便利です。




もう一つ、おまけ

循環バッファのアクセス

下記のsubscriptは、循環バッファのように、添え字が要素数を超えたら、先頭に移ってアクセスができるものです。逆に添え字がマイナスになると、今度は要素の最終に移ります。これは、pythonのマイナス添え字に似た考えです。

extension Array {
    subscript(ring ring: Int) -> Element {
        get { self[((ring % self.count) + self.count) % self.count] }
        set { self[((ring % self.count) + self.count) % self.count] = newValue }
    }
}

以下の実行例をみていただくと、ちゃんと循環していることが分かると思います。

IMG_0040.png

以上です。何かの参考になれば嬉しいです。

  1. 最近は、順序付きの Set, Dictionary (OrderedSet, OrderedDictionary)や 要素の並び順を保証した Set, Dictionary (SortedSet, SortedDictionary)のライブラリが、swift-collectionsとして公開されています。

  2. タプル内の要素の型が異なる場合は、専用の構造体(class/struct)を定義してHashableに準拠する必要があります。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?