Will the bindings defined in letfn be updated every time sqrt is called?

Provided that abs and avg defined:

 (defn sqrt [x] (letfn [(s [guess] (if (good-enough? guess) guess (s (improve guess)))) (good-enough? [guess] (< (abs (- (square guess) x)) 0.0001)) (improve [guess] (avg (/ x guess) guess))] (s 1.0))) 

Please ignore for a second that I am reinventing the wheel here. :) This is just an example.

If so, is there a way around this so that the names are not linked again and again each time the function is called and no more names are entered outside the function?

+5
source share
4 answers

Will the function defined in letfn be overridden every time sqrt is called?

Yes.

The s and good-enough will be redone every time the sqrt function is entered. What they are attached to is rebuilt.

  • The s and good-enough functions (effectively fn special forms) are compiled once when (defn sqrt ... ) is executed.
  • They close over x for each sqrt call.
  • Closing can be implemented on the JVM as inner classes following the IFn interface.
  • Closures / function objects are built every time sqrt although the classes they illustrate have been compiled.

It depends on what you mean by redefinition. I did not think, but the elements on both sides of the bindings are updated. So now I think so.


Is there a more idiomatic way to write the square root function?

Yes.

On the JVM ...

 (defn sqrt [x] (Math/sqrt x)) (sqrt 2);1.4142135623730951 
+3
source

The short answer is yes, the bindings will be updated. However, it is as cheap as creating a simple class. You can see below the decompiled Java classes, how clojure is implemented with a simple parameter, and how the nested functions will lead to several more classes. So it is still very cheap.

 public final class user$sqrt extends AFunction { public static final Object const__0 = Double.valueOf(1.0D); public static Object invokeStatic(Object x) { user$sqrt$s__44945 s = null; user$sqrt$good_enough_QMARK___44947 good_enough_QMARK_ = null; user$sqrt$improve__44949 improve = null; s = new user$sqrt$s__44945(good_enough_QMARK_, improve); good_enough_QMARK_ = new user$sqrt$good_enough_QMARK___44947(x); Object var10002 = x; x = null; improve = new user$sqrt$improve__44949(var10002); user$sqrt$s__44945 var10000 = (user$sqrt$s__44945)s; ((user$sqrt$s__44945)s).good_enough_QMARK_ = good_enough_QMARK_; var10000.improve = improve; user$sqrt$good_enough_QMARK___44947 var4 = (user$sqrt$good_enough_QMARK___44947)good_enough_QMARK_; user$sqrt$improve__44949 var5 = (user$sqrt$improve__44949)improve; return ((IFn)s).invoke(const__0); } } public final class user$sqrt$good_enough_QMARK___44947 extends AFunction { Object x; public static final Var const__1 = (Var)RT.var("user", "abs"); public static final Var const__3 = (Var)RT.var("user", "square"); public user$sqrt$good_enough_QMARK___44947(Object var1) { this.x = var1; } public Object invoke(Object guess) { DO var10000 = (DO)const__1.getRawRoot(); DO var10001 = (DO)const__3.getRawRoot(); Object var10002 = guess; guess = null; return Numbers.lt(var10000.invokePrim(RT.doubleCast((Number)Numbers.minus(var10001.invokePrim(RT.doubleCast((Number)var10002)), this.x))), 1.0E-4D)?Boolean.TRUE:Boolean.FALSE; } } public final class user$sqrt$improve__44949 extends AFunction { Object x; public static final Var const__0 = (Var)RT.var("user", "avg"); public user$sqrt$improve__44949(Object var1) { this.x = var1; } public Object invoke(Object guess) { DDO var10000 = (DDO)const__0.getRawRoot(); double var10001 = RT.doubleCast((Number)Numbers.divide(this.x, guess)); Object var10002 = guess; guess = null; return var10000.invokePrim(var10001, RT.doubleCast((Number)var10002)); } } 
+2
source

Well, the wise answer is that it would be very idiomatic not to define your own square root operation. (Math/sqrt x) , which is an interop call against java.util.Math.sqrt() . It is generally accepted that (defn sqrt [x] (Math/sqrt x)) floats, or at least something that I accomplished for several projects.

Better yet, answer clojure.algo.generic , which already defines sqrt among other operations in an extensible and idiomatic way.

This particular implementation of the Newton Method is fine in the style of a very traditional scheme, but since it uses arithmetic in the box after several fn , it will significantly exceed Math/sqrt and does not provide any of the numerical flexibility of the algo.generic tower or the equivalent implementation on algo.generic .

As for the functions in letfn that will be overridden each time, the traditional implementation of the letfn scheme looks something like this:

  • Create a nil binding for each fn name
  • setq each binding to fn body now that there is a binding for each name
  • Body shape assessment

Clojure does the same under the hood. Each fn in letfn compiled into an instance class AFn , which takes as arguments the argument of a reference to a closed one over fn s. The emitted bytecode logically matches the format of the circuit implementation:

  • Bind the named local for each fn to Var
  • Initialize each AFn instance with Var matching the other letfn tags that it closes on top
  • Bind each AFn instance to the corresponding Var
  • Body shape assessment

So, technically yes, every binding in letfn should be restored every time the body is executed, and the new binding has a new (anonymous) Var for a new instance of AFn compiled AFn prevent this, no raising of the lambda or any other transformations is required, however, the overhead from this is negligible.

0
source

The following code shows the difference. Note that a simple let is all you need to define local functions:

 (defn my-inc [x] (+ 1 x)) (defn my-square [x] (* xx)) (println :1 (my-square (my-inc 2))) (let [my-inc-2 (fn [x] (+ 1 x)) my-square-2 (fn [x] (* xx)) ] (println :2 (my-square-2 (my-inc-2 2)))) (println :sqrt-9 (Math/sqrt 9)) ;=> :1 9 ;=> :2 9 ;=> :sqrt-9 3.0 

The only difference between the two cases is that the visibility of my-inc-2 and my-square-2 limited to the let block. I like this method better than using letfn , as I find the regular syntax (fn ...) clearer.

As for sqrt , using Java's built-in function is much better than writing your own.

For numerical methods, in general, one effective method is to use the first approximation of the polynomial, followed by several iterations of the Newton method. So, for example, Matlab calculates the normal distribution function N(0,1) .

-1
source

Source: https://habr.com/ru/post/1262270/


All Articles