Scala Conditional List Building

EDIT: I just remembered that anti-aliasing has the same effect as my filter and map

I am using Scala 2.9.2 and would like to build a list based on some conditions.

Consider the following, where cond is a function taking a predicate p and a value of type T (in this case, t3):

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

The behavior I want is as follows. If p is true, this should give:

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

If p evaluates to false, this should give:

 List[T]( t1, t2, t4 ) 

I probably think about it completely wrong, but I try my best to come up with an elegant solution. I could use the parameters everywhere and then filter, but this makes the code more difficult to read:

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

This allows:

 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) 

However, this is not the most elegant solution, since it requires the user to wrap all his non-conditional elements in Some (), and also remember to do the smoothing at the end. Can anyone think of a more elegant solution?

+6
source share
4 answers

How about getting lists?

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

and then using them like:

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

Try creating and filtering a new list using your condition:

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

Thus, this will not work with the standard list, because the types are wrong: :: expects an element of type [A >: T] , where T is the type of the list, while you want to give it something, this may or may not create an element of such a type.

However, there is no reason why you should not define a method that is very happy to accept something that only does not necessarily create the next element. List itself is sealed, so we cannot distribute it directly, but we can quite easily reproduce the behavior that we need:

 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 

We basically recreate the list, but populate it with an overloaded prepend operation that accepts the condition.

You can add the ?:: method to the standard list using implicit conversion to another class that has the correct method. You mention that you are using 2.9.2, which is a shame, because otherwise it is that the implicit value classes are great, but we don't need them, they just simplify:

 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) 

And now we can do it:

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

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

And this:

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

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

+3
source

If you need to know the index to select the correct predicate, you can zipWithIndex reduce pairs of indexes with values, and then use collect instead of filter so that you can remove the index from the result and encode the predicate selection and application in the guard. For instance:.

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

All Articles