1
$\begingroup$

I'm currently trying to implement Newton's method of approximating roots to approximate the root of 2. I looked into some possible reasons as to why it would return Null, such as the scope of the Return statement from this post: Returning a value from a Module but adding Module to the Return statement has not seemed to do anything.

approxRootNewton[tolerance_,x0_,count_]:=
Module[{f,x1,check,output},
 f[x_]=x^2-2;
 x1=x0-(f[x0]/f'[x0]);
 If[f[x0]>0&&f[x0]<tolerance,
  Print[x1//N," after ",count," iterations"];
  Return[x1,Module]
  ,
  approxRootNewton[tolerance,x1,count+1];
 ];
];
Print[approxRootNewton[1*10^-6,1,0]]

The output is:

1.41421 after 4 iterations

Null

I've also tried putting static values in place of x2 but the function still ends up returning Null.

$\endgroup$
6
  • 1
    $\begingroup$ remove the extra ";" at end like this !Mathematica graphics $\endgroup$
    – Nasser
    Feb 7, 2023 at 15:56
  • $\begingroup$ The superfluous use of ; suppresses the returned value. A simple example of this is f[x_] := Module[{y = 2 x}, y;]. $\endgroup$ Feb 7, 2023 at 16:05
  • $\begingroup$ Although in this specific case the cause is an extra ; in the long run it's probably best to avoid constructs like this. More Mathematica'esque way would be to rewrite the function using NestWhile, or such. $\endgroup$
    – kirma
    Feb 7, 2023 at 16:12
  • $\begingroup$ ... and here's how I would write this code: 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$
    – kirma
    Feb 7, 2023 at 16:30
  • $\begingroup$ Module isn't returning Null. Print returns it. $\endgroup$ Feb 7, 2023 at 17:01

2 Answers 2

2
$\begingroup$

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 Associations (<| ... |>) and their special handling by Functions (& 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.

$\endgroup$
1
$\begingroup$

The comments basically answered your question, but maybe not as clearly as you might want. Try this:

approxRootNewton[3, 2, 0]

You get the printout "1.5 after 0 iterations", but you also get an actual, non-Null return value of 3/2. So, your function can produce non-Null output. So why isn't it doing so with something like the following?

approxRootNewton[.1, 2, 0]

Well, notice that this time we need to recurse once. So, the result of approxRootNewton[.1, 2, 0] is actually the result of approxRootNewton[.1, 3/2, 1];. Notice the ;. This is a CompoundExpression, and the way you've structured it, that particular CompoundExpression will always return Null. No matter how many more recursions we do, this will always bubble back up to something like CompoundExpression[<some numeric value>,Null].

Now, there's another interesting part to this. Notice that after evaluating Print[approxRootNewton[1*10^-6, 1, 0]] there is no actual output cell! The front end doesn't create an output cell if the result is just Null. The Null that you're seeing is what the Print expression is producing. So, we have a Print inside of your function that creates the 1.41421 after 4 iterations bit, and you've wrapped your function in another Print--two Prints.

General comment 1: Prints are not needed to display output. Whatever an expression eventually evaluates to is what will end up being displayed (unless that happens to be Null).

General comment 2: This is not a very idiomatic way to solve this problem in Mathematica. @kirma is showing you an alternate, and you can probably find several other alternates if you search this site. So, I won't provide yet another one here (and strictly speaking it's outside the scope of your question anyway).

$\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.