Есть ли, или будет когда-нибудь, условный оператор в Delphi?
Я слишком долго держал руки у Дельфи, наверное; За последние пару лет я много занимался Java и PHP. Теперь, когда я вернулся к выполнению небольшой работы Delphi, я понял, что действительно скучаю по условному оператору, который поддерживается как Java, так и PHP.
На сколько мест вы найдете такие строки в своих программах Delphi?
var s : string;
begin
...<here the string result is manipulated>...
if combo.Text='' then
s := 'null'
else
s := QuotedStr(combo.Text);
result := result + s;
end;
где простой
result := result + (combo.text='')?'null':quotedStr(combo.text);
было бы достаточно. Что мне нравится в этом, так это то, что он не только сокращает код, и я также избегаю объявления некоторой вспомогательной переменной s:string
.
Почему условные операторы не являются частью Delphi и - будут ли они когда-либо поддерживаться? Я заметил, что для версии Delphi (generics) 2009 года было сделано несколько языков, поэтому почему бы не добавить эту функцию?
Ответы
Ответ 1
Такой оператор не является частью текущей версии Delphi, поскольку он не был частью предыдущей версии, и спрос был недостаточным, чтобы оправдать затраты на его добавление. (Вы найдете, что это объяснение относится к множеству функций, которые вы хотели бы иметь в большом количестве продуктов.)
Delphi предоставляет набор функций IfThen
в модулях Math и StrUtils, но у них есть неудачное свойство оценки обоих параметров значений, поэтому такой код не будет выполняться:
Foo := IfThen(Obj = nil, '<none>', Obj.Name);
Чтобы все было правильно, нужна помощь от компилятора. В сообществе Delphi я чувствую общую неприязнь к синтаксису C-стиля, используя знак вопроса и двоеточие. Я видел предложения, которые использовали бы синтаксис следующим образом:
Foo := if Obj = nil then
'<none>'
else
Obj.Name;
Часть того, что делает условные операторы настолько привлекательными, заключается в том, что они позволяют писать сжатый код, но стиль Delphi для написания всего делает вышеупомянутое непривлекательным, даже если положить все на одну строку.
Это не обязательно должно быть в форме оператора. Delphi Prism предоставляет компилятор-магическую функцию Iif
, которая оценивает только один из двух параметров:
Foo := Iif(Obj = nil, '<none>', Obj.Name);
Вы спросили, почему такая функция не была бы добавлена вместе со всеми другими языковыми функциями, добавленными в Delphi 2009. Я думаю, что ваша причина. Было много других изменений языка, которые уже требовали деликатной обработки; разработчикам не нужно было обременять еще больше. Особенности не являются бесплатными.
Вы спросили, будет ли у Delphi такая функция. Я не участвую в совещаниях по планированию Эмбаркадеро, и мне пришлось отправить свой хрустальный шар на ремонт, поэтому я не могу сказать наверняка, но я предсказываю, что если бы у него когда-либо была такая особенность, это получилось бы в форме функции Delphi Prism Iif
. Эта идея появляется в конце обсуждение в Quality Central, и делается возражение, что, как новое зарезервированное слово, оно сломается назад совместимость с кодом других людей, который уже определяет функцию с тем же именем. Это не действительный объект, хотя, поскольку он не должен быть зарезервированным словом. Он может быть идентификатором и точно так же, как Writeln
и Exit
, он может быть переопределен в других единицах, даже если тот из системного блока обрабатывается специально.
Ответ 2
Здесь есть отчет о контроле качества (8451), который имеет разумное обсуждение.
Поднято в июне 2004 года, и, похоже, от Borland/CodeGear/Embarcadero не было ответа.
Ответ 3
Существует несколько доступных простых типов дескрипторов для перегруженной функции IFTHEN.
StrUtils.IfThen
(String
)
Math.IfThen
(Integer
)
Math.IfThen
(Int64
)
Math.IfThen
(Double
) (работает и для TDateTime
)
Эта модель падает, как показано в примере, который Андреас прокомментировал, но для простых типов это более чем разумно. Если следует соглашение методов Delphi/Pascal, а не поддаваться методу C использования наименьшего количества символов, насколько это возможно.
Лично я предпочел бы не видеть условный оператор (т.е. ?:
), введенный в Delphi, поскольку я предпочитаю читаемость Delphi/Pascal над C и его производными языками. Я бы предпочел увидеть более инновационные решения типа Delphi для чего-то подобного, чем реализовать больше C-isms.
Ответ 4
Ok. Код WTF дня:)
Как получить то, что в основном действует как тройная/условная функция.
program Project107;
{$APPTYPE CONSOLE}
uses SysUtils;
type
TLazyIfThen<T:record>=record
class function IfThen(aCondition:Boolean;aIfTrue, aIfFalse:TFunc<T>):T; static;
end;
class function TLazyIfThen<T>.IfThen(aCondition:Boolean;aIfTrue, aIfFalse:TFunc<T>):T;
begin
if aCondition then
Result := aIfTrue
else
Result := aIfFalse
end;
begin
WriteLn(
TLazyIfThen<Integer>.IfThen(
True,
function:Integer begin result := 0 end,
function:Integer begin result := 1 end
)
);
ReadLn;
end.
Да, это более или менее бесполезно, но это показывает, что это можно сделать.
Ответ 5
В Delphi нет условного оператора, и я серьезно сомневаюсь, что когда-нибудь будет один, но вы никогда не узнаете. Вы всегда можете выдать запрос в Embarcadero.
Альтернативой является определение функции Iff:
function Iff(const ACondition: Boolean; const ATrueValue, AFalseValue: XXX): XXX;
begin
if ACondition then
Result := ATrueValue
else
Result := AFalseValue;
end;
Где XXX - тип желания.
Использовать как:
Result := Result + Iff(combo.text='', 'null', quotedStr(combo.text));
Существует несколько причин, по которым не следует выполнять условный оператор. Одна из них - читаемость. Паскаль (а также Delphi) больше сосредоточен на читабельности, чем языки синтаксиса C, которые в большей степени сосредоточены на мощности символов (как можно больше информации на каждый символ). Условный оператор является мощным, но (по некоторым данным) нечитаемым. Но если вы посмотрите на (страшное) выражение с Delphi... (не нужно больше говорить).
Другая причина заключается в том, что условный оператор не требуется. Что является правдой. Но нет необходимости в том, чтобы все еще было реализовано.
В конце концов, это просто вопрос вкуса.
Но если вы хотите оценить только один аргумент, вы всегда можете использовать следующее, что нарушает как читаемость, так и концепцию мощности символов:
[overdesignmode]
// Please don't take this that serious.
type
TFunc = function(): XXX;
function Iff(const ACondition: Boolean; const ATrueFunc, AFalseFunc: TFunc): XXX;
begin
if ACondition then
ATrueFunc
else
AFalseFunc;
end;
[/overdesignmode]
Ответ 6
Я бы предпочел, чтобы они выполняли ленивую оценку, и она будет более мощной и может использоваться в разных сценариях. Подробнее см. Ссылку ниже.
http://www.digitalmars.com/d/2.0/lazy-evaluation.html
Приветствия
Ответ 7
На самом деле для строк вы можете использовать функцию StrUtils.IfThen:
function IfThen(AValue: Boolean;
const ATrue: string;
AFalse: string = ): string; overload;
Посмотрите в wiki справки delphi: http://docwiki.embarcadero.com/VCL/en/StrUtils.IfThen
Он делает именно то, что вам нужно.
Ответ 8
Другой вариант - использовать generics:
Cond<T> = class
public class function IIF(Cond: boolean; IfVal: T; ElseVal: T): T;
end;
implementation
class function Cond<T>.IIF(Cond: boolean; IfVal, ElseVal: T): T;
begin
if Cond then
Result := IfVal
else
Result := ElseVal;
end;
Это вполне читаемо:
var MyInt: Integer;
begin
MyInt:= Cond<Integer>.IIF(someCondition, 0, 42);
Примечание: как указал Алан Брайант (в 6/21/2004 7:26:21) в QR 8451, это будет всегда оценивайте все 3 аргумента - так что имейте в виду, что это не истинный тернарный оператор.
Ответ 9
Еще лучше - перегруженный IIF (inline if), который поддерживает несколько типов данных и результатов.
Ответ 10
Библиотека кода Jedi (JCL) реализовала трехмерный операнд с набором функций с именем Iff(). См. Здесь документацию:
http://wiki.delphi-jedi.org/wiki/JCL_Help:[email protected]@[email protected]
Чтобы загрузить JCL, вы можете посетить этот сайт:
http://sourceforge.net/projects/jcl/
Ответ 11
WTF код дня № 2:
program TernaryOpTest;
uses
System.SysUtils, Vcl.Dialogs;
type
TGetValue = reference to function(): Double;
function TernaryOp(condition: Boolean; trueFunc, falseFunc: TGetValue): Double;
begin
if condition then begin
if Assigned(trueFunc) then Result := trueFunc() else raise EArgumentNilException.Create('trueFunc not defined.');
end
else begin
if Assigned(falseFunc) then Result := falseFunc() else raise EArgumentNilException.Create('falseFunc not defined.');
end;
end;
procedure TernatyTest(x: Double);
var
v: Double;
begin
v := TernaryOp(x <> 0, function(): Double begin Result := 1/x; ShowMessage('True case'); end, function(): Double begin Result := 0; ShowMessage('False case'); end);
ShowMessage(FloatToStr(v));
end;
begin
ShowMessage('Testing true case');
TernatyTest(10);
ShowMessage('Testing false case');
TernatyTest(0);
ShowMessage('Testing exception');
TernaryOp(False, nil, nil);
end.