Skip to main content

syntax - Prefix operator with low precedence



The question is simple, but I will elaborate on the background as well for those interested in the idea:


How to define a new operator with specified precedence value?




Background


Mathematica was design to facilitate functional programming. I definitely find it easy to write code continuously: the output of a function becomes immediately the input of the next function. One thing I really miss though is a low-precedence prefix operator that applies to all things following it (up to e.g. CompoundExpression (;)). Consider the following example:


Log@N@Accumulate@# & /@ Partition[Range@300, 100] // Flatten // ListPlot

It partitions a dataset to multiple subparts, threads functions to each subpart, and then plots the joined datasets. I write such code a lot, as I find it easy that it can be written from the inside to the outside, from argument to head (right to left). The problem is that the extension of the above one might come up with intuitively does not behave like that:


ListPlot@Flatten@ Log@N@Accumulate@# & /@ Partition[Range@300, 100]


Operator precedences cause ListPlot@Flatten to be applied to each subpart of the partitioned list instead of the list as a whole. Now since I successively build up my calculations from the argument to the wrapping functions, I want to use a prefix operator. My concerns are:



  • One can use matchfix forms like f@(...) or f[...] to wrap around the Map, though it requires the matching of parentheses, which can be a PITA when multiple such functions are applied with prefix notation.

  • Using postfix // both breaks my cognitive process of writing from right-to-left, and (more importantly) breaks the principle of head-precedes-argument, which is very emphasized in Mathematica (and in $\lambda$-calculus).

  • The operator must be a free symbol. Modifying the built in @ operator should be avoided!

  • Also it must be simple enough to be used effectively. Postfix apply // is 2 keystrokes, while for example $\oplus$ requires 4 (escc+esc), making it worse than hitting end//.


So, how to define a new operator, e.g. \\ that has low precedence (perhaps 70) so that this:


ListPlot \\ Flatten \\ Log@N@Accumulate@# & /@ Partition[Range@300, 100]


equals this:


ListPlot[Flatten[Log@N@Accumulate@# & /@ Partition[Range@300, 100]]]

Frankly, I just started to wonder why this operator is not present at all in Mathematica, though WRI always emphasizes functional programming as a native style of the language.




I know I can apply Log or N to the partitioned list as a whole. I only used them here for demonstrating my case.



Answer



As explained by Michael Pilat you cannot create your own compound operators* with custom precedence. (You could conceivably write your own parser as Leonid has worked on, or attempt to coerce the Box form with CellEvaluationFunction.)


You can however use an existing operator with the desired precedence. Looking at the table Colon appears to be a good choice. The operator is entered with Esc:Esc. Example:


SetAttributes[Colon, HoldAll]

Colon[f__, x_] := Composition[f][Unevaluated@x]

ListPlot \[Colon] Flatten \[Colon] Log@N@Accumulate@# & /@ Partition[Range@300, 100]

Which appears as, and produces:


Mathematica graphics


Mathematica graphics


Since raw colon is already used for Pattern this may be confusing. However, if you are willing to edit your UnicodeFontMapping.tr file you can assign any symbol you like. Here I mapped \[Colon] to Klingon A:


Mathematica graphics


This was done by changing the line starting with 0x2236 in UnicodeFontMapping.tr.





* Rojo demonstrated that one can create two-dimensional compound operators, meaning use of SubscriptBox, SuperscriptBox, OverscriptBox, etc. See:



Comments

Popular posts from this blog

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Adding a thick curve to a regionplot

Suppose we have the following simple RegionPlot: f[x_] := 1 - x^2 g[x_] := 1 - 0.5 x^2 RegionPlot[{y < f[x], f[x] < y < g[x], y > g[x]}, {x, 0, 2}, {y, 0, 2}] Now I'm trying to change the curve defined by $y=g[x]$ into a thick black curve, while leaving all other boundaries in the plot unchanged. I've tried adding the region $y=g[x]$ and playing with the plotstyle, which didn't work, and I've tried BoundaryStyle, which changed all the boundaries in the plot. Now I'm kinda out of ideas... Any help would be appreciated! Answer With f[x_] := 1 - x^2 g[x_] := 1 - 0.5 x^2 You can use Epilog to add the thick line: RegionPlot[{y < f[x], f[x] < y < g[x], y > g[x]}, {x, 0, 2}, {y, 0, 2}, PlotPoints -> 50, Epilog -> (Plot[g[x], {x, 0, 2}, PlotStyle -> {Black, Thick}][[1]]), PlotStyle -> {Directive[Yellow, Opacity[0.4]], Directive[Pink, Opacity[0.4]],