Opened 8 years ago
Closed 6 years ago
#11776 closed enhancement (duplicate)
Holding an expression unevaluated: Something like hold_all() would be nice.
Reported by: | Pap | Owned by: | jason |
---|---|---|---|
Priority: | minor | Milestone: | sage-duplicate/invalid/wontfix |
Component: | misc | Keywords: | |
Cc: | Merged in: | ||
Authors: | Reviewers: | Travis Scrimshaw | |
Report Upstream: | N/A | Work issues: | |
Branch: | Commit: | ||
Dependencies: | Stopgaps: |
Description
A function that holds its arguments unevaluated, something like
hold_all(expression)
would be very nice, if implemented. It would be very useful when teaching Sage to students, and in general it would facilitate printing results together with the unevaluated expression.
Please have a look at http://test.sagenb.org/home/pub/6, it is a worksheet that explains (in detail, I hope) how a function like hold_all() would be useful in practice.
Change History (8)
comment:1 follow-up: ↓ 2 Changed 8 years ago by
comment:2 in reply to: ↑ 1 ; follow-up: ↓ 3 Changed 8 years ago by
Replying to nbruin:
Given that
expression
is already evaluated (leading to a simplified expression) beforehold_all
even gets a hold of it, this approach is a little problematic.
I know, that's why I suggested a different approach in the end of the Sage worksheet I posted (http://test.sagenb.org/home/pub/6). Something like
unevaluated_expr=hold_all( integrate(x^2+1,x) + diff(tan(x),x) )
then a command to evaluate it, if needed, say
evaluated_expr=evaluate(unevaluated_expr)
So one could pretty print the unevaluated expression, together with the the evaluated one with
join(["$",unevaluated_expr,"=",evaluated_expr,"$"])
Note that such a functionality is pretty much standard in many Computer Algebra systems. Maxima, for example, suspends evaluation if an expression is preceded by a single quote, and has a very convenient function called ev()
to evaluate the expression (or even parts of it) later on, so one could use
unevaluated_expr : '( integrate(x^2+1,x) + diff(tan(x),x) ) evaluated_expr : ev(unevaluated_expr) print(unevaluated_expr,"=",evaluated_expr)
where everything inside '(...)
is not evaluated. The same thing can be done in Mathematica and even yacas. I was actually surprised Sage doesn't have an easy way to do the same.
The basic feature is already available, but perhaps not very convenient for your taste. See
sin(pi,hold=True)
. Currently this isn't available for integrals etc., but there issage.calculus.calculus.dummy_integrate(x^2+1,x)
It is very reasonable to ask forintegrate(x^2+1,x,hold=True)
to be synonymous with that.
Well, the very basic feature is already available, indeed, but not for every function and not in a way one could call "convenient". Furthermore, integrate()
was just an example. I didn't know about the package dummy_integrate
(and I have no means to know which functions accept hold=True
and which don't). But anyway, it only solves the issue for that particular (and very simple) example. I am thinking of something more general and more powerful.
You can already do
def held(f): return lambda *args: f(*args,hold=True) sin=held(sin) cos=held(cos) sin(pi)^2+cos(pi)^2
one could have a package inert_symbolic_functions that has (essentially) those declarations, so that afrom held_symbolic_functions import *
would give you a "held" environment. With a little namespace injection magic one could probably also use that to implementwith held_function_context: expr=sin(pi)^2+cos(pi)^2
which is probably the closest to what you request, subject to being doable in python.
Well, doable, but sounds like reinventing the wheel, in my humble opinion. Not to mention it works only for functions that accept hold=True
, and I have no idea how I will suspend evaluation of operators that way.
Given the fact that Sage makes heavy use of LaTeX's power to typeset expressions perfectly (while Maxima or Mathematica either don't use LaTeX, or use it only with the aid of external programs), it is a pity we can't use that power to create educational worksheets, pretty much like books, where printing an expression unevaluated together with the corresponding evaluated one is a must. It is actually one of the first things I try to do as a test in every CAS I am learning.
comment:3 in reply to: ↑ 2 Changed 8 years ago by
Replying to Pap:
I know, that's why I suggested a different approach in the end of the Sage worksheet I posted (http://test.sagenb.org/home/pub/6). Something like
unevaluated_expr=hold_all( integrate(x^2+1,x) + diff(tan(x),x) )
That is exactly what can't work. This is equivalent to the python code
unevaluated_expr=hold_all(integrate(x^2+1,x).__add__(diff(tan(x),x)))
The meaning of this in python is that what is inside the parentheses gets executed and the result of that gets passed to hold_all
. By the time hold_all
executes, it is already too late. You need to inform integrate
, __add__
and diff
that they need to behave differently from what they would normally do. The "hold" parameter does that, but as you observe, it is rather burdensome that it potentially has to be supplied to all routines involved (the fact that not all relevant routines accept "hold" yet is just a matter of a bug to fix).
Basically what is needed is a flag on SR that sets "hold=True" to be the default rather than "hold=False". I don't know how easy and how thread-safe such a flag would be. It would definitely violate the stipulation that parents be immutable.
A "clean" solution would be to allow a second instance of SR that does have "hold=True" as default? This would allow something that you'll probably find painful too:
sage: SRheld = SymbolicRing( hold_by_default = True ) sage: SRheld(1) + SRheld(3) #note the need to turn 1,3 into symbolic objects before adding 1 + 3 sage: x = SRheld.var('x') sage: integrate(x^2+1,x) integral(x^2+1,x) sage: xt = SR(x) # the normal SR still behaves as before sage: integrate(xt^2+1,xt) 1/3*x^3+x
I'm afraid the proposed namespace magic I proposed earlier will never fully work because SR(1).__add__(3)
can't be reached that way. So we need to find a convenient place to store the "hold default value". SR itself would be a reasonable place except that changing the value definitely changes how SR behaves and parents are supposed to be immutable.
Perhaps if we make a context manager that sets and resets a "hold_by_default" flag, we localize the potential trouble a bit. localvars
has set a precedent for such:
with held_function_context(SR): expr=sin(pi)^2+cos(pi)^2
If in addition it would hold a lock on SR we would be thread safe as well (just not very thread friendly).
I think there is merit in having "hold" facilities more readily available but to implement it requires some serious architectural considerations and likely some relatively comprehensive modifications.
comment:4 follow-up: ↓ 5 Changed 8 years ago by
Does it look like #10035 is what you are asking for? I'm not sure whether these are dups, though it seems like they might be.
comment:5 in reply to: ↑ 4 Changed 8 years ago by
- Milestone changed from sage-4.7.2 to sage-duplicate/invalid/wontfix
Replying to kcrisman:
Does it look like #10035 is what you are asking for? I'm not sure whether these are dups, though it seems like they might be.
Yes! two people come up independently with the same solution. I think a context is the most convenient practical solution.
I'm reassigning the ticket to "duplicate" and put it up for review. (I think that is the procedure?) If someone else confirms the "dup" status they can give it a positive review. Otherwise, just revert the milestone and revert to "new status.
comment:6 Changed 6 years ago by
- Reviewers set to Travis Scrimshaw
- Status changed from new to needs_review
I also agree that this is a duplicate of #10035.
comment:7 Changed 6 years ago by
- Status changed from needs_review to positive_review
comment:8 Changed 6 years ago by
- Resolution set to duplicate
- Status changed from positive_review to closed
Given that
expression
is already evaluated (leading to a simplified expression) beforehold_all
even gets a hold of it, this approach is a little problematic. The basic feature is already available, but perhaps not very convenient for your taste. Seesin(pi,hold=True)
. Currently this isn't available for integrals etc., but there issage.calculus.calculus.dummy_integrate(x^2+1,x)
It is very reasonable to ask forintegrate(x^2+1,x,hold=True)
to be synonymous with that.You can already do
one could have a package inert_symbolic_functions that has (essentially) those declarations, so that a
would give you a "held" environment. With a little namespace injection magic one could probably also use that to implement
which is probably the closest to what you request, subject to being doable in python.