【问题标题】:Using immutable collections使用不可变集合
【发布时间】:2016-02-02 21:18:34
【问题描述】:

我正在学习 Scala,我不得不说我真的很喜欢它。 我正在将我以前的 Java 项目移植到 Scala,尝试在我可以/知道如何使用该语言时利用该语言(主要是模式匹配并尝试使用 val,而不是递归或函数)。

在 java 项目中,我有一个 HashMap,它一开始是空的,并会通过使用某些方法来填充。然后,当然,我会使用hmap.get(key) 来检索对象。 这次我想使用不可变的HashMap,但我有点挣扎!这是我的代码:

val recipes = new HashMap[String,Any]()

private def addCrucibleRecipe(tag: String, tagAddon: String, result: ItemStack, cat: Any, aspects: AspectList) { 
  val recipe = new CrucibleRecipe(some params)
  if(recipe.isInstanceOf[CrucibleRecipe]) recipes + (tag -> recipe) 
  else throw new IllegalArgumentException(tag + " is not a valid recipe!")
}

如您所见,我正在使用+ 运算符,它应该返回一个新的HashMap,其中包含以前的值和尾部的新值。 但是,当我调用recipes.get(key) 时,列表包含选项None。 这意味着我仍然指向旧的、空的HashMap

我怎么能更新参考?也许这听起来很愚蠢,但我对函数式编程真的很陌生。它比可变的更好吗?

如果在 Scala 中有更实用或更有效的方法来做到这一点,请告诉我,我很想学习 :)

【问题讨论】:

    标签: scala


    【解决方案1】:

    是的,+ 方法将返回一个新的更新地图实例。但是您在此处保留对可变值recipes的引用。不幸的是,可变映射继承了用于不可变映射的方法,例如+,因此您创建了一个新的(可变但不同的)映射并“将其丢弃”。我的意思是:

    import scala.collection.mutable
    
    val m0 = mutable.Map.empty[String, Int]
    m0.put("hello", 1)
    val m1 = m0 + ("world" -> 2)
    m0.keys // Set(hello) -- this is the "old" map still!
    m1.keys // Set(world, hello) -- this is the newly created map
    

    所以正确的方法是使用不可变映射和var 来存储它:

    var m2 = Map.empty[String, Int]  // immutable map
    m2 = m2 + ("hello" -> 1)
    m2 +=     ("world" -> 2)  // were m2 += ... is short for m2 = m2 + ...
    

    当然,现在您已经将可变状态转移为具有 变量 m2(或在您的示例中为 var recipes)。因此,您必须定义要在程序中存储状态的“断点”。例如,您可以将其上移一级:

    case class Recipe(ingredients: String*)
    
    case class Recipes(map: Map[String, Recipe]) {
      def add(name: String, recipe: Recipe): Recipes =
        new Recipes(map + (name -> recipe))
    }
    
    val r0 = Recipes(Map.empty)
    val r1 = r0.add("pancakes", Recipe("milk", "eggs", "farine"))
    

    请注意,在您的示例中 addCrucibleRecipe 没有返回值,因此您似乎打算覆盖包含对象中的某些状态。在此处的案例类示例中,我将返回该对象的全新副本。


    你可能想看看这些问题:

    【讨论】:

    • 感谢您的回答:)。但这不是比使用 mutable.HashMap 过于复杂吗?并且仍然依赖于可变性......另外,我不知道我需要向它添加多少次元素,所以我应该有一个方法来处理所有这些。听从你的建议,我能做什么: var recipes =new immutable.HashMap[String, Any];在我的方法中:recipes += ("string" -> 2)。我说的对吗?
    • 这取决于你想如何隔离你的状态。您可以将具有不可变状态的整个程序编写到顶层。使用不可变状态可以使您的程序更容易推理。例如,您可以从使用站点决定并发性,而如果您将可变性融入到您的对象中,您将很难让您的东西在并发下正确运行......
    • 也尽量不要使用像collection.immutable.HashMap这样的具体实现。改用通用类型,即collection.immtuable.Map,为了方便起见,它被别名为Map
    • 这行得通吗? pastebin.com/as9v1pUt 我仍在使用 + 运算符来生成一个新结构,并且每次都将后者重新分配给 var ...您认为这是一个好的解决方案吗?为什么通用地图更好?
    • 其实这样不好....当我用 recipes.get(key).asInstanceOf[CrucibleRecipe] 调用它时出现异常,因为我无法转换 scala.Some
    猜你喜欢
    • 2021-01-12
    • 1970-01-01
    • 2012-01-19
    • 2011-03-11
    • 1970-01-01
    • 2012-02-12
    • 2021-09-05
    • 2011-12-04
    • 1970-01-01
    相关资源
    最近更新 更多