如果abs
和avg
已定义:
(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)))
请忽略一下我在这里重新发明轮子。 :)这只是一个例子。
如果是这样,有没有办法绕过这个,所以每次调用函数时都不会反复绑定名称,并且不在函数外引入更多名称?
答案 0 :(得分:3)
每次调用
letfn
时,是否会重新定义sqrt
中定义的函数?
是。
s
和good-enough
的绑定将在每次输入sqrt
函数时重新制作。他们必须重新构建的东西。
s
和good-enough
个函数(有效fn
个特殊表单)
当(defn sqrt ... )
为时,编译一次
进行。x
的每次通话,它们都会被sqrt
关闭。IFn
接口。sqrt
时都会构造闭包/函数对象
虽然他们举例说明的课程已被编译,但已进入。这取决于重新定义的含义。我认为不是,但绑定两侧的元素都会更新。所以现在我想所以。
有没有更惯用的方法来编写平方根函数?
是。
在JVM上......
(defn sqrt [x] (Math/sqrt x))
(sqrt 2);1.4142135623730951
答案 1 :(得分:2)
简短的回答是&#34;是&#34;,绑定将被更新。但是,这与实例化一个非常简单的类一样便宜。您可以在下面看到反编译的java类,如何使用一个简单的参数实现clojure,以及嵌套函数将如何导致更多的类。所以这还是很便宜的。
import angular from 'angular';
import '../services/user';
import './modals/users';
const module = angular.module('MyApp.controllers.users', [
'MyApp.services.user',
'MyApp.services.globals',
'MyApp.modals.user',
]);
const UsersController = ($scope, UserService) => {
'ngInject';
$scope.title = 'Users';
$scope.users = UserService.GetAll();
}
module.exports = module.controller('UsersController', UsersController);
答案 2 :(得分:0)
明智的答案是,不定义自己的平方根操作是最惯用的。对(Math/sqrt x)
进行互操作的java.util.Math.sqrt()
将是首选。看到(defn sqrt [x] (Math/sqrt x))
浮出水面,或者至少是我为多个项目所做的事情,这是相当普遍的。
更好的答案是使用clojure.algo.generic已经以可扩展和惯用的方式定义sqrt
等其他操作。
牛顿方法的这种特殊实现是很好的,并且采用非常传统的方案风格,但由于它通过多个fn
使用盒装算术,因此它将大大优于{{ 1}}它没有提供algo.generic的数字塔灵活性,或algo.generic上的等效实现。
至于每次都会重新定义Math/sqrt
中的函数,letfn
的传统Scheme实现类似于:
letfn
名称nil
的绑定
fn
每个绑定到setq
正文,因为每个名称都有绑定 Clojure在幕后做了同样的事情。 fn
中的每个fn
都编译为letfn
实例类,该实例类接受对封闭的AFn
s的实例参数的引用。发出的字节码在逻辑上与Scheme实现的格式相同:
fn
的已命名本地绑定到fn
Var
正文相对应的AFn
初始化每个Var
个实例letfn
绑定到相应的AFn
技术上是的,每次执行主体时都需要重新建立Var
中的每个绑定,并且新绑定是对新letfn
实例的新(匿名)Var已编译的类。没有lambda提升或任何其他转换来防止这种情况,但这样做的开销可以忽略不计。
答案 3 :(得分:-1)
以下代码显示了差异。请注意,只需要一个简单的let
来定义本地函数:
(defn my-inc [x] (+ 1 x))
(defn my-square [x] (* x x))
(println :1 (my-square (my-inc 2)))
(let [my-inc-2 (fn [x] (+ 1 x))
my-square-2 (fn [x] (* x x)) ]
(println :2 (my-square-2 (my-inc-2 2))))
(println :sqrt-9 (Math/sqrt 9))
;=> :1 9
;=> :2 9
;=> :sqrt-9 3.0
两种情况之间的唯一区别是my-inc-2
和my-square-2
的可见性仅限于let
块。我比使用letfn
更喜欢这种方式,因为我认为常规(fn ...)
语法更清晰。
关于sqrt
,使用Java内置函数要比编写自己的函数好得多。
对于一般的数值技术,一种有效的技术是使用多项式第一近似,然后进行牛顿方法的几次迭代。这就是Matlab计算正态分布函数N(0,1)
的方法,例如。