Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

Context

I'm writing a function that look something like:

triDiagonalQ[mat_] := MapIndexed[ #1 == 0 || Abs[#2[[1]]-#2[[2]]] <= 1 &, mat, {2}] //
     Flatten // And @@ # &

Now, things like #2[[1]] and #2[[2]] are somewhat hard to read. I'd prefer to do something like:

triDiagonalQ[mat_] := MapIndexed[ #1 == 0 || Abs[i-j] <= 1 &, mat, {2}] // 
    Flatten // And @@ # & (* with a {i, j} <- #2 somewhere *)

Question

Is there someway to do something like "destructuring" in Mathematica?


The following links convey what I mean by "destructuring":

(These have nothing to do with Mathematica; they're posted mainly to demonstrate what is meant by "destructuring")

share|improve this question
add comment

6 Answers

up vote 13 down vote accepted

You can use macros / code generation to be able to use the syntax you like. Here is one possibility:

ClearAll[withLiteralIndices];
SetAttributes[withLiteralIndices, HoldAll];
withLiteralIndices[code_, inds : {__Symbol}] :=
  Block[inds,
     Unevaluated[code] /.
       MapIndexed[
          Function[{i, pos}, pos /. {p_} :> (i :> #2[[p]])], 
          inds
       ]
  ]

Now, you can pretty much literally use the code you would like to use:

withLiteralIndices[
  triDiagonalQ[mat_] := 
     MapIndexed[#1 == 0 || Abs[i - j] <= 1 &, mat, {2}] // Flatten // And @@ # &,
  {i, j}
]

When you look at the resulting definition, you can see that this is entirely equivalent to hand-written code using slots:

?triDiagonalQ

(*
  Global`triDiagonalQ

  triDiagonalQ[mat_]:=
    (And@@#1&)[Flatten[MapIndexed[#1==0||Abs[#2[[1]]-#2[[2]]]<=1&,mat,{2}]]]
*)

EDIT

Here is a simpler and perhaps more elegant version of the macro, which uses the injector pattern more explicitly:

ClearAll[withLiteralIndices];
SetAttributes[withLiteralIndices, HoldAll];
withLiteralIndices[code_, inds : {__Symbol}] :=
  Block[inds,
     Unevaluated[code] /.
        Replace[
          Range[Length[inds]],
          p_ :> (inds[[p]] :> #2[[p]]),
          {1}
        ]
  ]
share|improve this answer
1  
I don't understand this yet, but I will meditate on it. You should write a "On Mathematica", like the Mathematica version of "On Lisp". –  user1602 Jul 17 '12 at 0:06
    
@term-rewritica I should first read some more elementary Lisp books, like ANSI CL by the same PG, or Seibel's book. It has been several years already that I promise this to myself. Thanks for the accept. –  Leonid Shifrin Jul 17 '12 at 0:07
add comment

I think Mr. Wizard provided a very thorough answer to the question. I would however like to add a slight example of wrapping this up nicely in a format similar to Function[] but using destructuring:

SetAttributes[dFunction, HoldAll]
dFunction[pattern_, body_][arg___] /;MatchQ[{arg}, pattern] := {arg} /. pattern :> body

This then allows you to have nice syntax for an anonymous destructuring function using pattern matching:

triDiagonalQ3[mat_] := And @@ Flatten@ MapIndexed[
   dFunction[{v_, {i_, j_}}, v == 0 || Abs[i - j] <= 1], mat, {2}]

Syntax

As to the "clumsiness" of this syntax, I have to agree with Mr. Wizard that it's not as nicely looking as replacement rules, but then again I don't particularly like Function[x,x^2] either, and much prefer the aesthetics of the built-in shorthand which looks like x ↦ x^2(entered via EscfnEsc). The above code can be made to use a similar shorthand rather simply by setting RightArrowBar = dFunction; which allows the definition to look like :

{v_, {i_, j_}}  ⇥  (v == 0 || Abs[i - j] <= 1)

And if one is afraid of using the build-in undefined infix operators, you could of course define a custom symbol for it which doesn't lay claim on RightArrowBar. Personally I find this appearance of an anonymous replacement rule based function to be quite nice.

The value of pattern matching

As was discussed in comments, one of the interesting behaviors of this construct is that it closely mirrors the behavior of a function defined through pattern matching. if f[x_Integer]:=... is defined and called with arguments not matching the pattern the function remains unevaluated. For instance f[2.3] will return itself. The same behavior can be observed with dFunction where for instance dFunction[{x_Integer}, x][3.2] will return itself unevaluated.

Sadly due to the nature of SubValues, it's not possible to fully simulate pattern matching functions in an anonymous form using this method, since attributes such as HoldAll cannot be implemented. I do belive that this could be implimented using devious hacks similar to the method employed here http://mathematica.stackexchange.com/a/5458/1194 by Leonid Shifrin. However I fear that even if I did mange to implement it I would never use the result for fear of unexpected behavior, while I quite like the simplicity of my current dFunction.

share|improve this answer
    
I usually like your answers (in fact I know through ♦ magic that I've upvoted your posts more than anyone else), so please don't take offense, but honestly how is that in any way superior to the forms that I show? It looks very clumsy to me compared to triDiagonalQ[mat_] := And @@ Flatten @ MapIndexed[#2 /. {i_, j_} :> # == 0 || Abs[i - j] <= 1 &, mat, {2}] –  Mr.Wizard Aug 23 '12 at 12:47
    
@Mr.Wizard I didn't claim that it was superior in any way. It's a stylistic choice I sometimes find I have a short function which doesn't do much, so I replace it with an anonymous function, however if the function relies on pattern matching, as is often the case for my MapIndexed functions, I can't easily do this. But with this definition it's no problem. So really it's a style choice. I decided to post it since Mike also commented that it was a shame Function didn't support destructuring. –  jVincent Aug 23 '12 at 12:52
    
+1 Now it would be nice to support attributes –  Rojo Aug 23 '12 at 12:53
1  
@Mr.Wizard And another thing, if you call something like f[_Integer]:=code;f[2.3] You get the unresolved "call". If you try to emulate this using your method, you get the unchanged input to the replacement, instead of getting the unresolved "call". To be specific 0.3/._Integer:>code yields 0.3, while dFunction[{_Integer},code][0.3] yields itself, meaning that your output specifies that you have the unresolved call, rather then just passing on the input to higher functions. Speedwice my best bet is that you win by a factor of 2 due to my version having two pattern checks. –  jVincent Aug 23 '12 at 12:58
    
+1 for the RightArrowBar thing, which makes this a lot more usable. You're now set up to collect a 100pt bounty. Congrats! –  Mr.Wizard Aug 23 '12 at 13:30
show 1 more comment

Leonid provides a nice method for doing this within "pure functions" but I think it should be pointed out that the common method for doing this is pattern matching.

I argue that destructuring is the foundational use of pattern matching in Mathematica.

Every replacement pattern, be it an explicit rule (:>, ->) or part of a definition (:=, =), that uses a named pattern on the left-hand side that does not match the entire expression or argument is doing destructuring.

Applied to your specific example:

f[a_, {i_, j_}] := a == 0 || Abs[i - j] <= 1

triDiagonalQ[mat_] := And @@ Flatten @ MapIndexed[f, mat, {2}]

Or:

triDiagonalQ[mat_] := And @@ Flatten @
   MapIndexed[#2 /. {i_, j_} :> # == 0 || Abs[i - j] <= 1 &, mat, {2}]

The second example is almost exactly what you asked for: "with a {i, j} <- #2 somewhere"
It's just turned around: #2 /. {i_, j_}.

This destructuring is common in Mathematica programming for experienced users.

Among many examples:

Here I use it with to separate a + b + c:

(a + b + c) /. head_[body___] :> {head, body}  (* Out= {Plus, a, b, c} *)

Here Leonid uses it in a recursive function. ({x_, y_List})

Szabolcs uses it in iter, also recursive.

Heike uses it with /. in PerforatePolygons and with := in torn.

Here I used it simply in formula but also in MakeBoxes[defer[args__], fmt_] := where the parameter pattern defer[args__] serves to match the literal head defer while also destructuring.

In withOptions it is used both in the function definition and in the replacement rule.

The "injector pattern" is a form of destructing.

I also used it in inside, withTaggedMsg, pwSplit, dPcore etc.


Another, simpler form of destructuring exists in the form of Set and List ({}). A matching List structure on the left and right sides of = will assign values part-wise.

{{a, b}, c, {d}} = {{1, 2}, 3, {4}};

{a, b, c, d}
 {1, 2, 3, 4}

This is used e.g. in the first LUDecomposition example, and R.M uses it here.

share|improve this answer
1  
This is a very cute use of pattern matching. Never thought of it. –  user1602 Jul 17 '12 at 9:11
    
(purely out of courisity) Can pattern matching be combined with anonymous functions (i.e. #1 #2 ... &), or is it only for named functions? –  user1602 Jul 17 '12 at 9:11
2  
@term-rewritica it is not just a "cute" use, it is the foundational use. This is destructuring in Mathematica. My second example is a combination of pattern matching and anonymous functions. The second example is almost exactly what you asked for: "with a {i, j} <- #2 somewhere" -- it's just turned around (#2 /. {i_, j_}). –  Mr.Wizard Jul 17 '12 at 18:02
    
you're absolute right, I missed the ingenuinity of the /. {i+, j_} :> in the second example. :-) I'm going to try to work this into my "default/habitual mathematica style" –  user1602 Jul 18 '12 at 1:00
    
+1, to file under 'it just works'. –  Ryogi Aug 21 '12 at 0:59
add comment

My first answer explains that Mathematica's replacement rules perform destructuring. This answer is intended to complement jVincent's method, which I see is appreciated.

My aim is to provide Attributes for the pattern-based function. This requires that the head evaluate therefore SubValues may not be used. Here are two separate approaches.

Module

A direct approach is to automate the creation of a new DownValue function using Module.

SetAttributes[pFunc1, HoldAll]

pFunc1[pattern_, body_, attrib_: {}] :=
  Module[{pf},
    SetAttributes[pf, attrib];
    pf[pattern] := body;
    pf
  ]

pFunc1[_[__, b_, c_], Binomial[b, c], HoldAllComplete][9 + 7 + 6 + 3]

pFunc1[PatternSequence[_[__, b_, c_], x_], Binomial[b, c]/x, HoldAllComplete][9 + 7 + 6 + 3, 4]
20

5

ReplaceAll

The first method creates a new symbol pf$xxx for each use which could be seen as undesirable. A method that does not is to simply embed a ReplaceAll operation in the body of Function like this:

SetAttributes[pFunc2, HoldAll]

pFunc2[pattern_, body_, attrib_: {}] :=
  Function[
    Null,
    Unevaluated@{##} /. pattern :> body,
    attrib
  ]

This method returns an actual Function expression rather than a pf$xxx symbol.
It also allows mixing Slot (#) and pattern references in body should that be desired.

It however does not return an unevaluated function expression when the arguments do not match the pattern. (Though one could add a {__} :> $Failed type of rule to /., if that is all that is desired.)

share|improve this answer
add comment

You can use Subtract or Differences to get the differences in the index and use FreeQ to test if every element is True. This removes the need for the ugly #2[[1]]-#2[[2]] and also the Flatten and And@@ at the end.

triDiagonalQ[mat_?MatrixQ] :=FreeQ[MapIndexed[(#1 == 0 || Abs[Subtract @@ #2] <= 1) &, 
      mat, {2}], False]

The pattern test _?MatrixQ ensures that you only operate on matrices.

share|improve this answer
1  
This solution is more elegant than more. However, I'd still like to know how to do destructuring (if possible) :-) –  user1602 Jul 17 '12 at 0:00
    
@term-rewritica what do you mean by "destructuring"? Is it a common term that I'm missing or are you referring to something that way? A google search for the term brings this question among the very top hits (which is scary!) –  rm -rf Jul 17 '12 at 0:01
    
@RM: edited question to add links to "destructuring". It basically says: (1) here is a piece of data (2) here is an expression with a bunch of variables; figure out what values to assign to the variables so that (1) == (2). –  user1602 Jul 17 '12 at 0:05
add comment

Perhaps you could just use With in a Function?

lst = ConstantArray[0, {3, 3}];
MapIndexed[
   Function[{value, pos}, 
      With[{i = pos[[1]], j = pos[[2]]}, 
         {value, i, j}
      ]
   ], 
lst, {2}]
share|improve this answer
    
It's a shame that neither With[] nor Function[] support destructuring. –  M.R. Jul 16 '12 at 23:20
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.