scalabook
Функции - это значения
Хотя каждый когда-либо созданный язык программирования, вероятно, позволяет писать чистые функции,
вторая важная особенность ФП на Scala заключается в том, что функции можно создавать как значения,
точно так же, как создаются значения String
и Int
.
Эта особенность содержит много преимуществ, наиболее распространенными из которых являются:
- можно определять методы, принимающие в качестве параметров функции
- можно передавать функции в качестве параметров в методы
Это было видно во многих местах предыдущих глав, когда демонстрировались такие методы, как map
и filter
:
val nums = (1 to 10).toList// nums: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)val doubles = nums.map(_ * 2) // doubles: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) val lessThanFive = nums.filter(_ < 5) // lessThanFive: List[Int] = List(1, 2, 3, 4)
В этих примерах анонимные функции передаются в map
и filter
.
Анонимные функции также известны как лямбды.
Помимо передачи анонимных функций в filter
и map
, в них также можно передать методы:
def double(i: Int): Int = i * 2def underFive(i: Int): Boolean = i < 5val doubles = nums.filter(underFive).map(double) // doubles: List[Int] = List(2, 4, 6, 8)
Эта возможность обращаться с методами и функциями как со значениями — мощное свойство, предоставляемое языками функционального программирования.
Технически функция, которая принимает другую функцию в качестве входного параметра, известна как функция высшего порядка.
Функции, анонимные функции и методы
В примерах выше анонимная функция это:
_ * 2
Как было показано в обсуждении функций высшего порядка,
_ * 2
- сокращенная версия синтаксиса:
(i: Int) => i * 2
Такие функции называются "анонимными", потому что им не присваивается определенное имя. Для того чтобы это имя задать, достаточно просто назначить его переменной:
val double = (i: Int) => i * 2
Теперь появилась именованная функция, назначенная переменной double
.
Можно использовать эту функцию так же, как используется метод:
double(2)// res1: Int = 4
В большинстве случаев не имеет значения, является ли double
функцией или методом;
Scala позволяет обращаться с ними одинаково.
За кулисами технология Scala, которая позволяет обращаться с методами так же, как с функциями,
известна как Eta Expansion.
Эта способность беспрепятственно передавать функции в качестве переменных
является отличительной чертой функциональных языков программирования, таких как Scala.
И, как было видно на примерах map
и filter
,
возможность передавать функции в другие функции помогает создавать код,
который является кратким и при этом читабельным — выразительным.
Вот еще несколько примеров:
List("bob", "joe").map(_.toUpperCase) // res2: List[String] = List("BOB", "JOE") List("bob", "joe").map(_.capitalize) // res3: List[String] = List("Bob", "Joe") List("plum", "banana").map(_.length) // res4: List[Int] = List(4, 6)
val fruits = List("apple", "pear")// fruits: List[String] = List("apple", "pear")fruits.map(_.toUpperCase) // res5: List[String] = List("APPLE", "PEAR") fruits.flatMap(_.toUpperCase) // res6: List[Char] = List('A', 'P', 'P', 'L', 'E', 'P', 'E', 'A', 'R')
val nums = List(5, 1, 3, 11, 7)// nums: List[Int] = List(5, 1, 3, 11, 7)nums.map(_ * 2) // res7: List[Int] = List(10, 2, 6, 22, 14) nums.filter(_ > 3) // res8: List[Int] = List(5, 11, 7) nums.takeWhile(_ < 6) // res9: List[Int] = List(5, 1, 3) nums.sortWith(_ < _) // res10: List[Int] = List(1, 3, 5, 7, 11) nums.sortWith(_ > _) // res11: List[Int] = List(11, 7, 5, 3, 1) nums.takeWhile(_ < 6).sortWith(_ < _) // res12: List[Int] = List(1, 3, 5)
Ссылки: