clojure 宏
对我来说,学习一门新语言就像进海:一次只脚趾。
这是在学习Clojure的焦点series.Other职位包括第三职位:
- 解码Clojure代码,让您不知所措
- 学习Clojure:应对动态打字
- 学习Clojure:arrow和doto宏 (本文)
- 学习Clojure:动态调度
- 学习Clojure:依赖类型和基于合同的编程
- 学习Clojure:与Java流进行比较
- 关于学习Clojure的反馈:与Java流进行比较
- 学习Clojure:换能器
本周,我们将看一些强大的宏。
问题
当您不习惯Clojure时,括号有时会削弱代码的可读性。
(- 25 (+ 5 (* 3 (- 5 (/ 12 4)))))
上述片段的Kotlin等效项为:
25-(5+(3*(5-(12/4))))
显然,它与Clojure或括号都不相关。 让我们使用“数据管道”重新整理上面的Kotlin代码段,以提高可读性:
4.let { 12 / it }
.let { 5 - it }
.let { 3 * it }
.let { 5 + it }
.let { 25 - it }
解决方案
我敢肯定,以顺序方式呈现处理可以大大提高可读性,尤其是随着操作数量的增加。 Clojure是否可以提供相同的顺序处理功能不是很好吗? 幸运的是,它使用了特定的宏 :
(->> 4 (1)
(/ 12) (2)
(- 5) (2)
(* 3) (2)
(+ 5) (2)
(- 25)) (2)
- 从这个值开始
- 应用功能并将上一行的结果作为最后一个参数
与Kotlin相比,唯一的结构差异是it
参数是隐式的 ,并且用作最后一个参数。
->>
是一个宏 ,称为thread-last宏 。
巨集
Clojure具有程序化的宏系统,该系统允许用户代码扩展编译器。 宏可用于定义语法构造,这需要其他语言的原语或内置支持。 实际上,Clojure的许多核心构造不是基元,而是普通的宏。
https://clojure.org/reference/macros
宏是非常强大的语言构造,无论是Clojure还是其他语言。 谨慎而明智地使用它们,可以使某些代码摘录更易于阅读和编写。 在其他情况下,它们可以比说“宏”更快地破坏代码库的可读性。 在创建自己的模型之前,请务必谨慎并考虑它。
Clojure宏的macroexpand
可以通过macroexpand
函数解释它们。 结合REPL,它是开发人员手中的绝佳工具。 例如,让我们使用macroexpand
检查上面代码的结果:
(macroexpand '(->> 4 (1)
(/ 12)
(- 5)
(* 3)
(+ 5)
(- 25)))
- 圆括号前的单引号可防止Clojure将其解释为表达式的开始,而不是一个集合。
如预期的那样,它产生:
=> (- 25 (+ 5 (* 3 (- 5 (/ 12 4)))))
更多宏
Clojure中有大量可用的宏。 其中,有些与上面的thread-last宏密切相关:
-
线程优先
->>
宏将前一个表达式的结果用作最后一个参数,而线程优先->
将其用作第一个参数。 根据预期的类型,它可以:
- 由于类型不匹配而阻止代码运行
- 更改返回的结果
- 保持相同的结果( 即用于交换函数,例如加法)
例如,让我们将以上代码片段的thread-last宏更改为thread-first,然后检查每一行的结果:
(-> 4
(/ 12) (1)
(- 5) (2)
(* 3) (3)
(+ 5) (4)
(- 25)) (5)
- 1/3
- -14 / 3N
- -14N
- -9N
- -34N
-
去做
在上面的示例中,箭头函数(或线程函数)与let
相似。 它们都应用函数并返回结果-唯一的区别是隐式参数的位置。 尽管Clojure是一种功能编程语言,但有时还是需要处理可变引用。 通常,与Java集成时就是这种情况。
在Kotlin中,将使用apply
函数; 在Clojure中,等效项是doto
:
(doto (HashMap.)
(.put :a "Alpha")
(.put :b "Beta")) (1)
-
{:b "Beta", :a "Alpha"}
使用macroexpand
显示最终形式:
(macroexpand '(doto (HashMap.)
(.put :a "Alpha")
(.put :b "Beta")))
(let* [G__1755 (HashMap.)] (1)
(.put G__1755 :a "Alpha") (2)
(.put G__1755 :b "Beta") (2)
G__1755) (3)
- 在随机引用名称下创建
HashMap
的新实例 - 将数据放入地图
- 返回地图
结论
在本文中,我们看到了一些特定的宏,这些宏在开发过程中非常有用。 箭头功能类似于Kotlin的let
,而doto
是类似的apply
。
clojure 宏