scala - 我们可以有一组按名称参数的函数吗?

标签 scala

在 Scala 中我们有一个可以写的名字参数

def foo[T](f: => T):T = {
   f // invokes f    
}
// use as:
foo(println("hello"))

我现在想对一组方法做同样的事情,也就是说我想将它们用作:

def foo[T](f:Array[ => T]):T = {     // does not work
   f(0) // invokes f(0)              // does not work
}
foo(println("hi"), println("hello")) // does not work

有什么办法可以做我想做的事吗?我想到的最好的是:

def foo[T](f:() => T *):T = {
   f(0)() // invokes f(0)    
}
// use as:
foo(() => println("hi"), () => println("hello"))

def foo[T](f:Array[() => T]):T = { 
   f(0)() // invokes f(0)    
}
// use as: 
foo(Array(() => println("hi"), () => println("hello")))

编辑:Seth Tisue 在对 this answer 的评论中指出,提议的 SIP-24 不是很有用.

这会产生问题的示例是实用函数 trycatch 的以下代码:

type unitToT[T] = ()=>T
def trycatch[T](list:unitToT[T] *):T = list.size match {
  case i if i > 1 => 
    try list.head()
    catch { case t:Any => trycatch(list.tail: _*) }
  case 1 => list(0)()
  case _ => throw new Exception("call list must be non-empty")
}

此处 trycatch 获取类型为 ()=>T 的方法列表,并依次应用每个元素,直到成功或到达末尾。

现在假设我有两种方法:

def getYahooRate(currencyA:String, currencyB:String):Double = ???

def getGoogleRate(currencyA:String, currencyB:String):Double = ???

将一个单位的 currencyA 转换为 currencyB 并输出 Double

我将 trycatch 用作:

val usdEuroRate = trycatch(() => getYahooRate("USD", "EUR"), 
                           () => getGoogleRate("USD", "EUR"))

我更愿意:

val usdEuroRate = trycatch(getYahooRate("USD", "EUR"), 
                           getGoogleRate("USD", "EUR")) // does not work

在上面的示例中,我希望仅在 getYahooRate("USD", "EUR") 时调用 getGoogleRate("USD", "EUR")抛出异常。这不是 SIP-24 的预期行为。

最佳答案

Here是一个解决方案,尽管与直接按名称调用相比有一些限制:

import scala.util.control.NonFatal


object Main extends App {
  implicit class Attempt[+A](f: => A) {
    def apply(): A = f
  }

  def tryCatch[T](attempts: Attempt[T]*): T = attempts.toList match {
    case a :: b :: rest =>
      try a()
      catch {
        case NonFatal(e) =>
          tryCatch(b :: rest: _*)
      }
    case a :: Nil =>
      a()
    case Nil => throw new Exception("call list must be non-empty")
  }

  def a = println("Hi")
  def b: Unit = sys.error("one")
  def c = println("bye")
  tryCatch(a, b, c)

  def d: Int = sys.error("two")
  def e = { println("here"); 45 }
  def f = println("not here")

  val result = tryCatch(d, e, f)

  println("Result is " + result)
}

限制是:

  1. 使用 block 作为参数是行不通的;只有 block 的最后一个表达式将被包装在 Attempt 中。
  2. 如果表达式的类型为 Nothing(例如,如果 bd 未注释),则转换为 未插入 Attempt,因为 Nothing 是每个类型的子类型,包括 Attempt。大概同样适用于 Null 类型的表达式。

关于scala - 我们可以有一组按名称参数的函数吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32917373/

相关文章:

java - 线程中的异常 "main"java.io.IOException : Job failed

scala 模式匹配 (Try,Try)

scala - 在 IntelliJ 中使用猫库时的错误错误

scala - 如何使用Spark/Scala展平集合?

java - 在 Spark Scala 中处理微秒

scala - 在 Spark API 中,makeRDD 函数和并行化函数有什么区别?

list - Scala 划分为两个以上的列表

multithreading - akka 线程在应用程序空闲时使用 100% CPU

scala - Chisel 和 scala 中的范围

scala - 如何在生产中演化 akka-persistence 事件?