修正日: 07/06/20
練習問題4
回答編
解答編です。ちなみに2006年は4/16、2007年は4/8、2008年は3/23が正解のようですが、正しく作れましたか?私の作った解答例をこちらに置いておきます。
まずは計算方法ですが、私はここを参考にしました。「イースター 計算」でググると検索結果のトップに出てきます。計算方法も単純で利用しやすいですね。
ウィンドウのデザインはこんな感じ。
次、ソースコード。ちょっと長いですが。
- (IBAction)calculate:(id)sender
{
int anYear=[yearField intValue];
int aValue1, aValue2;
int aMonth, aDate;
//step(1)
aValue1=anYear%19;
//step(2)
aValue1=225-aValue1*11;
//step(3)
if(aValue1>=51)
{
while(aValue1>=51)
aValue1-=30;
}
//step(4)
if(aValue1>48)
aValue1-=1;
//step(5)
//->step(7)で(4)の計算結果を使うので
// aValue1の値を残しておく!
aValue2=anYear+anYear/4+aValue1+1;
//step(6)
aValue2=aValue2%7;
//step(7)
aValue1=aValue1+7-aValue2;
//step(8)
if(aValue1>31)
{
aMonth=4;
aDate=aValue1-31;
}
else
{
aMonth=3;
aDate=aValue1;
}
[monthField setIntValue:aMonth];
[dateField setIntValue:aDate];
}
やっていることは今までに勉強した四則演算だけなので、一行一行注意深く見ていけば問題無く理解できるはずです。参考サイトの対応する手順の番号をコメントで挿入しておきましたので、照らし合わせて確認してください。
これでめでたく完成!といきたいところですが、ちょっと待ってください。「Become An Xcoder」の中で、関数の構造化と再利用について学んだことを思い出してください。イースターの日付を計算する方法は、もしかしたらこの先のプログラミングで使うことがあるかもしれません。では、このメソッドはどうでしょうか?年をNSTextFieldから取り出して、計算結果の日付をNSTextFieldにセットしています。これでは使える場面が限られてしまいます。またテキストフィールドへのアウトレットの名前もyearField、monthField、dateFieldと固定されていて不便です。
そこで日付の計算部分だけを関数に独立させてみましょう。引き数として年を与えると、計算結果として月と日を取り出せる関数です。
ですが・・・怪訝に思った人もいるかもしれませんが、関数は実は返り値を一種類しか返すことができません。この場合、年を与えて計算しても、月か日のどちらかしか返り値を返せないのです。ではどうしたらいいでしょうか。
月を返す関数と日を返す関数を別々に作る、というのも一つのアイデアですが、同じことを二度繰り返すため、プログラミング的にも、ソフトウェアの実行速度的にも良くありません。「Become An Xcoder」11章で学んだポインタを使うことで解決できます。まずは11章を読み返してみてください・・・
つまり関数に月と日の変数のポインタを渡してあげて、関数の計算結果をポインタが指している変数に代入してあげるのです。具体的にはこんな感じです。
void calculateEaster(int pYear, int *pMonth, int *pDate)
{
int aValue1, aValue2;
//step(1)
aValue1=pYear%19;
//・・・中略・・・
//step(8)
if(aValue1>31)
{
*pMonth=4; //a
*pDate=aValue1-31; //b
}
else
{
*pMonth=3;
*pDate=aValue1;
}
}
途中の計算ルーチンはそのままです。重要なのは関数の引き数とa、bの三カ所です。
- まず引き数としてintではなくintへのポインタ、int *を指定します。月、日とも同じです。
- そしてaではもらったポインタではなく、ポインタが指している変数(ポインタの指している変数を取り出すには「*」を付けます。思い出しましたか?)に計算結果を代入してあげます。
- bでも同様に、ポインタが指し示す変数に計算結果を代入します。
以上をふまえて、西暦0年から西暦3000年までのイースターの日付をリストアップするメソッドを作ってみました。関数calculateEaster()の呼び出し方に注意してください。変数のポインタを渡したいときは「&」を変数の前につけるのでした。覚えておいてください。
- (IBAction)listupEaster:(id)sender
{
int anYear, aMonth, aDate;
for( anYear=0; anYear<=3000; ++anYear )
{
calculateEaster(anYear, &aMonth, &aDate);
NSLog(@"%4d/%2d/%2d", anYear, aMonth, aDate);
}
}
void calculateEaster(int pYear, int *pMonth, int *pDate)
{
//略
}
サンプルコードはこちら。実際に実行してどうなるか試してみてください。NSLogの結果は「実行ログ」で見るんですよね。覚えていますか?
計算結果が正しいのかどうかまでは知りません(^^;
ちなみにサンプルの中では、引き数にはp(parameter=引き数の意味)を、関数内部で使う変数にはaを、変数の頭につけて、変数がそれぞれどういう役割なのかわかるようにしています。こういう「自分ルール」を作ることで(このやり方は割合一般的なようですが)、コードをもっと読みやすくすることができます。