如何使用数组作为值在 Pascal 中创建关联数组
How to create an associative array in Pascal using arrays for the values
我有这个文件:
Bulgaria = Bulgarian
Croatia = Croatian
Austria = Croatian
Czech Republic = Czech
Slovakia = Czech
Denmark = Danish
Germany = Danish
Belgium = Dutch
Netherlands = Dutch
Ireland = English
Malta = English
United Kingdom = English
Estonia = Estonian
Finland = Finnish
Belgium = French
France = French
Italy = French
Luxembourg = French
Austria = German
Belgium = German
Denmark = German
Germany = German
Italy = German
Luxembourg = German
Cyprus = Greek
Greece = Greek
Austria = Hungarian
Hungary = Hungarian
Romania = Hungarian
Slovakia = Hungarian
Slovenia = Hungarian
Ireland = Irish
United Kingdom = Irish
Croatia = Italian
Italy = Italian
Slovenia = Italian
Latvia = Latvian
Lithuania = Lithuanian
Malta = Maltese
Poland = Polish
Portugal = Portuguese
Romania = Romanian
Slovakia = Slovak
Czech Republic = Slovak
Hungary = Slovak
Slovenia = Slovenian
Austria = Slovenian
Hungary = Slovenian
Italy = Slovenian
Spain = Spanish
Sweden = Swedish
Finland = Swedish
在 python 中,我使用此代码创建一个关联数组,其中一个字符串作为键,一个数组作为值:
from collections import defaultdict
mydata = defaultdict(list)
myfile = open("myfile", "r")
for line in myfile:
country, language = line.rstrip('\n').split(" = ")
mydata[country].append(language)
它创建了一个这样的数据结构:
'Bulgaria' = ['Bulgarian']
'Croatia' = ['Croatian']
'Austria' = ['Croatian', 'German', 'Hungarian', 'Slovenian']
# and so on
Perl 和 Ruby 有相似的关联数组。 Go 可以用 maps 和 append()
.
创建它
我在 Pascal 中看到很多对关联数组的引用,但我找不到使用 数组作为值.
的示例
我正在使用 FreePascal,我想避免使用外部库。你能举个例子吗?
PS:我知道这看起来像作业,但不是。
下面是演示程序。
打开 Lazarus,创建一个应用程序,然后将单元 fgl
添加到表单单元的 uses 子句中:
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, fgl;
在表单中添加一个 TButton (Button1) 和一个 TMemo (Memo1) 并为按钮提供以下代码:
{ Can't find anything suitable in the Lazarus runtime. I'm sure this }
{ can be improved. }
function Strip(const S: string): string;
var
left, right: Integer;
begin
if S = '' then
begin
Result := S;
Exit;
end;
left := 1;
while S[left] in [' ', #9] do
Inc(left);
right := Length(S);
while (right > 0) and (S[right] in [' ', #9]) do
Dec(right);
Result := Copy(S, left, right - left + 1);
end;
type
TMap = specialize TFPGMap<string, TStringList>;
procedure TAssocForm.Button1Click(Sender: TObject);
var
mydata: TMap;
myfile: Text;
line: string;
country: string;
language: string;
mypos: Integer;
SL: TStringList;
I: Integer;
begin
{ Error handling needs to be added, e.g. if file doesn't exist, or if
a line doesn't contain an =, etc. etc. }
mydata := TMap.Create;
{ Open file 'myfile.txt' for reading. }
System.Assign(myfile, '/Users/xxx/yyy/myfile.txt'); { adjust accordingly }
Reset(myfile);
{ Read lines. }
while not Eof(myfile) do
begin
Readln(myfile, line);
mypos := Pos('=', line);
{ Split line into country and language. }
country := Strip(Copy(line, 1, mypos - 1));
language := Strip(Copy(line, mypos + 1, MaxInt));
{ If key not present yet, add a new string list. }
if mydata.IndexOf(country) < 0 then
mydata.Add(country, TStringList.Create);
{ add language to string list of country. }
mydata[country].Add(language);
end;
System.Close(myfile);
Memo1.Lines.Clear;
Memo1.Lines.BeginUpdate;
for I := 0 to mydata.Count - 1 do
begin
{ Get key. }
country := mydata.Keys[I];
line := country + ' -> ';
{ Get string list. }
SL := mydata[country];
{ Get languages in the string list. }
for language in SL do
line := line + language + ' ';
{ Add line to memo. }
Memo1.Lines.Add(Strip(line));
end;
Memo1.Lines.EndUpdate;
{ Free the string lists. }
for I := 0 to mydata.Count - 1 do
mydata[mydata.Keys[I]].Free;
end;
end.
运行 程序并单击按钮。备忘录将填满国家和那里使用的语言,例如
Bulgaria -> Bulgarian
Croatia -> Croatian Italian
Austria -> Croatian German Hungarian Slovenian
Czech Republic -> Czech Slovak
Slovakia -> Czech Hungarian Slovak
etc...
数组
Pascal (Delphi) 语言在该语言中没有关联数组。只有具有序数类型索引的数组(即整数,而不是字符串或浮点数)。有多维数组,最近还有动态数组,但索引无论如何都是有序的。
但是,标准库单元(Classes.pas 和 System.Generics.Collections.pas)提供 class 实现您所说的功能的实体。
您可以使用新的 Delphi 泛型,尤其是 TDictionary,用于真正的关联数组,或者使用旧的 TStringList,仅用于普通字符串列表。 TStringList class 已在 Delphi 的较新版本中使用 Name+Delimiter+Value 功能进行了扩展。对于对,TStringList 比 Delphi 泛型的 TDictionary 慢,因为它只是在内部存储纯字符串并即时解析它们。然而,泛型使用高效的结构来快速添加和删除项目,使用值的散列,因此速度非常快。在 TStringList 中,相反,随机插入和删除很慢 - O(N),但通过索引获取字符串是瞬间的 - O(1)。
泛型
uses
System.Generics.Collections,
procedure TestGenerics;
type
TKey = string;
TValue = string;
TKeyValuePair = TPair<TKey, TValue>;
TStringDictionary = TDictionary<TKey, TValue>;
var
D: TStringDictionary;
K: TKey;
V: TValue;
P: TKeyValuePair;
ContainsKey, ContainsValue: Boolean;
begin
D := TStringDictionary.Create;
D.Add('Bulgaria', 'Bulgarian');
D.Add('Croatia', 'Croatian Italian');
K := D.Items['Bulgaria'];
P := D.ExtractPair('Bulgaria');
ContainsKey := D.ContainsKey('Bultaria');
ContainsValue := D.ContainsValue('Bultarian');
// you do not need to free P, since it is just a record in the stack
D.Free;
end;
字符串列表
请注意,Delphi的作者称它为Name+Value对,而不是Key+Value对,因为在TStringList中,这些实际上不是"Keys"的快速访问方式, 它只是同一个字符串的一部分。它们没有特殊的排序索引功能 - 如果您愿意,只需常规排序的 TStringList。
另请注意,当 TStringList 对象包含名称-值对或名称字符串时,请阅读键以访问字符串的名称部分。如果字符串不是名称-值对,Keys returns 完整字符串。分配键将为名称-值对写入新名称。这与名称 属性.
相反
此外,考虑到 TStringList 使用连续内存 space 来保存指向字符串数据的指针,因此当您添加新字符串时,它会使用预分配的 space更多条目,但随后分配一个新的更大的内存块并将旧指针复制到新指针,释放一个旧块。因此,如果您事先知道项目的数量,最好将该数字告诉 TStringList 以便它一劳永逸地预先分配缓冲区。这不会阻止以后扩大缓冲区,如果你需要更多的项目。
uses
Classes;
procedure TestStringList;
var
SL: TStringList;
FullString, Separator, FName, FValue: string;
begin
SL := TStringList.Create;
SL.AddPair('Bulgaria', 'Bulgarian'); // add a Name -> Value pair
SL.AddPair('Croatia', 'Croatian Italian');
// Names and KeyNames is the same
FName := SL.Names[0]; // Indicates the name part of strings that are name-value pairs.
FName := SL.KeyNames[0];
FValue := SL.Values['Bulgaria']; // Represents the value part of a string associated with a given name, on strings that are name-value pairs.
FValue := SL.ValueFromIndex[0]; // Represents the value part of a string with a given index, on strings that are name-value pairs.
FullString := SL.Strings[0]; // References the strings in the list by their positions (the whole Name+Separator+Value pair)
Separator := SL.NameValueSeparator; // Indicates the character used to separate names from values.
SL.Free;
end;
我有这个文件:
Bulgaria = Bulgarian
Croatia = Croatian
Austria = Croatian
Czech Republic = Czech
Slovakia = Czech
Denmark = Danish
Germany = Danish
Belgium = Dutch
Netherlands = Dutch
Ireland = English
Malta = English
United Kingdom = English
Estonia = Estonian
Finland = Finnish
Belgium = French
France = French
Italy = French
Luxembourg = French
Austria = German
Belgium = German
Denmark = German
Germany = German
Italy = German
Luxembourg = German
Cyprus = Greek
Greece = Greek
Austria = Hungarian
Hungary = Hungarian
Romania = Hungarian
Slovakia = Hungarian
Slovenia = Hungarian
Ireland = Irish
United Kingdom = Irish
Croatia = Italian
Italy = Italian
Slovenia = Italian
Latvia = Latvian
Lithuania = Lithuanian
Malta = Maltese
Poland = Polish
Portugal = Portuguese
Romania = Romanian
Slovakia = Slovak
Czech Republic = Slovak
Hungary = Slovak
Slovenia = Slovenian
Austria = Slovenian
Hungary = Slovenian
Italy = Slovenian
Spain = Spanish
Sweden = Swedish
Finland = Swedish
在 python 中,我使用此代码创建一个关联数组,其中一个字符串作为键,一个数组作为值:
from collections import defaultdict
mydata = defaultdict(list)
myfile = open("myfile", "r")
for line in myfile:
country, language = line.rstrip('\n').split(" = ")
mydata[country].append(language)
它创建了一个这样的数据结构:
'Bulgaria' = ['Bulgarian']
'Croatia' = ['Croatian']
'Austria' = ['Croatian', 'German', 'Hungarian', 'Slovenian']
# and so on
Perl 和 Ruby 有相似的关联数组。 Go 可以用 maps 和 append()
.
我在 Pascal 中看到很多对关联数组的引用,但我找不到使用 数组作为值.
的示例我正在使用 FreePascal,我想避免使用外部库。你能举个例子吗?
PS:我知道这看起来像作业,但不是。
下面是演示程序。
打开 Lazarus,创建一个应用程序,然后将单元 fgl
添加到表单单元的 uses 子句中:
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, fgl;
在表单中添加一个 TButton (Button1) 和一个 TMemo (Memo1) 并为按钮提供以下代码:
{ Can't find anything suitable in the Lazarus runtime. I'm sure this }
{ can be improved. }
function Strip(const S: string): string;
var
left, right: Integer;
begin
if S = '' then
begin
Result := S;
Exit;
end;
left := 1;
while S[left] in [' ', #9] do
Inc(left);
right := Length(S);
while (right > 0) and (S[right] in [' ', #9]) do
Dec(right);
Result := Copy(S, left, right - left + 1);
end;
type
TMap = specialize TFPGMap<string, TStringList>;
procedure TAssocForm.Button1Click(Sender: TObject);
var
mydata: TMap;
myfile: Text;
line: string;
country: string;
language: string;
mypos: Integer;
SL: TStringList;
I: Integer;
begin
{ Error handling needs to be added, e.g. if file doesn't exist, or if
a line doesn't contain an =, etc. etc. }
mydata := TMap.Create;
{ Open file 'myfile.txt' for reading. }
System.Assign(myfile, '/Users/xxx/yyy/myfile.txt'); { adjust accordingly }
Reset(myfile);
{ Read lines. }
while not Eof(myfile) do
begin
Readln(myfile, line);
mypos := Pos('=', line);
{ Split line into country and language. }
country := Strip(Copy(line, 1, mypos - 1));
language := Strip(Copy(line, mypos + 1, MaxInt));
{ If key not present yet, add a new string list. }
if mydata.IndexOf(country) < 0 then
mydata.Add(country, TStringList.Create);
{ add language to string list of country. }
mydata[country].Add(language);
end;
System.Close(myfile);
Memo1.Lines.Clear;
Memo1.Lines.BeginUpdate;
for I := 0 to mydata.Count - 1 do
begin
{ Get key. }
country := mydata.Keys[I];
line := country + ' -> ';
{ Get string list. }
SL := mydata[country];
{ Get languages in the string list. }
for language in SL do
line := line + language + ' ';
{ Add line to memo. }
Memo1.Lines.Add(Strip(line));
end;
Memo1.Lines.EndUpdate;
{ Free the string lists. }
for I := 0 to mydata.Count - 1 do
mydata[mydata.Keys[I]].Free;
end;
end.
运行 程序并单击按钮。备忘录将填满国家和那里使用的语言,例如
Bulgaria -> Bulgarian
Croatia -> Croatian Italian
Austria -> Croatian German Hungarian Slovenian
Czech Republic -> Czech Slovak
Slovakia -> Czech Hungarian Slovak
etc...
数组
Pascal (Delphi) 语言在该语言中没有关联数组。只有具有序数类型索引的数组(即整数,而不是字符串或浮点数)。有多维数组,最近还有动态数组,但索引无论如何都是有序的。
但是,标准库单元(Classes.pas 和 System.Generics.Collections.pas)提供 class 实现您所说的功能的实体。
您可以使用新的 Delphi 泛型,尤其是 TDictionary,用于真正的关联数组,或者使用旧的 TStringList,仅用于普通字符串列表。 TStringList class 已在 Delphi 的较新版本中使用 Name+Delimiter+Value 功能进行了扩展。对于对,TStringList 比 Delphi 泛型的 TDictionary 慢,因为它只是在内部存储纯字符串并即时解析它们。然而,泛型使用高效的结构来快速添加和删除项目,使用值的散列,因此速度非常快。在 TStringList 中,相反,随机插入和删除很慢 - O(N),但通过索引获取字符串是瞬间的 - O(1)。
泛型
uses
System.Generics.Collections,
procedure TestGenerics;
type
TKey = string;
TValue = string;
TKeyValuePair = TPair<TKey, TValue>;
TStringDictionary = TDictionary<TKey, TValue>;
var
D: TStringDictionary;
K: TKey;
V: TValue;
P: TKeyValuePair;
ContainsKey, ContainsValue: Boolean;
begin
D := TStringDictionary.Create;
D.Add('Bulgaria', 'Bulgarian');
D.Add('Croatia', 'Croatian Italian');
K := D.Items['Bulgaria'];
P := D.ExtractPair('Bulgaria');
ContainsKey := D.ContainsKey('Bultaria');
ContainsValue := D.ContainsValue('Bultarian');
// you do not need to free P, since it is just a record in the stack
D.Free;
end;
字符串列表
请注意,Delphi的作者称它为Name+Value对,而不是Key+Value对,因为在TStringList中,这些实际上不是"Keys"的快速访问方式, 它只是同一个字符串的一部分。它们没有特殊的排序索引功能 - 如果您愿意,只需常规排序的 TStringList。
另请注意,当 TStringList 对象包含名称-值对或名称字符串时,请阅读键以访问字符串的名称部分。如果字符串不是名称-值对,Keys returns 完整字符串。分配键将为名称-值对写入新名称。这与名称 属性.
相反此外,考虑到 TStringList 使用连续内存 space 来保存指向字符串数据的指针,因此当您添加新字符串时,它会使用预分配的 space更多条目,但随后分配一个新的更大的内存块并将旧指针复制到新指针,释放一个旧块。因此,如果您事先知道项目的数量,最好将该数字告诉 TStringList 以便它一劳永逸地预先分配缓冲区。这不会阻止以后扩大缓冲区,如果你需要更多的项目。
uses
Classes;
procedure TestStringList;
var
SL: TStringList;
FullString, Separator, FName, FValue: string;
begin
SL := TStringList.Create;
SL.AddPair('Bulgaria', 'Bulgarian'); // add a Name -> Value pair
SL.AddPair('Croatia', 'Croatian Italian');
// Names and KeyNames is the same
FName := SL.Names[0]; // Indicates the name part of strings that are name-value pairs.
FName := SL.KeyNames[0];
FValue := SL.Values['Bulgaria']; // Represents the value part of a string associated with a given name, on strings that are name-value pairs.
FValue := SL.ValueFromIndex[0]; // Represents the value part of a string with a given index, on strings that are name-value pairs.
FullString := SL.Strings[0]; // References the strings in the list by their positions (the whole Name+Separator+Value pair)
Separator := SL.NameValueSeparator; // Indicates the character used to separate names from values.
SL.Free;
end;