2

我是 scala 新手,我无法理解以下功能

val L = List(List(1, 1), 2, List(3, List(5, 8)))       

def flatten(l: List[Any]): List[Any] =  l flatMap {
    case ms:List[_] => flatten(ms)
    case l => List(l)
}                                         

flatten(L)                      // res2: List[Any] = List(1, 1, 2, 3, 5, 8)

特别是我不明白flatMap和模式匹配的组合以及第一种情况的含义ms:List[_]

有人可以解释一下,也许可以提供一个更简单的例子来澄清这个概念吗?

4

1 回答 1

16

mapflatMap

首先flatMap高阶函数。

map也是一个高阶函数,它将通过对 a的所有元素应用函数来将 a 转换List为新函数。例如,如果您有ListList

val l = List(1,2,3)

您可以map使用它到一个新列表

val doubled = l.map(_ * 2)         // List(2,4,6)

那么是什么flatMapflatMap当您的函数获取 aInt但返回 a时很有用List[Int]。在这种情况下,如果你将这个函数传递给map你得到的将是 aList[List[Int]]但有时你想得到 a List[Int]

你能做的就是flatMap改用。从数学上讲,它相当于 firstmap然后flatten是结果,但实际上它可能flatten是基于而flatMap不是相反的方式定义的函数。

抛开技术细节不谈,flatten意味着如果你有一个List[List[List...[Int]]],如果你flatten有,你就会得到一个List[Int]

当我们看

def flatten(l: List[Any]): List[Any] =  l flatMap {
    case ms:List[_] => flatten(ms)
    case l => List(l)
} 

我们首先需要知道这意味着什么。我们已经知道我们必须将一个函数传递给flatMap. 在这种情况下,我们传递的函数是

{
    case ms:List[_] => flatten(ms)
    case l => List(l)
} 

起初看起来不像是一个功能,但它是!它实际上是一个偏函数,您可以将其视为具有一些细微差别的函数!

什么是偏函数?

我们可以通过以下方式实现(几乎)相同的结果:

def flatten(l: List[Any]): List[Any] =  l flatMap { _ match {
        case ms:List[_] => flatten(ms)
        case l => List(l)
    }   
}

普通函数和偏函数的区别在于,对于某些特定的输入,偏函数可能是未定义的。所以编译器会自动从以关键字开头的主体生成部分函数case(这不是那么简单,但为了简单起见,我在这里跳过细节)。

按类型匹配模式

类似的模式ms:List[_]称为类型模式。这意味着ms将在运行时针对List[_]. _是一个通配符,表示任何类型,因此ms:List[_]字面意思是List任何类型的 a(由于类型擦除,您不能使用类似 的模式ms:List[Int],请参阅线程以获取更多信息)。

所以这个代码片段上的整个模式匹配意味着

  • 如果ms是列表,结果将是flatten(ms),否则结果将是List(l)

像这样定义的flatten函数是递归函数,它解包列表并执行此操作直到没有更多列表,然后它将再次将结果包装在List! 这种方式List[List[List.......[_]]]将转换为List[_].

类型模式的另一个示例:

def hello(o: Any) = o match {
    case _:Int => "Hi, I am an Int and I'm proud of it!"
    case _:List[_] => "I have many things to tell you! I am a list after all!"
    case b:Boolean => s"I'm simply a flag and my value is $b"
    case _ => "I'm everything else you can imagine :D"
}

消歧义

顺便说一下,部分函数与部分应用函数和部分应用完全不同!

Scala 中偏函数的概念与数学中的偏函数相同一个函数,对于其的某些值可能是未定义的。

于 2014-10-19T12:19:32.277 回答