8 Stimmen

Festlegen von Einstellungen für Optionen

Wie kann man die gültigen Einstellungen für eine Option explizit angeben? Nehmen Sie zum Beispiel dieses Beispiel

Options[myFunc] = {opt1 -> "SomeString"};
myFunc[OptionsPattern[]] := Print[OptionValue[opt1]];

myFunc gibt den Wert der Option aus. Wenn wir auswerten myFunc[opt1 -> {1, 2}] dann druckt es {1, 2} . Diese Funktion druckt im Wesentlichen alles, was Sie auf opt1 . Meine Frage ist, wie kann ich sicherstellen, dass meine Funktion nur eine bestimmte Anzahl von Werten für opt1 . Wir können mit etwas Einfachem beginnen wie einer String und ein Integer .

Um eine bessere Vorstellung von dem Verhalten zu bekommen, das wir erwarten würden, wenn wir die falschen Werte für opt1 können wir uns ansehen, was passiert, wenn wir die falschen Werte für PlotRange in der Funktion Plot .

enter image description here

In dem Beispiel auf dem Bild habe ich absichtlich falsche Werte für die PlotRange und gab eine Meldung aus, in der die richtige Art von Werten für die jeweilige Option angegeben wurde. Es scheint, dass es PlotRange schließlich den Standardwert und damit die Rückgabe der Graphics Objekt.

In diesem einfachen Beispiel möchten wir etwas wie folgt erhalten:

myFunc::sometag : Value of option opt1 -> `1` is not a string or integer.

Wie kann dies erreicht werden?

7voto

Leonid Shifrin Punkte 22309

Eine unkomplizierte Lösung

Hier ist ein einfacher Weg:

In[304]:= ClearAll[myFunc];
Options[myFunc] = {opt1 -> "SomeString"};
myFunc::badopt = "Value of option opt1 -> `1` is not a string or integer.";
myFunc[OptionsPattern[]] :=
   With[{val = OptionValue[opt1]},
      With[{error = ! MatchQ[val, _String | _Integer]},
         If[error, Message[myFunc::badopt , val]];
         (Print[val] /; ! error)]];

Zum Beispiel:

In[308]:= myFunc[opt1 -> 1]

During evaluation of In[308]:= 1

In[309]:= myFunc[opt1 -> {1, 2}]

During evaluation of In[309]:= myFunc::badopt: 
Value of option opt1 -> {1,2} is not a string or integer.

Out[309]= myFunc[opt1 -> {1, 2}]

Mit benutzerdefinierten Zuweisungsoperatoren allgemein machen

Wir können die Tatsache nutzen, dass OptionValue innerhalb einer Funktion arbeitet mit einem einzigen Argument, das ein Optionsname ist, um die mühsame Fehlerprüfung zu umgehen. Dies ist durch die Verwendung von mma-Meta-Programmierfunktionen möglich. Hier ist der Code für einen benutzerdefinierten Zuweisungsoperator:

