在Scala中查找匹配行的惯用方法

时间:2011-03-29 18:51:43

标签: scala

我有Iterable[String]表示文件中的行,我想找到该序列中与正则表达式匹配的第一行,并返回由正则表达式提取的数值。该文件足够大,将整个内容加载到内存然后调用toString()或其他东西是没有意义的,所以我需要一次一行地进行处理。 / p>

这是我拥有的(它有效):

val RateRegex : Regex = ".....".r

def getRate(source : Source) : Option[Double] = {
  import java.lang.Double._

  for(line <- source.getLines() ) {
    line match {
      case RateRegex(rawRate) => return Some(parseDouble(rawRate))
      case None => ()
    }
  }

  return None
}

这对我来说似乎很难看。这感觉非常紧迫,case None => ()可能会被评论所取代,并且说“你做错了。&#34;

我想我想要def findFirstWhereNonNone(p : Function[A,Option[B]]) => Option[B]之类的内容,其中集合的元素属于A类型。

是否有内置方法可以让我以更实用的方式执行此操作?我应该写那个方法吗?

P.S。虽然我在使用java.lang.Double.parseDouble,但还有其他选择吗? Scala的Double课程并没有公开它。

PPS我已经看到很多关于SO的帖子暗示Source API不应该用于制作,但它们都是从2008年和2009年开始的。它仍然是案子?如果是这样,我应该为IO使用什么?

更新

我现在有:

import util.matching.Regex.Groups

for{line <- source.getLines()
    Groups(rawRate) <- RateRegex.findFirstMatchIn(line)} {
  return Some(parseDouble(rawRate))
}

return None

对我来说感觉好多了。

2 个答案:

答案 0 :(得分:5)

编辑:第三种选择非常简洁:

source
.getLines()
.collectFirst{ case RateRegex(x) => x.toDouble}

不确定它是否更具功能性,但您可以在选项上使用foreach / for-comprehensions的行为

def getRate(source : Source) : Option[Double] = {

     for {line    <- source.getLines() 
          rawRate <- RateRegex.findFirstIn(line)}
       return  Some(rawRate toDouble)

  return None
}

这也有效(与EasyAngel的回答非常相似):

source
.getLines()
.map{RateRegex.findFirstMatchIn(_)}
.filter{_.isDefined}
.map{_.get.group(0).toDouble}
.head
.toList
.headOption

最后三个有点难看。 take(1)是为了确保我们只评估第一场比赛。 toList用于强制进行求值,headOption用于提取第一个值为Some()或None(如果没有)。有没有更惯用的方法呢?

答案 1 :(得分:1)

以下是可能的解决方案之一:

def getRates(source : Source) = source.getLines.map {
    case RateRegex(rate) => Some(rate toDouble)
    case _ => None
} filter (_ isDefined) toList

请注意,此功能现在立即返回所有找到的费率List[Option[Double]]。同样重要的是,在我致电toList

之前,Iterator仍然很懒惰

更新

正如评论中所提到的,这里是解决方案,只返回第一次出现:

def getRate(source : Source): Option[Double] = source.getLines.map {
  case RateRegex(rate) => Some(rate toDouble)
  case _ => None
} find (_ isDefined) getOrElse None