前一阵子在Scala邮件列表上被询问并回答:
凯文:
给定一些嵌套结构:
List[List[...List[T]]]
将它展平为最佳(最好是类型安全)的方法是什么List[T]
加斯帕:
隐含和默认参数的组合起作用:
case class Flat[T, U](fn : T => List[U])
implicit def recFlattenFn[T, U](implicit f : Flat[T, U] = Flat((l : T)
=> List(l))) =
Flat((l : List[T]) => l.flatMap(f.fn))
def recFlatten[T, U](l : List[T])(implicit f : Flat[List[T], U]) = f.fn(l)
Run Code Online (Sandbox Code Playgroud)
例子:
scala> recFlatten(List(1, 2, 3))
res0: List[Int] = List(1, 2, 3)
scala> recFlatten(List(List(1, 2, 3), List(4, 5)))
res1: List[Int] = List(1, 2, 3, 4, 5)
scala> recFlatten(List(List(List(1, 2, 3), List(4, 5)), List(List(6, 7))))
res2: List[Int] = List(1, 2, 3, 4, 5, 6, 7)
Run Code Online (Sandbox Code Playgroud)
我一直在看这段代码.我无法弄清楚它是如何工作的.似乎有一些涉及的递归......任何人都可以解决一些问题吗?是否有其他这种模式的例子,它有名字吗?
Kev*_*ght 12
哇,这是一个旧的!我将首先清理一下代码并将其与当前的惯用约定相符合:
case class Flat[T, U](fn: T => List[U])
implicit def recFlattenFn[T, U](
implicit f: Flat[T, U] = Flat((xs: T) => List(xs))
) = Flat((xs: List[T]) => xs flatMap f.fn)
def recFlatten[T, U](xs: List[T3])(implicit f: Flat[List[T], U]) = f fn xs
Run Code Online (Sandbox Code Playgroud)
然后,不用多说,分解代码.首先,我们有我们的Flat
班级:
case class Flat[T, U](fn: T => List[U])
Run Code Online (Sandbox Code Playgroud)
这只不过是函数的命名包装器T => List[U]
,它是一个List[U]
在给定类型实例时构建的函数T
.注意,T
这里也可以是a List[U]
,或a U
或a List[List[List[U]]]
等.通常,这样的函数可以直接指定为参数的类型.但是我们将在implicits中使用这个,因此命名的包装器可以避免任何隐式冲突的风险.
然后,从recFlatten
以下方向向后工作:
def recFlatten[T, U](xs: List[T])(implicit f: Flat[List[T], U]) = f fn xs
Run Code Online (Sandbox Code Playgroud)
此方法将采用xs
(a List[T]
)并将其转换为a U
.为了实现这一点,它找到了一个隐含的实例Flat[T,U]
并调用了封闭的函数,fn
然后,真正的魔力:
implicit def recFlattenFn[T, U](
implicit f: Flat[T, U] = Flat((xs: T) => List(xs))
) = Flat((xs: List[T]) => xs flatMap f.fn)
Run Code Online (Sandbox Code Playgroud)
这满足了所需的隐式参数recFlatten
,它还需要另一个隐式参数.最重要的是:
recFlattenFn
可以作为自己的隐含参数recFlattenFn
只能隐式解析为Flat[T,U]
if T
是aList
f
如果隐式解析失败,则隐式可以回退到默认值(即T
不是a List
)也许在其中一个例子的背景下最好地理解这一点:
recFlatten(List(List(1, 2, 3), List(4, 5)))
Run Code Online (Sandbox Code Playgroud)
T
被推断为List[List[Int]]
recFlattenFn
广义而言:
recFlattenFn[List[List[Int]], U] ( f =
recFlattenFn[List[Int], U] ( f =
Flat[Int,U]((xs: T) => List(xs)) //default value
)
)
Run Code Online (Sandbox Code Playgroud)
请注意,recFlattenFn
只会匹配a的隐式搜索,Flat[List[X], X]
并且类型params [Int,_]
会失败,因为Int
它不是a List
.这是触发回退到默认值的原因.
类型推断也可以向后反转该结构,U
在每个递归级别解析参数:
recFlattenFn[List[List[Int]], Int] ( f =
recFlattenFn[List[Int], Int] ( f =
Flat[Int,Int]((xs: T) => List(xs)) //default value
)
)
Run Code Online (Sandbox Code Playgroud)
这只是一个Flat
实例的嵌套,每个实例(最里面除外)执行一个flatMap
操作来展开嵌套List
结构的一个级别.最里面Flat
只需将所有单个元素包装回一个单独的元素中List
.
QED