Как вызвать метод n раз в Scala? - PullRequest
       28

Как вызвать метод n раз в Scala?

18 голосов
/ 23 сентября 2011

У меня есть случай, когда я хочу вызвать метод 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, но это не очень хорошее функциональное решение.

Ответы [ 5 ]

35 голосов
/ 23 сентября 2011

Самое простое решение - использовать диапазон, я думаю:

(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 доступным для всех целых.Так что в вашем случае это должно выглядеть так:

event.quantity times queue.enqueue(event.value) 
event.quantity times queue.dequeue() 
12 голосов
/ 23 сентября 2011

Не совсем ответ на ваш вопрос, но если у вас был эндоморфизм (то есть преобразование 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
10 голосов
/ 23 сентября 2011

Более функциональным решением было бы использование сгиба с неизменяемой очередью и 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)
}}

Это не дает прямого ответа на ваш вопрос (как вызывать функцию указанное количество раз),но это определенно более идиоматично.

8 голосов
/ 24 сентября 2011
import List._

fill(10) { println("hello") }

Простой, встроенный, и вы получите список единиц в качестве сувенира!

Но вам никогда не придется вызывать функцию несколько раз, если вы программируете функционально.

4 голосов
/ 23 сентября 2011

с рекурсией:

def repeat(n: Int)(f: => Unit) { 
  if (n > 0) {
    f
    repeat(n-1)(f)
  }
}

repeat(event.quantity) { queue.enqueue(event.value) }
...