2017-01-15 13:37:21

目录

代码风格

是啥?

遵守风格约定的意义

我只是想用R跑跑分析,又不要做码农,为什么还要遵守开发者代码风格?

  • 良好的代码风格代表了代码组织的最优实践
    • 降低了手残/笔误的几率
    • 便于理解,提高了代码的可读性(结构、注释)
  • 统一的代码风格能避免混乱,便利团队协作

编码风格约定 (Style Rules)

表示和命名 Notation And Naming

文件命名 Filenames

  • 脚本文件名应以 .R (大写) 结尾, 文件名本身要有意义。
    • 正例: predict_ad_revenue.R
    • 反例: foo.R, function.R
  • 不要把所有的功能函数放在一个脚本中。

标识符命名 Identifiers

  • 不要使用连字符( - ),也尽量不使用下划线 ( _ )。
  • 变量名:名词或名词性短语,用(.)分隔小写单词,或从第二个单词开始首字母大写(小驼峰法);
    • 不使用保留字或函数名,如c, list, TRUE, FALSE
    • 正例: avgClicks, avg.clicks
    • 反例: avg_Clicks, AvgClicks
  • 函数名:动词或动词性短语,用(_)分隔小写单词;或小驼峰法(不用大驼峰法);
    • 不输出(export)到命名空间(NAMESPACE)的函数名前加”.”前缀;
    • 正例: calculateAvgClicks, calculate_avg_clicks
    • 反例: calculate.avg.clicks, CalculateAvgClicks
  • S3类(S3 Class)名采用大驼峰法,S4类(S4 Class)名采用大驼峰法并加”S4”前缀
  • 方法名(method)也采用函数名相同规则
  • 常数命名规则同函数, 但需使用一个k 开头;或全大写:如kConstantName,CONSTANTNAME
  • 例外: 当创建一个含类 (class) 属性的对象时, 函数名 (也是constructor) 和类名 (class) 应当匹配 (例如, lm).

语法 Syntax

单行长度 Length of Lines

  • 最大单行长度为 80 个字符。

Rstudio中Tools -> Global Options -> Code -> Display-> General -> Show margin -> Margin column = 80。

缩进 Indentation

  • 使用4个空格来缩进代码. 不要使用制表符(tab)或混合使用二者。

Rstudio中Tools -> Global Options -> Code -> Editing -> General -> Insert spaces for tab -> Tab width=4。

空白 Spacing

  • 在所有二元操作符 (=, +, -, <-, 等等)的两侧加上空格。
    • 例外: 在函数调用中传递参数时 =两边的空格可不加。不可在逗号前加空格, 逗号后总须加空格。
    • 正例:

      tabPrior <- table(df[df$daysFromOpt < 0, "campaignid"])
      total <- sum(x[, 1])
      total <- sum(x[1, ])
    • 反例:

      tabPrior <- table(df[df$daysFromOpt<0, "campaignid"])   # 在 '<' 两侧需要增加空格
      tabPrior <- table(df[df$daysFromOpt < 0,"campaignid"])  # 逗号后需要一个空格
      tabPrior<- table(df[df$daysFromOpt < 0, "campaignid"])  # 在 <- 前需要一个空格
      tabPrior<-table(df[df$daysFromOpt < 0, "campaignid"])   # 在 <- 两侧需要增加空格
      total <- sum(x[,1])   # 逗号后需要一个空格
      total <- sum(x[ ,1])  # 逗号后需要一个空格, 而非逗号之前
      -2<-1                 # 想想看会发生什么?

  • 在前括号前加一个空格, 函数调用时除外。
    • 正例: if (debug)
    • 反例: if(debug)
  • 多加空格 (即,在行内使用多于一个空格) 也是可以的,如果这样做能够改善等号或箭头 (<-) 的对齐效果。

    plot(x    = xCoord,
         y    = dataMat[, makeColName(metric, ptiles[1], "roiOpt")],
         ylim = ylim,
         xlab = "dates",
         ylab = metric,
         main = (paste(metric, " for 3 samples ", sep="")))
  • 不要向圆括号或方括号中的代码两侧加入空格。但逗号后总要加空格。
    • 正例:

      if (debug)
      x[1, ]
    • 反例: if(debug)

      if ( debug )  # debug 的两边不要加空格
      x[1,]         # 需要在逗号后加一个空格 

花括号 Curly Braces

  • 前括号永远不应该独占一行;
  • 后括号应当总是独占一行。
  • 但在处理这类单个语句时, 必须前后一致地要么全部使用花括号,或者全部不用花括号。

    if (is.null(ylim)) {
        ylim <- c(0, 0.06)
    }

    或 (不可混用)

    if (is.null(ylim))
        ylim <- c(0, 0.06)
  • 总在新起的一行开始书写代码块的主体.
    • 反例:

      if (is.null(ylim))              ylim <- c(0, 0.06)
      if (is.null(ylim))              {ylim <- c(0, 0.06)}

赋值 Assignment

  • 尽量使用"<-"进行赋值, 不用"="赋值,以保证对早期R版本的兼容性;
  • 但对于仅用于较新版本环境的代码,可统一用"="赋值。

分号 Semicolons

不要以分号结束一行,也不要利用分号在同一行放多于一个命令。

代码组织 Organization

总体布局和顺序 General Layout And Ordering

