詳解Swift(5)〜基本的なデータ型

2016. 06. 28
(※2016年6月28日追記:この記事は詳解 Swift(初版第1刷)を元に、Xcode 7.3.1(Swift 2.2)下における加筆・修正を行っています。(Xcode 7.3からXcode 7.3.1での変更点はありません))



CHAPTER 05 基本的なデータ型



●5.1 整数と実数


・範囲型と区間型

範囲型のイニシャライザ init(start:end:) ですが「'init(start:end:)' は Swift 3 で削除するため廃止になりました。 '..<' 演算子を使用するように」と警告が出るようになりました。

8042.png

本書(Swift 1.1ベース)ではswitch文の条件に使える構造体は区間型のみで、範囲型を使うとエラーになるとありますが、Xcode 6.4(Swift 1.2)以降では範囲型も扱えるようです。

var rg = Range<Int>(0..<10)

let n = 5
switch n {
case rg: print("Range")        // Range(エラーなし)
default: break
}

Xcode Release Notes」などを検索しても、いつ改訂されたか分からなかったのですが、「The Swift Programming Language (Swift 2.1)(Language Guide/Control Flow/Conditional Statements/Switch)」に範囲演算子は区間型か範囲型を返すようにオーバーロードされるとの記述がありました。

「注
閉範囲演算子(...)と半開範囲演算子(..<)関数は共に、区間型または範囲型のいずれかを返すようにオーバーロードされます。
たとえばswitch文のcaseで照合する時など、特定の要素が含まれているかどうかを判断することができます。
範囲はfor-in文で反復処理される連続した値のコレクションです。」


ただしfor-in文は範囲型のみ使用可なのは変わらず、(半開区間・閉区間問わず)区間型はエラーになります。

var inth = HalfOpenInterval<Int>(0, 10)
for i in inth { print(i) }        // 'error: type 'HalfOpenInterval<Int>' does not conform to protocol 'SequenceType'


・Stride型

従来のstrideメソッドはエラーが出るようになりました。

var st1 = stride(from: 8, through: 20, by: 4)        // error: 'stride(from:through:by:)' is unavailable: call the 'stride(through:by:)' method instead
for i in st1 { print(" (i)", terminator: "") }
print()
var st2 = stride(from: 8, to: 20, by: 4)        // error: 'stride(from:to:by:)' is unavailable: call the 'stride(to:by:)' method instead
for i in st2 { print(" (i)", terminator: "") }
print()

こちらも「Xcode Release Notes」などを検索しても、いつ改訂されたか分からなかったのですが、「InfoQ/Swiftに対するIBMの反応」によるとグローバルメソッドからメンバーメソッドに置き換えられたようです。
したがって上記の例は以下のように修正することになります。

var st1 = 8.stride(through: 20, by: 4)
for i in st1 { print(" (i)", terminator: "") }        // 8 12 16 20
print("")
var st2 = 8.stride(to: 20, by: 4)
for i in st2 { print(" (i)", terminator: "") }        // 8 12 16
print("")

なお実数型に適用したst4の例で出力結果が「0.0, 0.1, ... 0.8」となっていますが、「0.0, 0.2, ... 0.8」の誤りです。



●5.2 文字列と文字


・String型のメソッド

compareメソッドの戻り値がInt型とありますが、現在はNSComparisonResultが返ってきます。

let str: String = "三"
let cmp: Int = str.compare("四")        // error: cannot convert value of type 'NSComparisonResult' to specified type 'Int'

ただ、このcompareメソッドに関しては「Swift Standard Library Reference/String Structure Reference」や「The Swift Programming Language (Swift 2.1)/Strings and Characters」では見当たらず、「Xcode 7 Release Notes」でも削除や変更について言及されていないのでよく分からない状態です。

The Swift Programming Language (Swift 2.1)/Strings and Characters」でも等価性の比較には == を使っているので、文字列の比較は等号・不等号を使用するようになったのかもしれません。

str == "三"        // true
str > "四"        // false

また toInt() メソッドは「詳解Swift(4)〜オプショナル」で述べたように Int(String) イニシャライザに改名されています。


・文字列の長さとNSStringへの変換

文字列の長さ(文字数)を数えるために使用している関数 countElements() はSwift 1.2で廃止されてcount() を使用するようになりました(「Qiita/仕様変更【 Swift1.2 / Xcode6.3】まとめ」「Dorapro エンジニアBLOG/[Swift] Xcode6.3に上げたらビルドが通らなかった話」参照)が、Swift 2で .characters.count に再変更されたようです(「Qiita/Swiftで文字列長」「Swift 2における文字列」参照)。

現在の文字数の数え方については「The Swift Programming Language (Swift 2.1)/Strings and Characters(Counting Characters)」で以下のように詳細が綴られています。

文字の数え方

文字列内のCharacter値の数を取得するには、文字列のcharactersプロパティのcountプロパティを使用します。

let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪"
print("unusualMenagerie has (unusualMenagerie.characters.count) characters")
// prints "unusualMenagerie has 40 characters"

SwiftのCharacter値では拡張書記素クラスタの使用は文字列の連結を意味し、変更が常に文字列の文字数に影響を与えるとは限らないことに注意してください。

例えば4文字からなる単語 cafe で新たに文字列を初期化した際、その後文字列の最後に COMBINING ACUTE ACCENT(U+0301)を追加すると、結果の文字列は文字数は4文字のままで4番目の文字は e ではなくé になります。

