Кафедра ИВТ и ПМ. Ветров С. В.
2023
val foo: Int => Double = (x:Int) => 42.0 + x
foo
— имя функцииInt => Double
— тип функции, принимающей Int и возвращающий Double =
— присваивание значения, в данном случае тела функции(x:Int) => 42.0 + x
— тело функции
val isOdd = (_:Int) % 2 == 0 // лямбда, проверяющая чётность числа
// аналогично: def isOddMethod(x:Int) = x % 2 == 0
Если переменная всего одна, то можно задать имя: _
def gareaterOn(f: Int => Int): (Int, Int) => Boolean =
(x,y) => f(x) > f(y)
// Int => Int -- тип функция, принимающая Int и возвращающая Int
// (Int, Int) => Boolean — возвр. тип -- логическая функция двух аргументов
// вызов:
gareaterOn( x => x)(1,2)
// вызов:
gareaterOn( x => x%10 )(1,2)
// вызов и запись результата в переменную:
val func = gareaterOnMod10( x => x%10 )
func(1,2)
Вложенный вызов функций можно записывать последовательно, в строку.
Для этого используются операторы:
f andThen g
— аналогично g( f(x) )
f compose g
— аналогично f( g(x) )
val f = (_:Double) + 2
val g = (_:Double) * 3
(f compose g)(1) // 5
(f andThen g)(1) // 9
Каррирование (карринг) или частичное применение функции — преобразование функции от многих аргументов в набор функций, каждая из которых является функцией от одного аргумента.
В результате частичного применения функции появляется новая функция, которая принимает оставшиеся аргументы. Например: f(x,y) преобразуется в f'(x)(y), где f'(x) — возвращает функцию.
/** Создаёт новый список на основе старого
* @param f -- функция фильтрации списка
* @param lst -- список из целых чисел
* @return список чисел, для которых функция f возвращает true
*/
def my_list_filter(f: Int => Boolean, lst: List[Int] ) =
var filtered: List[Int] = List() // новый пустой список
for (element <- lst) // перебор элементов списка с записью каждого в element
if ( f(element) )
filtered = filtered.appended( element ) // добавление элемента в новый список
filtered
def my_list_filter(f: Int => Boolean, lst: List[Int] ) =
var filtered: List[Int] = List() // новый пустой список
for (element <- lst) // перебор элементов списка с записью каждого в element
if ( f(element) )
filtered = filtered.appended( element ) // добавление элемента в новый список
filtered
val isOdd = (_:Int) % 2 == 0 // лямбда, проверяющая чётность числа
// аналогично: def isOddMethod(x:Int) = x % 2 == 0
my_list_filter( isOdd, List(1,2,3,4)) // -> List(2,4)
my_list_filter( (_:Int) % 2 == 0, List(1,2,3,4)) // -> List(2,4)
my_list_filter( _ % 2 == 0, List(1,2,3,4)) // -> List(2,4)
// каррирование метода my_list_filter,
// теперь он возвращает функцию, которая должна принять его второй параметр
// первый параметр уже задан
val oddFilter = my_list_filter.curried( isOdd )
// вызов каррированного метода
oddFilter( List(1,2,3,4) )
def plusxy: Int => Int => Int = x => y => x + y
// вызов
plusxy(1)(2)
[Int, Int]
val divide10: PartialFunction[Int, Int] = {
case 1 => 10
case 2 => 5
case 5 => 2
case 10 => 1
}
Если вариант case для текущего значения аргумента функции не предусмотрен, то бросается исключение. Однако в такую функцию встраивается метод `.isDefinedAt` которым можно проверить, определена ли функция для такого значения.
Класс - ссылочный тип.
// Можно использовать отступы и двоеточие (Scala 3)
class Customer:
// поля (public):
var id: Int = 0
var name: String = ""
// можно использовать скобки {}
class Customer {
// поля (public):
var id: Int = 0
var name: String = ""
}
/** Клиент */
class Customer {
// поля (public):
var id: Int = 0
var name: String = ""
private var note: String =""
protected var field: String =""
// переопределение метода; если нет параметров, то скобки () можно опустить
override def toString: String =
f"$id%6d $name%-32s; $note"
// %-32s — формат: 32 символа, выравнивание по левому краю
}
val c: Customer = new Customer
// Ошибка! Нельзя менять val переменную
c = new Customer;
// можно менять поля
c.name = "Martin O."
c.name = "Martin Odersky"
println(c.id)
println(c.name)
println(c.note) // Error: private
println( c.toString )
// вывод
0
Martin Odersky
0 Martin Odersky ;
class Customer {
// поля (public):
var id: Int = 0
var name: String = ""
// область инициализации (вызывается при создании объекта):
print("Object created")
// закрытое поле
private var note: String =""
}
@main
def main() =
val c: Customer = new Customer // Object created
class Customer(id:Int, name:String, note:String);
// параметры — private val поля
@main
def main() =
//Ошибка: нет конструктора по умолчанию
// val c: Customer = new Customer
val c: Customer = new Customer(0, "Martin Odersky", "")
// Ошибка: нельзя менять поля
// c.name = "Martin O."
// c.name = "Martin Odersky"
// Ошибка: нет доступа к полям
// println(c.id)
// println(c.name)
// println(c.note) // Error
// вызов унаследованного метода toString
println( c.toString )
class Customer(id:Int, name:String, note:String){
// параметры — private val поля
override def toString: String =
f"$id%6d $name%-32s; $note"
}
@main
def main() =
//Ошибка: нет конструктора по умолчанию
// val c: Customer = new Customer
val c: Customer = new Customer(0, "Martin Odersky", "")
// Ошибка: нельзя менять поля
// c.name = "Martin O."
// c.name = "Martin Odersky"
// Ошибка: нет доступа к полям
// println(c.id)
// println(c.name)
// println(c.note) // Error
// вызов унаследованного метода toString
println( c.toString )
Похож на абстрактный класс в Java:
abstract class IntSet {
// add an element to the set
def incl(x: Int): IntSet
// whether an element belongs to the set
def contains(x: Int): Boolean
// абстрактный класс может содержать методы с реализацией
def foo() = print("real method")
}
@main
def main() =
// Ошибка: нельзя создавать экземпляры абстрактного класса
val s: intSet = new IntSet;
class EmptyIntSet extends IntSet {
def contains(x : Int) = false
def incl(x : Int) =
new NonEmptyIntSet(x, this)
}
Похожи на интерфейсы в Java, но:
trait Employee:
// абстрактные методы:
def id: Int
def firstName: String
def lastName: String
def drink_coffe: Unit // не возвращает значения
// обычный метод
def work =
print("Do something....")
Отличия абстрактного класса и интерфейса в Java
object StringUtils:
def truncate(s: String, length: Int): String = s.take(length)
def containsWhitespace(s: String): Boolean = s.matches(".*\\s.*")
def isNullOrEmpty(s: String): Boolean = s == null || s.trim.isEmpty
object MathConstants:
val PI = 3.14159
val E = 2.71828
// Использование
StringUtils.truncate("Да, нет, наверное", 2) // "Да"
println(MathConstants.PI) // 3.14159
import scala.math.*
class Circle(val radius: Double):
def area: Double = Circle.calculateArea(radius)
object Circle:
private def calculateArea(radius: Double): Double = Pi * pow(radius, 2.0)
val circle1 = Circle(5.0)
circle1.area
Содержит методы
class Person:
var name = ""
var age = 0
override def toString = s"$name is $age years old"
object Person:
def apply(name: String): Person = // аналог конструктора
var p = new Person
p.name = name
p
def apply(name: String, age: Int): Person = // аналог конструктора
var p = new Person
p.name = name
p.age = age
p
val joe = Person("Joe")
val fred = Person("Fred", 29)
//val joe: Person = Joe is 0 years old
//val fred: Person = Fred is 29 years old
/** Решает квадратное уравнение с коэффициентами
* @return (D, x1, x2) */
def square_equation(a: Double, b:Double, c:Double): (Double, Option[Double], Option[Double])=
val D = pow(b, 2.0) - 4 * a*c
D match {
case D if D < 0 => (D, None, None)
case _ => (D, Option( (-b-sqrt(D))/2/a ), Option( (-b+sqrt(D))/2/a ) )
}
scala.collection.immutable
List[+A], Vector[+A], Set[+A], Map[K, +V]
scala.collection.mutable_
Buffer[A], Set[A], Map[K,V] Builder[-E, C]
// интерфейсы
scala.collection
Seq[+A], Set[+A], Map[K, +V], Iterator[+A]
Информация о том, что конкретные значения, в частности коллекции, неизменны (immutable) позволяет компилятору более эффективно оптимизировать код. В том числе помогает программисту и компилятору организовывать параллельные вычисления не беспокоясь об отсутствии потокобезопансоти.
// односвязный список с O(1) вставкой в начало и O(n) вставкой в конец
val l = List(10,2,3,3,5)
// создание списка;
val namesAgain = "Joel" :: "Chris" :: "Ed" :: Nil
// Nil — специальный тип для списков, "отсутствующий" элемент;
// используется для создания пустых списков и обозначения конца списка
// Vector реализован на основе префиксного дерева (trie),
// быстрое добавление в начало и конец, произвольный доступ за константное время
// перебор медленнее чем в списке
val v = Vector(1,2,3,3,5) // implemented as tree of blocks, provides fast random access
// множество
val s = Set(1,2,3,3,5) // -> Set(1,2,3,5)
// словарь
val m = Map("key1" -> 123, "key2" -> 456)
import scala.util.Random
// List.fill принимает два набора параметров: размер последовательности и выражение,
val rand_list = List.fill( 10 ) ( Random.nextInt(10) )
l(0) // -> 10
s(5) // true
s(55) // false
m("key1") // -> 123
m("qwerty") // Exception
m.get("key1") // Some(123)
m.get("qwerty") // None
s.contains(2) // true
l.head // -> 10
l.last // -> 5
s.size // -> 4
v.size // -> 5
l - 10 // удаление элемента; -> List(2,3,3,5)
m - "key1" // удаление пары c ключом key1
В переменной list лежит отсортированный в порядке возрастания список целых чисел. Со списком необходимо выполнить следующие операции:
val list = List(10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150)
list
.takeWhile(_ < 100) // отбор начальных элементов, для которых предикат = true
.filter( _ % 4 ==0) // отбор всех, для которых предикат = true
.init // взять все, кроме последнего
.map( _ / 4) // применить функцию
.foreach(println) // терминальный метод: применяет функцию
Предикат — функция с множеством значений: true и false.