作为Lisp方言的Clojure继承了Lisp的同音性。同源性使元编程更容易,因为代码可以被视为数据:语言中的反射(在运行时检查程序的实体)依赖于单个同构结构,并且它不必处理复杂语法中出现的几种不同结构[1]
更加同质的语言结构的缺点是语言结构,例如循环,嵌套ifs,函数调用或开关等,彼此更相似。
在clojure中:
;; if:
(if (chunked-seq? s)
(chunk-cons (chunk-first s) (concat (chunk-rest s) y))
(cons (first s) (concat (rest s) y)))
;; function call:
(repaint (chunked-seq? s)
(chunk-cons (chunk-first s) (concat (chunk-rest s) y))
(cons (first s) (concat (rest s) y)))
两种结构之间的差异只是一个词。用非同性语言:
// if:
if (chunked-seq?(s))
chunk-cons(chunk-first(s), concat(chunk-rest(s), y));
else
cons(first(s), concat(rest(s), y));
// function call:
repaint(chunked-seq?(s),
chunk-cons(chunk-first(s), concat(chunk-rest(s), y)),
cons(first(s), concat(rest(s), y));
有没有办法让这些程序结构在Clojure中更容易识别(更显眼)?也许一些推荐的代码格式或最佳实践?
答案 0 :(得分:5)
除了使用支持不同情况的语法突出显示的IDE之外,没有,在代码本身中没有办法区分它们。
您可以尝试使用格式来区分函数调用和宏:
(for [a b]
[a a])
(some-func [a b] [a a])
但是这会阻止您使用for
使用单行列表理解;有时它们可以整齐地放在一条线上。这也可以防止您将大型函数调用分解为多行。除非预先定义了还原功能,否则我对reduce
的大多数调用都采用以下形式:
(reduce (fn [a b] ...)
starting-acc
coll)
尝试限制调用格式的方式太多了。那些更复杂的宏如cond
怎么样?
我认为要理解的关键是表单的操作完全取决于表单中的第一个符号。不要依赖特殊的语法来区分它们,而是训练你的眼睛抓住表格中的第一个符号,然后快速“查找”你的头脑。
实际上,只有少数案例需要考虑:
if
和let
等特殊表单(实际上是let*
)。这些是语言的基本结构,因此您将不断接触它们。
if
时,你的大脑应该立即知道发生了什么。 There are so few special forms简单记忆是最佳途径。具有“异常”行为的宏,如线程宏和cond
。在某些情况下,我会查看某人的代码,并且因为他们使用的是我不太熟悉的宏,所以我需要花一点时间来弄清楚代码的流程
功能。如果它不是上述任何一个,它必须是一个函数,并遵循典型的函数调用语法。