2013-01-18 16 views
23

Cập nhật 2 @G. Grothendieck đăng hai cách tiếp cận. Thứ hai là thay đổi môi trường chức năng bên trong một hàm. Điều này giải quyết vấn đề của tôi quá nhiều bản sao mã hóa. Tôi không chắc chắn nếu đây là một phương pháp tốt để vượt qua kiểm tra CRAN khi làm cho các kịch bản của tôi thành một gói. Tôi sẽ cập nhật lại khi tôi có một số kết luận.Trong R, làm thế nào để biến các biến bên trong một hàm có sẵn cho hàm mức thấp hơn bên trong hàm này? (Với, đính kèm, môi trường)

Cập nhật

Tôi cố gắng để vượt qua rất nhiều biến tranh luận đầu vào f2 và không muốn đánh chỉ mục tất cả các biến bên trong hàm như env$c, env$d, env$calls, đó là lý do tại sao tôi đã cố gắng để sử dụng with trong f5f6 (sửa đổi f2). Tuy nhiên, assign không làm việc với with bên trong {}, di chuyển assign ngoài with sẽ làm công việc nhưng trong trường hợp thật của tôi Tôi có một vài assign s bên trong with biểu thức mà tôi không biết làm thế nào để di chuyển chúng ra khỏi with chức năng một cách dễ dàng .

Dưới đây là một ví dụ:

## In the <environment: R_GlobalEnv> 
a <- 1 
b <- 2 
f1 <- function(){ 
    c <- 3 
d <- 4 
f2 <- function(P){ 
    assign("calls", calls+1, inherits=TRUE) 
    print(calls) 
    return(P+c+d) 
} 
calls <- 0 
v <- vector() 
for(i in 1:10){ 
    v[i] <- f2(P=0) 
    c <- c+1 
    d <- d+1 
    } 
return(v) 
} 
f1() 

Chức năng f2 là bên trong f1, khi f2 được gọi, nó sẽ tìm kiếm các biến calls,c,d trong môi trường environment(f1). Đây là những gì tôi muốn.

Tuy nhiên, khi tôi muốn sử dụng f2 cũng có trong các chức năng khác, tôi sẽ xác định chức năng này trong môi trường toàn cầu thay vào đó, gọi nó là f4.

f4 <- function(P){ 
    assign("calls", calls+1, inherits=TRUE) 
    print(calls) 
    return(P+c+d) 
} 

này sẽ không làm việc, bởi vì nó sẽ tìm kiếm calls,c,d trong môi trường toàn cầu thay vì bên trong một hàm mà hàm được gọi. Ví dụ:

f3 <- function(){ 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
    v[i] <- f4(P=0) ## or replace here with f5(P=0) 
    c <- c+1 
    d <- d+1 
    } 
    return(v) 
} 
f3() 

Cách an toàn nên được xác định calls,c,d trong các đối số đầu vào của f4 và sau đó vượt qua các thông số này vào f4. Tuy nhiên, trong trường hợp của tôi, có quá nhiều biến được chuyển vào hàm này f4 và tốt hơn là tôi có thể chuyển nó thành môi trường và yêu cầu f4 không nhìn vào môi trường Toàn cầu (environment(f4)), chỉ nhìn vào bên trong environment khi gọi f3.

Cách tôi giải quyết ngay bây giờ là sử dụng môi trường làm danh sách và sử dụng chức năng with.

f5 <- function(P,liste){ 
    with(liste,{ 
    assign("calls", calls+1, inherits=TRUE) 
    print(calls) 
    return(P+c+d) 
    } 
) 
} 
f3 <- function(){ 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
    v[i] <- f5(P=0,as.list(environment())) ## or replace here with f5(P=0) 
    c <- c+1 
    d <- d+1 
    } 
    return(v) 
} 
f3() 

