33

我猜,我对 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 的一部分,并且 - 它们会得到支持吗?我注意到为 2009 版的 Delphi(泛型)做了很多语言扩展,那么为什么不添加这个功能呢?

4

12 回答 12

45

这样的运算符不是当前 Delphi 版本的一部分,因为它不是以前版本的一部分,而且需求不足以证明添加它的成本是合理的。(您会发现该解释适用于您希望在许多产品中拥有的许多功能。)

Delphi 在 Math 和 StrUtils 单元中提供了一组IfThen函数,但不幸的是,它们具有评估它们的值参数的不幸属性,因此这样的代码将失败:

Foo := IfThen(Obj = nil, '<none>', Obj.Name);

要真正做到正确,需要编译器的帮助。在 Delphi 社区中,我感到普遍不喜欢使用问号和冒号的 C 样式语法。我见过会使用如下语法的提案:

Foo := if Obj = nil then
         '<none>'
       else
         Obj.Name;

条件运算符如此吸引人的部分原因在于它们让您编写简洁的代码,但 Delphi 将所有内容都写出来的风格使得上述内容没有吸引力,即使将所有内容放在一行上。

它实际上不需要采用operator的形式。Delphi Prism 提供了一个编译器魔术函数Iif,它只计算它的两个值参数之一:

Foo := Iif(Obj = nil, '<none>', Obj.Name);

您问为什么不会添加这样的功能以及 Delphi 2009 中添加的所有其他语言功能。我认为这就是您的原因。还有很多其他的语言变化已经需要微妙的处理了。开发人员不需要承担更多负担。功能不是免费的。

你问德尔福是否会有这样的功能。我不知道 Embarcadero 的计划会议,我不得不把我的水晶球送去修理,所以我不能肯定,但我预测如果它有这样的功能,它会以形式出现德尔福棱镜的Iif功能。这个想法在 Quality Central 中的讨论接近尾声时出现,有人反对说,作为一个新的保留字,它会破坏与其他人的代码的向后兼容性,这些代码已经定义了同名的函数。不过,这不是一个有效的对象,因为它不需要是保留字。它可以是一个标识符,就像WritelnExit,它可以有资格在其他单元中重新定义,即使系统单元中的一个被特殊处理。

于 2010-01-21T15:34:43.217 回答
6

好的。当天的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.

是的,它或多或少没用,但它表明它可以做到。

于 2011-03-09T13:51:42.917 回答
5

对此有一份 QC 报告 ( 8451 ) 进行了合理的讨论。

2004 年 6 月提出,似乎没有来自 Borland/CodeGear/Embarcadero 的任何回应。

于 2010-01-21T12:18:04.810 回答
5

重载的 IFTHEN 函数上有许多可用的简单类型句柄。

StrUtils.IfThen( String)

Math.IfThen( Integer)

Math.IfThen( Int64)

Math.IfThen( Double) (也适用于TDateTime

如 Andreas 评论的示例所示,此模型失败,但对于简单类型,这非常合理。如果遵循 Delphi/Pascal 的方法约定,而不是屈从于 C 使用尽可能少的字符的方式。

就我个人而言,我宁愿不要?:在 Delphi 中看到条件运算符(即 ),因为我更喜欢 Delphi/Pascal 的可读性而不是 C 及其衍生语言。我更愿意看到更多创新的 Delphi 类型解决方案,而不是实现更多的 C-isms。

于 2010-01-21T15:36:30.667 回答
4

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));

为什么不实现条件运算符有几个原因。其中之一是可读性。Pascal(以及 Delphi)比 C 语法语言更注重可读性,后者更注重字符能力(每个字符尽可能多的信息)。条件运算符功能强大但(根据某些人)不可读。但是,如果您查看 Delphi 中的(可怕的)with 语句......(无需多说)。另一个原因是不需要条件运算符。这是真的。但是还有更多不需要的仍在实施。

最后只是口味问题。

但是,如果您只想评估一个参数,则始终可以使用以下内容,这违反了作为字符权力概念的可读性:

[过度设计模式]

// 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;

[/过度设计模式]

于 2010-01-21T11:04:44.580 回答
4

我希望他们实现惰性评估,它会更强大,可以在不同的场景中使用。请参阅以下链接的详细信息

http://www.digitalmars.com/d/2.0/lazy-evaluation.html

干杯

于 2010-01-31T17:20:03.933 回答
2

另一种选择是使用泛型:

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);

注意:正如 Alan Bryant(2004 年 6 月 21 日上午 7:26:21)在QR 8451中指出的那样,这将始终评估所有 3 个参数 - 所以请注意它不是真正的三元运算符。

于 2013-11-19T13:26:56.783 回答
1

实际上对于字符串,您可以使用:StrUtils.IfThen函数:

function IfThen(AValue: Boolean;
        const ATrue: string;
        AFalse: string = ): string; overload;

查看 delphi 帮助 wiki:http ://docwiki.embarcadero.com/VCL/en/StrUtils.IfThen

