7

私は 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クラスを変更して両方を処理するにはどうすればよいですか?

4

5 に答える 5

9

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

d2is の型はCodeRunner[String]which is に代入できないd1ことに注意してくださいCodeRunner[Int]

于 2011-06-14T20:00:02.273 に答える
3

ジェネリック型を使用すると、オブジェクトがインスタンス化されるときに指定されるプレースホルダー型でクラスを定義できます。コンパイラは、すべてがタイプ セーフであることを確認できるので満足しています。また、オブジェクトをインスタンス化して値に任意の型を渡すことができるので満足しています。

クラスでジェネリック型を使用するには、次のように変更できます。

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

「class CodeRunner」の後の [T] は重要な部分です。これは、クラス定義内で使用されるジェネリック型 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) を渡すことはできません。

于 2011-06-14T20:09:16.797 に答える
2

任意の関数を渡すには、確実にジェネリックを使用できます。

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)
于 2011-06-15T06:18:37.667 に答える
1

任意の関数参照を別の関数に渡すことはできますか? 上記の機能パラメータのアリティは固定されます

いつものように、開発している関数の型を書き留めてください。すべてが明確になります。

「任意」という言葉は、関数の引数が任意の型で機能することを示唆しています。つまり、それらは多態的な関数 (または一部の言語では汎用関数) です。

以下は、かなりきれいに 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

そして、他にもたくさん。

于 2011-06-14T19:59:14.890 に答える
0
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"
于 2011-06-14T23:03:39.830 に答える