Soru Scala'da birden fazla parametre listesi ve liste başına birden çok parametre arasındaki fark nedir?


Scala'da yazabilirsin (curried?)

def curriedFunc(arg1: Int) (arg2: String) = { ... }

Yukarıdakiler arasındaki fark nedir curriedFunc Tek parametre listesinde iki parametre listesi ve çoklu parametrelerle fonksiyon tanımı:

def curriedFunc(arg1: Int, arg2: String) = { ... }

Matematiksel açıdan bu (curriedFunc(x))(y) ve curriedFunc(x,y) ama yazabilirim def sum(x) (y) = x + y ve aynı olacak def sum2(x, y) = x + y

Tek bir fark biliyorum - bu kısmen uygulanan işlevlerdir. Ama her iki yol benim için eşdeğerdir.

Başka farklılıklar var mı?


76
2017-07-23 20:48


Menşei




Cevaplar:


Kesin olarak söylemek gerekirse, bu bir körelmiş işlev değildir, ancak bir işlev gibi görünse de, çoklu argüman listelerine sahip bir yöntemdir.

Dediğiniz gibi, çoklu argümanlar listesi, yöntemin kısmen uygulanmış bir fonksiyonun yerine kullanılmasına izin verir. (Genelde kullandığım aptalca örnekler için üzgünüm)

object NonCurr {
  def tabulate[A](n: Int, fun: Int => A) = IndexedSeq.tabulate(n)(fun)
}

NonCurr.tabulate[Double](10, _)            // not possible
val x = IndexedSeq.tabulate[Double](10) _  // possible. x is Function1 now
x(math.exp(_))                             // complete the application

Diğer bir avantaj ise ikinci argüman listesi tek bir fonksiyondan veya thunktan ibaretse parantez yerine kaşlı ayraçlar kullanabilmenizdir. Örneğin.

NonCurr.tabulate(10, { i => val j = util.Random.nextInt(i + 1); i - i % 2 })

e karşı

IndexedSeq.tabulate(10) { i =>
  val j = util.Random.nextInt(i + 1)
  i - i % 2
}

Ya da thunk için:

IndexedSeq.fill(10) {
  println("debug: operating the random number generator")
  util.Random.nextInt(99)
}

Bir başka avantaj ise, varsayılan argüman değerlerini tanımlamak için önceki argüman listesindeki argümanlara başvurabilirsiniz (bunun tek bir listede bunu yapamayacağının bir dezavantajı olduğunu da söyleyebiliriz :)

// again I'm not very creative with the example, so forgive me
def doSomething(f: java.io.File)(modDate: Long = f.lastModified) = ???

Son olarak, ilgili gönderiye yanıt olarak üç başka uygulama var Scala neden her iki liste için birden fazla parametre listesi ve çoklu parametre sağlıyor? . Onları buraya kopyalayacağım ama kredi Knut Arne Vedaa, Kevin Wright ve ekstempore'a gidiyor.

İlk olarak, birden fazla var arjiniz olabilir:

def foo(as: Int*)(bs: Int*)(cs: Int*) = as.sum * bs.sum * cs.sum

... tek bir argüman listesinde mümkün olmazdı.

İkincisi, tür çıkarımına yardımcı olur:

def foo[T](a: T, b: T)(op: (T,T) => T) = op(a, b)
foo(1, 2){_ + _}   // compiler can infer the type of the op function

def foo2[T](a: T, b: T, op: (T,T) => T) = op(a, b)
foo2(1, 2, _ + _)  // compiler too stupid, unfortunately

Ve son olarak, bu örtük ve örtük olmayan hatalara sahip olmanın tek yoludur. implicit tüm argüman listesi için bir değiştirici:

def gaga [A](x: A)(implicit mf: Manifest[A]) = ???   // ok
def gaga2[A](x: A, implicit mf: Manifest[A]) = ???   // not possible

81
2017-07-23 23:21



En çok oyulan cevap olduğu için, sorunun başlığı artık cevabına uymuyor. Bence başlık, "Scala, her iki liste için birden çok parametre listesi ve birden çok parametre neden sağlıyor?" Şeklinde değiştirilmelidir, yani, şu örneklerle zaten birleştirilmiştir: stackoverflow.com/questions/4684185/.... - Jacek Laskowski


0 __ tarafından mükemmel bir şekilde karşılanmayan başka bir fark var. Cevap: varsayılan parametreler. Bir parametre listesinden bir parametre, varsayılanı başka bir parametre listesinde hesaplarken, ancak aynı parametrede kullanılamaz.

Örneğin:

def f(x: Int, y: Int = x * 2) = x + y // not valid
def g(x: Int)(y: Int = x * 2) = x + y // valid

39
2017-07-26 02:57



Bu basit örnekte, bunun batırılması gerekiyordu. Bu aslında varsayılan parametreleri çok daha kullanışlı hale getiriyor. Teşekkürler! - Mike McFarland
İyi örnek, hariç, onu aramak için beş dakika harcadım: g(1)() 3 döndürür. g(1)(2) 5 döndürür. - Sapience


Bütün nokta bu, körleşmiş ve uncurried formlar eşdeğerdir! Başkalarının işaret ettiği gibi, bir veya diğer formu olabilir sözdizimsel duruma bağlı olarak çalışmak daha uygun ve diğerini tercih etmenin tek nedeni budur.

Scala'nın, körelmiş işlevleri açıklamak için özel bir sözdizimi olmasa bile, bunları hala yapılandırabildiğini anlamak önemlidir; Bu, işlevleri döndüren işlevler oluşturma yeteneğiniz olduğunda, sadece matematiksel bir kaçınılmazlıktır.

Bunu göstermek için, def foo(a)(b)(c) = {...} sözdizimi yoktu. Öyleyse yine aynı şeyi elde edebilirsiniz: def foo(a) = (b) => (c) => {...}.

Scala'daki birçok özellik gibi, bu zaten mümkün olabilecek bir şey yapmak için sözdizimsel bir kolaylık, ancak biraz daha fazla ayrıntıya sahip.


18
2017-07-23 23:18





İki form izomorftur. En önemli fark, körelmiş fonksiyonların kısmen uygulanmasının daha kolay olması, kör olmayan fonksiyonların ise en azından Scala'da biraz daha güzel sözdizimine sahip olmasıdır.


4
2017-07-23 20:55



Daha önce olmadığı söylenmişti, örnekler değil curried fonksiyonları? Körleşmiş bir fonksiyonun sadece tek bir argümanı olduğunu anlıyorum ve tek bir argümanla bir işlevi döndürebilir ve böylece tüm argümanların kapalı olduğu bir vücut ortaya çıkana kadar. Yanlış mıyım? - Jacek Laskowski