在R中,哪种面向对象编程风格会对Python程序员更易读?

15
我是CRAN上日志包的作者,虽然我不认为自己是R程序员,但我试图尽可能使其与Python标准日志包的代码兼容,现在我有一个问题,并希望这能给我学习更多R的机会!
我的问题与分层记录器有关。在Python中,我会创建一个记录器并发送日志记录:
l = logging.getLogger("some.lower.name")
l.debug("test")
l.info("some")
l.warn("say no")

在我的R软件包中,您不需要创建一个记录器来发送消息,而是调用一个函数,其中一个参数是记录器的名称。类似于

logdebug("test", logger="some.lower.name")
loginfo("some", logger="some.lower.name")
logwarn("say no", logger="some.lower.name")

问题在于每次想要发送日志消息时都需要重复记录器的名称。我在考虑,可以创建一个部分应用的函数对象,然后调用它,类似于:
logdebug <- curry(logging::logdebug, logger="some.lower.logger")

但是我需要为所有调试函数执行此操作... 那么你们R用户会如何处理这个问题?

大家好,我收到了两个有趣的答案,都对我的小型库添加了要求。从长远来看,我认为我更喜欢基于“ReferenceClasses”的那个,但在短期内,“proto”允许我使用R2.11... - mariotomo
3个回答

29

听起来需要使用引用类 ?setRefClass, ?ReferenceClasses

Logger <- setRefClass("Logger",
                  fields=list(name = "character"),
                  methods=list(
                    log = function(level, ...) 
                          { levellog(level, ..., logger=name) },
                    debug = function(...) { log("DEBUG", ...) },
                    info = function(...) { log("INFO", ...) },
                    warn = function(...) { log("WARN", ...) },
                    error = function(...) { log("ERROR", ...) }
                    ))

然后

> basicConfig()
> l <- Logger$new(name="hierarchic.logger.name")
> l$debug("oops")
> l$info("oops")
2011-02-11 11:54:05 NumericLevel(INFO):hierarchic.logger.name:oops
> l$warn("oops")
2011-02-11 11:54:11 NumericLevel(WARN):hierarchic.logger.name:oops
> 

我有一个跟随这个答案的问题 - mariotomo
我认为仅仅为了语法糖而使用引用类是一个不好的想法,因为它使得代码更难理解。你可以通过返回函数列表来实现相同的功能。 - hadley
@hadley 我认为这是将“日志记录器”概念与引用语义对齐,而不是语法糖。 - Martin Morgan
但实际上你并没有修改任何东西,这使得使用引用语义变得无意义。 - hadley
我原本期望记录器会写入(即修改)一个连接,例如硬盘上的文件。 - Martin Morgan

3
这可以使用proto包完成。该包支持较旧版本的R(已存在多年),因此您不会遇到新旧版本R之间的问题。
library(proto)
library(logging)

Logger. <- proto(
        new = function(this, name)
            this$proto(name = name),
        log = function(this, ...) 
            levellog(..., logger = this$name),
        setLevel = function(this, newLevel) 
            logging::setLevel(newLevel, container = this$name),
        addHandler = function(this, ...)
            logging::addHandler(this, ..., logger = this$name), 
        warn = function(this, ...)
            this$log(loglevels["WARN"], ...),
        error = function(this, ...)
            this$log(loglevels["ERROR"], ...) 
)
basicConfig()
l <- Logger.$new(name = "hierarchic.logger.name")
l$warn("this may be bad")
l$error("this definitely is bad")

这将产生输出:
> basicConfig()
> l <- Logger.$new(name = "hierarchic.logger.name")
> l$warn("this may be bad")
2011-02-28 10:17:54 WARNING:hierarchic.logger.name:this may be bad
> l$error("this definitely is bad")
2011-02-28 10:17:54 ERROR:hierarchic.logger.name:this definitely is bad

在上面的例子中,我们仅仅是在日志记录之上添加了proto,但是可以将每个日志记录对象转换为proto对象,即它们都是R环境。这样就可以摆脱多余的层次。
更多信息请参见http://r-proto.googlecode.com

Gabor,我想删除distributing R package with optional dependencies这个问题,但你在那里附上了一个在这里很有用的答案。如果你把它从那里移到这里,我会删除整个dRpwod问题。我试图自己移动,但我的编辑被拒绝了。 - mariotomo
有一个删除按钮,但似乎并没有删除帖子。它只是询问我是否要投票删除它。我已经移动了帖子,并用指向新帖子的指针替换了这个。 - G. Grothendieck
你能编辑这个回答并把另一个回答的文本粘贴在这里吗?接着我会投票删除那个问题。 - mariotomo
我尝试将proto添加到日志包中,但是我没有成功。毕竟,参考类更容易使用。 - mariotomo

1

为什么要重复名称?将日志对象直接传递给函数会更方便,例如:

logdebug("test",logger=l)
# or
logdebug("test",l)

在许多函数中使用连接的方式有点像。我想这似乎更符合R的做法。


可能更符合R语言的风格,将记录器作为第一个参数,然后logdebug是记录器类的一个方法。 - Spacedman
@Spacedman:我也这么说,但我尽可能地保持与OP的结构接近。不过如果使用S4会更容易些。 - Joris Meys
5
“用S4会更容易” - 一句著名的遗言 :P - hadley
@Hadley:好的,你抓住我了。 :-)(我的意思是将记录器作为第一个参数会使使用S4更容易...或更少痛苦...) - Joris Meys
另一个问题基于S4,但对于习惯于Python、Java和C++的人来说更容易理解。无论如何,我会遵循你们的提示,继续了解S4。感谢大家! - mariotomo
@mariotomo:参考类不是S4类,它们之间有很大的区别。对于来自其他语言的面向对象编程(OOP)程序员而言,参考类似乎更加自然。但我还是会坚持好老的函数式方法。 - Joris Meys

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接