29

我刚刚开始使用 Scala。我发现自己经常使用元组变量。

例如,这是我写的一些代码:

/* Count each letter of a string and return in a list sorted by character
 * countLetter("test") = List(('e',1),('s',1),('t',2))
*/
def countLetters(s: String): List[(Char, Int)] = {
  val charsListMap = s.toList.groupBy((c:Char) => c)
  charsListMap.map(x => (x._1, x._2.length)).toList.sortBy(_._1)
}

这种元组语法(x._1、x._2 等)是否受到 Scala 开发人员的反对?

4

3 回答 3

29

Scala 开发人员不喜欢元组访问器吗?

简短的回答:没有。

稍长(一个字符)答案:是的。

太多_n's 可能是代码异味,在我看来,在您的情况下,以下内容要清楚得多:

def countLetters(s: String): List[(Char, Int)] =
  s.groupBy(identity).mapValues(_.length).toList.sortBy(_._1)

有很多这样的方法mapValues是专门为减少对嘈杂的元组访问器的需求而设计的,所以如果你发现自己写_1了很多东西,这可能意味着你错过了一些不错的库方法。但有时它们是写东西的最干净的方式(例如,_1我重写的final)。

需要注意的另一件事是过度使用元组访问器应该被视为推动将元组提升为案例类的推动力。考虑以下:

val name = ("Travis", "Brown")

println("Hello, " + name._1)

相对于:

case class Name(first: String, last: String)

val name = Name("Travis", "Brown")

println("Hello, " + name.first)

第二个版本中额外的 case 类定义为单行代码购买了很多可读性。

于 2012-12-09T19:30:50.710 回答
19

那么有一个更好的解决方案x._N。使用元组的常用方法是模式匹配:

charsListMap.map{case (a, b) => (a, b.length)}

你也可以看看scalaz,有一些用于元组的工具

import scalaz._
import Scalaz._

scala> (1, "a") bimap (_ + 2, _ + 2)
res0: (Int, java.lang.String) = (3,a2)

scala> ('s, "abc") :-> { _.length }
res1: (Symbol, Int) = ('s,3)
于 2012-12-09T19:33:58.117 回答
1

从 开始Scala 3,使用参数 untupling功能,以下将成为 的替代方案.map(x => x._1 -> x._2.length)

.map(_ -> _.length)

因此,您的示例变为:

"test".toList.groupBy(identity).map(_ -> _.length).toList.sortBy(identity)
// List(("e", 1), ("s", 1), ("t", 2))

关于更具体的示例并从 开始Scala 2.13,您还可以使用groupMapReduce(顾名思义),它相当于 a和 reduce 步骤groupBymapValues

"test".groupMapReduce(identity)(_ => 1)(_ + _).toList.sortBy(identity)
于 2020-06-01T13:20:25.370 回答