深入了解lisp(clojure)-变量
来源:互联网 发布:国产男鞋品牌 知乎 编辑:程序博客网 时间:2024/06/05 06:58
本文由larrylgq编写,转载请注明出处:http://blog.csdn.net/larrylgq/article/details/7395261
作者:吕桂强
邮箱:larry.lv.word@gmail.com
clojure中变量可以分为词法变量(lexical)和动态变量(dynamic),有点类似于其它语言中的局部变量和全局变量。
一个变量是一个不需要声明保存类型的可以保存值的具名位置。它可以保存任何类型的值,并且这些值带有用于运行期检查的类型信息。并且如果类型出错会被动态的检测到
eg:(将一个非数字对象传给了+函数)
从这方面来看lisp是一个强类型的且动态类型的语言。
lisp是传值的,但是传递的值是对象的引用。这点和java,python类似。意味着如果一个变量修改了它指向的一个可变对象,这个改动会被应用到任何引用该对象的变量。
一种引入新变量的方式是通过函数形参:形参列表定义了在函数被调用的时候保存实参的变量。
eg:(defn foo [x y z] (+ x y z))
每次函数调用的时候clojure会创建新的绑定保存有调用者传递来的实参,该绑定的生命周期一直到运行期结束。(递归函数的形参会在每一次函数调用的时候重新绑定)
另一种引入新变量的方式是使用let特殊操作符。
eg:(let [variable*] body-form*)
variable可以赋初值也可以不赋,下面是一个let将x,y,z分别绑定到1,2,nil上:
(let [[x 1] [y 2] z]
...)
let函数是一个调用匿名函数的宏,上面的例子可以展开为((fn [x y z] (...)) 1 2 nil)
let在调用结束后,如果该变量在let之前有引用,则会重新指向所引用的对象。
函数定义和let这两种形式我们称之为绑定形式,多个嵌套对同名变量的绑定,内层的变量绑定会覆盖外层的。
clojure的闭包
如果一个匿名函数引用一个封闭作用域的变量就像这样:
(let [count 0] #(fn [] (inc count)))
根据作用域规则lambda表达式中对count的引用是合法的,而且这个引用了count的匿名函数会被当作返回值被let函数返回。
如果我们将这个表达式所创建的闭包赋值给一个全局变量例如:
(def *fn* (let [count 0] #(fn [] (inc count))))
这样我们就可以在外部调用它
USER>(*fn*)
1
USER>(*fn*)
2
当然除此之外多个闭包也可以引用相同的变量
例如:
(let [count 0]
(list
#(fn [] (inc count))
#(fn [] (deccount))
#(fn [] count)
)
)
动态变量
Clojure里面有4种动态变量类型:Vars,Refs,Atoms 和Agents.
下面这个表格是它们之间的比较:
这个表格里面提到的函数我们会在后面介绍。
(def name initial-value)
(ref initial-value)
(atom initial-value)
(agent initial-value)
(def name new-value)
给变量赋一个新值
(alter-var-root
(var name) update-fn args)
使用var和fn动态修改一个函数的引用
(set! name new-value) 用在binding内部,修改一个线程本地的值
(ref-set ref new-value)
给变量赋予一个新值,必须在dosync里面调用
(alter ref
update-fn arguments)修改ref的值,必须在dosync里面调用,如果事务开始之后值改变了,那么当前事务会进行重试
(commute ref
update-fn arguments)
修改ref的值,必须在dosync 里面调用,即使事务开始之后值改变了,当前事务不会进行重试
(reset! atom new-value)
不管旧的值是什么样子,都会保存新值
(compare-and-set! atom current-value new-value)设置新值之前检查旧值如果和current-value相同才会设置并返回true,否则只返回false
(swap! atom
update-fn arguments)对compare-and-set!的封装,当返回false的时候会自动重试
(send agent
update-fn arguments)
使用的线程池是(java.util.concurrent.Executors.newFixedThreadPool)线程个数是cpu个数+2
(send-off agent
update-fn arguments)使用的线程池是(java.util.concurrent.Executors.newCachedThreadPool)
线程的个数按照实际需要分配
注:
1:Software Transactional Memory (STM):
STM事务里面做的修改只能在事务提交之后才能被别的线程看到。这实现了ACID里面的A(原子性)和I(隔离性)
事务开始之后,如果有别的线程对这个Ref做了改动,事务将会回滚到开始的状态,这就实现了C(一致性)
2:send 在分配一个action给Agent之后会立刻返回,action运行结束后,将返回值赋值给Agent。
使用的线程池是(java.util.concurrent.Executors.newFixedThreadPool)线程个数是cpu个数+2
send-off与set类似,使用的线程池是(java.util.concurrent.Executors.newCachedThreadPool)
线程的个数按照实际需要分配。
当send,send-off 函数在事务里调用的时候。该action会一直等到线程提交的时候才被发送给另外一个线程去执行。
- 深入了解lisp(clojure)-变量
- 深入了解lisp(clojure)-宏
- lisp家族之Clojure
- Lisp 变量
- 深入了解JavaScript之变量
- 深入了解C语言(局部变量的定义)(转)
- 深入了解VB中的变量和指针(一)
- 现实世界的LISP:Clojure语言初探
- 为什么我喜欢Lisp语言(clojure)
- so库的static 变量深入了解
- lisp中的变量
- LISP 6.3 动态变量
- clojure解构(clojure destructuring)
- clojure解构(clojure destructuring)
- 用clojure实现《实用Common Lisp编程》中的单元测试框架
- 【转】现实世界的LISP:Clojure语言初探
- The Clojure (or Lisp) Equivalent of a Compound Boolean Test
- Lisp 匿名递归函数 v2:在 Common Lisp 中实现 Clojure 的 fn
- Android 绑定类型服务---其他注意事项
- php字符编码utf-8转gb2312的问题解决
- 计算机网络体系结构 题
- SQL Server 删除复制
- livemedia
- 深入了解lisp(clojure)-变量
- 数据链路层
- CentOS(RedHat)命令行临时修改IP地址、网关、DNS
- Android 绑定类型服务---管理绑定类型服务的生命周期
- AndroidNote008.BaiduMap开发手记2
- 检测iphone内存泄漏-内存泄漏工具教程(译)
- Annotated controller and requestParam
- Linux下的进程环境
- Android 内容提供器---简介