7

我正在尝试学习一些 Haskell,但我觉得很难。我目前的项目有一些问题。这个想法是我必须通过一个字符串并用新的子字符串替换某些字符。例如,如果我有一个字符串“FLXF”并且我想用一个名为“FLF”的子字符串替换每个 F,那么结果应该是“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

4 回答 4

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

这几乎是对的。

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

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

剩下的类型错误是因为then分支给出了 a String,但else分支给出了 a Char(两个分支必须各自给出相同类型的值)。所以我们将让else分支给出 a String(回想一下,这String是 的同义词[Char]):

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

现在的问题是这给了你一个[String]值而不是 a String。所以我们将所有这些字符串连接在一起:

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

concat和的这种组合map很常见,以至于有一个标准函数将它们组合在一起。

concatMap (\y -> if y == 'F' then "FLD" else [y]) "FLF"
于 2012-10-17T12:59:08.093 回答
2

concatMap是这里最直观的东西。这种在数据结构上映射一个本身返回数据结构类型(在本例中为列表)的函数和将结果组合回单个“紧密”列表之间的组合在 Haskell中确实常见,并且确实不仅适用于列表。

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

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

该行仍然非常清楚:您只需从字符串中取出第一个字符并将其与'F'. 在位“行人”手动将字符串分开,但很好。好吧,你给这个函数起的名字不是特别好,但我会在这里坚持下去。

               then do show "Hello"

现在这很有趣。您可能会认为do开始一个点列表,“先做这个,然后做那个”......就像在简单的Hello, World -ish 示例程序中一样。但永远记住:在 Haskell 中,通常没有计算东西的顺序这样的东西。这只发生在IO上下文中。但是IO您的代码中没有!?!

不确定你是否听说过IO实际上是什么,不管怎样,你去吧:它是一个Monad. 那些“你只在故事书中读过的神话般的 Haskell 构造”......

事实上,尽管这可能会导致这里有点远,但这个问题涵盖了所有关于Monads 的知识!那个怎么样?

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

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

所以我使用了这种奇怪do的语法,它不在 中IO,它看起来与你在 中写的完全不同IO,但它确实有效。如何?

   x <- str

do符号中,variable <- action总是意味着“从这个单子事物中取出一个值,然后调用它x”。你可能已经看到了类似的东西

response <- getLine

这意味着“将用户输入从现实世界中取出(从IO单子中取出!)并调用它response”。在x <- str中,它是我们拥有的字符串,而不是IO动作。所以我们从字符串中取出一个字符——既简单又好用!

但实际上,这并不完全正确。“取一个字符”是你用 做的apply (x:xs) = ...,它只取第一个。相反,x <- str实际上从字符串中取出所有可能的字符,一个接一个。如果您习惯于过程语言,这可能看起来与 非常不一致response <- getLine,但实际上并非如此:getLine它还包含用户可能给出的所有可能输入,并且程序必须按照此操作。

   if (x == 'F')

这里没有什么意外,但是

    then "FLF"

哇!就这样?我们先看下一行

    else return x

好的,这看起来很熟悉,但实际上并非如此。在其他语言中,这意味着“我们完成了我们的功能,x就是结果”。但这显然不是这里发生的事情,因为xis和isChar的“返回类型” 。在 Haskell 中,实际上与从函数返回值几乎没有关系,而是意味着“将该值放入我们正在处理的一元上下文中”。如果 monad 是,那将是完全相同的:将此值返回给现实世界的上下文(这并不意味着打印该值或其他东西,只是将其交给)。但是在这里,我们的上下文是一个字符串,或者更确切地说是一个列表(字符,所以它a )。apply'StringreturnIOString

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

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

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

所以我希望你现在已经掌握了为什么apply'有效。回到你的版本,虽然它实际上没有多大意义......

           then do
              show "Hello"
              apply xs

在这里,我们有一行不在do块的末尾,但里面没有 a <-。你通常会IO在类似的地方看到这个

main = do
   putStrLn "How ya doin'?"
   response <- getLine
   ...

请记住,“仅输出”操作IO()在 Haskell 中具有类型,这意味着它们不会直接返回任何有意义的值,而只是返回微不足道的值()。所以你并不真正关心这个,但你仍然可以评估它:

main = do
   trivial <- putStrLn "Hello, let's see what this IO action returns:"
   print trivial

编译和输出

你好,让我们看看这个 IO 动作返回什么:(
)

如果我们必须一直进行这种评估,那将是愚蠢()的,因此 Haskell 允许将其() <-排除在外。真的就是这样!

所以像块show "Hello"中间这样的一行do基本上意味着“取出一个字符show "Hello"(它只是一个带有 value 的字符串"\"Hello\""),但不要对这个字符做任何其他事情/只是把它扔掉”。

您定义的其余部分只是对 的其他递归调用apply,但是因为它们都没有比丢弃字符更有趣的事情,所以您最终会得到apply [] = [],所以这就是最终结果:一个空字符串。

于 2012-10-17T19:14:04.453 回答
2

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

因此,以下是我针对不同情况进行替换的解决方案。

  • 替换一个字符
  • 替换单词
  • 通过每个单词的函数替换

$猫替换.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 替换.hs

"Hello world ^fg(blue)"
"Hellz wzrld ^fg(blue)"
"Hello kitty ^fg(blue)"
"Yello kitty ^^fg(blue)"
于 2013-07-25T08:19:31.203 回答
0

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

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

replace [] = []  -- nothing to replace in an empty string
replace (c:cs) = if c == 'F' then 'F':'L':'F':replace cs
                             else c:replace cs
于 2012-10-17T22:06:45.323 回答