4
$\begingroup$

I have two functions, and the 1st calls the 2nd:

f[x_]:=Block[{},x^2]

g[y_]:=Block[{},Sin[f[y]]]

I would like to know, in each function how deep I am in the calling stack. If I execute g[1.2], the depth in g is going to be 1, and the depth in f is going to be 2. But If I were to define

h[z_]:Block[{},Cos[g[z]]]

and execute h[1.3], the depth in g is going to be 2, and the depth in f is going to be 3.

I would like to know how I can systematically find this depth.

Thank you

$\endgroup$
2
  • 1
    $\begingroup$ Take a look at Stack. $\endgroup$
    – Domen
    Feb 3 at 20:06
  • $\begingroup$ g also calls Sin, and f also calls Power (and both call Block), so how do you want to define depth? If you are just interested in depth of f, then as @Domen suggested, you could add Stack somewhere in f, maybe f[x_] := Block[{stack = Stack[]}, Print[stack]; x^2] (but you'll still have to determine what depth means for that). You might also be interested in Trace. It also depends on what you mean by "systematically". Do you want a mechanism that injects the Stack for a specified symbol or something? $\endgroup$
    – lericr
    Feb 3 at 20:28

4 Answers 4

3
$\begingroup$

Using Stack and measuring its depth, as suggested by @Domen:

depthfunc[name_, expr_] := (Echo[(Length[Stack[]] - 3)/2, name]; expr)

f[x_] := depthfunc["f", x^2]
g[y_] := depthfunc["g", Sin[f[y]]]
h[z_] := depthfunc["h", Cos[g[z]]]

h[1.3]
(*    f  3    *)
(*    g  2    *)
(*    h  1    *)
(*    0.54626    *)

update: cleaner depth calculation

Assuming a bit less about the structure of the call stack:

depthfunc[name_, expr_] := With[{s = Stack[]},
  Echo[1 + Length[DeleteCases[s, depthfunc | With | CompoundExpression]], name];
  expr]

f[x_] := depthfunc["f", x^2]
g[y_] := depthfunc["g", Sin[f[y]]]
h[z_] := depthfunc["h", Cos[g[z]]]

h[1.3]
(*    f  3    *)
(*    g  2    *)
(*    h  1    *)
(*    0.54626    *)

f[f[f[f[1.3]]]]
(*    f  4    *)
(*    f  3    *)
(*    f  2    *)
(*    f  1    *)
(*    66.5417    *)

It's interesting to see that Nest is tail-recursive and calls f iteratively at the same level instead of doing f[f[f[f[1.3]]]] as above:

Nest[f, 1.3, 4]
(*    f  2    *)
(*    f  2    *)
(*    f  2    *)
(*    f  2    *)
(*    66.5417    *)
$\endgroup$
3
$\begingroup$

You may program your own stack. In every function, increase the counter, calculate the result, then decrease the counter and return the result. E.g.:

count = 0;
stack = {};
f[x_] := 
 Block[{}, AppendTo[stack, "f:" <> ToString[++count]]; t = x^ 2; 
  count--; t]
g[y_] := 
  Block[{t}, AppendTo[stack, "g:" <> ToString[++count]]; 
   t = Sin[f[y]];  count--; t];
h[z_] := 
  Block[{t}, AppendTo[stack, "h:" <> ToString[++count]]; 
   t = Cos[g[z]]; count--; t];

If you now write:

h[x]
stack

you get:

Cos[Sin[x^2]]
{"h:1", "g:2", "f:3"}
$\endgroup$
0
$\begingroup$

I think the idea of depth is not well defined in this question as stated. But I'll assume that what is wanted is some sort of dependency-based depth that targets only certain symbols (which would be h, g, f in the given example). We can inspect the DownValues for a given symbol and extract the symbols it depends on. If we do this recursively, we can build a dependency graph. Once we have that, we can do the normal graph operations, including GraphDistance (which would be analogous to depth according to this definition that I'm working with).

We'll build up to the whole dependency graph in stages. First, it's easy to get primary degree dependencies. From these, we'll create "links", i.e. rules that indicate directional dependency. Once we have links, we can recursively add new links by finding the next degree of dependencies. We'll continue until nothing new is discovered. At that point, we'll have links that we can use as edges in a graph.

Someone with more experience with inspecting DownValues and carefully managing execution could probably clean this up, but here's what I've got:

Dependencies[symbolSpace : {___String}][symbol : (_Symbol | _String)] :=
  Block[
    {$hold},
    SetAttributes[$hold, HoldAllComplete];
    Intersection[
      symbolSpace,
      DeleteCases[
        Cases[
          MapAt[$hold, DownValues[symbol], {All, 2}][[All, 2]], 
          s_Symbol :> ToString[Unevaluated[s]], 
          Infinity, 
          Heads -> True], 
        ToString[$hold]]]];
Dependencies[symbolSpace : {___String}][symbols : {(_Symbol | _String) ...}] := 
  DeleteDuplicates[Flatten[Dependencies[symbolSpace] /@ symbols]];
DependencyLinks[symbolSpace : {___String}][symbol : (_Symbol | _String)] := 
  Thread[symbol -> Dependencies[symbolSpace][symbol]];
DependencyLinks[symbolSpace : {___String}][symbols : {(_Symbol | _String) ...}] := 
  DeleteDuplicates[Flatten[DependencyLinks[symbolSpace] /@ symbols]];
DependencyGraphStep[symbolSpace : {___String}][links : {___Rule}] := 
  Union[links, DependencyLinks[symbolSpace][Values@links]];
DependencyGraph[symbolSpace : {___String}][symbol : (_Symbol | _String)] := 
  DependencyGraph[symbolSpace][{ToString[symbol]}];
DependencyGraph[symbolSpace : {___String}][symbols : {(_Symbol | _String) ...}] := 
  Graph[FixedPoint[DependencyGraphStep[symbolSpace], DependencyLinks[symbolSpace][ToString /@ symbols], 4]]

We can try it out:

f[x_] := f[x, 1];
f[x_, y_] := x + y;
g[x_] := f[x]^2;
g[x_, y_] := f[x, y]^y;
h[x_] := Cos[g[x]];

test = DependencyGraph[{"h", "g", "f"}][{h}];
Graph[test, VertexLabels -> Automatic]

enter image description here

And

GraphDistance[test, SymbolName[h], SymbolName[f]]

2

Something a bit more interesting maybe:

test2 = DependencyGraph[Names["Global`*"]][{h}];
Graph[test2, VertexLabels -> Automatic]

enter image description here

Caveat: this depends on DownValues only, so SubValues and OwnValues would need to be added if that degree of completeness were required.

$\endgroup$
0
$\begingroup$

thank you all. Yes what I need is:

ls = Length[Stack[]];

Print["Calling depth = ", (ls - 1)/3];

$\endgroup$
1
  • $\begingroup$ This seems to be more appropriate as a comment or an edit the OP and not as an answer. Please act appropriately. $\endgroup$
    – bmf
    Feb 5 at 10:52

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.