var word = "cafe"
print("the number of characters in (word) is (word.characters.count)")
// prints "the number of characters in cafe is 4"

word += "u{301}"        // COMBINING ACUTE ACCENT, U+0301

print("the number of characters in (word) is (word.characters.count)")
// prints "the number of characters in café is 4"




拡張書記素クラスタは1つ以上のUnicodeスカラで構成することができます。
これは異なる文字(や同じ文字で異なる表現)を格納するために必要なメモリ量が異なることを意味します。
このためSwiftの文字は、文字列表現におけるメモリの各消費量が同じではありません。
したがって文字列の文字数は、拡張書記素クラスタ境界を決定するため文字列を反復処理しなければ算出することはできません。
特に長い文字列値を処理する場合、charactersプロパティは文字列の文字を決定するため、文字列全体のUnicodeスカラを反復処理する必要があることに注意してください。

charactersプロパティによって返される文字数は、同じ文字を含むNSStringのlengthプロパティと常に同じではありません。
NSStringでの長さは、文字列のUTF-16表現における16ビットコード単位での数に基づいており、文字列内のUnicode拡張書記素クラスタの数ではありません。


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

var a = "Life is short and time is swift."
countElements(a)        // error: use of unresolved identifier 'countElements'
count(a)        // error: cannot invoke 'count' with an argument list of type '(String)'
a.characters.count        // 32

その次のNSStringとString間の変換機能は、FoundationやCocoaだけでなくUIKitをインポートしていれば使用できます。

また utf16Count も「Xcode Release Notes(Xcode 6.3 Release Notes/Changes, Enhancements, and Notes/Swift Standard Library Enhancements and Changes)」にてSwift 1.2で削除されたとありましたが、

「utf16CountはStringから削除されました。代わりにStringのUTF16ビュー上でcountを使用してください。(17627758)」

Swift 2ではUTF-16ビューでのカウントは .utf16.count を代わりに使用するようにと促されます。

a.utf16Count        // error: 'utf16Count' is unavailable: Take the count of a UTF-16 view instead, i.e. str.utf16.count
count(a.utf16)        // error: 'count' is unavailable: access the 'count' property on the collection
a.utf16.count        // 32


・合成された文字の問題

ここでもutf16CountとcountElementsが使用されていますが、上記の通り使用不可なのでそれぞれ.utf16.countと.characters.countに修正することになります。

print( str.utf16Count )        // error: 'utf16Count' is unavailable: Take the count of a UTF-16 view instead, i.e. str.utf16.count
print( str.utf16.count )
print( countElements(str) )        // error: use of unresolved identifier 'countElements'
print( str.characters.count )

また「詳解Swift(1)〜Swiftでプログラミング」の「for-in文」の項で述べたように、String型がSequenceTypeプロトコルに準拠しなくなったので、文字列からfor-in文で文字を取り出すには characters プロパティを使用する必要があります。

for cc in str { print("(cc):", terminator: "") } ; print()        // error: type 'String' does not conform to protocol 'SequenceType'
for cc in str.characters { print("(cc):", terminator: "") } ; print()



●5.3 配列


・部分配列

変数・定数の型はdynamicTypeで調べることができます。
(「Qiita/Swiftで自分のクラス名を表示する方法」参照)

var conso = ["K", "S", "T", "N", "H", "M", "Y", "R", "W"]
let sub = conso[3...5]
print(sub)
conso.dynamicType        // Array<String>.Type
sub.dynamicType        // ArraySlice<String>.Type

let subarray = [String](sub)
subarray.dynamicType        // Array<String>.Type


・配列のイニシャライザ

上記の通りStride型メソッドの書式が変わりましたので、定数 arr2 は次のように定義することになります。

let arr2 = [Double](stride(from: 20.0, through:0.0, by:-0.5))        // error: 'stride(from:through:by:)' is unavailable: call the 'stride(through:by:)' method instead

let arr2 = [Double](20.0.stride(through:0.0, by:-0.5))


・配列のメソッド

配列を逆順にする reverse メソッドを単純に実行しようとすると上手くいきません。

let test = ["壱", "弐", "参"]
let rev = test.reverse()    // ReverseRandomAccessCollection<Array<String>>
print("\(rev), \(test)")    // "ReverseRandomAccessCollection<Array<String>>(_base: ["壱", "弐", "参"]), ["壱", "弐", "参"]\n"

これは「Qiita/ReverseRandomAccessCollectionの挙動」によると、新たに作成される逆順配列がインデックスされていないためのようです。
この記事では print 文中でイニシャライズして対処しています。

let rev = test.reverse()    // ReverseRandomAccessCollection<Array<String>>
print("\(Array(rev)), \(test)")    // "["参", "弐", "壱"], ["壱", "弐", "参"]\n"

ただ reverse メソッド実行時(逆順配列生成時)に問題が解決されていないのは少々気持ち悪いものです。
そこで思いつきで受け入れる定数 rev を型宣言してみたところ上手くいきました。

let rev: [String] = test.reverse()    // ["参", "弐", "壱"]
print("\(rev), \(test)")    // "["参", "弐", "壱"], ["壱", "弐", "参"]\n"

また配列の sort メソッドを実行しても並べ替えらず結果が変わりません。

var p = ["Mercury", "Venus", "Jupiter"]
p.sort( { $0 < $1 } )
print(p)        // ["Mercury", "Venus", "Jupiter"]

これは「モバイル開発系(K)/iOS Objective-C, Swift Tips/配列をソートする(Swift) [Array, sort, sortInPlace]」で指摘されているように、Swift 2.0 でメソッド名が変更されているためです。

