2011-08-21 125 views
8

我只是想知道是否有办法强制函数只接受某些数据类型,而不必在函数内检查它;或者,这是不可能的,因为R的类型检查是在运行时完成的(与编译期间完成类型检查的Java等编程语言相反)?强制特定数据类型作为函数的参数

例如,在Java中,必须指定一个数据类型:

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

在R,类似的功能可能是

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

但如果一个矢量被提供,载体将(显然)被退回。如果你只想要一个整数被接受,然后是做有函数内的一个条件的唯一途径,沿

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

致谢线,
克里斯

+3

作为代码样式点,在没有标量数的情况下,您可能想抛出一个错误(使用'stop'或'stopifnot')或给出警告(使用'warning')不仅仅是返回一个字符串。 –

回答

16

这是完全有可能的使用S3类。您的示例在上下文或R中有些人为设计,因为我无法想出为什么要创建单个值的类的实际原因。尽管如此,这是可能的。作为额外的好处,我演示了如何使用函数addone将数值1添加到数字向量(平凡)和字符向量(所以A变为B等):

首先创建一个通用S3方法addone,utlising的S3发货机构UseMethod

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

接下来,创建做作类single,其定义为无论传递给它的第一个元素:

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

现在创建米方法来处理各种类。默认的方法将被称为除非特定类被定义:

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

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

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

一些额外的信息:

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

    最后,一些示例数据测试它哈德利的devtools wiki是一切有关信息的宝贵来源,包括the S3 object system

  2. S3方法不提供严格的输入。它很容易被滥用。要获得更加严格的面向对象,请参阅S4 classesreference based classesproto package,了解Prototype基于对象的编程。

+0

S4类是一个好主意。特别是'setMethod'。 – Owen

+1

是的,S3类只处理第一个参数。 ...和'single'可能不是最好的名字 - 已经有'single'类(单精度浮点数)和'as.single'等 - 但它已被弃用。 – Tommy

4

你可以写一个包装类似如下:

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)) 
} 

而且使用它像

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 

由R没有装饰制造略感不便。这是一种哈克 ,它从一些严重的问题受到影响:

  1. 你输了懒惰的评价,因为您必须评估参数确定 其类型。

  2. 您仍然无法检查类型,直到通话时间;真正的静态类型检查 可以让您检查甚至从未实际发生过的呼叫的类型。

由于R使用懒评价;(2)可能使因为呼叫可能不会实际发生,直到很晚,或从未型不检查是非常有用的, 。

(2)的答案是添加静态类型信息。你可能可以通过转换表达式来做到这一点,但我不认为你想去那里。

3

我发现stopifnot()对于这些情况也非常有用。

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

它是如此有用的原因是因为它提供了一个非常明确的错误消息给用户,如果条件为false。

+2

请注意,这可以写成'stopifnot(is.vector(n),length(n)== 1)',这样做的好处是,如果失败,那么失败的两个条件中的哪一个将显示为错误消息。 –

+0

谢谢,我总是忘记,如果不是......条件是真的,那就停止。 –