Can I write {x, a, b} // Do [..., #] & instead of Do [..., {x, a, b}]?

I love Ruby. In this language, all the basic functions are actually methods. Therefore, I prefer postfix notation - when the data that I want to process is placed to the left of the body of the anonymous processing function, for example: array.map{...} . I believe that it has advantages in how easy it is to read this code.

But Mathetica, being functional (yes, it can be procedural if you want), dictates the style where the function name is placed to the left of the data. As we see in our manuals, // used only when it is a simple function without arguments, for example list // MatrixForm . When functions need a lot of arguments, the people who wrote the manuals use the F[data] syntax.
That would be nice, but my problem is with F[f,data] , for example Do[function, {x, a, b}] . Most Mathematica functions (if not all) have arguments in this order - [function, data] , and not [data, function] . Since I prefer to use pure functions to keep the namespace clean and not create many named functions in my laptop, the function argument may be too large - so large that the data argument will be placed on the 5-20th line of code after the line with the call functions.

That's why sometimes, when the evil nature of Ruby controls me, I rewrite such functions in a postfix:

Do [f (x), {x, a, b}] \ n {x, a, b} // Do [f (x), #] &

Since this is important to me, this pure function (potentially large code) is placed directly from data processing. Yes, I do it and I am happy. But there are two things:

  • this leads to the fact that Mathematica highlights the parser problem: x in postfix notation is highlighted in blue, not turquoise;
  • every time I look in the Mathematica manuals, I see examples like this: Do[x[[i]] = (v[[i]] - U[[i, i + 1 ;; n]].x[[i + 1 ;; n]])/ U[[i, i]], {i, n, 1, -1}]; that means ... hell, they find it easy to read / maintain / etc. ?!

So these two things made me ask this question here: "I'm so bad a boy who uses my Ruby style, and I have to write code like these guys , or itโ€™s OK, and I donโ€™t have to worry , and I have to write like me like?

+8
coding-style wolfram-mathematica postfix-notation
source share
6 answers

Mathematica supports 4 ways to apply a function to its arguments:

  • standard function form: f[x]
  • prefix: f@x or g@@{x,y}
  • postfix: x // f and
  • infix: x~g~y , which is equivalent to g[x,y] .

Which form you decide to use is up to you, and is often an aesthetic choice, more than anything else. Internally, f@x interpreted as f[x] . Personally, I mainly use postfix, just like you, because I see each function in the chain as a transformation, and it's easier to combine multiple transforms together. However, my code will be overwhelmed with both the standard form and the prefix form mainly depending on the whim, but I tend to use the standard form more often, as this causes a sense of restraint regarding the parameters of functions.

I took a bit of freedom with the prefix form since I included the abbreviated Apply form ( @@ ) next to Prefix ( @ ). Of the built-in commands, only the standard form, infix form and Apply allow you to easily pass more than one variable to your function without additional work. Apply (for example, g @@ {x,y} ) works by replacing the Head expression ( {x,y} ) with a function that evaluates a function with several variables ( g@@{x,y} == g[x,y] ).

The method that I use to pass multiple variables to my functions using a postfix form is lists. This requires a bit more work, as I have to write

 {x,y} // f[ #[[1]], #[[2]] ]& 

to indicate which List element matches the corresponding parameter. I tend to do this, but you can combine this with Apply as

 {x,y} // f @@ #& 

which includes less text input, but may be harder to interpret when you read it later.

Change I must point out that the f and g above are just placeholders, they can and are often replaced with pure functions, for example. #+1& @ x is basically equivalent to #+1&[x] , see Leonid's answer .

To clarify, for Leonid's answer , the equivalence between f@expr and f[expr] is true if f does not have attribute , which would make it expr to evaluate the expression expr before passing to f . For example, one of the Attributes of Do is HoldAll , which allows it to act as a definition contour, which allows its parameters to be evaluated internally without canceling the external impact. The expr point will be evaluated before it is passed to f , so if you need it to remain unvalued, you need to take extra care, for example, to create a pure function with the Hold style attribute .

+6
source share

The proposed style is often possible, but impractical in the case of Do The problem is that Do has a HoldAll attribute. This is important because the loop variable ( x in the example) should remain unvalued and be treated as a local variable. To see this, try evaluating these expressions:

 x = 123; Do[Print[x], {x, 1, 2}] (* prints 1 and 2 *) {x, 1, 2} // Do[Print[x], #]& (* error: Do::itraw: Raw object 123 cannot be used as an iterator. Do[Print[x], {123, 1, 2}] *) 

The error arises because the pure function Do[Print[x], #]& does not have the HoldAll attribute, causing an estimate of {x, 1, 2} . You can solve the problem by explicitly defining a pure function with the HoldAll attribute this way:

 {x, 1, 2} // Function[Null, Do[Print[x], #], HoldAll] 

... but I suspect that the treatment is worse than the disease :)

Thus, when you use โ€œbindingโ€ expressions such as Do , Table , Module , etc., it is most safe to match the herd.

+10
source share

I think you need to learn how to use styles that Mathematica most naturally supports. Of course, there is more than one way, and my code is not like everyone else. However, if you keep trying to beat the Mathematica syntax into your own biased style based on another language, I do not foresee anything but continuing frustration for you.

