Swiftで配列を宣言するには、
Array<要素>
か[要素]
と書きます。例えば、Int
型の配列ならArray<Int>
か[Int]
です。どちらの書き方でも意味はまったく同じ。
これだけだと、配列型
の宣言ですから、変数や定数とするためには、これに配列のサイズや要素を指定する必要があります。これには、大きく2通りの書き方があります。
一つ目の書き方は、サイズ0(空の)配列を定義して、その後、append
により、要素を追加していく方法です。(Array
のappend
は高速です。$O(1)$)
Int
型の空の配列を定義する書き方として、下記に4つあげましたが、これまた、この4つはどれも同じ意味です。(a3, a4は型推論)
var a1: Array<Int> = []
var a2: [Int] = []
var a3 = Array<Int>()
var a4 = [Int]()
こちらは、後からappend
するために、必ずvar
で宣言するため、厳密には定数
になり得ません。
二つ目の書き方は、あるサイズの配列を定義して、どの要素も同じ値で初期化する方法です。
Int
型の要素10
個の配列を定義して、どれも値を-1
にする書き方として、下記に4つあげますが、ご多分に漏れず、この4つもどれも同じ意味です。
let size = 10, value = -1
var a1: Array<Int> = Array(repeating: value, count: size)
var a2: [Int] = Array(repeating: value, count: size)
var a3 = Array<Int>(repeating: value, count: size)
var a4 = [Int](repeating: value, count: size)
こちらは、let
で宣言することもできるため定数
になり得ますが、用途はあまりないかも。
詳しい説明は省略しますが、ここで注意しなければいけないこととして、class型の配列をArray(repeating:count:)
で定義すると、各要素のインスタンスはどれも同じになるということです。
では、それぞれの要素の値が異なる配列の定義はどうすればよいでしょうか。
要素をその個数分並べて書くことができます。しかし、要素の数が10、20はよいですが、何百、何千となると、書くのが大変でしょう。
let a5 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
また、Array(_:)
を使えば、例えば、Int
型の0
〜999
の千個の配列は、次のように書くことができます。
let a6 = Array(0 ..< 1000)
//[0, 1, 2, 3, 4, ・・・ , 996, 997, 998, 999]
これに、map
を組み合わせると、使い方の幅が広がりますね。
let a7 = Array(0 ..< 1000).map { $0 * 3 } //3の倍数
//[0, 3, 6, 9, 12, 15, 18, 21, 24, 27, ・・・]
ですが、もっと一般化した配列の定義が欲しいと思いませんか?
そこで、Array
のextension
を定義してみました。
配列の定義の一般化
よく使う配列の型
(これは本題ではありませんが、)よく使う配列の型として、別名をつけました。
typealias IntArray = Array<Int>
typealias DoubleArray = Array<Double>
typealias BoolArray = Array<Bool>
typealias StringArray = Array<String>
簡単に配列を宣言できるextension
以下のextension
を使うと、配列の宣言が簡略化できます。
extension Array where Element: FixedWidthInteger {
init(_ size: Int) { self.init(size, .zero) }
}
extension Array where Element: BinaryFloatingPoint {
init(_ size: Int) { self.init(size, .zero) }
}
extension Array where Element: ExpressibleByBooleanLiteral {
init(_ size: Int) { self.init(size, false) }
}
extension Array where Element: StringProtocol {
init(_ size: Int) { self.init(size, "") }
}
extension Array {
init(_ size: Int, _ value: Element) {
self = [Element](repeating: value, count: size)
}
}
整数型、浮動小数点型、ブール型、文字列型を要素とする配列を簡単に書けます。
要素の初期値を省略すると、整数型は0
、浮動小数点型は0.0
、ブール型はfalse
、文字列型は""(空文字列)で初期化します。
let N = 5
var a8 = [UInt32](N) //UInt32型のN個の配列、初期値は0
var a9 = [CGFloat](5, -1) //CGFloat型の5個の配列、初期値は-1.0
var a10 = BoolArray(10, true) //Bool型の10個の配列、初期値は`true`
var a11 = StringArray(3) //String型の10個の配列、初期値は""(空文字列)
//初期値を指定すれば、任意の型の配列を宣言できる
var a12 = [CGPoint](10, CGPoint(x: -1, y: -1)) //CGPoint型の10個の配列、初期値は`(x: -1, y: -1)`
var a13 = [Int?](N, nil) //Optional<Int>型のN個の配列、初期値は`nil`
任意の初期値を指定できますが、初期値がリテラルで無い場合は、次に説明するextension
を使います。
一般化した配列の宣言
以下のextension
を使うと、配列の宣言をさらに柔軟に書くことができます。
extension Array {
init(_ size: Int, _ initValue: ((Int) -> Element)) {
self = [Element]()
for index in 0 ..< size {
let value = initValue(index)
self.append(value)
}
}
init(_ size: Int, _ initValue: (() -> Element)) {
self = [Element]()
for _ in 0 ..< size {
let value = initValue()
self.append(value)
}
}
}
あらゆる型の配列の初期値を要素ごとに書くことができます。
//let a7 = Array(0 ..< 1000).map { $0 * 3 } //と等価の書き方
let a14 = IntArray(1000) { $0 * 3 }
//Int型タプルのN個の配列、初期値は`(インデクス, 偶数)`
let N = 10
let a15 = [(Int, Int)](N) { n in (n, n * 2) }
//[(0, 0), (1, 2), (2, 4), (3, 6), (4, 8), (5, 10), (6, 12), (7, 14), (8, 16), (9, 18)]
//String型の10個の配列、初期値は標準入力(改行区切り)から取得
let a16 = StringArray(10) { readLine()! }
//Int型のN行、M列の(二次元)配列、初期値は標準入力(スペース区切り)から取得
let N = 3, M = 4
let a17 = [[Int]](N) { readLine()!.split(separator: " ").map { Int($0)! } }
//Int型のN行、X列の二次元配列、列方向の初期値は`空配列`。要素は後から追加していく。
var a18 = [[Int]](N) { [] }
//a18[row].append(col)
//CGPoint型の10個の配列、初期値は標準入力(スペース区切り)から取得
var a19 = [CGPoint](10) {
let xy = readLine()!.split(separator: " ").map { CGFloat(Double($0)!) }
return CGPoint(x: xy[0], y: xy[1])
}
汎用性があり過ぎて、例を出すのが難しいほどです。
class型の配列も、要素ごとにインスタンスを生成するため、安心して使うことができます。
おまけ
以下のextension
を使うと、Array
へのappend
が+=
で書けて便利です。
extension Array {
@inlinable static func += (lhs: inout Self, rhs: Self.Element) { lhs.append(rhs) }
}
var a20 = [Int]()
//(0 ..< 10).forEach { a20.append($0) } //と等価
(0 ..< 10).forEach { a20 += $0 }
//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
まあ、このextension
が無くても、もともと次のようには書けますが、扱いは、配列の結合ですから、上のextension
の方が性能はよいはずです。
var a20 = [Int]()
(0 ..< 10).forEach { a20 += [$0] }
//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
おまけ2
2D Array(任意の型のN行、M列の二次元配列)をアクセスする場合は、array[row][column] と書きますが、これを array[row, column] と書ける extension です。
array>型の extension 定義に苦労したので、備忘録を兼ねて残しておきます。同じ手法で x, y, z の三次元配列のアクセサを定義することも可能です。
extension MutableCollection where Self.Element: MutableCollection {
@inlinable subscript(_ row: Self.Index, _ column: Self.Element.Index) -> Self.Element.Element {
get {
//precondition(self.startIndex ..< self.endIndex ~= row)
//precondition(self[row].startIndex ..< self[row].endIndex ~= column)
return self[row][column]
}
set {
//precondition(self.startIndex ..< self.endIndex ~= row)
//precondition(self[row].startIndex ..< self[row].endIndex ~= column)
self[row][column] = newValue
}
}
}
let keywords = "associatedtype,borrowing,class,consuming,deinit,enum,extension,fileprivate,func,import,init,inout,internal,let,open,operator,private,precedencegroup,protocol,public,rethrows,static,struct,subscript,typealias,var,break,case,catch,continue,default,defer,do,else,fallthrough,for,guard,if,in,repeat,return,throw,switch,where,while,Any,as,await,catch,false,is,nil,rethrows,self,Self,super,throw,throws,true,try,#available,#colorLiteral,#else,#elseif,#endif,#fileLiteral,#if,#imageLiteral,#keyPath,#selector,#sourceLocation,#unavailable".split(separator: ",").map { Array($0) }
print(keywords[1]) //["b", "o", "r", "r", "o", "w", "i", "n", "g"]
print(keywords[1, 3]) //r
print(keywords[3]) //["c", "o", "n", "s", "u", "m", "i", "n", "g"]
print(keywords[3, 3]) //s
おわりに
[任意型](個数) { n in n番目の初期値 }
の書き方は、大変便利です。
一律の初期値の場合は、[任意型](個数) { 初期値 }
の書き方。
初期値
のところに、リテラルの他に、式や関数が書けるところがポイントです。
ご参考まで。