Scala PartialFunctions给出匹配错误:奇怪的行为

时间:2019-06-04 07:35:43

标签: scala

我正在尝试使用部分函数进行一些验证,让我们举一个字符串示例:

for i,row in df.iterrows():
    print(row['year'], "- adding ",row['title'])
    try:
        request.urlretrieve(row['pdfarticle'],"_tmp.pdf")
    except http.client.RemoteDisconnected:
        continue # this will skip the url throwing error
  

预期输出=> Some(“有效”)

但是我遇到了匹配错误:

  

scala.MatchError:有效(属于类java.lang.String)

任何人都可以帮助解决这里的问题,因为据我了解, .isDefinedAt 是在内部调用的,因此不应给出 matchError

PS忽略输入,这只是一个例子。

3 个答案:

答案 0 :(得分:5)

您的理解是错误的。 ScalaDocs page明确指出:“在呼叫isDefinedAt之前,呼叫者有责任呼叫apply ...”。

您的代码未调用isDefinedAt,这会导致异常,因此您必须显式调用它,或者可以使用其他方法在内部隐藏isDefinedAt

Seq("valid") collect (isLengthValid orElse isStringValid)
//res0: Seq[Option[String]] = List(Some(Valid))

Seq("vlad") collect (isLengthValid orElse isStringValid)
//res1: Seq[Option[String]] = List()

答案 1 :(得分:4)

如果您将最后一行写为

,这将按预期工作
Delete

我怀疑问题是您的版本降低了 (isLengthValid orElse isStringValid)("valid")

这意味着(isLengthValid.apply("valid")).orElse(isStringValid.apply("valid"))是在apply发生之前计算出来的,这意味着部分函数被视为总函数,并且如Valy Dia的答案所解释的,会抛出匹配错误。实际上在结果输出上调用orElse,而不是部分函数。

答案 2 :(得分:1)

错误消息来自第一个表达式-isLengthValid

仅针对长度严格大于5的string进行定义。因此,将其应用于长度为5的字符串"valid"时,它将抛出MatchError

scala>"valid".length
res5: Int = 5

isLengthValid("valid")
scala.MatchError: valid (of class java.lang.String)

如果方法isLengthValid以此方式定义(注意大于等号),则不会抛出MatchError

def isLengthValid: PartialFunction[String, Option[String]] ={
  case s:String if s.length >= 5 => Some("Invalid")
}

scala>isLengthValid("valid")
res8: Option[String] = Some("Invalid")

原始表达式将返回Option

scala>isLengthValid("valid") orElse isStringValid("valid")
res9: Option[String] = Some("Invalid")

您可以在此处进行操作以及对此question进行说明,而是改用此定义:

val isLengthValid = new PartialFunction[String, Option[String]] {
  def isDefinedAt(x: String) = x.length > 5
  def apply(x: String) = Some("Invalid")
}

scala>isLengthValid("valid")
res13: Some[String] = Some("Invalid")