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はIfThen、MathユニットとStrUtilsユニットで一連の関数を提供しますが、両方の値パラメーターを評価するという不幸な特性があるため、次のようなコードは失敗します。

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

それを本当に正しく行うには、コンパイラの助けが必要です。Delphiコミュニティ内では、疑問符とコロンを使用したCスタイルの構文が一般的に嫌われていると感じています。次のような構文を使用する提案を見てきました。

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

条件演算子を非常に魅力的なものにしているのは、簡潔なコードを記述できることですが、Delphiのすべてを書き出すスタイルでは、すべてを1行にまとめても、上記は魅力的ではありません。

実際には、演算子の形式である必要はありません。IifDelphi Prismは、 2つの値パラメーターのうちの1つのみを評価するコンパイラーマジック関数を提供します。

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

このような機能が、Delphi 2009で追加された他のすべての言語機能と一緒に追加されなかった理由を尋ねられました。それが、あなたの理由だと思います。すでに繊細な処理が必要な他の多くの言語変更が行われていました。開発者はそれ以上の負担をかける必要はありませんでした。機能は無料ではありません。

あなたはDelphiにそのような機能があるかどうか尋ねました。私はエンバカデロの企画会議に精通しておらず、修理のために水晶玉を送らなければならなかったので、はっきりとは言えませんが、もしそれがそのような機能を持っていれば、それは形になる予測していますデルファイプリズムのIif機能の。そのアイデアはQualityCentralでの議論の終わり近くに現れ、新しい予約語として、同じ名前の関数をすでに定義している他の人のコードとの下位互換性を壊すという反対意見が出されます。ただし、予約語である必要はないため、これは有効なオブジェクトではありません。それは識別子である可能性がありWritelnExit、システムユニットからのものが特別に扱われていても、他のユニットで再定義する資格がある場合があります。

于 2010-01-21T15:34:43.217 に答える
6

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.

ええ、それは多かれ少なかれ役に立たないです、しかしそれはそれができることを示しています。

于 2011-03-09T13:51:42.917 に答える
5

これに関するQCレポート(8451)があり、合理的な議論があります。

2004年6月に発生し、Borland / CodeGear/Embarcaderoからの応答はないようです。

于 2010-01-21T12:18:04.810 に答える
5

オーバーロードされたIFTHEN関数には、使用可能な単純型ハンドルがいくつかあります。

StrUtils.IfThenString

Math.IfThenInteger

Math.IfThenInt64

Math.IfThenDouble)(同様にTDateTime機能します)

アンドレアスがコメントした例に示されているように、このモデルは失敗しますが、単純なタイプの場合、これは合理的です。可能な限り少ない文字数を使用するCの方法に屈するのではなく、メソッドのDelphi/Pascal規則に従う場合。

?:個人的には、Cやその派生言語よりもDelphi / Pascalの可読性を好むため、Delphiで導入された条件演算子(つまり)は見たくありません。より多くのC-ismを実装するよりも、このようなものに対してより革新的なDelphiタイプのソリューションを見たいと思います。

于 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はdesirecタイプです。

使用:

Result := Result + Iff(combo.text='', 'null', quotedStr(combo.text));

条件演算子を実装しない理由はいくつかあります。これらの1つは読みやすさです。Pascal(およびDelphi)は、文字のパワー(文字ごとに可能な限り多くの情報)に重点を置いているC構文言語よりも読みやすさに重点を置いています。条件演算子は強力ですが、(一部によると)読み取り可能ではありません。しかし、Delphiの(恐ろしい)withステートメントを見ると...(これ以上言う必要はありません)。もう1つの理由は、条件演算子が必要ないことです。それは本当です。しかし、まだ実装されている必要はありません。

結局、それは好みの問題です。

ただし、1つの引数だけを評価する場合は、次のコマンドをいつでも使用できます。これは、文字のパワーの概念としての読みやすさの両方に違反します。

[オーバーデザインモード]

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

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

注:QR8451でAlanBryant(2004年6月21日7:26:21 AM)が指摘しているよう、これは常に3つの引数すべてを評価します。したがって、これは真の三項演算子ではないことに注意してください。

于 2013-11-19T13:26:56.783 に答える
1

実際には、文字列には次のものを使用できます。StrUtils.IfThen関数:

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

デルファイヘルプウィキをご覧ください: http://docwiki.embarcadero.com/VCL/en/StrUtils.IfThen

それはまさにあなたが必要とすることをします。

于 2010-01-21T15:19:04.547 に答える
0

さらに良いのは、複数のデータ型と結果をサポートするオーバーロードされたIIF(インラインif)です。

于 2010-01-23T09:50:40.553 に答える
0

Jediコードライブラリ(JCL)は、Iff()という名前の関数のセットを使用して3値演算子を実装しました。ドキュメントについては、こちらを参照してください。

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

その日の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データ型の可能な変更の1つは次のとおりです。

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 に答える
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 に答える