我无法理解函数式编程练习的解决方案:
仅使用
flatMap
,foldRight
和Nil
(利弊)实施::
。
解决方案如下:
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))
我试图将匿名函数分解为函数定义,以重写解决方案,但没有运气。我无法理解正在发生的事情或想到一种方法来打破它,所以它不那么复杂。因此,任何有关该解决方案的帮助或解释都将受到赞赏。
谢谢!
答案 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))
}
这是最终的实施。