woshidan's blog

そんなことよりコードにダイブ。

はてな教科書の「プログラミング言語 Swift」を読んでます 1

https://github.com/hatena/Hatena-Textbook/blob/master/swift-programming-language.md

のんびり読んでいきます。

序文を読んで

LLVM と Clang はその後の Apple プラットフォームにおける標準的なコンパイラの地位を占め、また OSSコンパイラとして多くのプロジェクトに採用されている。

OSSってコンパイラも選択するんですね。

web系のソフトウェアエンジニアなので、OSSといっても、DLするコードだけGitHubとかホスティングサーバにあって、コンパイルはDLしてから自分のIDEであったりサーバであったり、という雰囲気だったので、そういえば、OSSはライブラリだけではないのだったという感じでした。

Playground に REPL など

REPLって聞いたことがあるのだけれどなんだっけ、と思ったら対話型評価環境のことでした...。

Rubyでいうと、irb。PlayGroundにSwiftのREPLがあるそうで、iOSのプロジェクト*1のルートディレクトリあたりで、swiftと入力すると

$ swift

Welcome to Apple Swift version 2.2 (swiftlang-703.0.18.8 clang-703.0.30). Type :help for assistance.
  1>  

のような表示がでたあと対話モードになりました。

これに気づいたので残りの章は入力しながら試すことにしました。

参考:

http://qiita.com/dll7/items/206d5bf0cb72942b3681

また、まだアップデートが活発だそうですが、細かいことはおいおい覚えます。

Swift の言語仕様を読んで

Constants and variables

// letはval(Kotlin), final修飾子のついた定数(Java)
  6> let name = "Steve"
name: String = "Steve"
  7> name = "John"
repl.swift:7:6: error: cannot assign to value: 'name' is a 'let' constant
name = "John"
~~~~ ^
repl.swift:1:1: note: change 'let' to 'var' to make it mutable
let name = "Steve"
^~~
var
// varは再代入可能な定数
7> var age = 56
age: Int = 56
8> age = 20
9> print(age)
20
// 型の定義, 型推論周りはKotlinと同じ
10> var age: Int = 56

Literal

Swiftのリテラル

  • 数値
  • 真偽値
  • 文字列
  • nil

などがある。

数値

記述方法が多彩で、色のコードや大きな計算用の数値が読みやすくできそうですね。

let decimal = 21
let binary = 0b10101 // 二進数
let octal = 0o25 // 八進数
let hexadecimal = 0x15 // 十六進数
let decimal = 1.618
let exponent = 161.8e-2 // 常用対数

Optional

型を宣言するときに、その型かnilが入っていいよ、と宣言するのに使う型。

nilは正確にはOptional.NONEという値になるそうです。

OptionalはKotlinでいうNullableに近いです。

var phoneNumber: Optional<String> = nil
var phoneNumber: String? = nil

nilが入っていたら実行する、系のコードを一通り書いてみます。

 12> var testString: String? = "test" 
testString: String? = "test"
 13> if testString != nil {
 14.     print("testString is " + testString) 
 15. } 
repl.swift:14:30: error: value of optional type 'String?' not unwrapped; did you mean to use '!' or '?'?
    print("testString is " + testString) // ()で包む必要がありました...
                             ^
                                       !

 13> var testString: String? = "test" 
testString: String? = "test"
 14> if testString != nil { 
 15.     print("testString is \(testString)")
 16. } 
testString is Optional("test")
 14> if testString != nil { 
 15.     print("testString is \(testString)")
 16. } 
testString is Optional("test")
 17> testString = nil
 18> if testString != nil {  
 19.     print("testString is \(testString)") 
 20. } 
 21>  // 実行されない
 21> if testString != nil {   
 22.     print("testString is \(testString!)") 
 23. } 
 // 実行されない
 24> print("testString is \(testString!)") 
fatal error: unexpectedly found nil while unwrapping an Optional value
Execution interrupted. Enter code to recover and continue.
Enter LLDB commands to investigate (type :help for assistance.)

Optional!Nullable!!と同じように、Optionalの値がnilだったら例外を発生します。

 25> testString = "exist"
 26> if let assigned = testString { 
 27.     print("The testString is existing \(assigned)") 
 28. }
The testString is existing exist
// ブロックの外ではif文で代入した変数は利用できない
 32> print(assigned)
repl.swift:32:7: error: use of unresolved identifier 'assigned'
print(assigned)
      ^~~~~~~~
 32> if var assigned = testString { 
 33.     print("The testString is existing \(assigned)") 
 34.     assigned = "reassign" 
 35.     print("\(assigned)")
 36. } 