Tuy nhiên, bây giờ assign("calls", calls+1, inherits=TRUE) không hoạt động như nó phải được kể từ assign không sửa đổi đối tượng gốc. Biến số calls được kết nối với chức năng tối ưu hóa trong đó hàm mục tiêu là f5. Đó là lý do tôi sử dụng assign thay vì chuyển số calls làm đối số nhập. Sử dụng attach cũng không rõ ràng với tôi.Đây là cách của tôi để sửa chữa vấn đề assign:

f7 <- function(P,calls,liste){ 
    ##calls <<- calls+1 
    ##browser() 
    assign("calls", calls+1, inherits=TRUE,envir = sys.frame(-1)) 
    print(calls) 
    with(liste,{ 
    print(paste('with the listed envrionment, calls=',calls)) 
    return(P+c+d) 
    } 
) 
} 
######## 
################## 
f8 <- function(){ 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
    ##browser() 
    ##v[i] <- f4(P=0) ## or replace here with f5(P=0) 
    v[i] <- f7(P=0,calls,liste=as.list(environment())) 
    c <- c+1 
    d <- d+1 
    } 
    f7(P=0,calls,liste=as.list(environment())) 
    print(paste('final call number',calls)) 
    return(v) 
} 
f8() 

Tôi không chắc chắn cách này nên được thực hiện trong R. Am tôi về đúng hướng, đặc biệt là khi đi qua cran kiểm tra? Bất cứ ai cũng có một số gợi ý về điều này?

Trả lời

22

(1) Vượt qua môi trường của người gọi. Bạn có thể chuyển một cách rõ ràng môi trường và chỉ mục chính vào đó. Hãy thử điều này:

f2a <- function(P, env = parent.frame()) { 
    env$calls <- env$calls + 1 
    print(env$calls) 
    return(P + env$c + env$d) 
} 

a <- 1 
b <- 2 
# same as f1 except f2 removed and call to f2 replaced with call to f2a 
f1a <- function(){ 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
     v[i] <- f2a(P=0) 
     c <- c+1 
     d <- d+1 
     } 
    return(v) 
} 
f1a() 

(2) Thiết lập lại gọi là chức năng của môi trường là để thiết lập lại môi trường của f2b trong f1b như ở đây:

f2b <- function(P) { 
    calls <<- calls + 1 
    print(calls) 
    return(P + c + d) 
} 

a <- 1 
b <- 2 
# same as f1 except f2 removed, call to f2 replaced with call to f2b 
# and line marked ## at the beginning is new 
f1b <- function(){ 
    environment(f2b) <- environment() ## 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
     v[i] <- f2b(P=0) 
     c <- c+1 
     d <- d+1 
     } 
    return(v) 
} 
f1b() 

(3) Macro sử dụng eval.parent (thay thế (...)) Tuy nhiên, cách tiếp cận khác là xác định một cấu trúc giống như macro có hiệu quả tiêm cơ thể của f2c vào trong f1c1. Ở đây f2c giống với f2b ngoại trừ đường dây calls <- calls + 1 (không cần <<-) và gói toàn bộ phần thân trong eval.parent(substitute({...})). f1c giống với f1a ngoại trừ cuộc gọi đến f2a được thay thế bằng một cuộc gọi đến f2c.

f2c <- function(P) eval.parent(substitute({ 
    calls <- calls + 1 
    print(calls) 
    return(P + c + d) 
})) 

a <- 1 
b <- 2 
f1c <- function(){ 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
     v[i] <- f2c(P=0) 
     c <- c+1 
     d <- d+1 
     } 
    return(v) 
} 
f1c() 

(4) defmacro này là gần như giống như các giải pháp cuối cùng, ngoại trừ nó sử dụng defmacro trong gói gtools để xác định vĩ mô chứ không phải làm việc đó ourself. (Cũng xem gói Rcmdr cho một phiên bản defmacro khác.) Vì cách thức hoạt động củachúng ta cũng phải vượt qua calls nhưng kể từ khi macro của nó không phải là chức năng, nó chỉ cho phép thay thế calls và không giống như khi đi qua calls chức năng.

library(gtools) 

f2d <- defmacro(P, calls, expr = { 
    calls <- calls + 1 
    print(calls) 
    return(P + c + d) 
}) 

