2017-08-11 59 views
0

我嘗試運行一系列三個嵌套函數。我想將最內層函數的默認參數指定爲從最外層函數傳遞的列表對象中的元素。由於這有點難以解釋,我創建了一個可重複使用的示例,它使用與我的原始代碼相同的參數和對象。請原諒代碼的數量,但我想盡可能使這個例子儘可能接近原始。爲什麼嵌套的R函數無法將現有對象識別爲參數?

函數CreateBirthDates正在引發該問題。這四個參數都是列表對象sim內的所有元素(例如,sim$agents$input)。我給第一個函數wrapper調用sim,然後再次調用第二個函數UpdateAgentStates。在UpdateAgentStates內,我想使用sim內的其他對象(例如,sim$agents$birth_day)修改sim$agents$input。由於這些其他論點總是相同的,我想「硬連線」它們。但是如果我運行wrapper函數,CreateBirthDates不能識別sim,因此無法指定默認參數。

我創建了替代版本CreateBirthDatesCreateBirthDates_with_sim。這包括sim作爲參數。用包裝函數運行這個函數是可行的!

這看起來像一個「這是一種方式的R - 作品」的問題,但我不完全明白爲什麼。我想提高我的基本編程技能,所以任何建議或意見將不勝感激!

非常感謝你, 爪哇下面

見代碼:

# Create some example data and load the package lubridate ----- 
    library(lubridate) 

    t1 <- list("agents"=list("input"=data.frame(id=seq(1:5),class=rep("Male",5),age=rep(6,5))), 
     "pars"=list("global"=list("input_age_period"="years", 
            "birth_day"="01Sep", 
            "sim_start"=as.POSIXct("2000-10-01")))) 

    # Specify the original functions ------- 

    wrapper <- function(sim,use_sim=FALSE){ 

     if(use_sim==FALSE){ 
UpdateAgentStates(agent_states = NULL,sim=sim,init=TRUE) 
     } else { 
UpdateAgentStates_with_sim(agent_states = NULL,sim=sim,init=TRUE) 
     } 
    } 

    UpdateAgentStates <- function(agent_states = NULL, 
    sim = sim, 
    init = FALSE 
    ) { 
     if (init == TRUE) { 
input <- sim$agents$input 
input <- CreateBirthDate(input) 
sim$input <- input 
return(sim) 
     } 
    } 

    UpdateAgentStates_with_sim <- function(agent_states = NULL, 
          sim = sim, 
          init = FALSE 
    ) { 
     if (init == TRUE) { 
input <- sim$agents$input 
input <- CreateBirthDate_with_sim(input, sim=sim) 
sim$input <- input 
return(sim) 
     } 
    } 

    CreateBirthDate <- 
    function(input = sim$agents$input, 
    input_age_period = sim$pars$global$input_age_period, 
    birth_day = sim$pars$global$birth_day, 
    starting_day = sim$pars$global$sim_start 
    ){ 

     # Only proceed if there is no birth_date column 
     if(is.null(input$birth_date)){ 
# Loop through each row in the input 
for(a in 1:nrow(input)){ 
    # Is the age_period a year? 
    if(input_age_period == "year" || input_age_period == "years") { 
    # Determine the first sim_start date after the birth_day 
    one_year <- as.period(1, "year") 
    s0 <- as.Date(starting_day - (one_year*input$age[a])) 
    # Set the format of the birth_day 
    birth_day_format <- guess_formats(birth_day,"dm") 
    birth_day_format <- paste(birth_day_format,"%Y",sep="") 
    # Determine the first birth_day after s0 
    s1 <- as.Date(paste(birth_day,year(s0),sep=""), format=birth_day_format) 
    if(length(s1)>1){ 
     s1 <- s1[-(which(is.na(s1)))] 
    } 
    if(s0 >= s1) { 
     input$birth_date[a] <- as.character(s1) 
    } else { 
     input$birth_date[a] <- as.character(s1-one_year) 
    } 
    } else { 
    # If age period is not a year 
    age_period_unit <- as.period(1, input_age_period) 
    input$birth_date[a] <- as.character(starting_day - 
              (age_period_unit*input$age[a])) 
    } 
} 
     } 
     # Convert birth_date to a POSIXct object 
     # input$birth_date <- as.POSIXct(input$birth_date, tz = 
     # tz(sim$pars$global$sim_start)) 
     return(input) 
    } 

    # Specify the modified functions ------- 

    CreateBirthDate_with_sim <- 
     function(input = sim$agents$input, 
     input_age_period = sim$pars$global$input_age_period, 
     birth_day = sim$pars$global$birth_day, 
     starting_day = sim$pars$global$sim_start, sim=sim 
     ){ 

# Only proceed if there is no birth_date column 
if(is.null(input$birth_date)){ 
    # Loop through each row in the input 
    for(a in 1:nrow(input)){ 
    # Is the age_period a year? 
    if(input_age_period == "year" || input_age_period == "years") { 
     # Determine the first sim_start date after the birth_day 
     one_year <- as.period(1, "year") 
     s0 <- as.Date(starting_day - (one_year*input$age[a])) 
     # Set the format of the birth_day 
     birth_day_format <- guess_formats(birth_day,"dm") 
     birth_day_format <- paste(birth_day_format,"%Y",sep="") 
     # Determine the first birth_day after s0 
     s1 <- as.Date(paste(birth_day,year(s0),sep=""), format=birth_day_format) 
     if(length(s1)>1){ 
     s1 <- s1[-(which(is.na(s1)))] 
     } 
     if(s0 >= s1) { 
     input$birth_date[a] <- as.character(s1) 
     } else { 
     input$birth_date[a] <- as.character(s1-one_year) 
     } 
    } else { 
     # If age period is not a year 
     age_period_unit <- as.period(1, input_age_period) 
     input$birth_date[a] <- as.character(starting_day - 
              (age_period_unit*input$age[a])) 
    } 
    } 
     } 
     # Convert birth_date to a POSIXct object 
     # input$birth_date <- as.POSIXct(input$birth_date, tz = 
     # tz(sim$pars$global$sim_start)) 
     return(input) 
     } 

    # Try running the wrapper function ------------- 

    # Original version, doesn't work 
    wrapper(t1, use_sim = FALSE) 

    # But if I add an argument for sim to CreateBirthDate 
    wrapper(t1, use_sim = TRUE) 
