6

Delphi 7 コードに次のステートメントがあります。

TMyCharSet = set of char;

そのコードを Delphi XE4 に移行すると、上記の行で次のコンパイラ警告が表示されます。

W1050 WideChar reduced to byte char in set expressions.  Consider using 'CharInSet' function in 'SysUtils' unit.

TMyCharSet を再宣言するにはどうすればよいですか?

4

3 に答える 3

7

セットには、1 バイトを超えるアイテムを含めることはできません。UniCodeCharでは Delphi はWideCharサイズが 2 バイトであるため、集合型は不適切なコンテナです。

レコードに基づくジェネリック セット タイプの例を次に示しTSet<T>ます。これは、この型の変数の作成と破棄について考える必要がないことを意味します。この型を単純型のコンテナーとして使用します。セット型の動作のほとんどを模倣しようとしました。項目の加算と減算は、+ および - 演算子で実行できます。inオペレーターも追加。

注: レコードは動的配列にデータを保持します。変数を別の変数に割り当てると、同じ動的配列を使用して両方の変数が作成されます。組み込みのコピー オン ライト (COW) 保護により、1 つの変数の変更が他の変数に反映されるのを防ぎます。

unit GenericSet;

interface

Uses
  System.Generics.Defaults;

Type
  TSet<T> = record
    class operator Add(const aSet: TSet<T>; aValue: T) : TSet<T>; overload;
    class operator Add(const aSet: TSet<T>; const aSetOfT: TArray<T>) : TSet<T>; overload;
    class operator Add(const aSet1: TSet<T>; const aSet2: TSet<T>) : TSet<T>; overload;
    class operator Subtract(const aSet: TSet<T>; aValue: T): TSet<T>; overload;
    class operator Subtract(const aSet: TSet<T>; const aSetOfT: TArray<T>) : TSet<T>; overload;
    class operator Subtract(const aSet1: TSet<T>; const aSet2: TSet<T>) : TSet<T>; overload;
    class operator In(aValue: T; const aSet: TSet<T>): Boolean; overload;
    class operator In(const aSetOf: TArray<T>; const aSet: TSet<T>): Boolean; overload;
    class operator In(const aSet1: TSet<T>; const aSet2: TSet<T>): Boolean; overload;
  private
    FSetArray : TArray<T>;
    function GetEmpty: Boolean;
  public
    procedure Add(aValue: T);
    procedure AddSet(const setOfT: array of T); overload;
    procedure AddSet(const aSet: TSet<T>); overload;
    procedure Remove(aValue: T);
    procedure RemoveSet(const setOfT: array of T); overload;
    procedure RemoveSet(const aSet : TSet<T>); overload;
    function Contains(aValue: T): Boolean; overload;
    function Contains(const aSetOfT: array of T): Boolean; overload;
    function Contains(const aSet : TSet<T>): Boolean; overload;
    procedure Clear;
    property Empty: Boolean read GetEmpty;
  end;

implementation

procedure TSet<T>.Add(aValue: T);
begin
  if not Contains(aValue) then begin
    SetLength(FSetArray,Length(FSetArray)+1);
    FSetArray[Length(FSetArray)-1] := aValue;
  end;
end;

class operator TSet<T>.Add(const aSet: TSet<T>; aValue: T): TSet<T>;
begin
  Result.AddSet(aSet.FSetArray);
  Result.Add(aValue);
end;

class operator TSet<T>.Add(const aSet: TSet<T>; const aSetOfT: TArray<T>): TSet<T>;
begin
  Result.AddSet(aSet.FSetArray);
  Result.AddSet(aSetOfT);
end;

class operator TSet<T>.Add(const aSet1, aSet2: TSet<T>): TSet<T>;
begin
  Result.AddSet(aSet1.FSetArray);
  Result.AddSet(aSet2.FSetArray);
end;

procedure TSet<T>.AddSet(const setOfT: array of T);
var
  i : Integer;
begin
  for i := 0 to High(setOfT) do
    Self.Add(setOfT[i]);
end;

procedure TSet<T>.AddSet(const aSet: TSet<T>);
begin
  AddSet(aSet.FSetArray);
end;

procedure TSet<T>.RemoveSet(const setOfT: array of T);
var
  i : Integer;
begin
  for i := 0 to High(setOfT) do
    Self.Remove(setOfT[i]);
end;

procedure TSet<T>.RemoveSet(const aSet: TSet<T>);
begin
  RemoveSet(aSet.FSetArray);
end;

class operator TSet<T>.Subtract(const aSet1, aSet2: TSet<T>): TSet<T>;
begin
  Result.AddSet(aSet1.FSetArray);
  Result.RemoveSet(aSet2.FSetArray);
end;

class operator TSet<T>.Subtract(const aSet: TSet<T>;
  const aSetOfT: TArray<T>): TSet<T>;
begin
  Result.AddSet(aSet.FSetArray);
  Result.RemoveSet(aSetOfT);
end;

class operator TSet<T>.Subtract(const aSet: TSet<T>; aValue: T): TSet<T>;
begin
  Result.AddSet(aSet.FSetArray);
  Result.RemoveSet(aValue);
end;

class operator TSet<T>.In(aValue: T; const aSet: TSet<T>): Boolean;
begin
  Result := aSet.Contains(aValue);
end;

class operator TSet<T>.In(const aSetOf: TArray<T>; const aSet: TSet<T>): Boolean;
begin
  Result := aSet.Contains(aSetOf);
