代码之家  ›  专栏  ›  技术社区  ›  Jonathan

如何在Haskell中编写函数?

  •  2
  • Jonathan  · 技术社区  · 7 年前

    好吧,这真的很愚蠢,但我不明白如何在Haskell中编写主do区域之外的函数。我有一个脚本,它读取一个文件,找到该文件中某个单词的位置,然后将结果放入10个箱子中:

    {-# LANGUAGE OverloadedStrings #-}
    import Data.Text as T
    import Data.Text.Internal.Search
    import qualified Data.Vector as V
    import qualified Data.Text.IO as TIO
    import Statistics.Sample.Histogram
    
    main :: IO ()
    main = do
      rawText <- TIO.readFile "jane-eyre.asciidoc"
      let locations = indices "morning" rawText
          vec =  V.fromList $ Prelude.map fromIntegral locations
          (_, sizes) = histogram 10 vec
          histSizes = V.toList sizes
      print histSizes
    

    这很好用。但是现在,当我试图把一些东西抽象成函数时,我做不到 rawText 转换为其自身的功能,例如:

    {-# LANGUAGE OverloadedStrings #-}
    import Data.Text as T
    import Data.Text.Internal.Search
    import qualified Data.Vector as V
    import qualified Data.Text.IO as TIO
    import Statistics.Sample.Histogram
    
    rawText = TIO.readFile "jane-eyre.asciidoc"
    
    main :: IO ()
    main = do
      let locations = indices "morning" rawText
          vec =  V.fromList $ Prelude.map fromIntegral locations
          (_, sizes) = histogram 10 vec
          histSizes = V.toList sizes
      print histSizes
    

    我得到:

     • Couldn't match expected type ‘Text’ with actual type ‘IO Text’
     • In the second argument of ‘indices’, namely ‘rawText’
    

    下面是我尝试的另一个例子:

    {-# LANGUAGE OverloadedStrings #-}
    import Data.Text as T
    import Data.Text.Internal.Search
    import qualified Data.Vector as V
    import qualified Data.Text.IO as TIO
    import Statistics.Sample.Histogram
    
    hist text = V.toList sizes where
      locations = indices "morning" text
      vec =  V.fromList $ Prelude.map fromIntegral locations
      (_, sizes) = histogram 10 vec
    
    main :: IO ()
    main = do
      rawText <- TIO.readFile "jane-eyre.asciidoc"
      histSizes <- hist rawText
      print histSizes
    

    但我得到:

    • Couldn't match type ‘[]’ with ‘IO’
      Expected type: IO Integer
        Actual type: [Integer]
    • In a stmt of a 'do' block: histSizes <- hist rawText
      In the expression:
        do { rawText <- TIO.readFile "jane-eyre.asciidoc";
             histSizes <- hist rawText;
             print histSizes }
      In an equation for ‘main’:
          main
            = do { rawText <- TIO.readFile "jane-eyre.asciidoc";
                   histSizes <- hist rawText;
                   print histSizes }
    
    3 回复  |  直到 7 年前
        1
  •  11
  •   leftaroundabout    7 年前

    要回答标题问题“如何编写函数”: 你只是把它放在你的档案里 .

    ...但这显然不是你想问的。事实是, rawText 根本不是一个函数。在您的工作程序中,它只是一个文本值。而且,由于它依赖于外部数据,它实际上不是一个常数,而是一个 变量 . 全局变量是一个坏主意(在所有编程语言中),所以我不明白为什么要把它放在全局范围内。

    然而,你所能做的就是把过去 检索 模块范围中的文本:

    fetchRawText :: IO Text
    fetchRawText = TIO.readFile "jane-eyre.asciidoc"
    
    main :: IO ()
    main = do
      rawText <- fetchRawText
      let locations = indices "morning" rawText
          vec =  V.fromList $ Prelude.map fromIntegral locations
          (_, sizes) = histogram 10 vec
          histSizes = V.toList sizes
      print histSizes
    

    fetchRawText 现在其他语言可能会称之为“无参数函数”,如

    def fetchRawText():
        return open("jane-eyre.asciidoc").read()
    

    ...除了Haskell没有 无参数函数 (这些只是值),它表示了这样一个事实,即通过在 IO 单子。

    然而,一个可能更明智的方案是:

    locationHistogramSizes :: Text -> [Double]
    locationHistogramSizes rawText = V.toList sizes
     where locations = indices "morning" rawText
           vec =  V.fromList $ Prelude.map fromIntegral locations
           (_, sizes) = histogram 10 vec
    
    main :: IO ()
    main = do
       rawText <- TIO.readFile "jane-eyre.asciidoc"
       print $ locationHistogramSizes rawText
    

    在这里,我将所有有趣的逻辑与IO部分完全分离。除其他优点外,这使得单元测试更加容易。

        2
  •  5
  •   Carcigenicate    7 年前

    即使你已经把它从 main , rawText 仍然是一个不纯的值,但您试图在纯上下文中使用它。在使用Monad之前,您仍然需要将其取出:

    main = do
      raw <- rawValue 
      let locations = indices "morning" raw
    

    如果您尝试给它一个签名,这可能会更加明显(给事物明确的签名几乎总是一个好主意)。您是否写过:

    rawText :: Text
    rawText = TIO.readFile "jane-eyre.asciidoc"
    

    它会告诉您签名不正确,这会缩小问题的范围。

    因为代码很简单,去掉它不会带来太多收益。你最好把家里所有的都拿走 主要的 现在,让所有这些都成为自己的功能;如果它在概念上有意义的话。

        3
  •  2
  •   Silvio Mayolo    7 年前

    束缚 <- 语法不是常规赋值。它去糖化为一元结合。 indices 无法获取 IO 单子。任何 let 绑定可以很容易地抽象为函数,因为它们本质上是重命名的。然而 TIO.readFile 线必须绑定在 do 符号尝试抽象 允许 绑定到函数以供练习。但对于初学者来说 符号和 <- 绑定是一种神奇的东西,一旦你以后理解了单子,你就会担心它们到底意味着什么。