subscriptキーワード
本連載第1回で取り上げたArray、Dictionaryには、次のように [] で中身にアクセスしたり更新したりする機能が備わっていました。
var animals = ["dog", "cat", "elephant"] // [String] animals[1] // "cat"を取得 var countries = ["JPN" : "Japan", "USA" : "America"] // [String: String] countries["USA"] = "United States" // "USA"のキーに対応する値を更新
この機能をsubscriptといいます。自分で作成した型にsubscriptを宣言することでArrayのような[]を通じての中身のアクセスや更新の機能をカスタマイズすることができます。また、[]に入れる中身の型を変えることでsubscriptのオーバーロードを行うことも可能です。
subscriptの宣言方法は、プロパティのget/setとメソッドを組み合わせたような構文をとります。
subscript(引数のリスト) -> 返り値 { get { 文 return 返り値 } set(新たな値) { 新たにセットされた値を元に計算 } }
宣言の例を見てみましょう。
struct Page { let numberOfLines: Int let numberOfCols: Int private var contents: [String] init (numberOfLines: Int, numberOfCols: Int) { self.numberOfLines = numberOfLines self.numberOfCols = numberOfCols let emptyLine = String(count: numberOfCols, repeatedValue: " " as Character) self.contents = Array(count:numberOfLines, repeatedValue:emptyLine) } subscript(line: Int) -> String { get { return contents[line] } set (newLineContent) { let newContentCols: Int = countElements(newLineContent) if newContentCols <= numberOfCols { let newLine = newLineContent + String(count:numberOfCols - newContentCols, repeatedValue:" " as Character) contents[line] = newLine } } } } var somePage = Page(numberOfLines: 4, numberOfCols: 10) somePage[1] // " " somePage[1] = "content" somePage[1] // "content "
このサンプルコードは本のページを表したもので、行数と1行に入る文字数を指定できます。
subscriptキーワードの後にはメソッドの引数リストと、返り値の宣言のように [] の中に渡したい引数の型のリスト、および代入と取得をしたい型を記述します(private修飾子は次の節で解説します)。 その後で、計算プロパティのget/setの宣言のように、getとsetで取得時および代入時の操作を記述します。
なお、この例では [] の中で引数を1つしか渡していませんが、次のようにsubscriptを宣言することで、引数を2つ渡すこともできます。
struct Book { let name: String let numberOfPages: Int let numberOfLines: Int let numberOfCols: Int private var contents: [Page] init (name: String, numberOfPages: Int, numberOfLines: Int, numberOfCols: Int) { self.name = name self.numberOfPages = numberOfPages self.numberOfLines = numberOfLines self.numberOfCols = numberOfCols let emptyPage = Page(numberOfLines: numberOfLines, numberOfCols: numberOfCols) self.contents = Array(count:numberOfPages, repeatedValue:emptyPage) } subscript(page: Int, line: Int) -> String { get { return contents[page][line] } set { contents[page][line] = newValue } } } var someBook = Book(name: "some", numberOfPages: 4, numberOfLines: 40, numberOfCols: 20) someBook[1, 2] // " " someBook[1, 2] = "p one l2 content" someBook[1, 2] // "p one l2 content "
このサンプルコードは本を表したもので、本の名前とページ数、およびページの行数と1行に入る文字数を指定できます。
subscriptは、Optional列挙型を返すときにはメソッドのようにみなして、オプショナルチェイニング[1]することも可能です。
struct BookShelf { private var books: [String: Book] init (books: Book...) { var bookDict: [String: Book] = Dictionary() for book in books { bookDict[book.name] = book } self.books = bookDict } subscript(name: String) -> Book? { return books[name] } } var bookShelf = BookShelf(books: someBook) bookShelf["some"]?[1, 2] // "p one l2 content " bookShelf["any"]?[1, 2] // nil
このサンプルコードは本棚を表したもので、本の名前をキーにして本を貯めこむことができます。
[1] オプショナル列挙型を返すメソッドやプロパティを「.」(ピリオド)つなぎで次々に呼び出していくことです。「.」の前に「?」を置くことでそれが可能になります。