このページは「SwiftではじめるiOSアプリ開発」(清水美樹著、工学社刊、2014年12月12日発売、ISBN978-4-7775-1869-2)の著者による関連情報です。
第1章において、「整数どうしの掛け算プログラム」を作成しました。二つのテキストフィールドfieldA, fieldBに整数を入力してボタンをクリックすると、ラベルresultLabelに二つの数を掛け算した結果が表示されます。
このプログラムはXcodeの使い方やGUIの書き方の実践例として作ったものですが、このテキストフィールドに、小数も入力して計算できるようにするにはどうすればいいでしょうか。
GUIの変更はありませんが、より実用的なプログラムにするために、ひとつの方法を提案します。
最も簡単なのは、テキストフィールドに何を入力しても小数として扱うことです。「2」を入力したら「2.0」になります。
しかし問題は、Swiftには文字列を整数に変換する「toInt」という関数はあっても、小数に変換する「toDouble」という関数がないことです。
しかしSwiftはObjective-Cとの互換性がかなり高いので、「NSString」というObjective-Cの文字列型に簡単に変換できます。
NSString型であれば、doubleValueという関数が使えます。これは、変換できないような文字列に対しては「0.0」を返します。Swiftの「オプショナル型」で苦労しているのは一体なんなのだろうという気もしますね。
var myDoubleValue = (mySwiftString as NSString).doubleValue
全部小数に変換し、掛け算した結果をそのまま出すのであれば、リスト1の方法さえ見出せばあとは簡単です。
本書では、ボタンをクリックしたときに呼び出すメソッドとしてcalculateを定義しました。その中身をリスト2のように書き換えます。
@IBAction func calculate(sender: AnyObject) { let stringA = fieldA.text as NSString let stringB = fieldB.text as NSString let doubleA = stringA.doubleValue let doubleB = stringB.doubleValue let resultAB = doubleA*doubleB resultLabel.text = toString(resultAB) }
アプリを実行してみてください。小数を入力すると、掛け算結果が小数で表示されるようになりました。
しかし、整数を入力しても小数で表示されるのは煩雑ですね。
そこで、以下のようなプログラムにしましょう。
とすると、メソッドcalculateが複雑になりそうですね。そこで、「文字列と数値の変換」についていろいろなメソッドをもつクラスを別に作りましょう。
定義は「ViewController.swift」ファイルに、クラス「ViewController」の定義の外側に記述します。
テキストフィールドに入力したのは、整数か小数かを判断します。
この判断は正確さを要求しません。どちらでもないものは、さらに「0」または「0.0」に変換するからです。小数点「.」があれば小数、なければ整数という判断です。
class func isHandlingInt(origString:String)->Bool{ for ch in origString{ if(toString(ch) == "."){ return false } } return true }
上記のように、文字列を整数に変換するのと小数に変換するのはいろいろと方法が違います。そこで、このクラスのメソッドとして「toInt」と「toDouble」という名前にそれぞれまとめます。
SwiftのメソッドtoIntはオプショナルなので、リスト4のメソッドで「整数に変換できないときは0を戻す」ことを明記しています。リスト5では、Objective-CのメソッドdoubleValueでは自動的に0.0を返すので記述していません。
class func toInt(origString:String)->Int{ if let resultInt = origString.toInt(){ return resultInt } return 0 }
class func toDouble(origString:String)->Double{ return (origString as NSString).doubleValue }
以上のメソッドを定義したクラス「StringNumberHandler」はリスト6のような場所に書きます。
import UIKit class StringNumberHandler{ class func isHandlingInt(origString:String)->Bool{ for ch in origString{ if(toString(ch) == "."){ return false } } return true } class func toInt(origString:String)->Int{ if let resultInt = origString.toInt(){ return resultInt } return 0 } class func toDouble(origString:String)->Double{ return (origString as NSString).doubleValue } } class ViewController: UIViewController { .... クラスViewControllerの定義 .... }
クラスViewControllerの定義中に書いたメソッドcalculateを、リスト7のように書き換えます。クラスStringNumberHandlerの各メソッドを使います。
@IBAction func calculate(sender: AnyObject) { var stringA = fieldA.text var stringB = fieldB.text //どちらの値も整数なら if(StringNumberHandler.isHandlingInt(stringA) && StringNumberHandler.isHandlingInt(stringB)){ //StringNumberHandlerのメソッドtoIntを用いている var intResult = StringNumberHandler.toInt(stringA) * StringNumberHandler.toInt(stringB) resultLabel.text = toString(intResult) } else{ var doubleA = StringNumberHandler.toDouble(stringA) var doubleB = StringNumberHandler.toDouble(stringB) var doubleResult = doubleA * doubleB resultLabel.text = toString(doubleResult) } }
アプリを実行してみましょう。どちらも整数を入力したときは整数、どちらかが小数のときは小数の結果が表示されます。
この状態で、小数同士を掛け算すると、小数点以下の桁数が多くなります。小数点以下2桁同士の掛け算の結果は、小数点以下4桁の値で表示されます。ちょっと長すぎますね。
桁数があまり多くなりすぎないようにしましょう。データ処理の方法としてはかなりいい加減ですが、「もとの値で小数点以下の桁数が大きい方に合わせる」というのはどうでしょうか。
ある小数において、小数点以下の桁数を数えるにはどうすればよいでしょうか?
数学的な方法、プログラムのアルゴリズムはいろいろあると思いますが、ここでは単純な方法を用いましょう。
値がテキストフィールドを通じて、まず文字列で与えられることを利用します。
小数点である「.」という文字列を「デリミタ」にして、整数部分と、小数点以下に当たる「数字の文字列」を分離するのです。
これには、Swiftで文字列を処理するメソッド「componentsSeparatedByString」が使えます。
戻り値は文字列の配列です。「小数」に当たる文字列には「.」は一つしかありませんから、要素は2つです。
var array = toString(theValue).componentsSeparatedByString(".")
しかし、念のため二つ以上の要素が得られたときにだけ、その「0から数えて1番目の要素」の中身を取り出すことにします。
文字列であるその要素の文字数を数えれば、それが小数点以下の桁数になります。
if(array.count == 2){ return countElements(array[1]) }
何かの間違いでリスト9のif文に引っ掛からなかった場合は、0を戻します。
ある小数を指定桁数で丸めるためには数学的関数を使わなければなりませんが、このアプリでは「丸めて表示」すればいいだけなので、リスト10のような書式の整形ができます。
リスト10は、小数点以下の桁数を3桁に丸めた文字列を得る書き方です。この書き方でちゃんと四捨五入して表示してくれます。
String(format:"%.3f", 3.141516)
「なんの数値を処理するか」「何桁で丸めるか」をそれぞれ変数で表すには、リスト11のようにします。
String(format:"%.\(numberOfDigit)f", theValue)
「小数点以下の桁数の多い方」に合わせるとしました。「二つの数の大きさを比べる」のは他のプログラミング言語と同じです。
この関数はクラス「StringNumberHandler」に特有の処理ではないので、クラス「ViewController」の定義中にメソッドselectLargerIntとして定義しておくのがよいでしょう。
func selectLargerInt(a:Int, comparedTo b:Int)->Int{ return (a > b ?a : b) }
以上、メソッドcalculateを書き換えるための準備はリスト12のとおりです。
import UIKit class StringNumberHandler{ .... これまでの記述 ... class func getNumberOfFractionalDigit(theValue:Double)->Int{ var array = toString(theValue).componentsSeparatedByString(".") if(array.count == 2){ return countElements(array[1]) } return 0 } class func getStringForTrimmedDoubleNumber(theValue:Double, inNumber numberOfDigit:Int)->String{ return String(format:"%.\(numberOfDigit)f", theValue) } } class ViewController: UIViewController { ... これまでの記述 ..... func selectLargerInt(a:Int, comparedTo b:Int)->Int{ return (a > b ?a : b) } }
小数の変数doubleAについて、小数点以下の桁数を数えるには、クラスStringNumberHandlerのメソッドgetNumberOfFractionalDigitを用います。
StringNumberHandler.getNumberOfFractionalDigit(doubleA)
リスト13のように得られた桁数と、同じ方法で得られるdoubleBの小数点以下の桁数を、自分(ViewController)のメソッドselectLargeIntを用いて比べ、大きい方を採用します。クラスStringNumberHandlerのメソッド「getNumberOfFractionDigit」を用います。
var trim = selectLargerInt( StringNumberHandler.getNumberOfFractionalDigit(doubleA), comparedTo: StringNumberHandler.getNumberOfFractionalDigit(doubleB) )
以上で、「指定の数(doubleResult)」を「指定の桁数(trim)」で丸めて表示する方針が固まりました。クラスStringNumberHandlerのメソッド「getStringForTrimmedDoubleNumber」を用いて、resultLabelに表示する内容が決定です。
resultLabel.text = StringNumberHandler.getStringForTrimmedDoubleNumber(doubleResult, inNumber: trim)
以上、メソッドcalculateがリスト16のように完成します。
@IBAction func calculate(sender: AnyObject) { ...これまでの記述.... else{ ...これまでの記述.... var trim = selectLargerInt( StringNumberHandler.getNumberOfFractionalDigit(doubleA), comparedTo: StringNumberHandler.getNumberOfFractionalDigit(doubleB) ) //resultLabel.textの中身を書き換える resultLabel.text = StringNumberHandler.getStringForTrimmedDoubleNumber(doubleResult, inNumber: trim) } }
アプリを実行して、いろいろな小数や整数を入力してみてください。小数の桁数があまり多くならずに抑えられますね。