2012-06-30 13 views
14

Jestem upartym useR, który używa = zamiast <- przez cały czas, i najwyraźniej wielu programistów R będzie marszczyło brwi na tym. Napisałem pakiet , który może zastąpić = z <- na podstawie pakietu parser. Jak niektórzy z was mogli wiedzieć, parser został osierocony na CRAN kilka dni temu. Chociaż wróciłem, to sprawiło, że nie mogłem na tym polegać. Zastanawiam się, czy istnieje inny sposób bezpiecznego zastąpienia = z <-, ponieważ nie wszystkie znaczące zadania przypisano np. =, np. fun(a = 1). Wyrażenia regularne raczej nie będą wiarygodne (zobacz line 18 z funkcji mask.inline() w formatR), ale na pewno będę wdzięczny, jeśli możesz poprawić moje. Być może pakiet codetools może pomóc?niezawodny sposób, aby powiedzieć, czy = jest dla przypisania w kodzie R?

Kilka przypadków testowych:

# 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)) 
+6

Dobry i użyteczny kod open-source nie umiera i nigdy nie zaginie w nim - a teraz parser ma nowego opiekuna, jak można się spodziewać. Gdzie dokładnie jest problem w zależności od testowanego narzędzia, które wykonuje zadanie? –

+0

Na pewno mam zaufanie do Andrew, a społeczność nie pozwoli, by parser umarł. W rzeczywistości chciałem (https://github.com/yihui/knitr/issues/211) usunąć zależność od highlight/parser, zanim zostały osierocone; nie jest to wartość zależna od tej stosunkowo małej funkcji (długi łańcuch zależności ma problemy, takie jak konserwacja i czas ładowania pakietu). –

+1

Po co zawracać sobie głowę? I dlaczego pytanie o zastąpienie "=" przez "<-", gdy preferujesz pierwsze? – mdsumner

Odpowiedz

4

Kohske wysłał pull request do pakietu formatR której rozwiązano ten problem za pomocą pakietu codetools. Podstawową ideą jest ustawienie chodziarki kodu, aby przejść przez kod; gdy wykryje = jako symbol wywołania funkcjonalnego, zostanie zastąpiony przez <-. Jest to spowodowane "naturą Lispa" R: x = 1 jest w rzeczywistości `=`(x, 1) (zamieniamy ją na `<-`(x, 1)); oczywiście, = jest traktowane inaczej w drzewie parsowania fun(x = 1).

Pakiet formatR (> = 0.5.2) od tego czasu pozbył się zależności od pakietu parser, a teraz replace.assign powinien być solidny.

+0

Co jest warte, właśnie usłyszałem, że części parsera mogą zostać włączone do bazy R. –

+0

To jest wspaniała wiadomość! Dzięki, @DirkEddelbuettel –

-3

Najbezpieczniejszym (i prawdopodobnie najszybszy) sposób zastąpić = przez <- jest bezpośrednio wpisując <- zamiast próbować go zastąpić.

+0

To prawda, ale jeśli masz zilionowe skrypty, które musisz "upiększyć", może to być pomocne. –

+3

To jak mówienie, że programy sprawdzania gramatyki są bezużyteczne, ponieważ nigdy nie robisz literówek ... – flodel

+0

@flodel: wpisywanie '=' zamiast "<-" to IMHO nie literówka, ale coś celowego. To raczej nie koniugować czasowników i polegać wyłącznie na programach sprawdzających gramatykę, aby zrobić to za Ciebie. – Thierry

4

Ta odpowiedź używa wyrażeń regularnych. Istnieje kilka przypadków skrajnych, w których zawiedzie, ale powinno być w porządku dla większości kodu. Jeśli potrzebujesz doskonałego dopasowania, musisz użyć parsera, ale w razie napotkania problemów możesz zawsze poprawić wyrazy regularne.

Uważaj na

#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 
) 

Kod jest oparte na przykład na stronie ?regmatches. Podstawową ideą jest: zamiana zawartości funkcji na element zastępczy, dokonanie wymiany, a następnie powrót do zawartości funkcji.

#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") 
+0

Wielkie dzięki! Bardzo to doceniam. Kohske wymyślił genialne rozwiązanie za pomocą 'codetools' i wysłał mi żądanie [pull request] (https://github.com/kohske/formatR/commit/a2692d59f17ac9106e43f564857bc43d728fde6b) na Github. Właśnie takie rozwiązanie szukałem. –

+0

Wow. Rozwiązanie Kohske wygląda dobrze. I chodzenie po kodzie jest bardziej niezawodne niż metoda regex. Proszę odrzucić moją odpowiedź na "słodkie demo wyrażeń regularnych". –

Powiązane problemy