Frage

Ich bin neu in Scala und es ist ziemlich ordentlich, Funktionen an andere Funktionen zu übergeben- aber kann ich einen übergeben willkürlich Funktionsreferenz auf eine andere Funktion? Die Arität des funktionalen Parameters wird behoben (das heißt, ich bin auch gespannt, ob Sie auch eine Funktion mit willkürlicher Arität übergeben können). Ich stolpere immer wieder auf Typfehler. Ich habe versucht zu benutzen Any Aber es scheint nicht zu helfen.

Ich habe den Code unten:

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)

Und ich bekomme:

Running with input 4

Nehmen wir nun an, ich möchte stattdessen die folgende Funktion übergeben:

def arbitrary_code(input: String) = { println("Running with input " + input) }

Wie kann ich meine ändern CodeRunner Klasse für beide?

War es hilfreich?

Lösung

Mit generischen Typen können Sie eine Klasse mit einem Platzhaltertyp definieren, der angegeben wird, wenn ein Objekt instanziiert wird. Der Compiler ist glücklich, weil er sicherstellen kann, dass alles sicher ist, und Sie sind glücklich, weil Sie das Objekt instanziieren und willkürliche Typen für den Wert eingeben können.

Um einen generischen Typ mit Ihrer Klasse zu verwenden, können Sie ihn so ändern:

class CodeRunner[T] (val user_defined: (T) => Unit) {
  def run(input: T) = {
    user_defined(input)
  }
}

Das [t] nach "Class Coderunner" ist der wichtige Teil - es definiert, dass es einen generischen Typ T gibt (Sie können T durch einen anderen Großbuchstaben usw. ersetzen), der innerhalb der Klassendefinition verwendet wird.

Wenn Sie also eine Methode definieren:

def arbitrary_code(input: String) = { println("Running with input " + input) }

und dann weitergeben:

val d1 = new CodeRunner(arbitrary_code)

... Der Compiler sagt dann "AHA, für diese Instanz von COLLERUNNER, der generische Typ T ist eine Zeichenfolge". Und wenn Sie aufrufen

d1.run("string")

Der Compiler wird glücklich sein, lässt Sie aber nicht in D1.run (4) passieren.

Andere Tipps

Wie kann ich meine ändern CodeRunner Klasse für beide?

Sie können den willkürlichen Typ A -Parameter der Klasse erstellen:

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

Beachten Sie, dass die Art von d2 ist CodeRunner[String] das ist nicht zugeordnet d1 welches ist CodeRunner[Int].

Um eine willkürliche Funktion zu übergeben, können Sie sicherlich Generika verwenden:

def run[T,U](f: T => U) = println(f)

Für willkürliche Arität ist es unmöglich, da eine Funktion des Typs t => u Instanz der Funktion 1 [u, t] und eine Funktion des Typs (t, u) => v ist eine Instanz von Funktion2 [t, u, v]. (Außerdem konnte ich keinen nützlichen Anwendungsfall finden). Es gibt jedoch ein intelligentes Konzept namens "Currying". Es besteht darin, eine Funktion zu transformieren, die mehrere Argumente aufnimmt und einen Wert in einer Funktion zurückgibt, die ein einzelnes Argument annimmt und eine andere Funktion zurückgibt. Hier ist ein Beispiel:

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)

So können Sie jetzt "d1.run (CurriedAdd) machen. Sie können auch eine nicht gequerzliche Funktion in einer Curry-Funktion durch die "Curry" -Methode verwandeln:

d1.run(nonCurriedAdd.curried)

Kann ich eine willkürliche Funktionsreferenz auf eine andere Funktion übergeben? Die Arität des Funktionsparameters wird festgelegt

Schreiben Sie wie immer die Art der Funktion auf, die Sie entwickeln, und alles wird klar.

Ihr Wort "willkürlich" schlägt vor, dass die Funktionsargumente bei jedem Typ funktionieren. Das heißt, sie sind polymorphe Funktionen (oder generische Funktionen in einigen Sprachen).

Das Folgende sollte ziemlich sauber in Scala übersetzen:

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

Möglicherweise können Sie ein paar andere finden Funktionen höherer Ordnung, die Funktionen der festen Arität, aber willkürlicher (dh polymorpher) Typ erfordern und sie betätigen.

Klassische Funktionen höherer Ordnung sind zB z.

map :: (a -> b) -> [a] -> [b]

fold :: (a -> b -> b) -> b -> [a] -> b

Und viele, viele andere.

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"
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top