///修改
我遇到过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中调用该函数。但它不起作用..答案 0 :(得分:3)
是的,当然只能显示某些行。如果您希望将其基于行号,最简单的方法是使用zip
和filter
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 1
或return 1 :: [Int] === [1]
。它具有上下文意义,但它与函数putStrLn
没有区别。因此该行只会将something
转换为Int
,将其包装在IO
monad中,然后继续执行到下一行而不执行任何其他操作:
let response = check something
这不会编译,因为check
的类型为Int -> Int
,而不是String -> String
。说"hello, world" > 0 && "hello, world" <= 10
没有任何意义,你如何比较String
和Int
?相反,你想做
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"
一样轻松地对消息和返回类型进行硬编码。