Scala 中的简单税收计算
Simple tax calculation in Scala
假设我正在编写一个具有两个功能的玩具税收计算器:
// calculate the tax amount for a particular income given tax brackets
def tax(income: BigDecimal, brackets: Seq[Bracket]): BigDecimal = ???
// calculate the min. income for a particular tax rate given tax brackets
def income(taxRate: BigDecimal, brackets: Seq[Bracket]) = ???
我这样定义税级:
case class Bracket(maxIncomeOpt: Option[BigDecimal], rate: BigDecimal)
Bracket(Some(BigDecimal(10)), BigDecimal(10))
表示收入达到 10
的 10%
税级
Bracket(Some(BigDecimal(20)), BigDecimal(20))
表示收入达到 20
的 20%
税级
Bracket(None, BigDecimal(30))
表示任何收入
的税级 30%
现在我正在这样写函数tax
:
def tax(income: BigDecimal, brackets: Seq[Bracket]): BigDecimal = {
val (_, result) = brackets.foldLeft((BigDecimal(0), income)) { case ((result, rest), curr) =>
val taxable = curr.maxIncomeOpt.fold(rest)(_.min(rest))
(result + taxable * curr.rate / 100.0, rest - taxable)
}
result
}
函数 tax
似乎有效,但认为 Seq[Bracket]
不是定义税级的最佳方式。税收括号是不相交的 "back-to-back" 区间的排序序列,末尾有一个开区间。您如何定义税级?
我会建议大致相同,List[Tuple[Double, Double]]
作为原始形式。
其中元组在语义上 (lower_bound, tax_rate_in_range)
。关键区别在于行为的原子单位是税表,而不是单个括号。在此核心数据定义的时间表中,如果数字括号变大,您可以添加优化,并且您可以为原始解决方案保留重要的不变量,例如保持列表按 lower_bound
.
排序
我会将税级定义为分段常数函数:
def taxBracket(i: Int): Float = {
case _ if i < 10 => 0.1
case _ if i < 20 => 0.2
case _ => 0.3
}
易于阅读,易于对任何类型的值进行自定义行为(假设它在某处或任何地方变为线性,实际上,您可以根据需要将各个部分绑在一起),并计算金额 N 的税金就是这个函数在 0 和 N 之间的数值积分。
考虑使用代数数据类型定义括号和PositiveInfinity
模拟开区间的解决方案
abstract class TaxBracket(val from: Double, val to: Double, val rate: Double) {
def tax(income: Double) = {
if (income >= from)
if (to.isPosInfinity) (income - from) * rate
else if (income - to > 0) (to - from) * rate
else (income - (from - 1)) * rate
else
0.0
}
}
case object A extends TaxBracket(0, 12500, 0.0)
case object B extends TaxBracket(12501, 50000, 0.2)
case object C extends TaxBracket(50001, 150000, 0.4)
case object D extends TaxBracket(150001, Double.PositiveInfinity, 0.45)
现在税收计算简化为
def tax(income: Double, bands: List[TaxBracket]): Double =
bands.map(_.tax(income)).sum
例如,使用上面定义的UK tax bands我们得到
tax(60000, List(A, B, C, D)) // res0: Double = 11499.8
可以验证here.
要获得给定有效税率的最低收入,请尝试
def income(etr: Double, bands: List[TaxBracket]): Option[Double] = {
bands.map(b => (b.from, b.to)).find { case (from, to) =>
if (to.isPosInfinity) true
else (tax(to, bands) / to) >= etr
}.map { case (lowerBound, upperBound) => lowerBound }
}
income(0.4, List(A, B, C, D)) // res1: Option[Double] = Some(150001.0)
假设我正在编写一个具有两个功能的玩具税收计算器:
// calculate the tax amount for a particular income given tax brackets
def tax(income: BigDecimal, brackets: Seq[Bracket]): BigDecimal = ???
// calculate the min. income for a particular tax rate given tax brackets
def income(taxRate: BigDecimal, brackets: Seq[Bracket]) = ???
我这样定义税级:
case class Bracket(maxIncomeOpt: Option[BigDecimal], rate: BigDecimal)
Bracket(Some(BigDecimal(10)), BigDecimal(10))
表示收入达到 10
的 10%
税级
Bracket(Some(BigDecimal(20)), BigDecimal(20))
表示收入达到 20
的 20%
税级
Bracket(None, BigDecimal(30))
表示任何收入
30%
现在我正在这样写函数tax
:
def tax(income: BigDecimal, brackets: Seq[Bracket]): BigDecimal = {
val (_, result) = brackets.foldLeft((BigDecimal(0), income)) { case ((result, rest), curr) =>
val taxable = curr.maxIncomeOpt.fold(rest)(_.min(rest))
(result + taxable * curr.rate / 100.0, rest - taxable)
}
result
}
函数 tax
似乎有效,但认为 Seq[Bracket]
不是定义税级的最佳方式。税收括号是不相交的 "back-to-back" 区间的排序序列,末尾有一个开区间。您如何定义税级?
我会建议大致相同,List[Tuple[Double, Double]]
作为原始形式。
其中元组在语义上 (lower_bound, tax_rate_in_range)
。关键区别在于行为的原子单位是税表,而不是单个括号。在此核心数据定义的时间表中,如果数字括号变大,您可以添加优化,并且您可以为原始解决方案保留重要的不变量,例如保持列表按 lower_bound
.
我会将税级定义为分段常数函数:
def taxBracket(i: Int): Float = {
case _ if i < 10 => 0.1
case _ if i < 20 => 0.2
case _ => 0.3
}
易于阅读,易于对任何类型的值进行自定义行为(假设它在某处或任何地方变为线性,实际上,您可以根据需要将各个部分绑在一起),并计算金额 N 的税金就是这个函数在 0 和 N 之间的数值积分。
考虑使用代数数据类型定义括号和PositiveInfinity
模拟开区间的解决方案
abstract class TaxBracket(val from: Double, val to: Double, val rate: Double) {
def tax(income: Double) = {
if (income >= from)
if (to.isPosInfinity) (income - from) * rate
else if (income - to > 0) (to - from) * rate
else (income - (from - 1)) * rate
else
0.0
}
}
case object A extends TaxBracket(0, 12500, 0.0)
case object B extends TaxBracket(12501, 50000, 0.2)
case object C extends TaxBracket(50001, 150000, 0.4)
case object D extends TaxBracket(150001, Double.PositiveInfinity, 0.45)
现在税收计算简化为
def tax(income: Double, bands: List[TaxBracket]): Double =
bands.map(_.tax(income)).sum
例如,使用上面定义的UK tax bands我们得到
tax(60000, List(A, B, C, D)) // res0: Double = 11499.8
可以验证here.
要获得给定有效税率的最低收入,请尝试
def income(etr: Double, bands: List[TaxBracket]): Option[Double] = {
bands.map(b => (b.from, b.to)).find { case (from, to) =>
if (to.isPosInfinity) true
else (tax(to, bands) / to) >= etr
}.map { case (lowerBound, upperBound) => lowerBound }
}
income(0.4, List(A, B, C, D)) // res1: Option[Double] = Some(150001.0)