unp*_*680 7 recursion functional-programming clojure
在Clojure [1]中编写惯用的函数代码,如何编写一个用空格分割字符串但保持引用短语完整的函数?快速解决方案当然是使用正则表达式,但如果没有它们,这应该是可能的.快速浏览一下似乎很难!我在命令式语言中写了类似的东西,但我想看看功能性的递归方法是如何工作的.
快速检查我们的功能应该做什么:
"Hello there!" -> ["Hello", "there!"]
"'A quoted phrase'" -> ["A quoted phrase"]
"'a' 'b' c d" -> ["a", "b", "c", "d"]
"'a b' 'c d'" -> ["a b", "c d"]
"Mid'dle 'quotes do not concern me'" -> ["Mid'dle", "quotes do not concern me"]
Run Code Online (Sandbox Code Playgroud)
我不介意引号之间的间距是否变化(因此可以先使用空格分割简单).
"'lots of spacing' there" -> ["lots of spacing", "there"] ;is ok to me
Run Code Online (Sandbox Code Playgroud)
[1]这个问题可以在一般水平上回答,但我想Clojure中的一种功能方法可以轻松地转换为Haskell,ML等.
这是一个返回lazy seq of words/quoted strings的版本:
(defn splitter [s]
(lazy-seq
(when-let [c (first s)]
(cond
(Character/isSpace c)
(splitter (rest s))
(= \' c)
(let [[w* r*] (split-with #(not= \' %) (rest s))]
(if (= \' (first r*))
(cons (apply str w*) (splitter (rest r*)))
(cons (apply str w*) nil)))
:else
(let [[w r] (split-with #(not (Character/isSpace %)) s)]
(cons (apply str w) (splitter r)))))))
Run Code Online (Sandbox Code Playgroud)
试运行:
user> (doseq [x ["Hello there!"
"'A quoted phrase'"
"'a' 'b' c d"
"'a b' 'c d'"
"Mid'dle 'quotes do not concern me'"
"'lots of spacing' there"]]
(prn (splitter x)))
("Hello" "there!")
("A quoted phrase")
("a" "b" "c" "d")
("a b" "c d")
("Mid'dle" "quotes do not concern me")
("lots of spacing" "there")
nil
Run Code Online (Sandbox Code Playgroud)
如果输入中的单引号与正确匹配,则最终打开单引号中的所有内容都将构成一个"单词":
user> (splitter "'asdf")
("asdf")
Run Code Online (Sandbox Code Playgroud)
更新:另一个版本回答edbond的评论,更好地处理单词中的引号字符:
(defn splitter [s]
((fn step [xys]
(lazy-seq
(when-let [c (ffirst xys)]
(cond
(Character/isSpace c)
(step (rest xys))
(= \' c)
(let [[w* r*]
(split-with (fn [[x y]]
(or (not= \' x)
(not (or (nil? y)
(Character/isSpace y)))))
(rest xys))]
(if (= \' (ffirst r*))
(cons (apply str (map first w*)) (step (rest r*)))
(cons (apply str (map first w*)) nil)))
:else
(let [[w r] (split-with (fn [[x y]] (not (Character/isSpace x))) xys)]
(cons (apply str (map first w)) (step r)))))))
(partition 2 1 (lazy-cat s [nil]))))
Run Code Online (Sandbox Code Playgroud)
试运行:
user> (doseq [x ["Hello there!"
"'A quoted phrase'"
"'a' 'b' c d"
"'a b' 'c d'"
"Mid'dle 'quotes do not concern me'"
"'lots of spacing' there"
"Mid'dle 'quotes do no't concern me'"
"'asdf"]]
(prn (splitter x)))
("Hello" "there!")
("A quoted phrase")
("a" "b" "c" "d")
("a b" "c d")
("Mid'dle" "quotes do not concern me")
("lots of spacing" "there")
("Mid'dle" "quotes do no't concern me")
("asdf")
nil
Run Code Online (Sandbox Code Playgroud)
这个解决方案是在haskell中,但主要思想也应该适用于clojure.
解析器的两种状态(引号内部或外部)由两个相互递归的函数表示.
splitq = outside [] . (' ':)
add c res = if null res then [[c]] else map (++[c]) res
outside res xs = case xs of
' ' : ' ' : ys -> outside res $ ' ' : ys
' ' : '\'' : ys -> res ++ inside [] ys
' ' : ys -> res ++ outside [] ys
c : ys -> outside (add c res) ys
_ -> res
inside res xs = case xs of
' ' : ' ' : ys -> inside res $ ' ' : ys
'\'' : ' ' : ys -> res ++ outside [] (' ' : ys)
'\'' : [] -> res
c : ys -> inside (add c res) ys
_ -> res
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1353 次 |
| 最近记录: |