Ich füge hier nur meine Meinung hinzu, während ich einige Hilfsfunktionen für die funktionale Programmierung baue.
Meiner Erfahrung nach ist es zu einfach, sich ein wenig mitreißen zu lassen und eine übermäßig komplizierte Lösung zu entwickeln, die sowohl etwas langsam als auch etwas umständlich zu verstehen ist.
Hier ist also eine schnelle und einfache Implementierung für Map/Filter/Reduction, die fast so schnell ist wie integrierte Methoden (entweder Schleifen oder direkte integrierte Aufrufe) und den zusätzlichen Vorteil hat, dass die Funktion Expr(...) nicht verwendet werden muss sie zu nutzen.
map = Function( {inputs/* list, function*/},
/* uses a single underscore _ as the wild-card */
{__i__, __result__, __list__, _},
__result__ = {};
__list__ = Arg( inputs, 1 );
Eval(
Substitute(
Expr(
For( __i__ = N Items( __list__ ), __i__ > 0, __i__--,
Insert Into( __result__, _ = __list__[__i__]; __function__, 1 )
)
)
,
Expr( __function__ ), Arg( inputs, 2 )
);
);
__result__
);
reduce = Function( {inputs/*list, function, seed = 0*/},
/* uses a single underscore for current item, 'acc' for current value */
{__i__, __list__, _, acc = 0},
If( N Items( inputs ) == 3,
acc = Arg( inputs, 3 )
);
__list__ = Arg( inputs, 1 );
Eval(
Substitute(
Expr(
For( __i__ = N Items( __list__ ), __i__ > 0, __i__--,
_ = __list__[__i__];
acc = __function__
)
)
,
Expr( __function__ ), Arg( inputs, 2 )
)
);
acc
);
filter = Function( {inputs/*list, filter = _*/},
/* uses a single underscore _ for current item */
{__i__, __list__, __result__, _filter_, _},
__result__ = {};
If( N Items( inputs ) == 1,
_filter_ = Expr( _ )
,
_filter_ = Arg( inputs, 2 )
);
__list__ = Arg( inputs, 1 );
Eval(
Substitute(
Expr(
For( __i__ = N Items( __list__ ), __i__ > 0, __i__--,
_ = __list__[__i__];
If( __filter__,
__result__[N Items(__result__) + 1] = _
)
)
)
,
Expr( __filter__ ), Name Expr( _filter_ )
);
);
Reverse( __result__ )
);
filter({{1, 0, 2, Empty(), 3, .}});
lst = repeat( {{1, "a"}, {2, "b"}, {3, "c"}, {4, "d"}}, 10000 );
start = HP Time();
a = filter({lst, _[1] < 3} );
end = HP Time();
Write( "\!NMy Method: ", (end - start) / 1000000 );
reduce( {{"a", "b", "c"}, Uppercase( _ || acc ), seed = ""} );
list = Repeat( {Random Integer( 1, 100000000 )}, 50000 );
start = HP Time();
Show( reduce( {list, If( _ > acc, _, acc)} ) );
end = HP Time();
Write( "\!NMy Method: ", (end - start) / 1000000 );
start = HP Time();
Show( Max( list ) );
end = HP Time();
Write( "\!NBuiltin Method: ", (end - start) / 1000000 );
start = HP Time();
Show( reduce( {list, acc + _} ) );
end = HP Time();
Write( "\!NMy Method: ", (end - start) / 1000000 );
start = HP Time();
Show( Sum( list ) );
end = HP Time();
Write( "\!NBuiltin Method: ", (end - start) / 1000000 );
strings = Repeat( {"abcdefg"}, 1e5 ); // about a second
start = Tick Seconds();
substrings1 = map( {strings, Substr( _, 1 + 2, 1 )} );
stop = Tick Seconds();
Write( "\!n applyfunction time: ", stop - start );
start = Tick Seconds();
substrings2 = {};
For( i = N Items( strings ), i > 0, i--,
Insert Into( substrings2, Substr( strings[i], 3, 1 ), 1 )
);
stop = Tick Seconds();
Write( "\!n JSL loop time: ", stop - start );
If( substrings1 == substrings2,
Write( "\!nOK" ),
Write( "\!noops" )
);Wenn ich dies auf meinem (ziemlich langsamen) Laptop ausführe, erhalte ich die folgende Ausgabe:
/*<BR />My Method: 0.152577
reduce({list, If(_ > acc, _, acc)}) = 99994198;
My Method: 0.056897
Max(list) = 99994198;
Builtin Method: 0.024798
reduce({list, acc + _}) = 2495626010963;
My Method: 0.04259
Sum(list) = 2495626010963;
Builtin Method: 0.007765
applyfunction time: 0.166666666656965
JSL loop time: 0.0500000000029104
OK<BR />*/Beachten Sie, dass das Finden der maximalen Anzahl in einer Bitliste (50.000 Einträge) mithilfe der Reduzierungsfunktion nur etwa 2x so lange dauerte wie der integrierte Max()-Aufruf, während die Summe derselben Liste etwa 6x langsamer war.Angesichts der zusätzlichen Flexibilität der Funktionen „Zuordnen“, „Reduzieren“ und „Filter“ sind das ziemlich gute Zahlen.
Ich entschuldige mich für die Verzögerung bei diesem Artikel. Ich war unter anderem mit den Vorbereitungen sehr beschäftigt ein Vortrag über Functional Data Explorer bei JMP Discovery Summit . Es scheint, dass die regulären Mitwirkenden gleichermaßen beschäftigt waren, da ich für diese Herausforderung nur zwei Beiträge erhalten habe, die nicht von Mitarbeitern stammen.
Hier sind die Gesamtpunktzahlen nach der 5. Challenge:
Apropos Discovery Summit: Mein Kollege Stan Koprowski organisiert den JMP-Skript-Unsession . Für weitere Details folgen Sie dem Link; Wir würden uns freuen, Sie dort zu sehen.
In Herausforderung 5 habe ich Sie gebeten, Metafunktionen, Funktionen von Funktionen, zu schreiben. Meine Motivation bestand darin, Funktionen, die nur auf eine einzelne Zahl oder Zeichenfolge angewendet werden können, zu erweitern und sie so zu erweitern, dass sie auf ihre zusammengesetzten Werte, Vektoren und Listen angewendet werden können. Ich habe festgestellt, dass diese Art von Operation recht regelmäßig vorkommt und häufig mit einer Datentabellenformel implementiert werden kann, wenn noch keine JSL-Funktion vorhanden ist. Ich wollte, dass diese Funktionalität außerhalb der Datentabelle verfügbar ist.
Die Punkte 1 und 2 hätten relativ einfach sein sollen. Bei der Arbeit mit Vektoren ist es hilfreich, sich auf das zu verlassen, was JSL bereits durch bereits vorhandene JSL-Funktionen oder Matrixoperationen implementiert hat. Der einfachste Ansatz zur Berechnung der mittleren absoluten Abweichung (MAD) ist die eine Codezeile:
Median(Abs( inVec - Median( inVec ) ) )
Wo inVec ist der Eingabevektor. Diese Lösung hängt von der Kenntnis ab, dass der Median korrekt aus einer integrierten JSL-Funktion generiert werden kann. Der Code folgt direkt aus der Definition von MAD.
Die Berechnung des getrimmten Mittelwerts ist etwas schwieriger und bietet einen Einblick in die Bedeutung einer präzisen Funktionsdefinition (dazu später mehr). Ein üblicher Ansatz bestand darin, den Durchschnitt der Werte zu berechnen, die zwischen den zugehörigen Quantilen liegen pctLow Und pctHigh , Wo pctLow Und pctHi sind die Argumente, wie sie im Beitrag zu Herausforderung 5 definiert sind. Zum Beispiel:
loQ = Quantil( pctLow , inVec );
hiQ = Quantil(1- pctHi , inVec );
Bedeuten( inVec [Loc(loQ < inVec < hiQ)]);
Diese Lösung schneidet jedoch ab nicht mehr als pctLow von den niedrigen Werten und nicht mehr als pctHi vom hohen Wert. Aus diesem Grund kann der Gesamtprozentsatz, der gekürzt wird, geringer sein als pcLow + pctHi aber nie mehr . Dieser Ansatz steht im Widerspruch zu dem in der Verteilung Plattform, die nicht nur an beiden Enden gleichmäßig trimmt, sondern, was noch wichtiger ist, den Gesamtprozentsatz des Trimmens garantiert mindestens der angeforderte Betrag (und möglicherweise mehr). Der folgende Code erzeugt Ergebnisse, die dieser Definition entsprechen:
loRank = Decke( pctLo *N Zeile( inVec ));
hiRank = Decke((1- pctHi )*N Zeile( inVec ));
goodVals = (loRank < Ranking( inVec ) <= hiRank);
goodVals`* inVec /Sum(goodVals);
Für Punkt 3 suchte ich nach einer Funktion, die alle Vorkommen einer bestimmten Zeichenfolge in jedem Element einer Liste von Zeichenfolgen findet. Wenn die angegebene Zeichenfolge „a“ wäre und die Liste der Zeichenfolgen
{„calcaneoastragalar“, „Fuchs“, „Baby“}
Der zurückgegebene Wert wäre
{[2, 5, 9, 13, 15, 17],.,[2]}
Ein korrekt formuliertes Beispiel hätte geholfen, die Funktion genauer zu definieren. Meine Lösung mit Looping:
listContains = Function({listIn,valueString},{i,outList,nextMat,start},
outList = Repeat({.},N Items(listIn));
For(i=1,i<=N Items(listIn),i++,
nextMat = [];
start = 1;
While(start = Contains(listIn[i],valueString,start),
nextMat = nextMat |/ start;
start+=Length(valueString);
);
If(N Row(nextMat) > 0,outList[i] = nextMat,outList[i]=.);
);
outList;
);
Die Verarbeitung einer Liste von 1 Million zufällig ausgewählten Wörtern aus dem Wörterbuch in Herausforderung 1 dauert zwischen 2 und 3 Sekunden.
Mein Anstoß für applyFunction bestand darin, Funktionen zu replizieren, die ich in anderen Programmiersprachen verwendet hatte, wobei ich eine Funktion auf eine aggregierte Datenstruktur (normalerweise eine Liste von Zeichenfolgen oder eine Matrix) anwenden (oder abbilden) konnte. Ich wollte in der Lage sein, etwas zu verwenden wie:
Länge( listOfStrings )
um die Länge jedes Elements in der Liste zu ermitteln, anstatt eine Schleifen- oder Spaltenformel zu verwenden.
Nachdem ich die Herausforderung gepostet hatte, wurde mir klar, dass es Feinheiten gab, die ich nicht berücksichtigt hatte, wie zum Beispiel Situationen, in denen die Funktion auf die einzelnen Elemente in der Eingabeliste oder die gesamte Eingabeliste angewendet werden konnte. Außerdem habe ich nichts darüber gesagt, ob auf jedes Listenelement unterschiedliche Funktionsargumente angewendet werden könnten.
Vor diesem Hintergrund und im Einklang mit den Kommentaren im Challenge 5-Eintrag wurde die Implementierung von applyFunction Ich habe vier Argumente angegeben:
listIn – eine Liste mit einer oder mehreren Zahlen, Zeichenfolgen, Vektoren oder Zeichenfolgenlisten.
fn – der in Anführungszeichen gesetzte Name der Funktion, auf die angewendet werden soll listIn . Die Funktion kann integriert oder vom Benutzer bereitgestellt werden.
argList – eine Liste von Argumenten zu fn die danach erscheinen listIn oder eine Liste von Argumentlisten. Wenn eine Liste von Listen bereitgestellt wird, muss diese die gleiche Kardinalität haben wie listIn .
Flagge - 1 wenn fn wird auf jedes Element der Liste angewendet, 0, wenn es auf die gesamte Liste angewendet wird.
Ein einfacher, aber allgemeiner Ansatz wäre, die Funktionen mithilfe von Ausdrücken und Schleifen zu erstellen. Im Pseudocode:
If flag == 1 then
If argList is a list of lists then
For each item in listIn/argList, build an expression for fn
Else
For each item in listIn, build an expression for fn using the same items from argList
Else flag == 0
Build one expression for listIn and argList
Den Code für diesen Ansatz finden Sie in applyFunction (loops).JSL . @David_Burnham bietet einen rekursiven Ansatz in Challenge_5_burnham.JSL . Es ist zwar elegant (wer mag keine Rekursion?), stößt jedoch auf Probleme, wenn die Rekursion zu tief geht.
Versuche es zu machen applyFunction Eine schleifenlose Funktion erwies sich als ziemliche Herausforderung. Das Hauptproblem besteht darin, die Anzahl der Artikel nicht zu kennen argList a priori (oder, noch schwieriger, den Umgang mit Situationen, in denen sich die Zahl ändert).Zuerst hatte ich gehofft, dass ich dies durch den geschickten Einsatz der Ausdrucksbewertung erreichen könnte. Wenn die Funktion beispielsweise nur ein Argument benötigt, würde Folgendes funktionieren:
applyFunctionSimple = Function({listIn,fn,argList},{i,fnExpr,outList},
i = 1;
outList = {};
outList[1::N Items(listIn)] = Substitute(
Expr(func(in,arg)),
Expr(func),Parse(fn),
Expr(in), Expr(listIn[i++]),
Expr(arg), argList[1]
);
Eval List(outList)
);
Die Magie von Ersatz beginnt mit der Erstellung einer Längenliste N Artikel( listIn ) wobei jedes Element der Liste der Ausdruck ist fn (listIn[i++], arg1 ) , Wo fn Und arg1 sind in den Funktionsaufrufen und angegeben listIn[i++] ist wörtlich. Wann Bewertungsliste wird angewendet auf outList , jedoch jeder ich erhält einen separaten sequentiellen Wert und das Listenelement wird ausgewertet, wodurch die gewünschten Ergebnisse erzielt werden. Diese Herangehensweise an die Verwendung von Ausdrücken habe ich von gelernt @Joseph Morgan wer, zusammen mit @drewfoglia / @EvanMcCorkle haben hervorragende Discovery-Tutorials zu diesem Thema gegeben. Die Tutorials finden Sie hier Hier Und Hier .
Leider konnte ich nicht herausfinden, wie ich diesen Ansatz vollständig anwenden kann, und musste mich auf eine Technik verlassen, die ich aufgrund ihrer Ineffizienz und Uneleganz zu vermeiden versuche: eine allgemeine Funktion über String-Manipulation zu erstellen und dann die Ausdrucksauswertung zu verwenden, um die Ausgabe zu erhalten . Mein Code ist drin applyFunction (keine Schleifen).JSL . Während dieser Code einen Großteil des Flairs von behält applyFunctionSimple , Regex wird verwendet, um die geschweiften Klammern aus dem Ausdruck zu entfernen argList . Glücklicherweise, @Craige_Hales bietet eine rein ausdrucksbasierte Lösung in Craige 5.JSL . Da es mir vor späteren Klarstellungen zu Herausforderung 5 zugesandt wurde, gilt es fn zu jedem Element in listIn nur. Darüber hinaus werden für jeden Funktionsaufruf die gleichen Argumente verwendet. Sowohl diese als auch meine Lösung haben das gleiche Problem, dass sie langsamer sind als unsere schleifenbasierten Lösungen (zwischen 2 und 5).
Hoffentlich macht das alles Sinn. Wenn nicht, oder wenn Sie weitere Erkenntnisse haben, posten Sie bitte unten Ihre Kommentare.