У меня есть следующий код для создания надстрочных версий цифр от «0» до «9» и знаков «+» и «-».
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;
Это работает, но не очень элегантно. В идеале я хотел бы иметь что-то вроде
SuperDigits: array [DigitsAndSigns] of Char = ('⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹', '⁺', '⁻');
Но это даже не компилируется.
Можно ли как-то создать и установить элемент массива для каждого элемента в наборе?
Я знаю, что мог бы использовать более тяжелые компоненты, такие как TDictionary, но (если возможно) я хотел бы использовать наборы или перечисления.
Можно ли как-то создать и установить элемент массива для каждого элемента в наборе?
Нет.
Это принципиально невозможно, потому что набор является неупорядоченным контейнером.
В вашем случае Digits + Signs — это то же самое, что и Signs + 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;
На самом деле есть решение для достижения того, чего вы хотите, но, возможно, не того, чего вы ожидали:
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;
Преимущество этого способа в том, что cDigitsAndSigns также может быть таким же гибким, как = Digits + Signs, объединяя несколько строк по желанию.
Это очень близко к тому, что я хочу. С точки зрения производительности решение Андреаса с использованием case ... может быть лучше, но для небольших наборов это очень элегантное решение.
Дело требует столько сравнений, сколько поз. Сомневаюсь, что разницу в производительности можно измерить, не говоря уже о том, чтобы заметить. Если производительность является ключевым фактором, вы должны рассмотреть ассемблер или таблицу поиска.
Интересно, почему вы не продолжите с else if CharInSet(c, Signs) then Result := SuperSigns[c], а настаиваете на одиночных if. Также с точки зрения производительности case c of было бы лучше.