これに関しては「Xcode Release Notes(Xcode 7.0 Release Notes/Swift/Swift Standard Library)」で次のように記述されています。

「find は indexOf() に改名し、sortは sortInPlace()に改名、そして sorted() は sort() に改名しました。」

つまり上記の p.sort() は Swift 1.2 以前における p.sorted() が実行されたことになり、配列 p の要素が変更されなかったわけです。
実際 p.sort() の結果を別の配列 q に代入すると、q は並べ替えられた要素を持つ配列になります。

let q = p.sort( { $0 < $1 } )
print(p)        // ["Mercury", "Venus", "Jupiter"]
print(q)        // ["Jupiter", "Mercury", "Venus"]

したがって配列 p 自身の要素を並べ替えるには sortInPlace() を使用します。

p.sortInPlace( { $0 < $1 } )
print(p)        // ["Jupiter", "Mercury", "Venus"]


・配列とfor-in文

関数 findString の定義でエラーが出ますが、これは「詳解Swift(2)〜関数(2.1 関数定義の基本/外部引数名)」で述べたように、関数の内部引数名と外部引数名を同じにするための # は使用できなくなったためで、第1引数の外部引数名は明示する必要があります。

func findString(#key:String, words:[String]) -> Bool {        // '#' has been removed from Swift; double up 'key key' to make the argument label the same as the parameter name

func findString(key key:String, words:[String]) -> Bool {

また Swift 2.0 で reverse 関数が廃止されたようで(「tmp/Swift1.2 -> Swift2」参照)エラーが出るようになり、代わりにメソッドの reverse() を使用するよう促されます。

for v in reverse(arr) { print("\(v) ", terminator: "") }        // error: 'reverse' is unavailable: call the 'reverse()' method on the collection


・可変個の引数が利用できる関数

本項の主旨から逸れますが「詳解Swift(1)〜Swiftでプログラミング(1.2 制御構文/print文)」で述べたように、Swift 2.0 からprintln 関数が廃止されると同時に print 関数が可変長引数を持てるなど改善がなされました。
(「Swift Standard Library Reference/Swift Standard Library Functions Reference/print(_:separator:terminator:)」参照)
したがって現在は print 文単体で可変長引数を処理することができます。

print("Output:", 3, 1, 4, 6, 7)        // Output: 3 1 4 6 7



Wikipedia/NaN

Qiita/Swift - 範囲変数を使った case の書き方

Qiita/Swift での Int 型 switch case の落とし穴(?)

InfoQ/Swiftに対するIBMの反応

Swift Standard Library Reference/String Structure Reference

Xcode 7 Release Notes

The Swift Programming Language (Swift 2.1)/Strings and Characters

Qiita/仕様変更【 Swift1.2 / Xcode6.3】まとめ

Dorapro エンジニアBLOG/[Swift] Xcode6.3に上げたらビルドが通らなかった話

Qiita/Swiftで文字列長

Qiita/Swiftで自分のクラス名を表示する方法

モバイル開発系(K)/iOS Objective-C, Swift Tips/配列をソートする(Swift) [Array, sort, sortInPlace]

tmp/Swift1.2 -> Swift2

Swift Standard Library Reference/Swift Standard Library Functions Reference/print(_:separator:terminator:)

Qiita/ReverseRandomAccessCollectionの挙動

詳解 Swift 改訂版(Amazon)
 

詳解Swift(4)〜オプショナル

2016. 06. 22
(※2016年6月22日追記:この記事は詳解 Swift(初版第1刷)を元に、Xcode 7.3.1(Swift 2.2)下における加筆・修正を行っています。(Xcode 7.3からXcode 7.3.1での変更点はありません))



CHAPTER 04 オプショナル



●4.1 オプショナル型


・オプショナル型とnil

StringのtoInt()関数を実行するとエラーが出るようになりました。

var year : Int? = olympic.toInt()        // error: 'toInt()' is unavailable: Use Int() initializer

これに関して「Xcode Release Notes(Xcode 7.0 Release Notes/Swift/Swift Standard Library)」に、失敗のあるイニシャライザ Int(String)に改名されたと記述があります。

「初期化構文に型変換のための好ましいスタイルがあるので、String.toInt() は失敗のある Int(String) イニシャライザに改名されました。」

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

var year : Int? = Int(olympic)


・オプショナル型の値を開示する

オプショナル型の値がnilだった場合の例において、Int型変数remainを求める式が間違っており、三項演算子ではないかと警告が出ます。

let ww3 : Int? = nil
var remain : Int = ww3! ? 2014        // Expected ' : ' after ' ? ... ' in ternary expression

おそらく「?」は「-」などの二項演算子と思われます。
(ただし二項演算子に変更しても、オプショナル値を開示する際に予期せぬnilが発見されたという致命的エラーが出ます。)

var remain : Int = ww3! - 2014        // fatal error: unexpectedly found nil while unwrapping an Optional value


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

nilがオプショナルの型のどのような値よりも小さいということを確認してみます。
Int型の最小値は「Swift言語を学ぶ/数値型」によると関数 min で取得できます。

var x : Int? = nil
x < Int.min        // true
Int.min        // -9223372036854775808



●4.3 失敗のあるイニシャライザ


・失敗のあるイニシャライザの定義

List4-1において、「詳解Swift(3)〜構造体」で述べたように定数プロパティへの割り当ては一度のみとなったため、構造体Timeの定数in24hに初期値を与えているとエラーになります。

struct Time {
    let in24h : Bool = false
    var hour = 0, min = 0
    init?(_ h:Int, _ m:Int, in24h:Bool = false) {
        (中略)
        self.in24h = in24h    // error: immutable value 'self.in24h' may only be initialized once
        (中略)
    }
    init(time:Time, in24h:Bool) {
        (中略)
        self.in24h = in24h    // error: immutable value 'self.in24h' may only be initialized once
        (中略)
    }
}

in24h はインスタンス生成時に指定し直しているので、変数として定義しましょう。

実行例ですが、変数 u は生成時に Bool値 in24h の指定を省略しているため失敗のあるイニシャライザが呼び出されます。
つまり in24h は12時制を示す初期値 false であり、時が23であることから nil が返され、非オプショナル Int 型への nil の代入による致命的エラーが発生して実行が中断されます。

var u:Time = Time(23, 40)!
// Execution was interrupted. reason:EXC_BAD_INSTRUCTION (code= EXC_1386_INVOP, subcode=0x0).
// fatal error: unexpectedly found nil while unwrapping an Optional value

したがって、その後にある変数 u の更新を確認するには、変数 u の生成時に「Time(3, 40)」など12時制で有効な値を与える必要があります。



Swift言語を学ぶ/数値型

詳解 Swift 改訂版(Amazon)
 

詳解Swift(3)〜構造体

2016. 06. 12
(※2016年6月12日追記:この記事は詳解 Swift(初版第1刷)を元に、Xcode 7.3.1(Swift 2.2)下における加筆・修正を行っています。)



CHAPTER 03 構造体



●3.1 構造体の定義


・構造体の定数プロパティ

List3-2のように、初期値を持つ定数が含まれる構造体で全項目イニシャライザを使おうとするとエラーが出るようになりました。

struct Time {
    let in24h: Bool = false
    var hour = 0, min = 0
}

var t = Time(in24h: true, hour:20, min:0)
// error: cannot invoke initializer for type 'Time' with an argument list of type '(in24h: Bool, hour: Int, min: Int)'
// note: overloads for 'Time' exist with these partially matching parameter lists: (hour: Int, min: Int), ()


これは「Dorapro エンジニアBLOG/[Swift] Xcode6.3に上げたらビルドが通らなかった話」の「インスタンスのlet定数への代入は、初期値を含め一度のみ」でも指摘されているようにXcode6.3/Swift1.2で変更されたもので、「Xcode Release Notes(Xcode 6.3 Release Notes/Changes, Enhancements, and Notes/Swift Language Changes)」に以下のように記述されています。

「構造体やクラスのイニシャライザにおける不変(let)プロパティは、一般的な「一度初期化したら再割り当てや変更はできない」というモデルの標準に合わせるために改訂されました。以前はイニシャライザの本文内で完全に可変でした。現在は値を提供するために一度だけ割り当てることを許可されています。プロパティが宣言内で初期値を持っている場合、全てのイニシャライザでの初期値としてカウントされます。(19035287)」

これまでの定数プロパティは、構造体の宣言内で初期値が設定されていてもインスタンス生成時に再設定することが可能でしたが、「定数は一度だけ設定できる」という原則に従うようにしたというわけです。

したがって上記の構造体 Time の場合、インスタンス生成時に入力補完として定数プロパティ in24h は含まれません。
定数プロパティをインスタンス生成時に設定したい場合は、宣言時に初期値を与えないようにする必要があります。

struct Time {
    let in24h: Bool
    var hour = 0, min = 0
}

var t = Time(in24h: true, hour: 20, min: 0)


・複数個のイニシャライザを定義する

上記の通り定数プロパティは再設定できなくなっており、且つList3-4の最初のイニシャライザは時と分のみ与えているため in24h には初期値が必要であり、また二つ目のイニシャライザでは in24h を置き換えているため、in24h は変数プロパティにして試すと良いでしょう。

struct Time {
//     let in24h: Bool = false
    var in24h: Bool = false
    var hour = 0, min = 0
    init(hour:Int, min:Int) {
        self.hour = hour
        self.min = min
    }
    init(hourIn24 h:Int) {
        in24h = true
        hour = h
    }
    init(_ hour:Int) {
        self.init(hourIn24: hour)
    }
}



●3.2 メソッド


・インスタンスに対するメソッドの定義

詳解Swift(2)〜関数」の「外部引数名」の項で述べたように、Swift 2.0からメソッドと関数は外部引数名に同じ規則が適用されることになり、メソッドの規則が関数にも適用されることになりました。
したがってTable3-1は以下のようになります。

Table3-1 関数、イニシャライザ、メソッドの外部引数名に関するまとめ

関数イニシャライザメソッド
関数/メソッド名自由(関数の機能を説明するもの)
関数の機能と第1引数を説明するもの
常にinit関数の機能と第1引数を説明するもの
返り値自由なし自由
内部引数名と外部引数名の関係明示しなければ、外部引数名にはならない
第2引数以降について、自動的に外部引数名になる
自動的にすべてが外部引数名になる第2引数以降について、自動的に外部引数名になる
外部引数名を指定するには外部引数名を明示する。または#を指定して同じ名前を使うことを示す。
外部引数名を明示する。第2引数以降への#の指定は冗長(※ #は使用不可)
外部引数名を明示する。#の指定は冗長
(※ #は使用不可)
外部引数名を明示する。第2引数以降への#の指定は冗長(※ #は使用不可)
外部引数名を使わないようにするには何もしない。または外部引数名として「_」を指定する 
外部引数名として「_」を指定する。第1引数に対する指定は冗長
外部引数名として「_」を指定する外部引数名として「_」を指定する。第1引数に対する指定は冗長



●3.3 プロパティ


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

コードの動作確認にプレイグラウンドを使用している方も多いと思いますが、プレイグラウンドファイル作成時にPlatformでiOSを選択した場合、NSScreenクラスを使用しているList3-13は動かせません。

OS Xコード用に別途プレイグラウンドファイルを作成してもいいのですが、一時的にコードを試すだけならPlatformの設定を変えた方が早いでしょう。

1)Xcodeのメニューで、View > Utilities > Show File Inspectorを選択します。
2)ファイルインスペクタのPlayground Settingsで、PlatformをiOSからOS Xに変更します。
3)プラットフォームをOS Xにすると、先頭行にあるUIKitのインポートが無効になるのでコメントアウトします。