end;

class operator TSet<T>.In(const aSet1: TSet<T>; const aSet2: TSet<T>): Boolean;
begin
  Result := aSet2.Contains(aSet1.FSetArray);
end;

function TSet<T>.Contains(aValue: T): Boolean;
var
  i : Integer;
  c : IEqualityComparer<T>;
begin
  c := TEqualityComparer<T>.Default;
  Result := false;
  for i := 0 to Length(FSetArray)-1 do
    if c.Equals(FSetArray[i],aValue) then
      Exit(True);
end;

function TSet<T>.GetEmpty: Boolean;
begin
  Result := (Length(FSetArray) = 0);
end;

procedure TSet<T>.Clear;
begin
  SetLength(FSetArray,0);
end;

function TSet<T>.Contains(const aSetOfT: array of T): Boolean;
var
  i : Integer;
begin
  Result := High(aSetOfT) >= 0;
  for i := 0 to High(aSetOfT) do
  begin
    Result := Contains(ASetOfT[i]);
    if not Result then
      Exit(false);
  end;
end;

function TSet<T>.Contains(const aSet: TSet<T>): Boolean;
begin
  Result := Contains(aSet.FSetArray);
end;

procedure TSet<T>.Remove(aValue: T);
var
  i : Integer;
  c : IEqualityComparer<T>;
begin
  c := TEqualityComparer<T>.Default;
  for i := 0 to Length(FSetArray)-1 do
  begin
    if c.Equals(FSetArray[i],aValue) then
    begin
      SetLength(FSetArray,Length(FSetArray)); // Ensure unique dyn array
      if (i < Length(FSetArray)-1) then
        FSetArray[i] := FSetArray[Length(FSetArray)-1]; // Move last element
      SetLength(FSetArray,Length(FSetArray)-1);
      Break;
    end;
  end;
end;

end.

サンプル テスト プログラム:

program ProjectGenericSet;
{$APPTYPE CONSOLE}    
uses
  GenericSet in 'GenericSet.pas';

var
 mySet,mySet1 : TSet<Char>;
begin
  mySet.AddSet(['A','B','C']);
  WriteLn(mySet.Contains('C'));
  WriteLn(mySet.Contains('D'));  // False
  mySet := mySet + 'D';
  WriteLn(mySet.Contains('D'));
  WriteLn('D' in mySet);
  mySet := mySet - 'D';
  WriteLn(mySet.Contains('D'));  // False
  mySet := mySet + TArray<Char>.Create('D','E');
  WriteLn(mySet.Contains('D'));
  WriteLn(mySet.Contains(['A','D']));
  mySet1 := mySet;
  // Testing COW
  mySet1.Remove('A');
  WriteLn(mySet.Contains('A'));
  mySet1:= mySet1 + mySet;
  WriteLn(mySet1.Contains('A'));
  mySet := mySet1;
  mySet1.Clear;
  WriteLn(mySet.Contains('A'));
  ReadLn;
end.
于 2013-10-17T09:30:06.417 に答える
3

XE4 は Char 型の変数に WideChar (および String には WideString) を使用するため、警告が表示されるため、Char は 1 バイトではなく 2 バイトを使用するようになりました。String/Char で Unicode 文字を保持できるようになりましたが、同じ理由で、char のセットを使用することはできなくなりました (Delphi では固定サイズで、32 バイトのビット マップであり、最大 256 項目を保持できます)。

範囲 #0..#127 の文字のみを使用する場合 (ラテン語/通常の記号のみ)、単に Char -> AnsiChar を置き換えることができます (ただし、Char から割り当てると、別の警告が表示されます。使用する必要があります。明示的な型変換を抑制します)。

国別/Unicode シンボルが必要な場合、Delphi には「すぐに使用できる」構造はありませんが、この目的のために Tdictionary を使用できます。

type
  TEmptyRecord = record end;

  TSet<T> = class(TDictionary<T,TEmptyRecord>)
  public
    procedure Add(Value: T); reintroduce; inline;
    procedure AddOrSetValue(Value: T); reintroduce; inline;
    function Contains(Value: T):Boolean; reintroduce; inline;
  end;

procedure TSet<T>.Add(Value: T);
var Dummy: TEmptyRecord;
begin
  inherited AddOrSetValue(Value, Dummy);
end;

procedure TSet<T>.AddOrSetValue(Value: T);
var Dummy: TEmptyRecord;
begin
  inherited AddOrSetValue(Value, Dummy);
end;

function TSet<T>.Contains(Value: T): Boolean;
begin
  result := inherited ContainsKey(Value);
end;

もちろん、他の通常のクラスと同様に初期化します。ただし、それでも非常に効率的です (もちろん、「セット」は常に 256 アイテムの最大サイズに制限されていますが、高度に最適化されているため、「セット」ほど高速ではありません)。

または、ビットのマップとして unicode 文字の独自のセット クラスを作成することもできます。すべてのビットを保持するには 8kb のメモリが必要であり、「セット」とほぼ同じ速度になります。

于 2013-10-17T06:25:01.073 に答える
-1

Web からの 4 つの提案を参照してください。

if not (CharInSet(Key,['0'..'9',#8]) then key := #0;

から: http://www.activedelphi.com.br/forum/viewtopic.php?t=66035&sid=f5838cc7dc991f7b3340e4e2689b222a

于 2017-03-06T14:04:35.203 に答える