11

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

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

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

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

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

如果 p 评估为假,这应该给出:

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() 中,并记住在最后进行展平。谁能想到一个更优雅的解决方案?

4

5 回答 5

10

产生一个列表怎么样?

@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)
于 2013-03-22T11:06:14.720 回答
5

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

List[T](t1, t2, t3, t4).filter(p)
于 2013-03-22T11:07:40.663 回答
5

因此,这不可能与标准列表一起使用,因为类型是错误的:::需要一个类型为列表类型的元素[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

真实列表:列表 [任何] = 列表(1、2、3、4、5)

和这个:

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

假名单:List[Any] = List(1, 2, 3, 5)

于 2013-03-22T16:16:38.990 回答
1

作为一个选项,您可以考虑切换到使用 Scala.collection.mutable 包中的 ListBuffer

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

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

于 2018-07-05T03:09:04.013 回答
0

如果您需要知道索引以选择正确的谓词,您可以zipWithIndex将索引与值配对,然后使用collect而不是filter允许您从结果中删除索引并将谓词选择和应用程序编码在保护中。例如:

List(1, 4, 9, 16, 25).zipWithIndex.collect { case (n, i) if (n + i) % 3 <= 1 => n }
res0: List[Int] = List(1, 16)
于 2013-03-22T14:06:23.023 回答