【问题标题】:In clojure, map and stringify: make it simpler在clojure中,map和stringify:让它更简单
【发布时间】:2012-03-06 06:40:21
【问题描述】:

映射和字符串化

我有这段clojure代码:

(def up (memfn toUpperCase))   
(str "\n" (apply str (interpose "\n" (map up '("one" "two")))) "\n"); "\nONE\nTWO\n"

代码完全按照它应该做的事情:获取一个字符串列表,每个字符串都大写,然后用\n(前后加上)包围每个字符串。

但必须有一种方法可以以更优雅的方式编写此内容。请帮忙。

【问题讨论】:

    标签: clojure lisp


    【解决方案1】:

    你可以合并地图并插入:

    (apply str "\n" (map #(str (up %) "\n") '("one" "two")))
    

    还有,不一定更优雅,有点 timtowdi 的精神:

    (clojure.pprint/cl-format false "~%~{~:@(~A~)~%~}" '("one" "two"))
    

    有关 cl 格式字符串的教程,请参阅 practical common lisp

    【讨论】:

    • 不错。但我想应该有一种更优雅的方式来插入页眉和页脚。你怎么看?
    • 问题是你没有介入,可以这么说,你是在“介入”。
    • 是的! “在过去,至少会有一些后续行动展示如何以正确的“格式字符串与线路噪声无法区分”的方式执行此操作。”Tim Bradshaw, comp.lang.lisp
    【解决方案2】:

    我实际上非常喜欢 with-out-str 处理这类事情的方法:

    (with-out-str
      (println)
      (doseq [s ["one" "two"]]
        (println (.toUpperCase ^String s))))
    

    它似乎比原来的方法慢 2-3 倍,并且添加了类型提示的 Martin 的“组合映射和插入”变体(并且比 cl-format 快约 30 倍,但显然在凉爽因素上胜出 :-) )。 (有关提示和反思的说明,请参阅此答案的末尾。)

    另一个版本只是为了保持 timtowtdi 的精神:为了获得极致的速度(比原来的版本快约 2 倍),如果你有理由关心它,你可以使用

    (loop [sb (doto (StringBuilder.)
                (.append \newline))
           strs ["one" "two"]]
      (if-let [s (first strs)]
        (do (.append sb (.toUpperCase ^String s))
            (.append sb \newline)
            (recur sb (next strs)))
        (.toString sb)))))
    

    与主要问题有点切线,我在消除所有反射警告后对所有方法进行计时;特别是我用过

    (def up #(.toUpperCase ^String %))
    

    (顺便说一句,#(.foo %) 似乎比memfn 更频繁地使用更多,即使没有指定类型提示。)

    【讨论】:

    • #(.foo %) 的问题在于您必须手动担心 args 的数量,而 memfn 则不是这种情况。
    • 这不是真的,你需要说类似(memfn foo a b c)(使用任意符号代替a b c,只要它们成对不同)来为采用三个参数的方法创建一个包装器.详情请见(source memfn)
    • 感谢您的澄清。
    【解决方案3】:

    我想出了:

     (defn interpose-envelop-and-stringify [coll sep]
       (str sep
            (join sep coll)
            sep))
     (interpose-envelop-and-stringify (map up ["one" "two"]) "\n")
    

    我正在使用来自clojure.stringjoin

    【讨论】:

      【解决方案4】:

      你做对了。 一个建议-使用函数 arg 或 let form 来定义分隔符,在这种情况下,当您需要更改分隔符时,您只能在一个地方更改它(而不是像您的情况那样 3 个)

      (defn stringify [sq sep]
        (reduce str sep (map #(str (.toUpperCase %) sep) sq)))
      
      (stringify ["one" "two"] "\n") => "\nONE\nTWO\n"
      

      【讨论】:

      • 请注意:strapply(而不是reduce)一起使用效果最好——当使用多个参数调用时,它在内部使用StringBuilder,所以最好让它处理它自己的循环。 (这是我前段时间在another answer 中讨论过的更通用模式的一个实例。)
      • 谢谢!我执行了一些测试 apply str vs reduce str,并且对于 100k 字符串应用得更好。在其他方面,reduce + 比 apply + 略好。 (我阅读了您对相关问题的评论,有道理)
      【解决方案5】:

      画眉操作可以使事情变得相当干净,但是仍然需要一个封闭的应用程序来将列表加入一个大字符串。

      (defn stringify [s] (apply str "\n" (map #(-> % .toUpperCase (str "\n")) s)))
      
      (stringify '("one" "two")) ; yields "\nONE\nTWO\n"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-10-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-20
        • 2020-05-02
        • 1970-01-01
        相关资源
        最近更新 更多