[WSG23] Daily Study Group: Writing a Wolfram Language Function

A Wolfram U Daily Study Group on “Writing a Wolfram Language Function” begins on August 21, 2023.

Join me and a group of fellow learners in an exploration of what it takes to make a “good” function in the Wolfram Language. We’ll begin with standard function structure and then cover arguments, patterns, options and how to extend function capability with overloading, recursion and iteration, memorization and up-values. Later sessions will discuss error handling and extra tips.

Sessions include short lessons, poll questions to review key concepts, practice problems and live Q&A. A certificate of program completion will be awarded to participants who attend online sessions and pass a quiz.

Some prior Wolfram Language experience or knowledge is recommended for this Study Group.

REGISTER HERE

10 Likes

Wolfram U is looking forward to starting this Daily Study Group on Monday, along with @Daniel Robinson. Seats are still available! Sign up here.

2 Likes

Excellent first session!

1 Like

Thanks for first lecture. I am interested in learning more about sequences. Your introduction is difficult for me to apply to a sequence of mathematical operations. I want to write a function to accept pullback metrics and compute the sequence as a parameter varies. Any guidance/suggestion would be appreciated. Larry

I came across the following expression in a form I had not seen before:

g[h_, b_][x_] := 
 f[b] + (DifferenceQuotient[f[x], {x, h}] /. (x -> b)) (x - b)

What is going on with the [x_] in s separate bracket outside the g[h_,b_] on the left side of the := ?
It looks a little like the Function[parameters,body][x] format but it’s not that…

Carl

Hi Carl, this is covered in the third notebook in this series (Extending Capability) in the optional section, Operator Forms and Subvalues.
Although the usual syntax for a function might be f[x_, y_] := g[x, y], it’s perfectly acceptible to write f[x_][y_] := g[x, y], or even f[x_][y_][z_] := g[x, y, z] (and so on). These are known as operator forms, and are valid because the code on the left-hand side of the SetDelayed symbol (:=) is just a pattern—it can be more or less whatever you like.

For a concrete example, consider the Select function. It is normally used like this: Select[Range[10], EvenQ]. However, it can also be used as an operator like this: Select[EvenQ][Range[10]].

2 Likes

Any reference of how to order/place the functions in a mathematica packages , context etc, will be useful.

I want to construct a function that inputs an element of the symmetric group of n objects and returns the conjugacy class of this element. The inputs are thus the positive integer “n” and something like “Cycles[{{1,3,2}}}]” so my starting point is

getClass[n\_, cycle\_ ]

I know that in practice I tend:

  1. to forget to to include the argument n,
  2. I input an incorrect value of n (a cycle like Cycles[{{1,3,2}}}] permutes objects 1,2,3 so n>= 3 at least
  3. I tend to mess up the ordering of the argument.

So I want to build getClass[n_,cycle_] so

  • a) getClass[somecycle] returns a message like “order missing”,
  • b) getClass[q,somecycle] returns “order is too low” if q is incorrect
  • c) evaluates getClass[somecycle,n] equally well as getClass[n,somecycle]

I can handle c) by using PermutationCyclesQ, which will tell me if a particular argument is actually a cycle, and thus test if the first argument is a cycle or not.

I’m not sure how to proceed to handle the rest or make it work smoothly. The current working version of getClass is:

getClass[p_, cycle_] := Module[{listofcycles, shortlistofcycles, padding},
  listofcycles = Flatten[List @@ cycle, 1];
  shortlistofcycles = Table[Length[listofcycles[[k]]], {k, 1, Length[listofcycles]}];
  padding = p - Sum[shortlistofcycles[[k]], {k, 1, Length[shortlistofcycles]}];
  If[padding != 0, AppendTo[shortlistofcycles, Table[1, {q, 1, padding}]]];
  Reverse[Sort[Flatten[shortlistofcycles]]]]

Thus for instance
getClass[3,Cycles[{{1,3,2}}}] returns {3}

