Scalaで関数を定義するこれらの3つの方法の違い


92

同じ関数を表現する3つの方法を考えますf(a) := a + 1

val f1 = (a:Int) => a + 1
def f2 = (a:Int) => a + 1
def f3:(Int => Int) = a => a + 1

これらの定義はどのように異なりますか?REPLは明らかな違いを示していません。

scala> f1
res38: (Int) => Int = <function1>
scala> f2
res39: (Int) => Int = <function1>
scala> f3
res40: (Int) => Int = <function1>

11
上記の2番目のブロックf1では、REPL で評価すると、静的にバインドされた値が表示され、それらのメソッドを呼び出した結果f1を評価f2してf3表示することに注意してください。特に、新しいインスタンスが生成されるか、呼び出されるたびに、いつまでも同じです。Function1[Int, Int]f2f3f1Function1[Int, Int]
Randall Schulz

valバージョンが新しい関数インスタンスを必要としない場合、@ RandallSchulzは、なぜこの場合にdefを使用するのでしょうか?
virtualeyes 2012年

2
@virtualeyes FunctionN [...]値を生成するdefが見られる場所を思い出すことができる唯一の状況は、コンビネーターパーサーライブラリにあります。関数を生成するメソッドを作成することはあまり一般的ではなく、defを使用して意味的/機能的に変化しない関数の多くのコピーを生成することはほとんどありません。
Randall Schulz

回答:


112

f1 整数を取り、整数を返す関数です。

f2整数を取り、整数を返す関数を返す、アリティがゼロのメソッドです。(f2後でREPLで入力すると、メソッドの呼び出しになりf2ます。)

f3と同じf2です。そこでは型推論を採用していません。


6
なぜf1あるfunctionf2ありますかmethod
Freewind 2013年

17
@Freewind、関数はというメソッドを持つオブジェクトapplyです。メソッドもメソッドです。
missingfaktor 2013年

素晴らしい答え。質問:あなたはf2のアリティがゼロだと言いますが、それは単項ではありませんか?en.wikipedia.org/wiki/Arity「nullary関数は引数を取りません。単項関数は1つの引数を取ります。」ちょっと興味があるんだけど!
マシューコーネル

5
@MatthewCornell、f2それ自体は引数を受け入れません。それが返す関数オブジェクトが行います。
missingfaktor

122

クラスの中では、val一方で初期化時に評価されdefた場合にのみ評価され、そしてすべての時間は、関数が呼び出されます。以下のコードでは、オブジェクトが最初に使用されたときにxが評価されますが、xメンバーがアクセスされたときは評価されないことがわかります。対照的に、yはオブジェクトがインスタンス化されるときに評価されませんが、メンバーがアクセスされるたびに評価されます。

  class A(a: Int) {
    val x = { println("x is set to something"); a }
    def y = { println("y is set to something"); a }
  }

  // Prints: x is set to something
  val a = new A(1)

  // Prints: "1"
  println(a.x)

  // Prints: "1"                               
  println(a.x)

  // Prints: "y is set to something" and "1"                                  
  println(a.y)

  // Prints: "y is set to something" and "1"                                                                                   
  println(a.y)

@JacobusRはクラス内でのみ本当ですか?
Andrew Cassidy

例:scala> var b = 5 b:Int = 5 scala> val a:(Int => Int)= x => x + ba:Int => Int = <function1> scala> a(5)res48:Int = 10 scala> b = 6 b:Int = 6 scala> a(5)res49:Int = 11私はa(5)が10を返し、bの値がインライン化されることを期待していた
Andrew Cassidy

@AndrewCassidy関数aは不変であり、初期化時に評価されますがb、変更可能な値のままです。したがって、への参照bは初期化中に設定されますが、によって格納される値bは変更可能なままです。楽しみのために、新しいを作成できますval b = 123。この後、これは完全に新しい値であるa(5)ため、常に11になりbます。
ジャック

@JacobusRありがとう...これは理にかなっています。関数aは元の「var b」への参照を持っているため、これは「字句スコープ」の定義と一致します。私を混乱させたのは、次のように言っていると思います。varb = 5; val c = b; b = 6; 行動が異なります。元の「字句」スコープへの参照を運ぶ関数定義がIntと同じように動作することを期待するべきではないと思います。
Andrew Cassidy

3

def x = eなどの定義を実行しても、式eは評価されません。代わりに、xが使用されるたびにeが評価されます。あるいは、Scalaは値定義val x = eを提供します 。これは、定義の評価の一部として右側のeを評価します。その後、xが引き続き使用される場合、xは事前に計算されたeの値によって直ちに置き換えられるため、式を再度評価する必要はありません。

Martin OderskyによるScala By Example

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.