I implemented simple unapplySeq that extracts elements of filepath to sequence:
object PathSequence {
def unapplySeq(path: Path): Seq[String] =
if (path == null || path.getFileName == null) Seq.empty
else path.getFileName.toString +: unapplySeq(path.getParent)
}
After runing it like this:
val path = Path.of("C:/Users/WIN10/IdeaProjects/LearningSandbox/src/worksheet/resources/AddTwoNumbers.java")
val PathSequence(first, second, third, _*) = path
I get an error: Star pattern must correspond with varargs or unapplySeq.
unapplySeq is implemented correctly here, the result is List(AddTwoNumbers.java, resources, worksheet, src, LearningSandbox, IdeaProjects, WIN10, Users)
How to set first, second and third variables to first three elements of result sequence?
Apparently the problem is that unapplySeq also has to return an Option[Seq[A]].
However, if you are sure that you will always match you can return a Some[Seq[A]].
Check this:
object PathSequence {
#annotation.tailrec
private def loop(remaining: Option[Path], acc: List[String]): List[String] =
remaining match {
case Some(path) =>
Option(path.getFileName) match {
case Some(file) =>
loop(remaining = Option(path.getParent), file.toString :: acc)
case None =>
List.empty
}
case None =>
acc.reverse
}
def unapplySeq(path: Path): Some[List[String]] =
Some(loop(remaining = Option(path), acc = List.empty))
}
Which you can see how it working here.
Related
I have a function like this:
def foo(item: Item) : Option[Int] = Try{
// Some code that can blow up
}.toOption
I have a list of items and I want to map through them, and apply the above function. But if the function above blows up and returns a None then the result of the map should be an error:
items.map{
item => foo(item)
}
Is map not the right thing to do here? It doesn't seem like it
This is called traverse. If you can use cats, it is as simple as:
import cats.implicits._
val result = items.traverse(foo) // Option[List[Int]]
If not, you can implement it pretty easily:
def traverse[A, B](data: List[A])(f: A => Option[B]): Option[List[B]] = {
#annotation.tailrec
def loop(remaining: List[A], acc: List[B]): Option[List[B]] =
remaining match {
case a :: as => f(a) match {
case Some(b) => loop(remaining = as, b :: acc)
case None => None
}
case Nil => Some(acc.reverse)
}
loop(remaining = data, acc = List.empty)
}
Which you can use like:
val result = traverse(items)(foo) // Option[List[Int]]
(however, I would suggest you to use cats instead, since it is more general).
For out-of-the-box short-circuiting, consider wrapping the list-mapping with Try like so
def fooUnsafe(item: Item): Int = // might throw
Try(items.map(fooUnsafe))
If you wish to keep def foo(item: Item) : Option[Int] signature then the following will also short-circuit
Try(list.map(v => foo(v).get))
I want to update a sequence in Scala, I have this code :
def update(userId: Long): Either[String, Int] = {
Logins.findByUserId(userId) map {
logins: Login => update(login.id,
Seq(NamedParameter("random_date", "prefix-" + logins.randomDate)))
} match {
case sequence : Seq(Nil, Int) => sequence.foldLeft(Right(_) + Right(_))
case _ => Left("error.logins.update")
}
}
Where findByUserId returns a Seq[Logins] and update returns Either[String, Int] where Int is the number of updated rows,
and String would be the description of the error.
What I want to achieve is to return an String if while updating the list an error happenes or an Int with the total number of updated rows.
The code is not working, I think I should do something different in the match, I don't know how I can check if every element in the Seq of Eithers is a Right value.
If you are open to using Scalaz or Cats you can use traverse. An example using Scalaz :
import scalaz.std.either._
import scalaz.std.list._
import scalaz.syntax.traverse._
val logins = Seq(1, 2, 3)
val updateRight: Int => Either[String, Int] = Right(_)
val updateLeft: Int => Either[String, Int] = _ => Left("kaboom")
logins.toList.traverseU(updateLeft).map(_.sum) // Left(kaboom)
logins.toList.traverseU(updateRight).map(_.sum) // Right(6)
Traversing over the logins gives us a Either[String, List[Int]], if we get the sum of the List we get the wanted Either[String, Int].
We use toList because there is no Traverse instance for Seq.
traverse is a combination of map and sequence.
We use traverseU instead of traverse because it infers some of the types for us (otherwise we should have introduced a type alias or a type lambda).
Because we imported scalaz.std.either._ we can use map directly without using a right projection (.right.map).
You shouldn't really use a fold if you want to exit early. A better solution would be to recursively iterate over the list, updating and counting successes, then return the error when you encounter one.
Here's a little example function that shows the technique. You would probably want to modify this to do the update on each login instead of just counting.
val noErrors = List[Either[String,Int]](Right(10), Right(12))
val hasError = List[Either[String,Int]](Right(10), Left("oops"), Right(12))
def checkList(l: List[Either[String,Int]], goodCount: Int): Either[String, Int] = {
l match {
case Left(err) :: xs =>
Left(err)
case Right(_) :: xs =>
checkList(xs, (goodCount + 1))
case Nil =>
Right(goodCount)
}
}
val r1 = checkList(noErrors, 0)
val r2 = checkList(hasError, 0)
// r1: Either[String,Int] = Right(2)
// r2: Either[String,Int] = Left(oops)
You want to stop as soon as an update fails, don't you?
That means that you want to be doing your matching inside the map, not outside. Try is actually a more suitable construct for this purpose, than Either. Something like this, perhaps:
def update(userId: Long): Either[String, Int] = Try {
Logins.findByUserId(userId) map { login =>
update(login.id, whatever) match {
case Right(x) => x
case Left(s) => throw new Exception(s)
}
}.sum
}
.map { n => Right(n) }
.recover { case ex => Left(ex.getMessage) }
BTW, a not-too-widely-known fact about scala is that putting a return statement inside a lambda, actually returns from the enclosing method. So, another, somewhat shorter way to write this would be like this:
def update(userId: Long): Either[String, Int] =
Logins.findByUserId(userId).foldLeft(Right(0)) { (sum,login) =>
update(login.id, whatever) match {
case Right(x) => Right(sum.right + x)
case error#Left(s) => return error
}
}
Also, why in the world does findUserById return a sequence???
I have an Iterator[Record] which is ordered on record.id this way:
record.id=1
record.id=1
...
record.id=1
record.id=2
record.id=2
..
record.id=2
Records of a specific ID could occur a large number of times, so I want to write a function that takes this iterator as input, and returns an Iterator[Iterator[Record]] output in a lazy manner.
I was able to come up with the following, but it fails on StackOverflowError after 500K records or so:
def groupByIter[T, B](iterO: Iterator[T])(func: T => B): Iterator[Iterator[T]] = new Iterator[Iterator[T]] {
var iter = iterO
def hasNext = iter.hasNext
def next() = {
val first = iter.next()
val firstValue = func(first)
val (i1, i2) = iter.span(el => func(el) == firstValue)
iter = i2
Iterator(first) ++ i1
}
}
What am I doing wrong?
Trouble here is that each Iterator.span call makes another stacked closure for trailing iterator, and without any trampolining it's very easy to overflow.
Actually I dont think there is an implementation, which is not memoizing elements of prefix iterator, since followed iterator could be accessed earlier than prefix is drain out.
Even in .span implementation there is a Queue to memoize elements in the Leading definition.
So easiest implementation that I could imagine is the following via Stream.
implicit class StreamChopOps[T](xs: Stream[T]) {
def chopBy[U](f: T => U): Stream[Stream[T]] = xs match {
case x #:: _ =>
def eq(e: T) = f(e) == f(x)
xs.takeWhile(eq) #:: xs.dropWhile(eq).chopBy(f)
case _ => Stream.empty
}
}
Although it could be not the most performant as it memoize a lot. But with proper iterating of that, GC should handle problem of excess intermediate streams.
You could use it as myIterator.toStream.chopBy(f)
Simple check validates that following code can run without SO
Iterator.fill(10000000)(Iterator(1,1,2)).flatten //1,1,2,1,1,2,...
.toStream.chopBy(identity) //(1,1),(2),(1,1),(2),...
.map(xs => xs.sum * xs.size).sum //60000000
Inspired by chopBy implemented by #Odomontois here is a chopBy I implemented for Iterator. Of course each bulk should fit allocated memory. It doesn't looks very elegant but it seems to work :)
implicit class IteratorChopOps[A](toChopIter: Iterator[A]) {
def chopBy[U](f: A => U) = new Iterator[Traversable[A]] {
var next_el: Option[A] = None
#tailrec
private def accum(acc: List[A]): List[A] = {
next_el = None
val new_acc = hasNext match {
case true =>
val next = toChopIter.next()
acc match {
case Nil =>
acc :+ next
case _ MatchTail t if (f(t) == f(next)) =>
acc :+ next
case _ =>
next_el = Some(next)
acc
}
case false =>
next_el = None
return acc
}
next_el match{
case Some(_) =>
new_acc
case None => accum(new_acc)
}
}
def hasNext = {
toChopIter.hasNext || next_el.isDefined
}
def next: Traversable[A] = accum(next_el.toList)
}
}
And here is an extractor for matching tail:
object MatchTail {
def unapply[A] (l: Traversable[A]) = Some( (l.init, l.last) )
}
I would like to convert a List[Box[T]] into a Box[List[T]].
I know that I could use foldRight, but I can't find an elegant way into doing so.
EDIT I would like to keep the properties of Box, that is to say, if there is any failure, return a Box with this failure.
If you only want to collect the "Full" values
I'm not sure why you'd want a Box[List[T]], because the empty list should suffice to signal the lack of any values. I'll assume that's good enough for you.
I don't have a copy of Lift handy, but I know that Box is inspired by Option and has a flatMap method, so:
Long form:
for {
box <- list
value <- box
} yield value
Shorter form:
list.flatMap(identity)
Shortest form:
list.flatten
If you want to collect the failures too:
Here's the mapSplit function I use for this kind of problem. You can easily adapt it to use Box instead of Either:
/**
* Splits the input list into a list of B's and a list of C's, depending on which type of value the mapper function returns.
*/
def mapSplit[A,B,C](in: Traversable[A])(mapper: (A) ⇒ Either[B,C]): (Seq[B], Seq[C]) = {
#tailrec
def mapSplit0(in: Traversable[A], bs: Vector[B], cs: Vector[C]): (Seq[B], Seq[C]) = {
in match {
case t if t.nonEmpty ⇒
val a = t.head
val as = t.tail
mapper(a) match {
case Left(b) ⇒ mapSplit0(as, bs :+ b, cs )
case Right(c) ⇒ mapSplit0(as, bs, cs :+ c)
}
case t ⇒
(bs, cs)
}
}
mapSplit0(in, Vector[B](), Vector[C]())
}
And when I just want to split something that's already a Seq[Either[A,B]], I use this:
/**
* Splits a List[Either[A,B]] into a List[A] from the lefts and a List[B] from the rights.
* A degenerate form of {#link #mapSplit}.
*/
def splitEither[A,B](in: Traversable[Either[A,B]]): (Seq[A], Seq[B]) = mapSplit(in)(identity)
It's really easier to do this with a tail-recursive function than with a fold:
final def flip[T](l: List[Option[T]], found: List[T] = Nil): Option[List[T]] = l match {
case Nil => if (found.isEmpty) None else Some(found.reverse)
case None :: rest => None
case Some(x) :: rest => flip(rest, x :: found)
}
This works as expected:
scala> flip(List(Some(3),Some(5),Some(2)))
res3: Option[List[Int]] = Some(List(3, 5, 2))
scala> flip(List(Some(1),None,Some(-1)))
res4: Option[List[Int]] = None
One can also do this with Iterator.iterate, but it's more awkward and slower, so I would avoid that approach in this case.
(See also my answer in the question 4e6 linked to.)
I tried to create an unapply method to use in pattern matching, and I tried to make it return something different than Option, however, Eclipse shows that as an error. Is it a rule that unapply must return an Option[T] ?
EDIT: here's the code I'm trying to use. I switched the code from the previous section so that unapply returns a Boolean
import java.util.regex._
object NumberMatcher {
def apply(x:String):Boolean = {
val pat = Pattern.compile("\\d+")
val matcher = pat.matcher(x)
return matcher.find
}
def unapply(x:String):Boolean = {
val pat = Pattern.compile("\\d+")
val matcher = pat.matcher(x)
return matcher.find
}
}
object x {
def main(args : Array[String]) : Unit = {
val strings = List("geo12","neo493","leo")
for(val str <- strings) {
str match {
case NumberMatcher(group) => println(group)
case _ => println ("no")
}
}
}
}
Eclipse says wrong number of arguments for object NumberMatcher. Why is that?
If you want to return something with unapply, return it inside Some. Returning Boolean just tests if the match can be made or not.
Here is how a pattern matching is translated:
str match {
case NumberMatcher(group) => println(group)
case _ => println("no")
}
Assuming NumberMatcher returns Option[...], it will do:
val r1 = NumberMatcher.unapply(str)
if (r1 != None) {
val group = r1.get
println(group)
} else {
println("no")
}
If NumberMatcher returns Boolean, then you can have it receive something. In that case, this is what happens:
str match {
case NumberMatcher() => println("yes")
case _ => println("no")
}
becomes
val r1 = NumberMatcher.unapply(str)
if (r1) {
println("yes")
} else {
println("no")
}
Note that this is a very superficial explanation. Case matches can test for constants, have additional guard conditions, alternatives, use unapply recursively, use unapplySeq, etc. Here I'm only showing very basic usage to address a specific question. I strongly advise searching for a fuller explanation of pattern matching.
Take a look at this example again.
I quote
The return type of an unapply should be chosen as follows:
* If it is just a test, return a Boolean. For instance case even()
* If it returns a single sub-value of type T, return a Option[T]
* If you want to return several sub-values T1,...,Tn, group them in an optional tuple Option[(T1,...,Tn)].
When you defined unapply to return a Boolean, you were indicating that the pattern doesn't have any wildcards to match (or bind). So the case statement for this unextractor should be case NumberMatcher => println(str), and giving it a variable to fill is wrong.
Alternatively, to make case NumberMatcher(group) => println(group) you need to define unapply() to return Option[String]
package com.tutorial.extracters
object ExtracterwithBooleanReturnType extends App {
import java.util.regex._
object NumberMatcher {
def apply(x: String*) = {
x
}
def unapply(x: String): Option[Boolean] = {
val pat = Pattern.compile("\\d+")
val matcher = pat.matcher(x)
return Some(matcher.find)
}
}
val strings = NumberMatcher("geo12", "neo493", "leo")
for (str <- strings) {
str match {
case NumberMatcher(group) => println(group)
case _ => println("no")
}
}
}
we can achieve this with above code as well