2
$\begingroup$

I recently started creating Mathematica packages. I find it easiest to develop functions in a Notebook (.nb file). This allows me to add sections, comments and examples.

I don't want to duplicate code, certainly not by copy/pasting my function from the .nb file to the .m package file. So I would prefer automatically generate Package code from a Notebook.

Are there direct methods of generating a .m file? Alternative methods of developping Packages in a Notebook environment?

The following is just an example to illustrate one possible method to avoid duplication of code. Imagine that I have a .nb with the following function:

toSquare = Function[{x}, x^2]

Now I want to export this Input as pure code, for example by adding a button which exports this function to a .m file:

Button["Export as package", toPackage["packageName.m", ...]]

This should generate the file packageName.m with the following content:

BeginPackage["Square`"];
toSquare::usage="toSquare[x] squares numbers.";
toSquare=Function[{x},x^2];
EndPackage[]

Can Inputs be converted to strings? Can Functions be converted to strings? Exporting strings to a .m file seems slightly hacky to me, so if there are alternatives, I'd prefer that.

$\endgroup$
2

2 Answers 2

5
$\begingroup$

Writing your package in a notebook is the preferred way for many (perhaps most?). To generate the .m from the .nb all you need to do is make the Input cells that you want also be Initialization cells: Right-click on the cell bracket and use the check-box. Then use File -> Save As and choose .m. That will not change your notebook in any way, it will just write a .m file that contains all the initialization cells that you wanted.

Even better, you can skip all the Save As stuff by tracking down the option for automatically saving a .m file if your notebook has any initialization cells in it:

Edit -> Preferences -> Option Inspector -> Notebook Options -> File Options -> AutogeneratePackage

$\endgroup$
2
  • 1
    $\begingroup$ Yes, this is ideal. Thanks. This can also be found here, so I'm voting to close my own question. $\endgroup$
    – LBogaardt
    Commented Jul 17, 2019 at 15:35
  • 1
    $\begingroup$ Another great thing about using the autosaving .m from an .nb approach is you can make nicely formatted usage messages without any hassle with StyleBox or SubscriptBox. $\endgroup$
    – Chris K
    Commented Jul 17, 2019 at 16:14
1
$\begingroup$

I created a short function which grabs the previously evaluated Input Cell as a string, based on Mathematica's InString[] function:

getInputAsString = Function[{},StringReplace[ToString[InputForm[MakeExpression[ToExpression[InString[]],StandardForm]]],RegularExpression["HoldComplete\[(.*)\]"]->"$1;"]];

This can be combined with the following function which exports a list of such input strings as a .m package file.

toPackage = Function[{fullPath,contextName,inputStrings},With[{output=StringJoin[{"BeginPackage[\"",contextName,"\"];\n",Riffle[inputStrings,"\n"],"\nEndPackage[]"}]},Export[fullPath,output,"Text"];]]

The following example illustrates the use:

toSquare::usage = "toSquare[x] squares numbers." (*"toSquare[x] squares numbers."*)
toSquareUsageString = getInputAsString[] (*"toSquare::usage = \"toSquare[x] squares numbers.\";"*)

and

toSquare = Function[{x}, x^2] (*Function[{x}, x^2]*)
toSquareString = getInputAsString[] (*"toSquare = Function[{x}, x^2];"*)

Finally

toPackage[
    ToString[NotebookDirectory[]] <>"PackageSquare.m",
    "Square`",
    {toSquareUsageString, toSquareString}
]

generates a file "PackageSquare.m" in the same directory with content:

BeginPackage["Square`"];
toSquare::usage = "toSquare[x] squares numbers.";
toSquare = Function[{x}, x^2];
EndPackage[]
$\endgroup$
1
  • $\begingroup$ As mentioned in the OP, I find this method via strings slightly hacky. If you know a better method, I will accept that as the answer. $\endgroup$
    – LBogaardt
    Commented Jul 17, 2019 at 13:28

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