Cách bao gồm (nguồn) tập lệnh R trong các tập lệnh khác


108

Tôi đã tạo một tập lệnh R tiện ích, Prac.R, mà tôi muốn sử dụng từ các tập lệnh khác trong dự án của mình. Cách thích hợp để đảm bảo rằng chức năng mà tập lệnh này xác định có sẵn để hoạt động trong các tập lệnh khác của tôi?

Tôi đang tìm kiếm một cái gì đó tương tự như requirehàm, chỉ tải một gói nếu nó chưa được tải. Tôi không muốn gọi source("util.R")vì điều đó sẽ tải tập lệnh mỗi khi nó được gọi.

Tôi biết rằng tôi sẽ nhận được một số câu trả lời yêu cầu tôi tạo một gói, như trong Mã nguồn tổ chức R :) Nhưng tôi không tạo thứ gì đó sẽ được sử dụng ở nơi khác, nó chỉ là một dự án độc lập.


37
Tôi luôn tạo các gói cho các dự án độc lập. Nó không có nhiều công việc và lợi ích là rất lớn. Tiếp tục đi, bạn biết bạn muốn làm điều đó ...
Andrie

Câu trả lời:


93

Đây là một cách khả thi. Sử dụng existschức năng để kiểm tra một cái gì đó duy nhất trong util.Rmã của bạn .

Ví dụ:

if(!exists("foo", mode="function")) source("util.R")

(Đã chỉnh sửa để bao gồm mode="function", như Gavin Simpson đã chỉ ra)


4
Thoải mái sử dụng exists()- nhu cầu mode = "function"bổ sung để làm cho nó fool-proof
Gavin Simpson

1
exists()dường như gặp lỗi ngoại trừ việc trả về một lỗi trong R 3.0.2.
Michael Schubert

Cách sử dụng chính xác là `tồn tại (" foo ") và câu trả lời đã được chỉnh sửa.
Andrie

18

Không có thứ gì như vậy được tích hợp sẵn, vì R không theo dõi các cuộc gọi đến sourcevà không thể tìm ra những gì đã được tải từ đâu (trường hợp này không xảy ra khi sử dụng các gói). Tuy nhiên, bạn có thể sử dụng ý tưởng tương tự như trong .htệp C , tức là gói toàn bộ trong:

if(!exists('util_R')){
 util_R<-T

 #Code

}

và sau đó gọi source("util.R")trong ifmã, phải không?
rafalotufo

1
@rafalotufo Bạn sẽ lấy nguồn ("using.R") như bình thường. Đoạn mã trong bài đăng của mbq sẽ chuyển sang dạng use.R. Bạn chỉ cần đặt toàn bộ nội dung của những gì đang sử dụng ngay bây giờ vào một câu lệnh if () khổng lồ, nếu điều đó có ý nghĩa.
Keith Twombley

10

Nói util.Rtạo ra một hàm foo(). Bạn có thể kiểm tra xem chức năng này có khả dụng trong môi trường toàn cầu hay không và mã nguồn nếu nó không có:

if(identical(length(ls(pattern = "^foo$")), 0))
    source("util.R")

Điều đó sẽ tìm thấy bất cứ điều gì có tên foo. Nếu bạn muốn tìm một hàm, thì (như được đề cập bởi @Andrie) exists()là hữu ích nhưng cần được cho biết chính xác loại đối tượng cần tìm, ví dụ:

if(exists("foo", mode = "function"))
    source("util.R")

Đây là exists()hoạt động:

> exists("foo", mode = "function")
[1] FALSE
> foo <- function(x) x
> exists("foo", mode = "function")
[1] TRUE
> rm(foo)
> foo <- 1:10
> exists("foo", mode = "function")
[1] FALSE

Trong trường hợp này, bạn có thể muốn sử dụng grepl(..., value=TRUE)vì cụm từ tìm kiếm của bạn có thể không phải là regex. +1, nhân tiện.
Andrie

?? grepl()không có tranh luận value, nhưng tôi có lẽ nên sửa chữa các regexp trong ls()...
Gavin Simpson

Xin lỗi, là lỗi của tôi. Ý tôi làfixed=TRUE
Andrie

@Andrie - À, được rồi. Dù sao thì nó cũng không hoạt động. Bị lôi đi khi đang cân nhắc điều này. exists()tốt hơn nhưng bây giờ tôi thấy bạn đã đăng một Câu trả lời như vậy trong thời gian chờ đợi.
Gavin Simpson

5

Bạn có thể viết một hàm lấy tên tệp và tên môi trường, kiểm tra xem tệp đã được tải vào môi trường chưa và sử dụng sys.sourceđể tạo nguồn tệp nếu chưa.

Đây là một chức năng nhanh và chưa được thử nghiệm (hoan nghênh các cải tiến!):

include <- function(file, env) {
  # ensure file and env are provided
  if(missing(file) || missing(env))
    stop("'file' and 'env' must be provided")
  # ensure env is character
  if(!is.character(file) || !is.character(env))
    stop("'file' and 'env' must be a character")

  # see if env is attached to the search path
  if(env %in% search()) {
    ENV <- get(env)
    files <- get(".files",ENV)
    # if the file hasn't been loaded
    if(!(file %in% files)) {
      sys.source(file, ENV)                        # load the file
      assign(".files", c(file, files), envir=ENV)  # set the flag
    }
  } else {
    ENV <- attach(NULL, name=env)      # create/attach new environment
    sys.source(file, ENV)              # load the file
    assign(".files", file, envir=ENV)  # set the flag
  }
}

5

Đây là một chức năng tôi đã viết. Nó kết thúc base::sourcechức năng lưu trữ danh sách các tệp có nguồn gốc trong danh sách môi trường chung có tên sourced. Nó sẽ chỉ nguồn lại một tệp nếu bạn cung cấp một .force=TRUEđối số cho lệnh gọi đến nguồn. Chữ ký đối số của nó giống với chữ ký thực, source()vì vậy bạn không cần phải viết lại các tập lệnh của mình để sử dụng nó.

warning("overriding source with my own function FYI")
source <- function(path, .force=FALSE, ...) {
  library(tools)
  path <- tryCatch(normalizePath(path), error=function(e) path)
  m<-md5sum(path)

  go<-TRUE
  if (!is.vector(.GlobalEnv$sourced)) {
    .GlobalEnv$sourced <- list()
  }
  if(! is.null(.GlobalEnv$sourced[[path]])) {
    if(m == .GlobalEnv$sourced[[path]]) {
      message(sprintf("Not re-sourcing %s. Override with:\n  source('%s', .force=TRUE)", path, path))
      go<-FALSE
    }
    else {
      message(sprintf('re-sourcing %s as it has changed from: %s to: %s', path, .GlobalEnv$sourced[[path]], m))
      go<-TRUE
    }
  } 
  if(.force) {
    go<-TRUE
    message("  ...forcing.")
  }
  if(go) {
    message(sprintf("sourcing %s", path))
    .GlobalEnv$sourced[path] <- m
    base::source(path, ...)
  }
}

Nó khá là trò chuyện (rất nhiều cuộc gọi đến message()) vì vậy bạn có thể rút ra những dòng đó nếu bạn quan tâm. Mọi lời khuyên từ những người dùng R kỳ cựu đều được đánh giá cao; Tôi khá mới với R.


0

Tôi đã giải quyết vấn đề của mình bằng cách sử dụng toàn bộ địa chỉ nơi mã của tôi: Trước:

if(!exists("foo", mode="function")) source("utils.r")

Sau:

if(!exists("foo", mode="function")) source("C:/tests/utils.r")
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.