Clojure 学习随笔

--学习书籍《Practical Clojure》

 
1. 尾递归:不管函数式语言对于递归调用来说有多大的优势,但还是不得不面对运行环境的内存栈限制问题。所以为了应对这个问题,函数式语言采用尾递归的方式来对递归调用做最优化处理。
尾递归最优是指当递归的中心条件满足时,编译器不以消耗内存栈的方式来优化递归调用,再全面一点说,就是递归调用实在编译码中被实现为迭代。
 
Tail- call optimization  means that , if  certain conditions are met ,  the compiler can optimize the recursive calls in  such a way that  they do not  consume stack. Under the covers , they're implemented as iterations in the compiled machine code.
 
在大多数函数式语言中,只有递归调用出现于尾位(tail position)时才会进行优化。关于尾位的正式定义有许多种,但其中最容易记住也最重要的一条是:函数在返回前最后做了什么。如果外部函数的返回值被完全委托给内部函数,那么,这种调用就处在尾位;如果除非是内部函数返回值时,外部函数才能有所作为,那么这种调用不是尾递归,不能被优化。
 
递归调用不是尾位的例子: 
(defn power  
"计算数值的乘方,number为基数,exponent为指数值."
[number exponent]
(if (zero? exponent)
1
(* number (power number (- exponent 1)))))
(power 5 3)
==>125
在例子中可以看到,只有在指数递减为0,达到递归的中心条件时,该函数才能沿着调用的函数链一直往上递归计算。外部函数power在内部函数power(它自己)返回具体值前什么都不能做。
 
递归调用是尾位的例子:
;;根据牛顿的算法近似计算平放根
(defn abs
"计算绝对值"
[n]
(if (< n 0)
(* -1 n)
n))

(defn avg
"返回a b两数的平均值"
[a b]
(/ (+ a b) 2))

(defn good-enough?
"比较guess的平方与number相减的绝对值是否小于0.001"
[number guess]
(let [diff (- (* guess guess) number)]
(if (< (abs diff) 0.001)
true
false)))

(defn sqrt
"近似计算平方根"
([number] (sqrt number 1.0))
([number guess]
(if (good-enough? number guess)
guess
(sqrt number (avg guess (/ number guess))))))
 
根据sqrt的递归,可以看到,sqrt的每一步递归都有其内部函数avg返回具体值,每一次的递归都可以做计算,这种调用才是处于尾位,可以被优化
 
不同于其他函数语言,Clojure不是对所有的递归都做隐式的尾递归优化,而是要显示的指定recur 形式(form)来进行尾递归优化。
例如下列函数没有recur形式时,是不被Clojure直接优化的
(defn add-up 
"adds all the numbers below a given limit"
([limit] (add-up limit 0 0 ))
([limit current sum]
(if (< limit current)
sum
(add-up limit (+ 1 current) (+ current sum)))))
下列是加了recur 形式后的代码
 
(defn add-up 
"adds all the numbers below a given limit"
([limit] (add-up limit 0 0 ))
([limit current sum]
(if (< limit current)
sum
(recur limit (+ 1 current) (+ current sum)))))
需要注意的是,虽然尾递归优化是为了解决内存栈的限制,但也不能随意使用recur来指定尾递归,这样做会让代码难以维护和理解。建议正确的自然尾递归,会使代码更加清晰、方便,结构和功能也一目了然。recur只是保证递归不会超出内存栈的限制。

转载于:https://www.cnblogs.com/yoya/archive/2012/03/14/2395812.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值