2016-03-31 4 views
-1

ich in der folgenden Konstruktion interessiert bin:Wie Kette, die eine Liste von binären Funktionen

Nehmen wir an, ich eine Liste von n binären Funktionen und einen Vektor von n+1 Argumente haben. Für Argumente können aus Gründen

flist = c(`+`,`-`) 
args = 1:3 

Was verwenden würde ich die folgende Funktionsaufruf f2 (f1 (x1, x2), x3), dh in diesem Beispiel

`-`(`+`(1,2),3) 

wo der Rückgabewert zu tun ist, zu erstellen der Vektor der kumulierten Ergebnisse wird

[1] 3 0 

ich habe eine Lösung der Form

f = function(x,op,res = NULL){ 
    if(is.null(res)){ 
    res = op[[1]](x[1],x[2]) 
    x = x[-1] 
    } else{ 
    res = c(res,op[[1]](res[length(res)],x[1])) 
    } 
    if(length(op) == 1) res 
    else f(x[-1],op[-1],res) 
} 

, so dass ist gibt die richtige Antwort

f(x,flist) 
[1] 3 0 

aber es fühlt sich nicht besonders R wie oder elegant. Gibt es einen besseren Weg, dies zu tun? Ich vermute, dass meine Implementierung auch nicht die effizienteste ist, und daher wäre auch alles, was effizienter ist, von Interesse.

Hat jemand irgendwelche Ideen?

Alternativ, wenn die Anforderung nach kumulativen Antworten zu entspannen, d. H. Nur die endgültige Antwort 0 zurückgegeben wurde, gibt es eine schöne R Möglichkeit, dies zu tun? Ich weiß, dass ich meine f ändern könnte, um mit dieser Alternative umzugehen, aber wenn es einen Weg gibt, dies bereits zu tun, würde ich gerne über beide Optionen hören.

Edit:

Ein Kommentar vorgeschlagen, eine for Implementierung Schleife, so dass wir

falt = function(x,op){ 
    res = numeric(length(op)) 
    res[1] = op[[1]](x[1],x[2]) 
    for(i in 2:length(res)) res[i] = op[[i]](res[i-1],x[i+1]) 
    res 
} 

haben könnte, die effizienter macht trainieren. Aber ich denke immer noch, dass es einen besseren Weg geben muss, dies zu tun.

+0

Warum verwenden Sie Rekursion und wachsen 'res'? Verwenden Sie einfach eine "for" -Schleife und weisen Sie res die endgültige Größe zu. – Roland

+0

Es gab keinen besonderen Grund dafür, ich lerne gerade Scala und so ist mir die Rekursion sehr wichtig. Eine Implementierung, die eine For-Schleife verwendet, funktioniert in Ordnung und ist schneller, und in R ist die tiefe Rekursion schlecht, also ein vernünftiger Vorschlag. Es fühlt sich so an, als ob eine nette Reduktionsoperation dafür existieren sollte. Persönlich meckern, aber ich bin auch kein Fan von 'for' Loops in R aufgrund seiner funktionalen und vektorisierten Natur. Ich hatte auch vorübergehend die Torheit der wachsenden Objekte in R vergessen. – jamieRowen

+0

Möchten Sie die Reduce-Dokumentation ausprobieren? Suche nach Iterate ... es könnte helfen – chinsoon12

Antwort

2

Wenn Ihre Funktionen bereits in curried Form sind, dann ist es viel einfacher

comp <- function (f) function (g) function (x) f(g(x)) 

comp2 <- comp (comp) (comp) # if this is confusing, details later 

add <- function (x) function (y) y + x 
mult <- function (x) function (y) y * x 

comp2 (mult) (add) (3) (4) (5) 
# 5 * (4 + 3) 
# 5 * 7 
# 35 

Weil alles curried ist, können Sie so viele Argumente anwenden können, wie Sie wollen, dann den Rest später

compute <- comp2 (mult) (add) 
compute (5) (6) (7) 
# 7 * (6 + 5) 
# 7 * 30 
# 210 
gelten

Wenn Sie über eine Liste von Binärfunktionen verfügen, können Sie eine Linksfaltung (oder "Reduzieren") verwenden, um eine gesamte Sequenz zu erstellen

