8
$\begingroup$

Is it possible to convert the following list of strings to computable numbers with the comments preserved?

 str = {{{"8.0,1034(*g opx ksp pl ilm liq q *)"}, {"8.0,1035(*g opx ksp pl ilm liq q *)"}, 
         {"8.0,1037(*g opx ksp pl ilm liq q *)"}, {"8.0,1038(*g opx ksp pl ilm liq q *)"},   
         {"8.0,1039(*g opx ksp pl ilm liq q *)"}, {"8.0,1040(*g opx ksp pl ilm liq q *)"}},  
        {{"8.0,886(*g opx ksp pl ilm liq q  *)"}, {"8.0,889 (*g opx ksp pl ilm liq q *)"},   
         {"8.0,891(*g opx ksp pl ilm liq q  *)"}, {"8.0,894 (*g opx ksp pl ilm liq q  *)"},   
         {"8.0,896(*g opx ksp pl ilm liq q  *)"}, {"8.0,899 (*g opx ksp pl ilm liq q  *)"}}}

I'm after something like:

strc = {{{8.0,1034(*g opx ksp pl ilm liq q *)},....

Perhaps there is a way involving programatically adding the comments back onto the numbers after the numbers have been converted to an expression? Any Suggestions?

$\endgroup$
5
  • 3
    $\begingroup$ To do this robustly (expressions of any type) I believe you will need to parse the strings, and that was done very nicely in answer to this question: mathematica.stackexchange.com/q/17648/121 $\endgroup$
    – Mr.Wizard
    May 31, 2013 at 1:22
  • $\begingroup$ @Mr.Wizard I really like your link (+1), but I disagree that you need to parse strings. To get the string in the first place is hard to do programmatically (and is not addressed by Leonid). In case quotes are present in the expression, we cannot simply put ""'s around it. In case comments are present, we cannot resort to ToString, as it would delete the comments and destroy formatting. I have looked at front end stuff like CopyToClipBoard a while ago but I think I was unable to make a function that would "put quotes around an expression in a smart way", maybe I should ask a question about it. $\endgroup$ May 31, 2013 at 12:25
  • $\begingroup$ However, getting the cell expression programmatically is easy, as shown in my answer below. Also see my answer here, in your linked Q&A ,to see how we can also use this to delete comments and keep our formatting. (also @Kuba) $\endgroup$ May 31, 2013 at 12:27
  • $\begingroup$ @Mr.Wizard you can also get the string from the cell expression using ToString[NotebookRead[Cells[CellTags -> "targetCell"][[1]]][[1, 1]], InputForm], but I guess that should feel indirect. $\endgroup$ May 31, 2013 at 12:51
  • $\begingroup$ @Jacob Good points, and +1 on your answer. $\endgroup$
    – Mr.Wizard
    Jun 2, 2013 at 12:15

2 Answers 2

9
$\begingroup$

You can start with that.

StringCases["8.0,1034(*g opx ksp pl ilm liq q *)", 
 x__ ~~ y : ("(*" ~~ ___ ~~ "*)") :> {x, y}]

{{"8.0,1034", "(*g opx ksp pl ilm liq q *)"}}

And You can do with the first elements what You need.

Edit

I can not be passive after Jacob's remarks :) Following solution is based on his but simplified in order to avoid complicated procedures. I have also taken into account his comments to this post.

CellPrint[Cell[#, "Input"]] &@
 BoxData[ToBoxes[str] /. 
   x_String?(StringMatchQ[#, 
        "\"*" ~~ Verbatim["(*"] ~~ ___ ~~ Verbatim["*)\""]] &) :> 
    StringTake[x, {2, -2}]]

Definitely it works and is simple as it can be. I'm not sure why CellPrint[Cell[#, "Input"]] &@ BoxData[RowBox[{"{expr(*comm*)}"}]] gives us what we need. I hope Jacob Akkerboom will tell us more.

$\endgroup$
8
  • 2
    $\begingroup$ +1 y : ("(*" ~~ ___ ~~ "*)") is nice. $\endgroup$
    – DavidC
    May 30, 2013 at 13:40
  • 2
    $\begingroup$ Pff Kuba, I helped you twice, and now you are stealing my points with impressive but weak magic :P. Note that StringMatchQ["()", ("(*" ~~ ___ ~~ "*)")]-> True ;). You might as well have left out the asterix (*), it has special meaning in string pattern matching. I'm kidding, I shouldn't care about points, but please also see my answer :). (@DavidCarraher) $\endgroup$ May 30, 2013 at 20:39
  • 1
    $\begingroup$ Cool, looking forward to that :). Anyway the pattern you are really after is "(" ~~ _?(MatchQ[#, "*"] &) ~~ ___ ~~ _?(MatchQ[#, "*"] &) ~~ ")". $\endgroup$ May 30, 2013 at 20:53
  • 1
    $\begingroup$ The above is nicer with Verbatim I guess, but I can't really show it as this will just format my code instead of display what I want. So we want something like "(" ~~ Verbatim["*"] ~~ ___ ~~... $\endgroup$ May 30, 2013 at 21:19
  • 1
    $\begingroup$ Wow Kuba, now this good stuff, +1 :). Much more practical than my answer, using the existing RowBox works fine, you make good use of its flexibility. Your (replacement) rule can be written a bit shorter, I'll suggest an edit, but its up to you what you like best :). $\endgroup$ May 31, 2013 at 10:06
7
$\begingroup$

About the first section: This simulates the behavior of Mathematica a bit more closely than Kuba's answer, creating an additional RowBox. However, the choice of how general the different aspects of the solution are may be a bit odd. Kuba's answer feels cleaner.

Use the OPs definition of str. The following will generate an input cell where the comments are (still) present as comments.

store[str_String] := (Sow[str]; token);

CellPrint[Cell[#, "Input"]] &@
 BoxData[
  ReplacePart[#[[1]],
     MapThread[
      Rule, {Position[#[[1]], "token"], 
       Composition[RowBox, List] /@ #[[2, 1]]}]] &@
   Reap[ToBoxes@Map[Composition[store, ToString], str, {2, 2}]]]

Format preserving alternative

Suppose we start with the input cell

str = 
{{{"8.0,1034(*g opx ksp pl ilm liq q *)"}, {"8.0,1035(*g opx \
ksp pl ilm liq q *)"}}, {{"8.0,886(*g opx ksp pl ilm liq q  *)"}}}

We can give this a celltag by putting our cursor in the cell and doing Cell>Show Expression from the menu. The resulting cell will be the same as the cell printed by

Cell[BoxData[
    RowBox[{"str", "=", 
       RowBox[{"{", 
          RowBox[{
             RowBox[{"{", 
                RowBox[{

             RowBox[{"{", 
               "\"\<8.0,1034(*g opx ksp pl ilm liq q *)\>\"", "}"}], 
             ",", 

             RowBox[{"{", 
               "\"\<8.0,1035(*g opx ksp pl ilm liq q *)\>\"", 
               "}"}]}], "}"}], ",", 
             RowBox[{"{", 

           RowBox[{"{", 
             "\"\<8.0,886(*g opx ksp pl ilm liq q  *)\>\"", "}"}], 
           "}"}]}], 
          "}"}]}]], "Input", CellTags -> {"targetCell"}] // CellPrint

We can now do

NotebookRead[Cells[CellTags -> "targetCell"]] /. 
  x_String?(StringMatchQ[#, 
       "\"*" ~~ Verbatim["(*"] ~~ ___ ~~ Verbatim["*)\""]] &) :> 
   StringTake[x, {2, -2}] // CellPrint

to get a cell that is formatted in the same way as the cell with CellTag "targetCell", except for any formatting that may occur inside strings.

Format ignoring option using strings

Mr.Wizard hinted at a solution using strings, and Rm-rf gave the main code for this

CellPrint[Cell[#, "Input"]] &@
 First@MathLink`CallFrontEnd@
   UndocumentedTestFEParserPacket[ToString[str], False]

Which seems nice for this question, especially as having comments in strings hints at the expression str being the result of some Import command by Mathematica, or that this expression is at least generated in some way. In this case you don't care about existing formatting and in case of Import maybe it is an option to do all parsing by UndocumentedTestFEParserPacket. Who knows

$\endgroup$
9
  • $\begingroup$ @geordie woops, I have added that. $\endgroup$ May 30, 2013 at 13:26
  • $\begingroup$ Very nice. It works perfectly. I think I'll have to do some reading about cell expressions.. Many Thanks! $\endgroup$
    – geordie
    May 30, 2013 at 13:36
  • $\begingroup$ @geordie yay :), then I am glad. I have done this kind of thing before without any boxes (and comments for that matter), but that was not so nice. This was good practise for me :). $\endgroup$ May 30, 2013 at 13:40
  • 1
    $\begingroup$ @geordie Ah yes, nice. I upvoted that question (which I don't do often) and favorited it. I wanted to present a solution yesterday but I had no time. I also saw your solution, but I don't like working with strings, I prefer held expressions. I always regret decisions like determining the scope of a variable. So I'm very interested in how we can do something like that afterwards :). $\endgroup$ May 30, 2013 at 13:55
  • 1
    $\begingroup$ You can save yourself the trouble of constructing the boxes by using UndocumentedTestFEParserPacket. For example, First@MathLink`CallFrontEnd@UndocumentedTestFEParserPacket[s, False] where s is a string $\endgroup$
    – rm -rf
    May 31, 2013 at 13:12

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.