【问题标题】:How to organize big R functions?如何组织大 R 函数?
【发布时间】:2011-08-31 08:43:31
【问题描述】:

我正在编写一个 R 函数,它变得非常大。它承认多项选择,我是这样组织的:

myfun <- function(y, type=c("aa", "bb", "cc", "dd" ... "zz")){

   if (type == "aa") {
      do something
      - a lot of code here -
      ....
   }

   if (type == "bb") {
      do something
      - a lot of code here -
      ....
   }

   ....
}

我有两个问题:

  1. 有没有更好的方法,以便对参数类型的每个选择不使用“if”语句?
  2. 为每个“类型”选择编写一个子函数是否更实用?

如果我写子函数,它会是这样的:

myfun <- function(y, type=c("aa", "bb", "cc", "dd" ... "zz")){

   if (type == "aa") result <- sub_fun_aa(y)
   if (type == "bb") result <- sub_fun_bb(y)
   if (type == "cc") result <- sub_fun_cc(y)
   if (type == "dd") result <- sub_fun_dd(y)
   ....
}

子函数当然是在别处定义的(在 myfun 的顶部,或以其他方式)。

我希望我的问题很清楚。提前致谢。

- 附加信息 -

我正在编写一个函数,将一些不同的过滤器应用于图像(不同的过滤器 = 不同的“类型”参数)。有些过滤器共享一些代码(例如,“aa”和“bb”是两个高斯过滤器,仅一行代码不同),而其他过滤器则完全不同。

所以我不得不使用很多 if 语句,即

 if(type == "aa" | type == "bb"){
  - do something common to aa and bb -

    if(type == "aa"){
      - do something aa-related -
    }
    if(type == "bb"){
      - do something bb-related -
    }
 }

 if(type == "cc" | type == "dd"){
  - do something common to cc and dd -

    if(type == "cc"){
      - do something cc-related -
    }
    if(type == "dd"){
      - do something dd-related -
    }
 }

if(type == "zz"){
     - do something zz-related -
}

等等。 此外,代码中还有一些 if 语句“做某事”。 我正在寻找组织代码的最佳方式。

【问题讨论】:

标签: function r


【解决方案1】:

选项 1

一种选择是使用switch 而不是多个if 语句:

myfun <- function(y, type=c("aa", "bb", "cc", "dd" ... "zz")){
  switch(type, 
    "aa" = sub_fun_aa(y),
    "bb" = sub_fun_bb(y),
    "bb" = sub_fun_cc(y),
    "dd" = sub_fun_dd(y)
  )
}  

选项 2

在您编辑的问题中,您提供了更具体的信息。这是您可能要考虑的通用设计模式。此模式中的关键元素是看不到一个if。我将其替换为match.function,其中的关键思想是您的函数中的type 本身就是一个函数(是的,因为R 支持函数式编程,所以这是允许的)。:

sharpening <- function(x){
  paste(x, "General sharpening", sep=" - ")
}

unsharpMask <- function(x){
  y <- sharpening(x)
  #... Some specific stuff here...
  paste(y, "Unsharp mask", sep=" - ")
}

hiPass <- function(x) {
  y <- sharpening(x)
  #... Some specific stuff here...
  paste(y, "Hipass filter", sep=" - ")
}

generalMethod <- function(x, type=c(hiPass, unsharpMask, ...)){
  match.fun(type)(x)
}

然后这样称呼它:

> generalMethod("stuff", "unsharpMask")
[1] "stuff - General sharpening - Unsharp mask"
> hiPass("mystuff")
[1] "mystuff - General sharpening - Hipass filter"

【讨论】:

  • 这看起来更优雅,所以谢谢你的提示。我在 cmets 中向 Chris Taylor 添加了更多细节。
  • Andrie:S3 推荐很好,但新用户(我也算在内 ;-))可能不太了解如何使用它来代替一堆 ifswitch 操作。您能否详细说明或澄清如何使用 S3 代码?也许这可能是一个单独的答案,因为它有点长。
  • @Iterator @Tommasso 鉴于您的问题,我已经编辑了我的问题,其中包含一个大的新块,解释match.fun的使用
【解决方案2】:

几乎没有理由不将代码重构为更小的函数。在这种情况下,除了重组之外,还有一个额外的优势:受过教育的函数用户如果知道自己在哪里,可以立即调用子函数。

如果这些函数有很多参数,解决方案(便于维护)可能是将它们分组到“myFunctionParameters”类列表中,但这取决于您的情况。

如果代码在不同的 sub_fun_xxs 之间共享,只需将其插入您在每个 sub_fun_xxs 中使用的另一个函数,或者(如果可行)预先计算内容并将其直接传递到每个 sub_fun_xx。

【讨论】:

  • 感谢您的回答,这可能是最适合我的目的。我唯一不明白的是,为什么为每个子函数重写共享代码是一种不推荐使用的方法(假设如果一个子函数运行,另一个不会运行)。可能是为了节省空间的建议。
  • 想象一下共享代码中存在一个小错误(或者您在最初实施几个月后才想到的改进):如果您重复代码,您将不得不在多个地方对其进行编辑。
【解决方案3】:

这是一个关于程序设计的更普遍的问题。没有明确的答案,但几乎可以肯定有比您目前正在做的更好的路线。

编写处理不同类型的函数是一个很好的方法。它的有效性取决于几件事——例如,有多少种不同的类型?它们是否完全相关,例如其中一些是否可以由相同的函数处理,根据输入的不同行为略有不同?

您应该尝试以模块化的方式考虑您的代码。你有一项大任务要完成。你能把它分解成一系列较小的任务,并编写执行较小任务的函数吗?您能否以一种不会使函数(非常)难以编写,但赋予它们更广泛的适用性的方式概括这些任务?

如果您提供有关您的计划应该实现的目标的更多详细信息,我们将能够为您提供更多帮助。

【讨论】:

  • 我正在编写一个函数,将不同的过滤器应用于图像(不同的过滤器 = 不同的“类型”参数)。一些过滤器共享一些代码(即“aa”和“bb”是两个高斯过滤器,仅在一行代码上有所不同),而另一些则完全不同。所以我不得不使用大量的if语句,即if(type == "aa" | type == "bb"){ 做aa和bb共有的事情 if(type == "aa"){ 做aa相关的事情 } if(type == "bb"){ 做一些与bb有关的事情 } } if(type == "cc" | type == "dd"){ ...
  • @Tommaso 这个附加信息应该真正进入您的问题,而不是作为对特定答案的评论。我建议您改为提出问题。
【解决方案4】:

这更像是一个通用编程问题,而不是 R 问题。因此,您可以遵循代码质量的基本准则。有些工具可以通过阅读代码生成代码质量报告,并为您提供如何改进的指导。一个这样的例子是 .NET 代码的宪兵。下面是一个典型的准则,它会出现在方法过长的报告中:

AvoidLongMethodsRule

【讨论】:

    猜你喜欢
    • 2010-11-18
    • 2022-06-10
    • 1970-01-01
    • 1970-01-01
    • 2011-01-16
    • 2011-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多