Scala

2017-04-02 22 views
5

'deki bir satıra kadar listeden bir öğe almanın işlevsel yolu Yöntemin amacı, bir sınır ulaşılana kadar öğeleri bir listede almaktır.Scala

örn.

Ben 2 farklı uygulamaları

def take(l: List[Int], limit: Int): List[Int] = { 
    var sum = 0 
    l.takeWhile { e => 
    sum += e 
    sum <= limit 
    } 
} 

O basittir ile geldim, ama bir değişken devlet kullanılır.

def take(l: List[Int], limit: Int): List[Int] = { 
    val summed = l.toStream.scanLeft(0) { case (e, sum) => sum + e } 
    l.take(summed.indexWhere(_ > limit) - 1) 
} 

Daha temiz görünüyor, ancak bir akış gerektiğinden daha fazla verim ve daha az bellek etkin.

Daha iyi bir yolu var mı?

def take(l: List[Int], limit: Int): List[Int] = 
    l.fold((List.empty[Int], 0)) { case ((res, acc), next) => 
     if (acc + next > limit) 
     (res, limit) 
     else 
     (next :: res, next + acc) 
    } 

standart listeleri tembel değildir ve ne kat olduğu için, bu her zaman listenin tamamını üzerinden geçer:

cevap

4

Ayrıca kat ile tek geçişte bunu yapamaz. Bir alternatif, sınırlara ulaşıldığında kısa devrelerin uygulanması için cats' iteratorFoldM kullanılmasıdır.

Ayrıca kısa devre bu doğrultuda bir kuyruk Özyinelemeyi, bir şey kullanarak doğrudan katlamak yazabiliriz:

o kanıtlamak için ek çaba gerektirir olarak bu ikinci çözüm daha az zarif ayrıca daha hızlı olabilir ama o
def take(l: List[Int], limit: Int): List[Int] = { 
    @annotation.tailrec 
    def take0(list: List[Int], accList: List[Int], accSum: Int) : List[Int] = 
    list match { 
     case h :: t if accSum + h < limit => 
     take0(t, h :: accList, h + accSum) 
     case _ => accList 
    } 
    take0(l, Nil, 0).reverse 
} 

Not Uygulama sona erer, bir kat kullanırken belirgin bir şey.

+0

İkinci yönteminizle ilgili olarak: küçük bir yazım hatası var, L6'da 'take0' var.Ve liste ters sırada döndürülür. Scala standart lib, wdyt bir kat eksik değil mi? –

1

İlk yol, işlevinizin sonucu olarak mükemmel bir şekilde değiştirilemediği için mükemmel bir para cezasıdır.

Bir yan notta, bu aslında scala koleksiyonu kitaplığının kaç işlevinin uygulandığıdır, verimlilik için değişken bir oluşturucu oluşturur ve bunun dışında değişmez bir koleksiyon döndürür.

+0

Soru "bu kadar iyi" değil, "bunun nasıl işlevsel bir şekilde yapılacağı" ... – OlivierBlanvillain

+0

Senden birkaç cevap gördüm, iyiler! yardım ettiğin için teşekkür ederim! –

+0

Nazik ve cesaret verici kelimeler için teşekkür ederim. –

1

İşlevsel bir yol, yinelemeli işlev kullanmak ve yığının güvenli olduğundan emin olmaktır.

sadece temel scala kullanıyorsanız: Eğer scalaz Trambolin kullanabiliyorsa

import scala.annotation.tailrec 
    def take(l: List[Int], limit: Int) : List[Int] = { 
     @tailrec 
     def takeHelper(l:List[Int], limit:Int, r:List[Int]):List[Int] = 
     l match { 
      case h::t if (h <= limit) => takeHelper(t, limit-h, r:+h) 
      case _ => r 
     } 
     takeHelper(l, limit, Nil) 
    } 

, biraz daha güzel:

import scalaz._ 
import scalaz.Scalaz._ 
import Free._ 
def take(l: List[Int], limit: Int): Trampoline[List[Int]] = { 

    l match { 
     case h :: t if (h <= limit) => suspend(take(t, limit - h)).map(h :: _) 


    case _      => return_(Nil) 
    } 
    } 
    println(take(List(1, 2, 3, 4, 0, 0, 1), 10).run) 
    println(take(List.fill(10000)(1), 100000000).run) 
0

Kendi özelleştir yolunu uzatmak istiyorsanız, olabilir sonra böyle diyoruz

def custom(con: => Boolean)(i: Int)(a: => List[Int])(body: => Unit): List[Int] = { 
     if (con) { 
     body 
     custom(con)(i + 1)(a)(body) 
     } 
     else { 
     a.slice(0, i) 
     } 
    } 

: ayrıca gibi bir şey kullanmak

var j = 100 
    val t = customTake(j > 80)(0)((0 to 99).toList) { 
     j -= 1 
    } 
    println(t) 
0

Sanırım ikinci sürümünüz oldukça iyi. Böyle, bunu biraz küçük değişiklikler yapabilir:

val sums = l.toStream.scanLeft(0){_ + _} drop 1 
l zip sums takeWhile {_._2 <= limit} map (_._1) 

genellikle takip etmek biraz daha kolaydır Eğer endeksleri ile ilgili değildir Bu şekilde,.

+0

İyi fakat sıkıştırma gerekli değil: 'val sums = l.toStream.scan (0) (_ + _); l.take (sums.lastIndexWhere (_ <= limit)) ' – jwvh