2017-09-10 21 views
3

例如,讓說我有一個功能隨着斯卡拉要麼,你怎麼停在第一個錯誤,但得到的已經計算的值

def foo(): Either[String, Int] = ??? 

我想調用這個函數的3倍。如果所有的值都是Right,我想要這個總和。如果我有一個Left,我想得到錯誤和所有以前的Right值的總和(〜此時停止計算)。

我發現的唯一辦法做到這一點是:

List(foo, foo, foo).foldLeft((None, 0)) { 
    case ((Some(err), sum), _) => (Some(err), sum) 
    case ((None, sum), fn) => fn() match { 
    case Left(err) => (Some(err), sum) 
    case Right(x) => (None, sum + x) 
    } 
} 

有一些通用的函數式編程功能(與貓或scalaz例如)這樣做呢?

回答

0

差不多與尾遞歸函數的相同溶液:

def sumRights(allFoos: List[() => Either[String, Int]], accum: Int = 0): (Option[String], Int) = { 
    allFoos match { 
     case Nil => (None, accum) 
     case head :: tail => 
     head() match { 
      case Right(x) => sumRights(tail, accum + x) 
      case Left(err) => (Some(err), accum) 
     } 
    } 

另一個勢在必行的解決方案是在第一左分裂與splitWhere,並總結了第一輸出列表的所有值。

+1

你的尾遞歸不滿足'惰性計算的要求foo':「〜此時停止計算」。 – Aivean

+0

確實!重寫爲接受函數列表而不是值列表。 – Rumoku

0

我不知道,如果貓scalaz有專門爲這個事情,但在這裏是如何在香草斯卡拉解決這個問題:

  1. 懶流:

    Stream.continually(foo()).take(3) match { 
        case s => 
        val err = s.collectFirst { 
         case Left(err) => err 
        } 
        val sum = s.takeWhile(_.isRight) 
         .flatMap(_.right.toOption) 
         .sum 
    
        (err, sum) 
    } 
    

Stream將被延遲計算並緩存。

  • 尾遞歸:

    def rec(sum: Int = 0, i: Int = 2): (Option[String], Int) = 
        if (i < 0) (None, sum) else foo() match { 
        case Left(e) => (Some(e), sum) 
        case Right(s) => rec(s + sum, i - 1) 
        } 
    
  • 3

    使用Stream.span

    val (ints, rest) = Stream.continually(foo()).take(3).span(_.isRight) 
    
    val sum = ints.map(_.right.get).sum 
    val error = rest.headOption 
    
    相關問題