【问题标题】:An example of using Data.Map in Haskell在 Haskell 中使用 Data.Map 的示例
【发布时间】:2014-01-01 19:06:16
【问题描述】:

我是 Haskell 的新手,我需要定义一个空的 Data.map 并通过使用插入函数并更新值来为其键分配一个“整数列表”(例如 [1,2,3])。然后查找键值。

到目前为止我尝试过的是:

import qualified Data.Map

foo num =
    let
        my_map  = Data.Map.empty

        new_map = bar my_map  num 1

    in
        Data.Map.lookup 1 new_map


bar my_map num c =
    if c > num then my_map
    else
        Data.Map.insert  c  [c] my_map
        bar my_map  num  c+1

此代码不起作用。

你能举个简单的例子吗?

【问题讨论】:

  • 到目前为止你尝试过什么?您正在寻找的大部分功能都可以直接作为 Data.Map 中的函数使用。
  • @J.Abrahamson 我认为从 OP 的帖子中可以清楚地看出她/他尝试了什么。
  • @talonx 这个问题是在我发表评论后 3 天编辑的。

标签: haskell hashtable


【解决方案1】:

看看Data.Map in base.

虽然这个包确实导出 empty 来创建地图,但使用它更容易构建一个

myMap = Data.Map.fromList [(1,"hello"), (3,"goodbye")]

fromList 获取 (key,value) 元组的列表,并创建一个映射。如果您在构建时就知道所需的所有键值对。

您可以使用(!) 或Data.Map.lookup 访问元素

【讨论】:

  • 为什么使用fromList 比使用empty 更好?
  • 对不起,我错了。我认为 fromList 做了一些更聪明的事情,但它只是将一堆插入串在一起。
【解决方案2】:

您的代码表明您存在一些误解。

import qualified Data.Map

foo num =
    let
        my_map  = Data.Map.empty

        new_map = bar my_map  num 1

    in
        Data.Map.lookup 1 map

我看到你使用了map 变量,但可能意味着new_map。由于 Haskell 定义了一个名为 map 的函数,编译器会告诉你一个类型错误。纯粹为了提高可读性,减少空格和添加类型签名会有很大帮助。

-- foo takes an `Int` and produces a Maybe [Int].
foo :: Int -> Maybe [Int]
foo num =
    let my_map  = Data.Map.empty
        new_map = bar my_map  num 1
    in Data.Map.lookup 1 new_map

现在让我们看看bar:

bar my_map num c =
    if c > num then my_map
    else
        Data.Map.insert  c  [c] my_map
        bar my_map  num  c+1

这里有几个问题:

  • then 和 else 关键字应该在同一列中
  • 你的c+1 应该有括号
  • 您需要使用 let 绑定 - 这不是可变映射,因此通过插入您实际上是在创建具有新值的新映射。

所以:

bar :: Map Int [Int] -> Int -> Int -> Map Int [Int]
bar my_map num c =
    if c > num
      then my_map
      else let my_new_map = Data.Map.insert  c  [c] my_map
           in bar my_new_map  num  (c+1)

【讨论】:

  • 关于new_map,是的,你是对的。这是我现在修复的拼写错误。您将列表 [1] 作为函数“bar”的第二个参数传递,并且在“bar”中,您将列表与 num (c > num) 进行比较,这怎么可能?我打算对 Data.Map 拥有的是将 Key 类型作为“整数”,将 Value 类型作为“整数列表”。谢谢。
  • 对,foo 正在传递 [Int] 而 bar 只接受 Int - 我的错误。并不是bar 将Int 与[Int] 进行比较(注意该类型总是只接受Int),而是我在foo 中误用了bar。我对foo 进行了快速调整,使其仅通过Int 而不是Ints 的单例列表。
【解决方案3】:

人们通常使用这个样板导入Data.Map 模块:

import Data.Map (Map)
import qualified Data.Map as Map

这个想法是,由于模块中的许多名称与Prelude 和其他模块发生冲突,因此您希望将它们用作限定名称——而不是Map 类型本身。第二行中的as Map 位使您不必输入太多内容——您只需输入Map.map、Map.empty 等即可。

现在,构建地图的最简单和最常用的方法是使用模块中的fromList 函数。这将从键/值对列表中构造一个Map:Map.fromList :: Ord k => [(k, v)] -> Map k v。要构建这个键/值对列表,您可以使用 Haskell 列表处理函数的全部功能,如下例所示:

myMap :: Integer -> Map Integer [Integer]
myMap n = Map.fromList (map makePair [1..n])
    where makePair x = (x, [x])

GHCI 中的示例输出:

>>> myMap 3
fromList [(1,[1]),(2,[2]),(3,[3])]

请注意,Map 类型甚至会将其自身打印为将重构它的 fromList 调用。为什么?因为同样,这个函数确实是构建Map 的最常用方法。

相比之下,您在代码中所做的是尝试编写一个命令式循环,该循环连续地用一次一个条目来扩充初始空映射。 Haskell 等价的循环是列表函数。在我的版本中,我使用了以下内容:

  1. [1..n]——生成从1到n的整数列表。
  2. map——对列表的每个元素应用一个函数。
  3. Map.fromList——从键/值对列表构建Map。

为了进一步证明这一点,如果您查看the source code for Map.fromList,它实际上是使用列表折叠函数定义的。

我的建议是:在处理 Map 之前,先学习列表和 Data.List 模块。特别是:

  1. 了解那里有哪些可用的功能以及如何操作。
  2. 研究该模块中的foldr 函数——如何使用它以及如何编写它。
  3. 了解如何根据foldr 编写自己的map、filter 和find 版本。

【讨论】:

  • 哇!这很棒!现在,我知道在 Haskell 中使用 Data.Map 和这种优化方式来做我想做的事情。
  • 顺便问一下,你知道我的代码有什么问题吗?
【解决方案4】:

这是一个演示此功能的小程序:

module Main where

import qualified Data.Map as M

main = do
    let emptyMap = M.empty
        mapWithKeys = M.insert 5 "Four" emptyMap
        mapWithKeys' = M.insert 5 "Five" mapWithKeys
    putStrLn $ mapWithKeys' M.! 5

程序会用键 5 插入“Four”,然后将值更新为“Five”,最后查找并打印出来。

【讨论】:

  • 每个绑定都不需要let。只需一个,加上适当的缩进,就足够了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-25
  • 1970-01-01
  • 1970-01-01
  • 2023-03-20
  • 1970-01-01
  • 1970-01-01
  • 2019-03-21
相关资源
最近更新 更多