我正在尝试学习一些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的愚蠢知识,但我还是新手。我真的希望你们中的一些人可以帮助我,或者让我朝着正确的方向努力。如果我不清楚某些事情,请随时提问。
提前谢谢!
约翰
答案 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")
concat
和map
的这种组合很常见,因此有一个标准函数可以将它们组合在一起。
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;。但这显然不是这里发生的事情,因为x
是x
,&#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