为什么 RStudio Terminal 中的函数定义没有随 Global Environment 更新?
结论
这是正常行为。在 RStudio Terminal 中手动启动的 R 是独立进程,它的工作空间与 RStudio Console 使用的工作空间不同。Console 中更新的 foo 不会自动出现在 Terminal 的 R 会话中。
RStudio 的 Global Environment 面板通常显示主 R 会话的全局环境。Terminal 虽然嵌在同一个窗口里,但其中启动的 R 不共享这个环境,也不会默认自动同步。
为什么会看到旧定义?
Terminal 中的 R 启动时,如果恢复了保存的工作空间,例如 .RData,就会加载保存时的 foo。这是当时对象的快照,不会随其他会话中的修改而变化。
保存文件和执行代码也有区别:
- 修改并保存
.Rmd,只会更新磁盘上的文件。 - 在 Console 中执行定义
foo的代码,会更新 Console 所属会话中的对象。 - 在 Terminal 的 R 中执行同样的定义,会更新 Terminal 会话中的对象。
如果只编辑 .Rmd 而没有执行相应代码,Console 中的 foo 也可能仍是旧定义。
可以分别在 Console 和 Terminal 的 R 提示符下执行:
Sys.getpid()
如果得到的进程 ID 不同,就可以确认它们是独立进程。即使两边的 getwd() 相同,也不代表它们共享内存中的对象。
如何使用更新后的函数?
在 RStudio 中调试时,使用 Console
如果希望函数与 Global Environment 面板中的对象保持一致,可以在 RStudio Console 对应的会话中执行 .Rmd 里定义函数的代码块,然后调用函数。
例如,将返回值改为 3 后,执行:
foo <- function() {
return(3)
}
foo()
# [1] 3
只保存 .Rmd 不会执行这段代码。Knit 或 Render 通常也会使用独立的执行会话,不应依靠渲染来更新当前 Console 中的对象。
两个会话都需要函数时,加载同一份 R 脚本
可以将函数定义放在单独的 functions.R 文件中:
foo <- function() {
return(3)
}
然后分别在 Console 和 Terminal 的 R 中执行:
source("functions.R")
foo()
修改并保存 functions.R 后,需要在各个会话中重新运行 source(),才能更新各自的定义。相对路径以各会话的工作目录为准,必要时可以使用绝对路径。
不要直接用 source() 加载包含 Markdown 正文和代码围栏的 .Rmd 文件。可以执行其中的 R 代码块,或把可复用的函数提取到 .R 文件中。
临时传递对象时,显式保存和读取
如果要把 Console 中当前的 foo 传给 Terminal,可以通过文件传递。
在 Console 中执行:
saveRDS(foo, file = path.expand("~/foo-transfer.rds"))
在 Terminal 的 R 中执行:
foo <- readRDS(path.expand("~/foo-transfer.rds"))
foo
这会替换 Terminal 中当前的 foo。之后如果再次修改函数,需要重新保存和读取。示例文件名应避开已有的重要文件,因为 saveRDS() 会覆盖同名文件。
如果函数依赖其他对象、包或外部资源,还需要保证接收会话具备这些依赖。日常开发中,共享脚本并在需要时重新执行,通常比反复保存和恢复整个工作空间更容易追踪函数来自哪个版本。
备注:内容仅供参考。
