常量数组来自集合

huangapple go评论65阅读模式
英文:

Constant array from set

问题

我有以下用于创建数字'0'到'9'和符号'+'和'-'的上标版本的代码

const
  Digits = ['0' .. '9'];
  Signs = ['+', '-'];
  DigitsAndSigns = Digits + Signs;

function SuperScript(c: Char): Char;
{ 返回字符c的上标版本,仅适用于数字0到9和符号+,- }
const
  SuperDigits: array ['0' .. '9'] of Char = ('⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹');
begin
  if CharInSet(c, Digits) then
    Result := SuperDigits[c]
  else if c = '+' then
    Result := '⁺'
  else if c = '-' then
    Result := '⁻'
  else
    Result := c;
end;

这个代码可以工作,但不太优雅。理想情况下,我想要像下面这样:

SuperDigits: array [DigitsAndSigns] of Char = ('⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹', '⁺', '⁻');

但这甚至不能编译。是否可以以某种方式为集合中的每个元素创建和预设一个数组元素?

我知道我可以使用更重的组件,如TDictionary,但(如果可能的话)我想使用集合或枚举。

英文:

I have the following code for creating superscript versions of the digits '0' to '9' and the signs '+' and '-'

const
  Digits = ['0' .. '9'];
  Signs = ['+', '-'];
  DigitsAndSigns = Digits + Signs;

function SuperScript(c: Char): Char;
{ Returns the superscript version of the character c
  Only for the numbers 0..9 and the signs +, - }
const
  SuperDigits: array ['0' .. '9'] of Char = ('⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹');
begin
  if CharInSet(c, Digits) then
    Result := SuperDigits[c]
  else if c = '+' then
    Result := '⁺'
  else if c = '-' then
    Result := '⁻'
  else
    Result := c;
end;

This works, but is not very elegant. Ideally I would like to have something like

  SuperDigits: array [DigitsAndSigns] of Char = ('⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹', '⁺', '⁻');

but this does not even compile.

Is it somehow possible to create and preset an array element for every element in the set?

I am aware that I could use more heavy components like TDictionary, but (if possible) I would like to use sets or enumerations.

答案1

得分: 5

以下是代码的中文翻译:

实际上,有一种方法可以实现你想要的,但也许不是你所期望的:

type
  SuperDigit = record
  private
    class function GetItem(const C: Char): Char; static;
  public
    class property Item[const C: Char]: Char read GetItem; default;
  end;

class function SuperDigit.GetItem(const C: Char): Char;
const
  cDigitsAndSigns = '0123456789+-';
  cSuperScripts   = '⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻';
begin
  Result := C;
  var idx := Pos(C, cDigitsAndSigns);
  if idx >= 0 then
    Result := cSuperScripts[idx];
end;

通过这个声明,你可以编写类似这样的代码:

procedure ToSuperScript(var S: string);
begin
  for var I := 1 to Length(S) do
    S[I] := SuperDigit[S[I]];
end;
英文:

Actually there is a solution to achieve what you want, but perhaps not what you expected:

type
  SuperDigit = record
  private
    class function GetItem(const C: Char): Char; static;
  public
    class property Item[const C: Char]: Char read GetItem; default;
  end;

class function SuperDigit.GetItem(const C: Char): Char;
const
  cDigitsAndSigns = '0123456789+-';
  cSuperScripts   = '⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻';
begin
  Result := C;
  var idx := Pos(C, cDigitsAndSigns);
  if idx >= 0 then
    Result := cSuperScripts[idx];
end;

With this declaration your can write something like this:

procedure ToSuperScript(var S: string);
begin
  for var I := 1 to Length(S) do
    S[I] := SuperDigit[S[I]];
end;

答案2

得分: 2

不可能。

这是基本不可能的,因为集合是一个无序的容器。

在你的情况下,Digits + SignsSigns + Digits 是完全相同的,所以你怎么可能知道要按什么顺序枚举这些元素呢?

另外,值得指出的是,在

const
  Digits = ['0' .. '9'];

中的括号与

array ['0' .. '9'] of Char 

中的括号不是相同类型的括号。Digits 中的括号确实创建了一个集合,但静态数组的语法与集合无关。静态数组是由一个有序类型索引的。

理论上,你可以创建一个带有字符的枚举类型,但然后你需要将输入字符转换为枚举类型,然后再转换回映射的字符。所以这并不方便。

