似乎模板Haskell经常被Haskell社区视为一种不幸的便利.我很难准确地说出我在这方面所观察到的内容,但请考虑这几个例子
我已经看过各种博客文章,其中人们使用模板Haskell做了相当简洁的东西,实现了更好的语法,这在常规的Haskell中是不可能的,以及巨大的样板减少.那么为什么模板Haskell以这种方式受到鄙视呢?是什么让它不受欢迎?在什么情况下应避免模板Haskell,为什么?
当给定由\n分隔的文本输入文件时,此程序产生我期望的输出:
import System.IO
main :: IO ()
main = do h <- openFile "test.txt" ReadMode
xs <- getlines h
sequence_ $ map putStrLn xs
getlines :: Handle -> IO [String]
getlines h = hGetContents h >>= return . lines
Run Code Online (Sandbox Code Playgroud)
用withFile代替openFile并稍微重新排列
import System.IO
main :: IO ()
main = do xs <- withFile "test.txt" ReadMode getlines
sequence_ $ map putStrLn xs
getlines :: Handle -> IO [String]
getlines h = hGetContents h >>= return . lines
Run Code Online (Sandbox Code Playgroud)
我设法完全没有输出.我很难过.
编辑:不再难过了:感谢一个人和所有人的深思熟虑和发人深省的答案.我在文档中做了一些阅读,并了解到withFile可以理解为支架 …
我在Data.Text上做了很多阅读,但是我还没有找到什么时候更喜欢Strict over Lazy,反之亦然.
我的理解是Data.Text.Strict是内存中连续字符的数据结构,而Data.Text.Lazy是一块连续的字符.
我的问题是为什么我不应该总是使用Data.Text.Lazy?似乎唯一的开销是块管理,但我不知道它是否足够明显?作为交换,当Text值变大时,连接操作可以便宜得多.
欢迎思考和见解!
我正在尝试使用Haskell进行目录结构的递归下降.我想只根据需要检索子目录和文件(懒惰).
我编写了以下代码,但是当我运行它时,跟踪显示在第一个文件之前访问了所有目录:
module Main where
import Control.Monad ( forM, forM_, liftM )
import Debug.Trace ( trace )
import System.Directory ( doesDirectoryExist, getDirectoryContents )
import System.Environment ( getArgs )
import System.FilePath ( (</>) )
-- From Real World Haskell, p. 214
getRecursiveContents :: FilePath -> IO [FilePath]
getRecursiveContents topPath = do
names <- getDirectoryContents topPath
let
properNames =
filter (`notElem` [".", ".."]) $
trace ("Processing " ++ topPath) names
paths <- forM properNames $ \name -> do
let path = topPath …Run Code Online (Sandbox Code Playgroud) main = do
input <- sequence [getLine, getLine, getLine]
mapM_ print input
Run Code Online (Sandbox Code Playgroud)
让我们看看这个程序的实际效果:
m@m-X555LJ:~$ runhaskell wtf.hs
asdf
jkl
powe
"asdf"
"jkl"
"powe"
Run Code Online (Sandbox Code Playgroud)
令我惊讶的是,这里似乎没有懒惰。取而代之getLine的是,急切地评估所有3个值,将读取的值存储在内存中,然后而不是在此之前将所有值打印出来。
比较一下:
main = do
input <- fmap lines getContents
mapM_ print input
Run Code Online (Sandbox Code Playgroud)
让我们看一下实际情况:
m@m-X555LJ:~$ runhaskell wtf.hs
asdf
"asdf"
lkj
"lkj"
power
"power"
Run Code Online (Sandbox Code Playgroud)
完全不同的东西。行被一一读取并一一打印。这对我来说很奇怪,因为我看不到这两个程序之间有什么区别。
从LearnYouAHaskell:
当I / O操作所使用,
sequenceA是同样的事情sequence!它获取I / O操作的列表,并返回将执行这些操作中的每个操作的I / O操作,并以结果的形式列出这些I / O操作的结果。这是因为要将一个[IO a]值转换为一个IO [a]值,以使一个I / O操作在执行时产生结果列表,所以所有这些I / O操作都必须先进行排序,以便在评估时依次执行。被迫。如果不执行I / O操作,则无法获得结果。
我糊涂了。我不需要执行所有IO操作就可以得到一个结果。
该书前面几段显示了以下内容的定义sequence:
Run Code Online (Sandbox Code Playgroud)sequenceA :: …
我想使用Haskell函数
readFile :: FilePath -> IO String
Run Code Online (Sandbox Code Playgroud)
将文件的内容读入字符串.在文档中,我读到"文件是按需读取的,与getContents一样."
我不确定我完全理解这一点.例如,假设我写
s <- readFile "t.txt"
Run Code Online (Sandbox Code Playgroud)
执行此操作时:
length s 所有内容将被读取并且文件将被关闭).readFile就会关闭与此调用相关联的文件句柄(自动).我的第三个陈述是否正确?那么,我可以在readFile不关闭文件句柄的情况下调用吗?只要我没有消耗(访问)整个结果字符串,句柄是否会保持打开状态?
编辑
以下是有关我的疑虑的更多信息.假设我有以下内容:
foo :: String -> IO String
foo filename = do
s <- readFile "t.txt"
putStrLn "File has been read."
return s
Run Code Online (Sandbox Code Playgroud)
当putStrLn执行时,我会(直觉地)期望
s包含文件的全部内容t.txt,如果不是这样的话:
s时候putStrLn执行包含什么?putStrLn执行时文件句柄处于什么状态?putStrLn执行的时间s不包含文件的全部内容,实际上何时会读取此内容,何时关闭文件?似乎Haskell的IO相对较慢.
例如,将Haskell与Python进行比较
#io.py
import sys
s=sys.stdin.read()
sys.stdout.write(s)
Run Code Online (Sandbox Code Playgroud)
,
-- io.hs
main = do
s <- getContents
putStr s
Run Code Online (Sandbox Code Playgroud)
他们的表现(gen.py将512k数据写入stdout):
Python版本:
$ time python gen.py | python io.py > /dev/null
real 0m0.203s
user 0m0.015s
sys 0m0.000s
Run Code Online (Sandbox Code Playgroud)
Haskell版本:
$ time python gen.py | runhaskell io.hs > /dev/null
real 0m0.562s
user 0m0.015s
sys 0m0.000s
Run Code Online (Sandbox Code Playgroud)
似乎Haskell的价格要低得多.我的测试有问题吗?或者它只是Haskell的固有问题?
谢谢.
我想处理一个太大而无法读入内存的二进制文件。目前,我使用ByteString.Lazy.readFile来流传输字节。我认为使用流媒体包来提高程序速度是一个好主意。但是,的文档显示readFile:
readFile :: FilePath -> (Stream (Of String) IO () -> IO a) -> IO a使用类型为'Stream(Of String)IO()-> IO a'的函数读取文件的行,以将流转换为'IO a'类型的值。
所以streaming包只读取ASCII文本文件?我可以使用该程序包以字节为单位读取二进制文件吗?
getText = do
c <- getChar
s <- getText
return (c : s)
main = do
s <- getText
putStr s
Run Code Online (Sandbox Code Playgroud)
我期望看到的是每次按下'Enter'后输入线都会被回显.但没有任何回应...(我知道这是一个无限循环)它似乎不会" return"直到它上面的所有"IO"执行....
但是,以下代码:
main = do
s <- getContents
putStr s
Run Code Online (Sandbox Code Playgroud)
它在输入后立即显示该行.
鉴于功能getChar,我可以写一个getText表现得像getContents?
hGetContents返回一个惰性String对象,该对象可用于纯函数代码以从文件句柄中读取.如果在读取此延迟字符串时发生I/O异常,则会以静默方式关闭基础文件句柄,并且不会向延迟字符串添加其他字符.
如何检测此I/O异常?
作为一个具体的例子,考虑以下程序:
import System.IO -- for stdin
lengthOfFirstLine :: String -> Int
lengthOfFirstLine "" = 0
lengthOfFirstLine s = (length . head . lines) s
main :: IO ()
main = do
lazyStdin <- hGetContents stdin
print (lengthOfFirstLine lazyStdin)
Run Code Online (Sandbox Code Playgroud)
如果在读取文件的第一行时发生异常,则此程序将打印字符数,直到发生I/O异常.相反,我希望程序崩溃并使用适当的I/O异常.如何修改此程序以具有该行为?
编辑:仔细检查hGetContents实现后,似乎不会忽略I/O异常,而是通过调用纯函数代码冒泡到触发评估的任何IO代码,然后有机会处理它.(我以前没有意识到纯函数代码会引发异常.)因此,这个问题是一个误解.
旁白:如果通过经验验证这种特殊行为,那将是最好的.不幸的是,很难模拟低级I/O错误.
我试图在Haskell中正确读取文件,但我似乎得到了这个错误.
***例外:neo.txt:openFile:资源忙(文件被锁定)这是我的代码.
import Data.Char
import Prelude
import Data.List
import Text.Printf
import Data.Tuple
import Data.Ord
import Control.Monad
import Control.Applicative((<*))
import Text.Parsec
( Parsec, ParseError, parse -- Types and parser
, between, noneOf, sepBy, many1 -- Combinators
, char, spaces, digit, newline -- Simple parsers
)
Run Code Online (Sandbox Code Playgroud)
这些是电影领域.
type Title = String
type Director = String
type Year = Int
type UserRatings = (String,Int)
type Film = (Title, Director, Year , [UserRatings])
type Period = (Year, Year)
type Database = [Film]
Run Code Online (Sandbox Code Playgroud)
这是所有类型的解析,以便从文件中正确读取
-- …Run Code Online (Sandbox Code Playgroud) 我将尽可能简短地说明这个问题.
我希望在向命令行输入三个字符后完成此计算.但事实并非如此.我很好奇为什么会这样.
return . take 3 =<< sequence (repeat getChar)
Run Code Online (Sandbox Code Playgroud)
编辑:
我应该补充一点,我试图将选择与生成分开,因此希望产生一个我选择的无限字符流.
我有一小部分代码表现不像我预期的那样:
tempTest = do
(_,tHand) <- openTempFile "." "tempTest.txt"
hPutStr tHand "Test!"
read <- hGetContents tHand
putStrLn read
Run Code Online (Sandbox Code Playgroud)
它正确地写入文件,但当被要求将内容显示到屏幕时,它只显示一个新行(没有任何引号).鉴于这是多么简单,我假设我误解了getContents的工作原理.根据我的理解,它以块的形式读取文件,而不是一次读取所有文件; 这可以防止内存被正在读取的大量文件填满.为什么"阅读"没有从hGetContents接收任何内容?这里的任何帮助都将不胜感激.