2013-05-16 26 views
7

Một chương trình bên ngoài cần tệp đầu vào với một số tham số điều khiển và tôi muốn tạo tự động bằng R. Thông thường, tôi chỉ cần sử dụng paste("parameter1: ", param1, ...) để tạo chuỗi văn bản dài và xuất một tập tin, nhưng kịch bản nhanh chóng trở thành không đọc được. Vấn đề này có lẽ cũng phù hợp với râu ria,templating lỗi an toàn với brew/whisker

library(whisker) 

template= 'Hello {{name}} 
You have just won ${{value}}! 
' 

data <- list(name = "Chris", value= 124) 

whisker.render(template, data) 

Vấn đề của tôi ở đây, là không có an toàn kiểm tra rằng data chứa tất cả các biến cần thiết, ví dụ

whisker.render(template, data[-1]) 

sẽ tự động bỏ qua thực tế là tôi quên chỉ định tên. Chương trình kết thúc của tôi sẽ bị lỗi, tuy nhiên, nếu tôi không tạo ra một tệp cấu hình hoàn chỉnh.

Hệ thống tạo mẫu khác được cung cấp bởi brew; nó có lợi thế là thực sự đánh giá sự vật, và có khả năng này cũng có thể giúp phát hiện các biến thiếu,

library(brew) 

template2 = 'Hello <%= name %> 
You have just won $<%= value %>! 
' 

data <- list(name = "Chris", value= 124) 

own_brew <- function(template, values){ 
    attach(values, pos=2) 
    out = capture.output(brew(text = template)) 
    detach(values, pos=2) 
    cat(out, sep='\n') 
    invisible(out) 
} 

own_brew(template2, data) 
own_brew(template2, data[-1]) # error 

Tuy nhiên, tôi đang mắc kẹt với hai vấn đề:

  • attach() ... detach() không phải là lý tưởng, (cung cấp cho cảnh báo tất cả bây giờ và sau đó), hoặc ít nhất tôi không biết cách sử dụng nó đúng cách. Tôi đã cố gắng xác định một môi trường cho brew(), nhưng nó quá hạn chế và không biết về chức năng base nữa ...

  • mặc dù xảy ra lỗi, một chuỗi vẫn được hàm trả về. Tôi đã cố gắng kết thúc cuộc gọi theo số try() nhưng tôi không có kinh nghiệm xử lý lỗi. Làm thế nào để tôi nói với nó để bỏ chức năng sản xuất không có đầu ra?

Edit: Tôi đã cập nhật giải pháp brew sử dụng một môi trường mới thay vì attach(), và ngừng thực hiện trong trường hợp lỗi. (?capture.output cho thấy rằng nó không phải là chức năng quyền sử dụng ở đây, vì "Một cố gắng được thực hiện để viết ra càng nhiều càng tốt để nộp nếu có một lỗi trong việc đánh giá các thuật ngữ" ...)

own_brew <- function(template, values, file=""){ 
    env <- as.environment(values) 
    parent.env(env) <- .GlobalEnv 
    a <- textConnection("cout", "w") 
    out <- try(brew(text = template, envir=env, output=a)) 

    if(inherits(out, "try-error")){ 
    close(a) 
    stop() 
    } 
    cat(cout, file=file, sep="\n") 
    close(a) 
    invisible(cout) 
} 

Phải có một cách dễ dàng hơn với tryCatch, nhưng tôi không thể hiểu một điều duy nhất trong trang trợ giúp của nó.

Tôi hoan nghênh các đề xuất khác về vấn đề chung chung hơn.

Trả lời

4

Sử dụng biểu thức thông thường để lấy lại tên biến từ mẫu, bạn có thể xác nhận trước khi hoàn trả, ví dụ:

render <- function(template, data) { 
    vars <- unlist(regmatches(template, gregexpr('(?<=\\{\\{)[[:alnum:]_.]+(?=\\}\\})', template, perl=TRUE))) 
    stopifnot(all(vars %in% names(data))) 
    whisker.render(template, data) 
} 

render(template, data) 
+0

Cảm ơn, đó là một lựa chọn. Tôi thích thực tế là brew có khả năng đánh giá mã. – baptiste

+0

Tôi đã sử dụng ria mép trong ví dụ của mình, nhưng tất nhiên điều này có thể dễ dàng thay đổi để làm việc với các khuôn mẫu brew thay thế. –

+0

dễ dàng ... nếu bạn biết cụm từ thông dụng :) – baptiste

3

mới glue package cung cấp thay thế khác,

library(glue) 
template <- 'Hello {name} You have just won ${value}!' 
data <- list(name = "Chris", value= 124) 

glue_data(template, .x=data) 
# Hello Chris You have just won $124! 

glue_data(template, .x=data[-1]) 
# Error in eval(expr, envir, enclos) : object 'name' not found 
1

Kể từ phiên bản 1.1.0 (trên CRAN 19 tháng 8 năm 2016), stringr package bao gồm chức năng str_interp() (rất tiếc là không được đề cập trong tệp NEWS của bản phát hành).

template <- "Hello ${name} You have just won $${value}!" 
data <- list(name = "Chris", value= 124) 

stringr::str_interp(template, data) 
[1] "Hello Chris You have just won $124!" 
stringr::str_interp(template, data[-1L]) 
Error in FUN(X[[i]], ...) : object 'name' not found 
1

Trong khi chuẩn bị stringr answer tôi nhận thấy rằng những câu hỏi OP của liên quan đến việc sử dụng brew() chưa được giải quyết cho đến nay. Đặc biệt, OP đã yêu cầu làm thế nào để cung cấp data của mình cho môi trường và làm thế nào để ngăn chặn một chuỗi ký tự được trả về trong trường hợp có lỗi.

OP đã tạo một hàm own_brew() kết thúc cuộc gọi đến brew(). Mặc dù, có sẵn gói thay thế ngay bây giờ, tôi cảm thấy câu hỏi ban đầu xứng đáng là một câu trả lời.

Đây là nỗ lực của tôi để cải thiện baptiste's version:

own_brew <- function(template, values, file=""){ 
    a <- textConnection("cout", "w") 
    out <- brew::brew(text = template, envir=list2env(values), output=a) 
    close(a) 
    if (inherits(out, "try-error")) stop() 
    cat(cout, file=file, sep="\n") 
    invisible(cout) 
} 

Sự khác biệt chính là rằng list2env() được sử dụng để vượt qua danh sách các values để brew() và cuộc gọi đến try() được tránh bằng cách kiểm tra giá trị trả về out cho một lỗi.

template <- "Hello <%= name %> You have just won $<%= value %>!" 
data <- list(name = "Chris", value= 124) 

own_brew(template, data) 
Hello Chris You have just won $124! 
own_brew(template, data[-1L]) 
Error in cat(name) : object 'name' not found 
Error in own_brew(template, data[-1L]) :