詳解Swift改訂版(06)〜パターン

2017. 01. 18
この記事は詳解 Swift 改訂版(初版第1刷)を元に、Xcode 8.2.1(Swift 3.0.2)下における差異を記述しています。



CHAPTER 06 パターン



●6.1 タプル


・タプルの概要

本文ではタプル同士の比較はできないとありますが、前版の「詳解Swift(6)〜パターン」で述べたように特定条件下での比較は可能です。

(1, 2) == (1, 2)        // true



●6.2 列挙型


・シンプルな列挙型

列挙型の定義内でもメンバ名だけの参照はエラーが出るようになりました。

case .Up: return Right        // error: enum element 'Right' cannot be referenced as an instance member
case .Down: return Left        // error: enum element 'Left' cannot be referenced as an instance member
case .Right: return Down        // error: enum element 'Down' cannot be referenced as an instance member
case .Left: return Up        // error: enum element 'Up' cannot be referenced as an instance member

Qiita/Xcode 8 Release Notes 日本語翻訳メモ」によると、Swift 3 から要素のアクセスに「.」が必要になったようです。

これに関しては「Xcode 8 Release Notes(Xcode 8.0/Swift/New Features)」に、その旨の記述があります。

「インスタンスメソッド内でインスタンスメンバとして列挙型の要素にアクセスすると、コンパイラがエラーを出すようになりました。
例えば、

enum Color {
    case red, green, blue
 
    func combine(with color: Color) -> Color {
        return red
    }
}

これは Swift 2.2 では有効です。
Swift 3 での正しい方法は以下のように記述します。

return .red

(
SE-0036) 」

したがって実行文は以下のようになります。

case .Up: return .Right
case .Down: return .Left
case .Right: return .Down
case .Left: return .Up


・列挙型に対するメソッドとプロパティ

List6-5 において、上記の通り列挙型の要素にアクセスするには "." が必要なため、switch 文の case 節で指定している Right と Left は "." を加えなければなりません。



●6.3 共用型の列挙型


・if-case 文

変数 t の内容が残高 1200 円以上のプリペイドカードかどうかの例で、「詳解Swift改訂版(04)〜オプショナル」の「4.2 オプショナル束縛構文/if-let 文」で説明したように、Swift 3 から条件句は "where" ではなく "," で区切るように変更されました。