getClass[4,Cycles[{{1,2},{3,4}}] returns {2,2}

Thanks! That’s interesting. Just curious: Where in the vast warehouse of Documentation files would I find this little factoid?

Carl

Hi Hubert, your questions can be answered with overloading – something we will be covering in today’s session (Extending Capability).

To solve (c), you can use OrderlessPatternSequence, and to solve (b), you can use Condition (/;). Both of these were covered in the first session (The Basics). To solve (a), you can use Message, which will be covered in the fourth session (Error Handling).

Here’s some code to get you started:

ClearAll[getClass];

getClass::orderTooLow = "The order you have provided (`1`) is too low. It must be an integer greater than 2.";
getClass::badArgs = "Bad arguments. getClass takes exactly 2 arguments: an 'order' and a 'cycle'. The order must be an integer greater than 2 and the cycle must be a valid permutation cycle.";

(* Main definition: *)
getClass[
    OrderlessPatternSequence[
     order_?IntegerQ,
     cycle_?PermutationCyclesQ
     ]
    ] /; order > 2 := (* Your function *);

(* Order provided is too low: *)
getClass[
   OrderlessPatternSequence[
    order_?IntegerQ,
    cycle_?PermutationCyclesQ
    ]
   ] := (
   Message[getClass::orderTooLow, order];
   $Failed
   );

(* Arguments provided are either invalid, too numerous or too few: *)
getClass[badArgs___] := (
   Message[getClass::badArgs];
   $Failed
   );

You can add more definitions / error messages to handle more cases.

P.S. – you seem to have a lonely p in your function definition.

1 Like

I just did a search myself in the documentation and couldn’t find anything particularly helpful… (no tutorial that explains how to create your own operator forms, anyway).

However, in case it’s of interest to you, I did find some code on StackExchange that claims to list all of the bulit-in functions that have operator forms:

file = FileNameJoin[{$InstallationDirectory, "SystemFiles", "Kernel", 
        "TextResources", $Language, "Usage.m"}];

usage = Import[file, "HeldExpressions"];

usage //
  Cases[
    HoldPattern[
      _[_[sym_Symbol, "usage"] = _String?(StringContainsQ["operator form"])]
    ] :> sym
  ]

Thanks. I’m looking forward to the details. The lonely p is because the working code uses p rather than n, and I didn’t catch the typo before submitting.

Hi Francisco, these workflows explain the details:

For an example, take a look at the ImageStyler.wl file (which is referenced in the fifth notebook, Useful Tips).

1 Like

Hi Laurence, can you give me some more details? It would be best if you could write down a simple example that includes the setup you want to start with and the result you want to end with. I may then be able to help with the steps in between.

I decided to make a fun example to demonstrate some of the things we have learned in this series.

The function below tests the Collatz conjecture, calculating a sequence of expressions that—it is conjectured—will always end in 1. Together with the interactive tool that follows it, this function uses:

  • An argument (see notebooks 1 and 2)
  • A PatternTest, to make sure the argument is always an integer greater than 1 (see notebook 1)
  • Recursion (see notebook 3)
  • Overloading, to ensure the recursion stops (see notebook 3)
  • Sow and Reap, to collect intermediary values (see notebooks 4 and 5)
  • A Manipulate, to allow one to vary the starting value of the sequence (see notebook 5)
  • A Module, to localise a variable (see notebook 5)

Function:

ClearAll[collatz];
collatz[n_?(IntegerQ[#] && # > 0 &)] := If[EvenQ[Sow@n], collatz[n/2], collatz[3*n + 1]];
collatz[1] := Sow@1;

Interactive tool:

Manipulate[
 Module[
  {data = Table[Reap[collatz[n]][[2, 1]], {n, 2, maxN, 1}]},
  Row[
   {
    ListLinePlot[
     data,
     PlotRange -> {{0, 120}, {0, 10000}},
     Frame -> {{True, False}, {True, False}},
     FrameLabel -> {"Sequence length", "Sequence values"},
     PlotHighlighting -> None,
     PlotStyle -> {
       Sequence @@ 
        ConstantArray[
         Directive[GrayLevel[0.5, 0.5], Thickness[0.001]], maxN - 2],
       Directive[Purple, Thickness[0.003]]
       },
     ImageSize -> 600
    ],
    Framed[
     Pane[
      Row[Last[data], ","],
      ImageSize -> {200, 300},
      Scrollbars -> {False, True}
      ],
     RoundingRadius -> 3
     ]
    },
   BaseStyle -> "Text"
   ]
  ],
 {{maxN, 3, "n"}, 3, 100, 1, Appearance -> "Open"},
 ContinuousAction -> True
]

Enjoy! :slight_smile:

3 Likes

In the last session, I mentioned that Alternatives (a pattern object) and Or (a function) are not interchangeable. Here’s an example to demonstrate how they differ.

Consider trying to extract all of the cases from a list that are either 2 or True. Using Alternatives (|) gives the correct result:

Cases[{1, 2, 3, True, 4}, 2 | True]
(* {2, True} *)

However, using Or (||) gives an incorrect result:

Cases[{1, 2, 3, True, 4}, 2 || True]
(* True *)

This is because Or immediately evaluates if any of its arguments resolve to True or False. In this case, 2||True evaluates to True, so Cases only picks out the items that match True.

2 Likes

Wow! Note that it needs 13.3 to evaluate PlotHighlighting → None,
If you delete that statement it works with 13.2

1 Like

In the lecture, you show the full local names on a few occasions. How is that done?
For instance x=1; y=3, Plot{ x, {x,0,y}; x might show as x=1; y=3, Plot{ x$12345, {x$12345,0,y}; x clearly showing the localize, temporary definition of the x inside the Plot…
How do you switch between the two views of the cell?

Hi Paul, is that Plot example one that you saw in the lecture? I’m having trouble finding it in the notebook and I’m not sure how to accomplish something like that off the top of my head (or even after some searching). I know that you can do a more basic thing like Module[{x},x] to see the local name of x inside a module, but as far as seeing the names for automatically localized variables, I don’t recall seeing that in the presentation and can’t find it in the notebook. (Granted, I look at a lot of presentations and notebooks so it’s quite possible that I’ve missed it, in which case a pointed would be helpful!)

No, mostly made up, it was in the live presentation, but I don’t think shows in the notebook. I assume it a front end maybe cell type or something. The presenter switch back and forth but I didn’t hear if he indicated how. I was thinking it was a plot, but not sure - just some expression that had a mixture of localized and global in the expression.