在clojure中的向量的变构列表中映射每个元素

时间:2014-02-14 10:04:11

标签: data-structures vector clojure mapping destructuring

我是一名大学教练,试图让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))

我想创建另一个将映射成绩列表的函数,并根据其位置,以矢量中每个元素的特定权重将percentify函数应用于每个学生的成绩。这就是我现在正在使用的东西,但我不能在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))))

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

3 个答案:

答案 0 :(得分:3)

首先,您似乎将每个学生的成绩向量分配到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)

答案 1 :(得分:1)

数据自然是从学生编号(字符串)到成绩矢量的地图:

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

答案 2 :(得分:0)

我无法改进MichałMarczyk的答案,但我会建议一个可能的选择。目前对你的需求来说可能有点过分了。你想要做的主要是矩阵乘法:你将矩阵乘以一个向量,以产生一个向量。如果需要,可以使用core.matrix库直接完成。假设各个项目等级用于测验。首先,您需要在加载core.matrix库的情况下运行Clojure,例如使用Leiningen。然后在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很容易在两个表示之间进行转换功能,emap。现在你可以得到每个学生的最终成绩:

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

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