假设我有一个数字列表和功能列表:
val xs: List[Int] = List(1, 2, 3)
val fs: List[Int => Int] = List(f1, f2, f3)
现在我想使用Applicative
将f1
应用于1
,将f2
应用于2
等等。
val ys: List[Int] = xs <*> fs // expect List(f1(1), f2(2), f3(3))
如何使用Scalaz
?
答案 0 :(得分:4)
pure
会永久重复该值,因此无法为Scala List
(或类似列表之类的任何内容)定义zippy应用程序实例。 Scalaz确实为Zip
提供了Stream
标签以及相应的zippy应用实例,但据我所知,它仍然非常破碎。例如,这不会起作用(但应该):
import scalaz._, Scalaz._
val xs = Tags.Zip(Stream(1, 2, 3))
val fs = Tags.Zip(Stream[Int => Int](_ + 3, _ + 2, _ + 1))
xs <*> fs
你可以直接使用应用实例(如在另一个答案中那样),但是拥有语法很好,并且编写一个真实的&#34;并不难。 (即没有标记)包装。这是我使用过的解决方法,例如:
case class ZipList[A](s: Stream[A])
import scalaz._, Scalaz._, Isomorphism._
implicit val zipListApplicative: Applicative[ZipList] =
new IsomorphismApplicative[ZipList, ({ type L[x] = Stream[x] @@ Tags.Zip })#L] {
val iso =
new IsoFunctorTemplate[ZipList, ({ type L[x] = Stream[x] @@ Tags.Zip })#L] {
def to[A](fa: ZipList[A]) = Tags.Zip(fa.s)
def from[A](ga: Stream[A] @@ Tags.Zip) = ZipList(Tag.unwrap(ga))
}
val G = streamZipApplicative
}
然后:
scala> val xs = ZipList(Stream(1, 2, 3))
xs: ZipList[Int] = ZipList(Stream(1, ?))
scala> val fs = ZipList(Stream[Int => Int](_ + 10, _ + 11, _ + 12))
fs: ZipList[Int => Int] = ZipList(Stream(<function1>, ?))
scala> xs <*> fs
res0: ZipList[Int] = ZipList(Stream(11, ?))
scala> res0.s.toList
res1: List[Int] = List(11, 13, 15)
对于它的价值,at least a couple of years看起来已被打破。
答案 1 :(得分:1)
我看到了streamZipApplicative
的解决方案:
import scalaz.std.stream._
import scalaz.Tags
val xs: List[Int] = List(1, 2, 3)
val fs: List[Int => Int] = List(f1, f2, f3)
val zippedLists = streamZipApplicative.ap(Tags.Zip(xs.toStream)) (Tags.Zip(fs.toStream))
val result = Tag.unwrap(zippedLists).toList
答案 2 :(得分:1)
学习Scalaz在introduction to Applicatives中就这个主题花了几段话。他们引用LYAHFGG:
然而,[(+ 3),( 2)]&lt; &gt; [1,2]也可以这样工作,左边列表中的第一个函数应用于右边的第一个值,第二个函数应用于第二个值,依此类推。这将产生一个包含两个值的列表,即[4,4]。您可以将其视为[1 + 3,2 * 2]。
但后来补充道:
这可以在Scalaz中完成,但不容易。
“不容易”的部分使用streamZipApplicative
,如@ n1r3的回答:
scala> streamZipApplicative.ap(Tags.Zip(Stream(1, 2)))(Tags.Zip(Stream({(_: Int) + 3}, {(_: Int) * 2})))
res32: scala.collection.immutable.Stream[Int] with Object{type Tag = scalaz.Tags.Zip} = Stream(4, ?)
scala> res32.toList
res33: List[Int] = List(4, 4)
“不容易”是困扰我的部分。我想借用@Travis Brown精彩的回答。他正在比较使用monad和applicatives(即为什么在使用monad时使用applicative?):
第二个(相关地),使用功能最弱的抽象来完成工作只是一个可靠的开发实践。
所以,我会说,直到一个框架提供一个像你的第一个用例一样的应用程序:
val ys: List[Int] = xs <*> fs
在此处使用zip
和map
:
xs.zip(fs).map(p=>p._2.apply(p._1))
对我来说,这段代码比scalaz中的替代代码更清晰,更简单。这是完成工作的最不强大的抽象。