Scala - 在 map 或 flatMap 中访问集合成员
Scala - access collection members within map or flatMap
假设我使用各种map
s and/or flatMap
s 的序列来生成一系列集合。是否可以从任何这些方法中访问有关 "current" 集合的信息?例如,在不知道任何关于前面 map
或 flatMap
中使用的函数的具体信息,并且不使用任何中间声明的情况下,我如何获得最大值(或长度,或第一个元素,等)最后一个 map
所作用的集合?
List(1, 2, 3)
.flatMap(x => f(x) /* some unknown function */)
.map(x => x + ??? /* what is the max element of the collection? */)
编辑澄清:
在示例中,我不是在寻找初始 List
的最大值(或其他)。我正在寻找应用 flatMap
后集合的最大值。
"without using any intermediate declarations" 我的意思是我不想在获得最终结果的途中使用任何临时集合。因此,下面史蒂夫沃尔德曼的例子虽然给出了预期的结果,但并不是我想要的。 (我包括这个条件主要是出于审美原因。)
编辑澄清,第 2 部分:
理想的解决方案是一些神奇的关键字或语法糖,让我引用当前的集合:
List(1, 2, 3)
.flatMap(x => f(x))
.map(x => x + theCurrentList.max)
我准备接受这个事实,然而,这是不可能的。
也许只需将列表定义为 val
,这样您就可以命名它了?我不知道 map(...)
或 flatMap(...)
内置的任何功能会有帮助。
val myList = List(1, 2, 3)
myList
.flatMap(x => f(x) /* some unknown function */)
.map(x => x + myList.max /* what is the max element of the List? */)
更新:至少通过这种方法,如果您有多个转换并且想要查看转换后的版本,您必须为其命名。你可以逃脱
val myList = List(1, 2, 3).flatMap(x => f(x) /* some unknown function */)
myList.map(x => x + myList.max /* what is the max element of the List? */)
或者,如果会有多个转换,养成阶段命名的习惯。
val rawList = List(1, 2, 3)
val smordified = rawList.flatMap(x => f(x) /* some unknown function */)
val maxified = smordified.map(x => x + smordified.max /* what is the max element of the List? */)
maxified
更新 2:观察它在 REPL 中的工作,即使是异构类型也是如此:
scala> def f( x : Int ) : Vector[Double] = Vector(x * math.random, x * math.random )
f: (x: Int)Vector[Double]
scala> val rawList = List(1, 2, 3)
rawList: List[Int] = List(1, 2, 3)
scala> val smordified = rawList.flatMap(x => f(x) /* some unknown function */)
smordified: List[Double] = List(0.40730853571901315, 0.15151641399798665, 1.5305929709857609, 0.35211231420067435, 0.644241939254793, 0.15530230501048903)
scala> val maxified = smordified.map(x => x + smordified.max /* what is the max element of the List? */)
maxified: List[Double] = List(1.937901506704774, 1.6821093849837476, 3.0611859419715217, 1.8827052851864352, 2.1748349102405538, 1.6858952759962498)
scala> maxified
res3: List[Double] = List(1.937901506704774, 1.6821093849837476, 3.0611859419715217, 1.8827052851864352, 2.1748349102405538, 1.6858952759962498)
可能,但不是很漂亮,如果你是为了 "aesthetic reasons."
做的话,也不太可能是你想要的
import scala.math.max
def f(x: Int): Seq[Int] = ???
List(1, 2, 3).
flatMap(x => f(x) /* some unknown function */).
foldRight((List[Int](),List[Int]())) {
case (x, (xs, Nil)) => ((x :: xs), List.fill(xs.size + 1)(x))
case (x, (xs, xMax :: _)) => ((x :: xs), List.fill(xs.size + 1)(max(x, xMax)))
}.
zipped.
map {
case (x, xMax) => x + xMax
}
// Or alternately, a slightly more efficient version using Streams.
List(1, 2, 3).
flatMap(x => f(x) /* some unknown function */).
foldRight((List[Int](),Stream[Int]())) {
case (x, (xs, Stream())) =>
((x :: xs), Stream.continually(x))
case (x, (xs, curXMax #:: _)) =>
val newXMax = max(x, curXMax)
((x :: xs), Stream.continually(newXMax))
}.
zipped.
map {
case (x, xMax) => x + xMax
}
说真的,我只是接受了这个,看看我是否能做到。虽然代码并没有像我预期的那样糟糕,但我仍然不认为它的可读性特别好。我不鼓励在类似于 的东西上使用它。有时,简单地介绍一个 val
而不是教条式的更好。
在当前 map/collect 操作中引用先前输出的一种 somewhat-simple 方法是在地图外部使用命名引用,然后从地图块中引用它:
var prevOutput = ... // starting value of whatever is referenced within the map
myValues.map {
prevOutput = ... // expression that references prior `prevOutput`
prevOutput // return above computed value for the map to collect
}
这引起了我们在构建新序列时引用先前元素这一事实的注意。
不过,如果您想引用任意以前的值,而不仅仅是以前的值,这会更混乱。
您可以按照这些行定义一个 mapWithSelf
(resp. flatMapWithSelf
)操作,并将其作为隐式扩充添加到集合中。对于 List
,它可能看起来像:
// Scala 2.13 APIs
object Enrichments {
implicit class WithSelfOps[A](val lst: List[A]) extends AnyVal {
def mapWithSelf[B](f: (A, List[A]) => B): List[B] =
lst.map(f(_, lst))
def flatMapWithSelf[B](f: (A, List[A]) => IterableOnce[B]): List[B] =
lst.flatMap(f(_, lst))
}
}
充实基本上在操作之前固定集合的值并将其线程化。应该可以对此进行泛化(至少对于严格的集合),尽管它在 2.12 和 2.13+ 中看起来有点不同。
用法看起来像
import Enrichments._
val someF: Int => IterableOnce[Int] = ???
List(1, 2, 3)
.flatMap(someF)
.mapWithSelf { (x, lst) =>
x + lst.max
}
所以在使用现场,它是美观的。请注意,如果您正在计算遍历列表的内容,则每次都会遍历列表(导致二次运行时)。您可以通过一些可变性或仅在 flatMap
.
之后保存中间列表来解决这个问题
假设我使用各种map
s and/or flatMap
s 的序列来生成一系列集合。是否可以从任何这些方法中访问有关 "current" 集合的信息?例如,在不知道任何关于前面 map
或 flatMap
中使用的函数的具体信息,并且不使用任何中间声明的情况下,我如何获得最大值(或长度,或第一个元素,等)最后一个 map
所作用的集合?
List(1, 2, 3)
.flatMap(x => f(x) /* some unknown function */)
.map(x => x + ??? /* what is the max element of the collection? */)
编辑澄清:
在示例中,我不是在寻找初始
List
的最大值(或其他)。我正在寻找应用flatMap
后集合的最大值。"without using any intermediate declarations" 我的意思是我不想在获得最终结果的途中使用任何临时集合。因此,下面史蒂夫沃尔德曼的例子虽然给出了预期的结果,但并不是我想要的。 (我包括这个条件主要是出于审美原因。)
编辑澄清,第 2 部分:
理想的解决方案是一些神奇的关键字或语法糖,让我引用当前的集合:
List(1, 2, 3)
.flatMap(x => f(x))
.map(x => x + theCurrentList.max)
我准备接受这个事实,然而,这是不可能的。
也许只需将列表定义为 val
,这样您就可以命名它了?我不知道 map(...)
或 flatMap(...)
内置的任何功能会有帮助。
val myList = List(1, 2, 3)
myList
.flatMap(x => f(x) /* some unknown function */)
.map(x => x + myList.max /* what is the max element of the List? */)
更新:至少通过这种方法,如果您有多个转换并且想要查看转换后的版本,您必须为其命名。你可以逃脱
val myList = List(1, 2, 3).flatMap(x => f(x) /* some unknown function */)
myList.map(x => x + myList.max /* what is the max element of the List? */)
或者,如果会有多个转换,养成阶段命名的习惯。
val rawList = List(1, 2, 3)
val smordified = rawList.flatMap(x => f(x) /* some unknown function */)
val maxified = smordified.map(x => x + smordified.max /* what is the max element of the List? */)
maxified
更新 2:观察它在 REPL 中的工作,即使是异构类型也是如此:
scala> def f( x : Int ) : Vector[Double] = Vector(x * math.random, x * math.random )
f: (x: Int)Vector[Double]
scala> val rawList = List(1, 2, 3)
rawList: List[Int] = List(1, 2, 3)
scala> val smordified = rawList.flatMap(x => f(x) /* some unknown function */)
smordified: List[Double] = List(0.40730853571901315, 0.15151641399798665, 1.5305929709857609, 0.35211231420067435, 0.644241939254793, 0.15530230501048903)
scala> val maxified = smordified.map(x => x + smordified.max /* what is the max element of the List? */)
maxified: List[Double] = List(1.937901506704774, 1.6821093849837476, 3.0611859419715217, 1.8827052851864352, 2.1748349102405538, 1.6858952759962498)
scala> maxified
res3: List[Double] = List(1.937901506704774, 1.6821093849837476, 3.0611859419715217, 1.8827052851864352, 2.1748349102405538, 1.6858952759962498)
可能,但不是很漂亮,如果你是为了 "aesthetic reasons."
做的话,也不太可能是你想要的import scala.math.max
def f(x: Int): Seq[Int] = ???
List(1, 2, 3).
flatMap(x => f(x) /* some unknown function */).
foldRight((List[Int](),List[Int]())) {
case (x, (xs, Nil)) => ((x :: xs), List.fill(xs.size + 1)(x))
case (x, (xs, xMax :: _)) => ((x :: xs), List.fill(xs.size + 1)(max(x, xMax)))
}.
zipped.
map {
case (x, xMax) => x + xMax
}
// Or alternately, a slightly more efficient version using Streams.
List(1, 2, 3).
flatMap(x => f(x) /* some unknown function */).
foldRight((List[Int](),Stream[Int]())) {
case (x, (xs, Stream())) =>
((x :: xs), Stream.continually(x))
case (x, (xs, curXMax #:: _)) =>
val newXMax = max(x, curXMax)
((x :: xs), Stream.continually(newXMax))
}.
zipped.
map {
case (x, xMax) => x + xMax
}
说真的,我只是接受了这个,看看我是否能做到。虽然代码并没有像我预期的那样糟糕,但我仍然不认为它的可读性特别好。我不鼓励在类似于 val
而不是教条式的更好。
在当前 map/collect 操作中引用先前输出的一种 somewhat-simple 方法是在地图外部使用命名引用,然后从地图块中引用它:
var prevOutput = ... // starting value of whatever is referenced within the map
myValues.map {
prevOutput = ... // expression that references prior `prevOutput`
prevOutput // return above computed value for the map to collect
}
这引起了我们在构建新序列时引用先前元素这一事实的注意。
不过,如果您想引用任意以前的值,而不仅仅是以前的值,这会更混乱。
您可以按照这些行定义一个 mapWithSelf
(resp. flatMapWithSelf
)操作,并将其作为隐式扩充添加到集合中。对于 List
,它可能看起来像:
// Scala 2.13 APIs
object Enrichments {
implicit class WithSelfOps[A](val lst: List[A]) extends AnyVal {
def mapWithSelf[B](f: (A, List[A]) => B): List[B] =
lst.map(f(_, lst))
def flatMapWithSelf[B](f: (A, List[A]) => IterableOnce[B]): List[B] =
lst.flatMap(f(_, lst))
}
}
充实基本上在操作之前固定集合的值并将其线程化。应该可以对此进行泛化(至少对于严格的集合),尽管它在 2.12 和 2.13+ 中看起来有点不同。
用法看起来像
import Enrichments._
val someF: Int => IterableOnce[Int] = ???
List(1, 2, 3)
.flatMap(someF)
.mapWithSelf { (x, lst) =>
x + lst.max
}
所以在使用现场,它是美观的。请注意,如果您正在计算遍历列表的内容,则每次都会遍历列表(导致二次运行时)。您可以通过一些可变性或仅在 flatMap
.