Scala 3 — Book

Создание метода, возвращающего функцию

Language

Благодаря согласованности Scala написание метода, возвращающего функцию, похоже на то, что было описано в предыдущих разделах. Например, представьте, что вы хотите написать метод greet, возвращающий функцию. Еще раз начнем с постановки проблемы:

Необходимо создать метод greet, возвращающий функцию. Эта функция должна принимать строковый параметр и печатать его с помощью println. Начнем с простого шага: greet не принимает никаких входных параметров, а просто создает функцию и возвращает её.

Учитывая это утверждение, можно начать создавать greet. Известно, что это будет метод:

def greet()

Также известно, что этот метод должен возвращать функцию, которая (a) принимает параметр String и (b) печатает эту строку с помощью println.

Следовательно, эта функция имеет тип String => Unit:

def greet(): String => Unit = ???
           ----------------

Теперь нужно просто создать тело метода. Известно, что метод должен возвращать функцию, и эта функция принимает String и печатает ее. Эта анонимная функция соответствует следующему описанию:

(name: String) => println(s"Hello, $name")

Теперь вы просто возвращаете эту функцию из метода:

// метод, который возвращает функцию
def greet(): String => Unit = 
  (name: String) => println(s"Hello, $name")

Поскольку этот метод возвращает функцию, вы получаете функцию, вызывая greet(). Это хороший шаг для проверки в REPL, потому что он проверяет тип новой функции:

scala> val greetFunction = greet()
val greetFunction: String => Unit = Lambda....
    -----------------------------

Теперь можно вызвать greetFunction:

greetFunction("Joe")   // печатает "Hello, Joe"

Поздравляем, вы только что создали метод, возвращающий функцию, а затем запустили её.

Доработка метода

Метод greet был бы более полезным, если бы была возможность задавать приветствие. Например, передать его в качестве параметра методу greet и использовать внутри println:

def greet(theGreeting: String): String => Unit = 
  (name: String) => println(s"$theGreeting, $name")

Теперь, при вызове этого метода, процесс становится более гибким, потому что приветствие можно изменить. Вот как это выглядит, когда создается функция из этого метода:

scala> val sayHello = greet("Hello")
val sayHello: String => Unit = Lambda.....
    ------------------------

Выходные данные подписи типа показывают, что sayHello — это функция, которая принимает входной параметр String и возвращает Unit (ничего). Так что теперь, при передаче sayHello строки, печатается приветствие:

sayHello("Joe")   // печатает "Hello, Joe"

Приветствие можно менять для создания новых функций:

val sayCiao = greet("Ciao")
val sayHola = greet("Hola")

sayCiao("Isabella")   // печатает "Ciao, Isabella"
sayHola("Carlos")     // печатает "Hola, Carlos"

Более реалистичный пример

Этот метод может быть еще более полезным, когда возвращает одну из многих возможных функций, например, фабрику пользовательских функций.

Например, представим, что необходимо написать метод, который возвращает функции, приветствующие людей на разных языках. Ограничим это функциями, которые приветствуют на английском или французском языках, в зависимости от параметра, переданного в метод.

Созданный метод должен: (a) принимать “желаемый язык” в качестве входных данных и (b) возвращать функцию в качестве результата. Кроме того, поскольку эта функция печатает заданную строку, известно, что она имеет тип String => Unit. С помощью этой информации сигнатура метода должна выглядеть так:

def createGreetingFunction(desiredLanguage: String): String => Unit = ???

Далее, поскольку возвращаемые функции, берут строку и печатают ее, можно прикинуть две анонимные функции для английского и французского языков:

(name: String) => println(s"Hello, $name")
(name: String) => println(s"Bonjour, $name")

Для большей читабельности дадим этим анонимным функциям имена и назначим двум переменным:

val englishGreeting = (name: String) => println(s"Hello, $name")
val frenchGreeting = (name: String) => println(s"Bonjour, $name")

Теперь все, что осталось, это (a) вернуть englishGreeting, если desiredLanguage — английский, и (b) вернуть frenchGreeting, если desiredLanguage — французский. Один из способов сделать это - выражение match:

def createGreetingFunction(desiredLanguage: String): String => Unit =
  val englishGreeting = (name: String) => println(s"Hello, $name")
  val frenchGreeting = (name: String) => println(s"Bonjour, $name")
  desiredLanguage match
    case "english" => englishGreeting
    case "french" => frenchGreeting

И это последний метод. Обратите внимание, что возврат значения функции из метода ничем не отличается от возврата строкового или целочисленного значения.

Вот как createGreetingFunction создает функцию приветствия на французском языке:

val greetInFrench = createGreetingFunction("french")
greetInFrench("Jonathan")   // печатает "Bonjour, Jonathan"

И вот как - на английском:

val greetInEnglish = createGreetingFunction("english")
greetInEnglish("Joe")   // печатает "Hello, Joe"

Если вам понятен этот код — поздравляю — теперь вы знаете, как писать методы, возвращающие функции.

Contributors to this page: