代码之家  ›  专栏  ›  技术社区  ›  Andrzej Gis

处理复杂输入(具有嵌套值)

  •  0
  • Andrzej Gis  · 技术社区  · 7 年前

    1. 第一行是案例数量
    2. 对于每种情况,我都必须打印汇总的数字

    例子:

    Input:
    2
    5
    1 2 3 4 5
    2
    -100 100
    
    Output:
    15
    0
    

    这是我的实现

    import Control.Monad
    
    main = do 
        linesCount <- readLn :: IO Int
        numbers <- replicateM linesCount getCase
        mapM_ putStrLn $ map (show.sum) numbers
    
    getCase :: IO [Int]
    getCase = do
        numbersCount <- readLn :: IO Int -- actually I don't need this variable
        numbersString <- getLine
        let numbers = map read $ words numbersString
        return numbers
    

    2 回复  |  直到 7 年前
        1
  •  3
  •   erisco    7 年前

    如果您只是想缩短代码,请查看 Stack Exchange community for code golfing

    如果我们认为代码太多,可能不是因为我们需要缩短代码,而是因为我们需要缩短代码 . 实现这一目标需要经验和良好实践。我们要做的是分离出明显正确的简单概念,然后以明显正确的方式将它们结合起来。方法包括自上而下的设计(将解决方案分解为更小的部分)和自下而上的设计(从更小的部分构建到解决方案)及其混合。

    一个自下而上的作品直接打动了我,那就是对一系列数字求和的任务。这在哈斯克尔的前奏曲中有一个定义,叫做 sum :: (Num a, Foldable t) => t a -> a . 在最终解决方案的某个地方,我们将使用这个。

    另一种方法是简化问题。我们可能会被问题的措辞引入歧途。仔细检查后,我们可能会发现一个等效且更简单的短语。

    5
    1 2 3 4 5
    2
    -100 100
    

    1 2 3 4 5
    -100 100
    

    现在我们有了由行返回分隔的数字列表,其中每个数字由一个空格分隔。

    在这一点上,我们有一个明确的方法来分解解决方案在自上而下的方式。首先,我们简化输入。其次,我们解析数字列表。第三,我们对列表进行汇总。第四,我们打印总数。因此,这是我们解决方案的框架:

    simplifyInput :: String -> [String]
    
    parseNumberList :: String -> [Integer]
    
    -- Note we can use `sum` from Prelude to sum the number lists.
    
    printSums :: [Integer] -> IO ()
    
    main :: IO ()
    main = getContents >>= printSums . fmap (sum . parseNumberList) . simplifyInput
    

    现在,这只是实施解决方案中每个明显部分的问题。

    simplifyInput :: String -> [String]
    simplifyInput = dropEveryOther . drop 1 . lines
      where
      dropEveryOther :: [a] -> [a]
    

    以书面形式 simplifyInput

    dropEveryOther :: [a] -> [a]
    dropEveryOther [] = []
    dropEveryOther (x:y:xs) = y : dropEveryOther xs
    

    然后继续。。。

    parseNumberList :: String -> [Integer]
    parseNumberList = fmap read . words
    
    printSums :: [Integer] -> IO ()
    printSums = putStr . unlines . fmap show
    

    simplifyInput :: String -> [String]
    simplifyInput = dropEveryOther . drop 1 . lines
      where
      dropEveryOther :: [a] -> [a]
      dropEveryOther [] = []
      dropEveryOther (_:y:xs) = y : dropEveryOther xs
    
    parseNumberList :: String -> [Integer]
    parseNumberList = fmap read . words
    
    printSums :: [Integer] -> IO ()
    printSums = putStr . unlines . fmap show
    
    main :: IO ()
    main = getContents >>= printSums . fmap (sum . parseNumberList) . simplifyInput
    

        2
  •  2
  •   Andrzej Gis    7 年前

    Alec在其中一条评论中发布了我的原始代码的超级压缩版本。我决定发布一个小故障,以防有人迷路,不知道里面发生了什么:)

    以下代码段前面需要有有效的导入:

    import Control.Monad
    import Control.Applicative
    

    所以我们从亚历克的版本开始:

    main = readLn >>= flip replicateM_ (getLine >> sum . map read . words <$> getLine >>= print)
    

    flip 函数以删除一组括号:

    main = readLn >>= (`replicateM_` (getLine >> (print =<< sum . map read . words <$> getLine)))
    

    replicateM_ 复制项_ ,我们可以用λ替换is:

    main = readLn >>= \n -> replicateM_ n (getLine >> (print =<< sum . map read . words <$> getLine))
    

    printBatchResult = print =<< sum . map read . words <$> getLine
    main = readLn >>= \n -> replicateM_ n (getLine >> printBatchResult)
    

    我们可以翻转 print =<<

    printBatchResult = sum . map read . words <$> getLine >>= print
    main = readLn >>= \n -> replicateM_ n (getLine >> printBatchResult)
    

    printBatchResult = sum . map read . words <$> getLine >>= print
    handleBatch = getLine >> printBatchResult
    main = readLn >>= \n -> replicateM_ n handleBatch
    

    再说一次:

    sumLine = sum . map read . words
    printBatchResult = sumLine <$> getLine >>= print
    handleBatch = getLine >> printBatchResult
    main = readLn >>= \n -> replicateM_ n handleBatch
    

    sumLine = sum . map read . words
    handleNumbersLine = sumLine <$> getLine
    printBatchResult = handleNumbersLine >>= print
    handleBatch = getLine >> printBatchResult
    main = readLn >>= (\n -> replicateM_ n handleBatch)
    

    最后一次:

    sumLine = sum . map read . words
    handleNumbersLine = sumLine <$> getLine
    printBatchResult = handleNumbersLine >>= print
    handleBatch = getLine >> printBatchResult
    handleAllBatches n = replicateM_ n handleBatch
    main = readLn >>= handleAllBatches
    

    我们可以替换 <$> 具有 fmap :

    sumLine = sum . map read . words
    handleNumbersLine = fmap sumLine getLine
    printBatchResult = handleNumbersLine >>= print
    handleBatch = getLine >> printBatchResult
    handleAllBatches n = replicateM_ n handleBatch
    main = readLn >>= handleAllBatches
    

    sumLine line = (sum . map read . words) line
    handleNumbersLine = fmap sumLine getLine
    printBatchResult = handleNumbersLine >>= \sum -> print sum
    handleBatch = getLine >> printBatchResult
    handleAllBatches n = replicateM_ n handleBatch
    main = readLn >>= \numberOfBatches -> handleAllBatches numberOfBatches
    

    sumLine :: String -> Int
    sumLine line = (sum . map read . words) line
    
    handleNumbersLine :: IO Int
    handleNumbersLine = fmap sumLine getLine
    
    printBatchResult :: IO ()
    printBatchResult = handleNumbersLine >>= \sum -> print sum
    
    handleBatch :: IO ()
    handleBatch = getLine >> printBatchResult
    
    handleAllBatches :: Int -> IO ()
    handleAllBatches n = replicateM_ n handleBatch
    
    main = readLn >>= \numberOfBatches -> handleAllBatches numberOfBatches
    

    一些最终意见:

    • >>= bind 函数from monad将一个monad转换为另一个(或相同的)并转换其值。在里面 main 它需要的功能 IO Int ,转换lambda和返回 IO ()
    • >> -(用于 handleBatch )忽略左参数(一行中有多少数字(可以说)是不必要的),只返回右参数-这是一个处理带有数字的行的函数。