【问题标题】:mapping over each element in a destructured list of vectors in clojure映射clojure中向量解构列表中的每个元素
【发布时间】:2014-02-14 10:04:10
【问题描述】:

我是一名大学讲师,我想在享受 Clojure 乐趣的同时计算我的成绩。我将我所有的学生编号及其对应的成绩列成一个列表,如下所示:

(def grades-1 (let [s18129  [100    70  85  71  85]  
                    s18121  [80 75  85  81  85]
                    r18131  [75 60  80  56  75] ...])
                    ;; r before the number is shorthand for repeater 
                    ;; and not important to this question

我希望调整成绩,以便这些向量中的一、二、三、四和五年级的权重分别为 10%、20%、15%、25% 和 30%。为了帮助我完成这项任务,我创建了一个辅助函数:

(defn percentify
  "adjust raw score to weighted percentile"
  [raw percentile]
  (* (/ raw 100) percentile))

我想创建另一个函数,该函数将映射到成绩列表,并根据向量中每个元素的位置,以特定权重将百分比函数应用于每个学生的成绩。这就是我现在正在使用的东西,但我无法让它在 repl 中工作。我认为这与我如何构建班级数据有关,或者我对 println 的使用感到困惑。

(defn finalize [grades-list]
(let [[[student] [a b c d e]] grades-list]
  (println
   (percentify a 10.0)
   (percentify b 20.0)
   (percentify c 15.0)
   (percentify d 25.0)
   (percentify e 30.0))))

然后我想调用这个函数来以可读的形式返回带有学生人数的最终成绩。有人可以帮助我走上正轨吗?

【问题讨论】:

    标签: data-structures vector clojure mapping destructuring


    【解决方案1】:

    首先,您似乎将每个学生的成绩向量以let 形式分配给单独的本地。要跨所有等级向量映射函数,您需要首先将它们放在一个数据结构中:

    (def grades-1 [[100    70  85  71  85]  
                   [80 75  85  81  85]
                   [75 60  80  56  75]])
    

    现在,将权重向量应用于等级向量的函数将很有用(percentify 这是您的原始函数):

    ;; taking weights first for convenient use with partial
    (defn percentify-vector [pvec rvec]
      (mapv percentify rvec pvec))
    

    最后,我们可以将以上内容映射到等级向量集合中:

    (mapv (partial percentify-vector [10.0 20.0 15.0 25.0 30.0]) grades-1)
    

    【讨论】:

    • 这非常成功。谢谢你。但是,我正在尝试将每个增强向量减少为一个最终成绩序列。我已将上述内容压缩到一个函数中并尝试调用它,但似乎无法使其正常工作。你能给我最后一点帮助吗?
    • 当然。如果我理解正确,您想总结每个单独的成绩向量并获得所有结果总和的集合。让gs 指代应用了权重的等级向量的集合,(mapv #(apply + %) gs) 将产生一个总和向量。整个事情可以浓缩为(->> grades-1 (map (partial percentify-vector [10.0 20.0 15.0 25.0 30.0])) (mapv #(apply + %)))
    • 天哪,你擅长 Clojure。我希望有一天我可以像什么都没有一样写出这样的代码。谢谢。
    【解决方案2】:

    数据自然是从学生编号(字符串)到成绩向量的映射:

    (def grades-1 {"s18129"  [100 70  85  71  85]  
                   "s18121"  [80 75  85  81  85]
                   "r18131"  [75 60  80  56  75]})
    

    ...以及要应用于上述内容的权重向量:

    (def grade-weights [10 20 15 25 30])
    

    ...制作一张从学生编号到最终成绩的地图。我们如何考虑这一点?

    根据给定的weights向量计算平均值的函数是

    (fn [grades] (/ (reduce + 0.0 (map * weights grades)) (reduce + weights)))
    

    ...在哪里

    • 0.0 的初始值强制使用浮点数 算术和
    • 权重之和不必为 1.0 或 100 或任何特定数字。 包括缩放。

    以下将a-map的每个值转换为该值的函数f

    (fn [f a-map] (into {} (map (fn [[k v]] [k (f v)]) a-map)))
    

    因此,从成绩向量图和权重向量中传递最终成绩图的函数是......

    (defn av-grades [gt weights]
      (let [weight-av (fn [grades]
                        (/ (reduce + 0.0 (map * weights grades)) (reduce + weights)))
            convert-map (fn [f a-map] 
                          (into {} (map (fn [[k v]] [k (f v)]) a-map)))]
        (convert-map weight-av gt)))
    

    而且,果然……

    =>(av-grades grades-1 grade-weights)
    {"r18131" 68.0, "s18121" 81.5, "s18129" 80.0}
    

    【讨论】:

    • 非常感谢您为我的问题付出这么多考虑。我最终根据 Michael Marczyk 的回答采用了一种简化的方法,但这真的很棒,而且最初是我所要求的。
    【解决方案3】:

    我无法改进 Michał Marczyk 的回答,但我会建议一个可能的替代方案。目前,这可能对您的需求有点过分了。您想要做的基本上是矩阵乘法:您将矩阵乘以向量以产生向量。如果需要,这可以直接使用 core.matrix 库来完成。假设单个项目等级用于测验。首先,您需要在加载 core.matrix 库的情况下运行 Clojure,例如使用莱宁根。然后在 REPL 或文件中,你可以这样做:

    (use 'clojure.core.matrix)
    
    ;; Define quiz grade matrix: Each row is a student, each column is a quiz.
    (def quiz-grades [[100 70  85  71  85]
                      [ 80 75  85  81  85]
                      [ 75 60  80  56  75]])
    
    ;; Define weights representing contributions of each quiz to the final grade.
    (def quiz-weights [0.10 0.20 0.15 0.25 0.30])
    

    在这里,我将测验的权重定义为表示 1 的分数的小数,而不是整数,但使用 mapmapv 或类似的 core.matrix 函数 @987654324 可以很容易地在两种表示之间进行转换@。现在您可以像这样获得每个学生的最终成绩:

    user=> (mmul quiz-grades quiz-weights)
    [80.0 81.5 68.0]
    

    (注意:core.matrix 有不同的向量和矩阵的底层实现。在我的示例中,我使用了默认的持久向量实现,其中向量和矩阵等效于常规 Clojure 向量和向量的向量。按顺序为了在不同的实现之间轻松切换,最好将上面的 Clojure 向量嵌入到函数 matrix 的调用中,但对于持久向量实现来说,这不是必需的。)

    【讨论】:

    • 谢谢,火星。这听起来很棒,我将完全研究矩阵库。我对 Clojure 和编程都很陌生,所以这很棒。不过有一件事,最终的增强向量加起来应该是 100% 的数字。这是他们本学期的期末成绩,每个成绩分别代表家庭作业、演讲、论文、期中和期末成绩。因为我在学期开始时缺乏远见,所以它们的形式是这样的。
    猜你喜欢
    • 1970-01-01
    • 2022-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多