Реализация условного оператора IfThen в Object Pascal: проблемы и решения
В Object Pascal разработчики часто сталкиваются с необходимостью использовать условные операторы, возвращающие значения. В этой статье мы рассмотрим особенности реализации функции IfThen, её ограничения и альтернативные решения.
Проблема с стандартной функцией IfThen
Стандартная функция IfThen, доступная в модулях StrUtils и SysUtils, имеет существенное ограничение: она вычисляет оба возможных значения до проверки условия. Рассмотрим пример:
function ComplexCalculation1: String;
begin
// Длительные вычисления
Result := 'Результат 1';
end;
function ComplexCalculation2: String;
begin
// Длительные вычисления
Result := 'Результат 2';
end;
// Оба вычисления выполнятся, даже если нужно только одно
Result := IfThen(Condition, ComplexCalculation1, ComplexCalculation2);
Это приводит к неоптимальной работе кода, так как выполняются ненужные вычисления.
Решение с использованием generics в современных версиях FPC
Начиная с версии 3.2.0, Free Pascal поддерживает generic-версию IfThen:
{$mode delphi}
uses SysUtils;
var
a: Integer = 100;
b: Integer = 200;
begin
// Использование generic-версии IfThen
Writeln(IfThen<Integer>(True, a, b));
Writeln(IfThen<Integer>(False, a, b));
end.
В версии 3.3.1+ с включённым режимом implicitfunctionspecialization можно использовать ещё более компактный синтаксис:
function LazyIfThen<T>(Condition: Boolean;
TrueFunc, FalseFunc: TFunc<T>): T;
begin
if Condition then
Result := TrueFunc()
else
Result := FalseFunc();
end;
// Пример использования:
Result := LazyIfThen<String>(
Condition,
function: String begin Result := ComplexCalculation1 end,
function: String begin Result := ComplexCalculation2 end
);
2. Расширенная версия для Object Pascal
function IfThenEx<T>(Condition: Boolean;
const TrueValue: T; FalseFunc: TFunc<T>): T; overload;
begin
if Condition then
Result := TrueValue
else
Result := FalseFunc();
end;
function IfThenEx<T>(Condition: Boolean;
TrueFunc: TFunc<T>; const FalseValue: T): T; overload;
begin
if Condition then
Result := TrueFunc()
else
Result := FalseValue;
end;
function IfThenEx<T>(Condition: Boolean;
TrueFunc, FalseFunc: TFunc<T>): T; overload;
begin
if Condition then
Result := TrueFunc()
else
Result := FalseFunc();
end;
Практический пример с TCheckListBox
Рассмотрим исправленную версию кода из исходного вопроса:
// Оригинальная проблема:
// checkList.Checked[1] := ifThenStr(...); // Неправильно: Checked - Boolean
// Правильное решение:
checkList.Checked[1] := (checkList.Items[1] = foo('All Header: ON'));
// Если нужна инверсия:
checkList.Checked[1] := not (checkList.Items[1] = foo('All Header: ON'));
// Или с использованием IfThen для Boolean:
checkList.Checked[1] := IfThen(checkList.Items[1] = foo('All Header: ON'), False, True);
Будущее IfThen в Free Pascal
Как отметили участники обсуждения, в 2016 году предлагалось реализовать IfThen как встроенную функцию (intrinsic), что позволило бы избежать вычисления неиспользуемых веток. К сожалению, это предложение не было принято.
Однако с развитием возможностей generic-программирования в FPC, возможно, стоит пересмотреть это решение. На данный момент лучшим вариантом остаётся использование анонимных функций или ожидание реализации неявной специализации в будущих версиях компилятора.
Заключение
Хотя стандартная функция IfThen в Object Pascal имеет ограничения, существуют различные способы обойти их - от использования generic-версий до реализации собственных "ленивых" вариантов этой функции. Выбор конкретного решения зависит от версии компилятора и требований к производительности вашего кода.
Реализация условного оператора IfThen в Object Pascal: проблемы и решения.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS