Scala лямбда функция как параметр
Вы можете запустить Scala в браузере с помощью ScalaFiddle.
Это простой способ поэкспериментировать со Scala кодом без всяких настроек.
Большинство примеров кода в этой документации также интегрированы с ScalaFiddle, поэтому вы можете поэкспериментировать с ними, просто нажав кнопку Run.
Выражения
Выражения — это вычислимые утверждения.
Вы можете выводить результаты выражений, используя println .
Значения
Результаты выражений можно присваивать именам с помощью ключевого слова val .
Названные результаты, такие как x в примере, называются значениями. Вызов значения не приводит к его повторному вычислению.
Значения не изменяемы и не могут быть переназначены.
Типы значений могут быть выведены автоматически, но можно и явно указать тип, как показано ниже:
Обратите внимание, что объявление типа Int происходит после идентификатора x , следующим за : .
Переменные
Переменные похожи на значения константы, за исключением того, что их можно присваивать заново. Вы можете объявить переменную с помощью ключевого слова var .
Как и в случае со значениями, вы можете явно указать тип, если захотите:
Блоки
Вы можете комбинировать выражения, окружая их <> . Мы называем это блоком.
Результат последнего выражения в блоке будет результатом всего блока в целом.
Функции
Функции — это выражения, которые принимают параметры.
Вы можете определить анонимную функцию (т.е. без имени), которая возвращает переданное число, прибавив к нему единицу:
Слева от => находится список параметров. Справа — выражение, связанное с параметрами.
Вы также можете назвать функции.
Функции могут принимать множество параметров.
Или вообще не принимать никаких параметров.
Методы
Методы выглядят и ведут себя очень похоже на функции, но между ними есть несколько принципиальных различий.
Методы задаются ключевым словом def . За def следует имя, список параметров, возвращаемый тип и тело.
Обратите внимание, как объявлен возвращаемый тип сразу после списка параметров и двоеточия : Int .
Методы могут принимать несколько списков параметров.
Или вообще ни одного списка параметров.
Есть некоторые отличия от функций, но пока что их можно рассматривать как нечто похожее.
Методы также могут иметь многострочные выражения.
Последнее выражение в теле становится возвращаемым значением метода (у Scala есть ключевое слово return , но оно практически не используется).
Классы
Вы можете объявлять классы используя ключевое слово class , за которым следует его имя и параметры конструктора.
Возвращаемый тип метода greet это Unit , используется тогда, когда не имеет смысла что-либо возвращать. Аналогично void в Java и C. Поскольку каждое выражение Scala должно иметь какое-то значение, то при отсутствии возвращающегося значения возвращается экземпляр типа Unit. Явным образом его можно задать как () , он не несет какой-либо информации.
Вы можете создать экземпляр класса, используя ключевое слово new .
Позже мы рассмотрим классы подробнее.
Классы-образцы (Case Class)
В Scala есть специальный тип класса, который называется классом-образцом (case class). По умолчанию такие классы неизменны и сравниваются по значению из конструктора. Вы можете объявлять классы-образцы с помощью ключевых слов case class .
Можно создавать экземпляры класса-образца без использования ключевого слова new .
Они сравниваются по значению.
Есть еще много деталей, которые мы бы хотели рассказать про классы-образцы; мы уверены, что вы влюбитесь в них! Обязательно рассмотрим их позже.
Объекты
Объекты задаются и существуют в единственном экземпляре. Вы можете думать о них как об одиночках (синглтонах) своего собственного класса.
Вы можете задать объекты при помощи ключевого слова object .
Вы можете сразу получить доступ к объекту, ссылаясь на его имя.
Позже мы рассмотрим объекты подробнее.
Трейты
Трейты — как типы описывают характеристики классов, в нем могут объявляться определенные поля и методы. Трейты можно комбинировать.
Объявить трейт можно с помощью ключевого слова trait .
Трейты также могут иметь реализации методов и полей, которые предполагается использовать умолчанию.
Вы можете наследовать свойства трейтов, используя ключевое слово extends и переопределять реализацию с помощью ключевого слова override .
Здесь DefaultGreeter наследуется только от одного трейта, но можно наследоваться от нескольких.
Позже мы рассмотрим трейты подробнее.
Главный метод
Главный метод является отправной точкой в программе. Для Виртуальной Машины Java требуется, чтобы главный метод назывался main и принимал один аргумент, массив строк.
Лямбда-выражение относится к выражению, которое использует анонимную функцию вместо переменной или значения. Лямбда-выражения более удобны, когда у нас есть простая функция, которую можно использовать в одном месте. Эти выражения быстрее и выразительнее, чем определение целой функции. Мы можем сделать наши лямбда-выражения многоразовыми для любых преобразований. Он может перебирать коллекцию объектов и выполнять какие-то преобразования к ним.
Синтаксис :
Пример :
Работа с лямбда-выражениями
-
Мы можем передавать значения в лямбду, как обычный вызов функции.
Пример :
// Scala программа для показа
// обработка лямбда-выражения
def main(args : Array[String])
val ex 1 = (x : Int) => x + 2
// с несколькими параметрами
val ex 2 = (x : Int, y : Int) => x * y
println(ex 2 ( 2 , 3 ))
Выход:
// Scala программа для применения
// преобразование в коллекцию
def main(args : Array[String])
val l = List( 1 , 1 , 2 , 3 , 5 , 8 )
// возводим в квадрат каждый элемент списка
val res = l.map( (x : Int) => x * x )
/* ИЛИ
val res = l.map (x => x * x)
* /
Выход:
Мы видим, что определенная анонимная функция для выполнения операции квадрата не может использоваться повторно.
// Scala программа для применения
// преобразование в коллекцию
def main(args : Array[String])
val l 1 = List( 1 , 1 , 2 , 3 , 5 , 8 )
val l 2 = List( 13 , 21 , 34 )
val func = (x : Int) => x * x
// возводим в квадрат каждый элемент списков
val res 1 = l 1 .map( func )
val res 2 = l 2 .map( func )
Выход:
// Scala программа для прохождения лямбды
// как параметр функции
// преобразовать функцию с целым числом x и
// функция f в качестве параметра
// f принимает Int и возвращает Double
def transform( x : Int, f : Int => Double)
def main(args : Array[String])
// лямбда передается в f: Int => Double
val res = transform( 2 , r => 3.14 * r * r)
Выход:
В приведенном выше примере функция преобразования принимает целое число x, а функция f применяет преобразование к x, определенному функцией f. Лямбда, переданная в качестве параметра в вызове функции, возвращает тип Double . Следовательно, параметр f должен подчиняться лямбда-определению.
В этом уроке вы узнаете:
Обзор лекций
В первые несколько недель вы узнаете об основах синтаксиса и основных идеях, заложенных в язык Scala. Позже мы начнем раскрывать подробности на примерах.
Некоторые примеры будем писать прямо в интерпретаторе, другие в исходном файле.
Имея под рукой интерпретатор, можно легко исследовать проблемы.
Почему Scala?
- Выразительность
- Функции первого класса
- Замыкания
- Вывод типов
- Буквенный синтаксис для создания функции
- Можно использовать Java библиотеки
- Можно использовать Java инструменты
- Нет потерь производительности
Как работает Scala?
- Компиляция в байт-код Java
- Работает с любой стандартной JVM
- Или даже с некоторыми нестандартными JVM , например Dalvik
- Компилятор Scala написан автором компилятора Java
Что еще нужно знать о Scala
Scala лучше Java в некоторых аспектах. Перед тем как начинать изучать язык Scala, очистите свой разум, из этого выйдет больше толку.
Запускаем интерпретатор
Наберите в консоли sbt console .
Выражения
res0 – автоматически создаваемое имя для переменной, которое интерпретатор дает результату вашего выражения. Переменная имеет тип Int и содержит целочисленное значение 2.
Все (ну, почти) в Scala – выражение.
Константы
Вы можете присвоить собственное имя результату выражения.
Для переменной с ключевым словом val вы не можете изменить ранее присвоенное значение.
Переменные
Если вам нужно изменить значение константы, вы должны использовать ключевое слово var
Функции
Вы можете создать функцию с помощью ключевого слова def.
В Scala, вам нужно точно указывать тип, который принимает переменная в параметрах функции. К счастью, интерпретатор возвращает используемый функцией тип обратно.
Вы можете опускать скобки при использовании функций, если они не имеют аргументов.
Анонимные функции
Вы можете создавать анонимные функции.
Эта функция увеличит на 1 значение, которое было передано в анонимную функцию; значение именуется как x.
Вы можете передавать анонимные функции как параметры или сохранять их в переменных.
Если ваша функция состоит из множества выражений, вы можете использовать фигурные скобки <>, чтобы обезопасить себя.
Тоже самое верно и для анонимной функции
Вы часто будете видеть подобный синтаксис при передачи анонимной функции в качестве параметра.
Частичный вызов функций
Вы можете использовать частичный вызов функций, обозначаемый знаком нижнего подчеркивания(_), этот знак позже будет подменен вызовом функции.
Вы можете использовать частичный вызов функций с любым аргументом из списка аргументов, а не только с последним из них, как в примере.
Каррирование функций
Иногда требуется передача каких-то аргументов в вашу функцию прямо сейчас, а других через некоторое время.
Ниже пример функции, которая позволяет умножать два числа. В одном месте вызова функции вы решите, какой из аргументов будет множителем, а позднее вызывая функцию, вы сможете установить множимое.
Вы можете вызвать функцию напрямую с двумя аргументами.
Вы можете передать первый аргумент, а второй аргумент объявить как частично вызываемый.
Вы можете взять любую функцию с множеством аргументов и произвести ее каррирование. Давайте попробуем использовать функцию, которую рассматривали раньше, например adder
Использование переменного количества аргументов
Существует специальный синтаксис для методов, которые могут принимать параметры одного и того же типа.
Классы
В примере объявляется метод и поле с ключевым словом val. Методы – это функции, которые имеют доступ к внутренним сущностям класса.
Конструктор
Конструкторы не являются специальными методами, их код находится в классе за пределами определения метода. Давайте расширим наш пример с калькулятором. Будем принимать аргумент конструктора и использовать его для инициализации внутреннего состояния.
Обратите внимание на два различных способа написания комментариев.
Выражения
Наш пример с калькулятором дает хороший пример того, как Scala ориентирован на выражения (expression-oriented). Значение переменной color было присвоено благодаря if/else выражению. Scala сильно ориентирован на выражения: большинство вещей делается с помощью выражений, а не утверждений.
Наследование
Смотрите также: В Effective Scala указывается на то, что лучше использовать Псевдонимы типов вместо extends , особенно если подкласс практически ничем не отличается от суперкласса. В “Туре по языку Scala” вы найдете более подробное описание Подклассов.
Перегрузка методов
Трейты
Трейты – это коллекция полей и методов, которые вы можете расширить или примешать к вашему классу.
Смотрите также: В Effective Scala есть описание Трейтов.
Ранее вы могли видеть, что мы определили функцию, принимающая тип Int , который является одним из видов Number. Функция может быть объявлена как обобщенная (generic) и после этого может работать с любым типом. Когда объявлена такая функция, вы увидите размещенный внутри квадратных скобок:
Вы можете думать о них, как о множестве параметров-типов. Рассмотрим пример трейта Кэш (Cache), который принимает параметры-типы (K, V) для ключей и их значений.Методы тоже могут иметь параметры-типы
Built at @twitter by @stevej, @marius, and @lahosken with much help from @evanm, @sprsquish, @kevino, @zuercher, @timtrueman, @wickman, @mccv and @garciparedes; Russian translation by appigram; Chinese simple translation by jasonqu; Korean translation by enshahar;
Earlier in this book you saw that you can create a list of integers like this:
When you want to create a larger list, you can also create them with the List class range method, like this:
That code creates ints as a list of integers whose values range from 1 to 10. You can see the result in the REPL:
In this lesson we’ll use lists like these to demonstrate a feature of functional programming known as anonymous functions. It will help to understand how these work before we demonstrate the most common Scala collections methods.
Examples
An anonymous function is like a little mini-function. For example, given a list like this:
You can create a new list by doubling each element in ints , like this:
This is what that example looks like in the REPL:
As that shows, doubledInts is now the list, List(2, 4, 6) . In this example, this code is an anonymous function:
This is a shorthand way of saying, “Multiply an element by 2.”
Once you’re comfortable with Scala, this is a common way to write anonymous functions, but if you prefer, you can also write them using longer forms. Besides writing that code like this:
you can also write it like this:
All three lines have exactly the same meaning: Double each element in ints to create a new list, doubledInts .
The _ character in Scala is something of a wildcard character. You’ll see it used in several different places. In this case it’s a shorthand way of saying, “An element from the list, ints .”
Before going any further, it’s worth mentioning that this map example is the equivalent of this Java code:
The map example shown is also the same as this Scala code:
Anonymous functions with the filter method
Another good way to show anonymous functions is with the filter method of the List class. Given this List again:
This is how you create a new list of all integers whose value is greater than 5:
This is how you create a new list whose values are all less than 5:
And as a little more complicated example, this is how you create a new list that contains only even values, by using the modulus operator:
If that’s a little confusing, remember that this example can also be written in these other ways:
This is what the previous examples look like in the REPL:
Key points
- You can write anonymous functions as little snippets of code
- You can use them with methods on the List class like map and filter
- With these little snippets of code and powerful methods like those, you can create a lot of functionality with very little code
The Scala collections classes contain many methods like map and filter , and they’re a powerful way to create very expressive code.
Bonus: Digging a little deeper
You may be wondering how the map and filter examples work. The short answer is that when map is invoked on a list of integers — a List[Int] to be more precise — map expects to receive a function that transforms one Int value into another Int value. Because map expects a function (or method) that transforms one Int to another Int , this approach also works:
The last two lines of that example are the same as this:
Similarly, when called on a List[Int] , the filter method expects to receive a function that takes an Int and returns a Boolean value. Therefore, given a method that’s defined like this:
Функции высшего порядка могут принимать другие функции в качестве параметров или возвращать функцию в качестве результата. Такое возможно поскольку функции являются объектами первого класса в Scala. На текущем этапе терминология может казаться немного запутанной, мы используем следующую фразу “функция высшего порядка” как для методов, так и для функций, которые могут принимать другие функции в качестве параметров, или возвращать функции в качестве результата.
Одним из наиболее распространенных примеров функции высшего порядка является функция map , которая доступна в коллекциях Scala.
doubleSalary - это функция, которая принимает один Int x и возвращает x * 2 . В общем случае, кортеж (список имен в скобках) слева от стрелки => - это список параметров, а значение выражения следует справа. Это же значение возвращается в качестве результата. В строке 3 к каждому элементу списка зарплат (salaries) применяется функция doubleSalary .
Чтобы сократить код, мы можем сделать функцию анонимной и передать ее напрямую в качестве аргумента в map:
Обратите внимание, что в приведенном выше примере x не объявлен как Int . Это потому, что компилятор может вывести тип, основываясь на типе который ожидает функция map. Еще более элегантным способом написания этого же кода было бы таким:
Поскольку компилятор Scala уже знает тип параметров (Int), вам нужно только указать правую часть функции. Единственное условие заключается в том, что вместо имени параметра необходимо использовать _ (в предыдущем примере это было x ).
Преобразование методов в функции
Также возможно передавать методы в качестве аргументов функциям более высокого порядка, поскольку компилятор Scala может преобразовать метод в функцию.
Здесь метод convertCtoF передается в forecastInFahrenheit . Это возможно, потому что компилятор преобразовывает convertCtoF в функцию x => ConvertCtoF(x) (примечание: x будет сгенерированным именем, которое гарантированно будет уникальным в рамках своей области видимости).
Функции, которые принимают функции
Одной из причин использования функций высшего порядка является сокращение избыточного кода. Допустим, вам нужны какие-то методы, которые могли бы повышать чью-то зарплату по разным условиям. Без создания функции высшего порядка это могло бы выглядеть примерно так:
Обратите внимание, что каждый из этих трех методов отличается только коэффициентом умножения. Для упрощения можно перенести повторяющийся код в функцию высшего порядка:
Новый метод, promotion , берет зарплату и функцию типа Double => Double (т.е. функция, которая берет Double и возвращает Double) и возвращает их произведение.
Функции, возвращающие функции
Есть определенные случаи, когда вы хотите сгенерировать функцию. Вот пример метода, который возвращает функцию.
Читайте также: