这种递归List flattening如何工作?

时间:2011-08-27 07:28:31

标签: scala implicit

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) 

我一直在看这段代码。我无法弄清楚它是如何工作的。似乎有一些涉及的递归......任何人都可以解决一些问题吗?还有其他这种模式的例子吗?它有名字吗?

2 个答案:

答案 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],或UList[List[List[U]]]等。通常,此类函数可以直接指定为a的类型参数。但是我们将在implicits中使用这个,因此命名的包装器可以避免任何隐式冲突的风险。

然后,从recFlatten向后工作:

def recFlatten[T, U](xs: List[T])(implicit f: Flat[List[T], U]) = f fn xs 

此方法将xsList[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可以作为自己的隐含参数
  • 它返回一个Flat [List [X],X],因此如果recFlattenFnFlat[T,U] T将仅隐式解析为List >
  • 如果隐式解决方案失败,隐式f可以回退到默认值(即T不是List

也许在其中一个例子中最好地理解这一点:

recFlatten(List(List(1, 2, 3), List(4, 5))) 
  • 类型T推断为List[List[Int]]
  • 尝试对“Flat [List [List [Int]],U]
  • 进行隐式查找
  • 这与递归定义的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) 

在第一种情况下,res0T3的类型为Int,您无法推断U3的类型,但您知道您需要Flat[List[Int, U3]] {1}}将隐式提供的对象。只有一个“隐式候选者”:recFlattenFn函数的结果及其类型为Flat[List[T2], List[U2]]。因此T2 = IntU2 = U3(我们仍需要推断)。

现在,如果我们无法使用recFlatten,我们必须为其提供参数f这是诀窍。您可以使用隐式类型Flat[Int, U2] 类型Int => List[Int]的默认值。让我们看一下可用的含义。如前所述,recFlattenFn可以提供Flat[List[T2], U2](对于新的T2U2)对象。它此时不符合f的预期签名。因此,这里没有隐含的好候选者,我们必须使用默认参数。由于默认参数的类型是Int =>列表[Int],U2U3Int,我们就去了。

希望这篇长篇散文能有所帮助。我留给您的是res1res2

的分辨率