Haskell - 希望文件中的全局变量不会改变

use*_*194 2 haskell unsafe-perform-io

type Anagrams = Map String [String]

buildAnagrams :: IO Anagrams
buildAnagrams = do
          list <- readCSV "mydict.csv"
          return $ foldr f Map.empty list
            where
             f :: String -> Anagrams -> Anagrams
             f s = Map.insertWith (++) (sort s) [s]
Run Code Online (Sandbox Code Playgroud)

我有这个函数,它构建一个映射来从字典文件中查找anagrams,该文件不会改变.我希望将地图作为全局变量,因为它需要被其他函数使用.目前函数在buildAnagrams上使用unsafePerformIO,但我知道不建议这样做.整个程序也很慢,因为它多次构建地图.必须有更好的方法来做到这一点?

chi*_*chi 5

如果要将anagram数据保存在外部文件中,则需要IO才能读取它.所以,你有几个选择:

  • 传递anagram数据作为所有函数的额外参数
  • 使用隐式参数传递相同的数据
  • 将所有内容包装在Readermonad中(或添加ReaderT变换器)
  • 使用unsafePerformIO,与通常的警告

关于unsafePerformIO:如果您完全确定您阅读的文件不会改变,这是其"安全"用例之一.虽然不是真的推荐或优雅,但它可以解决这个问题.为避免多次重读数据,您应该使用

{-# NOINLINE allAnagrams #-}
allAnagrams :: Anagrams
allAnagrams = unsafePermformIO buildAnagrams
Run Code Online (Sandbox Code Playgroud)

这样外部文件只会被读取一次.

另一种选择是将您的外部文件包含在Haskell源中.这可以通过几种方式完成,包括

  • 普通的元编程:你编写一个程序,它接收你的外部文件并将其转换为.hs文件,该文件声明了一个大的字符串向量
  • 模板Haskell:与元编程相同的效果,但更优雅一点

  • 在Template Haskell方面,一个简单的方法是使用[file-embed](http://www.stackage.org/package/file-embed)包. (3认同)