我对clojure很新,而且之前我还没有做过大量的lisp。我有一个包含以下内容的函数:
(defn chord
([scale degree num_voices]
(if
(keyword? degree)
(take num_voices (take-nth 2 (cycle (invert scale (.indexOf scale degree)))))
(take num_voices (take-nth 2 (cycle (invert scale degree))))))
显然,这段代码很差,因为这里有两个几乎相同的函数调用是次优的,唯一的区别是(.indexOf scale degree)
vs degree
。
什么是Clojure / Lisp删除此代码重复的方法?我觉得它应该涉及让,但我不是积极的。还可以理解与此代码块相关的任何其他常规指针。
编辑:我根据andrew cooke的建议重新考虑了代码,该函数现在显示为:
(defn chord
([scale degree num_voices]
(let [degree (if (keyword? degree) (.indexOf scale degree) degree)]
(take num_voices (take-nth 2 (cycle (invert scale degree))))
)
)
感谢所有回答如此之快的人。
答案 0 :(得分:6)
if
返回一个表达式,因此反转函数的结构:
(defn chord
([scale degree num_voices]
(take num_voices (take-nth 2 (cycle (invert scale (if (keyword? degree)
(.indexOf scale degree)
(invert scale degree))))))))
如果您使用let来捕获if
的结果,那可能会更好。
答案 1 :(得分:6)
我会写:
(defn chord [scale degree num_voices]
(let [degree (if (keyword? degree) (.indexOf scale degree) degree)]
(take num_voices (take-nth 2 (cycle (invert scale degree)))))
不确定它有帮助 - 没有一般原则,除了使用let
。也许,也许其他人不喜欢我用degree
影子价值的方式,但在这里我认为它有助于显示意图。
编辑:与其他答案相比,我已经拿出了价值。我更喜欢这种嵌入,因为我发现很长一段时间的嵌入式评估难以阅读。因人而异。
如果你在多个地方使用这种风格(其中一个参数可以是从前一个值中提取数据的值或一个键),那么我会考虑写一个宏自动化该过程(即生成带有自动生成的上述形式的fn的东西)。主要的问题是如何确定如何以这种方式处理哪些paraneters(而且,我会担心这会如何混淆你正在使用的任何ide)。答案 2 :(得分:4)
在Clojure(和大多数其他lisps)中,if
返回一个值,就像每个其他表达式一样。例如,
(if (even? 3) 1 0)
评估为0
。
您可以使用此知识通过在if
语句之外移动代码的相同部分来重构代码,如下所示:
(defn chord [scale degree num-voices]
(take num-voices (take-nth 2
(cycle (invert scale
(if (keyword? degree)
(.indexOf scale degree)
degree))))))
此外,在Lisp中,-
并不特殊或保留,因此您可以并且应该在变量名中使用它。使用num-voices
代替num_voices
或numVoices
是更好的lisp风格,因为虚线选项被视为更具可读性。
答案 3 :(得分:0)
没有太多可以简化程序的方法,可能会将if
内部的take num_voices
移到(defn chord ([scale degree num_voices]
(take num_voices
(take-nth 2
(cycle (invert
scale
(if (keyword? degree) (.indexOf scale degree) degree)))))))
,如下所示:
{{1}}