Как вызвать метод n раз в Scala?
У меня есть случай, когда я хочу вызвать метод n раз, где n - Int. Есть ли хороший способ сделать это "функциональным" способом в Scala?
case class Event(name: String, quantity: Int, value: Option[BigDecimal])
// a list of events
val lst = List(
Event("supply", 3, Some(new java.math.BigDecimal("39.00"))),
Event("sale", 1, None),
Event("supply", 1, Some(new java.math.BigDecimal("41.00")))
)
// a mutable queue
val queue = new scala.collection.mutable.Queue[BigDecimal]
lst.map { event =>
event.name match {
case "supply" => // call queue.enqueue(event.value) event.quantity times
case "sale" => // call queue.dequeue() event.quantity times
}
}
Я думаю, что закрытие является хорошим решением для этого, но я не могу заставить его работать. Я также пытался использовать for-loop, но это не красивое функциональное решение.
Ответы
Ответ 1
Более функциональным решением будет использование сложения с неизменяемой очередью и методы Queue
fill
и drop
:
val queue = lst.foldLeft(Queue.empty[Option[BigDecimal]]) { (q, e) =>
e.name match {
case "supply" => q ++ Queue.fill(e.quantity)(e.value)
case "sale" => q.drop(e.quantity)
}
}
Или даже лучше, сделайте выделение "supply"
/"sale"
в подклассах Event
и избегайте неудобного бизнеса Option[BigDecimal]
:
sealed trait Event { def quantity: Int }
case class Supply(quantity: Int, value: BigDecimal) extends Event
case class Sale(quantity: Int) extends Event
val lst = List(
Supply(3, BigDecimal("39.00")),
Sale(1),
Supply(1, BigDecimal("41.00"))
)
val queue = lst.foldLeft(Queue.empty[BigDecimal]) { (q, e) => e match {
case Sale(quantity) => q.drop(quantity)
case Supply(quantity, value) => q ++ Queue.fill(quantity)(value)
}}
Это не дает прямого ответа на ваш вопрос (как вызвать функцию определенное количество раз), но это определенно более идиоматично.
Ответ 2
Самое простое решение - использовать диапазон, я думаю:
(1 to n) foreach (x => /* do something */)
Но вы также можете создать эту небольшую вспомогательную функцию:
implicit def intTimes(i: Int) = new {
def times(fn: => Unit) = (1 to i) foreach (x => fn)
}
10 times println("hello")
этот код будет печатать "привет" 10 раз. Неявное преобразование intTimes
делает метод times
доступным для всех int. Поэтому в вашем случае это должно выглядеть так:
event.quantity times queue.enqueue(event.value)
event.quantity times queue.dequeue()
Ответ 3
Не совсем ответ на ваш вопрос, но если у вас есть эндоморфизм (т.е. преобразование A => A
), то с помощью scalaz вы можете использовать естественный моноид для Endo[A]
N times func apply target
Итак, чтобы:
scala> import scalaz._; import Scalaz._
import scalaz._
import Scalaz._
scala> Endo((_:Int) * 2).multiply(5)
res3: scalaz.Endo[Int] = Endo(<function1>)
scala> res1(3)
res4: Int = 96
Ответ 4
import List._
fill(10) { println("hello") }
Простой, встроенный, и вы получаете список единиц в качестве сувенира!
Но вам не нужно будет вызывать функцию несколько раз, если вы программируете функционально.
Ответ 5
С рекурсией:
def repeat(n: Int)(f: => Unit) {
if (n > 0) {
f
repeat(n-1)(f)
}
}
repeat(event.quantity) { queue.enqueue(event.value) }