Scala'da foldLeft ve reduceLeft arasındaki fark


196

Temel fark öğrendim arasında foldLeftvereduceLeft

foldLeft:

  • başlangıç ​​değeri geçilmelidir

reduceLeft:

  • koleksiyonun ilk öğesini başlangıç ​​değeri olarak alır
  • koleksiyon boşsa istisna atar

Başka bir fark var mı?

Benzer işlevselliğe sahip iki yöntemin olması için belirli bir neden var mı?


Tavsiye etmenizi tavsiye ederim stackoverflow.com/questions/25158780/…
samthebest

Soruyu "Scala'da katlama ve azaltma arasındaki fark" olarak düzenlediyseniz harika olur.
pedram bashiri

Yanıtlar:


303

Gerçek cevabı vermeden önce burada belirtilecek birkaç şey:

  • Sorunuzla hiçbir ilgisi yok left, daha çok azaltma ve katlama arasındaki farkla ilgili
  • Fark uygulama değildir, sadece imzalara bakın.
  • Sorunun özellikle Scala ile bir ilgisi yok, daha ziyade fonksiyonel programlamanın iki kavramı hakkında.

Sorunuza geri dönün:

İşte imzası foldLeft ( foldRightyapacağım nokta için de olabilirdi ):

def foldLeft [B] (z: B)(f: (B, A) => B): B

Ve işte imzası reduceLeft (yine yön burada önemli değil)

def reduceLeft [B >: A] (f: (B, A) => B): B

Bu ikisi birbirine çok benzer ve karışıklığa neden oldu. reduceLeftözel bir durumfoldLeft (bu arada bazen aynı şeyi ikisinden birini kullanarak ifade edebileceğiniz anlamına gelir ).

Aradığınızda reduceLeftbir üzerinde söz sahibi List[Int]bu anlamıyla Çeşidi olacak tek bir değer, içine tamsayılar bütün liste azaltacaktırInt (veya bir süpertip Intdolayısıyla [B >: A]).

Aradığınızda foldLefta söylemekList[Int] tek bir değere (kağıt parçası haddeleme hayal) tüm listeyi kat edeceği, ancak bu değer bile ilişkili olmak zorunda değildir Int(dolayısıyla [B]).

İşte bir örnek:

def listWithSum(numbers: List[Int]) = numbers.foldLeft((List.empty[Int], 0)) {
   (resultingTuple, currentInteger) =>
      (currentInteger :: resultingTuple._1, currentInteger + resultingTuple._2)
}

Bu yöntem a alır List[Int]ve a Tuple2[List[Int], Int]veya döndürür (List[Int], Int). Toplamı hesaplar ve bir tamsayılar listesi içeren bir tuple döndürür. Bu arada liste geri döndürülür, çünküfoldLeft yerinefoldRight .

Daha ayrıntılı bir açıklama için hepsini yönetmek için Bir Katlamayı izleyin .


Neden Bbir süpertip olduğunu açıklayabilir misiniz A? BAslında Abir süper tip değil, bir alt tipi olmalı gibi görünüyor . Örneğin, Banana <: Fruit <: Foodbir Fruits listemiz olsaydı , bunun bazı Bananas içerebileceği varsayılarak , herhangi bir Foods içeriyorsa , tür Fooddoğru olur mu? Yani bu durumda, Bbir üst tip olduğu Ave her iki içeren bir liste var Bs ve As, liste tipte olmalıdır B, değil A. Bu tutarsızlığı açıklayabilir misiniz?
socom1880

Sorunuzu doğru anladığımdan emin değilim. 5 yaşındaki cevabım azaltma işlevi hakkında söylediklerim, a'nın List[Banana]tekli Bananaveya tekli Fruitveya tekli olarak azaltılabileceğidir Food. Çünkü Fruit :> Bananave `` Yiyecek:> Muz ''.
agilesteel

Evet ... bu gerçekten mantıklı. Başlangıçta anlamsız Bananabir Fruit" tür listesi içerebilir " olarak yorumluyordum . Açıklamanız mantıklıdır - filetilen işlev reduce()a Fruitveya a ile sonuçlanabilir Food, bu da Bimzada bir alt sınıf değil, bir üst sınıf olmalıdır.
socom1880

