来自递归的clojure重构代码

时间:2015-06-15 09:49:05

标签: clojure

我有以下一些代码可以产生正确的结果:

(ns scratch.core
  (require [clojure.string :as str :only (split-lines join split)]))

(defn numberify [str]
  (vec (map read-string (str/split str #" "))))

(defn process [acc sticks]
  (let [smallest (apply min sticks)
        cuts (filter #(> % 0) (map #(- % smallest) sticks))]

    (if (empty? cuts)
      acc
      (process (conj acc (count cuts)) cuts))))

(defn print-result [[x & xs]]
  (prn x)
  (if (seq xs)
    (recur xs)))

(let [input "8\n1 2 3 4 3 3 2 1"
      lines (str/split-lines input)
      length (read-string (first lines))
      inputs (first (rest lines))]
  (print-result (process [length] (numberify inputs))))

上面的process函数以递归方式调用自身,直到序列sticksempty?

我很想知道我是否可以使用take-while或其他技术来使代码更简洁?

如果我需要对一个序列做一些工作,直到它是空的,那么我使用递归,但我不能帮助认为有更好的方法。

2 个答案:

答案 0 :(得分:2)

您的核心问题可以描述为

  1. 如果棒数为零则停止
  2. 积累棍棒数
  3. 从每根棍子中减去最小的棍子
  4. 过滤阳性棒
  5. 回到1。
  6. 将最小的子问题识别为步骤3和4并在其周围放置一个框

    (defn cuts [sticks]
      (let [smallest (apply min sticks)]
        (filter pos? (map #(- % smallest) sticks))))
    

    请注意,sticks在步骤5和3之间没有变化,cuts是fn stick-&gt;棒,所以请使用iterate在其周围放置一个框:< / p>

    (defn process [sticks]
      (->> (iterate cuts sticks)
    ;; ----- 8< -------------------
    

    这会给出sticks(cuts sticks)(cuts (cuts sticks))等无限序列,等等

    合并第1步和第2步

    (defn process [sticks]
      (->> (iterate cuts sticks)
           (map count)         ;; count each sticks
           (take-while pos?))) ;; accumulate while counts are positive
    
    (process [1 2 3 4 3 3 2 1])
    ;-> (8 6 4 1)
    

    在场景后面,这个算法与你发布的算法几乎没有区别,因为懒惰的seqs是递归的延迟实现。它更具惯用性,更模块化,使用延迟取消,增加了它的表现力。此外,如果木棒是空的,它也不需要传递初始计数并做正确的事情。我希望这就是你要找的东西。

答案 1 :(得分:0)

我认为编写代码的方式是一种非常好的方式。当然,The Little Schema中有很多例子都遵循这种缩减/递归格式。

要替换递归,我通常会寻找一个涉及使用高阶函数的解决方案,在本例中为reduce。它在开始时用一种排序替换每次迭代的min次调用。

(defn process [sticks]
  (drop-last (reduce (fn [a i]
                       (let [n (- (last a) (count i))]
                         (conj a n)))
                     [(count sticks)]
                     (partition-by identity (sort sticks)))))

(process [1 2 3 4 3 3 2 1])
=> (8 6 4 1)

我已经通过在排序后对相同的数字进行分组,然后对每个组进行计数并减少计数大小来更改算法以适应reduce。