2017-04-15 48 views
2

我試圖理解foldMFoldablecats嘗試一個簡單的例子:假設我需要總結列表中的數字,而運行總和爲正值時打破,否則打破。如何使用foldM的摺疊?

val sumUp: (Int, Int) => Option[Int] = (x, y) => { 
    println(s"x = $x, y = $y") 
    val sum = x + y 
    if (sum > 0) Some(sum) else None 
} 

scala> val xs = List(1, 2, 3, -2, -5, 1, 2, 3) 
xs: List[Int] = List(1, 2, 3, -2, -5, 1, 2, 3) 

scala> Foldable[Stream].foldM(xs.toStream, 0)(sumUp) 
x = 0, y = 1 
x = 1, y = 2 
x = 3, y = 3 
x = 6, y = -2 
x = 4, y = -5 
res27: Option[Int] = None 

現在我需要編寫新功能sumUp2獲得輸入流尾,開始所述運行總和變成< = 0和foldM休息。例如,我需要這樣的:

scala> val tail = Foldable[Stream].foldM(xs.toStream, 0)(sumUp2) 
tail: Stream[Int] = Stream(-5, ?) 

scala>tail.toList 
res28: List[Int] = List(-5, 1, 2, 3) 

如何寫sumUp2

回答

3

你可以做的是累積兩個值(在一個元組中):運行總和直到它變成負數或零;和尾巴,然後開始積累價值。

val sumUp2: ((Int, List[Int]), Int) => Id[(Int, List[Int])] = (x, y) => { 
    val sum = if (x._1 < 0) x._1 else x._1 + y 
    if (sum > 0) (sum, x._2) else (-1, x._2 ++ List(y)) 
} 

然後,你可以從第二個元素的尾部元組:

val xs = List(1, 2, 3, -2, -5, 1, 2, 3) 
val res = Foldable[Stream].foldM(xs.toStream, (0, List[Int]()))(sumUp2) 

println(res._2) 

小提琴here

+0

不幸的是''sumUp2'被調用了'xs'的_all_元素,但是我想在運行總和<= 0時停止調用函數。無論如何,謝謝。 – Michael

1

我寫sumUp2返回Either[Int, (Int, Int)]:left是訪問元素的數量,右邊是一對訪問元素的數量和運行總和。

type IntOr[A] = Either[Int, A] 
val sumUp2: ((Int, Int), Int) => IntOr[(Int, Int)] = (pair, y) => { 
    val (size, x) = pair 
    val sum = x + y 
    println(s"sum = $sum, y = $y") 
    if (sum > 0) (size + 1, sum).asRight else size.asLeft 
} 

我們知道,foldM停止時sumUp2回報Left所以sumUp2將不會被調用所有元素:鑑於r: Either[Int, (Int, Int)]

scala> val r = Foldable[Stream].foldM(xs.toStream, (0, 0))(sumUp2) 
sum = 1, y = 1 
sum = 3, y = 2 
sum = 6, y = 3 
sum = 4, y = -2 
sum = -1, y = -5 
r: IntOr[(Int, Int)] = Left(4) 

我們可以得到的尾巴:

scala> r match { case Right(_) => Nil; case Left(n) => xs.drop(n) } 
res63: List[Int] = List(-5, 1, 2, 3) 

的解決方案似乎工作正常,但對我來說並不好看。你會如何改進它?

+0

我正要提出'Either'解決方案。現在,我會保持原樣(+1)。 –

+0

謝謝。當函數返回「Left」時,你知道'foldM'「知道」如何停止嗎?也許我會發表一個關於理解'foldM'如何工作的單獨問題。 – Michael