【问题标题】:Work on list of tuples in Scala - part 4在 Scala 中处理元组列表 - 第 4 部分
【发布时间】:2018-07-29 21:49:29
【问题描述】:

我是 Scala 的新手,并试图了解如何处理元组列表,因此我创建了一个虚构的人员列表:

val fichier : List[(String, Int)] = List(("Emma Jacobs",21), ("Mabelle Bradley",53), ("Mable Burton",47), ("Ronnie Walton",41), ("Bill Morton",36), ("Georgia Bates",30), ("Jesse Caldwell",46), ("Jeffery Wolfe",50), ("Roy Norris",18), ("Ella Gonzalez",48))

我想把这个列表分成两个列表(不管partitionfilter等专用方法)根据一定的条件(例如一边是偶数,另一边是奇数)和将这两个列表放在一起。类似List[evenList, oddList]

用 Python 编写代码很容易,但在 Scala 中显然不行:

Python code:

def separate(list):
    evenList = [] ; oddList  = []
    for (i, j) in list:
        if j%2==0:
            evenList.append((i,j))
        else:
            oddList.append((i,j))
    bothLists = [evenList, oddList]
    return bothLists

结果(为便于阅读而更改):

[
  [('Bill Morton', 36), ('Georgia Bates', 30), ('Jesse Caldwell', 46), ('Jeffery Wolfe', 50), ('Roy Norris', 18), ('Ella Gonzalez', 48)],
  [('Emma Jacobs', 21), ('Mabelle Bradley', 53), ('Mable Burton', 47), ('Ronnie Walton', 41)]
]

我也以这种方式对其进行了编码,以展示两个短列表如何从一个迭代演变为另一个:

Python code:

def separate(list):
    evenList = [] ; oddList  = []
    for (i, j) in list:
        if j%2==0:
            evenList.append((i,j))
            yield evenList
        else:
            oddList.append((i,j))
            yield oddList

结果:

[('Emma Jacobs', 21)]
[('Emma Jacobs', 21), ('Mabelle Bradley', 53)]
[('Emma Jacobs', 21), ('Mabelle Bradley', 53), ('Mable Burton', 47)]
[('Emma Jacobs', 21), ('Mabelle Bradley', 53), ('Mable Burton', 47), ('Ronnie Walton', 41)]
[('Bill Morton', 36)]
[('Bill Morton', 36), ('Georgia Bates', 30)]
[('Bill Morton', 36), ('Georgia Bates', 30), ('Jesse Caldwell', 46)]
[('Bill Morton', 36), ('Georgia Bates', 30), ('Jesse Caldwell', 46), ('Jeffery Wolfe', 50)]
[('Bill Morton', 36), ('Georgia Bates', 30), ('Jesse Caldwell', 46), ('Jeffery Wolfe', 50), ('Roy Norris', 18)]
[('Bill Morton', 36), ('Georgia Bates', 30), ('Jesse Caldwell', 46), ('Jeffery Wolfe', 50), ('Roy Norris', 18), ('Ella Gonzalez', 48)]

在下面找到我用 Scala 编写的代码:

Scala code:

def separate(list: List[(String, Int)]):  List[List[(String, Int)]]  = {
  val evenList = List[(String, Int)]()
  val oddList = List[(String, Int)]()
    for ( (i, j) <- list if j%2==0 ) (i,j)::evenList
    for ( (i, j) <- list if j%2!=0 ) (i,j)::oddList
  val evenAndodd = List(evenList,oddList)
  evenAndodd
  }

结果:

scala> separate(fichier) res16: List[List[(String, Int)]] = List(List(), List())

evenListoddList 在 evenAndodd 列表中为空。

我想我明白了,但我不知道英文单词。它是关于 Scala 中变量的“可达性”。

欢迎任何帮助

