Scala:使用foldRight实现flatMap

时间:2017-11-05 16:42:42

标签: scala functional-programming

我无法理解函数式编程练习的解决方案:

  

仅使用flatMapfoldRightNil(利弊)实施::

解决方案如下:

def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] = 
   xs.foldRight(List[B]())((outCurr, outAcc) =>
   f(outCurr).foldRight(outAcc)((inCurr, inAcc) => inCurr :: inAcc))

我试图将匿名函数分解为函数定义,以重写解决方案,但没有运气。我无法理解正在发生的事情或想到一种方法来打破它,所以它不那么复杂。因此,任何有关该解决方案的帮助或解释都将受到赞赏。

谢谢!

2 个答案:

答案 0 :(得分:4)

首先,在这种情况下,忽略约束并考虑flatMap函数。您有一个List[A]和一个函数f: A => List[B]。通常,如果您只是在列表中执行map并应用f功能,那么您将获得List[List[B]],对吧?所以要获得List[B],你会做什么?您只需在foldRight上附加List[List[B]]中的所有元素,List[B] List[List[B]]即可获得def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] = { val tmp = xs.map(f) // List[List[B]] tmp.foldRight(List[B]())((outCurr, outAcc) => outCurr ++ outAcc) } 。所以代码看起来会像这样:

flatMap

要验证我们到目前为止所拥有的内容,在REPL中运行代码并根据内置scala> def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] = { | val tmp = xs.map(f) // List[List[B]] | tmp.foldRight(List[B]())((outCurr, outAcc) => outCurr ++ outAcc) | } flatMap: [A, B](xs: List[A])(f: A => List[B])List[B] scala> flatMap(List(1, 2, 3))(i => List(i, 2*i, 3*i)) res0: List[Int] = List(1, 2, 3, 2, 4, 6, 3, 6, 9) scala> List(1,2,3).flatMap(i => List(i, 2*i, 3*i)) res1: List[Int] = List(1, 2, 3, 2, 4, 6, 3, 6, 9) 方法验证结果:

map

好的,现在,看看我们的约束,我们不允许在这里使用map。但我们并不需要,因为这里的xs仅用于遍历列表foldRight。然后,我们可以将map用于此目的。因此,让我们使用foldRight

重写def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] = { val tmp = xs.foldRight(List[List[B]]())((curr, acc) => f(curr) :: acc) // List[List[B]] tmp.foldRight(List[B]())((outCurr, outAcc) => outCurr ++ outAcc) } 部分
scala> def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] = {
     |         val tmp = xs.foldRight(List[List[B]]())((curr, acc) => f(curr) :: acc) // List[List[B]]
     |         tmp.foldRight(List[B]())((outCurr, outAcc) => outCurr ++ outAcc)
     |     }
flatMap: [A, B](xs: List[A])(f: A => List[B])List[B]

scala> flatMap(List(1, 2, 3))(i => List(i, 2*i, 3*i))
res3: List[Int] = List(1, 2, 3, 2, 4, 6, 3, 6, 9)

好的,让我们验证新代码:

foldRight
好的,到目前为止一切顺利。因此,让我们稍微优化代码,而不是按顺序排列两个foldRight,我们将它们合并为一个def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] = { xs.foldRight(List[B]()) { (curr, acc) => // Note: acc is List[B] val tmp2 = f(curr) // List[B] tmp2 ++ acc } } 。那不应该太难:

scala> def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] = {
     |     xs.foldRight(List[B]()) { (curr, acc) => // Note: acc is List[B]
     |         val tmp2 = f(curr) // List[B]
     |         tmp2 ++ acc
     |     }
     | }
flatMap: [A, B](xs: List[A])(f: A => List[B])List[B]

scala> flatMap(List(1, 2, 3))(i => List(i, 2*i, 3*i))
res4: List[Int] = List(1, 2, 3, 2, 4, 6, 3, 6, 9)

再次验证:

++

好的,让我们看看我们的约束,看起来我们不能使用++操作。好吧,List[B]只是将两个foldRight附加在一起的方法,所以我们当然可以使用def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] = { xs.foldRight(List[B]()) { (curr, acc) => // Note: acc is List[B] val tmp2 = f(curr) // List[B] tmp2.foldRight(acc)((inCurr, inAcc) => inCurr :: inAcc) } } 方法实现相同的目标,如下所示:

def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] = 
   xs.foldRight(List[B]())((curr, acc) =>
   f(curr).foldRight(acc)((inCurr, inAcc) => inCurr :: inAcc))

然后,我们可以将它们全部合并为一行:

library(HSAUR3)   
data(schizophrenia2)

layout(matrix(1:2, nrow =1))
ylim <- range(schizophrenia2$month)
Less <- subset(schizophrenia2, onset == "< 20 Years")
More <- subset(schizophrenia2, onset == "> 20 Years")

boxplot(month ~ disorder, data = Less, ylab = "Month",
    xlab = "Disorder", ylim = ylim, main = "Less than 20")

boxplot(month ~ disorder, data = More, ylab = "Month",
    xlab = "Disorder", ylim = ylim, main = "More than 20")

不是给定答案:)

答案 1 :(得分:0)

有时通过一个简单的例子可以更容易理解: 假设我们有一个val xs = List[Int](1,2,3)和一个函数f: Int => List[Int], f(x) = List(x,x) (lambda x => List(x,x)) 将f应用于xs List(f(1),f(2), f(3))的每个元素将导致List(List(1,1),List(2,2),List(3,3)),因此我们需要展平此List [List [Int]]。最终结果应为List(1,1,2,2,3,3)。给定Cons(::)非空List的构造函数,这应该是Cons(1, Cons(1, Cons(2, Cons(2, Cons(3, Cons(3, Nil))))))。观察结果中的foldRight操作,该操作将构造函数Cons(::)应用于应用于列表xs的每个元素的f的结果。所以flatMap的第一个实现是

def flatMap[A,B](xs: List[A])(f: A => List[B]): List[B] = xs match {
  case Cons(head, tail) => foldRight(f(head), Nil)((a,b) => Cons(a,b))
}

在此表单中,flatMap(List(1,2,3))将返回List(1,1)或Cons(1,1,Nil)(通过替换)。所以我们需要继续向尾部的flatMap递归调用(将问题从3(元素)减少到2(元素))并将空列表的基本情况添加为Nil (Nil是Cons操作的“零”元素)

def flatMap[A,B](xs: List[A])(f: A => List[B]): List[B] = xs match {
  case Nil => Nil
  case Cons(head, tail) => foldRight(f(head), flatMap(tail)(f))((a,b) => Cons(a,b))
}

这是最终的实施。