193

reduceLeftsadece kolaylık sağlayan bir yöntemdir. Eşdeğerdir

list.tail.foldLeft(list.head)(_)

11
İyi, özlü cevap :) reducelftGerçi yazım düzeltmek isteyebilirsiniz
Hanxue

10
İyi cevap. Bu aynı zamanda foldboş bir listede neden çalışmadığını da vurgular reduce.
Mansur Siddiqui

44

foldLeftdaha genel bir yöntemdir, orijinal olarak koyduğunuzdan tamamen farklı bir şey üretmek için kullanabilirsiniz. Oysa reduceLeftyalnızca aynı türden veya toplama türünün süper türünden bir sonuç üretebilir. Örneğin:

List(1,3,5).foldLeft(0) { _ + _ }
List(1,3,5).foldLeft(List[String]()) { (a, b) => b.toString :: a }

En foldLeftson katlanan sonuçla (ilk değeri ilk kez kullanırken) ve sonraki değeri içeren kapanışı uygular.

reduceLeftÖte yandan, ilk önce listeden iki değeri birleştirecek ve bunları kapatmaya uygulayacaktır. Daha sonra değerlerin geri kalanını kümülatif sonuçla birleştirecektir. Görmek:

List(1,3,5).reduceLeft { (a, b) => println("a " + a + ", b " + b); a + b }

Liste boşsa foldLeft, başlangıç ​​değeri yasal bir sonuç olarak sunulabilir. reduceLeftdiğer yandan listede en az bir değer bulamazsa yasal bir değeri yoktur.


5

Her ikisinin de Scala standart kütüphanesinde olmasının temel nedeni muhtemelen her ikisinin de Haskell standart kütüphanesinde ( foldlve olarak adlandırılır foldl1) olmasıdır. Olmasaydı reduceLeft, genellikle farklı projelerde bir kolaylık yöntemi olarak tanımlanırdı.


5

Başvuru için, reduceLeftaşağıdaki hatayla boş bir kapsayıcıya uygulanırsa hata verir.

java.lang.UnsupportedOperationException: empty.reduceLeft

Kullanılacak kodu yeniden işleme

myList foldLeft(List[String]()) {(a,b) => a+b}

potansiyel bir seçenektir. Başka reduceLeftOptionbir seçenek Seçenek sarılmış sonucu döndüren varyantı kullanmaktır .

myList reduceLeftOption {(a,b) => a+b} match {
  case None    => // handle no result as necessary
  case Some(v) => println(v)
}

2

Gönderen Scala Fonksiyonel Programlama İlkeleri (Martin Odersky):

Fonksiyon reduceLeftdaha genel bir fonksiyon olarak tanımlanır foldLeft,.

foldLeftgibidir reduceLeftancak boş bir listede çağrıldığında döndürülen ek bir parametre olarak bir akümülatör alır :zfoldLeft

(List (x1, ..., xn) foldLeft z)(op) = (...(z op x1) op ...) op x

[aksine reduceLeft, boş bir listede çağrıldığında bir istisna atar.]

Elbette, desen eşleştirme ve özyineleme kullanımlarında çok benzer olmalarına rağmen, bu işlevlerin farklılıklarını gösteren soyut tanımlarını sağlar.

abstract class List[T] { ...
  def reduceLeft(op: (T,T)=>T) : T = this match{
    case Nil     => throw new Error("Nil.reduceLeft")
    case x :: xs => (xs foldLeft x)(op)
  }
  def foldLeft[U](z: U)(op: (U,T)=>U): U = this match{
    case Nil     => z
    case x :: xs => (xs foldLeft op(z, x))(op)
  }
}

Not foldLefttürde bir değer verir Umutlaka aynı türde değildir, List[T]ama reduceLeft listesi aynı tipte) bir değerini verir.


0

Katlama / azaltma ile ne yaptığınızı gerçekten anlamak için şunu kontrol edin: http://wiki.tcl.tk/17983 çok iyi bir açıklama. katlama kavramını aldıktan sonra, azaltma yukarıdaki cevapla bir araya gelecek: list.tail.foldLeft (list.head) (_)

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.