Zmieniam klasę niektórych funkcji, które buduję w R, aby dodać atrybut opisu i ponieważ chcę używać generics S3, aby obsłużyć wszystko dla mnie. Zasadniczo mam strukturę jakDefiniowanie nowej klasy funkcji w R
foo <- function(x) x + 1
addFunction <- function(f, description) {
class(f) <- c("addFunction", "function")
attr(f, "description") <- description
f
}
foo <- addFunction(foo, "Add one")
a następnie zrobić rzeczy jak
description <- function(x) UseMethod("description")
description.default <- function(x) deparse(substitute(x))
description.addFunction <- function(x) attr(x, "description")
Działa to dobrze, ale to nie jest tak elegancki. Zastanawiam się, czy możliwe jest zdefiniowanie nowej klasy funkcji, tak aby instancje tej klasy można było zdefiniować w składni podobnej do składni function
. Innymi słowy, jest to możliwe do zdefiniowania addFunction
takie, że foo
generowany jest w następujący sposób:
foo <- addFunction(description = "Add one", x) {
x + 1
}
(lub coś podobnego, nie mam silne uczucia co gdzie atrybut powinien zostać dodany do funkcji)?
Dzięki za przeczytanie!
Aktualizacja: ja eksperymentowałem trochę więcej z ideą, ale naprawdę nie osiągnął jeszcze żadnych konkretnych rezultatów - więc jest to tylko przegląd moich obecnych (Aktualizacja) myśli na ten temat:
Próbowałem po prostu skopiować funkcję function()
, nadając jej inną nazwę, a następnie manipulując nią później. Jednak to nie działa i chciałbym żadnych nakładów na co się tu dzieje:
> function2 <- `function`
> identical(`function`, function2)
[1] TRUE
> function(x) x
function(x) x
> function2(x) x
Error: unexpected symbol in "function2(x) x"
> function2(x)
Error: incorrect number of arguments to "function"
Jak function()
jest prymitywne funkcja Próbowałem patrząc na C-kod zdefiniowaniem więcej wskazówek. Byłem szczególnie zaintrygowany komunikatem o błędzie z połączenia function2(x)
. C-kodu bazowego function()
jest
/* Declared with a variable number of args in names.c */
SEXP attribute_hidden do_function(SEXP call, SEXP op, SEXP args, SEXP rho)
{
SEXP rval, srcref;
if (TYPEOF(op) == PROMSXP) {
op = forcePromise(op);
SET_NAMED(op, 2);
}
if (length(args) < 2) WrongArgCount("function");
CheckFormals(CAR(args));
rval = mkCLOSXP(CAR(args), CADR(args), rho);
srcref = CADDR(args);
if (!isNull(srcref)) setAttrib(rval, R_SrcrefSymbol, srcref);
return rval;
}
i z tego wniosku, że z jakiegoś powodu, przynajmniej dwa z czterech argumentów call
, op
, args
i rho
są obecnie wymagane. Od podpisu do_function()
Zgaduję, że cztery argumenty przekazane do do_function
powinny być wywołaniem, obietnicą, listą argumentów, a następnie być może środowiskiem. Próbowałem wiele różnych kombinacji dla function2
(w tym utworzenie do dwóch z tych argumentów null), ale wciąż otrzymuję ten sam (nowy) komunikat o błędzie:
> function2(call("sum", 2, 1), NULL, list(x=NULL), baseenv())
Error: invalid formal argument list for "function"
> function2(call("sum", 2, 1), NULL, list(x=NULL), NULL)
Error: invalid formal argument list for "function"
Ten komunikat o błędzie jest zwracany z C- funkcja CheckFormals()
, które ja również spojrzał w górę:
/* used in coerce.c */
void attribute_hidden CheckFormals(SEXP ls)
{
if (isList(ls)) {
for (; ls != R_NilValue; ls = CDR(ls))
if (TYPEOF(TAG(ls)) != SYMSXP)
goto err;
return;
}
err:
error(_("invalid formal argument list for \"function\""));
}
nie jestem biegły w języku C w ogóle, więc od tej chwili nie jestem pewien, co robić dalej.
Więc to są moje zaktualizowane pytania:
- Dlaczego
function
ifunction2
nie zachowują się w ten sam sposób? Dlaczego muszę wywołaćfunction2
, używając innej składni, gdy są uznane za identyczne w R? - Jakie są właściwe argumenty, aby
function2([arguments])
rzeczywiście zdefiniować funkcję?
Nie sądzę, można to zrobić dokładnie w ten sposób. Można użyć operatora infiksów między 'addFunction (description =" Add one ", x)' i treścią funkcji. Operator infiksowy nazwałby wówczas 'body <-'. Ale prawdopodobnie zdefiniowałbym funkcję konstruktora, która ma argumenty funkcji i treść jako parametry. – Roland
Ewentualnie większą metodą R-ish byłoby stworzenie funkcji "description <-", a następnie wykonaj 'foo = function (x) {x * 2}; description (x) = "doubler" ' – Spacedman
Dzięki wam :) Obie są zdecydowanie dobrymi rozwiązaniami pod względem funkcjonalności. Ale z upartego punktu widzenia, naprawdę chciałbym to zrobić dokładnie tak, jak definicja funkcji. Ktoś kiedyś powiedział mi, że R to naprawdę tylko listy i funkcje, ale najwyraźniej to nie jest prawda ... – AHP