Clojure 函数执行期间 clojure 的结果不一致

发布于 2024-12-11 13:13:46 字数 2466 浏览 0 评论 0原文

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}

Clojure question

I have written the following function in clojure:
In the first loop it iterates a list of maps and creates a map.
Then the second loop iterates a list, matches data from the map previously created
and a vector and returns a new map. However, different runs with the same data produce
diffenet results. See below.

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

The returned output is of a map like the following:

{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}

Executing the function with the same parameters sometimes yields

{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}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

野心澎湃 2024-12-18 13:13:46

该函数中的所有内容都是确定性且纯粹的(除了 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)))

Everything in this function is deterministic and pure (except the info calls, which shouldn't matter), so it should return the same thing every time. You don't provide any sample inputs, so I can't disprove this assumption.

The code is hard to read, and with no context I don't really understand what you're doing. But I went through your code in several refactoring passes to try to make it clearer what's going on. Hopefully this will help someone else who is reading, or even make it clearer to you where your problem is.

First

Remove all the crazy formatting and pointless variable-copying, and use seq instead of testing 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)))))

Second

Destructuring and if-let instead of primitive keyword lookups, ifs, and 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)))))

Third

At this point it's finally clear that we're just iterating over a sequence and building up a result value as we go, so we can drop all the first/rest nonsense and just use reduce to traverse the sequence for us:

(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)))
内心荒芜 2024-12-18 13:13:46

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

  1. dd 从未在您的函数中使用,因此您可以摆脱那。
  2. Clojure 的惯用做法是拥有相当短的函数,因此我建议将在 let 中执行的部分分解到另一个函数中。这也将使 repl 的测试和实验变得更加容易。
  3. 在 let 中将 periods 重新映射到 periods 没有任何效果,因此请摆脱它。
  4. 您隐藏了很多本地变量(let 中的 case-detailsloop 中的 periods),这可以令人困惑,我建议不要这样做。
  5. 结果映射中的键是字符串,我认为形式为“31-10-10 20”。通过引号更容易辨别这一点。

老实说,我不明白这个 Clojure 函数如何负责为您提供不同的输出,您绝对确定输入是相同的吗?作为第一种情况下的观察,您会得到 BigDecimals 的值,所以我的猜测是,在第二种情况下,无法处理 BigDecimals 的东西与数据发生了联系。但我不明白在你提供的函数中怎么会发生这种情况。

It will be difficult to answer your question if we don't know what your data (function input) looks like, but a few points:

  1. dd is never used in your function, so you can get rid of that.
  2. It is idiomatic in Clojure to have fairly short functions, so I would suggest factoring the part that you do in let out into another function. That will also make testing and experimentation at the repl easier.
  3. Remapping periods to periods in the let has no effect, so get rid of that.
  4. You shadow a lot of locals (case-details in the let and periods in the loop) , this can be confusing and I would advise against it.
  5. The keys in your resulting map are strings, I presume of the form "31-10-10 20". This is easier to discern with the quotes.

I honestly don't see how this Clojure function could be responsible for giving you different outputs, are you absolutely sure that the input is identical? As an observation in the first case you get BigDecimals for the values, so my guess is that in the second case something that couldn't handle BigDecimals was in contact with the data. But I don't see how this could have happend in the function you provide.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文