We can use some of Mathematica's built-in tracing facilities to help us answer this question.
Let's start by ensuring that the symbols we are about to use carry no extraneous definitions:
ClearAll[f, g, x]
Now, we'll establish the definitions from the question:
g[x_] := x^2
f[x_] := Sqrt[g[x]]
We can turn on selective tracing of some functions to see what is going on:
On[f, g, Sqrt]
Now, let's evaluate f
for several values (here only 3 instead of 100 to keep the output manageable):
Map[f, Range[3]]

This shows us that f
and g
are being evaluated from first principles for each value in the range.
Should we wish to evaluate f[x]
only once, applying the resultant expression to each value, then we must do something like this:
Map[Function[{x}, Evaluate@f[x]], Range[3]]

The expression defines an anonymous function of x, whose body is the result of evaluating f[x]
. It is very important that x
has no value at this point -- that is why we cleared it at the beginning. Having done that, we can now see that f
and g
were only evaluated for x
instead of for every range value.
Incidentally, observe that f[x]
ultimately evaluates to Sqrt[x^2]
rather than x
since Mathematica does not assume that x
can only be a positive real number.
The syntax used in the preceding example can be abbreviated if we use so-called "slot" notation (#
):
Map[Evaluate@f[#] &, Range[3]]

Again, we can see that f
and g
are not evaluated for each range element. The slot reference #1
has taken the place of the named variable x
from the previous example.
Once we have completed our analysis, and are tired of seeing all those trace messages, we can turn off tracing like this:
Off[]
Refine[Sqrt[x^2], x \[Element] Reals] (*Abs[x]*)
$\endgroup$