5

我对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 方式是什么?我觉得它应该涉及一个让,但我并不积极。与此代码块相关的任何其他通用指针也值得赞赏。

编辑:我根据安德鲁·库克的建议重构了代码,函数现在显示为:

(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

4 回答 4

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.

于 2012-04-08T17:00:15.610 回答
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,但在这里我认为它有助于显示意图。

编辑:与其他答案相比,我已经提取了价值。我更喜欢嵌入,因为我发现一长串嵌入评估更难阅读。ymmv。

ps想多一些[这几天后]如果您在多个地方使用这种样式(其中参数可以是一个值或从前一个值中提取数据的键),那么我可能会考虑编写一个宏来自动化该过程(即使用上述形式的自动生成的 let 生成 fn 的东西)。主要问题是决定如何指示以这种方式处理哪些参数(而且,我担心这会如何混淆您正在使用的任何 ide)。

于 2012-04-08T17:00:34.223 回答
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 中,-不是特殊的或保留的,因此您可以并且应该在变量名中使用它。最好使用 lisp 样式来num-voices代替num_voicesor numVoices,因为虚线选项被认为更具可读性。

于 2012-04-08T17:00:18.497 回答
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)))))))
于 2012-04-08T17:03:20.777 回答