我是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[_]
有人可以解释一下,也许可以提供一个更简单的例子来澄清这个概念吗?
答案 0 :(得分:13)
map
和flatMap
首先flatMap
是一个higher-order函数。
map
也是一个高阶函数,它会通过在List
的所有元素上应用函数将List
转换为新的List
。例如,如果你有
val l = List(1,2,3)
您可以使用
map
将其添加到新列表中
val doubled = l.map(_ * 2) // List(2,4,6)
那么flatMap
是什么?当您的函数获得flatMap
但返回Int
时,List[Int]
非常有用。在这种情况下,如果您将此功能传递给map
,那么您获得的内容将是List[List[Int]]
,但有时您希望获得List[Int]
。
您可以使用flatMap
代替。数学上它等同于第一个map
然后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)
}
起初看起来不像是一个功能但是它!它实际上是一个partial function,您可以将其视为具有一些细微差别的函数!
我们可以通过以下方式获得(几乎)相同的结果:
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
(由于类型擦除,您无法使用ms:List[Int]
之类的模式,请参阅this主题获取更多信息)。
因此,此代码段上的整个模式匹配意味着
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"
}
顺便说一句部分功能与partially applied function和部分申请完全不同!
Scala中的部分函数的概念与数学中的partial function相同:对于domain的某些值可能未定义的函数。