web-dev-qa-db-ja.com

Scalaで関数を定義する2つの方法。違いはなんですか?

以下に、いくつかの機能を定義して試してみる小さなScalaセッションを示します。

scala> def test1(str: String) = str + str;    
test1: (str: String)Java.lang.String

scala> test1("ab")
res0: Java.lang.String = abab

うまく動作します。

scala> val test2 = test1
<console>:6: error: missing arguments for method test1 in object $iw;
follow this method with `_' if you want to treat it as a partially applied function
       val test2 = test1
                   ^

おっとっと。

scala> val test2 = test1 _
test2: (String) => Java.lang.String = <function1>

scala> test2("ab")
res1: Java.lang.String = abab

うまくいく!

さて、折り畳むときに_構文を見ました(_ + _など)。私が理解しているように、_は基本的に「引数」を意味します。 test1 _は基本的にtest1 "に渡される引数を持つ関数を意味します。しかし、なぜexactlytest1と同じではないのですか? _を追加すると違いがありますか?

だから私は探索し続けました...

scala> val test3 = (str: String) => str + str
test3: (String) => Java.lang.String = <function1>

scala> test3("ab")
res2: Java.lang.String = abab

scala> val test4 = test3
test4: (String) => Java.lang.String = <function1>

ここでは_なしで動作します! defed関数とvaled関数の違いは何ですか?

44
aioobe

Def'ed関数とval'ed関数の間に違いはありません。

scala> def test1 = (str: String) => str + str
test1: (String) => Java.lang.String

scala> val test2 = test1
test2: (String) => Java.lang.String = <function1>

scala> val test3 = (str: String) => str + str
test3: (String) => Java.lang.String = <function1>

scala> val test4 = test2
test4: (String) => Java.lang.String = <function1>

見る?これらはすべて関数であり、X => Yタイプで示されます。

scala> def test5(str: String) = str + str
test5: (str: String)Java.lang.String

X => Yタイプが表示されていますか?もしそうなら、眼科医に会いに行ってください。ここでの型は(X)Yで、一般的にmethodを示すために使用されます。

実際、test1test2test3、およびtest4はすべて、関数を返すメソッドです。 test5は、Java.lang.Stringを返すメソッドです。また、test1からtest4はパラメーターを取りません(とにかくtest1のみが取り得ました)、test5は取ります。

したがって、違いは非常に簡単です。最初のケースでは、valにメソッドを割り当てようとしましたが、メソッドが取るパラメーターを入力しませんでした。したがって、末尾のアンダースコアを追加するまで失敗しました。つまり、私のメソッドを関数に変換するを意味していました。

2番目の例では関数があったので、他に何もする必要はありませんでした。

メソッドは関数ではなく、その逆も同様です。関数は、FunctionNクラスのいずれかのオブジェクトです。メソッドは、オブジェクトに関連付けられたコードの一部へのハンドルです。

Stack Overflowのメソッドと関数に関するさまざまな質問をご覧ください。

53

defは、Javaでメソッドを定義する方法と同様に、周囲のオブジェクト/クラス/トレイト内でメソッドを宣言します。 defsは他のオブジェクト/クラス/トレイト内でのみ使用できます。 REPLでは、「隠されている」ため周囲のオブジェクトを見ることができませんが、実際には存在します。

defは値ではないため、defを値に割り当てることはできません。これはオブジェクトのメソッドです。

_(x: T) => x * x_は、実行時に存在する関数オブジェクトを宣言およびインスタンス化します。関数オブジェクトは、FunctionN特性を拡張する匿名クラスのインスタンスです。 FunctionNトレイトには、applyメソッドが付属しています。 applyという名前は、省略できるため特別です。式f(x)f.apply(x)に脱糖されます。

ボトムラインは-関数オブジェクトはヒープ上に存在するランタイム値であるため、値、変数、パラメーターに割り当てたり、メソッドから戻り値として返すことができます。

値にメソッドを割り当てる問題を解決するために(これは便利です)、Scalaを使用すると、プレースホルダー文字を使用してメソッドから関数オブジェクトを作成できます。式_test1 __上記の例は、メソッド_test1_のラッパー関数を実際に作成します-x => test1(x)と同等です。

58
axel22

アンダースコアは、異なるコンテキストで異なることを意味します。しかし、それは常にここに行くが、名前を付ける必要はないと考えることができます。

パラメーターの代わりに適用すると、メソッドを関数に持ち上げることになります。

scala> def test1(str: String) = str + str; 
test1: (str: String)Java.lang.String

scala> val f1 = test1 _
f1: (String) => Java.lang.String = <function1>

このメソッドは、タイプ(String)=> Stringの関数になっていることに注意してください。

Scalaのメソッドと関数の違いは、メソッドが従来のJavaメソッドに似ていることです。値として渡すことはできません。ただし、関数それ自体が値であり、入力パラメーターおよび戻り値として使用できます。

さらに持ち上げることができます:

scala> val f2 = f1 _
f2: () => (String) => Java.lang.String = <function0>

この機能を解除すると、別の機能が発生します。今回はタイプ()=>(String)=>(String)

私の知る限り、この構文はすべてのパラメーターを明示的にアンダースコアに置き換えることと同等です。例えば:

scala> def add(i: Int, j: Int) = i + j
add: (i: Int,j: Int)Int

scala> val addF = add(_, _)
addF: (Int, Int) => Int = <function2>

scala> val addF2 = add _    
addF2: (Int, Int) => Int = <function2>
11
Synesso