pipes in Mathematica
- To: mathgroup at yoda.ncsa.uiuc.edu
- Subject: pipes in Mathematica
- From: jacobson at cello.hpl.hp.com
- Date: Tue, 13 Aug 91 09:51:29 PDT
I use Mathematica a lot for data analysis. I like to use a functional programming style, but I have found this is more difficult in Mathematica than it ought to be. The problem is that when using functional programming, particularly in data analysis, we are usually starting with some list of values, and passing it successively through a sequence of operations. The problem is that some functions, such as Select, Take, Drop, and Transpose, have their "data" or "expr" argument first and their control arguments last. So you get into this situation of Drop[ <<< giant 12-line function>>>, 2] and sometimes that is embedded in something bigger. It is very difficult to tell that the "2" goes with the drop. For a possible solution, let us look at the tremendously successful pipe notation of the Unix C-shell. foo a | grep math | cut -2 | sort This means start with foo a, and pipe its result into grep math, and pipe its result into cut -2 and pipe is result into sort. Mathematica can do the same thing, but only if the functions (after the first) take exactly one argument. For example: foo[a,b,c]//Fourier//Reverse At some small runtime expense we can make this work with functions of all types foo[a,b,c]//Fourier//Drop[#,3]&//Map[1+1/#&,#]& Note that the last thing in the pipe got confusing because there are 2 #s close to each other that mean different things. (Maybe we could write .../Map[Function[x,1+1/x],#]&, but it is more verbose and looks confusing to me.) To overcome this, I have developed a hack that is working quite well for me. $$$/: Literal[f_[left___,$$$,right___]] := Function[x,f[left,x,right] This lets me write the above as foo[a,b,c]//Fourier//Drop[$$$,3]//Map[1+1/#&,$$$] where $$$ appearing in a top-level argument causes the expression to be converted to a function that accepts its input in that position. Note that only one $$$ is allowed. You must also be careful with using this in infix expressions, since what looks like a top-level parameter is not always. For example foo // 2/$$$ // bar would not do the right thing. If you want to put this into a file that you read in, you will have trouble if you reread the file. This what I actually use. ===================== $$$Switch=False Unprotect[$$$] $$$/: Literal[f_[left___,$$$,right___]] := Function[x,f[left,x,right], {HoldAll}]/; $$$Switch (* The HoldAll attributed keeps the Function from evaluating its argument, but normally, f will evaluate it anyway. This maintains exact compatibility with the normal function calling semantics. *) Protect[$$$] $$$Switch=True =================== I'm using 2.0beta3. Version 1.2 does not do true static scoping of varibles, so 1.2 users may wish to change "x" above to something like "Private`x" to be safe. They will also have to drop the {HoldAll} parameter. I'm hoping to find a scheme that is less of a hack than this. I would really like a system with a new symbol, such as /// that would mean "turn the thing to the right into a function and pipe the stuff to the left into it." Some special symbol, such as ###, would designate the formal parameter, which would not have to appear at the top level, and possibly might appear more than once. The construct should work properly when nested. Suggestions are welcome. -- David Jacobson