詳解Swift第3版(06)〜パターン

2017. 11. 12
この記事は詳解Swift 第3版(初版第1刷)を元に、Xcode 9.0(Swift 4.0)下における差異を記述しています。



CHAPTER 06 パターン



●6.3 共用型の列挙型


・プロトコルの採用

CustomStringConvertible プロトコルを使った例として List6-9 に示すとありますが、正しくは List6-8(P.150)です。



詳解Swift 第3版(Amazon)
 

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

2017. 11. 10
この記事は詳解Swift 第3版(初版第1刷)を元に、Xcode 9.0(Swift 4.0)下における差異を記述しています。



CHAPTER 05 基本的なデータ型



●5.1 整数と実数


・数値型のリテラル

数値型リテラルの記述方法の詳細について、「付録B(1) の構文図」を参照とありますが、正しくは「APPENDIX B Swiftの構文図」の「B.2 構文図(P.493)」です。


・数値型のイニシャライザ

失敗のあるイニシャライザで、例としてあげられている Int8 型の場合、引数に実数と整数を与えた場合の挙動が異なるようです。
Xcode 9.0(Swift 4.0)では引数に実数と整数を与えた場合の挙動が同じく nil になるようです。

Int8.max    // 127
let int8Value1 = Int8(exactly: 127.0)    // 127
let int8Value2 = Int8(exactly: 127.1)    // nil
let int8Value3 = Int8(exactly: 128.0)    // nil
let int8Value4 = Int8(exactly: 127)    // 127
print(int8Value4)    // Optional(127)
let int8Value5 = Int8(exactly: 128)    // error: integer overflows when converted from 'Int' to 'Int8?'(Xode 8.3.3(Swift 3.1))
let int8Value5 = Int8(exactly: 128)    // nil

Float.greatestFiniteMagnitude    // 3.402823e+38
let floatValue1 = Float(exactly: Float.greatestFiniteMagnitude)    // 3.402823e+38
let floatValue2 = Float(exactly: Double.greatestFiniteMagnitude)    // nil

Int8 型の失敗のあるイニシャライザの引数に Int8 型の最大値を超える値を実数で与えた場合は nil が返り、整数で与えた場合は実行時エラーが発生と、挙動が異なるので注意が必要です。
(「
Developers.IO/[Swift 3.1] 全ての数値型に失敗可能変換イニシャライザーが追加されました」参照)



●5.2 範囲型と Stride 型


・Stride 型の概要

読んでいればすぐに気付くと思いますが、P.108 冒頭の記述で、

「関数 stride(from:to:by:) で構成される StrideThrough 型は、順番に取り出した最後の値が終了点の値と一致した場合、その値を採用しません。
 一方、関数 stride(from:through:by:) で構成される StrideTo 型は終了点の値も含みます。
(中略)
 このように、StrideTo 型を for-in 文で使った場合、終了点の値も含むことがわかります。」

とありますが、正しくは

「関数 stride(from:to:by:) で構成される StrideTo 型は、順番に取り出した最後の値が終了点の値と一致した場合、その値を採用しません。
 一方、関数 stride(from:through:by:) で構成される StrideThrough 型は終了点の値も含みます。
(中略)
 このように、StrideThrough 型を for-in 文で使った場合、終了点の値も含むことがわかります。」

の誤りです。


・Stride 型とプロトコル Strideable

メモリ上の位置を表す型の参照先として「15.1 データ型の互換性」とありますが、これは旧版(詳解 Swift 改訂版)での表題で、第3版の表題は「互換なデータ型とポインタ」(P.344)となっています。



●5.3 配列


・配列のイニシャライザ

細かい話ですが、P.113 の3行目の「なお、型パラメータ付き方指定を使って〜」は「なお、型パラメータ付き指定を使って〜」の誤りです。



●5.4 文字列と文字


文字列型、文字型に関するプロパティやメソッドが付録 A.2.6、付録 A.2.7 に掲載されているとありますが、正しくは文字列型は付録 A.2.5(P.454)、文字型は付録 A.2.6(P.458)に掲載されています。


・文字データの取り出し方

Developers.IO/[iOS 11] Swift 4は前バージョンから何が変わったか比較した」や「Qiita/Swift4で何がかわりそうなのか」で指摘されているように、Swift 4 から String は再び Character のコレクションとなり、それに伴いインスタンスプロパティ characters が廃止されることになりました。

文字列と文字(Swift 4)」の「Character の処理」にあるように、文字列から文字を取り出す方法が簡略化されており、List5-3 の for-in ループは下記のように記述することになります。

