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

亀岡的プログラマ日記

京都のベッドタウン、亀岡よりだらだらとお送りいたします。

C#使いから見たSwift

ビッグウェーブに乗り遅れないよう、Swiftをちょっと勉強してみたのでメモ。 勉強がてらに非常にまどろっこしいFizzBuzzを実装してみたので、そのコードを参考がてらにちりばめながら。

全体はここに。ほんとに、ただのまどろっこしいFizzBuzzですがw

気になった機能とか文法とか

変数と定数

letによる宣言にはやっぱりこころが若干ぴょんぴょんはする。

let x = "hoge"
// 再代入はコンパイルエラー
// x = "fuga" 

まぁでも、そこまで深い意味はなくてreadonlyconstのAliasと思っておけばいい気がする。値型だろうが参照型だろうがlet宣言できるし、参照型だったら参照先に変数があれば変更し放題ではあります。 ちなみに変数はvar

var y = "fuga"
y = "piyo"

関数宣言

とても式を宣言している感じがして良い。思考順序と合っている感じ好き。

public func convert (input: Array<Int>) -> Array<String>{
    return ["hoge","fuga","piyo"]
}

まぁ、個人的にはもう少し型推論して欲しいけれど。

Optional

んで、Optionalである。 なんと?をつけて宣言する。どこのNullableだ??

というわけで、たとえば引数にIntのOptionalを含む関数はこんな感じ。

public func convert(input: Int?) -> String{
    return ""
}

ちなみに、Optional<a'>みたいな型になるのではなく、その宣言された値かnillが入る。どこのNu(ry

中身を取り出すには!をつければOK。

public func convert(input: Int?) -> String{
    return "\(input!)"
}

ただし、これだと当然ぬるぽ、ではなくにるぽっておいうか実行時エラーになる可能性があるので、一般的にはifで束縛する構文を書けます。

public func convert(input: Int?) -> String{
    if let temp = input {
        return "\(temp)"
    }
    return "0"
}

とはいえ、上記程度なら所謂"null結合演算子"??が使用できる。

public func convert(input: Int?) -> String{
    return return "\(input ?? 0)"
}

だからどこの(ry

話はそれますけど、文字列に変数を素直に埋め込めるのいいですよね。

let x = うさぎ;
let str = "ご注文は\(x)ですか?"

そしてそして、Optionalで入っているかどうかの確認を大いにすっとばせるOptionalチェーンという呼び方も有ります。

public func convert(input: Int?) -> String{
    if let number =  input {
        return number.fizzbuzz()
    }
    return "???"
}

という書き方が正当な書き方ですが、optionalで渡された関数のメンバ変数として呼べる時のみ、

public func convert(input: Int?) -> String{
    return input?.fizzbuzz() ?? "???"
}

と、自分がnilならnilを返し、そうでないならメソッドを呼ぶ という事ができます。

・・・はい、そうですね、null 条件演算子演算子です。だからおまえはどこのN(ry

パターンマッチっぽいswitch文

関数型言語言えば、パターンマッチですよね!!専用構文はありませんが、Switch Case文が非常にそれっぽい仕上がりにできます。

通常のSwitch Caseは、、、まぁいいでしょ。普通にできます。 あとはTupleによるマッチ。

switch tuple{
    case let (left , 3): //Tupleの片方のみを定数束縛とかできる
        println(left)
    case let (0...3,right): //RangeでのマッチもOK
        println(right)
    default:
        println()
}

whereによる条件マッチなんかも出来ます。

private func convert(input: Int) -> String{
    switch input{
    case _ where input % 15 == 0:
        return "FizzBuzz"
    case _ where input % 3 == 0:
        return "Fizz"
    case _ where input % 5 == 0:
        return "Buzz"
    default:
        return "\(input)"
    }
}

あとはcaseラベルを複数流れさせるfallthroghとかもあるんですが、まぁあんまり好きじゃないのでパス。

関数オブジェクト

さて、関数オブジェクトです。とはいえ、関数宣言をそのまんまletなりvarで定義できる、という感じ。

private let internalFunction: Int -> String
public init(delegateFunction: Int -> String){
    internalFunction = delegateFunction
}

個人の好みとは思いますが、関数宣言を変数に入れると、Let SomeFunction: Int -> String みたいな書き方になって普通にカッコイイと思います(こなみかん)

クロージャの記法

んで、上記のような関数オブジェクトを作ると、当然クロージャの出番となるんです!が!

let SomeFunction: Int -> String = {(input: Int) -> String in
        return "Hoge"
}

なんでこんなにまどろっこしいの・・・

正確に書きますと、

{(引数名1: 引数の型1, 引数名2:引数の型2, ...) -> 返り値の方 in
    ステートメント
}

となってまして、ほんとにこれだけはまどろっこしいと言わせていただきたい。

まぁさすがにこんな書き方することは少なくて、例えばこんな型があって

public class DelegateFizzbuzz: FizzbuzzFunction{
    
    private let internalFunction: Int -> String
    
    public init(delegateFunction: Int -> String){
        internalFunction = delegateFunction
    }
}

これの関数の引数として*1関数オブジェクトを渡す際際には省略記法が書けます。 即ち、

  • 関数オブジェクトがその関数の最後の引数のとき、カッコから出してそのまま定義式に続けられる
  • 型推論が効くようになる

という2つの恩恵が受けられるため、

let fizzbuzz = DelegateFizzbuzz(){input in
                return "\(input)"
}

と、ここまで記述を減らせます。

さらにもう一歩進むと、上は全ての引数、返り値に関する型推論は完了しているはずで、ただ内部のステートメントinputという名前を使いたいがためだけに上記のような記法となっているわけです。 swiftでは他の言語では"Trailing Clusure"と呼ばれる記法が使用できます。これは、関数の引数を$0, $1, ... みたいに参照できる記法です。この記法を使うと最初の記述まで不要になり、

let fizzbuzz = DelegateFizzbuzz(){"\($0)"}

と、一行で書けるようになっています。

ただし、上記2つの省略記法は使用箇所がわりと限定されるので注意が必要ですね。

プロパティオブザーバ

さて、これもなかなかおもしろいなー、と思ったのがプロパティオブザーバ。これは何かって言うと、、、

var notifyProperty: String{
    willSet{
        println("\(newValue) will be set")
    }
    didSet{
        println("\(oldValue) has been replaced")
    }
}

こんな感じで、値が変更される直前にwillSetが呼ばれ、その中で新しい値newValueを参照できます。また、値が変更された時にdidSetが呼ばれ、今度は変更された後の値がoldValueで参照できます。 これの使いドコロは、、、やっぱUI周りでの通知でしょう。うまく連鎖させれば、通知の機構を組めそう。*2

拡張クラス

クラスを自由に拡張できます。例えば、int型の整数をFizzBuzzした文字列に変換する関数を考えてみましょう。

private func convert(input: Int) -> String{
    switch input{
    case _ where input % 15 == 0:
        return "FizzBuzz"
    case _ where input % 3 == 0:
        return "Fizz"
    case _ where input % 5 == 0:
        return "Buzz"
    default:
        return "\(input)"
    }
}

この処理を、int型の拡張として定義することができます。

extension Int{
    func fizzbuzz() -> String{
        switch self{
        case _ where self % 15 == 0:
            return "FizzBuzz"
        case _ where self % 3 == 0:
            return "Fizz"
        case _ where self % 5 == 0:
            return "Buzz"
        default:
            return "\(self)"
        }
    }
}

あとはこれを宣言している部分が見える状態(同じファイルに書くか、importする)にしてやれば、

1.fizzbuzz() // "1"
3.fizzbuzz() // "Fizz"
5.fizzbuzz() // "Buzz"
15.fizzbuzz() // "FizzBuzz"

と呼ぶことが出来ます。

プロトコル

実装を持たないクラス。これを継承した場合は、定義されている全てのメソッド・プロパティを実装しないといけない感じになっています。 複数継承可能です。

public protocol FizzbuzzFunction {
    func convert(input: Int?) -> String
}

・・・えっと、ようするにinterfaceです。以上!

いや、名前がいいなと思ったんですよね。確かに、これってクラス間の通信規約 = プロトコルだよなーと。

例外

・・・がないんです。意外。Optional使えということなんでしょう。

全体的な印象

さて、ざっと見ましたけれど、どう思いました?最も新しい言語らしく、うまくトレンドを取り入れている印象は強い。関数オブジェクトやクロージャあたりの書き方(省略記法のほうね)もそう。つまりこの言語って、「(C# | Java | Ruby | その他いろいろ)みたいな」言語なんですよね。恐らくかなりな言語やってる人が、「swift自分のしってる○○に似てる」と思うのでは。

そんな中で嬉しいところはコンパイルできて静的型付けされている、ということでしょう。Xcodeさんに色々不満もありますが、動的型付けでやるよりははるかにマシです。

だから、「ついに関数型言語の波がiOSに!!」みたいな最初の見た目に反して、そこまで突き抜けてる言語とは思えなかったかな。*3

結局、例外がなくて、ほぼNullableのOptionalであって、プロパティオブザーバみたいなUIに寄り添う機能があって、とかを見ていると、どうもswiftiOSのUI開発言語として割と特化しているのかなぁと。1割の人が使うすげえ言語機能をオミットする代わりに、9割の人が楽しくプログラミングできるように考えられている、気がする。

なので言語としては非常にコンパクト。iOSアプリをさくさくっと書いていくなら、きっととても楽しい言語に見えます。

読んだ本

ひとまず全部読んだのは、これ。

あと、Swiftの言語リファレンス的にこっちも買った。

詳解 Swift

詳解 Swift

他にオススメあれば教えるといいと思う!

*1:そういえばコンストラクタはinit関数なんよね。これもC#er的には面白い

*2:この辺りは、そもそもiOSMVC的なアーキテクチャどう組むのか分かっていなくて色々考える所なんですが

*3:CPUの歓声は僕には聞こえなかった。残念。