Scala - 访问map或flatMap中的集合成员

时间:2017-06-13 22:30:59

标签: scala collections flatmap

假设我使用各种map和/或flatMap s的序列来生成一系列集合。是否可以从任何这些方法中访问有关“当前”集合的信息?例如,在不知道前一个mapflatMap中使用的函数的任何具体内容的情况下,如果不使用任何中间声明,我如何获得最大值(或长度,或第一个元素,最后map行动的集合的哪一部分?

List(1, 2, 3)
  .flatMap(x => f(x) /* some unknown function */)
  .map(x => x + ??? /* what is the max element of the collection? */)

编辑以澄清:

  1. 在示例中,我不是在寻找初始List的最大值(或其他值)。在flatMap被应用后,我正在寻找最大的收藏。

  2. 通过“不使用任何中间声明”,我的意思是我不想在途中使用任何临时集合到最终结果。因此,下面的史蒂夫沃尔德曼的例子虽然给出了理想的结果,但并不是我所追求的。 (我包括这种情况主要是出于审美原因。)

  3. 编辑以澄清,第2部分:

    理想的解决方案是一些神奇的关键词或语法糖,让我参考当前的集合:

    List(1, 2, 3)
      .flatMap(x => f(x))
      .map(x => x + theCurrentList.max)
    

    然而,我准备接受这样的事实,即这根本不可能。

2 个答案:

答案 0 :(得分:2)

也许只是将列表定义为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)

答案 1 :(得分:2)

可能,但不是很漂亮,如果你出于“审美原因”这样做,也不太可能是你想要的东西。

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
  }

说真的,我只是接受了这个,看看能否做到。虽然代码没有像我预期的那样糟糕,但我仍然认为它不是特别易读。我不鼓励使用类似于Steve Waldman's answer的东西。有时,仅仅引入一个val更好,而不是对它有教条。