在线阅读数字

Reading numbers inline

假设我通过 stdin 读取了一个如下所示的输入块:

3
12
16
19

第一个数字是后面的行数。我必须通过函数处理这些数字并报告由 space.

分隔的结果

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

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

当然,由于 read getLine.

,该函数无法编译

但是正确的(阅读:Haskell 方式)正确执行此操作的方法是什么?甚至可以将这个函数写成一行吗?

您可以使用 <$> (or fmap) and execute them all with sequence 构建单子操作列表。

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

Is it even possible to write this function as a one-liner?

当然可以,但是您的 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] 类型的值。最后,你可以 "pass" 那(使用 =<<)到

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

不过,代码是否惯用且可读?没那么多,在我看来...

[...] what is the correct (read: the Haskell way) way to do this properly?

没有 "correct" 的方法,但我觉得以下内容更自然、更易读:

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

Is it even possible to write this function as a one-liner?

嗯,是的,而且有点简洁,但是你自己看:

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 " "基本相同。

但是,请记住 interact 对 GHCi 不是很友好。