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的创造者
这很简洁,但它假设你的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)