用于处理Data.Text的递归函数替换输出比预期更长的文本

时间:2013-02-15 15:30:04

标签: haskell

我在Haskell中有一个handleReplace函数,其类型为

handleReplace :: Character -> T.Text -> T.Text

T只是Data.Text模块作为限定导入 所以这个函数采用了一个Character类型,它的定义如下:

data Character  = Character (String, String, String, String) [(String,String)] Case String Stringderiving (Read, Show)

和Text值。 它只关心String元组的列表,并尝试将元组中第一个项的每个匹配项替换为Data.Text字符串中元组中的第二个项,以及该元组列表中每个元素的每个元素。一个例外是如果要替换的事件位于以/开头的单词内。我定义了这样的函数:

handleReplace :: Character -> T.Text -> T.Text
handleReplace (Character _ []   _ _ _)        s = s
handleReplace (Character _ ((a, b):xs) _ _ _) s = handleReplace emptyCharacter string
                                                where emptyCharacter = Character ([], [], [], []) xs Normal [] []
                                                      string         = T.unwords $ map (\ x 
                                                                                         -> if (T.head x) == '/'
                                                                                                then x
                                                                                                else T.replace (T.pack a) (T.pack b) s
                                                                                      ) $ T.words s

不幸的是,它不起作用。它不会抛出任何错误,但我没有得到预期的输出。 运行时

handleReplace (Character ([],[],[],[]) [("u","U"),("v","wv")] Normal [] []) $ T.pack "/uu v uu vvuu"

我希望它会返回"/uu wv UU wvwvUU"(显然是一个Text类型)但是当我在ghci中尝试时,我得到:

"/uu /UU /uu /UU wv UU wvwvUU /UU wv UU wvwvUU /UU wv UU wvwvUU ...

等。 为什么呢?

1 个答案:

答案 0 :(得分:3)

在不同的范围内拥有大量单字母变量使其易于制作 这种错误。我怀疑你想拥有的是

else T.replace (T.pack a) (T.pack b) x

而不是

else T.replace (T.pack a) (T.pack b) s

否则,您要对整个字符串执行多次替换, 而不是一个特定的块。这种变化似乎可以提供所需的输出 你的测试用例,至少。

顺便说一下,这就是我写它的方式。不完全是重点 免费,但它更接近,更容易理解。

import Control.Arrow ((***))
import qualified Data.Text as T

handleReplace :: Character -> T.Text -> T.Text
handleReplace (Character _ [] _ _ _) = id
handleReplace (Character _ xs _ _ _) = doReplacements $ map (T.pack *** T.pack) xs

doReplacements :: [(T.Text, T.Text)] -> T.Text -> T.Text
doReplacements reps = T.unwords . map replaceAll . T.words
    where replaceAll word = foldl replaceSingle word reps

replaceSingle :: T.Text -> (T.Text, T.Text) -> T.Text
replaceSingle word (inp, out)
    | T.head word == '/' = word
    | otherwise          = T.replace inp out word