Надежный способ сказать if = для назначения в R-коде?
Я упрямый useR, который все время использует =
вместо <-
, и, по-видимому, многие программисты R будут хмуриться. Я написал пакет formatR
, который может заменить =
на <-
на основе parser
. Как некоторые из вас могут знать, parser
был потерян на CRAN несколько дней назад. Хотя сейчас он вернулся, это заставило меня зависеть от этого. Мне интересно, есть ли другой способ безопасно заменить =
на <-
, потому что не все =
означают назначение, например. fun(a = 1)
. Регулярные выражения вряд ли будут надежными (см. строка 18 функции mask.inline()
в formatR
), но я, безусловно, буду признателен, если вы может улучшить мою. Возможно, пакет codetools
может помочь?
Несколько тестовых случаев:
# should replace
a = matrix(1, 1)
a = matrix(
1, 1)
(a = 1)
a =
1
function() {
a = 1
}
# should not replace
c(
a = 1
)
c(
a = c(
1, 2))
Ответы
Ответ 1
Kohske отправил запрос на перенос в пакет formatR
, который решил проблему, используя пакет codetools
. Основная идея состоит в том, чтобы установить ходунок кода, чтобы пройти через код; когда он обнаруживает =
как символ функционального вызова, он заменяется на <-
. Это связано с "Lisp характером" R: x = 1
на самом деле `=`(x, 1)
(мы заменяем его на `<-`(x, 1)
); конечно, =
обрабатывается по-разному в дереве разбора fun(x = 1)
.
Пакет formatR
( >= 0.5.2) с тех пор избавился от зависимости от пакета parser
, а replace.assign
теперь должен быть надежным.
Ответ 2
В этом ответе используются регулярные выражения. Есть несколько краевых случаев, когда он провалится, но для большинства кода это должно быть хорошо. Если вам нужно идеальное совпадение, вам нужно будет использовать парсер, но регулярные выражения всегда могут быть изменены, если вы столкнулись с проблемами.
Следите за
#quoted function names
`my cr*azily*named^function!`(x = 1:10)
#Nested brackets inside functions
mean(x = (3 + 1:10))
#assignments inside if or for blocks
if((x = 10) > 3) cat("foo")
#functions running over multiple lines will currently fail
#maybe fixable with paste(original_code, collapse = "\n")
mean(
x = 1:10
)
Код основан на примере на странице ?regmatches
. Основная идея: содержимое функции замены для заполнителя, сделать замену, а затем вернуть содержимое функции.
#Sample code. For real case, use
#readLines("source_file.R")
original_code <- c("a = 1", "b = mean(x = 1)")
#Function contents are considered to be a function name,
#an open bracket, some stuff, then a close bracket.
#Here function names are considered to be a letter or
#dot or underscore followed by optional letters, numbers, dots or
#underscores. This matches a few non-valid names (see ?match.names
#and warning above).
function_content <- gregexpr(
"[[:alpha:]._][[:alnum:._]*\\([^)]*\\)",
original_code
)
#Take a copy of the code to modify
copy <- original_code
#Replace all instances of function contents with the word PLACEHOLDER.
#If you have that word inside your code already, things will break.
copy <- mapply(
function(pattern, replacement, x)
{
if(length(pattern) > 0)
{
gsub(pattern, replacement, x, fixed = TRUE)
} else x
},
pattern = regmatches(copy, function_content),
replacement = "PLACEHOLDER",
x = copy,
USE.NAMES = FALSE
)
#Replace = with <-
copy <- gsub("=", "<-", copy)
#Now substitute back your function contents
(fixed_code <- mapply(
function(pattern, replacement, x)
{
if(length(replacement) > 0)
{
gsub(pattern, replacement, x, fixed = TRUE)
} else x
},
pattern = "PLACEHOLDER",
replacement = regmatches(original_code, function_content),
x = copy,
USE.NAMES = FALSE
))
#Write back to your source file
#writeLines(fixed_code, "source_file_fixed.R")
Ответ 3
Самый безопасный (и, вероятно, самый быстрый) способ заменить =
на <-
- это просто ввести <-
вместо того, чтобы его заменить.