I have this logic that a data file needs to go through a series of operations, but whether or not an operation is needed is controlled by some boolean. Basically the data array goes through the process loop and yields some result.
I wonder if there is an elegant way to code this, hopefully not using vars and lots of if/else statements?
def op1(data): Seq[int]
def op2(data): Seq[int]
def op3(data): Seq[int]
def process(data: Seq[int], b1: boolean, b2: boolean, b3: boolean) = {
//NOT working code, some thing does the following:
var temp: Seq[int] = data
if (b1)
temp = op1(temp)
if(b2)
temp = op2(temp)
if(b3)
temp = op3(temp)
temp
}
In this case I actually prefer your solution, but this is a more functional alternative. I replaced the Seq with Strings, because they are easier to use for examples, but that shouldn't matter.
object SequenceOfOperations {
def op1(data: String) = data + "A" //> op1: (data: String)java.lang.String
def op2(data: String) = data + "B" //> op2: (data: String)java.lang.String
def op3(data: String) = data + "C" //> op3: (data: String)java.lang.String
def process(data: String, b1: Boolean, b2: Boolean, b3: Boolean) = {
val ops =Seq((b1 , op1(_)),(b2 , op2(_)),(b3 , op3(_)))
val filteredOps = ops.filter( _._1).map (_._2)
filteredOps.foldLeft(data)((l : String,o : String => String) => o(l))
} //> process: (data: String, b1: Boolean, b2: Boolean, b3: Boolean)String
process("Z", true, false, true) //> res0: String = ZAC
process("Y", true, true, false) //> res1: String = YAB
process("X", false, false, true) //> res2: String = XC
}
This looks like a typical fold, which you could wrap in the following function:
def maybeDo[A](bs: List[Boolean], fs: List[A => A], x: A) =
(bs zip fs).foldLeft(x) {
case (x, (b, f)) => if (b) f(x) else x
}
Use it for example as follows
> val bs = List(true, false, true)
> val fs: List[Int => Int] = List(_+1, _*3, _+2)
> maybeDo(bs, fs, 10)
res0: Int = 13
In your example it would be something like
val temp = maybeFold(List(b1, b2, b3), List(op1 _, op2 _, op3 _), data)
Assuming you want the process signature not to change...
// continuing from the ops psuedocode
def process(data: Seq[Int], b1:Boolean, b2:Boolean, b3:Boolean): Seq[Int] = {
#tailrec
def loop (acc: Seq[Int], ops: List[Seq[Int] => Seq[Int]]): Seq[Int] = {
ops match {
case List () => acc
case head :: tail => loop (head(acc), tail)
}
}
loop (data, List(( b1,op1 _), (b2,op2 _),(b3,op3 _)).filter{ _._1 }.map{ _._2 })
}
The solution is to pair the op with the correct Boolean, in a List, filter out the pairs with untrue Booleans, map the filtered pairs to a list of ops (basically dropping the boolean part of each surviving pair), Then accumulate your transformations recursively over your input data.
Here's what it results with some simple operators:
import scala.annotations.tailrec
def op1(s: Seq[Int]) = s map {_ + 1}
def op2(s: Seq[Int]) = s map {_ * 2}
def op3(s: Seq[Int]) = s map {_ - 5}
def process(data: Seq[Int], b1:Boolean, b2:Boolean, b3:Boolean): Seq[Int] = {
#tailrec
def loop (acc: Seq[Int], ops: List[Seq[Int] => Seq[Int]]): Seq[Int] = {
ops match {
case List () => acc
case head :: tail => loop (head(acc), tail)
}
}
loop (data, List(( b1,op1 _), (b2,op2 _),(b3,op3 _)).filter{ _._1 }.map{ _._2 })
}
process(Seq(1,2,3), true, false, true) // Seq[Int] = List(-3, -2, -1)
process(Seq(1,2,3), true, false, false) // Seq[Int] = List(2, 3, 4)
process(Seq(1,2,3), false, true, false) // Seq[Int] = List(2, 4, 6)
There are many ways you can achieve this..
One possible way can be..
def op1(data: Seq[Int]) = data.map(_ + 2) //your actual operation here..
def op2(data: Seq[Int]) = data.map(_ - 2) //..returning Seq[Int]
def op3(data: Seq[Int]) = data.map(_ * 2) //
implicit class SeqOps(val data: Seq[Int]) extends AnyVal {
def op(op: Seq[Int] => Seq[Int], b: Boolean) = if (b) op(data) else data
}
And then def process can be written as..
def process(data: Seq[Int], b1: Boolean, b2: Boolean, b3: Boolean) = {
data op (op1, b1) op (op2, b2) op (op3, b3)
}
Related
Why does Scala have both unapply and unapplySeq? What is the difference between the two? When should I prefer one over the other?
Without going into details and simplifying a bit:
For regular parameters apply constructs and unapply de-structures:
object S {
def apply(a: A):S = ... // makes a S from an A
def unapply(s: S): Option[A] = ... // retrieve the A from the S
}
val s = S(a)
s match { case S(a) => a }
For repeated parameters, apply constructs and unapplySeq de-structures:
object M {
def apply(a: A*): M = ......... // makes a M from an As.
def unapplySeq(m: M): Option[Seq[A]] = ... // retrieve the As from the M
}
val m = M(a1, a2, a3)
m match { case M(a1, a2, a3) => ... }
m match { case M(a, as # _*) => ... }
Note that in that second case, repeated parameters are treated like a Seq and the similarity between A* and _*.
So if you want to de-structure something that naturally contains various single values, use unapply. If you want to de-structure something that contains a Seq, use unapplySeq.
Fixed-arity vs. variable arity. Pattern Matching in Scala (pdf) explains it well, with mirroring examples. I also have mirroring examples in this answer.
Briefly:
object Sorted {
def unapply(xs: Seq[Int]) =
if (xs == xs.sortWith(_ < _)) Some(xs) else None
}
object SortedSeq {
def unapplySeq(xs: Seq[Int]) =
if (xs == xs.sortWith(_ < _)) Some(xs) else None
}
scala> List(1,2,3,4) match { case Sorted(xs) => xs }
res0: Seq[Int] = List(1, 2, 3, 4)
scala> List(1,2,3,4) match { case SortedSeq(a, b, c, d) => List(a, b, c, d) }
res1: List[Int] = List(1, 2, 3, 4)
scala> List(1) match { case SortedSeq(a) => a }
res2: Int = 1
So, which do you think is exhibited in the following example?
scala> List(1) match { case List(x) => x }
res3: Int = 1
Some examples:
scala> val fruit = List("apples", "oranges", "pears")
fruit: List[String] = List(apples, oranges, pears)
scala> val List(a, b, c) = fruit
a: String = apples
b: String = oranges
c: String = pears
scala> val List(a, b, _*) = fruit
a: String = apples
b: String = oranges
scala> val List(a, _*) = fruit
a: String = apples
scala> val List(a,rest # _*) = fruit
a: String = apples
rest: Seq[String] = List(oranges, pears)
scala> val a::b::c::Nil = fruit
a: String = apples
b: String = oranges
c: String = pears
scala> val a::b::rest = fruit
a: String = apples
b: String = oranges
rest: List[String] = List(pears)
scala> val a::rest = fruit
a: String = apples
rest: List[String] = List(oranges, pears)
How is it easier to implement function that find and immutable remove the first occurrence in Scala collection:
case class A(a: Int, b: Int)
val s = Seq(A(1,5), A(4,6), A(2,3), A(5,1), A(2,7))
val (s1, r) = s.findAndRemove(_.a == 2)
Result: s1 = Seq(A(1,5), A(4,6), A(5,1), A(2,7)) , r = Some(A(2,3))
It finds the first element that match, and keeps order. It can be improved with List instead of Seq.
case class A(a: Int, b: Int)
val s = Seq(A(1,5), A(4,6), A(2,3), A(5,1), A(2,7))
val (s1, r) = s.findAndRemove(_.a == 2)
println(s1)
println(r)
implicit class SeqOps[T](s:Seq[T]) {
def findAndRemove(f:T => Boolean):(Seq[T], Option[T]) = {
s.foldLeft((Seq.empty[T], Option.empty[T])) {
case ((l, None), elem) => if(f(elem)) (l, Option(elem)) else (l :+ elem, None)
case ((l, x), elem) => (l :+ elem, x)
}
}
}
Yeah, a little late to the party, but I thought I'd throw this in.
Minimum invocations of the predicate.
Works with most popular collection types: Seq, List, Array, Vector. Even Set and Map (but for those the collection has no order to preserve and there's no telling which element the predicate will find first). Doesn't work for Iterator or String.
-
import scala.collection.generic.CanBuildFrom
import scala.language.higherKinds
implicit class CollectionOps[U, C[_]](xs :C[U]) {
def findAndRemove(p :U=>Boolean
)(implicit bf :CanBuildFrom[C[U], U, C[U]]
,ev :C[U] => collection.TraversableLike[U, C[U]]
) :(C[U], Option[U]) = {
val (before, after) = xs.span(!p(_))
before ++ after.drop(1) -> after.headOption
}
}
usage:
case class A(a: Int, b: Int)
val (as, a) = Seq(A(1,5), A(4,6), A(2,3), A(5,1), A(2,7)).findAndRemove(_.a==2)
//as: Seq[A] = List(A(1,5), A(4,6), A(5,1), A(2,7))
//a: Option[A] = Some(A(2,3))
val (cs, c) = Array('g','t','e','y','b','e').findAndRemove(_<'f')
//cs: Array[Char] = Array(g, t, y, b, e)
//c: Option[Char] = Some(e)
val (ns, n) = Stream.from(9).findAndRemove(_ > 10)
//ns: Stream[Int] = Stream(9, ?)
//n: Option[Int] = Some(11)
ns.take(5).toList //List[Int] = List(9, 10, 12, 13, 14)
Try something like this
def findAndRemove(as: Seq[A])(fn: A => Boolean): (Seq[A], Option[A]) = {
val index = as.indexWhere(fn)
if(index == -1) as -> None
else as.patch(index, Nil, 1) -> as.lift(index)
}
val (s1, r) = findAndRemove(s)(_.a == 2)
My version:
def findAndRemove(s:Seq[A])(p:A => Boolean):(Seq[A], Option[A])={
val i = s.indexWhere(p)
if(i > 0){
val (l1, l2) = s.splitAt(i)
(l1++l2.tail, Some(l2.head))
}else{
(s, None)
}
}
When I'm matching value of case classes, such as:
sealed abstract class Op
case class UOp[T, K](f: T => K) extends Op
case class BOp[T, Z, K](f: (T, Z) => K) extends Op
like this:
def f(op: Op): Int =
op match
{
case BOp(g) => g(1,2)
case UOp(g) => g(0)
}
the compiler infers it as
val g: (Nothing, Nothing) => Any
val g: Nothing => Any
Why am I getting Nothing as the type? Is it because of JVM type erasure? Are there elegant ways to match functions against variables?
I came up with this "hackish" solution, maybe there are other ways or cleaner ways to do this still without relying on reflection.
Define a few partial functions which will handle various args:
scala> val f: PartialFunction[Any, String] = { case (x: Int, y: String) => y * x }
f: PartialFunction[Any,String] = <function1>
scala> val g: PartialFunction[Any, String] = { case x: Int => x.toString }
g: PartialFunction[Any,String] = <function1>
scala> def h: PartialFunction[Any, BigDecimal] = { case (a: Int, b: Double, c: Long) => BigDecimal(a) + b + c }
h: PartialFunction[Any,BigDecimal]
scala> val l: List[PartialFunction[Any, Any]] = f :: g :: h :: Nil
l: List[PartialFunction[Any,Any]] = List(<function1>, <function1>, <function1>)
Check which functions can handle different inputs:
scala> l.map(_.isDefinedAt(1))
res0: List[Boolean] = List(false, true, false)
scala> l.map(_.isDefinedAt((1, "one")))
res1: List[Boolean] = List(true, false, false)
Given input find and apply a function:
scala> def applyFunction(input: Any): Option[Any] = {
| l find (_.isDefinedAt(input)) map (_ (input))
| }
applyFunction: (input: Any)Option[Any]
scala> applyFunction(1)
res1: Option[Any] = Some(1)
scala> applyFunction((2, "one"))
res2: Option[Any] = Some(oneone)
scala> applyFunction("one")
res3: Option[Any] = None
scala> applyFunction(1, 1.1, 9L)
res10: Option[Any] = Some(11.1)
This looks quite type unsafe and there must be better ways to do this.
I think magnet pattern should handle this well in more typesafe manner.
Im looking to extended the iterator to create a new method takeWhileInclusive, which will operate like takeWhile but include the last element.
My issue is what is best practice to extend the iterator to return a new iterator which I would like to be lazy evaluated. Coming from a C# background I normal use IEnumerable and use the yield keyword, but such an option doesn't appear to exist in Scala.
for example I could have
List(0,1,2,3,4,5,6,7).iterator.map(complex time consuming algorithm).takeWhileInclusive(_ < 6)
so in this case the takeWhileInclusive would only have resolve the predicate on the values until I get the a result greater than 6, and it will include this first result
so far I have:
object ImplicitIterator {
implicit def extendIterator(i : Iterator[Any]) = new IteratorExtension(i)
}
class IteratorExtension[T <: Any](i : Iterator[T]) {
def takeWhileInclusive(predicate:(T) => Boolean) = ?
}
You can use the span method of Iterator to do this pretty cleanly:
class IteratorExtension[A](i : Iterator[A]) {
def takeWhileInclusive(p: A => Boolean) = {
val (a, b) = i.span(p)
a ++ (if (b.hasNext) Some(b.next) else None)
}
}
object ImplicitIterator {
implicit def extendIterator[A](i : Iterator[A]) = new IteratorExtension(i)
}
import ImplicitIterator._
Now (0 until 10).toIterator.takeWhileInclusive(_ < 4).toList gives List(0, 1, 2, 3, 4), for example.
This is one case where I find the mutable solution superior:
class InclusiveIterator[A](ia: Iterator[A]) {
def takeWhileInclusive(p: A => Boolean) = {
var done = false
val p2 = (a: A) => !done && { if (!p(a)) done=true; true }
ia.takeWhile(p2)
}
}
implicit def iterator_can_include[A](ia: Iterator[A]) = new InclusiveIterator(ia)
The following requires scalaz to get fold on a tuple (A, B)
scala> implicit def Iterator_Is_TWI[A](itr: Iterator[A]) = new {
| def takeWhileIncl(p: A => Boolean)
| = itr span p fold (_ ++ _.toStream.headOption)
| }
Iterator_Is_TWI: [A](itr: Iterator[A])java.lang.Object{def takeWhileIncl(p: A => Boolean): Iterator[A]}
Here it is at work:
scala> List(1, 2, 3, 4, 5).iterator takeWhileIncl (_ < 4)
res0: Iterator[Int] = non-empty iterator
scala> res0.toList
res1: List[Int] = List(1, 2, 3, 4)
You can roll your own fold over a pair like this:
scala> implicit def Pair_Is_Foldable[A, B](pair: (A, B)) = new {
| def fold[C](f: (A, B) => C): C = f.tupled(pair)
| }
Pair_Is_Foldable: [A, B](pair: (A, B))java.lang.Object{def fold[C](f: (A, B) => C): C}
class IteratorExtension[T](i : Iterator[T]) {
def takeWhileInclusive(predicate:(T) => Boolean) = new Iterator[T] {
val it = i
var isLastRead = false
def hasNext = it.hasNext && !isLastRead
def next = {
val res = it.next
isLastRead = !predicate(res)
res
}
}
}
And there's an error in your implicit. Here it is fixed:
object ImplicitIterator {
implicit def extendIterator[T](i : Iterator[T]) = new IteratorExtension(i)
}
scala> List(0,1,2,3,4,5,6,7).toStream.filter (_ < 6).take(2)
res8: scala.collection.immutable.Stream[Int] = Stream(0, ?)
scala> res8.toList
res9: List[Int] = List(0, 1)
After your update:
scala> def timeConsumeDummy (n: Int): Int = {
| println ("Time flies like an arrow ...")
| n }
timeConsumeDummy: (n: Int)Int
scala> List(0,1,2,3,4,5,6,7).toStream.filter (x => timeConsumeDummy (x) < 6)
Time flies like an arrow ...
res14: scala.collection.immutable.Stream[Int] = Stream(0, ?)
scala> res14.take (4).toList
Time flies like an arrow ...
Time flies like an arrow ...
Time flies like an arrow ...
res15: List[Int] = List(0, 1, 2, 3)
timeConsumeDummy is called 4 times. Am I missing something?
Why does Scala have both unapply and unapplySeq? What is the difference between the two? When should I prefer one over the other?
Without going into details and simplifying a bit:
For regular parameters apply constructs and unapply de-structures:
object S {
def apply(a: A):S = ... // makes a S from an A
def unapply(s: S): Option[A] = ... // retrieve the A from the S
}
val s = S(a)
s match { case S(a) => a }
For repeated parameters, apply constructs and unapplySeq de-structures:
object M {
def apply(a: A*): M = ......... // makes a M from an As.
def unapplySeq(m: M): Option[Seq[A]] = ... // retrieve the As from the M
}
val m = M(a1, a2, a3)
m match { case M(a1, a2, a3) => ... }
m match { case M(a, as # _*) => ... }
Note that in that second case, repeated parameters are treated like a Seq and the similarity between A* and _*.
So if you want to de-structure something that naturally contains various single values, use unapply. If you want to de-structure something that contains a Seq, use unapplySeq.
Fixed-arity vs. variable arity. Pattern Matching in Scala (pdf) explains it well, with mirroring examples. I also have mirroring examples in this answer.
Briefly:
object Sorted {
def unapply(xs: Seq[Int]) =
if (xs == xs.sortWith(_ < _)) Some(xs) else None
}
object SortedSeq {
def unapplySeq(xs: Seq[Int]) =
if (xs == xs.sortWith(_ < _)) Some(xs) else None
}
scala> List(1,2,3,4) match { case Sorted(xs) => xs }
res0: Seq[Int] = List(1, 2, 3, 4)
scala> List(1,2,3,4) match { case SortedSeq(a, b, c, d) => List(a, b, c, d) }
res1: List[Int] = List(1, 2, 3, 4)
scala> List(1) match { case SortedSeq(a) => a }
res2: Int = 1
So, which do you think is exhibited in the following example?
scala> List(1) match { case List(x) => x }
res3: Int = 1
Some examples:
scala> val fruit = List("apples", "oranges", "pears")
fruit: List[String] = List(apples, oranges, pears)
scala> val List(a, b, c) = fruit
a: String = apples
b: String = oranges
c: String = pears
scala> val List(a, b, _*) = fruit
a: String = apples
b: String = oranges
scala> val List(a, _*) = fruit
a: String = apples
scala> val List(a,rest # _*) = fruit
a: String = apples
rest: Seq[String] = List(oranges, pears)
scala> val a::b::c::Nil = fruit
a: String = apples
b: String = oranges
c: String = pears
scala> val a::b::rest = fruit
a: String = apples
b: String = oranges
rest: List[String] = List(pears)
scala> val a::rest = fruit
a: String = apples
rest: List[String] = List(oranges, pears)