(cljs/run-at (JSVM. :browser) "简单类型可不简单啊~")
2017-07-05 06:48
555 查看
前言
每逢学习一个新的语言时总要先了解这门语言支持的数据类型,因为数据类型决定这门语言所针对的问题域,像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/run-at (->JSVM :browser) "语言基础")
- (cljs/run-at (JSVM. :all) "一起实现柯里化")
- (cljs/run-at (JSVM. :all) "一次说白DataType、Record和Protocol")
- (cljs/run-at (JSVM. :all) "细说函数")
- (cljs/run-at (JSVM. :all) "Metadata就这样哦")
- asp.net中runat="server"的含义
- C# runat="server"
- STS (eclipse) run on server with Tomcat hangs at "Initializing Spring root WebApplicationContext"
- [ JS 进阶 ] 基本类型 引用类型 简单赋值 对象引用 (转)
- 从一个简单例子来理解js引用类型指针的工作方式
- 【转】简单了介绍js中的一些概念(词法结构) 和 数据类型(部分)。
- MTK模拟器Could not run "cl.exe"错误
- Node.Js + express, run "hello world " in windows 8.1
- Parse Fatal Error at line 41 column 24: 元素类型 "url-pattern" 必须由匹配的结束标记 "</url-pattern>" 终止
- input type="file" 在js中判断文件上传类型
- 使用Javascript和prototype.js框架创建类型及其相关的prototype属性的简单介绍
- js综合能力提升 简单类型和对象 栈和堆
- js 获取input type="file" 选择的文件大小、文件名称、上次修改时间、类型等信息