Domanda

Sono nuovo a Scala e essere in grado di passare le funzioni ad altre funzioni è abbastanza pulito, ma posso passare un arbitrario Riferimento della funzione a un'altra funzione? L'aria di detta parametro funzionale verrà fissata (Detto questo, sono anche curioso di sapere se puoi passare anche una funzione con Arity arbitraria). Continuo a fare scattare errori di tipo. Ho provato a usare Any Ma non sembra aiutare.

Ad esempio, ho il codice qui sotto:

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)

E ottengo:

Running with input 4

Ora, diciamo che voglio passare invece la seguente funzione:

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

Come posso cambiare il mio CodeRunner classe per gestire entrambi?

È stato utile?

Soluzione

I tipi generici consentono di definire una classe con un tipo di segnaposto che viene specificato quando un oggetto viene istanziato. Il compilatore è felice perché può assicurarsi che tutto sia sicuro e sei felice perché puoi istanziare l'oggetto e passare in tipi arbitrari per il valore.

Per utilizzare un tipo generico con la tua classe, potresti modificarlo in questo modo:

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

Il [t] dopo "Class Coderunner" è la parte importante: definisce che esiste un tipo T generico (è possibile sostituire T con un'altra lettera maiuscola, ecc.) Che verrà utilizzata all'interno della definizione della classe.

Quindi, se definisci un metodo:

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

e poi passalo in:

val d1 = new CodeRunner(arbitrary_code)

... Il compilatore dice quindi "aha, per questa istanza di coderunner il tipo generico T è una stringa". E se invochi

d1.run("string")

Il compilatore sarà felice, ma non ti lascerà passare in d1.run (4).

Altri suggerimenti

Come posso cambiare il mio CodeRunner classe per gestire entrambi?

Puoi creare il parametro di tipo A arbitrario della classe:

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

Si noti che il tipo di d2 è CodeRunner[String] che non è assegnabile a d1 che è CodeRunner[Int].

Per passare una funzione arbitraria puoi sicuramente usare generici:

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

Per l'aria arbitraria, è impossibile perché una funzione di tipo t => u è l'istanza di funzione1 [u, t] e una funzione di tipo (t, u) => v è un'istanza di funzione2 [t, u, v]. (Inoltre, non sono riuscito a trovare alcun caso d'uso utile). Tuttavia, esiste un concetto intelligente chiamato "Currying". Consiste nel trasformare una funzione che assume più argomenti e restituire un valore in una funzione che prende un singolo argomento e restituisce un'altra funzione. Ecco un esempio:

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)

Quindi, ora puoi fare `d1.run (Curriedadd). È inoltre possibile trasformare una funzione non curvata in una curry utilizzando il metodo "curry":

d1.run(nonCurriedAdd.curried)

Posso passare un riferimento di funzione arbitraria a un'altra funzione? L'aria di detta parametro funzionale verrà fissata

Come sempre, scrivi il tipo di funzione che stai sviluppando e tutto diventa chiaro.

La tua parola "arbitraria" suggerisce che gli argomenti della funzione funzionano su qualsiasi tipo. Cioè, sono funzioni polimorfiche (o funzioni generiche, in alcune lingue).

Quanto segue dovrebbe tradurre abbastanza pulitamente in 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

Potresti riuscire a trovare alcuni altri simili funzioni di ordine superiore, che prendono funzioni di arity fissa, ma tipo arbitrario (cioè polimorfico) e operano su di essi.

Le funzioni classiche di ordine superiore sono ad es.

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

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

E molti, molti altri.

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"
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top