如果所有人都以相同顺序安排代码内容, 就可以更加轻松快速地阅读并理解他人的脚本了。

  • 版权声明注释
  • 作者信息注释
  • 文件描述注释, 包括程序的用途, 输入和输出
  • source()library() 语句
  • 函数定义
  • 要执行的语句,如果有的话 (例如,printplot)
  • 所有的类定义都写入一个名为allClass.R的独立文件中
  • 基本过程/函数定义写入一个名为generic.R的独立文件中
  • 单元测试应在另一个名为原始的文件名_unittest.R的独立文件中进行。

编写R加载包的话,请全盘参考William Hadley的指南

注释准则 Commenting Guidelines

  • 注释您的代码。整行注释应以 # 后接一个空格开始。
  • 行内短注释应在代码后接两个空格,#,再接一个空格. 。
# Create histogram of frequency of campaigns by pct budget spent.

hist(df$pctSpent,
     breaks = "scott",  # method for choosing number of buckets
     main   = "Histogram: fraction budget spent by campaignid",
     xlab   = "Fraction of budget spent",
     ylab   = "Frequency (count of campaignids)")

函数的定义和调用 Function Definition And Calls

  • 函数定义应首先列出无默认值的参数,然后再列出有默认值的参数。
  • 函数定义和函数调用中,允许每行写多个参数;折行只允许在赋值语句外进行。
    • 正例:

      predictCTR <- function(query, property, numDays,
                         showPlot = TRUE)
    • 反例:

      predictCTR <- function(query, property, numDays, showPlot =
                         TRUE)
  • 理想情况下,单元测试应该充当函数调用的样例 (对于包中的程序来说)。

函数文档 Function Documentation

为便于使用roxygen2包将R脚本编译成加载包,强烈建议在函数定义行上方插入一个注释区. 这些注释应当符合roxygen2的语法,每一行都以#’ 开头:

  • 此函数的标题,用@title表示(或注释区第一行);
  • 此函数的简要描述和详细描述,用@description(或注释区第三行,第二行留空)和@details表示;
  • 此函数的参数列表, 用@param表示, 对每个参数的描述 (包括数据类型);
  • 以及对于返回值的描述, 以@return表示;
  • 其他可选注释,如@aliases@author@examples@export@importFrom

这些注释应当描述得足够充分, 这样调用者无须阅读函数中的任何代码即可使用此函数. 而编译后,roxygen2能自动将这些注释生成函数文档(.Rd)。

鼓励用RStudio中书写代码。可使用 Ctrl+Alt+Shift+R 快捷键在函数体上方插入Roxygen2文档骨架。

示例函数 Showcase

#' Computes the sample covariance between two vectors.
#' 
#' \code{CalculateSampleCovariance} computes the sample covariance between two 
#'       vectors.
#' 
#' @details \code{CalculateSampleCovariance} computes the sample covariance 
#'       between two vectors.
#'
#' @param x One of two vectors whose sample covariance is to be calculated.
#' @param y The other vector. x and y must have the same length, greater than one,
#'      with no missing values.
#' @param verbose If TRUE, prints sample covariance; if not, not. Default is TRUE.
#'
#' @return The sample covariance between x and y.
#' @export
#' @import base
#'
#' @examples
#' calculateSampleCovariance(x, y)
#'
#' @author Given Middle Surname, \email{name@@domain.com}
#' @references \url{http://xxxxxxxxxxx}
#' @seealso \code{\link{var}}
#' @keywords covariance
#'

calculateSampleCovariance <- function(x, y, verbose = TRUE) {
  n <- length(x)
  # Error handling
  if (n <= 1 || n != length(y)) {
    stop("Arguments x and y have invalid lengths: ",
         length(x), " and ", length(y), ".")
  }
  if (TRUE %in% is.na(x) || TRUE %in% is.na(y)) {
    stop(" Arguments x and y must not have missing values.")
  }
  covariance <- var(x, y)
  if (verbose)
    cat("Covariance = ", round(covariance, 4), ".\n", sep = "")
  return(covariance)
}

对于不输出到命名空间的隐函数,可将上述注释修改为非roxygen2格式的,即以“#”开头,而非“#’”。

TODO/FIXME 书写风格 Style

编码时通篇使用一种一致的风格来书写 TODO:

  • TODO(您的用户名): 所要采取行动的明确描述 或
  • FIXME(您的用户名): 所要采取行动的明确描述

语言规则 (Language)

不要用Attach()

使用 attach() 造成错误的可能数不胜数。避免使用它。

完整书写Comprehensive TRUE和FALSE

始终完整书写TRUE和FALSE,而不要用T或F简略。

  • 正例:

    a <- TRUE
  • 反例:

    a <- F

错误捕捉 Error Capturing

错误 (error) 应当使用 stop()stopifnot() 抛出。

对象和方法 Objects And Methods

  • S 语言中有两套面向对象系统,S3 和 S4,在 R 中这两套均可使用。S3 方法的可交互性更强,更加灵活;反之,S4 方法更加正式和严格。
  • 这里推荐使用 S3 对象和方法,除非您有很强烈的理由去使用 S4 对象和方法。使用 S4 对象的一个主要理由是在 C++ 代码中直接使用对象。使用一个 S4 泛型/方法的主要理由是对双参数的分发.
  • 避免混用 S3 和 S4: S4 方法会忽略 S3 中的继承,反之亦然。