Orderless storage of tabulated function values

I would like to elaborate on my previous question Function of arguments given in non-fixed order and a relevant related one Best practice of passing a large number of parameters to functions

I find that neither Options nor Association are perfect for what I need to do here, I am hoping to find some hint to a better solution here.

I have to handle a large set of data that are essentially the results of the evaluation of a very time-consuming function on some points and that now I want to store. What I want to achieve is a quick way to recall the results that I have stored and a quick way to store them when I evaluate the function.

Ideally I would like to achieve a point where I can do

MyStorage[lot of parameters]=LengthyFunction[lot of parameters]


so that I can recall the results as

MyStorage[lot of parameters]


If the list of parameters is small this is not a problem, you just use the two lines above. But when the list of parameters gets long it comes the trouble, because you need to put lot of parameters in the same order as the function is defined. This also comes with the additional difficulty that MyStorage[2,3,5] completely hides to you the meaning of the entries in the arguments sequence.

So I thought it was best to use something like MyStorage[{x->2,y->3,z->5}], which makes very explicit at what point you are calling the function. This, however, is not a solution, because the order of the arguments still matters. In fact defining MyStorage[{x -> 2, y -> 3, z -> 5}] = {a, d, b} then you can recall the stored value only by MyStorage[{x -> 2, y -> 3, z -> 5}] and not by MyStorage[{y -> 3, z -> 5, x -> 2}]

Using associations does not help either, because one runs in the exact same issue of ordering. So I am left with Options as the sole Orderless way to go. Unfortunately Options cannot be used directly in this example, unless I have missed something. The shortest way to use Options that I can think of is to use a "Put" and a "Get" function, that take Options as inputs and then do the job using an ordinary "ordered" function for storage of the type MyStorage[2,3,5].

    MyStoragePut[opt : OptionsPattern[], value_] := Module[{point},
point = {x, y, z} /. opt;
MyStorage[Sequence[point]] = value;
]


at this point you can fill in the storage without recalling the order of the variables. Indeed MyStoragePut[{y -> 3, z -> 99, x -> 2}, {2, 3, 34}] followed by MyStoragePut[{x -> 2, y -> 3, z -> 99}, {2, 23, 34}] would replace if the value at {x -> 2, y -> 3, z -> 99}.

To retrieve the values I define the "Get" method

   MyStorageGet[opt : OptionsPattern[]] := Module[{point},
point = {x, y, z} /. opt;
MyStorage[Sequence[point]]
]


which also works regardless of the order in which the values are specified: MyStorageGet[{x -> 2, y -> 3, z -> 99}] and MyStorageGet[{x -> 2, z -> 99, y -> 3}] give the same result stored in the symbol MyStorage[2,3,99].

I wanted to ask if this is a dumb way of reinventing a wheel that is already (better) implemented in Mathematica or there are anyways better strategies to achieve this orderless data storage and retrieval.

• In rescue of Association I should say that they are useful to take a long list of Options, and change the value of one of the options and obtain back a list of options. Say by doing ChangeOptionValue[mykey_, Val_, BaseParameters_] := Module[{myass = (Association[Sequence[BaseParameters]])}, myass[mykey] = Val; Normal[myass] ] – Rho Phi Jun 2 '15 at 10:02
• Related: (21354) – Mr.Wizard Jun 3 '15 at 16:46

I believe I have found something that may solve your problem. I discovered today:

DataUnorderedAssociation


This is an undocumented function that appears to work like Association at least in a limited set of operations, yet it puts its keys into a consistent order:

DataUnorderedAssociation /@ Permutations@{"d" -> 1, "b" -> 2, "a" -> 3, "c" -> 4}

SameQ @@ %

{<|"a" -> 3, "d" -> 1, "b" -> 2, "c" -> 4|>,
<|"a" -> 3, "d" -> 1, "b" -> 2, "c" -> 4|>, . . . }

True

• Note that this order is not the same as used by Sort.

The expression may be constructed piecewise and it still is the same:

uno = DataUnorderedAssociation[];

uno["d"] = 1;
uno["c"] = 4;
uno["b"] = 2;
uno["a"] = 3;

uno

<|"a" -> 3, "d" -> 1, "b" -> 2, "c" -> 4|>


Although formatted like Association the expression retains a different structure:

uno // FullForm

DataUnorderedAssociation[Rule["a", 3], Rule["d", 1], Rule["b", 2], Rule["c", 4]]

• Thanks for digging out this undocumented feature. I think is going to be useful! – Rho Phi Aug 29 '15 at 14:30

Using Orderless:

ClearAll[f]
SetAttributes[f, Orderless]
f[x[x_], y[y_], z[z_]] := {x, y, z}
f[x[1], z[3], y[2]]
(* Out: {1, 2, 3} *)


You have to make sure that the heads (x, y, z) do not have values.

If you prefer it you can also write the definition with prefix notation

f[x@x_, y@y_, z@z_] := {x, y, z}
f[x@1, z@3, y@2]


which is just a little bit shorter.

EDIT. I realize now that the question is also about quick lookup. george2079 has it right, you should use memoization for this. So it would look like this:

f[x@x_, y@y_, z@z_] := f[x,y,z] = {x, y, z}


This way it won't repeat calculations that is has already performed, regardless of in which order you put the arguments.

 Clear[f]
f[rules_] := f[Sort[rules]] = (Pause[2]; g[ x  , y ] /. rules )
f[{y -> 2, x -> 1}] // AbsoluteTiming
f[{x -> 1, y -> 2}] // AbsoluteTiming


{2.012387, g[1, 2]}

{0., g[1, 2]}

• But if you have more than two parameters and try another order of the parameters that is not canonical then it will run the function again unnecessarily, I believe. – C. E. Jun 3 '15 at 0:45