我目前正在下班后的空闲时间参加Coursera的Scala课程,试图最终尝试功能性编程。我目前正在进行一项任务,我们应该“计算”包含一些对象的两个集合的并集。我故意省略细节,因为这对我在这里要问的内容并不重要。然而,相关的是集合被定义为二叉树,每个节点包含一个元素和两个子树。
情况确实如此;讲座中的示例union
如下:
def union(other:BTSet) :BTSet = ((left union right) union other) incl element
问题1:坦率地说,即使阅读了相关的常见问题解答和其他论坛帖子,我仍然不明白这个功能的工作原理和原因。除了在头节点添加(incl
调用)元素之外,在union实现中绝对没有完成“动作”,它只是一遍又一遍地调用自身。我非常感谢一些解释......
问题2:课程论坛包含很多帖子,说明这个解决方案根本没有效率,而且还不够好。因为我不明白它是如何工作的,所以我不明白为什么它不够好。
请注意,我不以任何方式要求为分配解决方案提供扰流板。我更愿意“为年级做好工作”,但我根本不明白我应该在这里做什么。我不相信课程中提供的说明和指导足以让您了解函数式编程的怪癖,因此我欢迎任何解决如何正确思考而不是如何正确编码。
答案 0 :(得分:25)
A
/ \ union D
B C
((B union C) union D) incl A
^^^^^^^^^......................................assume it works
( B )
( \ union D ) incl A
( C )
(((0 union C) union D) incl B) incl A
^^^^^^^^^.....................................just C
(((C union D) incl B) incl A
^^^^^^^^^.....................................expand
((((0 union 0) union D) incl C) incl B) incl A
^^^^^^^^^....................................just 0
(((0 union D) incl C) incl B) incl A
^^^^^^^^^.....................................just D
((D incl C) incl B) incl A
^^^^^^^^^^^^^^^^^^^^^^^^^^.......................all incl now
一步一步地写出来。现在你看到联合减少为应用于右手参数的一堆incl语句。
答案 1 :(得分:4)
我认为incl
将元素插入到现有集合中?如果是这样,那就是所有实际工作的发生地。
union的定义是包含任一输入集中的所有内容的集合。给定两个存储为二叉树的集合,如果将第一个集合的联合与第二个集合的分支一起使用,结果中唯一可能缺少的元素是第二个树的根节点处的元素,所以如果你插入那个元素,你有两个输入集的联合。
这只是一种非常低效的方法,可以将两个集合中的每个元素插入到一个空白的新集合中。推测重复被incl
丢弃,因此结果是两个输入的并集。
暂时忽略树形结构可能会有所帮助;它对于基本算法并不重要。假设我们有抽象的数学集。给定一个包含未知元素的输入集,我们可以做两件事:
为了取两组{1,2}和{2,3}的并集,我们首先将第一组分解为元素1和子集{}和{2}。我们使用相同的过程递归地获取{},{2}和{2,3}的并集,然后在结果中插入1。
在每个步骤中,问题从较小的输入上的一个联合操作减少到两个联合操作;标准的分而治之算法。当达到单例集合{x}和空集{}的并集时,联合很简单{x},然后返回到链中。
树结构仅用于允许案例分析/分解为较小的集合,并使插入更有效。使用其他数据结构也可以完成相同的操作,例如分成两半的列表用于分解,并通过穷举检查唯一性来完成插入。要有效地使用union,需要一个更聪明的算法,并利用用于存储元素的结构。
答案 2 :(得分:3)
基于上面的所有回答,我认为真正的主力是incl
,而调用union
的递归方式只是用于遍历集合中的所有元素。
我想出了以下的union实现,这样更好吗?
def union(other:BTSet) :BTSet = right union (left union (other incl element))
答案 3 :(得分:2)
2
/ \ union 4
1 3
((1 union 3) union 4) incl 2
^^^^^^^^^......................................assume it works
(((E union E) union 3 incl 1) union 4) incl 2
^^^^^^^^^.....................................still E
(E union E) union 3 incl 1 = E union 3 incl 1 = 3 incl 1
以下子树应 3 包含 1
( 3 )
( \ union D ) incl 2
( 1 )
(((1 union E) union 4) incl 3) incl 2
^^^^^^^^^.......................................expand
(((( (E union E) union E) incl 1) union 4) incl 3) incl 2
^^^^^^^^^^^^^^^^^^^^^^^^^^..................still 1
((1 union 4) incl 3) incl 2
^^^^^^^^......................................continue
((((E union E) union 4) incl 1) incl 3) incl 2
^^^^^^^^^^^^^^^^^^^^^^^^^^^^..........expand 1 union 4
((4 incl 1) incl 3) incl 2
^^^^^^^^^^^^^^^^^^^^^^^^^............Final union result
谢谢@Rex Kerr提出的步骤。我将第二步替换为实际的运行时步骤,这可以更清楚地描述Scala union
函数。
答案 4 :(得分:1)
除非您查看基本案例,否则您无法理解递归算法。事实上,理解的关键通常在于首先理解基础案例。由于未显示基本情况(可能是因为您没有注意到首先存在一个),因此无法理解。
答案 5 :(得分:-3)
我正在做相同的课程,union
的上述实施确实效率极低。
我提出了以下不那么实用的解决方案来创建二叉树集合,这样会更有效:
def union(that: BTSet): BTSet = {
var result:BTSet = this
that.foreach(element => result = result.incl(element))
result
}