将函数应用于 a 的每个元素Map并最终返回相同的Map,不变的,以便它可以用于进一步的操作的最佳方法是什么?
我想避免:
myMap.map(el => {
effectfullFn(el)
el
})
Run Code Online (Sandbox Code Playgroud)
实现这样的语法:
myMap
.mapEffectOnKV(effectfullFn)
.foreach(println)
Run Code Online (Sandbox Code Playgroud)
map 不是我要找的,因为我必须指定地图中出现的内容(如第一个代码片段中所示),而我不想这样做。
我想要一个特殊的操作,它知道/假设在执行副作用函数后应该不加更改地返回地图元素。
事实上,这对我来说非常有用,我想把它用于Map, Array, List, Seq,Iterable ... 总的想法是偷看元素做某事,然后自动返回这些元素。
我正在处理的真实案例如下所示:
calculateStatistics(trainingData, indexMapLoaders)
.superMap { (featureShardId, shardStats) =>
val outputDir = summarizationOutputDir + "/" + featureShardId
val indexMap = indexMapLoaders(featureShardId).indexMapForDriver()
IOUtils.writeBasicStatistics(sc, shardStats, outputDir, indexMap)
}
Run Code Online (Sandbox Code Playgroud)
一旦我计算了每个分片的统计信息,我想附加将它们保存到磁盘的副作用,然后只返回这些统计信息,而不必创建 aval并将其val名称作为函数中的最后一个语句,例如:
val stats = calculateStatistics(trainingData, indexMapLoaders)
stats.foreach { (featureShardId, shardStats) =>
val outputDir = summarizationOutputDir + "/" + featureShardId
val indexMap = indexMapLoaders(featureShardId).indexMapForDriver()
IOUtils.writeBasicStatistics(sc, shardStats, outputDir, indexMap)
}
stats
Run Code Online (Sandbox Code Playgroud)
这可能不是很难实现,但我想知道 Scala 中是否已经有一些东西可以实现。
根据定义,函数不能有效,所以我不期望 scala-lib 中有任何方便的东西。但是,您可以编写一个包装器:
def tap[T](effect: T => Unit)(x: T) = {
effect(x)
x
}
Run Code Online (Sandbox Code Playgroud)
例子:
scala> Map(1 -> 1, 2 -> 2)
.map(tap(el => el._1 + 5 -> el._2))
.foreach(println)
(1,1)
(2,2)
Run Code Online (Sandbox Code Playgroud)
您还可以定义隐式:
implicit class TapMap[K,V](m: Map[K,V]){
def tap(effect: ((K,V)) => Unit): Map[K,V] = m.map{x =>
effect(x)
x
}
}
Run Code Online (Sandbox Code Playgroud)
例子:
scala> Map(1 -> 1, 2 -> 2).tap(el => el._1 + 5 -> el._2).foreach(println)
(1,1)
(2,2)
Run Code Online (Sandbox Code Playgroud)
为了进行更多抽象,您可以在 上定义此隐式TraversableOnce,因此它将适用于List,Set依此类推(如果需要):
implicit class TapTraversable[Coll[_], T](m: Coll[T])(implicit ev: Coll[T] <:< TraversableOnce[T]){
def tap(effect: T => Unit): Coll[T] = {
ev(m).foreach(effect)
m
}
}
scala> List(1,2,3).tap(println).map(_ + 1)
1
2
3
res24: List[Int] = List(2, 3, 4)
scala> Map(1 -> 1).tap(println).toMap //`toMap` is needed here for same reasons as it needed when you do `.map(f).toMap`
(1,1)
res5: scala.collection.immutable.Map[Int,Int] = Map(1 -> 1)
scala> Set(1).tap(println)
1
res6: scala.collection.immutable.Set[Int] = Set(1)
Run Code Online (Sandbox Code Playgroud)
它更有用,但需要一些带有类型的“mamba-jumbo”,因为它Coll[_] <: TraversableOnce[_]不起作用(Scala 2.12.1),所以我必须为此使用证据。
您还可以尝试CanBuildFrom方法:How to丰富 TraversableOnce with my own generic map?
关于处理迭代器上的直通副作用的总体建议是使用Streams (scalaz/fs2/monix) 和Task,因此它们有一个observe(或它的一些类似)函数,可以以异步(如果需要)方式执行您想要的操作。
在你提供你想要的例子之前我的回答
您可以表示有效的计算而没有副作用,并且具有表示之前和之后状态的不同值:
scala> val withoutSideEffect = Map(1 -> 1, 2 -> 2)
withoutSideEffect: scala.collection.immutable.Map[Int,Int] = Map(1 -> 1, 2 -> 2)
scala> val withSideEffect = withoutSideEffect.map(el => el._1 + 5 -> (el._2 + 5))
withSideEffect: scala.collection.immutable.Map[Int,Int] = Map(6 -> 6, 7 -> 7)
scala> withoutSideEffect //unchanged
res0: scala.collection.immutable.Map[Int,Int] = Map(1 -> 1, 2 -> 2)
scala> withSideEffect //changed
res1: scala.collection.immutable.Map[Int,Int] = Map(6 -> 6, 7 -> 7)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2889 次 |
| 最近记录: |