Scala条件列表构造

时间:2013-03-22 10:58:35

标签: scala collections

我正在使用Scala 2.9.2,并希望根据某些条件构建一个列表。

考虑以下内容,其中cond是一个函数,它采用谓词p和类型为T的值(在本例中为t3):

t1 :: t2 :: cond( p, t3 ) :: t4

我想要的行为如下。如果p为真,则应该给出:

List[T]( t1, t2, t3, t4 )

如果p的计算结果为false,则应该给出:

List[T]( t1, t2, t4 )

我可能完全以错误的方式考虑这个问题,但我很难想出一个优雅的解决方案。我可以在任何地方参与选项然后过滤,但这会使代码更难阅读:

def cond[T]( p : => Boolean, v : T ) : Option[T] =
{
    p match
    {
        case true => Some( v )
        case false => None
    }
}

这允许以下内容:

scala> ( Some( 1 ) :: Some( 2 ) :: cond( true, 3 ) :: Some( 4 ) :: Nil ).flatten
res4: List[Int] = List(1, 2, 3, 4)

scala> ( Some( 1 ) :: Some( 2 ) :: cond( false, 3 ) :: Some( 4 ) :: Nil ).flatten
res5: List[Int] = List(1, 2, 4)

但是,它不是最优雅的解决方案,因为它要求用户将所有非条件元素包装在Some()中,并且还要记住在最后进行展平。谁能想到更优雅的解决方案?

5 个答案:

答案 0 :(得分:5)

如何产生一个列表?

@inline def cond[T]( p : => Boolean, v : T ) : List[T] = if(p) v::Nil else Nil

然后将它们用作:

List(1,2,3) ++ cond(false, 3 ) ++ List(4)

答案 1 :(得分:4)

尝试使用您的条件创建和过滤新列表:

List[T](t1, t2, t3, t4).filter(p)

答案 2 :(得分:4)

所以,这对标准列表无效,因为类型错误:::期望类型为[A >: T]的元素,其中T是列表类型,而您想要给它一些可能会或可能不会产生该类型元素的东西。

然而,没有理由不应该定义一个非常乐意采取只能选择性地产生下一个元素的方法。 List本身是封闭的,所以我们不能直接扩展它,但我们可以很容易地复制我们需要的行为:

trait QList[+T] {

  def hd : T
  def tl : QList[T]

  def ?::[A >: T](hd : A) : QList[A] = new ?::[A](hd, this)
  def ?::[A >: T](hd : => Option[A]) : QList[A] = hd match {
    case Some(a) => ?::(a)
    case None => this
  }
}

case object QNil extends QList[Nothing] {
  def hd = throw new Exception("Head of empty list")
  def tl = throw new Exception("Tail of empty list")
}
case class ?::[+T](val hd: T, val tl : QList[T]) extends QList[T]

def cond[T]( p : => Boolean, v : T ) : Option[T] =
{
  p match
  {
    case true => Some( v )
    case false => None
  }
}

val truelist = 1 ?:: 2 ?:: 3 ?:: cond(true, 4) ?:: 5 ?:: QNil
val falselist = 1 ?:: 2 ?:: 3 ?:: cond(false, 4) ?:: 5 ?:: QNil

我们基本上会重新创建列表,但是会使用一个带有条件的重载前置操作来填充它。

通过使用隐式转换到具有正确方法的另一个类,可以将?::方法添加到标准列表中。你提到你正在使用2.9.2,这是一种耻辱,因为否则这就是隐式价值类非常适合的东西,但我们不需要它们,它们只会让事情变得更容易:

class ConditionallyAppend[T](tl : List[T]) {
  def ?::[A >: T](hd : A) : List[A] = hd :: tl
  def ?::[A >: T](x : => Option[A]) : List[A] = x match {
    case Some(a) => a :: tl
    case None => tl
  }
}

implicit def ListToConditionallyAppend[A](x : List[A]) = new ConditionallyAppend(x)

现在我们可以这样做:

  

val truelist = 1?:: 2?:: 3?:: cond(true,4)?:: 5?:: Nil

     

truelist:列出[Any] = List(1,2,3,4,5)

而且:

  

val falselist = 1?:: 2?:: 3?:: cond(false,4)?:: 5?:: Nil

     

falselist:列出[Any] = List(1,2,3,5)

答案 3 :(得分:0)

如果您需要知道索引以选择正确的谓词,您可以zipWithIndex将索引与值配对,然后使用collect代替filter以允许您删除索引从结果中编码谓词选择和应用程序在一个警卫。 E.g:

List(1, 4, 9, 16, 25).zipWithIndex.collect { case (n, i) if (n + i) % 3 <= 1 => n }
res0: List[Int] = List(1, 16)

答案 4 :(得分:0)

作为一种选择,您可以考虑切换为在Scala.collection.mutable包中使用ListBuffer

val listBuffer = new ListBuffer[<item_type>]
if(<item1_cond>) listBuffer += <item1>
if(<item2_cond>) listBuffer += <item2>

请注意,这仍然是指向可变集合的val(不可变引用)