我已经开始了使用 clojure 的旅程,并被我为自己设置的第一个问题所困扰。我有一个文本文件,基本上是一个 nXm 行/列的表格。第一行是列名,第一列是行名。我希望能够使用 clojure 解析此表,然后查询 table[row][column] 并获取该值。
a b c
1 7 8 9
2 s q r
3 2 7 1
那么,我将如何在 clojure 中使用上表?我不确定从哪里开始。有人可以让我朝着正确的方向前进吗?
我已经开始了使用 clojure 的旅程,并被我为自己设置的第一个问题所困扰。我有一个文本文件,基本上是一个 nXm 行/列的表格。第一行是列名,第一列是行名。我希望能够使用 clojure 解析此表,然后查询 table[row][column] 并获取该值。
a b c
1 7 8 9
2 s q r
3 2 7 1
那么,我将如何在 clojure 中使用上表?我不确定从哪里开始。有人可以让我朝着正确的方向前进吗?
@Hendekagon 的回答是完成工作的好方法,但我们可以看看从头开始的实现。虽然可能不是最好的解决方案,但希望示例设计可以帮助您开始。
如果您想查询您的结构,在 Clojure 中,您将考虑映射。让我们把看起来像这样的东西作为我们的目标:
{"1" {"a" "7", "b" "8", "c" "9"},
"2" {"a" "s", "b" "q", "c" "r"},
"3" {"a" "2", "b" "7", "c" "1"}}
在这里,行名是将列名映射到表元素的键。使用这种结构,我们可以轻松地使用get-in
.
(get-in table ["2" "b"]) ; => "q"
好的。我们该怎么做呢?
让我们假设我们已经在我们的文件中读取了一秒钟并将它作为一个字符串。然后,我们需要将其转换为我们的地图地图。我们的函数看起来像这样:
(defn parse-table
[raw-table-data]
...)
第一步是提取所有重要的数据位——行名、列名和表格元素。然而,在我们抓住它们之前,我们需要将raw-table-data
字符串解析成一个更容易遍历的结构。我们将在换行符上拆分字符串,然后使用辅助函数在空白处标记行tokens
。
(use '[clojure.string :only [split split-lines trim]])
(defn tokens
[s]
(-> s trim (split #"\s+")))
(defn parse-table
[raw-table-data]
(let [table-data (map tokens (split-lines raw-table-data))]
...
)
table-data
看起来像这样:
[["a", "b", "c"],
["1", "7", "8", "9"],
["2", "s", "q", "r"],
["3", "2", "7", "1"]]
这使得获得好东西变得容易:
(defn parse-table
[raw-table-data]
(let [table-data (map tokens (split-lines raw-table-data))
column-names (first table-data)
row-names (map first (next table-data))
contents (map next (next table-data))]
...
)
整理出数据后,我们只需要将它们拼接在一起。一个简单的方法是构建我们所有的行到列到元素的单独映射,然后将它们组合起来。我会提到这不是最有效的方法,但它很干净。
创建一个pairs
简单地将两个集合的元素并排放置的辅助函数,我们可以使用推导式获得一系列映射for
。
(defn pairs
[coll1 coll2]
(map vector coll1 coll2))
(for [[row-name row-contents] (pairs row-names contents)
[column-name element] (pairs column-names row-contents)]
{row-name {column-name element}})
这给出了一系列映射到映射。我们只需要把它合并成一张大图,功能就完成了。
(defn parse-table
[raw-table-data]
(let [table-data (map tokens (split-lines raw-table-data))
column-names (first table-data)
row-names (map first (next table-data))
contents (map next (next table-data))]
(apply merge-with merge
(for [[row-name row-contents] (pairs row-names contents)
[column-name element] (pairs column-names row-contents)]
{row-name {column-name element}}))))
现在,我们可以抓取一个表格文件并对其进行解析。
(def table
(->
"file"
slurp
parse-table))
这使我们达到了我们的目标。
(println (get-in table ["2" "b"])) ; => "q"
使用https://github.com/clojure/data.csv,您的文件将成为一系列向量,每个向量都是一行,然后您可以使用如下函数解析行:
(defn parse-row [[a b c]]
[(Integer/parseInt a) (Double/parseDouble b) (str c)])
(注意参数列表中的解构,这样更容易阅读列名)
然后(map parse-row rows)
获取解析表
但是,另一种方法是使用Incanter,它将您的 csv 文件转换为更易于查询的矩阵。