私はRでパッケージを開発しています。私はたくさんの関数を持っています、それらのいくつかはいくつかのグローバル変数を必要とします。パッケージ内のグローバル変数を管理するにはどうすればよいですか?
私は環境について何かを読んだことがありますが、それがどのように機能するのか、これが物事を進める方法であるかどうかはわかりません。
私はRでパッケージを開発しています。私はたくさんの関数を持っています、それらのいくつかはいくつかのグローバル変数を必要とします。パッケージ内のグローバル変数を管理するにはどうすればよいですか?
私は環境について何かを読んだことがありますが、それがどのように機能するのか、これが物事を進める方法であるかどうかはわかりません。
環境を通じてパッケージローカル変数を使用できます。これらの変数は、パッケージ内の複数の関数で使用できますが、ユーザーは(簡単に)アクセスできず、ユーザーのワークスペースに干渉しません。簡単な例は次のとおりです。
pkg.env <- new.env()
pkg.env$cur.val <- 0
pkg.env$times.changed <- 0
inc <- function(by=1) {
pkg.env$times.changed <- pkg.env$times.changed + 1
pkg.env$cur.val <- pkg.env$cur.val + by
pkg.env$cur.val
}
dec <- function(by=1) {
pkg.env$times.changed <- pkg.env$times.changed + 1
pkg.env$cur.val <- pkg.env$cur.val - by
pkg.env$cur.val
}
cur <- function(){
cat('the current value is', pkg.env$cur.val, 'and it has been changed',
pkg.env$times.changed, 'times\n')
}
inc()
inc()
inc(5)
dec()
dec(2)
inc()
cur()
option
たとえば、を設定できます
options("mypkg-myval"=3)
1+getOption("mypkg-myval")
[1] 4
一般に、グローバル変数は悪です。それらが悪である根本的な原則は、パッケージ内の相互接続を最小限に抑えたいということです。これらの相互接続は、多くの場合、関数に副作用をもたらします。つまり、結果が何であるかは、入力引数だけでなく、グローバル変数の値にも依存します。特に関数の数が増えると、これを正しく行うのが難しくなり、デバッグが大変になります。
R のグローバル変数については、このSO 投稿を参照してください。
あなたのコメントに応じて編集してください: 別の方法として、必要な情報を必要な機能に渡すだけでよい場合があります。この情報を含む新しいオブジェクトを作成できます:
token_information = list(token1 = "087091287129387",
token2 = "UA2329723")
この情報を必要とするすべての関数は、引数としてそれを持つ必要があります。
do_stuff = function(arg1, arg2, token)
do_stuff(arg1, arg2, token = token_information)
このようにして、トークン情報が関数に必要であることはコードから明らかであり、関数を単独でデバッグできます。さらに、関数の動作は入力引数によって完全に決定されるため、関数には副作用がありません。典型的なユーザー スクリプトは次のようになります。
token_info = create_token(token1, token2)
do_stuff(arg1, arg2, token_info)
これで物事がより明確になることを願っています。
パッケージに依存関係を追加してもかまわない場合は、@greg-snow の回答へのコメントで示唆されているように、同名パッケージR6
のオブジェクトを使用できます。
R6
オブジェクトは、パブリック メソッドとプライベート メソッドを追加できる実際の環境であり、非常に軽量であり、グローバル環境を汚染することなく、パッケージのグローバル変数を共有するための優れたより厳密なオプションになる可能性があります。
@greg-snow のソリューションと比較して、変数をより厳密に制御できます (たとえば、型をチェックするメソッドを追加できます)。欠点は、依存関係と、もちろんR6
構文の学習です。
library(R6)
MyPkgOptions = R6::R6Class(
"mypkg_options",
public = list(
get_option = function(x) private$.options[[x]]
),
active = list(
var1 = function(x){
if(missing(x)) private$.options[['var1']]
else stop("This is an environment parameter that cannot be changed")
}
,var2 = function(x){
if(missing(x)) private$.options[['var2']]
else stop("This is an environment parameter that cannot be changed")
}
),
private = list(
.options = list(
var1 = 1,
var2 = 2
)
)
)
# Create an instance
mypkg_options = MyPkgOptions$new()
# Fetch values from active fields
mypkg_options$var1
#> [1] 1
mypkg_options$var2
#> [1] 2
# Alternative way
mypkg_options$get_option("var1")
#> [1] 1
mypkg_options$get_option("var3")
#> NULL
# Variables are locked unless you add a method to change them
mypkg_options$var1 = 3
#> Error in (function (x) : This is an environment parameter that cannot be changed
2020-05-27 作成者reprex パッケージ(v0.3.0)