identity <- function(x) x 
comp <- function (f) function (g) function (x) f(g(x)) 
comp2 <- comp (comp) (comp) 
uncurry <- function (f) function (x,y) f(x)(y) 
reduce <- function(f) function(y) function (xs) Reduce(uncurry(f), xs, y) 
comp2All <- reduce (comp2) (identity) 

# some binary functions to use in our sequence 
sub <- function (x) function (y) y - x 
add <- function (x) function (y) y + x 
mult <- function (x) function (y) y * x 

# create a sequence of N binary functions 
compute <- comp2All (list(mult, sub, mult, add)) 

# apply the computation to N+1 args 
compute (3) (4) (5) (100) (0.2) 
# 0.2 * (100 - (5 * (3 + 4)) 
# 0.2 * (100 - (5 * 7)) 
# 0.2 * (100 - 35) 
# 0.2 * 65 
# => 13 

Sie mögen wahrscheinlich nicht gerne die Berechnung ein Argument gleichzeitig anwenden ...

# this kind sucks, right? 
compute (3) (4) (5) (6) (7) 

Nun, wir können das Problem beheben, indem eine Funktion zu machen, die eine curried Funktion auf eine Liste oder Argumente gilt

capply <- reduce (identity) 
capply (compute) (3:7) 
# 7 * (6 - (5 * (4 + 3))) 
# 7 * (6 - (5 * 7)) 
# 7 * (6 - 35) 
# 7 * -29 
# => -203 

Wenn Ihre binären Funktionen sind noch nicht curried:

Sie können eine Binärfunktion einfach mit curry2

curren
curry2 <- function(f) function(x) function(y) f(x,y) 
curry2 (`+`) (3) (4) 
# => 7 

Wenn Sie eine ganze Liste von binären Funktionen haben, die nicht bereits curried sind, können Sie die gesamte Liste map

map <- function (f) function (xs) Map(f,xs) 
compute <- comp2All (map (curry2) (list (`*`, `+`, `*`, `+`))) 
compute (3) (4) (5) (6) (7) 
# 7 * (6 + (5 * (3 + 4))) 
# 7 * (6 + (5 * 7)) 
# 7 * (6 + 35) 
# 7 * 41 
# => 287 

comp vs comp2 Transformation verwenden

Weil Sie wollen Um eine Sequenz von binären Funktionen zu erstellen, habe ich

verwendet
comp2All <- reduce (comp2) (identity) 

Wenn Sie eine Folge von einstelligen Funktionen wollen, würden Sie

compAll <- reduce (comp) (identity) 

verwenden, was ist comp2?

könnte Die comp2 Definition scheint verwirrend, aber es Ihnen

nicht zulassen, verwirrt
comp2 <- comp (comp) (comp) 

Wenn wir dies erweitern waren wir zunächst

comp2 <- function (x) comp(comp(x)) 

Bei der weiteren Expansion sehen würden (Dies ist eine lustige Übung für Sie), sollten Sie sehen

comp2 <- function (f) function (g) function (x) function (y) f(g(x)(y)) 

Was kann sein leicht zu verstehen, wie die Zusammensetzung der einstelligen Funktion, f, mit binärer Funktion, g

+0

Ich mag das Aussehen im Allgemeinen und kann sehen, wie es auf mein minimales Beispiel angewendet werden könnte. Wie würde man dies auf einen allgemeinen Fall ausweiten, sagen wir, die Liste der 'n'-Funktionen und' n + 1'-Argumente kommt von einem anderen Mechanismus. Wenn "n" groß ist, möchte ich nicht befürchten, was die Funktionen sind oder wie lange die Liste wirklich ist. – jamieRowen

+0

Sind alle Funktionen binäre Funktionen? Sind sie schon in Curry oder kannst du sie vorher curren? – naomik

+0

Alle Funktionen hier sind binäre ja, aber nicht Curry-Form im Allgemeinen. Ich habe dein letztes Beispiel ausprobiert, indem ich 'f' und' g' für definierte binäre Funktionen mit den Argumenten 'x' und' y' einsetze, aber wie oben beschrieben bekomme ich einen fehlenden Argumentfehler. Ich sehe derzeit auch nicht, wie ich es schaffen würde, etwas wie "comp2 (Curry (flist [[1]])) (Curry [flist [[2])) (.....") einzugeben, was ich eindeutig möchte Vermeiden Sie. – jamieRowen

Verwandte Themen