The testString is existing exist
reassign
// letだとブロックの中で再代入できない
 37> if let assigned = testString { 
 38.     print("The testString is existing \(assigned)") 
 39.     assigned = "reassign" 
 40.     print("\(assigned)") 
 41. } 
repl.swift:39:14: error: cannot assign to value: 'assigned' is a 'let' constant
    assigned = "reassign"
    ~~~~~~~~ ^

nilが代入された時点でぬるぽを発生させたい場合は、?の代わりに!を利用してImplicitlyUnwrappedOptional型で宣言する。

Tuple

 37> let steve = ("Steve Jobs", 56)
steve: (String, Int) = {
  0 = "Steve Jobs"
  1 = 56
}
// このへんの記法、rubyでメソッドの返り値が配列の場合の記法と似ていて(下記)
// まだ右辺がセットの値の組み合わせとして宣言されている分、
// こっちのほうがわかりやすいかも...? という気持ち
 38> let (name, age) = steve
name: String = "Steve Jobs"
age: Int = 56
 39> print(steve.0)
Steve Jobs
 40> print(name)
Steve Jobs
 41> print(age)
56

Rubyのメソッドの返り値が配列の場合の記法. Rails読んでるとたまに出会ったり...。

irb(main):001:0> def return_arr
irb(main):002:1>   [1, "a", nil]
irb(main):003:1> end
=> :return_arr
irb(main):004:0> num, str, null_var = return_arr
=> [1, "a", nil]
irb(main):005:0> num
=> 1
irb(main):006:0> str
=> "a"
irb(main):007:0> null_var
=> nil

Collection types

現在の Swift には Array, Dictionary, Set の3つのビルトインされたコレクション型がある。

それぞれ let で宣言されているとき、要素を変更することはできない。変更したい場合は var を使う。

 48> let fishes = ["Mackerel", "Saury", "Sardine"]
fishes: [String] = 3 values {
  [0] = "Mackerel"
  [1] = "Saury"
  [2] = "Sardine"
}
 49> fishes[0] = "Squale"
repl.swift:49:11: error: cannot assign through subscript: 'fishes' is a 'let' constant
fishes[0] = "Squale"
~~~~~~    ^
repl.swift:48:1: note: change 'let' to 'var' to make it mutable
let fishes = ["Mackerel", "Saury", "Sardine"]
^~~

リテラルでは上記の通り変更できないみたいですが、これ、リテラルでない場合はどうなのでしょう、とちらっと思ったり、思わなかったりしました。

DictonaryがRubyでいうHashみたいですね。

 49> let firstDictonary = [ 
 50.     "Attest": 6,
 51.     "Basic" : 5, 
 52.     "Core"  : 4 
 53.     ]
firstDictonary: [String : Int] = 3 key/value pairs {
  [0] = {
    key = "Basic"
    value = 5
  }
  [1] = {
    key = "Core"
    value = 4
  }
  [2] = {
    key = "Attest"
    value = 6
  }
}
 54> first
Available completions:
  (τ_0_0): τ_0_10?
  firstDictonary: [String : Int]
 54> firstDictonary["Basic"]
$R3: Int? = 5

演算子を読んで

インクリメント、デクリメントが削除される予定があること、三項演算子があること、nil 結合演算子を使って、

let someValue = optionalInt(optionalIntがnilでない場合採用) ?? 0(optionalIntがnilの場合採用)

というふうにかけること、範囲演算子の書き方(... や ..<)などが特徴的だな、と思いました。

Control flowを読んで

  • if文の条件文に渡す式は真偽値を返させる
  • switch/case派(when派ではない)
    • 上から順に基本的にマッチしたcaseの処理以外は行われない
    • 下の条件にもマッチさせたいときはfallthroughを使う
    • defaultが使えるが、全ての条件について記述する必要がある

switch文がとてもいい感じだなと思いました。

let theNumber: UInt = 42

switch theNumber {
case 0..<10:
    print("Single digit")
case 10..<100:
    print("Double digits")
case 100..<1000:
    print("Triple digits")
default:
    print("Very large")
}

caseに合致するかがパターンマッチで判断される、というのがRubyに近いなと思いました。

また、Optional型がnilかどうかでも判定ができるようです。

 55> let pair: (String?, String?) = ("Steve", "Bill")
pair: (String?, String?) = {
  0 = "Steve"
  1 = "Bill"
}
 56> switch pair { 
 57. case let (a?, b?): 
 58.     print("\(a) and \(b)") 
 59. case let (a?, _): 
 60.     print(a) 
 61. case let (_, b?): 
 62.     print(b) 
 63. case (_, _): 
 64.     print("No man") 
 65. }
Steve and Bill

明日に続きます。

*1:XCode7.3.1で作成