在你的特定情况下,你有一个映射 Char → Char。底层的Unicode代码点并不足够好,无法进行任何巧妙的技巧(就像你可以用ASCII小写字母转换为大写字母那样)。实际上,上标数字甚至不是连续的!所以你别无选择,只能进行一种普通的基于数据的映射。

我只会使用case构造 就像 UnicodeSuperscript 中的这个例子


function UnicodeSuperscript(const C: Char): Char;
begin
  case C of
    '0':
      Result := '⁰';
    '1':
      Result := '¹';
    '2':
      Result := '²';
    '3':
      Result := '³';
    '4':
      Result := '⁴';
    '5':
      Result := '⁵';
    '6':
      Result := '⁶';
    '7':
      Result := '⁷';
    '8':
      Result := '⁸';
    '9':
      Result := '⁹';
    '+':
      Result := '⁺';
    '-', '−':
      Result := '⁻';
  else
    Result := C;
  end;
end;

就优雅而言,我想你可能希望将数据与逻辑分开。一种(过度并且较慢!)的方法是像这样存储一个常量数组:

function UnicodeSuperscript(const C: Char): Char;
const
  Chars: array[0..12] of
    record
      B,
      S: Char
    end
    =
    (
      (B: '0'; S: '⁰'),
      (B: '1'; S: '¹'),
      (B: '2'; S: '²'),
      (B: '3'; S: '³'),
      (B: '4'; S: '⁴'),
      (B: '5'; S: '⁵'),
      (B: '6'; S: '⁶'),
      (B: '7'; S: '⁷'),
      (B: '8'; S: '⁸'),
      (B: '9'; S: '⁹'),
      (B: '+'; S: '⁺'),
      (B: '-'; S: '⁻'),
      (B: '−'; S: '⁻')
    );
begin
  for var X in Chars do
    if C = X.B then
      Exit(X.S);
  Result := C;
end;
英文:

> Is it somehow possible to create and preset an array element for every element in the set?

No.

This is fundamentally impossible because the set is an unordered container.

In your case, Digits + Signs is exactly the same thing as Signs + Digits, so how could you possibly know in what order to enumerate the elements?

Also, it might be worth pointing out that the brackets in

const
  Digits = ['0' .. '9'];

are not of the same kind as the brackets in

array ['0' .. '9'] of Char 

The brackets in Digits really do make a set, but the static array syntax has nothing to do with sets. A static array is indexed by an ordinal type.

In theory, you could create an enumerated type with your characters, but then you need to convert an input character to your enumerated type, and then back to the mapped character. So this is not convenient.

In your particular case, you have a mapping Char → Char. The underlying Unicode code points aren't really nice enough to facilitate any clever tricks (like you can do with ASCII lower case -> upper case, for example). In fact, the superscript digits are not even contiguous! So you have no choice but to do a plain, data-based mapping of some sort.

I'd just use a case construct like in UnicodeSuperscript here:


function UnicodeSuperscript(const C: Char): Char;
begin
  case C of
    '0':
      Result := '⁰';
    '1':
      Result := '¹';
    '2':
      Result := '²';
    '3':
      Result := '³';
    '4':
      Result := '⁴';
    '5':
      Result := '⁵';
    '6':
      Result := '⁶';
    '7':
      Result := '⁷';
    '8':
      Result := '⁸';
    '9':
      Result := '⁹';
    '+':
      Result := '⁺';
    '-', '−':
      Result := '⁻';
  else
    Result := C;
  end;
end;

In terms of elegance, I guess you may want to separate data from logic. One (overkill and slower!) approach would be to store a constant array like in

function UnicodeSuperscript(const C: Char): Char;
const
  Chars: array[0..12] of
    record
      B,
      S: Char
    end
    =
    (
      (B: '0'; S: '⁰'),
      (B: '1'; S: '¹'),
      (B: '2'; S: '²'),
      (B: '3'; S: '³'),
      (B: '4'; S: '⁴'),
      (B: '5'; S: '⁵'),
      (B: '6'; S: '⁶'),
      (B: '7'; S: '⁷'),
      (B: '8'; S: '⁸'),
      (B: '9'; S: '⁹'),
      (B: '+'; S: '⁺'),
      (B: '-'; S: '⁻'),
      (B: '−'; S: '⁻')
    );
begin
  for var X in Chars do
    if C = X.B then
      Exit(X.S);
  Result := C;
end;

huangapple
  • 本文由 发表于 2023年2月16日 05:30:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/75465620.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定