Arduino哈希表/字典

16

我正在尝试在Arduino Mega 2560上使用哈希表或字典。 我的目标是实现类似以下的功能:

dictionary[ENGLISH]["ACCOUNT"] = "Account";
dictionary[ENGLISH]["DATE_AND_TIME"] = "Date and Time";
dictionary[ENGLISH]["IDLE"] = "Idle";
dictionary[ENGLISH]["Language"] = "Languge"
dictionary[ENGLISH]["MAIN_MENU"] = "Main Menu";
dictionary[ENGLISH]["PRESCRIPTION"] = "Prescription";
dictionary[ENGLISH]["SETTINGS"] = "Settings";
dictionary[ENGLISH]["SOUND"] = "Sound";

其中ENGLISH实际上是一个常量0,我还会有SPANISH和FRENCH(分别为1和2),也就是一个包含三个字典元素的数组。

在首次谷歌搜索中,我找到了一个模拟C++ STL库的链接,但它在我的Arduino 1.0.3上根本无法工作。我想知道是否有人对于在arduino中使用maps /哈希表有替代方法,或者有方法使上述库正常工作。

为了解释我的情况,我正在通过触摸屏模拟菜单系统,它必须接受三种语言(用于按钮)。所选的语言可以在EEPROM中找到,并将其保存在变量'lang'中,当我需要向屏幕打印内容时,我会执行类似以下的操作:

    screen.print(dictionary[lang]["SOUND"], CENTER, 23);

根据用户选择的“lang”语言,相应地打印输出,这是理想情况。


1
std::map<int, std::map<std::string, std::string> > 怎么样? - user529758
1
目前我所知道的是,Arduino没有标准库。上面提到的那个库对我来说不起作用(我理解你的建议:不要制作字典数组,而是制作一个字典,其中 'lang' 是字典中的第一个元素,但我现在无法使用映射)。 (我澄清了我的问题!) - Robert Cardona
每种语言您将有多少个条目? - angelatlarge
也许是这个:http://playground.arduino.cc/Code/HashMap - spring
@user529758 Arduino没有stdlib实现。std命名空间中没有任何内容。 - Pharap
3个回答

28
我认为在这里使用哈希表可能是不必要的,而且在这个平台上避免使用它也有很好的理由。
为什么哈希表可能是不必要的
通常在这种情况下,没有必要使用字符串键,因为键仅在您的代码内部可见,并且从程序外部不与键交互。因此,通常的解决方案是使用(伪)整数键,形式为#define,由预处理器处理而不是您的程序:
#define kWORDIDX_LANGUAGE   1
#define kWORDIDX_SOUND      2
#define kWORDIDX_MAINMENU   3
#define kWORDIDX_SPAGHETTI  4
...
dictionary[ENGLISH][kWORDIDX_SOUND] = "Sound";
...

您可以像这样访问您的字典条目:Sreen.print(dictionary[lang][kWORDIDX_SOUND], CENTER, 23);或类似的方式。

这种方法的优点是:

  • 节省内存空间:不使用字符串键
  • 节省处理时间:虽然哈希表访问在技术上是O(1),但仍涉及计算哈希的常数因素。数组访问更快。
  • 您的代码更不容易出错:如果您使用字符串访问(这是一种魔术数字形式)拼写错误,则会得到哈希表未命中。如果您拼错了其中一个#define的键,您将获得编译错误,这正是您想要的

为什么您不想在Arduino上使用哈希表

Arduino是一个受限制的平台:它的内存非常有限。使用真正的哈希映射的问题如下:

  • 字符串占用的内存空间比整型变量(通常)要多。使用被编译器转换为整数字面值的#define键,每个键使用1、2或4个字节(取决于编译器设置),而每个字符串键需要strlen(s) + 1个内存空间来存储。这个空间实际上被使用了两次:一次在Flash中(那是变量初始化的地方),一次在SRAM中。
  • 哈希映射数据结构本身占用更多的内存:在开放地址法中,哈希映射中可能有未使用的条目开销,而在分离链接法中,哈希映射中的链表可能占据更多的空间。由于您的数据是只读的,因此您不需要这种开销,因为哈希表将不会添加任何内容。
  • 人们用来节省Arduino内存的一个技巧是仅将只读数据(如字符串)存储在程序存储器中(而不是SRAM),使用PROGMEM关键字。如果您使用哈希映射构建字典,则无法使用此方法。另一方面,如果按照上述#define类型的索引方案,将所有语言字符串存储为PROGMEM字符串将非常容易。

写得非常好!您能详细说明一下我如何根据上面概述的方法使用 PROGMEM 吗?我以前在绘制屏幕时存储精灵时使用过它,但从未像这样使用过。谢谢! - Robert Cardona
最好看一下这里的“字符串数组”部分(http://www.arduino.cc/en/Reference/PROGMEM):其中有一个完整的`PROGMEM`字符串表的示例。只是,你需要使用`strcpy_P(buffer, (char*)pgm_read_word(&(string_table[kMY_DEFINED_INDEX])));(或类似的代码),而不是strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i])));`(或类似的代码)。如果这还不能解决问题,也许可以在这里发布另一个问题:这似乎是一个有点不同的主题。你觉得呢? - angelatlarge
太好了!很高兴听到这个消息。 - angelatlarge
为什么我需要哈希表:如果数据被传递到Arduino并且您需要以灵活的方式解析和存储它,那么某种字符串-字符串字典(=哈希映射)将非常适合这项工作。虽然我通常非常赞同您的陈述,请记住您将无法避免所有类型的用例。 - Regis May

11
您可以使用数据结构来定义一个字典:
typedef struct { 
  uint8_t lang;
  char* sound;
  char* value;
} langDictionary;

然后您可以使用要使用的值定义该结构的数组:

const langDictionary myDictionaryArr[] {
    {0, "ENGLISH", "Settings"},
    {1, "SPANISH", "Ajustes"},
    {2, "FRENCH", "Paramètres"}
};

最后,您可以使用数组来搜索属性:

void setup() {
  Serial.begin(115200);
  for(uint8_t i = 0; i < sizeof(myDictionaryArr)/sizeof(langDictionary); ++i) {
      Serial.println(myDictionaryArr[i].value); //Prints the values: "Settings", "Ajustes" and "Paramètres"
  }
}

这是一个不错的技巧,当你想模拟字典时,其中键是数组的索引。 - Georgi Peev

4

使用HashMap库时出现错误,缺少WProgram.h - Ciasto piekarz
1
好的,请尝试删除include语句或将文件名更改为Arduino.h - jitter
@clankill3r,您能否请更具体一些? - jitter
3
操场上有一张提示牌: 提示:这不是一个真正的哈希表,而是一个键值映射。在该“哈希表”中搜索是通过遍历所有条目完成的,而不是通过哈希密钥查找。 一种在O(N)中执行查找的“哈希表”,即完全扫描数据的过程,并非真正的哈希表,而是掩盖了的数组。 - BigMan73
1
我发现你不能重载这个Arduino哈希映射来使用自定义结构体,它只能存储整数 :( - doublejosh
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接