【问题标题】:Using lazy evaluation functions in varargs在可变参数中使用惰性求值函数
【发布时间】:2011-02-06 19:32:32
【问题描述】:

下面的方法有什么问题?

def someMethod(funcs: => Option[String]*) = {
 ...
}

【问题讨论】:

  • 形式参数的名称funcs 非常可疑。 By-name 参数虽然是用 thunk 实现的,但并不是明显的函数。
  • 它们也不是通常所说的惰性求值参数。即,如果您多次使用该值,则作为实际参数给出的表达式将被多次评估。这会对性能产生影响,如果该表达式有副作用,它们也会多次出现。
  • 这可能是一个解决方案:stackoverflow.com/a/34373967/2825964

标签: scala variadic-functions lazy-evaluation


【解决方案1】:

显然重复的参数不适用于按名称参数。

【讨论】:

  • 但是,只是为了好玩,试试这个:def f(oi: Option[Int]*) = oi 在 REPL 中。很有趣,嗯?
  • @Rex_Kerr:我猜。你指的是什么有趣的事情?
  • 您可以从方法中返回 Option[Int]*,并且可以使用 (f _) 使其成为函数。但是尝试找到允许您表示类型的语法。所以我不清楚重复参数是否不适用于按名称参数,或者语法是否不允许您表达您想要的类型(或两者)。
  • 在 Scala 2.9.x 之后不正确。来自当前主干的示例 REPL 会话: scala> def f(oi: Option[Int]*) = oi ... f: (oi: Option[Int]*)Seq[Option[Int]]
【解决方案2】:

如果您添加括号,这实际上在 2.7.7 下“有效”:

scala> def someMethod(funcs: => (Option[String]*)) = funcs
someMethod: (=> Option[String]*)Option[String]*

除了它在运行时实际上不起作用:

scala> someMethod(Some("Fish"),None)
    scala.MatchError: Some(Fish)
at scala.runtime.ScalaRunTime$.boxArray(ScalaRunTime.scala:136)
at .someMethod(<console>:4)
at .<init>(<console>:6)
at .<clinit>(<console>) ...

在 2.8 中,它拒绝让您将 X* 指定为任何函数或按名称参数的输出,即使您可以将其指定为输入(这是 r21230,Beta 1 后):

scala> var f: (Option[Int]*) => Int = _
f: (Option[Int]*) => Int = null

scala> var f: (Option[Int]*) => (Option[Int]*) = _
<console>:1: error: no * parameter type allowed here
       var f: (Option[Int]*) => (Option[Int]*) = _

但如果你尝试从一个方法转换,它会起作用:

scala> def m(oi: Option[Int]*) = oi
m: (oi: Option[Int]*)Option[Int]*

scala> var f = (m _)
f: (Option[Int]*) => Option[Int]* = <function1>

scala> f(Some(1),None)
res0: Option[Int]* = WrappedArray(Some(1), None)

所以它并不完全一致。

在任何情况下,您都可以通过传入一个数组然后将该数组发送到需要重复参数的东西来实现您想要的:

scala> def aMethod(os: Option[String]*) { os.foreach(println) }
aMethod: (os: Option[String]*)Unit

scala> def someMethod(funcs: => Array[Option[String]]) { aMethod(funcs:_*) }
someMethod: (funcs: => Array[Option[String]])Unit

scala> someMethod(Array(Some("Hello"),Some("there"),None))
Some(Hello)
Some(there)
None

如果您真的想(轻松)传递一堆懒惰评估的参数,那么您需要一些基础设施,据我所知库中并不存在(这是 2.8 的代码;查看它作为 2.7 中类似策略的灵感):

class Lazy[+T](t: () => T, lt: Lazy[T]) {
  val params: List[() => T] = (if (lt eq null) Nil else t :: lt.params)
  def ~[S >: T](s: => S) = new Lazy[S](s _,this)
}
object Lz extends Lazy[Nothing](null,null) {
  implicit def lazy2params[T : Manifest](lz: Lazy[T]) = lz.params.reverse.toArray
}

现在您可以轻松创建一组延迟评估的参数:

scala> import Lz._  // To get implicit def
import Lz._

scala> def lazyAdder(ff: Array[()=>Int]) = {
     |   println("I'm adding now!");
     |   (0 /: ff){(n,f) => n+f()}
     | }
lazyAdder: (ff: Array[() => Int])Int

scala> def yelp = { println("You evaluated me!"); 5 }
yelp: Int

scala> val a = 3
a: Int = 3

scala> var b = 7
b: Int = 7

scala> lazyAdder( Lz ~ yelp ~ (a+b) )
I'm adding now!
You evaluated me!
res0: Int = 15

scala> val plist = Lz ~ yelp ~ (a+b)
plist: Lazy[Int] = Lazy@1ee1775

scala> b = 1
b: Int = 1

scala> lazyAdder(plist)
I'm adding now!
You evaluated me!
res1: Int = 9

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-09
    • 1970-01-01
    • 2021-01-13
    • 2017-05-12
    • 1970-01-01
    • 2014-04-30
    • 1970-01-01
    相关资源
    最近更新 更多