递归函数(recursive function),即调用自身的函数,通常有很好的实用价值,用来将复杂的问题分解为简单的情况,反复调用自身处理直到问题解决。
这里以还贷计算器中的等额本息(每月以相等金额还贷款的本金加利息)还款为例子,输出一个表格,例举出每一期还款所需要的偿还的金额,所还的利息以及每月的贷款本金减少额。
先做一个HTML表接受用户输入,代码如下:
<h2>等额本息还款</h2> <form action="<?php $PHP_SELF; ?>" method="post"> <p> 贷款本金:<br /> <input type="text" id="balance" name="balance" size="20" maxlength="40" /> </p> <p> 贷款年限:<br /> <input type="text" id="term" name="term" size="20" maxlength="40" /> </p> <p> 年利率(%):<br /> <input type="text" id="rate" name="rate" size="20" maxlength="40" /> </p> <input type="submit" id="submit" name="submit" value="计算" /> </form>
现在编写实现主要功能的递归函数。需要了解到的是,每月本金减少额=每月还款额-每月的利息。当贷款本金不等于0时,重复执行函数到贷款本金为0时结束。
注:《PHP与MySQL 5程序设计》(第2版)上有这个例子,但书上 if 判断当本金为 0 时执行 exit,将导致不能输出 </table> 结束标志和后面的 HTML代码,此处做了修改,本金为 0 时直接输出</table>。
//Recursive Function(递归函数) /* $paymentNum--还款期数 $balance--贷款本金 $periodicPayment--每月还款额 $paymentInterest--每月的利息 $paymentPrincipal--每月本金减少额 $monthlyInterest--月利率 */ function amortizationTable($paymentNum,$periodicPayment,$balance,$monthlyInterest) { $paymentInterest = round($balance * $monthlyInterest,2); $paymentPrincipal = round($periodicPayment - $paymentInterest,2); $newBalance = round($balance - $paymentPrincipal,2); echo "<tr> <td>$paymentNum</td> <td>\$".number_format($balance,2)."</td> <td>\$".number_format($periodicPayment,2)."</td> <td>\$".number_format($paymentInterest,2)."</td> <td>\$".number_format($paymentPrincipal,2)."</td> </tr>"; //If balance not yet zero,recursively call amortizationTable() if ($newBalance > 0) { $paymentNum++; amortizationTable($paymentNum,$periodicPayment,$newBalance,$monthlyInterest); } else { echo "</table>"; } } //end amortizationTable()
接下来计算每月还款额以及执行递归函数。
Tips:每月还款额=贷款本金×月利率×(1+月利率)^还款总期数/((1+月利率)^还款总期数-1 )
x^y 表示x的y次方
因为将表单和函数执行写在一个文件上,因此再添加一个 if 判断,有表单数据递交时才执行后面的代码
if (isset($_POST['submit'])) { # Loan balance (贷款余额) $balance = $_POST['balance']; # Loan interest(利、息) rate $interestRate = $_POST['rate'] / 100; # Mothly interest rate $monthlyInterest = $interestRate / 12; # Term length of the loan(固定贷款期限),in year. $termLength = $_POST['term']; # Number of payments per year. $paymentsPerYear = 12; # Payment iteration(迭代) $paymentNumber = 1; # Perform preliminary calculations $totalPayments = $termLength * $paymentsPerYear; $intCalc = 1 + $interestRate / $paymentsPerYear; # 1+月利率 $periodicPayment = $balance * pow($intCalc,$totalPayments) * ($intCalc -1 ) / (pow($intCalc,$totalPayments) - 1); $periodicPayment = round($periodicPayment,2); echo "<p>您计算的每月还款额为: ¥".$periodicPayment."</p>"; ?> <table width='50%' align='center' border='1'> <thead> <tr> <th>Payment Number</th><th>Balance</th> <th>Payment</th><th>Interest</th><th>Principal</th> </tr> </thead> <tfoot> <tr> <th colspan="5">Now We are Finished!</th> </tr> </tfoot> <?php # Call recursive function amortizationTable($paymentNumber,$periodicPayment,$balance,$monthlyInterest); } //End if ?>
使用递归策略通常能大幅减少代码量,提高重用性。虽然递归并不总是最好的解决办法,但是可以作为一个有益的补充。
PS:关于 HTML 表格的一些问题
<thead>, <tbody> 和 <tfoot>很少被用到,这是由于浏览器对它们的支持不太好。但是用这些标签定义表格可以在使用被支持的浏览器(如 Firefox)打印时产生一个效果,就是当表格过长超过一个打印页时将每页添加表格页眉和页脚,以方便查看数据。感兴趣的可以运行代码试试。
经过我的测试Windows 7下firefox和ie8,在只使用 <thead> 和 <tbody> 而不使用 <tfoot> 时打印页眉页脚正常。在 Mac 下仅 firefox 正常。至于为什么添加了 <tfoot> 反而出现问题我也不甚理解,请知晓的朋友在此留言。