引言
我们从一个链表的构造函数开始,“cons”,它接收一个必要参数“head”及一个可选参数“tail”(相对于支持这样的实现的语言来说)。该构造函数返回一个链表的表示结构体,其中第一个元素为“head”,其余的元素被包裹在“tail“链表中。空链表的话用JavaScript中的undefined或Python中的None来表示。
举例来说,直到微小变化,”cons(1, cons(2, cons(3, cons(4))))”构造了一个含有4个元素的链表,”1 2 3 4“。
为了便于检查链表中的内容,我们定义了”listToString“方法来将链表转换为相应的字符串,其中链表元素之间用空格隔开。
”myMap“方法接收一个一元函数”fn“和一个链表”list“。它循序遍历链表中的每一个元素,并返回一个各元素都被”fn“转化过了的链表。
”myReduce”方法会对输入的链表从头到尾使用一个reducer函数“fn”,然后返回最终结果。比如,假设链表为“cons(1, cons(2, cons(3,)))”,“myReduce(fn, accm, list)”应该返回执行“fn(fn(fn(accm, 1), 2), 3)”得到的结果。
上述的三个方法都是使用递归实现的,巧妙运用了链表的递归结构。
第1部分:实现“myReduceRight”
请实现“myReduceRight”方法。其类似于“myReduce”,不同之处在于它是从尾到头对输入的链表使用reducer函数“fn”的。比如,假设链表为“cons(1, cons(2, cons(3)))”,”myReduceRight(fn, accm, list)”应该返回执行“fn(1, fn(2, fn(3, accm)))”得到的结果。
要求:
- 你需要使用递归来实现,而不是任何显式的for/while循环;
- 你不能在你的实现中使用先前已定义好的”listToString“、”myMap“和”myReduce“方法;
- 你不能修改原始链表。
要检查你的实现的正确性,可以验证:
- ”
myReduceRight(xTimesTwoPlusY, 0, exampleList)
“应该得到“20”; - “
myReduceRight(unfoldCalculation, accm, exampleList)
“应该表示为”fn(1, fn(2, fn(3, fn(4, accm))))”; - “
myReduceRight(printXAndReturnY, 0, exampleList)
“应该按照输入链表的逆序来打印内容。
第2部分:实现”myMap2“
请基于“myReduceRight”方法实现”myMap2“,其应该在功能性上同质于”myMap“。
对实现的基本要求:
- 你不能在你的实现中使用先前已定义好的”listToString“、”myMap“和”myReduce“方法;
- 你不能修改任何函数的输入输出特征,包括”myReduceRight“的输入输出特征;
- 你不能在借在”myReduceRight“中投机取巧来助力实现”myMap2“,例如向”myReduceRight“传递隐藏标志以表示特殊处理;
- 你不能使用任何语言原生的特殊数据结构(举例,C++中的”std::vector”,Java中的“ArrayList”,Python中的“list”)。
如果你的实现满足以下尽可能多的要求,你将获得“加分”:
- 不要使用任何显式的递归调用。特别地,避免在实现中声明调用“myMap2”;
- 不要在实现中使用任何显式的for/while循环。为此你需要探究下“myReduceRight”的巧妙用法;
- 不要修改原始链表。
以下是你可以遵循的几个方向:
- 列表翻转;
- 在reducer方法中修改链表;
- 巧妙地使用闭包和lambda函数来调整代码执行顺序。特别地,考虑考虑延时执行,如
(() -> doSomething)()。
要检查你的实现的正确性,可以验证:
- “
listToString(myMap2(plusOne, exampleList))
”应该得到“2 3 4 5”; - “
myMap2(printAndReturn, exampleList)
”应该按照正确的次序打印链表内容(“1 2 3 4”分别各占据一行而不是“4 3 2 1”)。
JavaScript代码模板:
1 | // Refer to README for detailed instructions. |
Python代码模板:
1 | # Refer to README for detailed instructions. |
最终实现:
JavaScript实现:
1 | // Refer to README for detailed instructions. |
诀窍:使用蹦床函数trampoline优化递归调用。
打印结果:
1 | '1 2 3 4' 'should be 1 2 3 4' |
Python实现:
1 | # Refer to README for detailed instructions. |
备注:Python的trampoline蹦床函数实现有些复杂,直接递归处理了。可参考文章Tail recursion in Python, part 1: trampolines)。
打印结果:
1 | 1 2 3 4 should be 1 2 3 4 |