【问题讨论】:

    标签: scala list tuples


    【解决方案1】:

    你忘了从 for 表达式中产生值:

    def separate(list: List[(String, Int)]): List[List[(String, Int)]] = {
      val evenList = for ((i, j) <- list if j % 2 == 0) yield (i, j)
      val oddList = for ((i, j) <- list if j % 2 != 0) yield (i, j)
      val evenAndOdd = List(evenList, oddList)
      evenAndOdd
    }
    

    Scala 中有两种 for 表达式:

    1. for 循环
    2. 为了理解

    没有yield 的For 表达式是一个for 循环,它返回Unit

    val x = for ((i, j) <- fichier if j % 2 == 0) (i, j)
    x.getClass // Class[Unit] = void
    

    将此视为具有副作用的常规 Python 循环:

    items = []
    for item in [1, 2, 3]:
        items.append(item + 1)
    

    当你从for 中让步时,你正在使用 for comprehension,它会产生一个新的集合:

    val y = for ((i, j) <- fichier if j % 2 == 0) yield (i, j)
    y.take(2) // List(("Bill Morton", 36), ("Georgia Bates", 30))
    

    这更像是 Python 的推导式,您可以将其结果分配给变量:

    items = [item + 1 for item in [1, 2, 3]]
    

    【讨论】:

    • 好的,知道了。另外,我现在已经阅读了yield,它的工作方式与 Python 不同。在您的代码中,yield 会在每个新循环中为每个列表添加一个新元素,就像 pythonic for loop 在您的示例中执行 +1 一样
    • @Yuva 在您的这种特殊情况下,请考虑partition(恕我直言)更合适。查看 Andrey Tyukin 的答案,例如:val (odd, even) = fichier.partition { case (_, age) =&gt; age % 2 == 1 }
    【解决方案2】:

    首先,请注意List 上已经有一个方法partition

    val fichier : List[(String, Int)] = List(
      ("Emma Jacobs",21), ("Mabelle Bradley",53), 
      ("Mable Burton",47), ("Ronnie Walton",41), 
      ("Bill Morton",36), ("Georgia Bates",30), 
      ("Jesse Caldwell",46), ("Jeffery Wolfe",50), 
      ("Roy Norris",18), ("Ella Gonzalez",48)
    )
    
    val (odd, even) = fichier.partition(_._2 % 2 == 1)
    
    println(odd)
    println(even)
    

    打印出来:

    List((Emma Jacobs,21), (Mabelle Bradley,53), (Mable Burton,47), (Ronnie Walton,41))
    List((Bill Morton,36), (Georgia Bates,30), (Jesse Caldwell,46), (Jeffery Wolfe,50), (Roy Norris,18), (Ella Gonzalez,48))
    

    您当然可以自己轻松实现,如下所示:

    def separate[A](list: List[A])(predicate: A => Boolean):  (List[A], List[A]) = {
      import scala.collection.mutable.ListBuffer
      val trueListBuf = new ListBuffer[A]
      val falseListBuf = new ListBuffer[A]
      for (x <- list) {
        if (predicate(x)) trueListBuf += x
        else falseListBuf += x
      }
      (trueListBuf.toList, falseListBuf.toList)
    }
    
    val (odd2, even2) = separate(fichier){ case (n, a) => a % 2 == 1 }
    
    println(odd2)
    println(even2)
    

    这会输出与之前相同的结果。

    这里有几点需要注意:

    • 我们使用可变的ListBuffers 来提高效率,因为附加到可变的 ListBuffer 在恒定时间内工作,而对不可变对象做同样的事情 List 有点麻烦
    • 我们为所有可能的As 实现separate 方法 在列表中。我们没有将% 2-check 硬编码到方法中,而是通过 来自外部的通用predicate
    • 注意predicate 在第二个(单独的)参数列表中。 这使我们能够使用更好的(至少在我看来)语法 separate(list){ predicateImpl }。这也简化了类型推断 对于 predicate 参数。
    • 我们可以在实现的时候使用模式匹配case (name, age) =&gt; ... 谓词。

    【讨论】:

      【解决方案3】:

      你也可以通过模式匹配、递归和内部函数来解决它:

      def separate (list: List[(String, Int)]):  List[List[(String, Int)]]  = {
      
          def separate (list: List[(String, Int)], first: List[(String, Int)], second: List[(String, Int)]): 
              List[List[(String, Int)]] = list match {
      
              case Nil => List (first, second)
              case ((s, i)) :: ps =>  if (i % 2 !=0)
                  separate (ps, (s, i) :: first, second) else
                  separate (ps, first, (s, i) :: second)
          }
      
          separate (list,  List[(String, Int)](),  List[(String, Int)]())
      }
      

      外部函数只是设置了 2 个空列表,分别用于奇数和偶数,将它们传递下去,随便将下一个元素添加到其中一个。

      当对类型进行泛化时,它会变得更好。我们摆脱了所有那些 (String, Int) 元组并为每种类型提供了解决方案,我们只需将一个函数从 A 传递给 Boolean:

      def separate [A] (list: List[A]) (f: A => Boolean): List[List[A]]  = {
      
          def separate (list: List[A], first: List[A], second: List[A]): List[List[A]] = list match {
              case Nil => List (first, second)
              case p :: ps =>  if (f (p))
                  separate (ps, p :: first, second) else
                  separate (ps, first, p :: second)
          }
      
          separate (list, List[A](), List[A]())
      }
      
      def evan (si : (String, Int)) : Boolean = (si._2 % 2) == 0
      separate (fichier) (evan)
      

      separate (fichier) (_._2 % 2 == 0)
      

      【讨论】:

        猜你喜欢
        • 2018-07-25
        • 2018-07-26
        • 2018-07-24
        • 2015-07-06
        • 2014-01-23
        • 2020-04-05
        • 1970-01-01
        • 2014-12-30
        • 2014-08-03
        相关资源
        最近更新 更多