You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Haskell IO操作实现求助:读取输入生成Card自定义类型列表

Fixing Your Haskell IO Card Reader

Hey there! Let's work through this Haskell IO problem together—IO operations can feel a bit counterintuitive when you're starting out, but we'll get this sorted step by step.

First, let's break down what your code is missing and fix the core issues:

Key Issues in Your Current Code

Looking at your snippet, there are two main problems holding it back:

  • The returnCardList function has the wrong type: it's declared as [Card] -> [Card], but it uses do notation and getLine (which are IO operations), so it should be [Card] -> IO [Card].
  • You're using nested return calls incorrectly in readCards—since returnCardList will already return an IO [Card], you don't need to wrap it in another return.

Step-by-Step Solution

1. First, Define Your Card Type (and Parser)

Since you didn't include your Card definition, let's assume you have something like this (adjust based on your actual Card structure):

-- Example Card type (customize this to match your needs)
data Card = Card String Int deriving (Show, Read)

-- Optional: Custom parser if you don't want to rely on the default Read instance
-- parseCard :: String -> Maybe Card
-- parseCard s = case words s of
--   [name, numStr] -> case readMaybe numStr of
--     Just num -> Just (Card name num)
--     Nothing -> Nothing
--   _ -> Nothing

2. Fix the Recursive IO Function

We'll rewrite returnCardList to properly handle IO recursion. It takes an accumulator list of Cards, reads input, and either continues building the list or returns it when the user inputs ".".

3. Put It All Together

Here's the complete working code:

import Text.Read (readMaybe) -- For safer parsing (avoids runtime crashes)

-- Define your Card type here
data Card = Card String Int deriving (Show, Read)

readCards :: IO [Card]
readCards = returnCardList []
  where
    returnCardList :: [Card] -> IO [Card]
    returnCardList acc = do
      line <- getLine
      if line == "."
        then return (reverse acc) -- Reverse to preserve original input order
        else case readMaybe line of
          Just card -> returnCardList (card : acc)
          Nothing -> do
            putStrLn "Invalid card format—please try again!"
            returnCardList acc

Explanation

  • Recursion with Accumulator: returnCardList starts with an empty accumulator. Each valid Card is prepended to the accumulator (prepending is more efficient than appending in Haskell). When the user inputs ".", we reverse the accumulator to get the input order right and return it.
  • Safe Parsing: Using readMaybe from Text.Read prevents the program from crashing if the user enters a string that can't be parsed into a Card. If parsing fails, we show an error message and ask for input again.
  • IO Type Correctness: returnCardList has the type [Card] -> IO [Card], which makes sense because it performs IO (reading input) and eventually returns a list of Cards.

Alternative: More Functional Style with unfoldr

If you want a concise, functional approach without explicit recursion, you can use unfoldr from Data.List:

import Data.List (unfoldr)
import Text.Read (readMaybe)

data Card = Card String Int deriving (Show, Read)

readCards :: IO [Card]
readCards = unfoldr step ()
  where
    step _ = do
      line <- getLine
      if line == "."
        then return Nothing
        else case readMaybe line of
          Just card -> return (Just (card, ()))
          Nothing -> do
            putStrLn "Invalid card—try again!"
            step ()

This works because unfoldr builds a list by repeatedly applying a function that returns Maybe (a, b)—when it returns Nothing, the list stops.

内容的提问来源于stack exchange,提问作者Elif Ak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:35:02