Python vs. R сфера

1

Я пытаюсь понять, почему я получаю другой вывод в двух разных функциях в 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). Может ли кто-нибудь дать мне пифоновую интерпретацию?

  • 2
    Вам может понадобиться n1 = copy.deepcopy(n) и затем поработать над n1
  • 3
    Google pass by value or reference чтобы получить представление о том, что происходит
Показать ещё 1 комментарий
Теги:

3 ответа

2

Я не могу говорить о том, как R передает параметры, но довольно часто для языков программирования (включая Python), чтобы мутации на изменяемых объектах отражались вне функции, которая выполняла мутацию. Java, С# и другие популярные языки, поддерживающие ООП (объектно-ориентированное программирование).

Списки, подобные [1] являются изменяемыми объектами, поэтому вы видите эту мутацию вне функции. Этот тип поведения делает объектно-ориентированное программирование намного более удобным.

Если это поведение нежелательно, рассмотрите возможность использования стиля функционального программирования в python (неизменяемые объекты, map, filter, reduce) или передачу копий ваших изменяемых объектов в ваши функции.

Я не думаю, что здесь многое происходит, что связано с тем, что оно является питоническим или нет. Это языковой механизм: ничего более.

  • 1
    R передает большинство аргументов по значению. (Есть несколько исключений, но массивы не являются одним из них. Изменяемые объекты в R обычно создаются в средах, которые являются исключением из правила передачи по значению.)
1

Это можно интерпретировать в терминах идентичности объекта.

Список 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

1

На 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
> 
  • 0
    Хорошо, удалил мой комментарий.
  • 0
    «Я тоже» слишком короткий, но я тоже так сделал
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню