namespace/symbol/:keyword/::keyword in Clojure
此文已由作者张佃鹏授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
1.关键字(:keyword/::keyword):
最近在学习使用clojure.spec(验证数据的格式)这个库,但在使用clojure.spec/def定义一个spec时,必须使用双冒号如下:
;;引入clojure.spec库
(use '[clojure.spec :as s])
;;定义一个名为::spec-test的spec
(s/def ::spec-test even?)
当时比较懵逼,不知道为什么会这样写,还以为是spec这个库特有的,就急着去使用该库了。后来才发现这也是一种关键字(::keyword),在某命名空间下的关键字(在其它语言c++中,::不是表示全局作用域吗??),我们可以使用namespace函数和name函数解释其如下:
(namespace ::spec-test)
;;=> "my-clj.core"
(name ::spec-test)
;;=> "spec-test"
原来::keyword就是关键字,只是表示的是当前命名空间下的关键字,主要为了解决关键字重名问题,也可以在当前命名空间下显式的定义namespace keyword:
;;定义一个带有命名空间的关键字,该命名空间可以随意指定,不一定是已经存在命名空间
(def ns-keyword :hello/A)
;;=> #'my-clj.core/ns-keyword ;;获取其namespace是"hello"
(namespace ns-keyword)
;;=> "hello" ;;获取其name是"A"
(name ns-keyword)
;;=> "A"
也就是说,有两种关键字,一种是全局的单冒号关键字,一种命名空间下的关键字,对于后者,可以使用::来定义关键字,表示是在当前命名空间下的关键字,也可以显式的写出命名空间的定义方法。
2.符号(symbol):
符号是一个标志,在使用时不对其进行解释,前面需要加一个引号,阻止对其求值,如下:
;;定义一个变量a=3
(def a 3)
;;=> #'my-clj.core/a
;;直接使用a会对其进行求值
a
;;=> 3
;;如果使用symbol的方式则不对其进行求值,返回符号本身 'a
;;=> a
symbol与keyword一样,也分全局的symbol和命名空间下的symbol,如下:
;;全局的symbol
(namespace 'A)
;;=> nil ;;命名空间下的symbol,可以避免重名
(namespace 'hello/A)
;;=> "hello"
符号(symbol)主要用于创建列表和宏定义时,如下:
;;创建一个新的列表时,为了阻止其当作函数求值 '(1 2 3)
;;=> (1 2 3) ;;定义宏时,阻止对if当作函数计算
(defmacro unless [expr form]
(list 'if expr nil form))
;;=> #'my-clj.core/unless
3.symbol/keyword/string/namespace之间的转换:
在实际使用中,我们经常会遇到keyword和string之间的转换,尤其是如果将map中的key由string变为keyword的时候,会带来很多好处,比如我们使用的IDE中的keyword可以高亮显示,而且keyword还可以作为函数,更重要是的对map的结构会带来很多方便,所以掌握keyword和string之间的转换尤为重要。
使用name函数将“关键字”转换为“字符串”(考虑namespace):
首先可以使用name函数获取一个keyword的string形式:
(name :A)
;;=> "A"
;;如果一个keyword中遇到"/",name函数会将第一个"/"前的字符串作为namespace,所以其返回的第一个"/"后的字符串
(name :hello/A)
;;=> "A"
但是有时候我们不希望把第一个斜杠"/"前的内容当作namespace,尤其是在将map转换为json格式的字符串时,有些keyword中带有“/”,但是它不是命名空间下的关键字,这时候如果使用name就会出错,但是大部分类似map2json的函数都会使用name来解释,所以会导致意想不到的错误发生。
使用str函数将“关键字”转换为“字符串”(不考虑namespace):
鉴于以上情况,我们可以使用str函数将“关键字”转换为“字符串”,如下:
;;str函数返回的字符串会带有冒号
(str :hello/A)
;;=> ":hello/A" ;;在str函数的基础上调用.substring函数,则可以正确转换为我们想要的string
(.substring (str :hello/A) 1)
;;=> "hello/A"
使用namespace和name函数将“关键字”转换为“字符串”(不考虑namespace)
可以使用另外一种方法获取和str方法效果一样,就是使用namespace函数和name函数的组合,namespace函数可以获取一个“关键字”或“符号”的命名空间的字符串表示,如果无命名空间则返回nil:
;;namespace函数的用法:
(namespace :a)
;;=> nil
(namespace :A/a)
;;=> "A"
;;它只会把第一个"/"前的字符串当作命名空间
(namespace :A/B/a)
;;=> "A" ;;使用namespace和name函数将keyword转换为string
(str (namespace :A/a) "/" (name :A/a))
;;=> "A/a"
特别注意的是,在clojurescript中,不要使用第三种方法,因为namespace函数在遇到多个斜杠时,会出现奇怪的问题,尽量使用第二种方法。
使用keyword函数将“字符串”转换为“关键字”
将“字符串”转换为关键字,直接使用keyword函数便可,keyword函数可以接收一个参数或者两个参数,如果是两个参数的情况下,会把第一个参数当作命名空间,然后在两个参数中间加一个斜杠"/":
(keyword "a")
;;=> :a
;;keyword可以接收有斜杠的字符串
(keyword "A/a")
;;=> :A/a
;;其实接收两个参数和接收一个参数本质上没有太大的区别,可以把两个参数用str合并后再调用keyword,可能只是为了突出强调namespace而已
(keyword "A" "a")
;;=> :A/a
(keyword "A/B" "a")
;;=> :A/B/a
与“符号”(symbol)之间的相关转换:
(1)其中symbol函数和keyword函数类似,可以将“字符串”转换为“符号”,同样也可以接收一个或者两个参数,如下:
(symbol "a")
;;=> a
;;如果两个参数,第一个参数作为命名空间
(symbol "A" "a")
;;=> A/a
(2)“符号”也可以作为namespace函数和name函数的参数,用法和“关键字”作为参数类似,可以将一个“符号”转换为“字符串”,如下:
(namespace 'hello/A)
;;=> "hello"
(name 'hello/A)
;;=> "A"
;;无命名空间时,namespace总是返回nil
(namespace 'A)
;;=> nil
(name 'A)
;;=> "A"
参考文章:
https://kotka.de/blog/2010/05/Did_you_know_III.html
http://stackoverflow.com/questions/2481984/when-should-clojure-keywords-be-in-namespaces
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 一步步教你学会browserify
【推荐】 云计算节点故障自动化运维服务设计
【推荐】 3分钟掌握一个有数小技能:利用参数完成动态排序
namespace/symbol/:keyword/::keyword in Clojure的更多相关文章
- CoreJava_线程并发(堵塞队列):在某个目录下搜索含有某keyword的文件
Java多线程编程是很考验一个程序猿水平的. 传统的WEB程序中.由于框架提供了太多的健壮性.并发性.可靠性的支持,所以我们都是将全部的注意力放到了业务实现上.我们不过依照业务逻辑的要求.不停的积累自 ...
- Swift标识符和keyword
不论什么一种计算机语言都离不开标识符和keyword,下面我们将具体介绍Swift标识符和keyword. 标示符 标识符就是给变量.常量.方法.函数.枚举.结构体.类.协议等指定的名字.构成标识符的 ...
- JavaSE入门学习5:Java基础语法之keyword,标识符,凝视,常量和变量
一keyword keyword概述:Java语言中有一些具有特殊用途的词被称为keyword.keyword对Java的编译器有着特殊的意义.在程 序中应用时一定要谨慎. keyword特点:组成k ...
- Native Clojure with GraalVM
转自:https://www.innoq.com/en/blog/native-clojure-and-graalvm/ GraalVM is a fascinating piece of techn ...
- (cljs/run-at (JSVM. :all) "细说函数")
前言 作为一门函数式编程语言,深入了解函数的定义和使用自然是十分重要的事情,下面我们一起来学习吧! 3种基础定义方法 defn 定义语法 (defn name [params*] exprs*) 示 ...
- IBM DB2 SQL error code list
SQL return codes that are preceded by a minus sign (-) indicate that the SQL statement execution was ...
- 【基于WPF+OneNote+Oracle的中文图片识别系统阶段总结】之篇四:关于OneNote入库处理以及审核
篇一:WPF常用知识以及本项目设计总结:http://www.cnblogs.com/baiboy/p/wpf.html 篇二:基于OneNote难点突破和批量识别:http://www.cnblog ...
- PHP 天巡机票接口
一个旅游网站项目,网站需要机票预订接入了天巡机票接口,获取机票信息,不搞不知道,一搞吓一跳比较麻烦. 搜索机票信息需要分2步,首先POST获得一个SESSION,2秒之后,根据这个SESSION,从一 ...
- 1036. Crypto Columns 2016 11 02
/* 对于题目多读几遍,然后再关键字排序的时候,把对应的数组序号也排序, EYDE MBLR THAN MEKT ETOE EOTH MEETME B ...
随机推荐
- Qt中如何用QImage::Format_Indexed8表示灰度图
QImage *qi = new QImage(data_ptr, width, height, QImage::Format_Indexed8); QVector<QRgb> grayT ...
- IIS调用批处理权限的处理[转]
最近公司希望将Windows 2003升级为Windows 2008,做完安全设置后发现.net调用批处理拒绝访问的情况.网上很多说更改应用程序池的权限,建议不需要修改该权限,我这里强烈建议使用默认的 ...
- Annotation之四:注解中的-Xlint:unchecked和 -Xlint:deprecation
一.-Xlint:unchecked用法 对如下Test.java编译时 package com.dxz.annotation; import java.util.ArrayList; import ...
- Python unittest excel数据驱动 写入
之前写过一篇关于获取excel数据进行迭代的方法,今天补充上写入的方法.由于我用的是Python3,不兼容xlutils,所以无法使用copy excel的方式来写入.这里使用xlwt3创建excel ...
- HDLM工具介绍
HDLM提供了以下一些工具,以方便HDLM多路径管理. 1. dlmgetras hdlm信息收集工具,用来收集hdlm相关的各种日志.trace.配置等文件,以方便进行hdlm故障分析. 命令格式 ...
- DAY7-面向对象之绑定方法与非绑定方法
一.类中定义的函数分成两大类 一:绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入): 1. 绑定到类的方法:用classmethod装饰器装饰的方法. 为类量身定制 类.boud_met ...
- 【262】pscp命令 实现windows与linux互传文件
首先将pscp.exe文件放在某个文件夹中 新建*.bat文件 w-wx.bat代码 @echo off pscp.exe -pw l*****h D:\Windows-Linux\Data\* oc ...
- springmvc 初始化参数绑定(使用属性编辑器) 来处理类型转换问题
处理一种日期格式 处理器中的写法: index.jsp中的写法: 处理多种日期格式: 处理器的写法: 自定义的属性编辑器: index.jsp的写法:
- 修改oracle xe的8080端口
1.用sys管理员身份登录,利用dbms_xdb修改端口设置 SQL> -- Change the HTTP/WEBDAV port from 8080 to 8081 SQL> call ...
- Lucene源码解析--Analyzer之Tokenizer
Analyzer包含两个核心组件,Tokenizer以及TokenFilter.两者的区别在于,前者在字符级别处理流,而后者则在词语级别处理流.Tokenizer是Analyzer的第一步,其构造函数 ...