2011-09-24 65 views
8

Tôi có nền tảng của Java và Python và tôi đang học R gần đây.Làm thế nào để xử lý đối tượng R trong cuộc gọi chức năng?

Hôm nay tôi thấy R có vẻ xử lý các đối tượng hoàn toàn khác với Java và Python.

Ví dụ, đoạn mã sau:

x <- c(1:10) 
print(x) 
sapply(1:10,function(i){ 
      x[i] = 4 
     }) 
print(x) 

Mã này cung cấp cho các kết quả sau:

[1] 1 2 3 4 5 6 7 8 9 10 
[1] 1 2 3 4 5 6 7 8 9 10 

Nhưng tôi hy vọng dòng thứ hai của đầu ra là tất cả '4' kể từ khi tôi sửa đổi vector trong các chức năng sapply.

Vậy điều này có nghĩa là R tạo bản sao của các đối tượng trong hàm gọi thay vì tham chiếu đến các đối tượng không?

Trả lời

18

x được định nghĩa trong môi trường toàn cầu, không phải trong chức năng của bạn.

Nếu bạn cố gắng sửa đổi đối tượng không phải cục bộ như x trong một hàm thì R tạo bản sao của đối tượng và sửa đổi bản sao để mỗi khi bạn chạy chức năng ẩn danh của mình, một bản sao của x được thực hiện và thành phần thứ i của nó được đặt thành 4. Khi chức năng thoát khỏi bản sao đã bị biến mất mãi mãi. Bản gốc x không được sửa đổi.

Nếu chúng tôi viết x[i] <<- i hoặc nếu chúng tôi viết x[i] <- 4; assign("x", x, .GlobalEnv) thì R sẽ viết lại. Một cách khác để viết nó trở lại sẽ được thiết lập e, nói rằng, đối với môi trường mà x được lưu trữ trong và làm điều này:

e <- environment() 
sapply(1:10, function(i) e$x[i] <- 4) 

hoặc có thể đây:

sapply(1:10, function(i, e) e$x[i] <- 4, e = environment()) 

Thông thường người ta không viết như vậy mã trong R. Thay một tạo ra kết quả như đầu ra của hàm như thế này:

x <- sapply(1:10, function(i) 4) 

(trên thực tế trong trường hợp này người ta có thể viết x[] <- 4)

thêm:.

Sử dụng proto package ai có thể làm điều này mà phương pháp f đặt thành phần thứ i của x sở hữu đến 4.

library(proto) 

p <- proto(x = 1:10, f = function(., i) .$x[i] <- 4) 

for(i in seq_along(p$x)) p$f(i) 
p$x 

thêm:.

gia tăng trên một tùy chọn trong đó chúng ta một cách rõ ràng thông qua môi trường mà x được lưu trữ trong

+0

Cảm ơn! Nhưng tại sao không ai viết mã như vậy trong R? Có một số rủi ro tiềm ẩn hay chỉ là một quy ước? Tôi nghĩ rằng nó là khá bình thường để sửa đổi các đối tượng toàn cầu trong một chức năng trong các ngôn ngữ khác. –

+3

Trong các chức năng ngôn ngữ chức năng không thể có tác dụng phụ. R không phải là nghiêm ngặt nhưng nó vẫn đúng rằng R chức năng giới hạn tác dụng phụ. Nó tốt hơn để làm việc theo cách nó được dự định sẽ được sử dụng hơn là cố gắng viết như thể bạn đang viết bằng một ngôn ngữ khác. Có một số hệ thống đối tượng (S3, S4, Lớp tham khảo). S3 được sử dụng phổ biến nhất. S4 phức tạp hơn nhiều. Các lớp tham chiếu là một bổ sung gần đây. Bạn có thể muốn khám phá các Lớp tham khảo, đặc biệt. Ngoài ra còn có một số gói người dùng đóng góp cung cấp các mô hình khác nhau: proto và R.oo (và có thể cả những người khác). –

+0

@Spirit Plus bạn có thể sử dụng 'parent.frame (3)' thay vì '.GlobalEnv' để lưu trữ x trong một đóng cửa trong đó sapply được chạy, những gì sẽ được an toàn hơn nhiều. (Tại sao 3? 1 khung chức năng ẩn danh, khung 2 cực kỳ, bao vây 3 mặt) – mbq

7

Có, bạn nói đúng. Kiểm tra Định nghĩa Ngôn ngữ R: 4.3.3 Argument Evaluation

AFAIK, R không thực sự sao chép dữ liệu cho đến khi bạn đang sửa đổi dữ liệu, do đó tuân theo ngữ nghĩa Copy-on-write.

+0

Cảm ơn Anatoliy! Nhưng sao chép lại có quá nhiều thời gian và bộ nhớ nếu dữ liệu sao chép thực sự lớn? Hoặc nó không thực sự sao chép dữ liệu, nhưng chỉ trung hòa hiệu ứng modfication vào cuối cuộc gọi hàm? –

+0

Có một bản sao, nhưng "x" bên trong hàm không phải là đối tượng giống với đối tượng bên ngoài hàm. Có môi trường và bạn có một x trong môi trường gọi và một x khác trong môi trường chức năng. Chỉ bằng cách gán kết quả sẽ thay đổi được hiển thị trong môi trường gọi. –

0

Bạn cần gán kết quả đầu ra một cách thủ công cho đối tượng, nếu không nó sẽ biến mất. (Trên thực tế bạn có thể khôi phục lại nó vì nó cũng được giao cho .Last.value)

