使用包装器模拟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 = 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)

然而,我最近在互联网上搜索了一下,看看这是否是一种常见的模式,但找不到太多关于它的信息。具体来说,我想知道三件事:

  1. 这是常见的模式吗
  2. 这种做法好吗
  3. 如果没有,还有其他选择吗

可以这样做,但它会增加函数的"副作用",大多数程序员建议尽可能减少副作用。相反,您可以使用函数的返回值。然后,您的代码仍然会将"上一个"(或"开始"(值作为基元传递,但将返回

以下是它在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还允许将其扩展到需要设置多个基元值的情况:函数可以返回数组/列表/元组/对象,然后可以通过将它们拆包/销毁为单独的变量来分配它们。

最新更新