如何读取haskell字符串

Rar*_*ima 4 io haskell

我是Haskell的完全初学者,虽然熟悉Python,F#,Java,C#和C++等语言的功能范例(以有限的方式).

只是一直在逃避我的事情是在哈斯克尔的IO.我尝试过几次,甚至在尝试绕过它的过程中学习了C#和F#.

更具体地说,我指的是不使用符号来获取IO,使用符号,IO变得微不足道.这可能是不好的做法,但在我的业余时间,我喜欢看看我是否可以用一个连续的表达来完成任务.这是一个很糟糕的做法,很有趣.

这样的表达式通常是那种(在伪haskell中):

main = getStdinContentsAsString 
           >>= ParseStringToDataStructureNeeded
           >>= DoSomeComputations 
           >>= ConvertToString 
           >>= putStrLn
Run Code Online (Sandbox Code Playgroud)

我对最后四个部分没有任何问题.我学习F#的原因之一就是看看是否有什么东西我没有把我的脑袋放在IO之外,但是只要我有方便的F#的Console.ReadLine()它返回一个普通的老字符串它基本上是一帆风顺.

这让我再次回到了haskell,再次被IO机制所阻止.

我已经管理(在这里使用另一个问题)从控制台读取一个int,并打印"Hello World!" 很多次

main = (readLn :: IO Int) >>= \n -> mapM_ putStrLn $ replicate n "Hello World!"
Run Code Online (Sandbox Code Playgroud)

我想至少得到一些"通用"的方式只是读取stdin的整个内容(可能是多行,所以getContents需要是选择的函数)作为一个字符串,然后我可以使用其他功能,如取消,然后映射.

我已经尝试过的一些事情:

正如我所说,getContents将是我需要的(除非有一些相当于它).

使用逻辑,因为

getContents :: IO String
Run Code Online (Sandbox Code Playgroud)

然后我需要一些带有IO String的东西并返回一个旧的String.这是(据我所知)

unsafePerformIO :: IO a -> a
Run Code Online (Sandbox Code Playgroud)

但由于某些原因,ghc不满意:

* Couldn't match type `[Char]' with `IO (IO b)'
  Expected type: String -> IO b
    Actual type: IO (IO b) -> IO b
* In the second argument of `(>>=)', namely `unsafePerformIO'
  In the expression: getContents >>= unsafePerformIO
Run Code Online (Sandbox Code Playgroud)

我试过的另一件事:这没有问题;

main = getContents >>= putStrLn
Run Code Online (Sandbox Code Playgroud)

即使getContents返回的类型是IO操作,而不是putStrLn想要的本身String

getContents :: IO String
putStrLn    :: String -> IO ()
Run Code Online (Sandbox Code Playgroud)

不知何故,动作被神奇地执行,结果字符串被传递给put函数.

但是当我尝试添加内容时,比如在打印之前简单地将"hello"添加到输入中:

main = getContents >>= (++ " hello") >>= putStrLn
Run Code Online (Sandbox Code Playgroud)

我突然得到一个类型不匹配:

Couldn't match type `[]' with `IO'
  Expected type: String -> IO Char
    Actual type: [Char] -> [Char]
* In the second argument of `(>>=)', namely `(++ " hello")'
  In the first argument of `(>>=)', namely
    `getContents >>= (++ " hello")'
  In the expression: getContents >>= (++ " hello") >>= putStrLn
Run Code Online (Sandbox Code Playgroud)

不知何故,IO动作不再执行(或者我可能不理解这一点).

我自己也尝试了许多东西,用的组合getLine,readLn,getContents,unsafePerformIO,read,fmap无济于事.

这只是一个非常基本的例子,但它完全说明了让我几次放弃haskell的问题(可能我不是唯一一个),虽然顽固的想要了解它,并了解什么是几乎这些函数式编程语言让我回归.

总结:

  1. 有没有我没有得到的东西?(99%是的)

  2. 如果是,那又怎样?

  3. 我应该如何阅读整个标准输入并在一个连续的表达式中处理它?(如果我只需要一行,我想无论解决方案是什么,它都适用于getLine正弦它基本上是getContents的姐妹)

提前致谢!

mel*_*ene 11

你不考虑的主要事情似乎是>>=:

(>>=) :: IO a -> (a -> IO b) -> IO b
Run Code Online (Sandbox Code Playgroud)

换句话说,你不需要"打开" IO StringString; 该>>=运营商已经把一个普通String其右操作数(功能):

getContents >>= (\s -> ...)
--                ^ s :: String
Run Code Online (Sandbox Code Playgroud)

getContents >>= (++ " hello")失败的原因是>>=需要函数返回一个IO ...值,但是(++ "hello") :: String -> String.

您可以通过添加return :: a -> IO a到混合中来解决此问题:

getContents >>= (return . (++ "hello"))
Run Code Online (Sandbox Code Playgroud)

整个表达式都有类型IO String.执行时,它将读取数据stdin,附加"hello"到它,并返回结果字符串.

从而,

getContents >>= (return . (++ "hello")) >>= putStrLn
Run Code Online (Sandbox Code Playgroud)

应该工作正常.但它比必要的更复杂.从概念上讲,再次return包装一个值IO并将>>=其展开(有点).

我们可以融合右边的return/ >>=位:

getContents >>= (\s -> putStrLn (s ++ "hello"))
Run Code Online (Sandbox Code Playgroud)

即不是采取getContents :: IO String,添加"hello"它以形成一个新的IO String动作,然后附加putStrLn :: String -> IO ()到它,我们包装putStrLn以创建一个新的String -> IO ()功能("hello"在将事物交给之前附加到其参数putStrLn).

现在,如果我们想要,我们可以摆脱s标准的免费技巧:

getContents >>= (putStrLn . (++ "hello"))
Run Code Online (Sandbox Code Playgroud)

有关的注意事项IO一般:要记住的事情是,IO ...是正常的Haskell类型.这里没有神奇的事情发生.>>=不执行任何行动; 它只是组合了一个类型的值IO something和一个函数来构造一个新的类型值IO somethingelse.

您可以将Haskell视为纯粹的元语言,将构造命令性程序(即执行指令列表)构建为内存中的数据结构.实际执行的唯一事情是绑定的值Main.main.也就是说,它就像一个命令式运行时运行你的Haskell代码来生成一个纯值main :: IO ().然后,该值的内容将作为命令性指令执行:

main :: IO ()
main =
    putChar 'H' >>
    putChar 'i' >>
    putChar '\n'
Run Code Online (Sandbox Code Playgroud)

main绑定到表示命令性程序的数据结构print 'H'; print 'i'; print newline.运行Haskell程序构建此数据结构,然后运行时执行它.

但是,此模型并不完整:命令式运行时可以回调Haskell代码.>>=可以用来在命令式代码中"嵌入"Haskell函数,然后可以(在运行时)检查值,决定下一步做什么,等等.但所有这些都以纯Haskell代码的形式发生; 只有IO从返回值fx >>= f事项(f本身有没有副作用).

  • 使用非常需要(对新手)parens的荣誉,即使无关和不冷静.:) (2认同)