在与 Scala 的同一行中有条件地使用 .reverse

Conditionally using .reverse in the same line with Scala

我在 Scala 中有一个组合器组合,最后一个是 .top,我可以根据布尔参数将其用作 .top(num)(Ordering[(Int, Int)].reverse)

如何根据布尔参数在同一行中实现此组合器组合以使用或不使用 .reverse?我的意思是,不创建另一个 val 来指示是否使用 .reverse

val mostPopularHero = sparkContext
  .textFile("resource/marvel/Marvel-graph.txt") // build up superhero co-apperance data
  .map(countCoOccurrences) // convert to (hero ID, number of connections) RDD
  .reduceByKey((x, y) => x + y) // combine entries that span more than one line
  .map(x => (x._2, x._1)) // flip it from (hero ID, number of connections) to (number of connections, hero ID)
  .top(num)(Ordering[(Int, Int)].reverse)

我不太确定你是否可以访问这里的布尔参数,但你可以按如下方式解决这个问题:

 .top(num)(if (booleanParameter) Ordering[(Int, Int)].reverse else Ordering[(Int, Int)])

解决方案0

正如 nicodp 已经指出的那样,如果范围内有一个布尔变量 b,您可以简单地替换表达式

Ordering[(Int, Int)]

通过 if-表达式

if (b) Ordering[(Int, Int)] else Ordering[(Int, Int)].reverse

我不得不承认这是我能想到的最短、最清晰的解决方案。

但是...我不太喜欢表达式 Ordering[(Int, Int)] 在代码中出现 两次 。在这种情况下这并不重要,因为它很短,但是如果表达式更长一点呢?显然,甚至

所以,我想出了一些方法来不重复 子表达式Ordering[(Int, Int)]。最好的解决方案是如果我们在标准库中有一个默认的 Id-monad 实现,因为这样我们就可以简单地将一个值包装在 pure 中,然后使用布尔值 map 它. 但是标准库中没有 Id 。所以,这里有一些其他的建议,只是针对有问题的表达式变长的情况:

解决方案 1

你可以在scala中使用块作为表达式,所以你可以替换上面的 Ordering[(Int, Int)] 来自:

{val x = Ordering[(Int, Int)]; if (b) x else x.reverse}

更新:等等!这比有重复的版本更短! ;)

解决方案 2

定义有条件地反转排序的函数,将 Ordering[(Int, Int)] 声明为参数类型,然后 而不是重新键入 Ordering[(Int, Int)] 作为表达式,使用 implicitly:

((x: Ordering[(Int, Int)]) => if (b) x else x.reverse)(implicitly)

解决方案 3

我们没有Id,但我们可以滥用其他仿函数的构造函数和消除器。例如,可以将复杂表达式包装在 ListOption 中,然后 map ,然后解包结果。这是 Some 的变体:

Some(Ordering[(Int, Int)]).map{ x => if(b) x else x.reverse }.get

理想情况下,这应该是 Id 而不是 Some。请注意,解决方案 1 对默认环境 monad 做了类似的事情。

解决方案 4

最后,如果上述模式在您的代码中出现不止一次,引入一些额外的语法来处理它可能是值得的:

implicit class ReversableOrderingOps[X](ord: Ordering[X]) {
  def reversedIf(b: Boolean): Ordering[X] = if (b) ord.reverse else ord
}

现在您可以像这样定义顺序:

val myConditionHolds = true
val myOrd = Ordering[(Int, Int)] reversedIf myConditionHolds

或直接在冗长的表达式中使用它:

val mostPopularHero = sparkContext
  .textFile("resource/marvel/Marvel-graph.txt")
  .map(countCoOccurrences)
  .reduceByKey((x, y) => x + y)
  .map(x => (x._2, x._1))
  .top(num)(Ordering[(Int, Int)] reversedIf myConditionHolds)