假设我想修改函数内部的数字或其他原语。例如,类似这样的东西(注意:伪代码(:
// apply fn to every value in a tree, in-order traversal
function treeReduce (tree, fn, result):
if (tree == undefined) return
treeReduce(tree.left, fn, result)
result = fn(result, tree.value)
treeReduce(tree.right, fn, result)
sum = 0
treeReduce(myTree, +, sum)
显然,这是行不通的,因为result
刚刚被重新分配,传入的sum
不会看到修改。因此,我绕过这一问题的一种常见方法(在任何传递值的语言中,如Python或Javascript(是使用包装器:
// apply fn to every value in a tree, in-order traversal
function treeReduce (tree, fn, result):
if (tree == undefined) return
treeReduce(tree.left, fn, result)
result[0] = fn(result[0], tree.value)
treeReduce(tree.right, fn, result)
sumWrapper = [0]
treeReduce(myTree, +, sumWrapper)
然而,我最近在互联网上搜索了一下,看看这是否是一种常见的模式,但找不到太多关于它的信息。具体来说,我想知道三件事:
- 这是常见的模式吗
- 这种做法好吗
- 如果没有,还有其他选择吗
它可以这样做,但它会增加函数的"副作用",大多数程序员建议尽可能减少副作用。相反,您可以使用函数的返回值。然后,您的代码仍然会将"上一个"(或"开始"(值作为基元传递,但将返回。
以下是它在JS中的样子(我用一个不那么琐碎的fn
来演示它执行有序执行(:
// apply fn to every value in a tree, in-order traversal
function treeReduce (tree, fn, start) {
if (tree === undefined) return start
let result = treeReduce(tree.left, fn, start)
result = fn(result, tree.value)
result = treeReduce(tree.right, fn, result)
return result
}
let myTree = { value: 1, left: { value: 2 }, right: { value: 3 } }
let result = treeReduce(myTree, (a,b) => a*a+b, 0)
console.log(result)
请注意,以上内容现在可以写得更简洁:
// apply fn to every value in a tree, in-order traversal
function treeReduce (tree, fn, start) {
return !tree ? start
: treeReduce(tree.right, fn, fn(treeReduce(tree.left, fn, start), tree.value))
}
let myTree = { value: 1, left: { value: 2 }, right: { value: 3 } }
let result = treeReduce(myTree, (a,b) => a*a+b, 0)
console.log(result)
在Python中:
import collections
Tree = collections.namedtuple('Tree', ['value', 'left', 'right'])
# apply fn to every value in a tree, in-order traversal
def treeReduce (tree, fn, start):
return start if not tree else (
treeReduce(tree.right, fn, fn(treeReduce(tree.left, fn, start), tree.value))
)
myTree = Tree(1, Tree(2,None,None), Tree(3,None,None))
result = treeReduce(myTree, lambda a,b: a*a+b, 0)
print(result)
JS和Python还允许将其扩展到需要设置多个基元值的情况:函数可以返回数组/列表/元组/对象,然后可以通过将它们拆包/销毁为单独的变量来分配它们。