Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
String 通过测试环遍历_String_Haskell_Traversal - Fatal编程技术网

String 通过测试环遍历

String 通过测试环遍历,string,haskell,traversal,String,Haskell,Traversal,我在读一篇文章,有人试图在Haskell中执行一个简单的字符串处理操作,但得到的代码相当慢。他的(最终,下一页a方式)代码的一些问题: 整个文件一次读入 他使用相对昂贵的isSpace,然后将生成的程序与只考虑简单空格和换行符的C代码进行比较 他使用scanl的方式看起来非常不友好,在不必要的情况下使用计算字符作为每个步骤的输入 我认为,最自然的方法是使用lazyByteStrings(就像他以前的一些尝试那样),并放弃scanl,转而使用zipWith',将字符串压缩到一个字符串上:zipWi

我在读一篇文章,有人试图在Haskell中执行一个简单的字符串处理操作,但得到的代码相当慢。他的(最终,下一页a方式)代码的一些问题:

  • 整个文件一次读入
  • 他使用相对昂贵的
    isSpace
    ,然后将生成的程序与只考虑简单空格和换行符的C代码进行比较
  • 他使用
    scanl
    的方式看起来非常不友好,在不必要的情况下使用计算字符作为每个步骤的输入
  • 我认为,最自然的方法是使用lazy
    ByteString
    s(就像他以前的一些尝试那样),并放弃
    scanl
    ,转而使用
    zipWith'
    ,将字符串压缩到一个字符串上:
    zipWith f s(cons)

    问题 通过testring压缩一个懒惰的
    本身的移位版本不会利用两个字符串之间的关系。它对块的结尾和字符串的结尾执行许多不必要的检查。我确信我可以编写一个专门的函数,用两个字符的“窗口”遍历
    ByteString
    ,我确信我是一个比我更好的程序员,可以编写一个利用块表示细节的函数,但我更愿意找到一种更易访问的方法。有什么想法吗


    编辑添加:另一种方法可能是使用
    foldr
    生成
    ByteString
    builder,遵循相同的一般方法,但使用(希望是未装箱的)元组来避免数据依赖性;我不确定我是否完全理解这些构建器或它们的效率。

    惰性I/O可能是一个问题,但这是完成这项小任务的最简单方法

    import Data.Text.Lazy (toTitle)
    import Data.Text.Lazy.IO (readFile, putStr)
    import Prelude hiding (readFile, putStr)
    
    main = readFile "file" >>= putStr . toTitle
    
    实际上,它将花费时间正确地执行Unicode(分词和标题大小写),但这可能是您想要的。如果您想避免懒惰的I/O,那么pipes文本包应该生成一些不大的内容

    如果你真的想把所有的东西都当作ASCII码,并且假设所有的单词都以字母开头,我仍然认为惰性I/O在这里是一个胜利,但是它有点复杂

    import Data.Bits (.&.)
    import Data.ByteString.Lazy (ByteString, cons', putStrLn, readFile, uncons)
    import Data.ByteString.Lazy.Char8 (lines, unlines, unwords, words)
    import Data.Word (Word8)
    import Prelude hiding (putStrLn, readFile, lines, unlines, unwords, words)
    
    capitalize :: ByteString -> ByteString
    capitalize word = case uncons word of
      Just (h, t) -> cons' (h .|. complement 32) t
      Nothing     -> word
    
    main = readFile "file"
       >>= putStrLn . unlines
                    . map (unwords . map capitalize . words)
                    . lines
    
    同样,避免惰性I/O就像使用管道bytestring一样简单


    这篇文章还有一个reddit线程,他们似乎从构建器抽象中获得了很好的性能,加上一种更好的上框方式。构建器抽象可能会比我的bytestring hack更快,因为它在写入输出数据之前会更好地将其分块。

    我将使用以下导入

    import Data.Char 
    import Data.List           
    import qualified Data.Text.Lazy as T                      
    
    import Criterion.Main
    import Test.QuickCheck
    
    与博客文章中的参考实现相比,我获得了惊人的速度:

    capitalize :: T.Text -> T.Text
    capitalize = T.tail . T.scanl (\a b -> if isSpace a then toUpper b else b) ' '
    
    使用
    mapAccumL
    要快得多。以下是
    字符串
    文本
    版本

    {-# INLINE f #-}
    f a b = (b, if isSpace a then toUpper b else b)
    
    string :: String -> String
    string = snd . mapAccumL f ' '
    
    text :: T.Text -> T.Text
    text = snd . T.mapAccumL f ' '
    
    首先,让我们确保优化是有效的

    λ. quickCheck $ \xs -> 
        capitalize (T.pack xs) == text (T.pack xs)
    +++ OK, passed 100 tests.
    
    现在,对于来自
    标准的一些基准测试结果
    ,在Lorem Ipsum的3.2m文件上运行每个函数。这是我们的参考速度

    benchmarking reference
    collecting 100 samples, 1 iterations each, in estimated 56.19690 s
    mean: 126.4616 ms, lb 126.0039 ms, ub 128.6617 ms, ci 0.950
    std dev: 4.432843 ms, lb 224.7290 us, ub 10.55986 ms, ci 0.950
    
    String
    仅比优化的参考
    Text
    版本慢约30%,而使用
    Text
    mapAccumL
    版本的速度几乎是优化后的两倍

    benchmarking string
    collecting 100 samples, 1 iterations each, in estimated 16.45751 s
    mean: 165.1451 ms, lb 165.0927 ms, ub 165.2112 ms, ci 0.950
    std dev: 301.0338 us, lb 250.2601 us, ub 370.2991 us, ci 0.950
    
    benchmarking text
    collecting 100 samples, 1 iterations each, in estimated 16.88929 s
    mean: 67.67978 ms, lb 67.65432 ms, ub 67.72081 ms, ci 0.950
    std dev: 162.8791 us, lb 114.9346 us, ub 246.0348 us, ci 0.950
    
    但还有更容易的收获
    Data.Char.isSpace
    以其性能问题而闻名,因此让我们试试快速
    Data.Attoparsec.Char8.isSpace
    。我们的
    quickcheck
    测试不会通过,但性能非常好

    benchmarking string/atto
    collecting 100 samples, 1 iterations each, in estimated 12.91881 s
    mean: 129.2176 ms, lb 129.1328 ms, ub 129.4941 ms, ci 0.950
    std dev: 705.3433 us, lb 238.2757 us, ub 1.568524 ms, ci 0.950
    
    benchmarking text/atto
    collecting 100 samples, 1 iterations each, in estimated 15.76300 s
    mean: 38.63183 ms, lb 38.62850 ms, ub 38.63730 ms, ci 0.950
    std dev: 21.41514 us, lb 15.27777 us, ub 33.98801 us, ci 0.950
    
    我们现在比原始参考快了约3倍。相比之下,非常快的python代码(只是调用C)


    使用
    words
    unwords
    30ms

    中撕开文本文件,丢失制表符等等,到处检查空单词确实看起来像一个丑陋的黑客。问题的一部分是,各种“split-on”函数通常使用分隔符,这并不总是正确的。半相关的是,在行/未行中嵌套单词/未用词在这里很难看,因为在问题描述中对空格的处理和对新行的处理之间没有真正的区别。
    print open('lorem.txt').read().title()