May 13, 2021 Scala
The first few weeks will cover basic syntax and concepts, and then we'll expand on them with more practice.
Some examples are given in the form of interpreter interactions, while others are given in the form of source files.
Installing an interpreter can make it easier to explore problem spaces.
Scala is more than just a better Java. You should learn it with a fresh mind, and you will realize this from these lessons.
Install Scala See: Scala installation and environment configuration
Start with the sbt console that come with it.
$ sbt console[...]
Welcome to Scala version 2.8.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20).
Type in expressions to have them evaluated.Type :help for more information.
scala>
scala> 1 + 1
res0: Int = 2
res0 is the name of a variable that is automatically created by the interpreter and is used to refer to the evaluation of an expression. It is an Int type with a value of 2.
Everything in Scala is an expression.
You can give the result of an expression a name and assign an invaris (val).
scala> val two = 1 + 1
two: Int = 2
You can't change the value of this constant amount.
If you need to modify the binding of this name and result, you can choose to
var
.
scala> var name = "steve"
name: java.lang.String = steve
scala> name = "marius"
name: java.lang.String = marius
You can use def to create functions.
scala> def addOne(m: Int): Int = m + 1
addOne: (m: Int)Int
In Scala, you need to specify a type signature for the function parameters.
scala> val three = addOne(2)
three: Int = 3
If the function does not have arguments, you can not write parentheses.
scala> def three() = 1 + 2
three: ()Int
scala> three()
res2: Int = 3
scala> three
res3: Int = 3
You can create anonymous functions.
scala> (x: Int) => x + 1
res2: (Int) => Int = <function1>
This function is an Int variable named x plus 1.
scala> res2(1)
res3: Int = 2
You can pass anonymous functions or save them as invariable.
scala> val addOne = (x: Int) => x + 1
addOne: (Int) => Int = <function1>
scala> addOne(1)
res4: Int = 2
If your function has many expressions, you can format the code to make it easy to read.
def timesTwo(i: Int): Int = {
println("hello world") i * 2
}
The same is true for anonymous functions.
scala> { i: Int =>
println("hello world")
i * 2
}
res0: (Int) => Int = <function1>
This syntax is often used when passing an anonymous function as an argument.
You can apply one function using the underscore " S cala uses underscores to represent different things in different contexts, which you can often think of as an unnamed magic wildcard. I t represents an anonymous parameter in the context of ' . You can use it this way:
scala> def adder(m: Int, n: Int) = m + n
adder: (m: Int,n: Int)Int
scala> val add2 = adder(2, _:Int)
add2: (Int) => Int = <function1>
scala> add2(3)
res50: Int = 5
You can apply any parameter in the parameter list in part, not just the last one.
Sometimes there is a need to allow others to apply some parameters to your function for a while, and then to apply additional parameters.
For example, a multiplication function needs to select a multiplier in one scene, while another scenario needs to select a multiplier.
scala> def multiply(m: Int)(n: Int): Int = m * n
multiply: (m: Int)(n: Int)Int
You can pass in two parameters directly.
scala> multiply(2)(3)
res0: Int = 6
You can fill in the first argument and partially apply the second.
scala> val timesTwo = multiply(2) _
timesTwo: (Int) => Int = <function1>
scala> timesTwo(3)
res1: Int = 6
You can perform Curryization on any multi-argument function. For example, the previous adder function
The first pass is a plus, returning a function, calling the second function pass, and another number, resulting in a result of and
scala> (adder _).curried
res1: (Int) => (Int) => Int = <function1>
scala> res1(2)
res2: (Int) => Int = <function1>
scala> res2(3)
res3: Int = 5
This is a special syntax that can pass in as many parameters of the same type to a method. For example, to execute String's capitalize function on more than one string, you can write something like this:
def capitalizeAll(args: String*) = {
args.map { arg =>
arg.capitalize
}
}
scala> capitalizeAll("rarity", "applejack")
res2: Seq[String] = ArrayBuffer(Rarity, Applejack)
scala> class Calculator {
| val brand: String = "HP"
| def add(m: Int, n: Int): Int = m + n
| }
defined class Calculator
scala> val calc = new Calculator
calc: Calculator = Calculator@e75a11
scala> calc.add(1, 2)
res1: Int = 3
scala> calc.brand
res2: String = "HP"
The example above shows how to define methods in classes with def and field values with val. A method is a function that can access the state of a class.
Constructors are not special methods, they are code other than the method definition of the class. Let's extend the calculator's example, add a constructor argument, and use it to initialize the internal state.
class Calculator(brand: String) {
/**
* A constructor.
*/
val color: String = if (brand == "TI") {
"blue"
} else if (brand == "HP") {
"black"
} else {
"white"
}
// An instance method.
def add(m: Int, n: Int): Int = m + n
}
Pay attention to two different styles of comments.
You can use constructors to construct an instance:
scala> val calc = new Calculator("HP")
calc: Calculator = Calculator@1e64cc4d
scala> calc.color
res0: String = black
The Calculator example above illustrates how Scala is expression-oriented. T
he value of the color is bound to
if/else
expression.
Scala is highly expression-oriented: most things are expressions, not instructions.
Functions and methods are largely interchangeable. B ecause functions and methods are so similar, you probably don't know whether what you're calling is a function or a method. And when you really encounter a difference between methods and functions, you may be confused.
scala> class C {
| var acc = 0
| def minc = { acc += 1 }
| val finc = { () => acc += 1 }
| }
defined class C
scala> val c = new C
c: C = C@1af1bd6
scala> c.minc // calls c.minc()
scala> c.finc // returns the function as a value:
res2: () => Unit = <function0>
When you can call a "function" without parentheses, but you have to bracket the other, you might think, oh, I thought I knew how Scala works. M aybe they need brackets sometimes? You might think you're using a function, but you're actually using a method.
In practice, you can do great things with Scala, even if you don't understand the difference between methods and functions. I f you're new to Scala, and you're reading about the differences, you may not be able to keep up. B ut that doesn't mean you're in trouble with Scala. It just means that the difference between functions and methods is subtle and can only be understood clearly if you go deep inside the language.
class ScientificCalculator(brand: String) extends Calculator(brand) {
def log(m: Double, base: Double) = math.log(m) / math.log(base)
}
The reference Toa Scala indicates that the type alias is superior to inheritance if the child class is virtually indescessive from the parent class. A Tour of Scala details sub-classification.
class EvenMoreScientificCalculator(brand: String) extends ScientificCalculator(brand) {
def log(m: Int): Double = log(m, math.exp(1))
}
You can define an abstract class that defines methods but does not implement them. I nstead, these methods are defined by sub-classes that extend abstract classes. You cannot create instances of abstract classes.
scala> abstract class Shape {
| def getArea():Int //子类应定义为这个
| }
defined class Shape
scala> class Circle(r: Int) extends Shape {
| def getArea():Int = { r * r * 3 }
| }
defined class Circle
scala> val s = new Shape
<console>:8: error: class Shape is abstract; cannot be instantiated
val s = new Shape
^
scala> val c = new Circle(2)
c: Circle = Circle@65c0035b
Traits are collections of fields and behaviors that can be extended or mixed into your class.
trait Car {
val brand: String
}
trait Shiny {
val shineRefraction: Int
}
class BMW extends Car {
val brand = "BMW"
}
With keywords, a class can extend multiple traits:
class BMW extends Car with Shiny {
val brand = "BMW"
val shineRefraction = 12
}
Refer to Effective Scala's view of traits.
When should I use traits instead of abstract classes? I f you want to define a type that resembles an interface, you may find it difficult to choose between traits and abstract classes. B oth forms allow you to define some behavior of one type and require the inheritor to define some other behavior. Some rules of experience:
Previously, we defined an argument for a function as Int, indicating that the input is a numeric type. I n fact, functions can also be generic to apply to all types. W hen this happens, you'll see type parameters introduced with square bracket syntax. The following example shows a cache that uses generic keys and values.
trait Cache[K, V] {
def get(key: K): V
def put(key: K, value: V)
def delete(key: K)
}
Methods can also introduce type parameters.
def remove[K](key: K)