for ch in s.characters {    // characters プロパティは非推奨

for ch in s {    // Swift 4 から文字列はコレクション化

逆順の取り出しも以下のようになります。

for ch in s.characters.reversed() {    // Swift 3

for ch in s.reversed() {    // Swift 4


・文字列の長さ

上記の通り String が Character のコレクションとなったため、「文字列と文字(Swift 4)」の「文字を数える」にもあるように文字列内の Character 値の数を数えるには count プロパティを使用します。
ただし、これは CharacterView での数え方であり、UnicodeScalarView や UTF8View、UTF16View での文字数の数え方は今まで通りです。
したがって List5-4 の最初の print 文は以下のようになります。

print("Ch:\(str.characters.count), " + "US:\(str.unicodeScalars.count), U16:\(str.utf16.count)")    // Swift 3

print("Ch:\(str.count), " + "US:\(str.unicodeScalars.count), U16:\(str.utf16.count)")    // Swift 4


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

List5-6 において、部分文字列を返す return 文でエラーが出るようになりました。

return s[range]
// error: subscripts returning String were obsoleted in Swift 4; explicitly construct a String from subscripted result

これは「文字列と文字(Swift 4)」の「部分文字列」で説明されているように、文字列から添字を使用して取り出した部分文字列の型は String 型ではなく Substring 型になるためで、String 型に変換する必要があります。

return String(s[range])


・String.Index 型の意義

P.126 の2行目で「試みに、List5-5で、〜」とありますが、正しくは List5-8 です。


・String 型のメソッド

insert(contentsOf:at:) は、本書では第一引数(Character を要素とするコレクション)に文字列を直接指定できないとありますが、上記の通り Swift 4 から String が Character のコレクションとなったため、文字列を直接指定することができるようになっています。

var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)    // hello!

welcome.insert(contentsOf: " there", at: welcome.index(before: welcome.endIndex))    // hello there!

メソッド replaceRange(_:with:) は replaceSubrange(_:with:) に改名されており、範囲は半開区間(Range<String.Index>)か閉区間(ClosedRange<String.Index>)、置換するデータは文字(Character 型)の Collection か文字列(String 型)を指定することになります。

let range = welcome.index(welcome.endIndex, offsetBy: -9)..<welcome.endIndex

welcome.replaceRange(range, with: "icopter")    // error: 'replaceRange(_:with:)' has been renamed to 'replaceSubrange'

welcome.replaceSubrange(range, with: "icopter")    // helicopter



●5.5 辞書


・辞書へのアクセス

辞書のキーはプロトコル Hashable に適合した型でなければならないとして参照先を示していますが、「集合とプロトコル」は 10.4 ではなく 11.4(P.255)です。



Developers.IO/[Swift 3.1] 全ての数値型に失敗可能変換イニシャライザーが追加されました

Developers.IO/[iOS 11] Swift 4は前バージョンから何が変わったか比較した

Qiita/Swift4で何がかわりそうなのか

詳解Swift 第3版(Amazon)
 

文字列と文字(Swift 4)

2017. 11. 07
この記事は「The Swift Programming Language (Swift 4)/Strings and Characters」の翻訳ですが、素人翻訳ゆえ内容は保証しかねますのでご留意ください。



文字列とは "hello, world" や "albatross" など一連の文字のことです。
Swift での文字列は String 型で表されます。
String の内容には Character 値のコレクションを含め、様々な方法でアクセスすることができます。

Swift のString と Character 型は、コード内のテキストを処理するための高速で Unicode に準拠した方法を提供します。
文字列の生成や操作の構文は軽量で読み易く、文字列のリテラル構文は C と似ています。
文字列の連結は +  演算子で2つの文字列を結合するのと同じくらい容易であり、そして文字列の可変性は Swift の他の値と同様に定数または変数を選択することによって管理されます。
また文字列埋め込みと呼ばれる方法で、文字列を使用してより長い文字列に定数や変数、リテラル、そして式を挿入することができます。
これにより表示や保存、出力用のカスタム文字列値を容易に作成できます。

構文の単純さにも関わらず、Swift の String 型は高速で、モダンな文字列の実装です。
全ての文字列はエンコードに依存しない Unicode 文字で構成され、様々な Unicode 表現でそれらの文字にアクセスするためのサポートを提供します。



Swift の String 型は Foundation の NSString クラスと対応付けられています。
また Foundation は NSString で定義されているメソッドが受けられるよう、String を拡張しています。
これは Foundation をインポートするとキャスト無しに String へ NSString メソッドがアクセスできることを意味します。

Foundation や Cocoa でString を使用する方法についての詳細は Using Swift with Cocoa and Objective-C (Swift 4)Working with Cocoa Data Types を参照してください。



●文字列リテラル

文字列リテラルとしてコード内に定義済みの String 値を含めることができます。
文字列リテラルは二重引用符 ( " ) で囲んだ一連の文字です。

定数または変数の初期値として文字列リテラルを使用します。

let someString = "Some string literal value"

文字列リテラル値で初期化されるため、Swift は someString 定数を String 型と推論します。


複数行の文字列リテラル

複数の行にまたがる文字列が必要な場合、一連の文字列を3つの二重引用符で囲む複数行文字列リテラルを使用します。

let quotation = """
The White Rabbit put on his spectacles.  "Where shall I begin,
please your Majesty?" he asked.

"Begin at the beginning," the King said gravely, "and go on
till you come to the end; then stop."
"""

複数行文字列リテラルでは、その前後の引用符間にある全ての行が含まれます。
文字列は開始引用符 ( """ ) の後の最初の行から始まり、終了引用符の前の行で終わりますので、文字列の開始または終了時の改行はされないことを意味します。

let singleLineString = "These are the same."
let multilineString = """
These are the same.
"""

ソースコードで複数行文字列リテラル内に改行が含まれている場合、その改行も文字列の値として表現されます。
改行を使ってソースコードを読み易くしたいが、文字列の値の一部として改行したくない場合は、行末にバックスラッシュ ( \ ) を記述します。

let softWrappedQuotation = """
The White Rabbit put on his spectacles.  "Where shall I begin, \
please your Majesty?" he asked.

"Begin at the beginning," the King said gravely, "and go on \
till you come to the end; then stop."
"""

改行文字で開始または終了する複数行文字列リテラルを作成するには、最初または最後の行として空白行を記述します。
例えば、

let lineBreaks = """

This string starts with a line break.
It also ends with a line break.

"""

複数行の文字列は周囲のコードに合わせてインデントすることができます。
終了引用符 ( """ ) の前の空白は、全ての他の行の前にある空白を無視するよう Swift に伝えます。
ただし終了引用符の前にあるものに加えて行頭に空白を記述した場合、その空白は含まれます。

multilineStringWhitespace_2x.png

上記の例では、複数行文字列リテラル全体がインデントされていても、文字列の最初と最後の行は空白で始まりません。
中間の行は終了引用符よりインデントが多いので、追加されている4つの空白インデントで始まります。


文字列リテラル内の特殊文字

文字列リテラルは以下の特殊文字を含めることができます。

  • エスケープ特殊文字:\0(ヌル文字)、\\(バックスラッシュ)、\t(水平タブ)、\n(ラインフィード)、\r(キャリッジリターン)、\"(二重引用符)、\'(一重引用符)

  • 任意の Unicode スカラーは \u{n} と記述し、n は1〜8桁の16進数で、有効な Unicode のコードポイントに等しい値です。(Unicode については下記の「Unicode」で説明しています)

以下のコードで、これらの特殊文字の例を示します。
wiseWords 定数では2つのエスケープされた二重引用符が含まれています。
dollarSign、blackHeart、そして sparklingHeart 定数では Unicode スカラー形式を示しています。

let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imagination is more important than knowledge" - Einstein
let dollarSign = "\u{24}"                 // $,  Unicode scalar U+0024
let blackHeart = "\u{2665}"            // ♥,  Unicode scalar U+2665
let sparklingHeart = "\u{1F496}"    // 💖, Unicode scalar U+1F496

複数行文字列リテラルは1つではなく3つの二重引用符を使用するため、エスケープすることなしに複数行文字列リテラル内に二重引用符 ( " ) を含めることはできません。
複数行文字列にテキスト """ を含めるには、少なくとも1つの引用符をエスケープします。
例えば、

let threeDoubleQuotes = """
Escaping the first quote \"""
Escaping all three quotes \"\"\"
"""



●空の文字列を初期化する

長い文字列を構築するための開始点として空の String 値を作成するには、変数に空の文字列リテラルを割り当てるか、イニシャライザ構文で新たな String インスタンスを初期化します。

var emptyString = ""                          // empty string literal
var anotherEmptyString = String()    // initializer syntax
// these two strings are both empty, and are equivalent to each other

Bool 値の isEmpty プロパティをチェックすることで、String 値が空かどうかを調べることができます。

if emptyString.isEmpty {
    print("Nothing to see here")
}
// Prints "Nothing to see here"



●文字列の可変性

String は変数(この場合は変更可能)または定数(この場合は変更不可)に割り当てることによって、変更(または修正)することができるかどうかを指定します。

var variableString = "Horse"
variableString += " and carriage"
// variableString is now "Horse and carriage"

let constantString = "Highlander"
constantString += " and another Highlander"
// this reports a compile-time error - a constant string cannot be modified



Objective-C や Cocoa における文字列の可変性はこれとは異なり、文字列を変更できるかどうかを示す2つのクラス(NSString と NSMutableString)のどちらかを選択します。



●String は値型

Swift の String 型は値型です。
新しい String 値を作成すると、その String 値は関数またはメソッドに渡される時、または定数や変数に割り当てられた時にコピーされます。
いずれの場合も既存の String 値の新しいコピーが作成され、元のバージョンではなく新しいコピーが渡されるまたは割り当てられます。
値型については Structures and Enumerations Are Value Types で説明しています。

Swift における String のデフォルトのコピー動作は、関数またはメソッドに String 値を渡す時に、どこから来たのかに関わらず、要求された String 値が正確なものであることを保証します。
渡された文字列は自分で変更しない限り変更されないと確信することができます。

Swift のコンパイラは裏側で文字列の使用を最適化して、絶対に必要な時に実際のコピーが行われるようにしています。
これは文字列を値型として処理する時、常に優れたパフォーマンスが得られることを意味します。



●Character の処理

for-in ループで文字列を反復処理することで、String の個々の Character 値にアクセスすることができます。

for character in "Dog!🐶" {
    print(character)
}
// D
// o
// g
// !
// 🐶

for-in ループは For-In Loops で説明しています。

または Character 型のアノテーションを提供することで、単一文字の文字列リテラルからスタンドアローンの Character 定数あるいは変数を作成することができます。

let exclamationMark: Character = "!"

String 値はイニシャライザへの引数として Character 値の配列を渡すことによって作成することができます。

let catCharacters: [Character] = ["C", "a", "t", "!", "🐱"]
let catString = String(catCharacters)
print(catString)
// Prints "Cat!🐱"



●文字列と文字の連結

String 値は加算演算子 ( + ) で加算(または連結)して新しい String 値を作成することができます。

let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
// welcome now equals "hello there"

また加算代入演算子 (+= ) で既存の String 変数に String 値を追加することもできます。

var instruction = "look over"
instruction += string2
// instruction now equals "look over there"

String 型の append() メソッドで String 変数に Character 値を追加することができます。

let exclamationMark: Character = "!"
welcome.append(exclamationMark)
// welcome now equals "hello there!"



Character 値は単一文字しか含まれないので、既存の Character 変数に String や Character を追加することはできません。

複数行文字列リテラルを使用して長い文字列の行を構築している場合、最後の行を含め、全ての文字列の行を改行で終わらせたいことがあります。
例えば、

let badStart = """
one
two
"""
let end = """
three
"""
print(badStart + end)
// Prints two lines:
// one
// twothree


let goodStart = """
one
two

"""
print(goodStart + end)
// Prints three lines:
// one
// two
// three

上記のコードでは、badStart と end を連結すると2行の文字列が生成されますが、これは目的の結果ではありません。
badStart の最後の行では末尾が改行されず、その行が end の最初の行と結合されているからです。
対照的に goodStart の末尾は双方とも改行され、end と組み合わせると想定通り3行になります。



●文字列の埋め込み

文字列の埋め込みは、文字列リテラルの中に値を含めることによって、定数、変数、リテラル、そして式を組み合わせて新しい String 値を作成する方法です。
文字列の埋め込みは、単一行および複数行の文字列リテラル双方で使用することができます。
文字列リテラルに挿入する各項目は、バックスラッシュ ( \ ) を接頭辞とした丸括弧で囲みます。

let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message is "3 times 2.5 is 7.5"

上記の例では、multiplier の値は \(multiplier) として文字列リテラルに挿入されます。
このプレースホルダは、実際に文字列を作成するために文字列の埋め込みが評価された時、multiplier の実際の値が置換されます。

multiplier の値は、文字列後半の大きな式の一部です。
この式は Double(multiplier) * 2.5 の値を計算し、文字列に結果 (7.5) を挿入します。
この場合、式が文字列リテラルの中に含まれている時に \(Double(multiplier) * 2.5) として記述されます。



埋め込みされた文字列内の丸括弧内に記述された式には、エスケープされていないバックスラッシュ ( \ )、キャリッジリターン、またはラインフィードを含めることはできません。
ただし他の文字列リテラルを含めることはできます。



●Unicode

Unicode は異なる書き込みシステムでテキストをエンコーディング、表現、および処理するための国際標準です。
これにより標準化された形式であらゆる言語のほぼ全ての文字を表現し、テキストファイルやウェブページなどの外部ソース間でそれら文字を読み書きすることができます。
Swift の String と Character 型は、この節で説明するように完全に Unicode に準拠しています。


Unicode スカラ

裏側では、Swift のネイティブな String 型は Unicode スカラ値から構築されます。
Unicode スカラは、LATIN SMALL LETTER A ("a") の U+0061 や FRONT-FACING BABY CHICK ("🐥") の U+1F425 など、文字または修飾子の固有な21ビットの数値です。



Unicode スカラは U+0000 〜 U+D7FF または U+E000 〜 U+10FFFF の範囲に含まれるコードポイントです。
Unicode スカラは U+D800 〜 U+DFFF の範囲に含まれるコードポイントであるサロゲートペアのコードポイントは含まれません。

いくつかのスカラは将来の割り当て用に予約されているものもあり、21ビット Unicode スカラの全てに文字が割り当てられているわけではないことに注意してください。


拡張書記素クラスタ

Swift の Character 型の全てのインスタンスは、単一の拡張書記素クラスタで表されます。
拡張書記素クラスタは、(組み合わせの場合)人間が読める単一の文字を生成する1つ以上の Unicode スカラのシーケンスです。

例を示します。
文字 é は単一の Unicode スカラ é(LATIN SMALL LETTER E WITH ACUTE、または U+00E9)として表すことができます。
ただし同じ文字を標準文字 e(LATIN SMALL LETTER E、または U+0065)と COMBINING ACUTE ACCENT スカラ(U+0301)とのスカラのペアとして表すこともできます。
COMBINING ACUTE ACCENT スカラはその前にあるスカラへのグラフィックに適用され、Unicode 対応テキストレンダリングシステムによってレンダリングされた時に e を é に変換します。

どちらの場合でも、文字 é は拡張書記素クラスタを表す単一の Swift の Character 値として表されます。
前者ではクラスタに単一のスカラが含まれており、後者は2つのスカラのクラスタです。

let eAcute: Character = "\u{E9}"                               // é
let combinedEAcute: Character = "\u{65}\u{301}"    // e followed by ́
// eAcute is é, combinedEAcute is é

拡張書記素クラスタは多くの複雑なスクリプト文字を単一の Character 値として表現する柔軟な方法です。
例えば韓国語のアルファベットのハングル音節は、予め合成されたまたは分解されたシーケンスのいずれかとして表すことができます。
これらの表現は双方とも、Swift では単一の Character 値として見なされます。

let precomposed: Character = "\u{D55C}"                            // 한
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}"    // ᄒ, ᅡ, ᆫ
// precomposed is 한, decomposed is 한

拡張書記素クラスタでは単一の Character 値の一部として他の Unicode スカラを囲むために囲み記号のスカラ(COMBINING ENCLOSING CIRCLE、または U+20DD など)を使用することができます。

let enclosedEAcute: Character = "\u{E9}\u{20DD}"
// enclosedEAcute is é⃝

REGIONAL INDICATOR SYMBOL LETTER U(U+1F1FA)と REGIONAL INDICATOR SYMBOL LETTER S(U+1F1F8)の組み合わせのように、地域標識記号用の Unicode スカラは単一の Character 値を作成するために2つ1組で組み合わせることができます。

let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUS is 🇺🇸



●文字を数える

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

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

Character 値に Swift の拡張書記素クラスタを使用した場合、文字列の連結や変更が文字列の文字数に必ずしも影響するとは限りません。
例えば4文字の単語 cafe で新しい文字列を初期化し、文字列の末尾に COMBINING ACUTE ACCENT(U+0301)を追加すると、文字列の結果は4文字で、4文字目の文字は e ではなく é となります。

var word = "cafe"
print("the number of characters in \(word) is \(word.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.count)")
// Prints "the number of characters in café is 4"



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

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



●文字列へのアクセスと変更

文字列へのアクセスや変更を行うには、メソッドやプロパティ、または添字構文を使用します。


文字列インデックス

各 String 値は、文字列内の各 Character の位置に対応する関連付けられた index 型の String.Index があります。

上述のように、異なる文字は格納するために必要なメモリ量が異なるため、特定の位置にある Character を特定するには、その文字列の先頭または末尾から各 Unicode スカラを反復処理する必要があります。
したがって Swift の文字列は整数値で索引付けすることはできません。

String の最初の Character の位置にアクセスするには startIndex プロパティを使用します。
endIndex プロパティは String の最後の文字の後の位置です。
したがって endIndex プロパティは文字列の添字として有効な引数ではありません。
String が空の場合、startIndex と endIndex は等しくなります。

指定したインデックスの前後のインデックスにアクセスするには、String の index(before:) と index(after:) メソッドを使用します。
指定したインデックスから遠く離れたインデックスにアクセスするには、これらのメソッドのどちらかを複数回呼び出すのではなく、代わりに index(_:offsetBy:) メソッドを使用することができます。
特定の String インデックスにある Character にアクセスするには、添字構文を使用することができます。

let greeting = "Guten Tag!"
greeting[greeting.startIndex]
// G
greeting[greeting.index(before: greeting.endIndex)]
// !
greeting[greeting.index(after: greeting.startIndex)]
// u
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index]
// a

文字列の範囲外のインデックスにある Character にアクセスしようとすると実行時エラーが発生します。

greeting[greeting.endIndex]    // Error
greeting.index(after: greeting.endIndex)    // Error

文字列内の個々の文字の全てのインデックスへのアクセスには indices プロパティを使用します。

for index in greeting.indices {
    print("\(greeting[index]) ", terminator: "")
}
// Prints "G u t e n   T a g ! "



Collection プロトコルに準拠する全ての型において、startIndex と endIndex プロパティ、そして index(before:)、index(after:)、index(_:offsetBy:) メソッドを使用することができます。
これにはここで示している String だけでなく、コレクション型の Array、Dictionary、および Set が含まれます。


挿入と削除

文字列の指定されたインデックスに単一の文字を挿入するには insert(_:at:) メソッドを、指定されたインデックスに別の文字列の内容を挿入するには insert(contentsOf:at:) メソッドを使用します。

var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome now equals "hello!"

welcome.insert(contentsOf: " there", at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there!"

文字列の指定されたインデックスから単一の文字を削除するには remove(at:) メソッドを、指定された範囲の部分文字列を削除するには removeSubrange(_:) メソッドを使用します。

welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there"

let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)
// welcome now equals "hello"



RangeReplaceableCollection プロトコルに準拠する全ての型において、insert(_:at:)、insert(contentsOf:at:)、remove(at:)、そして removeSubrange(_:) メソッドを使用することができます。
これにはここで示している String だけでなく、コレクション型の Array、Dictionary、および Set が含まれます。



●部分文字列

添字や prefix(_:) のようなメソッドを使用して文字列から部分文字列を取得した場合、その結果は別の文字列ではなく Substring のインスタンスとなります。
Swift の部分文字列には文字列と同じメソッドのほとんどがあり、文字列と同じ方法で部分文字列を処理することができることを意味します。
ただし文字列と違い、部分文字列は文字列に対してアクションを実行している間の短時間のみ使用します。
長時間結果を格納する準備ができたら、部分文字列は String のインスタンスに変換します。
例えば、

let greeting = "Hello, world!"
let index = greeting.index(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index]
// beginning is "Hello"

// Convert the result to a String for long-term storage.
let newString = String(beginning)

文字列と同様に、各部分文字列は部分文字列を構成する文字が格納されるメモリ領域を持ちます。
文字列と部分文字列の違いはパフォーマンスの最適化として、部分文字列は元の文字列の格納に使用しているメモリの一部、または別の部分文字列の格納に使用しているメモリの一部を再利用できることです。
(文字列も同様の最適化を行いますが、2つの文字列がメモリを共有する場合で、それらは同じ物です。)
このパフォーマンスの最適化は、文字列または部分文字列のどちらかを変更するまで、メモリをコピーするための実行コストを支払う必要がないことを意味します。
前述のように、部分文字列は元の文字列の記憶域を再利用し、部分文字列が使用されている間は元の文字列全体をメモリに保持する必要があるため、長期間の格納には適していません。

上記の例では greeting は文字列で、文字列を構成する文字が格納されているメモリ領域を持っていることを意味します。
biginning は greeting の部分文字列であるため、greeting が使用するメモリが再利用されます。
対照的に、部分文字列から作成された文字列である newString は独自の記憶域を持ちます。
これらの関係を下図に示します。

stringSubstring_2x.png  



String と Substring は双方とも StringProtocol プロトコルに準拠しており、これは多くの場合において文字列操作関数が StringProtocol 値を受け入れることが便利であることを意味します。
このような関数は String または Substring のいずれかの値を呼び出すことができます。



●文字列の比較

Swift には、文字列と文字の同値性、接頭辞の同値性、接尾辞の同値性と、テキスト値を比較する3つの方法があります。


文字列と文字の同値性

文字列と文字の同値性は、Comparison Operators で説明されているように等号演算子 ( == ) と不等号演算子 ( != ) で調べることができます。

let quotation = "We're a lot alike, you and I."
let sameQuotation = "We're a lot alike, you and I."
if quotation == sameQuotation {
    print("These two strings are considered equal")
}
// Prints "These two strings are considered equal"

2つの String 値(または2つの Character 値)は、拡張書記素クラスタが正準等価である場合に等しいとみなされます。
拡張書記素クラスタは、たとえ異なる内情の Unicode スカラで構成されていたとしても、言語上で同じ意味と外観を持つ場合は正準等価です。
(編注:正準等価については「Wikipedia/Unicodeの等価性」を参照)

例えば LATIN SMALL LETTER E WITH ACUTE(U+00E9)は、LATIN SMALL LETTER E(U+0065)と COMBINING ACUTE ACCENT(U+0301)の組み合わせと正準等価です。
これらの拡張書記素クラスタは双方とも文字 é を表す有効な方法であり、正準等価と見なされます。

// "Voulez-vous un café?" using LATIN SMALL LETTER E WITH ACUTE
let eAcuteQuestion = "Voulez-vous un caf\u{E9}?"

// "Voulez-vous un café?" using LATIN SMALL LETTER E and COMBINING ACUTE ACCENT
let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?"

if eAcuteQuestion == combinedEAcuteQuestion {
    print("These two strings are considered equal")
}
// Prints "These two strings are considered equal"

逆に、英語で使用されている LATIN CAPITAL LETTER A(U+0041、または "A")は、ロシア語で使用されている CYRILLIC CAPITAL LETTER A(U+0410、または "А")と同等ではありません。
文字は視覚的に似ていますが、言語上の意味は同じではありません。

let latinCapitalLetterA: Character = "\u{41}"

let cyrillicCapitalLetterA: Character = "\u{0410}"

if latinCapitalLetterA != cyrillicCapitalLetterA {
    print("These two characters are not equivalent.")
}
// Prints "These two characters are not equivalent."



Swift での文字列や文字の比較はロケールに依存しません。


接頭辞と接尾辞の同値性

文字列が特定の文字列接頭辞または接尾辞を持っているかどうかを調べるには、文字列の hasPrefix(_:) および hasSuffix(_:) メソッドを呼び出しますが、双方とも String 型の単一引数を取りブールチを返します。

以下の例では、シェイクスピアのロミオとジュリエットの最初の2つの"幕"から"場"の位置を表す文字列の配列を考えます。

let romeoAndJuliet = [
    "Act 1 Scene 1: Verona, A public place",
    "Act 1 Scene 2: Capulet's mansion",
    "Act 1 Scene 3: A room in Capulet's mansion",
    "Act 1 Scene 4: A street outside Capulet's mansion",
    "Act 1 Scene 5: The Great Hall in Capulet's mansion",
    "Act 2 Scene 1: Outside Capulet's mansion",
    "Act 2 Scene 2: Capulet's orchard",
    "Act 2 Scene 3: Outside Friar Lawrence's cell",
    "Act 2 Scene 4: A street in Verona",
    "Act 2 Scene 5: Capulet's mansion",
    "Act 2 Scene 6: Friar Lawrence's cell"
]

配列 romeoAndJuliet で hasPrefix(_:) メソッドを使用することで、劇の第一幕の場の数を数えることができます。

var act1SceneCount = 0
for scene in romeoAndJuliet {
    if scene.hasPrefix("Act 1 ") {
        act1SceneCount += 1
    }
}
print("There are \(act1SceneCount) scenes in Act 1")
// Prints "There are 5 scenes in Act 1"

同様に hasSuffix(_:) メソッドを使用することで、Capulet’s mansion や Friar Lawrence’s cell の中や周辺で行われる場の数を数えることができます。

var mansionCount = 0
var cellCount = 0
for scene in romeoAndJuliet {
    if scene.hasSuffix("Capulet's mansion") {
        mansionCount += 1
    } else if scene.hasSuffix("Friar Lawrence's cell") {
        cellCount += 1
    }
}
print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
// Prints "6 mansion scenes; 2 cell scenes"



String and Character Equality で説明されているように、hasPrefix(_:) と hasSuffix(_:) メソッドは各文字列内の拡張書記素クラスタ間で文字単位での正準等価比較を実行します。



●文字列の Unicode 表現

Unicode 文字列がテキストファイルまたはその他ストレージに書き込まれている時、その文字列の Unicode スカラはいくつかの Unicode 定義のエンコード形式の一つでエンコードされます。
各形式は符号単位として知られる小さな塊で文字列をエンコードします。
これには UTF-8 エンコード形式(8ビット符号単位で文字列をエンコード)、UTF-16 エンコード形式(16ビット符号単位で文字列をエンコード)、UTF-32 エンコード形式(32ビット符号単位で文字列をエンコード)が含まれています。

Swift は文字列の Unicode 表現にアクセスするために、いくつかの異なる方法を提供しています。
文字列を for-in 文で反復処理し、Unicode の拡張書記素クラスタとして個々の Character 値にアクセスすることができます。
この工程については Working with Characters を参照してください。

または3つの Unicode の対応表現の内の1つで String 値にアクセスすることができます。

  • UTF-8 符号単位のコレクション(文字列の utf8 プロパティでアクセス)

  • UTF-16 符号単位のコレクション(文字列の utf16 プロパティでアクセス)

  • 文字列の UTF-32 エンコード形式に相当する、21ビット Unicode スカラ値のコレクション(文字列の unicodeScalars プロパティでアクセス)

以下に、文字 D、o、g、‼(DOUBLE EXCLAMATION MARK、または Unicode スカラ U+203C)と、文字 🐶(DOG FACE、または Unicode スカラ U+1F436)で構成された文字列の異なる表現を各例で示します。

let dogString = "Dog‼🐶"


UTF-8 表現

utf8 プロパティを反復処理することで String の UTF-8 表現にアクセスすることができます。
このプロパティは、文字列の UTF-8 表現を各バイト毎に区切った、符号なし8ビット(UInt8)値のコレクションである String.UTF8View 型です。

UTF8_2x.png

for codeUnit in dogString.utf8 {
    print("\(codeUnit) ", terminator: "")
}
print("")
// Prints "68 111 103 226 128 188 240 159 144 182 "

上記の例では、最初の3つの10進数の codeUnit 値(68、111、103)が、UTF-8 表現と ASCII 表現が同じ、文字 D、o、そして g を表します。
次の3つの10進数の codeUnit 値(226、128、188)は、文字 DOUBLE EXCLAMATION MARK の3バイト UTF-8 表現です。
最後の4つの codeUnit 値(240、159、144、182)は、文字 DOG FACE の4バイト UTF-8 表現です。


UTF-16 表現

utf16 プロパティを反復処理することで String の UTF-16 表現にアクセスすることができます。
このプロパティは、文字列の UTF-16 表現を各16ビット符号単位毎に区切った、符号なし16ビット(UInt16)値のコレクションである String.UTF16View 型です。

UTF16_2x.png

for codeUnit in dogString.utf16 {
    print("\(codeUnit) ", terminator: "")
}
print("")
// Prints "68 111 103 8252 55357 56374 "

ここでも最初の3つの codeUnit 値(68、111、103)は、UTF-16 符号単位が(これらの Unicode スカラは ASCII 文字を表すため)文字列の UTF-8 表現と同じ値となるので、文字 D、o、そして g を表します。

4番目の codeUnit 値(8252)は、文字 DOUBLE EXCLAMATION MARK の Unicode スカラ U+203C を表す16進数の値 203C に相当する10進数です。
この文字は UTF-16 では単一の符号単位として表すことができます。

5番目と6番目の codeUnit 値(55357 と 56374)は文字 DOG FACE の UTF-16 サロゲートペア表現です。
これらの値は、U+D83D(10進数値 55357)の上位サロゲート値と U+DC36(10進数値 56374)の下位サロゲート値です。


Unicode スカラ表現

unicodeScalars プロパティを反復処理することで String 値の Unicode スカラ表現にアクセスすることができます。
このプロパティは、UnicodeScalar 型の値のコレクションである UnicodeScalarView 型です。

各 UnicodeScalar には UInt32 値で表されるスカラの21ビット値を返す value プロパティがあります。

UnicodeScalar_2x.png

for scalar in dogString.unicodeScalars {
    print("\(scalar.value) ", terminator: "")
}
print("")
// Prints "68 111 103 8252 128054 "

最初の3つの UnicodeScalar 値(68、111、103)の value プロパティは、再度文字 D、o、そして g を表します。

4番目の codeUnit 値(8252)もまた、文字 DOUBLE EXCLAMATION MARK の Unicode スカラ U+203C を表す16進数の値 203C に相当する10進数です。

5番目と最後の UnicodeScalar の value プロパティである 128054 は、文字 DOG FACE の Unicode スカラ U+1F436 を表す16進数の値 1F436 に相当する10進数です。

value プロパティを照会する代わりに、文字列の埋め込みなど、各 UnicodeScalar 値を使用して新しい String 値を作成することもできます。

for scalar in dogString.unicodeScalars {
    print("\(scalar) ")
}
// D
// o
// g
// ‼
// 🐶



Wikipedia/エスケープ文字

Wikipedia/Unicode

Wikipedia/Regional Indicator Symbol

CyberLibrarian/Unicode 囲み英数字補助

Wikipedia/Unicodeの等価性

Wikipedia/ロミオとジュリエット

0 CommentsPosted in Swift

詳解Swift第3版(03)〜構造体

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



CHAPTER 03 構造体



●3.1 構造体の定義


・構造体の初期値

既定イニシャライザを使って構造体 SimpleDate のインスタンスを初期化する式が間違っています。

var d = Date()        // 旧版「詳解 Swift 改訂版」での構造体名が Date

var d = SimpleDate()


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

オーバーロードは 2.2 ではなく 2.3(P.48)です。



●3.2 メソッド


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

List3-9 の実行例で変数 tic が構造体 Time のインスタンスになっていますが、正しくは構造体 Clock のインスタンスです。
また標準出力でインスタンス tm のメソッド toString() を呼び出していますが、インスタンス tic の誤りです。
(この誤りは旧版「詳解 Swift 改訂版」も同じで、「詳解 Swift」時のインスタンス名が混在しています。)

var tic = Clock(hour: 19, min: 40)    // 旧版「詳解 Swift」での構造体名が Time
tic.advance(min: 19)
tic.inc()
print(tic.toString())    // 旧版「詳解 Swift」でのインスタンス名が tm


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

List3-11 において、イニシャライザ内の年月日を表すプロパティを設定している行にミスがあります。

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

year = y; month = m; day = d



●3.3 プロパティ


・計算型プロパティに対する特殊な設定

配列は 5.2 ではなく 5.3(P.110)です。


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

List3-17 において、これまで iOS プラットフォームのプレイグラウンドで試されている場合は macOS プラットフォームに変更して試す必要があります。

変更方法については「詳解Swift(3)〜構造体/CHAPTER 03 構造体/3.3 プロパティ/グローバルなスコープを持つ計算型プロパティの定義」を参照してください。
(注:現在は Platform 名が OS X から macOS になっています)

また macOS 10.13(もしくは Xcode 9.0?)から、NSScreen クラスの main がメソッドではなくタイププロパティに変更されていますので、修正する必要があります。

let size = NSScreen.main()!.frame.size    // error: cannot call value of non-function type 'NSScreen?'

let size = NSScreen.main!.frame.size



詳解Swift 第3版(Amazon)
 

詳解Swift第3版(02)〜関数

2017. 10. 10
この記事は詳解Swift 第3版(初版第1刷)を元に、Xcode 9.0(Swift 4.0)下における差異を記述しています。



CHAPTER 02 関数



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


・返り値を使わない場合を許す指定

(本文 P.39 で述べられている通り)プレイグラウンドでは @discardableResult を指定せずに返り値を使わないような呼び出しをしても警告を発しないので注意が必要です。


・関数内の関数

ひと月のカレンダーを印字するプログラムは List1-7(P.47)ではなく List1-6(P.23)です。



●2.3 オーバーロード


・オーバーロードとは

2つの値を交換する mySwap 関数は List2-2 ではなく List2-3 (P.44)です。



Wikipedia/フィボナッチ数

詳解Swift 第3版(Amazon)
 






QuietControl 30 wireless headphones
Calendar
10 | 2017/11 | 12
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

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