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

Clojure-JVM上的函数式编程语言(9)宏 作者: R. Mark Volkmann

阅读更多

 原帖地址:http://java.ociweb.com/mark/clojure/article.html#Macros

 作者:R. Mark Volkmann

 译者:RoySong

 

宏(Macros)

    宏被用来为语言添加新的功能结构。它们是在读取时(read-time)用来产生代码的代码。

 

    函数总是要对它所有的参数求值,然而宏可以决定它的哪个参数被求值。这点对于实现诸如

(if condition then-expr else-expr )这样的form非常重要。如果 condition 为true,那么只有

"then"表达式会被求值。如果condition 为false,那么只有"else"表达式会被求值。这代表着if

不能被实现为函数(实际上它是一个特殊form,而不是一个宏)。其他同样因为这个原因被实

现为宏的form包括and和or,因为它们需要做“短循环”("short-circuit")。

 

    为了确定一个指定的操作是作为函数还是宏来实现,既可以在REPL中输入(doc name ),也可以

检查它的元数据。如果是个宏的话,它的元数据会包含一个:macro关键字并有一个true值。比如,

为了确定and的实现类型,在REPL中输入以下内容:

((meta (var and)) :macro) ; long way -> true
(^#'and :macro) ; short way -> true
 

    让我们通过编写和使用宏来轻松地实现一些例子。假设我们的代码中有很多地方需要进行不同的

操作基于某个某个数字是否真正接近于0,正数或者负数。我们想避免代码重复。这必须采用宏而不

是函数来实现,因为在某个条件下应该是一条语句被求值而不是三条(正,负,0)。采用defmacro

宏来创建一个宏:

(defmacro around-zero [number negative-expr zero-expr positive-expr]
  `(let [number# ~number] ; so number is only evaluated once
    (cond
      (< (Math/abs number#) 1e-15) ~zero-expr 
      (pos? number#) ~positive-expr
      true ~negative-expr)))
 

    读取器会将对around-zero宏的调用展开到对 let 特殊form的调用上去。let特殊form里面包含了一个

对cond函数的调用,cond的参数就是各项条件以及对应的返回值。在这儿采用let特殊form是为了在

第一个参数number接收的是一个表达式而非简单值得情况下提升效率。它只会对number求值一次,

然后在cond里面两次采用这个值。而系统自动生成变量声明(auto-gensym)number#是用来产生

一个独特的符号名而不会和其他符号名冲突。这样就允许了对 hygienic macros 的创建。

 

    宏定义开头的后引号(“`”,又名语法引证syntax quote,编者注:键盘上1左边那个

键,不是单引号)避免了里面的所有内容被求值,直到引文结束。这代表着宏主体里面的内容都会按照

字面被展开,除了带波浪线的元素外(在上面的例子中,是 number , zero-expr , positive-expr

negative-expr)。而这些在语法引证 列表中前面带波浪线的符号,都会在展开时以其对应的值来代替。

在语法引证列表中的绑定如果它的值是序列,则可以在它的前面加上~@来代替它的个体值。

 

    下面是使用这个宏的两个例子,预期的输出都是“+”:

(around-zero 0.1 (println "-") (println "0") (println "+"))
(println (around-zero 0.1 "-" "0" "+")) ; same thing
 

    如果需要在某处进行不止一次的求值,则采用do特殊form来包装它们。举个例子,如果number代表

温度,而我们用一个log函数来将它写入到日志文件中,那么我们会这么编写:

(around-zero 0.1
  (do (log "really cold!") (println "-"))
  (println "0")
  (println "+"))
 

    为了验证这个宏是否正确地展开了,我们在REPL中输入以下内容:

(macroexpand-1
  '(around-zero 0.1 (println "-") (println "0") (println "+")))
 

    输入结果如下,不过实际中没有缩进:

(clojure.core/let [number__3382__auto__ 0.1]
  (clojure.core/cond
    (clojure.core/< (Math/abs number__3382__auto__) 1.0E-15) (println "0")
    (clojure.core/pos? number__3382__auto__) (println "+")
    true (println "-")))
 

    下面的函数采用了around-zero宏,并将返回值封装成单词:

(defn number-category [number]
  (around-zero number "negative" "zero" "positive"))
 

    下面是一些使用函数的例子:

(println (number-category -0.1)) ; -> negative
(println (number-category 0)) ; -> zero
(println (number-category 0.1)) ; -> positive
 

    因为宏不会对其参数求值,所以未印证的函数名可以作为参数传递给宏,然后就可以构造对这些函数

的带参调用。函数定义无法做到这一点,与之替代的是传递一个匿名函数来包装对函数的调用。

 

    下面有一个接收两个参数的宏,第一个参数是一个函数,它拥有一个参数用于接受一个弧度数值,就像

三角函数;第二个参数直接接收一个角度数值。如果在这儿采用函数定义来替代宏,我们就不得不采用

#(Math/sin %)这种形式来代替简单的 Math/sin。注意对 #号后缀的使用通过系统来生成独特的本地绑定

名,这通常是必要的来避免同其他绑定名冲突。#和~都只能在语法印证列表中使用。

(defmacro trig-y-category [fn degrees]
  `(let [radians# (Math/toRadians ~degrees)
         result# (~fn radians#)]
     (number-category result#)))
 

    让我们实验一下,底下的调用预期的输出是 "zero", "positive", "zero"和"negative"。

(doseq [angle (range 0 360 90)] ; 0, 90, 180 and 270
  (println (trig-y-category Math/sin angle)))
 

    宏的名字不能作为参数传递给函数。举个例子,一个宏的名称and不能传递给函数reduce。一种变通方案

是定义一个匿名函数来调用宏。举个例子,采用(fn [x y] (and x y)) 或者 #(and %1 %2)这样的形式。宏

会在读取时在匿名函数内部展开。当这个匿名函数作为参数传递给其他函数比如reduce,实际上是一个函数

对象而不是宏的名字被传递。

 

    对宏的调用是在读取时被处理的。

分享到:
评论

相关推荐

    函数式编程语言:Clojure.zip

    史上最全编程语言全套教程,共99门...函数式编程语言 壳编程语言 常见编程语言 并行编程语言 数据分析编程语言 数据库查询语言 系统编程语言 脚本编程语言 逻辑编程语言 面向对象编程语言 等所有常见的变成语言系列教程

    Clojure入门教程- Clojure – Functional Programming for the JVM中文版

    - **Clojure**作为一种功能强大且灵活的函数式编程语言,在JVM上运行具有独特的优势。它不仅支持高效的函数式编程,还具备出色的并发编程能力,使其成为开发高性能、高并发系统的理想选择。 - 对于希望深入了解函数...

    Clojure脚本:函数式编程的现代演绎

    Clojure是一种运行在Java虚拟机(JVM)上的动态函数式编程语言,它属于Lisp家族。Clojure的设计哲学强调了函数作为一等公民的地位,以及数据的不可变性,这些特性使得Clojure在处理并发编程和数据密集型应用时表现...

    clojure-utils, 各种小型但方便的clojure实用程序函数库.zip

    clojure-utils, 各种小型但方便的clojure实用程序函数库 各种小型但方便的Clojure实用程序函数库特别关注:Clojure.java - 用于从Java调用Clojure的实用工具函数的Java类arrays.clj - 操作Java数组core.clj - 应该在...

    来源clojure-toolbox.com___下载.zip

    1. **Clojure编程语言**:Clojure是一种基于Lisp的函数式编程语言,运行在Java虚拟机(JVM)上。它强调 immutability(不可变性)、concurrency(并发性)和functional programming(函数式编程)特性。 2. **函数...

    Scala与Clojure函数式编程模式:Java虚拟机高效编程1

    Scala和Clojure都是现代函数式编程语言,它们都运行在Java虚拟机(JVM)上,能够利用Java的生态系统和资源。Scala是一种多范式语言,结合了面向对象和函数式编程的特点,提供了强大的类型系统和模式匹配功能。...

    clojure-1.5.1.jar

    clojure-1.5.1.jar

    clojure-sha-3-源码.rar

    Clojure是一种基于Lisp的现代函数式编程语言,它运行在Java虚拟机(JVM)上,充分利用了Java平台的强大功能。SHA-3,全称为Secure Hash Algorithm 3,是美国国家标准与技术研究所(NIST)发布的一种密码散列函数标准...

    CS1807-U201814745-朱槐志函数式编程1

    Lisp是一种古老的函数式编程语言,其主要特点是使用S-表达式(Symbolic Expression)表示数据和程序。Lisp的数据结构核心是列表,而列表是通过递归定义的,这使得在Lisp中实现递归算法变得十分自然。它的宏系统允许...

    基于Java平台的多语言混合编程.pdf

    5. Clojure:基于JVM的动态类型语言,支持函数式编程和基于宏的 metabrogramming。 Java平台的多语言混合编程的未来发展趋势是: 1. 软件项目的未来在于混合语言编程,Java仍将是JVM生态系统中的重要组成部分。 2....

    clojure-basics-源码.rar

    Clojure是一种基于Lisp的函数式编程语言,它运行在Java虚拟机(JVM)上,充分利用了Java的生态系统。这个“clojure-basics-源码.rar”压缩包很可能是包含了一些基本Clojure编程概念的示例代码或者教程。虽然没有具体...

    clojure-must-watch-源码.rar

    Clojure是一种功能强大的Lisp方言,它在Java虚拟机(JVM)上运行,并且具有丰富的函数式编程特性。这个压缩包"clojure-must-watch-源码.rar"似乎包含了与Clojure相关的源代码,可能是为了帮助学习者深入理解Clojure...

    Scala与Clojure函数式编程

    虽然Java本身不是一种函数式编程语言,但是JVM强大的生态系统为运行函数式编程语言提供了坚实的基础。 本书的作者Michael Bevilacqua-Linn通过自己的经验,结合了多个编程实践者的点评,将关于Scala和Clojure中函数...

    clojure-1.6.0-RC3.zip

    Clojure是一种基于Lisp的函数式编程语言,它运行在Java虚拟机(JVM)上,充分利用了Java生态系统的优势。这个压缩包很可能是Clojure 1.6.0-RC3的源代码发布,开发者可以下载、编译和测试以确保其稳定性和兼容性。 ...

    函数式编程初探共2页.pdf.zip

    8. **函数式编程语言**:一些语言天生就是函数式的,如Haskell、Lisp和Clojure;还有一些语言虽然不是纯函数式的,但支持函数式编程特性,如Python、JavaScript和Scala。 9. **monads(范畴论)**:在高级函数式...

    Clojure入门介绍: Clojure - Functional Programming for the JVM

    Clojure入门介绍: Clojure - Functional Programming for the JVM

    clojure1.3.0及资料

    Clojure是一种基于Lisp的函数式编程语言,它运行在Java虚拟机(JVM)上,充分利用了Java的生态系统。Clojure的设计目标是提供一种静态类型的、并发的、内存安全的语言,同时保持Lisp的简洁性和灵活性。在这个压缩包...

    clojure-1.5.0.zip

    该版本为稳定版,将zip文件解压,放到某个指定目录,cd进入这个目录,执行以下命令即可java -cp clojure-1.5.0.jar clojure.main。...当今最主流的运算平台JVM,把函数式编程语言引入JVM也是新方向。

    并行编程语言:Clojure.zip

    史上最全编程语言全套教程,共99门...函数式编程语言 壳编程语言 常见编程语言 并行编程语言 数据分析编程语言 数据库查询语言 系统编程语言 脚本编程语言 逻辑编程语言 面向对象编程语言 等所有常见的变成语言系列教程

    函数式编程另类指南.pdf

    - **Lisp**及其衍生语言(如Scheme和Clojure):历史悠久的函数式编程语言,支持宏定义等高级特性。 - **Scala**:结合了面向对象和函数式编程特性的多范式语言。 - **Erlang**:强调并发处理和分布式系统的函数式...

Global site tag (gtag.js) - Google Analytics