我有一个文本文件中的数据,格式如下:
1 2
3 4
5 6
7 8
9 0
我想为每一列创建一个列表,因此:
((1 3 5 7 9) (2 4 6 8 0))
我实际上已经想出了一个可行的解决方案:
(defn make-lists []
(let [lines (clojure.string/split-lines (slurp "input"))]
(let [l1 (map #(parse-long (first (clojure.string/split % #" "))) lines)
l2 (map #(parse-long (nth (clojure.string/split % #" ") 3)) lines)]
(list l1 l2))))
但是,我认为这个解决方案存在一些问题。我实际上并不清楚map
其内部是如何工作的,但我担心这会导致对输入进行两次迭代,而理论上一次迭代就足够了。
不幸的是,输入数据中每行的两个值之间有多个空格。并且clojure.string/split
每个空格都会产生一个结果。因此,我nth 3
在代码中随意查找 l2,我想摆脱它。
我很感激任何关于如何改进此代码的想法。
一般来说,你不应该太在意对某件事进行多次迭代。这很常见,没关系,只有当你已经证明它是一个瓶颈时才考虑优化它。
关于空格 - 的参数
str/split
是正则表达式。只需使用#" +"
而不是 即可#" "
。考虑到这一点,我会这样做:
为了澄清一下第一个答案,我喜欢展开开始的步骤。在 tupelo 库中,有宏
let-spy
,let-spy-pretty
可以打印每个步骤的结果:结果:
我喜欢
(str/trim ...)
在流程早期使用,在解析/分割行、解析为 int/float 等之前删除所有前导/尾随空格(和换行符!)。我还喜欢使用mapv
而不是因为map
它是非惰性的并且总是产生一个矢量结果(更容易剪切/粘贴到单元测试中而无需引用)。一切正常后,只需替换
let-spy-pretty
为let
并按正常格式进行格式化即可。我通常不费心用->
或其他东西来压缩各个步骤,因此对于后续的代码读者来说,转换是如何一步步发生的会更加明显(特别是如果他们需要修改某些内容或添加调试打印语句)。