它完全符合您的需要。

于 2010-01-21T15:19:04.547 回答
0

Jedi 代码库 (JCL) 使用一组名为 IFF() 的函数实现了三元运算符。有关文档,请参见此处:

http://wiki.delphi-jedi.org/wiki/JCL_Help:Iff@Boolean@Boolean@Boolean

要下载 JCL,您可以访问此站点:

http://sourceforge.net/projects/jcl/

于 2011-03-07T13:54:49.387 回答
0

更好的是支持多种数据类型和结果的重载 IIF(内联 if)。

于 2010-01-23T09:50:40.553 回答
0

如果它有帮助,与 Math.IfThen 函数相比的最大好处是它使用“内联”(假设编译器支持它)。

使用 TFunc 或 TFunc<T,T> 的版本会有点难看,因为 Delphi 的匿名方法没有 Lambda 表达式(尽管我发现了一些 Variant 表达式的实验)

unit Zoomicon.Generics.Functors;

interface
  uses SysUtils; //for TPredicate

type
  TF = class
    class function Iff<T>(const Condition: Boolean; const ValueIfTrue, ValueIfFalse: T): T; overload; inline;
    class function Iff<T>(const Condition: Boolean; const ValueIfTrue, ValueIfFalse: TFunc<T>): T; overload; inline;
    class function Iff<T>(const Value: T; const Condition: TPredicate<T>; const ValueIfTrue, ValueIfFalse: T): T; overload; inline;
    class function Iff<T>(const Value: T; const Condition: TPredicate<T>; const ValueIfTrue, ValueIfFalse: TFunc<T>): T; overload; inline;
    class function Iff<T>(const Value: T; const Condition: TPredicate<T>; const ValueIfTrue, ValueIfFalse: TFunc<T,T>): T; overload; inline;
  end;

implementation

class function TF.Iff<T>(const Condition: Boolean; const ValueIfTrue, ValueIfFalse: T): T;
begin
  if Condition then
    result := ValueIfTrue
  else
    result := ValueIfFalse;
end;

class function TF.Iff<T>(const Condition: Boolean; const ValueIfTrue, ValueIfFalse: TFunc<T>): T;
begin
  if Condition and Assigned(ValueIfTrue) then
    result := ValueIfTrue
  else
    result := ValueIfFalse; //Note: will fail if ValueIfFalse is not assigned
end;

class function TF.Iff<T>(const Value: T; const Condition: TPredicate<T>; const ValueIfTrue, ValueIfFalse: T): T;
begin
  if Assigned(Condition) then
    result := Iff(Condition(Value), ValueIfTrue, ValueIfFalse)
  else
    result := ValueIfFalse;
end;

class function TF.Iff<T>(const Value: T; const Condition: TPredicate<T>; const ValueIfTrue, ValueIfFalse: TFunc<T>): T;
begin
  //result := Iff(Value, Condition, ValueIfTrue(), ValueIfFalse()); //use of () seems to be needed else compiler seems to match the same function (infinite recursion) //DOESN'T COMPILE (probably Delphi bug)
  if Assigned(Condition) then
    result := Iff(Condition(Value), ValueIfTrue, ValueIfFalse) //TODO: not sure if evaluation is deferred here (aka which "Iff" gets called [CTRL+Click in Delphi doesn't seem that clever], @ValueIfTrue/@ValueIfFalse to force that don't seem to compile)
  else
    result := ValueIfFalse; //Note: will fail if ValueIfFalse is not assigned
end;

class function TF.Iff<T>(const Value: T; const Condition: TPredicate<T>; const ValueIfTrue, ValueIfFalse: TFunc<T,T>): T;
begin
  //result := Iff(Value, Condition, ValueIfTrue(Value), ValueIfFalse(Value)); //DOESN'T COMPILE (probably Delphi bug)
  if Assigned(Condition) and Assigned(ValueIfTrue) {and Assigned(ValueIfFalse)} then //no need to check Assigned(ValueIfFalse) here, since in any case it will fail
    result := Iff(Condition(Value), ValueIfTrue(Value), ValueIfFalse(Value)) //Note: will fail if ValueIfFalse is not assigned
  else
    result := ValueIfFalse(Value); //Note: will fail if ValueIfFalse is not assigned
end;

end.
于 2021-10-21T10:29:23.977 回答
0

当天的 WTF 代码 Nr。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 TernaryTest(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');
  TernaryTest(10);
  ShowMessage('Testing false case');
  TernaryTest(0);
  ShowMessage('Testing exception');
  TernaryOp(False, nil, nil);
end.

Variant 数据类型的一种可能修改是:

type
  TGetValue = reference to function(): Variant;

function TernaryOp(condition: Boolean; trueFunc, falseFunc: TGetValue): Variant;
begin
  Result := Unassigned;
  if condition then begin
    if Assigned(trueFunc) then Result := trueFunc();
  end
  else begin
    if Assigned(falseFunc) then Result := falseFunc();
  end;
end;
于 2019-06-03T12:18:57.993 回答