读内联数字

时间:2015-08-18 11:04:07

标签: haskell io-monad

想象一下,我通过stdin读取了一个输入块,如下所示:

3
12
16
19

第一个数字是以下行数。我必须通过函数处理这些数字并报告由空格分隔的结果。

所以我写了这个主要功能:

main = do         
    num <- readLn
    putStrLn $ intercalate " " [ show $ myFunc $ read getLine | c <- [1..num]]

当然,由于read getLine,该函数无法编译。

但正确的(读取:Haskell方式)正确的方法是什么?是否可以将此功能写为单行?

3 个答案:

答案 0 :(得分:2)

您可以使用<$>(或fmap)构建一系列monadic操作,并使用sequence执行所有操作。

λ intercalate " " <$> sequence [show . (2*) . read <$> getLine | _ <- [1..4]]
1
2
3
4
"2 4 6 8"

答案 1 :(得分:2)

  

是否可以将此功能编写为单行?

当然,但main功能的最后一行存在问题。因为您尝试将intercalate " "应用于

[ show $ myFunc $ read getLine | c <- [1..num]]

我猜你希望后者有[String]类型,但实际上它不是一个类型很好的表达式。怎么能修好?我们先来定义

getOneInt :: IO Int
getOneInt = read <$> getLine

为方便起见(我们将在代码中多次使用它)。现在,你的意思可能就像

[ show . myFunc <$> getOneInt | c <- [1..num]]

,如果myFunc的类型与其他类型对齐,则类型为[IO String]。然后,您可以将其传递给sequence,以获得类型IO [String]的值。最后,你可以通过&#34; (使用=<<

putStrLn . intercalate " "

为了获得所需的单行:

import Control.Monad ( replicateM )
import Data.List     ( intercalate )

main :: IO ()
main = do
    num  <- getOneInt
    putStrLn . intercalate " " =<< sequence [ show . myFunc <$> getOneInt | c <- [1..num]]
  where
    myFunc = (* 3) -- for example

getOneInt :: IO Int
getOneInt = read <$> getLine

在GHCi中:

λ> main
3
45
23
1
135 69 3

但代码是否具有惯用性和可读性?在我看来,并非如此......

  

[...]正确的(读取:Haskell方式)正确的方法是什么?

没有&#34;正确&#34;这样做的方式,但以下只是让我觉得更自然和可读:

import Control.Monad ( replicateM )
import Data.List     ( intercalate )

main :: IO ()
main = do
    n  <- getOneInt
    ns <- replicateM n getOneInt
    putStrLn $ intercalate " " $ map (show . myFunc) ns
  where
    myFunc = (* 3) -- replace by your own function

getOneInt :: IO Int
getOneInt = read <$> getLine

或者,如果您想避开do表示法:

main =
    getOneInt                                        >>=
    flip replicateM getOneInt                        >>=
    putStrLn . intercalate " " . map (show . myFunc)
  where
    myFunc = (* 3) -- replace by your own function

答案 2 :(得分:2)

  

是否可以将此功能编写为单行?

嗯,它确实很简洁,但请亲自看看:

main = interact $ unwords . map (show . myFunc . read) . drop 1 . lines

那么,这是如何工作的?

  1. interact :: (String -> String) -> IO ()从STDIN获取所有内容,将其传递给给定函数,然后打印输出。
  2. 我们使用unwords . map (show . myFunc . read) . drop 1 . lines :: String -> String
    1. lines :: String -> [String]在行尾打破字符串。
    2. drop 1删除了第一行,因为我们实际上并不需要行数。
    3. map (show . myFunc . read)将每个String转换为正确的类型,使用myFunc,然后将其转换回`String。
    4. unwordsintercalate " "基本相同。
  3. 但是,请记住interact对GHCi不是很友好。