【问题标题】:Functional solution for converting a string to a vector of integers in ClojureClojure 中将字符串转换为整数向量的功能解决方案
【发布时间】:2017-11-22 15:57:04
【问题描述】:

我已经在 Clojure 中成功编写了一个函数,该函数将空格分隔的整数字符串转换为整数向量,但由于我对函数式语言(非常)陌生,我担心我仍然在程序上思考太多。

该函数使用split 对字符串进行标记,然后遍历返回的向量,分别将标记转换为整数,然后将它们附加到新向量。我使用 read-string 是因为输入是自己提供的,我并不真正关心安全性。

    (defn parser [myStr]
        ;;counter
        (def i 0)
        ;;tokenizes string and returns vector of tokens
        (def buffer (clojure.string/split myStr #"\s"))
        ;;reads vector of strings as integers then appends them to a new vector x
        (def x (vector-of :int))
        (while ( < i (count buffer))
            (def x (conj x (read-string (nth buffer i)))) 
            (def i (inc i)))
        (println x))

我的代码有效,但我担心通过更改状态和迭代缓冲区向量,我有点作弊并坚持我的程序根源。

有没有更优雅或更实用的方法来解决这个问题?

【问题讨论】:

  • 我对 clojure 了解不多,但我想(map read-string (clojure.string/split myStr #"\s")) 可能有用吗?
  • split 对于这种情况不是很好,因为它可能会在 seq 中留下空字符串,例如:“10 20 30”(带空格)。我会使用(map read-string (re-seq #"\d+" "10 20 30"))
  • @xs0 我担心使用 map,因为我不确定将其转换为矢量是否会增加额外的中间复杂性。
  • @leetwinski 我不知道这种拆分行为,谢谢。

标签: string eclipse vector clojure


【解决方案1】:

这里有一些非常值得注意的事情:

  • 切勿defn 中使用def,除非您有非常好的理由。这里的用例是不合理的。只需改用let

    (defn parser [myStr]
      (let [i 0
            buffer (clojure.string/split myStr #"\s")
            x (vector-of :int)]
         ...)
    

    要查看有什么区别,请运行您的函数,然后检查 i 的内容。 def 创建在函数退出后仍然存在的全局变量,这会泄漏函数的状态并污染命名空间。

  • 您正在使用read-string 进行解析。不要那样做。只需使用 Java 的Long/parseLongread-string 具有 eval 行为,这永远不会被滥用。 您还可以使用clojure.edn/read-string,它可以读取 Clojure 结构和文字,但不执行代码。

  • 当你真的可以使用loop 或许多其他功能方法时,你正在使用while 来执行副作用。 @xs0 基本上是正确的。我会把你的函数写成:

    (defn parser [myStr]
      ; The v in mapv means it returns a vector
      ; Just map returns a lazy seq
      (mapv #(Long/parseLong %) (clojure.string/split myStr #"\s"))
    

    不幸的是,Long/parseLong 需要包装在一个函数中,因为 Java 互操作方法不能像普通 Clojure 函数那样使用。

    Long/parseLong 只有在您可以保证split 返回的每个标记都是可解析的情况下才能安全使用。当然,如果没有这样的保证,你需要做一些错误处理,或者在尝试解析之前清理输入。

【讨论】:

  • 感谢您的快速回答!我什至不知道mapv 的存在,现在我正在研究它,这是一个更好的解决方案。
  • @Aut0 即使没有map,也应该很少使用while。如果您需要循环,但不知道循环何时停止准确(您不是循环遍历列表,而是循环直到满足更复杂的条件),请使用looploop 是解决每个循环问题的锤子。它并不总是最好或最简洁的解决方案,但它是语言中最通用的功能循环结构。
  • 正如我上面评论的那样,用\s 分割字符串是不好的,首先因为这会在项目之间有多个空格时失败。如果输入有前导空格,即使\s+ 也会出错。我的建议是使用re-seq,或者从(clojure.string/split ...)的结果中过滤掉空字符串
  • 我注意到只有在您可以保证输入是可解析的情况下才有效。老实说,我很少使用正则表达式,所以我不熟悉 Clojure 的正则表达式工具,甚至不熟悉一般的正则表达式。恐怕我会捏造这个例子。如果你经常使用它们,你可能会给出一个更好的例子来说明它们的使用。
  • 好答案。 read-string 也可以替换为clojure.edn/read-string,这是安全的(并且没有互操作语法)。另外,我认为在 Clojure 中 Long/parseLong 是首选,因为 Clojure 使用 Long 表示整数(而不是 Integer)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-24
  • 1970-01-01
  • 2016-05-03
  • 2011-10-31
  • 2022-01-08
相关资源
最近更新 更多