寻找处理关系的FP排名实现(即相等的值)

Rah*_*thy 8 functional-programming scala

从排序的值序列开始,我的目标是为每个值分配一个等级,使用相同的等级来表示相等的值(又称为tie):

输入: Vector(1, 1, 3, 3, 3, 5, 6)

输出: Vector((0,1), (0,1), (1,3), (1,3), (1,3), (2,5), (3,6))

一些类型别名的可读性:

type Rank = Int
type Value = Int
type RankValuePair = (Rank, Value)
Run Code Online (Sandbox Code Playgroud)

使用可变rank变量的命令式实现可能如下所示:

var rank = 0
val ranked1: Vector[RankValuePair] = for ((value, index) <- values.zipWithIndex) yield {
  if ((index > 0) && (values(index - 1) != value)) rank += 1
  (rank, value)
}

// ranked1: Vector((0,1), (0,1), (1,3), (1,3), (1,3), (2,5), (3,6))
Run Code Online (Sandbox Code Playgroud)

为了磨练我的FP技能,我试图提出一个功能实现:

val ranked2: Vector[RankValuePair] = values.sliding(2).foldLeft((0 , Vector.empty[RankValuePair])) {
  case ((rank: Rank, rankedValues: Vector[RankValuePair]), Vector(currentValue, nextValue)) =>
    val newRank = if (nextValue > currentValue) rank + 1 else rank
    val newRankedValues =  rankedValues :+ (rank, currentValue)
    (newRank, newRankedValues)
}._2

// ranked2: Vector((0,1), (0,1), (1,3), (1,3), (1,3), (2,5))
Run Code Online (Sandbox Code Playgroud)

它的可读性较差,而且 - 更重要的是 - 缺少最后一个值(由于使用sliding(2)了奇数个值).

怎么能修复和改进?

Dan*_*ich 11

这适合我:

// scala
val vs = Vector(1, 1, 3, 3, 3, 5, 6)
val rank = vs.distinct.zipWithIndex.toMap
val result = vs.map(i => (rank(i), i))
Run Code Online (Sandbox Code Playgroud)

在使用Javaslang的Java 8中也是如此:

// java(slang)
Vector<Integer>                  vs = Vector(1, 1, 3, 3, 3, 5, 6);
Function<Integer, Integer>       rank = vs.distinct().zipWithIndex().toMap(t -> t);
Vector<Tuple2<Integer, Integer>> result = vs.map(i -> Tuple(rank.apply(i), i));
Run Code Online (Sandbox Code Playgroud)

这两种变体的输出是

Vector((0, 1), (0, 1), (1, 3), (1, 3), (1, 3), (2, 5), (3, 6))
Run Code Online (Sandbox Code Playgroud)

*)披露:我是Javaslang的创造者


jwv*_*wvh 5

这很简洁,但它假设你的Values不会消极.(实际上它只是假设它们永远不能以-1开头.)

val vs: Vector[Value] = Vector(1, 1, 3, 3, 3, 5, 6)

val rvps: Vector[RankValuePair] =
  vs.scanLeft((-1,-1)){ case ((r,p), v) =>
    if (p == v) (r, v) else (r + 1, v)
  }.tail
Run Code Online (Sandbox Code Playgroud)

编辑

根据@Kolmar的建议,不做任何假设的修改.

vs.scanLeft((0,vs.headOption.getOrElse(0))){ case ((r,p), v) =>
  if (p == v) (r, v) else (r + 1, v)
}.tail
Run Code Online (Sandbox Code Playgroud)