a <- 1 
b <- 2 
f1d <- function(){ 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
     v[i] <- f2d(P=0, calls) 
     c <- c+1 
     d <- d+1 
     } 
    return(v) 
} 
f1d() 
+0

Điều này cho kết quả tôi muốn nhưng tôi không viết rõ ràng yêu cầu của tôi. Trong thực tế, tôi muốn tránh lập chỉ mục tất cả các biến đi vào 'f2', tức là, không viết' env $ ', đó là lý do tại sao tôi cố gắng sử dụng' with'. Tôi muốn có thể thay đổi 'bên ngoài' bên trong 'f2' nhưng không thay đổi các biến khác trong môi trường hiện tại. Tôi sẽ cập nhật câu hỏi. – Zhenglei

+0

Tôi đã thêm phương pháp thứ hai. –

+0

Nó trông giống như những gì tôi cần. Tôi sẽ kiểm tra nó với trường hợp thực sự của tôi và cập nhật sau. Cảm ơn bạn rất nhiều vì sự giúp đỡ! – Zhenglei

1

Nói chung, tôi sẽ nói rằng bất kỳ biến nào cần thiết bên trong một hàm phải được truyền qua các đối số của nó. Ngoài ra, nếu giá trị của nó là cần thiết sau đó bạn vượt qua nó trở lại từ chức năng. Không làm điều này có thể nhanh chóng dẫn đến kết quả lạ, ví dụ: nếu có nhiều chức năng xác định một biến số x, cái nào sẽ được sử dụng. Nếu số lượng biến lớn hơn, bạn tạo cấu trúc dữ liệu tùy chỉnh cho nó, ví dụ: đặt chúng vào một danh sách có tên.

+0

Tôi đồng ý với bạn @Paul nói chung. Tôi đang cố gắng để làm cho mã của tôi một gói R nhưng không thể vượt qua kiểm tra CRAN dễ dàng với nhiều cảnh báo như ràng buộc biến toàn cầu. Có nhiều mã lặp lại vì 'f2' được định nghĩa bên trong một hàm và tôi muốn sử dụng nó trong một hàm mới khác. Tôi nhận ra rằng sao chép dán nó không phải là một lựa chọn tốt và có thể gây ra vấn đề trong các bước sau. Tôi cũng muốn giảm bớt nỗ lực vì tôi không muốn thay đổi các kịch bản đã tồn tại quá nhiều. Đó là lý do tại sao tôi cố gắng vượt qua môi trường thay vì xác định cấu trúc dữ liệu mới. – Zhenglei

1

Bạn cũng có thể sử dụng chức năng xác định lại các chức năng khác trong môi trường được chỉ định.

test_var <- "global" 

get_test_var <- function(){ 
    return(test_var) 
} 

some_function <- function(){ 
    test_var <- "local" 
    return(get_test_var()) 

} 

some_function() # Returns "global". Not what we want here... 

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

some_function2 <- function(){ 
    test_var <- "local" 
    # define function locally 
    get_test_var2 <- function(){ 
    return(test_var) 
    } 
    return(get_test_var2()) 
} 

some_function2() # Returns "local", but 'get_test_var2' can't be used in other places. 

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

add_function_to_envir <- function(my_function_name, to_envir) { 
    script_text <- capture.output(eval(parse(text = my_function_name))) 
    script_text[1] <- paste0(my_function_name, " <- ", script_text[1]) 
    eval(parse(text = script_text), envir = to_envir) 
} 

some_function3 <- function(){ 
    test_var <- "local" 
    add_function_to_envir("get_test_var", environment()) 
    return(get_test_var()) 
} 

some_function3() # Returns "local" and we can use 'get_test_var' from anywhere. 

Ở đây add_function_to_envir(my_function_name, to_envir) nắm bắt tập lệnh của hàm, phân tích và đánh giá lại nó trong môi trường mới.

Lưu ý: tên của hàm cho my_function_name cần phải được báo giá.

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