In Effective Scala article by Twitter, they say the following:
Use returns to clarify and enhance readability, but not as you would
in an imperative language; avoid using them to return the results of a
computation. Instead of
def suffix(i: Int) = {
if (i == 1) return "st"
else if (i == 2) return "nd"
else if (i == 3) return "rd"
else return "th"
}
prefer:
def suffix(i: Int) =
if (i == 1) "st"
else if (i == 2) "nd"
else if (i == 3) "rd"
else "th"
but using a match expression is superior to either:
def suffix(i: Int) = i match {
case 1 => "st"
case 2 => "nd"
case 3 => "rd"
case _ => "th"
}
In this specific example, why is match expression superior compared to the if/else?
In general match is more readable and explains your intention more clearly. But in this exact example, it has an added benefit, that match will emit not a series of ifs, but a switch bytecode instruction (the tableswitch one in this case), which is likely to improve performance a bit.
You can assert that in this case switch will be emitted with the #switch annotation:
def suffix(i: Int) = (i: #switch) match {
case 1 => "st"
case 2 => "nd"
case 3 => "rd"
case _ => "th"
}
Essential in pattern matching is data type extraction. For examples and discussion see for instance Extractor Objects and The Neophyte's Guide to Scala Part 1: Extractors.
In the examples above the data type proves trivial and the benefit may come chiefly from readability.
Related
Suppose I have a Seq(1, 2, 3, 4, 5) and I want to know if value "4" exists in it. I want to know if it is possible to find and extract the answer only using pattern matching.
Seq(1, 2, 3, 4, 5) match {
case Seq(_, 4, _*) => true
case _ => false
}
match clauses like this only works if the position of our specified value is known. I am curious to know if it is possible to do this in Scala, I am new to Scala and not completely familiar with its syntax and features.
obviously using the if gaurd is not helpful because it does not allow extracting the value to a variable.
you may ask why it is even important and why not use current existing exists or filter methods. Imagine the case below.
case class Score(eventName: String, amount: Integer)
case class Person(name: String, scores: Seq[Score])
suppose I have a collection of Persons and I want to find the highest score for a certain event. a person may have a score for that event or may not.
persons.collect({
case x # _ if x.scores.exists(_.eventName == "event1") =>
x.scores.find(_.eventName == "event1").get.amount
}).max
the problem is that the filter on eventName is processed two times.
I know it is possible to do this using filter and map with mutiple iterations. but I am interested to know if there is a solution using pattern matching, with pattern matching you can find and extract the value to a variable at the same time.
You can write a custom matcher with an unapply method:
case class EventScore(eventName: String) {
def unapply(person: Person): Option[Int] =
person.scores.collectFirst { case s if s.eventName == eventName => s.amount }
}
val score = EventScore("event1")
persons.collect({
case score(amount) => amount
}).max
You might be able to do it by pattern matching inside a recursive method but I think this is all you need:
persons.flatMap(_.scores.filter(_.eventName == "ev1")
.map(_.amount))
.maxOption
.getOrElse(0)
Or the prettier for comprehension:
(for {
person <- persons
score <- person.scores
if score.eventName == "ev1"
} yield score.amount).maxOption.getOrElse(0)
//using only pattern matching
def has(xs: Seq[Int], target: Int) : Boolean = xs match {
case Seq() => false
case Seq(`target`, _ #_*) => true
case Seq(_, tl #_*) => has(tl, target)
}
testing:
has(List(2,4,5,11,44,66), 11) //true
has(List(2,4,5,11,44,66), 12) //false
has(List(2,4,5,11,44,66), 66) //true
There is no way currently to introduce term inside guard part of pattern matching (I mean if ... part). I was looking for something like that but without luck.
In your example tho you don't need it. Few words about example:
Using two brackets is not necessary collect({..}).
# _ in case is not necessary either
.max will blow on empty collection. It is especially risky with collect
Here is my proposition:
persons.map {
case x => x.scores.find(_.eventName == "event1").getOrElse(0)
}.max
When compare two numbers, we can use if-else or pattern matching:
If-else
if (guess < secretNum) println("Too small!\n")
else if (guess > secretNum) println("Too big!\n")
else {
println("You win!\n")
continueLooping = false
}
Pattern matching
guess match {
case x if x < secretNum => println("Too small!\n")
case x if x > secretNum => println("Too big!\n")
case _ => {
println("You win!\n")
continueLooping = false
}
}
Are there any clearer way to write this? and which is encouraged?
Edit
Here is the Rust way:
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => { println!("You win!"); break;
}
If you have access to the Scalaz library you can match Rust for style and clarity:
import scalaz._
import Scalaz._
guess ?|? secretNum
match {
case Ordering.LT => println("Too small!")
case Ordering.GT => println("Too big!")
case Ordering.EQ => println("You win!")
}
In terms of preference, it depends. If performance is critical then the if-else beats the pattern matching idiom. If readability is paramount, then pattern matching with the facilities Scalaz provides leads to more readable code in my opinion; It avoids the use of equational guards as you did in your example.
The if-else way. The pattern matching seems weird since you're not actually trying to match any patterns, so you end up just having more syntactic noise.
Consider method signum available in Double, Float, Long and Int classes, which maps the sign of the numerical value onto -1 (for negative), 0 (for zero) or 1 (for positive),
(gess - secretNum).signum match {
case -1 => "Too small"
case 1 => "Too big"
case _ => "You win"
}
I have a configuration value that matches to one of the values in a map and depending on to which it matches i take an action. Here is some sample code of what i am trying to do
val x = 1 // or 2 or 3
val config = Map("c1"-> 1, "c2"-> 2, "c3"-> 3)
x match {
case config("c1") =>
println("1")
case config("c2") =>
println("2")
case config("c3") =>
println("3")
}
Now this should print 1 because config("c1") evaluates to 1 but it gives error
error: value config is not a case class, nor does it have an unapply/unapplySeq member
case config("c1") =>
Similarly for the other 2 cases. Why should i have an unapply here? Any pointers?
An expression like that looks like an extractor, hence the message about unapply/unapplySeq methods. If you don't want to use an extractor but just want to match against a plain value, you need to store that value in a stable identifier - you can't use an arbitrary expression as a match case:
val case1 = config("c1")
x match {
case case1 => println("1")
...
}
To the best of my knowledge, in Scala, x match {case config("c1") gets translated to config.unapply(x) with the branching dependent on the result of the unapply method. As Imm already mentioned in his answer, this isn't the case for stable identifiers (literals and val), and I'd encourage you to use his solution.
Nevertheless, to show you how you could solve the problem using extractors, I'd like to post a different solution:
def main(args: Array[String]): Unit = {
object config {
val configData = Map("c1" -> 1, "c2" -> 2, "c3" -> 3)
def unapply(value: Int): Option[String] = configData find (_._2 == value) map (_._1)
}
1 to 4 foreach {
case config("c1") => println("1")
case config("c2") => println("2")
case config("c3") => println("3")
case _ => println("no match")
}
}
I changed the match for a foreach to show the different results, but this has no effect on the implementation. This would print:
1
2
3
no match
As you can see, case config("c1") now calls the unapply method and checks whether the result is Some("c1"). Note that this is inverse to how you'd use a map: The key is searched according to the value. However, this makes sense: If in the map, "c1" and "c2" both map to 1, then 1 matches both, the same way _ matches everything, in our case even 4 which is not configured.
Here's also a very brief tutorial on extractors. I don't find it particularly good, because both, the returned type and the argument type are Int, but it might help you understand what's going on.
As others have stated, with x match { case config("c1") => ..., scala looks for an extractor by the name of config (something with an unapply method that takes a single value and returns an Optional value); Making pattern matching work this way seems like an abuse of the pattern, and I would not use an extractor for this.
Personally, I would recommend one of the following:
if (x == config("c1"))
println("1")
else if (x == config("c2"))
println("2")
else ...
Or, if you're set on using a match statement, you can use conditionals like this:
x match {
case _ if x == config("c1") =>
println("1")
case _ if x == config("c2") =>
println("2")
case _ if x == config("c3") =>
println("3")
}
Not as clean; unfortunately, there isn't a way to invoke a method call literally where the extractor goes. You can use back-ticks to tell scala "match against the value of this variable" (rather than default behavior, which would yield the value named as that variable):
val (c1,c2,c3) = (config("c1"), config("c2"), config("c3"))
x match {
case `c1` =>
println("1")
case `c2` =>
println("2")
case `c3` =>
println("3")
}
Finally, if your goal is to reverse-apply a map, maybe try this instead?
scala> Map("a" -> 1).map { case (k,v) => (v,k) }
res0: scala.collection.immutable.Map[Int,String] = Map(1 -> a)
Given that a case is statistically more probable than the others, would the execution be quicker if that "case" was put first in a Switch statement?
Logically that should be the case, but I've been running some benchmarks, and I can't see much difference.
Is Switch/Case the best way to make use of the fact that a "case" is more probable than others?
This is very simple to test:
struct MyInteger {
let value: Int
}
func ~=(a: Int, b: MyInteger) -> Bool {
println("Evaluating pattern \(a)")
return (a == b.value)
}
for i in 0...3 {
let number = MyInteger(value: i)
switch (number) {
case 0:
println("Case 0")
case 1:
println("Case 1")
case 2:
println("Case 2")
default:
println("Default branch")
}
}
The output is
Evaluating pattern 0
Case 0
Evaluating pattern 0
Evaluating pattern 1
Case 1
Evaluating pattern 0
Evaluating pattern 1
Evaluating pattern 2
Case 2
Evaluating pattern 0
Evaluating pattern 1
Evaluating pattern 2
Default branch
So, the order of case statements matters and they are evaluated only until we find one evaluating to true.
This is also the reason why the default statement must always be the last (this is not enforced in languages like Java or C where switch is just a jump table).
Every case is a call to a pattern matching function ~= (or more of them).
This proves that cases are evaluated in the order they are written:
func testing(a : Int)->Int{
println(a)
return a
}
let x = 2
switch x {
case testing(1) :
println("one")
case testing(2) :
println("two")
case testing(3) :
println("three")
case testing(4) :
println("four")
default:
println("Default")
}
this prints:
1
2
two
If you want to evaluate the following cases, then use the keyword fallthrough
I'm novice in Scala. Recently I was writing a hobby app and caught myself trying to use pattern matching instead of if-else in many cases.
user.password == enteredPassword match {
case true => println("User is authenticated")
case false => println("Entered password is invalid")
}
instead of
if(user.password == enteredPassword)
println("User is authenticated")
else
println("Entered password is invalid")
Are these approaches equal? Is one of them more preferrable than another for some reason?
class MatchVsIf {
def i(b: Boolean) = if (b) 5 else 4
def m(b: Boolean) = b match { case true => 5; case false => 4 }
}
I'm not sure why you'd want to use the longer and clunkier second version.
scala> :javap -cp MatchVsIf
Compiled from "<console>"
public class MatchVsIf extends java.lang.Object implements scala.ScalaObject{
public int i(boolean);
Code:
0: iload_1
1: ifeq 8
4: iconst_5
5: goto 9
8: iconst_4
9: ireturn
public int m(boolean);
Code:
0: iload_1
1: istore_2
2: iload_2
3: iconst_1
4: if_icmpne 11
7: iconst_5
8: goto 17
11: iload_2
12: iconst_0
13: if_icmpne 18
16: iconst_4
17: ireturn
18: new #14; //class scala/MatchError
21: dup
22: iload_2
23: invokestatic #20; //Method scala/runtime/BoxesRunTime.boxToBoolean:(Z)Ljava/lang/Boolean;
26: invokespecial #24; //Method scala/MatchError."<init>":(Ljava/lang/Object;)V
29: athrow
And that's a lot more bytecode for the match also. It's fairly efficient even so (there's no boxing unless the match throws an error, which can't happen here), but for compactness and performance one should favor if/else. If the clarity of your code is greatly improved by using match, however, go ahead (except in those rare cases where you know performance is critical, and then you might want to compare the difference).
Don't pattern match on a single boolean; use an if-else.
Incidentally, the code is better written without duplicating println.
println(
if(user.password == enteredPassword)
"User is authenticated"
else
"Entered password is invalid"
)
One arguably better way would be to pattern match on the string directly, not on the result of the comparison, as it avoids "boolean blindness". http://existentialtype.wordpress.com/2011/03/15/boolean-blindness/
One downside is the need to use backquotes to protect the enteredPassword variable from being shadowed.
Basically, you should tend to avoid dealing with booleans as much as possible, as they don't convey any information at the type level.
user.password match {
case `enteredPassword` => Right(user)
case _ => Left("passwords don't match")
}
Both statements are equivalent in terms of code semantics. But it might be possible that the compiler creates more complicated (and thus inefficient) code in one case (the match).
Pattern matching is usually used to break apart more complicated constructs, like polymorphic expressions or deconstructing (unapplying) objects into their components. I would not advice to use it as a surrogate for a simple if-else statement - there's nothing wrong with if-else.
Note that you can use it as an expression in Scala. Thus you can write
val foo = if(bar.isEmpty) foobar else bar.foo
I apologize for the stupid example.
It's 2020, the Scala compiler generates far more efficient bytecode in the pattern matching case. The performance comments in the accepted answer are misleading in 2020.
The pattern match generated byte code gives a tough competition to if-else at times pattern matching wins giving much better and consistent results.
One can use pattern match or if-else based on the situation & simplicity.
But the pattern matching has poor performance conclusion is no longer valid.
You can try the following snippet and see the results:
def testMatch(password: String, enteredPassword: String) = {
val entering = System.nanoTime()
password == enteredPassword match {
case true => {
println(s"User is authenticated. Time taken to evaluate True in match : ${System.nanoTime() - entering}"
)
}
case false => {
println(s"Entered password is invalid. Time taken to evaluate false in match : ${System.nanoTime() - entering}"
)
}
}
}
testMatch("abc", "abc")
testMatch("abc", "def")
Pattern Match Results :
User is authenticated. Time taken to evaluate True in match : 1798
Entered password is invalid. Time taken to evaluate false in match : 3878
If else :
def testIf(password: String, enteredPassword: String) = {
val entering = System.nanoTime()
if (password == enteredPassword) {
println(
s"User is authenticated. Time taken to evaluate if : ${System.nanoTime() - entering}"
)
} else {
println(
s"Entered password is invalid.Time taken to evaluate else ${System.nanoTime() - entering}"
)
}
}
testIf("abc", "abc")
testIf("abc", "def")
If-else time results:
User is authenticated. Time taken to evaluate if : 65062652
Entered password is invalid.Time taken to evaluate else : 1809
PS: Since the numbers are at nano precision the results may not accurately match to the exact numbers but the argument on performance holds good.
For the large majority of code that isn't performance-sensitive, there are a lot of great reasons why you'd want to use pattern matching over if/else:
it enforces a common return value and type for each of your branches
in languages with exhaustiveness checks (like Scala), it forces you to explicitly consider all cases (and noop the ones you don't need)
it prevents early returns, which become harder to reason if they cascade, grow in number, or the branches grow longer than the height of your screen (at which point they become invisible). Having an extra level of indentation will warn you you're inside a scope.
it can help you identify logic to pull out. In this case the code could have been rewritten and made more DRY, debuggable, and testable like this:
val errorMessage = user.password == enteredPassword match {
case true => "User is authenticated"
case false => "Entered password is invalid"
}
println(errorMesssage)
Here's an equivalent if/else block implementation:
var errorMessage = ""
if(user.password == enteredPassword)
errorMessage = "User is authenticated"
else
errorMessage = "Entered password is invalid"
println(errorMessage)
Yes, you can argue that for something as simple as a boolean check you can use an if-expression. But that's not relevant here and doesn't scale well to conditions with more than 2 branches.
If your higher concern is maintainability or readability, pattern matching is awesome and you should use it for even minor things!
I am here to offer a different opinion:
For the specific example you offer, the second one (if...else...) style is actually better because it is much easier to read.
In fact, if you put your first example into IntelliJ, it will suggest you to change to the second (if...else...) style. Here is the IntelliJ style suggestion:
Trivial match can be simplified less... (⌘F1)
Suggests to replace trivial pattern match on a boolean expression with a conditional statement.
Before:
bool match {
case true => ???
case false => ???
}
After:
if (bool) {
???
} else {
???
}
I'v came across same question, and had written tests:
def factorial(x: Int): Int = {
def loop(acc: Int, c: Int): Int = {
c match {
case 0 => acc
case _ => loop(acc * c, c - 1)
}
}
loop(1, x)
}
def factorialIf(x: Int): Int = {
def loop(acc: Int, c: Int): Int =
if (c == 0) acc else loop(acc * c, c - 1)
loop(1, x)
}
def measure(e: (Int) => Int, arg:Int, numIters: Int): Long = {
def loop(max: Int): Unit = {
if (max == 0)
return
else {
val x = e(arg)
loop(max-1)
}
}
val startMatch = System.currentTimeMillis()
loop(numIters)
System.currentTimeMillis() - startMatch
}
val timeIf = measure(factorialIf, 1000,1000000)
val timeMatch = measure(factorial, 1000,1000000)
timeIf : Long = 22
timeMatch : Long = 1092
In my environment (scala 2.12 and java 8) I get different results. Match performs consistently better in the code above:
timeIf: Long = 249
timeMatch: Long = 68