【问题标题】:Create HashTable in Haskell在 Haskell 中创建哈希表
【发布时间】:2013-12-10 15:32:24
【问题描述】:

我想在 Haskell 中创建一个 HashTable,在里面插入哈希值并在这个 HashTable 中查找。

我找到了这个documentation ,但我刚开始使用 Haskell,因此我真的不知道如何使用这些功能。

如果你们中的一些人可以给我看几行代码就完美了。

【问题讨论】:

  • 我同意@Ingo,学习语言,然后尝试制作一个HashTable,因为它是一个相当复杂的数据结构。如果您不了解 FP 的语言和概念,那么在像 Haskell 这样的语言中让它变得高效也有点困难。查看 Learn You a Haskell 教程,然后学习 Real World Haskell(两者都可以在线免费获得),它们一起很好地介绍了该语言。

标签: haskell hashtable


【解决方案1】:

我赞同 Ingo 关于从更简单的事情开始的评论。不过,我会稍微详细地分解一些事情。

首先,我假设您已经安装了最新的Haskell Platform。在平台的网站上有a page with collected documentation for the libraries included with it。任何不在该页面中的库都需要您单独安装。

该平台确实包含Data.HashTable,因此您无需安装任何东西,但如果您查看the latest Platform's documentation on it,您会发现它已被弃用并且很快就会被删除。所以我不会使用那个模块。

Haskell 平台带有地图/字典数据结构的两个最流行的 Haskell 实现:

  • Data.Map。 (大部分文档都在Data.Map.Lazy 中。)这将映射实现为一种平衡搜索树,这意味着键需要是有序类型——实现Ord 类的类型。很多内置的 Haskell 类型已经实现了这个类,所以这可能是你最开始的选择。
  • Data.HashMap 模块层次结构,有两个变体; Data.HashMap.Lazy 将是一个很好的起点。这将映射实现为一种哈希表,因此键需要实现Hashable 类。此类较新且不如 Ord 受欢迎,因此您可能经常需要为您的键类型实现此类。

所以Data.Map 是更容易使用的类型。但是要有效地使用它,除了最基本的语言结构之外,您还需要了解一些东西:

  1. 如何在源文件中导入模块。
  2. 如何使用限定导入——Data.Map 的函数名称与 Haskell 中的许多内置函数名称冲突,这需要一些特殊的语法。
  3. 如何将模块加载到 ghci 解释器中。
  4. 如何编译使用Data.Map 所在的containers 库的项目(使用cabal 工具)。

一旦完成,构建映射的最简单方法是使用键/值对列表:

module MyModule where

import Data.Map (Map)             -- This just imports the type name
import qualified Data.Map as Map  -- Imports everything else, but with names 
                                  -- prefixed with "Map." (with the period).

-- Example: make a Map from a key/value pair
ages :: Map String Integer
ages = Map.fromList [("Joe", 35), ("Mary", 37), ("Irma", 16)]

关于如何使用地图的几个例子:

-- Example: look up somebody and return a message saying what their age is.
-- 'Nothing' means that the map didn't have the key.
findAge :: String -> String
findAge name = case Map.lookup name ages of
                 Nothing  -> "I don't know the age of " ++ name ++ "."
                 Just age -> name ++ " is " ++ show age ++ " years old."

-- Example: make a map with one extra entry compared to `ages` above.
moreAges :: Map String Integer
moreAges = Map.insert "Steve" 23 ages

-- Example: union of two maps.
evenMoreAges :: Map String Integer
evenMoreAges = Map.union moreAges anotherMap
    where anotherMap = Map.fromList [("Metuselah", 111), ("Anuq", 3)]

【讨论】:

    【解决方案2】:

    作为对 Ingo 答案的补充,请考虑使用纯函数 Data.Map

     import qualified Data.Map as M
    
     myMap :: M.Map Int String
     myMap = M.fromList $ zip [1..10] ['a'..'j']
    
     insertedMap :: M.Map Int String
     insertedMap = M.insert 11 "fizzbuzz" oldMap
    
     at11 :: Maybe String
     at11 = M.lookup 11 insertedMap
    

    然后您可以使用M.lookupM.insert 和许多其他功能来修改/查询地图。这种数据结构也是纯粹的功能/持久性(注意 IO 在类型中是如何无处的)。这意味着我们可以做类似的事情

      let newMap = M.insert key val oldMap
      in M.union oldMap otherMap
    

    看看我们如何在插入一些东西后仍然可以使用旧版本的地图?这就是“持久性”,我们永远不会破坏旧版本的数据结构。

    【讨论】:

    • 给出实现后,myMap 将具有签名myMap :: M.Map Int Char
    【解决方案3】:

    为了避免有人称 Haskell 社区傲慢,这里是您需要的第一个函数的简短分解:

    new :: (key -> key -> Bool) -> (key -> Int32) -> IO (HashTable key val)
    

    这告诉我们以下内容:要为特定键类型key 创建一个 HashTable,您需要传递一个检查键是否相等的函数,以及一个计算键哈希值的函数。因此,如果 eqhashit 是所需的函数,则如下:

    new eq hashit
    

    在 IO-Monad 中为您提供一个空的 HashTable。

    更简单的方法是使用预定义的散列函数之一从列表中创建散列表:

     fromList hashInt [(42, "forty-two"), (0, "zero")]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-20
      • 1970-01-01
      • 1970-01-01
      • 2010-09-06
      • 2015-09-24
      • 1970-01-01
      • 1970-01-01
      • 2013-12-20
      相关资源
      最近更新 更多