Могу ли я передать произвольную функцию другой функции в Scala?
-
27-10-2019 - |
Вопрос
Я новичок в Scala, и возможность передавать функции другим функциям довольно удобна - но могу ли я передать произвольный ссылка функции на другую функцию?Арность указанного функционального параметра будет исправлена (тем не менее, мне также интересно, можете ли вы также передать функцию с произвольной арностью).Я постоянно натыкаюсь на ошибки типа.Я пробовал использовать Any
но, похоже, это не помогает.
Например, у меня есть приведенный ниже код:
class CodeRunner(val user_defined: (Int) => Unit) {
def run(input: Int) = {
user_defined(input)
}
}
def arbitrary_code(input: Int) = { println("Running with input " + input) }
val d1 = new CodeRunner(arbitrary_code)
d1.run(4)
И я получаю:
Running with input 4
Теперь предположим, что вместо этого я хочу передать следующую функцию:
def arbitrary_code(input: String) = { println("Running with input " + input) }
Как я могу изменить свой CodeRunner
класс для обработки обоих?
Решение
Универсальные типы позволяют вам определить класс с типом-заполнителем, который указывается при создании экземпляра объекта.Компилятор доволен, потому что он может убедиться, что все типобезопасно, а вы счастливы, потому что можете создать экземпляр объекта и передать произвольные типы для значения.
Чтобы использовать универсальный тип с вашим классом, вы могли бы изменить его следующим образом:
class CodeRunner[T] (val user_defined: (T) => Unit) {
def run(input: T) = {
user_defined(input)
}
}
[T] после "class CodeRunner" является важной частью - она определяет, что существует универсальный тип T (вы могли бы заменить T другой заглавной буквой и т.д.), Который будет использоваться в определении класса.
Итак, если вы определяете метод:
def arbitrary_code(input: String) = { println("Running with input " + input) }
а затем передайте это в:
val d1 = new CodeRunner(arbitrary_code)
...затем компилятор говорит: "ага, для этого экземпляра CodeRunner универсальный тип T является строкой".И если вы призовете
d1.run("string")
компилятор будет доволен, но не позволит вам перейти в d1.run(4).
Другие советы
Как я могу изменить свой
CodeRunner
класс для обработки обоих?
Вы можете сделать произвольный тип параметром класса:
class CodeRunner[T](val user_defined: (T) => Unit) {
def run(input: T) = {
user_defined(input)
}
}
def arbitrary_code(input: Int) = { println("Running with input " + input) }
val d1 = new CodeRunner(arbitrary_code)
d1.run(4)
def arbitrary_code2(input: String) = { println("Running with input " + input) }
val d2 = new CodeRunner(arbitrary_code2)
d2.run("hello")
Обратите внимание, что тип d2
является CodeRunner[String]
который не может быть присвоен d1
который является CodeRunner[Int]
.
Чтобы передать произвольную функцию, вы, конечно, можете использовать generics :
def run[T,U](f: T => U) = println(f)
Для произвольной арности это невозможно, потому что функция типа T => U является экземпляром Function1[U,T], а функция типа (T,U) => V является экземпляром Function2[T,U,V].(Кроме того, я не смог найти ни одного полезного варианта использования).Однако существует умная концепция под названием "каррирование".Он заключается в преобразовании функции, которая принимает несколько аргументов и возвращает значение, в функцию, которая принимает один аргумент и возвращает другую функцию.Вот пример :
def nonCurriedAdd(x: Int, y: Int) = x + y
// nonCurriedAdd(4,6)
def curriedAdd(x: Int) = (y: Int) => x + y
// we can use some syntax sugar
def curriedAdd(x: Int)(y: Int) = x + y
// curriedAdd(4)(6)
Итак, теперь вы можете выполнить `d1.run(curriedAdd).Вы также можете преобразовать функцию без использования карри в функцию с использованием карри, используя метод "curried" :
d1.run(nonCurriedAdd.curried)
могу ли я передать произвольную ссылку на функцию другой функции?Арность указанного функционального параметра будет фиксированной
Как всегда, запишите тип разрабатываемой вами функции, и все станет ясно.
Ваше слово "произвольный" предполагает, что аргументы функции работают с любым типом.То есть это полиморфные функции (или универсальные функции, в некоторых языках).
Следующее должно быть достаточно четко переведено на Scala:
== The type of an "arbitrary" function of fixed arity
f :: a -> b -> c -> d
-- The type of a function that accepts such a
-- function as an argument, and does something with it:
g :: (a -> b -> c -> d) -> a -> b -> c -> d
-- And a function that implements something of that type
g f a b c = f a b c
Возможно, вы смогли бы придумать несколько других подобных функции более высокого порядка, которые принимают функции фиксированной арности, но произвольные (т.е.полиморфный) тип и оперируйте с ними.
Классическими функциями более высокого порядка являются, например,
map :: (a -> b) -> [a] -> [b]
fold :: (a -> b -> b) -> b -> [a] -> b
И многие, многие другие.
scala> object codeRunner {
| def run[InputType, OutputType](code: InputType => OutputType) = (input: InputType) => code(input)
| }
defined module codeRunner
scala> def someCode(x: Int) {
| println("This code uses " + x)
| }
someCode: (x: Int)Unit
scala> def otherCode(y: String) {
| println("This code uses " + y)
| }
otherCode: (y: String)Unit
scala> codeRunner.run(someCode)(10)
This code uses 10
scala> codeRunner.run(otherCode)("hello")
This code uses "hello"