Swiftで遊ぼう! on Hatena

あしたさぬきblogでやってた初心者オヤジのiOSプログラミング奮闘記がHatenaに来ました

Swiftで遊ぼう! - 672 - Applying MVC 4

講義2真っ只中、今日の内容は私にとって重要でした。

Developing iOS 9 Apps with Swift - Free Course by Stanford on iTunes U

MVCモデルの構築のため演算部分の機能をM(モデル)としてのCalculatorBrainに移すことに成功しました。私ならこれで満足していたでしょう。関数ボタンの拡張は「switch-case」文のcaseを増やすことで対応できます。

しかし、ちゃんとしたプログラマーを目指すなら、case文を増やしていくことのコードの冗長性に気づき特定の項目でまとめて可読性を高めてやる努力をしなければならないんです。こういう能力は今の私に欠けています。というか今後こういう能力が育つのだろうか?

学生の頃、受験勉強で数学の問題に取り組んでいた時に似ていますね。数学の公式を知っていても利用できなければ問題は解けないからです。プログラミングはまさに数学的アプローチが必要ですね。アルゴリズムを考えるというのがそういうことなんでしょうね。

ポール先生は、ここでcase文で得られる共通パターンを説明しています。symbolという「String型」を与えることで、すべてaccumulatorという「Double型」を得ているところに注目しています。

この関係性をDictionaryを使ってテーブル化して抜き出しているんです。ここでDictionary型の説明が入ります。

    private var operations: Dictionary<String, Double> = [
        "π": M_PI,
        "e": M_E
    ]

まず「π」などの定数をDictionaryでテーブル化します。「√」はこの後すぐに説明が入るので無視します。このようにテーブル化した場合、performOperationメソッドはかなりシンプルになります。

    func performOpeation(symbol: String) {
        if let constant = operations[symbol] {
            accumulator = constant
        }
    }

以前のswitch-case文の場合、定数が増えれば増えるほど関数の記述が長ったらしくなってコードの可読性が悪化しますが、このスタイルだとテーブルを増やすだけで対応できるのでスッキリします。

しかし、それは定数の場合だけです。じゃあ「√」はどう対応したらいいのでしょう?

    private var operations: Dictionary<String, Double> = [
        "π": M_PI,
        "e": M_E,
        "√": sqrt, // エラーが表示
        "cos": cos // エラーが表示
    ]

試しにそのままテーブルに「√」を加えるとエラーが表示されます。それは当然です。「sqrt」はDouble型じゃないからです。ここでもプログラマーの気づきが必要なところです。どういうオペレーションが存在するか考えてグルーピングするんです。ここでSwiftの素晴らしい機能、Enumrationを使っています。

    enum Operation {
        case Constant
        case UnaryOperation
        case BinaryOperation
        case Equals
    }

そしてDictionaryのテーブルを列挙型(Enumration)に併せて変更します。

    private var operations: Dictionary<String, Operation> = [
        "π": Operation.Constant, //M_PI,
        "e": Operation.Constant, //M_E,
        "√": Operation.UnaryOperation, //sqrt,
        "cos": Operation.UnaryOperation //cos
    ]

なんか綺麗にまとまって上手くいっているように見えますが、今度はperformOperationメソッドでエラーが表示されています。それは当然です。具体的な定数や関数を読み取ることができないからです。これを解決するためにenumのassociated valueを使うんです。

ますConstant(定数)のみ実装します。

    enum Operation {
        case Constant(Double) // associated valueを設定
        case UnaryOperation
        case BinaryOperation
        case Equals
    }

次にテーブルとしてのDictionaryも変更します。

    private var operations: Dictionary<String, Operation> = [
        "π": Operation.Constant(M_PI),
        "e": Operation.Constant(M_E),
        "√": Operation.UnaryOperation, //sqrt,
        "cos": Operation.UnaryOperation //cos
    ]

そしてメソッドでassociated valueを取り出します。

    func performOpeation(symbol: String) {
        if let operation = operations[symbol] {
            switch operation {
            case .Constant(let value): accumulator = value
            case .UnaryOperation: break
            case .BinaryOperation: break
            case .Equals: break
            }
        }
    }

めちゃくちゃ感動です!私はこんなコーディングできそうにないけど、Swiftのパワー全開というようなコーディングですね。本当に使えるようになりたいです。

これでランして「π」ボタンを押すとちゃんと動きます!

次の問題は「√」です。associated valueに何を使うのか? 私はすぐにfunction(関数)だと思いました。Swiftの概念として重要なのはfunctionも「型」の1つということです。次の様にenumを変更します。

    enum Operation {
        case Constant(Double)
        case UnaryOperation((Double) -> Double)
        case BinaryOperation
        case Equals
    }

これに併せてテーブルのDictionaryにも変更が入ります。

    private var operations: Dictionary<String, Operation> = [
        "π": Operation.Constant(M_PI),
        "e": Operation.Constant(M_E),
        "√": Operation.UnaryOperation(sqrt),
        "cos": Operation.UnaryOperation(cos)
    ]

そしてこれを利用するメソッドを次のように変更します。

    func performOpeation(symbol: String) {
        if let operation = operations[symbol] {
            switch operation {
            case .Constant(let value): accumulator = value
            case .UnaryOperation(let function): accumulator = 
                                         function(accumulator)
            case .BinaryOperation: break
            case .Equals: break
            }
        }
    }

感動です。ポール先生の講義は本当に分かり易いです。でも利用できるかどうか私に問題がありますが(^_^;)

次にBinaryOperationの話に入りますが長くなったので今日はここまで。