読者です 読者をやめる 読者になる 読者になる

【Swift】Appleの新言語「Swift」のリファレンスを読む(1) - The Basic

なんかApple製品だけで使える新しい言語が登場したらしいじゃないですか。まぁObjective-Cとか言う…いや、やめておきましょう。

リファレンスがiBooksだけでなくちゃんとウェブ版も用意されています。「apple reference swift」でぐぐったら出てきましたよ。

イントロダクションとしてはこっちの方がわかりやすいと思います。

APIがあったら叩きたくなるのと同様に、仕様があったら読みたくなるものです。と言うわけで流し読みしているんですが、かなりモダンって言うかこれ丸パクリじゃねーかな構文がいっぱい出てくるので、ここ4〜5年ぐらいのトレンドを広く浅くでもちゃんと追っかけていればコードを見るだけですんなり飲み込める形になっています。

個人的な理由でこの言語を使うことがこの先あるかどうかはわかりません(恐らくないです)が、万が一お仕事で使うことになっても困らないようLanguage Guideの内容をメモしていきます。当然ながら結構なボリュームがあるので何回かにわけることになりますが。

目次

注意

  • あくまでメモ書きなので細かい部分を端折りますし、色々間違ってるかもしれません。ちゃんとした内容は原文を読んでね。
  • コード例は基本的に原文からそのまま引用していますが、ちょっとした注釈をつけたり、統合したりしています。
  • SyntaxHighlighterが対応してないので微妙に読みにくいです。SyntaxHighlighterはこちらのものを使用させて頂いてます。
  • 他言語にそっくりな部分でも指摘しない。(自戒)

The Basics(基本構文)

Constants and Variables

変数宣言用のキーワードとしてvarletが用意されています。varが変数で、letが定数です。

// 定数
let maximumNumberOfLoginAttempts = 10

// 変数
var currentLoginAttempt = 0

同時に複数の変数を定義することも出来ます。

var x = 0.0, y = 0.0, z = 0.0

型推論がサポートされているので明示的に型を書く必要はありません。が、敢えて型を書く場合はこうなります。

var welcomeMessage: String
welcomeMessage = "Hello"

変数名はUnicodeなら基本的になんでもOKです。

ただし数学記号や矢印(原文ではarrowsとなっているけど、「->」のことかな?)、プライベートな(不正な)Unicodeは変数名に使えません。

let π = 3.14159
let ?好 = "?好世界"
// 絵文字らしいんだけどWindowsじゃ全然表示されない
let ???? = "dogcow"

varで宣言した変数は別の値(もしくは互換性を持つ型)に置き換えることが出来ます。

var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome is now "Bonjour!"

letで宣言した定数は値を変更しようとするとコンパイルエラーになります。

let languageName = "Swift"
languageName = "Swift++"
// this is a compile-time error - languageName cannot be changed

printlnやprintと言うメソッドではバックスラッシュ記法を使うことで簡単に変数をコンソール(要は標準出力)に表示できます。

(別にprintlnじゃなくても変数宣言etcでこの記法は使えるし、もっと言えば型がStringでなくIntやDoubleでも使えるし、括弧内の計算結果も出せるみたいだけど、この章では何の説明もない。)

println("The current value of friendlyWelcome is \(friendlyWelcome)")
// prints "The current value of friendlyWelcome is Bonjour!"

Comments

もう既に例として書いちゃったけど、C Familyなコメント記法です。

// こう言うのとか

/* こう言うのだね。 */

Semicolons

基本的にセミコロンレスですが、一行内に複数処理を書きたい場合は明示的にセミコロンを付ける必要があります。

// 何で例を絵文字にしたんだろう。

let cat = "??"; println(cat)
// prints "??"

Integers

Int8,16,32,64がサポートされています。unsignedなintegerが欲しい場合はUIntです。また、各型にmaxとminと言うプロパティが用意されています。

let minValue = UInt8.min // minValue is equal to 0, and is of type UInt8
let maxValue = UInt8.max // maxValue is equal to 255, and is of type UInt8

Floating-Point Numbers

Doubleが64-bit floating-point number、Floatが32-bit floating-point numberだそうです。(雑)

(「Double has a precision of at least 15 decimal digits, whereas the precision of Float can be as little as 6 decimal digits.」と注釈として書かれている。)

Type Safety and Type Inference

セクション名の通り、型安全と型推論に関してつらつらと書かれています。

このコードだけ読んでおけばまぁいいかなって感じですね。

let meaningOfLife = 42
// meaningOfLife is inferred to be of type Int

let pi = 3.14159
// pi is inferred to be of type Double

let anotherPi = 3 + 0.14159
// anotherPi is also inferred to be of type Double

Numeric Literals

原文読むだけでもいいと思いますが一応。

数値は10進数、2進数、8進数、16進数で書くことができます。以下はすべて「17」です。

let decimalInteger = 17
let binaryInteger = 0b10001 // 2進数はprefixとして0bをつける
let octalInteger = 0o21 // 8進数はprefixとして0oをつける
let hexadecimalInteger = 0x11 // 16進数はprefixとして0xをつける

指数もサポートされています。以下はすべて「12.1875」です。

let decimalDouble = 12.1875
let exponentDouble = 1.21875e1 // 1.21875 * 10^1
let hexadecimalDouble = 0xC.3p0 // (16進数) C.3 * 2^0

また、こんな記法も許されています。

let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1

Numeric Type Conversion

範囲が広い型に変換するにはインスタンスを作り直せと仰られています。

let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine

DoubleやFloatをIntに変換すると小数点以下はすべて切り捨てのようです。

let integerPi = Int(pi)
// integerPi equals 3, and is inferred to be of type Int

Type Aliases

