Clojure: 在“if”语句中去除重复的惯用方式是什么?

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)degree
Clojure/Lisp的方法是如何消除这种代码重复的?我觉得应该涉及let,但我不确定。任何与此代码块相关的其他一般指针也将不胜感激。
编辑:我已经按照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))))
        )
    )

感谢所有快速回答的人。

2
至少对于common-lisp(我认为clojure也是如此),那最后两个括号通常放在(take...)行的末尾;使用一个良好的编辑器来正确缩进lisp代码,缩进将代替您使用这最后两个括号的位置。 - Clayton Stanley
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 的结果,可能会更好。


谢谢,已点赞。你的回答当然是正确的,但我认为“let”让它更易读。 - Paul Sanwald
@PaulSanwald 是的,这就是为什么我的回答会这样说的原因。 - Marcin

6

i would write:

(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来隐藏值的方式,但我认为这能表达出意图。

编辑:与其他答案相比,我已提取出该值。我更喜欢这种方法,因为我发现嵌套评估的长链更难读懂。你的经验可能有所不同。

顺便思考一下[几天后],如果你在多个地方使用这种风格(其中参数可以是值或从前一个值中提取数据的键),那么我可能会考虑编写一个宏来自动化这个过程(即生成具有上述形式的自动生成的let的fn)。主要问题是决定如何指示哪些参数以这种方式处理(还有,我担心这可能会混淆你正在使用的任何IDE)。


4
在Clojure(以及大多数其他的Lisp方言)中,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风格,因为虚线选项被视为更易读。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接