迭代字符串并用haskell中的子字符串替换单个字符

时间:2012-10-17 12:51:15

标签: haskell

我正在尝试学习一些Haskell,但我发现它很难。我和我有一些问题 当前的项目。我的想法是,我必须通过一个字符串并替换某些字符 使用新的子串。例如,如果我有一个字符串“FLXF”,我想替换每个F 使用名为“FLF”的子字符串,结果应为“FLFLXFLF”。现在我一直在努力 几小时的具体问题。我一直在阅读可能派上用场的各种类型,不同的功能(地图,折叠等),但我还是无法解决这个问题。

以下代码是我的一些不同尝试:

apply :: String -> String
apply []     = []
apply (x:xs) = if (x == 'F')
               then do show "Hello"
                       apply xs
               else (apply (xs))

这个例子在这里我只是试图在每次遇到'F'时显示你好,但它显示的只是“”,所以这显然不起作用。我真的不确定if else语句是否可以去这里。我也在想功能图可能会起作用。这里我想到的代码看起来像这样:

map (\x y -> if y == 'F' then "FLD" else y) "FLF"

但是这给了我一个类型错误。所以你可以看到我迷路了。请原谅我对Haskell的愚蠢知识,但我还是新手。我真的希望你们中的一些人可以帮助我,或者让我朝着正确的方向努力。如果我不清楚某些事情,请随时提问。

提前谢谢!

约翰

4 个答案:

答案 0 :(得分:14)

map (\x y -> if y == 'F' then "FLD" else y) "FLF"

这几乎是正确的。

首先......为什么函数有两个参数?

map (\y -> if y == 'F' then "FLD" else y) "FLF"

剩下的类型错误是因为then分支提供了String,但else分支提供了Char(两个分支必须分别给出一个值{1}}相同类型)。因此,我们将else分支改为String(回想String[Char]的同义词):

map (\y -> if y == 'F' then "FLD" else [y]) "FLF"

现在的问题是,这会为您提供[String]值,而不是String。所以我们将所有这些字符串连接在一起:

concat (map (\y -> if y == 'F' then "FLD" else [y]) "FLF")

concatmap的这种组合很常见,因此有一个标准函数可以将它们组合在一起。

concatMap (\y -> if y == 'F' then "FLD" else [y]) "FLF"

答案 1 :(得分:2)

concatMap是最直观的事情。这种组合在数据结构上的映射是一个函数,它自己返回数据结构的类型(在这种情况下,列表)并将结果组合回一个紧密的"列表确实是非常在Haskell中很常见,实际上不仅仅是列表。

我想解释为什么你的第一次尝试完全编译,以及它实际上做了什么 - 因为它与你想象的完全不同!

apply (x:xs) = if (x == 'F')

该行仍然非常清晰:您只需从字符串中取出第一个字符并将其与'F'进行比较。位于"行人"手动将字符串分开,但很好。嗯,你赋予这个功能的名字并不是特别好,但我会坚持下去。

               then do show "Hello"

现在这很有意思。您可能认为do开始一个点列表,"首先执行此操作,然后执行此操作" ...就像在简单的 Hello,World -ish示例程序中一样。但请记住:在Haskell中,通常没有计算内容的 order 。这只发生在IO上下文中。但是你的代码中没有IO!?!

不确定你是否已经听说过IO实际上是什么,无论如何你要去:它是Monad。那些神秘的Haskell构建了你只在故事书中读到的#34; ...

事实上,虽然这可能会在这里引出一点点,这个问题涵盖了所有关于Monad s 的知识!那怎么样?

这是另一种(正确的!)方式来定义你的功能。

apply' str = do
   x <- str
   if (x == 'F')
    then "FLF"
    else return x

