在论文Eric Torreborre's blogpost的Essence of the Iterator Pattern中,他描述了导线的笛卡尔积也是如何遍历。
任何人都可以使用scalaz library向我展示一个例子,因为我无法弄明白。让我们说问题是,对于List[Int]
,我想提供两个:
Int
总和List[String]
s Int
醇>
我的理解是我可以使用traverse
执行此操作,但这样做的方式只是实际遍历我的结构一次,与此解决方案不同:
val xs = List(1, 2, 3, 4)
val (sum, strings) = (xs.sum, xs map (_.toString + "Z"))
注1 - 我知道还有其他方法可以做到这一点,我不需要遍历这个例子,也不一定是最简单的解决方法。但是,我试图了解遍历,所以我真的在寻找所述问题的答案
编辑 - 感谢下面的 missingfaktor ,了解如何使用State
执行此操作。我想我想知道的是我如何组成两个独立的计算。例如;我的功能概念如下:
val shape = (_ : List[Int]) map (_.toString + "Z")
val accum = (_ : List[Int]).sum
我想让这些累积独立的机制相互依赖,然后选择是否使用 或两者遍历我的List[Int]
他们。我想象一些代码有点像这样:
xs traverse shape //A List[String]
xs traverse accum //An Int
xs traverse (shape <x> accum) //The pair (List[String], Int)
Eric意味着这是可能的,但我不知道如何做到这一点〜即我没有看到如何定义shape
和accum
以便它们可以被组合,以及如何撰写它们。
注意2 shape
和accum
并不意味着具有上述签名的功能。它们是具有执行上述遍历所必需的类型的表达式。
答案 0 :(得分:4)
我在Jason的建议中添加了我自己的答案,以显示遍历列表的不同方式:
import org.specs2._
import scalaz.std.anyVal._, scalaz.std.list._
import scalaz._, std.tuple._
import scalaz.{Monoid, Applicative}
class TraverseSpec extends mutable.Specification {
implicit val Sum = Monoid[Int].applicative
implicit val Concat = Monoid[List[String]].applicative
implicit val A: Applicative[({type λ[α] = (Int, List[String])})#λ] = Sum.product[({type λ[α]=List[String]})#λ](Concat)
val xs = List(1, 2, 3, 4)
"traverse - by folding the list with a Monoid" >> {
val (sum, text) = Foldable[List].foldMap(xs)(a => (a, List(a.toString + "Z")))
(sum, text) === (10, List("1Z", "2Z","3Z", "4Z"))
}
"traverse - with a function returning a tuple" >> {
val (sum, text) = A.traverse(xs)(a => (a, List(a.toString + "Z")))
(sum, text.reverse) === (10, List("1Z", "2Z","3Z", "4Z"))
}
"traverse - with 2 functions and 2 traversals" >> {
val count = (a: Int) => a
val collect = (a: Int) => List(a.toString+"Z")
val sum = Sum.traverse(xs)(count)
val text = Concat.traverse(xs)(collect)
(sum, text.reverse) === (10, List("1Z", "2Z","3Z", "4Z"))
}
"traverse - with 2 functions and 1 fused traversal" >> {
val sum = (a: Int) => a
val collect = (a: Int) => List(a.toString+"Z")
implicit def product[A, B, C](f: A => B): Product[A, B] = Product(f)
case class Product[A, B](f: A => B) {
def <#>[C](g: A => C) = (a: A) => (f(a), g(a))
}
val (total, text) = A.traverse(xs)(sum <#> collect)
(total, text.reverse) === (10, List("1Z", "2Z","3Z", "4Z"))
}
}
我认为最后一个例子展示了你所追求的:2个独立定义的函数,可以组成只进行一次遍历。
答案 1 :(得分:3)
Debasish Ghosh在这个主题上写了一篇不错的post。基于该帖子中的代码:
scala> List(1, 2, 3, 4)
res87: List[Int] = List(1, 2, 3, 4)
scala> .traverse[({ type L[X] = State[Int, X] })#L, String] { cur =>
| state { (acc: Int) => (acc + cur, cur.toString + "Z") }
| }
res88: scalaz.State[Int,List[String]] = scalaz.States$$anon$1@199245
scala> .apply(0)
res89: (Int, List[String]) = (10,List(1Z, 2Z, 3Z, 4Z))
修改强>
您有两个函数List[A] => B
和List[A] => C
,并且您需要一个函数List[A] => (B, C)
。这就是&&&
的用途。这不会融合循环。我无法想象如何为这种情况融合循环。
Fwiw,代码:
scala> val shape = (_ : List[Int]) map (_.toString + "Z")
val accum = (_ : List[Int]).sum
shape: List[Int] => List[java.lang.String] = <function1>
accum: List[Int] => Int = <function1>
scala> val xs = List(1, 2, 3, 4)
xs: List[Int] = List(1, 2, 3, 4)
scala> (shape &&& accum) apply xs
res91: (List[java.lang.String], Int) = (List(1Z, 2Z, 3Z, 4Z),10)
编辑2:
如果您有A => B
和A => C
函数,则可以使用A => (B, C)
将它们合并到&&&
。现在,如果B : Monoid
和C : Monoid
,您可以使用foldMap
获取List[A] => (B, C)
。这将在一个循环中完成。
代码:
scala> val f: Int => Int = identity
f: Int => Int = <function1>
scala> val g: Int => List[String] = i => List(i.toString + "Z")
g: Int => List[String] = <function1>
scala> List(1, 2, 3, 4).foldMap(f &&& g)
res95: (Int, List[String]) = (10,List(1Z, 2Z, 3Z, 4Z))
最终修改(我发誓我不会再次编辑此内容了。)
由于这些概念起源于Haskell,我认为在 Haskell 标签下重新发布这个问题是个好主意,我做到了。 The answer there似乎与我在这个帖子中所说的一致。 Hôpe这有帮助。
答案 2 :(得分:3)
你不会在这里看到一个大赢家,因为你只是将普通的'Monoids推广到Applicative中,所以你将它们融合在一起。
import scalaz.std.anyVal._, scalaz.std.list._, scalaz.std.string._
val Sum = Monoid[Int].applicative
val Concat = Monoid[List[String]].applicative
val A: Applicative[({type λ[α] = (Int, List[String])})#λ] = Sum.product[({type λ[α]=List[String]})#λ](Concat)
val xs = List(1, 2, 3, 4)
val (sum, text) = A.traverse(xs)(a => (a, List(a.toString + "Z")))
println(sum, text) // 10, List("1Z", "2Z", "3Z", "4Z")
也可以使用Monoid[(Int, List[String])]
来解决所述问题:
import scalaz._, std.tuple._
val (sum1, text1) = Foldable[List].foldMap(xs)(a => (a, List(a.toString + "Z")))
println(sum1, text1) // 10, List("1Z", "2Z", "3Z", "4Z")
如果您想要遍历的其中一个效果是非平凡的Applicative,例如State
,那么事情会变得更有趣。
答案 3 :(得分:2)
如果我理解正确的话,你应该在scala-seven分支示例中描述:WordCount。它还涉及国家。我在移动设备上,否则我会提供链接。
这是链接:
HTH Andreas
编辑:
好一些解释。我认为你的问题的根本问题是如何撰写功能或相应的应用。这可以通过应用上的产品方法来实现。
https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Applicative.scala#L46
所以你需要为你的两个函数shape和accum定义applicative。如果累积将被建模为国家申请。
如果我们从这个例子看这行: val WordCount = StateT.stateMonad [Int] .compose({typeλ[α] = Int})#λ
它创造了一个“有效”的应用程序(对不起我的不良措辞)。通常在遍历中,您只有当前元素。但是如果你想在以前的计算中计算你需要状态,那么这会创建一个state-applicative,它为每个遍历的元素返回1(参见Monoid [Int] .applicative)。
现在实际上我们需要查看atWordStart方法,你需要定义一个可以使用构造的WordCount应用程序(使用State)的方法
这是scalaz 6的另一个例子,它更简单。我认为观察initialValue以及transform1方法的作用非常重要:
import scalaz._
import Scalaz._
object StateTraverseExample {
type StS[x] = State[(Set[Int], Boolean), x]
def main(args: Array[String]): Unit = {
println("apparently it works " + countAndMap(Vector.range(0, 20)))
}
def transform1(i: Int, l: Set[Int], result: Boolean): (Set[Int],Boolean) = {
if (result || (l contains i))
(l, true)
else
(l + i, false)
}
def countAndMap(l: Vector[Int]): (Set[Int],Boolean) = {
val initialValue=(Set.empty[Int], false)
val counts = l.traverse[StS, Unit] { i =>
state { case (set, result) => (transform1(i,set,result), println(i)) }
} ~> initialValue
counts
}
}
我现在记得因为这个话题对我很感兴趣。我问为什么埃里克在他的博文中没有提供适用的产品。他说他放弃了与类型签名的摔跤。那个时候杰森为scalaz7修了WordCount的例子(六个例子没有提供动作计数字)