【问题标题】:folding hash function in clojureClojure中的折叠哈希函数
【发布时间】:2016-03-30 18:36:59
【问题描述】:

折叠散列函数将其输入(在本例中为 int)分解为 p 长度的段并添加这些部分。如果(not (= (mod input p) 0 )),即输入的长度不是p的倍数,则最后一段可能是小于p的任意长度。

到目前为止,这是我的代码:

(def firstX
  (fn [x item]
    (take x (str item))))

(def remX
  (fn [x item]
    (drop x (str item))))

(def divItemEq
  (fn [a digits]
      (cond (> digits (count (str a))) (a)
            true (cons (list (firstX digits a)) (divItemEq (remX digits a) digits)))))

(def sumDigits
  (fn [x]
    (reduce + x)))

(def exItem1 76123451001214)
(print (sumDigits (divItemEq exItem1 3)))

此时,由于 divItemEq 中的递归条件从未满足,因此会发生无限递归(堆栈溢出)。我尝试解决这个问题导致了各种类型/转换错误。

对如何改进divItemEq有任何建议吗?

【问题讨论】:

  • 您已经解释了实际行为(StackOverflowError);你能给出预期/期望的行为吗?
  • 期望的行为:(divItemEq exItem1 3) 计算结果为 ((761) (234) (510) (012) (14)),它被传递给 sumDigits。该应用程序产生 1531。旁注:我在第二次检查时更正了递归情况。我预计我的麻烦源于我对 Clojure 类型系统的基本理解。
  • 改用partition
  • 我希望避免使用内置函数,但感谢partition@jmargolisvt 的建议
  • 我想我的麻烦是将 int 输入转换为 lat(原子列表)之类的东西

标签: clojure hash-function


【解决方案1】:

这里有很多需要改进的地方,首先是使用更惯用的defn 而不是(def name (fn 方案。接下来是你的名字,我觉得不太清楚。其他人已经向您指出了使用 partition 的方向,我会同意,但在下面的解决方案中,我试图保持您的方法的精神。

正如您自己发现的那样,将数字转换为数字序列非常有意义,因为无论如何我们都需要序列操作。所以让我们开始吧:

(defn decimal-digits [number]
  (letfn [(ddigits-helper [number result]
              (if (number > 0)
                  (recur (int (/ number 10)) (conj result (mod number 10)))
                  result))]
     (ddigits-helper number '())))

这种方法使用简单的数学来收集数字。它还展示了使用recur 进行递归并使用累加器(result 参数)来收集中间结果。

这是我们假设现在对数字列表进行操作的两个初始函数:

(defn new-part [number size]
  (take size number))

(defn rest-part [number size]
  (drop size number))

如前所述,您也可以使用partitionsplit-at

现在,您的 main 函数可能如下所示:

(defn partition-number
        [number size]
        (let [digits (decimal-digits number)]
          (cond (> size (count digits)) digits
                true (cons (new-part digits size)
                           (partition-number 
                              (digits-to-decimal (rest-part digits size)) 
                              size)))))

这在风格上与您的方法非常接近,但表明我们需要将数字变回数字,因为这是 partition-number 所期望的。

(defn digits-to-decimal [digits]
  (let [powers-of-ten (iterate (partial * 10) 1)
        digits (reverse digits)]
    (reduce +
        (map-indexed (fn [position digit]
                         (* digit (nth powers-of-ten position)))
                     digits))))

但是,如果我们重组 partition-number 以同时使用带有累加器的辅助函数,我们也可以使用 recur,这样可以避免在 clojure 中的递归调用上炸毁堆栈:

(defn partition-number
  [number size]
  (letfn [(partnumb-helper [digits size result]
               (cond (> size (count digits)) (conj result digits)
                     true (recur (rest-part digits size) 
                                 size
                                 (conj result (new-part digits size)))))]
     (partnumb-helper (decimal-digits number) size '[])))

请注意,这样我们也可以一直在数字和数字之间进行转换。

【讨论】:

  • 回答得很好
【解决方案2】:

我不确定您不想使用哪些其他核心功能,但我想出了以下解决方案:

(defn chars->int [chars]
  (Integer/parseInt (apply str chars)))

(defn int-partition [n count]
  (loop [acc [] xs (str n)]
    (if (seq xs)
      (recur (conj acc (chars->int (take count xs))) (drop count xs))
      acc)))

(int-partition 76123451001214 3)
;; yields
[761 234 510 12 14]

(reduce + (int-partition 76123451001214 3))
;; yields
1531

请注意,此版本不是惰性的,可能不适合大型输入。您可能想使用lazy-seq 对其进行修改。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-13
    • 1970-01-01
    • 2015-11-02
    • 1970-01-01
    • 1970-01-01
    • 2017-01-22
    相关资源
    最近更新 更多