I started to learn Scala language and I have a question. How do you think, is it a right way to swap first and last x elements in List in a functional style?
def swap(l: List[Any], x: Int) = {
val l1 = l.take(x)
val l2 = l.slice(x, l.length - x)
val l3 = l.takeRight(x)
l3 ::: l2 ::: l1
}
It doesn't matter what happened if x would be more than half list length. I'm interested to find out algorithm.
This code is correct and is in a reasonable functional style. It's not the most efficient since it has to traverse the list four times to create the pieces l1 through l3. Also, you probably want to preserve the type that the list contains, so a slight improvement is:
def swap[A](l: List[A], x: Int) = {
val (l1,rest) = l.splitAt(x)
val (l2,l3) = rest.splitAt(rest.length-x)
l3 ::: l2 ::: l1
}
I tried it and it worked fine:
scala> swap(List(1, 2, 3, 4, 5),2)
res0: List[Any] = List(4, 5, 3, 1, 2)
Is there anything wrong with the code you provided yourself?
Related
here is my code:
val l1 = List(1,2,3)
val l2 = List(4,5,6)
val l1l2_1 = l1 ::: l2
val l1l2_2 = l1.:::(l2)
val l1l2_3 = l2.:::(l1)
println(s"l1 = $l1 \nl2 = $l2 \nl1 ::: l2 = $l1l2_1 \nl1.:::(l2) = $l1l2_2 \nl2.:::(l1) = $l1l2_3 }")
and here is output:
l1 = List(1, 2, 3)
l2 = List(4, 5, 6)
l1 ::: l2 = List(1, 2, 3, 4, 5, 6)
l1.:::(l2) = List(4, 5, 6, 1, 2, 3)
l2.:::(l1) = List(1, 2, 3, 4, 5, 6) }
Why l1 ::: l2 is not equal with l1.:::(l2)?
Operators that end in : are treated differently from other ones. When used with infix notation, they reverse their arguments - the method is invoked on the argument on the right and the argument on the left is used as the parameter. So l1 ::: l2 is the same as l2.:::(l1).
Reason is two rules in Scala:
1. : operator/method is right associative.
2. Operator/Methods associativity is determined by the last character in the operator name.
For example:
Any method with arbitrary name, but ending with a :
def myMethod:(a: Any)
is treated like a :, so x myMethod: y is right associative too, and executed like y.myMethod:(x)
Read more in Programming in Scala book: Basic Types and Operations
How to split a List of elements into lists with at most N items?
ex: Given a list with 7 elements, create groups of 4, leaving the last group possibly with less elements.
split(List(1,2,3,4,5,6,"seven"),4)
=> List(List(1,2,3,4), List(5,6,"seven"))
I think you're looking for grouped. It returns an iterator, but you can convert the result to a list,
scala> List(1,2,3,4,5,6,"seven").grouped(4).toList
res0: List[List[Any]] = List(List(1, 2, 3, 4), List(5, 6, seven))
There is much easier way to do the task using sliding method.
It works this way:
val numbers = List(1, 2, 3, 4, 5, 6 ,7)
Lets say you want to break the list into smaller lists of size 3.
numbers.sliding(3, 3).toList
will give you
List(List(1, 2, 3), List(4, 5, 6), List(7))
Or if you want to make your own:
def split[A](xs: List[A], n: Int): List[List[A]] = {
if (xs.size <= n) xs :: Nil
else (xs take n) :: split(xs drop n, n)
}
Use:
scala> split(List(1,2,3,4,5,6,"seven"), 4)
res15: List[List[Any]] = List(List(1, 2, 3, 4), List(5, 6, seven))
edit: upon reviewing this 2 years later, I wouldn't recommend this implementation since size is O(n), and hence this method is O(n^2), which would explain why the built-in method becomes faster for large lists, as noted in comments below. You could implement efficiently as follows:
def split[A](xs: List[A], n: Int): List[List[A]] =
if (xs.isEmpty) Nil
else (xs take n) :: split(xs drop n, n)
or even (slightly) more efficiently using splitAt:
def split[A](xs: List[A], n: Int): List[List[A]] =
if (xs.isEmpty) Nil
else {
val (ys, zs) = xs.splitAt(n)
ys :: split(zs, n)
}
I am adding a tail recursive version of the split method since there was some discussion of tail-recursion versus recursion. I have used the tailrec annotation to force the compiler to complain in case the implementation is not indeed tail-recusive. Tail-recursion I believe turns into a loop under the hood and thus will not cause problems even for a large list as the stack will not grow indefinitely.
import scala.annotation.tailrec
object ListSplitter {
def split[A](xs: List[A], n: Int): List[List[A]] = {
#tailrec
def splitInner[A](res: List[List[A]], lst: List[A], n: Int) : List[List[A]] = {
if(lst.isEmpty) res
else {
val headList: List[A] = lst.take(n)
val tailList : List[A]= lst.drop(n)
splitInner(headList :: res, tailList, n)
}
}
splitInner(Nil, xs, n).reverse
}
}
object ListSplitterTest extends App {
val res = ListSplitter.split(List(1,2,3,4,5,6,7), 2)
println(res)
}
I think this is the implementation using splitAt instead of take/drop
def split [X] (n:Int, xs:List[X]) : List[List[X]] =
if (xs.size <= n) xs :: Nil
else (xs.splitAt(n)._1) :: split(n,xs.splitAt(n)._2)
Is there any difference between ::: and ++ for concatenating lists in Scala?
scala> List(1,2,3) ++ List(4,5)
res0: List[Int] = List(1, 2, 3, 4, 5)
scala> List(1,2,3) ::: List(4,5)
res1: List[Int] = List(1, 2, 3, 4, 5)
scala> res0 == res1
res2: Boolean = true
From the documentation it looks like ++ is more general whereas ::: is List-specific. Is the latter provided because it's used in other functional languages?
Legacy. List was originally defined to be functional-languages-looking:
1 :: 2 :: Nil // a list
list1 ::: list2 // concatenation of two lists
list match {
case head :: tail => "non-empty"
case Nil => "empty"
}
Of course, Scala evolved other collections, in an ad-hoc manner. When 2.8 came out, the collections were redesigned for maximum code reuse and consistent API, so that you can use ++ to concatenate any two collections -- and even iterators. List, however, got to keep its original operators, aside from one or two which got deprecated.
Always use :::. There are two reasons: efficiency and type safety.
Efficiency
x ::: y ::: z is faster than x ++ y ++ z, because ::: is right associative. x ::: y ::: z is parsed as x ::: (y ::: z), which is algorithmically faster than (x ::: y) ::: z (the latter requires O(|x|) more steps).
Type safety
With ::: you can only concatenate two Lists. With ++ you can append any collection to List, which is terrible:
scala> List(1, 2, 3) ++ "ab"
res0: List[AnyVal] = List(1, 2, 3, a, b)
++ is also easy to mix up with +:
scala> List(1, 2, 3) + "ab"
res1: String = List(1, 2, 3)ab
::: works only with lists, while ++ can be used with any traversable. In the current implementation (2.9.0), ++ falls back on ::: if the argument is also a List.
A different point is that the first sentence is parsed as:
scala> List(1,2,3).++(List(4,5))
res0: List[Int] = List(1, 2, 3, 4, 5)
Whereas the second example is parsed as:
scala> List(4,5).:::(List(1,2,3))
res1: List[Int] = List(1, 2, 3, 4, 5)
So if you are using macros, you should take care.
Besides, ++ for two lists is calling ::: but with more overhead because it is asking for an implicit value to have a builder from List to List. But microbenchmarks did not prove anything useful in that sense, I guess that the compiler optimizes such calls.
Micro-Benchmarks after warming up.
scala>def time(a: => Unit): Long = { val t = System.currentTimeMillis; a; System.currentTimeMillis - t}
scala>def average(a: () => Long) = (for(i<-1 to 100) yield a()).sum/100
scala>average (() => time { (List[Int]() /: (1 to 1000)) { case (l, e) => l ++ List(e) } })
res1: Long = 46
scala>average (() => time { (List[Int]() /: (1 to 1000)) { case (l, e) => l ::: List(e ) } })
res2: Long = 46
As Daniel C. Sobrai said, you can append the content of any collection to a list using ++, whereas with ::: you can only concatenate lists.
scala> last(List(1, 1, 2, 3, 5, 8))
res0: Int = 8
for having a result above, I wrote this code:
val yum = args(0).toInt
val thrill:
def last(a: List[Int]): List[Int] = {
println(last(List(args(0).toInt).last)
}
What is the problem with this code?
You can use last, which returns the last element or throws a NoSuchElementException, if the list is empty.
scala> List(1, 2, 3).last
res0: Int = 3
If you do not know if the list is empty or not, you may consider using lastOption, which returns an Option.
scala> List().lastOption
res1: Option[Nothing] = None
scala> List(1, 2, 3).lastOption
res2: Option[Int] = Some(3)
Your question is about List, but using last on a infinite collection (e.g. Stream.from(0)) can be dangerous and may result in an infinite loop.
Another version without using last (for whatever reason you might need it).
def last(L:List[Int]) = L(L.size-1)
You should better do:
val a = List(1,2,3) //your list
val last = a.reverse.head
Cleaner and less error-prone :)
Albiet this is a very old question, it might come handy that the performance impact of head and last operations seems to be laid out here http://docs.scala-lang.org/overviews/collections/performance-characteristics.html.
The recursive function last should following 2 properties. Your last function doesn't have any of them.
Requirement #1. An exit condition that does not call recursive
function further.
Requirement #2. A recursive call that reduces the elements that we began with.
Here are the problems I see with other solutions.
Using built in function last might not be an option in interview
questions.
Reversing and head takes additional operations, which the interviewer might ask to reduce.
What if this is a custom linked list without the size member?
I will change it to as below.
def last(a: List[Int]): Int = a match {
//The below condition defines an end condition where further recursive calls will not be made. requirement #1
case x::Nil => x
//The below condition reduces the data - requirement#2 for a recursive function.
case x:: xs => last(xs)
}
last(List(1,2,3))
Result
res0: Int = 3
In these types of questions the useful take and takeRight are often overlooked. Similar to last, one avoids the slow initial reversing of a list, but unlike last, can take the last (or first) n items as opposed to just a single one:
scala> val myList = List(1,2,3)
myList: List[Int] = List(1, 2, 3)
scala> myList.takeRight(2)
res0: List[Int] = List(2, 3)
scala> myList.takeRight(1)
res1: List[Int] = List(3)
I tried googling for this but all I got were stories about minor celebrities. Given the lack of documentation, what is a DList?
It's a Difference List, along the lines of "Difference List as functions"
scala> val (l1, l2, l3) = (List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
l1: List[Int] = List(1, 2, 3)
l2: List[Int] = List(4, 5, 6)
l3: List[Int] = List(7, 8, 9)
Efficient prepending:
scala> l1 ::: l2 ::: l3
res8: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
Inefficient appending. This creates an intermediate list (l1 ++ l2), then ((l1 ++ l2) ++ l3)
scala> l1 ++ l2 ++ l3 // inefficient
res9: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
DList stores up the appends, and only needs to create one complete list, effectively invoking:
scala> List(l1, l2, l3) reduceRight ( _ ::: _)
res10: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
Difference lists are a list-like data structure that supports O(1) append operations.
Append, and other operations that modify a list are represented via function composition of the modification functions, rather than directly copying the list.
An example, from Haskell's dlist library:
-- Lists as functions
newtype DList a = DL { unDL :: [a] -> [a] }
-- The empty list
empty = DL id
-- The append case: composition, a la Hughes
append xs ys = DL (unDL xs . unDL ys)
-- Converting to a regular list, linear time.
toList = ($[]) . unDL
The technique goes back at least to Hughes 84, A novel representation of lists and its application to the function "reverse", R. John Hughes, 1984., where, he proposes representing lists as functions, and append as function composition, allowing e.g. reverse to run in linear time. From the paper:
It's a data type in the non-canonical scalaz package, useful for typed lists with constant-time access at both ends. (The trick is to google for "scala" AND "dlist".)
From the project page of scalaz:
DList, a data type for representing elements of the same type with constant time append/prepend operations.