假设我们有两个列表:
val l1=List("a","b","c")
val l2 = List("1","2","3")
我想要的是:List("a1", "b2", "c3")
即将l1的第n个元素添加到l2的第n个元素
实现目标的方法是:
(l1 zip l2).map (c => {c._1+c._2})
我只是想知道是否可以通过Applicative实现它。我试过了:
(l1 |@| l2) { _+ _ }
但它提供了所有组合:
List(a1, a2, a3, b1, b2, b3, c1, c2, c3)
有什么想法吗?
谢谢
贝努瓦
答案 0 :(得分:5)
你不能用严格的列表来做,所以改为使用惰性列表,即流。您必须定义Applicative[Stream]
实例,如下所示。 (您可以在名为ZipList的Haskell标准库中找到它。)
scala> val s1 = Stream("a", "b", "c")
s1: scala.collection.immutable.Stream[java.lang.String] = Stream(a, ?)
scala> val s2 = Stream("1", "2", "3")
s2: scala.collection.immutable.Stream[java.lang.String] = Stream(1, ?)
scala> implicit object StreamApplicative extends Applicative[Stream] {
| def pure[A](a: => A) = Stream.continually(a)
| override def apply[A, B](f: Stream[A => B], xs: Stream[A]): Stream[B] = (f, xs).zipped.map(_ apply _)
| }
defined module StreamApplicative
scala> (s1 |@| s2)(_ + _)
res101: scala.collection.immutable.Stream[java.lang.String] = Stream(a1, ?)
scala> .force
res102: scala.collection.immutable.Stream[java.lang.String] = Stream(a1, b2, c3)
无法通过严格列表进行此操作的原因是因为无法在其上定义满足适用法律的pure
。
顺便说一下,Scala让你比OP中使用的代码更简洁:
scala> (l1, l2).zipped.map(_ + _)
res103: List[java.lang.String] = List(a1, b2, c3)
答案 1 :(得分:3)
答案是,就我所见,你无法用一个应用程序实现这一目标。正如您所知,列表的应用程序将该功能应用于所有组合。对于你想要的东西并不是很好,但对于创造笛卡尔产品这样的东西来说真棒。
稍微不那么详细的方法可能会使用scalaz提供的Tuple2W.fold
:
(l1 zip l2).map (_ fold (_ + _))