如何使用foldM of Foldable?

时间:2017-04-15 09:36:49

标签: scala lazy-evaluation fold scala-cats

我试图理解foldMFoldable的{​​{1}}尝试一个简单的例子:假设我需要总结一个列表中的数字,而运行总和为正数并且在它不是。

cats

现在我需要编写新函数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 来获取输入流尾部,它从运行总和变为< = 0并且sumUp2中断开始。例如,我需要得到这样的东西:

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)

2 个答案:

答案 0 :(得分: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

答案 1 :(得分: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返回sumUp2Left停止,因此sumUp2不会为所有元素调用:

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)

鉴于r: Either[Int, (Int, Int)]我们可以得到尾巴:

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

解决方案似乎运行良好,但对我来说并不好看。你会如何改进它?