後はCocoaをインポートすればOS X用のコードを記述できます。
(エラーが出る場合はファイルをセーブ後に一旦閉じて、再度開くと治るかもしれません。)

元のiOS用に戻すには逆の手順を踏みます。

1)Cocoaのインポートを含むOS X用コードを削除ないしコメントアウトする。
2)コメントアウトしたUIKitのインポートを復活させる。
3)ファイルインスペクタでPlatformをiOSに変更する。



●3.4 添字付け


・2次元以上の添字付け

List3-16 の 2つ目の subscript ですが、subscript も関数と同様に仮引数を変数としようとすると「'var' は Swift 3 で削除するため廃止になりました」と警告が出るようになりました。

8043.png

したがって以下のように修正する必要があります。

subscript(曜日:Week, var 時間:Int) -> String {    // 'var' parameters are deprecated and will be removed in Swift 3
    let arr = self[曜日]
    時間--    // '--' is deprecated: it will be removed in Swift 3
    return 時間 < arr.count ? arr[時間] : ""
}

subscript(曜日:Week, 時間:Int) -> String {    // 仮引数 '時間' の var を削除
    let arr = self[曜日]
    let 時間_ = 時間 - 1    // '時間' をデクリメントして定数 '時間_' に代入
   return 時間_ < arr.count ? arr[時間_] : ""
}



