【问题标题】:What is the correct way to make a list in Scala?在 Scala 中制作列表的正确方法是什么?
【发布时间】:2019-03-09 01:17:28
【问题描述】:

具有面向对象编程的背景,我无法理解如何在 Scala 中制作不可变列表。

示例;我想列出 10 个随机的人:

object MyApplication extends App {

  val numberOfPersons = 10 : Int
  val listOfPersons = makeListOfPersons(numberOfPersons) : List[Person]

  def makeListOfPersons( numberOfPersons : Int ) : List[Person] = {
    // TODO: return a immutable list of 10 persons
  }

}

class Person {
  /**
    Generic content, 
    like age and name.
  * */
}

在 Scala 中创建不可变列表的“正确”方法是什么?

【问题讨论】:

    标签: scala function dictionary recursion


    【解决方案1】:

    在 scala 中创建 List 有 5 种方法:

    Lisp 风格:

    val list = 1::2::3::Nil
    

    这种风格也可以被认为是 Haskell 或函数式编程 (FP) 风格。

    Java 风格:

    val list = List(1,2,3)
    

    具有范围方法的 Scala 列表

    List.range(1, 10)
    

    使用填充创建 scala 列表

    List.fill(3)(5)
    

    带有表格的 Scala 列表

    List.tabulate(5)(n => n * n)
    

    列表的元素是根据我们提供的函数创建的。

    更多信息请阅读:

    Preferred way to create a Scala list

    【讨论】:

      【解决方案2】:

      由于 Scala 中的默认 List 是不可变的,因此添加元素的正确方法是返回一个包含新元素和旧元素的新列表。 事实上,List 有两种方法:

      +:
      ++
      

      第一个接受一个元素,将其添加为第一个元素,并将列表的其余部分添加为尾部,然后返回结果列表。 另一个将另一个“集合”作为参数,并在开始时将其添加到第一个列表中。 List 有另一种添加新元素作为最后一个元素的方法。 在 Scala 中,这些操作是允许的,但要考虑到始终会使用请求的修改来检索新实例,因为默认情况下所有对象都是不可变的。

      至于你的代码,你可以试试这样的:

      object MyApplication extends App {
      
       val numberOfPersons: Int = 10
       val listOfPersons: List[Person] = makeListOfPersons(numberOfPersons)
      
       def makeListOfPersons( numberOfPersons : Int ) : List[Person] = {
         (1 to numberOfPersons).foldLeft(List.empty[Person]){ (accum, elem) =>
           new Person() :: accum
         }
       }
      
      }
      

      (1 to numberOfPersons) 创建一个范围,可以看作是一个整数列表,将由 foldLeft 遍历。此方法将遍历该列表,并接收一个种子,在本例中是一个空的 Person 列表。然后,对于 int 列表中的每个元素,创建一个新的 Person 并将其添加到列表中,作为最后一个表达式返回并使用累加器进行下一次迭代。最后,检索到一个包含十个 Person 实例的列表。

      【讨论】:

        【解决方案3】:

        在这种特殊情况下,

        List.fill(numberOfPersons){ codeThatCreatesASinglePerson }
        

        似乎最合适。

        在大多数其他情况下:Nil 创建一个空列表,x :: y 将元素 x 添加到列表 y

        如果您想 追加 到列表中,而不是添加到它前面,那么您可以使用 collection.mutable.ListBuffer,将您想要在列表中包含的所有元素追加到它上面,然后完成后调用toList...或者只使用内置的工厂方法来完成此操作。

        【讨论】:

        • 以这种方式使用fill确实有缺点,它依赖于副作用并且没有功能,所以从纯粹主义者的角度来看,最好使用tabulate并播种索引中的随机生成器,以便代码可重复和可测试。但也许我很迂腐……
        • @Tim 在您指定对纯度的要求之前,这并不是一个缺点。你不能使用索引来seed RNG:它会破坏分布(你总是会得到相同的列表),而且tabulate 对于在致电makePerson。您可以使用一些一元RNG对numberOfPersons进行采样,但是您又回到了square1:如何实现那个?顺便说一句:(1 to 10).map(foo)List.tabulate(10)(_ => foo) 也起作用,只是因为它们具有改变 RNG 状态的副作用。
        • 功能性代码通常被认为比非功能性代码更好(至少在 Scala 社区中)。但我确实说过我可能很迂腐:)
        • @Tim 这是一个相当奇怪的无条件声明...整个 scala 社区突然间认为功能代码在所有用例中无条件地更好 ?必须有人告诉 Dotty 人,他们目前的编译器代码中至少有 240 个while-loops...有效的世界末日”场景 - 必须有人这样做,而且它不会纯粹是功能性的,因为归根结底,有这些奇怪的物理机器会改变它们的状态.
        • 您是如何从“在 Scala 社区中普遍认为更好[]”到“在所有用例中[对于]整个 Scala 社区无条件更好”的?
        【解决方案4】:

        如果你知道你想要什么集合类型,你可以在那个类型上使用tabulate 方法:

        List.tabulate(10)(makePerson)
        

        在这种情况下,makePerson 是一个函数,它接受 Int 并为该 Int 返回 Person 对象。

        如果您不关心集合类型,可以像这样在1 to 10 范围内调用map

        (1 to 10).map(makePerson)
        

        如果你不需要使用Int参数,你可以这样做:

        List.tabulate(10)(_ => makeRandomPerson())
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-06-05
          • 2014-04-15
          • 2012-01-13
          • 2021-12-07
          • 2017-09-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多