加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Scala和Clojure中的简单字符串模板替换

发布时间:2020-12-16 19:01:18 所属栏目:安全 来源:网络整理
导读:以下是在 Scala和Clojure中编写的函数,以便简单地替换字符串中的模板.每个函数的输入是包含{key}形式的模板和从Symbol / Keyword到替换值的映射的String. 例如: 斯卡拉: replaceTemplates("This is a {test}",Map('test - "game")) Clojure的: (replace-t
以下是在 Scala和Clojure中编写的函数,以便简单地替换字符串中的模板.每个函数的输入是包含{key}形式的模板和从Symbol / Keyword到替换值的映射的String.

例如:

斯卡拉:

replaceTemplates("This is a {test}",Map('test -> "game"))

Clojure的:

(replace-templates "This is a {test}" {:test "game"})

将返回“这是一个游戏”.

输入图使用符号/关键字,所以我不必处理字符串中的模板包含大括号的角落的情况.

不幸的是,算法效率不高.

这是Scala代码:

def replaceTemplates(text: String,templates: Map[Symbol,String]): String = {
  val builder = new StringBuilder(text)

  @tailrec
  def loop(key: String,keyLength: Int,value: String): StringBuilder = {
    val index = builder.lastIndexOf(key)
    if (index < 0) builder
    else {
      builder.replace(index,index + keyLength,value)
      loop(key,keyLength,value)
    }
  }

  templates.foreach {
    case (key,value) =>
      val template = "{" + key.name + "}"
      loop(template,template.length,value)
  }

  builder.toString
}

这里是Clojure代码:

(defn replace-templates
  "Return a String with each occurrence of a substring of the form {key}
   replaced with the corresponding value from a map parameter.
   @param str the String in which to do the replacements
   @param m a map of keyword->value"
  [text m]
  (let [sb (StringBuilder. text)]
    (letfn [(replace-all [key key-length value]
              (let [index (.lastIndexOf sb key)]
                (if (< index 0)
                  sb
                  (do
                    (.replace sb index (+ index key-length) value)
                    (recur key key-length value)))))]
      (doseq [[key value] m]
        (let [template (str "{" (name key) "}")]
          (replace-all template (count template) value))))
    (.toString sb)))

这是一个测试用例(Scala代码):

replaceTemplates("""
Lorem ipsum dolor sit amet,consectetur adipiscing elit. Pellentesque
elit nisi,egestas et tincidunt eget,{foo} mattis non erat. Aenean ut
elit in odio vehicula facilisis. Vestibulum quis elit vel nulla
interdum facilisis ut eu sapien. Nullam cursus fermentum
sollicitudin. Donec non congue augue. {bar} Vestibulum et magna quis
arcu ultricies consectetur auctor vitae urna. Fusce hendrerit
facilisis volutpat. Ut lectus augue,mattis {baz} venenatis {foo}
lobortis sed,varius eu massa. Ut sit amet nunc quis velit hendrerit
bibendum in eget nibh. Cras blandit nibh in odio suscipit eget aliquet
tortor placerat. In tempor ullamcorper mi. Quisque egestas,metus eu
venenatis pulvinar,sem urna blandit mi,in lobortis augue sem ut
dolor. Sed in {bar} neque sapien,vitae lacinia arcu. Phasellus mollis
blandit commodo.
""",Map('foo -> "HELLO",'bar -> "GOODBYE",'baz -> "FORTY-TWO"))

并输出:

Lorem ipsum dolor sit amet,HELLO mattis non erat. Aenean ut
elit in odio vehicula facilisis. Vestibulum quis elit vel nulla
interdum facilisis ut eu sapien. Nullam cursus fermentum
sollicitudin. Donec non congue augue. GOODBYE Vestibulum et magna quis
arcu ultricies consectetur auctor vitae urna. Fusce hendrerit
facilisis volutpat. Ut lectus augue,mattis FORTY-TWO venenatis HELLO
lobortis sed,in lobortis augue sem ut
dolor. Sed in GOODBYE neque sapien,vitae lacinia arcu. Phasellus mollis
blandit commodo.

该算法横跨输入图,对于每对,在输入String中替换,暂时保存在StringBuilder中.对于每个键/值对,我们搜索键的最后一个出现(括在大括号中),并将其替换为值,直到不再出现.

如果我们在StringBuilder中使用.lastIndexOf与.indexOf,它是否会产生任何性能差异?

如何改进算法?编写Scala和/或Clojure代码有更为惯用的方法吗?

更新:见我的follow-up.

更新2:这是一个更好的Scala实现; O(n)的长度为String.请注意,根据几个人的推荐,我将Map修改为[String,String]而不是[Symbol,String]. (感谢mikera,kotarak):

/**
 * Replace templates of the form {key} in the input String with values from the Map.
 *
 * @param text the String in which to do the replacements
 * @param templates a Map from Symbol (key) to value
 * @returns the String with all occurrences of the templates replaced by their values
 */
def replaceTemplates(text: String,templates: Map[String,String]): String = {
  val builder = new StringBuilder
  val textLength = text.length

  @tailrec
  def loop(text: String): String = {
    if (text.length == 0) builder.toString
    else if (text.startsWith("{")) {
      val brace = text.indexOf("}")
      if (brace < 0) builder.append(text).toString
      else {
        val replacement = templates.get(text.substring(1,brace)).orNull
          if (replacement != null) {
            builder.append(replacement)
            loop(text.substring(brace + 1))
          } else {
            builder.append("{")
            loop(text.substring(1))
          }
      }
    } else {
      val brace = text.indexOf("{")
      if (brace < 0) builder.append(text).toString
      else {
        builder.append(text.substring(0,brace))
        loop(text.substring(brace))
      }
    }
  }

  loop(text)
}

更新3:这是一组Clojure测试用例(Scala版本作为一个练习:-)):

(use 'clojure.test)

(deftest test-replace-templates
  (is (=        ; No templates
        (replace-templates "this is a test" {:foo "FOO"})
        "this is a test"))

  (is (=        ; One simple template
        (replace-templates "this is a {foo} test" {:foo "FOO"})
        "this is a FOO test"))

  (is (=        ; Two templates,second at end of input string
        (replace-templates "this is a {foo} test {bar}" {:foo "FOO" :bar "BAR"})
        "this is a FOO test BAR"))

  (is (=        ; Two templates
        (replace-templates "this is a {foo} test {bar} 42" {:foo "FOO" :bar "BAR"})
        "this is a FOO test BAR 42"))

  (is (=        ; Second brace-enclosed item is NOT a template
        (replace-templates "this is a {foo} test {baz} 42" {:foo "FOO" :bar "BAR"})
        "this is a FOO test {baz} 42"))

  (is (=        ; Second item is not a template (no closing brace)
        (replace-templates "this is a {foo} test {bar" {:foo "FOO" :bar "BAR"})
        "this is a FOO test {bar"))

  (is (=        ; First item is enclosed in a non-template brace-pair
        (replace-templates "this is {a {foo} test} {bar" {:foo "FOO" :bar "BAR"})
        "this is {a FOO test} {bar")))

(run-tests)

解决方法

我认为您可以构建的最佳算法是输入字符串的长度为O(n),并且可以是:

>初始化一个空的StringBuilder
>扫描字符串以找到第一个“{”,将之前的任何子字符串添加到您的Stringbuilder.如果没有发现“{”,你已经完成了!
>扫描直到下一个“}”.使用大括号之间的任何内容在String-> String hashmap中进行地图查找,并将结果添加到StringBuilder
>返回到2.并继续扫描后的“}”

转换为Scala / Clojure作为练习:-)

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读