(cljs/run-at (JSVM. :browser) "简单类型可不简单啊~")
前言
每逢学习一个新的语言时总要先了解这门语言支持的数据类型,因为数据类型决定这门语言所针对的问题域,像Bash那样内置只支持字符串的脚步明显就是用于文本处理啦。而数据类型又分为标量类型(Scalar)、结构类型(Struct)和集合类型(Collection),标题中的简单类型实质就是指标量类型。
cljs中内置的标量类型比js的丰富得多,一方面方便了操作,另一个方面增加了学习成本,因此从js转向cljs时可能会略感不适,下面我们一起来认识吧!
标量类型一览
;; 空值/空集
nil
;; 字符串,必须使用双引号包裹
"I am a string!"
;; 字符,以斜杆开头
\&
\newline
;; 布尔类型(Boolean),nil隐式类型转换为false,0和空字符串等均隐式类型转换为true
true
false
;; 长整型(Long)
1
;; 浮点型(Float)
1.2
;; 整型十六进制
0x0000ff
;; 指数表示法
1.2e3
;; 键(Keyword),以:为首字符,一般用于Map作为key
:i-am-a-key
;; Symbol,标识符
i-am-symbol
;; Var
i-am-var
;; Special Form
;; 如if, let, do等
(if pred then else?)
(let [a 1] expr1 expr2)
(do expr*)
;; 函数
(fn [a]
(println a))
;; 宏
(defmacro out [s]
`(println ~s))
Keyword真心不简单啊!
位于cljs.core/Keyword
的关键字并不是仅仅如上述那样简单,其实一共有3种定义方式:
1.所见即所得
;; 通过literal来定义
:i-am-a-keyword
:i-am-a-namespace/i-am-a-keyword
;; 通过keyword函数来定义
(keyword "i-am-a-keyword")
(keyword "i-am-a-namespace" "i-am-a-keyword")
2.自动扩展为以当前命名空间为前缀
(ns cljs.user)
;; 自动扩展为以当前命名空间为前缀的keywork
::keyword ;;=> :cljs.user/keyword
3.自动扩展为
;; 自动查找以aliased-ns为别名的命名空间,并以找到的命名空间作为前缀创建keyword
;; 因此需要先通过require 引入命名空间才能通过别名解析出原来的命名空间
(ns cljs.user
(:require '[test.core :as test]))
::test/keyword ;;=> :test.core/my-keyword
另外Keyword还可以作为函数使用呢!
(def person {:name "fsjohnhuang", "sex" "male"})
(:name person) ;;=> "fsjohnhuang"
("sex" person) ;;=> 报错
(get person "sex") ;;=> "male"
什么是Symbol?
在任何Lisp方言中Symbol作为标识符(Identity),如命名空间名称、函数名称、变量名称、Special Form名称等等。而凡是标识符均会被限制可使用的字符集范围,那么合法的cljs.core/Symbol
需遵守以下规则:
- 首字符不能是
[0-9:]
- 后续字符可为
[a-zA-Z0-9*+-_!?|:=<>$&]
- 末尾字符不能是
:
- 区分大小写
命名习惯:
- 全小写
- 单词间以
-
分隔 - 常量和全局标识,首尾为
*
,如*main-cli-fn*
*x
,标识内置变量,且经常值变化x?
,标识断言函数x!
,标识产生副作用的函数x-
,标识其将产生私有方法,如defn-
和deftest-
_
,标识可忽略的symbol
既然Symbol仅仅作为标识符来使用,为何不见JS、C#等会将标识符独立出来作为一种类型呢?原因十分简单但又难以理解——Lisp中代码即数据,数据即代码。作为Lisp的方言cljs自然传承了这一耀眼的特性!
;; 定义一个List实例,其元素为a和b两个Symbol实例
(def symbol-list (list 'a 'b))
大家有没有注意到'
这个符号啊?由于symbol根据它在列表中的位置解析为Special Form或Var,为阻止这一过程需要通过quote
函数来处理,而'
就是quote
的reader macro。不信大家试试(cljs.reader/read-string "'a")
它会扩展为(cljs.core/quote a)
另外
;; 判断是否为cljs.core/Symbol类型
(symbol? 'a) ;;=> true
;; symbol可以作为函数使用
(def a {'b 1})
('b a) ;;=> 1
Var又是什么呢?
在clj/cljs中Var是一个容器,其内容为指向实际值的地址,当其内容为nil时称之为unbound,非nil时则称为bound。而一个Var可以对应1~N个Symbol。
;; Symbol a和b都对应同一个Var,这个Var指向1所在的内存地址
(def a 1)
(def b 1)
这个和JAVA、C#中的String是一样的。另外Clojure还有一个十分有趣的特性就是Symbol直接绑定值,中间没有Var,因此就不存在重新赋值的可能
(defn say [s]
(println s))
(defn say1 [s]
(def s 2)
(println s))
(say "say") ;;=> say
(say1 "say1") ;;=> say1
和Symbol同样,Var可以作为数据处理,不过由于Var会根据其所在列表中的位置解析为是Macro还是函数还是值,因此需要通过#'
来阻止,而#'
就是var
的reader macro。
(def b 1)
(def c 2)
(def a (list #'b #'c))
注意:#'
或var
操作前必须要先定义好同名变量、内置或第三方库已定义的变量,否则会报错。
Special Form又是什么鬼?
实质上就是语言原语,其他函数和Macro均基于它们来构造,当解析器遇到一个Symbol时会解析的顺序是Special Form
-> Var
。
如if
就是一个原语,即使是Macro也没有办法从无来构造一个,不信大家自己试试吧!
部分常用的Special Form如下:
(def symbol init?)
(if test then else?)
(do exprs*)
(let [binding*] exprs*)
(quote form)
(var symbol)
(fn name? [params*]
exprs*)
(fn name?
([params*]
exprs*)+)
(fn name? [params*]
condition-map? exprs*)
(fn name?
([params*]
condition-map?
exprs*)+)
(loop [binding*]
exprs*)
(recur exprs*)
(throw expr)
(try expr* catch-clause* finally-clause?)
怎么函数也纳入标量呢?
函数式编程当中第一条规则就是“函数是一等公民”,就是函数和String、Integer等一样可以作入参、函数返回值,更确切来说函数的构造不依赖其他类型或类型实例。而面向对象中,没有函数只有方法,而方法的构造前必须先构建其所依赖的类型或类型实例。
另外cljs中确实是用定义变量的方式来定义函数
(defn a [x] (println x))
;; defn是macro,实质上会展开成
(def a (fn [x] (println x)))
是不是清楚多了啊!
总结
本文较详尽地介绍了Keyword,然后稍微介绍了Symbol、Var和Special Form,而Lisp中“代码即数据,数据即代码”需要结合Symbol的解释过程说明效果才有所体现,这个由于篇幅较大,就打算日后再另起一篇来描述了。
作为函数式编程语言,cljs的函数定义又怎么会只有(defn name [params*] exprs*)
呢?下一篇(cljs/run-at (JSVM. :all) "细说函数"),我们一起细说吧!
尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/7119333.html _肥仔John
REF
http://www.cnblogs.com/or2-/p/3579745.html
(cljs/run-at (JSVM. :browser) "简单类型可不简单啊~")的更多相关文章
- (cljs/run-at (JSVM. :browser) "命名空间就这么简单")
前言 一个cljs文件定义一个命名空间,通过命名空间可以有效组织代码,这是构建大型系统必备的基础设施.本篇我们就深入理解cljs中的命名空间吧! 好习惯从"头"开始 每个cljs ...
- (cljs/run-at (JSVM. :browser) "搭建刚好可用的开发环境!")
前言 书接上一回,在了解cljs基本语法后并在clojurescript.net的奇特错误提示后,我们必须痛定思痛地搭建一个本地的开发环境,以便后续深入地学习cljs. 现有的构建工具 由于浏览器 ...
- (cljs/run-at (->JSVM :browser) "语言基础")
前言 两年多前知道cljs的存在时十分兴奋,但因为工作中根本用不上,国内也没有专门的职位于是搁置了对其的探索.而近一两年来又刮起了函数式编程的风潮,恰逢有幸主理新项目的前端架构,于是引入Ramda. ...
- ASP.NET - Web API,从简单类型到复杂类型的参数传递用例,以及传递简单string类型的解决办法
一,简单类型的传值 比如 public Users Get(int id) ,它可以使用两种方式获取: api/default/ $.get("/api/default",{id: ...
- 6.Struts2简单类型数据的接受
简单类型数据的接收 在Action类中定义与请求参数同名的属性, 即,要定义该属性的set方法,便能够使struts2自动接收请求参数并赋予同名属性. 简单类型数据的接受举例: 新建工程项目,名称为: ...
- XAF应用开发教程(二)业务对象模型之简单类型属性
使用过ORM的朋友对这一部分理解起来会非常快,如果没有请自行补习吧:D. 不说废话,首先,我们来开发一个简单的CRM系统,CRM系统第一个信息当然是客户信息.我们只做个简单 的客户信息来了解一下XAF ...
- Chrome浏览器扩展开发系列之四:Browser Action类型的Chrome浏览器扩展
Browser Action类型的Google Chrome扩展程序,通常在Chrome浏览器的工具栏中,地址栏的右侧,有一个始终存在的图标.也就是说,这个图标与浏览器相关,只要安装了该Chrome扩 ...
- Java的简单类型不能够精确的对浮点数进行运算
由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精确的浮点数运算,包括加减乘除和四舍五入. import java.math.BigDecimal; /** * 由于Java的简单类 ...
- 从简单类型到复杂类型的参数传递用例,以及传递简单string类型的解决办法
一,简单类型的传值 比如 public Users Get(int id) ,它可以使用两种方式获取: api/default/5 $.get("/api/default" ...
随机推荐
- SQLCODE=-668, SQLSTATE=57016, SQLERRMC=7
当前表出于 装入暂挂状态,使用重组命令(reorg) 不起作用,报SQL-104, 然后从网上百度了大量解除 DB2暂挂的命令均不好使,最后采用了对表的runstats单个优化,也是类似reorg的单 ...
- [原创]首次SDN比赛的记录-部分
SDN大赛环境搭建和第一大题实现 由于物理设备不足的原因,故用虚拟机实现Floodlight控制器,openvswitch(以下简称:OVS)和mininet各种要用到的SDN环境的搭建.下面将给出它 ...
- Easy machine learning pipelines with pipelearner: intro and call for contributors
@drsimonj here to introduce pipelearner – a package I'm developing to make it easy to create machine ...
- Ant + Jenkies +Tomcat 自动构建部署Web项目
前言:博主资历尚浅,很多东西都还在刚起步学习的阶段,这几天开发任务比较轻,就在自己window系统下,模拟部署远程服务器,利用Jenkies + Ant + Tomcat 搭建了一个自动发布部署的环境 ...
- nodejs零基础详细教程1:安装+基础概念
第一章 建议学习时间2小时 课程共10章 学习方式:详细阅读,并手动实现相关代码 学习目标:此教程将教会大家 安装Node.搭建服务器.express.mysql.mongodb.编写后台业务逻辑. ...
- 前端打包文件在nginx上403的解决办法
遇到的问题表现出来就是如题所述,因此作为题目. 我们知道,前端有很多方便的构建和打包工具,如webpack等,通常我们会把前端文件打包到dist目录下,部署到服务器上,如nginx等. 这次遇到的问题 ...
- PyQt5环境搭建及cx_freeze打包exe
Python的图形库也有好几个,Qt文档和使用面还是要广一些. 打包成可执行文件的也有好几个,PyInstaller用的比较多,但是PyInstaller目前还不支持Python3.6(开发版支持3. ...
- C#简单构架之EF进行读写分离+多数据库(Mysql/SqlService)
最近因为项目需要,研究了下EF的读写分离,所以做了一个demo进行测试,下面是项目的结构 表现层view 主要提供Web.WebApi等表现层的解决方案 公共层public 主要提供项目公共类库,数据 ...
- 如何提取Redis中的大KEY
工作中,经常有些Redis实例使用不恰当,或者对业务预估不准确,或者key没有及时进行处理等等原因,导致某些KEY相当大. 那么大Key会带来哪些问题呢? 如果是集群模式下,无法做到负载均衡,导致请求 ...
- Vue-cli 记录
出自http://www.cnblogs.com/nutritious/p/6494479.html 先给出能正确安装的步骤: 1.进盘符 2,为啥不用npm,这是国外的东西,有些电脑无法FQ,会导致 ...