fork download
  1. object Main extends App {
  2. //Есть продукты A, B, C, D, E, F, G, H, I, J, K, L, M. Каждый продукт стоит определенную сумму.
  3. //
  4. //Есть набор правил расчета итоговой суммы:
  5. //
  6. //Если одновременно выбраны А и B, то их суммарная стоимость уменьшается на 10% (для каждой пары А и B)
  7. //Если одновременно выбраны D и E, то их суммарная стоимость уменьшается на 5% (для каждой пары D и E)
  8. //Если одновременно выбраны E,F,G, то их суммарная стоимость уменьшается на 5% (для каждой тройки E,F,G)
  9. //Если одновременно выбраны А и один из [K,L,M], то стоимость выбранного продукта уменьшается на 5%
  10. //Если пользователь выбрал одновременно 3 продукта, он получает скидку 5% от суммы заказа
  11. //Если пользователь выбрал одновременно 4 продукта, он получает скидку 10% от суммы заказа
  12. //Если пользователь выбрал одновременно 5 продуктов, он получает скидку 20% от суммы заказа
  13. //
  14. //Описанные скидки 5,6,7 не суммируются, применяется только одна из них
  15. //Продукты A и C не участвуют в скидках 5,6,7
  16. //Каждый товар может участвовать только в одной скидке. Скидки применяются последовательно в порядке описанном выше.
  17.  
  18. case class Product(name: String, price: Double)
  19.  
  20. case class DiscountResult(discountAction: Double => Double, usedProducts: Seq[Product])
  21.  
  22. trait Discount{
  23. def calc(order: Seq[Product]): DiscountResult
  24. }
  25.  
  26. //@tailrec // may be optimized
  27. def repeat[T](value: Seq[T], count: Int): Seq[T] = {
  28. if(count == 0) Seq()
  29. else if(count == 1) value
  30. else value ++ repeat(value, count - 1)
  31. }
  32.  
  33. class ConcreteProductsDiscount(products: Set[Product], discount: Double) extends Discount{
  34. def calc(order: Seq[Product]) = {
  35. val bunchCount = products.map(p => order.count(_ == p)).min
  36. val d = bunchCount * products.toSeq.map(_.price).sum * discount
  37. DiscountResult({ _ - d }, repeat(products.toSeq, bunchCount))
  38. }
  39. }
  40.  
  41. class OrderCountDiscount(apply: Int => Boolean, ignoredProducts: Set[Product], discount: Double) extends Discount{
  42. def calc(order: Seq[Product]) = {
  43. val count = order.count(!ignoredProducts.contains(_))
  44. val multiplier = if (apply(count)) 1 - discount else 1
  45. DiscountResult({ _ * multiplier }, order)
  46. }
  47. }
  48.  
  49. def calculate(order: Seq[Product], discounts: Seq[Discount]): Double = {
  50. val initPrice = order.map(_.price).sum
  51. val foldedOrder = discounts.foldLeft((initPrice, order)) {
  52. (acc, discount) => discount.calc(acc._2) match {
  53. case DiscountResult(f, used) => (f(acc._1), acc._2 diff used)
  54. }
  55. }
  56. foldedOrder._1
  57. }
  58.  
  59. val A = Product("A", 100)
  60. val B = Product("B", 100)
  61. val C = Product("C", 100)
  62. val D = Product("D", 100)
  63. val E = Product("E", 100)
  64. val F = Product("F", 100)
  65. val G = Product("G", 100)
  66. val H = Product("H", 100)
  67. val I = Product("I", 100)
  68. val J = Product("J", 100)
  69. val K = Product("K", 100)
  70. val L = Product("L", 100)
  71. val M = Product("M", 100)
  72.  
  73. val discounts = Seq(new ConcreteProductsDiscount(Set(A, B), 0.1),
  74. new ConcreteProductsDiscount(Set(D, E), 0.05),
  75. new ConcreteProductsDiscount(Set(E, F, G), 0.05),
  76. new OrderCountDiscount({ _ == 3}, Set(A, C), 0.05),
  77. new OrderCountDiscount({ _ == 4}, Set(A, C), 0.1),
  78. new OrderCountDiscount({ _ >= 5}, Set(A, C), 0.2))
  79.  
  80. println(calculate(Seq(A, B, A, B, K, K, K, E, F, G), discounts))
  81. }
Success #stdin #stdout 0.42s 382208KB
stdin
Standard input is empty
stdout
897.75