Match items in list, forward and reverse order without sorting list or defining list as Orderless

I'm using the following code to pick out (match) two specified items in a list (specified as anum_ and bnum_), change them both and then return an output list. The output list has the two specified items changed but the rest left intact:

qubits = {qubit[1, "x", "y", "z"], qubit[2, "x", "y", "z"], qubit[3, "x", "y", "z"]};

processQubits[anum_, bnum_, {before___, qubit[anum_, s1_, s2_, s3_], middle___, qubit[bnum_, t1_, t2_, t3_], after___}] := {before, qubit[anum, s1<>t1, s2<>t2, s3<>t3], middle, qubit[bnum, s1<>t1, s2<>t2, s3<>t3], after}

(*Matches and returns "xx", "yy", "zz" etc.*)
processQubits[1, 2, qubits]
(*Does not match due to ordering*)
processQubits[2, 1, qubits]

Out[4]= {qubit[1,xx,yy,zz],qubit[2,xx,yy,zz],qubit[3,x,y,z]}

Out[6]= processQubits[2,1,{qubit[1,x,y,z],qubit[2,x,y,z],qubit[3,x,y,z]}]


Unsurprisingly, the match succeeds when I list the items in the order in which they appear in the list (e.g. processQubits[1, 2, ..], but not when I list them in reverse order (e.g. processQubits[2, 1, ...]).

Is there a well-used idiom to match these two items and return an output list regardless of which order the items are specified in?

Does adding an ancillary definition for processQubits such as:

processQubits[anum_, bnum_, rest__] :=
processQubits[bnum, anum, rest] /; ! OrderedQ[{anum, bnum}]


do what you want, i.e. both calls above return the same thing?

This extra definition ensures that anum and bnum always appear in the argument list of processQubits in "canonical" order. In effect it is applying the Orderless attribute to only the first two arguments.

• Thanks. I assume that your solution uses the fact that a list of arguments in Mathematica is considered as a standard list and the !OrderedQ lets the engine know that they can appear in any order? Would be great if you could add a little more info about how the solution works; it's quite a step for folks like me who are a little more used to procedural programming languages! – David B Sep 29 '17 at 8:56
• A slightly cleaner form, IMHO: /; Order[anum, bnum] < 0( @DavidB ) – Mr.Wizard Sep 29 '17 at 9:28

In versions 10.1+ there is OrderlessPatternSequence

ClearAll[pQubits]
pQubits[OrderlessPatternSequence[anum_, bnum_], {before___, qubit[anum_, s1_, s2_, s3_],
middle___, qubit[bnum_, t1_, t2_, t3_], after___}] :=
{before, qubit[anum, s1 <> t1, s2 <> t2, s3 <> t3], middle,
qubit[bnum, s1 <> t1, s2 <> t2, s3 <> t3], after}


Examples:

pQubits[1, 2, qubits]


{qubit[1,"xx","yy","zz"], qubit[2,"xx","yy","zz"], qubit[3,"x","y","z"]}

pQubits[2, 1, qubits]


{qubit[1,"xx","yy","zz"], qubit[2,"xx","yy","zz"], qubit[3,"x","y","z"]}

pQubits[3, 2, RotateRight@ qubits]


{qubit[3,"xx","yy","zz"], qubit[1,"x","y","z"], qubit[2,"xx","yy","zz"]}

pQubits[2, 3, qubits]


{qubit[1,"x","y","z"], qubit[2,"xx","yy","zz"], qubit[3,"xx","yy","zz"]}