[英]ZipList with Scalaz
假設我有一個數字列表和一個函數列表:
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
做到這Scalaz
?
zip列表的pure
永遠重復該值,因此無法為Scala的List
(或類似list的東西)定義一個zippy應用實例。 Scalaz確實為Stream
和適當的zippy應用實例提供了一個Zip
標簽,但據我所知它仍然很殘破。 例如,這將不起作用(但應該):
import scalaz._, Scalaz._
val xs = Tags.Zip(Stream(1, 2, 3))
val fs = Tags.Zip(Stream[Int => Int](_ + 3, _ + 2, _ + 1))
xs <*> fs
您可以直接使用應用實例(如在其他答案中所示),但是擁有語法很好,並且編寫“真實的”(即未標記的)包裝並不難。 例如,這是我使用的解決方法:
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)
就其價值而言,看來這已經被打破了至少兩年了 。
我看到了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
學習Scalaz在介紹Applicatives時在此主題上花了幾段。 他們引用LYAHFGG :
但是,[(+ 3),( 2)] < > [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和應用程序的用法(即為什么在有monad的情況下使用應用程序的用法):
其次(以及相關),使用最不強大的抽象來完成工作只是一種可靠的開發實踐。
因此,我要說的是,直到框架提供一個類似於第一個用例的應用程序為止:
val ys: List[Int] = xs <*> fs
要在此處使用zip
和map
,請執行以下操作:
xs.zip(fs).map(p=>p._2.apply(p._1))
對我來說,此代碼比scalaz中的替代代碼更清晰,更簡單。 這是完成工作的最不強大的抽象。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.