我怎样才能更好地完成我的palidrom计划?

时间:2016-04-26 15:15:26

标签: haskell

我的程序有问题:程序必须逐行读取文件,并且必须说它是否是一个palindrom和每个单词的长度。我在Haskell中非常新,只有这个,但它不起作用:(

main = do
  inhalt <- readFile "palindrom.txt"
  laengeWort = length inhalt
  if istPalindrom inhalt
    then putStrLn (inhalt ++ " ist ein Palindrom und hat die Laenge "++ laengeWort
    else putStrLn (inhalt ++" ist kein Palindrom und hat die Laenge " ++ laengeWort)

isPalindrom w = w == reverse w

请帮我正确完成我的程序:)

2 个答案:

答案 0 :(得分:2)

首先,在do块中,在引入变量时,您应该使用 let 关键字。 (或者在提取monad的值时使用<-,就像读取文件时一样。)

laengeWort = length inhalt

变为

let laengeWort = length inhalt

其次,laengeWort变量的类型为Int,您需要使用show函数将其转换为String。 (Haskell并没有像其他语言那样将类型隐式转换为String。)

最后,您错误地将isPalindrom误认为istPalindrome 而你忘了从then putStrLn (开始关闭parens

语法上正确的解决方案是(我把它翻译成英文):

main = do
  contents <- readFile "palindrom.txt"
  let contentLength =  length contents
  if isPalindrome contents then
    putStrLn (contents ++ " is a palindrome of length "++ show contentLength)
  else
    putStrLn (contents ++" is not a palindrome, it has the length " ++ show contentLength)

此外,此解决方案对整个文件进行操作,就像是单个字符串/单词一样。从您的描述中可以看出,您希望将每一行视为一个不同的词。为此,请使用lines函数按行分割内容,并使用partition将单词过滤为回文单词和非单词。然后,您可以使用mapM_打印每个单词。这是一个相当简洁的例子(尽管它没有保留单词的顺序):

import Data.List (partition)
main = do
  contents <- lines `fmap` readFile "palindrom.txt"
  let (palins,notPalins) = partition isPalindrome contents
  mapM_ (\w -> putStrLn $ "the word " ++ w ++ " is a palindrome of length " ++ show (length w)) palins
  mapM_ (\w -> putStrLn $ "the word " ++ w ++ " is not a palindrome, it has length" ++ show (length w)) notPalins

isPalindrome w = w == reverse w

我使用了&#39; fmap&#39;将lines函数(采用纯字符串)映射到 readFile(在IO monad中返回一个字符串)的结果,得到IO monad中的字符串列表,以及&#39;&lt; - &#39;运算符从monad中提取该列表。 这相当于说:

rawData <- readFile "palindrom.txt"
let contents = lines rawData

答案 1 :(得分:2)

问题是您正在使用readFile,它会将"palindrom.txt"的全部内容读入单个String。您需要将文件的内容拆分为行,然后将isPalindrom应用于行。您可以使用lines内置函数执行此操作,然后使用mapM_。为了使其更清晰,您可以使用forM_中的Control.MonadmapM_只更换参数顺序:

mapM_ :: Monad m => (a -> m b) -> [a] -> m ()
forM_ :: Monad m => [a] -> (a -> m b) -> m ()

我大多数时候都喜欢它,因为它看起来更像是来自其他语言的for循环。所以你的代码看起来像

import Control.Monad (forM_)

main = do
    inhalt <- readFile "palindrom.txt"
    forM_ (lines inhalt) (\wort -> do
        let laengeWort = length wort
        putStrLn (if istPalindrom wort
            then wort ++ " ist ein Palindrom und hat die Laenge " ++ show laengeWort
            else wort ++ " ist kein Palindrom und hat die Laenge " ++ show laengeWort
        )
    )

istPalindrom w = w == reverse w

我还将putStrLn提升到if语句之前,使其更清洁,并使线条更短。

你在语法方面遇到了一些问题,但是当你是初学者并且大部分教程都没有用你的母语时,这是非常可原谅的。

为了清理这些代码,我可能会将消息格式化为另一个函数:

macheNachricht :: String -> String
macheNachricht wort =
    let nachricht = if istPalindrom wort
            then " ist ein Palindrom"
            else " ist kein Palindrom"
        laengeWort = show (length wort)
    in wort ++ nachricht ++ " und hat die Laenge " ++ laengeWort

然后main看起来像

main = do
    inhalt <- readFile "palindrom.txt"
    forM_ (lines inhalt) (\wort -> putStrLn (macheNachrict wort))

这可以使用Haskell的函数组合运算符.进一步减少,该运算符被定义为

f . g = \x -> f (g x)

由于这正是\wort -> putStrLn (macheNachricht wort)作为forM_的第二个参数所具有的,我们可以将其写为putStrLn . macheNachricht

main = do
    inhalt <- readFile "palindrom.txt"
    forM_ (lines inhalt) (putStrLn . macheNachricht)

(对不起,如果makeMessagemacheNachricht翻译为德语{{1}}有点不精确,我自己就开始学习德语,而且我还不是很好。)< / p>