Skip to content

Instantly share code, notes, and snippets.

@JonNorman
Created October 23, 2018 10:59
Show Gist options
  • Save JonNorman/5b588c8ea853b15459d96a6f55a56fae to your computer and use it in GitHub Desktop.
Save JonNorman/5b588c8ea853b15459d96a6f55a56fae to your computer and use it in GitHub Desktop.
A quick overview of the concepts covered in week 3 of scala-school
/**************************************************************************************
* Control structures
*
* Control structures are bits of code that determine "where" your program goes - it
* isn't that useful having a program that does the same thing every time you run it,
* you want to be able to specify conditions and checks and for it to act according to
* those results
**************************************************************************************/
/*
If statements are the main way that we follow different behaviour based on some condition.
They require:
A condition - some expression that returns a Boolean value (Boolean = true or false)
The 'true' code - what to do if the condition returns true
(Optional) The 'else' or 'false" code - what to do if the condition returns false
Note: if you don't provide an 'else' block and none of the if statements above match then
nothing will be executed.
*/
def adviseMeOnMyPacking(expectedWeather: String): String =
if (expectedWeather == "Rainy") {
"Take an umbrella"
} else {
"Pack your shorts"
}
// let's define a list of various weather types...
val variousWeathers = List(
"Rainy",
"Sunny",
"Snowy",
"Icy",
"Something completely unrelated to weather")
// let's see what advice we get by iterating over those weathers with 'foreach' and printing the results...
variousWeathers.foreach(weather =>
println(adviseMeOnMyPacking(weather)))
/*
We could make our function better by listing more of the types of weather we expect and chaining some
more 'else if' statements on, finally ending in our catch-all 'else' block.
*/
def adviseMeOnMyPacking2(expectedWeather: String): String =
if (expectedWeather == "Rainy") {
"Take an umbrella"
} else if (expectedWeather == "Sunny") {
"Pack your shorts"
} else if (expectedWeather == "Snowy" || expectedWeather == "Icy"){
"Pack your gloves and crampons"
} else {
"No idea what you're talking about mate, try again"
}
// let's see what we get now...
variousWeathers.foreach(weather =>
println(adviseMeOnMyPacking2(weather)))
/*
Before we go on, we should quickly recap how we can work with Booleans, after all these
will drive where our programs go and how they operate. Just what can we express?
*/
// we have the literal boolean values
if (true) {
println("This will always be executed")
} else {
println("This blocked will NEVER be reached")
}
// we have the "NOT" operator: ! which inverts the Boolean value it is applied to
if(!true) {
println("Now this will never be reached: not true is always false!")
} else {
println("This will always be reached: not false is always true")
}
// we can combine Boolean values together and it works logically as if you were saying it
def tellMeWhatTheyAreLike(isOld: Boolean, isEmployed: Boolean, doesDance: Boolean): String = {
// here we use double ampersand (&&) to mean AND; if BOTH conditions are true then it is true, otherwise false
if (isOld && isEmployed && doesDance) {
"a work-hard play-hard long-in-the-tooth tap dancer"
// here we use double pipe (||) to mean OR; if EITHER condition is true, then it is true, otherwise false
} else if (!doesDance || isOld) {
"a non-dancer or an older person"
} else if (!doesDance && (!isOld || isEmployed)){
"non-dancing youth or worker"
} else {
"a person I can't describe"
}
}
tellMeWhatTheyAreLike(true, true, true)
tellMeWhatTheyAreLike(false, true, true)
tellMeWhatTheyAreLike(false, true, false)
/*
We can also use the CASE statement to do pattern matching. In this example, it will seem
slightly contrived but it will have great benefit later when we define our own types and
want to match on subtypes.
*/
/* here we put our values into a Tuple3 and we match on that tuple. In each case statement we
can say what the values inside this Tuple are expected to be and then provide the result based on that.
Note: the underscore (_) is used here to indicate that we don't care about a value - it is a throwaway
*/
def tellMeWhatTheyAreLike2(isOld: Boolean, isEmployed: Boolean, doesDance: Boolean): String = {
(isOld, isEmployed, doesDance) match {
case (true, true, true) => "a work-hard play-hard long-in-the-tooth tap dancer"
case (true, _, _) => "a non-dancer or an older person"
case (_, _, false) => "a non-dancer or an older person"
case (false, _ ,false) => "non-dancing youth or worker"
case (_, true ,false) => "non-dancing youth or worker"
case _ => "a person I can't describe" // here we don't provide any constraints on the case, ANYTHING will match
}
}
/* the above isn't great, it is more typing and we have repeated ourselves a fair amount
You can actually add conditions on your case statements
*/
def tellMeWhatTheyAreLike3(isOld: Boolean, isEmployed: Boolean, doesDance: Boolean): String = {
(isOld, isEmployed, doesDance) match {
case (true, true, true) => "a work-hard play-hard long-in-the-tooth tap dancer"
case _ if !doesDance || isOld => "a non-dancer or an older person"
case (_, _, false) if !isOld || isEmployed => "non-dancing youth or worker"
case _ => "a person I can't describe"
}
}
/* If these case statements look a little contrived it is because they are! But this pattern matching
* paradigm becomes really useful when we start using custom types a little more. */
/*
There are other control structures you might know from other languages such as the 'while' loop
but we won't go over them here - the 'while' loop is rarely used as there are more 'functional'
ways of iterating such as `fold`, `map`, `foreach` etc.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment