Я пытаюсь понять, почему я получаю другой вывод в двух разных функциях в R против той же (?) Реализации в python.
питон:
def increment(n):
n = n + 1
print(n)
n = 1
increment(n)
print(n)
2
1
def increment2(x):
x[0] = x[0] + 1
print(x)
n = [1]
increment2(n)
print(n)
2
2
Р:
increment <- function(n){
n = n + 1
print(n)
}
n = 1
increment(n)
2
print(n)
1
increment2 <- function(n){
n[1] = n[1] + 1
print(n)
}
n = c(1)
increment2(n)
2
print(n)
1
В моей голове кажется более последовательным вывод R. все находится внутри функции и не выходит наружу (если я не вернусь и не верну выход на n). Может ли кто-нибудь дать мне пифоновую интерпретацию?
Я не могу говорить о том, как R передает параметры, но довольно часто для языков программирования (включая Python), чтобы мутации на изменяемых объектах отражались вне функции, которая выполняла мутацию. Java, С# и другие популярные языки, поддерживающие ООП (объектно-ориентированное программирование).
Списки, подобные [1]
являются изменяемыми объектами, поэтому вы видите эту мутацию вне функции. Этот тип поведения делает объектно-ориентированное программирование намного более удобным.
Если это поведение нежелательно, рассмотрите возможность использования стиля функционального программирования в python (неизменяемые объекты, map
, filter
, reduce
) или передачу копий ваших изменяемых объектов в ваши функции.
Я не думаю, что здесь многое происходит, что связано с тем, что оно является питоническим или нет. Это языковой механизм: ничего более.
Это можно интерпретировать в терминах идентичности объекта.
Список x
в python похож на указатель на то, что он имеет идентификатор, не зависящий от его содержимого, поэтому присвоение нового значения элементу списка не изменяет личность списка. Изменение содержимого функции не изменяет идентификатор списка, и кажется, что функция может свободно изменять содержимое.
Вектор в R не имеет тождества, кроме его содержимого. Изменение содержимого в функции создает новый вектор. Исходный вектор не изменяется. R имеет объекты, имеющие идентификатор объекта - они называются средами.
increment3 <- function(e){
e$n = e$n + 1
print(e$n)
}
e <- new.env()
e$n <- 1
increment3(e)
## [1] 2
print(e$n)
## [1] 2
В R также можно изменить вектор на месте с использованием внешнего кода C или C++. Например, см. Https://gist.github.com/ggrothendieck/53811e2769d0582407ae
На R в значительной степени влияют функциональные языки, в первую очередь Scheme. В функциональных языках "функция" понимается так же, как и в математике, она не (и не может) изменять свои аргументы, а ее вывод зависит только от аргументов (и ничего больше).
# pseudocode
let x be 1
tell_me sin(x) # 0.841
tell_me x # still 1
Вполне возможно, что sin(x)
совершит грех (с функциональной точки зрения) и присвоит новое значение x
.
Однако R не является чисто функциональным языком.
(1) Вы можете (легко, а иногда и с плохими последствиями) получить доступ к объектам внутри функции.
> rm(jumbo) # if you're not running this for the first time
> mumbo <- function() jumbo
> mumbo()
Error in mumbo() : object 'jumbo' not found
> jumbo <- 1
> mumbo()
[1] 1
[edit] В комментарии было высказано возражение о том, что некоторые объекты должны быть видимыми внутри функции. Это совершенно верно, например, невозможно определить арифметические операции в каждой функции. Поэтому определение +
должно быть доступно... но разница в том, что на некоторых языках у вас есть явный контроль над тем, что доступно, а что нет. Я не эксперт по python, но я предполагаю, что это подразумевается под
from jumbo import *
R имеет пакеты, которые вы можете присоединить аналогичным образом, но разница в том, что все в вашей рабочей области по умолчанию видимо внутри функции. Это может быть полезно, но также опасно, поскольку вы можете непреднамеренно ссылаться на объекты, которые вы забыли определить внутри функции... и вещь будет работать неправильно, как в следующем примере:
X <- 1e+10
addone <- function(x) X + 1 # want to increment the argument by 1
addone(3)
# [1] 1e+10
addone(3)==1e+10+1
# [1] TRUE
Этого можно избежать в пакетах, поэтому функция в пакете не может случайно получить значения из вашей глобальной рабочей области. И если вы так склонны, вы можете изменить среду своих собственных функций. Это может быть способ предотвратить такие случайные ошибки (не обязательно удобный способ):
environment(mumbo) # .GlobalEnv
environment(mumbo) <- baseenv() # changing the environment
mumbo() # error: object 'jumbo' not found
[/редактировать]
(2) Вы можете, если хотите, изменить внешние объекты изнутри функции, например, с помощью <<-
(в отличие от <-
):
> increment.n <- function(){
+ n <<- n + 1
+ print(n)
+ }
> increment.n()
Error in increment.n() : object 'n' not found
> n <- 1
> increment.n()
[1] 2
> n
[1] 2
>
n1 = copy.deepcopy(n)
и затем поработать надn1
pass by value or reference
чтобы получить представление о том, что происходит