REPL(READ EVALUATE PROCESS LOAD)
---------------------------------
Scalaís REPL provides support for evaluating and executing code one line at a time with helpful feedback
You can enter single lines of code to evaluate and compile, and any variables you create are available for the lifetime of your session. A multiline paste mode supports entering multiple lines of code to be compiled together (instead of individually), and external source code and libraries can be loaded at any time.
Here is how the input and response should appear in the REPL:
scala> println("Hello, World") Hello, World
scala> Congratulations, you have now written and executed Scala code.
The println() function, universally available to all Scala code, prints a message to the JVMís stdout stream
REPL history is stored between sessions, so you can quit, run the scala command again, and press up arrow to access your previous commands.
scala> 5 * 7
res0: Int = 35
Now that res0 contains the output of the multiplication command, lets make use of it. Type 2 * res0 at a fresh prompt and press Return. You should see something like this:
scala> 2 * res0
res1: Int = 70
Working with Data: Literals, Values, Variables, and Types
********************************************************************
we will cover the core data and variable types in Scala. Letís start with the definitions of the terms literal, value, variable, and type:
ï A literal (or literal data) is data that appears directly in the source code, like the number 5, the character A, and the text ìHello, World.î
ï A value is an immutable, typed storage unit. A value can be assigned data when it is defined, but can never be reassigned.
ï A variable is a mutable, typed storage unit. A variable can be assigned data when it is defined and can also be reassigned data at any time.
ï A type is the kind of data you are working with, a definition or classification of data. All data in Scala corresponds to a specific type, and all Scala types are defined as classes with methods that operate on the data.
The data stored in values and variables in Scala will get automatically deallocated by the Java Virtual Machineís garbage collection when they are no longer used. There is no ability, or need, to deallocate them manually.
Values
----------------
Values are immutable, typed storage units, and by convention are the default method for storing data.
You can define a new value using the val keyword.
Syntax: Defining a Value
----------------------------
val <identifier>[: <type>] = <data>
Values require both a name and assigned data, but they do not require an explicit type. If the type is not specified (i.e., the ì: <type>î syntax is not included), the Scala compiler will infer the type based on the assigned data.
Here are some examples of defining values with their type in the Scala REPL:
scala> val x: Int = 20 x: Int = 20
scala> val greeting: String = "Hello, World" greeting: String = Hello, World
scala> val atSymbol: Char = '@' atSymbol: Char = @
The Scala compiler will then discern the type of the value from its assignment, a process known as type inference. Values defined without a type are not typeless; they are assigned the proper type just as if the type had been included in the definition.
The Scala compiler, via the REPL, was able to deduce that the literal 20 corresponds to the type Int, the literal "Hello, World" to the type String, and the literal @ to the type Char.
val x: Int = "Hello"
The error here affirms that an Int type cannot be used to store a String.
VARIBLES:
-------------------
In computer science the term variable typically refers to a unique identifier corre- sponding to an allocated or reserved memory space, into which values can be stored and from which values can be retrieved. As long as the memory space is reserved, it can be assigned new values over and over again. Thus, the contents of the memory space are dynamic, or variable.
Variables are dynamic, mutable, and reassignable (with the exception of those defined with special restrictions such as Javaís final keyword).
an immutable value will be more stable and less prone to errors than mutable data that may be modified at unexpected times.
The var keyword is used to define a variable with a given name, type, and assignment.
Syntax: Defining a Variable
var <identifier>[: <type>] = <data>
NAMING:
--------------
Scala names can use letters, numbers, and a range of special operator characters. This makes it possible to use standard mathematical operators (e.g., * and :+) and constants (e.g., p and f) in place of longer names to make the code more expressive
while periods are reserved for access to the fields and methods of objects (instantiated types).
Here are the rules for combining letters, numbers, and characters into valid identifiers in Scala:
1. A letter followed by zero or more letters and digits.
Naming | 13
2. A letter followed by zero or more letters and digits, then an underscore (_), and then one or more of either letters and digits or operator characters.
3. One or more operator characters.
4. One or more of any character except a backquote, all enclosed in a pair of back- quotes.
Names enclosed in backquotes can, unlike the other names, be re- served keywords in Scala such as true, while, =, and var
Letís try out some of these naming rules in the REPL:
scala> val p = 3.14159
p: Double = 3.14159
scala> val $ = "USD currency symbol"
$: String = USD currency symbol
scala> val o_O = "Hmm"
o_O: String = Hmm
scala> val 50cent = "$0.50"
<console>:1: error: Invalid literal number
val 50cent = "$0.50"^
scala> val a.b = 25
<console>:7: error: not found:
value a val a.b = 25
scala> val `a.b` = 4
a.b: Int = 4
The special character ìpî is a valid Scala identifier.
The value name ì50centî is invalid because names cannot start with numbers. In this case the compiler started parsing the name as a literal number and ran into problems at the letter ìcî.
The value name ìa.bî is invalid because a period isnít an operator character. Rewriting this value with backquotes fixes the problem, although the aesthetics of using backquotes isnít that great.
Value and variable names, by convention, should start with a lowercase letter and then capitalize additional words. This is popularly known as camel case, and though not
Types
-----------------
Scala has both numeric (e.g., Int and Double) and nonnumeric types (e.g., String) that can be used to define values and variables. These core types are the building blocks for all other types including objects and collections, and are themselves objects that have methods and operators that act on their data.
Scala only supports its own integer class, Int.
Numeric Data Types Table 2-1 displays Scalaís numeric data types.
Table 2-1. Core numeric types
Name Description Size Min Max
Byte Signed integer 1 byte ñ127 128
Short Signed integer 2 bytes ñ32768 32767 Int Signed integer 4 bytes ñ231 231ñ1 Long Signed integer 8 bytes ñ263 263ñ1
Float Signed floating point 4 bytes n/a n/a
Double Signed floating point 8 bytes n/a n/a
Scala does not allow automatic conversion from higher ranked types to lower ranked types. This makes sense, because you could otherwise lose data if you convert to a type with less storage. Here is an example of trying to automatically convert a higher ranked type to a lower ranked type and the ensuing error:
scala> val l: Long = 20 l: Long = 20
scala> val i: Int = l
<console>:8: error: type mismatch; found : Long required: Int val i: Int = l You can choose to manually convert between types using the toType methods available on all numeric types.
For example, here is a Long value that can be safely converted to type Int using the toInt method, because its data is within the storage bounds of an Int:
scala> val l: Long = 20 l: Long = 20
scala> val i: Int = l.toInt i: Int = 20 An alternative to using explicit types is to specify the type of your literal data directly, using Scalaís notation for literal types.
Strings
---------------
The String type represents ìstringsî of text, one of the most common core types in any programming language. Scalaís String is built on Javaís String and adds unique fea- tures like multiline literals and string interpolation.
Write String literals using double quotes, with special characters escaped with back- slashes:
scala> val hello = "Hello There" hello: String = Hello There
scala> val signature = "With Regards, \nYour friend" signature: String = With Regards, Your friend
Like numeric types, the String type supports the use of math operators.
For example, use the equals operator (==) to compare two String values. Unlike Java, the equals operator (==) checks for true equality, not object reference equality:
scala> val greeting = "Hello, " + "World" greeting: String = Hello, World
scala> val matched = (greeting == "Hello, World") matched: Boolean = true
scala> val theme = "Na " * 16 + "Batman!" // what do you expect this to print?
String interpolation Building a String based on other values is reasonably easy to do with string addition.
Here is a String built by adding text before and after the Float value:
scala> val approx = 355/113f approx: Float = 3.141593
scala> println("Pi, using 355/113, is about " + approx + "." ) Pi, using 355/113, is about 3.141593. A more direct way to combine your values or variables inside a String is with string interpolation, a special mode where external value and variable names are recognized and resolved. The Scala notation for string interpolation is an ìsî prefix added before the first double quote of the string. Then dollar sign operators ($) (with optional braces) can be used to note references to external data.
Here is the example again using string interpolation:
-------------------------
scala> println(s"Pi, using 355/113, is about $approx." )
Pi, using 355/113, is about 3.141593.
scala> s"How do you like them ${item}s?" res0: String = How do you like them apples?
scala> s"Fish n chips n vinegar, ${"pepper "*3}salt" res1: String = Fish n chips n vinegar, pepper pepper pepper salt
To use printf notation change the prefix to an ìfî and follow the end of the reference immediately with the printf notation:
scala> f"Enjoying this $item ${355/113.0}%.5f times today"
res3: String = Enjoying this apple 3.14159 times today
These printf notations make the references a little harder to read than in the previous examples, but do provide essential control over the output.
Regular expressions
--------------------------------
regular expressions may be different from the format you have used with other languages and tools.
The String type provides a number of built-in operations that support regular expres- sions. Table 2-3 displays a selection of these operations
Table 2-3. Regular expression operations
Name Example Description
-------- -------------------- --------------------------------------------
matches "Froggy went a' courting" matches ".* courting" Returns true if the regular expression matches the entire string.
replaceAll "milk, tea, muck" replaceAll ("m[^ ] +k", "coffee") Replaces all matches with replacement text.
replaceFirst "milk, tea, muck" replaceFirst ("m[^ ] +k", "coffee") Replaces the first match with replacement text.
Syntax: Capturing Values with Regular Expressions
val <Regex value>(<identifier>) = <input string>
overview variable datatypes
----------------------
Figure 2-1 shows the hierarchy of Scalaís core (numeric and nonnumeric) types.
At the bottom of the Scala type hierarchy are the Nothing and Null types. Nothing is a subtype of every other type and exists to provide a compatible return type for operations that significantly affect a programís flow
Type operations
----------------------
Table 2-5 displays the operations available on all types in Scala. The toString and hashCode methods are required on all JVM instances.
Table 2-5. Common type operations
Name Example Description
asInstanceOf[<type>] 5.asInstanceOf[Long] converts the value to a value of the desired type. Causes an error if the value is not compatible with the new type.
getClass (7.0 / 5).getClass Returns the type (i.e., the class) of a value.
isInstanceOf (5.0).isInstanceOf[Float] Returns true if the value has the given type.
hashCode "A".hashCode Returns the hash code of the value, useful for hash- based collections.
to<type> 20.toByte; 47.toFloat Conversion functions to convert a value to a compatible value.
toString (3.0 / 4.0).toString Renders the value to a String.
NOTE:
----------
Avoid asInstanceOf The asInstanceOf operation will cause an error if the value can- not be converted to the requested type. To avoid runtime errors with this operation, prefer the to<type> typed conversion operations when possible
Tuples
---------------------
A tuple is an ordered container of two or more values, all of which may have different types. You may be familiar with this term from working with relational databases, where a single row of a table is considered its own tuple. Tuples can be useful when you need to logically group values, representing them as a coherent unit. Unlike lists and arrays, however, there is no way to iterate through elements in a tuple. Its purpose is only as a container for more than one value. You can create a tuple by writing your values separated by a comma and surrounded by a pair of parentheses.
Syntax: Create a Tuple( <value 1>, <value 2>[, <value 3>...] )
For example, here is a tuple containing Int, String, and Boolean values:
scala> val info = (5, "Korben", true) info: (Int, String, Boolean) = (5,Korben,true)
You can access an individual element from a tuple by its 1-based index (e.g., where the first element is 1, second is 2, etc.):
scala> val name = info._2 name: String = Korben
An alternate form of creating a 2-sized tuple is with the relation operator (->). This is a popular shortcut for representing key-value pairs in tuples:
scala> val red = "red" -> "0xff0000" red: (String, String) = (red,0xff0000)
scala> val reversed = red._2 -> red._1 reversed: (String, String) = (0xff0000,red)
Types | 25
Tuples provide a generic means to structure data, and are useful when you need to group discrete elements for handling.
scala>val info="sss" -> "hi"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Expressions and Conditionals
****************************************************************************************************************************************************
The term expression as used in this book indicates a unit of code that returns a value after it has been executed. One or more lines of code can be considered an expression if they are collected together using curly braces ({ and }). This is known as an expression block. Expressions provide a foundation for functional programming because they make it possible to return data instead of modifying existing data (such as a variable). This enables the use of immutable data, a key functional programming concept where new data is stored in new values instead of in existing variables.
The return values of expressions will be passed to other expressions or stored into values. As you migrate from using variables, your functions and expressions will have fewer side effects. In other words, they will purely act on the input you give them without affecting any data other than their return value. This is one of the main goals and benefits of functional programming
Defining Values and Variables with Expressions
-----------------------------------------------------------
Syntax: Defining Values and Variables, Using Expressions
val <identifier>[: <type>] = <expression> var <identifier>[: <type>] = <expression>
Expression Blocks
--------------------------
The last expression in the block is the return value for the entire block.
Expression blocks can span as many lines as you need. The preceding example could have been rewritten without the semicolons as follows:
scala> val amount = { | val x = 5 * 20 | x + 10 | }
amount: Int = 110 Expression blocks are also nestable, with each level of expression block having its own scoped values and variables.
Here is a short example demonstrating a three-deep nested expression block:
scala> { val a = 1; { val b = a * 2; { val c = b + 4; c } } }
res5: Int = 6
Statements
----------------
A statement is just an expression that doesnít return a value
scala> val x = 1
x: Int = 1
The REPL repeats the definition of x but there is not actual data returned that can be used to create a new value. A statement block, unlike an expression block, does not return a value. Because a state- ment block has no output, it is commonly used to modify existing data or make changes outside the scope of the application
If..Else Expression Blocks
------------------------------------
As a matter of practice you can write these same ìif .. else if .. elseî blocks in Scala and they will work just as you have experienced them in Java and other languages. As a matter of formal syntax, however, Scala only supports a single ìifî and optional ìelseî block, and does not recognize the ìelse ifî block as a single construct. So how do ìelse ifî blocks still work correctly in Scala? Because ìif .. elseî blocks are based on expression blocks, and expression blocks can be easily nested, an ìif .. else if .. elseî expression is equivalent to a nested ìif .. else { if .. else }î expression. Logically this is exactly the same as an ìif .. else if .. elseî block, and as a matter of syntax Scala recognizes the second ìif elseî as a nested expression of the outer ìif .. elseî block
If Expressions Syntax: Using an If Expression
if (<Boolean expression>) <expression>
If the Boolean expression returns false, what do you expect the if block to return?
scala> val result = if ( false ) "what does this return?"
result: Any = ()
The type of the result value in this example is unspecified so the compiler used type inference to determine the most appropriate type. Either a String or Unit could havebeen returned, so the compiler chose the root class Any. This is the one class common to both String (which extends AnyRef) and to Unit (which extends AnyVal).
If-Else Expressions Syntax: If .. Else Expressions
if (<Boolean expression>) <expression> else <expression>
Here is an example:
scala> val x = 10; val y = 20
x: Int = 10 y: Int = 20
scala> val max = if (x > y) x else y
max: Int = 20
--->Some wonder why Scala doesnít have a ternary expression (popular in C and Java) where the punctuation characters ? and : act as a one-line if and else expression
---> if expressions without an else should always use curly braces, because they tend to be statements that create side effects.
---->if..else blocks are a simple and common way to write conditional logic. There are other, more elegant ways to do so in Scala, however, using match expressions
----------------------------------------------------------
Match Expressions
---------------------------------------------------------
Match expressions are akin to Cís and Javaís ìswitchî statements, where a single input item is evaluated and the first pattern that is ìmatchedî is executed and its value re- turned.
--->in scala matching expressions are maily in two types and one is alternative expression.they are
---->In fact, most Scala developers prefer match expressions over ìif .. elseî blocks because of their expressiveness and concise syntax
Syntax: Using a Match Expressio<expression> match { case <pattern match> => <expression> [case...] }
scala> val status = 500
status: Int = 500
scala> val message = status match {
| case 200 => "ok"
| case 400 => {
| println("ERROR - we called the service incorrectly")
| "error"
|
}
|
case 500 => {
| println("ERROR - the service encountered an erro
| "error"
|
}
|
}
ERROR - the service encountered an error
message: String = error
There is no limit to the number of statements and expressions you can have inside a case block, although only the last expression will be used for the match expressionís return value.
Syntax: A Pattern Alternative
-------------------------------------
case <pattern 1> | <pattern 2> .. => <one or more expressions>
scala> val day = "MON" day: String = MON
scala> val kind = day match { | case "MON" | "TUE" | "WED" | "THU" | "FRI" => | "weekday" |
case "SAT" | "SUN" => | "weekend" |
}
kind: String = weekday
Matching with Wildcard Patterns
-------------------------------------------
Syntax: A Value Binding Pattern
case <identifier> => <one or more expressions>
Here is an example that tries to match a specific literal and otherwises uses value binding to ensure all other possible values are matched:
scala> val message = "Ok"
message: String = Ok
scala> val status = message match { | case "Ok" => 200 |
case other => { | println(s"Couldn't parse $other") | -1 |
}
|
}
status: Int = 200
The value other is defined for the duration of the case block and is assigned the value of message, the input to the match expression. The other type of wildcard pattern is the use of the wildcard operator. This is an un- derscore (_) character that acts as an unnamed placeholder for the eventual value of an expression at runtime. As with value binding, the underscore operator doesnít provide a pattern to match against, and thus it is a wildcard pattern that will match any input value.
Syntax: A Wildcard Operator Pattern
-----------------------------------------------
case _ => <one or more expressions>
The wildcard cannot be accessed on the right side of the arrow, unlike with value bind- ing. If you need to access the value of the wildcard in the case block, consider using a value binding, or just accessing the input to the match expression (if available).
Why an Underscore as a wildcard? Using underscores to indicate unknown values comes from the field of mathematics, arithmetic in particular, where missing amounts are denoted in problems with one or more underscores. For example, the equation 5 * _ = 15 is a problem that must be solved for _, the missing value.
Here is a similar example to the one earlier only
Here is a similar example to the one earlier only with a wildcard operator instead of a bound value:
scala> val message = "Unauthorized"
message: String = Unauthorized
scala> val status = message match { | case "Ok" => 200 | case _ => { | println(s"Couldn't parse $message") | -1 | } | } Couldn't parse Unauthorized status: Int = -1
In this case the underscore operator matches the runtime value of the input to the match expression.
However, it canít be accessed inside the case block as a bound value would, and thus the input to the match expression is used to create an informative println statement.
Matching with Pattern Guards
---------------------------------------
A pattern guard adds an if expression to a value-binding pattern, making it possible to mix conditional logic into match expressions. When a pattern guard is used the pattern will only be matched when the if expression returns true.
Syntax: A Pattern Guard
-------------------------------
case <pattern> if <Boolean expression> => <one or more expressions>
Unlike regular if expressions, the if expression here doesnít require parentheses (( and )) around its Boolean expression. Regular if expressions require the parentheses in order to simplify the job of parsing the full command and delineate the Boolean expression from the conditional expression. In this case the arrow (=>) handles that task and simplifies parsing. You can, however, add the parentheses around the Boolean ex- pression if you wish. Letís use a pattern guard to differentiate between a nonnull and a null response and report the correct message:
scala> val response: String = null
response: String = null
scala> response match { | case s if s != null => println(s"Received '$s'") | case s => println("Error! Received a null response") | }
Error! Received a null response
Matching Types with Pattern Variables
------------------------------------------------
Another way to do pattern matching in a match expression is to match the type of the input expression. Pattern variables, if matched, may convert the input value to a value with a different type. This new value and type can then be used inside the case block.
Syntax: Specifying a Pattern Variable
----------------------------------------------
case <identifier>: <type> => <one or more expressions>
port of polymorphic types in Scala should be a clue to a match expressionís utility. A value of type Int may get assigned to another value of type Any, or it may be returned as Any from a Java or Scala library call
Letís reproduce this situation by creating an Int, assigning it to an Any, and using a match expression to resolve its true type:
scala> val x: Int = 12180
x: Int = 12180
scala> val y: Any = x
y: Any = 12180
scala> y match { | case x: String => s"'x'" | case x: Double => f"$x%.2f" | case x: Float => f"$x%.2f" | case x: Long => s"${x}l" | case x: Int => s"${x}i" | }
res9: String = 12180i
Nested Iterators
-----------------------
Nested iterators are extra iterators added to a for-loop, multiplying the total number of iterations by their iterator count. They are called nested iterators because adding them to an existing loop has the same effect as if they were written as a separate nested loop. Because the total number of iterations is the product of all of the iterators, adding a nested loop that will iterate once will not change the number of iterations, whereas a nested loop that does not iterate will cancel all iterations
Here is an example of a for-loop with two iterators:
scala> for { x <- 1 to 2 | y <- 1 to 3 } | { print(s"($x,$y) ") }
scala>(1,1) (1,2) (1,3) (2,1) (2,2) (2,3)
Because the product of the two iterators is six iterations, the print statement is called six times.
While and Do/While Loops
---------------------------------------
In addition to for-loops Scala also supports ìwhileî and ìdo/whileî loops, which repeat a statement until a Boolean expression returns false. These are not as commonly used as for-loops in Scala, however, because they are not expressions and cannot be used to yield values.
Syntax: A While Loop
----------------------------
while (<Boolean expression>) statement
As a very simple example here is a while loop that decrements a number repeatedly until it is no longer greater than zero:
scala> var x = 10; while (x > 0) x -= 1
The ìdo/whileî loop is similar but the statement is executed before the Boolean expres- sion is first evaluated. In this example I have a Boolean expression that will return false, but is only checked after the statement has had a chance to run:
scala> val x = 0 x: Int = 0
scala> do println(s"Here I am, x = $x") while (x > 0)
Here I am, x = 0
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
FUNCTIONS
****************************************************************
---> Functions are the core building blocks of reusable logic.
----> In Scala, functions are named, reusable expressions. They may be parameterized and they may return a value, but neither of these features are required. These features are, however, useful for ensuring maximum reusability and composability. They will also help you write shorter, more readable, and more stable applications. Using parameter- ized functions you can normalize duplicated code, simplifying your logic and making it more discoverable. Testing your code becomes easier, because normalized and para- meterized logic is easier to test than denormalized logic repeated throughout your code
Syntax: Defining an Input-less Function (SOME TIMES ITS NOWN TO BE UNNUMOUS FUNCTIONS)
------------------------------------------------
def <identifier> = <expression> OR def() <identifier> = <expression>
NOTE:A Scala convention for input-less functions is that they should be defined with empty parentheses if they have side effects (i.e., if the function modifies data outside its scope). For example, an input-less function that writes a message to the console should be defined with empty parentheses
EXAMPLE:
--------------
scala> def hi = "hi"
hi: String
scala> hi
res0: String = hi
OR
scala> def hi(): String = "hi"
hi: ()String
scala> hi()
res1: String = hi
scala> hi r
es2: String = hi
Syntax: Defining a Function with a Return Type
-------------------------------------------------------------
def <identifier>: <type> = <expression>
scala> def hi: String = "hi"
hi: String
NAMED FUNCTIONS
------------------------------
Now weíre ready to look at a full function definition.
Syntax: Defining a Function
def <identifier>(<identifier>: <type>[, ... ]): <type> = <expression>
Letís try creating a function that performs an essential mathematical operation:
scala> def multiplier(x: Int, y: Int): Int = { x * y } //HERE multiplier is the function name
multiplier: (x: Int, y: Int)Int
scala> multiplier(6, 7)
res0: Int = 42
A common use of an early function exit is to stop further execution in the case of invalid or abnormal input values.
For example, this ìtrimî function validates that the input value is nonnull before calling the JVM Stringís ìtrimî method:
scala> def safeTrim(s: String): String = {
| if (s == null) return null
| s.trim()
|
}
safeTrim: (s: String)String
-----------------------
PROCEDURES
---------------------
A procedure is a function that doesnít have a return value
Here is a simple logging procedure, defined with an implicit return type and then with an explict return type:
scala> def log(d: Double) = println(f"Got value $d%.2f")
log: (d: Double)Unit
scala> def log(d: Double): Unit = println(f"Got value $d%.2f")
log: (d: Double)Unit
scala> log(2.23535)
Got value 2.24
deprecated syntax you will see for procedures is to define them without the Unit return type and without an equals sign before the pro- cedure body. With this syntax the example log() method would be written like this:
scala> def log(d: Double) { println(f"Got value $d%.2f") }
log: (d: Double)Unit
-------------------------------------------------------
Function Invocation with Expression Blocks
-------------------------------------------------------
Function Invocation with Expression Blocks
One example where using an expression block to invoke a function may be preferable is when you have to send a calculated value to the function. Instead of calculating the amount and storing it in local values to be passed to the function, you can do the cal- culations inside the expression block. The expression block will be evaluated before the function is called and the blockís return value will be used as the function argument. Here is an example of calculating values inside a function block used for invoking a function:
scala> def formatEuro(amt: Double) = f"Ä$amt%.2f"
formatEuro: (amt: Double)String
scala> formatEuro(3.4645)
res4: String = Ä3.46
scala> formatEuro { val rate = 1.32; 0.235 + 0.7123 + rate * 5.32 }
res5: String = Ä7.97
-------------------------
Recursive Functions
-----------------------------
A recursive function is one that may invoke itself, preferably with some type of parameter or external condition that will be checked to avoid an infinite loop of function invoca- tion. Recursive functions are very popular in functional programming because they offer a way to iterate over data structures or calculations without using mutable data, because each function call has its own stack for storing function parameters. Hereís an example of a recursive function that raises an integer by a given positive exponent:
scala> def power(x: Int, n: Int): Long = {
| if (n >= 1) x * power(x, n-1)
| else 1
|
}
power: (x: Int, n: Int)Long
scala> power(2, 8)
res6: Long = 256
scala> power(2, 1)
res7: Long = 2
scala> power(2, 0)
res8: Long = 1
One problem with using recursive functions is running into the dreaded ìStack Over- flowî error, where invoking a recursive function too many times eventually uses up all of the allocated stack space. To prevent this scenario, the Scala compiler can optimize some recursive functions with tail-recursion so that recursive calls do not use additional stack space. With tail- recursionñoptimized functions, recursive invocation doesnít create new stack space but instead uses the current functionís stack space. Only functions whose last statement is the recursive invocation can be optimized for tail-recursion by the Scala compiler. If the result of invoking itself is used for anything but the direct return value, a function canít be optimized. Fortunately there is a function annotation available to mark a function as being intended to be optimized for tail-recursion. A function annotation is special syntax carried over
50 | Chapter 4: Functions
from the Java programming language where the ìatî sign (@) and an annotation type is placed before a function definition to mark it for special use. A function marked with the tail-recursion function annotation will cause an error at compilation time if it cannot be optimized for tail-recursion.
To mark a function as intended for tail-recursion, add the text @annotation.tailrec before the function definition or on the previous line. Hereís the same example again only marked with the ìtailrecî annotation, to let the Scala compiler know we expect it to be optimized for tail-recursion and that if it cannot be, the compiler should treat it as an error:
scala> @annotation.tailrec
| def power(x: Int, n: Int): Long = { | if (n >= 1) x * power(x, n-1) | else 1 | }
<console>:9: error: could not optimize @tailrec annotated method power: it contains a recursive call not in tail position
if (n >= 1) x * power(x, n-1) Ah,
the function couldnít be optimized because the recursive call is not the last statement in the function. This is understandable. Iíll switch the ìifî and ìelseî conditions and try again:
scala> @annotation.tailrec
| def power(x: Int, n: Int): Long = { | if (n < 1) 1 | else x * power(x, n-1) | }
<console>:11: error: could not optimize @tailrec annotated method power: it contains a recursive call not in tail position
else x * power(x, n-1)
^ Hmm, the recursive call is the last item in the function. Oh I see, weíre taking the result of the recursive call and multiplying it by a value, so that multiplication is actually the last statement in the function, not the recursive call. A good way to fix this is to move the multiplication into the beginning of the invoked function instead of multiplying its result. Now the end of the function is a simple un- touched result from the recursive call:
scala> @annotation.tailrec | def power(x: Int, n: Int, t: Int = 1): Int = { | if (n < 1) t | else power(x, n-1, x*t) | }
power: (x: Int, n: Int, t: Int)Int
Recursive Functions | 51
scala> power(2,8)
res9: Int = 256 Success!
The ìtailrecî annotation and successful compile guarantees that the function will be optimized with tail-recursion, so that each successive call will not add more stack frames. Although this example may seem challenging to get through, recursion and tail- recursion are still valuable methods for iterating without using mutable data. Youíll find that many of the data structures weíll explore later in the book will be rich with functions that are implemented with tail-recursion.
-----------------------
Nested Functions
--------------------------
Functions are named, parameterized expression blocks and expression blocks are ne- stable, so it should be no great surprise that functions are themselves nestable. There are times when you have logic that needs to be repeated inside a method, but would not benefit from being extrapolated to an external method. In these cases defining an internal function inside another function, to only be used in that function, may be worthwhile. Letís have a look at a method that takes three integers and returns the one with the highest value:
scala> def max(a: Int, b: Int, c: Int) = { | def max(x: Int, y: Int) = if (x > y) x else y | max(a, max(b, c)) | }
max: (a: Int, b: Int, c: Int)Int
scala> max(42, 181, 19)
res10: Int = 181
The logic inside the max(Int, Int) nested function was defined once but used twice inside the outer function, making it possible to reduce duplicated logic and simplify the overall function. The nested function here has the same name as its outer function, but because their parameters are different (the nested one only takes two integers) there is no conflict between them. Scala functions are differentiated by their name and the list of their parameter types. However, even if the names and parameter types were the same there would be no confict because the local (nested) one takes precedence over the outer one
NOTE: we must be variate the function name in inner and outer functions by applay the diffrent function name or diffrent parameterrs.
----------------------------------------------------
Calling Functions with Named Parameters
-----------------------------------------------------
Syntax: Specifying a Parameter by Name
<function name>(<parameter> = <value>)
In this example, a simple two-parameter function is invoked twice, first using the con- vention of specifying parameters by their order and then by assigning values by parameter name:
scala> def greet(prefix: String, name: String) = s"$prefix $name"
greet: (prefix: String, name: String)String
scala> val greeting1 = greet("Ms", "Brown")
greeting1: String = Ms Brown
scala> val greeting2 = greet(name = "Brown", prefix = "Mr")
greeting2: String = Mr Brown
------------------------------------------------------------------------
Parameters with Default Values or Function Overloading
------------------------------------------------------------------------
A common problem when defining functions is deciding which input parameters they should take to maximize reuse. In Scala, Java, and other languages, a common solution is to provide multiple versions of the same function with the same name but different lists of input parameters. This practice is known as function overloading due to the functionís name being reused for different inputs. The common practice is to copy a function with x number of parameters to a new function with xñ1 parameters that invokes the original function using a default value for the missing parameter.
Syntax: Specifying a Default Value for a Function Parameter
def <identifier>(<identifier>: <type> = <value>): <type>
scala> def greet(prefix: String = "", name: String) = s"$prefix$name"
greet: (prefix: String, name: String)String
scala> val greeting1 = greet(name = "Paul")
greeting1: String = Paul
ala> def greet(name: String, prefix: String = "") = s"$prefix$name"
greet: (name: String, prefix: String)String
scala> val greeting2 = greet("Ola")
greeting2: String = Ola
-------------------------
Vararg Parameters
--------------------------
scala also supports vararg parameters, so you can define a function with a variable number of input arguments. The vararg parameter cannot be followed by a nonvararg parameter because there would be no way to distinguish them. Inside the function, the vararg parameter, implemented as a collection (which weíll study in Chapter 6), can be used as an iterator in for loops. To mark a function parameter as matching one or more input arguments, add an asterisk symbol (*) after the parameterís type in the function definition. Here is an example of using a vararg parameter to create a summing function that returns a sum of all of its input integers:
scala> def sum(items: Int*): Int = { | var total = 0 | for (i <- items) total += i | total | }
sum: (items: Int*)Int
scala> sum(10, 20, 30)
res11: Int = 60
scala> sum()
res12: Int = 0
-----------------------------------------------------------------------------------
Parameter Groups OR Curried Functions
------------------------------------------------------------------------------------
So far we have looked at parameterized function definitions as a list of parameters surrounded by parentheses. Scala provides the option to break these into groups of parameters, each separated with their own parentheses. Here is an example of the ìmaxî function where the two input parameters have been split into their own parameter groups:
scala> def max(x: Int)(y: Int) = if (x > y) x else y
max: (x: Int)(y: Int)Int
scala> val larger = max(20)(39)
larger: Int = 39
*******************************************************************************
-----------------------------------------------------------------------------------------------------------------------
FIRST CLASS FUNCTION
-----------------------------------------------------------------------------------------------------------------------
---> Functions that accept other functions as parameters and/or use functions as return values are known as higher-order functions. You may have heard of two of the most famous higher-order functions, map() and reduce().
-->Scala has full support for first-class functions, higher-order functions, and the use of declarative programming. As with other types of data such as String or Int, functions have types based on the types of their input arguments and their return value. A function can be stored in a value or variable, passed to a function, returned from a function, and used with data structures to support map(), reduce(), fold(), and filter(), among many other higher-order functions.
--------------------------------------------------------------------------------------------------------------
Function Types and Values
---------------------------------------------------------------------------------------------------------------
The type of a function is a simple grouping of its input types and return value type, arranged with an arrow indicating the direction from input types to output type.
Syntax: A Function Type
([<type>, ...]) => <type>
For example, the function def double(x: Int): Int = x * 2 has the function type Int => Int, indicating that it has a single Int parameter and returns an Int. The function name, ìdouble,î is an identifier and isnít part of the type. The body of the function, a simple multiplication of the input by 2, does not affect the type of the func- tion. The rest of the information is the input types and return type, and so these make up the function type itself.
Letís try using a function type in the REPL, by creating a function and then assigning it to a function value:
scala> def double(x: Int): Int = x * 2
double: (x: Int)Int
scala> double(5)
res0: Int = 10
scala> val myDouble: (Int) => Int = double
myDouble: Int => Int = <function1>
scala> myDouble(5)
res1: Int = 10
Syntax: Assigning a Function with the Wildcard Operator
----------------------------------------------------------------------
val <identifier> = <function name> _
Letís try this out with the ìmyDoubleî function value:
scala> def double(x: Int): Int = x * 2
double: (x: Int)Int
scala> val myDouble = double _
myDouble: Int => Int = <function1>
scala> val amount = myDouble(20)
amount: Int = 40
-->This time, the explicit function type for myDouble wasnít required to distinguish it from a function invocation. The underscore (_) served as a placeholder for a future invocation of the function, returning a function value that we could store in myDouble
Hereís an example of a function value defined with an explicit function type using mul- tiple parameters, enclosed with parentheses:
scala> def max(a: Int, b: Int) = if (a > b) a else b
max: (a: Int, b: Int)Int
scala> val maximize: (Int, Int) => Int = max maximize: (Int, Int) => Int = <function2>
scala> maximize(50, 30)
res3: Int = 50
HIGHORDER FUNCTION SAMPLE EXAMPLE
---------------------------------------------------------------
scala> def logStart() = "=" * 50 + "\nStarting NOW\n" + "=" * 50
logStart: ()String
scala> val start: () => String = logStart
start: () => String = <function0>
scala> println( start() ) ==================================================
Starting NOW
==================================================
---->This was a gentle introduction to how functions may be treated as data, by storing them in values and assigning them static types
--->the next few sections on higher-order functions and function literals are going to build on this knowledge and cover some challenging new syntax
------------------------------------------------------------
Higher-Order Functions
---------------------------------------------------------------
DESCRIPTION: We have already defined values that have a function type. A higher-order function is a function that has a value with a function type as an input parameter or return value.
EXAMPLE:
---------------
scala> def safeStringOp(s: String, f: String => String) = {
| if (s != null) f(s) else s
|
}
safeStringOp: (s: String, f: String => String)String
scala> def reverser(s: String) = s.reverse
reverser: (s: String)String
scala> safeStringOp(null, reverser)
res4: String = null
scala> safeStringOp("Ready", reverser)
res5: String = ydaeR
--->The call with ìnullî safely returned the same value back, whereas the call with a valid String returned the reverse of the input value
---------------------------------------
Function Literals
---------------------------------------
Now weíll tackle a difficult concept covered by a variety of names by starting with an easy example. In this example we will create a function literal, a working function that lacks a name, and assign it to a new function value:
scala> val doubler = (x: Int) => x * 2 doubler: Int => Int = <function1>
scala> val doubled = doubler(22)
doubled: Int = 44
The function literal in this example is the syntax (x: Int) => x * 2, which defines a typed input argument (x) and the function body (x * 2). Function literals can be stored in function values and variables, or defined as part of a higher-order function invocation. You can express a function literal in any place that accepts a function type.
Syntax: Writing a Function Literal
-------------------------------------------
([<identifier>: <type>, ... ]) => <expression>
Letís define a function value and assign it a new function literal:
scala> val greeter = (name: String) => s"Hello, $name"
greeter: String => String = <function1>
scala> val hi = greeter("World")
hi: String = Hello, World
If you think about it, a function literal is essentially a parameterized expression. We know about expressions that return a value, but now have a way to parameterize their input.
-------------------------------------------
Placeholder Syntax
------------------------------------------
Placeholder syntax is a shortened form of function literals, replacing named parameters with wildcard operators (_). It can be used when
(a) the explicit type of the function is specified outside the literal and
(b) the parameters are used no more than once
Here is an example of a doubling function literal using wildcard operators in place of named parameters:
scala> val doubler: Int => Int = _ * 2
doubler: Int => Int = <function1>
--->Placeholder syntax is valid here because the input parameter is only used once and the literalís type has an external explicit definition (in the value).
Letís demonstrate how this ordering of placeholders works by trying an example with two placeholders:
scala> def combination(x: Int, y: Int, f: (Int,Int) => Int) = f(x,y)
combination: (x: Int, y: Int, f: (Int, Int) => Int)Int
scala> combination(23, 12, _ * _)
res13: Int = 276
Letís kick the number of placeholders up for the last time, from two to three. Is this example still easily readable?
scala> def tripleOp(a: Int, b: Int, c: Int, f: (Int, Int, Int) => Int) = f(a,b,c)
tripleOp: (a: Int, b: Int, c: Int, f: (Int, Int, Int) => Int)Int
scala> tripleOp(23, 92, 14, _ * _ + _)
res14: Int = 2130
--->The tripleOp function takes four parameters: three Int values and a function that can reduce them down to a single Int. The actual function body is far shorter than the parameter list, and applies the function to the input values.
--->This example function, tripleOp, is limited to integer values. However, wouldnít it be more useful if it was generic and supported type parameters?
one for the common input type and one for the single return value type. This will give us some flexibility to call the tripleOp function with any type of inputs or anonymous functions that
scala> def tripleOp[A,B](a: A, b: A, c: A, f: (A, A, A) => B) = f(a,b,c)
tripleOp: [A, B](a: A, b: A, c: A, f: (A, A, A) => B)B
scala> tripleOp[Int,Int](23, 92, 14, _ * _ + _)
res15: Int = 2130
scala> tripleOp[Int,Double](23, 92, 14, 1.0 * _ / _ / _)
res16: Double = 0.017857142857142856
scala> tripleOp[Int,Boolean](93, 92, 14, _ > _ + _)
res17: Boolean = false
***USEAGE***
---------------------
Placeholder syntax is especially helpful when working with data structures and collec- tions. Many of the core sorting, filtering, and other data structure methods tend to use first-class functions, and placeholder syntax reduces the amount of extra code required to call these methods.
---------------------------------------------------------------------------------------------------------------------
Partially Applied Functions and Currying
----------------------------------------------------------------------------------------------------------------------
What if you wanted to reuse a function invocation and retain some of the parameters to avoid typing them in again?
A cleaner way to partially apply functions is to use functions with multiple parameter lists. Instead of breaking up a parameter list into applied and unapplied parameters, apply the parameters for one list while leaving another list unapplied. This is a technique known as currying the function
To demonstrate this answer, Iíll use the example of a two-parameter function that checks if a given number is a factor of the other number:
scala> def factorOf(x: Int, y: Int) = y % x == 0
factorOf: (x: Int, y: Int)
Boolean If you want a shortcut to the function without retaining any parameters, you can use the wildcard operator (_) assignment we covered in this chapterís introduction:
scala> val f = factorOf _
f: (Int, Int) => Boolean = <function2>
scala> val x = f(7, 20)
x: Boolean = false
If you want to retain some of the parameters, you can partially apply the function by using the wildcard operator to take the place of one of the parameters. The wildcard operator here requires an explicit type, because it is used to generate a function value with a declared input type:
scala> val multipleOf3 = factorOf(3, _: Int)
multipleOf3: Int => Boolean = <function1>
scala> val y = multipleOf3(78)
y: Boolean = true
The new function value, multipleOf3, is a partially applied function, because it contains some but not all of the parameters for the factorOf() function
-------------------------------------------------------------------------------------
Invoking Higher-Order Functions with Function Literal Blocks
-------------------------------------------------------------------------------------
a higher-order function can wrap a given expression block in a single database session or transaction
Iíll use the ìsafeStringOpsî function to demonstrate when this syntax may be desirable and how to use it. To start, here is the ìsafeStringOpsî function used with a regular function literal, before converting it to the desired syntax:
scala> def safeStringOp(s: String, f: String => String) = { | if (s != null) f(s) else s | } safeStringOp: (s: String, f: String => String)String
scala> val uuid = java.util.UUID.randomUUID.toString
uuid: String = bfe1ddda-92f6-4c7a-8bfc-f946bdac7bc9
scala> val timedUUID = safeStringOp(uuid, { s => | val now = System.currentTimeMillis | val timed = s.take(24) + now | timed.toUpperCase | })
timedUUID: String = BFE1DDDA-92F6-4C7A-8BFC-1394546043987
A UUID utility in Javaís java.util package, accessible (as are all JDK classes) from Scala. System.currentTimeMillis provides the epoch time (elapsed time since January 1, 1970 GMT) in milliseconds, useful for creating timestamps. The take(x) method returns the first x items from the String, in this case the first four sections of the UUID
*******************************************************************************
---------------------------------------------------------------------------------------------------------------------
Common Collections
----------------------------------------------------------------------------------------------------------------------
A collections framework provides data structures for collecting one or more values of a given type such as arrays, lists, maps, sets, and trees. Most of the popular programming languages have their own collections framework (or, at the least, lists and maps) because these data structures are the building blocks of modern software projects.
---> Because Scala is a JVM language, you can access and use the entire Java collections library from your Scala code.
--> which make switching between immutable data (for stability) and mutable data (when necessary) convenient.
---.The root of all iterable collections, Iterable, provides a common set of methods for (you guessed it) iterating through and manipulating collection data
-------------------------------------------------
Lists, Sets, and Maps
--------------------------------------------------
Letís start with the List type, an immutable singly linked list. You can create a list by invoking it as a function, passing in its contents in the form of comma-separated pa- rameters:
scala> val numbers = List(32, 95, 24, 21, 17)
numbers: List[Int] = List(32, 95, 24, 21, 17)
scala> val colors = List("red", "green", "blue")
colors: List[String] = List(red, green, blue)
scala> println(s"I have ${colors.size} colors: $colors")
I have 3 colors: List(red, green, blue)
--->The size method, available on all collections and String instances, returns the number of items in the collection. Defined without parentheses the size method is simply invoked by name
Use the Lisp-style head() and tail() methods to access the first and remaining ele- ments of a list, respectively. To access a single element directly, invoke the list as a func- tion and pass it the zero-based index of that element:
scala> val colors = List("red", "green", "blue")
colors: List[String] = List(red, green, blue)
scala> colors.head res0: String = red
scala> colors.tail
res1: List[String] = List(green, blue)
scala> colors(1)
res2: String = green
scala> colors(2)
res3: String = blue
------------------------------------------
USING RANGE COLLECTION
------------------------------------------
Letís try out using for-loops to iterate over the ìnumbersî and ìcolorsî lists:
scala> val numbers = List(32, 95, 24, 21, 17)
numbers: List[Int] = List(32, 95, 24, 21, 17)
scala> var total = 0; for (i <- numbers) { total += i }
total: Int = 189
scala> val colors = List("red", "green", "blue")
colors: List[String] = List(red, green, blue)
scala> for (c <- colors) { println(c) }
red
green
blue
Hereís an example of the foreach(), map(), and reduce() higher-order functions avail- able in List and other collections. Respectively, these functions iterate over the list, convert the list, and reduce the list down to a single item. For each method, a function literal is passed in, including the input parameter in parentheses and its function body:
scala> val colors = List("red", "green", "blue")
colors: List[String] = List(red, green, blue)
scala> colors.foreach( (c: String) => println(c) )
red
green
blue
scala> val sizes = colors.map( (c: String) => c.size )
sizes: List[Int] = List(3, 5, 4)
scala> val numbers = List(32, 95, 24, 21, 17)
numbers: List[Int] = List(32, 95, 24, 21, 17)
scala> val total = numbers.reduce( (a: Int, b: Int) => a + b )
total: Int = 189
foreach() takes a function (a procedure, to be accurate) and invokes it with every item in the list. map() takes a function that converts a single list element to another value and/or type. reduce() takes a function that combines two list elements into a single element
--------------------------------------
MAP
--------------------------------------
A Map is an immutable key-value store, also known as a hashmap, dictionary, or asso- ciative array in other languages. Values stored in a Map with a given unique key may beretrieved using that key. The key and the value are type-parameterized; you can just as easily create a mapping from strings to integers as a mapping from integers to strings.
When creating a Map, specify the key-value pairs as tuples (see ìTuplesî on page 25). You can use the relation operator (->) to specify the key and value tuple.
EXAMPLE
----------------
scala> val colorMap = Map("red" -> 0xFF0000, "green" -> 0xFF00, "blue" -> 0xFF) colorMap: scala.collection.immutable.Map[String,Int] = Map(red -> 16711680, green -> 65280, blue -> 255)
scala> val redRGB = colorMap("red")
redRGB: Int = 16711680
scala> val cyanRGB = colorMap("green") | colorMap("blue")
cyanRGB: Int = 65535
scala> val hasWhite = colorMap.contains("white") hasWhite: Boolean = false
scala> for (pairs <- colorMap) { println(pairs) }
(red,16711680)
(green,65280)
For example, you can create a collection of collections:
scala> val oddsAndEvents = List(List(1, 3, 5), List(2, 4, 6))
oddsAndEvents: List[List[Int]] = List(List(1, 3, 5), List(2, 4, 6))
Or you can have a collection of 2-sized tuples, and create a List that looks similar to a Map:
scala> val keyValues = List(('A', 65), ('B',66), ('C',67))
keyValues: List[(Char, Int)] = List((A,65), (B,66), (C,67)) (blue,255)
You can access a single element from a list by invoking it as a function with a (zero- based) index number. Here is an example of accessing the first and fourth elements of a List by their index:
scala> val primes = List(2, 3, 5, 7, 11, 13)
primes: List[Int] = List(2, 3, 5, 7, 11, 13)
scala> val first = primes(0)
first: Int = 2
scala> val fourth = primes(3)
fourth: Int = 7
You can decompose a list into its head, the first item in the list, and its tail, the remaining items in the list:
scala> val first = primes.head
first: Int = 2
scala> val remaining = primes.tail
remaining: List[Int] = List(3, 5, 7, 11, 13)
Creating a new, empty list will actually return Nil instead of a fresh instance. Because Nil is immutable, there is essentially no difference between it and a fresh, empty list instance. Likewise, creating a new list that has a single entry just creates a single list element that points to Nil as its tail.
Letís demonstrate these points with some examples:
scala> val l: List[Int] = List()
l: List[Int] = List()
scala> l == Nil
res0: Boolean = true
scala> val m: List[String] = List("a")
m: List[String] = List(a)
scala> m.head res1: String = a
scala> m.tail == Nil res2: Boolean = true
I have used lists of two explicit types, Int and String, to demonstrate that regardless of the type of their data, a List will always end with Nil.
--------------------------------------
The Cons Operator (::)
---------------------------------------
There is an alternate way to construct lists that takes advantage of this relationship with Nil. As another nod to Lisp, Scala supports use of the cons (short for construct) operator to build lists. Using Nil as a foundation and the right-associative cons operator :: for binding elements, you can build a list without using the traditional List(Ö) format.
Here is an example of building a list with the cons operator:
scala> val numbers = 1 :: 2 :: 3 :: Nil numbers: List[Int] = List(1, 2, 3)
You could use traditional dot notation with the cons operator, but it would look a bit odd. OK, letís try it out anyway:
scala> val first = Nil.::(1) first: List[Int] = List(1)
scala> first.tail == Nil res3: Boolean = true
This time we will use it to prepend (insert at the front) a value to an existing list, thus creating a brand new list. We have actually tried this before, because it is the same as the operation of prepending a value to Nil, the empty list:
scala> val second = 2 :: first second: List[Int] = List(2, 1)
scala> second.tail == first
res4: Boolean = true
Although the ìsecondî list includes the ìfirstî list, both are valid lists that can be used independently. This example of building one list by adding a value to another demon- strates the recursive and reusable nature of Scalaís immutable lists, and provides a good summary of this section
------------------------------------------------
List Arithmetic
-------------------------------------------------
In this section we will focus on basic arithmetic oper- ations on lists. By ìarithmetic,î a term I use loosely, I mean operations that add, remove, split, combine, and otherwise modify the organization of lists without changing the list elements (i.e., their contents) themselves. And of course by ìmodifyî I mean ìreturn a new list with the requested changesî because List is an immutable collection
Table 6-1. Arithmetic operations on lists
---------------------------------------------------
Name Example Description
---------------------------------------------------------------------------------------------------------------
:: 1 :: 2 :: Nil Appends individual elements to this list. A right- associative operator.
::: List(1, 2) ::: List(2, 3) Prepends another list to this one. A right- associative operator.
++ List(1, 2) ++ Set(3, 4, 3) Appends another collection to this list.
== List(1, 2) == List(1, 2) Returns true if the collection types and contents are equal.
distinct List(3, 5, 4, 3, 4).distinct Returns a version of the list without duplicate elements.
drop List('a', 'b', 'c', 'd') drop 2 Subtracts the first n elements from the list.
filter List(23, 8, 14, 21) filter (_ > 18) Returns elements from the list that pass a true/ false function.
flatten List(List(1, 2), List(3, 4)).flatten Converts a list of lists into a single list of elements.
partition List(1, 2, 3, 4, 5) partition (_ < 3) Groups elements into a tuple of two lists based on the result of a true/false function
reverse List(1, 2, 3).reverse Reverses the list.
slice List(2, 3, 5, 7) slice (1, 3) Returns a segment of the list from the first index up to but not including the second index
sortBy List("apple", "to") sortBy (_.size) Orders the list by the value returned from he given function.
sorted List("apple", "to").sorted Orders a list of core Scala types by their natural value
splitAt List(2, 3, 5, 7) splitAt 2 Groups elements into a tuple of two lists based on if they fall before or after the given index
take List(2, 3, 5, 7, 11, 13) take 3 Extracts the first n elements from the list.
zip List(1, 2) zip List("a", "b") Combines two lists into a list of tuples of elements at each index.
-----------------------------------------------------------------------------------------------------------------------
NOTE: some examples used operator notation (e.g., list drop 2) whereas others used dot notation (e.g., list.flatten). Selecting the right notation is a personal choice, except where dot notation is required due to lack of an operation parameter (as in list.flatten).
Collection methods that are also higher-order functions, such as filter, map, and partition, are excellent candidates for using placeholder syntax. The function param- eter they take as input acts on a single element in their list. Thus the underscore (_) in an anonymous function sent to one of these methods represents each item in the list
An important point to make about these arithmetic methods is that ::, drop, and take act on the front of the list and thus do not have performance penalties. Recall that List is a linked list, so adding items to or removing items from its front does not require a full traversal. A list traversal is a trivial operation for short lists, but when you start getting into lists of thousands or millions of items, an operation that requires a list traversal can be a big deal
The corollary operations to ::, drop, and take are +: (a left-associative operator), dropRight, and takeRight. The arguments to these operators are the same as to their corollary operations. Here are examples of these list-appending operations:
scala> val appended = List(1, 2, 3, 4) :+ 5
appended: List[Int] = List(1, 2, 3, 4, 5)
scala> val suffix = appended takeRight 3
suffix: List[Int] = List(3, 4, 5)
scala> val middle = suffix dropRight 2
middle: List[Int] = List(3)
-----------------------------------
MAP
------------------------------------
Map methods are those that take a function and apply it to every member of a list, collecting the results into a new list. In set theory, and the field of mathematics in general, to map is to create an assocation between each element in one set to each element in another set. In a sense, both definitions describe what the map methods in List are doing: mapping each item from one list to another list, so that the other list has the same size as the first but with different data or element types.
List mapping operations
-------------------------------
Name Example Description
-------------------------------------------------------------------------------------------------------------------
collect List(0, 1, 0) collect {case 1 => "ok"} Transforms each element using a partial function, retaining applicable elements
flatMap List("milk,tea") flatMap (_.split(',')) Transforms each element using the given function and ìflattensî the list of results into this list.
map List("milk","tea") map (_.toUpperCase) Transforms each element using the given function
---------------------------------------------------------------------------------------------------------------------
Letís see how these list-mapping operators work in the REPL:
scala> List(0, 1, 0) collect {case 1 => "ok"}
res0: List[String] = List(ok)
scala> List("milk,tea") flatMap (_.split(','))
res1: List[String] = List(milk, tea)
scala> List("milk","tea") map (_.toUpperCase)
res2: List[String] = List(MILK, TEA)
The flatMap example uses the String.split() method to convert pipe-delimited text into a list of strings. Specifically this is the java.lang.String.split() method, and it returns a Java array, not a list. Fortunately, Scala converts Java arrays to its own type, Array, which extends Iterable. Because List is also a subtype of Iterable, and the flatMap method is defined at the Iterable level, a list of string arrays can be safely flattened into a list of strings.
------------------------------------------------------------------
reducing operations
------------------------------------------------------------------
Scalaís collections support mathematical reduction operations (e.g., finding the sum of a list) and Boolean reduction operations (e.g., determining if a list contains a given element). They also support generic higher-order operations known as folds that you can use to create any other type of list reduction algorithm.
Math reduction operations
----------------------------------
Name Example Description
-------------------------------------------------------------------------------------------------------------
max List(41, 59, 26).max Finds the maximum value in the list.
min List(10.9, 32.5, 4.23, 5.67).min Finds the minimum value in the list.
product List(5, 6, 7).product Multiplies the numbers in the list.
sum List(11.3, 23.5, 7.2).sum Sums up the numbers in the list.
Boolean reduction operations
---------------------------------------
contains List(34, 29, 18) contains 29 Checks if the list contains this element
endsWith List(0, 4, 3) endsWith List(4, 3) Checks if the list ends with a given list.
exists List(24, 17, 32) exists (_ < 18) Checks if a predicate holds true for at least one element in the list.
forall List(24, 17, 32) forall (_ < 18) Checks if a predicate holds true for every element in the list.
startsWith List(0, 4, 3) startsWith List(0) Tests whether the list starts with a given list.
-----------------------------------------------------
converting collections
-----------------------------------------------------
Because a List.toList() operation would be silly (but possible), the examples demonstrate converting from one type to a completely different type.
-------------------------------------------------
Operations to convert collections
----------------------------------------------
Name Example Description
--------------------------------------------------------------------------------------------------------------------
mkString List(24, 99, 104).mkString(", ") Renders a collection to a Set using the given delimiters
toBuffer List('f', 't').toBuffer Converts an immutable collection to a mutable one.
toList Map("a" -> 1, "b" -> 2).toList Converts a collection to a List.
toMap Set(1 -> true, 3 -> true).toMap Converts a collection of 2-arity (length) tuples to a Map.
toSet List(2, 5, 5, 3, 2).toSet Converts a collection to a Set.
toString List(2, 5, 5, 3, 2).toString Renders a collection to a String, including the collectionís type.
-----------------------------------------------------------
pattern matching with collection
-------------------------------------------------------------
scala> val statuses = List(500, 404) s
tatuses: List[Int] = List(500, 404)
scala> val msg = statuses.head match {
| case x if x < 500 => "okay"
| case _ => "whoah, an error"
|
}
msg: String = whoah, an error With a pattern guard (see ìMatching with Pattern Guardsî on page 36), you could also match a single value inside a collection:
scala> val msg = statuses match { | case x if x contains(500) => "has error" | case _ => "okay" | }
msg: String = has error
scala> val msg = statuses match { case List(404, 500) => "not found & error" | case List(500, 404) => "error & not found" | case List(200, 200) => "okay" | case _ => "not sure what happened" | }
msg: String = error & not found
*******************************************************************************
MORE COLLECTIONS
**************************************
Weíll start with mutable collections, which probably can be considered ubiquitous be- cause more languages support them than they do immutable collections. Then weíll move on to arrays, streams, and other collections
---------------------------------------
Mutable Collections
---------------------------------------
The List, Set, and Map immutable collections we are familiar with cannot be changed after they have been created (see the definition of ìimmutableî). They can, however, be transformed into new collections. For example, we can create an immutable map, and then transform it by removing one mapping and adding another:
scala> val m = Map("AAPL" -> 597, "MSFT" -> 40)
m: scala.collection.immutable.Map[String,Int] = Map(AAPL -> 597, MSFT -> 40)
scala> val n = m - "AAPL" + ("GOOG" -> 521)
n: scala.collection.immutable.Map[String,Int] = Map(MSFT -> 40, GOOG -> 521)
scala> println(m)
Map(AAPL -> 597, MSFT -> 40) A new map with ìAAPLî and ìMSFTî keys. Removing ìAPPLî and adding ìGOOGî gives us a different collectionÖ Ö
while the original collection in ìmî remains the same.
107
What you end up with is a completely new collection stored in ìnî. The original collec- tion, stored in the ìmî value, remains untouched. And this is exactly the point of im- mutable data, namely that data and data structures should not be mutable or change their state in order to improve code stability and prevent bugs. As an example, data structures that are rigid and never change state are safer to use with concurrent code than data structures that may change at any point and are prone to corruption (e.g., reading a data structure while it is undergoing a state change).
---------------------------------------------
Creating New Mutable Collections
---------------------------------------------
The most straightforward way to modify collections is with a mutable collection type. See Table 7-1 for the mutable counterparts(meening PRATIRUPALU) to the standard immutable List, Map, and Set types.
---------------------------------------------------------------------------------------------------
Table 7-1. Mutable collection types
---------------------------------------------------------------------------------------------------
Immutable type Mutable counterpart
-------------------- --------------------------------------------
collection.immutable.List collection.mutable.Buffer
collection.immutable.Set collection.mutable.Set
collection.immutable.Map collection.mutable.Map
----------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------
Whereas the collection.immutable package is automatically added to the current namespace in Scala, the collection.mutable package is not. When creating mutable collections, make sure to include the full package name for the type.
The collection.mutable.Buffer type is a general-purpose mutable sequence, and supports adding elements to its beginning, middle, and end.
Example
----------
scala> val nums = collection.mutable.Buffer(1)
nums: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1)
scala> for (i <- 2 to 10) nums += i
scala> println(nums)
Buffer(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
You can convert your mutable buffer back to an immutable list at any time with the toList method:
scala> println(nums)
Buffer(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> val l = nums.toList l:
List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
Likewise for sets and maps, use the toSet and toMap methods to convert these mutable collections to their immutable counterparts.
Here is an example of converting an immutable map to a mutable one and then changing it back:
scala> val m = Map("AAPL" -> 597, "MSFT" -> 40)
m: scala.collection.immutable.Map[String,Int] = Map(AAPL -> 597, MSFT -> 40)
scala> val b = m.toBuffer
b: scala.collection.mutable.Buffer[(String, Int)] = ArrayBuffer((AAPL,597), (MSFT,40))
scala> b trimStart 1
scala> b += ("GOOG" -> 521)
ArrayBuffer((MSFT,40), (GOOG,521))
scala> val n = b.toMap
n: scala.collection.immutable.Map[String,Int] = Map(MSFT -> 40, GOOG -> 521)
The map, containing key-value pairs, is now a sequence of tuples. trimStart removes one or more items from the start of the buffer. After removing the ìAAPLî entry weíll add a ìGOOGî entry. This buffer of tuples is now an immutable map again
---------------------------------
Using Collection Builders
----------------------------------
A Builder is a simplified form of a Buffer, restricted to generating its assigned collec- tion type and supporting only append operations.
To create a builder for a specific collection type, invoke the typeís newBuilder method and include the type of the collectionís element. Invoke the builderís result method to convert it back into the final Set. Here is a
scala> val b = Set.newBuilder[Char]
b: scala.collection.mutable.Builder[Char,scala.collection.immutable. Set[Char]] = scala.collection.mutable.SetBuilder@726dcf2c
scala> b += 'h'
res3: b.type = scala.collection.mutable.SetBuilder@d13d812
scala> b ++= List('e', 'l', 'l', 'o')
res4: b.type = scala.collection.mutable.SetBuilder@d13d812
scala> val helloSet = b.result
helloSet: scala.collection.immutable.Set[Char] = Set(h, e, l, o) Adding a single item, one of two append operations. Adding multiple items, the second of two append operations. Unlike with buffers, a builder knows its immutable counterpart.
So, why use Builder versus Buffer or one of the mutable collection types? The Build er type is a good choice if you are only building a mutable collection iteratively in order to convert it to an immutable collection. If you need Iterable operations while building your mutable collection, or donít plan on converting to an immutable collection, using one of the Buffer or other mutable collection types is a better match.
------------------------------
ARRAY
------------------------------
An Array is a fixed-size, mutable, indexed collection. Itís not officially a collection, be- cause it isnít in the ìscala.collectionsî package and doesnít extend from the root Itera ble type (although it has all of the Iterable operations like map and filter). The Array type is actually just a wrapper around Javaís array type with an advanced feature called an implicit class allowing it to be used like a sequence. Scala provides the Array type for compatibility with JVM libraries and Java code and as a backing store for indexed col- lections, which really require an array to be useful. Here are some examples of working with arrays, demonstrating their cell mutability and support for Iterable operations:
scala> val colors = Array("red", "green", "blue") colors: Array[String] = Array(red, green, blue)
scala> colors(0) = "purple"
scala> colors res0: Array[String] = Array(purple, green, blue)
scala> println("very purple: " + colors) very purple: [Ljava.lang.String;@70cf32e3
scala> val files = new java.io.File(".").listFiles files: Array[java.io.File] = Array(./Build.scala, ./Dependencies.scala, ./build.properties, ./JunitXmlSupport.scala, ./Repositories.scala, ./plugins.sbt, ./project, ./SBTInitialization.scala, ./target)
scala> val scala = files map (_.getName) filter(_ endsWith "scala") scala: Array[String] = Array(Build.scala, Dependencies.scala, JunitXmlSupport.scala, Repositories.scala, SBTInitialization.scala) Use a zero-based index to replace any item in an Array. The Scala REPL knows how to print an Array Ö Ö but not println(), which can only call a typeís toString() method. The listFiles method in java.io.File, a JDK class, returns an array that we can easily map and filter
----------------------------------
Seq and Sequences
----------------------------------
Seq is the root type of all sequences, including linked lists like List and indexed (direct- access) lists like Vector. The Array type, if it were a collection, could be considered an indexed sequence because its elements are directly accessible without traversal. As a root type, Seq itself cannot be instantiated, but you can invoke it as a shortcut for creating a List:
scala> val inks = Seq('C','M','Y','K') inks: Seq[Char] = List(C, M, Y, K)
----> you can access items in a Vector directly by their index
---->The Seq shortcut for List linked lists and the IndexedSeq shortcut for Vector indexed lists are only marginally useful, because the savings for writing them is one character and negative four characters, respectively
Hereís an example of using String as a subtype of Iterable and as a java.lang.String wrapper, using methods from both types:
scala> val hi = "Hello, " ++ "worldly" take 12 replaceAll ("w","W") hi: String = Hello, World
---------------------
Streams
---------------------
The Stream type is a lazy collection, generated from one or more starting elements and a recursive function. Elements are added to the collection only when they are accessed for the first time, in constrast to other immutable collections that receive 100% of their contents at instantiation time. The elements that a stream generates are cached for later retrieval, ensuring that each element is only generated once. Streams can be unbounded, theoretically infinite collections where elements are only realized upon access. They can also be terminated with Stream.Empty, a counterpart to List.Nil
Here is an example function that builds and recursively generates a new stream. By incrementing the starting integer value, it will end up creating a collection of consecu- tively increasing integers:
scala> def inc(i: Int): Stream[Int] = Stream.cons(i, inc(i+1)) inc: (i: Int)Stream[Int]
scala> val s = inc(1) s: Stream[Int] = Stream(1, ?)
------------------------------------
Try Collections
----------------------------------
The util.Try collection turns error handling into collection management. It provides a mechanism to catch errors that occur in a given function parameter, returning either the error or the result of the function if successful
To throw an exception, use the throw keyword with a new Exception instance. The text message provided to Exception is optional:
scala> throw new Exception("No DB connection, exiting...") java.lang.Exception: No DB connection, exiting... ... 32 elided
Scala does support try {} .. catch {} blocks, where the catch block contains a series of case statements that attempt to match the thrown error. I recommend using util.Try() exclusively because it offers a safer, more expressive, and fully monadic approach to handling er- rors.
*******************************************************************************
*****************************************************************************
PART II
Object-Oriented Scala
*****************************************************************************
--------------------------------------------------------------------------------------------------------------------
Classes
---------------------------------------------------------------------------------------------------------------------
In Part 1 of this book you learned about Scalaís core types and how to group them into collections. Now it is time to build your own types with classes. Classes are the core building block of object-oriented languages, a combination of data structures with functions (ìmethodsî). A class defined with values and variables can be instantiated as many times as needed, each one initialized with its own input data. With inheritance classes can extend other classes, creating a hierarchy of subclasses and su- perclasses. Polymorphism makes it possible for these subclasses to stand in for their parent classes, while encapsulation provides privacy controls to manage the outward appearance of a class
Weíll start by defining the simplest possible class and instantiating it:
scala> class User
defined class User
scala> val u = new User
u: User = User@7a8c8dcf
scala> val isAnyRef = u.isInstanceOf[AnyRef]
isAnyRef: Boolean = true
Letís redesign our User class and make it more useful. Weíll add a value and some methods that operate on the value. Weíll also override the default toString method and provide a more informative version:
scala> class User {
| val name: String = "Yubaba"
| def greet: String = s"Hello from $name"
| override def toString = s"User($name)"
| }
defined class User
scala> val u = new User
u: User = User(Yubaba)
scala> println( u.greet )
Hello from Yubaba
In Scala, class parameters (if any) are specified after the name of the class, much like a functionís parameters follow the name of the function in its definition:
scala> class User(n: String) {
| val name: String = n
| def greet: String = s"Hello from $name"
| override def toString = s"User($name)"
| }
defined class User
scala> val u = new User("Zeniba")
u: User = User(Zeniba)
scala> println(u.greet)
Hello from Zeniba
Class parameters are available for initializ- ing fields (values and variables in a class) or for passing to functions, but once the class has been created the parameters arenít available. Instead of using a class parameter for intitialization purposes, we can instead declare one of the fields as a class parameter. By adding the keywords val or var before a class parameter, the class parameter then becomes a field in the class. Letís try this by moving the ìnameî field to the class parameters:
scala> class User(val name: String) {
| def greet: String = s"Hello from $name"
| override def toString = s"User($name)"
| }
defined class User
Now that we have a short and useful class, letís put it to use. Hereís an example of using this new class with lists:
scala> val users = List(new User("Shoto"), new User("Art3mis"), new User("Aesch"))
users: List[User] = List(User(Shoto), User(Art3mis), User(Aesch))
scala> val sizes = users map (_.name.size)
sizes: List[Int] = List(8, 7, 5)
scala> val sorted = users sortBy (_.name)
sorted: List[User] = List(User(Aesch), User(Art3mis), User(Shoto))
scala> val third = users find (_.name contains "3")
third: Option[User] = Some(User(Art3mis))
scala> val greet = third map (_.greet) getOrElse "hi"
greet: String = Hello from Art3mis //Can you see why String is the correct result of a combination of map and getOrElse against our Option[String]?
Letís round up our introduction to classes by working through examples of inheritance and polymorphism. A class can extend up to one other class in Scala with the extends keyword, and override (i.e., supplant) the behavior of an inherited method with the override keyword. The fields and methods in a class can be accessed (if strictly necessary) with the this keyword, while the fields and methods in the parent class(es) can be accessed with the super keyword. The super keyword is especially useful when a method needs to still access the similar method in its parent class that it is overriding.
Iíll demonstrate this with a parent class, ìA,î and subclass, ìC,î and a class situtated between these two, ìBî:
scala> class A {
| def hi = "Hello from A"
| override def toString = getClass.getName
| }
defined class A
scala> class B extends A
defined class B
scala> class C extends B { override def hi = "hi C -> " + super.hi }
defined class C
scala> val hiA = new A().hi
hiA: String = Hello from A
scala> val hiB = new B().hi
hiB: String = Hello from A
scala> val hiC = new C().hi
hiC: String = hi C -> Hello from A
Letís put this knowledge to use to create a list of instances of A, B, and C. What should we declare as the type of such a list, A, B, or C? To ensure that the list can include instances of each of these classes, we should define the list as List[A], which is compatible with all of these classes:
scala> val misc = List(new C, new A, new B)
misc: List[A] = List(C, A, B)
scala> val messages = misc.map(_.hi).distinct.sorted
messages: List[String] = List(Hello from A, hi C -> Hello from A)
Whoops! Despite my warning to define the list as List[A], I forgot to add an explicit type. Fortunately, the Scala compiler was able to infer the common type of the three instances as being A, the parent class, and set the listís type parameter correctly. Come to think of it, thatís a great use for the compilerís type inference featureófinding the lowest (most specific) common denominator of one or more instances.
----------------------------------------------------
Defining Classes
-----------------------------------------------------
Syntax: Defining a Class with Input Parameters
class <identifier> ([val|var] <identifier>: <type>[, ... ])
[extends <identifier>(<input parameters>)]
[{ fields and methods }]
A class with input parameters gives a programmer a reason to create multiple instances, because each instance can have its own unique contents.
Letís try creating a class with both value and variable fields as parameters:
scala> class Car(val make: String, var reserved: Boolean) {
| def reserve(r: Boolean): Unit = { reserved = r }
| }
defined class Car
scala> val t = new Car("Toyota", false)
t: Car = Car@4eb48298
scala> t.reserve(true)
scala> println(s"My ${t.make} is now reserved? ${t.reserved}")
My Toyota is now reserved? true
Syntax: Defining a Class with Input Parameters and Default Values
-----------------------------------------------------------------------------------
class <identifier> ([val|var] <identifier>: <type> = <expression>[, ... ]) [extends <identifier> (<input parameters>)]
[{
fields and methods
}]
EXAMPLE
---------------
scala> class Car(val make: String, var reserved: Boolean = true,val year: Int = 2015) {
| override def toString = s"$year $make, reserved = $reserved"
| }
defined class Car
scala> val a = new Car("Acura") (1)
a: Car = 2015 Acura, reserved = true
scala> val l = new Car("Lexus", year = 2010) (2)
l: Car = 2010 Lexus, reserved = true
scala> val p = new Car(reserved = false, make = "Porsche") (3)
p: Car = 2015 Porsche, reserved = false
1)Only the first parameter is required, and we can invoke it by position.
2)Here, the first and third parameters are specified. Because the third parameter is out of order weíll have to specify it by name.
3)This time none of the parameters are invoked by position, with the final one skipped.
List[String] may contain String instances and support operations that take and re- turn a String.
Syntax: Defining a Class with Type Parameters
-----------------------------------------------------------
class <identifier> [type-parameters] ([val|var] <identifier>: <type> = <expression>[, ... ]) [extends <identifier>[type-parameters](<input parameters>)]
[{ fields and methods }]
Letís create our own collection and use a type parameter to ensure type safety. The new collection will extend Traversable[A], the parent class of Iterable
scala> class Singular[A](element: A) extends Traversable[A] { (1)
| def foreach[B](f: A => B) = f(element) (2)
| }
defined class Singular
scala> val p = new Singular("Planes") (3)
p: Singular[String] = (Planes)
scala> p foreach println (4)
Planes
scala> val name: String = p.head (5)
name: String = Planes
1)A good example of passing a type parameter to the parent class in the class definition.
2)By defining a foreach() operation, Traversable will ensure our class is a real collection and can use this to enable every other collection operation.
3)Here is a validation of our type-parameterized class, with the REPL printing the class name and the name of the parameterized type used to instantiate it (a String).
4)An example usage of the foreach method we defined, reduced to its most unadorned invocation.
5)Another example usage of foreach, indirectly this time, as we access Traversa ble.head, which invokes foreach for us. By extending Traversable we can access head and a range of other standard collection operations.
---------------------------------------------------
Abstract Classes
----------------------------------------------------
An abstract class is a class designed to be extended by other classes but not instantiated itself. Abstract classes are designated so by the abstract keyword, placed before the class keyword when defining the class.
An abstract class can be used to define the core fields and methods required by its subclasses without providing an actual implementation. Thanks to polymorphism, a value with the type of the abstract class can actually point to an instance of one of its nonabstract subclasses, and invoke methods that actually end up being invoked on the subclass.
Abstract classes provide unimplemented fields and methods by declaring them without defining them. A declared field or method will include the name and parameters but not a starting value or implementation, respectively. A class that extends an abstract class with declared fields and methods, and is not itself marked as abstract, must provide their implementations. An abstract class can also have its own implemented fields and methods, which would not require implementations in subclasses. Letís create our own abstract class with a declared value and method, and experiment with implementations:
scala> abstract class Car {
val year: Int
val automatic: Boolean = true
def color: String
}
defined class Car
scala> new Car()
<console>:9: error: class Car is abstract; cannot be instantiated
new Car()
scala> class RedMini(val year: Int) extends Car {
def color = "Red"
}
defined class RedMini
scala> val m: Car = new RedMini(2005)
m: Car = RedMini@5f5a33ed
An experiment with instantiating our abstract class Car by itself didnít work, for the obvious reason that it is abstract and uninstantiatable. Still, itís nice to see that Scalaís compiler pointed this out with a helpful message.
Creating a subclass that extends Car but adds a value parameter and a concrete imple- mentation of the color method solved the problem. The RedMini class is a successful implementation of its parent abstract class and can be instantiated with only its year as a parameter.
--------------------------------------------------
Anonymous Classes
--------------------------------------------------
With anonymous classes, class definitions donít need to be stable or reusable. When a subclass will only be needed once, the anonymous class syntax can help to simplify your code base.
Hereís a more illustrative example of when you may find it useful to create an anonymous class. We have a class, Listening, that can register a Listener and trigger it later as necessary. Instead of instantiating the anonymous class on one line and passing it to the registration function on another, we can combine these into a single step of defining the anonymous class as part of the method invocation. This should look familiar to those with JavaScript experience, especially if you have worked on jQuery-style event handlers:
scala> abstract class Listener { def trigger }
defined class Listener
scala> class Listening { | var listener: Listener = null | def register(l: Listener) {
listener = l }
| def sendNotification() { listener.trigger }
| }
defined class Listening
scala> val notification = new Listening()
notification: Listening = Listening@66596c4c
scala> notification.register(new Listener {
| def trigger { println(s"Trigger at ${new java.util.Date}") }
| })
scala> notification.sendNotification
Trigger at Fri Jan 24 13:15:32 PDT 2014
Overloaded Methods
------------------------------
An overloaded method is a strategy for providing choices to callers. A class may have two or more methods with the same name and return value but with different arrange- ments of input parameters. By overloading a method name with multiple implemen- tations, multiple choices for invoking a method with a specific name are made available.
Here is an example of overloaded methods, where the methods share the same name but take different parameters. In the example the second overloaded method calls the first after modifying its input parameters appropriately:
scala> class Printer(msg: String) {
| def print(s: String): Unit = println(s"$msg: $s")
| def print(l: Seq[String]): Unit = print(l.mkString(", "))
| }
defined class Printer
scala> new Printer("Today's Report").print("Foggy" :: "Rainy" :: "Hot" :: Nil)
Today's Report: Foggy, Rainy, Hot
It is not possible to have two methods with the same name and input parameters, but different return values. Doing so will cause a Scala compiler error, because there is no way for only one of the methods to be specifically selected during compilation
Apply Methods
----------------------
Methods named ìapply,î sometimes referred to as a default method or an injector meth- od, can be invoked without the method name. The apply method is essentially a shortcut, providing functionality that can be triggered using parentheses but without a method name.
Letís try it with a class that multiplies numbers by a predefined amount:
scala> class Multiplier(factor: Int) {
def apply(input: Int) = input * factor
}
defined class Multiplier
scala> val tripleMe = new Multiplier(3)
tripleMe: Multiplier = Multiplier@339cde4b
scala> val tripled = tripleMe.apply(10)
tripled: Int = 30
scala> val tripled2 = tripleMe(10)
tripled2: Int = 30
Our ìtripleMeî instance can be used with or without the ìapplyî name to triple a given number.
You might remember this syntax from retrieving an element from a list by its index, which happens to use the List.apply method:
scala> val l = List('a', 'b', 'c')
l: List[Char] = List(a, b, c)
scala> val character = l(1)
character: Char = b
Here, the List.apply(index) method provides access to an element by index, an op- eration so common that it makes a good candidate for being the default method of lists. One potential disadvantage to making a method be the default one is if it makes the code look odd. Accessing the default method should be natural, like the accessor method for lists. Try to only use the apply method where it makes sense, like an accessor method for a list.
Lazy Values
-------------------
Lazy values are a great way to ensure that time- or performance-sensitive operations can be executed only once in a classís lifetime. They are popularly used to store infor- mation such as file-based properties, open database connections, and other immutable data that should only be initialized if it is really necessary. By initializing this data in a lazy valís expression, you can ensure that it will only operate if the lazy val is accessed at least once in the class instanceís lifetime
This concept is perhaps better explained with an example. Hereís one that shows when a regular value is calculated versus a lazy value:
scala> class RandomPoint { | val x = { println("creating x"); util.Random.nextInt }
lazy val y = { println("now y"); util.Random.nextInt }
| }
defined class RandomPoint
scala> val p = new RandomPoint()
creating x
p: RandomPoint = RandomPoint@6c225adb //here x created but not the y
scala> println(s"Location is ${p.x}, ${p.y}") //here y also created when first access time
now y
Location is 2019268581, -806862774
scala> println(s"Location is ${p.x}, ${p.y}")
Location is 2019268581, -806862774
Our class, RandomPoint, initializes its two fields with expressions that print a message before returning their randomly generated number. The ìxî field, a regular value, is initialized when our instance ìpî is created. The ìyî field, a lazy value, is initialized the first time we access it, but only the first time. In the second printout, both values have been initialized and are stable
---------------
Packaging
--------------
Packages are Scalaís (and Javaís) system for code organization. They make it possible to organize Scala code by directory using period-separated paths. Use the package key-word
at the top of a Scala source file to declare all classes in that file to be included in the package.
Packaging Syntax
----------------------
package <identifier> { <class definitions> } (or)
package <identifier>
Scala source files should be stored in directories that match their packages. For example, a ìDateUtilitiesî class in the ìcom.netflix.utilitiesî package should be stored under com/ netflix/utilities/DateUtilities.scala. The Scala compiler will store the generated .class files (the standard binary format for JVM-executable code) in a directory structure that matches the package. Letís try this out by creating a source file with a package and compiling it. Weíll use the scalac command to compile the source file and generate a class file local to the current directory:
$ mkdir -p src/com/oreilly
$ cat > src/com/oreilly/Config.scala package com.oreilly class Config(val baseUrl: String = "http://localhost")
$ scalac src/com/oreilly/Config.scala
$ ls com/oreilly/Config.class com/oreilly/Config.class
The src directory is a nice way to separate the source code from whatever else is in the current directory, but it wasnít actually used by the compiler. It took the relative path to the source file, compiled it, and generated a class file relative to the directory you launched the compiler from
Accessing Packaged Classes
--------------------------------------
Letís try this out by accessing the JDKís Date class, located in the java.util package:
scala> val d = new java.util.Date
d: java.util.Date = Wed Jan 22 16:42:04 PDT 2014
A more convenient way to access classes in other packages is to import them into the current namespace. That way, the class can be accessed without its package prefix. To import a class, use the import keyword followed by the full package and name of the class.
Syntax: Importing a Packaged Class
-----------------------------------------
import <package>.<class>
Letís create a new Date, but only after importing the class into the namespace so we can refer to it by name:
scala> import java.util.Date import java.util.Date
scala> val d = new Date
d: java.util.Date = Wed Jan 22 16:49:17 PDT 2014
The Date class we are instantiating still lives in its java.util package, but is now also part of the current namespace.
The import command is a statement, because it doesnít return a value. Unlike in Java (which has a similar import keyword), an import can be placed anywhere in your code where you might use a statement.
Letís exercise the ability to place imports wherever we might use any other statement. In this example Iíll add an import for Javaís UUID class in the middle of a println call:
scala> println("Your new UUID is " + {import java.util.UUID; UUID.randomUUID})
Your new UUID is 47ba6844-3df5-403e-92cc-e429e614c9e5
You may not always want to add imports in the restricted scope of a function call. However, adding your imports near the code where you are using the imported classes helps to make the intent of the import more clear. It may also prevent name conflicts caused by importing multiple classes of the same name from different packages. By adding the conflicting imports in separate scopes, as opposed to adding them at the top of the file, the classes can be used without conflict.
An alternative to importing the full package and class is to import part of the pack- age, reducing the need to refer to the full package but not quite importing a class. Scalaís imports are accumulative, so importing a package allows us to remove that package from the full path of a class in the package.
Scala also supports importing the entire contents of a package at once with the under- score (_) operator. After doing so, every class in that package will be added to the name- space. You might remember when we used this to import multiple Future helper classes (see ìHandling futures synchronouslyî on page 129) without importing them one at a time
Letís use the import-all feature to import all of the mutable collections into our current namespace, and then experiment with the ArrayBuffer and Queue collections in that package:
scala> import scala.collection.mutable._
scala> val b = new ArrayBuffer[String]
b: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer()
scala> b += "Hello" res0:
b.type = ArrayBuffer(Hello)
scala> val q = new Queue[Int]
q: scala.collection.mutable.Queue[Int] = Queue()
scala> q.enqueue(3, 4, 5)
scala> val pop = q.dequeue
pop: Int = 3
scala> println(q)
Queue(4, 5)
Speaking of ArrayBuffer, did you notice that we imported everything in the collection.mutable package but the REPL printed its full class name as scala.collection.mutable.ArrayBuffer? Scala does its own automatic imports in every Scala class, importing the entire scala._ and java.lang._ packages. This makes it possible to access the classes and packages in scala and java.lang directly without using the full path.
IMPORT GROUP
------------------------
An alternative to importing a full package is to use an import group. With this feature, you can list a group of class names to import instead of a complete package.
Syntax: Using an Import Group
import <package>.{<class 1>[, <class 2>...]} With an import group I could have imported the Queue and ArrayBuffer collections directly without importing the mutable Map:
scala> import scala.collection.mutable.{Queue,ArrayBuffer}
scala> val q = new Queue[Int]
q: scala.collection.mutable.Queue[Int] = Queue()
scala> val b = new ArrayBuffer[String]
b: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer()
scala> val m = Map(1 -> 2)
m: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
After importing only the mutable collections that we wanted, we can use the Queue and ArrayBuffer mutable collections while still accessing the Map immutable collection. In
veral classes from the same package they can visibly reduce the size of an ìimportî section.
Thereís actually a way to add both the immutable and the mutable Map collections to the current namespace without having a conflict. To do this, use an import alias that renames one of the types inside the local namespace. Whatís renamed is the local namespace reference to the class, not the class itself, so there is no actual change to classes outside your namespace (typically the file you are editing).
IMPORT ALIIAS
-------------------------------------
Syntax: Using an Import Alias
import <package>.{<original name>=><alias>}
Letís use an import alias to bring the collection.mutable.Map collection into our namespace, but in a way that wonít conflict with our standard immutable Map:
scala> import collection.mutable.{Map=>MutMap}
import collection.mutable.{Map=>MutMap}
scala> val m1 = Map(1 -> 2)
m1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
scala> val m2 = MutMap(2 -> 3)
m2: scala.collection.mutable.Map[Int,Int] = Map(2 -> 3)
scala> m2.remove(2); println(m2)
Map()
With our aliased collection ìMutMapî (short for ìmutable,î not ìmuttî!) we can create both mutable and immutable maps by name, without specifying their packages
PACKAGE WRITING
------------------------------
Syntax: Packaging Classes
package <identifier> { <class definitions> }
Letís rewrite our ìConfigî example (from ìPackagingî on page 151) using packaging syntax. Because we arenít relying on a file to delimit the end of the package, we can write the entire package in the REPL:
The Scala REPL Requires ìRawî Paste Mode for Packages .
Packages are traditionally used to mark files, and thus are unsuppor- ted in the standard editing mode in the REPL. The workaround is to enter the ìrawî paste mode with :paste -raw and then paste the contents of a Scala file, which will be fully compiled but available from the REPL.
scala> :paste -raw // Entering paste mode (ctrl-D to finish)
package com {
package oreilly {
class Config(val baseUrl: String = "http://localhost")
}
}
// Exiting paste mode, now interpreting.
scala> val url = new com.oreilly.Config().baseUrl url: String = http://localhost
Our new class is now available at com.oreilly.Config and clearly packaged.
Privacy Controls
-------------------------
A corollary to packaging code is using privacy controls to manage its access. While you are organizing your code into separate packages (or subpackages), youíll probably find certain functionality in one package that ought to be hidden from other packages. For example, low-level persistence code could be hidden from your user interface-level code to force your layers to use a middle layer for communication. Or you may want to limit who can extend a subclass so that your parent class can keep track of its implementers.
One privacy control is marking fields and methods as protected, which will limit their access to code from the same class or its subclasses. No other code except that class or subclasses will have access. Use the protected keyword before a val, var, or def key- word to mark that entity as being protected
Hereís an example of protecting a field from access by outside classes. The field is still accessible by a subclass, however:
scala> class User { protected val passwd = util.Random.nextString(10) } defined class User
scala> class ValidUser extends User { def isValid = ! passwd.isEmpty } defined class ValidUser
scala> val isValid = new ValidUser().isValid isValid: Boolean = true To verify that the ìpasswdî field is only accessible to ìUserî and its subclasses, try cre- ating a new instance of ìUserî and access its protected field directly. You should see an error from the compiler alerting you that the ìpasswdî field is not accessible from out- side the class (or subclasses).
When you need a more stringent level of protection, mark fields and methods as pri- vate to limit their access to only the class in which they are defined. No other code outside the class, and not even subclasses, will have access to that field.
Finally, weíll add a validation system, again without exposing our private (literally!) password to external reads or writes:
scala> class User(private var password: String) { | def update(p: String) { | println("Modifying the password!") | password = p | } | def validate(p: String) = p == password | } defined class User
scala> val u = new User("1234") u: User = User@94f6bfb
scala> val isValid = u.validate("4567") isValid: Boolean = false
scala> u.update("4567") Modifying the password!
scala> val isValid = u.validate("4567") isValid: Boolean = true
Privacy Access Modifiers
---------------------------------
The private and protected keywords provide class-hierarchy restrictions, but there are times when you want more fine-grained access control for your classís members. For example, a class in a persistence package may only want to reveal some of its database-level methods to other classes in the same package to reduce bugs and ensure a single point of access.
You can add this level of control by specifying access modifiers in addition to your private or protected designation. An access modifier specifies that such a designation is only active up to a given point, such as a package, class, or instance, and then is inactive within that point
Letís try this out with an example of specifying both package-level and instance-level protections. Weíll use the packaging syntax to denote the classís package, and thus the ìrawî paste mode in the REPL to support the packaging:
scala> :paste -raw // Entering paste mode (ctrl-D to finish)
package com.oreilly {
private[oreilly] class Config { (1)
val url = "http://localhost"
}
class Authentication {
private[this] val password = "jason" // TODO change me (2) def validate = password.size > 0
}
class Test { println(s"url = ${new Config().url}")
}
}
// Exiting paste mode, now interpreting. (3)
scala> val valid = new com.oreilly.Authentication().validate
valid: Boolean = true
scala> new com.oreilly.Test url = http://localhost (4)
res0: com.oreilly.Test = com.oreilly.Test@4c309d4d
scala> new com.oreilly.Test url = http://localhost (5)
res0: com.oreilly.Test = com.oreilly.Test@4c309d4d
scala> new com.oreilly.Config
<console>:8: error: class Config Access to the ìConfigî class is now restricted to the ìcom.oreillyî package. Only the last part of the package path is required here. Our secretive ìpasswordî field is now off-limits to everyone except code within the same instance of the same class. Here we are verifying ìpasswordî access from the same instance. The ìTestî class was able to successfully instantiate a ìConfigî classÖ Ö but we were not able to do the same from outside the package.
in package oreilly cannot be accessed in package com.oreilly New com.oreilly.Config
1)Access to the ìConfigî class is now restricted to the ìcom.oreillyî package. Only the last part of the package path is required here.
2)Our secretive ìpasswordî field is now off-limits to everyone except code within the same instance of the same class.
3)Here we are verifying ìpasswordî access from the same instance.
4)The ìTestî class was able to successfully instantiate a ìConfigî classÖ
5) Ö but we were not able to do the same from outside the package.
*******************************************************************************
Objects, Case Classes, and Traits
*********************************************
-------------
Objects
-------------
An object is a type of class that can have no more than one instance, known in object- oriented design as a singleton. Instead of creating an instance with a new keyword, just access the object directly by name. An object gets automatically instantiated the first time it is accessed in a running JVM, which also means that until it is accessed the first time it wonít get instantiated
Use the object keyword, in place of class, to define an object. Objects do not take any parameters (they are automatically instantiated), but you can define the same fields, methods, and internal classes as you can with regular classes.
Syntax: Defining an Object
object <identifier> [extends <identifier>] [{ fields, methods, and classes }]
Letís design an object that will demonstrate how objects are automatically instantiated:
scala> object Hello { println("in Hello"); def hi = "hi" }
defined object Hello
scala> println(Hello.hi)
in Hello hi
scala> println(Hello.hi)
hi
The println at the top level of the object is invoked at instantiation/initialization, which only occurs when it is accessed for the first time. Repeating the call to the objectís ìhiî method reused the same global instance so there was no additional initialization
+As an example, weíll create an object that provides pure functions as utilities, one of my favorite uses for objects:
scala> object HtmlUtils { | def removeMarkup(input: String) = { | input
| .replaceAll("""</?\w[^>]*>""","") | .replaceAll("<.*>","") | } | }
defined object HtmlUtils
scala> val html = "<html><body><h1>Introduction</h1></body></html>" html: String = <html><body><h1>Introduction</h1></body></html>
scala> val text = HtmlUtils.removeMarkup(html)
text: String = Introduction
Apply Methods and Companion Objects
----------------------------------------------------
We have covered the apply method for classes (see ìApply Methodsî on page 150), which makes it possible to invoke an instance. The same feature works for objects, making it possible to invoke an object by name. By defining one or more of these methods, your object can be invoked by name, much like List(1, 2, 3).
The Future object uses apply() to take your function parameter and invoke it in a background thread. This is known as the Objects factory pattern in object-oriented programming, and is a popular use of the apply() method in objects. Specifically, the factory pattern is a popular way to generate new instances of a class from its companion object. A companion object is an object that shares the same name as a class and is defined together in the same file as the class. Having a companion object for a class is a common pattern in Scala, but there is also a feature from which they can benefit. Companion objects and classes are considered a single unit in terms of access controls, so they can access each otherís private and protected fields and methods.
Letís try out the apply() factory pattern and the companion object pattern in the same example. We will use the REPLís :paste mode to simulate a class and object defined together in the same file, because otherwise the REPL would assume they are separate:
However, we havenít yet seen the benefit of a companion object, namely the special access controls that it shares with a companion class. Letís try this out in a new example where the class accesses private members of its companion object:
scala> :paste // Entering paste mode (ctrl-D to finish)
object DBConnection {
private val db_url = "jdbc://localhost"
private val db_user = "franken"
private val db_pass = "berry"
def apply() = new DBConnection
}
class DBConnection {
private val props = Map(
"url" -> DBConnection.db_url,
"user" -> DBConnection.db_user,
"pass" -> DBConnection.db_pass )
println(s"Created new connection for " + props("url"))
}
// Exiting paste mode, now interpreting.
defined object DBConnection
defined class DBConnection
scala> val conn = DBConnection()
Created new connection for jdbc://localhost
conn: DBConnection = DBConnection@4d27d9d
Our new DBConnection object stores the database connection data in private constants, while the class of the same name can read them when creating a connection. The con- stants are global, because the settings are constant across the application, and safe from being read by any other part of the system.
This approach is suitable for testing, but doesnít make it possible to reuse your code. The scala command will execute the contents of your file as if they were entered in a REPL, but you donít end up with compiled classes. In order to write reusable, compiled code, youíll need to compile your classes and objects with the scalac command and then execute them from your own application. In the next section weíll learn how to write command-line applications with Scala so you can start reusing your classes and objects
Command-Line Applications with Objects
-----------------------------------------------------
weíll learn how to write command-line applications with Scala so you can start reusing your classes and objects
Here is a new example that emulates the Unix command cat, which prints the contents of a file to the console. It takes one or more filenames (or paths) and prints each one to the console:
$ cat > Cat.scala object Cat { def main(args: Array[String]) { for (arg <- args) {
println( io.Source.fromFile(arg).mkString ) } } }
$ scalac Cat.scala
$ scala Cat Date.scala
object Date { def main(args: Array[String]) { println(new java.util.Date) } }
This time weíre making use of the input arguments. The fromFile method in the Scala libraryís io.Source object (we can call it by its correct name now) is used to read each file, and the collection method mkString is used to convert the lines back into a single String for printing
USE;
------
In a way, the best command-line applications are like pure functions: they read input, process it, and write output. Like the operations in Scalaís collections they are only good for a single task, but when chained together they create a bounty of new opportunities and possibilities. Command-line applications written in Scala may not replace native tools and shell scripts, because their slower startup time (a known problem in the JVM) and greater memory requirements may make them less desirable for all environments. They do make writing command-line tools more fun, however, and are a great way to learn the language. I recommend taking the time to rewrite some of your favorite (and shorter) shell scripts in Scala. Itís a great way to continue learning and practicing with the language, and you may find your Scala applications to be shorter and more stable than those written in other languages.
------------------------------------
Case Classes
-----------------------------------
A case class is an instantiable class that includes several automatically generated meth- ods. It also includes an automatically generated companion object with its own auto- matically generated methods. All of these methods in the class and in the companion object are based on the classís parameter list, with the parameters being used to formulate methods like an equals implemention that iteratively compares every field and a to String method that cleanly prints out the class name and all of its field values
And extending a case class with a regular class could lead to invalid results from the generated methods, which canít take into account fields added by sub-classes. However, if you want a class with a definitive set of fields, and these automatically generated methods are useful, then a case class may be right for you
To create a case class, just add the keyword case before your class definition.
Syntax: Defining a Case Class
----------------------------------------
case class <identifier> ([var] <identifier>: <type>[, ... ])
[extends <identifier>(<input parameters>)] [{ fields and methods }]
-->The val Keyword Is Assumed for Case Class Parameters :
---------------------------------------------------------------------
By default, case classes convert parameters to value fields so it isnít necessary to prefix them with the val keyword. You can still use the var keyword if you need a variable field.
--->displays the class and object methods that get automatically generated for case classes.
Generated case class methods
--------------------------------------------------------------------------------------------------------------------
Name Location Description
--------------------------------------------------------------------------------------------------------------------
apply Object A factory method for instantiating the case class.
copy Class Returns a copy of the instance with any requested changes. The parameters are the classís fields with the default values set to the current field values.
equals Class Returns true if every field in another instance match every field in this instance. Also invocable by the operator ==.
hashCode Class Returns a hash code of the instanceís fields, useful for hash-based collections.
toString Class Renders the classís name and fields to a String.
unapply Object Extracts the instance into a tuple of its fields, making it possible to use case class instances or pattern matching.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
The methods generated by the Scala compiler for case classes arenít special in any way, other than that they are automatically generated for you. You could skip using case classes and add the methods and companion object yourself. The benefit that case classes bring is convenience, because writing all of these methods correctly for every data-based class would require a lot of work and maintenance. They also add a certain level of consistency, because all case classes carry the same features.
scala> case class Character(name: String, isThief: Boolean) (1)
defined class Character
scala> val h = Character("Hadrian", true) (2)
h: Character = Character(Hadrian,true)
scala> val r = h.copy(name = "Royce") // r,h is instances (3)
r: Character = Character(Royce,true)
scala> h == r (4)
res0: Boolean = false
scala> h match {
case Character(x, true) => s"$x is a thief" (5)
case Character(x, false) => s"$x is not a thief"
| }
res1: String = Hadrian is a thief
1)Hereís our companion objectís factory method, Character.apply().
2) The generated toString method, printed here by the REPL, is a clean and simple representation of the fields in our instance.
3)Our second instance shares the same value for the second field, so we only need to specify a new value for the first field in the copy method.
4)If both instances are nonnull, the == operator triggers an instanceís equals method, acting as a useful shortcut to the field comparisonñbased method generated for us.
5) The companion objectís unapply method allows us to decompose the instance into its parts, binding the first field (see ìMatching with Wildcard Patternsî on page 34) and using a literal value to match the second field.
All of the generated methods we used in the example depended on the case class having two fields, name and isThief, based on the case class parameters. If our case class had extended another class with its own fields, but we hadnít added the fields as case class parameters, the generated methods wouldnít have been able to make use of them. This is an important caveat to know about before using case classes.
------------------------------------------------------------
Traits
------------------------------------------------------------
A trait is a kind of class that enables multiple inheritance. Classes, case classes, objects, and (yes) traits can all extend no more than one class but can extend multiple traits at the same time. Unlike the other types, however, traits cannot be instantiated
To define a trait, use the trait keyword in place of where you would normally use the class keyword.
Syntax: Defining a Trait
-------------------------------
trait <identifier> [extends <identifier>] [{ fields, methods, and classes }]
scala> trait HtmlUtils { | def removeMarkup(input: String) = { | input | .replaceAll("""</?\w[^>]*>""","") | .replaceAll("<.*>","") | } | }
defined trait HtmlUtils
scala> class Page(val s: String) extends HtmlUtils { | def asPlainText = removeMarkup(s) | }
defined class Page
scala> new Page("<html><body><h1>Introduction</h1></body></html>").asPlainText
res2: String = Introduction Our Page class can now use the removeMarkup method directly without specifying an object name.
This works pretty well, but a class version of HtmlUtils could have done the same job. Letís make it more interesting by adding a second trait. This time weíll use a new key- word, with, which is required for extending the second and later traits:
scala> trait SafeStringUtils { // Returns a trimmed version of the string wrapped in an Option, | // or None if the trimmed string is empty. |
def trimToNone(s:String): Option[String] = { | Option(s) map(_.trim) filterNot(_.isEmpty) | } | }
defined trait SafeStringUtils
scala> class Page(val s: String) extends SafeStringUtils with HtmlUtils { | def asPlainText: String = { | trimToNone(s) map removeMarkup getOrElse "n/a" | } | } defined class Page
scala> new Page("<html><body><h1>Introduction</h1></body></html>").asPlainText
res3: String = Introduction
scala> new Page(" ").asPlainText
res4: String = n/a
scala> new Page(null).asPlainText
res5: String = n/a
Our new, more robust Page class now extends two traits and can handle null or empty strings by returning the message n/a.
This process of taking a horizontal list of a class and traits being extended, and reforming them into a vertical chain of one class extending another, is known as linearization. It is a kind of coping mechanism for supporting multiple inheritance in an execution environment that only supports single inheritance. The fact that the JVM only supports single inheritance ensures that all class hierarchies are nondeterministic and prevents the possibility of confusing two traits that have competing members
The most important point to understand about linearization is in what order the Scala compiler arranges the traits and optional class to extend one another. The multiple inheritance ordering, from the lowest subclass up to the highest base class, is right to left.
Thus, a class defined as class D extends A with B with C, where A is a class and B and C are traits, would be reimplemented by the compiler as class D extends C ex tends B extends A. The rightmost trait is the immediate parent of the class being defined, and either the class or the first trait becomes the last parent class. This is a lot to remember, so letís write a quick test to verify this ordering:
scala> trait Base { override def toString = "Base" }
defined trait Base
scala> class A extends Base { override def toString = "A->" + super.toString }
defined class A
scala> trait B extends Base { override def toString = "B->" + super.toString }
defined trait B
scala> trait C extends Base { override def toString = "C->" + super.toString }
defined trait C
scala> class D extends A with B with C { override def toString = "D->" + super.toString } defined class D
scala> new D()
res50: D = D->C->B->A->Base
The toString method overridden in D prints the class name and then appends the output of its parent classís implementation. Fortunately all of its parent classes also override this method, so we can see the exact ordering of methods called. First the toString in D was invoked, followed by the one in trait C, trait B, class A, and finally the common base class Base.
The process of linearization may seem odd, but itís a useful compromise between the theory of a language supporting multiple inheritance versus the practice of an envi- ronment that doesnít. It also provides a solid method for determining invocation, because the constructed hierarchy ensures that method handling is decided at compile time and never at runtime
Another benefit of linearization is that you can write traits to override the behavior of a shared parent class. Hereís an example of a solid base class plus traits that add extra functionality when combined with a subclass. The example is rather lengthy so weíll cover it in two parts. First, hereís the parent class and two traits that extend it:
scala> class RGBColor(val color: Int) { def hex = f"$color%06X" }
defined class RGBColor
scala> val green = new RGBColor(255 << 8).hex
green: String = 00FF00
scala> trait Opaque extends RGBColor { override def hex = s"${super.hex}FF" }
defined trait Opaque
scala> trait Sheer extends RGBColor { override def hex = s"${super.hex}33" }
defined trait Sheer
The two traits, Opaque and Sheer, extend the RGBColor class and add an opacity level to the red-green-blue color of its parent. The extra byte is often known as an alpha channel in computer graphics, so the traits are convering an RGB color value to an RGBA (a for alpha) color value, in hexadecimal format.
------------------------------
Self Types
------------------------------
A self type is a trait annotation that asserts that the trait must be mixed in with a specific type, or its subtype, when it is added to a class. A trait with a self type cannot be added to a class that does not extend the specified type. In a way, it is a guarantee that the trait will always be extending that type, while not actually directly extending it.
A popular use of self types is to add functionality with traits to classes that require input parameters. A trait cannot easily extend a class that takes input parameters, because the trait itself cannot take input parameters. However, it can declare itself to be a subtype of that parent class with a self type and then add its functionality.
A self type is added immediately following the opening brace of the trait definition, and includes an identifier, the requested type, and an arrow (=>). A trait with a self type can access fields of that type as if it explicitly extended that type
Syntax: Defining a Self Type
trait ..... { <identifier>: <type> => .... }
The standard identifier used in self types is ìself,î although any other identifier may be used. That is, except for a keyword like this. The benefit of using the common identifier ìselfî is that it can help to make your code more readable to other Scala developers.
Here is an example of a trait using a self type to ensure that it will always be a subtype of the specified type when mixed into a class:
scala> class A { def hi = "hi" }
defined class A
scala> trait B { self: A => override def toString = "B: " + hi } (1)
defined trait B
scala> class C extends B (2)
<console>:9: error: illegal inheritance;
self-type C does not conform to B's selftype B with A
class C extends B ^
scala> class C extends A with B (3)
defined class C
scala> new C() (4)
res1: C = B: hi
1)Our trait B has a self type, adding the requirement that the trait can only ever be mixed into a subtype of the specified type, the A class. Ö
2) but just to prove it, letís try defining a class with trait B but without the requested class. No luck.
3)This time, trait B is directly extending its requested type, A, so its self type requirement has been met.
4)When our C class is instantiated, B.toString is invoked, which then invokes A.hi. The B trait is indeed used as a subtype of A here and can invoke one of its methods.
-------------------------------------
Instantiation WITH Traits
-------------------------------------
In this chapter we have used traits by having classes extend them, using the extends or with keyword in the class definition. The class that extends the trait will pick up the fields and methods of that trait, whether they are implemented by the trait or inherited from its own subtypes
You can add one or more traits to a class using the WITH keyword. The EXTENDS keyword cannot be used here, which is appropriate; your class is not actually extending the traits but instead being extended by them.
Here is an example of a class extended by a trait with a self type of that class, ensuring that the trait will extend the class:
scala> class A
defined class A
scala> trait B { self: A => }
defined trait B
scala> val a = new A with B
a: A with B = $anon$1@26a7b76d
we created an instance where trait B extended trait A.
Letís experiment with dependency injection by taking a data-oriented class common in most applications, User, and altering its output in new and mysterious ways:
scala> class User(val name: String) { def suffix = "" override def toString = s"$name$suffix" | }
defined class User
scala> trait Attorney { self: User => override def suffix = ", esq." }
defined trait Attorney
scala> trait Wizard { self: User => override def suffix = ", Wizard" }
defined trait Wizard
scala> trait Reverser { override def toString = super.toString.reverse }
defined trait Reverser
scala> val h = new User("Harry P") with Wizard
h: User with Wizard = Harry P, Wizard
scala> val g = new User("Ginny W") with Attorney
g: User with Attorney = Ginny W, esq.
scala> val l = new User("Luna L") with Wizard with Reverser
l: User with Wizard with Reverser = draziW ,L anuL
------------------------------------------
Importing Instance Members
------------------------------------------
The import keyword can also be used to import members of classes and objects into the current namespace. This makes it possible to access them directly without specifying their enclosing instance (for classes) or name (for objects).
The syntax for importing class and object members is the same as importing packaged classes. You can import a single member of a class instance by name, or the entire set of fields and methods with the underscore character. Importing fields and methods does not override privacy controls, so only those that would be normally accessible can be imported.
Here is an example of a case classís members being imported for better accessibility:
scala> case class Receipt(id: Int, amount: Double, who: String, title: String)
defined class Receipt
scala> {
val latteReceipt = Receipt(123, 4.12, "fred", "Medium Latte")
import latteReceipt._
println(s"Sold a $title for $amount to $who")
}
Sold a Medium Latte for 4.12 to fred By importing the fields from a value with a lengthy name, latteReceipt, we could access them directly in our println statement with a much simpler line of code.
As an example of object imports, letís add all of the methods from the util.Random object. This object extends the util.Random class, providing a single global instance thatís useful to use when you donít need to set a new seed for random number generation:
scala> import util.Random._
scala> val letters = alphanumeric.take(20).toList.mkString
letters: String = MwDR3EyHa1cr0JqsP9Tf
scala> val numbers = shuffle(1 to 20)
numbers: scala.collection.immutable.IndexedSeq[Int] = Vector(5, 10, 18, 1, 16, 8, 20, 14, 19, 11, 17, 3, 15, 7, 4, 9, 6, 12, 13, 2)
The alphanumeric(): Stream and shuffle(Traversable) methods, members of the util.Random object (and parent class), are here made accessible without their objectís prefix.
USAGE:
-----------
Importing instance members is a great way to streamline your code. Care must be taken to avoid naming conflicts, however, as well as any reductions in code readability. If readers of your code will get confused by the source of the imported members you are using, consider locating your import statement closer to the affected code.
--------------------------------------------------------------------------------
VARIABLE DECLARATION AND EXAMPLES
---------------------------------------------------------------------------------
VALUES
*******
Values are immutable, typed storage units, and by convention are the default method for storing data.
You can define a new value using the val keyword.
The Scala compiler, via the REPL, was able to deduce that the literal 20 corresponds to the type Int, the literal "Hello, World" to the type String, and the literal @ to the type Char.
INTEGER TYPE:
---------------
scala> val m=5;
m: Int = 5
scala> m
res1: Int = 5
FLOAT TYPE:
-----------
scala> val mm =5f //here f suffix the value belongs to float without f it belongs to double.
mm: Float = 5.0
scala> mm
res9: Float = 5.0
scala> val nn = 15.04
nn: Double = 15.04
scala> nn
res2: Double = 15.04
CHAR TYPE:
-----------
scala> val cha = 'a'
cha: Char = a
scala> cha
res3: Char = a
STRING TYPE:
------------
scala> van name:string = "7hills"
<console>:1: error: ';' expected but '=' found.
van name:string = "7hills"
^
scala> name
<console>:12: error: not found: value name
name
^
scala> val name:string = "7hills"
<console>:11: error: not found: type string
val name:string = "7hills"
^
scala> val name:String = "7hills"
name: String = 7hills
scala> val mm = println("what are you doing");
what are you doing
mm: Unit = () //here mm is unit() type
scala> mm
scala> mm
NOTE:when using println command it can not store into variable it just print the data only but we can declare as a string it would be print and retrive anyware
scala> val mm ="what are you doing"
mm: String = what are you doing
scala> mm
res8: String = what are you doing
VARIBLE:
---------
The var keyword is used to define a variable with a given name, type, and assignment.
Syntax: Defining a Variable
var <identifier>[: <type>] = <data>
scala> p
res10: Double = 7.5
scala> var naam = "7hills"
naam: String = 7hills
scala> var naam = "8hills"
naam: String = 8hills
scala> val nn = 16.2
nn: Double = 16.2
scala> nn = 15.2 //because nn is immutable it intiate by val
<console>:12: error: reassignment to val
nn = 15.2
^
scala> naam = "jfkdj"
naam: String = jfkdj
*******************************************************************************
IF...ELSE EXPRESSIONS
--------------------------------------------------------------------------------
NOTE: 1) if expressions without an else should always use curly braces, ****** because they tend to be statements that create side effects.
2) in scala doesn't have the NESTED-IF OR LEADER-IF.
3) where the punctuation characters ? and : act as a one-line if and else expression.
scala> val x = 2
x: Int = 2
scala> x
res0: Int = 2
scala> val nn ={if (x%2 == 0)}
<console>:1: error: illegal start of simple expression
val nn ={if (x%2 == 0)}
^
scala> val nn ={if (x%2 == 0) println("x is divisible by 2")}
x is divisible by 2
nn: Unit = ()
scala> val x = 5
x: Int = 5
scala> val nn ={if (x%2 == 0) println("x is divisible by 2")}
nn: Unit = ()
scala> val nn ={if (x%2 == 0) println("x is divisible by 2") else println("x is not divisible by 2")}
x is not divisible by 2
nn: Unit = ()
------------------------------------------------------------------------------
Match Expressions
-------------------------------------------------------------------------------
scala> val day="sat"
day: String = sat
scala> val week = day match
| { case "mon"|"tue"|"wed"|"thurs"|"fri" => "regularday"
| case "sat"|"sun" => "weekday"
| }
week: String = weekday
scala> val student = sno match{
| case 102 => println("\n student name is 7hills \n student number is $sno")
| case 103 => println("\n student name is 8hills \n student number is $sno")
| case 104 => println("\n student name is 10hills \n studen number is $sno")
| }
student name is 7hills
student number is $sno
student: Unit = ()
printl the single value
-----------------------
scala> val sno= 103
sno: Int = 103
scala> val stu = sno match{
| case 102 => println("student name is 7hills sno")
| case 103 => s"${sno}"
| }
stu: Any = 103
scala> val stu = sno match{
| case 102 => println("student name is 7hills sno")
| case 103 => "${sno}"
| }
stu: Any = ${sno}
TO PRINT THE NAME AND VALUE IN SINGLE LINE BY USING CASE STATEMENT
------------------------------------------------------------------
scala> val sno= 105
sno: Int = 105
scala> val student = sno match{
| case 102 => "stuent name is 7hills" + "number is "+ sno
| case 103 => "stuent name is pavan" + "number is "+ sno
| case 105 => "student name is sarvan" + "number is" + sno
| }
student: String = student name is sarvannumber is105
scala> val sno =102
sno: Int = 102
scala> val stu = sno match{
| case 102 => "stuent name is 7hills" + "number is "+ sno
| }
stu: String = stuent name is 7hillsnumber is 102
NOTE:Why an Underscore as a wildcard? Using underscores to indicate unknown values comes from the field of mathematics, arithmetic in particular, where missing amounts are denoted in problems with one or more underscores
scala> val sno= 107
sno: Int = 107
scala> val student = sno match{
| case 102 => "stuent name is 7hills" + "number is "+ sno
| case 103 => "stuent name is pavan" + "number is "+ sno
| case 105 => "student name is sarvan" + "number is" + sno
| case _ => println("the given number is invalid")
| }
the given number is invalid
student: Any = ()
ERROR:
-----
val stu = sno match{
| case 102 => "stuent name is 7hills" + "number is "
| }
scala.MatchError: 103 (of class java.lang.Integer)
... 34 elided
SOLUTION: sno must be match with any case value
here sno is 103 that way it shows but we write 102
MATCHING WITH PATTERN GURDS OR CASE STATEMENT WITH IF()EXPRESSIONS
-------------------------------------------------------------------
scala> val a=5
a: Int = 5
scala> val b=10
b: Int = 10
scala> val option=2
option: Int = 2
scala> val arthi = option match{
| case 1=> "the addition of a and b is" + a+b
| case 2 => "the subtraction of a and b is" + (a-b)
| case 3=> "the multiplication of a and b is" + a*b
| case 4 => "the subtraction of a and b is" + a/b
| case 5 => "the modulo of a and b is" + a%b
| }
arthi: String = the subtraction of a and b is-5
scala> val arthi = option match{
| case 1 if(a!=0 && b!=0)=> "the addition of a and b is" + a+b
| case 2 if(a!=0 && b!=0)=> "the subtraction of a and b is" + (a-b)
| }
arthi: String = the subtraction of a and b is-5
ERROR
------
NOTE: SUBTRACTION OPERTION MUST BE IN () PARANTHISIS.
scala> val arthi = option match{
| case 1 if(a!=0 && b!=0)=> "the addition of a and b is" + a+b
| case 2 if(a!=0 && b!=0)=> "the subtraction of a and b is" + a-b
| }
<console>:16: error: value - is not a member of String
case 2 if(a!=0 && b!=0)=> "the subtraction of a and b is" + a-b
^
NESTED FOR LOOP
---------------
// there is no need to create a relation (fo)
scala> val fo=for(x<- 1 to 2){
| for(y <- 3 to 4){
| println(x,y)
| }
| }
(1,3)
(1,4)
(2,3)
(2,4)
fo: Unit = ()
scala> fo
//not display any result
scala>
********************************************************************************
-------------------------------------------------------------------------------
FUNCTIONS
-------------------------------------------------------------------------------
UNNUMOUS FUNCTIONS
-------------------
scala> val x=9
x: Int = 9
scala> val y = 10
y: Int = 10
scala> def mul:Int = x*y
mul: Int
scala> mul
res0: Int = 90
scala> def fun ="hi"
fun: String
scala> fun
res1: String = hi
--------------------
NAMED FUNCTIONS
--------------------
scala> def MULTIPLICATION(mul:Int):Int = x*y
MULTIPLICATION: (mul: Int)Int
scala> MULTIPLICATION
<console>:15: error: missing argument list for method MULTIPLICATION
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `MULTIPLICATION _` or `MULTIPLICATION(_)` instead of `MULTIPLICATION`.
MULTIPLICATION
^
scala> MULTIPLICATION
<console>:15: error: missing argument list for method MULTIPLICATION
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `MULTIPLICATION _` or `MULTIPLICATION(_)` instead of `MULTIPLICATION`.
MULTIPLICATION
^
scala> MULTIPLICATION(5,5)
<console>:15: error: too many arguments for method MULTIPLICATION: (mul: Int)Int
MULTIPLICATION(5,5)
^
scala> MULTIPLICATION(5)
res5: Int = 90
scala> MULTIPLICATION()
<console>:15: error: not enough arguments for method MULTIPLICATION: (mul: Int)Int.
Unspecified value parameter mul.
MULTIPLICATION()
^
scala> def MULTIPLICATION(mul:Int):Int = mul*y
MULTIPLICATION: (mul: Int)Int
scala> MULTIPLICATION(5)
res7: Int = 50
scala> def factorial1(n : Int) : Int = {
| var res = 1
| for(x <- 1 to n) res = res * x
//here don't have the return res so it shows error
| }
<console>:14: error: type mismatch;
found : Unit
required: Int
for(x <- 1 to n) res = res * x
^
scala> def factorial1(n : Int) : Int = {
| var res = 1
| for(x <- 1 to n) res = res * x
| res
| }
factorial1: (n: Int)Int
scala> factorial1(4)
res10: Int = 24
scala> def factrec(n : Int) : Int = {
| if(n==1)
| n
| else
| n*factrec(n-1) //here give the return statement it show st.consol error
| }
factrec: (n: Int)Int
scala> factrec(3)
res13: Int = 6
/* TO FIND THE FIBNOIC SERIES OF GIVEN NO */
scala> def fib(n:Int):Int={
| var c=0;var a=0;var b=1
| for(x <- 1 to (n+1)){
| c=a+b
| println("a :" + a +"b: " + b +"c:" + c)
| a=b;b=c;
| }
| c
| }
fib: (n: Int)Int
scala> fib(3)
a :0b: 1c:1
a :1b: 1c:2
a :1b: 2c:3
a :2b: 3c:5
res23: Int = 5
scala> def fib(n:Int):Int={
| var c=0;var a=0;var b=1
| for(x <- 1 to n){
| c=a+b
| println("a :" + a +"b: " + b +"c:" + c)
| a=b;b=c;
| }
| c
| }
fib: (n: Int)Int
scala> fib(3)
a :0b: 1c:1
a :1b: 1c:2
a :1b: 2c:3
res25: Int = 3
scala> fib(5)
a :0b: 1c:1
a :1b: 1c:2
a :1b: 2c:3
a :2b: 3c:5
a :3b: 5c:8
res26: Int = 8
-------------------
nested functions
-------------------
scala> def max(x: Int)(y: Int) = if (x > y) x else y
max: (x: Int)(y: Int)Int
scala> val larger = max(20)(39)
larger: Int = 39
-----------------
varge parameters (*)
----------------
scala> def sum(items: Int*): Int = {
| var total = 0
| for (i <- items){ total += i
| println("i : " + i +" item : " + items)
| }
| total
| }
sum: (items: Int*)Int
scala> sum(10,20,90)
i : 10 item : WrappedArray(10, 20, 90)
i : 20 item : WrappedArray(10, 20, 90)
i : 90 item : WrappedArray(10, 20, 90)
res29: Int = 120
----------------------------------------
Calling Functions with Named Parameters
----------------------------------------
scala> val res = sum(20,30,40)
i : 20 item : WrappedArray(20, 30, 40)
i : 30 item : WrappedArray(20, 30, 40)
i : 40 item : WrappedArray(20, 30, 40)
res: Int = 90
scala> res
res0: Int = 90
----------------------------------------------
Assigning a Function with the Wildcard Operator
-----------------------------------------------
scala> def double(x: Int): Int = x * 2
double: (x: Int)Int
scala> val myDouble = double _
myDouble: Int => Int = <function1>
scala> val amount = myDouble(20)
amount: Int = 40
scala> def double(x: Int,y:Int): Int = x * y * 2
double: (x: Int, y: Int)Int
scala> val myDouble = double _,_
<console>:1: error: ';' expected but ',' found.
val myDouble = double _,_
^
scala> val myDouble = double _ _
<console>:1: error: ';' expected but '_' found.
val myDouble = double _ _
^
scala> val myDouble = double _ _
scala> def mydouble= double _+_
mydouble: String => String
scala> val rr = mydouble(20,30)
<console>:14: error: too many arguments for method apply: (v1: String)String in trait Function1
val rr = mydouble(20,30)
^
Anonymous Functions
Scala represents anonymous functions with a elegant syntax. The _ acts as a placeholder for parameters in the anonymous function. The _ should be used only once, But we can use two or more underscores to refer different parameters.
scala> List(1,2,3,4,5).foreach(print(_))
12345
scala> List(1,2,3,4,5).foreach( a => print(a))
12345
scala> val sum = List(1,2,3,4,5).reduceLeft(_+_)
sum: Int = 15
scala> val sum = List(1,2,3,4,5).reduceLeft(_+_)
sum: Int = 15
Scala is a functional language. So we can treat function as a normal variable. If you try to assign a function to a new variable, the function will be invoked and the result will be assigned to the variable. This confusion occurs due to the optional braces for method invocation. We should use _ after the function name to assign it to another variable.
class Test {
def fun = {
// some code
}
val funLike = fun _
}
--------------------------------------------
HIGH ORDER FUNCTION
--------------------------------------------
1)
scala> def logStart() = "-" * 50 + "\nStarting NOW\n" + "*" * 50
logStart: ()String
scala> val start: () => String = logStart
start: () => String = <function0>
scala> println( start() )
--------------------------------------------------
Starting NOW
**************************************************
2)
scala> def safeStringOp(s: String, f: String => String) = {
| if (s != null) f(s) else s
| }
safeStringOp: (s: String, f: String => String)String //f:String is func
scala> def reverser(s: String) = s.reverse
reverser: (s: String)String
scala> safeStringOp(null, reverser)
res2: String = null
scala> safeStringOp("7HILLS", reverser)
res3: String = SLLIH7
------------------------------------
String literals
------------------------------------
scala> val greater=(name:String)=>s"hello$name"
greater: String => String = <function1>
scala> val great= greater(7hills)
<console>:1: error: Invalid literal number
val great= greater(7hills)
^
scala> val great= greater("7hills")
great: String = hello7hills
scala> val great= greater("7hills")
great: String = hello7hills
scala> def tripleOp(a: Int, b: Int, c: Int, f: (Int, Int, Int) => Int) = f(a,b,c)
tripleOp: (a: Int, b: Int, c: Int, f: (Int, Int, Int) => Int)Int
scala> tripleOp(23, 92, 14, _ * _ + _)
res4: Int = 2130
scala> def tripleOp[A,B](a: A, b: A, c: A, f: (A, A, A) => B) = f(a,b,c)
tripleOp: [A, B](a: A, b: A, c: A, f: (A, A, A) => B)B
scala> ripleOp[Int,Int](23, 92, 14, _ * _ + _)
<console>:12: error: not found: value ripleOp
ripleOp[Int,Int](23, 92, 14, _ * _ + _)
^
scala> tripleOp[Int,Int](23, 92, 14, _ * _ + _)
res6: Int = 2130
scala> tripleOp[Int,Int](23, 92, 14, _ * _ + _)
res7: Int = 2130
scala> tripleOp[Int,Double](23, 92, 14, 1.0 * _ / _ / _)
res8: Double = 0.017857142857142856
********************************************************************************
more collections
***********************************
------------
List
-----------
scala> val numbers = List(32, 95, 24, 21, 17)
numbers: List[Int] = List(32, 95, 24, 21, 17)
scala> numbers[0]
<console>:1: error: identifier expected but integer literal found.
numbers[0]
^
scala> numbers(0)
res9: Int = 32
scala> numbers(n)
<console>:13: error: not found: value n
numbers(n)
^
scala> numbers(4)
res11: Int = 17
scala> val colors=list("red","green","yeelow")
<console>:12: error: too many arguments for method apply: (n: Int)Int in trait LinearSeqOptimized
val colors=list("red","green","yeelow")
^
scala> val colors=List("red","green","yeelow")
colors: List[String] = List(red, green, yeelow)
scala> colors.size
res2: Int = 3
scala> colors.head
res5: String = red
scala> colors.tail
res6: List[String] = List(green, yellow)
scala> colors.<press TAB> //now it will display predefined functions with
scala> . <press TAB>
++ flatMap min sortBy
++: flatten minBy sortWith
+: fold mkString sorted
/: foldLeft nonEmpty span
:+ foldRight orElse splitAt
:: forall padTo startsWith
::: foreach par stringPrefix
:\ genericBuilder partition sum
WithFilter groupBy patch tail
addString grouped permutations tails
aggregate hasDefiniteSize prefixLength take
andThen hashCode product takeRight
apply head productArity takeWhile
applyOrElse headOption productElement to
canEqual indexOf productIterator toArray
collect indexOfSlice productPrefix toBuffer
collectFirst indexWhere reduce toIndexedSeq
combinations indices reduceLeft toIterable
companion init reduceLeftOption toIterator
compose inits reduceOption toList
contains intersect reduceRight toMap
containsSlice isDefinedAt reduceRightOption toSeq
copyToArray isEmpty repr toSet
copyToBuffer isTraversableAgain reverse toStream
corresponds iterator reverseIterator toString
count last reverseMap toTraversable
diff lastIndexOf reverse_::: toVector
distinct lastIndexOfSlice runWith transpose
drop lastIndexWhere sameElements union
dropRight lastOption scan unzip
dropWhile length scanLeft unzip3
endsWith lengthCompare scanRight updated
equals lift segmentLength view
exists map seq withFilter
filter mapConserve size zip
filterNot max slice zipAll
find maxBy sliding zipWithIndex
----------------------------------------------
TO SEE THE FULL SYNTAX OF FUNCTION
-------------------------------------
scala> values.sortBy <press TAB>
def sortBy[B](f: Int => B)(implicit ord: scala.math.Ordering[B]): List[Int]
scala> values.sum <press Enter>
def sum[B >: Int](implicit num: Numeric[B]): B
scala> def sum[B >: Int](implicit num: Numeric[B]): B
| = 5 + 10
sum: [B >: Int](implicit num: Numeric[B])B
scala> sum
res14: Int = 15
scala> list.map<press TAB>
map mapConserve
scala> list.map<press TAB>
final override def map[B, That](f: Int => B)(implicit bf: scala.collection.generic.CanBuildFrom[List[Int],B,That]): That
def map[B](f: Int => B): scala.collection.TraversableOnce[B]
----------------------------------------------------------
CREATING A LIST BY USING CONS(::) OPERATOR
------------------------------------------------------------
scala> val list2=5::2::6::
<console>:11: error: value :: is not a member of Int
val list2=5::2::6::
^
scala> val list2=5::2::6::nill
<console>:11: error: not found: value nill
val list2=5::2::6::nill
^
scala> val list2=5::2::6::Nill
<console>:11: error: not found: value Nill
val list2=5::2::6::Nill
^
scala> val list2= 5 :: 2 :: 6 :: Nill
<console>:11: error: not found: value Nill
val list2= 5 :: 2 :: 6 :: Nill
^
scala> val list2= 5 :: 2 :: 6 :: Nil
list2: List[Int] = List(5, 2, 6)
scala> val list3=5::2::6::Nil
list3: List[Int] = List(5, 2, 6)
scala> list3
res18: List[Int] = List(5, 2, 6)
scala> list3::10
<console>:13: error: value :: is not a member of Int
list3::10
^
scala> list2
res20: List[Int] = List(5, 2, 6)
----------------------------------
append the data to list
-----------------------------------
scala> list2
res103: List[Int] = List(1, 7, 3, 4, 2)
NOTE :list can shows updated but not applay the original list
scala> list2::2
<console>:13: error: value :: is not a member of Int
list2::2
^
scala> list2:+2
res105: List[Int] = List(1, 7, 3, 4, 2, 2)
scala> 10+:list2:+2
res106: List[Int] = List(10, 1, 7, 3, 4, 2, 2)
scala> list2.sorted
res107: List[Int] = List(1, 2, 3, 4, 7)
NOTE: IF YOU WANT UPDATE BASE WE SHOULD FOLLOW THE BELOW
scala> list2.sorted
res109: List[Int] = List(1, 2, 2, 3, 4, 5, 7)
scala> val list2=List(1, 7, 3, 4, 2, 2):+6
list2: List[Int] = List(1, 7, 3, 4, 2, 2, 6)
scala> list2.sorted
res110: List[Int] = List(1, 2, 2, 3, 4, 6, 7)
ERRORS:
-------
scala> val list2= list2:+2
<console>:12: error: recursive value list2 needs type
val list2= list2:+2
^
scala> val list2= val list2:+2
<console>:1: error: illegal start of simple expression
val list2= val list2:+2
^
scala> val list2=list2:+2
<console>:12: error: recursive value list2 needs type
val list2=list2:+2
---------------------------------
append one list to another list (:::)
----------------------------------
scala> list2:::list3
res21: List[Int] = List(5, 2, 6, 5, 2, 6)
-----------------------------------------
comparision of two lists (if correct return true)
-----------------------------------------
scala> list2 == list3
res35: Boolean = true
scala> val list= List(5, 2, 6, 5, 2, 6)
list: List[Int] = List(5, 2, 6, 5, 2, 6)
scala> list == list3
res37: Boolean = false
scala> list.distinct
res39: List[Int] = List(5, 2, 6)
---------------------------------------------------
cut the data from list using DROP COMMAND
---------------------------------------------------
scala> val later=List('a','v','s','e')
later: List[Char] = List(a, v, s, e)
scala> leter.drop 2
<console>:1: error: ';' expected but integer literal found.
leter.drop 2
^
scala> leter drop 2
<console>:12: error: not found: value leter
leter drop 2
^
scala> leter.drop 2
<console>:1: error: ';' expected but integer literal found.
leter.drop 2
^
scala> leter.
| drop
<console>:12: error: not found: value leter
leter.
^
scala> later drop 2
res43: List[Char] = List(s, e)
scala> later drop 1
res45: List[Char] = List(v, s, e)
scala> later drop 0
res47: List[Char] = List(a, v, s, e)
----------------------------------------
filter the data by using filter command
-----------------------------------------
scala> later drop 0
res47: List[Char] = List(a, v, s, e)
scala> later filter(_>5)
res48: List[Char] = List(a, v, s, e)
scala> list3 filter(_>5)
res49: List[Int] = List(6)
-------------------------------------------
flatten operator
------------------------------------------
scala> List(list2,list3).flatten
res50: List[Int] = List(5, 2, 6, 5, 2, 6)
scala> List(list2,list).flatten
res51: List[Int] = List(5, 2, 6, 5, 2, 6, 5, 2, 6)
scala> List(list2,later).flatten
res52: List[AnyVal] = List(5, 2, 6, a, v, s, e)
--------------------------------------------------
PARTITION THE GIVEN LIST
--------------------------------------------------
scala> list
res57: List[Int] = List(5, 2, 6, 5, 2, 6)
scala> list partition(_<2)
res58: (List[Int], List[Int]) = (List(),List(5, 2, 6, 5, 2, 6))
scala> list partition(_>2)
res59: (List[Int], List[Int]) = (List(5, 6, 5, 6),List(2, 2))
scala> list partition(_>3)
res60: (List[Int], List[Int]) = (List(5, 6, 5, 6),List(2, 2))
scala> list partition(_>4)
res61: (List[Int], List[Int]) = (List(5, 6, 5, 6),List(2, 2))
------------------------------------------------------
TO REVERSE THE GIVEN LIST
-------------------------------------------------------
scala> list.reverse
res62: List[Int] = List(6, 2, 5, 6, 2, 5)
scala> list2.reverse
res63: List[Int] = List(6, 2, 5)
scala> list1.reverse
<console>:12: error: not found: value list1
list1.reverse
^
scala> list3.reverse
res65: List[Int] = List(6, 2, 5)
-------------------------------------------------
SLICE(CUT) THE GIVEN LIST BY USING SLICE COMMAND
--------------------------------------------------
scala> list
res67: List[Int] = List(5, 2, 6, 5, 2, 6)
scala> list slice(1,3)
res66: List[Int] = List(2, 6)
scala> val sli=list slice(1,3)
sli: List[Int] = List(2, 6)
scala> sli
res68: List[Int] = List(2, 6)
note: it first position is called one not zero.
----------------------------------------------------
sorting the list by using sort and sortBy
-------------------------------------------------------
sort the integer list by usind <.sorted> command (by the list of natural values)
------------------------------------------------
scala> list.sorted
res74: List[Int] = List(2, 2, 5, 5, 6, 6)
sort the char list by size using < sortedBy> command don't give the .<dot>
-----------------------------------------------------------------------
scala> List("apple", "to") sortBy(_.size)
res71: List[String] = List(to, apple)
-----------------------------------------------------------------------
split the given list by using splitAt <position>/<int>
----------------------------------------------------------------------
scala> list
res83: List[Int] = List(5, 2, 6, 5, 2, 6)
scala> list splitAt2
<console>:13: error: value splitAt2 is not a member of List[Int]
list splitAt2
^
scala> list splitAt 2
res78: (List[Int], List[Int]) = (List(5, 2),List(6, 5, 2, 6))
scala> list splitAt 3
res79: (List[Int], List[Int]) = (List(5, 2, 6),List(5, 2, 6))
scala> list splitAt 4
res80: (List[Int], List[Int]) = (List(5, 2, 6, 5),List(2, 6))
scala> list splitAt 5
res81: (List[Int], List[Int]) = (List(5, 2, 6, 5, 2),List(6))
scala> list splitAt 6
res82: (List[Int], List[Int]) = (List(5, 2, 6, 5, 2, 6),List())
--------------------------------------------------------------------
Extracts the n elmenents by using command:take <int/value> command
--------------------------------------------------------------------
syntax: list take <int/value>
------
scala> list
res83: List[Int] = List(5, 2, 6, 5, 2, 6)
scala> list taken 3
<console>:13: error: value taken is not a member of List[Int]
list taken 3
^
scala> list take 3
res85: List[Int] = List(5, 2, 6)
scala> list take 6
res86: List[Int] = List(5, 2, 6, 5, 2, 6)
scala> list take 4
res87: List[Int] = List(5, 2, 6, 5)
----------------------------------------------------------------------
combines two list in tuple point of view by using zip command
----------------------------------------------------------------------
syntax: <list1> zip <list2>
-------
scala> list2
res91: List[Int] = List(5, 2, 6)
scala> list2 zip list3
res90: List[(Int, Int)] = List((5,5), (2,2), (6,6))
scala> list3
res92: List[Int] = List(5, 2, 6)
scala> List(1, 2) zip List("a", "b")
res88: List[(Int, String)] = List((1,a), (2,b))
scala> val list4=List(1, 2) zip List("a", "b")
list4: List[(Int, String)] = List((1,a), (2,b))
scala> list4
res89: List[(Int, String)] = List((1,a), (2,b))
scala> li
res2: List[Int] = List(1, 2, 3)
scala> val lis=List(5,6)
lis: List[Int] = List(5, 6)
scala> li zip lis
res4: List[(Int, Int)] = List((1,5), (2,6))
note:in zip function work as one to one mapping only if it has any other variables are there it's leav that.
-----------------------------------------------------------------------
Drop some values in our list by using
------------------------------------------------------------------------
scala> list2
res112: List[Int] = List(1, 2, 2, 3, 4, 6, 7)
scala> suffix
res113: List[Int] = List(4, 6, 7)
scala> val drop = list2 dropRight 2
drop: List[Int] = List(1, 2, 2, 3, 4)
scala> list2
res114: List[Int] = List(1, 2, 2, 3, 4, 6, 7)
scala> drop
res115: List[Int] = List(1, 2, 2, 3, 4)
Errors:
-------
scala> val drop = list2 dropmid 2
<console>:12: error: value dropmid is not a member of List[Int]
val drop = list2 dropmid 2
^
scala> val drop = list2 dropleft 2
<console>:12: error: value dropleft is not a member of List[Int]
val drop = list2 dropleft 2
^
scala> val drop = list2 dropstart 2
<console>:12: error: value dropstart is not a member of List[Int]
val drop = list2 dropstart 2
---------------------------------------------------------------------
takeRight command for taking the right side values in our list
------------------------------------------------------------------------
synatax: <list> <takeRight> <value/int>
-----------
NOTE: In takeRight command take the values in our list based on our list
----- so you can check what is what data you have in list
perhaps you are sorted with out update the original list.it will perform
based on original list.
scala> list2
res108: List[Int] = List(1, 7, 3, 4, 2, 2, 5)
scala> list2.sorted
res109: List[Int] = List(1, 2, 2, 3, 4, 5, 7)
scala> val list2=List(1, 7, 3, 4, 2, 2):+6
list2: List[Int] = List(1, 7, 3, 4, 2, 2, 6)
scala> list2.sorted
res110: List[Int] = List(1, 2, 2, 3, 4, 6, 7)
scala> val suffix = list2 takeRight 3 *** because original list not modified**
suffix: List[Int] = List(2, 2, 6)
scala> list2
res111: List[Int] = List(1, 7, 3, 4, 2, 2, 6) **base/original list ****
scala> list2
res112: List[Int] = List(1, 2, 2, 3, 4, 6, 7)
scala> val suffix = list2 takeRight 3
suffix: List[Int] = List(4, 6, 7)
scala> suffix
res113: List[Int] = List(4, 6, 7)
----------------------------------------------------------
MAP
-------------------------------------------------------------
note:zip is
scala> val list2= List(1, 7, 3, 4, 2, 2, 6)
list2: List[Int] = List(1, 7, 3, 4, 2, 2, 6)
scala> val list4=List(1, 2) zip List("a", "b")
list4: List[(Int, String)] = List((1,a), (2,b))
scala> val list= List(5, 2, 6, 5, 2, 6)
list: List[Int] = List(5, 2, 6, 5, 2, 6)
scala> val list3= List(1, 7, 3)
list3: List[Int] = List(1, 7, 3)
scala> val list4= List(1, 7, 3,9,9,10)
list4: List[Int] = List(1, 7, 3, 9, 9, 10)
scala> val listchar = List("7hills","hi","how","are","you")
listchar: List[String] = List(7hills, hi, how, are, you)
scala> val list6=List(8,10,5)
list6: List[Int] = List(8, 10, 5)
MAPPING OPERATION
------------------
COLLECT:
--------
Description
------------
Transforms each element using a partial function, retaining applicable elements
scala> val list6=List(8,10,5)
list6: List[Int] = List(8, 10, 5)
scala> val mapc= List(0, 1, 0) collect {case 1 => "ok"}
mapc: List[String] = List(ok)
scala> mapc
res0: List[String] = List(ok)
scala> val mapc= List4 collect {case 1 => "ok" //Does not work list4
| case 2=> "this value is 7" you should give complete
| case 4=>"this value is 9"} list of values
<console>:11: error: not found: value List4
val mapc= List4 collect {case 1 => "ok"
^
scala> val mapc= List4.collect {case 1 => "ok"
| }
<console>:11: error: not found: value List4
val mapc= List4.collect {case 1 => "ok"
^
scala> val mapc= List(1, 7, 3,9,9,10) collect {case 1 => "this is index 1"
| case 2=> "this value is 7" //here return string type
| case 4=>"this value is 9"} List[String] = List(this is index 1)
mapc: List[String] = List(this is index 1)
scala> val mapc= List(1, 7, 3,9,9,10) collect {case 1 => println("this is index 1")
| case 2=> println("this value is 7") //here println convert unit() type
| case 4=> println("this value is 9")} List[Unit] = List(())
this is index 1
mapc: List[Unit] = List(())
------------
flatmap
---------
scala> list.flatMap
final override def flatMap[B, That](f: Int =>scala.collection.GenTraversableOnce[B])(implicit bf: scala.collection.generic.CanBuildFrom[List[Int],B,That]): That
def flatMap[B](f: Int => scala.collection.GenTraversableOnce[B]): scala.collection.TraversableOnce[B]
scala> List("milk,tea") flatMap (_.split('|')
| )
res5: List[String] = List(milk,tea)
scala> val mapfla=List("milk,tea") flatMap (_.split('|'))
mapfla: List[String] = List(milk,tea)
scala> mapfla
res6: List[String] = List(milk,tea)
the flatMap example uses the String.split() method to convert pipe-delimited text into a list of strings. Specifically this is the java.lang.String.split() method, and it returns a Java array, not a list.
Fortunately(adrustam), Scala converts Java arrays to its own type, Array, which extends Iterable. Because List is also a subtype of Iterable, and the flatMap method is defined at the Iterable level,
a list of string arrays can be safely flattened into a list of strings.
scala> def flatten(ls: List[Any]): List[Any] = ls flatMap {
| case i: List[_] => flatten(i)
| case e => List(e)
| }
flatten: (ls: List[Any])List[Any]
scala> val k = List(1, List(2, 3), List(List(List(List(4)), List(5)), List(6, 7)), 8)
k: List[Any] = List(1, List(2, 3), List(List(List(List(4)), List(5)), List(6, 7)), 8)
scala> flatten(k)
res26: List[Any] = List(1, 2, 3, 4, 5, 6, 7, 8)
------------
map
-------------
Desription: Transforms each element using the given function
-----------
scala> list.map<press TAB>
map mapConserve
scala> list.map<press TAB>
final override def map[B, That](f: Int => B)(implicit bf: scala.collection.generic.CanBuildFrom[List[Int],B,That]): That
def map[B](f: Int => B): scala.collection.TraversableOnce[B]
scala> List("milk","tea") map (_.toUpperCase)
res7: List[String] = List(MILK, TEA)
----------------
reduce operation
-----------------
SCALA REDUCE OPERATIONS ARE FOLLWS
1) MATH OPERATIONS
2)BOOLEAN OPERATIONS
3) HIGHOREDER OPERATIONS
-----------------
MATH OPERATIONS
----------------
max,min,sum,product
scala> list
res13: List[Int] = List(5, 2, 6, 5, 2, 6)
scala> list.max
res15: Int = 6
scala> list.min
res16: Int = 2
scala> list.sum
res17: Int = 26
scala> list.product
res18: Int = 3600
---------------------
BOOLEAN OPERATOR
--------------------
IF CONDATION/CHECKING VALUE IS THERE IT DISPLAYS TRUE
<list> contains <value> ,endswith,exists,forall,startwith
contains:Checks if the list contains this element
scala> list4 contains 6
res11: Boolean = false
endsWith:Checks if the list ends with a given list
scala> list4 endsWith list4(10)
<console>:13: error: type mismatch;
found : Int
required: scala.collection.GenSeq[?]
list4 endsWith list4(10)
^
scala> list4 endsWith List(9,10)
res10: Boolean = true
exists:Checks if a predicate holds true for at least one element in the list.
scala> list4 exists (_<0)
res14: Boolean = false
scala> list4 exists (_<10)
res12: Boolean = true
forall:Checks if a predicate holds true for every element in the list.
------
scala> list4
res21: List[Int] = List(1, 7, 3, 9, 9, 10)
scala> list4 forall (_<11)
res22: Boolean = true
scala> list4 forall (_<21)
res23: Boolean = true
scala> list4 forall (_<2)
res24: Boolean = false
scala> list4 forall (_<9)
res15: Boolean = false
scala> list4 forall (_<25)
res16: Boolean = true
scala> list4 forall (_<2,5)
<console>:13: error: too many arguments for method forall: (p: Int => Boolean)Boolean
list4 forall (_<2,5)
^
scala> list4 forall (_<2 5)
<console>:1: error: ')' expected but integer literal found.
list4 forall (_<2 5)
^
scala> list4 forall (_<10)
res18: Boolean = false
scala> list4 forall (_<15)
res19: Boolean = true
scala> list4 forall (_<78)
res20: Boolean = true
StartWith:Tests whether the list starts with a given list.
----------
scala> list4 startsWith List(1)
res26: Boolean = true
scala> list4 startsWith List(8)
res27: Boolean = false
scala> list4 startsWith list4 //internally calls first element to other list 1st element
res29: Boolean = true
Error: here Genseq[] meens you should type List
-----
scala> list4 startsWith list(8)
<console>:14: error: type mismatch;
found : Int
required: scala.collection.GenSeq[?]
list4 startsWith list(8)
-----------------------------------------------------
converting collections
-----------------------------------------------------
mkString; Renders a collection to a Set using the given delimiters
scala> listchar
res30: List[String] = List(7hills, hi, how, are, you)
scala> val listchar2=listchar.mkString("|")
listchar2: String = 7hills|hi|how|are|you
toBuffer; Converts an immutable collection to a mutable one.
scala> List('f', 't').toBuffer
res37: scala.collection.mutable.Buffer[Char] = ArrayBuffer(f, t)
scala> listchar.toBuffer
res38: scala.collection.mutable.Buffer[String] = ArrayBuffer(7hills, hi, how, are, you)
toList:Converts a collection to a List.
------
scala> val map=Map(1->"hi",2->"hello")
map: scala.collection.immutable.Map[Int,String] = Map(1 -> hi, 2 -> hello)
scala> map.toList
res45: List[(Int, String)] = List((1,hi), (2,hello))
toMap;converts a collection to a Map
-----
scala> val maplist=List(1->"hi",2->"hello")
maplist: List[(Int, String)] = List((1,hi), (2,hello))
scala> maplist.toMap
res43: scala.collection.immutable.Map[Int,String] = Map(1 -> hi, 2 -> hello)
scala> maplist
res44: List[(Int, String)] = List((1,hi), (2,hello))
scala> val cmaplist=maplist.toMap
cmaplist: scala.collection.immutable.Map[Int,String] = Map(1 -> hi, 2 -> hello)
toSet:Converts a collection to a Set.
-----
scala> map.toSet
res46: scala.collection.immutable.Set[(Int, String)] = Set((1,hi), (2,hello))
scala> list4.toSet
res47: scala.collection.immutable.Set[Int] = Set(10, 1, 9, 7, 3)
toString: Renders a collection to a String, including the collection’s type.
--------
scala> list4
res50: List[Int] = List(1, 7, 3, 9, 9, 10)
scala> list4.toString
res51: String = List(1, 7, 3, 9, 9, 10)
scala> map
res49: scala.collection.immutable.Map[Int,String] = Map(1 -> hi, 2 -> hello)
scala> map.toString
res48: String = Map(1 -> hi, 2 -> hello)
-----------------------------------------------------------
pattern matching with collection
-------------------------------------------------------------
scala> val colpatern=list4.head match{
| case x if(x == 1)=> "head value is 1"
| case x if(x == 9)=> " middle value"
| }
colpatern: String = head value is 1
scala> val colpatern=list4 match{
| case List(9,9) => "not "
| case List(1, 7, 3, 9, 9, 10) => "not found & error"
| }
colpatern: String = not found & error
DOUBT: HOW TO CHECK EACH VALUE THROUGH MATCHING PATTERN BY THE SINGLE VALUE.
*******************************************************************************
MORE COLLECTIONS
*******************************
We’ll start with mutable collections, which probably can be considered ubiquitous be- cause more languages support them than they do immutable collections. Then we’ll move on to arrays, streams, and other collections
---------------------------------------
Mutable Collections
---------------------------------------
Mutable collection types
--------------------------
collection.mutable.Buffer:
--------------------------
scala> val m = Map("AAPL" -> 597, "MSFT" -> 40)
m: scala.collection.immutable.Map[String,Int] = Map(AAPL -> 597, MSFT -> 40)
scala> val b = m.toBuffer
b: scala.collection.mutable.Buffer[(String, Int)] = ArrayBuffer((AAPL,597), (MSFT,40))
scala> b trimStart 1
scala> b
res55: scala.collection.mutable.Buffer[(String, Int)] = ArrayBuffer((MSFT,40))
scala> m
res56: scala.collection.immutable.Map[String,Int] = Map(AAPL -> 597, MSFT -> 40)
scala> b += ("GOOG" -> 521)
res57: b.type = ArrayBuffer((MSFT,40), (GOOG,521))
scala> val n = b.toMap
n: scala.collection.immutable.Map[String,Int] = Map(MSFT -> 40, GOOG -> 521)
--------------------
Arrays
--------------------
scala> val colors = Array("red", "green", "blue")
colors: Array[String] = Array(red, green, blue)
scala> colors(0) = "purple"
scala> colors
res59: Array[String] = Array(purple, green, blue)
scala> colors :+ "red"
res60: Array[String] = Array(purple, green, blue, red)
scala> colmut trimStart 1
scala> colmut
res74: scala.collection.mutable.Buffer[String] = ArrayBuffer(green, blue)
----------------------------------
Seq and Sequences
----------------------------------
scala> val inks = Seq('C','M','Y','K')
inks: Seq[Char] = List(C, M, Y, K)
scala> inks(0)
res75: Char = C
scala> inks(1)
res76: Char = M
scala> inks(4)
java.lang.IndexOutOfBoundsException: 4
at scala.collection.LinearSeqOptimized$class.apply(LinearSeqOptimized.scala:65)
at scala.collection.immutable.List.apply(List.scala:84)
... 32 elided
scala> inks(3)
res78: Char = K
-------------------------
Using Collection Builder
-----------------------------
scala> val b = Set.newBuilder[Char]
b: scala.collection.mutable.Builder[Char,scala.collection.immutable.Set[Char]] = scala.collection.mutable.SetBuilder@6debcae2
scala> b += 'h'
res0: b.type = scala.collection.mutable.SetBuilder@6debcae2
scala> b ++= List('e', 'l', 'l', 'o')
res1: b.type = scala.collection.mutable.SetBuilder@6debcae2
scala> b
res2: scala.collection.mutable.Builder[Char,scala.collection.immutable.Set[Char]] = scala.collection.mutable.SetBuilder@6debcae2
scala> val helloSet = b.result
helloSet: scala.collection.immutable.Set[Char] = Set(h, e, l, o)
scala> b.result
res3: scala.collection.immutable.Set[Char] = Set(h, e, l, o)
------------------------------
ARRAY
------------------------------
scala> def inc(i: Int): Stream[Int] = Stream.cons(i, inc(i+1))
inc: (i: Int)Stream[Int]
scala> val s = inc(1)
s: Stream[Int] = Stream(1, ?)
*****************************************************************************
Object-Oriented Scala
--------------------------------------------------------------------------------------------------------------------
Classes
---------------------------------------------------------------------------------------------------------------------
Design the user class in 2 ways
--------------------------
1)scala> class hills
defined class hills
scala> val u= new hills
u: hills = hills@77681ce4
2)scala> class username{
val name="7hills"
val pass="777"
def greet:String=s"hello $name"
override def toString=s"username($name)"
}
defined class username
scala> val u = new username()
u: username = username(7hills)
scala> val u = new username
u: username = username(7hills)
scala> println(u.greet)
hello 7hills
scala> class hills{
| val nn=10
| def greet:String = s"this no is $nn"
| override def toString=s"hills($nn)"
| }
defined class hills
scala> val u = new hills()
u: hills = hills(10)
scala> u.greet
res1: String = this no is 10
scala> println(u.greet)
this no is 10
------------------------------------------
CREATING A CLASS FOR USER AUTHENTICATION
------------------------------------------
(1) METHOD -1
==============
class username(pass:String) {
val name="7hills"
def greet:String=s"your name $name"
println("enter your password")
if(pass=="7777"){
println("hi" + name + "your")
println("successfully login")
}
else
println("you entered wrong password")
}
output:
-------
CREATE OBJECT ON CLASS
-----------------------
scala> val u= new username("7777")
enter your password
hi7hillsyour
successfully login
u: username = username@1b96f15e
output:
-------
scala> val u= new username("8888")
enter your password
you entered wrong password
u: username = username@1f22d7e4
class username {
val name="7hills"
def greet:String=s"your name $name"
def passvalidat(pass:String) = {
println("enter your password")
if(pass=="7777"){
println("hi" + name + "your")
println("successfully login")
}
else
println("you entered wrong password")
}
}
output:
-------
scala> u.passvalidat("7777")
hi7hillsyour
successfully login
scala> u.passvalidat("777")
you entered wrong password
scala> val u=new username()
u: username = username@addefe5
scala>
scala> u.passvalidat("7777")
enter your password
hi7hillsyour
successfully login
--------------------
class username {
val name="7hills"
def greet:String=s"your name $name"
def passvalidat(pass:String) = {
println("enter your password")
if(pass=="7777"){
println("hi" + name + "your")
println("successfully login")
}
else
println("you entered wrong password")
}
}
(2) METHOD - 2
================
class usr(name:String) {
val para = name
para + "the val is"
def passvali = {
if(para == "555")
para + "your are sucessfully login"
else
println(" you entered wrong password")
}
}
scala> val u= new usr("896") //create the object on class
u: usr = usr@3956ae6e
scala> u.passvali //call the defined function
you entered wrong password
res29: Any = ()
NOTE: YOU SHOULD CALL FIRST IN PARA VARIABLE
-----
scala> u.para // call the name parameter
res30: String = 896
scala> val u= new usr("555") //
u: usr = usr@406b6988
scala> u.para
res31: String = 555
scala> u.passvali
res32: Any = 555your are sucessfully login
(3) METHOD - 2
================
class usr(name:String) {
val para = name
para + "the val is"
def passvali = {
if(para == "555")
para + "your are sucessfully login"
else
println(" you entered wrong password")
}
}
class calling extends usr{override def passvali="your name" + super.para + super.passvali}
-------------------------------------------------------------
INNER CLASS CALLING (or) call one class to another class
-----------------------------------------------------------------
BASE CLASS:
------------
scala> class usr(name:String) {
| val para = name
| para + "the val is"
| def passvali = {
| if(para == "555")
| para + "your are sucessfully login"
| else
| println(" you entered wrong password")
| }
| }
defined class usr
----------
CHILD CLASS
-----------
scala> class calling extends usr("7777"){override def passvali="your name" + super.passvali}
defined class calling
scala> val cals = new calling().passvali
you entered wrong password
cals: String = your name()
scala> class calling extends usr("555"){override def passvali="your name" + super.passvali}
defined class calling
scala> val cals = new calling().passvali
cals: String = your name555your are sucessfully login
ERRORS:
-------
scala> class calling extends use{override def passvali="your name" + super.para + super.passvali}
<console>:11: error: not found: type use
class calling extends use{override def passvali="your name" + super.para + super.passvali}
^
<console>:11: error: value para is not a member of AnyRef
class calling extends use{override def passvali="your name" + super.para + super.passvali}
^
<console>:11: error: value passvali is not a member of AnyRef
class calling extends use{override def passvali="your name" + super.para + super.passvali}
^
scala> class calling extends usr{override def passvali="your name" + super.para + super.passvali}
<console>:13: error: not enough arguments for constructor usr: (name: String)usr.
Unspecified value parameter name.
class calling extends usr{override def passvali="your name" + super.para + super.passvali}
^
scala> class calling extends usr("7777"){override def passvali="your name" + super.para + super.passvali}
<console>:13: error: super may not be used on value para
class calling extends usr("7777"){override def passvali="your name" + super.para + super.passvali}
^
------------
SOLUTION
-----------
1) YOU SHOULD PASS THE PARAMETERS IN BASE WHEN CALLING THROUG CHILD CLASS ALSO
2)YOU SHOULD NOT CALL VARIABLES/VALUES THROUGH SUPER KEYWORD.WE KNOW SUPER KEYWORD USING ONLY CALLS THE PARENT/BASE CLASS METHODS/FUNCTIONS.
ANOTHER EXAMPLE
----------------
scala> class A { def hi = "Hello from A"
| override def toString = getClass.getName }
defined class A
scala> class B extends A
defined class B
scala> class C extend B{ override def hi = "hi C -> " + super.hi }
scala> val hiA= new A().hi
hiA: String = Hello from A
OR
scala> hiA
res35: String = Hello from A
scala> val hiB= new B().hi
hiB: String = Hello from A
scala> val hiC= new C().hi
hiC: String = hi C -> Hello from A
Let’s try creating a class with both value and variable fields as parameters:
----------------------------------------------------------------------------
scala> class Car(val make: String, var reserved: Boolean) {
| def reserve(r: Boolean): Unit = { reserved = r } }
defined class Car
scala> val t = new Car("Toyota", false)
t: Car = Car@e283976
scala> t.reserve(true)
scala> println(s"My ${t.make} is now reserved? ${t.reserved}")
My Toyota is now reserved? true
scala> t.reserve(false)
scala> println(s"My ${t.make} is now reserved? ${t.reserved}")
My Toyota is now reserved? false
NOTE:WE CAN NOT CHANGE THE make value because it is val.
Class with Input Parameters and Default Values
-----------------------------------------------
---------------------------------------------------------
ABSTRACT CLASS
---------------------------------------------------------
scala> abstract class car{
| val year:Int
| val automatic:Boolean = true
| def Color:String
| }
defined class car
scala> new car
<console>:13: error: class car is abstract; cannot be instantiated
new car
^
scala> class red(val year:Int) extends car
<console>:12: error: class red needs to be abstract, since method Color in class car of type => String is not defined
class red(val year:Int) extends car
^
scala> class red(val year:Int) extends car{
| val Color = "red"
| }
defined class red
scala> val m=new red(2017)
m: red = red@3ada9e37
scala> m.Color
res1: String = red
scala> m.year
res2: Int = 2017
scala> m.automatic
res3: Boolean = true
-------------------------------------------------------------------------
Anonymous Classes (OR) write the function operation when call the method
-----------------------------------------------------------------------
We have a class, Listening, that can register a Listener and trigger it later as necessary. Instead of instantiating the anonymous class on one line and passing it to the registration function on another, we can combine these into a single step of defining the anonymous class as part of the method invocation.
scala> abstract class Listener { def trigger }
defined class Listener
scala> class Listening { var listener: Listener = null
| def register(l: Listener) {listener = l }
| def sendNotification() { listener.trigger }
| }
defined class Listening
scala> val notification = new Listening()
notification: Listening = Listening@4671e53b
scala> notification.register(new Listener {
| def trigger { println(s"Trigger at ${new java.util.Date}") }
| })
scala> notification.sendNotification
Trigger at Sat Sep 02 18:44:10 IST 2017
Error:
---------
<console>:14: error: reassignment to val
listenerv=l}
^
note:you can not assign the value to val type variable because it is immutable
-------------------------------------------------------
Overloaded Methods
------------------------------
Here is an example of overloaded methods, where the methods share the same name but take different parameters. In the example the second overloaded method calls the first after modifying its input parameters appropriately:
(1)
scala> class Printer(msg: String) {
| def print(s:String):Unit = println(s"$msg: $s")
| def print(l:Seq[String]):Unit = print(l.mkString(","))
| }
defined class Printer
scala> val p = new Printer("hi this").print("this"::"is"::"fizzy"::Nil)
hi this: this,is,fizzy
p: Unit = ()
(2)
scala> class Printer(msg: String) {
| def print(s:String):Unit = println(s"$msg: $s")
| def print(l:Seq[String]):Unit = println(l.mkString(","))
| }
defined class Printer
scala> val p = new Printer("hi this").print("this"::"is"::"fizzy"::Nil)
this,is,fizzy
p: Unit = ()
note; 1st method use println only but we use two methods in println statement 2nd method only use print statement
Errors
------
scala> val p = new Printer("hi this").print("this"::"is"::"fizzy")
<console>:12: error: value :: is not a member of String
val p = new Printer("hi this").print("this"::"is"::"fizzy")
^
sol:end with Nil
---------------------
Apply Methods
----------------------
scala> class Multiplier(factor: Int) {
| def applay(f:Int) = factor * f}
defined class Multiplier
scala> val mul=new Multiplier(5)
mul: Multiplier = Multiplier@79d7035
scala> mul
res11: Multiplier = Multiplier@79d7035
scala> mul.applay(5)
res12: Int = 25
scala> val mul=new Multiplier(6)
mul: Multiplier = Multiplier@25791d40
scala> val mu=new Multiplier(6).applay(5)
mu: Int = 30
------------------------------------------
Accessing Packaged Classes
--------------------------------------
scala> import collection.immutable._
import collection.immutable._
scala> import collection.mutable._
import collection.mutable._
scala> val b = new ArrayBuffer[String]
b: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer()
scala> b += "hello"
res15: b.type = ArrayBuffer(hello)
scala> val q = new Queue[Int]
q: scala.collection.mutable.Queue[Int] = Queue()
scala> q += 10
res16: q.type = Queue(10)
scala> val l = new [Int]
<console>:18: error: class List is abstract; cannot be instantiated
val l = new List[Int]
^
scala> val l = List[Int]
<console>:18: error: missing argument list for method apply in object List
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `apply _` or `apply(_)` instead of `apply`.
val l = List[Int]
^
scala> val q = new Queue[Int]
q: scala.collection.mutable.Queue[Int] = Queue()
scala> q.enqueue(3, 4, 5)
scala> q.dequeue(3)
<console>:20: error: too many arguments for method dequeue: ()Int
q.dequeue(3)
^
scala> q.dequeue
res19: Int = 3
scala> q
res20: scala.collection.mutable.Queue[Int] = Queue(4, 5)
---------------------------------------
IMPORT ALIIAS
-------------------------------------
syntax:
import <package>.{<original name>=><alias>}
scala> import collection.mutable.{Map=>MutMap}
import collection.mutable.{Map=>MutMap}
scala> val m1 = Map(1 -> 2)
m1: scala.collection.mutable.Map[Int,Int] = Map(1 -> 2)
scala> val m2 = MutMap(2 -> 3)
m2: scala.collection.mutable.Map[Int,Int] = Map(2 -> 3)
scala> m2.remove(1)
res21: Option[Int] = None
scala> m2.remove(2)
res22: Option[Int] = Some(3)
scala> val m1 = Map(1 -> 2)
m1: scala.collection.mutable.Map[Int,Int] = Map(1 -> 2)
scala> val m2 = MutMap(2 -> 3)
m2: scala.collection.mutable.Map[Int,Int] = Map(2 -> 3)
scala> m2
res24: scala.collection.mutable.Map[Int,Int] = Map(2 -> 3)
scala> m1
res25: scala.collection.mutable.Map[Int,Int] = Map(1 -> 2)
scala> m1.remove(1)
res26: Option[Int] = Some(2)
scala> m1
res27: scala.collection.mutable.Map[Int,Int] = Map()
With our aliased collection “MutMap” (short for “mutable,” not “mutt”!) we can create both mutable and immutable maps by name, without specifying their packages
-------------
Objects
-------------
Syntax: Defining an Object
object <identifier>/objectname [extends <identifier>] [{ fields, methods, and classes }]
scala> object Hello { println("in Hello"); def hi = "hi" }
defined object Hello
scala> Hello
in Hello
res34: Hello.type = Hello$@557665b2
scala> Hello.hi
res35: String = hi
scala> hi
<console>:20: error: not found: value hi
hi
^
scala> Hello
res37: Hello.type = Hello$@557665b2
The println at the top level of the object is invoked at instantiation/initialization, which only occurs when it is accessed for the first time. Repeating the call to the object’s “hi” method reused the same global instance so there was no additional initialization
------------------------------------
Case Classes
-----------------------------------
scala> case class Character(name:String,isthief:Boolean)
defined class Character
scala> val h=new Character("7hills",true)
h: Character = Character(7hills,true)
scala> val u=new Character("hills",false)
u: Character = Character(hills,false)
scala> h match{
| case Character("x",true) => s"$x is theif"
| case Character("x",true) => s"$x is nottheif"
| }
<console>:16: error: not found: value x
case Character("x",true) => s"$x is theif"
^
<console>:17: error: not found: value x
case Character("x",true) => s"$x is nottheif"
^
scala> h match{
| case Character(x,true) => s"$x is nottheif"
| case Character(x,false) => s"$x is nottheif"
| }
res1: String = 7hills is nottheif
scala> h
res2: Character = Character(7hills,true)
scala> val r = h.Copy(name = "sarvan")
<console>:14: error: value Copy is not a member of Character
val r = h.Copy(name = "sarvan")
^
<console>:14: error: not found: value name
val r = h.Copy(name = "sarvan")
^
scala> val r = h.copy(name = "sarvan")
r: Character = Character(sarvan,true)
scala> r == h
res3: Boolean = false
scala> h match{
| case Character("x",true) => s"$x is theif"
| case Character(x,true) => s"$x is not theif"
| }
<console>:16: error: not found: value x
case Character("x",true) => s"$x is theif"
^
scala> h match{
| case Character(x,true) => s"$x is theif"
| case Character(x,false) => s"$x is not theif"
| }
res5: String = 7hills is theif
scala> r match{
| case Character(x,true) => s"$x is theif"
| case Character(x,false) => s"$x is not theif"
| }
res6: String = sarvan is theif
scala> h
res7: Character = Character(7hills,true)
scala> r
res8: Character = Character(sarvan,true)
scala> val u=new Character("7hills",true)
u: Character = Character(7hills,true)
scala> h == u
res9: Boolean = true
scala> u match{
| case Character(x,true) => s"$x is theif"
| case Character(x,false) => s"$x is not theif"
| }
res10: String = 7hills is theif
scala> val u=new Character("7hills",false)
u: Character = Character(7hills,false)
scala> u == h
res11: Boolean = false
scala> u match{
| case Character(x,true) => s"$x is theif"
| case Character(x,false) => s"$x is not theif"
| }
res12: String = 7hills is not theif
------------------------------------------------------------
Traits
------------------------------------------------------------
A trait is a kind of class that enables multiple inheritance. Classes, case classes, objects, and (yes) traits can all extend no more than one class but can extend multiple traits at the same time. Unlike the other types, however, traits cannot be instantiated
The process of linearization may seem odd, but it’s a useful compromise between the theory of a language supporting multiple inheritance versus the practice of an envi- ronment that doesn’t. It also provides a solid method for determining invocation, because the constructed hierarchy ensures that method handling is decided at compile time and never at runtime
Another benefit of linearization is that you can write traits to override the behavior of a shared parent class
scala> trait Base { override def toString = "Base" }
defined trait Base
scala> trait B extends Base { override def toString = "B->" + super.toString }
defined trait B
scala> trait C extends Base { override def toString = "B->" + super.toString }
defined trait C
scala> class D extends A with B with C { override def toString = "B->" + super.toString }
defined class D
scala> new D()
res14: D = B->B->B->A->Base
ERRoRS
------
scala> class A extends Base { override def toString = "A->" + super.toString }
defined class A
scala> class B extends Base { override def toString = "B->" + super.toString }
defined class B
scala> class C extends Base { override def toString = "C->" + super.toString }
defined class C
scala> class D extends A with B with C{ override def toString = "D>" + super.toString }
<console>:15: error: class B needs to be a trait to be mixed in
class D extends A with B with C{ override def toString = "D>" + super.toString }
^
<console>:15: error: class C needs to be a trait to be mixed in
class D extends A with B with C{ override def toString = "D>" + super.toString }
---------
solution: we only extennds the trait not the class
---------
scala> trait D extends A with B with C { override def toString = "B->" + super.toString }
defined trait D
scala> new D()
<console>:17: error: trait D is abstract; cannot be instantiated
new D()
^
---------
solution: we only instance/intiate(new) on class only but not on traits.so in this situvation
--------- last D extends with trait and it also creat as a class not trait(you sould use class keyword instead on trait
2nd example
----------
scala> class RGBColor(val color: Int) { def hex = f"$color%06X" }
defined class RGBColor
scala> val green = new RGBColor(255 << 8).hex
green: String = 00FF00
scala> trait Opaque extends RGBColor { override def hex = s"${super.hex}FF" }
defined trait Opaque
scala> trait Sheer extends RGBColor { override def hex = s"${super.hex}33" }
defined trait Sheer
------------------------------
Self Types
------------------------------
A self type is added immediately following the opening brace of the trait definition, and includes an identifier, the requested type, and an arrow (=>). A trait with a self type can access fields of that type as if it explicitly extended that type
Syntax: Defining a Self Type
trait ..... { <identifier>: <type> => .... }
scala> class A { def hi = "hi" }
defined class A
scala> trait B { self: A => override def toString = "B: " + hi} //SHOULD BE MENTION self keyword and =>
defined trait B
scala> class C extends B
<console>:13: error: illegal inheritance;
self-type C does not conform to B's selftype B with A
class C extends B
^
scala> class C extends A with B
defined class C
scala> new C()
res15: C = B: hi
1)Our trait B has a self type, adding the requirement that the trait can only ever be mixed into a subtype of the specified type, the A class. …
2) but just to prove it, let’s try defining a class with trait B but without the requested class. No luck.
3)This time, trait B is directly extending its requested type, A, so its self type requirement has been met.
4)When our C class is instantiated, B.toString is invoked, which then invokes A.hi. The B trait is indeed used as a subtype of A here and can invoke one of its methods.
Here is an example of a class extended by a trait with a self type of that class, ensuring that the trait will extend the class:
scala> class A
defined class A
scala> trait B { self: A => }
defined trait B
scala> val a = new A with B
a: A with B = $anon$1@331ff3ac
we created an instance where trait B extended trait A
Let’s experiment with dependency injection by taking a data-oriented class common in most applications, User, and altering its output in new and mysterious ways:
scala> class User(val name: String){
| def suffix =""
| override def toString = s"$name$suffix"
| }
defined class User
scala> trait Attorney { self: User => override def suffix = ", esq." }
defined trait Attorney
scala> trait Wizard { self: User => override def suffix = ", Wizard" }
defined trait Wizard
scala> trait Reverser { override def toString = super.toString.reverse }
defined trait Reverser
scala> val h = new User("Harry P") with Wizard
h: User with Wizard = Harry P, Wizard
scala> val g = new User("Ginny W") with Attorney
g: User with Attorney = Ginny W, esq.
------------------------------------------
Importing Instance Members
------------------------------------------
scala> case class Receipt(id: Int, amount: Double, who: String, title: String)
defined class Receipt
scala> {
| val latteReceipt = Receipt(123, 4.12, "fred", "Medium Latte")
| import latteReceipt._
| println(s"Sold a $title for $amount to $who")
| }
Sold a Medium Latte for 4.12 to fred
scala> import util.Random._ import util.Random._
<console>:1: error: ';' expected but 'import' found.
import util.Random._ import util.Random._
^
scala> import util.Random._
import util.Random._
scala> val letters = alphanumeric.take(20).toList.mkString
letters: String = WoVkuFqZOdP0Snh00FeF
scala> val numbers = shuffle(1 to 20)
numbers: scala.collection.immutable.IndexedSeq[Int] = Vector(5, 8, 10, 18, 14, 11, 9, 16, 12, 2, 4, 7, 19, 17, 15, 13, 1, 3, 20, 6)
********************************************************************
--------------------------------------------------------------
SCALA distinct predefined method
---------------------------------------------------------------
List[Int] = List(10, 20, 3, 6, 35)
distinct
-----------
scala> list.distinct
def distinct: List[Int]
distinct explanation: def -----------> keyword for function
-------------------- distinct -------> that is function name
note: it should return list after function operation is completed that function open with { and colose with }
scala> def distinct = {
| list
| }
distinct: List[Int]
scala> distinct
res13: List[Int] = List(10, 20, 3, 6, 35)
-----------
diff
-----------
scala> list.diff
def diff[B >: Int](that: scala.collection.GenSeq[B]): List[Int]
scala> def apply(n:Int) = { val list2=List(1, 7, 3, 4, 2, 2):+6
| list
| }
apply: (n: Int)List[Int]
scala> def diff[B >: Int](that: scala.collection.GenSeq[B]): List[Int]
| =
| {
| apply(B)
| }
diff: [B >: Int](that: scala.collection.GenSeq[B])List[Int]
***************************************************************************
TO PRINT THE LIST VALUES BY USING NAMEDFUNCTION
****************************************************************************
scala> listbig
res145: List[Int] = List(20, 30, 65, 88, 99, 101, 450, 101, 256, 12, 6, 8, 99, 45, 101)
scala> listbig.tail.size
res143: Int = 14 // it consider only within value position
scala> listbig.tails.size
res144: Int = 15 //it consider Nil position also
scala> def eachret() = {
| for(z <- 1 to listbig.tail.size) <--tail---->
| println("\n z is:"+z + ",listval:"+listbig(z))
| }
eachret: ()Unit
scala> eachret
z is:1,listval:30
z is:2,listval:65
z is:3,listval:88
z is:4,listval:99
z is:5,listval:101
z is:6,listval:450
z is:7,listval:101
z is:8,listval:256
z is:9,listval:12
z is:10,listval:6
z is:11,listval:8
z is:12,listval:99
z is:13,listval:45
z is:14,listval:101
********
ERROR
*******
scala> def eachret() = {
| for(z <- 0 to listbig.tails.size) <---------tails------>
| println("\n z is:"+z + ",listval:"+listbig(z))
| }
eachret: ()Unit
scala> eachret
z is:0,listval:20
z is:1,listval:30
z is:2,listval:65
z is:3,listval:88
z is:4,listval:99
z is:5,listval:101
z is:6,listval:450
z is:7,listval:101
z is:8,listval:256
z is:9,listval:12
z is:10,listval:6
z is:11,listval:8
z is:12,listval:99
z is:13,listval:45
z is:14,listval:101
java.lang.IndexOutOfBoundsException: 15
at scala.collection.LinearSeqOptimized$class.apply(LinearSeqOptimized.scala:65)
at scala.collection.immutable.List.apply(List.scala:84)
at $anonfun$eachret$1.apply$mcVI$sp(<console>:16)
at scala.collection.immutable.Range.foreach$mVc$sp(Range.scala:160)
at .eachret(<console>:15)
... 32 elided
solution
--------
Array Index out of bounds of exception comes when extract value without having that.
************************************************************************
TAKE THE LIST AS A PARAMETER AND DISPLAY TOTAL VALUES WITH POSTIONS BY USING SWITCH STATEMENTS
**************************************************************************
scala> def f(k:Any) = k match {
case k if(k == listbig) => for(z <- 1 to listbig.tail.size)
println("\n z is:"+z + ",listval:"+listbig(z))
case k if(k == list) => list
case _=> println("there is no values")
}
f: (k: Any)Any
scala> f(listbig)
z is:1,listval:30
z is:2,listval:65
z is:3,listval:88
z is:4,listval:99
z is:5,listval:101
z is:6,listval:450
z is:7,listval:101
z is:8,listval:256
z is:9,listval:12
z is:10,listval:6
z is:11,listval:8
z is:12,listval:99
z is:13,listval:45
z is:14,listval:101
res150: Any = ()
scala> f(list)
res151: Any = List(25, 33, 45, 66, 88)
scala> f(list1)
there is no values
res152: Any = ()
ERROR
------
1)
scala> def f(k:Any) = k Match {
| case if(k == listbig) => for(z <- 1 to listbig.tail.size)
<console>:2: error: illegal start of simple pattern
case if(k == listbig) => for(z <- 1 to listbig.tail.size)
^
SOL) case k if(k == listbig)=> for(z <- 1 to listbig.tail.size)
^
2)
scala> def f(k:Any) = k Match {
| case k if(k == listbig) => for(z <- 1 to listbig.tail.size)
| println("\n z is:"+z + ",listval:"+listbig(z))
| case k if(k == list) => list
| case _=> println("there is no values")
| }
<console>:15: error: value Match is not a member of Any
def f(k:Any) = k Match {
^
sol)match not Match
***************************************************************************
TO SEND THE DIFFERENT LIST PARAMETERS AND A VALUE AFTER SEARCHING IT WILL BE DISPLAY WHICH LIST CONTAINS THE VALUE
*****************************************************************************
scala> def f(y:Any,k:Any*) = k match {
| case k if(k == listbig) => for(z <- 1 to listbig.tail.size)
| if(listbig(z) == y)
| println("value found in the :"+listbig)
| case k if(k == list) => for(z <- 1 to list.tail.size)
| if(list(z) == y)
| println("value found in the :"+list)
| case k if(k == list1) => for(z <- 1 to list1.tail.size)
| if(list1(z) == 'y')
| println("value found in the :"+list1)
| case _=> println("there is no values")
| }
f: (y: Any, k: Any*)Unit
***NOTE: HERE IT IS NOT WORKING BECAUSE K:ANY* IS TAKE THE VALUES AS A ARRAY TYPE SO IT IS NOT POSSIBLE TO COMPARE INDIVIDUALLY IN LISTS
scala> def f(k:Any*,y:Any) = k match {
| case k if(k == listbig) => for(z <- 1 to listbig.tail.size)
| if(listbig(z) == y)
| println("value found in the :"+listbig)
| case k if(k == list) => for(z <- 1 to list.tail.size)
| if(list(z) == y)
| println("value found in the :"+list)
| case k if(k == list1) => for(z <- 1 to list1.tail.size)
| if(list1(z) == 'y')
| println("value found in the :"+list1)
| case _=> println("there is no values")
| }
<console>:16: error: *-parameter must come last
def f(k:Any*,y:Any) = k match {
^
============================================================================
CONNNECT THE MYSQL DATABASE USING SCALA AND RETRIVE THE DATA
============================================================================
scala> :paste
// Entering paste mode (ctrl-D to finish)
import java.sql.DriverManager
import java.sql.Connection
object DBConnection {
val driver="com.mysql.jdbc.Driver"
val url = "jdbc:mysql://localhost/7hills"
val username = "root"
val password = "hadoop"
def apply() = new DBConnection
}
class DBConnection{
private val props = Map(
"url" -> DBConnection.url,
"user" -> DBConnection.username,
"pass" -> DBConnection.password )
println(s"Created new connection for " + props("url"))
val connection = DriverManager.getConnection(DBConnection.url, DBConnection.username, DBConnection.password)
DriverManager.registerDriver(new com.mysql.jdbc.Driver ());
val statement = connection.createStatement()
val resultSet = statement.executeQuery("SELECT movieId,title FROM movies")
while(resultSet.next() ) {
val movieId = resultSet.getString("movieId")
val title = resultSet.getString("title")
println("movieId,title="+movieId+","+title)
}
}
ctrl+D
// Exiting paste mode, now interpreting.
import java.sql.DriverManager
import java.sql.Connection
defined object DBConnection
defined class DBConnection
scala> val conn = new DBConnection()
Created new connection for jdbc:mysql://localhost/7hills
movieId,title=1,the hero
conn: DBConnection = DBConnection@42bc14c1
*AFTER ADDING SOME ROWS IN MOVIES TABLE ***
scala> val conn = new DBConnection()
Created new connection for jdbc:mysql://localhost/7hills
movieId,title=1,the hero
movieId,title=2,theLEGEND
movieId,title=3,thetarminator
movieId,title=3,brundavanam
movieId,title=4,kithakithalu
conn: DBConnection = DBConnection@5fc930f0
****println("movieId ="+movieId+":TITLE:"+title)
scala> val conn = new DBConnection()
Created new connection for jdbc:mysql://localhost/7hills
movieId =1:TITLE:the hero
movieId =2:TITLE:theLEGEND
movieId =3:TITLE:thetarminator
movieId =3:TITLE:brundavanam
movieId =4:TITLE:kithakithalu
conn: DBConnection = DBConnection@4bc33720
===============================================================================
---------------------------------
Scalaís REPL provides support for evaluating and executing code one line at a time with helpful feedback
You can enter single lines of code to evaluate and compile, and any variables you create are available for the lifetime of your session. A multiline paste mode supports entering multiple lines of code to be compiled together (instead of individually), and external source code and libraries can be loaded at any time.
Here is how the input and response should appear in the REPL:
scala> println("Hello, World") Hello, World
scala> Congratulations, you have now written and executed Scala code.
The println() function, universally available to all Scala code, prints a message to the JVMís stdout stream
REPL history is stored between sessions, so you can quit, run the scala command again, and press up arrow to access your previous commands.
scala> 5 * 7
res0: Int = 35
Now that res0 contains the output of the multiplication command, lets make use of it. Type 2 * res0 at a fresh prompt and press Return. You should see something like this:
scala> 2 * res0
res1: Int = 70
Working with Data: Literals, Values, Variables, and Types
********************************************************************
we will cover the core data and variable types in Scala. Letís start with the definitions of the terms literal, value, variable, and type:
ï A literal (or literal data) is data that appears directly in the source code, like the number 5, the character A, and the text ìHello, World.î
ï A value is an immutable, typed storage unit. A value can be assigned data when it is defined, but can never be reassigned.
ï A variable is a mutable, typed storage unit. A variable can be assigned data when it is defined and can also be reassigned data at any time.
ï A type is the kind of data you are working with, a definition or classification of data. All data in Scala corresponds to a specific type, and all Scala types are defined as classes with methods that operate on the data.
The data stored in values and variables in Scala will get automatically deallocated by the Java Virtual Machineís garbage collection when they are no longer used. There is no ability, or need, to deallocate them manually.
Values
----------------
Values are immutable, typed storage units, and by convention are the default method for storing data.
You can define a new value using the val keyword.
Syntax: Defining a Value
----------------------------
val <identifier>[: <type>] = <data>
Values require both a name and assigned data, but they do not require an explicit type. If the type is not specified (i.e., the ì: <type>î syntax is not included), the Scala compiler will infer the type based on the assigned data.
Here are some examples of defining values with their type in the Scala REPL:
scala> val x: Int = 20 x: Int = 20
scala> val greeting: String = "Hello, World" greeting: String = Hello, World
scala> val atSymbol: Char = '@' atSymbol: Char = @
The Scala compiler will then discern the type of the value from its assignment, a process known as type inference. Values defined without a type are not typeless; they are assigned the proper type just as if the type had been included in the definition.
The Scala compiler, via the REPL, was able to deduce that the literal 20 corresponds to the type Int, the literal "Hello, World" to the type String, and the literal @ to the type Char.
val x: Int = "Hello"
The error here affirms that an Int type cannot be used to store a String.
VARIBLES:
-------------------
In computer science the term variable typically refers to a unique identifier corre- sponding to an allocated or reserved memory space, into which values can be stored and from which values can be retrieved. As long as the memory space is reserved, it can be assigned new values over and over again. Thus, the contents of the memory space are dynamic, or variable.
Variables are dynamic, mutable, and reassignable (with the exception of those defined with special restrictions such as Javaís final keyword).
an immutable value will be more stable and less prone to errors than mutable data that may be modified at unexpected times.
The var keyword is used to define a variable with a given name, type, and assignment.
Syntax: Defining a Variable
var <identifier>[: <type>] = <data>
NAMING:
--------------
Scala names can use letters, numbers, and a range of special operator characters. This makes it possible to use standard mathematical operators (e.g., * and :+) and constants (e.g., p and f) in place of longer names to make the code more expressive
while periods are reserved for access to the fields and methods of objects (instantiated types).
Here are the rules for combining letters, numbers, and characters into valid identifiers in Scala:
1. A letter followed by zero or more letters and digits.
Naming | 13
2. A letter followed by zero or more letters and digits, then an underscore (_), and then one or more of either letters and digits or operator characters.
3. One or more operator characters.
4. One or more of any character except a backquote, all enclosed in a pair of back- quotes.
Names enclosed in backquotes can, unlike the other names, be re- served keywords in Scala such as true, while, =, and var
Letís try out some of these naming rules in the REPL:
scala> val p = 3.14159
p: Double = 3.14159
scala> val $ = "USD currency symbol"
$: String = USD currency symbol
scala> val o_O = "Hmm"
o_O: String = Hmm
scala> val 50cent = "$0.50"
<console>:1: error: Invalid literal number
val 50cent = "$0.50"^
scala> val a.b = 25
<console>:7: error: not found:
value a val a.b = 25
scala> val `a.b` = 4
a.b: Int = 4
The special character ìpî is a valid Scala identifier.
The value name ì50centî is invalid because names cannot start with numbers. In this case the compiler started parsing the name as a literal number and ran into problems at the letter ìcî.
The value name ìa.bî is invalid because a period isnít an operator character. Rewriting this value with backquotes fixes the problem, although the aesthetics of using backquotes isnít that great.
Value and variable names, by convention, should start with a lowercase letter and then capitalize additional words. This is popularly known as camel case, and though not
Types
-----------------
Scala has both numeric (e.g., Int and Double) and nonnumeric types (e.g., String) that can be used to define values and variables. These core types are the building blocks for all other types including objects and collections, and are themselves objects that have methods and operators that act on their data.
Scala only supports its own integer class, Int.
Numeric Data Types Table 2-1 displays Scalaís numeric data types.
Table 2-1. Core numeric types
Name Description Size Min Max
Byte Signed integer 1 byte ñ127 128
Short Signed integer 2 bytes ñ32768 32767 Int Signed integer 4 bytes ñ231 231ñ1 Long Signed integer 8 bytes ñ263 263ñ1
Float Signed floating point 4 bytes n/a n/a
Double Signed floating point 8 bytes n/a n/a
Scala does not allow automatic conversion from higher ranked types to lower ranked types. This makes sense, because you could otherwise lose data if you convert to a type with less storage. Here is an example of trying to automatically convert a higher ranked type to a lower ranked type and the ensuing error:
scala> val l: Long = 20 l: Long = 20
scala> val i: Int = l
<console>:8: error: type mismatch; found : Long required: Int val i: Int = l You can choose to manually convert between types using the toType methods available on all numeric types.
For example, here is a Long value that can be safely converted to type Int using the toInt method, because its data is within the storage bounds of an Int:
scala> val l: Long = 20 l: Long = 20
scala> val i: Int = l.toInt i: Int = 20 An alternative to using explicit types is to specify the type of your literal data directly, using Scalaís notation for literal types.
Strings
---------------
The String type represents ìstringsî of text, one of the most common core types in any programming language. Scalaís String is built on Javaís String and adds unique fea- tures like multiline literals and string interpolation.
Write String literals using double quotes, with special characters escaped with back- slashes:
scala> val hello = "Hello There" hello: String = Hello There
scala> val signature = "With Regards, \nYour friend" signature: String = With Regards, Your friend
Like numeric types, the String type supports the use of math operators.
For example, use the equals operator (==) to compare two String values. Unlike Java, the equals operator (==) checks for true equality, not object reference equality:
scala> val greeting = "Hello, " + "World" greeting: String = Hello, World
scala> val matched = (greeting == "Hello, World") matched: Boolean = true
scala> val theme = "Na " * 16 + "Batman!" // what do you expect this to print?
String interpolation Building a String based on other values is reasonably easy to do with string addition.
Here is a String built by adding text before and after the Float value:
scala> val approx = 355/113f approx: Float = 3.141593
scala> println("Pi, using 355/113, is about " + approx + "." ) Pi, using 355/113, is about 3.141593. A more direct way to combine your values or variables inside a String is with string interpolation, a special mode where external value and variable names are recognized and resolved. The Scala notation for string interpolation is an ìsî prefix added before the first double quote of the string. Then dollar sign operators ($) (with optional braces) can be used to note references to external data.
Here is the example again using string interpolation:
-------------------------
scala> println(s"Pi, using 355/113, is about $approx." )
Pi, using 355/113, is about 3.141593.
scala> s"How do you like them ${item}s?" res0: String = How do you like them apples?
scala> s"Fish n chips n vinegar, ${"pepper "*3}salt" res1: String = Fish n chips n vinegar, pepper pepper pepper salt
To use printf notation change the prefix to an ìfî and follow the end of the reference immediately with the printf notation:
scala> f"Enjoying this $item ${355/113.0}%.5f times today"
res3: String = Enjoying this apple 3.14159 times today
These printf notations make the references a little harder to read than in the previous examples, but do provide essential control over the output.
Regular expressions
--------------------------------
regular expressions may be different from the format you have used with other languages and tools.
The String type provides a number of built-in operations that support regular expres- sions. Table 2-3 displays a selection of these operations
Table 2-3. Regular expression operations
Name Example Description
-------- -------------------- --------------------------------------------
matches "Froggy went a' courting" matches ".* courting" Returns true if the regular expression matches the entire string.
replaceAll "milk, tea, muck" replaceAll ("m[^ ] +k", "coffee") Replaces all matches with replacement text.
replaceFirst "milk, tea, muck" replaceFirst ("m[^ ] +k", "coffee") Replaces the first match with replacement text.
Syntax: Capturing Values with Regular Expressions
val <Regex value>(<identifier>) = <input string>
overview variable datatypes
----------------------
Figure 2-1 shows the hierarchy of Scalaís core (numeric and nonnumeric) types.
At the bottom of the Scala type hierarchy are the Nothing and Null types. Nothing is a subtype of every other type and exists to provide a compatible return type for operations that significantly affect a programís flow
Type operations
----------------------
Table 2-5 displays the operations available on all types in Scala. The toString and hashCode methods are required on all JVM instances.
Table 2-5. Common type operations
Name Example Description
asInstanceOf[<type>] 5.asInstanceOf[Long] converts the value to a value of the desired type. Causes an error if the value is not compatible with the new type.
getClass (7.0 / 5).getClass Returns the type (i.e., the class) of a value.
isInstanceOf (5.0).isInstanceOf[Float] Returns true if the value has the given type.
hashCode "A".hashCode Returns the hash code of the value, useful for hash- based collections.
to<type> 20.toByte; 47.toFloat Conversion functions to convert a value to a compatible value.
toString (3.0 / 4.0).toString Renders the value to a String.
NOTE:
----------
Avoid asInstanceOf The asInstanceOf operation will cause an error if the value can- not be converted to the requested type. To avoid runtime errors with this operation, prefer the to<type> typed conversion operations when possible
Tuples
---------------------
A tuple is an ordered container of two or more values, all of which may have different types. You may be familiar with this term from working with relational databases, where a single row of a table is considered its own tuple. Tuples can be useful when you need to logically group values, representing them as a coherent unit. Unlike lists and arrays, however, there is no way to iterate through elements in a tuple. Its purpose is only as a container for more than one value. You can create a tuple by writing your values separated by a comma and surrounded by a pair of parentheses.
Syntax: Create a Tuple( <value 1>, <value 2>[, <value 3>...] )
For example, here is a tuple containing Int, String, and Boolean values:
scala> val info = (5, "Korben", true) info: (Int, String, Boolean) = (5,Korben,true)
You can access an individual element from a tuple by its 1-based index (e.g., where the first element is 1, second is 2, etc.):
scala> val name = info._2 name: String = Korben
An alternate form of creating a 2-sized tuple is with the relation operator (->). This is a popular shortcut for representing key-value pairs in tuples:
scala> val red = "red" -> "0xff0000" red: (String, String) = (red,0xff0000)
scala> val reversed = red._2 -> red._1 reversed: (String, String) = (0xff0000,red)
Types | 25
Tuples provide a generic means to structure data, and are useful when you need to group discrete elements for handling.
scala>val info="sss" -> "hi"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Expressions and Conditionals
****************************************************************************************************************************************************
The term expression as used in this book indicates a unit of code that returns a value after it has been executed. One or more lines of code can be considered an expression if they are collected together using curly braces ({ and }). This is known as an expression block. Expressions provide a foundation for functional programming because they make it possible to return data instead of modifying existing data (such as a variable). This enables the use of immutable data, a key functional programming concept where new data is stored in new values instead of in existing variables.
The return values of expressions will be passed to other expressions or stored into values. As you migrate from using variables, your functions and expressions will have fewer side effects. In other words, they will purely act on the input you give them without affecting any data other than their return value. This is one of the main goals and benefits of functional programming
Defining Values and Variables with Expressions
-----------------------------------------------------------
Syntax: Defining Values and Variables, Using Expressions
val <identifier>[: <type>] = <expression> var <identifier>[: <type>] = <expression>
Expression Blocks
--------------------------
The last expression in the block is the return value for the entire block.
Expression blocks can span as many lines as you need. The preceding example could have been rewritten without the semicolons as follows:
scala> val amount = { | val x = 5 * 20 | x + 10 | }
amount: Int = 110 Expression blocks are also nestable, with each level of expression block having its own scoped values and variables.
Here is a short example demonstrating a three-deep nested expression block:
scala> { val a = 1; { val b = a * 2; { val c = b + 4; c } } }
res5: Int = 6
Statements
----------------
A statement is just an expression that doesnít return a value
scala> val x = 1
x: Int = 1
The REPL repeats the definition of x but there is not actual data returned that can be used to create a new value. A statement block, unlike an expression block, does not return a value. Because a state- ment block has no output, it is commonly used to modify existing data or make changes outside the scope of the application
If..Else Expression Blocks
------------------------------------
As a matter of practice you can write these same ìif .. else if .. elseî blocks in Scala and they will work just as you have experienced them in Java and other languages. As a matter of formal syntax, however, Scala only supports a single ìifî and optional ìelseî block, and does not recognize the ìelse ifî block as a single construct. So how do ìelse ifî blocks still work correctly in Scala? Because ìif .. elseî blocks are based on expression blocks, and expression blocks can be easily nested, an ìif .. else if .. elseî expression is equivalent to a nested ìif .. else { if .. else }î expression. Logically this is exactly the same as an ìif .. else if .. elseî block, and as a matter of syntax Scala recognizes the second ìif elseî as a nested expression of the outer ìif .. elseî block
If Expressions Syntax: Using an If Expression
if (<Boolean expression>) <expression>
If the Boolean expression returns false, what do you expect the if block to return?
scala> val result = if ( false ) "what does this return?"
result: Any = ()
The type of the result value in this example is unspecified so the compiler used type inference to determine the most appropriate type. Either a String or Unit could havebeen returned, so the compiler chose the root class Any. This is the one class common to both String (which extends AnyRef) and to Unit (which extends AnyVal).
If-Else Expressions Syntax: If .. Else Expressions
if (<Boolean expression>) <expression> else <expression>
Here is an example:
scala> val x = 10; val y = 20
x: Int = 10 y: Int = 20
scala> val max = if (x > y) x else y
max: Int = 20
--->Some wonder why Scala doesnít have a ternary expression (popular in C and Java) where the punctuation characters ? and : act as a one-line if and else expression
---> if expressions without an else should always use curly braces, because they tend to be statements that create side effects.
---->if..else blocks are a simple and common way to write conditional logic. There are other, more elegant ways to do so in Scala, however, using match expressions
----------------------------------------------------------
Match Expressions
---------------------------------------------------------
Match expressions are akin to Cís and Javaís ìswitchî statements, where a single input item is evaluated and the first pattern that is ìmatchedî is executed and its value re- turned.
--->in scala matching expressions are maily in two types and one is alternative expression.they are
---->In fact, most Scala developers prefer match expressions over ìif .. elseî blocks because of their expressiveness and concise syntax
Syntax: Using a Match Expressio<expression> match { case <pattern match> => <expression> [case...] }
scala> val status = 500
status: Int = 500
scala> val message = status match {
| case 200 => "ok"
| case 400 => {
| println("ERROR - we called the service incorrectly")
| "error"
|
}
|
case 500 => {
| println("ERROR - the service encountered an erro
| "error"
|
}
|
}
ERROR - the service encountered an error
message: String = error
There is no limit to the number of statements and expressions you can have inside a case block, although only the last expression will be used for the match expressionís return value.
Syntax: A Pattern Alternative
-------------------------------------
case <pattern 1> | <pattern 2> .. => <one or more expressions>
scala> val day = "MON" day: String = MON
scala> val kind = day match { | case "MON" | "TUE" | "WED" | "THU" | "FRI" => | "weekday" |
case "SAT" | "SUN" => | "weekend" |
}
kind: String = weekday
Matching with Wildcard Patterns
-------------------------------------------
Syntax: A Value Binding Pattern
case <identifier> => <one or more expressions>
Here is an example that tries to match a specific literal and otherwises uses value binding to ensure all other possible values are matched:
scala> val message = "Ok"
message: String = Ok
scala> val status = message match { | case "Ok" => 200 |
case other => { | println(s"Couldn't parse $other") | -1 |
}
|
}
status: Int = 200
The value other is defined for the duration of the case block and is assigned the value of message, the input to the match expression. The other type of wildcard pattern is the use of the wildcard operator. This is an un- derscore (_) character that acts as an unnamed placeholder for the eventual value of an expression at runtime. As with value binding, the underscore operator doesnít provide a pattern to match against, and thus it is a wildcard pattern that will match any input value.
Syntax: A Wildcard Operator Pattern
-----------------------------------------------
case _ => <one or more expressions>
The wildcard cannot be accessed on the right side of the arrow, unlike with value bind- ing. If you need to access the value of the wildcard in the case block, consider using a value binding, or just accessing the input to the match expression (if available).
Why an Underscore as a wildcard? Using underscores to indicate unknown values comes from the field of mathematics, arithmetic in particular, where missing amounts are denoted in problems with one or more underscores. For example, the equation 5 * _ = 15 is a problem that must be solved for _, the missing value.
Here is a similar example to the one earlier only
Here is a similar example to the one earlier only with a wildcard operator instead of a bound value:
scala> val message = "Unauthorized"
message: String = Unauthorized
scala> val status = message match { | case "Ok" => 200 | case _ => { | println(s"Couldn't parse $message") | -1 | } | } Couldn't parse Unauthorized status: Int = -1
In this case the underscore operator matches the runtime value of the input to the match expression.
However, it canít be accessed inside the case block as a bound value would, and thus the input to the match expression is used to create an informative println statement.
Matching with Pattern Guards
---------------------------------------
A pattern guard adds an if expression to a value-binding pattern, making it possible to mix conditional logic into match expressions. When a pattern guard is used the pattern will only be matched when the if expression returns true.
Syntax: A Pattern Guard
-------------------------------
case <pattern> if <Boolean expression> => <one or more expressions>
Unlike regular if expressions, the if expression here doesnít require parentheses (( and )) around its Boolean expression. Regular if expressions require the parentheses in order to simplify the job of parsing the full command and delineate the Boolean expression from the conditional expression. In this case the arrow (=>) handles that task and simplifies parsing. You can, however, add the parentheses around the Boolean ex- pression if you wish. Letís use a pattern guard to differentiate between a nonnull and a null response and report the correct message:
scala> val response: String = null
response: String = null
scala> response match { | case s if s != null => println(s"Received '$s'") | case s => println("Error! Received a null response") | }
Error! Received a null response
Matching Types with Pattern Variables
------------------------------------------------
Another way to do pattern matching in a match expression is to match the type of the input expression. Pattern variables, if matched, may convert the input value to a value with a different type. This new value and type can then be used inside the case block.
Syntax: Specifying a Pattern Variable
----------------------------------------------
case <identifier>: <type> => <one or more expressions>
port of polymorphic types in Scala should be a clue to a match expressionís utility. A value of type Int may get assigned to another value of type Any, or it may be returned as Any from a Java or Scala library call
Letís reproduce this situation by creating an Int, assigning it to an Any, and using a match expression to resolve its true type:
scala> val x: Int = 12180
x: Int = 12180
scala> val y: Any = x
y: Any = 12180
scala> y match { | case x: String => s"'x'" | case x: Double => f"$x%.2f" | case x: Float => f"$x%.2f" | case x: Long => s"${x}l" | case x: Int => s"${x}i" | }
res9: String = 12180i
Nested Iterators
-----------------------
Nested iterators are extra iterators added to a for-loop, multiplying the total number of iterations by their iterator count. They are called nested iterators because adding them to an existing loop has the same effect as if they were written as a separate nested loop. Because the total number of iterations is the product of all of the iterators, adding a nested loop that will iterate once will not change the number of iterations, whereas a nested loop that does not iterate will cancel all iterations
Here is an example of a for-loop with two iterators:
scala> for { x <- 1 to 2 | y <- 1 to 3 } | { print(s"($x,$y) ") }
scala>(1,1) (1,2) (1,3) (2,1) (2,2) (2,3)
Because the product of the two iterators is six iterations, the print statement is called six times.
While and Do/While Loops
---------------------------------------
In addition to for-loops Scala also supports ìwhileî and ìdo/whileî loops, which repeat a statement until a Boolean expression returns false. These are not as commonly used as for-loops in Scala, however, because they are not expressions and cannot be used to yield values.
Syntax: A While Loop
----------------------------
while (<Boolean expression>) statement
As a very simple example here is a while loop that decrements a number repeatedly until it is no longer greater than zero:
scala> var x = 10; while (x > 0) x -= 1
The ìdo/whileî loop is similar but the statement is executed before the Boolean expres- sion is first evaluated. In this example I have a Boolean expression that will return false, but is only checked after the statement has had a chance to run:
scala> val x = 0 x: Int = 0
scala> do println(s"Here I am, x = $x") while (x > 0)
Here I am, x = 0
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
FUNCTIONS
****************************************************************
---> Functions are the core building blocks of reusable logic.
----> In Scala, functions are named, reusable expressions. They may be parameterized and they may return a value, but neither of these features are required. These features are, however, useful for ensuring maximum reusability and composability. They will also help you write shorter, more readable, and more stable applications. Using parameter- ized functions you can normalize duplicated code, simplifying your logic and making it more discoverable. Testing your code becomes easier, because normalized and para- meterized logic is easier to test than denormalized logic repeated throughout your code
Syntax: Defining an Input-less Function (SOME TIMES ITS NOWN TO BE UNNUMOUS FUNCTIONS)
------------------------------------------------
def <identifier> = <expression> OR def() <identifier> = <expression>
NOTE:A Scala convention for input-less functions is that they should be defined with empty parentheses if they have side effects (i.e., if the function modifies data outside its scope). For example, an input-less function that writes a message to the console should be defined with empty parentheses
EXAMPLE:
--------------
scala> def hi = "hi"
hi: String
scala> hi
res0: String = hi
OR
scala> def hi(): String = "hi"
hi: ()String
scala> hi()
res1: String = hi
scala> hi r
es2: String = hi
Syntax: Defining a Function with a Return Type
-------------------------------------------------------------
def <identifier>: <type> = <expression>
scala> def hi: String = "hi"
hi: String
NAMED FUNCTIONS
------------------------------
Now weíre ready to look at a full function definition.
Syntax: Defining a Function
def <identifier>(<identifier>: <type>[, ... ]): <type> = <expression>
Letís try creating a function that performs an essential mathematical operation:
scala> def multiplier(x: Int, y: Int): Int = { x * y } //HERE multiplier is the function name
multiplier: (x: Int, y: Int)Int
scala> multiplier(6, 7)
res0: Int = 42
A common use of an early function exit is to stop further execution in the case of invalid or abnormal input values.
For example, this ìtrimî function validates that the input value is nonnull before calling the JVM Stringís ìtrimî method:
scala> def safeTrim(s: String): String = {
| if (s == null) return null
| s.trim()
|
}
safeTrim: (s: String)String
-----------------------
PROCEDURES
---------------------
A procedure is a function that doesnít have a return value
Here is a simple logging procedure, defined with an implicit return type and then with an explict return type:
scala> def log(d: Double) = println(f"Got value $d%.2f")
log: (d: Double)Unit
scala> def log(d: Double): Unit = println(f"Got value $d%.2f")
log: (d: Double)Unit
scala> log(2.23535)
Got value 2.24
deprecated syntax you will see for procedures is to define them without the Unit return type and without an equals sign before the pro- cedure body. With this syntax the example log() method would be written like this:
scala> def log(d: Double) { println(f"Got value $d%.2f") }
log: (d: Double)Unit
-------------------------------------------------------
Function Invocation with Expression Blocks
-------------------------------------------------------
Function Invocation with Expression Blocks
One example where using an expression block to invoke a function may be preferable is when you have to send a calculated value to the function. Instead of calculating the amount and storing it in local values to be passed to the function, you can do the cal- culations inside the expression block. The expression block will be evaluated before the function is called and the blockís return value will be used as the function argument. Here is an example of calculating values inside a function block used for invoking a function:
scala> def formatEuro(amt: Double) = f"Ä$amt%.2f"
formatEuro: (amt: Double)String
scala> formatEuro(3.4645)
res4: String = Ä3.46
scala> formatEuro { val rate = 1.32; 0.235 + 0.7123 + rate * 5.32 }
res5: String = Ä7.97
-------------------------
Recursive Functions
-----------------------------
A recursive function is one that may invoke itself, preferably with some type of parameter or external condition that will be checked to avoid an infinite loop of function invoca- tion. Recursive functions are very popular in functional programming because they offer a way to iterate over data structures or calculations without using mutable data, because each function call has its own stack for storing function parameters. Hereís an example of a recursive function that raises an integer by a given positive exponent:
scala> def power(x: Int, n: Int): Long = {
| if (n >= 1) x * power(x, n-1)
| else 1
|
}
power: (x: Int, n: Int)Long
scala> power(2, 8)
res6: Long = 256
scala> power(2, 1)
res7: Long = 2
scala> power(2, 0)
res8: Long = 1
One problem with using recursive functions is running into the dreaded ìStack Over- flowî error, where invoking a recursive function too many times eventually uses up all of the allocated stack space. To prevent this scenario, the Scala compiler can optimize some recursive functions with tail-recursion so that recursive calls do not use additional stack space. With tail- recursionñoptimized functions, recursive invocation doesnít create new stack space but instead uses the current functionís stack space. Only functions whose last statement is the recursive invocation can be optimized for tail-recursion by the Scala compiler. If the result of invoking itself is used for anything but the direct return value, a function canít be optimized. Fortunately there is a function annotation available to mark a function as being intended to be optimized for tail-recursion. A function annotation is special syntax carried over
50 | Chapter 4: Functions
from the Java programming language where the ìatî sign (@) and an annotation type is placed before a function definition to mark it for special use. A function marked with the tail-recursion function annotation will cause an error at compilation time if it cannot be optimized for tail-recursion.
To mark a function as intended for tail-recursion, add the text @annotation.tailrec before the function definition or on the previous line. Hereís the same example again only marked with the ìtailrecî annotation, to let the Scala compiler know we expect it to be optimized for tail-recursion and that if it cannot be, the compiler should treat it as an error:
scala> @annotation.tailrec
| def power(x: Int, n: Int): Long = { | if (n >= 1) x * power(x, n-1) | else 1 | }
<console>:9: error: could not optimize @tailrec annotated method power: it contains a recursive call not in tail position
if (n >= 1) x * power(x, n-1) Ah,
the function couldnít be optimized because the recursive call is not the last statement in the function. This is understandable. Iíll switch the ìifî and ìelseî conditions and try again:
scala> @annotation.tailrec
| def power(x: Int, n: Int): Long = { | if (n < 1) 1 | else x * power(x, n-1) | }
<console>:11: error: could not optimize @tailrec annotated method power: it contains a recursive call not in tail position
else x * power(x, n-1)
^ Hmm, the recursive call is the last item in the function. Oh I see, weíre taking the result of the recursive call and multiplying it by a value, so that multiplication is actually the last statement in the function, not the recursive call. A good way to fix this is to move the multiplication into the beginning of the invoked function instead of multiplying its result. Now the end of the function is a simple un- touched result from the recursive call:
scala> @annotation.tailrec | def power(x: Int, n: Int, t: Int = 1): Int = { | if (n < 1) t | else power(x, n-1, x*t) | }
power: (x: Int, n: Int, t: Int)Int
Recursive Functions | 51
scala> power(2,8)
res9: Int = 256 Success!
The ìtailrecî annotation and successful compile guarantees that the function will be optimized with tail-recursion, so that each successive call will not add more stack frames. Although this example may seem challenging to get through, recursion and tail- recursion are still valuable methods for iterating without using mutable data. Youíll find that many of the data structures weíll explore later in the book will be rich with functions that are implemented with tail-recursion.
-----------------------
Nested Functions
--------------------------
Functions are named, parameterized expression blocks and expression blocks are ne- stable, so it should be no great surprise that functions are themselves nestable. There are times when you have logic that needs to be repeated inside a method, but would not benefit from being extrapolated to an external method. In these cases defining an internal function inside another function, to only be used in that function, may be worthwhile. Letís have a look at a method that takes three integers and returns the one with the highest value:
scala> def max(a: Int, b: Int, c: Int) = { | def max(x: Int, y: Int) = if (x > y) x else y | max(a, max(b, c)) | }
max: (a: Int, b: Int, c: Int)Int
scala> max(42, 181, 19)
res10: Int = 181
The logic inside the max(Int, Int) nested function was defined once but used twice inside the outer function, making it possible to reduce duplicated logic and simplify the overall function. The nested function here has the same name as its outer function, but because their parameters are different (the nested one only takes two integers) there is no conflict between them. Scala functions are differentiated by their name and the list of their parameter types. However, even if the names and parameter types were the same there would be no confict because the local (nested) one takes precedence over the outer one
NOTE: we must be variate the function name in inner and outer functions by applay the diffrent function name or diffrent parameterrs.
----------------------------------------------------
Calling Functions with Named Parameters
-----------------------------------------------------
Syntax: Specifying a Parameter by Name
<function name>(<parameter> = <value>)
In this example, a simple two-parameter function is invoked twice, first using the con- vention of specifying parameters by their order and then by assigning values by parameter name:
scala> def greet(prefix: String, name: String) = s"$prefix $name"
greet: (prefix: String, name: String)String
scala> val greeting1 = greet("Ms", "Brown")
greeting1: String = Ms Brown
scala> val greeting2 = greet(name = "Brown", prefix = "Mr")
greeting2: String = Mr Brown
------------------------------------------------------------------------
Parameters with Default Values or Function Overloading
------------------------------------------------------------------------
A common problem when defining functions is deciding which input parameters they should take to maximize reuse. In Scala, Java, and other languages, a common solution is to provide multiple versions of the same function with the same name but different lists of input parameters. This practice is known as function overloading due to the functionís name being reused for different inputs. The common practice is to copy a function with x number of parameters to a new function with xñ1 parameters that invokes the original function using a default value for the missing parameter.
Syntax: Specifying a Default Value for a Function Parameter
def <identifier>(<identifier>: <type> = <value>): <type>
scala> def greet(prefix: String = "", name: String) = s"$prefix$name"
greet: (prefix: String, name: String)String
scala> val greeting1 = greet(name = "Paul")
greeting1: String = Paul
ala> def greet(name: String, prefix: String = "") = s"$prefix$name"
greet: (name: String, prefix: String)String
scala> val greeting2 = greet("Ola")
greeting2: String = Ola
-------------------------
Vararg Parameters
--------------------------
scala also supports vararg parameters, so you can define a function with a variable number of input arguments. The vararg parameter cannot be followed by a nonvararg parameter because there would be no way to distinguish them. Inside the function, the vararg parameter, implemented as a collection (which weíll study in Chapter 6), can be used as an iterator in for loops. To mark a function parameter as matching one or more input arguments, add an asterisk symbol (*) after the parameterís type in the function definition. Here is an example of using a vararg parameter to create a summing function that returns a sum of all of its input integers:
scala> def sum(items: Int*): Int = { | var total = 0 | for (i <- items) total += i | total | }
sum: (items: Int*)Int
scala> sum(10, 20, 30)
res11: Int = 60
scala> sum()
res12: Int = 0
-----------------------------------------------------------------------------------
Parameter Groups OR Curried Functions
------------------------------------------------------------------------------------
So far we have looked at parameterized function definitions as a list of parameters surrounded by parentheses. Scala provides the option to break these into groups of parameters, each separated with their own parentheses. Here is an example of the ìmaxî function where the two input parameters have been split into their own parameter groups:
scala> def max(x: Int)(y: Int) = if (x > y) x else y
max: (x: Int)(y: Int)Int
scala> val larger = max(20)(39)
larger: Int = 39
*******************************************************************************
-----------------------------------------------------------------------------------------------------------------------
FIRST CLASS FUNCTION
-----------------------------------------------------------------------------------------------------------------------
---> Functions that accept other functions as parameters and/or use functions as return values are known as higher-order functions. You may have heard of two of the most famous higher-order functions, map() and reduce().
-->Scala has full support for first-class functions, higher-order functions, and the use of declarative programming. As with other types of data such as String or Int, functions have types based on the types of their input arguments and their return value. A function can be stored in a value or variable, passed to a function, returned from a function, and used with data structures to support map(), reduce(), fold(), and filter(), among many other higher-order functions.
--------------------------------------------------------------------------------------------------------------
Function Types and Values
---------------------------------------------------------------------------------------------------------------
The type of a function is a simple grouping of its input types and return value type, arranged with an arrow indicating the direction from input types to output type.
Syntax: A Function Type
([<type>, ...]) => <type>
For example, the function def double(x: Int): Int = x * 2 has the function type Int => Int, indicating that it has a single Int parameter and returns an Int. The function name, ìdouble,î is an identifier and isnít part of the type. The body of the function, a simple multiplication of the input by 2, does not affect the type of the func- tion. The rest of the information is the input types and return type, and so these make up the function type itself.
Letís try using a function type in the REPL, by creating a function and then assigning it to a function value:
scala> def double(x: Int): Int = x * 2
double: (x: Int)Int
scala> double(5)
res0: Int = 10
scala> val myDouble: (Int) => Int = double
myDouble: Int => Int = <function1>
scala> myDouble(5)
res1: Int = 10
Syntax: Assigning a Function with the Wildcard Operator
----------------------------------------------------------------------
val <identifier> = <function name> _
Letís try this out with the ìmyDoubleî function value:
scala> def double(x: Int): Int = x * 2
double: (x: Int)Int
scala> val myDouble = double _
myDouble: Int => Int = <function1>
scala> val amount = myDouble(20)
amount: Int = 40
-->This time, the explicit function type for myDouble wasnít required to distinguish it from a function invocation. The underscore (_) served as a placeholder for a future invocation of the function, returning a function value that we could store in myDouble
Hereís an example of a function value defined with an explicit function type using mul- tiple parameters, enclosed with parentheses:
scala> def max(a: Int, b: Int) = if (a > b) a else b
max: (a: Int, b: Int)Int
scala> val maximize: (Int, Int) => Int = max maximize: (Int, Int) => Int = <function2>
scala> maximize(50, 30)
res3: Int = 50
HIGHORDER FUNCTION SAMPLE EXAMPLE
---------------------------------------------------------------
scala> def logStart() = "=" * 50 + "\nStarting NOW\n" + "=" * 50
logStart: ()String
scala> val start: () => String = logStart
start: () => String = <function0>
scala> println( start() ) ==================================================
Starting NOW
==================================================
---->This was a gentle introduction to how functions may be treated as data, by storing them in values and assigning them static types
--->the next few sections on higher-order functions and function literals are going to build on this knowledge and cover some challenging new syntax
------------------------------------------------------------
Higher-Order Functions
---------------------------------------------------------------
DESCRIPTION: We have already defined values that have a function type. A higher-order function is a function that has a value with a function type as an input parameter or return value.
EXAMPLE:
---------------
scala> def safeStringOp(s: String, f: String => String) = {
| if (s != null) f(s) else s
|
}
safeStringOp: (s: String, f: String => String)String
scala> def reverser(s: String) = s.reverse
reverser: (s: String)String
scala> safeStringOp(null, reverser)
res4: String = null
scala> safeStringOp("Ready", reverser)
res5: String = ydaeR
--->The call with ìnullî safely returned the same value back, whereas the call with a valid String returned the reverse of the input value
---------------------------------------
Function Literals
---------------------------------------
Now weíll tackle a difficult concept covered by a variety of names by starting with an easy example. In this example we will create a function literal, a working function that lacks a name, and assign it to a new function value:
scala> val doubler = (x: Int) => x * 2 doubler: Int => Int = <function1>
scala> val doubled = doubler(22)
doubled: Int = 44
The function literal in this example is the syntax (x: Int) => x * 2, which defines a typed input argument (x) and the function body (x * 2). Function literals can be stored in function values and variables, or defined as part of a higher-order function invocation. You can express a function literal in any place that accepts a function type.
Syntax: Writing a Function Literal
-------------------------------------------
([<identifier>: <type>, ... ]) => <expression>
Letís define a function value and assign it a new function literal:
scala> val greeter = (name: String) => s"Hello, $name"
greeter: String => String = <function1>
scala> val hi = greeter("World")
hi: String = Hello, World
If you think about it, a function literal is essentially a parameterized expression. We know about expressions that return a value, but now have a way to parameterize their input.
-------------------------------------------
Placeholder Syntax
------------------------------------------
Placeholder syntax is a shortened form of function literals, replacing named parameters with wildcard operators (_). It can be used when
(a) the explicit type of the function is specified outside the literal and
(b) the parameters are used no more than once
Here is an example of a doubling function literal using wildcard operators in place of named parameters:
scala> val doubler: Int => Int = _ * 2
doubler: Int => Int = <function1>
--->Placeholder syntax is valid here because the input parameter is only used once and the literalís type has an external explicit definition (in the value).
Letís demonstrate how this ordering of placeholders works by trying an example with two placeholders:
scala> def combination(x: Int, y: Int, f: (Int,Int) => Int) = f(x,y)
combination: (x: Int, y: Int, f: (Int, Int) => Int)Int
scala> combination(23, 12, _ * _)
res13: Int = 276
Letís kick the number of placeholders up for the last time, from two to three. Is this example still easily readable?
scala> def tripleOp(a: Int, b: Int, c: Int, f: (Int, Int, Int) => Int) = f(a,b,c)
tripleOp: (a: Int, b: Int, c: Int, f: (Int, Int, Int) => Int)Int
scala> tripleOp(23, 92, 14, _ * _ + _)
res14: Int = 2130
--->The tripleOp function takes four parameters: three Int values and a function that can reduce them down to a single Int. The actual function body is far shorter than the parameter list, and applies the function to the input values.
--->This example function, tripleOp, is limited to integer values. However, wouldnít it be more useful if it was generic and supported type parameters?
one for the common input type and one for the single return value type. This will give us some flexibility to call the tripleOp function with any type of inputs or anonymous functions that
scala> def tripleOp[A,B](a: A, b: A, c: A, f: (A, A, A) => B) = f(a,b,c)
tripleOp: [A, B](a: A, b: A, c: A, f: (A, A, A) => B)B
scala> tripleOp[Int,Int](23, 92, 14, _ * _ + _)
res15: Int = 2130
scala> tripleOp[Int,Double](23, 92, 14, 1.0 * _ / _ / _)
res16: Double = 0.017857142857142856
scala> tripleOp[Int,Boolean](93, 92, 14, _ > _ + _)
res17: Boolean = false
***USEAGE***
---------------------
Placeholder syntax is especially helpful when working with data structures and collec- tions. Many of the core sorting, filtering, and other data structure methods tend to use first-class functions, and placeholder syntax reduces the amount of extra code required to call these methods.
---------------------------------------------------------------------------------------------------------------------
Partially Applied Functions and Currying
----------------------------------------------------------------------------------------------------------------------
What if you wanted to reuse a function invocation and retain some of the parameters to avoid typing them in again?
A cleaner way to partially apply functions is to use functions with multiple parameter lists. Instead of breaking up a parameter list into applied and unapplied parameters, apply the parameters for one list while leaving another list unapplied. This is a technique known as currying the function
To demonstrate this answer, Iíll use the example of a two-parameter function that checks if a given number is a factor of the other number:
scala> def factorOf(x: Int, y: Int) = y % x == 0
factorOf: (x: Int, y: Int)
Boolean If you want a shortcut to the function without retaining any parameters, you can use the wildcard operator (_) assignment we covered in this chapterís introduction:
scala> val f = factorOf _
f: (Int, Int) => Boolean = <function2>
scala> val x = f(7, 20)
x: Boolean = false
If you want to retain some of the parameters, you can partially apply the function by using the wildcard operator to take the place of one of the parameters. The wildcard operator here requires an explicit type, because it is used to generate a function value with a declared input type:
scala> val multipleOf3 = factorOf(3, _: Int)
multipleOf3: Int => Boolean = <function1>
scala> val y = multipleOf3(78)
y: Boolean = true
The new function value, multipleOf3, is a partially applied function, because it contains some but not all of the parameters for the factorOf() function
-------------------------------------------------------------------------------------
Invoking Higher-Order Functions with Function Literal Blocks
-------------------------------------------------------------------------------------
a higher-order function can wrap a given expression block in a single database session or transaction
Iíll use the ìsafeStringOpsî function to demonstrate when this syntax may be desirable and how to use it. To start, here is the ìsafeStringOpsî function used with a regular function literal, before converting it to the desired syntax:
scala> def safeStringOp(s: String, f: String => String) = { | if (s != null) f(s) else s | } safeStringOp: (s: String, f: String => String)String
scala> val uuid = java.util.UUID.randomUUID.toString
uuid: String = bfe1ddda-92f6-4c7a-8bfc-f946bdac7bc9
scala> val timedUUID = safeStringOp(uuid, { s => | val now = System.currentTimeMillis | val timed = s.take(24) + now | timed.toUpperCase | })
timedUUID: String = BFE1DDDA-92F6-4C7A-8BFC-1394546043987
A UUID utility in Javaís java.util package, accessible (as are all JDK classes) from Scala. System.currentTimeMillis provides the epoch time (elapsed time since January 1, 1970 GMT) in milliseconds, useful for creating timestamps. The take(x) method returns the first x items from the String, in this case the first four sections of the UUID
*******************************************************************************
---------------------------------------------------------------------------------------------------------------------
Common Collections
----------------------------------------------------------------------------------------------------------------------
A collections framework provides data structures for collecting one or more values of a given type such as arrays, lists, maps, sets, and trees. Most of the popular programming languages have their own collections framework (or, at the least, lists and maps) because these data structures are the building blocks of modern software projects.
---> Because Scala is a JVM language, you can access and use the entire Java collections library from your Scala code.
--> which make switching between immutable data (for stability) and mutable data (when necessary) convenient.
---.The root of all iterable collections, Iterable, provides a common set of methods for (you guessed it) iterating through and manipulating collection data
-------------------------------------------------
Lists, Sets, and Maps
--------------------------------------------------
Letís start with the List type, an immutable singly linked list. You can create a list by invoking it as a function, passing in its contents in the form of comma-separated pa- rameters:
scala> val numbers = List(32, 95, 24, 21, 17)
numbers: List[Int] = List(32, 95, 24, 21, 17)
scala> val colors = List("red", "green", "blue")
colors: List[String] = List(red, green, blue)
scala> println(s"I have ${colors.size} colors: $colors")
I have 3 colors: List(red, green, blue)
--->The size method, available on all collections and String instances, returns the number of items in the collection. Defined without parentheses the size method is simply invoked by name
Use the Lisp-style head() and tail() methods to access the first and remaining ele- ments of a list, respectively. To access a single element directly, invoke the list as a func- tion and pass it the zero-based index of that element:
scala> val colors = List("red", "green", "blue")
colors: List[String] = List(red, green, blue)
scala> colors.head res0: String = red
scala> colors.tail
res1: List[String] = List(green, blue)
scala> colors(1)
res2: String = green
scala> colors(2)
res3: String = blue
------------------------------------------
USING RANGE COLLECTION
------------------------------------------
Letís try out using for-loops to iterate over the ìnumbersî and ìcolorsî lists:
scala> val numbers = List(32, 95, 24, 21, 17)
numbers: List[Int] = List(32, 95, 24, 21, 17)
scala> var total = 0; for (i <- numbers) { total += i }
total: Int = 189
scala> val colors = List("red", "green", "blue")
colors: List[String] = List(red, green, blue)
scala> for (c <- colors) { println(c) }
red
green
blue
Hereís an example of the foreach(), map(), and reduce() higher-order functions avail- able in List and other collections. Respectively, these functions iterate over the list, convert the list, and reduce the list down to a single item. For each method, a function literal is passed in, including the input parameter in parentheses and its function body:
scala> val colors = List("red", "green", "blue")
colors: List[String] = List(red, green, blue)
scala> colors.foreach( (c: String) => println(c) )
red
green
blue
scala> val sizes = colors.map( (c: String) => c.size )
sizes: List[Int] = List(3, 5, 4)
scala> val numbers = List(32, 95, 24, 21, 17)
numbers: List[Int] = List(32, 95, 24, 21, 17)
scala> val total = numbers.reduce( (a: Int, b: Int) => a + b )
total: Int = 189
foreach() takes a function (a procedure, to be accurate) and invokes it with every item in the list. map() takes a function that converts a single list element to another value and/or type. reduce() takes a function that combines two list elements into a single element
--------------------------------------
MAP
--------------------------------------
A Map is an immutable key-value store, also known as a hashmap, dictionary, or asso- ciative array in other languages. Values stored in a Map with a given unique key may beretrieved using that key. The key and the value are type-parameterized; you can just as easily create a mapping from strings to integers as a mapping from integers to strings.
When creating a Map, specify the key-value pairs as tuples (see ìTuplesî on page 25). You can use the relation operator (->) to specify the key and value tuple.
EXAMPLE
----------------
scala> val colorMap = Map("red" -> 0xFF0000, "green" -> 0xFF00, "blue" -> 0xFF) colorMap: scala.collection.immutable.Map[String,Int] = Map(red -> 16711680, green -> 65280, blue -> 255)
scala> val redRGB = colorMap("red")
redRGB: Int = 16711680
scala> val cyanRGB = colorMap("green") | colorMap("blue")
cyanRGB: Int = 65535
scala> val hasWhite = colorMap.contains("white") hasWhite: Boolean = false
scala> for (pairs <- colorMap) { println(pairs) }
(red,16711680)
(green,65280)
For example, you can create a collection of collections:
scala> val oddsAndEvents = List(List(1, 3, 5), List(2, 4, 6))
oddsAndEvents: List[List[Int]] = List(List(1, 3, 5), List(2, 4, 6))
Or you can have a collection of 2-sized tuples, and create a List that looks similar to a Map:
scala> val keyValues = List(('A', 65), ('B',66), ('C',67))
keyValues: List[(Char, Int)] = List((A,65), (B,66), (C,67)) (blue,255)
You can access a single element from a list by invoking it as a function with a (zero- based) index number. Here is an example of accessing the first and fourth elements of a List by their index:
scala> val primes = List(2, 3, 5, 7, 11, 13)
primes: List[Int] = List(2, 3, 5, 7, 11, 13)
scala> val first = primes(0)
first: Int = 2
scala> val fourth = primes(3)
fourth: Int = 7
You can decompose a list into its head, the first item in the list, and its tail, the remaining items in the list:
scala> val first = primes.head
first: Int = 2
scala> val remaining = primes.tail
remaining: List[Int] = List(3, 5, 7, 11, 13)
Creating a new, empty list will actually return Nil instead of a fresh instance. Because Nil is immutable, there is essentially no difference between it and a fresh, empty list instance. Likewise, creating a new list that has a single entry just creates a single list element that points to Nil as its tail.
Letís demonstrate these points with some examples:
scala> val l: List[Int] = List()
l: List[Int] = List()
scala> l == Nil
res0: Boolean = true
scala> val m: List[String] = List("a")
m: List[String] = List(a)
scala> m.head res1: String = a
scala> m.tail == Nil res2: Boolean = true
I have used lists of two explicit types, Int and String, to demonstrate that regardless of the type of their data, a List will always end with Nil.
--------------------------------------
The Cons Operator (::)
---------------------------------------
There is an alternate way to construct lists that takes advantage of this relationship with Nil. As another nod to Lisp, Scala supports use of the cons (short for construct) operator to build lists. Using Nil as a foundation and the right-associative cons operator :: for binding elements, you can build a list without using the traditional List(Ö) format.
Here is an example of building a list with the cons operator:
scala> val numbers = 1 :: 2 :: 3 :: Nil numbers: List[Int] = List(1, 2, 3)
You could use traditional dot notation with the cons operator, but it would look a bit odd. OK, letís try it out anyway:
scala> val first = Nil.::(1) first: List[Int] = List(1)
scala> first.tail == Nil res3: Boolean = true
This time we will use it to prepend (insert at the front) a value to an existing list, thus creating a brand new list. We have actually tried this before, because it is the same as the operation of prepending a value to Nil, the empty list:
scala> val second = 2 :: first second: List[Int] = List(2, 1)
scala> second.tail == first
res4: Boolean = true
Although the ìsecondî list includes the ìfirstî list, both are valid lists that can be used independently. This example of building one list by adding a value to another demon- strates the recursive and reusable nature of Scalaís immutable lists, and provides a good summary of this section
------------------------------------------------
List Arithmetic
-------------------------------------------------
In this section we will focus on basic arithmetic oper- ations on lists. By ìarithmetic,î a term I use loosely, I mean operations that add, remove, split, combine, and otherwise modify the organization of lists without changing the list elements (i.e., their contents) themselves. And of course by ìmodifyî I mean ìreturn a new list with the requested changesî because List is an immutable collection
Table 6-1. Arithmetic operations on lists
---------------------------------------------------
Name Example Description
---------------------------------------------------------------------------------------------------------------
:: 1 :: 2 :: Nil Appends individual elements to this list. A right- associative operator.
::: List(1, 2) ::: List(2, 3) Prepends another list to this one. A right- associative operator.
++ List(1, 2) ++ Set(3, 4, 3) Appends another collection to this list.
== List(1, 2) == List(1, 2) Returns true if the collection types and contents are equal.
distinct List(3, 5, 4, 3, 4).distinct Returns a version of the list without duplicate elements.
drop List('a', 'b', 'c', 'd') drop 2 Subtracts the first n elements from the list.
filter List(23, 8, 14, 21) filter (_ > 18) Returns elements from the list that pass a true/ false function.
flatten List(List(1, 2), List(3, 4)).flatten Converts a list of lists into a single list of elements.
partition List(1, 2, 3, 4, 5) partition (_ < 3) Groups elements into a tuple of two lists based on the result of a true/false function
reverse List(1, 2, 3).reverse Reverses the list.
slice List(2, 3, 5, 7) slice (1, 3) Returns a segment of the list from the first index up to but not including the second index
sortBy List("apple", "to") sortBy (_.size) Orders the list by the value returned from he given function.
sorted List("apple", "to").sorted Orders a list of core Scala types by their natural value
splitAt List(2, 3, 5, 7) splitAt 2 Groups elements into a tuple of two lists based on if they fall before or after the given index
take List(2, 3, 5, 7, 11, 13) take 3 Extracts the first n elements from the list.
zip List(1, 2) zip List("a", "b") Combines two lists into a list of tuples of elements at each index.
-----------------------------------------------------------------------------------------------------------------------
NOTE: some examples used operator notation (e.g., list drop 2) whereas others used dot notation (e.g., list.flatten). Selecting the right notation is a personal choice, except where dot notation is required due to lack of an operation parameter (as in list.flatten).
Collection methods that are also higher-order functions, such as filter, map, and partition, are excellent candidates for using placeholder syntax. The function param- eter they take as input acts on a single element in their list. Thus the underscore (_) in an anonymous function sent to one of these methods represents each item in the list
An important point to make about these arithmetic methods is that ::, drop, and take act on the front of the list and thus do not have performance penalties. Recall that List is a linked list, so adding items to or removing items from its front does not require a full traversal. A list traversal is a trivial operation for short lists, but when you start getting into lists of thousands or millions of items, an operation that requires a list traversal can be a big deal
The corollary operations to ::, drop, and take are +: (a left-associative operator), dropRight, and takeRight. The arguments to these operators are the same as to their corollary operations. Here are examples of these list-appending operations:
scala> val appended = List(1, 2, 3, 4) :+ 5
appended: List[Int] = List(1, 2, 3, 4, 5)
scala> val suffix = appended takeRight 3
suffix: List[Int] = List(3, 4, 5)
scala> val middle = suffix dropRight 2
middle: List[Int] = List(3)
-----------------------------------
MAP
------------------------------------
Map methods are those that take a function and apply it to every member of a list, collecting the results into a new list. In set theory, and the field of mathematics in general, to map is to create an assocation between each element in one set to each element in another set. In a sense, both definitions describe what the map methods in List are doing: mapping each item from one list to another list, so that the other list has the same size as the first but with different data or element types.
List mapping operations
-------------------------------
Name Example Description
-------------------------------------------------------------------------------------------------------------------
collect List(0, 1, 0) collect {case 1 => "ok"} Transforms each element using a partial function, retaining applicable elements
flatMap List("milk,tea") flatMap (_.split(',')) Transforms each element using the given function and ìflattensî the list of results into this list.
map List("milk","tea") map (_.toUpperCase) Transforms each element using the given function
---------------------------------------------------------------------------------------------------------------------
Letís see how these list-mapping operators work in the REPL:
scala> List(0, 1, 0) collect {case 1 => "ok"}
res0: List[String] = List(ok)
scala> List("milk,tea") flatMap (_.split(','))
res1: List[String] = List(milk, tea)
scala> List("milk","tea") map (_.toUpperCase)
res2: List[String] = List(MILK, TEA)
The flatMap example uses the String.split() method to convert pipe-delimited text into a list of strings. Specifically this is the java.lang.String.split() method, and it returns a Java array, not a list. Fortunately, Scala converts Java arrays to its own type, Array, which extends Iterable. Because List is also a subtype of Iterable, and the flatMap method is defined at the Iterable level, a list of string arrays can be safely flattened into a list of strings.
------------------------------------------------------------------
reducing operations
------------------------------------------------------------------
Scalaís collections support mathematical reduction operations (e.g., finding the sum of a list) and Boolean reduction operations (e.g., determining if a list contains a given element). They also support generic higher-order operations known as folds that you can use to create any other type of list reduction algorithm.
Math reduction operations
----------------------------------
Name Example Description
-------------------------------------------------------------------------------------------------------------
max List(41, 59, 26).max Finds the maximum value in the list.
min List(10.9, 32.5, 4.23, 5.67).min Finds the minimum value in the list.
product List(5, 6, 7).product Multiplies the numbers in the list.
sum List(11.3, 23.5, 7.2).sum Sums up the numbers in the list.
Boolean reduction operations
---------------------------------------
contains List(34, 29, 18) contains 29 Checks if the list contains this element
endsWith List(0, 4, 3) endsWith List(4, 3) Checks if the list ends with a given list.
exists List(24, 17, 32) exists (_ < 18) Checks if a predicate holds true for at least one element in the list.
forall List(24, 17, 32) forall (_ < 18) Checks if a predicate holds true for every element in the list.
startsWith List(0, 4, 3) startsWith List(0) Tests whether the list starts with a given list.
-----------------------------------------------------
converting collections
-----------------------------------------------------
Because a List.toList() operation would be silly (but possible), the examples demonstrate converting from one type to a completely different type.
-------------------------------------------------
Operations to convert collections
----------------------------------------------
Name Example Description
--------------------------------------------------------------------------------------------------------------------
mkString List(24, 99, 104).mkString(", ") Renders a collection to a Set using the given delimiters
toBuffer List('f', 't').toBuffer Converts an immutable collection to a mutable one.
toList Map("a" -> 1, "b" -> 2).toList Converts a collection to a List.
toMap Set(1 -> true, 3 -> true).toMap Converts a collection of 2-arity (length) tuples to a Map.
toSet List(2, 5, 5, 3, 2).toSet Converts a collection to a Set.
toString List(2, 5, 5, 3, 2).toString Renders a collection to a String, including the collectionís type.
-----------------------------------------------------------
pattern matching with collection
-------------------------------------------------------------
scala> val statuses = List(500, 404) s
tatuses: List[Int] = List(500, 404)
scala> val msg = statuses.head match {
| case x if x < 500 => "okay"
| case _ => "whoah, an error"
|
}
msg: String = whoah, an error With a pattern guard (see ìMatching with Pattern Guardsî on page 36), you could also match a single value inside a collection:
scala> val msg = statuses match { | case x if x contains(500) => "has error" | case _ => "okay" | }
msg: String = has error
scala> val msg = statuses match { case List(404, 500) => "not found & error" | case List(500, 404) => "error & not found" | case List(200, 200) => "okay" | case _ => "not sure what happened" | }
msg: String = error & not found
*******************************************************************************
MORE COLLECTIONS
**************************************
Weíll start with mutable collections, which probably can be considered ubiquitous be- cause more languages support them than they do immutable collections. Then weíll move on to arrays, streams, and other collections
---------------------------------------
Mutable Collections
---------------------------------------
The List, Set, and Map immutable collections we are familiar with cannot be changed after they have been created (see the definition of ìimmutableî). They can, however, be transformed into new collections. For example, we can create an immutable map, and then transform it by removing one mapping and adding another:
scala> val m = Map("AAPL" -> 597, "MSFT" -> 40)
m: scala.collection.immutable.Map[String,Int] = Map(AAPL -> 597, MSFT -> 40)
scala> val n = m - "AAPL" + ("GOOG" -> 521)
n: scala.collection.immutable.Map[String,Int] = Map(MSFT -> 40, GOOG -> 521)
scala> println(m)
Map(AAPL -> 597, MSFT -> 40) A new map with ìAAPLî and ìMSFTî keys. Removing ìAPPLî and adding ìGOOGî gives us a different collectionÖ Ö
while the original collection in ìmî remains the same.
107
What you end up with is a completely new collection stored in ìnî. The original collec- tion, stored in the ìmî value, remains untouched. And this is exactly the point of im- mutable data, namely that data and data structures should not be mutable or change their state in order to improve code stability and prevent bugs. As an example, data structures that are rigid and never change state are safer to use with concurrent code than data structures that may change at any point and are prone to corruption (e.g., reading a data structure while it is undergoing a state change).
---------------------------------------------
Creating New Mutable Collections
---------------------------------------------
The most straightforward way to modify collections is with a mutable collection type. See Table 7-1 for the mutable counterparts(meening PRATIRUPALU) to the standard immutable List, Map, and Set types.
---------------------------------------------------------------------------------------------------
Table 7-1. Mutable collection types
---------------------------------------------------------------------------------------------------
Immutable type Mutable counterpart
-------------------- --------------------------------------------
collection.immutable.List collection.mutable.Buffer
collection.immutable.Set collection.mutable.Set
collection.immutable.Map collection.mutable.Map
----------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------
Whereas the collection.immutable package is automatically added to the current namespace in Scala, the collection.mutable package is not. When creating mutable collections, make sure to include the full package name for the type.
The collection.mutable.Buffer type is a general-purpose mutable sequence, and supports adding elements to its beginning, middle, and end.
Example
----------
scala> val nums = collection.mutable.Buffer(1)
nums: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1)
scala> for (i <- 2 to 10) nums += i
scala> println(nums)
Buffer(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
You can convert your mutable buffer back to an immutable list at any time with the toList method:
scala> println(nums)
Buffer(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> val l = nums.toList l:
List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
Likewise for sets and maps, use the toSet and toMap methods to convert these mutable collections to their immutable counterparts.
Here is an example of converting an immutable map to a mutable one and then changing it back:
scala> val m = Map("AAPL" -> 597, "MSFT" -> 40)
m: scala.collection.immutable.Map[String,Int] = Map(AAPL -> 597, MSFT -> 40)
scala> val b = m.toBuffer
b: scala.collection.mutable.Buffer[(String, Int)] = ArrayBuffer((AAPL,597), (MSFT,40))
scala> b trimStart 1
scala> b += ("GOOG" -> 521)
ArrayBuffer((MSFT,40), (GOOG,521))
scala> val n = b.toMap
n: scala.collection.immutable.Map[String,Int] = Map(MSFT -> 40, GOOG -> 521)
The map, containing key-value pairs, is now a sequence of tuples. trimStart removes one or more items from the start of the buffer. After removing the ìAAPLî entry weíll add a ìGOOGî entry. This buffer of tuples is now an immutable map again
---------------------------------
Using Collection Builders
----------------------------------
A Builder is a simplified form of a Buffer, restricted to generating its assigned collec- tion type and supporting only append operations.
To create a builder for a specific collection type, invoke the typeís newBuilder method and include the type of the collectionís element. Invoke the builderís result method to convert it back into the final Set. Here is a
scala> val b = Set.newBuilder[Char]
b: scala.collection.mutable.Builder[Char,scala.collection.immutable. Set[Char]] = scala.collection.mutable.SetBuilder@726dcf2c
scala> b += 'h'
res3: b.type = scala.collection.mutable.SetBuilder@d13d812
scala> b ++= List('e', 'l', 'l', 'o')
res4: b.type = scala.collection.mutable.SetBuilder@d13d812
scala> val helloSet = b.result
helloSet: scala.collection.immutable.Set[Char] = Set(h, e, l, o) Adding a single item, one of two append operations. Adding multiple items, the second of two append operations. Unlike with buffers, a builder knows its immutable counterpart.
So, why use Builder versus Buffer or one of the mutable collection types? The Build er type is a good choice if you are only building a mutable collection iteratively in order to convert it to an immutable collection. If you need Iterable operations while building your mutable collection, or donít plan on converting to an immutable collection, using one of the Buffer or other mutable collection types is a better match.
------------------------------
ARRAY
------------------------------
An Array is a fixed-size, mutable, indexed collection. Itís not officially a collection, be- cause it isnít in the ìscala.collectionsî package and doesnít extend from the root Itera ble type (although it has all of the Iterable operations like map and filter). The Array type is actually just a wrapper around Javaís array type with an advanced feature called an implicit class allowing it to be used like a sequence. Scala provides the Array type for compatibility with JVM libraries and Java code and as a backing store for indexed col- lections, which really require an array to be useful. Here are some examples of working with arrays, demonstrating their cell mutability and support for Iterable operations:
scala> val colors = Array("red", "green", "blue") colors: Array[String] = Array(red, green, blue)
scala> colors(0) = "purple"
scala> colors res0: Array[String] = Array(purple, green, blue)
scala> println("very purple: " + colors) very purple: [Ljava.lang.String;@70cf32e3
scala> val files = new java.io.File(".").listFiles files: Array[java.io.File] = Array(./Build.scala, ./Dependencies.scala, ./build.properties, ./JunitXmlSupport.scala, ./Repositories.scala, ./plugins.sbt, ./project, ./SBTInitialization.scala, ./target)
scala> val scala = files map (_.getName) filter(_ endsWith "scala") scala: Array[String] = Array(Build.scala, Dependencies.scala, JunitXmlSupport.scala, Repositories.scala, SBTInitialization.scala) Use a zero-based index to replace any item in an Array. The Scala REPL knows how to print an Array Ö Ö but not println(), which can only call a typeís toString() method. The listFiles method in java.io.File, a JDK class, returns an array that we can easily map and filter
----------------------------------
Seq and Sequences
----------------------------------
Seq is the root type of all sequences, including linked lists like List and indexed (direct- access) lists like Vector. The Array type, if it were a collection, could be considered an indexed sequence because its elements are directly accessible without traversal. As a root type, Seq itself cannot be instantiated, but you can invoke it as a shortcut for creating a List:
scala> val inks = Seq('C','M','Y','K') inks: Seq[Char] = List(C, M, Y, K)
----> you can access items in a Vector directly by their index
---->The Seq shortcut for List linked lists and the IndexedSeq shortcut for Vector indexed lists are only marginally useful, because the savings for writing them is one character and negative four characters, respectively
Hereís an example of using String as a subtype of Iterable and as a java.lang.String wrapper, using methods from both types:
scala> val hi = "Hello, " ++ "worldly" take 12 replaceAll ("w","W") hi: String = Hello, World
---------------------
Streams
---------------------
The Stream type is a lazy collection, generated from one or more starting elements and a recursive function. Elements are added to the collection only when they are accessed for the first time, in constrast to other immutable collections that receive 100% of their contents at instantiation time. The elements that a stream generates are cached for later retrieval, ensuring that each element is only generated once. Streams can be unbounded, theoretically infinite collections where elements are only realized upon access. They can also be terminated with Stream.Empty, a counterpart to List.Nil
Here is an example function that builds and recursively generates a new stream. By incrementing the starting integer value, it will end up creating a collection of consecu- tively increasing integers:
scala> def inc(i: Int): Stream[Int] = Stream.cons(i, inc(i+1)) inc: (i: Int)Stream[Int]
scala> val s = inc(1) s: Stream[Int] = Stream(1, ?)
------------------------------------
Try Collections
----------------------------------
The util.Try collection turns error handling into collection management. It provides a mechanism to catch errors that occur in a given function parameter, returning either the error or the result of the function if successful
To throw an exception, use the throw keyword with a new Exception instance. The text message provided to Exception is optional:
scala> throw new Exception("No DB connection, exiting...") java.lang.Exception: No DB connection, exiting... ... 32 elided
Scala does support try {} .. catch {} blocks, where the catch block contains a series of case statements that attempt to match the thrown error. I recommend using util.Try() exclusively because it offers a safer, more expressive, and fully monadic approach to handling er- rors.
*******************************************************************************
*****************************************************************************
PART II
Object-Oriented Scala
*****************************************************************************
--------------------------------------------------------------------------------------------------------------------
Classes
---------------------------------------------------------------------------------------------------------------------
In Part 1 of this book you learned about Scalaís core types and how to group them into collections. Now it is time to build your own types with classes. Classes are the core building block of object-oriented languages, a combination of data structures with functions (ìmethodsî). A class defined with values and variables can be instantiated as many times as needed, each one initialized with its own input data. With inheritance classes can extend other classes, creating a hierarchy of subclasses and su- perclasses. Polymorphism makes it possible for these subclasses to stand in for their parent classes, while encapsulation provides privacy controls to manage the outward appearance of a class
Weíll start by defining the simplest possible class and instantiating it:
scala> class User
defined class User
scala> val u = new User
u: User = User@7a8c8dcf
scala> val isAnyRef = u.isInstanceOf[AnyRef]
isAnyRef: Boolean = true
Letís redesign our User class and make it more useful. Weíll add a value and some methods that operate on the value. Weíll also override the default toString method and provide a more informative version:
scala> class User {
| val name: String = "Yubaba"
| def greet: String = s"Hello from $name"
| override def toString = s"User($name)"
| }
defined class User
scala> val u = new User
u: User = User(Yubaba)
scala> println( u.greet )
Hello from Yubaba
In Scala, class parameters (if any) are specified after the name of the class, much like a functionís parameters follow the name of the function in its definition:
scala> class User(n: String) {
| val name: String = n
| def greet: String = s"Hello from $name"
| override def toString = s"User($name)"
| }
defined class User
scala> val u = new User("Zeniba")
u: User = User(Zeniba)
scala> println(u.greet)
Hello from Zeniba
Class parameters are available for initializ- ing fields (values and variables in a class) or for passing to functions, but once the class has been created the parameters arenít available. Instead of using a class parameter for intitialization purposes, we can instead declare one of the fields as a class parameter. By adding the keywords val or var before a class parameter, the class parameter then becomes a field in the class. Letís try this by moving the ìnameî field to the class parameters:
scala> class User(val name: String) {
| def greet: String = s"Hello from $name"
| override def toString = s"User($name)"
| }
defined class User
Now that we have a short and useful class, letís put it to use. Hereís an example of using this new class with lists:
scala> val users = List(new User("Shoto"), new User("Art3mis"), new User("Aesch"))
users: List[User] = List(User(Shoto), User(Art3mis), User(Aesch))
scala> val sizes = users map (_.name.size)
sizes: List[Int] = List(8, 7, 5)
scala> val sorted = users sortBy (_.name)
sorted: List[User] = List(User(Aesch), User(Art3mis), User(Shoto))
scala> val third = users find (_.name contains "3")
third: Option[User] = Some(User(Art3mis))
scala> val greet = third map (_.greet) getOrElse "hi"
greet: String = Hello from Art3mis //Can you see why String is the correct result of a combination of map and getOrElse against our Option[String]?
Letís round up our introduction to classes by working through examples of inheritance and polymorphism. A class can extend up to one other class in Scala with the extends keyword, and override (i.e., supplant) the behavior of an inherited method with the override keyword. The fields and methods in a class can be accessed (if strictly necessary) with the this keyword, while the fields and methods in the parent class(es) can be accessed with the super keyword. The super keyword is especially useful when a method needs to still access the similar method in its parent class that it is overriding.
Iíll demonstrate this with a parent class, ìA,î and subclass, ìC,î and a class situtated between these two, ìBî:
scala> class A {
| def hi = "Hello from A"
| override def toString = getClass.getName
| }
defined class A
scala> class B extends A
defined class B
scala> class C extends B { override def hi = "hi C -> " + super.hi }
defined class C
scala> val hiA = new A().hi
hiA: String = Hello from A
scala> val hiB = new B().hi
hiB: String = Hello from A
scala> val hiC = new C().hi
hiC: String = hi C -> Hello from A
Letís put this knowledge to use to create a list of instances of A, B, and C. What should we declare as the type of such a list, A, B, or C? To ensure that the list can include instances of each of these classes, we should define the list as List[A], which is compatible with all of these classes:
scala> val misc = List(new C, new A, new B)
misc: List[A] = List(C, A, B)
scala> val messages = misc.map(_.hi).distinct.sorted
messages: List[String] = List(Hello from A, hi C -> Hello from A)
Whoops! Despite my warning to define the list as List[A], I forgot to add an explicit type. Fortunately, the Scala compiler was able to infer the common type of the three instances as being A, the parent class, and set the listís type parameter correctly. Come to think of it, thatís a great use for the compilerís type inference featureófinding the lowest (most specific) common denominator of one or more instances.
----------------------------------------------------
Defining Classes
-----------------------------------------------------
Syntax: Defining a Class with Input Parameters
class <identifier> ([val|var] <identifier>: <type>[, ... ])
[extends <identifier>(<input parameters>)]
[{ fields and methods }]
A class with input parameters gives a programmer a reason to create multiple instances, because each instance can have its own unique contents.
Letís try creating a class with both value and variable fields as parameters:
scala> class Car(val make: String, var reserved: Boolean) {
| def reserve(r: Boolean): Unit = { reserved = r }
| }
defined class Car
scala> val t = new Car("Toyota", false)
t: Car = Car@4eb48298
scala> t.reserve(true)
scala> println(s"My ${t.make} is now reserved? ${t.reserved}")
My Toyota is now reserved? true
Syntax: Defining a Class with Input Parameters and Default Values
-----------------------------------------------------------------------------------
class <identifier> ([val|var] <identifier>: <type> = <expression>[, ... ]) [extends <identifier> (<input parameters>)]
[{
fields and methods
}]
EXAMPLE
---------------
scala> class Car(val make: String, var reserved: Boolean = true,val year: Int = 2015) {
| override def toString = s"$year $make, reserved = $reserved"
| }
defined class Car
scala> val a = new Car("Acura") (1)
a: Car = 2015 Acura, reserved = true
scala> val l = new Car("Lexus", year = 2010) (2)
l: Car = 2010 Lexus, reserved = true
scala> val p = new Car(reserved = false, make = "Porsche") (3)
p: Car = 2015 Porsche, reserved = false
1)Only the first parameter is required, and we can invoke it by position.
2)Here, the first and third parameters are specified. Because the third parameter is out of order weíll have to specify it by name.
3)This time none of the parameters are invoked by position, with the final one skipped.
List[String] may contain String instances and support operations that take and re- turn a String.
Syntax: Defining a Class with Type Parameters
-----------------------------------------------------------
class <identifier> [type-parameters] ([val|var] <identifier>: <type> = <expression>[, ... ]) [extends <identifier>[type-parameters](<input parameters>)]
[{ fields and methods }]
Letís create our own collection and use a type parameter to ensure type safety. The new collection will extend Traversable[A], the parent class of Iterable
scala> class Singular[A](element: A) extends Traversable[A] { (1)
| def foreach[B](f: A => B) = f(element) (2)
| }
defined class Singular
scala> val p = new Singular("Planes") (3)
p: Singular[String] = (Planes)
scala> p foreach println (4)
Planes
scala> val name: String = p.head (5)
name: String = Planes
1)A good example of passing a type parameter to the parent class in the class definition.
2)By defining a foreach() operation, Traversable will ensure our class is a real collection and can use this to enable every other collection operation.
3)Here is a validation of our type-parameterized class, with the REPL printing the class name and the name of the parameterized type used to instantiate it (a String).
4)An example usage of the foreach method we defined, reduced to its most unadorned invocation.
5)Another example usage of foreach, indirectly this time, as we access Traversa ble.head, which invokes foreach for us. By extending Traversable we can access head and a range of other standard collection operations.
---------------------------------------------------
Abstract Classes
----------------------------------------------------
An abstract class is a class designed to be extended by other classes but not instantiated itself. Abstract classes are designated so by the abstract keyword, placed before the class keyword when defining the class.
An abstract class can be used to define the core fields and methods required by its subclasses without providing an actual implementation. Thanks to polymorphism, a value with the type of the abstract class can actually point to an instance of one of its nonabstract subclasses, and invoke methods that actually end up being invoked on the subclass.
Abstract classes provide unimplemented fields and methods by declaring them without defining them. A declared field or method will include the name and parameters but not a starting value or implementation, respectively. A class that extends an abstract class with declared fields and methods, and is not itself marked as abstract, must provide their implementations. An abstract class can also have its own implemented fields and methods, which would not require implementations in subclasses. Letís create our own abstract class with a declared value and method, and experiment with implementations:
scala> abstract class Car {
val year: Int
val automatic: Boolean = true
def color: String
}
defined class Car
scala> new Car()
<console>:9: error: class Car is abstract; cannot be instantiated
new Car()
scala> class RedMini(val year: Int) extends Car {
def color = "Red"
}
defined class RedMini
scala> val m: Car = new RedMini(2005)
m: Car = RedMini@5f5a33ed
An experiment with instantiating our abstract class Car by itself didnít work, for the obvious reason that it is abstract and uninstantiatable. Still, itís nice to see that Scalaís compiler pointed this out with a helpful message.
Creating a subclass that extends Car but adds a value parameter and a concrete imple- mentation of the color method solved the problem. The RedMini class is a successful implementation of its parent abstract class and can be instantiated with only its year as a parameter.
--------------------------------------------------
Anonymous Classes
--------------------------------------------------
With anonymous classes, class definitions donít need to be stable or reusable. When a subclass will only be needed once, the anonymous class syntax can help to simplify your code base.
Hereís a more illustrative example of when you may find it useful to create an anonymous class. We have a class, Listening, that can register a Listener and trigger it later as necessary. Instead of instantiating the anonymous class on one line and passing it to the registration function on another, we can combine these into a single step of defining the anonymous class as part of the method invocation. This should look familiar to those with JavaScript experience, especially if you have worked on jQuery-style event handlers:
scala> abstract class Listener { def trigger }
defined class Listener
scala> class Listening { | var listener: Listener = null | def register(l: Listener) {
listener = l }
| def sendNotification() { listener.trigger }
| }
defined class Listening
scala> val notification = new Listening()
notification: Listening = Listening@66596c4c
scala> notification.register(new Listener {
| def trigger { println(s"Trigger at ${new java.util.Date}") }
| })
scala> notification.sendNotification
Trigger at Fri Jan 24 13:15:32 PDT 2014
Overloaded Methods
------------------------------
An overloaded method is a strategy for providing choices to callers. A class may have two or more methods with the same name and return value but with different arrange- ments of input parameters. By overloading a method name with multiple implemen- tations, multiple choices for invoking a method with a specific name are made available.
Here is an example of overloaded methods, where the methods share the same name but take different parameters. In the example the second overloaded method calls the first after modifying its input parameters appropriately:
scala> class Printer(msg: String) {
| def print(s: String): Unit = println(s"$msg: $s")
| def print(l: Seq[String]): Unit = print(l.mkString(", "))
| }
defined class Printer
scala> new Printer("Today's Report").print("Foggy" :: "Rainy" :: "Hot" :: Nil)
Today's Report: Foggy, Rainy, Hot
It is not possible to have two methods with the same name and input parameters, but different return values. Doing so will cause a Scala compiler error, because there is no way for only one of the methods to be specifically selected during compilation
Apply Methods
----------------------
Methods named ìapply,î sometimes referred to as a default method or an injector meth- od, can be invoked without the method name. The apply method is essentially a shortcut, providing functionality that can be triggered using parentheses but without a method name.
Letís try it with a class that multiplies numbers by a predefined amount:
scala> class Multiplier(factor: Int) {
def apply(input: Int) = input * factor
}
defined class Multiplier
scala> val tripleMe = new Multiplier(3)
tripleMe: Multiplier = Multiplier@339cde4b
scala> val tripled = tripleMe.apply(10)
tripled: Int = 30
scala> val tripled2 = tripleMe(10)
tripled2: Int = 30
Our ìtripleMeî instance can be used with or without the ìapplyî name to triple a given number.
You might remember this syntax from retrieving an element from a list by its index, which happens to use the List.apply method:
scala> val l = List('a', 'b', 'c')
l: List[Char] = List(a, b, c)
scala> val character = l(1)
character: Char = b
Here, the List.apply(index) method provides access to an element by index, an op- eration so common that it makes a good candidate for being the default method of lists. One potential disadvantage to making a method be the default one is if it makes the code look odd. Accessing the default method should be natural, like the accessor method for lists. Try to only use the apply method where it makes sense, like an accessor method for a list.
Lazy Values
-------------------
Lazy values are a great way to ensure that time- or performance-sensitive operations can be executed only once in a classís lifetime. They are popularly used to store infor- mation such as file-based properties, open database connections, and other immutable data that should only be initialized if it is really necessary. By initializing this data in a lazy valís expression, you can ensure that it will only operate if the lazy val is accessed at least once in the class instanceís lifetime
This concept is perhaps better explained with an example. Hereís one that shows when a regular value is calculated versus a lazy value:
scala> class RandomPoint { | val x = { println("creating x"); util.Random.nextInt }
lazy val y = { println("now y"); util.Random.nextInt }
| }
defined class RandomPoint
scala> val p = new RandomPoint()
creating x
p: RandomPoint = RandomPoint@6c225adb //here x created but not the y
scala> println(s"Location is ${p.x}, ${p.y}") //here y also created when first access time
now y
Location is 2019268581, -806862774
scala> println(s"Location is ${p.x}, ${p.y}")
Location is 2019268581, -806862774
Our class, RandomPoint, initializes its two fields with expressions that print a message before returning their randomly generated number. The ìxî field, a regular value, is initialized when our instance ìpî is created. The ìyî field, a lazy value, is initialized the first time we access it, but only the first time. In the second printout, both values have been initialized and are stable
---------------
Packaging
--------------
Packages are Scalaís (and Javaís) system for code organization. They make it possible to organize Scala code by directory using period-separated paths. Use the package key-word
at the top of a Scala source file to declare all classes in that file to be included in the package.
Packaging Syntax
----------------------
package <identifier> { <class definitions> } (or)
package <identifier>
Scala source files should be stored in directories that match their packages. For example, a ìDateUtilitiesî class in the ìcom.netflix.utilitiesî package should be stored under com/ netflix/utilities/DateUtilities.scala. The Scala compiler will store the generated .class files (the standard binary format for JVM-executable code) in a directory structure that matches the package. Letís try this out by creating a source file with a package and compiling it. Weíll use the scalac command to compile the source file and generate a class file local to the current directory:
$ mkdir -p src/com/oreilly
$ cat > src/com/oreilly/Config.scala package com.oreilly class Config(val baseUrl: String = "http://localhost")
$ scalac src/com/oreilly/Config.scala
$ ls com/oreilly/Config.class com/oreilly/Config.class
The src directory is a nice way to separate the source code from whatever else is in the current directory, but it wasnít actually used by the compiler. It took the relative path to the source file, compiled it, and generated a class file relative to the directory you launched the compiler from
Accessing Packaged Classes
--------------------------------------
Letís try this out by accessing the JDKís Date class, located in the java.util package:
scala> val d = new java.util.Date
d: java.util.Date = Wed Jan 22 16:42:04 PDT 2014
A more convenient way to access classes in other packages is to import them into the current namespace. That way, the class can be accessed without its package prefix. To import a class, use the import keyword followed by the full package and name of the class.
Syntax: Importing a Packaged Class
-----------------------------------------
import <package>.<class>
Letís create a new Date, but only after importing the class into the namespace so we can refer to it by name:
scala> import java.util.Date import java.util.Date
scala> val d = new Date
d: java.util.Date = Wed Jan 22 16:49:17 PDT 2014
The Date class we are instantiating still lives in its java.util package, but is now also part of the current namespace.
The import command is a statement, because it doesnít return a value. Unlike in Java (which has a similar import keyword), an import can be placed anywhere in your code where you might use a statement.
Letís exercise the ability to place imports wherever we might use any other statement. In this example Iíll add an import for Javaís UUID class in the middle of a println call:
scala> println("Your new UUID is " + {import java.util.UUID; UUID.randomUUID})
Your new UUID is 47ba6844-3df5-403e-92cc-e429e614c9e5
You may not always want to add imports in the restricted scope of a function call. However, adding your imports near the code where you are using the imported classes helps to make the intent of the import more clear. It may also prevent name conflicts caused by importing multiple classes of the same name from different packages. By adding the conflicting imports in separate scopes, as opposed to adding them at the top of the file, the classes can be used without conflict.
An alternative to importing the full package and class is to import part of the pack- age, reducing the need to refer to the full package but not quite importing a class. Scalaís imports are accumulative, so importing a package allows us to remove that package from the full path of a class in the package.
Scala also supports importing the entire contents of a package at once with the under- score (_) operator. After doing so, every class in that package will be added to the name- space. You might remember when we used this to import multiple Future helper classes (see ìHandling futures synchronouslyî on page 129) without importing them one at a time
Letís use the import-all feature to import all of the mutable collections into our current namespace, and then experiment with the ArrayBuffer and Queue collections in that package:
scala> import scala.collection.mutable._
scala> val b = new ArrayBuffer[String]
b: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer()
scala> b += "Hello" res0:
b.type = ArrayBuffer(Hello)
scala> val q = new Queue[Int]
q: scala.collection.mutable.Queue[Int] = Queue()
scala> q.enqueue(3, 4, 5)
scala> val pop = q.dequeue
pop: Int = 3
scala> println(q)
Queue(4, 5)
Speaking of ArrayBuffer, did you notice that we imported everything in the collection.mutable package but the REPL printed its full class name as scala.collection.mutable.ArrayBuffer? Scala does its own automatic imports in every Scala class, importing the entire scala._ and java.lang._ packages. This makes it possible to access the classes and packages in scala and java.lang directly without using the full path.
IMPORT GROUP
------------------------
An alternative to importing a full package is to use an import group. With this feature, you can list a group of class names to import instead of a complete package.
Syntax: Using an Import Group
import <package>.{<class 1>[, <class 2>...]} With an import group I could have imported the Queue and ArrayBuffer collections directly without importing the mutable Map:
scala> import scala.collection.mutable.{Queue,ArrayBuffer}
scala> val q = new Queue[Int]
q: scala.collection.mutable.Queue[Int] = Queue()
scala> val b = new ArrayBuffer[String]
b: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer()
scala> val m = Map(1 -> 2)
m: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
After importing only the mutable collections that we wanted, we can use the Queue and ArrayBuffer mutable collections while still accessing the Map immutable collection. In
veral classes from the same package they can visibly reduce the size of an ìimportî section.
Thereís actually a way to add both the immutable and the mutable Map collections to the current namespace without having a conflict. To do this, use an import alias that renames one of the types inside the local namespace. Whatís renamed is the local namespace reference to the class, not the class itself, so there is no actual change to classes outside your namespace (typically the file you are editing).
IMPORT ALIIAS
-------------------------------------
Syntax: Using an Import Alias
import <package>.{<original name>=><alias>}
Letís use an import alias to bring the collection.mutable.Map collection into our namespace, but in a way that wonít conflict with our standard immutable Map:
scala> import collection.mutable.{Map=>MutMap}
import collection.mutable.{Map=>MutMap}
scala> val m1 = Map(1 -> 2)
m1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
scala> val m2 = MutMap(2 -> 3)
m2: scala.collection.mutable.Map[Int,Int] = Map(2 -> 3)
scala> m2.remove(2); println(m2)
Map()
With our aliased collection ìMutMapî (short for ìmutable,î not ìmuttî!) we can create both mutable and immutable maps by name, without specifying their packages
PACKAGE WRITING
------------------------------
Syntax: Packaging Classes
package <identifier> { <class definitions> }
Letís rewrite our ìConfigî example (from ìPackagingî on page 151) using packaging syntax. Because we arenít relying on a file to delimit the end of the package, we can write the entire package in the REPL:
The Scala REPL Requires ìRawî Paste Mode for Packages .
Packages are traditionally used to mark files, and thus are unsuppor- ted in the standard editing mode in the REPL. The workaround is to enter the ìrawî paste mode with :paste -raw and then paste the contents of a Scala file, which will be fully compiled but available from the REPL.
scala> :paste -raw // Entering paste mode (ctrl-D to finish)
package com {
package oreilly {
class Config(val baseUrl: String = "http://localhost")
}
}
// Exiting paste mode, now interpreting.
scala> val url = new com.oreilly.Config().baseUrl url: String = http://localhost
Our new class is now available at com.oreilly.Config and clearly packaged.
Privacy Controls
-------------------------
A corollary to packaging code is using privacy controls to manage its access. While you are organizing your code into separate packages (or subpackages), youíll probably find certain functionality in one package that ought to be hidden from other packages. For example, low-level persistence code could be hidden from your user interface-level code to force your layers to use a middle layer for communication. Or you may want to limit who can extend a subclass so that your parent class can keep track of its implementers.
One privacy control is marking fields and methods as protected, which will limit their access to code from the same class or its subclasses. No other code except that class or subclasses will have access. Use the protected keyword before a val, var, or def key- word to mark that entity as being protected
Hereís an example of protecting a field from access by outside classes. The field is still accessible by a subclass, however:
scala> class User { protected val passwd = util.Random.nextString(10) } defined class User
scala> class ValidUser extends User { def isValid = ! passwd.isEmpty } defined class ValidUser
scala> val isValid = new ValidUser().isValid isValid: Boolean = true To verify that the ìpasswdî field is only accessible to ìUserî and its subclasses, try cre- ating a new instance of ìUserî and access its protected field directly. You should see an error from the compiler alerting you that the ìpasswdî field is not accessible from out- side the class (or subclasses).
When you need a more stringent level of protection, mark fields and methods as pri- vate to limit their access to only the class in which they are defined. No other code outside the class, and not even subclasses, will have access to that field.
Finally, weíll add a validation system, again without exposing our private (literally!) password to external reads or writes:
scala> class User(private var password: String) { | def update(p: String) { | println("Modifying the password!") | password = p | } | def validate(p: String) = p == password | } defined class User
scala> val u = new User("1234") u: User = User@94f6bfb
scala> val isValid = u.validate("4567") isValid: Boolean = false
scala> u.update("4567") Modifying the password!
scala> val isValid = u.validate("4567") isValid: Boolean = true
Privacy Access Modifiers
---------------------------------
The private and protected keywords provide class-hierarchy restrictions, but there are times when you want more fine-grained access control for your classís members. For example, a class in a persistence package may only want to reveal some of its database-level methods to other classes in the same package to reduce bugs and ensure a single point of access.
You can add this level of control by specifying access modifiers in addition to your private or protected designation. An access modifier specifies that such a designation is only active up to a given point, such as a package, class, or instance, and then is inactive within that point
Letís try this out with an example of specifying both package-level and instance-level protections. Weíll use the packaging syntax to denote the classís package, and thus the ìrawî paste mode in the REPL to support the packaging:
scala> :paste -raw // Entering paste mode (ctrl-D to finish)
package com.oreilly {
private[oreilly] class Config { (1)
val url = "http://localhost"
}
class Authentication {
private[this] val password = "jason" // TODO change me (2) def validate = password.size > 0
}
class Test { println(s"url = ${new Config().url}")
}
}
// Exiting paste mode, now interpreting. (3)
scala> val valid = new com.oreilly.Authentication().validate
valid: Boolean = true
scala> new com.oreilly.Test url = http://localhost (4)
res0: com.oreilly.Test = com.oreilly.Test@4c309d4d
scala> new com.oreilly.Test url = http://localhost (5)
res0: com.oreilly.Test = com.oreilly.Test@4c309d4d
scala> new com.oreilly.Config
<console>:8: error: class Config Access to the ìConfigî class is now restricted to the ìcom.oreillyî package. Only the last part of the package path is required here. Our secretive ìpasswordî field is now off-limits to everyone except code within the same instance of the same class. Here we are verifying ìpasswordî access from the same instance. The ìTestî class was able to successfully instantiate a ìConfigî classÖ Ö but we were not able to do the same from outside the package.
in package oreilly cannot be accessed in package com.oreilly New com.oreilly.Config
1)Access to the ìConfigî class is now restricted to the ìcom.oreillyî package. Only the last part of the package path is required here.
2)Our secretive ìpasswordî field is now off-limits to everyone except code within the same instance of the same class.
3)Here we are verifying ìpasswordî access from the same instance.
4)The ìTestî class was able to successfully instantiate a ìConfigî classÖ
5) Ö but we were not able to do the same from outside the package.
*******************************************************************************
Objects, Case Classes, and Traits
*********************************************
-------------
Objects
-------------
An object is a type of class that can have no more than one instance, known in object- oriented design as a singleton. Instead of creating an instance with a new keyword, just access the object directly by name. An object gets automatically instantiated the first time it is accessed in a running JVM, which also means that until it is accessed the first time it wonít get instantiated
Use the object keyword, in place of class, to define an object. Objects do not take any parameters (they are automatically instantiated), but you can define the same fields, methods, and internal classes as you can with regular classes.
Syntax: Defining an Object
object <identifier> [extends <identifier>] [{ fields, methods, and classes }]
Letís design an object that will demonstrate how objects are automatically instantiated:
scala> object Hello { println("in Hello"); def hi = "hi" }
defined object Hello
scala> println(Hello.hi)
in Hello hi
scala> println(Hello.hi)
hi
The println at the top level of the object is invoked at instantiation/initialization, which only occurs when it is accessed for the first time. Repeating the call to the objectís ìhiî method reused the same global instance so there was no additional initialization
+As an example, weíll create an object that provides pure functions as utilities, one of my favorite uses for objects:
scala> object HtmlUtils { | def removeMarkup(input: String) = { | input
| .replaceAll("""</?\w[^>]*>""","") | .replaceAll("<.*>","") | } | }
defined object HtmlUtils
scala> val html = "<html><body><h1>Introduction</h1></body></html>" html: String = <html><body><h1>Introduction</h1></body></html>
scala> val text = HtmlUtils.removeMarkup(html)
text: String = Introduction
Apply Methods and Companion Objects
----------------------------------------------------
We have covered the apply method for classes (see ìApply Methodsî on page 150), which makes it possible to invoke an instance. The same feature works for objects, making it possible to invoke an object by name. By defining one or more of these methods, your object can be invoked by name, much like List(1, 2, 3).
The Future object uses apply() to take your function parameter and invoke it in a background thread. This is known as the Objects factory pattern in object-oriented programming, and is a popular use of the apply() method in objects. Specifically, the factory pattern is a popular way to generate new instances of a class from its companion object. A companion object is an object that shares the same name as a class and is defined together in the same file as the class. Having a companion object for a class is a common pattern in Scala, but there is also a feature from which they can benefit. Companion objects and classes are considered a single unit in terms of access controls, so they can access each otherís private and protected fields and methods.
Letís try out the apply() factory pattern and the companion object pattern in the same example. We will use the REPLís :paste mode to simulate a class and object defined together in the same file, because otherwise the REPL would assume they are separate:
However, we havenít yet seen the benefit of a companion object, namely the special access controls that it shares with a companion class. Letís try this out in a new example where the class accesses private members of its companion object:
scala> :paste // Entering paste mode (ctrl-D to finish)
object DBConnection {
private val db_url = "jdbc://localhost"
private val db_user = "franken"
private val db_pass = "berry"
def apply() = new DBConnection
}
class DBConnection {
private val props = Map(
"url" -> DBConnection.db_url,
"user" -> DBConnection.db_user,
"pass" -> DBConnection.db_pass )
println(s"Created new connection for " + props("url"))
}
// Exiting paste mode, now interpreting.
defined object DBConnection
defined class DBConnection
scala> val conn = DBConnection()
Created new connection for jdbc://localhost
conn: DBConnection = DBConnection@4d27d9d
Our new DBConnection object stores the database connection data in private constants, while the class of the same name can read them when creating a connection. The con- stants are global, because the settings are constant across the application, and safe from being read by any other part of the system.
This approach is suitable for testing, but doesnít make it possible to reuse your code. The scala command will execute the contents of your file as if they were entered in a REPL, but you donít end up with compiled classes. In order to write reusable, compiled code, youíll need to compile your classes and objects with the scalac command and then execute them from your own application. In the next section weíll learn how to write command-line applications with Scala so you can start reusing your classes and objects
Command-Line Applications with Objects
-----------------------------------------------------
weíll learn how to write command-line applications with Scala so you can start reusing your classes and objects
Here is a new example that emulates the Unix command cat, which prints the contents of a file to the console. It takes one or more filenames (or paths) and prints each one to the console:
$ cat > Cat.scala object Cat { def main(args: Array[String]) { for (arg <- args) {
println( io.Source.fromFile(arg).mkString ) } } }
$ scalac Cat.scala
$ scala Cat Date.scala
object Date { def main(args: Array[String]) { println(new java.util.Date) } }
This time weíre making use of the input arguments. The fromFile method in the Scala libraryís io.Source object (we can call it by its correct name now) is used to read each file, and the collection method mkString is used to convert the lines back into a single String for printing
USE;
------
In a way, the best command-line applications are like pure functions: they read input, process it, and write output. Like the operations in Scalaís collections they are only good for a single task, but when chained together they create a bounty of new opportunities and possibilities. Command-line applications written in Scala may not replace native tools and shell scripts, because their slower startup time (a known problem in the JVM) and greater memory requirements may make them less desirable for all environments. They do make writing command-line tools more fun, however, and are a great way to learn the language. I recommend taking the time to rewrite some of your favorite (and shorter) shell scripts in Scala. Itís a great way to continue learning and practicing with the language, and you may find your Scala applications to be shorter and more stable than those written in other languages.
------------------------------------
Case Classes
-----------------------------------
A case class is an instantiable class that includes several automatically generated meth- ods. It also includes an automatically generated companion object with its own auto- matically generated methods. All of these methods in the class and in the companion object are based on the classís parameter list, with the parameters being used to formulate methods like an equals implemention that iteratively compares every field and a to String method that cleanly prints out the class name and all of its field values
And extending a case class with a regular class could lead to invalid results from the generated methods, which canít take into account fields added by sub-classes. However, if you want a class with a definitive set of fields, and these automatically generated methods are useful, then a case class may be right for you
To create a case class, just add the keyword case before your class definition.
Syntax: Defining a Case Class
----------------------------------------
case class <identifier> ([var] <identifier>: <type>[, ... ])
[extends <identifier>(<input parameters>)] [{ fields and methods }]
-->The val Keyword Is Assumed for Case Class Parameters :
---------------------------------------------------------------------
By default, case classes convert parameters to value fields so it isnít necessary to prefix them with the val keyword. You can still use the var keyword if you need a variable field.
--->displays the class and object methods that get automatically generated for case classes.
Generated case class methods
--------------------------------------------------------------------------------------------------------------------
Name Location Description
--------------------------------------------------------------------------------------------------------------------
apply Object A factory method for instantiating the case class.
copy Class Returns a copy of the instance with any requested changes. The parameters are the classís fields with the default values set to the current field values.
equals Class Returns true if every field in another instance match every field in this instance. Also invocable by the operator ==.
hashCode Class Returns a hash code of the instanceís fields, useful for hash-based collections.
toString Class Renders the classís name and fields to a String.
unapply Object Extracts the instance into a tuple of its fields, making it possible to use case class instances or pattern matching.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
The methods generated by the Scala compiler for case classes arenít special in any way, other than that they are automatically generated for you. You could skip using case classes and add the methods and companion object yourself. The benefit that case classes bring is convenience, because writing all of these methods correctly for every data-based class would require a lot of work and maintenance. They also add a certain level of consistency, because all case classes carry the same features.
scala> case class Character(name: String, isThief: Boolean) (1)
defined class Character
scala> val h = Character("Hadrian", true) (2)
h: Character = Character(Hadrian,true)
scala> val r = h.copy(name = "Royce") // r,h is instances (3)
r: Character = Character(Royce,true)
scala> h == r (4)
res0: Boolean = false
scala> h match {
case Character(x, true) => s"$x is a thief" (5)
case Character(x, false) => s"$x is not a thief"
| }
res1: String = Hadrian is a thief
1)Hereís our companion objectís factory method, Character.apply().
2) The generated toString method, printed here by the REPL, is a clean and simple representation of the fields in our instance.
3)Our second instance shares the same value for the second field, so we only need to specify a new value for the first field in the copy method.
4)If both instances are nonnull, the == operator triggers an instanceís equals method, acting as a useful shortcut to the field comparisonñbased method generated for us.
5) The companion objectís unapply method allows us to decompose the instance into its parts, binding the first field (see ìMatching with Wildcard Patternsî on page 34) and using a literal value to match the second field.
All of the generated methods we used in the example depended on the case class having two fields, name and isThief, based on the case class parameters. If our case class had extended another class with its own fields, but we hadnít added the fields as case class parameters, the generated methods wouldnít have been able to make use of them. This is an important caveat to know about before using case classes.
------------------------------------------------------------
Traits
------------------------------------------------------------
A trait is a kind of class that enables multiple inheritance. Classes, case classes, objects, and (yes) traits can all extend no more than one class but can extend multiple traits at the same time. Unlike the other types, however, traits cannot be instantiated
To define a trait, use the trait keyword in place of where you would normally use the class keyword.
Syntax: Defining a Trait
-------------------------------
trait <identifier> [extends <identifier>] [{ fields, methods, and classes }]
scala> trait HtmlUtils { | def removeMarkup(input: String) = { | input | .replaceAll("""</?\w[^>]*>""","") | .replaceAll("<.*>","") | } | }
defined trait HtmlUtils
scala> class Page(val s: String) extends HtmlUtils { | def asPlainText = removeMarkup(s) | }
defined class Page
scala> new Page("<html><body><h1>Introduction</h1></body></html>").asPlainText
res2: String = Introduction Our Page class can now use the removeMarkup method directly without specifying an object name.
This works pretty well, but a class version of HtmlUtils could have done the same job. Letís make it more interesting by adding a second trait. This time weíll use a new key- word, with, which is required for extending the second and later traits:
scala> trait SafeStringUtils { // Returns a trimmed version of the string wrapped in an Option, | // or None if the trimmed string is empty. |
def trimToNone(s:String): Option[String] = { | Option(s) map(_.trim) filterNot(_.isEmpty) | } | }
defined trait SafeStringUtils
scala> class Page(val s: String) extends SafeStringUtils with HtmlUtils { | def asPlainText: String = { | trimToNone(s) map removeMarkup getOrElse "n/a" | } | } defined class Page
scala> new Page("<html><body><h1>Introduction</h1></body></html>").asPlainText
res3: String = Introduction
scala> new Page(" ").asPlainText
res4: String = n/a
scala> new Page(null).asPlainText
res5: String = n/a
Our new, more robust Page class now extends two traits and can handle null or empty strings by returning the message n/a.
This process of taking a horizontal list of a class and traits being extended, and reforming them into a vertical chain of one class extending another, is known as linearization. It is a kind of coping mechanism for supporting multiple inheritance in an execution environment that only supports single inheritance. The fact that the JVM only supports single inheritance ensures that all class hierarchies are nondeterministic and prevents the possibility of confusing two traits that have competing members
The most important point to understand about linearization is in what order the Scala compiler arranges the traits and optional class to extend one another. The multiple inheritance ordering, from the lowest subclass up to the highest base class, is right to left.
Thus, a class defined as class D extends A with B with C, where A is a class and B and C are traits, would be reimplemented by the compiler as class D extends C ex tends B extends A. The rightmost trait is the immediate parent of the class being defined, and either the class or the first trait becomes the last parent class. This is a lot to remember, so letís write a quick test to verify this ordering:
scala> trait Base { override def toString = "Base" }
defined trait Base
scala> class A extends Base { override def toString = "A->" + super.toString }
defined class A
scala> trait B extends Base { override def toString = "B->" + super.toString }
defined trait B
scala> trait C extends Base { override def toString = "C->" + super.toString }
defined trait C
scala> class D extends A with B with C { override def toString = "D->" + super.toString } defined class D
scala> new D()
res50: D = D->C->B->A->Base
The toString method overridden in D prints the class name and then appends the output of its parent classís implementation. Fortunately all of its parent classes also override this method, so we can see the exact ordering of methods called. First the toString in D was invoked, followed by the one in trait C, trait B, class A, and finally the common base class Base.
The process of linearization may seem odd, but itís a useful compromise between the theory of a language supporting multiple inheritance versus the practice of an envi- ronment that doesnít. It also provides a solid method for determining invocation, because the constructed hierarchy ensures that method handling is decided at compile time and never at runtime
Another benefit of linearization is that you can write traits to override the behavior of a shared parent class. Hereís an example of a solid base class plus traits that add extra functionality when combined with a subclass. The example is rather lengthy so weíll cover it in two parts. First, hereís the parent class and two traits that extend it:
scala> class RGBColor(val color: Int) { def hex = f"$color%06X" }
defined class RGBColor
scala> val green = new RGBColor(255 << 8).hex
green: String = 00FF00
scala> trait Opaque extends RGBColor { override def hex = s"${super.hex}FF" }
defined trait Opaque
scala> trait Sheer extends RGBColor { override def hex = s"${super.hex}33" }
defined trait Sheer
The two traits, Opaque and Sheer, extend the RGBColor class and add an opacity level to the red-green-blue color of its parent. The extra byte is often known as an alpha channel in computer graphics, so the traits are convering an RGB color value to an RGBA (a for alpha) color value, in hexadecimal format.
------------------------------
Self Types
------------------------------
A self type is a trait annotation that asserts that the trait must be mixed in with a specific type, or its subtype, when it is added to a class. A trait with a self type cannot be added to a class that does not extend the specified type. In a way, it is a guarantee that the trait will always be extending that type, while not actually directly extending it.
A popular use of self types is to add functionality with traits to classes that require input parameters. A trait cannot easily extend a class that takes input parameters, because the trait itself cannot take input parameters. However, it can declare itself to be a subtype of that parent class with a self type and then add its functionality.
A self type is added immediately following the opening brace of the trait definition, and includes an identifier, the requested type, and an arrow (=>). A trait with a self type can access fields of that type as if it explicitly extended that type
Syntax: Defining a Self Type
trait ..... { <identifier>: <type> => .... }
The standard identifier used in self types is ìself,î although any other identifier may be used. That is, except for a keyword like this. The benefit of using the common identifier ìselfî is that it can help to make your code more readable to other Scala developers.
Here is an example of a trait using a self type to ensure that it will always be a subtype of the specified type when mixed into a class:
scala> class A { def hi = "hi" }
defined class A
scala> trait B { self: A => override def toString = "B: " + hi } (1)
defined trait B
scala> class C extends B (2)
<console>:9: error: illegal inheritance;
self-type C does not conform to B's selftype B with A
class C extends B ^
scala> class C extends A with B (3)
defined class C
scala> new C() (4)
res1: C = B: hi
1)Our trait B has a self type, adding the requirement that the trait can only ever be mixed into a subtype of the specified type, the A class. Ö
2) but just to prove it, letís try defining a class with trait B but without the requested class. No luck.
3)This time, trait B is directly extending its requested type, A, so its self type requirement has been met.
4)When our C class is instantiated, B.toString is invoked, which then invokes A.hi. The B trait is indeed used as a subtype of A here and can invoke one of its methods.
-------------------------------------
Instantiation WITH Traits
-------------------------------------
In this chapter we have used traits by having classes extend them, using the extends or with keyword in the class definition. The class that extends the trait will pick up the fields and methods of that trait, whether they are implemented by the trait or inherited from its own subtypes
You can add one or more traits to a class using the WITH keyword. The EXTENDS keyword cannot be used here, which is appropriate; your class is not actually extending the traits but instead being extended by them.
Here is an example of a class extended by a trait with a self type of that class, ensuring that the trait will extend the class:
scala> class A
defined class A
scala> trait B { self: A => }
defined trait B
scala> val a = new A with B
a: A with B = $anon$1@26a7b76d
we created an instance where trait B extended trait A.
Letís experiment with dependency injection by taking a data-oriented class common in most applications, User, and altering its output in new and mysterious ways:
scala> class User(val name: String) { def suffix = "" override def toString = s"$name$suffix" | }
defined class User
scala> trait Attorney { self: User => override def suffix = ", esq." }
defined trait Attorney
scala> trait Wizard { self: User => override def suffix = ", Wizard" }
defined trait Wizard
scala> trait Reverser { override def toString = super.toString.reverse }
defined trait Reverser
scala> val h = new User("Harry P") with Wizard
h: User with Wizard = Harry P, Wizard
scala> val g = new User("Ginny W") with Attorney
g: User with Attorney = Ginny W, esq.
scala> val l = new User("Luna L") with Wizard with Reverser
l: User with Wizard with Reverser = draziW ,L anuL
------------------------------------------
Importing Instance Members
------------------------------------------
The import keyword can also be used to import members of classes and objects into the current namespace. This makes it possible to access them directly without specifying their enclosing instance (for classes) or name (for objects).
The syntax for importing class and object members is the same as importing packaged classes. You can import a single member of a class instance by name, or the entire set of fields and methods with the underscore character. Importing fields and methods does not override privacy controls, so only those that would be normally accessible can be imported.
Here is an example of a case classís members being imported for better accessibility:
scala> case class Receipt(id: Int, amount: Double, who: String, title: String)
defined class Receipt
scala> {
val latteReceipt = Receipt(123, 4.12, "fred", "Medium Latte")
import latteReceipt._
println(s"Sold a $title for $amount to $who")
}
Sold a Medium Latte for 4.12 to fred By importing the fields from a value with a lengthy name, latteReceipt, we could access them directly in our println statement with a much simpler line of code.
As an example of object imports, letís add all of the methods from the util.Random object. This object extends the util.Random class, providing a single global instance thatís useful to use when you donít need to set a new seed for random number generation:
scala> import util.Random._
scala> val letters = alphanumeric.take(20).toList.mkString
letters: String = MwDR3EyHa1cr0JqsP9Tf
scala> val numbers = shuffle(1 to 20)
numbers: scala.collection.immutable.IndexedSeq[Int] = Vector(5, 10, 18, 1, 16, 8, 20, 14, 19, 11, 17, 3, 15, 7, 4, 9, 6, 12, 13, 2)
The alphanumeric(): Stream and shuffle(Traversable) methods, members of the util.Random object (and parent class), are here made accessible without their objectís prefix.
USAGE:
-----------
Importing instance members is a great way to streamline your code. Care must be taken to avoid naming conflicts, however, as well as any reductions in code readability. If readers of your code will get confused by the source of the imported members you are using, consider locating your import statement closer to the affected code.
--------------------------------------------------------------------------------
VARIABLE DECLARATION AND EXAMPLES
---------------------------------------------------------------------------------
VALUES
*******
Values are immutable, typed storage units, and by convention are the default method for storing data.
You can define a new value using the val keyword.
The Scala compiler, via the REPL, was able to deduce that the literal 20 corresponds to the type Int, the literal "Hello, World" to the type String, and the literal @ to the type Char.
INTEGER TYPE:
---------------
scala> val m=5;
m: Int = 5
scala> m
res1: Int = 5
FLOAT TYPE:
-----------
scala> val mm =5f //here f suffix the value belongs to float without f it belongs to double.
mm: Float = 5.0
scala> mm
res9: Float = 5.0
scala> val nn = 15.04
nn: Double = 15.04
scala> nn
res2: Double = 15.04
CHAR TYPE:
-----------
scala> val cha = 'a'
cha: Char = a
scala> cha
res3: Char = a
STRING TYPE:
------------
scala> van name:string = "7hills"
<console>:1: error: ';' expected but '=' found.
van name:string = "7hills"
^
scala> name
<console>:12: error: not found: value name
name
^
scala> val name:string = "7hills"
<console>:11: error: not found: type string
val name:string = "7hills"
^
scala> val name:String = "7hills"
name: String = 7hills
scala> val mm = println("what are you doing");
what are you doing
mm: Unit = () //here mm is unit() type
scala> mm
scala> mm
NOTE:when using println command it can not store into variable it just print the data only but we can declare as a string it would be print and retrive anyware
scala> val mm ="what are you doing"
mm: String = what are you doing
scala> mm
res8: String = what are you doing
VARIBLE:
---------
The var keyword is used to define a variable with a given name, type, and assignment.
Syntax: Defining a Variable
var <identifier>[: <type>] = <data>
scala> p
res10: Double = 7.5
scala> var naam = "7hills"
naam: String = 7hills
scala> var naam = "8hills"
naam: String = 8hills
scala> val nn = 16.2
nn: Double = 16.2
scala> nn = 15.2 //because nn is immutable it intiate by val
<console>:12: error: reassignment to val
nn = 15.2
^
scala> naam = "jfkdj"
naam: String = jfkdj
*******************************************************************************
IF...ELSE EXPRESSIONS
--------------------------------------------------------------------------------
NOTE: 1) if expressions without an else should always use curly braces, ****** because they tend to be statements that create side effects.
2) in scala doesn't have the NESTED-IF OR LEADER-IF.
3) where the punctuation characters ? and : act as a one-line if and else expression.
scala> val x = 2
x: Int = 2
scala> x
res0: Int = 2
scala> val nn ={if (x%2 == 0)}
<console>:1: error: illegal start of simple expression
val nn ={if (x%2 == 0)}
^
scala> val nn ={if (x%2 == 0) println("x is divisible by 2")}
x is divisible by 2
nn: Unit = ()
scala> val x = 5
x: Int = 5
scala> val nn ={if (x%2 == 0) println("x is divisible by 2")}
nn: Unit = ()
scala> val nn ={if (x%2 == 0) println("x is divisible by 2") else println("x is not divisible by 2")}
x is not divisible by 2
nn: Unit = ()
------------------------------------------------------------------------------
Match Expressions
-------------------------------------------------------------------------------
scala> val day="sat"
day: String = sat
scala> val week = day match
| { case "mon"|"tue"|"wed"|"thurs"|"fri" => "regularday"
| case "sat"|"sun" => "weekday"
| }
week: String = weekday
scala> val student = sno match{
| case 102 => println("\n student name is 7hills \n student number is $sno")
| case 103 => println("\n student name is 8hills \n student number is $sno")
| case 104 => println("\n student name is 10hills \n studen number is $sno")
| }
student name is 7hills
student number is $sno
student: Unit = ()
printl the single value
-----------------------
scala> val sno= 103
sno: Int = 103
scala> val stu = sno match{
| case 102 => println("student name is 7hills sno")
| case 103 => s"${sno}"
| }
stu: Any = 103
scala> val stu = sno match{
| case 102 => println("student name is 7hills sno")
| case 103 => "${sno}"
| }
stu: Any = ${sno}
TO PRINT THE NAME AND VALUE IN SINGLE LINE BY USING CASE STATEMENT
------------------------------------------------------------------
scala> val sno= 105
sno: Int = 105
scala> val student = sno match{
| case 102 => "stuent name is 7hills" + "number is "+ sno
| case 103 => "stuent name is pavan" + "number is "+ sno
| case 105 => "student name is sarvan" + "number is" + sno
| }
student: String = student name is sarvannumber is105
scala> val sno =102
sno: Int = 102
scala> val stu = sno match{
| case 102 => "stuent name is 7hills" + "number is "+ sno
| }
stu: String = stuent name is 7hillsnumber is 102
NOTE:Why an Underscore as a wildcard? Using underscores to indicate unknown values comes from the field of mathematics, arithmetic in particular, where missing amounts are denoted in problems with one or more underscores
scala> val sno= 107
sno: Int = 107
scala> val student = sno match{
| case 102 => "stuent name is 7hills" + "number is "+ sno
| case 103 => "stuent name is pavan" + "number is "+ sno
| case 105 => "student name is sarvan" + "number is" + sno
| case _ => println("the given number is invalid")
| }
the given number is invalid
student: Any = ()
ERROR:
-----
val stu = sno match{
| case 102 => "stuent name is 7hills" + "number is "
| }
scala.MatchError: 103 (of class java.lang.Integer)
... 34 elided
SOLUTION: sno must be match with any case value
here sno is 103 that way it shows but we write 102
MATCHING WITH PATTERN GURDS OR CASE STATEMENT WITH IF()EXPRESSIONS
-------------------------------------------------------------------
scala> val a=5
a: Int = 5
scala> val b=10
b: Int = 10
scala> val option=2
option: Int = 2
scala> val arthi = option match{
| case 1=> "the addition of a and b is" + a+b
| case 2 => "the subtraction of a and b is" + (a-b)
| case 3=> "the multiplication of a and b is" + a*b
| case 4 => "the subtraction of a and b is" + a/b
| case 5 => "the modulo of a and b is" + a%b
| }
arthi: String = the subtraction of a and b is-5
scala> val arthi = option match{
| case 1 if(a!=0 && b!=0)=> "the addition of a and b is" + a+b
| case 2 if(a!=0 && b!=0)=> "the subtraction of a and b is" + (a-b)
| }
arthi: String = the subtraction of a and b is-5
ERROR
------
NOTE: SUBTRACTION OPERTION MUST BE IN () PARANTHISIS.
scala> val arthi = option match{
| case 1 if(a!=0 && b!=0)=> "the addition of a and b is" + a+b
| case 2 if(a!=0 && b!=0)=> "the subtraction of a and b is" + a-b
| }
<console>:16: error: value - is not a member of String
case 2 if(a!=0 && b!=0)=> "the subtraction of a and b is" + a-b
^
NESTED FOR LOOP
---------------
// there is no need to create a relation (fo)
scala> val fo=for(x<- 1 to 2){
| for(y <- 3 to 4){
| println(x,y)
| }
| }
(1,3)
(1,4)
(2,3)
(2,4)
fo: Unit = ()
scala> fo
//not display any result
scala>
********************************************************************************
-------------------------------------------------------------------------------
FUNCTIONS
-------------------------------------------------------------------------------
UNNUMOUS FUNCTIONS
-------------------
scala> val x=9
x: Int = 9
scala> val y = 10
y: Int = 10
scala> def mul:Int = x*y
mul: Int
scala> mul
res0: Int = 90
scala> def fun ="hi"
fun: String
scala> fun
res1: String = hi
--------------------
NAMED FUNCTIONS
--------------------
scala> def MULTIPLICATION(mul:Int):Int = x*y
MULTIPLICATION: (mul: Int)Int
scala> MULTIPLICATION
<console>:15: error: missing argument list for method MULTIPLICATION
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `MULTIPLICATION _` or `MULTIPLICATION(_)` instead of `MULTIPLICATION`.
MULTIPLICATION
^
scala> MULTIPLICATION
<console>:15: error: missing argument list for method MULTIPLICATION
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `MULTIPLICATION _` or `MULTIPLICATION(_)` instead of `MULTIPLICATION`.
MULTIPLICATION
^
scala> MULTIPLICATION(5,5)
<console>:15: error: too many arguments for method MULTIPLICATION: (mul: Int)Int
MULTIPLICATION(5,5)
^
scala> MULTIPLICATION(5)
res5: Int = 90
scala> MULTIPLICATION()
<console>:15: error: not enough arguments for method MULTIPLICATION: (mul: Int)Int.
Unspecified value parameter mul.
MULTIPLICATION()
^
scala> def MULTIPLICATION(mul:Int):Int = mul*y
MULTIPLICATION: (mul: Int)Int
scala> MULTIPLICATION(5)
res7: Int = 50
scala> def factorial1(n : Int) : Int = {
| var res = 1
| for(x <- 1 to n) res = res * x
//here don't have the return res so it shows error
| }
<console>:14: error: type mismatch;
found : Unit
required: Int
for(x <- 1 to n) res = res * x
^
scala> def factorial1(n : Int) : Int = {
| var res = 1
| for(x <- 1 to n) res = res * x
| res
| }
factorial1: (n: Int)Int
scala> factorial1(4)
res10: Int = 24
scala> def factrec(n : Int) : Int = {
| if(n==1)
| n
| else
| n*factrec(n-1) //here give the return statement it show st.consol error
| }
factrec: (n: Int)Int
scala> factrec(3)
res13: Int = 6
/* TO FIND THE FIBNOIC SERIES OF GIVEN NO */
scala> def fib(n:Int):Int={
| var c=0;var a=0;var b=1
| for(x <- 1 to (n+1)){
| c=a+b
| println("a :" + a +"b: " + b +"c:" + c)
| a=b;b=c;
| }
| c
| }
fib: (n: Int)Int
scala> fib(3)
a :0b: 1c:1
a :1b: 1c:2
a :1b: 2c:3
a :2b: 3c:5
res23: Int = 5
scala> def fib(n:Int):Int={
| var c=0;var a=0;var b=1
| for(x <- 1 to n){
| c=a+b
| println("a :" + a +"b: " + b +"c:" + c)
| a=b;b=c;
| }
| c
| }
fib: (n: Int)Int
scala> fib(3)
a :0b: 1c:1
a :1b: 1c:2
a :1b: 2c:3
res25: Int = 3
scala> fib(5)
a :0b: 1c:1
a :1b: 1c:2
a :1b: 2c:3
a :2b: 3c:5
a :3b: 5c:8
res26: Int = 8
-------------------
nested functions
-------------------
scala> def max(x: Int)(y: Int) = if (x > y) x else y
max: (x: Int)(y: Int)Int
scala> val larger = max(20)(39)
larger: Int = 39
-----------------
varge parameters (*)
----------------
scala> def sum(items: Int*): Int = {
| var total = 0
| for (i <- items){ total += i
| println("i : " + i +" item : " + items)
| }
| total
| }
sum: (items: Int*)Int
scala> sum(10,20,90)
i : 10 item : WrappedArray(10, 20, 90)
i : 20 item : WrappedArray(10, 20, 90)
i : 90 item : WrappedArray(10, 20, 90)
res29: Int = 120
----------------------------------------
Calling Functions with Named Parameters
----------------------------------------
scala> val res = sum(20,30,40)
i : 20 item : WrappedArray(20, 30, 40)
i : 30 item : WrappedArray(20, 30, 40)
i : 40 item : WrappedArray(20, 30, 40)
res: Int = 90
scala> res
res0: Int = 90
----------------------------------------------
Assigning a Function with the Wildcard Operator
-----------------------------------------------
scala> def double(x: Int): Int = x * 2
double: (x: Int)Int
scala> val myDouble = double _
myDouble: Int => Int = <function1>
scala> val amount = myDouble(20)
amount: Int = 40
scala> def double(x: Int,y:Int): Int = x * y * 2
double: (x: Int, y: Int)Int
scala> val myDouble = double _,_
<console>:1: error: ';' expected but ',' found.
val myDouble = double _,_
^
scala> val myDouble = double _ _
<console>:1: error: ';' expected but '_' found.
val myDouble = double _ _
^
scala> val myDouble = double _ _
scala> def mydouble= double _+_
mydouble: String => String
scala> val rr = mydouble(20,30)
<console>:14: error: too many arguments for method apply: (v1: String)String in trait Function1
val rr = mydouble(20,30)
^
Anonymous Functions
Scala represents anonymous functions with a elegant syntax. The _ acts as a placeholder for parameters in the anonymous function. The _ should be used only once, But we can use two or more underscores to refer different parameters.
scala> List(1,2,3,4,5).foreach(print(_))
12345
scala> List(1,2,3,4,5).foreach( a => print(a))
12345
scala> val sum = List(1,2,3,4,5).reduceLeft(_+_)
sum: Int = 15
scala> val sum = List(1,2,3,4,5).reduceLeft(_+_)
sum: Int = 15
Scala is a functional language. So we can treat function as a normal variable. If you try to assign a function to a new variable, the function will be invoked and the result will be assigned to the variable. This confusion occurs due to the optional braces for method invocation. We should use _ after the function name to assign it to another variable.
class Test {
def fun = {
// some code
}
val funLike = fun _
}
--------------------------------------------
HIGH ORDER FUNCTION
--------------------------------------------
1)
scala> def logStart() = "-" * 50 + "\nStarting NOW\n" + "*" * 50
logStart: ()String
scala> val start: () => String = logStart
start: () => String = <function0>
scala> println( start() )
--------------------------------------------------
Starting NOW
**************************************************
2)
scala> def safeStringOp(s: String, f: String => String) = {
| if (s != null) f(s) else s
| }
safeStringOp: (s: String, f: String => String)String //f:String is func
scala> def reverser(s: String) = s.reverse
reverser: (s: String)String
scala> safeStringOp(null, reverser)
res2: String = null
scala> safeStringOp("7HILLS", reverser)
res3: String = SLLIH7
------------------------------------
String literals
------------------------------------
scala> val greater=(name:String)=>s"hello$name"
greater: String => String = <function1>
scala> val great= greater(7hills)
<console>:1: error: Invalid literal number
val great= greater(7hills)
^
scala> val great= greater("7hills")
great: String = hello7hills
scala> val great= greater("7hills")
great: String = hello7hills
scala> def tripleOp(a: Int, b: Int, c: Int, f: (Int, Int, Int) => Int) = f(a,b,c)
tripleOp: (a: Int, b: Int, c: Int, f: (Int, Int, Int) => Int)Int
scala> tripleOp(23, 92, 14, _ * _ + _)
res4: Int = 2130
scala> def tripleOp[A,B](a: A, b: A, c: A, f: (A, A, A) => B) = f(a,b,c)
tripleOp: [A, B](a: A, b: A, c: A, f: (A, A, A) => B)B
scala> ripleOp[Int,Int](23, 92, 14, _ * _ + _)
<console>:12: error: not found: value ripleOp
ripleOp[Int,Int](23, 92, 14, _ * _ + _)
^
scala> tripleOp[Int,Int](23, 92, 14, _ * _ + _)
res6: Int = 2130
scala> tripleOp[Int,Int](23, 92, 14, _ * _ + _)
res7: Int = 2130
scala> tripleOp[Int,Double](23, 92, 14, 1.0 * _ / _ / _)
res8: Double = 0.017857142857142856
********************************************************************************
more collections
***********************************
------------
List
-----------
scala> val numbers = List(32, 95, 24, 21, 17)
numbers: List[Int] = List(32, 95, 24, 21, 17)
scala> numbers[0]
<console>:1: error: identifier expected but integer literal found.
numbers[0]
^
scala> numbers(0)
res9: Int = 32
scala> numbers(n)
<console>:13: error: not found: value n
numbers(n)
^
scala> numbers(4)
res11: Int = 17
scala> val colors=list("red","green","yeelow")
<console>:12: error: too many arguments for method apply: (n: Int)Int in trait LinearSeqOptimized
val colors=list("red","green","yeelow")
^
scala> val colors=List("red","green","yeelow")
colors: List[String] = List(red, green, yeelow)
scala> colors.size
res2: Int = 3
scala> colors.head
res5: String = red
scala> colors.tail
res6: List[String] = List(green, yellow)
scala> colors.<press TAB> //now it will display predefined functions with
scala> . <press TAB>
++ flatMap min sortBy
++: flatten minBy sortWith
+: fold mkString sorted
/: foldLeft nonEmpty span
:+ foldRight orElse splitAt
:: forall padTo startsWith
::: foreach par stringPrefix
:\ genericBuilder partition sum
WithFilter groupBy patch tail
addString grouped permutations tails
aggregate hasDefiniteSize prefixLength take
andThen hashCode product takeRight
apply head productArity takeWhile
applyOrElse headOption productElement to
canEqual indexOf productIterator toArray
collect indexOfSlice productPrefix toBuffer
collectFirst indexWhere reduce toIndexedSeq
combinations indices reduceLeft toIterable
companion init reduceLeftOption toIterator
compose inits reduceOption toList
contains intersect reduceRight toMap
containsSlice isDefinedAt reduceRightOption toSeq
copyToArray isEmpty repr toSet
copyToBuffer isTraversableAgain reverse toStream
corresponds iterator reverseIterator toString
count last reverseMap toTraversable
diff lastIndexOf reverse_::: toVector
distinct lastIndexOfSlice runWith transpose
drop lastIndexWhere sameElements union
dropRight lastOption scan unzip
dropWhile length scanLeft unzip3
endsWith lengthCompare scanRight updated
equals lift segmentLength view
exists map seq withFilter
filter mapConserve size zip
filterNot max slice zipAll
find maxBy sliding zipWithIndex
----------------------------------------------
TO SEE THE FULL SYNTAX OF FUNCTION
-------------------------------------
scala> values.sortBy <press TAB>
def sortBy[B](f: Int => B)(implicit ord: scala.math.Ordering[B]): List[Int]
scala> values.sum <press Enter>
def sum[B >: Int](implicit num: Numeric[B]): B
scala> def sum[B >: Int](implicit num: Numeric[B]): B
| = 5 + 10
sum: [B >: Int](implicit num: Numeric[B])B
scala> sum
res14: Int = 15
scala> list.map<press TAB>
map mapConserve
scala> list.map<press TAB>
final override def map[B, That](f: Int => B)(implicit bf: scala.collection.generic.CanBuildFrom[List[Int],B,That]): That
def map[B](f: Int => B): scala.collection.TraversableOnce[B]
----------------------------------------------------------
CREATING A LIST BY USING CONS(::) OPERATOR
------------------------------------------------------------
scala> val list2=5::2::6::
<console>:11: error: value :: is not a member of Int
val list2=5::2::6::
^
scala> val list2=5::2::6::nill
<console>:11: error: not found: value nill
val list2=5::2::6::nill
^
scala> val list2=5::2::6::Nill
<console>:11: error: not found: value Nill
val list2=5::2::6::Nill
^
scala> val list2= 5 :: 2 :: 6 :: Nill
<console>:11: error: not found: value Nill
val list2= 5 :: 2 :: 6 :: Nill
^
scala> val list2= 5 :: 2 :: 6 :: Nil
list2: List[Int] = List(5, 2, 6)
scala> val list3=5::2::6::Nil
list3: List[Int] = List(5, 2, 6)
scala> list3
res18: List[Int] = List(5, 2, 6)
scala> list3::10
<console>:13: error: value :: is not a member of Int
list3::10
^
scala> list2
res20: List[Int] = List(5, 2, 6)
----------------------------------
append the data to list
-----------------------------------
scala> list2
res103: List[Int] = List(1, 7, 3, 4, 2)
NOTE :list can shows updated but not applay the original list
scala> list2::2
<console>:13: error: value :: is not a member of Int
list2::2
^
scala> list2:+2
res105: List[Int] = List(1, 7, 3, 4, 2, 2)
scala> 10+:list2:+2
res106: List[Int] = List(10, 1, 7, 3, 4, 2, 2)
scala> list2.sorted
res107: List[Int] = List(1, 2, 3, 4, 7)
NOTE: IF YOU WANT UPDATE BASE WE SHOULD FOLLOW THE BELOW
scala> list2.sorted
res109: List[Int] = List(1, 2, 2, 3, 4, 5, 7)
scala> val list2=List(1, 7, 3, 4, 2, 2):+6
list2: List[Int] = List(1, 7, 3, 4, 2, 2, 6)
scala> list2.sorted
res110: List[Int] = List(1, 2, 2, 3, 4, 6, 7)
ERRORS:
-------
scala> val list2= list2:+2
<console>:12: error: recursive value list2 needs type
val list2= list2:+2
^
scala> val list2= val list2:+2
<console>:1: error: illegal start of simple expression
val list2= val list2:+2
^
scala> val list2=list2:+2
<console>:12: error: recursive value list2 needs type
val list2=list2:+2
---------------------------------
append one list to another list (:::)
----------------------------------
scala> list2:::list3
res21: List[Int] = List(5, 2, 6, 5, 2, 6)
-----------------------------------------
comparision of two lists (if correct return true)
-----------------------------------------
scala> list2 == list3
res35: Boolean = true
scala> val list= List(5, 2, 6, 5, 2, 6)
list: List[Int] = List(5, 2, 6, 5, 2, 6)
scala> list == list3
res37: Boolean = false
scala> list.distinct
res39: List[Int] = List(5, 2, 6)
---------------------------------------------------
cut the data from list using DROP COMMAND
---------------------------------------------------
scala> val later=List('a','v','s','e')
later: List[Char] = List(a, v, s, e)
scala> leter.drop 2
<console>:1: error: ';' expected but integer literal found.
leter.drop 2
^
scala> leter drop 2
<console>:12: error: not found: value leter
leter drop 2
^
scala> leter.drop 2
<console>:1: error: ';' expected but integer literal found.
leter.drop 2
^
scala> leter.
| drop
<console>:12: error: not found: value leter
leter.
^
scala> later drop 2
res43: List[Char] = List(s, e)
scala> later drop 1
res45: List[Char] = List(v, s, e)
scala> later drop 0
res47: List[Char] = List(a, v, s, e)
----------------------------------------
filter the data by using filter command
-----------------------------------------
scala> later drop 0
res47: List[Char] = List(a, v, s, e)
scala> later filter(_>5)
res48: List[Char] = List(a, v, s, e)
scala> list3 filter(_>5)
res49: List[Int] = List(6)
-------------------------------------------
flatten operator
------------------------------------------
scala> List(list2,list3).flatten
res50: List[Int] = List(5, 2, 6, 5, 2, 6)
scala> List(list2,list).flatten
res51: List[Int] = List(5, 2, 6, 5, 2, 6, 5, 2, 6)
scala> List(list2,later).flatten
res52: List[AnyVal] = List(5, 2, 6, a, v, s, e)
--------------------------------------------------
PARTITION THE GIVEN LIST
--------------------------------------------------
scala> list
res57: List[Int] = List(5, 2, 6, 5, 2, 6)
scala> list partition(_<2)
res58: (List[Int], List[Int]) = (List(),List(5, 2, 6, 5, 2, 6))
scala> list partition(_>2)
res59: (List[Int], List[Int]) = (List(5, 6, 5, 6),List(2, 2))
scala> list partition(_>3)
res60: (List[Int], List[Int]) = (List(5, 6, 5, 6),List(2, 2))
scala> list partition(_>4)
res61: (List[Int], List[Int]) = (List(5, 6, 5, 6),List(2, 2))
------------------------------------------------------
TO REVERSE THE GIVEN LIST
-------------------------------------------------------
scala> list.reverse
res62: List[Int] = List(6, 2, 5, 6, 2, 5)
scala> list2.reverse
res63: List[Int] = List(6, 2, 5)
scala> list1.reverse
<console>:12: error: not found: value list1
list1.reverse
^
scala> list3.reverse
res65: List[Int] = List(6, 2, 5)
-------------------------------------------------
SLICE(CUT) THE GIVEN LIST BY USING SLICE COMMAND
--------------------------------------------------
scala> list
res67: List[Int] = List(5, 2, 6, 5, 2, 6)
scala> list slice(1,3)
res66: List[Int] = List(2, 6)
scala> val sli=list slice(1,3)
sli: List[Int] = List(2, 6)
scala> sli
res68: List[Int] = List(2, 6)
note: it first position is called one not zero.
----------------------------------------------------
sorting the list by using sort and sortBy
-------------------------------------------------------
sort the integer list by usind <.sorted> command (by the list of natural values)
------------------------------------------------
scala> list.sorted
res74: List[Int] = List(2, 2, 5, 5, 6, 6)
sort the char list by size using < sortedBy> command don't give the .<dot>
-----------------------------------------------------------------------
scala> List("apple", "to") sortBy(_.size)
res71: List[String] = List(to, apple)
-----------------------------------------------------------------------
split the given list by using splitAt <position>/<int>
----------------------------------------------------------------------
scala> list
res83: List[Int] = List(5, 2, 6, 5, 2, 6)
scala> list splitAt2
<console>:13: error: value splitAt2 is not a member of List[Int]
list splitAt2
^
scala> list splitAt 2
res78: (List[Int], List[Int]) = (List(5, 2),List(6, 5, 2, 6))
scala> list splitAt 3
res79: (List[Int], List[Int]) = (List(5, 2, 6),List(5, 2, 6))
scala> list splitAt 4
res80: (List[Int], List[Int]) = (List(5, 2, 6, 5),List(2, 6))
scala> list splitAt 5
res81: (List[Int], List[Int]) = (List(5, 2, 6, 5, 2),List(6))
scala> list splitAt 6
res82: (List[Int], List[Int]) = (List(5, 2, 6, 5, 2, 6),List())
--------------------------------------------------------------------
Extracts the n elmenents by using command:take <int/value> command
--------------------------------------------------------------------
syntax: list take <int/value>
------
scala> list
res83: List[Int] = List(5, 2, 6, 5, 2, 6)
scala> list taken 3
<console>:13: error: value taken is not a member of List[Int]
list taken 3
^
scala> list take 3
res85: List[Int] = List(5, 2, 6)
scala> list take 6
res86: List[Int] = List(5, 2, 6, 5, 2, 6)
scala> list take 4
res87: List[Int] = List(5, 2, 6, 5)
----------------------------------------------------------------------
combines two list in tuple point of view by using zip command
----------------------------------------------------------------------
syntax: <list1> zip <list2>
-------
scala> list2
res91: List[Int] = List(5, 2, 6)
scala> list2 zip list3
res90: List[(Int, Int)] = List((5,5), (2,2), (6,6))
scala> list3
res92: List[Int] = List(5, 2, 6)
scala> List(1, 2) zip List("a", "b")
res88: List[(Int, String)] = List((1,a), (2,b))
scala> val list4=List(1, 2) zip List("a", "b")
list4: List[(Int, String)] = List((1,a), (2,b))
scala> list4
res89: List[(Int, String)] = List((1,a), (2,b))
scala> li
res2: List[Int] = List(1, 2, 3)
scala> val lis=List(5,6)
lis: List[Int] = List(5, 6)
scala> li zip lis
res4: List[(Int, Int)] = List((1,5), (2,6))
note:in zip function work as one to one mapping only if it has any other variables are there it's leav that.
-----------------------------------------------------------------------
Drop some values in our list by using
------------------------------------------------------------------------
scala> list2
res112: List[Int] = List(1, 2, 2, 3, 4, 6, 7)
scala> suffix
res113: List[Int] = List(4, 6, 7)
scala> val drop = list2 dropRight 2
drop: List[Int] = List(1, 2, 2, 3, 4)
scala> list2
res114: List[Int] = List(1, 2, 2, 3, 4, 6, 7)
scala> drop
res115: List[Int] = List(1, 2, 2, 3, 4)
Errors:
-------
scala> val drop = list2 dropmid 2
<console>:12: error: value dropmid is not a member of List[Int]
val drop = list2 dropmid 2
^
scala> val drop = list2 dropleft 2
<console>:12: error: value dropleft is not a member of List[Int]
val drop = list2 dropleft 2
^
scala> val drop = list2 dropstart 2
<console>:12: error: value dropstart is not a member of List[Int]
val drop = list2 dropstart 2
---------------------------------------------------------------------
takeRight command for taking the right side values in our list
------------------------------------------------------------------------
synatax: <list> <takeRight> <value/int>
-----------
NOTE: In takeRight command take the values in our list based on our list
----- so you can check what is what data you have in list
perhaps you are sorted with out update the original list.it will perform
based on original list.
scala> list2
res108: List[Int] = List(1, 7, 3, 4, 2, 2, 5)
scala> list2.sorted
res109: List[Int] = List(1, 2, 2, 3, 4, 5, 7)
scala> val list2=List(1, 7, 3, 4, 2, 2):+6
list2: List[Int] = List(1, 7, 3, 4, 2, 2, 6)
scala> list2.sorted
res110: List[Int] = List(1, 2, 2, 3, 4, 6, 7)
scala> val suffix = list2 takeRight 3 *** because original list not modified**
suffix: List[Int] = List(2, 2, 6)
scala> list2
res111: List[Int] = List(1, 7, 3, 4, 2, 2, 6) **base/original list ****
scala> list2
res112: List[Int] = List(1, 2, 2, 3, 4, 6, 7)
scala> val suffix = list2 takeRight 3
suffix: List[Int] = List(4, 6, 7)
scala> suffix
res113: List[Int] = List(4, 6, 7)
----------------------------------------------------------
MAP
-------------------------------------------------------------
note:zip is
scala> val list2= List(1, 7, 3, 4, 2, 2, 6)
list2: List[Int] = List(1, 7, 3, 4, 2, 2, 6)
scala> val list4=List(1, 2) zip List("a", "b")
list4: List[(Int, String)] = List((1,a), (2,b))
scala> val list= List(5, 2, 6, 5, 2, 6)
list: List[Int] = List(5, 2, 6, 5, 2, 6)
scala> val list3= List(1, 7, 3)
list3: List[Int] = List(1, 7, 3)
scala> val list4= List(1, 7, 3,9,9,10)
list4: List[Int] = List(1, 7, 3, 9, 9, 10)
scala> val listchar = List("7hills","hi","how","are","you")
listchar: List[String] = List(7hills, hi, how, are, you)
scala> val list6=List(8,10,5)
list6: List[Int] = List(8, 10, 5)
MAPPING OPERATION
------------------
COLLECT:
--------
Description
------------
Transforms each element using a partial function, retaining applicable elements
scala> val list6=List(8,10,5)
list6: List[Int] = List(8, 10, 5)
scala> val mapc= List(0, 1, 0) collect {case 1 => "ok"}
mapc: List[String] = List(ok)
scala> mapc
res0: List[String] = List(ok)
scala> val mapc= List4 collect {case 1 => "ok" //Does not work list4
| case 2=> "this value is 7" you should give complete
| case 4=>"this value is 9"} list of values
<console>:11: error: not found: value List4
val mapc= List4 collect {case 1 => "ok"
^
scala> val mapc= List4.collect {case 1 => "ok"
| }
<console>:11: error: not found: value List4
val mapc= List4.collect {case 1 => "ok"
^
scala> val mapc= List(1, 7, 3,9,9,10) collect {case 1 => "this is index 1"
| case 2=> "this value is 7" //here return string type
| case 4=>"this value is 9"} List[String] = List(this is index 1)
mapc: List[String] = List(this is index 1)
scala> val mapc= List(1, 7, 3,9,9,10) collect {case 1 => println("this is index 1")
| case 2=> println("this value is 7") //here println convert unit() type
| case 4=> println("this value is 9")} List[Unit] = List(())
this is index 1
mapc: List[Unit] = List(())
------------
flatmap
---------
scala> list.flatMap
final override def flatMap[B, That](f: Int =>scala.collection.GenTraversableOnce[B])(implicit bf: scala.collection.generic.CanBuildFrom[List[Int],B,That]): That
def flatMap[B](f: Int => scala.collection.GenTraversableOnce[B]): scala.collection.TraversableOnce[B]
scala> List("milk,tea") flatMap (_.split('|')
| )
res5: List[String] = List(milk,tea)
scala> val mapfla=List("milk,tea") flatMap (_.split('|'))
mapfla: List[String] = List(milk,tea)
scala> mapfla
res6: List[String] = List(milk,tea)
the flatMap example uses the String.split() method to convert pipe-delimited text into a list of strings. Specifically this is the java.lang.String.split() method, and it returns a Java array, not a list.
Fortunately(adrustam), Scala converts Java arrays to its own type, Array, which extends Iterable. Because List is also a subtype of Iterable, and the flatMap method is defined at the Iterable level,
a list of string arrays can be safely flattened into a list of strings.
scala> def flatten(ls: List[Any]): List[Any] = ls flatMap {
| case i: List[_] => flatten(i)
| case e => List(e)
| }
flatten: (ls: List[Any])List[Any]
scala> val k = List(1, List(2, 3), List(List(List(List(4)), List(5)), List(6, 7)), 8)
k: List[Any] = List(1, List(2, 3), List(List(List(List(4)), List(5)), List(6, 7)), 8)
scala> flatten(k)
res26: List[Any] = List(1, 2, 3, 4, 5, 6, 7, 8)
------------
map
-------------
Desription: Transforms each element using the given function
-----------
scala> list.map<press TAB>
map mapConserve
scala> list.map<press TAB>
final override def map[B, That](f: Int => B)(implicit bf: scala.collection.generic.CanBuildFrom[List[Int],B,That]): That
def map[B](f: Int => B): scala.collection.TraversableOnce[B]
scala> List("milk","tea") map (_.toUpperCase)
res7: List[String] = List(MILK, TEA)
----------------
reduce operation
-----------------
SCALA REDUCE OPERATIONS ARE FOLLWS
1) MATH OPERATIONS
2)BOOLEAN OPERATIONS
3) HIGHOREDER OPERATIONS
-----------------
MATH OPERATIONS
----------------
max,min,sum,product
scala> list
res13: List[Int] = List(5, 2, 6, 5, 2, 6)
scala> list.max
res15: Int = 6
scala> list.min
res16: Int = 2
scala> list.sum
res17: Int = 26
scala> list.product
res18: Int = 3600
---------------------
BOOLEAN OPERATOR
--------------------
IF CONDATION/CHECKING VALUE IS THERE IT DISPLAYS TRUE
<list> contains <value> ,endswith,exists,forall,startwith
contains:Checks if the list contains this element
scala> list4 contains 6
res11: Boolean = false
endsWith:Checks if the list ends with a given list
scala> list4 endsWith list4(10)
<console>:13: error: type mismatch;
found : Int
required: scala.collection.GenSeq[?]
list4 endsWith list4(10)
^
scala> list4 endsWith List(9,10)
res10: Boolean = true
exists:Checks if a predicate holds true for at least one element in the list.
scala> list4 exists (_<0)
res14: Boolean = false
scala> list4 exists (_<10)
res12: Boolean = true
forall:Checks if a predicate holds true for every element in the list.
------
scala> list4
res21: List[Int] = List(1, 7, 3, 9, 9, 10)
scala> list4 forall (_<11)
res22: Boolean = true
scala> list4 forall (_<21)
res23: Boolean = true
scala> list4 forall (_<2)
res24: Boolean = false
scala> list4 forall (_<9)
res15: Boolean = false
scala> list4 forall (_<25)
res16: Boolean = true
scala> list4 forall (_<2,5)
<console>:13: error: too many arguments for method forall: (p: Int => Boolean)Boolean
list4 forall (_<2,5)
^
scala> list4 forall (_<2 5)
<console>:1: error: ')' expected but integer literal found.
list4 forall (_<2 5)
^
scala> list4 forall (_<10)
res18: Boolean = false
scala> list4 forall (_<15)
res19: Boolean = true
scala> list4 forall (_<78)
res20: Boolean = true
StartWith:Tests whether the list starts with a given list.
----------
scala> list4 startsWith List(1)
res26: Boolean = true
scala> list4 startsWith List(8)
res27: Boolean = false
scala> list4 startsWith list4 //internally calls first element to other list 1st element
res29: Boolean = true
Error: here Genseq[] meens you should type List
-----
scala> list4 startsWith list(8)
<console>:14: error: type mismatch;
found : Int
required: scala.collection.GenSeq[?]
list4 startsWith list(8)
-----------------------------------------------------
converting collections
-----------------------------------------------------
mkString; Renders a collection to a Set using the given delimiters
scala> listchar
res30: List[String] = List(7hills, hi, how, are, you)
scala> val listchar2=listchar.mkString("|")
listchar2: String = 7hills|hi|how|are|you
toBuffer; Converts an immutable collection to a mutable one.
scala> List('f', 't').toBuffer
res37: scala.collection.mutable.Buffer[Char] = ArrayBuffer(f, t)
scala> listchar.toBuffer
res38: scala.collection.mutable.Buffer[String] = ArrayBuffer(7hills, hi, how, are, you)
toList:Converts a collection to a List.
------
scala> val map=Map(1->"hi",2->"hello")
map: scala.collection.immutable.Map[Int,String] = Map(1 -> hi, 2 -> hello)
scala> map.toList
res45: List[(Int, String)] = List((1,hi), (2,hello))
toMap;converts a collection to a Map
-----
scala> val maplist=List(1->"hi",2->"hello")
maplist: List[(Int, String)] = List((1,hi), (2,hello))
scala> maplist.toMap
res43: scala.collection.immutable.Map[Int,String] = Map(1 -> hi, 2 -> hello)
scala> maplist
res44: List[(Int, String)] = List((1,hi), (2,hello))
scala> val cmaplist=maplist.toMap
cmaplist: scala.collection.immutable.Map[Int,String] = Map(1 -> hi, 2 -> hello)
toSet:Converts a collection to a Set.
-----
scala> map.toSet
res46: scala.collection.immutable.Set[(Int, String)] = Set((1,hi), (2,hello))
scala> list4.toSet
res47: scala.collection.immutable.Set[Int] = Set(10, 1, 9, 7, 3)
toString: Renders a collection to a String, including the collection’s type.
--------
scala> list4
res50: List[Int] = List(1, 7, 3, 9, 9, 10)
scala> list4.toString
res51: String = List(1, 7, 3, 9, 9, 10)
scala> map
res49: scala.collection.immutable.Map[Int,String] = Map(1 -> hi, 2 -> hello)
scala> map.toString
res48: String = Map(1 -> hi, 2 -> hello)
-----------------------------------------------------------
pattern matching with collection
-------------------------------------------------------------
scala> val colpatern=list4.head match{
| case x if(x == 1)=> "head value is 1"
| case x if(x == 9)=> " middle value"
| }
colpatern: String = head value is 1
scala> val colpatern=list4 match{
| case List(9,9) => "not "
| case List(1, 7, 3, 9, 9, 10) => "not found & error"
| }
colpatern: String = not found & error
DOUBT: HOW TO CHECK EACH VALUE THROUGH MATCHING PATTERN BY THE SINGLE VALUE.
*******************************************************************************
MORE COLLECTIONS
*******************************
We’ll start with mutable collections, which probably can be considered ubiquitous be- cause more languages support them than they do immutable collections. Then we’ll move on to arrays, streams, and other collections
---------------------------------------
Mutable Collections
---------------------------------------
Mutable collection types
--------------------------
collection.mutable.Buffer:
--------------------------
scala> val m = Map("AAPL" -> 597, "MSFT" -> 40)
m: scala.collection.immutable.Map[String,Int] = Map(AAPL -> 597, MSFT -> 40)
scala> val b = m.toBuffer
b: scala.collection.mutable.Buffer[(String, Int)] = ArrayBuffer((AAPL,597), (MSFT,40))
scala> b trimStart 1
scala> b
res55: scala.collection.mutable.Buffer[(String, Int)] = ArrayBuffer((MSFT,40))
scala> m
res56: scala.collection.immutable.Map[String,Int] = Map(AAPL -> 597, MSFT -> 40)
scala> b += ("GOOG" -> 521)
res57: b.type = ArrayBuffer((MSFT,40), (GOOG,521))
scala> val n = b.toMap
n: scala.collection.immutable.Map[String,Int] = Map(MSFT -> 40, GOOG -> 521)
--------------------
Arrays
--------------------
scala> val colors = Array("red", "green", "blue")
colors: Array[String] = Array(red, green, blue)
scala> colors(0) = "purple"
scala> colors
res59: Array[String] = Array(purple, green, blue)
scala> colors :+ "red"
res60: Array[String] = Array(purple, green, blue, red)
scala> colmut trimStart 1
scala> colmut
res74: scala.collection.mutable.Buffer[String] = ArrayBuffer(green, blue)
----------------------------------
Seq and Sequences
----------------------------------
scala> val inks = Seq('C','M','Y','K')
inks: Seq[Char] = List(C, M, Y, K)
scala> inks(0)
res75: Char = C
scala> inks(1)
res76: Char = M
scala> inks(4)
java.lang.IndexOutOfBoundsException: 4
at scala.collection.LinearSeqOptimized$class.apply(LinearSeqOptimized.scala:65)
at scala.collection.immutable.List.apply(List.scala:84)
... 32 elided
scala> inks(3)
res78: Char = K
-------------------------
Using Collection Builder
-----------------------------
scala> val b = Set.newBuilder[Char]
b: scala.collection.mutable.Builder[Char,scala.collection.immutable.Set[Char]] = scala.collection.mutable.SetBuilder@6debcae2
scala> b += 'h'
res0: b.type = scala.collection.mutable.SetBuilder@6debcae2
scala> b ++= List('e', 'l', 'l', 'o')
res1: b.type = scala.collection.mutable.SetBuilder@6debcae2
scala> b
res2: scala.collection.mutable.Builder[Char,scala.collection.immutable.Set[Char]] = scala.collection.mutable.SetBuilder@6debcae2
scala> val helloSet = b.result
helloSet: scala.collection.immutable.Set[Char] = Set(h, e, l, o)
scala> b.result
res3: scala.collection.immutable.Set[Char] = Set(h, e, l, o)
------------------------------
ARRAY
------------------------------
scala> def inc(i: Int): Stream[Int] = Stream.cons(i, inc(i+1))
inc: (i: Int)Stream[Int]
scala> val s = inc(1)
s: Stream[Int] = Stream(1, ?)
*****************************************************************************
Object-Oriented Scala
--------------------------------------------------------------------------------------------------------------------
Classes
---------------------------------------------------------------------------------------------------------------------
Design the user class in 2 ways
--------------------------
1)scala> class hills
defined class hills
scala> val u= new hills
u: hills = hills@77681ce4
2)scala> class username{
val name="7hills"
val pass="777"
def greet:String=s"hello $name"
override def toString=s"username($name)"
}
defined class username
scala> val u = new username()
u: username = username(7hills)
scala> val u = new username
u: username = username(7hills)
scala> println(u.greet)
hello 7hills
scala> class hills{
| val nn=10
| def greet:String = s"this no is $nn"
| override def toString=s"hills($nn)"
| }
defined class hills
scala> val u = new hills()
u: hills = hills(10)
scala> u.greet
res1: String = this no is 10
scala> println(u.greet)
this no is 10
------------------------------------------
CREATING A CLASS FOR USER AUTHENTICATION
------------------------------------------
(1) METHOD -1
==============
class username(pass:String) {
val name="7hills"
def greet:String=s"your name $name"
println("enter your password")
if(pass=="7777"){
println("hi" + name + "your")
println("successfully login")
}
else
println("you entered wrong password")
}
output:
-------
CREATE OBJECT ON CLASS
-----------------------
scala> val u= new username("7777")
enter your password
hi7hillsyour
successfully login
u: username = username@1b96f15e
output:
-------
scala> val u= new username("8888")
enter your password
you entered wrong password
u: username = username@1f22d7e4
class username {
val name="7hills"
def greet:String=s"your name $name"
def passvalidat(pass:String) = {
println("enter your password")
if(pass=="7777"){
println("hi" + name + "your")
println("successfully login")
}
else
println("you entered wrong password")
}
}
output:
-------
scala> u.passvalidat("7777")
hi7hillsyour
successfully login
scala> u.passvalidat("777")
you entered wrong password
scala> val u=new username()
u: username = username@addefe5
scala>
scala> u.passvalidat("7777")
enter your password
hi7hillsyour
successfully login
--------------------
class username {
val name="7hills"
def greet:String=s"your name $name"
def passvalidat(pass:String) = {
println("enter your password")
if(pass=="7777"){
println("hi" + name + "your")
println("successfully login")
}
else
println("you entered wrong password")
}
}
(2) METHOD - 2
================
class usr(name:String) {
val para = name
para + "the val is"
def passvali = {
if(para == "555")
para + "your are sucessfully login"
else
println(" you entered wrong password")
}
}
scala> val u= new usr("896") //create the object on class
u: usr = usr@3956ae6e
scala> u.passvali //call the defined function
you entered wrong password
res29: Any = ()
NOTE: YOU SHOULD CALL FIRST IN PARA VARIABLE
-----
scala> u.para // call the name parameter
res30: String = 896
scala> val u= new usr("555") //
u: usr = usr@406b6988
scala> u.para
res31: String = 555
scala> u.passvali
res32: Any = 555your are sucessfully login
(3) METHOD - 2
================
class usr(name:String) {
val para = name
para + "the val is"
def passvali = {
if(para == "555")
para + "your are sucessfully login"
else
println(" you entered wrong password")
}
}
class calling extends usr{override def passvali="your name" + super.para + super.passvali}
-------------------------------------------------------------
INNER CLASS CALLING (or) call one class to another class
-----------------------------------------------------------------
BASE CLASS:
------------
scala> class usr(name:String) {
| val para = name
| para + "the val is"
| def passvali = {
| if(para == "555")
| para + "your are sucessfully login"
| else
| println(" you entered wrong password")
| }
| }
defined class usr
----------
CHILD CLASS
-----------
scala> class calling extends usr("7777"){override def passvali="your name" + super.passvali}
defined class calling
scala> val cals = new calling().passvali
you entered wrong password
cals: String = your name()
scala> class calling extends usr("555"){override def passvali="your name" + super.passvali}
defined class calling
scala> val cals = new calling().passvali
cals: String = your name555your are sucessfully login
ERRORS:
-------
scala> class calling extends use{override def passvali="your name" + super.para + super.passvali}
<console>:11: error: not found: type use
class calling extends use{override def passvali="your name" + super.para + super.passvali}
^
<console>:11: error: value para is not a member of AnyRef
class calling extends use{override def passvali="your name" + super.para + super.passvali}
^
<console>:11: error: value passvali is not a member of AnyRef
class calling extends use{override def passvali="your name" + super.para + super.passvali}
^
scala> class calling extends usr{override def passvali="your name" + super.para + super.passvali}
<console>:13: error: not enough arguments for constructor usr: (name: String)usr.
Unspecified value parameter name.
class calling extends usr{override def passvali="your name" + super.para + super.passvali}
^
scala> class calling extends usr("7777"){override def passvali="your name" + super.para + super.passvali}
<console>:13: error: super may not be used on value para
class calling extends usr("7777"){override def passvali="your name" + super.para + super.passvali}
^
------------
SOLUTION
-----------
1) YOU SHOULD PASS THE PARAMETERS IN BASE WHEN CALLING THROUG CHILD CLASS ALSO
2)YOU SHOULD NOT CALL VARIABLES/VALUES THROUGH SUPER KEYWORD.WE KNOW SUPER KEYWORD USING ONLY CALLS THE PARENT/BASE CLASS METHODS/FUNCTIONS.
ANOTHER EXAMPLE
----------------
scala> class A { def hi = "Hello from A"
| override def toString = getClass.getName }
defined class A
scala> class B extends A
defined class B
scala> class C extend B{ override def hi = "hi C -> " + super.hi }
scala> val hiA= new A().hi
hiA: String = Hello from A
OR
scala> hiA
res35: String = Hello from A
scala> val hiB= new B().hi
hiB: String = Hello from A
scala> val hiC= new C().hi
hiC: String = hi C -> Hello from A
Let’s try creating a class with both value and variable fields as parameters:
----------------------------------------------------------------------------
scala> class Car(val make: String, var reserved: Boolean) {
| def reserve(r: Boolean): Unit = { reserved = r } }
defined class Car
scala> val t = new Car("Toyota", false)
t: Car = Car@e283976
scala> t.reserve(true)
scala> println(s"My ${t.make} is now reserved? ${t.reserved}")
My Toyota is now reserved? true
scala> t.reserve(false)
scala> println(s"My ${t.make} is now reserved? ${t.reserved}")
My Toyota is now reserved? false
NOTE:WE CAN NOT CHANGE THE make value because it is val.
Class with Input Parameters and Default Values
-----------------------------------------------
---------------------------------------------------------
ABSTRACT CLASS
---------------------------------------------------------
scala> abstract class car{
| val year:Int
| val automatic:Boolean = true
| def Color:String
| }
defined class car
scala> new car
<console>:13: error: class car is abstract; cannot be instantiated
new car
^
scala> class red(val year:Int) extends car
<console>:12: error: class red needs to be abstract, since method Color in class car of type => String is not defined
class red(val year:Int) extends car
^
scala> class red(val year:Int) extends car{
| val Color = "red"
| }
defined class red
scala> val m=new red(2017)
m: red = red@3ada9e37
scala> m.Color
res1: String = red
scala> m.year
res2: Int = 2017
scala> m.automatic
res3: Boolean = true
-------------------------------------------------------------------------
Anonymous Classes (OR) write the function operation when call the method
-----------------------------------------------------------------------
We have a class, Listening, that can register a Listener and trigger it later as necessary. Instead of instantiating the anonymous class on one line and passing it to the registration function on another, we can combine these into a single step of defining the anonymous class as part of the method invocation.
scala> abstract class Listener { def trigger }
defined class Listener
scala> class Listening { var listener: Listener = null
| def register(l: Listener) {listener = l }
| def sendNotification() { listener.trigger }
| }
defined class Listening
scala> val notification = new Listening()
notification: Listening = Listening@4671e53b
scala> notification.register(new Listener {
| def trigger { println(s"Trigger at ${new java.util.Date}") }
| })
scala> notification.sendNotification
Trigger at Sat Sep 02 18:44:10 IST 2017
Error:
---------
<console>:14: error: reassignment to val
listenerv=l}
^
note:you can not assign the value to val type variable because it is immutable
-------------------------------------------------------
Overloaded Methods
------------------------------
Here is an example of overloaded methods, where the methods share the same name but take different parameters. In the example the second overloaded method calls the first after modifying its input parameters appropriately:
(1)
scala> class Printer(msg: String) {
| def print(s:String):Unit = println(s"$msg: $s")
| def print(l:Seq[String]):Unit = print(l.mkString(","))
| }
defined class Printer
scala> val p = new Printer("hi this").print("this"::"is"::"fizzy"::Nil)
hi this: this,is,fizzy
p: Unit = ()
(2)
scala> class Printer(msg: String) {
| def print(s:String):Unit = println(s"$msg: $s")
| def print(l:Seq[String]):Unit = println(l.mkString(","))
| }
defined class Printer
scala> val p = new Printer("hi this").print("this"::"is"::"fizzy"::Nil)
this,is,fizzy
p: Unit = ()
note; 1st method use println only but we use two methods in println statement 2nd method only use print statement
Errors
------
scala> val p = new Printer("hi this").print("this"::"is"::"fizzy")
<console>:12: error: value :: is not a member of String
val p = new Printer("hi this").print("this"::"is"::"fizzy")
^
sol:end with Nil
---------------------
Apply Methods
----------------------
scala> class Multiplier(factor: Int) {
| def applay(f:Int) = factor * f}
defined class Multiplier
scala> val mul=new Multiplier(5)
mul: Multiplier = Multiplier@79d7035
scala> mul
res11: Multiplier = Multiplier@79d7035
scala> mul.applay(5)
res12: Int = 25
scala> val mul=new Multiplier(6)
mul: Multiplier = Multiplier@25791d40
scala> val mu=new Multiplier(6).applay(5)
mu: Int = 30
------------------------------------------
Accessing Packaged Classes
--------------------------------------
scala> import collection.immutable._
import collection.immutable._
scala> import collection.mutable._
import collection.mutable._
scala> val b = new ArrayBuffer[String]
b: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer()
scala> b += "hello"
res15: b.type = ArrayBuffer(hello)
scala> val q = new Queue[Int]
q: scala.collection.mutable.Queue[Int] = Queue()
scala> q += 10
res16: q.type = Queue(10)
scala> val l = new [Int]
<console>:18: error: class List is abstract; cannot be instantiated
val l = new List[Int]
^
scala> val l = List[Int]
<console>:18: error: missing argument list for method apply in object List
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `apply _` or `apply(_)` instead of `apply`.
val l = List[Int]
^
scala> val q = new Queue[Int]
q: scala.collection.mutable.Queue[Int] = Queue()
scala> q.enqueue(3, 4, 5)
scala> q.dequeue(3)
<console>:20: error: too many arguments for method dequeue: ()Int
q.dequeue(3)
^
scala> q.dequeue
res19: Int = 3
scala> q
res20: scala.collection.mutable.Queue[Int] = Queue(4, 5)
---------------------------------------
IMPORT ALIIAS
-------------------------------------
syntax:
import <package>.{<original name>=><alias>}
scala> import collection.mutable.{Map=>MutMap}
import collection.mutable.{Map=>MutMap}
scala> val m1 = Map(1 -> 2)
m1: scala.collection.mutable.Map[Int,Int] = Map(1 -> 2)
scala> val m2 = MutMap(2 -> 3)
m2: scala.collection.mutable.Map[Int,Int] = Map(2 -> 3)
scala> m2.remove(1)
res21: Option[Int] = None
scala> m2.remove(2)
res22: Option[Int] = Some(3)
scala> val m1 = Map(1 -> 2)
m1: scala.collection.mutable.Map[Int,Int] = Map(1 -> 2)
scala> val m2 = MutMap(2 -> 3)
m2: scala.collection.mutable.Map[Int,Int] = Map(2 -> 3)
scala> m2
res24: scala.collection.mutable.Map[Int,Int] = Map(2 -> 3)
scala> m1
res25: scala.collection.mutable.Map[Int,Int] = Map(1 -> 2)
scala> m1.remove(1)
res26: Option[Int] = Some(2)
scala> m1
res27: scala.collection.mutable.Map[Int,Int] = Map()
With our aliased collection “MutMap” (short for “mutable,” not “mutt”!) we can create both mutable and immutable maps by name, without specifying their packages
-------------
Objects
-------------
Syntax: Defining an Object
object <identifier>/objectname [extends <identifier>] [{ fields, methods, and classes }]
scala> object Hello { println("in Hello"); def hi = "hi" }
defined object Hello
scala> Hello
in Hello
res34: Hello.type = Hello$@557665b2
scala> Hello.hi
res35: String = hi
scala> hi
<console>:20: error: not found: value hi
hi
^
scala> Hello
res37: Hello.type = Hello$@557665b2
The println at the top level of the object is invoked at instantiation/initialization, which only occurs when it is accessed for the first time. Repeating the call to the object’s “hi” method reused the same global instance so there was no additional initialization
------------------------------------
Case Classes
-----------------------------------
scala> case class Character(name:String,isthief:Boolean)
defined class Character
scala> val h=new Character("7hills",true)
h: Character = Character(7hills,true)
scala> val u=new Character("hills",false)
u: Character = Character(hills,false)
scala> h match{
| case Character("x",true) => s"$x is theif"
| case Character("x",true) => s"$x is nottheif"
| }
<console>:16: error: not found: value x
case Character("x",true) => s"$x is theif"
^
<console>:17: error: not found: value x
case Character("x",true) => s"$x is nottheif"
^
scala> h match{
| case Character(x,true) => s"$x is nottheif"
| case Character(x,false) => s"$x is nottheif"
| }
res1: String = 7hills is nottheif
scala> h
res2: Character = Character(7hills,true)
scala> val r = h.Copy(name = "sarvan")
<console>:14: error: value Copy is not a member of Character
val r = h.Copy(name = "sarvan")
^
<console>:14: error: not found: value name
val r = h.Copy(name = "sarvan")
^
scala> val r = h.copy(name = "sarvan")
r: Character = Character(sarvan,true)
scala> r == h
res3: Boolean = false
scala> h match{
| case Character("x",true) => s"$x is theif"
| case Character(x,true) => s"$x is not theif"
| }
<console>:16: error: not found: value x
case Character("x",true) => s"$x is theif"
^
scala> h match{
| case Character(x,true) => s"$x is theif"
| case Character(x,false) => s"$x is not theif"
| }
res5: String = 7hills is theif
scala> r match{
| case Character(x,true) => s"$x is theif"
| case Character(x,false) => s"$x is not theif"
| }
res6: String = sarvan is theif
scala> h
res7: Character = Character(7hills,true)
scala> r
res8: Character = Character(sarvan,true)
scala> val u=new Character("7hills",true)
u: Character = Character(7hills,true)
scala> h == u
res9: Boolean = true
scala> u match{
| case Character(x,true) => s"$x is theif"
| case Character(x,false) => s"$x is not theif"
| }
res10: String = 7hills is theif
scala> val u=new Character("7hills",false)
u: Character = Character(7hills,false)
scala> u == h
res11: Boolean = false
scala> u match{
| case Character(x,true) => s"$x is theif"
| case Character(x,false) => s"$x is not theif"
| }
res12: String = 7hills is not theif
------------------------------------------------------------
Traits
------------------------------------------------------------
A trait is a kind of class that enables multiple inheritance. Classes, case classes, objects, and (yes) traits can all extend no more than one class but can extend multiple traits at the same time. Unlike the other types, however, traits cannot be instantiated
The process of linearization may seem odd, but it’s a useful compromise between the theory of a language supporting multiple inheritance versus the practice of an envi- ronment that doesn’t. It also provides a solid method for determining invocation, because the constructed hierarchy ensures that method handling is decided at compile time and never at runtime
Another benefit of linearization is that you can write traits to override the behavior of a shared parent class
scala> trait Base { override def toString = "Base" }
defined trait Base
scala> trait B extends Base { override def toString = "B->" + super.toString }
defined trait B
scala> trait C extends Base { override def toString = "B->" + super.toString }
defined trait C
scala> class D extends A with B with C { override def toString = "B->" + super.toString }
defined class D
scala> new D()
res14: D = B->B->B->A->Base
ERRoRS
------
scala> class A extends Base { override def toString = "A->" + super.toString }
defined class A
scala> class B extends Base { override def toString = "B->" + super.toString }
defined class B
scala> class C extends Base { override def toString = "C->" + super.toString }
defined class C
scala> class D extends A with B with C{ override def toString = "D>" + super.toString }
<console>:15: error: class B needs to be a trait to be mixed in
class D extends A with B with C{ override def toString = "D>" + super.toString }
^
<console>:15: error: class C needs to be a trait to be mixed in
class D extends A with B with C{ override def toString = "D>" + super.toString }
---------
solution: we only extennds the trait not the class
---------
scala> trait D extends A with B with C { override def toString = "B->" + super.toString }
defined trait D
scala> new D()
<console>:17: error: trait D is abstract; cannot be instantiated
new D()
^
---------
solution: we only instance/intiate(new) on class only but not on traits.so in this situvation
--------- last D extends with trait and it also creat as a class not trait(you sould use class keyword instead on trait
2nd example
----------
scala> class RGBColor(val color: Int) { def hex = f"$color%06X" }
defined class RGBColor
scala> val green = new RGBColor(255 << 8).hex
green: String = 00FF00
scala> trait Opaque extends RGBColor { override def hex = s"${super.hex}FF" }
defined trait Opaque
scala> trait Sheer extends RGBColor { override def hex = s"${super.hex}33" }
defined trait Sheer
------------------------------
Self Types
------------------------------
A self type is added immediately following the opening brace of the trait definition, and includes an identifier, the requested type, and an arrow (=>). A trait with a self type can access fields of that type as if it explicitly extended that type
Syntax: Defining a Self Type
trait ..... { <identifier>: <type> => .... }
scala> class A { def hi = "hi" }
defined class A
scala> trait B { self: A => override def toString = "B: " + hi} //SHOULD BE MENTION self keyword and =>
defined trait B
scala> class C extends B
<console>:13: error: illegal inheritance;
self-type C does not conform to B's selftype B with A
class C extends B
^
scala> class C extends A with B
defined class C
scala> new C()
res15: C = B: hi
1)Our trait B has a self type, adding the requirement that the trait can only ever be mixed into a subtype of the specified type, the A class. …
2) but just to prove it, let’s try defining a class with trait B but without the requested class. No luck.
3)This time, trait B is directly extending its requested type, A, so its self type requirement has been met.
4)When our C class is instantiated, B.toString is invoked, which then invokes A.hi. The B trait is indeed used as a subtype of A here and can invoke one of its methods.
Here is an example of a class extended by a trait with a self type of that class, ensuring that the trait will extend the class:
scala> class A
defined class A
scala> trait B { self: A => }
defined trait B
scala> val a = new A with B
a: A with B = $anon$1@331ff3ac
we created an instance where trait B extended trait A
Let’s experiment with dependency injection by taking a data-oriented class common in most applications, User, and altering its output in new and mysterious ways:
scala> class User(val name: String){
| def suffix =""
| override def toString = s"$name$suffix"
| }
defined class User
scala> trait Attorney { self: User => override def suffix = ", esq." }
defined trait Attorney
scala> trait Wizard { self: User => override def suffix = ", Wizard" }
defined trait Wizard
scala> trait Reverser { override def toString = super.toString.reverse }
defined trait Reverser
scala> val h = new User("Harry P") with Wizard
h: User with Wizard = Harry P, Wizard
scala> val g = new User("Ginny W") with Attorney
g: User with Attorney = Ginny W, esq.
------------------------------------------
Importing Instance Members
------------------------------------------
scala> case class Receipt(id: Int, amount: Double, who: String, title: String)
defined class Receipt
scala> {
| val latteReceipt = Receipt(123, 4.12, "fred", "Medium Latte")
| import latteReceipt._
| println(s"Sold a $title for $amount to $who")
| }
Sold a Medium Latte for 4.12 to fred
scala> import util.Random._ import util.Random._
<console>:1: error: ';' expected but 'import' found.
import util.Random._ import util.Random._
^
scala> import util.Random._
import util.Random._
scala> val letters = alphanumeric.take(20).toList.mkString
letters: String = WoVkuFqZOdP0Snh00FeF
scala> val numbers = shuffle(1 to 20)
numbers: scala.collection.immutable.IndexedSeq[Int] = Vector(5, 8, 10, 18, 14, 11, 9, 16, 12, 2, 4, 7, 19, 17, 15, 13, 1, 3, 20, 6)
********************************************************************
--------------------------------------------------------------
SCALA distinct predefined method
---------------------------------------------------------------
List[Int] = List(10, 20, 3, 6, 35)
distinct
-----------
scala> list.distinct
def distinct: List[Int]
distinct explanation: def -----------> keyword for function
-------------------- distinct -------> that is function name
note: it should return list after function operation is completed that function open with { and colose with }
scala> def distinct = {
| list
| }
distinct: List[Int]
scala> distinct
res13: List[Int] = List(10, 20, 3, 6, 35)
-----------
diff
-----------
scala> list.diff
def diff[B >: Int](that: scala.collection.GenSeq[B]): List[Int]
scala> def apply(n:Int) = { val list2=List(1, 7, 3, 4, 2, 2):+6
| list
| }
apply: (n: Int)List[Int]
scala> def diff[B >: Int](that: scala.collection.GenSeq[B]): List[Int]
| =
| {
| apply(B)
| }
diff: [B >: Int](that: scala.collection.GenSeq[B])List[Int]
***************************************************************************
TO PRINT THE LIST VALUES BY USING NAMEDFUNCTION
****************************************************************************
scala> listbig
res145: List[Int] = List(20, 30, 65, 88, 99, 101, 450, 101, 256, 12, 6, 8, 99, 45, 101)
scala> listbig.tail.size
res143: Int = 14 // it consider only within value position
scala> listbig.tails.size
res144: Int = 15 //it consider Nil position also
scala> def eachret() = {
| for(z <- 1 to listbig.tail.size) <--tail---->
| println("\n z is:"+z + ",listval:"+listbig(z))
| }
eachret: ()Unit
scala> eachret
z is:1,listval:30
z is:2,listval:65
z is:3,listval:88
z is:4,listval:99
z is:5,listval:101
z is:6,listval:450
z is:7,listval:101
z is:8,listval:256
z is:9,listval:12
z is:10,listval:6
z is:11,listval:8
z is:12,listval:99
z is:13,listval:45
z is:14,listval:101
********
ERROR
*******
scala> def eachret() = {
| for(z <- 0 to listbig.tails.size) <---------tails------>
| println("\n z is:"+z + ",listval:"+listbig(z))
| }
eachret: ()Unit
scala> eachret
z is:0,listval:20
z is:1,listval:30
z is:2,listval:65
z is:3,listval:88
z is:4,listval:99
z is:5,listval:101
z is:6,listval:450
z is:7,listval:101
z is:8,listval:256
z is:9,listval:12
z is:10,listval:6
z is:11,listval:8
z is:12,listval:99
z is:13,listval:45
z is:14,listval:101
java.lang.IndexOutOfBoundsException: 15
at scala.collection.LinearSeqOptimized$class.apply(LinearSeqOptimized.scala:65)
at scala.collection.immutable.List.apply(List.scala:84)
at $anonfun$eachret$1.apply$mcVI$sp(<console>:16)
at scala.collection.immutable.Range.foreach$mVc$sp(Range.scala:160)
at .eachret(<console>:15)
... 32 elided
solution
--------
Array Index out of bounds of exception comes when extract value without having that.
************************************************************************
TAKE THE LIST AS A PARAMETER AND DISPLAY TOTAL VALUES WITH POSTIONS BY USING SWITCH STATEMENTS
**************************************************************************
scala> def f(k:Any) = k match {
case k if(k == listbig) => for(z <- 1 to listbig.tail.size)
println("\n z is:"+z + ",listval:"+listbig(z))
case k if(k == list) => list
case _=> println("there is no values")
}
f: (k: Any)Any
scala> f(listbig)
z is:1,listval:30
z is:2,listval:65
z is:3,listval:88
z is:4,listval:99
z is:5,listval:101
z is:6,listval:450
z is:7,listval:101
z is:8,listval:256
z is:9,listval:12
z is:10,listval:6
z is:11,listval:8
z is:12,listval:99
z is:13,listval:45
z is:14,listval:101
res150: Any = ()
scala> f(list)
res151: Any = List(25, 33, 45, 66, 88)
scala> f(list1)
there is no values
res152: Any = ()
ERROR
------
1)
scala> def f(k:Any) = k Match {
| case if(k == listbig) => for(z <- 1 to listbig.tail.size)
<console>:2: error: illegal start of simple pattern
case if(k == listbig) => for(z <- 1 to listbig.tail.size)
^
SOL) case k if(k == listbig)=> for(z <- 1 to listbig.tail.size)
^
2)
scala> def f(k:Any) = k Match {
| case k if(k == listbig) => for(z <- 1 to listbig.tail.size)
| println("\n z is:"+z + ",listval:"+listbig(z))
| case k if(k == list) => list
| case _=> println("there is no values")
| }
<console>:15: error: value Match is not a member of Any
def f(k:Any) = k Match {
^
sol)match not Match
***************************************************************************
TO SEND THE DIFFERENT LIST PARAMETERS AND A VALUE AFTER SEARCHING IT WILL BE DISPLAY WHICH LIST CONTAINS THE VALUE
*****************************************************************************
scala> def f(y:Any,k:Any*) = k match {
| case k if(k == listbig) => for(z <- 1 to listbig.tail.size)
| if(listbig(z) == y)
| println("value found in the :"+listbig)
| case k if(k == list) => for(z <- 1 to list.tail.size)
| if(list(z) == y)
| println("value found in the :"+list)
| case k if(k == list1) => for(z <- 1 to list1.tail.size)
| if(list1(z) == 'y')
| println("value found in the :"+list1)
| case _=> println("there is no values")
| }
f: (y: Any, k: Any*)Unit
***NOTE: HERE IT IS NOT WORKING BECAUSE K:ANY* IS TAKE THE VALUES AS A ARRAY TYPE SO IT IS NOT POSSIBLE TO COMPARE INDIVIDUALLY IN LISTS
scala> def f(k:Any*,y:Any) = k match {
| case k if(k == listbig) => for(z <- 1 to listbig.tail.size)
| if(listbig(z) == y)
| println("value found in the :"+listbig)
| case k if(k == list) => for(z <- 1 to list.tail.size)
| if(list(z) == y)
| println("value found in the :"+list)
| case k if(k == list1) => for(z <- 1 to list1.tail.size)
| if(list1(z) == 'y')
| println("value found in the :"+list1)
| case _=> println("there is no values")
| }
<console>:16: error: *-parameter must come last
def f(k:Any*,y:Any) = k match {
^
============================================================================
CONNNECT THE MYSQL DATABASE USING SCALA AND RETRIVE THE DATA
============================================================================
scala> :paste
// Entering paste mode (ctrl-D to finish)
import java.sql.DriverManager
import java.sql.Connection
object DBConnection {
val driver="com.mysql.jdbc.Driver"
val url = "jdbc:mysql://localhost/7hills"
val username = "root"
val password = "hadoop"
def apply() = new DBConnection
}
class DBConnection{
private val props = Map(
"url" -> DBConnection.url,
"user" -> DBConnection.username,
"pass" -> DBConnection.password )
println(s"Created new connection for " + props("url"))
val connection = DriverManager.getConnection(DBConnection.url, DBConnection.username, DBConnection.password)
DriverManager.registerDriver(new com.mysql.jdbc.Driver ());
val statement = connection.createStatement()
val resultSet = statement.executeQuery("SELECT movieId,title FROM movies")
while(resultSet.next() ) {
val movieId = resultSet.getString("movieId")
val title = resultSet.getString("title")
println("movieId,title="+movieId+","+title)
}
}
ctrl+D
// Exiting paste mode, now interpreting.
import java.sql.DriverManager
import java.sql.Connection
defined object DBConnection
defined class DBConnection
scala> val conn = new DBConnection()
Created new connection for jdbc:mysql://localhost/7hills
movieId,title=1,the hero
conn: DBConnection = DBConnection@42bc14c1
*AFTER ADDING SOME ROWS IN MOVIES TABLE ***
scala> val conn = new DBConnection()
Created new connection for jdbc:mysql://localhost/7hills
movieId,title=1,the hero
movieId,title=2,theLEGEND
movieId,title=3,thetarminator
movieId,title=3,brundavanam
movieId,title=4,kithakithalu
conn: DBConnection = DBConnection@5fc930f0
****println("movieId ="+movieId+":TITLE:"+title)
scala> val conn = new DBConnection()
Created new connection for jdbc:mysql://localhost/7hills
movieId =1:TITLE:the hero
movieId =2:TITLE:theLEGEND
movieId =3:TITLE:thetarminator
movieId =3:TITLE:brundavanam
movieId =4:TITLE:kithakithalu
conn: DBConnection = DBConnection@4bc33720
===============================================================================
Nice Blog
ReplyDeleteGood information
ReplyDeleteSuperb explanation Manjunath.......
ReplyDelete"Please let me know if you’re looking for an author for your site. You have some great posts, and I think I would be a good asset. If you ever want to take some of the load off, I’d like to write some material for your blog in exchange for a link back to mine. Please shoot me an email if interested. Thanks.
ReplyDelete"
Authorized iphone service center in Chennai | iphone service center in chennai | Mobile service center in chennai | Authorized iphone service center in Chennai | iphone service center in chennai