Space is not evil, and you can easily add line breaks to separate long arguments:

 Do[ x[[i]] = (v[[i]] - U[[i, i + 1 ;; n]].x[[i + 1 ;; n]]) / U[[i, i]] , {i, n, 1, -1} ]; 

This suggests that I like to write using a larger number of prefixes ( f @ x ) and infix ( x ~ f ~ y ), which I usually see, and I consider it valuable because it is easy to determine that such functions receive one and two arguments respectively. This is somewhat non-standard, but I don't think it kicks traces of Mathematica syntax. Rather, I see this as using syntax in favor. Sometimes this causes syntax highlighting to fail, but I can live with this:

 f[x] ~Do~ {x, 2, 5} 

When using anything other than the standard form f[x, y, z] (with line breaks as necessary), you should be more careful with the order of evaluation, and IMHO readability may suffer. Consider this contrived example:

 {x, y} // # + 1 & @@ # & 

I do not find this intuitive. Yes, for someone who is close to the order of operations of Mathematica, it reads, but I believe that it does not improve clarity. I try to reserve a postfix // for the named functions, where reading is natural:

 Do[f[x], {x, 10000}] //Timing //First 
+8
source share

I would say that this is one of the biggest mistakes to try a program in language B ways that are idiomatic for language A , just because you are well aware of the latter and like it. There is nothing wrong with borrowing idioms, but you need to make sure that you understand the second language well enough so that you know why other people use it the way they do.

In the particular case of your example, and in general, I want to draw attention to some things that others have not talked about. First, Do is the scope of the scope that uses dynamic scaling to localize its iterator characters. Therefore you have:

 In[4]:= x=1; {x,1,5}//Do[f[x],#]& During evaluation of In[4]:= Do::itraw: Raw object 1 cannot be used as an iterator. >> Out[5]= Do[f[x],{1,1,5}] 

What a surprise, isn't it. This will not happen if you use Do standard way.

Secondly, note that although this fact is largely ignored, f[#]&[arg] does NOT always match f[arg] . Example:

 ClearAll[f]; SetAttributes[f, HoldAll]; f[x_] := Print[Unevaluated[x]] f[5^2] 5^2 f[#] &[5^2] 25 

This does not affect your example, but your use is close enough to the cases that it affected, since you are manipulating areas.

+7
source share

Of course, you can do this, as you obviously know. Personally, I would not worry about how the instructions write the code, and just write the way I find it natural and memorable.

However, I noticed that I usually fall under certain patterns. For example, if I create a list after some calculations and, by the way, I speak to make sure that I expected it, I usually do

 prodListAfterLongComputation[ args, ]//ListPlot[#,PlotRange->Full]& 

If I have a list, say lst , and now I am focused on creating a complex plot, I will do

 ListPlot[ lst, Option1->Setting1, Option2->Setting2 ] 

So, in principle, everything that is random and possibly not important for reading (I donโ€™t need to instantly analyze the first ListPlot , since it is not the point of this bit of code) ends up postfix so as not to violate the already written complex code to which it applies . On the contrary, complex code, which I usually write in a way that is easier for me to analyze later, that in my case there is something like

 f[ g[ a, b, c ] ] 

although more text input is required, and if you are not using the Workbench / Eclipse plugin, it does more work on code reorganization.

So, I believe that I would answer your question: "Do everything that is most convenient, given the need for readability and possible loss of convenience, such as code highlighting, additional work with refactoring code, etc."

Of course, all this applies if you work only with some part of the code; if there are others, this is all a different matter.

But this is just an opinion. I doubt anyone can offer more than that.

+5
source share

For functions with one argument (f@(arg)) , ((arg)//f) and f[arg] completely equivalent even in the sense of using the attributes f . For functions with multiple arguments, you can write f@Sequence[args] or Sequence[args]//f with the same effect:

 In[1]:= SetAttributes[f,HoldAll]; In[2]:= arg1:=Print[]; In[3]:= f@arg1 Out[3]= f[arg1] In[4]:= f@Sequence[arg1,arg1] Out[4]= f[arg1,arg1] 

So, it seems that the solution for those who like postfix notation is to use Sequence :

 x=123; Sequence[Print[x],{x,1,2}]//Do (* prints 1 and 2 *) 

Some difficulties may arise with functions that have the SequenceHold or HoldAllComplete :

 In[18]:= Select[{#, ToExpression[#, InputForm, Attributes]} & /@ Names["System`*"], MemberQ[#[[2]], SequenceHold | HoldAllComplete] &][[All, 1]] Out[18]= {"AbsoluteTiming", "DebugTag", "EvaluationObject", \ "HoldComplete", "InterpretationBox", "MakeBoxes", "ParallelEvaluate", \ "ParallelSubmit", "Parenthesize", "PreemptProtect", "Rule", \ "RuleDelayed", "Set", "SetDelayed", "SystemException", "TagSet", \ "TagSetDelayed", "Timing", "Unevaluated", "UpSet", "UpSetDelayed"} 
+1
source share

All Articles