Clojure Inconsistent在执行函数期间导致clojure

时间:2011-10-21 08:24:11

标签: clojure

Clojure问题

我在clojure中编写了以下函数: 在第一个循环中,它迭代一个地图列表并创建一个地图。 然后第二个循环迭代一个列表,匹配先前创建的地图中的数据 和一个矢量,并返回一个新的地图。但是,使用相同数据生成不同的运行 差异结果。见下文。

(defn resolve-case-per-period
  "Constructs a map by matching data existing in input parameter vectors"
  [dd case-details periods]
  (let [cases ((fn [in] 
                 (loop [case-details in, result-map {}] 
                   (if (= (count case-details) 0) 
                     result-map 
                     (recur (rest case-details) 
                            (assoc result-map
                              (:priority (first case-details)) 
                              (:caseid (first case-details)))))))
               case-details)
         periods periods]
    (info "Mapping cases to periods step 1 completed " cases)
    (loop [periods periods, result-map {}]
      (if (= (count periods) 0)
        result-map
        (recur (rest periods) 
               (conj result-map
                     { (str (:period (first periods)))
                       (get cases (:priority (first periods)))}))))))

返回的输出是如下地图:

{31-10-10 20 10020101030122036M, 31-10-10 10 10020101030122036M, 31-10-10 21 10020101030122036M, 30-10-10 21 10020101030200157M, 31-10-10 00 10020101030122036M, 31-10-10 11 10020101030122036M, 31-10-10 22 10020101031112152M, 30-10-10 22 10020101030122036M, 31-10-10 01 10020101030122036M, 31-10-10 12 10020101030122036M, 30-10-10 23 10020101030122036M, 31-10-10 02 10020101030122036M, 31-10-10 13 10020101030122036M, 31-10-10 03 10020101030122036M, 31-10-10 14 10020101030122036M, 31-10-10 04 10020101030122036M, 31-10-10 15 10020101030122036M, 31-10-10 05 10020101030122036M, 31-10-10 16 10020101030122036M, 31-10-10 06 10020101030122036M, 31-10-10 17 10020101030122036M, 31-10-10 07 10020101030122036M, 31-10-10 18 10020101030122036M, 31-10-10 08 10020101030122036M, 31-10-10 19 10020101030122036M, 31-10-10 09 10020101030122036M}

执行具有相同参数的函数有时会产生

{31-10-10 20 nil, 31-10-10 10 nil, 31-10-10 21 nil, 30-10-10 21 nil, 31-10-10 00 nil, 31-10-10 11 nil, 31-10-10 22 nil, 30-10-10 22 nil, 31-10-10 01 nil, 31-10-10 12 nil, 30-10-10 23 nil, 31-10-10 02 nil, 31-10-10 13 nil, 31-10-10 03 nil, 31-10-10 14 nil, 31-10-10 04 nil, 31-10-10 15 nil, 31-10-10 05 nil, 31-10-10 16 nil, 31-10-10 06 nil, 31-10-10 17 nil, 31-10-10 07 nil, 31-10-10 18 nil, 31-10-10 08 nil, 31-10-10 19 nil, 31-10-10 09 nil}

2 个答案:

答案 0 :(得分:7)

此函数中的所有内容都是确定性且纯粹的(info调用除外,这应该无关紧要),因此每次都应该返回相同的内容。您没有提供任何样本输入,因此我无法反驳这一假设。

代码难以阅读,没有上下文我真的不明白你在做什么。但是我在几个重构过程中经历了你的代码,试图让它更清楚地发生了什么。希望这可以帮助正在阅读的其他人,甚至可以让你在问题所在的地方更清楚。

第一

删除所有疯狂格式和无意义的变量复制,并使用seq代替测试count = 0

(defn resolve-case-per-period
  "Constructs a map by matching data existing in input parameter vectors"
  [dd case-details periods]
  (let [cases (loop [case-details case-details, result-map {}] 
                (if (seq case-details)
                  (recur (rest case-details)
                         (assoc result-map 
                           (:priority (first case-details))
                           (:caseid (first case-details)))) 
                  result-map))]
    (info "Mapping cases to periods step 1 completed " cases)
    (loop [periods periods, result-map {}]
      (if (seq periods)
        (recur (rest periods) 
               (assoc result-map
                 (str (:period (first periods)))
                 (get cases (:priority (first periods)))))
        (do (info "Mapping cases to periods step 2 completed " result-map)
            result-map)))))

第二

解构和if-let而不是原始关键字查找,ifs和seqs:

(defn resolve-case-per-period
  "Constructs a map by matching data existing in input parameter vectors"
  [dd case-details periods]
  (let [cases (loop [case-details case-details, result-map {}] 
                (if-let [[{:keys [priority case-id]} & more] (seq case-details)]
                  (recur more
                         (assoc result-map priority caseid)) 
                  result-map))]
    (info "Mapping cases to periods step 1 completed " cases)
    (loop [periods periods, result-map {}]
      (if-let [[{:keys [period priority]} & more] (seq periods)]
        (recur more
               (assoc result-map
                 (str period)
                 (get cases priority)))
        (do (info "Mapping cases to periods step 2 completed " result-map)
            result-map)))))

第三

此时我们最终清楚地知道我们只是迭代一个序列并构建一个结果值,因此我们可以删除所有的第一个/其余的废话并只使用reduce来遍历序列对我们来说:

(defn resolve-case-per-period
  "Constructs a map by matching data existing in input parameter vectors"
  [dd case-details periods]
  (let [cases (reduce (fn [result-map {:keys [priority case-id]}]
                        (assoc result-map priority caseid))
                      {}, case-details)]
    (info "Mapping cases to periods step 1 completed " cases)
    (reduce (fn [result-map {:keys [period priority]}]
              (assoc result-map
                (str period)
                (get cases priority)))
            {}, periods)))

答案 1 :(得分:1)

如果我们不知道您的数据(函数输入)是什么样子,那么很难回答您的问题,但有几点:

  1. dd从未在您的函数中使用,因此您可以摆脱它。
  2. 在Clojure中具有相当短的函数是惯用的,所以我建议将你在let中所做的部分分解为另一个函数。这也将使repl的测试和实验更容易。
  3. 在let中重新映射periodsperiods没有任何效果,所以摆脱它。
  4. 你影响了许多本地人(case-details中的letperiods中的loop),这可能令人困惑,我会建议不要这样做。
  5. 结果地图中的键是字符串,我推测形式为“31-10-10 20”。这更易于用引号辨别。
  6. 老实说,我不知道这个Clojure函数如何为你提供不同的输出,你绝对确定输入是相同的吗?作为第一种情况的观察,你得到值的BigDecimals,所以我的猜测是,在第二种情况下,无法处理BigDecimals的东西与数据接触。但我不知道你在提供的功能中会发生这种情况。