typealiasと言うキーワードを用いることで型に別名を与えることができます。具体的にどこで書け、みたいな内容がないんですが、どこで書いてもいいんですかね?スコープとかどうなるんですかね?

typealias AudioSample = UInt16

var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0

Boolean

型名的には「Bool」だそうです。

let orangesAreOrange = true
let turnipsAreDelicious = false

if turnipsAreDelicious {
    println("Mmm, tasty turnips!")
} else {
    println("Eww, turnips are horrible.")
}
// prints "Eww, turnips are horrible."

Intの1はtrueじゃねぇからな、とのことです。何時代の話をしているのでしょう。

let i = 1

if i {
    // this example will not compile, and will report an error
}

if i == 1 {
    // this example will compile successfully
}

Tuples

タプルがサポートされています。素直に羨ましいです。

let http404Error = (404, "Not Found")
// http404Error is of type (Int, String), and equals (404, "Not Found")

let (statusCode, statusMessage) = http404Error
println("The status code is \(statusCode)")
// prints "The status code is 404"

println("The status message is \(statusMessage)")
// prints "The status message is Not Found"

// 無視したい部分はアンダースコアをつけろ、とのこと
let (justTheStatusCode, _) = http404Error
println("The status code is \(justTheStatusCode)")
// prints "The status code is 404"

いちいち別のタプルを作り直したくない場合は0〜nでとってこれます。

println("The status code is \(http404Error.0)")
// prints "The status code is 404"

println("The status message is \(http404Error.1)")
// prints "The status message is Not Found"

0〜nとかダサすぎて死んで欲しい?じゃあ最初から名前をつけてあげましょう。

let http200Status = (statusCode: 200, description: "OK")

println("The status code is \(http200Status.statusCode)")
// prints "The status code is 200"

println("The status message is \(http200Status.description)")
// prints "The status message is OK"

Optionals

ここで言うOptionalsとは、ScalaのOptionとか、C#のNullableと似たようなものだと考えればOKです。HaskellだとMaybeですね。

let possibleNumber = "123"
let convertedNumber = possibleNumber.toInt()
// convertedNumber is inferred to be of type "Int?", or "optional Int"

String#toInt()によってconvertedNumberは「Int?」と言う型になります。型名 + ?のoptionalな型は、そのままifステートメントに放り込むことが出来ます。

if convertedNumber {
    // valueを取得するときは末尾に"!"をつける
    println("\(possibleNumber) has an integer value of \(convertedNumber!)")
} else {
    println("\(possibleNumber) could not be converted to an integer")
}
// prints "123 has an integer value of 123"

コメントに書いた通りvalueを取得するには変数名の末尾に「!」をつけるんですが、valueを持ってないのに取得しようとするとruntime errorとなるそうです。

また、こんな構文が用意されています。

if let constantName = someOptional {
    statements
}

なのでこんなことが出来ます。

if let actualNumber = possibleNumber.toInt() {
    // この例だと"!"がないんだけど、toStringみたいなものが呼ばれているらしい
    println("\(possibleNumber) has an integer value of \(actualNumber)")
} else {
    println("\(possibleNumber) could not be converted to an integer")
}
// prints "123 has an integer value of 123"

if letをif varとすることは出来ません。そもそもする必要がないと思います。actualNumberのvalueを取得してアレコレする分には構わないでしょうが、actualNumberを書き換えて得する人は誰もいないでしょう。

また、optionalな型にのみnilを設定することが出来ます。

nilのケースがありえるかどうかをまず型で判断できる」と言うのは、賛否両論ありそうですが、悪くないんじゃないですかね。

var serverResponseCode: Int? = 404
// serverResponseCode contains an actual Int value of 404
serverResponseCode = nil
// serverResponseCode now contains no value

optionalな型に初期値を与えないと自動でnilがセットされるそうです。

var surveyAnswer: String?
// surveyAnswer is automatically set to nil

optionalな型の宣言方法はもう一つあります。型名の末尾に「!」をつける方法です。これは「Implicitly unwrapped optionals」として紹介されています。(良い訳が思いつかない…Implicitlyの意味は「暗黙的」です。)

let possibleString: String? = "An optional string."
println(possibleString!) // requires an exclamation mark to access its value
// prints "An optional string."

let assumedString: String! = "An implicitly unwrapped optional string."
println(assumedString) // no exclamation mark is needed to access its value
// prints "An implicitly unwrapped optional string."

「?」をつける宣言方法との違いは「valueを取得する際に末尾に『!』をつける必要がない」と言う点です。簡便化しようとしたんでしょうが、逆に複雑になってる気もしますね。例がprintlnなのでありがたみがわかり辛いだけかもしれません。

if assumedString {
    println(assumedString)
}
// prints "An implicitly unwrapped optional string."

if let definiteString = assumedString {
    println(definiteString)
}

注釈として「Implicitly unwrapped optionalsに対して後からnilをセットすべきではない(原文ではshould not beなので、コンパイル自体は通るのかな…?)」と書かれているので、イマイチ効果的な使い方が思いつきません。

Assertions

assertと言う組み込み関数(?)があります。与えられた条件(Condition)に対してfalseが返されると例外を投げてくれる便利なやつです。

let age = -3
assert(age >= 0, "A person's age cannot be less than zero")
// this causes the assertion to trigger, because age is not >= 0

が、「If your code triggers an assertion while running in a debug environment」と書かれていますし、セクションの最後にある注釈でも「これはあくまでもデバッグに使うものであって、リリース後にこれで引っかかるようなことがあってはならない」と書かれています。

普通に便利なんだからいいじゃん、と思うんですが、まだ例外関係のドキュメントは読んでないのでもっとクールな方法を用意しているのかもしれません。