Dorapro エンジニアBLOG/[Swift] Xcode6.3に上げたらビルドが通らなかった話

Wikipedia/液量オンス

Qiita/Swift 2.2でインクリメントとデクリメント

詳解 Swift 改訂版(Amazon)
 


詳解Swift(2)〜関数

2016. 06. 02
(※2016年6月2日追記:この記事は詳解 Swift(初版第1刷)を元に、Xcode 7.3.1(Swift 2.2)下における加筆・修正を行っています。(Xcode 7.3からXcode 7.3.1での変更点はありません))



CHAPTER 02 関数



●2.1 関数定義の基本


・関数定義の概要

List 2-1の関数isTriangle内の文で、elseの後の { が欠落しています。

    } else    // Expected '{' after 'else'
        return c < a + b

    } else {
        return c < a + b

またサンプルコードを入力している際の入力補完で気付くと思いますが、Swift 2以降はプレイグラウンドでも関数呼び出し時に2つ目以降の引数にラベルを付けるように統一されたようです。
(「Google ブックス/Swiftではじめる iPhoneアプリ開発の教科書 【Swift 2&Xcode 7対応】 - 森巧尚」、「Qiita/Swift で関数一つ呼ぶのに苦労した話し」、「Qiita/Swiftにおける名前付き引数」参照)

print(isTriangle(12.0, 20.0, 8.5))        // error: missing argument labels 'b:c:' in call
print(isTriangle(12.0, b: 20.0, c: 8.5))


・外部引数名

外部引数名と関数定義内での仮引数名を同じにする場合は仮引数名の前に # を付けることになっていましたが、エラーが出るようになりました。

func area(#height:Double, #width:Double) -> Double {

// '#' has been removed from Swift; double up 'height height' to make the argument label the same as the parameter name
// '#' has been removed from Swift; 'width' already has an argument label

一つ目のエラーは #height に対するもので「#height」を「height height」に修正するように、二つ目のエラーは #width に対するもので「#」を削除するようにと言われます。

これに関して「Xcode 7 Release Notes(Xcode 7.0 Release Notes/Swift/Swift Standard Library)」に、Xcode 7.0/Swift 2.0からメソッドと関数は外部引数名に同じ規則が適用されると記述があります。

「メソッドと関数は引数名に同じ規則を持ちます。"_"を与えることで外部引数名を省略することができます。さらにモデルを簡素化するため、デフォルト引数が特別な規則を持っているように、引数名を省略するための簡略表現 "#" は削除されました。

宣言
    func printFunction(str: String, newline: Bool)
    func printMethod(str: String, newline: Bool)
    func printFunctionOmitParameterName(str: String, _  newline: Bool)

呼出
    printFunction("hello", newline: true)
    printMethod("hello", newline: true)
    printFunctionOmitParameterName("hello", true)

(17218256)」


つまり、

・内部引数名と外部引数名を同じにするための「#」は使用できないので削除する
・第1引数の外部引数名はデフォルトで無し
 指定する際は明示する
 非表示にする場合の _ 指定は冗長(警告が出る)
・第2引数以降の外部引数名はデフォルトで内部引数名が自動割当
 別途指定する際は明示する
 非表示にする場合は _ を指定する

ということになるため、先のarea関数は以下のように修正することになります。
(「Qiita/Swiftの関数名と外部引数名の付け方メモ」参照)

func area(height height:Double, width:Double) -> Double {


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

List2-5 で仮引数 m と y を変数にして関数内で変更できるようにしようとしていますが、「'var' は Swift 3 で削除するため廃止になりました」と警告が出るようになりました。

8041.png

ここでは元の実引数はそのままに関数内での処理で変更するだけなので「Qiita/既存アプリの Xcode 7.3 + Swift 2.2 対応」に倣い、仮引数リスト上ではなく関数内で別の変数に入れ替えるといいでしょう。

func dayOfWeek(var m:Int, d:Int, var year y:Int = 2015) -> Int {    // 'var' parameters are deprecated and will be removed in Swift 3

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

仮引数と同名の変数だと煩雑なので別名にした方がいいかもしれません。



●2.2 オーバーロード


・オーバーロードとは

List2-2のInt型引数2つで戻り値無しのmySwap関数に対し、List 2-8のBool型戻り値を持つmySwap関数をオーバーロードした場合、単純に呼び出すとエラーになります。

// List2-2
func mySwap(inout a:Int, inout b:Int) {
    let t = a; a = b; b = t
}

// List2-8
func mySwap(inout a:Int, inout b:Int) -> Bool {
    if a == b {
        return false
    }
    let t = a; a = b; b = t
    return true
}

var s = 10, t = 20
mySwap(&s, b: &t)        // error: ambiguous use of 'mySwap(_:b:)'

呼び出し行に「mySwapの不明瞭な使用」とエラーが出るとともに、2つのmySwap関数の定義が「note: found this candidate」と候補に挙げられます。

戻り値無しのmySwap関数を使用したい場合は、「Apple Developer/Swift Blog/Redefining Everything with the Swift REPL」の「Redefinition or Overload?」にあるように、後ろに「as Void」と追加することで呼び出すことができます。

mySwap(&s, b: &t) as Void
print("s=\(s), t=\(t)")        // "s=20, t=10"

また読者の多くは本書を読み進めながら逐次プレイグラウンドでコードを試していると思われますが、一つ不可解なエラーに遭遇すると思います。
List2-2、List2-7、List2-8と続けて試して行くと、List2-8の例を実行した際にエラーが出るのです。

// List2-2
func mySwap(inout a:Int, inout b:Int) {
    let t = a; a = b; b = t
}

// List2-7
func mySwap(inout a:Int, inout b:Int, inout c:Int) {
    let t = a; a = b; b = c; c = t
}

var s = 10, t = 20
var x = 1, y = 2, z = 3
mySwap(&s, b: &t) as Void
print("s=\(s), t=\(t)")        // "s=20, t=10"
mySwap(&x, b: &y, c: &z)
print("x=\(x), y=\(y), z=\(z)")        // "x=2, y=3, z=1"

func mySwap(inout a:String, inout b:String) {
    let t = a; a = b; b = t
}

// List2-8
func mySwap(inout a:Int, inout b:Int) -> Bool {
    if a == b {
        return false
    }
    let t = a; a = b; b = t
    return true
}

let flag : Bool = mySwap(&s, b: &t)        // Cannot convert value of type '()' to specified type 'Bool'
print("s=\(s), t=\(t), flag=\(flag)")        // "s=10, t=20, flag=true"

一応実行結果は意図通りに出力されますが、Bool値を返す関数mySwapを実行すると「Cannot convert value of type '()' to specified type 'Bool'」というエラーが出ます。
理由は不明ですが、このエラーはmySwap関数群の定義をひとまとめにし、その後に実行をまとめて行った場合には出ません。

// List2-2
func mySwap(inout a:Int, inout b:Int) {
    let t = a; a = b; b = t
}

// List2-7
func mySwap(inout a:Int, inout b:Int, inout c:Int) {
    let t = a; a = b; b = c; c = t
}

func mySwap(inout a:String, inout b:String) {
    let t = a; a = b; b = t
}

// List2-8
func mySwap(inout a:Int, inout b:Int) -> Bool {
    if a == b {
        return false
    }
    let t = a; a = b; b = t
    return true
}

var s = 10, t = 20
var x = 1, y = 2, z = 3
mySwap(&s, b: &t) as Void
print("s=\(s), t=\(t)")        // "s=20, t=10"
mySwap(&x, b: &y, c: &z)
print("x=\(x), y=\(y), z=\(z)")        // "x=2, y=3, z=1"

let flag : Bool = mySwap(&s, b: &t)        // エラー出ず
print("s=\(s), t=\(t), flag=\(flag)")        // "s=10, t=20, flag=true"

前節の「関数の呼び出し関係」でSwiftは前方参照について考慮する必要はないとありますし、定義と実行の前後関係でエラーが出たり出なかったりするのも不可解ですが、この問題はSwift 2.2でも発生するようです。



Google ブックス/Swiftではじめる iPhoneアプリ開発の教科書 【Swift 2&Xcode 7対応】 - 森巧尚

Qiita/Swift で関数一つ呼ぶのに苦労した話し

Qiita/Swiftにおける名前付き引数

Qiita/Swiftの関数名と外部引数名の付け方メモ

Wikipedia/ツェラーの公式

Qiita/既存アプリの Xcode 7.3 + Swift 2.2 対応

詳解 Swift 改訂版(Amazon)
 

詳解Swift(1)〜Swiftでプログラミング

2016. 06. 01
(※2016年6月1日追記:この記事は詳解 Swift(初版第1刷)を元に、Xcode 7.3.1(Swift 2.2)下における加筆・修正を行っています。)



Objective-C時代に開発者の必携だった「詳解 Objective-C 2.0 第3版」の著者である荻原剛志さんが書かれたSwift本「詳解 Swift」が昨年満を持して発売されました。(2015年12月25日にSwift2に対応した「詳解 Swift 改訂版」が発売されています。)

無料で読めるウェブサイトの入門記事などでは理解し難かった点が丁寧に解説されており、第1章を読んだだけでも買ってよかったと思えるほどの良本で、今後の開発者にとって必携と言えるでしょう。

この本1冊でiOSアプリを開発できるわけではありませんが、初心者の方は入門書1、2冊と「詳解Swift」を一緒に買って勉強するのが良いと思います。



CHAPTER 01 Swiftでプログラミング



●1.1 データ型と変数


・型パラメータ

リストで配列のパラメータ付き型指定の部分は、ArratではなくArrayです。

var a : Arrat<Int>        // error: use of undeclared type 'Arrat'
var a : Array<Int>



●1.2 制御構文


・while文

List1-1 の while 文中の前置インクリメント部分に「'++'は Swift 3 で削除するため廃止になりました」と警告が出るようになりました。

Xcode 7 Release Notes(Xcode 7.3 Release Notes/New Features/Swift)」に、++/-- 演算子に関する記述があります。


「++ と -- 演算子は Swift 3.0 で削除する予定のため廃止されました。
その代わりとして、整数または浮動小数点型では x += 1 を、インデックス型では x = x.successor() を使用してください。(23708702)」


したがって " ++i " は " i += 1 " に置換することになります。

8037.png


・do-while文

Xcode 7 Release Notes(Xcode 7.0 Release Notes/Swift/Swift Standard Library)」に、Xcode 7.0/Swift 2.0からdo/whileループはrepeat/whileに名称変更されたと記述があります。

「do/whileループは、先頭のキーワードからその文がループであるかどうかを明示するために、repeat/whileに名称変更されました。

Swift 1.2:

do {
...
} while <condition>

Swift 2.0:

repeat {
...
} while <condition>

(20336424)」



・print文

List1-2のdo-while文は上記の理由により、記述すると「'do-while' statement is not allowed: use 'repeat-while' instead」とrepeat-whileに変更するようにエラーが出ます。

またprintln関数も「error: 'println' has been renamed to 'print'」というエラーが出ます。

このprint / println関数についても「Xcode 7 Release Notes(Xcode 7.0 Release Notes/Swift/Swift Standard Library)」に、Xcode 7.0/Swift 2.0から変更がなされていると記述があります。

「printおよびdebugPrint関数が改善されています:

 ・両関数は可変長引数となっており、一度の呼び出しで任意の数の項目を出力することができます。
 ・項目を分割することができるように、separator: String = " " が追加されました。
 ・appendNewline: bool = true は terminator: String = "\n" に置き換えられました。
  この変更により、print(x, appendNewline: false) はprint(x, terminator: "") と表します。
 ・変化する出力ストリームに対応するため、引数ラベル toStream がストリーム引数に追加されました。

 Swift 1.2からのprintln関数は削除されました。」


つまり「いものやま。/Xcode 7にアップデートしてみた。」で説明されているように、println()関数は廃止され、print()関数は自動で改行されるようになりました。

・改行無しのコンソール出力:print(x)    → print(x, terminator: "")
・改行有りのコンソール出力:println(x) → print(x)

したがってList1-2は下記のように修正することになります。

var n = 7
repeat {
    print("\(n) ", terminator: "")
    if n % 2 == 0 {
        n /= 2
    }else {
        n = n * 3 + 1
    }
}while n > 1
print(n)

ただしXcode 6.xまではAssistant Editorに出力されていたprint() / println()関数のコンソール出力は、Xcode 7.0から(Assistant Editorには何も出力されず)Debug Areaに出力されるようになったので注意が必要です。
(「iPhoneDev.tv/Xcode 7 moves Playgrounds Console Output and breaks print() and println()」参照)

以降の例においてもprint() / println()関数は多用されているので、その都度修正してください。


・for文

旧println()関数では空の改行を「println()」と記述していましたが、print()関数では「print()」ではなく「print("")」と空引数を渡す必要があるようです。

Xcode 7.3.1では空改行する際に空引数を渡さず単純に「print()」と記述するだけでも警告は出ないようです。
(以前の記法「print("")」でも問題ありません)

println()        // error: 'println()' is unavailable: use print("")
print()          // error: 'print' is unavailable: Please wrap your tuple argument in parentheses: 'print((...))'
print("")

また先述の前置インクリメントと同様に List1-3 の後置インクリメントも警告が出るため、" i++ " は " i += 1 " に置換することになります。

8038.png

しかしインクリメント演算子を修正すると今度は「Swift の将来のバージョンで削除されるため、C(言語)様式の文は推奨されない」と警告が出ます。

8039_.png   

この件に関しては「Qiita/Swift 初心者がまとめる Xcode 7.3 で Warning になった Swift の文法」や「Qiita/既存アプリの Xcode 7.3 + Swift 2.2 対応」などで for-in 文への修正方法が提案されています。

for var i = 1; i <= 9; i += 1 {        // C-style for statement is deprecated and will be removed in a future version of Swift
    for var j = 1; j <= 9; j += 1 {        // C-style for statement is deprecated and will be removed in a future version of Swift

for i in 1...9 {
    for j in 1...9 {

ただ for-in 文では List1-4 のように初期化部で複数の変数を初期化するというような記述はできないので、普通に分けて記述するしかないでしょうか?

for var i=0, m=1; i < 32; i++, m *= 2 {        // '++' is deprecated: it will be removed in Swift 3
// C-style for statement is deprecated and will be removed in a future version of Swift

    print("\(i)  \(m)")
}

var m = 1
for i in 0..<32
{
    print("\(i)  \(m)")
    m *= 2
}


・for-in文

List1-5のfor-in内の文で、String型イニシャライザで変数chを初期化する部分は小文字のstringではなく型名のStringです。

result = string(ch) + result    // error: use of unresolved identifier 'string'

result = String(ch) + result

またSwift 2.0以降、文字列から一文字ずつ取り出すfor-in文はエラーが出るようになりました。

for ch in str {        // error: type 'String' does not conform to protocol 'SequenceType'

これは「String型はSequenceTypeプロトコルに準拠していません」(「articles of samekard/Swiftのコンパイルエラー寄せ集め」参照)というエラーです。
SequenceTypeとは本書の335頁に「for-in文で繰り返しができる型を表すプロトコル」とあります。

これに関しては「Xcode 7 Release Notes(Xcode 7.0 Release Notes/Swift/Swift Standard Library)」に、Xcode 7.0/Swift 2.0からString型の変更について記述があります。

「String no longer conforms to SequenceType in order to prevent non-Unicode correct sequence algorithms from being prominently available on String. To perform grapheme-cluster-based, UTF8-based, or UTF-16-based algorithms, use the .characters, .utf8, and .utf16 projections respectively.(抄訳:StringはSequeceTypeに準拠しなくなりましたので、書記素クラスタ単位で実行するにはそれぞれ.characters、.utf8、.utf16プロパティを使ってください。)」

したがって、List1-5のfor-in文の式は以下のようになります。

for ch in str.characters {


・ラベル付きのループ文

List1-6 に関してですが、先述の通りインクリメント演算子や C 言語様式の for 文は使えなくなっていますので修正する必要があります。

let days = 31
let firstDay = 2
var w = 0

//for ; w < firstDay; w++ {    // '++' is deprecated: it will be removed in Swift 3
//for ; w < firstDay; w += 1 {    // C-style for statement is deprecated and will be removed in a future version of Swift

for w0 in 0..<firstDay {    // 外部変数 w の代わりに内部定数 w0 を設定
        print("   ", terminator: "")
        w = w0 + 1    // 内部定数 w0 をインクリメントして外部変数 w に代入
}

var d = 1
loop: while true {

//    for ; w < 7; w++ {    // '++' is deprecated: it will be removed in Swift 3
//    for ; w < 7; w += 1 {    // C-style for statement is deprecated and will be removed in a future version of Swift

        for _ in w..<7 {    // 内部定数は使用しないので '_' で省略
                print(d < 10 ? "  " : " ", terminator: "")
                print(d, terminator: "")

//        if ++d > days {    // '++' is deprecated: it will be removed in Swift 3
//        if d += 1 > days {    // error: binary operator '+=' cannot be applied to operands of type 'Int' and 'Bool'
//        if (d += 1) > days {    // error: cannot convert value of type '()' to expected argument type 'Int'
//        if ((d += 1) > days) {    // error: cannot convert value of type '()' to expected argument type 'Int'

        d += 1    // if 文の条件式内で二項演算することはできないようなので手前でインクリメント
        if d > days {    // 条件式は単純な比較
            print("")
            break loop
        }

    }
    print("")
    w = 0
}

1つ目の for/for-in 文ですが、元の for 文では w = 2 でループを抜けるのに対し、for-in 文では w0 = 1 の状態でループから抜けてしまうので、w に代入する際にインクリメントしていることに注意してください。
(式を閉区間にすると文が1つ余計に実行されてしまうので半開区間にしています)

それと if 文の条件式内で二項演算は括弧付きでもできないようなので、手前で d をインクリメントした後に if 文では単純な比較の条件式にしています。



●1.3 簡単な実行方法

List1-7 の for 文を for-in 文に修正する場合、範囲型の式は実数で表すとエラーになるので整数で表し、文中で Double 型にする必要があります。

var e = 0.0, t = 1.0
//for var i = 1.0; i <= 20.0; i++ {
//for i in 1.0...20.0 {    // error: binary operator '...' cannot be applied to two 'Double' operands

for i in 1...20 {
    e += t
    t /= Double(i)
}
print("e=\(e)")

なお変数の推移を表すグラフは現在エディタ領域に表示されるようになっています。

8040.png



Wikipedia/Swift (プログラミング言語)

IT用語辞典/リテラル

Wikipedia/キャメルケース

Wikipedia/コラッツの問題

いものやま。/Xcode 7にアップデートしてみた。

iPhoneDev.tv/Xcode 7 moves Playgrounds Console Output and breaks print() and println()

articles of samekard/Swiftのコンパイルエラー寄せ集め

Qiita/Swift 初心者がまとめる Xcode 7.3 で Warning になった Swift の文法

Qiita/既存アプリの Xcode 7.3 + Swift 2.2 対応

詳解 Swift 改訂版(Amazon)
 






bose_soundlink_revolve
Calendar
05 | 2016/06 | 07
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 - -
Recent Articles
iTunes


Swift
Categories
Tips
Profile

水月杏香

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

Wish List
WACOM


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

bose_soundlink_revolve
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