ClearAll[def, OptionSpecs];
SetAttributes[def, HoldAll];
def[f_[args___] :> body_,OptionSpecs[optionSpecs : {(_ ->  {_, Fail :> _}) ..}]] :=
  f[args] :=
    Module[{error = False},
      Scan[
        With[{optptrn = First[# /. optionSpecs], optval = OptionValue[#]},
          If[! MatchQ[optval, optptrn ],
             error = True;
             Return[(Fail /. Last[# /. optionSpecs])[optval]]]] &, 
        optionSpecs[[All, 1]]
      ];
      body /; ! error];

Dabei wird eine Funktionsdefinition als Regel verwendet f_[args___]:>body_ und auch die Spezifikationen für die zulässigen Optionseinstellungen und die Aktionen, die bei Erkennung eines Fehlers in einer der übergebenen Optionen durchgeführt werden sollen. Anschließend injizieren wir den Code zur Fehlerprüfung ( Scan ), bevor der Body ausgeführt wird. Sobald die erste Option mit ungeeigneter Einstellung gefunden wird, wird das Fehlerflag auf True und den Code, der in der Datei Fail:>code_ Teil der Spezifikationen für diese Option. Das Muster der Optionsspezifikation (_ -> {_, Fail :> _}) sollte lauten (optname_ -> {optpattern_, Fail :> onerror_}) donde optname ist ein Optionsname, optpattern ist ein Muster, dem der Optionswert entsprechen muss, und onerror ist beliebiger Code, der ausgeführt wird, wenn ein Fehler entdeckt wird. Beachten Sie, dass wir RuleDelayed sur Fail:>onerror_ um eine vorzeitige Auswertung des Codes zu verhindern. Beachten Sie b.t.w., dass die OptionSpecs Wrapper wurde nur aus Gründen der Lesbarkeit hinzugefügt - es ist ein völlig unbenutztes Symbol, dem keine Regeln zugeordnet sind.

Hier ist ein Beispiel für eine Funktion, die mit diesem benutzerdefinierten Zuweisungsoperator definiert wurde:

ClearAll[myFunc1];
Options[myFunc1] = {opt1 -> "SomeString", opt2 -> 0};
myFunc1::badopt1 = "Value of option opt1 -> `1` is not a string or integer.";
myFunc1::badopt2 =  "Value of option opt2 -> `1` is not an integer.";
def[myFunc1[OptionsPattern[]] :> 
       Print[{OptionValue[opt1], OptionValue[opt2]}],
    OptionSpecs[{
       opt1 -> {_Integer | _String, 
           Fail :> ((Message[myFunc1::badopt1, #]; Return[$Failed]) &)},
       opt2 -> {_Integer,
           Fail :> ((Message[myFunc1::badopt2, #]; Return[$Failed]) &)}}
]];

Hier sind Beispiele für die Verwendung:

In[473]:= myFunc1[]
During evaluation of In[473]:= {SomeString,0}

In[474]:= myFunc1[opt2-> 10]
During evaluation of In[474]:= {SomeString,10}

In[475]:= myFunc1[opt2-> 10,opt1-> "other"]
During evaluation of In[475]:= {other,10}

In[476]:= myFunc1[opt2-> 1/2]
During evaluation of In[476]:= myFunc1::badopt2: 
Value of option opt2 -> 1/2 is not an integer.

Out[476]= $Failed

In[477]:= myFunc1[opt2-> 15,opt1->1/2]
During evaluation of In[477]:= myFunc1::badopt1: 
Value of option opt1 -> 1/2 is not a string or integer.

Out[477]= $Failed

Automatisches Hinzufügen von Optionsprüfungen zu bereits definierten Funktionen

Vielleicht interessieren Sie sich auch für ein Paket, das ich geschrieben habe, um die übergebenen Optionen zu testen: CheckOptions , verfügbar aquí . Das Paket wird mit einem Notizbuch geliefert, das seine Verwendung illustriert. Es analysiert die Definitionen Ihrer Funktion und erstellt zusätzliche Definitionen, um die Optionen zu überprüfen. Der derzeitige Nachteil (abgesehen von der Erzeugung neuer Definitionen, die nicht immer angemessen sind) ist, dass es nur die ältere Art der Definition von Optionen durch OptionQ Prädikat (ich habe es noch nicht aktualisiert, um die OptionValue - OptionsPattern . Zur Veranschaulichung der Funktionsweise gebe ich hier einen Teil des Begleithefts wieder:

Betrachten Sie eine Modellfunktion:

In[276]:= ClearAll[f];
f[x_, opts___?OptionQ]:= x^2;
f[x_, y_, opts___?OptionQ] := x + y;
f[x_, y_, z_] := x*y*z;

Angenommen, wir wollen eine Fehlermeldung zurückgeben, wenn eine Option FontSize wird an unsere Funktion übergeben:

In[280]:= 
f::badopt="Inappropriate option";
test[f,heldopts_Hold,heldArgs_Hold]:=(FontSize/.Flatten[List@@heldopts])=!=FontSize;
rhsF[f,__]:=(Message[f::badopt];$Failed);

Wir fügen die Option - Überprüfung von Definitionen - hinzu:

In[283]:= AddOptionsCheck[f,test,rhsF]
Out[283]= {HoldPattern[f[x_,opts___?OptionQ]/;test[f,Hold[opts],Hold[x,opts]]]:>
                rhsF[f,Hold[opts],Hold[x,opts]],
           HoldPattern[f[x_,y_,opts___?OptionQ]/;test[f,Hold[opts],Hold[x,y,opts]]]:>
                rhsF[f,Hold[opts],Hold[x,y,opts]],
           HoldPattern[f[x_,opts___?OptionQ]]:>x^2,
           HoldPattern[f[x_,y_,opts___?OptionQ]]:>x+y,
           HoldPattern[f[x_,y_,z_]]:>x y z}

Wie Sie sehen können, rufen wir einmal AddOptionsCheck erzeugt sie neue Definitionen. Sie nimmt den Funktionsnamen, die Testfunktion und die Funktion, die im Fehlerfall ausgeführt werden soll. Die Testfunktion akzeptiert den Namen der Hauptfunktion, die an sie übergebenen Optionen (verpackt in Hold ), und Nicht-Options-Argumente, die ihm übergeben werden (ebenfalls verpackt in Hold ). Aus den generierten Definitionen können Sie ersehen, was sie bewirkt.

Wir prüfen nun verschiedene Eingaben:

In[284]:= f[3]
Out[284]= 9

In[285]:= f[3,FontWeight->Bold]
Out[285]= 9

In[286]:= f[3,FontWeight->Bold,FontSize->5]
During evaluation of In[286]:= f::badopt: Inappropriate option
Out[286]= $Failed

In[289]:= f[a,b]
Out[289]= a+b

In[290]:= f[a,b,FontWeight->Bold]
Out[290]= a+b

In[291]:= f[a,b,FontWeight->Bold,FontSize->5]
During evaluation of In[291]:= f::badopt: Inappropriate option
Out[291]= $Failed

In[292]:= OptionIsChecked[f,test]
Out[292]= True

Bitte beachten Sie, dass die Testfunktion auf Folgendes testen kann willkürlich Bedingung mit dem Funktionsnamen, den übergebenen Argumenten und den übergebenen Optionen. Es gibt ein weiteres Paket von mir, PackageOptionChecks die eine einfachere Syntax hat, um speziell die r.h.s. von Optionen zu testen, und die auch auf das gesamte Paket angewendet werden kann, ist auf derselben Seite verfügbar. Ein praktisches Beispiel für seine Verwendung ist ein weiteres Paket, PackageSymbolsDependencies dessen Funktionsoptionen "geschützt" sind durch PackageOptionChecks . Auch, PackageOptionChecks kann auf Funktionen in Global' Kontext ist es auch nicht notwendig, ein Paket zu haben.

Eine weitere Einschränkung der aktuellen Implementierung ist, dass wir die Funktion nicht unbewertet zurückgeben können. Eine detailliertere Diskussion finden Sie im Notebook, das dem Paket beiliegt. Wenn genügend Interesse daran besteht, werde ich in Erwägung ziehen, das Paket zu aktualisieren, um einige der genannten Einschränkungen zu beseitigen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X