Использование R, как получить par и setPar для доступа к внутренностям par()

#r #plot #variadic-functions #getter-setter

#r #сюжет #переменные-функции #получатель-сеттер

Вопрос:

Я работаю над некоторыми общими функциями доступа для par() опции в R.

getPar

 getPar = function(key)
    {
    par()[[key]];   
    }
 

Это работает, как и ожидалось.

 getPar("mar");
 

setPar

 # save memory ... restoreState ... pdf 
setPar = function(key, val)
    {
    # par(mar=c(0.25, 0.25, 0.25, 0.25)
    # R.O. indicates read-only arguments: These may only be used in queries and cannot be set. ("cin", "cra", "csi", "cxy", "din" and "page" are always read-only.)
    # https://www.rdocumentation.org/packages/graphics/versions/3.6.2/topics/par
    pnames = names( par(no.readonly = TRUE) );
    if(is.element(key, pnames))
        {
        par()[[key]] = val;
        }
    }
 

Это не работает:

 mar = c(0.25, 0.25, 0.25, 0.25);
setPar("mar", mar);
 

И выдает ошибку:

 Error in par()[[key]] = val : invalid (NULL) left side of assignment
 

Есть идеи о том, как я могу написать setter функцию, как описано в общих чертах?

Да, я понимаю, я могу перейти par(mar = c(0.25, 0.25, 0.25, 0.25)) непосредственно к «установке» значения. Я специально ищу решение, которое будет работать внутри этой простой setter функции. Вариационный подход.

Ответ №1:

Для присвоения спискам (т. Е. [<- И [[<- ) Требуется объект в LHS присваивания, а не выражение, которое создает список. Аналогично,

 A <- list(a=1)
A$b <- 2
A
# $a
# [1] 1
# $b
# [1] 2
list(a=1)$b <- 3
# Error in list(a = 1)$b <- 3 : 
#   target of assignment expands to non-language object
 

Я предлагаю вам изменить setPar , чтобы фактически установить значение, используя par , а не пытаться работать со списком.

 setPar = function(key, val)
    {
    # par(mar=c(0.25, 0.25, 0.25, 0.25)
    # R.O. indicates read-only arguments: These may only be used in queries and cannot be set. ("cin", "cra", "csi", "cxy", "din" and "page" are always read-only.)
    # https://www.rdocumentation.org/packages/graphics/versions/3.6.2/topics/par
    pnames = names( par(no.readonly = TRUE) );
    if(is.element(key, pnames))
        {
        par(setNames(list(val), key))
        }
    }

par("mar")
# [1] 5.1 4.1 4.1 2.1
setPar("mar", 1:4)
par("mar")
# [1] 1 2 3 4
 

Комментарии:

1. Итак, он появляется getPar и par возвращает то же самое, и я также могу удалить эту идею «доступа к списку».

2. Да, ваш getPar эквивалент par . Однако, если вы действительно хотите попасть в сорняки, par(key) это почти в 4 раза быстрее, чем par()[[key]] , и в остальном совершенно эквивалентно. Даже более медленный par()[[key]] процесс занимает порядка 34 микросекунд на моей машине, так что это не критическая точка сбоя кода. Попробуйте bench::mark(par("mar"), par()[["mar"]]) увидеть более полное сравнение.

3. Когда мы говорим о efficiency временных критериях, это только один фактор. Важным фактором также является меньшая когнитивная нагрузка.

4. Я полностью согласен. То, что вы называете «когнитивной нагрузкой», я обычно объединяю в ремонтопригодность (для меня) и удобочитаемость (для других и для будущего меня), которые являются (imo) разумными причинами для намеренного предотвращения истинного улучшения производительности с использованием запутанных или неясных сокращений кода-golf в коде. (К сожалению, иногда моя читаемость имеет измеримый уровень разочарования, когда нагрузка умножается на 3 строки …). Но да, вы правы, и именно поэтому я предположил, что тратить несколько микросекунд не стоило, если вы предпочитаете прежний код.

5. Я полностью согласен. Возможно, присвоение функции чего-то более подробного, например getParKey , уменьшит мою будущую «когнитивную нагрузку». R потрясающий, но и причудливый, как показывает ваш ответ. Иногда он следует правилам абстрактной алгебры обратных функций, и оболочку get/set легче написать: getAttribute = function(key, obj) { attributes(obj)[[key]]; } setAttribute = function(key, value, obj) { attributes(obj)[[key]] = value; obj; # no object referencing, so I must return }