В чем разница между eval (parse ()), eval (str2lang ()), eval (str2expression ()) и eval (call () [[1]])? - PullRequest
1 голос
/ 23 апреля 2020

Я пишу функцию для извлечения переменной, представленной в виде строки, из заданного data.frame df или из среды env. Изначально я использовал для этого конструкцию eval(parse(text=s), df, env), но я узнал, что есть более эффективные альтернативы. Другие опции включают в себя:

  1. eval(str2lang(s), df, env)
  2. eval(str2expression(s), df, env)
  3. eval(call(s)[[1]], df, env)

Может быть get решение, но я не знаю, сможет ли он проверить, находится ли переменная сначала в df, прежде чем обратиться к env, если это не так.

При использовании microbenchmark, кажется, что call является самым быстрым:

library(microbenchmark)

x1 = 1
df = data.frame(x2 = 2)

microbenchmark(call = eval(call('x1')[[1]], df), 
               parse = eval(parse(text='x1'), df), 
               str2lang = eval(str2lang('x1'), df), 
               str2exp = eval(str2expression('x1'), df), 
               check = "identical")
#> Unit: microseconds
#>      expr    min      lq     mean  median      uq     max neval cld
#>      call  1.128  1.2115  1.60815  1.4585  1.6360   4.659   100  a 
#>     parse 39.183 39.8705 46.60755 40.2405 42.0415 135.462   100   b
#>  str2lang  2.235  2.3570  3.26144  2.5995  2.8925  24.641   100  a 
#>   str2exp  2.230  2.3200  2.81387  2.4780  2.6970  10.312   100  a

microbenchmark(call = eval(call('x2')[[1]], df), 
               parse = eval(parse(text='x2'), df), 
               str2lang = eval(str2lang('x2'), df), 
               str2exp = eval(str2expression('x2'), df), 
               check = "identical")
#> Unit: microseconds
#>      expr    min     lq     mean  median      uq     max neval cld
#>      call  1.124  1.194  1.47770  1.3675  1.5795   9.031   100  a 
#>     parse 38.254 38.762 40.21497 38.9630 39.3120 116.510   100   b
#>  str2lang  2.214  2.304  2.55036  2.3960  2.6530  10.639   100  a 
#>   str2exp  2.238  2.331  2.50011  2.4210  2.6515   3.619   100  a

Создано в 2020-04-23 пакетом представитель (v0.3.0)

Поэтому я склонен использовать call, но я хочу убедиться, что не будет никаких непреднамеренных последствий этого, вместо использования других решений. Другими словами, в каких ситуациях (в контексте, в котором я их использую) четыре метода не дают один и тот же ответ, приводя один к предпочтению одного над другими?

1 Ответ

0 голосов
/ 23 апреля 2020

Я думаю, это зависит от того, какие строки вы пытаетесь оценить. Если это всегда одно имя, то, вероятно, as.name('x1') также является претендентом; мои тесты показывают, что это немного медленнее, чем ваше решение с call, но я бы сказал, что оно предпочтительнее только потому, что оно менее неясно и менее вероятно вызовет будущие головные боли.

Могут возникнуть головные боли из-за этого: в настоящее время call("x1") создает языковой объект x1(), а затем вы извлекаете имя вызываемой функции. Но что, если в какой-то будущей версии R он вспомнит, что x1 в этом выражении должен быть функцией? eval(x1()) уже знает, что нужно игнорировать нефункциональные объекты с именем x1, поэтому не нужно ломать много кода для внесения этого изменения, и это может быть полезно с точки зрения эффективности.

as.name также допускает не символьные аргументы, например as.name(123); это может быть положительным или отрицательным для вас. (Оба решения позволят "123".)

...