Regex 使用有状态PartialFunction和collectFirst时出现NullPointerException

Regex 使用有状态PartialFunction和collectFirst时出现NullPointerException,regex,xml,scala,configuration,Regex,Xml,Scala,Configuration,考虑一下这个(非常难看的代码): 而它与getLinkOld完美结合 这里的问题是什么?您的匹配器是在定义的中作为副作用创建的。将副作用函数传递给例程,例如map,通常会导致灾难,但这里甚至不会发生这种情况。您的代码要求在applyis之前调用isDefined,参数相同。这使得您的代码非常脆弱,这就是您应该更改的内容 PartialFunction的客户端通常不必遵循该协议。例如,想象一下 if (f.isDefinedAt(x) && f.isDefinedAt(y)) {f

考虑一下这个(非常难看的代码):

而它与
getLinkOld
完美结合


这里的问题是什么?

您的匹配器是在
定义的
中作为副作用创建的。将副作用函数传递给例程,例如
map
,通常会导致灾难,但这里甚至不会发生这种情况。您的代码要求在
apply
is之前调用
isDefined
,参数相同。这使得您的代码非常脆弱,这就是您应该更改的内容

PartialFunction
的客户端通常不必遵循该协议。例如,想象一下

if (f.isDefinedAt(x) && f.isDefinedAt(y)) {fx = f(x); fy = f(y)}. 
这里调用
apply
的代码甚至不是你的,而是集合类,因此你无法控制发生了什么

您在
getLinkNew
中遇到的具体问题是
已定义
从未被调用。
collectFirst
PartialFunction
参数是
{case m=>…}
。调用的
isDefined
就是此函数的
isDefined
。由于
m
是一个无可辩驳的模式,因此它始终是正确的,如果存在第一个元素,collectFirst将始终返回第一个元素。分部函数返回另一个分部函数(
映射
),该函数恰好没有在
m
处定义,这与此无关

编辑-可能的解决方法

一个非常简单的更改是检查
匹配器是否可用,如果不可用,则创建它。更好的方法是,保留用于创建它的
实体
字符串,以便检查它是否正确。只要没有多线程,这将使副作用变得良性。但是,请不要使用
null
,使用
选项
,这样编译器就不会让您忽略它可能是
None

var _currentMatcher : Option[(String, Matcher)] = None
def currentMatcher(entity: String) : Matcher = _currentMatcher match{
  case Some(e,m) if e == entity => m
  case _ => {
    _currentMatcher = (entity, pattern.matcher(entity))
    _currentmatcher._2
  }
}
再次编辑。愚蠢的我

抱歉,所谓的变通方法确实使类更安全,但它不能使
collectFirst
解决方案工作。同样,总是定义
case m=>
分部函数(注意:
实体
甚至没有出现在
getLinkNew
代码中,这应该令人担忧)。问题是需要NodeSeq的PartialFunction(而不是entity,该函数已知,但不作为参数传递)。将调用isDefined,然后应用。模式和匹配器取决于NodeSeq,因此它们不能预先创建,而只能在isDefined和/或apply中创建。本着同样的精神,您可以缓存在isDefined中计算的内容,以便在Apply中重用。这绝对不漂亮

def linkFor(entity: String) = new PartialFunction[NodeSeq, String] {
  var _matcher : Option[String, Matcher] = None
  def matcher(regexp: String) = _matcher match {
    case Some(r, m) where r == regexp => m
    case None => {
      val pattern = Pattern.compile(regexp)
      _matcher = (regexp, pattern.matcher(entity))
      _matcher._2
    }
  }
  def isDefined(mapping: NodeSeq) = {
    matcher(mapping \ "match" text).matches
  }
  def apply(mapping: NodeSeq) = {
     // call matcher(...), it is likely to reuse previous matcher, build result
  }

}

您可以将其与
(config\mapping)一起使用。collectFirst(linkFor(entity))
您的匹配器在
中被定义为副作用。将副作用函数传递给例程,例如
map
,通常会导致灾难,但这里甚至不会发生这种情况。您的代码要求在
apply
is之前调用
isDefined
,参数相同。这使得您的代码非常脆弱,这就是您应该更改的内容

PartialFunction
的客户端通常不必遵循该协议。例如,想象一下

if (f.isDefinedAt(x) && f.isDefinedAt(y)) {fx = f(x); fy = f(y)}. 
这里调用
apply
的代码甚至不是你的,而是集合类,因此你无法控制发生了什么

您在
getLinkNew
中遇到的具体问题是
已定义
从未被调用。
collectFirst
PartialFunction
参数是
{case m=>…}
。调用的
isDefined
就是此函数的
isDefined
。由于
m
是一个无可辩驳的模式,因此它始终是正确的,如果存在第一个元素,collectFirst将始终返回第一个元素。分部函数返回另一个分部函数(
映射
),该函数恰好没有在
m
处定义,这与此无关

编辑-可能的解决方法

一个非常简单的更改是检查
匹配器是否可用,如果不可用,则创建它。更好的方法是,保留用于创建它的
实体
字符串,以便检查它是否正确。只要没有多线程,这将使副作用变得良性。但是,请不要使用
null
,使用
选项
,这样编译器就不会让您忽略它可能是
None

var _currentMatcher : Option[(String, Matcher)] = None
def currentMatcher(entity: String) : Matcher = _currentMatcher match{
  case Some(e,m) if e == entity => m
  case _ => {
    _currentMatcher = (entity, pattern.matcher(entity))
    _currentmatcher._2
  }
}
再次编辑。愚蠢的我

抱歉,所谓的变通方法确实使类更安全,但它不能使
collectFirst
解决方案工作。同样,总是定义
case m=>
分部函数(注意:
实体
甚至没有出现在
getLinkNew
代码中,这应该令人担忧)。问题是需要NodeSeq的PartialFunction(而不是entity,该函数已知,但不作为参数传递)。将调用isDefined,然后应用。模式和匹配器取决于NodeSeq,因此它们不能预先创建,而只能在isDefined和/或apply中创建。本着同样的精神,您可以缓存在isDefined中计算的内容,以便在Apply中重用。这绝对不漂亮

def linkFor(entity: String) = new PartialFunction[NodeSeq, String] {
  var _matcher : Option[String, Matcher] = None
  def matcher(regexp: String) = _matcher match {
    case Some(r, m) where r == regexp => m
    case None => {
      val pattern = Pattern.compile(regexp)
      _matcher = (regexp, pattern.matcher(entity))
      _matcher._2
    }
  }
  def isDefined(mapping: NodeSeq) = {
    matcher(mapping \ "match" text).matches
  }
  def apply(mapping: NodeSeq) = {
     // call matcher(...), it is likely to reuse previous matcher, build result
  }

}

您可以将其与
(config\mapping)一起使用。collectFirst(linkFor(entity))

谢谢!在你看来,什么样的解决方案才是一个不需要计算两次的好方法呢?建议了一个对代码进行最小更改的解决方案,而不是作为一个注释,以获得更多的空间。谢谢!在你看来,什么样的解决方案才是一个不需要计算两次的好方法呢?提出了一个对代码进行最小更改的解决方案,而不是作为一个注释来获得更多的空间。