wolfram mathematica - programas - ¿Cuáles son los beneficios de cambiar de Regla y/. a OptionsPattern[] y OptionValue en una aplicación grande?
graficar funciones en wolfram mathematica (4)
Parece que confiar en el comparador de patrones produce una ejecución más rápida que utilizando el PatternTest
ya que este último implica la invocación del evaluador. De todos modos, mis tiempos indican que se pueden lograr algunos aumentos de velocidad, pero no creo que sean tan críticos como para incitar a la re-factorización.
In[7]:= f[x__, opts : OptionsPattern[NIntegrate]] := {x,
OptionValue[WorkingPrecision]}
In[8]:= f2[x__, opts___?OptionQ] := {x,
WorkingPrecision /. {opts} /. Options[NIntegrate]}
In[9]:= AbsoluteTiming[Do[f[1, 2, PrecisionGoal -> 17], {10^6}];]
Out[9]= {5.0885088, Null}
In[10]:= AbsoluteTiming[Do[f2[1, 2, PrecisionGoal -> 17], {10^6}];]
Out[10]= {8.0908090, Null}
In[11]:= f[1, 2, PrecisionGoal -> 17]
Out[11]= {1, 2, MachinePrecision}
In[12]:= f2[1, 2, PrecisionGoal -> 17]
Out[12]= {1, 2, MachinePrecision}
Los viejos hábitos son duros y me doy cuenta de que he estado usando opts___Rule
patrones y las construcciones como esta thisoption /. {opts} /. Options[myfunction]
thisoption /. {opts} /. Options[myfunction]
thisoption /. {opts} /. Options[myfunction]
en el paquete muy grande que estoy desarrollando actualmente. El "Libro de cocina de Mathematica" de Sal Manango me recuerda que la forma posterior a la versión 6 de hacerlo es con opts:OptionsPattern[]
y OptionValue[thisoption]
. El paquete requiere la versión 8 de todos modos, pero nunca había cambiado la forma en que escribí este tipo de código a lo largo de los años.
¿Vale la pena refactorizar todo eso desde mi forma de hacer las cosas de la versión previa a la 6? ¿Hay rendimiento u otros beneficios?
Saludos
Verbeia
EDITAR: Resumen
Se hicieron muchos puntos positivos en respuesta a esta pregunta, así que gracias (y más uno, por supuesto) a todos. Para resumir, sí, debería refactorizar el uso de OptionValue
y OptionValue
. (NB: OptionsPattern
no OptionPattern
como lo tenía antes!) Hay varias razones por las que:
- Es un toque más rápido (@Sasha)
- Se manejan mejor las funciones donde los argumentos deben estar en
HoldForm
(@Leonid) -
OptionsPattern
comprueba automáticamente que está pasando una opción válida a esa función (FilterRules
seguiría siendo necesario si pasa a una función diferente (@Leonid) - Maneja
RuleDelayed
(:>
) mucho mejor (@rcollyer) - Maneja listas de reglas anidadas sin usar
Flatten
(@Andrew) - Es un poco más fácil asignar múltiples variables locales usando
OptionValue /@ list
lugar de tener múltiples llamadas asomeoptions /. {opts} /. Options[thisfunction]
someoptions /. {opts} /. Options[thisfunction]
someoptions /. {opts} /. Options[thisfunction]
(surgió en comentarios entre @rcollyer y yo)
EDITAR: 25 de julio al principio pensé que la única vez utilizando el /.
la sintaxis aún puede tener sentido si está extrayendo deliberadamente una opción predeterminada de otra función, no a la que realmente se está llamando. Resulta que esto se maneja utilizando la forma de OptionsPattern[]
con una lista de cabezas dentro, por ejemplo: OptionsPattern[{myLineGraph, DateListPlot, myDateTicks, GraphNotesGrid}]
(consulte la sección "Más información" en la documentation ) . Sólo trabajé esto recientemente.
Si bien varias respuestas han enfatizado diferentes aspectos de la forma antigua en comparación con la nueva forma de usar las opciones, me gustaría hacer algunas observaciones adicionales. Las construcciones más nuevas OptionValue
- OptionValue
proporcionan más seguridad que OptionQ
, ya que OptionValue
inspecciona una lista de Opciones globales para asegurarse de que la función conoce la opción pasada. El OptionQ
más OptionQ
parece, sin embargo, más fácil de entender, ya que se basa solo en el patrón estándar y no está directamente relacionado con ninguna de las propiedades globales. Si desea o no esta seguridad adicional provista por cualquiera de estas construcciones depende de usted, pero creo que la mayoría de la gente lo encuentra útil, especialmente para proyectos más grandes.
Una razón por la que estas verificaciones de tipo son realmente útiles es que a menudo las opciones se pasan como parámetros mediante funciones similares a una cadena, se filtran, etc., por lo que sin tales verificaciones, algunos de los errores de coincidencia de patrones serían muy difíciles de detectar, ya que Estaría causando daño "lejos" del lugar de su origen.
En términos del lenguaje central, las construcciones OptionValue
- OptionValue
son una adición al patrón de comparación, y quizás la más "mágica" de todas sus características. No fue necesario semánticamente, siempre y cuando uno esté dispuesto a considerar las opciones como un caso especial de reglas. Además, OptionValue
conecta la coincidencia de patrones con Options[symbol]
, una propiedad global. Entonces, si uno insiste en la pureza del lenguaje, las reglas como en las opts___?OptionQ
parecen más fáciles de entender: no se necesita nada, excepto la semántica estándar de sustitución de reglas para entender esto:
f[a_, b_, opts___?OptionQ] := Print[someOption/.Flatten[{opts}]/.Options[f]]
(Recuerdo que el predicado OptionQ
fue diseñado específicamente para reconocer opciones en las versiones anteriores de Mathematica), mientras que esto:
f[a_, b_, opts:OptionsPattern[]] := Print[OptionValue[someOption]]
Parece bastante mágico. Se vuelve un poco más claro cuando usa Trace
y ve que la forma corta de OptionValue
evalúa a una forma más larga, pero el hecho de que automáticamente determina el nombre de la función de OptionValue
es aún notable.
Hay algunas consecuencias más de que OptionsPattern
sea parte del lenguaje de patrones. Una de ellas son las mejoras de velocidad discutidas por @Sasha. Sin embargo, los problemas de velocidad a menudo se sobreestiman (esto no es para restar valor a sus observaciones), y espero que esto sea especialmente cierto para las funciones con opciones, ya que estas tienden a ser las funciones de nivel superior, que probablemente no tengan Cuerpo trivial, donde se gastará la mayor parte del tiempo de cómputo.
Otra diferencia bastante interesante es cuando se necesita pasar opciones a una función que contiene sus argumentos. Considere un ejemplo siguiente:
ClearAll[f, ff, fff, a, b, c, d];
Options[f] = Options[ff] = {a -> 0, c -> 0};
SetAttributes[{f, ff}, HoldAll];
f[x_, y_, opts___?OptionQ] :=
{{"Parameters:", {HoldForm[x], HoldForm[y]}}, {" options: ", {opts}}};
ff[x_, y_, opts : OptionsPattern[]] :=
{{"Parameters:", {HoldForm[x], HoldForm[y]}}, {" options: ", {opts}}};
Esto esta bien:
In[199]:= f[Print["*"],Print["**"],a->b,c->d]
Out[199]= {{Parameters:,{Print[*],Print[**]}},{ options: ,{a->b,c->d}}}
Pero aquí, nuestra función basada en OptionQ
filtra la evaluación como parte del proceso de coincidencia de patrones:
In[200]:= f[Print["*"],Print["**"],Print["***"],a->b,c->d]
During evaluation of In[200]:= ***
Out[200]= f[Print[*],Print[**],Print[***],a->b,c->d]
Esto no es completamente trivial. Lo que sucede es que el patrón de coincidencia, para establecer un hecho de coincidencia o no coincidencia, debe evaluar la tercera Print
, como parte de la evaluación de OptionQ
, ya que OptionQ
no tiene argumentos. Para evitar la fuga de la evaluación, es necesario utilizar la Function[opt,OptionQ[Unevaluated[opt]],HoldAll]
en lugar de OptionQ
. Con OptionsPattern
no tenemos este problema, ya que el hecho de la coincidencia se puede establecer de manera puramente sintáctica:
In[201]:= ff[Print["*"],Print["**"],a->b,c->d]
Out[201]= {{Parameters:,{Print[*],Print[**]}},{ options: ,{a->b,c->d}}}
In[202]:= ff[Print["*"],Print["**"],Print["***"],a->b,c->d]
Out[202]= ff[Print[*],Print[**],Print[***],a->b,c->d]
Entonces, para resumir: creo que elegir un método sobre otro es en gran medida una cuestión de gustos: cada uno se puede utilizar de manera productiva y también se puede abusar de cada uno. Estoy más inclinado a usar la forma más nueva, ya que proporciona más seguridad, pero no excluyo que existan algunos casos de esquina en los que te sorprenderá, mientras que el método más antiguo es semánticamente más fácil de entender. Esto es algo similar a la comparación de C-C ++ (si es apropiada): automatización y (posiblemente) seguridad contra simplicidad y pureza. Mis dos centavos.
Su código en sí tiene un defecto sutil, pero solucionable. El patrón opts___Rule
no coincidirá con las opciones de la forma a :> b
, por lo que si alguna vez necesita usarlo, deberá actualizar su código. La solución inmediata es reemplazar opts___Rule
con opts:(___Rule | ___RuleDelayed)
que requiere más escritura que OptionsPattern[]
. Pero, para los perezosos entre nosotros, OptionValue[...]
requiere más escritura que la forma corta de ReplaceAll
. Sin embargo, creo que es un código de lectura más limpio.
Considero que el uso de OptionsPattern[]
y OptionValue
es más fácil de leer y comprende al instante lo que se está haciendo. La forma más antigua de opts___ ...
y ReplaceAll
fue mucho más difícil de comprender en una primera lectura completa. Agregue a eso, las claras ventajas de tiempo , y me gustaría actualizar su código.
Un hecho poco conocido (pero frecuentemente útil) es que las opciones pueden aparecer en listas anidadas:
In[1]:= MatchQ[{{a -> b}, c -> d}, OptionsPattern[]]
Out[1]= True
Las opciones que manejan funciones como FilterRules saben:
In[2]:= FilterRules[{{PlotRange -> 3}, PlotStyle -> Blue,
MaxIterations -> 5}, Options[Plot]]
Out[2]= {PlotRange -> 3, PlotStyle -> RGBColor[0, 0, 1]}
OptionValue tiene en cuenta:
In[3]:= OptionValue[{{a -> b}, c -> d}, a]
Out[3]= b
Pero ReplaceAll (/.) No toma esto en cuenta, por supuesto:
In[4]:= a /. {{a -> b}, c -> d}
During evaluation of In[4]:= ReplaceAll::rmix: Elements of {{a->b},c->d} are a mixture of lists and nonlists. >>
Out[4]= a /. {{a -> b}, c -> d}
Por lo tanto, si usa documentation , probablemente también debería usar OptionValue para asegurarse de que puede consumir el conjunto de opciones que pasa el usuario.
Por otro lado, si usa ReplaceAll (/.), Debe atenerse a opts___Rule
por el mismo motivo.
Tenga en cuenta que opts___Rule
también es un poco demasiado indulgente en ciertos casos ( opts___Rule
):
No es una opción válida:
In[5]:= MatchQ[Unevaluated[Rule[a]], OptionsPattern[]]
Out[5]= False
Pero ___Rule
deja pasar:
In[6]:= MatchQ[Unevaluated[Rule[a]], ___Rule]
Out[6]= True
Actualización: como lo señaló rcollyer , otro problema más serio con ___Rule
es que pierde las opciones especificadas con RuleDelayed (:>) . Puede solucionarlo (vea la respuesta de rcollyer), pero es otra buena razón para usar OptionValue.