Scalaの別の関数に任意の関数を渡すことはできますか?
-
27-10-2019 - |
質問
私は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
両方を処理するクラス?
解決
一般的なタイプを使用すると、オブジェクトがインスタンス化されたときに指定されるプレースホルダータイプのクラスを定義できます。コンパイラは、すべてがタイプ安全であることを確認できるため、幸せです。オブジェクトをインスタンス化し、値に対して任意のタイプを渡すことができるので、あなたは幸せです。
クラスで一般的なタイプを使用するには、次のように変更できます。
class CodeRunner[T] (val user_defined: (T) => Unit) {
def run(input: T) = {
user_defined(input)
}
}
クラスCoderunner]の後の[T]は重要な部分です。これは、クラス定義内で使用される一般的なタイプT(Tを別の大文字などに置き換えることができる)があることを定義しています。
したがって、メソッドを定義する場合:
def arbitrary_code(input: String) = { println("Running with input " + input) }
そして、それを渡します:
val d1 = new CodeRunner(arbitrary_code)
...その後、コンパイラは「Aha、このインスタンスのCoderunnerの場合、汎用タイプtは文字列です」と言います。そして、あなたが呼び出すなら
d1.run("string")
コンパイラは幸せになりますが、D1.run(4)を渡すことはできません。
他のヒント
どうすれば変更できますか
CodeRunner
両方を処理するクラス?
クラスの任意のタイプAパラメーターを作成できます。
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の関数は関数1 [u、t]のインスタンスであり、型(t、u)=> vの関数が関数2 [t、u、v]のインスタンスであるため、不可能です。 (また、有用なユースケースが見つかりませんでした)。ただし、「Currying」と呼ばれるスマートコンセプトがあります。複数の引数を取り、単一の引数を取り、別の関数を返す関数の値を返す関数を変換することで構成されます。これが例です:
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)を行うことができます。また、「カレー」メソッドを使用して、カラーの機能で非銃撃機能を変換することもできます。
d1.run(nonCurriedAdd.curried)
別の関数に任意の関数参照を渡すことはできますか?上記の機能パラメーターの不安は修正されます
いつものように、開発している機能のタイプを書き留めてください。すべてが明らかになります。
あなたの「arbitrary意的」という言葉は、関数の引数があらゆるタイプで機能することを示唆しています。つまり、それらは多型関数(または一部の言語では汎用関数)です。
以下は、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
そして多くの、他の多く。
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"