使 Clojure let 语句更具功能性

Making Clojure let statement more functional

在我的第一个 Clojure 项目中,一切都很顺利,除了最后的这一部分:

(let [broken-signs    (->> (:symbols field)
                           (map make-sign)
                           (filter broken?))
      broken-count    (count broken-signs)
      unfixable-count (-> (filter (complement fixable?) broken-signs)
                          (count))]
  (println
    (if (> unfixable-count 0)
      -1
      (- broken-count unfixable-count))))

缩进看起来不对,感觉不实用,因为我在 let 块中重用状态。我基本上先数一下损坏的标牌数量,然后再数可修复标牌的数量。如果任何标志是不可修复的,我打印-1,否则我打印要修复的标志数。

如果我 mapped/filtered 两次,我会有重复的代码,但最重要的是它会 运行 变慢。尽管如此,是否有改进此代码的方法?

编辑:这是我确定的

 (defn count-broken-yet-fixable []
   (let [broken (->> (:symbols field)
                     (map make-sign)
                     (filter broken?))
         unfixable (remove fixable? broken)]
     (when (empty? unfixable)
       (count broken))))

 (defn solve-task []
   (if-let [result (count-broken-yet-fixable)]
     result
     -1))

(println (solve-task))

减法确实没有必要,计数也不必发生在 let 块中。在输入错误时输出 -1 也不是函数的工作,只是任务的一部分。

您的代码没有副作用(打印除外,我们会修复),所以我不会说它不起作用。 let 专为建立中间值而设计。在评论中有一些潜在的改进。

                      ; align here (controversial)
(let [broken-signs    (->> (:symbols field)
                           (map make-sign)
                           (filter broken?))
      broken-count    (count broken-signs)
      unfixable-count (->> broken-signs ; maybe emphasize non-function start
                           (filter (complement fixable?))
                           count)] ; no parens needed
  ;; don't print; just return number
  (if (< 0 unfixable-count)  ; prefer less-than (Elements of Clojure book)
    -1
    (- broken-count unfixable-count)))

我强烈推荐 Clojure Style Guide 以获得相关建议。

我认为您的方法没有任何 "non-functional" 或错误。缩进看起来不错。

(let [broken (->> (:symbols field)
                  (map make-sign)
                  (filter broken?))
      unfixable (remove fixable? broken)]
  (when (seq unfixable)
    (- (count broken) (count unfixable))))
  • 您可以将 filter (complement 替换为 remove
  • 可以使用 pos? 而不是 (> n 0)
  • 我可能会在 if 中放两个 println,但实际上最好 return 一个值
  • 您可以内联 broken-count 绑定,因为它只用在一个地方
  • 我个人认为使用较少的线程宏更容易阅读
  • 由于需要计算 unfixables 是有条件的,您可以先用 seq 测试值
  • 如果你 return -1 作为标记值,​​我会用 nil 代替;当不满足 when 条件时,这自然会发生
  • 条件逻辑似乎倒退:当unfixable-count为正时你return-1,只有当它不是正时才使用它的值(这意味着它是零b/c计数不能为负数),例如它可以重写为 (- broken-count 0) 然后只是 broken-count
(let [broken-signs (->> (:symbols field)
                        (map make-sign)
                        (filter broken?))]
  (if-let [unfixable-signs (seq (remove fixable? broken-signs))]
    -1
    (- (count broken-signs) (count unfixable-signs)))

您的代码已经非常简洁且布局合理。我想做的唯一改变是尽可能长时间地留在域中——在本例中是 signs 对象。并且只在很久以后使用计数。

return 值然后可以使用打印来执行实际操作。