Is there a reason that match written against Seq would work differently on IndexedSeq types than the way it does on LinearSeq types? To me it seems like the code below should do the exact same thing regardless of the input types. Of course it doesn't or I wouldn't be asking.
import collection.immutable.LinearSeq
object vectorMatch {
def main(args: Array[String]) {
doIt(Seq(1,2,3,4,7), Seq(1,4,6,9))
doIt(List(1,2,3,4,7), List(1,4,6,9))
doIt(LinearSeq(1,2,3,4,7), LinearSeq(1,4,6,9))
doIt(IndexedSeq(1,2,3,4,7), IndexedSeq(1,4,6,9))
doIt(Vector(1,2,3,4,7), Vector(1,4,6,9))
}
def doIt(a: Seq[Long], b: Seq[Long]) {
try {
println("OK! " + m(a, b))
}
catch {
case ex: Exception => println("m(%s, %s) failed with %s".format(a, b, ex))
}
}
#annotation.tailrec
def m(a: Seq[Long], b: Seq[Long]): Seq[Long] = {
a match {
case Nil => b
case firstA :: moreA => b match {
case Nil => a
case firstB :: moreB if (firstB < firstA) => m(moreA, b)
case firstB :: moreB if (firstB > firstA) => m(a, moreB)
case firstB :: moreB if (firstB == firstA) => m(moreA, moreB)
case _ => throw new Exception("Got here: a: " + a + " b: " + b)
}
}
}
}
Running this on 2.9.1 final, I get the following output:
OK! List(2, 3, 4, 7)
OK! List(2, 3, 4, 7)
OK! List(2, 3, 4, 7)
m(Vector(1, 2, 3, 4, 7), Vector(1, 4, 6, 9)) failed with scala.MatchError: Vector(1, 2, 3, 4, 7) (of class scala.collection.immutable.Vector)
m(Vector(1, 2, 3, 4, 7), Vector(1, 4, 6, 9)) failed with scala.MatchError: Vector(1, 2, 3, 4, 7) (of class scala.collection.immutable.Vector)
It runs fine for List-y things, but fails for Vector-y things. Am I missing something? Is this a compiler bug?
The scalac -print output for m looks like:
#scala.annotation.tailrec def m(a: Seq, b: Seq): Seq = {
<synthetic> val _$this: object vectorMatch = vectorMatch.this;
_m(_$this,a,b){
<synthetic> val temp6: Seq = a;
if (immutable.this.Nil.==(temp6))
{
b
}
else
if (temp6.$isInstanceOf[scala.collection.immutable.::]())
{
<synthetic> val temp8: scala.collection.immutable.:: = temp6.$asInstanceOf[scala.collection.immutable.::]();
<synthetic> val temp9: Long = scala.Long.unbox(temp8.hd$1());
<synthetic> val temp10: List = temp8.tl$1();
val firstA$1: Long = temp9;
val moreA: List = temp10;
{
<synthetic> val temp1: Seq = b;
if (immutable.this.Nil.==(temp1))
{
a
}
else
if (temp1.$isInstanceOf[scala.collection.immutable.::]())
{
<synthetic> val temp3: scala.collection.immutable.:: = temp1.$asInstanceOf[scala.collection.immutable.::]();
<synthetic> val temp4: Long = scala.Long.unbox(temp3.hd$1());
<synthetic> val temp5: List = temp3.tl$1();
val firstB: Long = temp4;
if (vectorMatch.this.gd1$1(firstB, firstA$1))
body%11(firstB){
_m(vectorMatch.this, moreA, b)
}
else
{
val firstB: Long = temp4;
val moreB: List = temp5;
if (vectorMatch.this.gd2$1(firstB, moreB, firstA$1))
body%21(firstB,moreB){
_m(vectorMatch.this, a, moreB)
}
else
{
val firstB: Long = temp4;
val moreB: List = temp5;
if (vectorMatch.this.gd3$1(firstB, moreB, firstA$1))
body%31(firstB,moreB){
_m(vectorMatch.this, moreA, moreB)
}
else
{
body%41(){
throw new java.lang.Exception("Got here: a: ".+(a).+(" b: ").+(b))
}
}
}
}
}
else
{
body%41()
}
}
}
else
throw new MatchError(temp6)
}
};
You can't use :: for anything other than List. The Vector is failing to match because :: is a case class that extends List, so its unapply method does not work for Vector.
val a :: b = List(1,2,3) // fine
val a :: b = Vector(1,2,3) // error
But you can define your own extractor that works for all sequences:
object +: {
def unapply[T](s: Seq[T]) =
s.headOption.map(head => (head, s.tail))
}
So you can do:
val a +: b = List(1,2,3) // fine
val a +: b = Vector(1,2,3) // fine
Followed pattern match works for List, Seq, LinearSeq, IndexedSeq, Vector.
Vector(1,2) match {
case a +: as => s"$a + $as"
case _ => "empty"
}
In Scala 2.10 object +: was introduced at this commit. Since then, for every SeqLike, you can do:
#annotation.tailrec
def m(a: Seq[Long], b: Seq[Long]): Seq[Long] = {
a match {
case Nil => b
case firstA +: moreA => b match {
case Nil => a
case firstB +: moreB if (firstB < firstA) => m(moreA, b)
case firstB +: moreB if (firstB > firstA) => m(a, moreB)
case firstB +: moreB if (firstB == firstA) => m(moreA, moreB)
case _ => throw new Exception("Got here: a: " + a + " b: " + b)
}
}
}
Code run at Scastie.
Related
I have a ListBuffer of List[String], val tList = ListBuffer[TCount] where TCount is case class TCount(l: List[String], c: Long). I want to find those list l from tList which are not the subset of any other element of tlist and their c value is less than their superset c value. The following program works but I have to use two for loop that makes the code inefficient. Is there any better approach I can use to make the code efficient?
val _arr = tList.toArray
for (i <- 0 to (_arr.length - 1)) {
val il = _arr(i).l.toSet
val ic = _arr(i).c
for (j <- 0 to (_arr.length - 1)) {
val jl = _arr(j).toSet
val jc = _arr(j).c
if (i != j && il.subsetOf(jl) && ic >= jc) {
tList.-=(_arr(i))
}
}
}
Inspired by the set-trie comment:
import scala.collection.SortedMap
class SetTrie[A](val flag: Boolean, val children: SortedMap[A, SetTrie[A]])(implicit val ord: Ordering[A]) {
def insert(xs: List[A]): SetTrie[A] = xs match {
case Nil => new SetTrie(true, children)
case a :: rest => {
val current = children.getOrElse(a, new SetTrie[A](false, SortedMap.empty))
val inserted = current.insert(rest)
new SetTrie(flag, children + (a -> inserted))
}
}
def containsSuperset(xs: List[A], strict: Boolean): Boolean = xs match {
case Nil => !children.isEmpty || (!strict && flag)
case a :: rest => {
children.get(a).map(_.containsSuperset(rest, strict)).getOrElse(false) ||
children.takeWhile(x => ord.lt(x._1, a)).exists(_._2.containsSuperset(xs, false))
}
}
}
def removeSubsets[A : Ordering](xss: List[List[A]]): List[List[A]] = {
val sorted = xss.map(_.sorted)
val setTrie = sorted.foldLeft(new SetTrie[A](false, SortedMap.empty)) { case (st, xs) => st.insert(xs) }
sorted.filterNot(xs => setTrie.containsSuperset(xs, true))
}
Here is a method that relies on a data structure somewhat similar to Set-Trie, but which stores more subsets explicitly. It provides worse compression, but is faster during lookup:
def findMaximal(lists: List[List[String]]): List[List[String]] = {
import collection.mutable.HashMap
class Node(
var isSubset: Boolean = false,
val children: HashMap[String, Node] = HashMap.empty
) {
def insert(xs: List[String], isSubs: Boolean): Unit = if (xs.isEmpty) {
isSubset |= isSubs
} else {
var isSubsSubs = false || isSubs
for (h :: t <- xs.tails) {
children.getOrElseUpdate(h, new Node()).insert(t, isSubsSubs)
isSubsSubs = true
}
}
def isMaximal(xs: List[String]): Boolean = xs match {
case Nil => children.isEmpty && !isSubset
case h :: t => children(h).isMaximal(t)
}
override def toString: String = {
if (children.isEmpty) "#"
else children.flatMap{
case (k,v) => {
if (v.children.isEmpty) List(k)
else (k + ":") :: v.toString.split("\n").map(" " + _).toList
}
}.mkString("\n")
}
}
val listsWithSorted = for (x <- lists) yield (x, x.sorted)
val root = new Node()
for ((x, s) <- listsWithSorted) root.insert(s, false)
// println(root)
for ((x, s) <- listsWithSorted; if root.isMaximal(s)) yield x
}
Note that I'm allowed to do any kind of mutable nonsense inside the body of the method, because the mutable trie data structure never escapes the scope of the method, and can therefore not be inadvertently shared with another thread.
Here is an example with sets of characters (converted to lists of strings):
println(findMaximal(List(
"ab", "abc", "ac", "abd",
"ade", "efd", "adf", "bafd",
"abd", "fda", "dba", "dbe"
).map(_.toList.map(_.toString))))
The output is:
List(
List(a, b, c),
List(a, d, e),
List(e, f, d),
List(b, a, f, d),
List(d, b, e)
)
so indeed, the non-maximal elements ab, ac, abd, adf, fda and dba are eliminated.
And here is what my not-quite-set-trie data structure looks like (child nodes are indented):
e:
f
b:
e
d:
e
f
c
f
d:
e:
f
f
a:
e
b:
d:
f
c
f
d:
e
f
c
f
c
f
Not sure if you can avoid the complexity, but, I guess I'd write like this:
val tList = List(List(1, 2, 3), List(3, 2, 1), List(9, 4, 7), List(3, 5, 6), List(1, 5, 6), List(6, 1, 5))
val tSet = tList.map(_.toSet)
def result = tSet.filterNot { sub => tSet.count(_.subsetOf(sub)) > 1 }
Here's one approach:
Create an indexed Map for identifying the original List elements
Turn Map of List-elements into Map of Sets (with index)
Generate combinations of the Map elements and use a custom filter to capture the elements that are subset of others
Remove those subset elements from the Map of Sets and retrieve remaining elements from the Map of Lists via the index
Sample code:
type TupIntSet = Tuple2[Int, Set[Int]]
def subsetFilter(ls: List[TupIntSet]): List[TupIntSet] =
if ( ls.size != 2 ) List.empty[TupIntSet] else
if ( ls(0)._2 subsetOf ls(1)._2 ) List[TupIntSet]((ls(0)._1, ls(0)._2)) else
if ( ls(1)._2 subsetOf ls(0)._2 ) List[TupIntSet]((ls(1)._1, ls(1)._2)) else
List.empty[TupIntSet]
val tList = List(List(1,2), List(1,2,3), List(3,4,5), List(5,4,3), List(2,3,4), List(6,7))
val listMap = (Stream from 1).zip(tList).toMap
val setMap = listMap.map{ case (i, l) => (i, l.toSet) }
val tSubsets = setMap.toList.combinations(2).toSet.flatMap(subsetFilter)
val resultList = (setMap.toSet -- tSubsets).map(_._1).map(listMap.getOrElse(_, ""))
// resultList: scala.collection.immutable.Set[java.io.Serializable] =
// Set(List(5, 4, 3), List(2, 3, 4), List(6, 7), List(1, 2, 3))
I have found this answer in the forum earlier to a question I have been looking for, How to find a matching element in a list and map it in as an Scala API method?
// Returns Some(66)
List(1, 2, 3) collectFirst { case i if (i * 33 % 2 == 0) => i * 33 }
Now if I am replacing the if clause with a function
//
List(1, 2, 3) collectFirst { case i if ( test(i) > 0) => test(i) }
this works but test() will be evaluated twice. Is there a better solution to apply a function to a list and return a result when a condition is met (not having to go through all elements and not havong to call the function twice (for evaluation and for returning the value.
Something like this, perhaps?
Breaking it into two separate operations lets you save/reuse intermediate results.
List(1,2,3).iterator.map(test).find(_ > 0)
You can wrap your function in a custom extractor:
def test(i: Int): Int = i - 1
object Test {
def unapply(i: Int): Option[Int] = Some(test(i))
}
scala> List(1, 10, 20) collectFirst { case Test(i) if i > 0 => i }
res0: Option[Int] = Some(9)
You can generalize this solution and make a class for that kind of extractors:
case class Extract[T, U](f: T => U) {
def unapply(t: T): Option[U] = Some(f(t))
}
scala> val Test2 = Extract(test)
Test2: Extract[Int,Int] = Extract($$Lambda$1326/1843609566#69c33ea2)
scala> List(1, 10, 20) collectFirst { case Test2(i) if i > 0 => i }
res1: Option[Int] = Some(9)
You can also wrap the guard into the extractor as well:
case class ExtractWithGuard[T, U](f: T => U)(pred: U => Boolean) {
def unapply(t: T): Option[U] = {
val u = f(t)
if (pred(u)) Some(u)
else None
}
}
scala> val Test3 = ExtractWithGuard(test)(_ > 0)
Test3: ExtractWithGuard[Int,Int] = ExtractWithGuard($$Lambda$1327/391731126#591a4d25)
scala> List(1, 10, 20) collectFirst { case Test3(i) => i }
res2: Option[Int] = Some(9)
In the following Scala code I have a sequence of currying functions with different signatures. I want to iterate through them and invoke.
def intCheck(b: Int)(a: Int) = a == b
def stringCheck(b: String)(a: String) = a == b
def doubleCheck(b: Double)(a: Double) = a == b
val list = Seq(intCheck(1) _, stringCheck("a") _, doubleCheck(2.3) _)
for (f <- list) {
//if f is 1st function
f(2) // LINE 1
//if f is 2nd function
f("a") // LINE 2
//if f is 3rd function
f(2.0) // LINE 3
}
But for the LINE 1,2 & 3 I get a compilation error "Type mismatch, expected: String with Int with Double, actual: Int". How can I enforce compiler to avoid type-check here if I am sure about the type here.
i think this is a way...
sealed abstract class AnyChecker(val a:Any,val b:Any) {
def eval = a==b
}
class IntChecker(override val a:Int,override val b:Int) extends AnyChecker(a,b)
class DoubleChecker(override val a:Double,override val b:Double) extends AnyChecker(a,b)
class StringChecker(override val a:String,override val b:String) extends AnyChecker(a,b)
object IntChecker {
def apply(a:Int,b:Int) = new IntChecker(a,b)
def unapply(intChecker: IntChecker) = Some(intChecker.a,intChecker.b)
}
object DoubleChecker {
def apply(a:Double,b:Double) = new DoubleChecker(a,b)
def unapply(doubleChecker: DoubleChecker) = Some(doubleChecker.a,doubleChecker.b)
}
object StringChecker {
def apply(a:String,b:String) = new StringChecker(a,b)
def unapply(stringChecker: StringChecker) = Some(stringChecker.a,stringChecker.b)
}
val list = List(IntChecker(1,3), StringChecker("a","a"), DoubleChecker(2.3,3.1), StringChecker("a","b"), StringChecker("a","c"), StringChecker("x","x"))
for (f <- list) {
f match {
case a:IntChecker => println(s"a:${a.a}, b:${a.b}, ${a.eval}")
case a:DoubleChecker => println(s"a:${a.a}, b:${a.b}, ${a.eval}")
case StringChecker("a","a") => println("equals")
case StringChecker("a","b") => println("not equals")
case StringChecker("a",b) => println( StringChecker("a",b).eval)
case StringChecker(a,b) => println(StringChecker(a,b).eval)
}
}
Please, check this, i am sure this will help you.
http://bplawler.tumblr.com/post/7493366722/scala-programming-unapply-and-case-classes
Also, another simple way is only using AnyChcker....
class AnyChecker[T](val a: T, val b: T) {
def eval = a == b
override def toString = s"Checker($a,$b)"
}
object AnyChecker {
def apply[T](a: T, b: T) = new AnyChecker(a, b)
def unapply[T](doubleChecker: AnyChecker[T]) = Some(doubleChecker.a, doubleChecker.b)
}
val list2 = List(AnyChecker(1, 3), AnyChecker("a", "a"), AnyChecker(2.3, 3.1), AnyChecker("a", "b"), AnyChecker("a", "c"), AnyChecker("x", "x"))
for (checker <- list2) {
checker match {
case AnyChecker(1, 3) => println("ints")
case AnyChecker(2.3, 3.1) => println("doubles")
case AnyChecker("a","a") => println("double a")
case checker1: AnyChecker[Any] =>println(checker1)
}
}
if you only write like a tupes....
val list3 = List((1, "1"), ("a", "a"), (2.3, 3.1), ("a", "b"), ("a", "c"), ("x", "x")).map(element=>AnyChecker(element._1,element._2))
for (checker <- list3) {
checker match {
case AnyChecker(1, 3) => println("ints")
case AnyChecker(2.3, 3.1) => println("doubles")
case AnyChecker("a","a") => println("double a")
case checker1: AnyChecker[Any] =>println(checker1.eval)
}
}
If I were splitting a string, I would be able to do
"123,456,789".split(",")
to get
Seq("123","456","789")
Thinking of a string as a sequence of characters, how could this be generalized to other sequences of objects?
val x = Seq(One(),Two(),Three(),Comma(),Five(),Six(),Comma(),Seven(),Eight(),Nine())
x.split(
number=>{
case _:Comma => true
case _ => false
}
)
split in this case doesn't exist, but it reminds me of span, partition, groupby, but only span seems close, but it doesn't handle leading/ending comma's gracefully.
implicit class SplitSeq[T](seq: Seq[T]){
import scala.collection.mutable.ListBuffer
def split(sep: T): Seq[Seq[T]] = {
val buffer = ListBuffer(ListBuffer.empty[T])
seq.foreach {
case `sep` => buffer += ListBuffer.empty
case elem => buffer.last += elem
}; buffer.filter(_.nonEmpty)
}
}
It can be then used like x.split(Comma()).
The following is 'a' solution, not the most elegant -
def split[A](x: Seq[A], edge: A => Boolean): Seq[Seq[A]] = {
val init = (Seq[Seq[A]](), Seq[A]())
val (result, last) = x.foldLeft(init) { (cum, n) =>
val (total, prev) = cum
if (edge(n)) {
(total :+ prev, Seq.empty)
} else {
(total, prev :+ n)
}
}
result :+ last
}
Example result -
scala> split(Seq(1,2,3,0,4,5,0,6,7), (_:Int) == 0)
res53: Seq[Seq[Int]] = List(List(1, 2, 3), List(4, 5), List(6, 7))
This is how I've solved it in the past, but I suspect there is a better / more elegant way.
def break[A](xs:Seq[A], p:A => Boolean): (Seq[A], Seq[A]) = {
if (p(xs.head)) {
xs.span(p)
}
else {
xs.span(a => !p(a))
}
}
I have a collection of ints that repeat themselves in a pattern:
val repeatingSequence = List(1,2,3,1,2,3,4,1,2,1,2,3,4,5)
I'd like to section that List up when the pattern repeats itself; in this case, when the sequence goes back to 1:
val groupedBySequence = List(List(1,2,3), List(1,2,3,4), List(1,2), List(1,2,3,4,5))
Notice that I'm grouping when the sequence jumps back to 1, but that the sequence can be of arbitrary length. My colleague and I have solved it by adding an additional method called 'groupWhen'
class IteratorW[A](itr: Iterator[A]) {
def groupWhen(fn: A => Boolean): Iterator[Seq[A]] = {
val bitr = itr.buffered
new Iterator[Seq[A]] {
override def hasNext = bitr.hasNext
override def next = {
val xs = collection.mutable.ListBuffer(bitr.next)
while (bitr.hasNext && !fn(bitr.head)) xs += bitr.next
xs.toSeq
}
}
}
}
implicit def ToIteratorW[A](itr: Iterator[A]): IteratorW[A] = new IteratorW(itr)
> repeatingSequence.iterator.groupWhen(_ == 1).toSeq
List(List(1,2,3), List(1,2,3,4), List(1,2), List(1,2,3,4,5))
However, we both feel like there's a more elegant solution lurking in the collection library.
Given an iterator itr, this will do the trick:
val head = iter.next()
val out = (
Iterator continually {iter takeWhile (_ != head)}
takeWhile {!_.isEmpty}
map {head :: _.toList}
).toList
As well all know, fold can do everything... ;)
val rs = List(1,2,3,1,2,3,4,1,2,1,2,3,4,5)
val res = (rs++List(1)).foldLeft((List[List[Int]](),List[Int]()))((acc,e) => acc match {
case (res,subl) => {
if (e == 1) ((subl.reverse)::res,1::Nil) else (res, e::subl)
}
})
println(res._1.reverse.tail)
Please regard this as an entry for the obfuscated Scala contest rather than as a real answer.
Here's a not-exactly-elegant solution I bashed out using span:
def groupWhen[A](fn: A => Boolean)(xs: List[A]): List[List[A]] = {
xs.span(!fn(_)) match {
case (Nil, Nil) => Nil
case (Nil, z::zs) => groupWhen(fn)(zs) match {
case ys::yss => (z::ys) :: yss
case Nil => List(List(z))
}
case (ys, zs) => ys :: groupWhen(fn)(zs)
}
}
scala> groupWhen[Int](_==1)(List(1,2,3,1,2,3,4,1,2,3,4,5))
res39: List[List[Int]] = List(List(1, 2, 3), List(1, 2, 3, 4), List(1, 2, 3, 4, 5))
scala> groupWhen[Int](_==1)(List(5,4,3,2,1,2,3,1,2,3,4,1,2,3,4,5))
res40: List[List[Int]] = List(List(5, 4, 3, 2), List(1, 2, 3), List(1, 2, 3, 4), List(1, 2, 3, 4, 5))
import scala.collection.mutable.ListBuffer
import scala.collection.breakOut
val repeatingSequence = List(1,2,3,1,2,3,4,1,2,1,2,3,4,5)
val groupedBySequence: List[List[Int]] = repeatingSequence.foldLeft(ListBuffer[ListBuffer[Int]]()) {
case (acc, 1) => acc += ListBuffer(1)
case (acc, n) => acc.last += n; acc
}.map(_.toList)(breakOut)