Scala'da bir işlevi tanımlamanın bu üç yolu arasındaki farklar


92

Aynı işlevi ifade etmenin üç yolu verildiğinde f(a) := a + 1:

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

Bu tanımlar nasıl farklılık gösterir? REPL herhangi bir bariz farklılığı göstermez:

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

11
Sen değerlendirerek, yukarıda 2 blokta belirtmeliyiz f1değeri statik olarak bağlı REPL gösterir f1değerlendirirken f2ve f3sonucunu göstermek çağırarak bu yöntemleri. Özellikle, Function1[Int, Int]her seferinde ya f2da f3çağrıldığında yeni bir örnek üretilirken , sonsuza kadar f1aynıdır Function1[Int, Int].
Randall Schulz

@RandallSchulz val sürümünün yeni bir işlev örneği gerektirmediği göz önüne alındığında, bu durumda neden def kullanılsın?
virtualeyes

2
@virtualeyes FonksiyonN [...] değerleri veren defs'i gördüğümde hatırlayabildiğim tek durum, birleştirici ayrıştırıcı kitaplığıdır. İşlevleri veren yöntemler yazmak çok yaygın değildir ve anlamsal / işlevsel olarak değişmeyen bir işlevin birçok kopyasını elde etmek için neredeyse hiçbir zaman def kullanılmaz.
Randall Schulz

Yanıtlar:


112

f1 bir tamsayı alan ve bir tamsayı döndüren bir işlevdir.

f2bir tamsayı alan ve bir tamsayı döndüren bir işlev döndüren sıfır uçlu bir yöntemdir. (Daha f2sonra REPL'e yazdığınızda, yönteme bir çağrı olur f2.)

f3ile aynıdır f2. Orada tip çıkarımını kullanmıyorsun.


6
Neden f1a functionve f2a'dır method?
Freewind

17
@Freewind, bir işlev, adlı bir yönteme sahip bir nesnedir apply. Bir yöntem, bir yöntemdir.
lostfaktor

Harika cevap. Soru: f2'nin sıfır toplumu olduğunu söylüyorsunuz, ancak tekli değil mi? en.wikipedia.org/wiki/Arity "Bir boş işlev bağımsız değişken almaz. Tekli işlev bir bağımsız değişken alır." Sadece merak!
Matthew Cornell

5
@MatthewCornell, f2kendisi hiçbir argüman kabul etmez. Döndürdüğü işlev nesnesi yapar.
lostfaktor

122

Bir sınıfın içinde, yalnızca işlev çağrıldığında ve her seferindeval değerlendirilirken, başlatmada defdeğerlendirilir . Aşağıdaki kodda x'in nesne ilk kullanıldığında değerlendirildiğini, ancak x üyesine erişildiğinde tekrar değerlendirilmediğini göreceksiniz. Bunun aksine, nesne somutlaştırıldığında y değerlendirilmez, üyeye her erişildiğinde değerlendirilir.

  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 bu sadece bir sınıf içinde mi geçerli?
Andrew Cassidy

örneğin: 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 bekliyordum bir 10 ve b değerini döndürmek için (5) inlined edilmiş
Andrew Cassidy

@AndrewCassidy işlev adeğişmezdir ve başlangıçta değerlendirilir, ancak bdeğiştirilebilir bir değer olarak kalır. Bu nedenle referans, bbaşlatma sırasında ayarlanır, ancak tarafından saklanan değer bdeğişebilir. Eğlenmek için şimdi yeni bir tane yaratabilirsiniz val b = 123. Bundan sonra a(5)her zaman 11 verirsiniz, çünkü bartık tamamen yeni bir değerdir.
Jack

@JacobusR teşekkürler ... bu mantıklı. Bu, a işlevi orijinal "var b" ye bir gönderme yaptığı için "sözcük kapsamı" tanımıyla çakışır. Sanırım kafamı karıştıran şey şudur: var b = 5; val c = b; b = 6; farklı davranır. Sanırım orijinal "sözcüksel" kapsama referanslar taşıyan bir işlev tanımının bir Int ile aynı şekilde davranmasını beklememeliyim.
Andrew Cassidy

3

Gibi bir tanım yürütme def X = e ifade değerlendirmek olmaz e . Bunun yerine , e her değerlendirilir X kullanılır. Seçenek olarak ise, Scala değeri tespiti sunmaktadır val , x = e sağ tarafı değerlendirmek etmez, e tanımının değerlendirilmesi parçası olarak kullanılabilir. Daha sonra x kullanılırsa, hemen önceden hesaplanmış e değeri ile değiştirilir , böylece ifadenin tekrar değerlendirilmesine gerek kalmaz.

Örnekle Scala, Martin Odersky

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.