将函数应用于Map
的每个元素的最佳方法是什么,最后返回相同的Map
,不变,以便可以在进一步的操作中使用它?
我想避免:
myMap.map(el => {
effectfullFn(el)
el
})
实现这样的语法:
myMap
.mapEffectOnKV(effectfullFn)
.foreach(println)
map
不是我想要的,因为我必须指定地图中的内容(如第一个代码段中所示),而我不想这样做。
我想要一个知道/假设的特殊操作,在执行副作用功能后,应该不返回地图元素。
事实上,这对我来说非常有用,我想为Map
,Array
,List
,Seq
,{{ 1}} ...一般的想法是窥视要做某事的元素,然后自动返回这些元素。
我正在研究的真实案例如下:
Iterable
一旦我计算了每个分片的统计信息,我想附加将它们保存到磁盘的副作用,然后只返回这些统计信息,而不必创建 calculateStatistics(trainingData, indexMapLoaders)
.superMap { (featureShardId, shardStats) =>
val outputDir = summarizationOutputDir + "/" + featureShardId
val indexMap = indexMapLoaders(featureShardId).indexMapForDriver()
IOUtils.writeBasicStatistics(sc, shardStats, outputDir, indexMap)
}
并具有{{1} } name是函数中的最后一个语句,例如:
val
它可能不是很难实现,但我想知道Scala中是否已经存在某些内容。
答案 0 :(得分:2)
根据定义,函数不能有效,因此我不希望在scala-lib中有任何方便。但是,您可以编写包装器:
def tap[T](effect: T => Unit)(x: T) = {
effect(x)
x
}
示例:
scala> Map(1 -> 1, 2 -> 2)
.map(tap(el => el._1 + 5 -> el._2))
.foreach(println)
(1,1)
(2,2)
您还可以定义隐式:
implicit class TapMap[K,V](m: Map[K,V]){
def tap(effect: ((K,V)) => Unit): Map[K,V] = m.map{x =>
effect(x)
x
}
}
示例:
scala> Map(1 -> 1, 2 -> 2).tap(el => el._1 + 5 -> el._2).foreach(println)
(1,1)
(2,2)
要抽象更多内容,您可以在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)
它更有用,但需要一些&#34; mamba-jumbo&#34;由于Coll[_] <: TraversableOnce[_]
不起作用的类型(Scala 2.12.1),所以我不得不使用证据。
您还可以尝试CanBuildFrom
方法:How to enrich a TraversableOnce with my own generic map?
关于处理迭代器的直通副作用的总体建议是使用Stream
s(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)
答案 1 :(得分:1)
看起来你所追求的概念类似于Unix tee
实用程序 - 获取输入并将其指向两个不同的输出。 (tee
从字母&#39; T&#39;的形状得到它的名字,它看起来像一个
管道从左到右,另一条管线向下分支。)
这是Scala版本:
package object mypackage {
implicit class Tee[A](a: A) extends AnyVal {
def tee(f: A => Unit): A = { f(a); a }
}
}
有了这个,我们可以做到:
calculateStatistics(trainingData, indexMapLoaders) tee { stats =>
stats foreach { case (featureShardId, shardStats) =>
val outputDir = summarizationOutputDir + "/" + featureShardId
val indexMap = indexMapLoaders(featureShardId).indexMapForDriver()
IOUtils.writeBasicStatistics(sc, shardStats, outputDir, indexMap)
}
}
请注意,根据定义,Tee
非常通用 - 它可以有效
对任何值进行操作,然后返回原始传入的值。
答案 2 :(得分:0)
使用您的有效功能调用foreach
上的Map
。原始Map
不会被更改,因为scala中的Maps
是不可变的。
val myMap = Map(1 -> 1)
myMap.foreach(effectfullFn)
如果您尝试将此操作链接起来,可以使用map
myMap.map(el => {
effectfullFn(el)
el
})