2011-08-21 39 views
8

Tôi đã tự hỏi liệu có cách nào buộc một hàm chỉ chấp nhận một số kiểu dữ liệu nhất định mà không phải kiểm tra nó trong hàm; hoặc, điều này là không thể bởi vì việc kiểm tra kiểu R được thực hiện khi chạy (trái ngược với các ngôn ngữ lập trình đó, chẳng hạn như Java, nơi kiểm tra kiểu được thực hiện trong quá trình biên dịch)?Bắt buộc các kiểu dữ liệu cụ thể làm đối số cho hàm

Ví dụ, trong Java, bạn phải chỉ định một kiểu dữ liệu:

class t2 { 
    public int addone (int n) { 
     return n+1; 
    } 
} 

Trong R, một chức năng tương tự có thể

addone <- function(n) 
{ 
    return(n+1) 
} 

nhưng nếu một vector được cung cấp, một vector sẽ (hiển nhiên) được trả lại. Nếu bạn chỉ muốn có một số nguyên duy nhất để được chấp nhận, sau đó là cách duy nhất để làm gì để có một điều kiện bên trong hàm, dọc theo dòng của

addone <- function(n) 
{ 
    if(is.vector(n) && length(n)==1) 
    { 
    return(n+1) 
    } else 
    { 
    return ("You must enter a single integer") 
    } 
} 

Cảm ơn,
Chris

+3

Là điểm kiểu mã, trong trường hợp bạn không có số vô hướng, có thể bạn muốn ném một lỗi (với 'stop' hoặc' stopifnot') hoặc đưa ra cảnh báo (với 'warning') thay vì hơn là chỉ trả về một chuỗi. –

Trả lời

16

này là hoàn toàn có thể sử dụng các lớp S3. Ví dụ của bạn là phần nào bị lôi kéo trong bối cảnh hoặc R, vì tôi không thể nghĩ ra một lý do thực tế tại sao người ta muốn tạo ra một lớp của một giá trị duy nhất. Tuy nhiên, điều này là có thể. Như một phần thưởng thêm, tôi chứng minh cách addone chức năng có thể được sử dụng để thêm giá trị của một đến số vectơ số (tầm thường) và vectơ ký tự (do đó A chuyển thành B, v.v.):

Bắt đầu bằng cách tạo một S3 chung phương pháp cho addone, utlising cơ chế mau lẹ S3 UseMethod:

addone <- function(x){ 
    UseMethod("addone", x) 
} 

Tiếp theo, tạo lớp giả tạo single, được xác định là yếu tố đầu tiên của bất cứ điều gì được truyền cho nó:

as.single <- function(x){ 
    ret <- unlist(x)[1] 
    class(ret) <- "single" 
    ret 
} 

Bây giờ, tạo m ethods để xử lý các lớp khác nhau. Phương pháp mặc định sẽ được gọi là trừ khi một lớp học cụ thể được xác định:

addone.default <- function(x) x + 1 
addone.character <- function(x)rawToChar(as.raw(as.numeric(charToRaw(x))+1)) 
addone.single <- function(x)x + 1 

Cuối cùng, thử nghiệm nó với một số dữ liệu mẫu:

addone(1:5) 
[1] 2 3 4 5 6 

addone(as.single(1:5)) 
[1] 2 
attr(,"class") 
[1] "single" 

addone("abc") 
[1] "bcd" 

Một số thông tin thêm:

  1. devtools wiki của Hadley là một nguồn thông tin có giá trị trên tất cả mọi thứ, bao gồm the S3 object system.

  2. Phương pháp S3 không cung cấp khả năng gõ nghiêm ngặt. Nó có thể dễ dàng bị lạm dụng. Để có hướng đối tượng chặt chẽ hơn, hãy xem S4 classes, reference based classes hoặc proto package để lập trình dựa trên đối tượng Prototype.

+0

Lớp học S4 là một ý tưởng hay. 'setMethod' nói riêng. – Owen

+1

Có, các lớp S3 chỉ xử lý đối số đầu tiên. ... và 'single' có lẽ không phải là tên tốt nhất - đã có một lớp' single' (float chính xác đơn) với 'as.single' etc - nhưng nó không được dùng nữa. – Tommy

4

Bạn có thể viết một wrapper như sau:

check.types = function(classes, func) { 
    n = as.name 

    params = formals(func) 
    param.names = lapply(names(params), n) 

    handler = function() { } 
    formals(handler) = params 

    checks = lapply(seq_along(param.names), function(I) { 
     as.call(list(n('assert.class'), param.names[[I]], classes[[I]])) 
    }) 
    body(handler) = as.call(c(
     list(n('{')), 
     checks, 
     list(as.call(list(n('<-'), n('.func'), func))), 
     list(as.call(c(list(n('.func')), lapply(param.names, as.name)))) 
    )) 

    handler 
} 

assert.class = function(x, cls) { 
    stopifnot(cls %in% class(x)) 
} 

Và sử dụng nó như

f = check.types(c('numeric', 'numeric'), function(x, y) { 
    x + y 
}) 

> f(1, 2) 
[1] 3 

> f("1", "2") 
Error: cls %in% class(x) is not TRUE 

Made hơi bất tiện bởi R không có trang trí. Đây là loại hacky và nó bị một số vấn đề nghiêm trọng:

  1. Bạn mất thẩm lười biếng, bởi vì bạn phải đánh giá một cuộc tranh cãi để xác định loại của nó.

  2. Bạn vẫn không thể kiểm tra các loại cho đến khi thời gian gọi; kiểm tra loại tĩnh thực cho phép bạn kiểm tra các loại ngay cả khi cuộc gọi không bao giờ thực sự xảy ra.

Kể từ R sử dụng đánh giá lười biếng, (2) có thể làm cho loại kiểm tra không phải là rất hữu ích, vì cuộc gọi có thể không thực sự xảy ra cho đến khi rất muộn, hoặc không bao giờ.

Câu trả lời cho (2) sẽ là thêm thông tin loại tĩnh. Bạn có thể có thể làm điều này bằng cách chuyển đổi các biểu thức, nhưng tôi không nghĩ bạn muốn đến đó.

3

Tôi đã tìm thấy stopifnot() rất hữu ích cho các tình huống này.

x <- function(n) { 
stopifnot(is.vector(n) && length(n)==1) 
print(n) 
} 

Lý do hữu ích là vì nó cung cấp thông báo lỗi khá rõ ràng cho người dùng nếu điều kiện là sai.

+2

Lưu ý rằng điều này có thể được viết 'stopifnot (is.vector (n), length (n) == 1)' và sẽ có lợi thế là nếu nó thất bại thì trong hai điều kiện không thành công sẽ được hiển thị như một phần của thông báo lỗi. –

+0

Touche, tôi luôn quên rằng nó dừng lại nếu không ... điều kiện là đúng. –

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