【问题标题】:How to write a lazy, variable argument version of "orElse"如何编写“orElse”的惰性可变参数版本
【发布时间】:2010-12-22 00:29:07
【问题描述】:

是否可以从Option 编写一个通用的orElse 方法,它接受可变数量的参数?也就是说,而不是:

lazy val o1 = { println("foo"); None }
lazy val o2 = { println("bar"); Some("bar") }
lazy val o3 = { println("baz"); Some("baz") } 
// ...
o1 orElse o2 orElse o3 // orElse ...

你可以使用:

orElse(o1, o2, o3) //, ...

【问题讨论】:

    标签: scala option variadic-functions lazy-evaluation


    【解决方案1】:

    根据The Scala Language Specification(4.6 函数声明和定义),您不能按名称定义可变参数:

    ParamType ::= Type
    | ‘=>’ Type
    | Type ‘*’
    
    scala> def orElse(x : (=> String)*)
    <console>:1: error: no by-name parameter type allowed here
           def orElse(x : (=> String)*)
    

    你可以用函数和隐式类型转换替换惰性参数:

    def orElse[T](x : (()=> Option[T])*) : Option[T] = 
        if(x.isEmpty) None else x.first.apply.orElse(orElse((x drop 1) :_*))
    implicit def anyToFun0[T](t : => T) : (() => T) = () => t
    orElse(o1, o2, o3)
    

    【讨论】:

    • 我在 repl 上试过这个,但不幸的是它似乎强制所有三个参数。
    • 你是对的,隐式类型转换强制评估。我修复了代码。
    【解决方案2】:

    我发现这个问题有点晚了:)。一种可能性是将=&gt; A 与帮助函数一起包装到帮助类中以简化其创建:

    import scala.language.implicitConversions
    
    class Helper[+A](value: => A) extends Function0[A] {
      override def apply(): A = value;
    }
    object Helper {
      def unapply[A](h: Helper[A]): Option[A] = Some(h());
    }
    implicit def toHelper[A](body: => A) = new Helper(body);
    

    提取器不是必需的,它只允许在助手上轻松匹配。然后我们可以写

    def orElse[A](xs: Helper[Option[A]]*): Option[A] =
      xs.collectFirst[A]({
        case Helper(Some(r)) => r;
      })
    
    lazy val o1 = { println("foo"); None }
    lazy val o2 = { println("bar"); Some("bar") }
    lazy val o3 = { println("baz"); Some("baz") }
    
    orElse(o1, o2, o3) //, ...
    

    这只是一个简化的解决方案,一个更现实的解决方案应该是

    def orElse[A](x: Option[A], xs: Helper[Option[A]]*): Option[A]
    

    实现更高效。


    Scalaz 中已经有一个类似于Helper 的类,称为Name,实现Need 确保主体最多被评估一次。所以使用 Scalaz,它可以实现为

    import scala.language.implicitConversions
    import scalaz._
    import scalaz.Scalaz._
    
    implicit def toNeed[A](body: => A): Name[A] = Need(body);
    
    def orElse[A](xs: Name[Option[A]]*): Option[A] =
      xs.collectFirst[A]({
        case Name(Some(r)) => r;
      })
    
    lazy val o1 = { println("foo"); None }
    lazy val o2 = { println("bar"); Some("bar") }
    lazy val o3 = { println("baz"); Some("baz") }
    
    orElse(o1, o2, o3) //, ...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-03
      • 1970-01-01
      • 1970-01-01
      • 2018-03-21
      • 1970-01-01
      • 2011-02-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多