Conditionally invoke member function without a temporary var - scala

I have an expression returning an object, and I want to invoke a method on the resulting object only if a certain boolean condition is true. I want to get the result (whether the object, or the result of invoking the method on the object) in a val.
One way is to use a temporary var, such as in the following example, in which List(3, 1, 2) is the (potentially complicated) expression returning an object, list is the temporary var, and .sorted is the method I want to conditionally invoke:
import scala.util.Random
val condition = Random.nextBoolean
val result = {
var list = List(3, 1, 2);
if (condition) list = list.sorted
list
}
What would be the canonical way to do this, perhaps without using a temporary var?
Note that
if (condition) List(3, 1, 2).sorted else List(3, 1, 2)
is not quite satisfactory because List(3, 1, 2) may in general be a complicated expression that I don't want to repeat.
Here is one method I found that unfortunately involves giving explicit types (and is longer and more complicated than introducing a temporary var as above):
val condition = Random.nextBoolean
val result =
(
if (condition)
{l: List[Int] => l.sorted}
else
identity(_: List[Int])
).apply(List(3, 1, 2))
I suspect there must be a tidier way that I have failed to recognize.
Update: A slightly less ugly method that unfortunately still requires explicit type information:
val condition = Random.nextBoolean
val result = {
l: List[Int] => if (condition) l.sorted else l
}.apply(List(3, 1, 2))

You can use the forward pipe operator:
implicit class PipedObject[A](value: A) {
def |>[B](f: A => B): B = f(value)
}
scala> List(3, 1, 2) |> (xs => if (true) xs.sorted else xs)
res1: List[Int] = List(1, 2, 3)

Starting Scala 2.13, the standard library now provides the chaining operation pipe which can be used to convert/pipe a value with a function of interest, and thus avoids an intermediate variable:
import scala.util.chaining._
List(3, 1, 2).pipe(list => if (condition) list.sorted else list)

Related

Scala flatten depth function confusion

