2
$\begingroup$

(I'm not sure if the title of this question is appropriate, sorry!)

After defining selfApply function that resolves the mutual references contained in Association, I wrote the following code:

selfApply[x_Association] := x //. x;

u[a_] := Module[{f},
  f[t_] := t^2; (*f is actually intended to be non-Listable function *)
  
  selfApply@<|"x" -> 2 a, "y" -> (f /@ "x"), "z" -> "y"[[2]]|>
  ]
u[Range[3]]

The result I expect is <|"x" -> {2, 4, 6}, "y" -> {4, 16, 36}, "z" -> 16|>, but the output is

Part::partd: Part specification y[[2]] is longer than depth of object.
<|"x" -> {2, 4, 6}, "y" -> {2, 4, 6}, "z" -> 4|>

with warning.

It is obvious that it stucks at the stage of evaluating f /@ "x" and "y"[[2]], so I tried to rewrite it as follows using RuleDelayed:

selfApply[x_Association] := x //. x;

u[a_] := Module[{f},
  f[t_] := t^2;
  
  selfApply@<|"x" -> 2 a, "y" :> f /@ "x", "z" :> "y"[[2]]|>
  ]
v = u[Range[3]]
v["y"]
v["z"]

and got outputs:

<|"x" -> {2, 4, 6}, "y" :> f$729435 /@ {2, 4, 6},"z" :> (f$729435 /@ {2, 4, 6})[[2]]|>
{4, 16, 36}
16

The results of v["y"],v["z"] are just what I want, but this behavior is uncomfortable in the following ways:

  1. It appears that f is not evaluated when the result is assigned to v, but is evaluated when specifically calling v["y"]. Because of this, calculation results are unsure just by calling v. Also, if the processing of f is complicated, there is a concern that performance will be affected because f will run every time an element of v is accessed.

  2. The scope of f defined in Module is leaking, and the definitions multiply with each execution. Example:

s; s; s;
Names["Global`f$*"]

and output:

{"f$", "f$9454", "f$9455", "f$9456"}

The definitions of the form f$* increases each time s is executed.


My naive idea of using Evaluate also didn't work:Evaluate /@ u[Range[3]] or AssociationMap[Evaluate, u[Range[3]]].

I feel like my idea of ​​using RuleDelayed is possibly fundamentally wrong... Is there any elegant solution?

$\endgroup$
8
  • 1
    $\begingroup$ If you insist on selfApply (which I don't understand the motivation), //AssociationThread[Keys[#],Values[#]]& can evaluate the delayed values. $\endgroup$
    – Lacia
    Commented Nov 8, 2023 at 2:49
  • $\begingroup$ The scope of f is not leaking - it's the designed behaviour. Every time Module is evaluated, the local vars will be generated to ensure the uniqueness. Why enclose f? You can simply hide it in some other context. $\endgroup$
    – Lacia
    Commented Nov 8, 2023 at 2:51
  • $\begingroup$ I often use the strategy of selfApply when solving physical systems with a large number of parameters and their relationships mixed together. For example: selfApply@<|diam -> 1., dens -> 3., vel -> 2., area -> Pi (diam/2)^2, flux -> dens*vel*area|> , while I'm not sure if this way is common. $\endgroup$
    – user14061
    Commented Nov 8, 2023 at 3:53
  • $\begingroup$ Using AssociationThread looked trivial at first, but it seems to meet my needs successfully. Thank you! $\endgroup$
    – user14061
    Commented Nov 8, 2023 at 3:59
  • $\begingroup$ And yes, I know that local variables in a Module will be added to the global context. No matter how many times I run Module[{x}, x = 1]; Names["x*"], the result will be {x}, while the example I presented will multiply variables of the form f$* in the context each time it is run. This is what I'm concerned about since it can be interpreted as a kind of memory leak. $\endgroup$
    – user14061
    Commented Nov 8, 2023 at 4:13

1 Answer 1

2
$\begingroup$

Besides reconstruct the association via e.g. AssociationThread, you can also use the trick of in-place evaluation:

selfApply[x_Association] := x //. x;

u[a_] := Module[{f},
  f[t_] := t^2;
  <|"x" -> 2 a, "y" :> f /@ "x", "z" :> "y"[[2]]|>//selfApply//Replace[#,x_:>RuleCondition@x,{1}]&
  ]
v = u[Range[3]]

(*<|"x" -> {2, 4, 6}, "y" :> {4, 16, 36}, "z" :> 16|>*)

see e.g. https://mathematica.stackexchange.com/a/128456/86893. The undocumented RuleCondition (but quite stable across different versions) can also be replaced by documented functions, see e.g. https://mathematica.stackexchange.com/a/29318/86893

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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