如何在Scala中调用n次方法?

Posted

技术标签:

【中文标题】如何在Scala中调用n次方法?【英文标题】:How to call a method n times in Scala? 【发布时间】:2011-11-23 17:13:17 【问题描述】:

我有一个例子,我想调用一个方法 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 循环,但这不是一个漂亮的功能解决方案。

【问题讨论】:

相关:***.com/questions/2842540/… 您不会获得具有可变队列的功能解决方案。 @Apocalisp:可变队列不是必需的。 【参考方案1】:

不是你的问题的完全答案,但如果你有一个自同态(即转换 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

【讨论】:

猫是这样的吗?【参考方案2】:
import List._

fill(10)  println("hello") 

简单、内置,你会得到一个单位列表作为纪念品!

但是,如果您使用函数式编程,则永远不需要多次调用函数。

【讨论】:

“但如果你在进行函数式编程,你永远不需要多次调用一个函数。” 如何在不调用“add”函数四次的情况下将四个数字添加到列表中? @Jonas Apocalisp 在他的评论中是正确的,如果您正在寻找函数式代码,则不应使用可变数据结构,因为其本质上需要命令式操作。 (这并不是说 mutable 不是最好的解决方案 - 有时它会是。但它不是函数式样式。)对于不可变结构,答案基本上是 a)递归,b)使用 flatMapList.fill(1 to n).map...,或 c) 使用 for 表达式更清晰地执行此操作。 @missingfaktor 我的意思是在给定范围内使用相同的参数,这是没有意义的 @Luigi:确实有道理,因为这是将多个项目添加到队列中的唯一方法,就像本例一样。 @Jonas 具有像改变队列这样的副作用的方法不是一个函数——它是一个命令式过程。您正在询问如何使用函数来做一些必要的事情 - 将项目添加到可变队列中。由于根据定义,函数对于给定的参数总是返回相同的东西,并且没有副作用,这是不可能的。使用不可变队列将有助于您理解这一点。【参考方案3】:

更实用的解决方案是使用带有不可变队列和Queuefilldrop 方法的折叠:

 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)
   
 

或者更好的是,在Event 的子类中捕获您的"supply"/"sale" 区别,并避免尴尬的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)

这并不能直接回答您的问题(如何将函数调用指定次数),但它肯定更惯用。

【讨论】:

【参考方案4】:

使用递归:

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


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

【讨论】:

【参考方案5】:

我认为最简单的解决方案是使用范围:

(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")

此代码将打印“hello”10 次。隐式转换 intTimes 使方法 times 在所有整数上都可用。因此,在您的情况下,它应该如下所示:

event.quantity times queue.enqueue(event.value) 
event.quantity times queue.dequeue() 

【讨论】:

谢谢,但我不能使用第一个建议,因为我的queue.dequeue() 返回了BigDecimal for (i &lt;- 1 to n) /* do something */foreach 略短 :) @Luigi Plinge:我认为 4 个额外的字符并没有太大的区别,但我同意,这对于理解来说看起来更好一点 :) 如何修改times 以将i 作为fn 的参数包含在内?示例:假设我想在您的示例中打印 "hello" + i where i = 1 ... 10 @Kevin 你可以像这样重写times 函数:def times(fn: Int =&gt; Unit) = (1 to i) foreach fn。现在的用法如下所示:10 times (i =&gt; println("hello" + i))。因此,times 现在不再采用名称参数,而是采用 Int =&gt; Unit 函数作为参数。

以上是关于如何在Scala中调用n次方法?的主要内容,如果未能解决你的问题,请参考以下文章

Scala:如何在循环中组合数据帧

如何使用 Mockery 在模拟方法的第 N 次调用中引发异常

应用程序在后台时如何每n分钟调用一次方法?

在 Scala 中调用其他对象的主要方法

如何在函数的 N 次递归调用中中断程序?

等待 N 秒让演员在 Scala 中返回