FizzBuzz
を通してプログラミング考え方、swiftの使い方、を紹介します。
わかりやすくするために、説明は、正確さよりわかりやすい大まかな書き方にしています。
コードに関して、省略できる文もわかりやすさ優先で書かれています。
このプログラムのテストや実行は,macbookProとXcode11.2.1
で行われています。
ipadのアプリSwift Playgrounds
においても、ソースコードをコピーすれば、実行可能です。
その場合は、print()コードの結果が、一部しか観れないので注意してください。
githubにコードが上がっています。
画面上のボタンからいいねしてくれるとても私が助かります。
FizzBuzzについて
数字を読み上げるゲームです。
アーちゃん "1"
ブラちゃん "2"
数字が3で割れる時はFizzと言います
アーちゃん "Fizz"
ブラちゃん "4"
数字が5で割れる時はBuzzと言います
アーちゃん "Buzz"
ブラちゃん "6"
数字が3でも5割れる時は、FizzBuzzと言います
アーちゃん "14"
ブラちゃん "FizzBuzz"
アーちゃん "16"
FizzBuzzを答えのみでのみると
1,2,Fizz,4,Buzz,6,7,8,Fizz,Buzz,11,Fizz,13,14,FizzBuzz,16...
となります。
以上でFizzBuzzの基本規則の説明は終わりです。
他にも、プログラムごとに追加規則はありますが、その都度説明します。
FizzBuzz基本的考え方
FizzBuzzは数字に対して、文字列を返すゲームです。今回は単純に、答えの文字を返します
まず、数字が順番に入力される状況を作ります。1から15を作ります。
次に、場合分けを行います。4つに分けます。
最後に、文字を出力します。xcodeならの右下の部分(コンソール)に、文字列を表示します
それでは、順番に解説していきます
数字の入力
1から15を数字(Int)として,受け取れるようにします。
for in は、forのあとの**変数(i)に,inの後で渡された集合(1...15)**を1個つづ入れて,{}内のプログラムを実行してくれます。
/// 1...15には,数字が、小さい方から順番に1,2,3...13,14,15と入っています。
for i in 1...15 {
// letで、定数(値を入れらる箱 "suuji" )を用意します。``=``は,右側(suuji)に左側(i)を代入します。
let suuji = i
// print(i) は、入力された文字列(i)を表示してくれる
// separator:"" は、文字列が複数与えられた時、その間文字を入れてくれる。今回は不使用
// terminator: "," は,文字列の最後に","付ける
print(suuji, separator: "", terminator: ",")
}
実行結果
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
場合分け
与えられた数字によって場合分け行います。今回分けるのは、4つの場合です。
3で割れる
5で割れる
3でも5でも割れる
どちらでも割れない
場合分けには、if文を使います。
if は、与えられた条件式を真理判断(正しいか正しくないか)をして,実行部分を動作させます。
if(条件式){実行式}の形で利用します。
一つづつ説明していきます。
3で割れる
剰余演算%
(割り算のあまりを答えてくれる)とif
文を利用して分けていきます。
//ここでは入力される数字は固定式
let i = 15
//i を``%``を使って3で割った``あまり``をi3Amariに代入します。
let i3Amari = i % 3
//if文は, ``if``の後に与えられた、条件式(i3Amari == 0)が``真``(正しい)の場合、{}内部のプログラムを実行します。
//==は両辺を比較し、等しければ(同じ値であれば)、真を返します。
if i3Amari == 0 {
print("Fizz")
}
実行結果
Fizz
5で割れる
上記のコード内のあまり計算
を3から5
にします。そして、文字列
をFizzからBuzz
に書き換えます。
//変数名も書き換えます。
let i5Amari = i % 5
//if文内の条件式も変えるのを忘れないように
if i5Amari == 0 {
print("Buzz")
}
実行結果
Buzz
3でも5でも割れる
3と5で利用した、条件式を合体させます。条件式を合体
させるには、論理積&&
を使います。
/// 論理積&&は、両側が、ともに真であれば、真になります。片側だけでも、偽であれば、偽が返ります。
if i3Amari == 0 && i5Amari == 0 {
print("FizzBuzz")
}
実行結果
FizzBuzz
どちらでも割れない
3でも5でも、割った余りが0でないという事です。。そのために、不等価演算!=
を利用します。
不等価演算は、左右が同じでない場合に、真を返します。
//テスト結果を見るために、iの数字を変更してください。
let i = 14
//省略 今までのコードが入ります
if i3Amari != 0 && i5Amari != 0 {
print(i)
}
実行結果
14
文字列を出力する
文字列を、標準出力print
で出力します。
print()は、()内部に入力された文字列を、コンソール画面(文字列を表示する場所)に出力します。
文字列は""
で囲む必要があります。文字列Fizzと入力するためには,"Fizz"
と書き込みます。
printは,与えられる文字列が、文字列そのもでなく、定数や変数でも出力できます。
FizzBuzzでは、変数に、答えを入れ出力させます。
下記のコードprint.swiftでは、文字列の一部だけを、変数にして、print()
利用してます。
//var は変数(値を変えらる箱)を作ることができる。 ""で囲む事で、14(数字)を文字列と認識させている。
var kotae = "14"
// \()で囲う事でプログラムとして、処理することができる
print("答えは,\(kotae)です")
//変数は、中身を何度も入れ替えることができる。
kotae = "FizzBuzz"
print("答えは,\(kotae)です")
実行結果
答えは,14です
答えは,FizzBuzzです
FizzBuzzを出力する際に、
1
2
Fizz
と毎回改行(\n
)していたら読みにくいですよね。ですから、改行せず
に、
1,2,Fizz,
連続して出力できるように改行を消します。
kotae = "16"
//terminator:は,末尾に追加する文字列を指定している。\nは、改行コード テキスト入力のエンターキーと同じ
print("答えは,\(kotae)です",separator: "",terminator: "よね知ってます。\n")
kotae = "17"
//swiftのprintは、末尾に改行(\n)が自動で足されるので,改行させないならterminator:に""を入れる。自動で入る改行コードが(\n)上書きされて、改行されなくなる
print("答えは\(kotae)です",separator: "",terminator: "。改行しません。")
print("このprintは,改行されずに、前のprint続いて表示されます")
//連続して出力するためには,末尾に(,)を足す。改行コードを入力しない。
kotae = "1"
print(kotae,separator: "",terminator: ",")
kotae = "2"
print(kotae, separator: "", terminator: ",")
kotae = "Fizz"
print(kotae, separator: "", terminator: ",")
実行結果
答えは,16ですよね知ってます。
答えは17です。改行しません。このprintは,改行されずに、前のprint続いて表示されます
1,2,Fizz,
以上で、要素ごとの説明は終わりです。この3つを、まとめてFizzBuzzを作っていきます。
FizzBuzz
基本となるFizzBuzz
実行は、1〜15まで、外部入力(引数)はなし、出力は文字列で標準出力(print)
基本的考え方で示したコードを統合します。
数字の入力のfor_in_
の中に場合分け4種類
と文字列を出力
を入れます.
//for in の中に,"場合分け"と"文字列を出力するをコピーする"
for i in 1...15 {
// let suuji = i
// print(suuji, separator: "", terminator: ",")
//回答用文字列を、"文字列を出力する"から持ってくる
var kotae = ""
//場合分け4種類
let i3Amari = i % 3
if i3Amari == 0 {
//print("Buzz") コードを無効化して、kotaeに文字列を入れる
kotae = "Fizz"
}
let i5Amari = i % 5
if i5Amari == 0 {
//print("Buzz")
kotae = "Buzz"
}
if i3Amari == 0 && i5Amari == 0 {
//print("FizzBuzz")
kotae = "FizzBuzz"
}
if i3Amari != 0 && i5Amari != 0 {
//print(i)
//iは数字(Int)なので,文字列Stringに変更している
kotae = String(i)
}
//文字列の出力
//ここで答えが出力されている.
print(kotae, terminator: ",")
}
実行結果
1,2,Fizz,4,Buzz,Fizz,7,8,Fizz,Buzz,11,Fizz,13,14,FizzBuzz,
以上で、基本のFizzBuzzの作りれました。
次は、FizzBuzzをより使いやすくEngine化させたり、ランダムに対応したりします。
FizzBuzzEngine
ゲームエンジン(Unity)とか検索エンジン(google)とかありますね。
複雑に思えるでしょう。実際複雑です、何万行もあるからね。でもFizzBuzzなら簡単なんです。
そこでFizzBuzzをエンジンにしていきます。
題してFizzBuzzEngine
です。
まずエンジン(Engine
)とは何か。それは、入力されたエネルギーを別のものに変換して出力するものです。
様々な入力に対して、相手に合わせた固有の作業をして、ある一定の結果を返すものです。
特定の、値しか返さないものは、エンジンではありません。
大きく分けると3つの作用があります。
入力:様々な燃料や画像(写真)を受け取る
変換:燃料を動力に変える。画像を加工(盛る)する
出力:タイヤを回す。画像をネット(インスタ)に上げる。
です。例えば、Googleなどの検索エンジン
(キーワードを入力するとなっている部分)で言うと、
入力 -> 検索する文字列を受け取る
変換 -> 該当するページをデータにまとめる
出力 -> ウェブサイトに表示する
キーワードを受け取る、キーワードに合ったウェブサイトをまとめたデータ
に変換する、ウェブページに表示する。これを、FizzBuzzに適応すると
入力 -> ランダムな数字を
受け取る
変換 -> 数字,Fizz,Buzz,FizzBuzzに変換
する
出力 -> 文字列として出力
する
となります。ここで、FizzBuzz基本のコードにエンジンかどうか
当て嵌めてみましょう。
入力は、for in で1~15に
固定
されています。
変換は、4つに場合分けされています。数値ごとに変換
が出来ています。
出力は、printで、コンソールに文字列が出力
されています。
出来ていないのは、入力だけのようです。
つまり、数値の受け取り
を出来るようになれば、エンジンと言えそうです。
数値を受け取る
数値を受け取るには、関数(function)を利用します。
関数とは、複数のプログラムを命令として集めて固めた
ものです。
命令を受ける際に、数値を受け取る
ことが出来ます。
swiftで関数は
**func 関数名(受け取る数値につける名前:数値の型){プログラム}
**で作ることが出来ます。
- 受け取る数値につける名前
- 関数内部で、受け取った数値を区別して呼び出すために必要なものです。
通常は、
仮引数
(かりひきすう)と言います。 - 数値の型
- その数値がどのような型(クラス)に属しているか指示するものです。
省略して
型
と書くのが基本です。 ここでは、数値の場合はInt。文字列の場合はStringだと知っていれば十分です。
簡単な例として、数値を受け取って、2倍にしてprintしてくれる関数BaiBaiを作ってみましょう
// 関数名 (仮引数 : 型) {プログラム}
func BaiBai(hikisu : Int){
// *は掛け算の意味です
let bai = hikisu * 2
print("\(hikisu)の2倍は\(bai)です")
}
関数BaiBaiを利用してみましょう。
関数を利用するには、
**
関数名(仮引数名:与える値)
**で利用します
- 与える値
-
関数で数値を受け取る数値です。実際の値が入ります。関数定義の仮引数と区別をつけるために、
実引数
(じつひきすう)と言います。
FizzBuzzEngine
基本形のFizzBuzzをエンジン化しましょう。やることは単純です.
まず、FizzBuzzの基本形を、関数の中
に入れます。
そして、固定入力のfor in
を取り外します。
//仮引数名は"nyuryoku"です。数値が入ります。FizzBuzzEngine内部で呼び出せます。
func FizzBuzzEngine(nyuryoku : Int) {
//for inの無効化 コードを無効化するには、//を行頭に書き込むことで、その行を無効化できる。
// for i in 1...15 {
//答えを入れる変数。FizzBuzzEngineが呼び出されるたびに,毎回生成される。初期値は""で、文字列がない状態を表している。
var kotae:String = ""
//4つの場合分け
//nyuryokuを呼び出して、条件式に利用してます if文にの条件式内に、直接計算を書くことができます。
if nyuryoku % 3 == 0{
kotae = "Fizz"
}
// % と == だと計算の優先度が%のほうが高いので、先に計算されます。
//優先度が不明な場合は、()で囲むと安全に独立して計算させることが出来ます。
if (nyuryoku % 5) == 0 {
kotae = "Buzz"
}
//複数の条件式をまとめることも可能です。&&が,優先度が低いので,&&が最後に判断されます。
//ここでは先に(nyuryoku % 3 == 0)と(nyuryoku % 5 == 0)が、計算され真理判定を終えます。
if nyuryoku % 3 == 0 && nyuryoku % 5 == 0 {
kotae = "FizzBuzz"
}
//変数kotaeが、初期値から変更されたかどうかを判断することで、3と5で割れなかったことを判断しています。
if kotae == "" {
kotae = String(nyuryoku)
}
//文字出力
//separator: は,複数の文字列の入力があった際に、間に入る言葉を入力する。ここでは,nyuryokuとkotaeの間に入ってくれる。
print(nyuryoku, kotae, separator: "に対する答えは、", terminator: "です。\n")
//for in の終了コード 無効化を忘れないように
//}
}
実行してみましょう。
FizzBuzzEngine(nyuryoku: 1)
FizzBuzzEngine(nyuryoku: 3)
FizzBuzzEngine(nyuryoku: 5)
FizzBuzzEngine(nyuryoku: 15)
FizzBuzzEngine(nyuryoku: 2501)
実行結果
1に対する答えは、1です。
3に対する答えは、Fizzです。
5に対する答えは、Buzzです。
15に対する答えは、FizzBuzzです。
2501に対する答えは、2501です。
数値を受け取れるようになりました。
実引数を変えても、適切な答えが返ってくるので、様々な数値を入れて試してみて下さい。
終わりに(Engine)
これでエンジン要件、入力->変換->出力
を一通り満たすことが出来ました。
これでFizzBuzzEngine完成です。
見てもらったようにエンジンは、難しくありません。内部の変換作業は、難しいものもあります。
しかし、入力と出力がわかっていれば、どのようなものでも利用可能です。
例えば、print()
コードも内部が分からなくても、利用出来ます。どうか恐れずにプログラミングしてみて下さい。
以上で,FizzBuzzEngineの説明を終わります。
FizzBuzzEngineReturn
FizzBuzzEngineは滅びぬ!何度でも蘇るさ!と言う事で、数値を返す(Returnする)エンジンに改良して行きたいと思います。
なぜなら、前回のFizzBuzzEngineは,プログラム上では値がわからなかったからです。
printで表示されるだけでは、人間はわかりますが、プログラム側では、わからない
では困るのです。
目標はプログラム側で読めるようにすること。そのために返値
(かえりち)を導入します。
返値は、プログラムを実行したときに、プログラム上で返ってくる値です。
返値があるプログラムは、実行後に、他のプログラム
が値を受け取る
ことが出来ます。
swiftで返値は
func 関数名(仮引数名:型)->返値の型
{プログラム**return 返値
**}で作ることが出来ます。
- 返値の型
- 今回はFizzBuzzでは文字列を示す、
String
,数値を示すInt
を使います。 - return 返値
- この値は、変数、定数でも直接書き込んでも構いません。ただし、返値の型と同じ型である必要があります。
コード内で実行された場合、その行より背後の行は、実行されません。全ての処理(プログラム)は,returnの前に実行する必要があります。
返値の例として、数字を受け取って、5を掛けて、数字で戻してくれる。コードを作ってみましょう。
//func 関数名(仮引数名:型)->返値の型{プログラム return 返値}
func gobai(suuji:Int) -> Int {
let kotae = suuji * 5
// return 返値 間にあるスペースを忘れないこと
return kotae
print("ここは実行されない")
}
実行してみましょう
//printの内部で呼び出して受け取ることができる
print(gobai(suuji: 5))
//計算に使うことも出来る ここでは、gobaiの結果50と1が足算されて51が,定数(tasizann)に入る。
let tasizann = gobai(suuji: 10) + 1
print(tasizann)
実行結果
25
51
では、FizzBuzzEngineに返値をつけて行きます。
返値は、数字と文字列(FizzBuzz)が混ざっているので、文字列(String)にします。
// func 関数名 (仮引数 : 型) -> 返値の型{ プログラム return 返値}
func FizzBuzzEngineReturn(nyuryoku : Int) -> String {
// 変数宣言 変数名 :型 = 初期値 返す値の型(String)と同じなので、答えを入れておくために利用します.
var kotae:String = ""
//FizzBuzzEngineの場合分け部分
if nyuryoku % 3 == 0 {
kotae = "Fizz"
}
if (nyuryoku % 5) == 0 {
kotae = "Buzz"
}
if nyuryoku % 3 == 0 && nyuryoku % 5 == 0 {
kotae = "FizzBuzz"
}
if kotae == "" {
kotae = String(nyuryoku)
}
// return 返す値
return kotae
}
実行してみましょう
//FizzBuzzEngineReturnの実行結果を受け取る
var EngineReturn = FizzBuzzEngineReturn(nyuryoku: 15)
print(EngineReturn)
EngineReturn = FizzBuzzEngineReturn(nyuryoku: 404)
print("\(EngineReturn)を,受け取っています。")
//print内で直接実行することもできる。
print("35で実行すると、結果は\(FizzBuzzEngineReturn(nyuryoku: 35))です。")
//if文の条件式に使うこともできる
if FizzBuzzEngineReturn(nyuryoku: 45) == "FizzBuzz"{
print("45は,FizzBuzzだよ")
}
実行結果
FizzBuzz
404を,受け取っています。
35で実行すると、結果はBuzzです。
45は,FizzBuzzだよ
プログラム上で、FizzBuzzEngineの結果を受け取ることが出来ました。
これで、様々な利用をすることが出来ます。
配列に入れたり、テストをしたり、計算したり出来ます。
終わりに(Return)
ほぼ全ての関数は、値を返すことを目標に作られています。
なぜなら、全てを一つのプログラムで実行するのは、作るのも直すのも大変だからです。
ですから、関数
を複数組み合わせて作る
のが現代のプログラムの基本です。
これを、オブジェクト指向
と言います。
オブジェクト指向とは、乱暴に言えば、プログラムを物(オブジェクト)のように扱う
と言うことです。
その基本となるのは、受け取って(引数
)、加工して(プログラム
)、送り返す(返値
)ことです。
引数と返値だけわかれば、利用することが出来るので、使う方も作る方も楽なのです。
ちなみ、指向と言うのは、目指しますと言う意味です。
なのでそこまで、拘らなくてもいですが、型は明示することを強くお勧めします。
以上で,FizzBuzzEngineReturnの説明を終わります。
FizzBuzzTest
自分の書いてるコードが正しいかどうか不安になりませんか、そんな時テストしてくれるコードあると安心ですよね。
ですからFizzBuzzが適切に行われているか調べるFizzBuzzTestを作って行きましょう。
何をテストするのか、それは関数の実行結果
です。引数として数字を与えたとき正しい返値(文字列FizzBuzz)が帰ってきているかどうかテストをします。
今回のテスト対象はFizzBuzzEngineReturn
を使います。
テストの具体的方法は,
テスト対象の関数を受け取る。
関数を実行する。
正解のコードと比較する。
テスト結果を返す。
問題になるのは、テスト対象の関数を受け取るとテスト結果を返すです。順番に説明します。
関数を受け取る
関数を受け取るには、引数
に、関数を与えます
。その際には、型を指定する必要があります。
型
とは、コンピュータ
が値を理解するため
に必要なタグ
付です。
型がわからないと、コンピューターは、その値の扱い方がわからず操作できなくなります。
そのために、数字なら数字を示す型であるInt
つけて説明しますし、文字列なら、文字列を示すString
をつけます。
人間も読みやすいので利用しますが、コンピューターに理解させるためにあるものです。
内容は、引数の型と返値の型を教えるためのものです。
そのため、関数の型は、(引数の型) -> 返値の型
になります。つまり,関数の宣言部分がわかれば、型がわかります。
ここで、FizzBuzzEngineReturnの関数宣言を見てみましょう。
func FizzBuzzEngineReturn(nyuryoku : Int) -> String {}
型においては、関数名や仮引数名は必要ありません。全体を囲う必要性もありません。下のようになります
(Int) -> String
これを引数の型として使えば、関数を受け取ることが出来ます。
試しに、数字を受け取って、文字列を返す関数yama
を受け取ってみましょう。
型は(Int) -> String です。
受け取る関数は、kawa
です。
//数字を受け取って、文字列を返す関数
func yama(uketoru:Int) -> String {
let kotae = String(uketoru)
return (kotae + kotae + kotae)
}
//(Int)->String関数を受け取って、数字を与えて777が返って来れば、フィーバーする関数
func kawa(kansuu:(Int)->String) -> String {
var jyoutai = "通常"
if kansuu(7) == "777" {
jyoutai = "フィーバー"
}
return jyoutai
}
実行してみます。
print(yama(uketoru: 6))
let kekka = kawa(kansuu: yama(uketoru:))
print("結果は,\(kekka)です")
実行結果
666
結果は,フィーバーです
関数も型がわかれば、受け取ることが出来ます。
テスト結果を返す。
テスト結果は、真理値(しんりち)
を、利用します。
真理値(Bool)は、真(true)と偽(false)の二つの値を持ちます。
if文などで==
を使って判定したときに、返ってきている値です。
else文について、
if条件式{プログラム}
else
{プログラム}
の形で利用します。else文は、if文が未達成
の場合にかぎり、自分のプログラムを実行します。
if文が成立した場合実行されません。
//真理値の宣言
var singi:Bool = true
//if文で直接判定できる
if singi {
print("trueです。実行されます。")
//else文は、if文が実行されなかったときに実行される。
}else {
print("falseです。実行されません。")
}
//中身を反転させる
singi = false
print("boolを反転させました。もう一度同じコードを実行します。")
if singi == true {
print("trueです。実行されます。")
}else {
print("falseです。実行されません。")
}
print("現在singiに入っているのは\(singi)です。")
実行結果
trueです。実行されます。
boolを反転させました。もう一度同じコードを実行します。
falseです。実行されません。
現在singiに入っているのはfalseです。
FizzBuzzTest
FizzBuzzTestを作って行きます。
今までと比べて多少長いので、行っていることを文章にしておきました。
引数は、関数です。型は(Int)->String
型です。仮引数名はsikenFunc
です。
引数sikenFuncを実行し得られた返値を、変数kaitou
に保存します。
テスト範囲は、1〜30です。
正しいFizzBuzzの提供は、"FizzBuzzEngineReturn"です。
返値は、真理値 (Bool)
です。合格(true)が不合格(false)の2択です。
内部的には、点数をつけて判断しています。
内容がわからないと困るので、適時printを使って、テスト結果を出力します。
//関数宣言 関数名(仮引数名:(仮引数) -> 返値) -> 返値 {プログラム}
// "(仮引数) -> 返値"が型として使われている 今回は (Int)->Stringが仮引数の型
func FizzBuzzTest(sikenFunc : (Int) -> String ) -> Bool {
//テストの点数 点数をつけることで評価する
var tensuu:Int = 0
//全体を通してテストに通ったかを記録する場所。 Boolは、真理値でtrue(真)かfalse(偽)が入る
var sougouKekka:Bool = false
//for定数名in集合{プログラム} テスト範囲は1から30
for i in 1...30 {
//FizzBuzzEngineReturnを使って正しい答えを得る
let seikai = FizzBuzzEngineReturn(nyuryoku: i)
//テスト対象の関数を呼び出して テスト数値(i)を与える 呼び出す際は仮引数名(sikenFunc)で呼び出せる。
let kaitou = sikenFunc(i)
//if文を使ってFizzBuzzEngineReturnと同等の結果かを返しているか判断する。
if seikai == kaitou{
//同等であれば点数をつける `= `より`+`のほうが優先度が高い
//まず=の左側のtensuuと1が足された数字が作られる、その後=が評価されtensuuが上書きされるので点数が1増える
tensuu = tensuu + 1
print("\(i)正解", separator: "", terminator: "")
//elseはif文が真で無かった時に、実行される。この場合は,答えが間違っていたときに実行される
}else{
print("\(i)不正解", separator: "", terminator: "")
}
}
print("評価終了")
//点数評価
if tensuu == 30{
sougouKekka = true
print("満点合格です")
}else if tensuu >= 16 {
print("ある程度正解しています。\(tensuu)点でした。")
}else if tensuu > 0 {
print("正答率が低いです。\(tensuu)点です")
}else if tensuu == 0 {
print("全く回答出来ていません。0点です")
}
//return の後に書かれた変数が返値になる。 ここでは、変数の真理値(Bool)が返る
return sougouKekka
}
実行して行きます。
実行する際に、間違った回答が必要なので、ダメなFizzBuzzを製造しています。
//FizzBuzzTestを呼び出して、試験を受ける関数を渡しています。関数を与えるとき、引数に、値を与える必要性はありません。呼び出したままでOK
var sikenkekka = FizzBuzzTest(sikenFunc: FizzBuzzEngineReturn(nyuryoku:))
print("試験結果は\(sikenkekka)でした。")
//数字を受け取って文字列に変更するだけの間違ったFizzBuzz
func dameFizzBuzz(ukeru:Int) -> String {
//String()は、文字列を作り出してくれる。数字(Int)を入れると、文字列に変更してくれる。
return String(ukeru)
}
sikenkekka = FizzBuzzTest(sikenFunc:dameFizzBuzz(ukeru:))
print("試験結果は\(sikenkekka)でした。")
実行結果
1正解2正解3正解 ~省略~ 29正解30正解評価終了
満点合格です
試験結果はtrueでした。
1正解2正解3不正解 ~省略~ 29正解30不正解評価終了
ある程度正解しています。16点でした。
試験結果はfalseでした。
FizzBuzzをテストすることが出来ました。
これでコードが正しいか間違っているかを画面と睨めっこせずに済みます。
終わりに(Test)
プログラムのテストは、人間には向いていません
。まずテスト量が非常に多いからです。
かつプログラム同士の中間層は、概ね数字です。並べらてもわかりません。
そして、開発中は、様々な変更がかかるので、人間がテストを毎回行えません。
そのために、プログラムを作る前に、テストを先行して作る、テスト駆動開発(TDD)
が主流になっています。
テストを作り、テストに合格するプログラムであればいいと言う行動方針です。
コードが汚くなりそうですが、確実に開発を進めるには最適な方針です。テストを先行させましょう。
以上で、FizzBuzzTestの説明を終わります。
FizzBuzzTestTimer
正確さが確認できたら、次は何かと言われれば、時間
ですよね。
なので時間を測って行きたいと思います。
プログラムで時間を測るのは、時計で時間を測るのと同じです。
開始の時間と終了時間を計測し引き算するだけです。
swiftでは2つのプログラムを利用して測ります。
Date() この関数は、実行すると現在時刻を持ったDate型(でーとかた)を返します。
//timeIntervalSineceは、終了時刻と開始時刻の差を返してくれるSinecは以来
の意味があります
終了時間Date().timeIntervalSinec
(開始時間Date)
.timeIntervalSinece
は今まで使ってこなかった呼び出し方をされています。
より抽象的に書くと
値.関数名(引数)
の形で呼びだされています
これは、メンバ関数
と呼ばれるもので、値の型に書き込まれた関数
を呼び出すものです。
値のメンバー(仲間
)の関数で、メンバ関数です。
値によって型(設計図)が違うので、型によって呼び出せる関数が違います。
多くの型には、専用のメンバ関数が、多く作られており、自身で作る必要なく高度な機能が使えます。
今回で言えば、かかった時間を、返値で返して来れます。
引数のないものや返値の無いメンバ関数もあります。ですが、.
で呼び出す点は変わりません。
基礎的知識に含まれますが、非常にたくさんあり知らないものがあるのは当然
なので、標準ドキュメントやインターネット検索を駆使して、利用するのが基本です。
では、FizzBuzzTestTimerを作って行きましょう。
今後のためも考えて、TimeInterval型を返値にします。
TimeInterval型は、時間差を示す型で、実数(Double)の値が入っています。
時間が記録されていると理解できれば十分です
//実行時間を計測してくれる。TimeIntervalは時間が、実数(小数点を含む数。Double)で入っている。
func FizzBuzzTestTimer(kaitou : (Int) -> String ) -> TimeInterval {
// 計測開始
let kaisi = Date()
//10万回実行する
for i in 1...100000{
//返値は利用しないので`_`で無名を指定
let _ = kaitou(i)
}
//終了時間を計測する let 変数名:型 = 関数()
//()は、関数を実行する意味 引数がないので"()"だけになっている
let owari:Data = Date()
//かかった時間を計測する。終了時間の時間から、呼び出す。
let jikann = owari.timeIntervalSince(kaisi)
print("実行にかかった時間は\(jikann)秒です。")
return jikann
}
実行してみます。
// "_"(アンダ-ライン)は、変数名につける場合、無名の意味
let _ = FizzBuzzTestTimer(kaitou: FizzBuzzEngineReturn(nyuryoku:))
//利用しない返値が有る場合、変数名をつけずに"_"で受け取るのが安全です。
let _ = FizzBuzzTestTimer(kaitou: dameFizzBuzz(ukeru:))
実行結果
実行にかかった時間は0.14515197277069092秒です。
実行にかかった時間は0.15594792366027832秒です
実行結果は毎回違いますし、使っているmacの性能によっても違います。
速さも、ここまで小さいコードだと、前後することもあります。
安定させたい場合、実行回数を増やしてください。
終わりに(Timer)
コードの実行時間は、あまり気にされなくなっています。
なぜなら、機械の性能が上がって差が気にならなくなっているからです。
ですが、コードの質を確認すると言う点では利用価値があります。
そして、遅いコードは、総じてコードが混乱している傾向があります。
時間を計ることで問題点を発見できる場合もあります。
動かなくなったとき解らなくなったとき、時間を計ってみましょう。
以上で、FizzBuzzTestTimerの説明を終わります。