繰り返し構文を使わずに繰り返し処理を行う方法を学んだ話

こんにちは!

 今日はタイトルの話をしたいんですけど、

その前に今日解いた問題を載せていこうと思います。

https://www.codewars.com/kata/growth-of-a-population/ruby

 

 

問題文

 

人口1000人の小さな町があります。

この町は年に2%ずつ定期的に増えており、さらに年に50人の新しい住民が町に住んでいます。

町の人口が1200人以上になるまでに何年かかる必要がありますか?

初期の人口がp0 

定期的に増える割合をpercent

定期的に増える固定の数をaug ※augmentの略

目標人口をpとします。

p0とp0は正の整数 percentは0か整数 augは整数です。

この変数の値はTESTごとに変わって行くので、上記の条件にだけ当てはまるコードじゃダメって事ですね。

 

考えた事

pがp0より同等かそれ以上になるまで処理を繰り替えせばいいので

whileかuntilを使って繰り返せば良いのかな?

whileは条件が真の間、untilは条件が偽の間繰り返されるってイメージなので

untilを使うと良いのかな?

percentは整数で渡されるのでパーセントとして計算するにはpercentを100で割らないといけないですね。
ということで書いたコード (その1)

def nb_year(p0, percent, aug, p)
n = 0
until p0 >= p
p0 += (p0 * percent / 100 ) + aug
n += 1
end
return n

end

これでサンプルテストに通してみて

f:id:novalis222:20190518200023p:plain

 通った!いや〜7kyuも楽勝ですわwwwwwんじゃ提出。

 

・・・あれ?

f:id:novalis222:20190518200145p:plain

 なんで本番ではエラーが・・・?

自分が書いたコードでは3年と出たのに、本来は4年となるのが正しいようです。

ぱっと見コードには間違いが無いように思えるけどわからないんで与えられた条件をpで出力して確認してみます

f:id:novalis222:20190518200803p:plain

これは1000人スタートで年に2%と50人ずつ増えた時、1214人になるときはいつ?って事だからちょっと検証の為に自分で計算してみます。

1年目 人口(p)= 1000+(1000*0.02)+50 =1070

2年目 人口(p)=1070+(1070*0.02)+50 =1141.4

あー!人口(p)って整数のみなのに小数点入ってる!

切り捨ての処理を入れてなかったからエラーが起きたんですね。

もう原因はわかりましたが3年目も計算すると

3年目 人口(p)=1141+(1141*0.02)+50 = 1213.82

多分この小数点以下が切り上げされて1214となって、3年目と返したんだと思うんです。という事で修正して以下のコードで提出したところ正解が出ました。

def nb_year(p0, percent, aug, p)
  n = 0
  until p0  >= p
    p0 +=  (p0 * percent / 100).to_i  +aug
    n += 1
  end
  return n
end

 .to_iで整数に切り捨て処理を行ってます。

 

で、ここから本題(長かった)

他の人はどんなコード書いてるのかな〜と思って探してみたらこんなコードが。

def nb_year(p0, percent, aug, p, year = 0)
return year if p0 >= p
nb_year( ( p0 * ( 1 + ( percent / 100.0 ) ) + aug ).floor, percent, aug, p, year + 1 )
end

このコードみた時びっくりしました。

これforとかwhile、untilなどの繰り返し構文が書かれてないんですよ。

でもこれで正解してるということは繰り返し処理が行われてるんですよね。

なんでだろう?と考えてふとこの記事のこの関数を思い出しました。

 

https://qiita.com/Yametaro/items/11dcd3ec26027ff30214

const fibo = n => (n === 0 || n === 1)? n: fibo(n - 1) + fibo(n - 2);

この関数はフィボナッチ数列を求める物で、関数の内容はさておき関数fiboが

式中に何度も出てきている事に注目してください。

記事内で登場人物がこんな事を言っています。

「関数fiboの中で関数fiboを使うことで、関数fiboは完成するんです」 

 これと同じだ!

 

nb_yearメソッドの中でnb_yearを呼び出す事でループを生み出して、p0がpを上回った時にyearを返して処理を止める。

nb_yearメソッドの中でnb_yearメソッドを使う事で、nb_yearメソッドは完成する。

賢いこと考えるな〜と感心しました。

 

CodeWars、めちゃくちゃ面白いですね。

プログラミングの基礎をなんとな〜く抑えた人にもぜひやってほしいです。