[Haskell] CodeWars|Growth of a Population

http://www.codewars.com/kata/growth-of-a-population/haskell

问题描述

某个小镇第一年的初始人口为p0 = 1000。人口规模总是每年增长2%,然后每年又还有50个外来人口迁入这个城镇。请问多少年这个小镇的人口会达到(即大于等于p = 1200呢?

第一年结束时这个小镇的人口将达到:
1000+1000×0.02+50=1070

第二年结束时这个小镇的人口将达到:
1070+1070×0.02+50=1141 (人口数目肯定是个整数,而且是向下取整)

第三年结束时这个小镇的人口将达到:
1141+1141×0.02+50=1213

故需要3年小镇人口就超过1200人了。

推广这个问题:
给定正整数p0表示小镇的初始人口,非负浮点数percent表示小镇人口的增长速率(如果增长速率为2%,那么percent=2),正整数aug表示小镇每年的外来人口数目,正整数p表示小镇最后需要达到或超过的人口数目。

你需要实现函数nbYear,这个函数的返回值表示小镇人口达到或超过p需要多少年。

样例

nbYear 1500 5 100 5000 == 15
nbYear 1500000 2.5 10000 2000000 == 10

题解

这题类似A+B Problem #2,但是这题可以考验我们的代码质量。
本题实现方法挺多的,我看别人的实现方法都好高端。。看来是我的FP能力不足。。
首先我们定义:

nbYear :: Int -> Double -> Int -> Int -> Int

以下为最无脑的方法,当然是直接循环(尾递归)啦:

nbYear p0 percent aug p
    | p0 >= p = 0
    | otherwise = nbYear np percent aug p + 1
    where np = p0 + floor (fromIntegral p0 * percent / 100) + aug

显然小镇每年人口组成的序列(高数里叫序列,数分里叫数列。。)是一个无限列表,而且存在递推公式,所以我们可以利用iterate函数推出这个列表(列表虽然是无限的,但是Haskell的lazy特性使得并不会真正推出整个列表),然后takeWhile取出没有达到p的元素,再取元素个数即可。(感觉这才是正常的FP思路?)

nbYear p0 percent aug p = length $ takeWhile (<p) $ iterate population p0
    where population p0 = p0 + floor (fromIntegral p0 * percent / 100) + aug

其中,popluation还可以改写,利用函数的复合运算构造population函数,其中运算顺序可以想成从右到左。这样我们得到了一段比较精炼的代码(不知道有没有更好的写法,但这么写,代码可读性降低了很多?):

nbYear p0 percent aug p = length $ takeWhile (<p) $ iterate ((+ aug) . (+ p0) . floor . (* (percent / 100)) . fromIntegral) p0

小结

Codewars有很多针对Haskell/FP的好题,本题虽然是最简单的题,却也向我们展示了Functional Programming的威力。本题中我们从传统的顺序式编程的思路出发,最后提炼出一段精炼的FP代码。既然学习Haskell语言,我们就要适应并养成FP的思维,不仅能学好Haskell,还可以扩展视野,使我们在写C/C++/Java/C#等代码时写出更健壮的程序(比如可持久化数据结构系列→_→)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值