clojure编程-延迟计算
2015-08-21 15:40
197 查看
5.2 如何延迟
函数编程大量使用递归定义,递归定义由两部分组成:
(1) 基础,明确列出序列的一些元素
(2) 归纳,提供规则利用序列元素,生成更多的元素
Clojure中有多种方法实现递归:
(1) 简单递归,使用函数调用自己实现归纳步
(2) 尾部递归,只在函数末尾调用函数自己实现归纳步
(3) 延迟序列,消除实际的递归,延后直到需要时再计算值
在Clojure中,采用延迟序列实现递归通常是最合适的方法。
示例:
简单递归
(defn stack-consuming-fibo
(cond
(= n 0) 0
(= n 1) 1
:else (+ (stack-consuming-fibo (- n 1))
(stack-consuming-fibo (- n 2)))))
使用这种方法计算很大的Fibonacci数时堆栈异常
尾部递归
(defn tail-fibo
(letfn [(fib [current next n] (if (zero? n) current (fib next (+ current next) (dec n))))]
(fib 0 1 n)))
其中letfn宏
(letfn fnspecs & body)
; fnspecs ==> (fname [params*] exprs)+
leftn像let但是用来定义局部函数
使用这种方法计算很大的Fibonacci数时堆栈异常
使用recur自递归
在Clojure中,可以将尾部调用自己的函数转换为使用recur的自递归函数。
上面的程序可以修改为:
(defn recur-fibo
(letfn [(fib [current next n] (if (zero? n) current (recur next (+ current next) (dec n))))]
(fib 0 1 n)))
使用这种方法可以计算很大的Fibonacci数,但如果计算多个时,将多次计算非常浪费资源。
延迟序列
延迟序列通过lazy-seq宏构造
(lazy-seq & body)
延迟序列只在需要时才调用它的body,不管是序列是直接还是间接被调用,然后延迟序列将结果进行缓存为子序列调用做预备。
使用延迟序列上面的程序可以修改为:
(defn lazy-seq-fibo
([] (concat [0 1] (lazy-seq-fibo 0 1)))
([a b] (let [n (+ a b)] (lazy-seq (cons n (lazy-seq-fibo b n))))))
通过iterate实现
(defn fibo []
(map first (iterate (fn [[a b]] [b (+ a b)]) [0 1])))
实现(取延迟序列元素)
通过take获取前n个元素序列,如
(def lots-o-fibs (take 10000000 (bibo)))
通过nth获取序列第n个元素
(nth lots-o-fibs 100)
为了方便,可以设置参数*print-length*,指定打印原色个数
user=> (set! *print-length* 10)
10
设置后,只输出前10个元素,如下:
user=> (take 1000000000 (fibo))
(0 1 1 2 3 5 8 13 21 34 ...)
说明:
(1) 如果需要实现整个序列,可以使用doall或dorun。
(2) 将延迟序列作为函数形式暴露,返回一个需要的序列;而不要延迟序列作为一个变量来使用(这会导致延迟序列中已经访问过的变量不能被垃圾回收器自动收回,导致内存溢出)
版权声明:本文为博主原创文章,未经博主允许不得转载。
函数编程大量使用递归定义,递归定义由两部分组成:
(1) 基础,明确列出序列的一些元素
(2) 归纳,提供规则利用序列元素,生成更多的元素
Clojure中有多种方法实现递归:
(1) 简单递归,使用函数调用自己实现归纳步
(2) 尾部递归,只在函数末尾调用函数自己实现归纳步
(3) 延迟序列,消除实际的递归,延后直到需要时再计算值
在Clojure中,采用延迟序列实现递归通常是最合适的方法。
示例:
简单递归
(defn stack-consuming-fibo
(cond
(= n 0) 0
(= n 1) 1
:else (+ (stack-consuming-fibo (- n 1))
(stack-consuming-fibo (- n 2)))))
使用这种方法计算很大的Fibonacci数时堆栈异常
尾部递归
(defn tail-fibo
(letfn [(fib [current next n] (if (zero? n) current (fib next (+ current next) (dec n))))]
(fib 0 1 n)))
其中letfn宏
(letfn fnspecs & body)
; fnspecs ==> (fname [params*] exprs)+
leftn像let但是用来定义局部函数
使用这种方法计算很大的Fibonacci数时堆栈异常
使用recur自递归
在Clojure中,可以将尾部调用自己的函数转换为使用recur的自递归函数。
上面的程序可以修改为:
(defn recur-fibo
(letfn [(fib [current next n] (if (zero? n) current (recur next (+ current next) (dec n))))]
(fib 0 1 n)))
使用这种方法可以计算很大的Fibonacci数,但如果计算多个时,将多次计算非常浪费资源。
延迟序列
延迟序列通过lazy-seq宏构造
(lazy-seq & body)
延迟序列只在需要时才调用它的body,不管是序列是直接还是间接被调用,然后延迟序列将结果进行缓存为子序列调用做预备。
使用延迟序列上面的程序可以修改为:
(defn lazy-seq-fibo
([] (concat [0 1] (lazy-seq-fibo 0 1)))
([a b] (let [n (+ a b)] (lazy-seq (cons n (lazy-seq-fibo b n))))))
通过iterate实现
(defn fibo []
(map first (iterate (fn [[a b]] [b (+ a b)]) [0 1])))
实现(取延迟序列元素)
通过take获取前n个元素序列,如
(def lots-o-fibs (take 10000000 (bibo)))
通过nth获取序列第n个元素
(nth lots-o-fibs 100)
为了方便,可以设置参数*print-length*,指定打印原色个数
user=> (set! *print-length* 10)
10
设置后,只输出前10个元素,如下:
user=> (take 1000000000 (fibo))
(0 1 1 2 3 5 8 13 21 34 ...)
说明:
(1) 如果需要实现整个序列,可以使用doall或dorun。
(2) 将延迟序列作为函数形式暴露,返回一个需要的序列;而不要延迟序列作为一个变量来使用(这会导致延迟序列中已经访问过的变量不能被垃圾回收器自动收回,导致内存溢出)
版权声明:本文为博主原创文章,未经博主允许不得转载。
相关文章推荐
- 详细解读PHP的Yii框架中登陆功能的实现
- 邻接矩阵实现代码
- JAVA基础之数组,List,MAP。排序,最值
- 黑马程序员-[C语言-指针和文件操作]学习日记(四)
- 在C#中调用格式工厂进行任意视频格式到FLV的转换
- c#**************
- [php] smarty模板引擎
- 快速切换天财商龙门店后台.VB6.0
- servlet面试时遇到问题和Eclipse中servlet显示无法导入javax.servlet包问题的解决方案
- php大力力 [001节]2015-08-21.php在百度文库的几个基础教程新手上路日记 大力力php 大力同学 2015-08-21 15:28
- C++无名命名空间详解
- 编程语言与脚本语言之间的区别(转)
- strcpy手写代码
- IOS APNS 含java服务器实现
- (?)QTableView翻页控件--MVC实现
- C#通过重写Panel改变边框颜色与宽度的方法
- Java关键字final、static使用总结
- 【我的技术我做主】那些年从事过的.NET,追求的Java
- C#获取“所有用户”的路径, 我的桌面,我的文档,我的音乐,我的图片等等目录
- struts2学习第一篇 环境配置 之 登录页面