Scala条件列表构造

pau*_*ies 7 collections scala

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

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

t1 :: t2 :: cond( p, t3 ) :: t4
Run Code Online (Sandbox Code Playgroud)

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

List[T]( t1, t2, t3, t4 )
Run Code Online (Sandbox Code Playgroud)

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

List[T]( t1, t2, t4 )
Run Code Online (Sandbox Code Playgroud)

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

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

这允许以下内容:

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)
Run Code Online (Sandbox Code Playgroud)

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

om-*_*nom 5

如何产生一个列表?

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

然后使用它们:

List(1,2,3) ++ cond(false, 3 ) ++ List(4)
Run Code Online (Sandbox Code Playgroud)


Leo*_*dal 5

尝试根据您的条件创建和过滤新列表:

List[T](t1, t2, t3, t4).filter(p)
Run Code Online (Sandbox Code Playgroud)


Imp*_*ive 5

因此,这无法与标准列表一起使用,因为类型是错误的:::期望类型的元素[A >: T]whereT是列表类型,而您想给它一些可能会或可能不会产生该类型元素的东西.

但是,没有理由不能定义一种方法,它非常乐意接受只能选择性地产生下一个元素的东西。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
Run Code Online (Sandbox Code Playgroud)

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

?::通过使用到具有正确方法的另一个类的隐式转换,可以将方法添加到标准列表中。您提到您使用的是 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)
Run Code Online (Sandbox Code Playgroud)

现在我们可以这样做:

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

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

和这个:

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

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