`
songry
  • 浏览: 84566 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

clojure API学习(2) 比较操作

阅读更多

注:本文基于jdk1.6,clojure1.2

比较操作

等于=

    clojure中的等于和java中的equals方法类似,但是clojure中的=还能够作用于nil、数字和集合上面。看看例子:

user> (= 3)
true
user> (= 5 5)
true
user> (= "a" "a")
true
user> (= '(7 8.0 9) [7 8.0 9])
true
user> (= '(7 8.0 9) [7 8 9])
true
user> (= (java.lang.String. "nice") (java.lang.String. "nice"))
true

    从上面的例子来看,=应该是做的值比较,那我们再来验证一下看看:

user> (= (java.lang.String. "nice") (java.lang.String. "nice"))
true
user> (= 1.25 5/4)
true
user> (= 8.00M 8)
false
user> (= 8.00 8)
true
user> (= 127 0x7f)
true
user> (= (java.lang.Float. 127.0) 127.0)
true

    除了声明为BigDecimal的数字之外,其他的都成功了。让我们来看看源码:

user> (source =)
(defn =
  "Equality. Returns true if x equals y, false if not. Same as
  Java x.equals(y) except it also works for nil, and compares
  numbers and collections in a type-independent manner.  Clojure's immutable data
  structures define equals() (and thus =) as a value, not an identity,
  comparison."
  {:inline (fn [x y] `(. clojure.lang.Util equiv ~x ~y))
   :inline-arities #{2}
   :added "1.0"}
  ([x] true)
  ([x y] (clojure.lang.Util/equiv x y))
  ([x y & more]
   (if (= x y)
     (if (next more)
       (recur y (first more) (next more))
       (= y (first more)))
     false)))

    从上面源码中我们可以看出,如果=函数的传入参数为1个时,会直接返回true; 即使参数是nil也是如此:

user> (= nil)
true

    传入参数为2个时,会调用clojure.lang.Util类的equiv静态方法,这个方法首先判断两个参数的引用是否相等,

如果引用相等,则返回true;然后判断两个参数是否都是Number的子类,如果是,则调用Number的equiv方法

来判断两个参数的值是否相等,如果相等,则返回true;接着判断其中一个参数是否IPersistentCollection的子类

,如果是,则调用其equiv方法并返回结果;如果此时还没有返回,就会直接调用参数的equals方法来做比较了。

    当传入参数大于2时,按惯例,必定是要进行递归了。但是我们注意到,这儿的递归和之前数学运算中的递归

有所不同,这儿采用了recur特殊form,这是个什么东西?实际上,这是clojure的显式尾递归标识。

    众所周知,递归调用的一般形式是逐层嵌套调用函数直至满足某个条件后返回结果并逐层向外传递直至到达最

外层得出最终结果,而每一次递归调用时都会往本地方法栈中压入一个栈帧,如果递归的层数够多,毫无疑问,

stackoverflow。但是很多递归实际上只需要最后一次执行的结果,递归过程中产生的结果是无用的,在这种情况

下,就可以采用尾递归。尾递归跟一般递归的不同在于,每次递归执行的方法会替换上次执行的栈帧,换句话说,

每次执行的递归操作除了将参数传递给下一次递归之外,方法体本身是没有在内存中保留的;当执行到最后一次

递归操作时,它会把结果直接返回给最外层的调用者,而不是逐层上报。这在理论上是可以进行“无限”递归的。

    让我们看看当前的情况,这是一个典型的尾递归场景,不管中间运行多少次的结果为true,只要某一次运行的

结果为false,就结束递归,将false传回给调用者。如果执行到最后一次递归仍然返回true,那么直接将这个true

返回给调用者就可以了,因为如果递归中途有false就根本执行不到最后一步递归,已经执行到了最后这一步,就代表

前面的所有结果都为true。

    我们再看看recur的用法,其实在一般情况下,recur都跟loop配对使用,loop声明递归点并建立临时绑定,recur

进行递归操作;而在当前的例子中,recur调用的尾递归实际上是对应的[x y & more],注意它们的参数匹配。如果

我们采用loop来重构,那么它的形式将是:(省略了说明性文字和元数据)

(defn =
  ([x] true)
  ([x y] (clojure.lang.Util/equiv x y))
  ([x y & more]
   (loop [one x two y others more]
     (if (= one two)
       (if (next others)
         (recur two (first others) (next others))
         (= two  (first others)))
       false))))

    这样子结构是否要清晰一些呢,只要执行到recur,就跳转到loop处开始执行。

