Почему тидьевальские кавычки терпят неудачу в лямдах? - PullRequest
1 голос
/ 28 апреля 2020

Ниже приведен простой пример того, как цитата используется для динамического переименования столбца tibble.

quoteExample = function() {  
   new_name = quo("new_name_value"); 
   tibble(old_name=list(1,2,3)) %>% 
       rename( !! quo_name(new_name) := old_name) 
}

quoteExample()

Result = tibble ( new_name_value = list (1,2,3) )

Ниже приведен тот же простой пример, но только в лямде.

{function () 
   new_name = quo("new_name_value"); 
   tibble(old_name=list(1,2,3)) %>% 
       rename( !! quo_name(new_name) := old_name)
} ()

Результат = Ошибка в is_quosure (quo): объект 'новое_имя' не найден

Почему сбои в кавычках в лямде, но не в именованной функции? Откуда эта разница? Я делаю что-то неправильно?

РЕДАКТИРОВАТЬ : Приведенный выше пример был решен Akrun, но ниже приведен еще один пример, который не работает, хотя было предложено предлагаемое решение:

df = tibble(data=list(tibble(old_name= c(1,2,3))))

df %>% 
   mutate(data = map(data, (function(d){
      new_name = quo("new_value")
      d %>% rename( !! quo_name(new_name) := old_name)
    })))

Результат: Ошибка в is_quosure (quo): объект 'new_name' не найден

Это сбой из-за другой проблемы?

Ответы [ 2 ]

2 голосов
/ 28 апреля 2020

Это в основном та же проблема, что и здесь . Основной причиной является оператор !!, заставляющий немедленно вычислить свой аргумент, до создается среда анонимной функции. В вашем случае !! quo_name(new_name) пытается найти определение new_name относительно выражения в целом (т. Е. Всего выражения mutate(...)). Поскольку new_name определено в самом выражении, вы получите циклическую зависимость, которая приведет к ошибке «объект не найден».

У вас есть три варианта:

1) Вытащите лямбду в автономную функцию, чтобы убедиться, что сначала создается ее среда, поэтому все переменные в этой среде должным образом инициализируются до того, как оператор !! принудительно выполнит их оценку:

f <- function(d) {
    new_name = sym("new_value")
    d %>% rename(!!new_name := old_name)
}

df %>% mutate(data = map(data, f))

2) Определите new_name вне выражения, которое пытается форсировать его оценку с помощью !!

new_name = sym("new_value")
df %>%
    mutate(data = map(data, function(d) {d %>% rename(!!new_name := old_name)}))

3) Переписать ваше выражение так, чтобы оно не использовало оператор !! для оценки переменных, которые еще не были инициализированы (new_name в этом case):

df %>%
   mutate(data = map(data, function(d) {
     new_name = "new_value"
     do.call( partial(rename, d), set_names(syms("old_name"), new_name) )
   }))

SIDE ПРИМЕЧАНИЕ: Вы заметите, что я заменил ваши quo() звонки на sym(). Функция quo() захватывает выражение вместе с его окружением. Поскольку строковый литерал "new_value" всегда будет иметь одно и то же значение, нет необходимости отмечать его окружение. В общем, правильный глагол для ввода имен столбцов в виде символов: sym().

1 голос
/ 28 апреля 2020

Если мы сделаем его автономным с () или {}, оно должно работать

(function() {  
     new_name = quo("new_name_value"); 
     tibble(old_name=list(1,2,3)) %>% 
           rename( !! quo_name(new_name) := old_name) 
    })()

# A tibble: 3 x 1
#  new_name_value
#  <list>        
#1 <dbl [1]>     
#2 <dbl [1]>     
#3 <dbl [1]>  

Если анонимная функция содержит только одно expr эссе, она не нужна используйте {}, но если оно имеет более одной строки выражения, мы заключаем в {}. Согласно ?body

Тела всех, кроме самых простых, являются выражениями в скобках, то есть вызовами {: см. Раздел «Примеры» для создания такого вызова.

...