Могу ли я передать произвольную функцию другой функции в Scala?

Я новичок в 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 класс для работы?

5 ответов


универсальные типы позволяют определить класс с типом-заполнителем, который задается при создании экземпляра объекта. Компилятор счастлив, потому что он может убедиться, что все безопасно, и вы счастливы, потому что вы можете создать экземпляр объекта и передать произвольные типы для значения.

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

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].


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

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.беги (curriedAdd). Вы также можете преобразовать функцию без Карри в карри, используя метод "Карри":

d1.run(nonCurriedAdd.curried)

могу ли я передать произвольную ссылку на функцию другой функции? Arity указанного функционального параметра будет исправлена

как всегда, запишите тип функции вы разрабатываете, и все станет ясно.

слово "произвольный" предполагает, что аргументы функции работают на любом типе. То есть они являются полиморфными функциями (или родовыми функциями, в некоторых языках).

следующее должно перевести довольно чисто на Скала:

== 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"