Могу ли я передать произвольную функцию другой функции в Scala? - PullRequest
7 голосов
/ 14 июня 2011

Я новичок в 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 ]

9 голосов
/ 15 июня 2011

Как я могу изменить мой 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].

3 голосов
/ 15 июня 2011

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

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

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

2 голосов
/ 15 июня 2011

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

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)
1 голос
/ 14 июня 2011

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

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

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

Следующее должно довольно четко перевести на 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

И многие, многие другие.

0 голосов
/ 15 июня 2011
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"
...