Swiftを触ってみた

Swiftの基本を学習したメモ。 Objective-CCocoaの知識は全く無い。とりあえずObjective-Cより速いとか。LLVMでネイティブコードにコンパイル出来るらしい。

参考

ドキュメントは公式を読む。 とりあえずSwift Tourをザッと眺めてみる。

Web

https://developer.apple.com/swift/ https://developer.apple.com/library/prerelease/ios/referencelibrary/GettingStarted/LandingPage/index.html

書籍

https://itunes.apple.com/jp/book/swift-programming-language/id881256329?l=en&mt=11

ダウンロード

Xcode 6 Betaをダウンロード。

https://developer.apple.com/xcode/downloads/

REPL

コマンドラインから実行します。

$ /Applications/Xcode6-Beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift

.bsh_profileにpathを追加。

export PATH=/Applications/Xcode6-Beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/:$PATH

swiftコマンドでREPLが起動。swift -iで外部スクリプトを実行できるようになる。

swift -i main.swift

文法

まずはHello World

println("Hello, world")

Xcode 6」(現在ベータ版)の「Playgrounds」ではSwiftで書いたコードを即座に出力して表示できる。

コメント

appledocが使えたりするのだろうか。

// comment
/* comment */

文末

セミコロンは必要無いが、ワンライナーを書く際に使用できる。

変数と定数

varとletを使って宣言できる。letは再代入不可。

var myVariable = 42
myVariable = 50
let myConstant = 42

// ,(カンマ区切りで)一度に定義することもできる
var x = 0.0, y = 0.0, z = 0.0

型を明記する事もできる。型アノテーションというらしい。

let explicitDouble: Double = 70

暗黙的キャストは行われないので、明示的にキャストを実行する必要がある。

let label = "The width is "
let width = 94
let widthLabel = label + String(width)

また、変数名や文字列リテラルUnicodeの文字が大体使える(全部ではないらしい)。つまり日本語や絵文字が使える。

let π = 3.14159
let アプリの名前 = "SnapDish"

文字列テラル内で式展開を行なう事ができる。backslash()を用いる。

let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."

配列とディクショナリ

ブラケット([])を使用して、配列や辞書を作成し、括弧内のインデックスまたはキーを書き込むことで、その要素にアクセスする。

var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"
 
var occupations = [
    "Malcolm": "Captain",
    "Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"

空配列やディクショナリを作成する場合はイニシャライザが用意されている。ディクショナリにはジェネリクスが使用できる。

let emptyArray = String[]()
let emptyDictionary = Dictionary<String, Float>()

型推論が有効な場合は型の宣言を省略出来る。

shoppingList = []   // Went shopping and bought everything.
shoppingDictionary = [:]

タプル

タプルは複数の値を一つの混合値として扱う事ができる。 タプルの値には、異なる型の値を代入することが出来る。 関数から複数の値を一度に返したい時はタプルを使うのが流儀っぽい。

let http404Error = (404, "Not Found!")
// (Int, String)

index 番号を使うこともできる。

println("ステータスコードは、\(http404Error.0)")
println("ステータスメッセージは、\(http404Error.1)")

それぞれのエレメントに命名も出来る。

let http200Status = (statusCode: 200, description: "OK")
println("ステータスコードは、 \(http200Status.statusCode)")
println("ステータスメッセージは、 \(http200Status.description)")

制御文

if,switch,for-in,for,while,do-whileが利用できる。

let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
    if score > 50 {
        teamScore += 3
    } else {
        teamScore += 1
    }
}
teamScore
let interestingNumbers = [
    "Prime": [2, 3, 5, 7, 11, 13],
    "Fibonacci": [1, 1, 2, 3, 5, 8],
    "Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
        }
    }
}
largest

for-in句でRubyのRangeオブジェクトの様な事ができる。

var firstForLoop = 0
for i in 0..3 {
    firstForLoop += i
}
firstForLoop

.....が使える。...は上限(上記の場合は3)を含む。

Optional

変数の型アノテーションの末尾に?をつけると変数がOptionalとなる。Optionalとはnil(何もない)事を許容するような概念で、Swiftでは下記の様な特別な構文を使用する事ができる。

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
    greeting = "Hello, \(name)"
}

初見で分かりにくいが、optionalValueに値が入っている場合(Some)の場合はnameにoptionalValueの値が代入されブロック文が実行される。optionalValueがnilの場合はブロック文は実行されない。

Switch

全ては把握できていないがパターンマッチはかなり強力っぽい。breakは要らない!

let vegetable = "red pepper"
switch vegetable {
case "celery":
    let vegetableComment = "Add some raisins and make ants on a log."
case "cucumber", "watercress":
    let vegetableComment = "That would make a good tea sandwich."
case let x where x.hasSuffix("pepper"):
    let vegetableComment = "Is it a spicy \(x)?"
default:
    let vegetableComment = "Everything tastes good in soup."
}

関数

golangっぽくfuncという予約語が宣言されている。

unc greet(name: String, day: String) -> String {
    return "Hello \(name), today is \(day)."
}
greet("Bob", "Tuesday")

タプルを用いて複数の値をラップして返却できる。

func getGasPrices() -> (Double, Double, Double) {
    return (3.59, 3.69, 3.79)
}
getGasPrices()

可変長引数は...で実現する。

func sumOf(numbers: Int...) -> Int {
    var sum = 0
    for number in numbers {
        sum += number
    }
    return sum
}
sumOf()
sumOf(42, 597, 12)

関数は入れ子にすることができます。ネストされた関数は、外側の関数で宣言された変数にアクセスすることができます。

func returnFifteen() -> Int {
    var y = 10
    func add() {
        y += 5
    }
    add()
    return y
}
returnFifteen()

関数は第一級市民なので、戻り値として関数を返却できる。

func makeIncrementer() -> (Int -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
increment(7)

これは(Int -> Int)な関数をreturnする事を示している。若干分かりにくい気もする。

関数を関数の引数とする事ができる。

func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool {
    for item in list {
        if condition(item) {
            return true
        }
    }
    return false
}
func lessThanTen(number: Int) -> Bool {
    return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, lessThanTen)

関数は、実際にはクロージャの特殊なケース。中括弧({})で囲むコードによって無名クロージャを書くことができる。引数を分離し、inを使う事で引数とコードブロックからの戻り値を記述できる。

numbers.map({
    (number: Int) -> Int in
    let result = 3 * number
    return result
})

クロージャは更に簡潔に書く事ができる。 引数や戻り値が明らかな場合は省略する事ができる。一行のクロージャは暗黙的に文の戻り値を返却する。

numbers.map({ number in 3 * number })

更に簡潔に、変数名ではなく$numberという形で参照する事ができる。

sort([1, 5, 3, 12, 2]) { $0 > $1 }

オブジェクトとクラス

クラス宣言。

class Shape {
    var numberOfSides = 0
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

インスタンス化するシンタックスPythonっぽい。

var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

コンストラクタinitという予約語で宣言する。Javathisself。 全てのインスタンス変数は宣言またはinitで初期化する必要があるらしい。

class NamedShape {
    var numberOfSides: Int = 0
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

デストラクタはdeinitとして宣言できる。calされる事が保証されているのかはよく分からない。

継承とかsuperとかoverrideとか。 コンストラクタの所でさらっと出てきているけど、名前付き引数相当の事もできる。Obj-Cでいう所のラベルか。

class Square: NamedShape {
    var sideLength: Double
    
    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 4
    }
    
    func area() ->  Double {
        return sideLength * sideLength
    }
    
    override func simpleDescription() -> String {
        return "A square with sides of length \(sideLength)."
    }
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()

ゲッターセッター。 セッターで新しく代入された値はnewValueという変数名で暗黙的に参照できる。

var perimeter: Double {
   get {
       return 3.0 * sideLength
   }
   set {
       sideLength = newValue / 3.0
   }
}

プロパティの変更の前後のタイミングに処理を差し込みたい場合はwillSetdidSetというものがある。

class TriangleAndSquare {
    var triangle: EquilateralTriangle {
    willSet {
        square.sideLength = newValue.sideLength
    }
    }
    var square: Square {
    willSet {
        triangle.sideLength = newValue.sideLength
    }
    }
    init(size: Double, name: String) {
        square = Square(sideLength: size, name: name)
        triangle = EquilateralTriangle(sideLength: size, name: name)
    }
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
triangleAndSquare.square.sideLength
triangleAndSquare.triangle.sideLength
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
triangleAndSquare.triangle.sideLength

CoffeeScriptっぽく値がnilの場合はメソッドチェーンを切る事ができる。

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength

EnumとStruct

Enum(列挙型)

Javaっぽい感じで扱える。色々できそうな事は分かったが全部は追えなかった。

enum ServerResponse {
    case Result(String, String)
    case Error(String)
}
 
let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")
 
switch success {
case let .Result(sunrise, sunset):
    let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
    let serverResponse = "Failure...  \(error)"
}

Struct

  • 継承を指定できるのはclassだけ
  • デイニシャライザが利用できるのはclassだけ
  • struct(とenum)は常に値渡し、classは参照渡し
  • classとの使い分けがよく分かってない
struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()

Protocols and Extensions

Protocols

インタフェースもしくはtraitのようなもの。mixinに使う? mutatingというキーワードをメソッドにつけると実装先でシグニチャを変更できる。

protocol ExampleProtocol {
    var simpleDescription: String { get }
    mutating func adjust()
}

クラス、Enum、StructはProtocolを採用できる。

class SimpleClass: ExampleProtocol {
    var simpleDescription: String = "A very simple class."
    var anotherProperty: Int = 69105
    func adjust() {
        simpleDescription += "  Now 100% adjusted."
    }
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription
 
struct SimpleStructure: ExampleProtocol {
    var simpleDescription: String = "A simple structure"
    mutating func adjust() {
        simpleDescription += " (adjusted)"
    }
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription

Extension

プロトコルを用いて既存のクラスを拡張できる。Rubyのモンキーパッチ的な事を実現できる。

extension Int: ExampleProtocol {
    var simpleDescription: String {
    return "The number \(self)"
    }
    mutating func adjust() {
        self += 42
    }
}
7.simpleDescription

プロトコルを変数の型アノテーションに使用する事もできる。

let protocolValue: ExampleProtocol = a
protocolValue.simpleDescription
// protocolValue.anotherProperty  // Uncomment to see the error

ジェネリクス

func repeat<T>(item: T, times: Int) -> T[] {
    var result = T[]()
    for i in 0..times {
        result += item
    }
    return result
}
repeat("knock", 4)

ここまでSwift Tour。

その他気になった所

Subscripts

クラス、Struct、Enumは内部のメンバへのアクセスショートカットとしてsubscriptsを定義する事ができる。

struct TimesTable {
    let multiplier: Int
    subscript(index: Int) -> Int {
        return multiplier * index
    }
}
let threeTimesTable = TimesTable(multiplier: 3)
println("six times three is \(threeTimesTable[6])")
// prints "six times three is 18"

Attributes

Javaアノテーション的なもの。 よく分からないが@lazyというので遅延評価を実行できる?

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Attributes.html#//apple_ref/doc/uid/TP40014097-CH35-XID_460

生アドレスを触る

func swapTwoInts(inout a: Int, inout b: Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)”

アサーション

let age = -3
assert(age >= 0, "年齢は必ず0歳以上")
// assert(age >= 0) とも書く事ができる

エイリアス型指定

型名にエイリアスを指定する事ができる。

typealias AudioSample = UInt16
var maxAmplitudeFond = AudoSample.min

予約語

backtick(`)で挟めば使用できるらしい。

  • 宣言:class, deinit, enum, extension, func, import, init, let, protocol, static, struct, subscript, typealias, var
  • 文: break, case, continue, default, do, else, fallthrough, if, in, for, return, switch, where, while
  • 式・タイプ: as, dynamicType, is, new, super, self, Self, Type, __COLUMN__, __FILE__, __FUNCTION__, __LINE__
  • 特別なコンテキストでの予約語: associativity, didSet, get, infix, inout, left, mutating, none, nonmutating, operator, override, postfix, precedence, prefix, right, set, unowned, unowned(safe), unowned(unsafe), weak, willSet

よく分からない事

  • 並列処理の仕組みはあるのか。NSThreadとかCocoa組み込みのオブジェクトを使う?
  • 例外処理は存在しない?
  • プロパティの可視性はあるのか?
  • namespaceがあるという噂があるが…

触ってみた感じ全体的良くできてる。モダンな言語をそれなりにブレンドしたらできました感がすごい。組み込みオブジェクトとかは後で調べる。