Although this doesn't directly answer the question involved, I would personally try to avoid constructs such as Return
, and rewrite the implementation using NestWhile
which is a very good fit for the task:
ClearAll[approxRootNewton];
approxRootNewton[f_, tolerance_, x0_] :=
NestWhile[
(* Compute next guess and iteration count. *)
Apply[Function[{x, count}, {x - f[x]/f'[x], count + 1}]],
(* Initial arguments. *)
{x0, 0},
(* Continue as long as this condition is true. *)
Apply[Function[{x, count}, f[x] < 0 || f[x] > tolerance]]] //
Apply[
(* Apply to the result. *)
Function[{x, count},
Print[N[x, 1 + Max[Log10[x], 0] - Log10[tolerance]],
" after ", count, " iterations"];
x]]
approxRootNewton[Function[x, x^2 - 2], 10^-6, 1]
(* 1.414214 after 4 iterations *)
(* 665857/470832 *)
This code differs from yours slightly, it prints and returns the first estimate ("x0
") which fulfils the tolerance criterion, not the one calculated after it ("x1
").
One might ask "what's that Apply
?" The most convenient way to pass arguments to and inside NestWhile
is a list, but unless one defines explicit pattern-matching functions which take this into account (f[{x_, count_}] := ...
), one has to explicitly extract list members from these lists, which is tedious and doesn't ease understanding of code. If one wraps a Function
with Apply
, elements of the list are interpreted as individual arguments.
There's an alternative way to accomplish this using Association
s (<| ... |>
) and their special handling by Function
s (&
shorthand):
ClearAll[approxRootNewton];
approxRootNewton[f_, tolerance_, x0_] :=
NestWhile[
(* Compute next guess and iteration count. *)
<|"x" -> #x - f[#x]/f'[#x], "count" -> #count + 1|> &,
(* Initial arguments.*)
<|"x" -> x0, "count" -> 0|>,
(* Continue as long as this condition is true. *)
f[#x] < 0 || f[#x] > tolerance &] //
(* Apply to the result. *)
(Print[N[#x, 1 + Max[Log10[#x], 0] - Log10[tolerance]],
" after ", #count, " iterations"];
#x) &
approxRootNewton[Function[x, x^2 - 2], 10^-6, 1]
(* 1.414214 after 4 iterations *)
(* 665857/470832 *)
And now we head truly to the off-topic end of this subject.
These methods are roughly equally efficient but sadly this, in my opinion less convenient version can be over a magnitude faster if functions themself are relatively fast to execute:
ClearAll[approxRootNewton];
approxRootNewton[f_, tolerance_, x0_] :=
NestWhile[
(* Compute next guess and iteration count. *)
With[{x = #[[1]], count = #[[2]]}, {x - f[x]/f'[x], count + 1}] &,
(* Initial arguments. *)
{x0, 0},
(* Continue as long as this condition is true. *)
With[{x = #[[1]]}, f[x] < 0 || f[x] > tolerance] &] //
(* Apply to the result. *)
With[{x = #[[1]], count = #[[2]]},
Print[N[x, 1 + Max[Log10[x], 0] - Log10[tolerance]],
" after ", count, " iterations"];
x] &
approxRootNewton[Function[x, x^2 - 2], 10^-6, 1]
(* 1.414214 after 4 iterations *)
(* 665857/470832 *)
Even faster code can be constructed, but personally I shoot first for clarity, and try to avoid premature optimisation.
;
suppresses the returned value. A simple example of this isf[x_] := Module[{y = 2 x}, y;]
. $\endgroup$;
in the long run it's probably best to avoid constructs like this. More Mathematica'esque way would be to rewrite the function usingNestWhile
, or such. $\endgroup$NestWhile[Apply[Function[{f, tol, x, count}, {f, tol, x - f[x]/f'[x], count + 1}]], {Function[x, x^2 - 2], 10^-6, 1, 0}, Apply[Function[{f, tol, x, count}, f[x] < 0 || f[x] > tol]]]
$\endgroup$Module
isn't returningNull
.Print
returns it. $\endgroup$