【问题标题】:Scala immutable lists adding a 'Unit' elementScala 不可变列表添加“单元”元素
【发布时间】:2012-09-06 07:10:03
【问题描述】:

我是 scala 的初学者,我正在寻找最好的/惯用的方式来做我打算在这里做的事情。

这就是我想做的事情

def someMethod(obj:MyObj):List[String] = {
     List[String]() +: 
      {if (somecondition is satisfied) .. " element"} +: 
      { if (another condition) .. " something else " }


}

即方法检查输入参数对象的一些属性并将元素添加到List(即要返回)。如果不满足任何条件,它应该返回一个空列表。

  1. 当然代码不能编译。但不知何故,对我来说 List[T] + Unit 应该返回 List[T] 似乎很直观。为什么我错了?

还有 2. 请告诉我做这个 Scala 的正确方法。如果我正在迭代一个条件列表,我可以使用推导式。

【问题讨论】:

    标签: list scala functional-programming immutability


    【解决方案1】:

    我宁愿使用一元或空列表,而不是过滤单元:

    def someMethod(obj:MyObj): List[String] = {
        Nil ++
        ( if (somecondition is satisfied) List(" element") else Nil ) ++
        ( if (another condition) .. List(" something else ") else Nil ) ++
    }
    

    编辑:关于您在下面的评论,如果您发现上面的代码过于冗长且难以维护,您仍然可以创建一个辅助函数:

    def optElem[T]( condition: Boolean)( value: => T ): Option[T] = if ( condition ) Option( value ) else None
    def someMethod(obj:MyObj): List[String] = {
      Nil ++
      optElem (somecondition is satisfied)( " element") ++
      optElem (another condition)(" something else " )
    }
    

    【讨论】:

    • 是的,更加智能和高效
    • 顺便说一句,这正是我在发布问题之前所做的(我使用了空列表)。我发现这更冗长.. 本质上,我一直在寻找一种方法,让碰巧在未来维护代码的新开发人员发现难度最小
    【解决方案2】:

    if-else 是 Scala 中的表达式。你写的变成了:

    List[String]() +: 
          {if (somecondition is satisfied) {" element"; () } else () }+: 
          { if (another condition) { " something else "; () } else () }
    

    如您所见,常见的分支类型为Unit。

    整个表达式的类型是List[Any],因为这是String 和Unit 的共同超类型。

    实现你想要的一些方法:

    // #1. Ugly.
    def someMethod(obj:MyObj):List[String] = {
      val xs = List[String]() 
      val xs1 = if (somecondition is satisfied) xs :+ " element" else xs
      val xs2 = if (another condition) xs1 :+ " something else" else xs1
      xs2
    }
    
    // #2. Better, but uses mutable builder.
    def someMethod(obj:MyObj):List[String] = {
      val b = List.newBuilder[String]
      if (somecondition is satisfied) b += " element"
      if (another condition) b += " something else"
      b.result
    } 
    
    // #3. Best way IMO, but computationally expensive.
    def someMethod(obj:MyObj):List[String] = {
      List[String]() ++
      Option("element").filter(some condition) ++ // for more correct semantics
                                                  // use LazyOption from Scalaz.
      Option("something else").filter(another condition)
    } 
    

    【讨论】:

    • 如果过滤条件不是元素的函数,那么 someBool option "element" 是 Scalaz 中一个不错的皮条客。
    • #3,我认为这就是 ++: 方法的设计目的:Option("element").filter(some condition) ++: Option("something else").filter(another condition) ++: Nil。这应该更有效,因为它预先设置了一些东西。
    【解决方案3】:

    你可以这样写:

    def someMethod(obj:MyObj):List[String] = {
         List(
          if (somecondition is satisfied) " element", 
          if (another condition) " something else "
         ) collect{ case x: String => x }
    }
    

    假设所有条件都失败:您将首先得到List(Unit,Unit),它将按类型过滤。单元在collect中不满足类型条件,所以结果为空字符串列表

    好消息是,与 filter 方法不同,collect 选择尽可能紧凑的类型(因此您将获得字符串序列——这将从 collect 内部的函数中推断出来)。

    另一种可能性是将.collect { ... } 替换为flatten,但您会丢失类型信息。

    【讨论】:

    • 你能解释一下这段代码是如何工作的吗?特别是我不知道 .. 在 Scala 中做了什么...
    • @ziggystar 这里没有魔法,在 scala 中 .. 什么都不是,我只是从 op sn-p 复制了一些代码,我猜他不想说这只是。我添加了有关工作原理的详细信息,并删除了 .. 以消除混淆。
    【解决方案4】:

    对此的另一种看法是构建一个选项列表,然后将列表展平。

    def f(obj: MyObj): List[String] = {
      List(if (cond1(obj)) Some("element")        else None,
           if (cond2(obj)) Some("something else") else None).flatten
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-02
      • 2018-08-29
      • 2015-12-16
      • 1970-01-01
      • 2013-11-05
      • 2017-09-11
      相关资源
      最近更新 更多