数值等于==

    ==的功能和=相似,不过==只用于比较数字的值:

user> (== 2 2.00000)
true

    如果我们采用非Number类型的参数,就会抛出类型转换异常:

user> (== '(1 3) [1 3])
clojure.lang.PersistentList cannot be cast to java.lang.Number
  [Thrown class java.lang.ClassCastException]

    让我们看看源码:

user> (source ==)
(defn ==
  "Returns non-nil if nums all have the same value, otherwise false"
  {:inline (fn [x y] `(. clojure.lang.Numbers (equiv ~x ~y)))
   :inline-arities #{2}
   :added "1.0"}
  ([x] true)
  ([x y] (. clojure.lang.Numbers (equiv x y)))
  ([x y & more]
   (if (== x y)
     (if (next more)
       (recur y (first more) (next more))
       (== y (first more)))
     false)))

    很明显,这个源码的结构和=的几乎是一模一样,唯一的区别是在做二元判断时直接调用了clojure.lang.Numbers

的equiv方法,这就决定了==只能用于数字的比较,因为多元判断同样是递归了二元判断。

    不过我们也注意到,当参数只有一个的时候,实际上是没有类型判断的:

user> (== nil)
true

    但这个应该没什么实际用途,哈哈。

不等于not=

    not=的本质其实就是对=判断的结果取反而已,所以=支持什么类型,not=就支持什么类型:

user> (not= 3)
false
user> (not= 5 3)
true
user> (not= '(7 8 9) [7 8 9])
false
user> (not= 8M 8)
false
user> (not= 8.0M 8)
true

    源码一下就暴露出其本质了:

user> (source not=)
(defn not=
  "Same as (not (= obj1 obj2))"
  {:tag Boolean
   :added "1.0"}
  ([x] false)
  ([x y] (not (= x y)))
  ([x y & more]
   (not (apply = x y more))))

    不过这儿跟之前不同的地方在于,没有用递归(当然=的源码中还是有递归的),而是直接采用了apply。apply的

作用其实很好理解,它接收两部分的参数,第一部分是一个函数,在当前的例子中是=函数;第二部分是数个参数,这

个参数的数量跟第一部分的参数函数有关,比如:在当前例子中,=函数可以传入三个参数,那么第二部分我们就传入

三个参数x、y和一个集合more。执行时apply会首先把第二部分的参数传入第一部分的函数中,在当前例子,就是把

x、y和集合more中的所有元素传入到=函数中去,=函数执行的结果就作为apply表达式的结果返回。

    这时候,我们可能要问了,为什么不能直接采用(not (= x y more))这样的形式来调用呢?

    这儿就涉及我们之前没有提及的一个小问题:[x y & more]这种可变长度参数定义的处理方式。采用[x y & more]

这种定义时,比如,我们传入[1 2 3 4 5],1会被绑定到x,2会被绑定到y,而3、4、5则会被绑定到一个叫more的

集合中去,下面我们验证一下:

user> (defn cus+
	  ([x] x)
	([x y] (+ x y))
	([x y & more] (class more)))
#'user/cus+
user> (cus+ 1 2 3 4 5)
clojure.lang.ArraySeq

    这样子就明白了吧,如果我们直接采用(not (+ x y more))这样的形式,那么传入+函数的就是一个集合,从而

产生参数类型不匹配的异常:

user> (defn cus+
	  ([x] x)
	([x y] (+ x y))
	([x y & more] (+ x y more)))
#'user/cus+
user> (cus+ 1 2 3 4 5)
clojure.lang.ArraySeq cannot be cast to java.lang.Number
  [Thrown class java.lang.ClassCastException]

    而apply的作用是可以将集合中的所有元素取出来传入对应的函数中去,这样就保障了传入函数的必然是集合中

的元素,而非集合本身。

小于<

    <直接用来判断数字的数值大小:

user> (< 1 2 3 4)
true
user> (< 0x4f 2r11111111 54e5)
true
user> (< 54M 89)
true
user> (< \a \b)
java.lang.Character cannot be cast to java.lang.Number
  [Thrown class java.lang.ClassCastException]

    如上所见,只要是Number类型的数字都可以进行比较;而不是Number类型的参数就会抛出类型转换异常。让我们

看看源码:

user> (source <)
(defn <
  "Returns non-nil if nums are in monotonically increasing order,
  otherwise false."
  {:inline (fn [x y] `(. clojure.lang.Numbers (lt ~x ~y)))
   :inline-arities #{2}
   :added "1.0"}
  ([x] true)
  ([x y] (. clojure.lang.Numbers (lt x y)))
  ([x y & more]
   (if (< x y)
     (if (next more)
       (recur y (first more) (next more))
       (< y (first more)))
     false)))

    结构跟=函数基本上一模一样,最大的不同就在于二元操作时调用的是clojure.lang.Numbers的lt方法,进行数值

大小的比较。

大于>

    >也是直接用来判断数字的数值大小,同样支持多个参数:

user> (> 5 4 3 -1)
true
user> (> 0xf9 8r6 2r111 5e-2)
false
user> (> 54M 54)
false

    可以预见,源码跟<基本一致:

user> (source >)
(defn >
  "Returns non-nil if nums are in monotonically decreasing order,
  otherwise false."
  {:inline (fn [x y] `(. clojure.lang.Numbers (gt ~x ~y)))
   :inline-arities #{2}
   :added "1.0"}
  ([x] true)
  ([x y] (. clojure.lang.Numbers (gt x y)))
  ([x y & more]
   (if (> x y)
     (if (next more)
       (recur y (first more) (next more))
       (> y (first more)))
     false)))

    果然如此,除了调用的是clojure.lang.Numbers的gt方法。

小于等于<=

    <=同样也是进行数字的数值判断:

user> (<= 1 1 2 4 5.0 5)
true
user> (<= 0x3 8r6 2r111 13/2 5e2)
false
user> (<= 3M 3)
true

    让我们看看源码:

user> (source <=)
(defn <=
  "Returns non-nil if nums are in monotonically non-decreasing order,
  otherwise false."
  {:inline (fn [x y] `(. clojure.lang.Numbers (lte ~x ~y)))
   :inline-arities #{2}
   :added "1.0"}
  ([x] true)
  ([x y] (. clojure.lang.Numbers (lte x y)))
  ([x y & more]
   (if (<= x y)
     (if (next more)
       (recur y (first more) (next more))
       (<= y (first more)))
     false)))

    同样的结构,除了调用的是clojure.lang.Numbers的lte方法。

大于等于>=

    >=也是进行数字的数值判断:

user> (>= 30.33 30 18/5 5e-2)
true
user> (>= 0xff 8r13 2r1111)
false
user> (>= 5.43M 5.43)
true

    可以预见,源码是没什么惊喜了:

user> (source >=)
(defn >=
  "Returns non-nil if nums are in monotonically non-increasing order,
  otherwise false."
  {:inline (fn [x y] `(. clojure.lang.Numbers (gte ~x ~y)))
   :inline-arities #{2}
   :added "1.0"}
  ([x] true)
  ([x y] (. clojure.lang.Numbers (gte x y)))
  ([x y & more]
   (if (>= x y)
     (if (next more)
       (recur y (first more) (next more))
       (>= y (first more)))
     false)))
 

 

 

1
2
分享到:
评论

相关推荐

    Clojure学习——使用clojure jdbc操作mysql

    标题 "Clojure学习——使用clojure jdbc操作mysql" 指出的是一个关于使用Clojure编程语言通过Java Database Connectivity (JDBC) API来操作MySQL数据库的主题。Clojure是一种基于Lisp的函数式编程语言,它运行在Java...

    linux-用于Kubernetes操作的Clojure工具

    Keenest-Rube项目是一个开源的Clojure库,它为Kubernetes提供了简洁的API封装,使开发者能够用Clojure语法优雅地进行Kubernetes资源的创建、更新、查询和删除等操作。使用Clojure的宏和高阶函数,Keenest-Rube简化了...

    programming-clojure-3rd

    在Clojure中,Java interoperability(Java互操作性)是一个重要特性,书里会讲解如何无缝地与Java库和API交互,利用JVM的强大生态。同时,Clojure的REPL(Read-Eval-Print Loop)环境也是其一大特色,它促进了快速...

    Clojure入门教程.pdf

    ### Clojure入门教程知识点概览 #### 一、Clojure简介 ...无论是对于希望深入了解函数式编程的新手,还是对于希望在JVM平台上构建高性能系统的资深开发者而言,Clojure都是一种值得学习和使用的优秀编程语言。

    Python-Clojurecademy一个交互式平台提供基于Clojure的课程

    2. **引用(References)**:Clojure的并发机制,如原子(Atoms)、代理(Agents)和映射(Refs),确保在多线程环境中的数据一致性。 3. **映射(Maps)**:Clojure的主要数据结构之一,用于存储键值对。 4. **序列...

    用于Clojure开发的VisualStudioCode扩展

    自动完成功能则能够快速提供函数、变量和常量的建议,减少手动输入时间,同时帮助学习和记忆Clojure的API。代码格式化则保持了代码的一致性,使得团队协作更为顺畅。 其次,错误检查或Linting工具可以实时检测代码...

    Clojure封装讯飞语音SDK提供给Emacs语音调用接口通过Cider和讯飞语音通讯

    6. **Clojure与JavaScript交互**: 虽然标签中提到的是JavaScript开发,但在Clojure中,我们可以使用cljs.js库或者GraalVM的JavaScript引擎来实现Clojure和JavaScript代码的互操作,从而调用讯飞SDK的原生JavaScript...

    Lightmod一个全功能的Clojure全栈开发工具

    7. **文档和学习资源**:Lightmod通常会附带丰富的文档和教程,帮助初学者快速上手Clojure全栈开发,同时也为有经验的开发者提供深入学习的资料。 8. **社区支持**:作为开源项目,Lightmod拥有活跃的社区,开发者...

    programming clojure

    - **互操作性**:Clojure提供了丰富的工具来操作Java对象,如`dot`操作符。 **复用Java基础设施:** - **企业级应用**:可以轻松地将Clojure集成到现有的Java企业应用中,利用其强大的并发能力和简洁的代码风格提升...

    clojang:用于ErlangOTP通信的Clojure API(基于jiface构建)

    clojang就是这样一座桥,它是一个用Clojure编写的API,允许Clojure程序与Erlang OTP系统无缝通信。clojang是基于jiface构建的,jiface是Java对Erlang VM(BEAM)的接口,使得Java开发者可以利用Erlang的特性。 首先...

    clojure-notes:Clojure学习笔记

    2. 函数式编程基础:Clojure提供了map、filter、reduce等函数,用于对集合进行操作,这些函数遵循纯函数原则,不改变原始数据,返回新的结果。 三、Clojure的并行处理 1. 并发模型:Clojure的并发模型基于软件事务...

    spec-swagger:使用Clojure(Script)和clojure.spec掌握Swagger2和OpenAPI3的主要规格

    通过这个项目,你可以学习如何在Clojure和ClojureScript环境中利用clojure.spec实现对Swagger2和OpenAPI3规范的支持,从而提升API设计的质量和一致性。同时,这种做法也便于自动化测试和文档生成,简化了API的开发和...

    为mysql设计的Clojure库.zip

    这些库通常由Clojure开发者编写,以提供方便的API接口,使Clojure程序员能更高效地与MySQL进行交互。 描述中的"mysql为mysql设计的Clojure库.zip"进一步确认了这个压缩包的目标是为Clojure提供MySQL支持。这意味着...

    clojure-ipfs-api:Clojure的IPFS包装器

    使用 `clojure-ipfs-api`,Clojure 开发者可以执行以下操作: - **添加文件**:将本地文件或数据流添加到 IPFS 网络,获得其 IPFS 哈希。 - **检索文件**:通过文件的 IPFS 哈希从网络中获取文件。 - **链接数据**...

    Apress.Practical.Clojure.May.2010.rar

    本书是针对那些希望通过学习Clojure来拓宽编程视野、提升开发效率的开发者们准备的。 Clojure的核心特点包括: 1. 函数式编程:Clojure鼓励使用纯函数,避免副作用,这使得代码更易于测试和理解。它内置了高阶函数...

    从Clojure读取和写入Office文档

    `docjure`是Clojure社区开发的一个开源项目,它提供了方便的API来操作Excel文件,包括读取、写入和修改数据。该库依赖于Apache POI库,一个强大的Java API,用于处理Microsoft Office格式的文件。 在使用`docjure`...

    Python-利用Clojure实现的一个可拖放的看板示例

    标题中的"Python-利用Clojure实现的一个可拖放的看板示例"表明这是一个结合了Python和Clojure技术的项目,旨在创建一个可交互式的看板应用,支持用户通过拖放操作来管理任务或信息。看板在项目管理和敏捷开发中广泛...

    d2q:一个简单,性能友好且普遍适用的引擎,用于在Clojure中实现图拉API服务器

    它鼓励开发者使用Clojure的自然语法来定义路由和处理函数,从而降低学习曲线,提高开发效率。同时,其底层实现优化了请求处理流程,确保了服务的高性能。 3. **API路由** 在`d2q`中,定义API路由非常直观。你可以...

Global site tag (gtag.js) - Google Analytics