3
$\begingroup$

I'm working on some classical data structures (stack, queue, etc.) and want to mimic oo style in MMA. As a first attempt, I want to store an array in an Association, like this:

q = <|elems -> ConstantArray[Null, 4]|>
<|elems -> {Null, Null, Null, Null}|>

Later, I want to side-effect my array, like this

q[elems][[2]] = 42;

Set::setps: q[elems] in the part assignment is not a symbol. >>

Ahh, yes, of course, I need a symbol... Next attempt is this:

q = Module[{storage = ConstantArray[Null, 4]},
  <|elems -> Hold[storage]|>]
<|elems -> Hold[storage$1987]|>
In[4]:= ReleaseHold[q[elems]][[2]] = 42

During evaluation of In[4]:= Set::setps: ReleaseHold[q[elems]] in the part assignment is not a symbol. >>

 42

Oh, yeah, that's not going to work.

I could do the following, but it's going to copy the array every time and defeat the purpose of implementing classical algorithms (that being "efficiency"):

q = <|elems -> ConstantArray[Null, 4]|>;
SetAttributes[setQ, HoldFirst];
setQ[q_, slot_, item_] :=
  Module[{newElems = q[elems]},
   newElems[[slot]] = item;
   q[elems] = newElems;
   q[elems]];
setQ[q, 2, 42]
{Null, 42, Null, Null}

It looks like I need some kind of variant of Part that doesn't evaluate a held symbol on its left-hand side -- a PartHoldFirst. I don't see a way to do this with stuff I know.

$\endgroup$
4
$\begingroup$

Maybe I don't understand what you're trying to do, but...

q = <|elems -> ConstantArray[Null, 4]|>

q[[1, 2]] = 42;

q
<|elems -> {Null, 42, Null, Null}|>
q[[Key[elems], 4]] = 99;
<|elems -> {Null, 42, Null, 99}|>

So whether you set by position or Key it works.

$\endgroup$
  • $\begingroup$ Interesting -- you can index into Associations with Part (double brackets) & Key (for non-string keys) as well as with single-brackets, function-call notation, and then you can continue to index into them with commas in the double brackets, similarly to levels in ordinary lists. I hadn't appreciated this fact deeply enough. $\endgroup$ – Reb.Cabin Aug 17 '14 at 13:07
  • $\begingroup$ A bigger question is "why have the function-call notation at all?" It seems the double-bracket does at least as much, plus it obviously does much more, enabling whole scenarios like oop-emulation. $\endgroup$ – Reb.Cabin Aug 17 '14 at 13:25
  • $\begingroup$ Another implication of positional notation is that order does matter, that, intentionally, <|a->1,b->2|>=!=<|b->2,a->1|> but Sort[<|a->1,b->2|>]===Sort[<|b->2,a->1|>]. I sometimes wonder how web programmers deal with the fact that determining equality of two JSON objects without a canonical ordering on keys is combinatorial in the number of keys. Maybe JSON objects have a canonical ordering on keys. $\endgroup$ – Reb.Cabin Aug 17 '14 at 13:51
  • 1
    $\begingroup$ I found out something that functional notation [...] can do and that part notation [[...]] can't do: side-effectfully add a key. Let t = <|"a"->1|>, then t[["b"]]=2 succeeds and produces 2, but t is still <|"a"->1|>. Now t["b"]=2 succeeds and produces 2, but t is now modified and equals <|"a"->1, "b"->2|>. $\endgroup$ – Reb.Cabin Aug 19 '14 at 2:06
  • $\begingroup$ @Reb Thanks for the note. $\endgroup$ – Mr.Wizard Aug 19 '14 at 4:44
3
$\begingroup$

I had a flash of insight and did the following. Notice particularly the necessary RuleDelayed on the elems tag in the association:

q = Module[{storage = ConstantArray[Null, 4]},
      <|elems :> storage, set -> ((storage[[#1]] = #2) &)|>];
q[set][2, 42];
q[elems]
{Null, 42, Null, Null}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.