+1

這似乎是太多的代碼來排序通過一個最小的,可重複的例子。 'CreateBirthDate'函數默認值都使用'sim $'',但'sim'不是該函數的參數。我不清楚自己想要的行爲是什麼。這裏的所有內容都按照我預期的方式運行。R函數中的參數無法在其自己的詞彙範圍外查看變量。如果你想讓一個函數使用一個值,把它作爲參數傳入。 – MrFlick

回答

0

還有使用另一個全局函數中的全局函數,並定義另一個函數中的一個函數之間的差異。在第一個例子爲inner_func範圍是全球環境和SIM卡未在全球環境中存在的(即不唯一的變量是T1):

t1 <- 1 

inner_func <- function() { 
    final <- sim*2 
    return(final) 
} 

outer_func <- function(sim = w){ inner_func() } 

outer_func(t1) 

Error in inner_func() : object 'sim' not found 

但是,如果我們定義裏面outer_func的inner_func,那麼你將得到你期待的行爲:

t1 <- 1 

outer_func <- function(sim = w){ 
    inner_func <- function() { 
    final <- sim*2 
    return(final) 
    } 
    inner_func() 
} 

outer_func(t1) 
[1] 2 
+0

感謝您的迴應!我想我很困惑爲什麼,如果'CreateBirthDate'嵌套在'UpdateAgentStates'中並且'Sim'作爲'UpdateAgentStates'中的一個參數包含,爲什麼'sim'不存在?我認爲你提供給函數作爲參數的任何東西都成爲工作環境的一部分,並且可以用於任何後來嵌套的函數,如果這很有意義......?再次感謝你的幫助! – JBauder

+0

查看更新的答案。 – AidanGawronski

相關問題