从文件中提取记录 - Haskell

时间:2014-03-11 14:25:00

标签: haskell extract criteria

///修改

我遇到过haskell的问题。如果有人可以帮助我,那就太好了。

我正在使用以下代码将记录插入文件:

check ::  Int -> Int
check a
|a > 0 && a<=10 = a
|otherwise = error "oh. hmm.. enter a number from the given interval"


ame :: IO ()
ame = do
    putStr "Enter the file name: "
    name <- getLine
    putStrLn "Do you want to add new records? "
    question <- getLine
    if question == "yes" then do
        putStrLn "Enter your records:"
        newRec <- getLine
        appendFile name ('\n':newRec)

        --new lines--
        putStrLn "enter a number between 0 and 10: "
        something <- getLine
        return (read something:: Int)
        let response = check something 
        putStrLn response
        appendFile name ('\n':something)

        putStrLn "enter something new again: "
        something2 <- getLine
        appendFile name ('\n':something2)
        putStrLn "a"
    else
        putStr "b"

现在我想使用特定条件从此文件中提取一些记录。例如,我想从偶数(或奇数或任何其他标准)行中提取和显示记录。我能这样做吗?如果有,怎么样?

...还 我想检查用户输入。假设我不希望他/她输入字符串而不是整数。我还可以查看他/她的输入吗?我是否需要创建另一个函数并在上面的代码中调用该函数?

///修改

谢谢你的回答。但是我怎么能把它嵌入到我之前的代码中呢? 我现在尝试创建一个函数(你可以看到上面的代码),然后在我的IO中调用该函数。但它不起作用..

1 个答案:

答案 0 :(得分:3)

是的,当然只能显示某些行。如果您希望将其基于行号,最简单的方法是使用zipfilter

type Record = String

onlyEven :: [Record] -> [Record]
onlyEven records =
    map snd $                -- Drop the numbers and return the remaining records
    filter (even . fst) $    -- Filter by where the number is even
    zip [1..]                -- All numbers
        records              -- Your records

这种技术可以在很多情况下使用,甚至可以抽象一点

filterByIdx :: Integral i => (i -> Bool) -> [a] -> [a]
filterByIdx condition xs = map snd $ filter (condition . fst) $ zip [1..] xs
-- Could also use 0-based of `zip [0..] xs`, up to you

onlyEven :: [a] -> [a]
onlyEven = filterByIdx even

如果您想检查输入是否为Int,最简单的方法是使用Text.Read.readMaybe功能:

import Text.Read (readMaybe)

promptUntilInt :: IO Int
promptUntilInt = do
    putStr "Enter an integer: "
    response <- getLine
    case readMaybe response of
        Just x  -> return x
        Nothing -> do
            putStrLn "That wasn't an integer!"
            promptUntilInt

这可以让您了解如何使用该功能。请注意,在某些情况下,您必须手动将类型签名指定为case (readMaybe response :: Maybe Int) of ...,但此处它可以正常工作,因为它可以从Int的类型签名中推断出promptUntilInt。如果您无法确定如何使用Read a的实例,则需要手动指定类型。


你有

something <- getLine
return (read something:: Int)
let response = check something 
putStrLn response

要逐步完成您尝试使用这些行的内容:

something <- getLine

getLine的类型为IO String,这意味着它会执行IO操作并返回String。您可以将do表示法中的值提取为

something <- getLine

就像你上面一样。现在something是一个String,它具有在该行输入的任何值。接着,

return (read something :: Int)

something转换为Int,然后将其传递给函数 return。请记住,return在Haskell中并不特殊,它只是一个在monad中包含纯值的函数。例如,return 1 :: Maybe Int === Just 1return 1 :: [Int] === [1]。它具有上下文意义,但它与函数putStrLn没有区别。因此该行只会将something转换为Int,将其包装在IO monad中,然后继续执行到下一行而不执行任何其他操作:

let response = check something

这不会编译,因为check的类型为Int -> Int,而不是String -> String。说"hello, world" > 0 && "hello, world" <= 10没有任何意义,你如何比较StringInt?相反,你想做

let response = check (read something)

但同样,这是不安全的。在无效读取时抛出错误或read something大于10会使崩溃程序完全失败,Haskell的错误与大多数语言不同。最好做一些像

这样的事情
check :: Int -> Bool
check a = a > 0 && a <= 10

...

something <- getLine
case readMaybe something of
    Nothing -> putStrLn "You didn't enter a number!"
    Just a  -> do
        if check a
            then putStrLn "You entered a valid number!"
            else putStrLn "You didn't enter a valid number!"
putStrLn "This line executes next"

虽然这段代码有点复杂,但它也是安全的,它不会崩溃,它会明确而恰当地处理每个案例。顺便说一句,error的使用通常被认为是错误的,Haskell捕获此函数抛出的错误的能力有限,但错误可以由Maybe和{{1}等数据结构表示},它为我们提供了不安全和不可预测的异常的纯粹替代品。

最后,

Either

如果能够编译,那么putStrLn response 将具有类型response,因为这是Int返回的内容。然后这一行会有一个类型错误,因为check,如名称所暗示的那样,放置一个带有新行的字符串,它不会打印putStrLn值。为此,您可以使用Int,其定义为print

由于这有点复杂,我会做一个较小的函数来处理它并循环直到给出一个有效值,如

print x = putStrLn $ show x

然后你可以用它作为

prompt :: Read a => String -> String -> IO a
prompt msg failMsg = do
    putStr msg
    input <- getLine
    case readMaybe input of
        Nothing -> do
            putStrLn failMsg
            prompt
        Just val -> return val

但是,您不必使它如此通用。您可以像我之前使用main = do -- other stuff here -- ... -- ... (anInt :: Int) <- prompt "Enter an integer: " "That wasn't an integer!" -- use `anInt` now if check anInt then putStrLn $ "Your number multiplied by 10 is " ++ show (anInt * 10) else putStrLn "The number must be between 1 and 10 inclusive" 一样轻松地对消息和返回类型进行硬编码。