【问题标题】:Scala Newb Question - about scoping and variablesScala Newb 问题 - 关于范围和变量
【发布时间】:2009-11-25 11:40:47
【问题描述】:

我正在解析 XML,并不断发现自己在编写如下代码:

val xml = <outertag>
<dog>val1</dog>
<cat>val2</cat>
</outertag>

var cat = ""
var dog = ""

for (inner <- xml \ "_") {
  inner match {
    case <dog>{ dg @ _* }</dog> => dog = dg(0).toString()
    case <cat>{ ct @ _* }</cat> => cat = ct(0).toString()
  }
}

/* do something with dog and cat */

这让我很恼火,因为我应该能够将 cat 和 dog 声明为 val(不可变),因为我只需要设置它们一次,但我必须让它们可变。除此之外,似乎在 scala 中必须有更好的方法来做到这一点。有什么想法吗?

【问题讨论】:

    标签: scala scoping


    【解决方案1】:

    这里有两个(现在是三个)可能的解决方案。第一个非常快速和肮脏。您可以在 Scala 解释器中运行全部内容。

    val xmlData = <outertag>
    <dog>val1</dog>
    <cat>val2</cat>
    </outertag>
    
    // A very simple way to do this mapping.
    def simpleGetNodeValue(x:scala.xml.NodeSeq, tag:String) = (x \\ tag).text
    
    val cat = simpleGetNodeValue(xmlData, "cat")
    val dog = simpleGetNodeValue(xmlData, "dog")
    

    cat 将是“val2”,dog 将是“val1”。

    请注意,如果没有找到任何一个节点,将返回一个空字符串。您可以解决这个问题,或者您可以用更惯用的方式编写它:

    // A more idiomatic Scala way, even though Scala wouldn't give us nulls.
    // This returns an Option[String].
    def getNodeValue(x:scala.xml.NodeSeq, tag:String) = {
      (x \\ tag).text match {
        case "" => None
        case x:String => Some(x)
      }
    }
    
    val cat1 = getNodeValue(xmlData, "cat") getOrElse "No cat found."
    val dog1 = getNodeValue(xmlData, "dog") getOrElse "No dog found."
    val goat = getNodeValue(xmlData, "goat") getOrElse "No goat found."  
    

    cat1 将是“val2”,dog1 将是“val1”,goat 将是“找不到山羊”。

    更新:这是获取标签名称列表并将其匹配项作为 Map[String, String] 返回的另一种便捷方法。

    // Searches for all tags in the List and returns a Map[String, String].
    def getNodeValues(x:scala.xml.NodeSeq, tags:List[String]) = {
      tags.foldLeft(Map[String, String]()) { (a, b) => a(b) = simpleGetNodeValue(x, b)}
    }
    
    val tagsToMatch = List("dog", "cat")
    val matchedValues = getNodeValues(xmlData, tagsToMatch)
    

    如果你运行它,matchedValues 将是Map(dog -&gt; val1, cat -&gt; val2)

    希望有帮助!

    更新 2:根据 Daniel 的建议,我正在使用双反斜杠运算符,它将下降到子元素中,随着 XML 数据集的发展,这可能会更好。

    【讨论】:

    • 我感觉最近有人一直在使用 XML... :-) 但是,我建议您在第一个示例中使用双反斜杠而不是单反斜杠。 (该死的 stackoverflow 一直在弄乱反斜杠)
    • 我并没有真正使用过 XML ——我只是想了解一下 Scala。无论如何,我正在尝试清理更简单的问题,以便您可以回答问题,例如一些奇怪的 Python 转换。
    • 对双反斜杠的好调用,顺便说一句。 :)
    【解决方案2】:
    scala> val xml = <outertag><dog>val1</dog><cat>val2</cat></outertag>
    xml: scala.xml.Elem = <outertag><dog>val1</dog><cat>val2</cat></outertag>
    
    scala> val cat = xml \\ "cat" text
    cat: String = val2
    
    scala> val dog = xml \\ "dog" text
    dog: String = val1
    

    【讨论】:

      【解决方案3】:

      考虑将 XML 检查和模式匹配封装在一个函数中,该函数将您需要的多个值作为元组 (Tuple2[String, String]) 返回。但是停下来考虑一下:看起来有可能不匹配任何 dogcat 元素,这将使您为一个或两个元组组件返回 null 。也许你可以返回一个 Option[String] 的元组,或者如果其中一个元素模式绑定失败,则抛出异常。

      在任何情况下,您通常可以通过将组成语句包装到函数中以产生表达式来解决这些初始化问题。一旦你有了一个表达式,你就可以用它的计算结果来初始化一个常量。

      【讨论】:

      • 谢谢!你能展示一个使用 yield 表达式的例子吗?或者至少是一个谈论它们的好网址。就像我说的,我是新手 =)
      • 这不是我的意思。通过定义一个返回值的函数——这里是一个 Tuple2[String, String]——你现在有了一个可用的表达式,因为调用一个返回值的函数就是一个表达式。声明和初始化一个“val”本身就是一个语句,右手边(初始值)必须是一个表达式,而不是一个语句。这意味着右侧可以是文字值或函数调用,但不能是一个或多个语句。正是这种语句和表达式之间的区别经常鼓励人们编写这些小函数。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-30
      • 2011-11-21
      • 2011-04-13
      相关资源
      最近更新 更多