clojure:在“if”中删除重复的惯用方法?

时间:2012-04-08 16:39:39

标签: clojure refactoring lisp

我对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))))
        )
    )

感谢所有回答如此之快的人。

4 个答案:

答案 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_voicesnumVoices是更好的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}}