Swiftで遊ぼう! on Hatena

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

Swiftで遊ぼう! - 214 - Developing iOS 8 Apps with Swift - More Xcode and Swift, MVC - 時間はかかるよ

Swiftで遊ぼう!の古い記事-> Life-LOG OtherSide

Calculatorプロジェクトのコードの続き。

昨日は、下のトピックに話題が入る前に疲れて終わっちゃいました(^_^;)

  • switch
  • type型としての関数
  • 関数をあっという間に定義するクロージャ

switchの説明は、operatorボタン(× ÷、+、−)で共通したアクションメソッドoperateを利用するところに出てくる。

f:id:yataiblue:20150131101026j:plain

次のコードはswitchの使用例で、このアクションメソッドの中で計算させている。コーディングしているのは「×」だけで、他のオペレーションはすべてコピペしているだけなんで、例えばという話でイメージしてください。これはあまりスマートなコーディングじゃないですよね。

@IBAction func operate(sender: UIButton) {
 let operation = sender.currentTitle!
 if userIsInTheMiddleOfTypingNumber {
  enter()
 }
 switch operation {
  case "×":
   if operandStack.count >= 2 {
    displayValue = 
    operandStack.removeLast() * operandStack.removeLast()
    enter()
   }
  case "÷":
   if operandStack.count >= 2 {
    displayValue = 
    operandStack.removeLast() * operandStack.removeLast()
    enter()
   }
  case "+":
   if operandStack.count >= 2 {
    displayValue = 
    operandStack.removeLast() * operandStack.removeLast()
    enter()
   }
  case "−":
   if operandStack.count >= 2 {
    displayValue = 
    operandStack.removeLast() * operandStack.removeLast()
    enter()
   }
  default: break
 }
}

ここで共通部分を関数として取り出すと次のようにできる。

@IBAction func operate(sender: UIButton) {
 let operation = sender.currentTitle!
 if userIsInTheMiddleOfTypingNumber {
  enter()
 }
 switch operation {
 case "×": performOperation(multiply)
// case "÷":
// case "+":
// case "−":
 default: break
 }
}
    
func performOperation(operation:(Double, Double) -> Double) {
 if operandStack.count >= 2 {
  displayValue = 
  operation(operandStack.removeLast(), operandStack.removeLast())
  enter()
 }
}
    
func multiply(op1: Double, op2: Double) -> Double {
 return op1 * op2
}

実はこのようにメソッドの引数にメソッドを渡すというやり方はSwiftで一般的で、この使い方を自由に使えないと後で苦労するだろう。でも、頭の悪い私にはまだ自分で使えそうにない(T_T) しかし、ここから次のクロージャーに発展していく。

@IBAction func operate(sender: UIButton) {
 let operation = sender.currentTitle!
 if userIsInTheMiddleOfTypingNumber {
  enter()
 }
switch operation {
 case "×": performOperation{(op1: Double, op2: Double) -> Double in
  return op1 * op2
  }
//  case "÷":
//  case "+":
//  case "−":
 default: break
 }
}
    
func performOperation(operation:(Double, Double) -> Double) {
 if operandStack.count >= 2 {
 displayValue = 
 operation(operandStack.removeLast(), operandStack.removeLast())
 enter()
 }
}

そしてクロージャーの真骨頂は次のように省略するのが一般的だ。この変化はThe Swift Programming Language本でしっかり学んでおこう。

@IBAction func operate(sender: UIButton) {
 let operation = sender.currentTitle!
 if userIsInTheMiddleOfTypingNumber {
  enter()
 }
switch operation {
 case "×": performOperation{ $0 * S1}
 case "÷": performOperation{ $1 / S0}
 case "+": performOperation{ $0 + S1}
 case "−": performOperation{ $1 - S0}
 default: break
 }
}
    
func performOperation(operation:(Double, Double) -> Double) {
 if operandStack.count >= 2 {
 displayValue = 
 operation(operandStack.removeLast(), operandStack.removeLast())
 enter()
 }
}

これがクロージャ、関数を引数として渡す時に有用ですね。

もう一つだけ、クロージャーのところで注意点が示されている。

Main.storyboard上でシフトキーを押して「×」「÷」「+」「−」の4つのボタンを選択してからOpt + ドラッグして右横にボタンのコピーを作ってください。

上3つのボタンはタイトルを消して(消してボタンを消さないように、後でレイアウト調整で使います)、一番下のボタンを「√(ルート)」に変更してください。
f:id:yataiblue:20150203100419j:plain
ボタンが増えたので、switchのcaseを1つ増やします。

case "√": performOperation{ sqrt($0) }

しかし、よく見て下さい。このperformOperation()では引数を1つしか与えていないのでエラーになります。

じゃあどうするか?

答えは引数を1つだけ受け取る同じ名前のメソッドを用意すれば、Swiftが自動で判断してくれるんです。凄いですね。

@IBAction func operate(sender: UIButton) {
 let operation = sender.currentTitle!
 if userIsInTheMiddleOfTypingNumber {
  enter()
 }
 switch operation {
  case "×": performOperation{ $0 * $1 }
  case "÷": performOperation{ $1 / $0 }
  case "+": performOperation{ $0 + $1 }
  case "−": performOperation{ $1 - $0 }
  case "√": performOperation{ sqrt($0) }
  default: break
  }
 }
    
func performOperation(operation:(Double, Double) -> Double) {
 if operandStack.count >= 2 {
  displayValue = 
  operation(operandStack.removeLast(), operandStack.removeLast())
  enter()
 }
}
    
func performOperation(operation: Double -> Double) {
 if operandStack.count >= 1 {
  displayValue = operation(operandStack.removeLast())
  enter()
 }
}

今日はここまで。