From 1bebd2ccba5ebfff44a94d41c570094871f79065 Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Sat, 31 Aug 2024 16:06:14 -0300 Subject: [PATCH] Blanks as singleton (#1081) Looking at the initialization process, `Blank*` PatternObjets are created several times. This PR makes that these elements be created just once when a parameter is not provided. This would also help to optimize the pattern-matching algorithm when we face that problem. --- CHANGES.rst | 6 ++++++ mathics/builtin/patterns.py | 16 ++++++++++++++++ mathics/core/builtin.py | 25 ++++++++++++------------- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 5f041f999..e8a99c4bc 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -19,6 +19,12 @@ Internals Operator precedence has been gone over and is picked up in tables from the Mathics Scanner project. +Performance +----------- + +* `Blank*` patterns without arguments are now singletons. + + 7.0.0 ----- diff --git a/mathics/builtin/patterns.py b/mathics/builtin/patterns.py index 0d60dc60b..7117bf7a8 100644 --- a/mathics/builtin/patterns.py +++ b/mathics/builtin/patterns.py @@ -1169,6 +1169,22 @@ def get_default_value( class _Blank(PatternObject): arg_counts = [0, 1] + _instance = None + + def __new__(cls, *args, **kwargs): + if kwargs.get("expression", None) is False: + return super().__new__(cls, *args, **kwargs) + + num_elem = len(args[0].elements) + assert num_elem < 2, f"{cls} should have at most an element." + + if num_elem != 0: + return super().__new__(cls, *args, **kwargs) + # no arguments. Use the singleton + if cls._instance is None: + cls._instance = super().__new__(cls, *args, **kwargs) + return cls._instance + def init( self, expr: Expression, evaluation: OptionalType[Evaluation] = None ) -> None: diff --git a/mathics/core/builtin.py b/mathics/core/builtin.py index b97f0cbd6..78504c0e0 100644 --- a/mathics/core/builtin.py +++ b/mathics/core/builtin.py @@ -46,7 +46,8 @@ from mathics.core.list import ListExpression from mathics.core.number import PrecisionValueError, dps, get_precision, min_prec from mathics.core.parser.util import PyMathicsDefinitions, SystemDefinitions -from mathics.core.rules import BuiltinRule, Pattern, Rule +from mathics.core.pattern import Pattern +from mathics.core.rules import BuiltinRule, Rule from mathics.core.symbols import ( BaseElement, BooleanType, @@ -191,16 +192,15 @@ def __new__(cls, *args, **kwargs): # well documented. # Notice that this behavior was used extensively in # mathics.builtin.inout - if kwargs.get("expression", None) is not False: return to_expression(cls.get_name(), *args) - else: - instance = super().__new__(cls) - if not instance.formats: - # Reset formats so that not every instance shares the same - # empty dict {} - instance.formats = {} - return instance + + instance = super().__new__(cls) + if not instance.formats: + # Reset formats so that not every instance shares the same + # empty dict {} + instance.formats = {} + return instance def __init__(self, *args, **kwargs): super().__init__() @@ -472,12 +472,11 @@ def is_literal(self) -> bool: class BuiltinElement(Builtin, BaseElement): def __new__(cls, *args, **kwargs): new_kwargs = kwargs.copy() + # In a Builtin element, we never return an Expression object, + # so we create it with the option `expression=False`. new_kwargs["expression"] = False instance = super().__new__(cls, *args, **new_kwargs) - if not instance.formats: - # Reset formats so that not every instance shares the same empty - # dict {} - instance.formats = {} + # If `expression` is not `False`, we need to initialize the object: if kwargs.get("expression", None) is not False: try: instance.init(*args, **kwargs)