【问题标题】:Scala HashMap throwing key not found exceptionScala HashMap 抛出键未找到异常
【发布时间】:2014-04-12 20:26:16
【问题描述】:

我对 Scala 非常陌生,希望能得到任何帮助(我到处寻找,过去 8 个小时都在试图解决这个问题)

目前我有

def apply(file: String) : Iterator[String] =  {
    scala.io.Source.fromFile(file).getLines().map(_.toLowerCase)
    }

还有

def groupFreq[A,B](xs: Iterator[A], f: A => B): HashMap[B, Int] = { 
    var freqMap = new HashMap[B, Int]
    for (x <- xs) freqMap = freqMap + ( f(x) -> ( freqMap.getOrElse( f(x) , 0 ) +1 )  )  
    freqMap
  }

apply 只获取我们传入的单词文件。

GroupFreq 采用 xs: Iterator[A] 和一个将 A 值转换为其 B 组的分组函数 f。 该函数返回一个 HashMap,对于每个 B 组,计算属于该组的 A 值的数量。

我使用这两个函数来帮助我使用 charFreq,这个函数同时使用 apply 和 groupFreq 来传回一个 HashMap,它计算一个 Char 在整个文件中出现的次数。如果 char 没有出现在文件中的任何位置,那么应该没有它的映射。

def charFreq(file: String): HashMap[Char, Int] = 
  {
    var it = Iterator[Char]()
    val words = apply(file)
    for {
        xs<-words
    } yield { it = it ++ xs.toIterator }

    val chars   = it
    val grouper = (x: Char) => x
    groupFreq(chars, grouper) 
  }

我的解决方案编译和应用,groupFreq 按预期工作,但是当我运行 charFreq 时,它说

charFreq 抛出异常:java.util.NoSuchElementException: key not 找到:d

我认为我做错了什么,很可能是我的 for 循环和产量,但我已经多次了解逻辑,但我不明白为什么它不起作用。

Google 和 StackOverflow 都推荐了平面地图,但我也无法让它发挥作用。

任何帮助将不胜感激。请记住,这是一个设置了骨架方法的类分配,因此我无法更改 apply 和 groupFreq 和 charFreq 的设置方式,我只能操作我尝试过的主体。

【问题讨论】:

    标签: java scala iterator hashmap key


    【解决方案1】:

    A.关于您的问题,我可以在代码中发现至少一个问题:

    您构建迭代器的方式(在 charFreq 中)似乎过于繁重。 words.toIterator 就足够了。

    你更新地图的方式对我来说也很奇怪。我宁愿这样做:

    val mapped = f(x)
    if (!(freqMap contains mapped) freqMap(mapped) = 0
    freqMap(mapped)+=1   
    

    B.据我了解,这个问题可以用单行来解决(这就是为什么 Scala 如此酷的原因 ;-))

    def charFreq(file:String) = 
        file.toCharArray.groupBy(m=>m).map(m => (m._1,m._2.size))
    

    解释:

    1) toCharArray 将您的字符串转换为 Char 元素的数组

    2) groupBy(m=>m) 将具有相同值的所有元素组合在一起,结果将是Map[Char,Array[Char]] 类型,其中每个字符都映射到字符串中所有出现该字符的数组。

    3) 现在我们只需要使用映射map(m =&gt; (m._1,m._2.size))Map[Char,Array[Char]] 的每个条目映射到Map[Char,Int]],它获取每个元素(键-> 值),保持键不变并转换值(一个数组)到该数组的大小。

    4) 如果您的输入字符串会非常大(我尚未对此进行评估,但如果它在 MB 的范围内,我会开始担心),那么我可能会使用另一种解决方案,使用可变的我会在遍历源代码时填写的地图:

    def charFreq(hugeFile:String) = {
        //create a mutable map, which can be updated when needed
        val mm = scala.collection.mutable.Map[Char,Int]()
        //iterate over the string
        for (m <- hugeFile) { 
            //ensure that our map contains the entry for the given character
            if (! (mm contains m)) mm(m) = 0          
            mm(m) = mm(m)+1 
        }
        //return the result as an immutable map
        mm.toMap
    }
    

    【讨论】:

    • 感谢您清理我的代码。正如我所说,第一次在 scala 中编码,所以这有助于我理解为什么 scala 如此出色(而不是因为我不熟悉它而讨厌它)
    【解决方案2】:

    我无法使用一些随机的字符串文本文件重现您的错误。我怀疑它发生在groupFreq() 没有getOrElse() 类型测试的早期迭代中。

    但是,在运行您的代码时,我最终得到一个从调用 charFreq() 的空映射。 charFreq() 中的循环/产量是有问题的,这是正确的。当您将val l = 放在for 前面并检查IDE 中的值时,更容易看到l 应该是Iterator[Unit] 类型。

    for 循环不需要 varsfor 循环与 C 风格的 for 循环不同,相当于在其元素上调用 flatMap/map(尽管其他人可以比我更好地表达这一点)。产量被连接到你的东西(由你在其中采取的步骤定义)。

    您可以通过以下两种方式获得Iterator[Char],以便您致电groupFreq()

    1> 去掉不必要的var it,直接用for理解循环填充chars

    val chars = for {
        xs<-words
        i<-xs.toIterator
    } yield { i }
    

    2> 直接在words val 上调用flatMap

    val chars = words.flatMap( s => s )
    

    【讨论】:

    • 非常感谢。我尝试了您的第一个解决方案,它奏效了。这是我在 Scala 的第一个任务,所以对我来说很难,但非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-14
    • 1970-01-01
    • 1970-01-01
    • 2016-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多