繁体   English   中英

Scala,让我的循环功能更强大

[英]Scala, make my loop more functional

我正在努力减少像Java一样编写Scala(2.8)的程度。 这是我遇到的问题的简化。 您能否建议改进我的“功能更强大”的解决方案?

变换地图

val inputMap = mutable.LinkedHashMap(1->'a',2->'a',3->'b',4->'z',5->'c')

通过丢弃值为'z'的任何条目并在遇到它们时索引字符

第一次尝试

var outputMap = new mutable.HashMap[Char,Int]()
var counter = 0
for(kvp <- inputMap){
  val character = kvp._2
  if(character !='z' && !outputMap.contains(character)){
    outputMap += (character -> counter)
    counter += 1
  }
}

第二次尝试(不是更好,但使用不可变的地图和'foreach')

var outputMap = new immutable.HashMap[Char,Int]()
var counter = 0
inputMap.foreach{
  case(number,character) => {
    if(character !='z' && !outputMap.contains(character)){
      outputMap2 += (character -> counter)
      counter += 1
    }
  }
}

更好的解决方案:

inputMap.toList.filter(_._2 != 'z').map(_._2).distinct.zipWithIndex.toMap

我发现这个解决方案比arjan更简单:

inputMap.values.filter(_ != 'z').toSeq.distinct.zipWithIndex.toMap

个别步骤:

inputMap.values       // Iterable[Char]   = MapLike(a, a, b, z, c)
   .filter(_ != 'z')  // Iterable[Char]   = List(a, a, b, c)
   .toSeq.distinct    // Seq[Char]        = List(a, b, c)
   .zipWithIndex      // Seq[(Char, Int)] = List((a,0), (b,1), (c,2))
   .toMap             // Map[Char, Int]   = Map((a,0), (b,1), (c,2))

请注意,您的问题本身并不涉及地图作为输入,因为您只是丢弃了密钥。 如果我正在编写这个,我可能会写一个像这样的函数

def buildIndex[T](s: Seq[T]): Map[T, Int] = s.distinct.zipWithIndex.toMap

并将其作为

buildIndex(inputMap.values.filter(_ != 'z').toSeq)

首先,如果你在功能上这样做,你应该使用不可变的地图。

然后,为了摆脱某些东西,你使用filter方法:

inputMap.filter(_._2 != 'z')

最后,要进行重新映射,您可以使用zipWithIndex的值(但作为一组), zipWithIndex计数,然后转换回地图:

inputMap.filter(_._2 != 'z').values.toSet.zipWithIndex.toMap

由于价值的顺序无论如何都不会被保留*,可能是因为订单可能已经使用集合转换再次洗牌并不重要。

编辑:有一个类似的更好的解决方案; 看到Arjan的。 假设(*)是错误的,因为它是LinkedHashMap。 所以你需要保留秩序,这是Arjan的解决方案所做的。

我会像这样创建一些“管道”,但这有很多操作,可能会缩短。 这两个List.map可以放在一个,但我想你有一个大概。

inputMap
.toList // List((5,c), (1,a), (2,a), (3,b), (4,z))
.sorted // List((1,a), (2,a), (3,b), (4,z), (5,c))
.filterNot((x) => {x._2 == 'z'}) // List((1,a), (2,a), (3,b), (5,c))
.map(_._2) // List(a, a, b, c)
.zipWithIndex // List((a,0), (a,1), (b,2), (c,3))
.map((x)=>{(x._2+1 -> x._1)}) // List((1,a), (2,a), (3,b), (4,c))
.toMap // Map((1,a), (2,a), (3,b), (4,c))

在列表上执行这些操作会保持元素的排序。

编辑:我误读了OP问题 - 认为你想要运行长度编码。 以下是我对您实际问题的看法:

val values = inputMap.values.filterNot(_ == 'z').toSet.zipWithIndex.toMap

编辑2:如评论中所述,如果保留顺序很重要,请使用toSeq.distinct或类似内容。

val values = inputMap.values.filterNot(_ == 'z').toSeq.distinct.zipWithIndex.toMap

根据我的经验,我发现地图和功能语言并不好看。 您将注意到目前为止所有答案都以某种方式涉及将地图转换为列表,过滤列表,然后将列表转回地图。

我认为这是由于地图本质上是可变数据结构。 考虑在构建列表时,列表的基础结构在附加新元素时不会更改,如果是真实列表,则追加是一个常量O(1)操作。 而对于地图而言,当添加新元素时,地图的内部结构会发生巨大变化,即。 当加载因子变得太高并且添加算法调整地图大小时。 通过这种方式,由于引入新的键/值对可能产生的副作用,函数式语言不能仅创建一系列值并将其弹出到地图中。

也就是说,我仍然认为应该更好地支持过滤,映射和折叠/缩小地图。 由于我们从地图开始,我们知道地图的最大尺寸,并且应该很容易创建一个新地图。

如果你想要掌握功能性编程,那么我建议开始转向地图。 坚持使用函数式语言设计的东西 - 列表操作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM