使用包装器在 Python/Javascript 中模仿引用传递 - 好的做法?

Mimicking Pass-By-Reference in Python/Javascript Using Wrappers - Good Practice?

假设我想修改函数内部的数字或其他原语。例如,像这样的东西(注意:伪代码):

// 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. 如果没有,还有其他选择吗?

可以 那样做,但是它会增加 "sideeffects" 你的功能,大多数编码人员会建议尽可能减少。相反,您可以为它使用函数的 return 值。然后,您的代码仍会将 "previous"(或 "start")值作为原始值传递,但会传递 return 结果。

这是它在 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 也允许将其扩展到需要设置多个原始值的情况:函数可以 return arrays/lists/tuples/objects,然后可以由unpacking/destructuring 将它们分成单独的变量。