

現在、Swiftの勉強をしております。  
基本的な構文がなんとなくわかってきたので、正しい計算ができる電卓を作りたいです。

正しい計算式をプログラムで書くには、RPN電卓というものが入門としては便利ということまではわかりました。

文字列からRPNっぽい計算ができるプログラムは書けたのですが、計算式をRPN向きの文章にする方法がいまいち理解できておりません。

例) 1+3+4×2 の場合、先頭から計算すると16になってしまう  
"1 3 4 2 × + +" という文字列でRPNで計算すると12になる

参考  
<http://www.rpn.jp/lecture/rpn/rpn100/rpn101.html>  
<https://knowledge.sakura.ad.jp/220/>

RPNの文字列をうまくプログラムで表現したいのですが、どのようにすればよいでしょうか。  
各種ボタンなどで入力などはとれる状態です。

※Swiftでstackの書き方がわからないため、結構無理やりなソースになっています。

[code]

    func calcString(_ str:String){
    
        let arr:[String] = str.components(separatedBy: " ")
        var stack:[String] = []
    
        // 分割
        for dat in arr {
            //print(dat)
            if isOnlyNumber(dat) {
                // 数字のみ
                stack.append(dat)
            } else {
                // 数字のみでない
                if(isOnlyArithmetic(dat)){
    
                    // stackが2つ以上あるか
                    if(stack.count >= 2){
                        let num1:Double = Double(stack[stack.count-1])!
                        let num2:Double = Double(stack[stack.count-2])!
                        var res:Double = 0
    
                        // ２つ要素を削除
                        stack.removeLast()
                        stack.removeLast()
    
                        // 計算
                        switch dat {
                        case "+":
                            res = num1 + num2
                        case "-":
                            res = num1 - num2
                        case "×":
                            res = num1 * num2
                        case "÷":
                            res = num1 / num2
                        default:
                            break
                        }
    
                        stack.append(String(res))
    
                    }else{
                        print("error stack.count => " + String(stack.count))
                    }
    
                }else{
                    print("error dat => " + dat)
                }
    
            }
        }
    
        print(stack.count)
        print(stack[stack.count-1])
    
    
    }
    
    // 数字のみかを調べる。
    func isOnlyNumber(_ str:String) -> Bool {
        let predicate = NSPredicate(format: "SELF MATCHES '\\\\d+'")
        return predicate.evaluate(with: str)
    }
    
    // 符号のみか調べる
    func isOnlyArithmetic(_ str:String) -> Bool {
        if(str == "+" || str == "-" || str == "×" || str == "÷"){
            return true
        }
        return false
    }
    
[/code]


CC BY-SA 4.0
2018-10-10T07:54:13.757
49133
2018-10-10T16:39:44.963
30451
question
swift xcode swift4
SwiftでRPN電卓の作成


はじめまして。 質問としては「文字列で表現したRPN式を処理出来るが、スタックをどう表現すべきか？」という趣旨でよろしいでしょうか？

> 各種ボタンなどで入力などはとれる状態です。

との事ですので、ボタンと`IBAction`の接続で、ボタンが押されたら、この関数を実行するプログラムを書けるレベルには至っておられるものとして回答させていただきます。

まず。スタックはSwiftではこの様に書けると思います。あくまでも一例でもっと良い書き方もあるかも知れませんが、実用上は充分だと思います。

[code]

    struct Stack<Float> {
        var stack: Array<Float> = Array<Float>()
    
        mutating func push (_ item: Float) -> Void {
            stack.append(item)
        }// end push
    
        mutating func pop () -> Float {
            return stack.removeLast()
        }// end pop
    }// end struct Stack
    
[/code]

(`class`ではなく`struct`を使っているため、`func`の前に`mutating`と宣言しています)

計算結果の表示は、計算式を入力したラベルを計算結果で更新する形で良いと思います。

余談ですが、質問のソースについてですが、そのままでも充分動くのですが、

[code]

    let num1:Double = Double(stack[stack.count-1])!
    let num2:Double = Double(stack[stack.count-2])!
    var res:Double = 0
    
        // ２つ要素を削除
    stack.removeLast()
    stack.removeLast()
    
[/code]

と、ありますが、

[code]

    let num1:Double = Double(stack.removeLast())!
    let num2:Double = Double(stack.removeLast())!
    var res:Double = 0
    
[/code]

と、するほうが、よりスタック処理らしくてよいと思います。

* * *

そうではなくて、表示部とボタン部があり、ボタンを押していくことでRPN入力順に入力中の値や計算結果が表示される普通の電卓の様な外見を持ったアプリにしたいのでしたら、

更に画面には、数字ボタン(0~9と小数点ボタン)と、演算子ボタン（+,-,×,÷, 他には関数など)とエンター（数字入力確定）ボタンが必要になると思います。  
これらのボタンを配置したら、接続するアクションを3つ作ります。

まず、数字の処理は、`0`-`9`のそれぞれ、と`+/-`と`.`のボタンが押されることで、押したボタンに応じて数字を作る事になると思います。  
そのため、この様なクラスを作ってみました。

[code]

    class DecimalNumber {
        private var number: Float
        private var fraction: Float
    
        init() {
            number = 0
            fraction = 0
        }// end init
    
        public func addDigit(_ digit: Int) {
            if fraction == 0 {
                number *= 10
                number += digit
            } else {
                number += digit * fraction
                fraction *= 0.1
            }// end if digit is integer part or fractional part
        }// end addDigit
    
        public func changeSign () {
            number *= -1
        }// end changeSign
    
        public func switchToFract () {
            if fraction != 0 { return }
            fraction = 0.1
        }// end switchToFract
    
        public func getNumber () -> Float {
            let currentNumber: Float = number
            number = 0
            return currentNumber
        }// end getNumber
    }// end class Digits
    
[/code]

この様なクラス（のインスタンス。例えば`decimal`としましょうか）を作る事で、

  * `0`-`9`のボタンが押されたら、`decimal.addDigit([押されたボタンの数字])`
  * `+/-`のボタンが押されたら、`decimal.changeSign()`
  * `.`のボタンが押されたら、`decimal.switchToFract()`

を実行しつつ、電卓の数字表示欄に押されたボタンの文字を`+=`していく、それぞれのボタンに対応する`IBAction`を書けば、ボタンを押した通りの数字が作れると思います。

次に、エンターボタンや関数ボタンが押されたら、decimalから数字をスタックに`push`します。ここでは、`Stack`のインスタンスを`stack`という名前にしたことにして、

  * エンターボタンが押されたら、`decimal`から数字を取り出して`stack.push(decimal.getNumber())` だけして、次の入力待ちに戻ります

  * エンター以外の演算子ボタンが押されたら、  
1 エンターと同様に`stack.push(decimal.getNumber())`します  
2 押されたボタンの演算子が必要とする数だけスタックから値を取り出(`numN = stack.pop()`)し、演算子で計算を行います  
3 行われた計算結果を`stack.push()`で、スタックの最後に書き戻します  
4 スタックの最後の数字を計算結果部に表示します

とすることで、最低限の「ボタンを押すことで数字を入力し、計算を行う電卓アプリ」が作成出来ると思います。


CC BY-SA 4.0
2018-10-10T16:39:44.963
49155
2018-10-10T16:39:44.963
14745
49133
answer