if case .カード(let y, _) = t where y > 1200 {        // Expected ',' joining parts of a multi-clause condition

if case .カード(let y, _) = t, y > 1200 {


・for-in 文で case パターンを使う

Ticket 型の要素を持つ配列 passes は「詳解 Swift 改訂版 サンプルダウンロード」の「samples/Chap06/p150.swift」からコピーすると便利です。

また前述の条件区の区切りが "where" から "," に変更された件は if、guard、while 文に対してであり、for-in 文では適用されないので例文通り "where" のままとなります。


・オプショナル型と列挙型

実行例でメンバ Some を使用した場合にエラーが出ます。

if case let .Some(x) = opi { print(x) }        // error: 'Some' has been renamed to 'some'

これは Optional の定義が変更されたためです。

public enum Optional<Wrapped> : ExpressibleByNilLiteral {
    case none
    case some(Wrapped)
    public init(_ some: Wrapped)
(以下略)
}

したがって以下のように修正することになります。

if case let .some(x) = opi { print(x) }



Qiita/Xcode 8 Release Notes 日本語翻訳メモ

詳解Swift 第3版(Amazon)
 

詳解Swift改訂版(05)〜基本的なデータ型

2017. 01. 13
この記事は詳解 Swift 改訂版(初版第1刷)を元に、Xcode 8.2.1(Swift 3.0.2)下における差異を記述しています。



CHAPTER 05 基本的なデータ型



●5.1 整数と実数


・範囲型と区間型

Qiita/Swift 3.0のRangeまとめ」や「Qiita/Swift 3のRange徹底解説」で述べられているように、Swift 3 で範囲型と区間型の仕様が変わりました。

Swift 2.2 での区間型 HalfOpenInterval<T> と ClosedInterval<T> は Swift 3.0 では Range<T> と ClosedRange<T> になり、これまで閉区間のなかった(閉区間を指定できるが実際には半開区間だった)範囲型 Range<T> は CountableRange<T> と CountableClosedRange<T> に分けられました。
(特に Range<T> が範囲型から区間型に変更されたことに注意が必要です。)

範囲型区間型
半開区間閉区間半開区間閉区間
Swift 2.2Range<T>-HalfOpenInterval<T>ClosedInterval<T>
Swift 3.0Countable
Range<T>
Countable
ClosedRange<T>
Range<T>ClosedRange<T>
整数Int
UInt
実数Double
Float
××
文字
文字列
Character
String
××
BoundComparable
Strideable××
Bound.StrideSignedInteger××
RandomAccessCollection××
for-in 文での利用××
switch 文での利用

したがって型宣言をした範囲演算子による初期化は以下のようになります。

var inth: HalfOpenInterval<Int> = 0..<10        // error: 'HalfOpenInterval' has been renamed to 'Range'
var inth: Range<Int> = 0..<10

var intc: ClosedInterval<Int> = 0...10        // error: 'ClosedInterval' has been renamed to 'ClosedRange'
var inc: ClosedRange<Int> = 0...10

var rg: Range<Int> = 0..<10        // 範囲型ではなく半開区間型になる
var rgh: CountableRange<Int> = 0..<10

var rgc: CountableClosedRange<Int> = 0...10

イニシャライザを使った初期化は以下のようになります。

var inth = Range<Int>(uncheckedBounds: (0, 10))
var intc = ClosedRange<Int>(uncheckedBounds: (0, 10))
var rgh = CountableRange<Int>(uncheckedBounds: (0, 10))
var rgc = CountableClosedRange<Int>(uncheckedBounds: (0, 10))

パターンマッチ演算子の例において、型を指定せず範囲演算子に文字列(または文字や実数)を指定した場合は区間型に、整数を指定した場合は範囲型になります。

var sintv = "archer" ... "saber"        // ClosedRange("archer"..."saber")

var rg = -5 ... 5        // CountableClosedRange(-5...5)

let intv = 0.0 ..< 8.0        // Range(0.0..<8.0)



●5.2 配列


・配列とプロトコル

配列の要素を逆順に並べ換える reverse() メソッドはエラーが出るようになりました。

for item in s.reverse() { print(item) }        // error: 'reverse()' has been renamed to 'reversed()'

Study Swift/Reverse Array in Swift 2.2/3.0」によると、Swift 3 から reversed() に改名されたようです。

for item in s.reversed() { print(item) }        // 冬 秋 夏 春


・配列のイニシャライザ

イニシャライザ init(count:Int, repeatedValue:T) はエラーが出るようになりました。

var data = [Double](count:10, repeatedValue:0.0)        // error: argument 'repeatedValue' must precede argument 'count'

articles of samekard/Swift2.2からSwift3.0への変換を行ってみて」によると、Swift 3 から init(repeating:T, count:Int) に変更されたようです。

var data = [Double](repeating:0.0, count:10)        // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]


・配列のメソッド

メソッド insert(_: T, atIndex: Int) はエラーが出るようになりました。

s.insert("零", atIndex:0)        // error: incorrect argument label in call (have '_:atIndex:', expected '_:at:')

Swift 3 から insert(_: T, at: Int) に変更されたようです。

s.insert("零", at: 0)        // ["零", "壱", "弐", "参"]

また removeAtIndex(_: Int) -> T もエラーが出るようになりました。

let one = s.removeAtIndex(1)        // error: 'removeAtIndex' has been renamed to 'remove(at:)'

Swift 3 から remove(at: Int) -> T に改名されたようです。

let one = s.remove(at: 1)        // "壱"



●5.3 文字列と文字


・String型のイニシャライザ

String 型のイニシャライザで配列を渡すとエラーが出るようになりました。

let b = String([6001, 5210])        // error: 'init' has been renamed to 'init(describing:)'

Developers.IO/[Swift 3] 型名を取得する」によると、init(describing:) が Swift 3 で追加されたようです。

let b = String(describing: [6001, 5210])        // "[6001, 5210]"

また同じ値を複数回繰り返すイニシャライザ init(count:repeatedValue:) は init(repeating:count:) に改名され、Character 型および UnicodeScalar 型は指定できなくなり、String 型のみになりました。
(String 型を繰り返す init(count:repeatedValue:) は元々無かった?)

// init(count:repeatedValue:)

let char: Character = "花"
let sc = String(count: 10, repeatedValue: char)        // error: 'init(count:repeatedValue:)' is unavailable: Renamed to init(repeating:count:) and reordered parameters

let str: String = "華"
let ss = String(count: 10, repeatedValue: str)        // error: cannot invoke initializer for type 'String' with an argument list of type '(count: Int, repeatedValue: String)'

let us = (UnicodeScalar(0x83ef))!
let su = String(count: 10, repeatedValue: us)        // error: 'init(count:repeatedValue:)' is unavailable: Renamed to init(repeating:count:) and reordered parameters


// init(repeating:count:)

let sc = String(repeating: char, count: 10)        // error: 'init(repeating:count:)' is unavailable: Replaced by init(repeating: String, count: Int)

let ss = String(repeating: str, count: 10)        // "華華華華華華華華華華"

let su = String(repeating: us, count: 10)        // error: 'init(repeating:count:)' is unavailable: Replaced by init(repeating: String, count: Int)


・UnicodeScalar 型

Int 型と String 型を引数とした UnicodeScalar 型定数の比較でエラーが出るようになりました。

let us1 = UnicodeScalar(0x83ef)        // Int 型引数
let us2 = UnicodeScalar("澪")        // String 型引数
let us3 : UnicodeScalar = "箒"        // unicodeScalarLiteral

us1 > us2        // error: binary operator '>' cannot be applied to two 'UnicodeScalar?' operands

これは「Apple Developer Documentation/Swift Standard Library/UnicodeScalar」で説明されているように、Int 型や String 型を引数としたイニシャライザの返り値はオプショナル型であり、また「詳解Swift改訂版(04)〜オプショナル」で述べたように比較演算子がオプショナル型の値を受け付けなくなったためです。
したがって比較するには開示する必要があります。

type(of: us1)        // Optional<UnicodeScalar>.Type
type(of: us2)        // Optional<UnicodeScalar>.Type
type(of: us3)        // UnicodeScalar.Type

us1! > us2!        // true

List5-3 においても、定数 first と last は Int 型を引数としたイニシャライザのためオプショナル型になるので、プロパティ unicodeScalars の値と比較するには開示する必要があります。

let first = UnicodeScalar(0x3041)        // Optional<UnicodeScalar>.Type
let last = UnicodeScalar(0x309f)        // Optional<UnicodeScalar>.Type

for sc in text.unicodeScalars {        // String.UnicodeScalarView.Type
    if sc >= first && sc <= last {        // error: binary operator '>=' cannot be applied to operands of type 'String.UnicodeScalarView.Iterator.Element' (aka 'UnicodeScalar') and 'UnicodeScalar?'


let first = UnicodeScalar(0x3041)!        // UnicodeScalar.Type
let last = UnicodeScalar(0x309f)!        // UnicodeScalar.Type

また String 型のインスタンスメソッド append() の引数は String 型もしくは Character 型 のみに対応しているので、UnicodeScalar 値を使うにはイニシャライザを使う必要があります。
(「Apple Developer Documentation/Swift Standard Library/String」参照)

kana.append(sc)        // error: 'append' is unavailable: Replaced by append(_: String)

kana.append(String(sc))


・指定位置の文字および部分文字列の取り出し方

本文では使用していませんが、指定範囲の文字列を取得する subscript は範囲型(の旧 Range<String.Index> )ではなく区間型になり、半開区間が subscript(Range<String.Index>)、閉区間が subscript(ClosedRange<String.Index>) となっています。

文字列中の任意の文字、部分文字列を参照する例ですが、プロパティ startIndex や endIndex で示しているインデックス位置を前後させるメソッド successor() と predecessor() は廃止され、代わりに index(after: String.Index) と index(before: String.Index) を使用することになりました。

また指定した値分を移動させる advance() も廃止されて代わりに index(String.Index, offsetBy: String.IndexDistance) か index(String.Index, offsetBy: String.IndexDistance, limitedBy: String.Index) を使用することになっています。
(「大津真 Web/Swift 3 文字列操作(その2)」参照)

print( str[str.startIndex.successor()] )        // error: 'successor()' is unavailable: To get the next index call 'index(after:)' on the CharacterView instance that produced the index.
print( str[advance(str.startIndex, 9)] )        // error: use of unresolved identifier 'advance'

print( str[str.endIndex.predecessor()] )        // error: 'predecessor()' is unavailable: To get the previous index call 'index(before:)' on the CharacterView instance that produced the index.
print( str[advance(str.endIndex, -3)] )        // error: use of unresolved identifier 'advance'

let x = advance(str.startIndex, 7)        // error: use of unresolved identifier 'advance'
let y = advance(x, 5)        // error: use of unresolved identifier 'advance'

簡略化のため startIndex と endIndex を定数に入れ、メソッドを修正すると以下のようになります。

let sIndex = str.startIndex        // 0
let eIndex = str.endIndex        // 19

print( str[sIndex] )        // 俺
print( str[str.index(after: sIndex)] )        // 、
print( str[str.index(sIndex, offsetBy: 9)] )        // グ

print( str[str.index(before: eIndex)] )        // 。
print( str[str.index(eIndex, offsetBy: -3)] )        // ま

let x = str.index(sIndex, offsetBy: 7)        // 7
let y = str.index(x, offsetBy: 5)        // 13

List5-5 において、String.Index 型の変数 x1 と x2 はインクリメント演算子が使えないだけでなく、Int 値との演算もできません。

x1 += 1; x2 += 1        // error: binary operator '+=' cannot be applied to operands of type 'String.Index' (aka 'String.CharacterView.Index') and 'Int'

したがってインデックス位置を進めるメソッド index(after:) を使用する必要があります。

x1 = s1.index(after: x1); x2 = s2.index(after: x2)

なお subscript で単純な整数を使えない理由に関しては「Medium/Swift・iOSコラム/Swift 3のStringのCharacter Viewを、なぜIntでsubscript出来ないのか分からない」と「Medium/Swift・iOSコラム/Swift 3のStringのViewに対して、Intでsubscript出来ない理由」を参照すると良いでしょう。


・String 型のメソッド

メソッド append() について、上述の通り現在は append(String) と append(Character) は使えますが UnicodeScalar 型を引数にすることはできませんので注意してください。

let a: Character = "a"
let b: String = "bbb"
var c: String = "ccc"

c.append(a)        // ccca
c.append(b)        // cccabbb

let d: UnicodeScalar = "d"

c.append(d)        // error: 'append' is unavailable: Replaced by append(_: String)

メソッド appendContentsOf() は append(contentsOf:) に、insert(_:atIndex:) は insert(_:at:) に、insertContentsOf(_:at:) は insert(contentsOf:at:) に改名されています。

let e: [Character] = ["e", "E"]        // Character 型を要素とする CollectionType 型

// append(contentsOf:)

c.appendContentsOf(e)        // error: 'appendContentsOf' has been renamed to 'append(contentsOf:)'
c.append(contentsOf: e)        // cccabbbeE

let sIndex = c.startIndex
let eIndex = c.endIndex

// insert(_:at:)

c.insert("f", atIndex: c.index(sIndex, offsetBy: 8))        // error: incorrect argument label in call (have '_:atIndex:', expected '_:at:')
c.insert("f", at: c.index(sIndex, offsetBy: 8))        // cccabbbefE

// insert(contentsOf:at:)

c.insertContentsOf(e, at: eIndex)        // error: 'insertContentsOf(_:at:)' has been renamed to 'insert(contentsOf:at:)'
c.insert(contentsOf: e, at: eIndex)        // cccfabbbefeEE
// 直前の insert(_:at:) で eIndex の示す位置が "E の次" ではなく "E" になるため cccabbbefEeE とはならない

メソッド removeAtIndex() は remove(at:) に改名されています。

c.removeAtIndex(sIndex)        // error: 'removeAtIndex' has been renamed to 'remove(at:)'
c.remove(at: sIndex)        // c

余談ですが remove(at:) で試しに endIndex の定数を指定したところ(そのインデックスに値が存在しているのに)エラーになりました。

c.remove(at: eIndex)        // error:Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode= 0x0).

不思議に思い前後関係を調べると、endIndex と index(after:) で求めたその次のインデックスが同じという結果でした。

c.index(before: eIndex)        // 8
eIndex        // 9
c.index(after: eIndex)        // 9

理由は分かりませんが subscript でも endIndex を指定するとエラーになり、当該インデックスを指定するには index(after:) で指定しなければならないようなので注意が必要です。

print(c)        // ccabbbefeEE

print(c[c.index(before: eIndex)])        // e
print(c[eIndex])        // error:Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode= 0x0).
print(c[c.index(after: eIndex)])        // E

メソッド removeRange() は半開区間の removeSubrange(Range<String.Index>) と閉区間の removeSubrange(ClosedRange<String.Index>) に改名されました。

let x = c.index(sIndex, offsetBy: 1)        // 1
let y = c.index(x, offsetBy: 2)        // 3
let xy1 = x ..< y        // 1..<3
let xy2 = x ... y        // 1...3

print(c)        // ccabbbefeE

c.removeRange(xy1)        // error: 'removeRange' has been renamed to 'removeSubrange'

c.removeSubrange(xy1)        // cbbbefeE
c.removeSubrange(xy2)        // cefeE

メソッド replaceRange(_:with:) は半開区間と閉区間、置換するものが Character 型の文字を要素とする CollectionType 型か String 型の文字列かの4種類になりました。

半開区間閉区間
文字
コレクション
replaceSubrange
(Range<String.Index>, with: C)
replaceSubrange
(ClosedRange<String.Index>, with: C)
文字列replaceSubrange
(Range<String.Index>, with: String)
replaceSubrange
(ClosedRange<String.Index>, with: String)

var z = "012345"
let g: [Character] = ["g", "G"]
let h: String = "hhh"

z.replaceRange(xy1, with: g)        // error: 'replaceRange(_:with:)' has been renamed to 'replaceSubrange'

z.replaceSubrange(xy1, with: g)        // 0gG345("12"を"gG"に置換)
z.replaceSubrange(xy1, with: h)        // 0hhh345("gG"を"hhh"に置換)
z.replaceSubrange(xy2, with: g)        // 0gG345("hhh"を"gG"に置換)
z.replaceSubrange(xy2, with: h)        // 0hhh45("gG3"を"hhh"に置換)



●5.5 集合


・集合の型宣言と初期値

Table5-3 で紹介しているメドッソのほとんどが改名されています。

集合旧メソッド新メソッド
和集合union(_:) -> Setunion(_:) -> Set
unionInPlace(_:)formUnion(_:)
差集合subtract(_:) -> Setsubtracting(_:) -> Set
subtractInPlace(_:)subtract(_:)
積集合intersect(_:) -> Setintersection(_:) -> Set
intersectInPlace(_:)formIntersection(_:)
対称差集合exclusiveOr(_:) -> SetsymmetricDifference(_:) -> Set
exclusiveOrInPlace(_:)formSymmetricDifference(_:)

// 和集合

var a = Set<Int>(0...3)        // {2, 0, 1, 3}
var b = Set<Int>(3...6)        // {5, 6, 3, 4}
var c = Set<Int>(4...7)        // {5, 6, 7, 4}

let z = a.union(b)        // {2, 4, 5, 6, 0, 1, 3}
a.unionInPlace(c)        // error: 'unionInPlace' has been renamed to 'formUnion(_:)'
a.formUnion(c)        // {2, 4, 5, 6, 7, 0, 1, 3}

// 差集合

var d = Set<Int>(0...3)        // {2, 0, 1, 3}
var e = Set<Int>(3...6)        // {5, 6, 3, 4}
var f = Set<Int>(2...5)        // {2, 0, 1, 3}

let y = d.subtracting(e)        // {2, 0, 1}
d.subtractInPlace(f)        // error: 'subtractInPlace' has been renamed to 'subtract(_:)'
d.subtract(f)        // {0, 1}

// 積集合

var g = Set<Int>(0...3)        // {2, 0, 1, 3}
var h = Set<Int>(3...6)        // {5, 6, 3, 4}
var i = Set<Int>(2...5)        // {5, 2, 3, 4}

g.intersect(h)        // error: 'intersect' has been renamed to 'intersection(_:)'
var x = g.intersection(h)        // {3}
g.intersectInPlace(i)        // error: 'intersectInPlace' has been renamed to 'formIntersection(_:)'
g.formIntersection(i)        // {2, 3}

// 対称差集合

var j = Set<Int>(0...3)        // {2, 0, 1, 3}
var k = Set<Int>(3...6)        // {5, 6, 3, 4}
var l = Set<Int>(2...5)        // {5, 2, 3, 4}

var w = j.exclusiveOr(k)        // error: 'exclusiveOr' has been renamed to 'symmetricDifference(_:)'
var w = j.symmetricDifference(k)        // {5, 6, 2, 0, 1, 4}
j.exclusiveOrInPlace(l)        // error: 'exclusiveOrInPlace' has been renamed to 'formSymmetricDifference(_:)'
j.formSymmetricDifference(l)        // {5, 0, 1, 4}

したがって実行例で積集合を取っているメソッドは修正する必要があります。

jset.intersectInPlace(w)        // error: cannot convert value of type '[String]' to expected argument type 'Set<String>'

jset.formIntersection(w)        // ["幽霊"]



Qiita/Swift 3.0のRangeまとめ

Qiita/Swift 3のRange徹底解説

Study Swift/Reverse Array in Swift 2.2/3.0

articles of samekard/Swift2.2からSwift3.0への変換を行ってみて

Developers.IO/[Swift 3] 型名を取得する

大津真 Web/Swift 3 文字列操作(その2)

Qiita/「文字数」ってなぁに?〜String, NSString, Unicodeの基本〜

Medium/Swift・iOSコラム/Swift 3のStringのCharacter Viewを、なぜIntでsubscript出来ないのか分からない

Medium/Swift・iOSコラム/Swift 3のStringのViewに対して、Intでsubscript出来ない理由

詳解Swift 第3版(Amazon)
 

詳解Swift改訂版(04)〜オプショナル

2017. 01. 04
この記事は詳解 Swift 改訂版(初版第1刷)を元に、Xcode 8.2.1(Swift 3.0.2)下における差異を記述しています。



CHAPTER 04 オプショナル



●4.1 オプショナル型


・条件判定とオプショナル型

大小比較ができるデータ型のオプショナル型を開示せずに大小比較する例でエラーが出るようになりました。

var x : Int? = 1964
x > 2000        // error: value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?
x = nil
x < 2000        // error: value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?

Build Insider/Swift 3.0でなぜ「Cスタイルのforループ」「++/--演算子」などの仕様が廃止されたのか」によると、Swift 3 で4つの比較演算子(<、>、<=、=>)はオプショナル型の値を受け付けないことになったようです。

正当な値なら開示すれば比較できるのですが、値が nil の変数/定数を開示すると当然致命的エラーが発生します。

x! > 2000        // false
x = nil
x! < 2000        // fatal error: unexpectedly found nil while unwrapping an Optional value



●4.2 オプショナル束縛構文


・if-let 文

if-let 文の条件句で複数個のオプショナル型の値をしていするとエラーが出るようになりました。

if let sapporo = Int("1972"), nagano = Int("1998") {        // Expected 'let' in conditional

Qiita/Xcode 8 Release Notes 日本語翻訳メモ」によると、Swift 3 で if 文などの条件句の書式がより標準的に修正されたようです。

これに関しては「Xcode 8 Release Notes(Xcode 8.0/Swift/New Features)」に、その旨の記述があります。

「if、guard、そして while 文の条件句では、より標準的な構文を使用します。
各パターンやオプショナル束縛の接頭辞にそれぞれ case または let を付け、"where" ではなく "," で区切ります。
例えば以前に書かれたコードが、

if let a = a, b = b where a == b {}

となっていた場合、現在は次のように記述する必要があります。

if let a = a, let b = b, a == b {}

(
SE-0099) 」

したがって先の if-let 文は以下のようになります。

if let sapporo = Int("1972"), let nagano = Int("1998") {


・オプショナル束縛と条件式

オプショナル束縛構文で where による条件式の追加はエラーが出るようになりました。

if nagano < 2000, let tokyo = Int("2020") where tokyo > 2000 {        // Expected ',' joining parts of a multi-clause condition


これは上記の通り、条件の区切りが "where" から "," に変更されたためです。
したがってこの if-let 文は以下のようになります。

if nagano < 2000, let tokyo = Int("2020"), tokyo > 2000 {



Build Insider/Swift 3.0でなぜ「Cスタイルのforループ」「++/--演算子」などの仕様が廃止されたのか

Qiita/Xcode 8 Release Notes 日本語翻訳メモ

詳解Swift 第3版(Amazon)
 

詳解Swift改訂版(03)〜構造体

2017. 01. 03
この記事は詳解 Swift 改訂版(初版第1刷)を元に、Xcode 8.2.1(Swift 3.0.2)下における差異を記述しています。



CHAPTER 03 構造体



●3.1 構造体の定義


・関数とイニシャライザの記法のまとめ

詳解Swift改訂版(02)〜関数」で述べたように、Swift 3 から関数の引数は第1引数も含めて全てラベル付け(外部引数名化)されたので、引数名に関するイニシャライザと関数/メソッドの記法の差が無くなり統一されました。
(Objective-C に引き摺られたルールですから、むしろ何故最初からこうしなかったのかというようなことでしたし…)

したがって Table3-1 は次ようになります。

イニシャライザ関数/メソッド
手続き名常にinit自由(関数の機能と第1引数を説明するもの)
※第1引数を説明する必要がないので、関数の機能だけでも良い?
返り値記述しない自由
明示的に指定しない場合の、内部引数名と外部引数名の関係内部引数名は、自動的にすべてが外部引数名になる第1引数には外部引数名は使われない。
第2引数以降について、内部引数名が自動的に外部引数名になる

内部引数名は、自動的にすべてが外部引数名になる
外部引数名を使わないようにするには外部引数名として「_」を指定する外部引数名として「_」を指定する。
第1引数に対する指定は冗長

また前述のとおり「(1) 引数に対する var または let の指定」は Swift 3 からできません。



●3.2 メソッド


・構造体の内容を変更するメソッド

流れで分かると思いますが、構造体 Clock の実行例で、変数 tic は Time ではなくClock、最後の出力する変数は tm ではなく tic です。
(前版ではこの内容の例を構造体 Time、変数 tm で行なっていたため、単純な修正ミスです)

var tic = Time(hour: 19, min: 40)
var tic = Clock(hour: 19, min: 40)

print(tm.toString())
print(tic.toString())


・イニシャライザとメソッド

List3-11 において、イニシャライザの「year = y」の後ろは「,」ではなく「;」です。

year = y, month = m; day = d        // Consecutive statements on a line must be separated by ';'

year = y; month = m; day = d



●3.3 プロパティ


・格納型プロパティの初期値を式で設定する

List3-14 において、定数プロパティ serial の初期値設定はインクリメント演算子を使用しているためエラーが出ます。

let serial = "CZ:\(++serialNumber)"        // error: '++' is unavailable: it has been removed in Swift 3

しかし文字列埋め込みに複合代入演算子を用いた式は使えません。

let serial = "CZ:\(serialNumber += 1)"        // CZ:()

かといってグローバル変数 serialNumber のインクリメント演算を構造体の定義上で行うことはできません。
(「Qiita/[Swift] メモ」参照)

serialNumber += 1        // Expected declaration
let serial = "CZ:\(serialNumber)"

定数 serial はインスタンスが生成される度にインクリメントされるものですから、演算はイニシャライザで行うのが自然です。
ただし本文中で解説している通り、「プロパティの初期値を指定する式は、イニシャライザの実行の直前に評価されます」ので、定数 serial の値を設定した後にイニシャライザでインクリメントしても間に合いません。

let serial = "CZ:\(serialNumber)"
init(_ w:Int, _ h:Int) {
    serialNumber += 1        // CZ:2127

したがって定数 serial は型宣言だけにし、イニシャライザでインクリメント後に値を設定する必要があります。

let serial: String
init(_ w:Int, _ h:Int) {
    serialNumber += 1
    serial = "CZ:\(serialNumber)"        // CZ:2128

ただ本項の目的は「格納型プロパティの初期値を式で設定する」ですから、「型定義+イニシャライザでの設定」では意味がありません。
今回は設定する式が Swift 3 で廃止されたインクリメント演算で、代替の複合代入演算が文字列埋め込みで使えないために目的から逸れた解決法になりましたが、代入を伴わない演算であれば定義上で設定できるはずです。


・グローバルなスコープを持つ計算型プロパティの定義

List3-17 において、NSScreen クラスの mainScreen() は main() に改名されたようです。

let size = NSScreen.mainScreen()!.frame.size        // error: 'mainScreen()' has been renamed to 'main()'

let size = NSScreen.main()!.frame.size



Qiita/[Swift] メモ

詳解Swift 第3版(Amazon)
 

詳解Swift改訂版(02)〜関数

2016. 12. 28
この記事は詳解 Swift 改訂版(初版第1刷)を元に、Xcode 8.2.1(Swift 3.0.2)下における差異を記述しています。



CHAPTER 02 関数



●2.1 関数定義の基本


・関数定義の概要

List2-1 において、関数の引数のラベルは第1引数でも必要になりました。

func count(n: Int) -> Int {
    total += n
    return total
}

count(10)        // error: missing argument label 'n:' in call

Qiita/Xcode 8 Release Notes 日本語翻訳メモ」によると、Swift 3 で関数の引数は全ての引数でラベル付けされることになったようです。

これに関しては「Xcode 8 Release Notes(Xcode 8.0/Swift/New Features)」に、その旨の記述があります。

「関数の引数は現在、全ての関数の引数に渡って一貫したラベル付けを行うようになりました。
このアップデートでは第1引数の宣言が、これまでの第2以降の引数の動作と一致するようになりました。

以前に書かれたコードでは、

func Resolved Issues(x: Int, y: Int) {}
Resolved Issues(1, y: 2)

func bar(a a: Int, b: Int) {}
bar(a: 3, b: 4)

となっていましたが、現在は次のように記述します。

func Resolved Issues(_ x: Int, y: Int) {}
Resolved Issues(1, y: 2)
func bar(a: Int, b: Int) {}
bar(a: 3, b: 4)

(
SE-0046)」

したがって関数 count の呼び出しは以下のようになります。

count(n: 10)


・外部引数名

上記の通り関数は全ての引数でラベル付けされるようになりましたので、これまでの Objective-C のメソッド記法のように関数名に第1引数の意味を含める必要は無くなったと思われます。
今後はシンプルな関数名で構わないでしょう。

List2-2 を例にすると、

func buyProduct(product:Int, price:Int, quantity:Int) {
    print("Product:\(product), amount = \(price * quantity)")
}

buyProduct(product: 19090, price: 180000, quantity: 1)        // Product:19090, amount = 180000

関数名に第1引数の意味を含み、且つ第1引数のラベルもあって冗長になりますので、関数名から第1引数の意味を取り除いた方がいいでしょう。

func buy(product:Int, price:Int, quantity:Int) {
    print("Product:\(product), amount = \(price * quantity)")
}

buy(product: 19090, price: 180000, quantity: 1)        // Product:19090, amount = 180000

また今回の例で言えば、関数名 buy だけでは単純すぎて心許なく、引数の product はカタログ番号であることから、関数名をそのままに第1引数名を number とする手もあるでしょう。

func buyProduct(number:Int, price:Int, quantity:Int) {
    print("Number:\(number), amount = \(price * quantity)")
}

buyProduct(number: 19090, price: 180000, quantity: 1)        // Number:19090, amount = 180000

もしくは「_」で第1引数の外部引数名を省略することで、これまでと同じ挙動にすることもできます。

func buyProduct(_ product:Int, price:Int, quantity:Int) {
    print("Product:\(product), amount = \(price * quantity)")
}

buyProduct(19090, price: 180000, quantity: 1)        // Product:19090, amount = 180000

いずれにせよ、これまでの記法には再考の余地があります。

※ 以降において、第1引数のラベル付けに関する修整については、分かり易さのために「_」で呼び出し時の外部引数名を省略することにし、特別な理由がなければ説明を割愛します。


・外部引数名の指定と省略

上記の通り第1引数も自動的に外部引数名が付与されることになりましたが、

func area(h:Double, w:Double) -> Double {
    return h * w
}

let a = area(h: 10.0, w: 12.5)

これまでと同様、内部引数名と別途に外部引数名を指定することもできます。

func area(height h:Double, width w:Double) -> Double {
    return h * w
}

let a = area(height: 10.0, width: 12.5)



●2.2 関数定義におけるさまざまな設定


・inout 引数

List 2-3 において、inout 引数でエラーが出るようになりました。

func mySwap(inout _ a:Int, inout _ b:Int) {        // 'inout' before a parameter name is not allowed. place it before the parameter type instead

Qiita/Xcode 8 Release Notes 日本語翻訳メモ」によると、Swift 3 で inout 引数は : の後ろに書くことになったようです。

これに関しては「Xcode 8 Release Notes(Xcode 8.0/Swift/New Features)」に、その旨の記述があります。

「inout 属性の場所が : の後ろ且つ引数型の前に移動されています。
例えば以前に書かれたコードでは、

func Resolved Issues(inout x: Int) {
}

ですが、現在は次のように記述する必要があります。

func Resolved Issues(x: inout Int) {
}

(SE-0031)」


したがって関数 mySwap の引数は以下のようになります。

func mySwap(_ a: inout Int, _ b: inout Int) {


・関数の引数に規定値を指定する

List2-4 の関数 setFont の実行結果の例で、規定値を指定した引数の順序を入れ替えた場合にエラーが出るようになりました。

setFont(name: "Times", bold:true, size:18.0)        // error: argument 'size' must precede argument 'bold'

Qiita/Xcode 8 Release Notes 日本語翻訳メモ」によると、Swift 3 で規定値を持つ引数は宣言順に指定することになったようです。

これに関しては「Xcode 8 Release Notes(Xcode 8.0/Swift/New Features)」に、その旨の記述があります。

「デフォルト値を持つ関数の引数は、現在宣言順に指定する必要があります。
呼び出し時に提供する引数の位置は、常に宣言された順序で関数に渡す必要があります。

func requiredArguments(a: Int, b: Int, c: Int) {}
func defaultArguments(a: Int = 0, b: Int = 0, c: Int = 0) {}

requiredArguments(a: 0, b: 1, c: 2)
requiredArguments(b: 0, a: 1, c: 2)        // error
defaultArguments(a: 0, b: 1, c: 2)
defaultArguments(b: 0, a: 1, c: 2)        // error

指定された引数が宣言の順序に従う限り、デフォルト値を持つ任意のラベル付き引数を省略することができます。

defaultArguments(a: 0)        // ok
defaultArguments(b: 1)        // ok
defaultArguments(c: 2)        // ok
defaultArguments(a: 1, c: 2)        // ok
defaultArguments(b: 1, c: 2)        // ok
defaultArguments(c: 1, b: 2)        // error

(SE-0060)」


したがって関数 setFont の引数は以下のようになります。

setFont(name: "Times", size:18.0, bold:true)        // Times 18.0 [B]


・引数の値を処理中に変更できるようにする

List2-6 の関数 dayOfWeek において、引数への var 修飾語の付与はエラーが出るようになりました。

func dayOfWeek(var _ m:Int, _ d:Int, var year y:Int = 2016) -> Int {        // error: parameters may not have the 'var' specifier

Qiita/Xcode 8 Release Notes 日本語翻訳メモ」によると、Swift 3 で関数の引数で var が使えなくなったようです。

これに関しては「Xcode 8 Release Notes(Xcode 8.0/Swift/New Features)」に、その旨の記述があります。

「var はもはや関数の引数属性として認められません。
コンパイラは関数本体にシャドウコピーを作成するための修正を提供します。
例えば以前に書かれたコードが、

func Resolved Issues(var x: Int) {
}

となっていた場合、現在は次のように記述する必要があります。

func Resolved Issues(x: Int) {
  var x = x
}

(
SE-0003)」

したがって関数 dayOfWeek の引数指定は以下のようになります。

func dayOfWeek(_ m:Int, _ d:Int, year y:Int = 2016) -> Int {
    var m = m, y = y

また本文中で「変更しないことを明示するために let という修飾語を使うことができます。」とありますが、「Qiita/Xcode 8 Release Notes 日本語翻訳メモ」にSwift 3で無駄な let は削除されたとあり、「Xcode 8 Release Notes(Xcode 8.0/Swift/New Features)」に、その旨の記述があります。

「let はもはや関数の引数属性として認められません。
コンパイラは関数の宣言から削除するための修正を提供します。(
SE-0053)」

例えば関数 dayOfWeek の引数 d に let を指定すると警告が出ます。

func dayOfWeek(_ m:Int, let _ d:Int, year y:Int = 2016) -> Int {        // 'let' as a parameter attribute is not allowed


・返り値を必ず使うための指定

返り値を伴う関数に @warn_unused_result 属性を指定すると警告が出るようになりました。

@warn_unused_result        // 'warn_unused_result' attribute behavior is now the default
func dayOfWeek(_ m:Int, _ d:Int, year y:Int = 2016) -> Int {

Tomorrow Never Comes./[Swift]関数の返り値を使わない時は」によると、Swift 3 から関数の返り値を使用しない場合はデフォルトで警告が出るようになったため、@warn_unused_result は不要になったようです。

逆に返り値を使用しないことを明示するには @discardableResult 属性を指定することになります。

ただしプレイグラウンドでは、返り値を伴う関数を返り値を使用しない形で実行しても警告が出ないようなので注意が必要です。



Qiita/Xcode 8 Release Notes 日本語翻訳メモ

Tomorrow Never Comes./[Swift]関数の返り値を使わない時は

詳解Swift 第3版(Amazon)
 






QuietControl 30 wireless headphones
Calendar
04 | 2017/05 | 06
Sun Mon Tue Wed Thu Fri Sat
- 1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31 - - -
Recent Articles
iTunes


Swift
Categories
Tips
Profile

水月杏香

Author:水月杏香
永遠の初心者プログラマ。

Wish List
WACOM


ARC
Technical Q&A
情報プロパティリストキー
Start Developing iOS Apps Today
BOSE

QuietControl 30 wireless headphones
Reference
NSApplicationDelegateプロトコル
NSArrayクラス
NSAutoreleasePoolクラス
NSBundleクラス
NSBundle UIKit追加分
NSCalendarクラス
NSCoderクラス
NSCodingプロトコル
NSCopyingプロトコル
NSDataクラス
NSDateクラス
NSDateFormatterクラス
NSDictionaryクラス
NSEntityDescriptionクラス
NSEnumeratorクラス
NSErrorクラス
NSExceptionクラス
NSFetchRequestクラス
NSFileHandleクラス
NSFileManagerクラス
NSIndexPathクラス
NSIndexPath UIKit追加分
NSKeyedArchiverクラス
NSKeyedUnarchiverクラス
NSKeyValueCodingプロトコル
NSLocaleクラス
NSManagedObjectクラス
NSManagedObjectContextクラス
NSManagedObjectModelクラス
NSMutableArrayクラス
NSMutableCopyingプロトコル
NSMutableDictionaryクラス
NSMutableSetクラス
NSNotificationクラス
NSNotificationCenterクラス
NSNullクラス
NSNumberクラス
NSObjectクラス
NSObject UIKit追加分
NSObjectプロトコル
NSPersistentStoreクラス
NSPersistentStoreCoordinatorクラス
NSPredicateクラス
NSPropertyListSerializationクラス
NSRunLoopクラス
NSSetクラス
NSStringクラス
NSString UIKit追加分
NSTimerクラス
NSTimeZoneクラス
NSURLクラス
NSURLProtectionSpaceクラス
NSURLRequestクラス
NSUserDefaultsクラス
NSValueクラス

UIActionSheetクラス
UIActionSheetDelegateプロトコル
UIActivityIndicatorViewクラス
UIAlertViewクラス
UIAlertViewDelegateプロトコル
UIApplicationクラス
UIApplicationDelegateプロトコル
UIBarButtonItemクラス
UIBarItemクラス
UIButtonクラス
UIColorクラス
UIControlクラス
UIDatePickerクラス
UIDeviceクラス
UIEventクラス
UIFontクラス
UIGestureRecognizerクラス
UIImageクラス
UIImageViewクラス
UIKit Function
UILabelクラス
UINavigationControllerクラス
UINavigationItemクラス
UIPickerViewクラス
UIPickerViewDataSourceプロトコル
UIPickerViewDelegateプロトコル
UIPinchGestureRecognizerクラス
UIResponderクラス
UIScreenクラス
UIScrollViewクラス
UISearchBarクラス
UISearchBarDelegateプロトコル
UISegmentedControlクラス
UISliderクラス
UISwipeGestureRecognizerクラス
UISwitchクラス
UITableViewクラス
UITableViewCellクラス
UITableViewControllerクラス
UITableViewDataSourceプロトコル
UITableViewDelegateプロトコル
UITapGestureRecognizerクラス
UITextFieldクラス
UITextFieldDelegateプロトコル
UITextInputTraitsプロトコル
UITextViewクラス
UITextViewDelegateプロトコル
UIToolbarクラス
UITouchクラス
UIViewクラス
UIViewControllerクラス
UIWebViewクラス
UIWebViewDelegateプロトコル
UIWindowクラス

AVAudioPlayerクラス
AVAudioPlayerDelegateプロトコル

CADisplayLinkクラス
CAEAGLLayerクラス
CALayerクラス

CGAffineTransform
CGBitmapContext
CGColor
CGColorSpace
CGContext
CGGeometry
CGImage
CGPath

EAGLContextクラス
EAGLDrawableプロトコル

Foundation Constants
Foundation Data Types
Foundation Functions

MPMediaItemクラス
MPMediaItemArtworkクラス
MPMediaPlaylistクラス
MPMediaPropertyPredicateクラス
MPMediaQueryクラス
MPMusicPlayerControllerクラス

Randomization Services

System Sound Services
Amazon


OpenGL ES
SQLite
Monthly Archives
Recent Comments
Recent TrackBacks
RSS Link
Visitors
QR Code
QR