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.