Подражая Pass-By-Reference в Python / Javascript с использованием оболочек - хорошая практика? - PullRequest
0 голосов
/ 15 ноября 2018

Скажем, я хочу изменить число или какой-то другой примитив внутри функции.Например, что-то вроде этого (примечание: псевдокод):

// 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. , если нет, какие-либо альтернативы?

1 Ответ

0 голосов
/ 15 ноября 2018

Это может быть сделано таким образом, однако это увеличивает "побочные эффекты" вашей функции, которые большинство кодировщиков посоветовали бы свести к минимуму настолько, насколько это возможно. Вместо этого вы можете использовать для функции возвращаемое значение . В этом случае ваш код будет по-прежнему передавать значение «предыдущий» (или «старт») как примитив, но вернет результат.

Вот как это будет выглядеть в 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 также позволяют распространить это на случай, когда вам потребуется установить несколько примитивных значений: функции могут возвращать массивы / списки / кортежи / объекты, которые затем могут быть назначены путем распаковки / деструктурирования их в отдельные переменные.

...