x <- c(1:10) 
print(x) 
[1] 1 2 3 4 5 6 7 8 9 10 
x <- sapply(1:10,function(i){ 
      x[i] = 4 
     }) 
print(x) 
[1] 4 4 4 4 4 4 4 4 4 4 
+0

Xin lỗi, nhưng điều này không trả lời được câu hỏi và chỉ gây nhầm lẫn. – mbq

+0

Bây giờ tôi là người bối rối. OP tạo ra một vectơ 4 và sau đó không làm gì với nó. Nếu anh ta muốn "x" thay đổi, anh ta cần sử dụng phép toán. Tôi nghĩ tôi đã trả lời câu hỏi một cách chính xác. –

+0

Bạn dường như gợi ý rằng 'v <-sapply (..., function (...) {... v ...})' xây dựng bằng cách nào đó xuất 'v' từ môi trường hàm thành một parent. Không được đề cập đến câu hỏi là khá trực tiếp về việc liệu R có thực hiện gọi theo bản sao hoặc gọi theo tham chiếu hay không. – mbq

3

Các x đó là bên trong hàm nặc danh là không các x trong môi trường toàn cầu (không gian làm việc của bạn). Đây là bản sao của x, địa phương cho chức năng ẩn danh. Nó không phải là quá đơn giản để nói rằng R bản sao đối tượng trong các cuộc gọi chức năng; R sẽ cố gắng để không sao chép nếu nó có thể, mặc dù một khi bạn sửa đổi một cái gì đó R đã sao chép các đối tượng.

Như @DWin chỉ ra, phiên bản này sao chép của x đó đã được sửa đổi trả về bởi các sapply() cuộc gọi, đầu ra tuyên bố của bạn không phải là những gì tôi nhận được:

> x <- c(1:10) 
> print(x) 
[1] 1 2 3 4 5 6 7 8 9 10 
> sapply(1:10,function(i){ 
+    x[i] = 4 
+   }) 
[1] 4 4 4 4 4 4 4 4 4 4 
> print(x) 
[1] 1 2 3 4 5 6 7 8 9 10 

Rõ ràng, các mã đã gần như những gì bạn nghĩ rằng nó sẽ. Vấn đề là đầu ra từ sapply() không được gán cho một đối tượng và do đó được in và từ đó bị loại bỏ.

Lý do bạn mã thậm chí hoạt động là do các quy tắc phạm vi của R. Bạn thực sự nên chuyển sang một hàm làm đối số cho bất kỳ đối tượng nào mà hàm cần. Tuy nhiên, nếu R không thể tìm thấy một đối tượng cục bộ với hàm, nó sẽ tìm kiếm môi trường cha mẹ cho một đối tượng khớp với tên, và sau đó là cha của môi trường đó nếu thích hợp, cuối cùng nhấn môi trường toàn cục, vùng làm việc. Vì vậy, mã của bạn hoạt động vì nó cuối cùng tìm thấy một x để làm việc với, nhưng đã được sao chép ngay lập tức, bản sao đó được trả lại vào cuối cuộc gọi sapply().

Quá trình sao chép này mất nhiều thời gian và bộ nhớ trong nhiều trường hợp. Đây là một trong những lý do mọi người nghĩ rằng các vòng for chậm trong R; họ không phân bổ lưu trữ cho một đối tượng trước khi điền nó với một vòng lặp. Nếu bạn không cấp phát bộ nhớ, R phải sửa đổi/sao chép đối tượng để thêm kết quả tiếp theo của vòng lặp.

Một lần nữa, mặc dù nó không phải lúc nào cũng đơn giản, ở khắp mọi nơi trong R, ví dụ với môi trường, nơi một bản sao của một môi trường thực sự chỉ đề cập đến phiên bản gốc:

> a <- new.env() 
> a 
<environment: 0x1af2ee0> 
> b <- 4 
> assign("b", b, env = a) 
> a$b 
[1] 4 
> c <- a ## copy the environment to `c` 
> assign("b", 7, env = c) ## assign something to `b` in env `c` 
> c$b ## as expected 
[1] 7 
> a$b ## also changed `b` in `a` as `a` and `c` are actually the same thing 
[1] 7 

Nếu bạn hiểu những loại của mọi thứ, đọc hướng dẫn sử dụng R Language Definition bao gồm nhiều chi tiết về những gì diễn ra dưới mui xe trong R.

+0

Rất tiếc - tôi đã viết giờ này (giờ sáng Vương quốc Anh) nhưng phải có lề và không nhấp vào nút gửi. –

0

Nếu bạn muốn thay đổi đối tượng "toàn cầu" từ bên trong một hàm thì bạn có thể sử dụng gán cục bộ.

x <- c(1:10) 
# [1] 1 2 3 4 5 6 7 8 9 10 
print(x) 
sapply(1:10,function(i){ 
      x[i] <<- 4 
     }) 
print(x) 
# [1] 4 4 4 4 4 4 4 4 4 4 

Mặc dù trong trường hợp này bạn chỉ có thể có nó gọn hơn như x[]<-4

Đó là, bằng cách này, một trong những tính năng tốt đẹp của R - thay vì sapply(1:10,function(i) x[i] <<- 4 hoặc for(i in 1:10) x[i]<-4 (for không phải là một chức năng, vì vậy bạn không cần <<- ở đây) bạn chỉ có thể viết x[]<-4 :)

Các vấn đề liên quan