Scala邮件列表上的一段时间this was asked and answered:
凯文:
给定一些嵌套结构:
的最佳(最好是类型安全的)方式List[List[...List[T]]]
什么是将其展平为List[T]
的Jesper:
implicits和默认参数的组合起作用:
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)
示例:
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)
我一直在看这段代码。我无法弄清楚它是如何工作的。似乎有一些涉及的递归......任何人都可以解决一些问题吗?还有其他这种模式的例子吗?它有名字吗?
答案 0 :(得分:11)
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
然后,不用多说,分解代码。首先,我们有Flat
类:
case class Flat[T, U](fn: T => List[U])
这只不过是函数T => List[U]
的命名包装器,该函数在给定类型为List[U]
的实例时将构建T
。请注意,T
此处也可以是List[U]
,或U
或List[List[List[U]]]
等。通常,此类函数可以直接指定为a的类型参数。但是我们将在implicits中使用这个,因此命名的包装器可以避免任何隐式冲突的风险。
然后,从recFlatten
向后工作:
def recFlatten[T, U](xs: List[T])(implicit f: Flat[List[T], U]) = f fn xs
此方法将xs
(List[T]
)并将其转换为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)
这满足recFlatten
所需的隐式参数,它还需要另一个隐式参数。最重要的是:
recFlattenFn
可以作为自己的隐含参数recFlattenFn
是Flat[T,U]
List
>
f
可以回退到默认值(即T
不是List
)也许在其中一个例子中最好地理解这一点:
recFlatten(List(List(1, 2, 3), List(4, 5)))
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
)
)
请注意,recFlattenFn
只会匹配Flat[List[X], X]
的隐式搜索,并且类型参数[Int,_]
会失败,因为Int
不是{{1} }}。这是触发回退到默认值的原因。
类型推断也会向后反转该结构,在每个递归级别解析List
参数:
U
这只是recFlattenFn[List[List[Int]], Int] ( f =
recFlattenFn[List[Int], Int] ( f =
Flat[Int,Int]((xs: T) => List(xs)) //default value
)
)
个实例的嵌套,每个实例(最里面除外)执行Flat
操作以展开嵌套flatMap
结构的一个级别。最里面的List
只需将所有单个元素包装回一个Flat
。
Q.E.D。
答案 1 :(得分:2)
可能是一个很好的解决方案,试着看看这些类型是如何被感染的。为避免歧义,让我们重命名泛型:
case class Flat[T, U](fn : T => List[U])
implicit def recFlattenFn[T2, U2](implicit f : Flat[T2, U2] =
Flat((l : T2) => List(l))) =
Flat((l : List[T2]) => l.flatMap(f.fn))
def recFlatten[T3, U3](l : List[T3])(implicit f : Flat[List[T3], U3]) = f.fn(l)
在第一种情况下,res0
,T3
的类型为Int
,您无法推断U3
的类型,但您知道您需要Flat[List[Int, U3]]
{1}}将隐式提供的对象。只有一个“隐式候选者”:recFlattenFn
函数的结果及其类型为Flat[List[T2], List[U2]]
。因此T2
= Int
和U2
= U3
(我们仍需要推断)。
现在,如果我们无法使用recFlatten
,我们必须为其提供参数f
。 这是诀窍。您可以使用隐式类型Flat[Int, U2]
或类型Int => List[Int]
的默认值。让我们看一下可用的含义。如前所述,recFlattenFn
可以提供Flat[List[T2], U2]
(对于新的T2
和U2
)对象。它此时不符合f
的预期签名。因此,这里没有隐含的好候选者,我们必须使用默认参数。由于默认参数的类型是Int =>列表[Int],U2
和U3
为Int
,我们就去了。
res1
和res2
。的分辨率