LoginSignup
32
29

More than 5 years have passed since last update.

タプルは用法用量を守って正しくお使いください

Last updated at Posted at 2016-03-31

タプルの用法用量

タプルは強力ですよね。使っているといろいろな場面で使いたくなってきます。

ただ使いすぎるのも微妙だと思っていたので用法用量をつくってみました。

タプルの用法用量

用法:

  • 関連するデータの組み合わせが欲しい時にご利用ください
  • あまり広いスコープでは使わないでください
  • なるべく可読性を保つようにしてください

用量:

  • 要素はなるべく6個までにとどめてください
    • 構造が複雑になる場合はstructclassのご利用をご検討ください

Swift2.2で追加された比較機能も考慮して6個としているのですが、なぜ要素数を6個に制限しているかについても調べました。それなりの理由があったのでこの理由についても書いています。

タプルを使うケース

まずはタプルをどのような場面で使うべきなのかを整理しようと思います。

Appleが出しているThe Swift Programming Languageでは以下のように説明されています。

Tuples are useful for temporary groups of related values. They are not suited to the creation of complex data structures. If your data structure is likely to persist beyond a temporary scope, model it as a class or structure, rather than as a tuple.

– Apple Inc. “The Swift Programming Language” iBooks https://itun.es/jp/jEUH0.l”

ざっくりと訳すとこんな感じです。

  • 一時的に使用する関連する値のグループを扱う際に有用(複雑なデータ構造には向いていない)
  • 一時的なスコープを超える(スコープが広い)ものはタプルよりもclassやstructを定義した方がいい

「一時的なスコープで複数個のデータの組み合わせが欲しいけど、structやclassを定義するまでもないような場合」に使うべき、そんな感じでしょうか。

タプルの可読性

タプルを実際に使う場合、以下のようなコードを書くことが多いと思います。

let http404 = (404, "Not Found")
print("code:\(http404.0)")
print("message:\(http404.1)")

上記の例だと、インデックスでタプルの各要素を取得していますが、各インデックスに何が入っているのか分かりづらいです。また、タプルは異なる型を入れられるので直感的に分かりづらくなっています。

キーワード

タプルの各要素にはキーワードをつけることができるのでこれを積極的に活用した方が良いと思います。

以下のようにキーワードをつけることができます。

let http404 = (code: 404, message: "Not Found")
print("code:\(http404.code)")
print("message:\(http404.message)")

インデックスでアクセスするよりも分かりやすくなると思います(インデックスでのアクセスができなくなるというわけではありません)。

また、キーワードがないタプルは以下のような代入が可能ですが、

var http403 = (403, "Forbidden")
var http404 = (404, "Not Found")

http404 = http403 // no error

キーワード名の異なるタプルは代入ができません(キーワードが同じであれば代入可能です)。

var http403 = (statusCode: 403, message: "Forbidden")
var http404 = (code: 404, message: "Not Found")

http404 = http403
// error: cannot assign value of type '(statusCode: Int, message: String)' to type '(code: Int, message: String)'

型チェックの恩恵も得られますし、可読性も上がるのでキーワードは積極的に使う方が良いと思います。

typealias

またtypealiasで別名をつけるとstructなどの構造を定義したかのように扱うことができます。

typealias Pokemon = (name: String, type: String)
let pikachu = Pokemon(name: "Pikachu", type: "Electric")

当然、メソッドやプロパティを定義はできませんが型が好きなSwiftプログラマーであれば使いたくなってしまうのではないでしょうか。

Swift2.2でのタプルの進化

タプルはSwift2.2でちょっと進化しました。

  1. 要素数が6個以下である
  2. 各要素がComparable, Equatableに準拠

以上の条件を満たしている場合、タプル同士の比較ができます。

let numbers1 = (1, 2, 3, 4, 5, 6)
let numbers2 = (1, 2, 3, 4, 5, 6)

numbers1 < numbers2 // false
numbers1 == numbers2 // true

タプルの要素が7個以上の場合はエラーになります。

let numbers1 = (1, 2, 3, 4, 5, 6, 7)
let numbers2 = (1, 2, 3, 4, 5, 6, 7)

numbers1 == numbers2 // error: binary operator '==' cannot be applied to two '(Int, Int, Int, Int, Int, Int, Int)' operands

また、タプルをタプルの中に入れれば7個以上も比較可能なのでは?と思ったのですが、タプル自体はComparable, Equatableに準拠していないのでできませんでした。

let numbers1 = (1, 2, 3, 4, 5, 6)
let numbers2 = (1, 2, 3, 4, 5, 6)

let numbersNumbers1 = (numbers1, numbers2)
let numbersNumbers2 = (numbers1, numbers2)

numbersNumbers1 == numbersNumbers2 // error: binary operator '==' cannot be applied to two '((Int, Int, Int, Int, Int, Int), (Int, Int, Int, Int, Int, Int))' operands

なぜ6個なのか?

比較する要素数として6個が上限に決められていますが、apple/swift-evolution の proposal SE-0015メーリングリストにその理由が記載されています。

The proposed arity here is 6, which is large enough for most reasonable tuples (but not as large as I'd prefer), without having massive code increase.

Swift自体の大規模なコードの増加をせず、かつタプルにとって最も合理的で十分な大きさである、みたいな感じでしょうか。

実際に要素数を12個まで増やした場合にどれくらいコード量が増えるかも記載されていました。

まとめ

要素数が6個に制限されている理由の一つとして、「合理的で十分な大きさ」であることが挙げられていましたが、基本的にタプルは複雑なことに使わないようにすることが肝心なのではないかと思っています。

なお、ここで取り上げている内容はpotatotips #27でもほぼ同じ内容を発表したので補足としてご覧ください。

もう一度、Appleが出しているThe Swift Programming Languageのタプルの説明を見てみます。

Tuples are useful for temporary groups of related values. They are not suited to the creation of complex data structures. If your data structure is likely to persist beyond a temporary scope, model it as a class or structure, rather than as a tuple.

– Apple Inc. “The Swift Programming Language” iBooks https://itun.es/jp/jEUH0.l”

They are not suited to the creation of complex data structures

やはり複雑なデータ構造には向いていないと書かれています。

If your data structure is likely to persist beyond a temporary scope, model it as a class or structure, rather than as a tuple.

またスコープをまたがったりするような場合はclassやstructを使うように言及されています。

Swift2.2から追加された要素の比較は6個が上限というのは、6個を超えたらタプル以外での定義を考えた方が良い、という指針にもなるのではないでしょうか(要素が6個を超えるタプルを作るべきではない、という意味でありません)。

最後にもう一度用法用量を整理します。

タプルの用法用量

用法:

  • 関連するデータの組み合わせが欲しい時にご利用ください
  • あまり広いスコープでは使わないでください
  • なるべく可読性を保つようにしてください

用量:

  • 要素はなるべく6個までにとどめてください
    • 構造が複雑になる場合はstructclassのご利用をご検討ください

これくらいの方針があると使い過ぎへの対策になるのではないでしょうか。

32
29
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
32
29