所以我使用了这种奇怪的do语法,而且它不在IO中,它看起来与你在{{1}写的完全不同但是它有效。怎么样?

IO

x <- str 表示法中,do总是表示类似于&#34;从这个monadic thingy中取出一个值,并称之为variable <- action&#34;。您可能已经看到过的是

x

表示&#34;将用户输入从现实世界中取出(response <- getLine monad!)并将其称为IO&#34;。在response中,它是我们拥有的字符串,而不是x <- str操作。所以我们从一个字符串中取出一个字符 - 很简单!

实际上,它并不完全正确。 &#34;带 字符&#34;是你用IO做的,它只是第一个。相比之下,apply (x:xs) = ...实际上将所有可能的字符从字符串中逐一取出。如果您习惯使用过程语言,这似乎与x <- str非常不一致,但实际上并非如此:response <- getLine也包含每个可能的输入用户可能会给出,并且程序必须根据此行动。

getLine

这里没什么意外,但是

   if (x == 'F')

哇!就像那样?让我们先来看下一行

    then "FLF"

好的,这个看起来很熟悉,但实际上并非如此。在其他语言中,这意味着&#34;我们已经完成了我们的功能, else return x 就是结果&#34;。但这显然不是这里发生的事情,因为xx,&#34;返回类型&#34; Char的{​​{1}}是apply'。在Haskell中,String实际上与从函数返回值几乎没有关系,而是意味着&#34;将该值放入我们正在使用的monadic上下文中。如果monad是return,那就完全一样了:将这个值赋回现实世界的上下文(这并不意味着打印价值或其他东西,只是交给它上)。但是在这里,我们的上下文是一个字符串,或者更确切地说是一个列表(字符,所以它一个IO)。

是的,如果String不是x,我们会将其放回字符串中。这听起来很合理,但'F'呢?请注意,我也可以这样写:

then "FLF"

这意味着,我从 if (x == 'F') then do x' <- "FLF" return x' else return x 中取出所有字符并将它们返回到整体结果中。但是,没有必要只考虑最终结果,我们也可以只隔离这部分"FLW" - 而且很明显,它的值只不过是字符串do { x' <- "FLF"; return x' }本身! / p>

所以我希望你现在已经掌握了"FLF"的工作原理。回到你的版本,虽然它实际上没有多大意义......

apply'

此处我们有一行不在 then do show "Hello" apply xs 块的末尾,但其中没有do。您通常会在<-中看到类似

的内容
IO

请记住&#34;仅输出&#34;动作在Haskell中具有类型main = do putStrLn "How ya doin'?" response <- getLine ... ,这意味着,它们不会直接返回任何有意义的值,只是简单的值IO()。所以你并不关心这一点,但你仍然可以评估它:

()

编译和输出

  

您好,让我们看看此IO操作返回的内容:
()

如果我们必须一直评估main = do trivial <- putStrLn "Hello, let's see what this IO action returns:" print trivial ,那将是愚蠢的,所以Haskell允许只留下()。它真的就是这样!

因此,() <-块中间的show "Hello"行基本上意味着&#34;从do中取出一个字符(这只是一个值为{{的字符串) 1}}),但不要对这个角色做任何其他事情/只是扔掉它#34;。

你的定义的其余部分只是对show "Hello"的其他递归调用,但由于它们都没有做任何比扔掉字符更有趣的事情,你最终会在"\"Hello\""结束,所以&#39;最后的结果:一个空字符串。

答案 2 :(得分:1)

if-then-else ...我知道Haskell支持这些,但是,我非常感到惊讶,没有人在这里删除它们......

以下是针对不同替换案例的解决方案。

  • 替换字符
  • 替换
  • 替换每个单词的功能

$ cat replace.hs

import Data.List (isPrefixOf)

replaceC :: Char -> Char -> String -> String
replaceC _ _ [] = []
replaceC a b (x:xs)
  | x == a    = b:replaceC a b xs
  | otherwise = x:replaceC a b xs

replaceW :: String -> String -> String -> String
replaceW a b s = unwords . map replaceW' $ words s
  where replaceW' x | x == a    = b
                    | otherwise = x

replaceF :: (String -> String) -> String -> String
replaceF f = unwords . map f . words

string = "Hello world ^fg(blue)"

main = do
    print string
    print $ replaceC 'o' 'z' string
    print $ replaceW "world" "kitty" string
    print . replaceF f . replaceW "world" "kitty" $ replaceC 'H' 'Y' string
  where f s | "^" `isPrefixOf` s = '^':'^':drop 1 s
            | otherwise = s

$ runhaskell replace.hs

"Hello world ^fg(blue)"
"Hellz wzrld ^fg(blue)"
"Hello kitty ^fg(blue)"
"Yello kitty ^^fg(blue)"

答案 3 :(得分:0)

您的基本错误是您想要使用String替换String中的Char。 这是不可能的,因为String是Char的列表,Char是Char而不是short String。即使长度为1,字符串也不是字符串。

因此,你真正想要的是用一些其他的Chars替换一些Char。你的方法很有希望,可以这样完成:

replace [] = []  -- nothing to replace in an empty string
replace (c:cs) = if c == 'F' then 'F':'L':'F':replace cs
                             else c:replace cs