17
$\begingroup$

Background

Conversion from string/boxes to expression is quite common. Very often the goal would be a simple switch from "1" to 1. Or "foo" to foo

Mindless ToExpression can lead to evaluation caused by unexpected input or by someone's bad will. E.g. 1 + ToExpression["1"] is fine but 1 + ToExpression["Quit[]"] not anymore.

Ideally the function's signature should be foo[_String] -> _expectedHead | $Failed. But unevaluated will do too.

Example

An example could be Symbol returning symbols of strings containing valid symbol's names. If we need $Failed it could be enhanced

Symbol[str] /. Verbatim[Symbol][_String] -> $Failed

Questions

What other alternatives are there?

What if there is no alternative and we need to cook up a custom interpreter? Can we do better than:

Interpreter["Integer"] @ "Evaluate @ Print[1]"

or

ToExpression["1+1", StandardForm, HoldComplete] // 
  Replace[HoldComplete[Except[_Integer]] -> {$Failed}] // First
$\endgroup$
4
  • $\begingroup$ The plan is that I will gather given answers in the question as an index. $\endgroup$
    – Kuba
    Commented May 4, 2018 at 12:37
  • 2
    $\begingroup$ Already +1 for addressing such a vital problem! $\endgroup$ Commented May 4, 2018 at 12:58
  • 3
    $\begingroup$ I have a feeling that I asked about something similar before, but I can't find it ... This is related: mathematica.stackexchange.com/q/72507/12 For symbols, there's Symbol. For numbers, I do not think there is an alternative that is safe, fast, and robust. FromDigits is safe, but not robust (use it on strings). ToExpression is fast, but not safe or robust. Internal`StringToDouble is fast, but not robust (no error reporting). Interpeter can do a lot, it is safe, and I think it is robust. But it is awfully slow. Finally, we could simply use Read and StringToStream. $\endgroup$
    – Szabolcs
    Commented May 4, 2018 at 13:32
  • 6
    $\begingroup$ I think that the lack of a good solution is just another manifetation of Wolfram not caring about developers. Mathematica is for interactive use. Want to develop robust and high quality packages? Forget about it. It's a real pain. Why they renamed Mathematica to "Wolfram Language" to "attract programmers" is then beyond me. $\endgroup$
    – Szabolcs
    Commented May 4, 2018 at 13:33

2 Answers 2

4
$\begingroup$

This is too long to fit in a comment so I post it as an answer, sorry.

The last example provided in the question seems already very effective and useful to me. What are the shortcomings of this method that made you look for a different one? Is it unsafe? Not generic enough? Too slow?

It can also be extended to allow for more heads, like:

ToExpressionSafer[string_String] := ReleaseHold@Replace[
   ToExpression[string, StandardForm, HoldComplete],
   {h : _Plus | _Times | _Integer | _Real :> h, _ -> $Failed},
   Infinity
 ]

In[]:= ToExpressionSafer["1+2"]
(* 3 *)

In[]:= ToExpressionSafer["1+2*4+8.5"]
(* 17.5 *)

In[]:= x = 100;
ToExpressionSafer["1+x"]
(* 1 + $Failed *)

In[]:= ToExpressionSafer["Quit[]"]
(*$Failed*)

Some timings:

ToExpressionSafer["12.5"]; // TimeIt
(* 4.9 * 10^-5 *)

ToExpression["12.5"]; // TimeIt
(* 5.9 * 10^-6 *)

Internal`StringToDouble["12.5"]; // TimeIt
(* 4.0 * 10^-7 *)

For this simple string to real conversion, the methods become one order of magnitude slower for each increase in complexity and error checking. That seems quite reasonable to me.

$\endgroup$
2
  • $\begingroup$ I was hoping that there are more than Internal`StringToDouble, to list them and to compare pros and cons. General solution would be nice too as it is hard to expect to find all variations built-in. And such function should take a string and a type/pattern which is expected to be found. p.s. TimeIt is not defined. $\endgroup$
    – Kuba
    Commented May 7, 2018 at 7:51
  • $\begingroup$ Internal`StringToBoolean, Internal`StringToMInteger, Internal`StringToMReal, Internal`StringToDouble, Internal`StringToMRational. $\endgroup$
    – Mark Adler
    Commented Dec 28, 2021 at 4:10
2
$\begingroup$

In more recent versions (certainly in 13.3), there is an Interpreter type of "ComputedNumber" which I think achieves your desired goals:

(* evaluate simple arithmetic expressions *)
In[4]:= Interpreter["ComputedNumber"]["1+1"]

Out[4]= 2


(* Non-numeric computations give a Failure *)
In[5]:= Interpreter["ComputedNumber"]["1+Quit[]"]

Out[5]= Failure["RestrictionFailure", <|
 "MessageTemplate" :> MessageName[Interpreter, "number"], 
  "MessageParameters" -> <|"Input" -> "1+Quit[]"|>, "Input" -> 
  "1+Quit[]"|>]


(* Yet, you can still compute more complicated expressions *)

In[6]:= Interpreter["ComputedNumber"]["1+Sin[3.2 Pi]"]

Out[6]= 0.412215


(* ComputerNumber has access to expressions outside of the input scope, so long as they compute a number *)

In[7]:= a = {1, 2, 3};
Interpreter["ComputedNumber"]["1+a[[2]]+a[[3]]"]

Out[7]= 6
```
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.