In the Scala tutorials I have been using, I have always seen lists constructed in this manner:
(1 :: (2 :: Nil))
Is there a difference between this and the non-parentheses version?
1 :: 2 :: Nil
They are the same. The parentheses can be added to illustrate the order of operation but they don't change anything. In fact they're rather pointless because no other order of operation is possible. In both cases the infix (i.e. dot-less) notation is de-sugared to the following.
Nil.::(2).::(1)
"How's that," you say? Remember that method names ending with a colon, :, reverse the class.method(argument) order when using infix notation.
Both are same when compiled by Scala compiler
(1 :: (2 :: Nil))
//res0: List[Int] = List(1, 2)
And
1 :: 2 :: Nil
//res1: List[Int] = List(1, 2)
The brackets used are just to show the grouping and order of prepending in a list as Nil being the last element.
Related
I was exploring the Scala foldLeft and its operator /:. And I came across an article here.
Here the /: is used to evaluate the sum of 1 to 6 as
(0 /: (1 to 6))(_+_)
The syntax of foldLeft that I am aware of is like Range./:(Initial Value){(z, i) => some anonymous function definition}
How is (0 /: (1 to 6))(_+_) evaluated and how does it work?
Operators ending in : are right associative. this means it's the same as explicitly using dot notation (1 to 6)./:(0)(_+_)
It is the same reason 1 :: 2 :: Nil doesn't have to be 1 :: (2 :: Nil)
I am learning scala and I've noticed that the following line of code doesn't work
val worldFreq = ("India", 1) :: ("US", 2) :: ("Berlin", 10)
Results in the error : error: value :: is not a member of (String, Int) val worldFreq = ("India", 1) :: ("US", 2) :: ("Berlin", 10)
However this line of code works perfectly
val worldFreq = ("India", 1) :: ("US", 2) :: ("Berlin", 10) :: Nil
worldFreq: List[(String, Int)] = List((India,1), (US,2), (Berlin,10))
Can someone help me understand the error message and the fact the it works with Nil.
It happens because :: is right-associative operator.
So, when you type (1, 2) :: Nil it transforms to Nil.::((1,2)). And obviously, there is no :: method on tuples, so you can't write (1, 2) :: (3, 4).
You can read more here: Scala's '::' operator, how does it work?
From the Book programming in Scala I got the following line of code:
val second: List[ Int] => Int = { case x :: y :: _ => y }
//warning: match may not be exhaustive.
It states that this function will return the second element of a list of integers if the list is not empty or nil. Stil this part is a bit awkward to me:
case x :: y :: _
How does this ecxactly work? Does this mathches any list with at least 2 Elements and than return the second? If so can somebody still explain the syntax? I understood that :: is invoked on the right operand. So it could be written as
(_.::(y)).::(X)
Still I than don't get why this would return 2
val second: List[ Int] => Int = { case x :: y :: _ => y }
var x = List(1,2)
second(x) //returns 2
In the REPL, you can type:
scala> val list = "a" :: "b" :: Nil
list: List[String] = List(a, b)
which is to be read from right to left, and means take the end of a List (Nil), prepend String "b" and to this List ("b" :: Nil) prepend String a, a :: ("b" :: Nil) but you don't need the parens, so it can be written "a" :: "b" :: Nil.
In pattern matching you will more often see:
... list match {
case Nil => // ...
case x :: xs => // ...
}
to distinguish between empty list, and nonempty, where xs might be a rest of list, but matches Nil too, if the whole list is ("b" :: Nil) for example, then x="b" and xs=Nil.
But if list= "a" :: "b" :: Nil, then x="a" and xs=(b :: Nil).
In your example, the deconstruction is just one more step, and instead of a name like xs, the joker sign _ is used, indicating, that the name is probably not used and doesn't play a role.
The value second is of function type, it takes List[Int] and returns Int.
If the list has first element ("x"), and a second element ("y"), and whatever comes next (we don't care about it), we simply return the element "y" (which is the second element of the list).
In any other case, the function is not defined. You can check that:
scala> val second: PartialFunction[List[Int], Int] = {
| case x :: y :: _ => y
| }
second: PartialFunction[List[Int],Int] = <function1>
scala> second.isDefinedAt(List(1,2,3))
res18: Boolean = true
scala> second.isDefinedAt(List(1,2))
res19: Boolean = true
scala> second.isDefinedAt(List(0))
res20: Boolean = false
First of all. When you think about pattern matching you should think about matching a structure.
The first part of the case statement describes a structure. This structure may describe one or more things (variables) which are useful to deriving your result.
In your example, you are interested in deriving the second element of a list. A shorthand to build a list in Scala is to use :: method (also called cons). :: can also be used to describe a structure in case statement. At this time, you shouldn't think about evaluation of the :: method in first part of case. May be that's why you are saying about evaluation of _.::(y).::(x). The :: cons operator help us describe the structure of the list in terms of its elements. In this case, the first element (x) , the second element (y) and the rest of it (_ wildcard). We are interested in a structure that is a list with at least 2 elements and the third can be anything - a Nil to indicate end of list or another element - hence the wildcard.
The second part of the case statement, uses the second element to derive the result (y).
More on List and Consing
List in Scala is similar to a LinkedList. You know about the first element called head and start of the rest of the list. When traversing the linked list you stop if the rest of the list is Nil. This :: cons operator helps us visualise the structure of the linked list. Although Scala compile would actually be calling :: methods evaluating from right to left as you described _.::(y).::(x)
As an aside, you might have already noticed that the Scala compiler might be complain that your match isn't exhaustive. This means that this second method would work for list of any size. Because there isn't any case statement to describe list with zero or one element. Also, as mentioned in comments of previous answers, if you aren't interested in first element you can describe it as a wildcard _.
case _ :: y :: _ => y
I hope this helped.
If you see the structure of list in scala its head::tail, first element is treated as head and all remaining ones as tail(Nil will be the last element of tail). whenever you do x::y::_, x will match the head of the list and remaining will be tail and again y will match the head of the next list(tail of first list)
eg:
val l = List(1,2,3,4,5)
you can see this list in differnt ways:
1::2::3::4::5::Nil
1::List(2,3,4,5)
1::2::List(2,3,4,5)
and so on
So try matching the pattern. In your question y will give the second element
If I have a function List[A] => List[List[A]] and need to return an "empty" value, is there a theoretical preference between the following
a) Nil
b) List(Nil)
... or does it depend on the function?
For a concrete example, I could implement a function to split a list into sublists of length n or less as follows:
def sublists[A](xs: List[A], n: Int): List[List[A]] = {
val (ys, zs) = xs.splitAt(n)
if (zs.isEmpty) ys :: Nil
else ys :: sublists(zs, n)
}
If xs is empty this returns List(Nil). Do I need to include a check on whether xs is empty, returning Nil, for this function to be correct?
Without doubt, the correct empty value for a List is the empty List, no matter what the type of the List elements is.
The same is true for more elaborated types. A set containing the empty set is very different from an empty set, and so forth.
Think of it like this: A list result allows you to ask: how many results do we have? If you use a list with an empty List as empty value, then the answer would be incorrectly 1.
I would stick with Nil, aka List(). For example,
sublists(1 :: 2 :: 3 :: 4 :: Nil), 2)
returns
List(1 :: 2 :: Nil, 3 :: 4 :: Nil)
and not
List(1 :: 2 :: Nil, 3 :: 4 :: Nil, Nil)
The terminal Nil is usually chopped off. So for consistency I would keep sublists(Nil) -> List().
I don't know the context, so I don't feel comfortable being definitive. In general, I think the answer is context-dependent. In the abstract, I'd definitely prefer Nil as a client-code writer - in writing a closure over the results, I don't think I'd be expecting an empty sublist.
Rearranging to put the null-check before the splitAt will do the trick.
I have the following code:
val text = "some text goes here"
val (first, rest) = text.splitAt(4)
println(first + " *" + rest)
That works fine.
However, I want to have two cases, defining "first" and "rest" outside, like this:
val text = "some text goes here"
var (first, rest) = ("", "")
if (text.contains("z")) {
(first, rest) = text.splitAt(4)
} else {
(first, rest) = text.splitAt(7)
}
println(first + " *" + rest)
But that gives me an error:
scala> | <console>:2: error: ';' expected but '=' found.
(first, rest) = text.splitAt(4)
Why is it an error to do (first, rest) = text.splitAt(4) but not to do val (first, rest) = text.splitAt(4)? And what can I do?
Edit: Can't re-assign val, changed to var. Same error
The answer by Serj gives a better way of writing this, but for an answer to your question about why your second version doesn't work, you can go to the Scala specification, which makes a distinction between variable definitions and assignments.
From "4.2 Variable Declarations and Definitions":
Variable definitions can alternatively have a pattern (§8.1) as
left-hand side. A variable definition var p = e where p is a
pattern other than a simple name or a name followed by a colon and a
type is expanded in the same way (§4.1) as a value definition val p
= e, except that the free names in p are introduced as mutable variables, not values.
From "6.15 Assignments":
The interpretation of an assignment to a simple variable x = e depends
on the definition of x. If x denotes a mutable variable, then the
assignment changes the current value of x to be the result of
evaluating the expression e.
(first, rest) here is a pattern, not a simple variable, so it works in the variable definition but not in the assignment.
First of all val is immutable, so you can't reassign it. Second, if, like all control structures in Scala, can return a value. So, you can do it like this:
val text = "some text goes here"
val (first, rest) = if (text.contains("z")) text.splitAt(4) else text.splitAt(7)
println(first + " *" + rest)
SerJ de SuDDeN answer is absolutely correct but some more details why the code you mentioned works the way it works.
val (a, b) = (1, 2)
is called an extractor of a pattern-match-expression. The value on the right side is matched to the extractor of the left side. This can be done everywhere in Scala and can have different faces. For example a pattern match on a List can look something like
scala> val head :: tail = 1 :: 2 :: 3 :: Nil
head: Int = 1
tail: List[Int] = List(2, 3)
On the right side the ::-symbol is a method of class List which prepends elements to it. On the left side the ::-symbol is an extractor of class ::, a subclass of List.
Some other places can be for-comprehensions
scala> for ((a, b) <- (1 to 3) zip (4 to 6)) println(a+b)
5
7
9
or the equivalent notation with higher-order-methods
scala> (1 to 3) zip (4 to 6) foreach { case (a, b) => println(a+b) }
5
7
9