I am a bit confused with documentation about this function/with examples of use provided. Does flatten can happen only once? like
List(List(1, 2), List(3, List(4, 5, 6))) -> List(1, 2, 3, List(4, 5, 6))
Or you somehow specify the depth of flattening, so that it could become a List(1, 2, 3, 4, 5, 6) ?
Because, for example, in JS it seems that function can flat() whatever depth you want into a 1D array. Can Scala flatten do that or it's capable of only elevating once?
I am trying to recreate that function by myself and want to mimic the required behavior and understand the reason why it may work differently.
As mentioned in the comments defining such a method in Scala 2 would not be straightforward to do in a typesafe manner. First of all, recursive types are not supported directly in Scala 2, so you'd have to operate on List[Any] and use runtime reflection to distinguish if the element is a list or an integer.
Recently released Scala 3 has many improvements in its type system, so I wondered that maybe it would be possible to implement such a method there? I tried and I think I was able to achieve usable implementation.
First of all, I wondered if it would be possible to implement recursive union type (a similar thing is possible in typescript):
type ListOr[A] = A | List[ListOr[A]]
unfortunately, such type was raising compiler error:
illegal cyclic type reference: alias ... of type ListOr refers back to the type itself
That was disappointing, but after some digging, I found that I could define such recursive type as:
type ListOr[A] = A match {
case AnyVal => AnyVal | List[ListOr[AnyVal]]
case _ => A | List[ListOr[A]]
}
and it was usable:
val ints: ListOr[Int] = List(List(1), 2, 3, List(List(4, List(5)), 6), 7, 8, List(9))
val strings: ListOr[String] = List(List("A", "B", "C"), List(List("D", List("E")), "F"), "G", "H", List("I"), "J")
so now I needed to just implement the flattening function:
//I needed class tag for A to be able to do a match
def deepFlatten[A: ClassTag](s: ListOr[A]): List[A] =
s match
case a: A => List(a)
case ls: List[_ <: ListOr[A]] => ls.flatMap(deepFlatten(_))
and it seemed to be working correctly:
#main
def main =
val i: List[Int] = deepFlatten[Int](ints) //List(1, 2, 3, 4, 5, 6, 7, 8, 9)
val j: List[String] = deepFlatten[String](strings)//List(A, B, C, D, E, F, G, H, I, J)
Obviously, such implementation could be improved (it's not tail-recursive), but it's doing its job.
Since I'm Scala 3 novice I'm not sure if that's the best implementation, but it's definitely possible to implement such arbitrarily deep flattening functions as type-safe.
Scastie with the solution.
This is an attempt of doing something similar using a simple Tree data structure.
final case class Tree[+A](value: A, children: List[Tree[A]] = Nil)
def flattenTree[A](tree: Tree[A]): List[A] = {
#annotation.tailrec
def loop(remainingNodes: List[Tree[A]], acc: List[A]): List[A] =
remainingNodes match {
case Tree(value, children) :: tail =>
loop(
remainingNodes = children reverse_::: tail,
value :: acc
)
case Nil =>
acc
}
loop(remainingNodes = tree :: Nil, acc = List.empty)
}
Which can be used like this:
val tree = Tree(
value = 1,
children = List(
Tree(value = 2),
Tree(
value = 3,
children = List(
Tree(value = 4),
Tree(value = 5),
Tree(value = 6)
)
)
)
)
val flattenedTree = flattenTree(tree)
println(flattenedTree.mkString("[", ", ", "]"))
Which will produce the following output:
[2, 4, 5, 6, 3, 1]
As you can see is a reversed DFS (whose results are also reversed). If the order doesn't matter this is a straightforward and efficient implementation, if order matters then one can play with the code.
Another approach would be to use a data structure like:
sealed trait ListOr[+A] extends Product with Serializable
final case class NestedList[+A](data: List[ListOr[A]]) extends ListOr[A]
final case class SingleValue[+A](value: A) extends ListOr[A]
You can see the code running here.

Scala: mutability of indexed sequences and what happens when converting to mutable and back

I have run into this scenario several times recently:
a class has an immutable (indexed?) sequence member
a factory member method creates a new instance with the sequence somewhat modified
What's an efficient way to do this?
class A( xs: IndexedSeq[Int] ) {
def another: A = {
val ys = xs.toArray.clone() // !!!
ys(7) = 13
new A(ys)
}
}
I do .toArray so that I can modify this sequence in place and .clone because I'm afraid that if the original xs was an array already, toArray will just return this and I will modify the objects (meant-to-be-immutable) values. However, this obviously makes two copies if xs was not an Array and I would really like to avoid that. Obviously, I could just check its type, but that seems very inelegant and I'm not too sure if I'd have to check against other mutable sequences which can wrap an Array. What do?
scala> val xs: Seq[Int] = Array(1, 2, 3)
ss: Seq[Int] = WrappedArray(1, 2, 3)
scala> val ys = xs.toArray
ys: Array[Int] = Array(1, 2, 3)
scala> ys(1) = 22
scala> ys
res1: Array[Int] = Array(1, 22, 3)
scala> xs
res2: Seq[Int] = WrappedArray(1, 22, 3)
If you don't really need mutability, then the best is just to ask for an immutable sequence; that way you don't need to worry about whether changing the data has side effects.
class A(xs: collection.immutable.IndexedSeq[Int]) {
def another: A = {
val ys = xs.updated(7, 13)
new A(ys)
}
}

Split a collection to those items that satisfy a predicate, and those that don't [duplicate]

How do I split a sequence into two lists by a predicate?
Alternative: I can use filter and filterNot, or write my own method, but isn't there a better more general (built-in) method ?
By using partition method:
scala> List(1,2,3,4).partition(x => x % 2 == 0)
res0: (List[Int], List[Int]) = (List(2, 4),List(1, 3))
Good that partition was the thing you wanted -- there's another method that also uses a predicate to split a list in two: span.
The first one, partition will put all "true" elements in one list, and the others in the second list.
span will put all elements in one list until an element is "false" (in terms of the predicate). From that point forward, it will put the elements in the second list.
scala> Seq(1,2,3,4).span(x => x % 2 == 0)
res0: (Seq[Int], Seq[Int]) = (List(),List(1, 2, 3, 4))
You might want to take a look at scalex.org - it allows you to search the scala standard library for functions by their signature. For example, type the following:
List[A] => (A => Boolean) => (List[A], List[A])
You would see partition.
You can also use foldLeft if you need something a little extra. I just wrote some code like this when partition didn't cut it:
val list:List[Person] = /* get your list */
val (students,teachers) =
list.foldLeft(List.empty[Student],List.empty[Teacher]) {
case ((acc1, acc2), p) => p match {
case s:Student => (s :: acc1, acc2)
case t:Teacher => (acc1, t :: acc2)
}
}
I know I might be late for the party and there are more specific answers, but you could make good use of groupBy
val ret = List(1,2,3,4).groupBy(x => x % 2 == 0)
ret: scala.collection.immutable.Map[Boolean,List[Int]] = Map(false -> List(1, 3), true -> List(2, 4))
ret(true)
res3: List[Int] = List(2, 4)
ret(false)
res4: List[Int] = List(1, 3)
This makes your code a bit more future-proof if you need to change the condition into something non boolean.
If you want to split a list into more than 2 pieces, and ignore the bounds, you could use something like this (modify if you need to search for ints)
def split(list_in: List[String], search: String): List[List[String]] = {
def split_helper(accum: List[List[String]], list_in2: List[String], search: String): List[List[String]] = {
val (h1, h2) = list_in2.span({x: String => x!= search})
val new_accum = accum :+ h1
if (h2.contains(search)) {
return split_helper(new_accum, h2.drop(1), search)
}
else {
return accum
}
}
return split_helper(List(), list_in, search)
}
// TEST
// split(List("a", "b", "c", "d", "c", "a"), {x: String => x != "x"})

How to split a sequence into two pieces by predicate?

How do I split a sequence into two lists by a predicate?
Alternative: I can use filter and filterNot, or write my own method, but isn't there a better more general (built-in) method ?
By using partition method:
scala> List(1,2,3,4).partition(x => x % 2 == 0)
res0: (List[Int], List[Int]) = (List(2, 4),List(1, 3))
Good that partition was the thing you wanted -- there's another method that also uses a predicate to split a list in two: span.
The first one, partition will put all "true" elements in one list, and the others in the second list.
span will put all elements in one list until an element is "false" (in terms of the predicate). From that point forward, it will put the elements in the second list.
scala> Seq(1,2,3,4).span(x => x % 2 == 0)
res0: (Seq[Int], Seq[Int]) = (List(),List(1, 2, 3, 4))
You might want to take a look at scalex.org - it allows you to search the scala standard library for functions by their signature. For example, type the following:
List[A] => (A => Boolean) => (List[A], List[A])
You would see partition.
You can also use foldLeft if you need something a little extra. I just wrote some code like this when partition didn't cut it:
val list:List[Person] = /* get your list */
val (students,teachers) =
list.foldLeft(List.empty[Student],List.empty[Teacher]) {
case ((acc1, acc2), p) => p match {
case s:Student => (s :: acc1, acc2)
case t:Teacher => (acc1, t :: acc2)
}
}
I know I might be late for the party and there are more specific answers, but you could make good use of groupBy
val ret = List(1,2,3,4).groupBy(x => x % 2 == 0)
ret: scala.collection.immutable.Map[Boolean,List[Int]] = Map(false -> List(1, 3), true -> List(2, 4))
ret(true)
res3: List[Int] = List(2, 4)
ret(false)
res4: List[Int] = List(1, 3)
This makes your code a bit more future-proof if you need to change the condition into something non boolean.
If you want to split a list into more than 2 pieces, and ignore the bounds, you could use something like this (modify if you need to search for ints)
def split(list_in: List[String], search: String): List[List[String]] = {
def split_helper(accum: List[List[String]], list_in2: List[String], search: String): List[List[String]] = {
val (h1, h2) = list_in2.span({x: String => x!= search})
val new_accum = accum :+ h1
if (h2.contains(search)) {
return split_helper(new_accum, h2.drop(1), search)
}
else {
return accum
}
}
return split_helper(List(), list_in, search)
}
// TEST
// split(List("a", "b", "c", "d", "c", "a"), {x: String => x != "x"})

Scala wants from me apply method

import java.util.Random
class Kostka {
val rand = new Random(System.currentTimeMillis())
val value: List[Int] = List(rand.nextInt(6+1))
}
object MyRandom {
def Fill[A](n: Int): List[A] = {
if (n<=0) Nil
else {
var lst = List[A]
for (i <- 1 to n){
lst ++= (new Kostka).value
}
return lst
}
}
}
object Gra {
def main(args: Array[String]): Unit = {
println("Podaj liczbe kosci\n")
val kosci: List[Kostka] = MyRandom.Fill[Kostka](10)
// Policzenie wyniku
println("Strzelaj ile razem wypadło\n")
// przyjecie wyniku
// dopisac ile wypadlo czyli wynik
println("Wypadlo: ")
println(kosci.toString)
}
}
And error:
a.scala:10: error: missing arguments for method apply in object List;
follow this method with `_' if you want to treat it as a partially applied function
var lst = List[A]
^
one error found
When I have:
var lst = List[A]()
i got that error:
a.scala:12: error: type mismatch;
found : List[Any]
required: List[A]
lst ++= (new Kostka).value
^
one error found
in your declaration of lst you forgot the parens : lst = List[A]()
In fact, List[A](a,b,c) is a synthatic sugar for List[A].apply(a,b,c), that's why the compiler complained about apply's arguments.
Edit: you can use a ListBuffer instead of your List (in the Fill method, and by the way, the name should be fill(cf http://davetron5000.github.com/scala-style/)) . When you finish the work to do on the buffer, you can call toList, which computes on constant time ;) .
See Aymen's answer for general guidelines. After your update you have the following effect.
Kostka.value has type List[Int]. lst has type List[A]. The result of the append (++) is the least common supertype of List[Int] and List[A] which is List[Any]. But List[Any] is not a subtype of List[A]. That's why you get the type mismatch.
Your Fill method should not be generic in the first place, unless you make Kostka generic, too.
Furthermore, the use of new Kostka combined with the PRNG initialization looks strange as well.
Finally, in Scala 2.8 there is a fill method on the collection companions:
scala> val r = new java.util.Random
r: java.util.Random = java.util.Random#14a616
scala> List.fill(10) {r.nextInt(6+1)}
res4: List[Int] = List(3, 6, 4, 1, 2, 4, 0, 4, 6, 4)
and, unless your dice are 7-sided, you might go for
scala> List.fill(10) {r.nextInt(6) + 1}
res5: List[Int] = List(2, 5, 2, 1, 1, 4, 4, 2, 6, 3)
instead.