PDA

View Full Version : رفع اشکال فیلتر کامپوننت DevExpress : arguments are of the wrong type, are out of acceptable range,



HosseinSaberi
پنج شنبه 28 فروردین 1393, 17:27 عصر
با سلام خدمت دوستان و خوانندگان عزيز
حتماً براي دوستاني که با دلفي برنامه نويسي ميکنند نام شرکت DevExpress آشناست.
من هم از اين کامپوننت ها استفاده ميکردم اما چند وقتي بود که با اشکال مهمي برخورد کردم:
وقتي ميخواستم روي چند ستون همزمان فيلتر اعمال کنم با اين پيغام خطا مواجه ميشدم:
arguments are of the wrong type, are out of acceptable range, or are in conflict with one another
اين پيغام براي چيست؟
بعد از کلي تحقيق دي اينترنت متوجه شدم که اين خطا براي چه رخ ميدهد.
في الواقع اين خطا زماني رخ ميدهد که متِن فيلتِر جدول شامل تعدادي شرط باشد که با يکديگر به صورت AND ترکيب شده باشند. به مورد زير نگاه کنيد:
((Code = 1) OR (Code = 2)) AND ((L_Name = ''علي'') OR (F_Name = ''علي اصغر'')

براي رفع خطا چه بايد کرد؟
براي رفع اين خطا بايد بجاي استفاده از ترکيب AND براي فيلتر جدول از ترکيب OR استفاده نمود. به عنوان مثال مورد بالا به براي تبديل به OR به صورت زير نوشته ميشود:
((Code = 1) AND (L_Name = ''علي'')) OR ((Code = 1) AND (F_Name = ''علي اصغر'')) OR ((Code = 2) AND (L_Name = ''علي'')) OR ((Code = 2) AND (F_Name = ''علي اصغر''))

اما کامپوننت هاي شرکت DevExpress اين کار را نميکند و براي همين با خطا مواجه ميشويم.
براي رفع اين خطا من يک يونيت درست کردم که به صورت اتوماتيک مثال اول را به دوم تبديل ميکند.
براي رفع خطا مراحل زير را به ترتيب طي کنيد:
1- يونيت من را از
اينجا (http://dl.talant.ir/Com/Prg/Delphi/And2OrU.rar)
دانلود کنيد
2- يونيت دانلود شده را در کنار يونيتcxDBData قرار دهيد و يونيتcxDBData را با دلفي باز کنيد.
3- با استفاده از قسمت Use Unit يونيت من را (که دانلود کرده ايد) به يونيت cxDBData معرفي کنيد.
4- حالا در يونيت cxDBData به دنبال سطر زير بگرديد:
ADataSet.Filter := AFilterText;

و آنرا با خط زير جابجا کنيد:
ADataSet.Filter := And2Or(AFilterText);

يونيتي که من ايجاد کردم براي تبديل AND به OR از روش پيچيده اي استفاده ميکند. اما نقطه ضعفش اين است که فقط ميتوانيد روي 5 ستون به صورت همزمان فيلتر اعمال کنيد، براي افزايش اين تعداد با اندکي دقت در يونيت ميتوانيد اين کار را انجام دهيد.
اگر ايرادي توي يونيت پيدا شد و يا سوالي داشتيد و يا انتقادي به تابع من داشتيد ميتوانيد همينجا مطرح کنيد تا اگر توانستم پاسخ بدهم.
منبع تالانت (http://www.Talant.ir)

یوسف زالی
جمعه 29 فروردین 1393, 03:11 صبح
سلام.
کدتون رو دیدم. از ذات کار خوشم اومد برای همین خودم هم یک پیاده سازی ازش کردم.
این تابع And رو به Or تبدیل نمی کنه. فقط قوانین دمورگان رو در اون بسط می ده. تبدیل And به Or چیز دیگه هست.
تابع JoinAnds در پیاده سازی مشکل بزرگی داره. در بدترین حالت باید بازگشتی کار می کردید.
البته قصد من زیر سوال بردن ارزش کارتون نیست. خیلی هم خوبه که کدهاتون رو در اختیار برنامه نویسان دیگه قرار دادید. اما راههای بسیار بهتری هم برای این بسط موجوده. راههایی که محدودیتی در تعداد هم ندارند. من نمونه ای رو براتون می گذارم. همون طور که خواهید دید نمونه من هم مثل نمونه شما Not و Xor رو ساپورت نمی کنه. افزودنش کاری نداره ولی خروجی مورد قبول کامپوننتتون رو چون دقیق نمی دونم اضافه نکردم.


unit Unit1;


interface


uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;


type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;


var
Form1: TForm1;


implementation


uses StrUtils, Math;


{$R *.dfm}


procedure StandardizeExpression(var S: string);
begin
S := StringReplace(S, 'and', '&', [rfReplaceAll, rfIgnoreCase]);
S := StringReplace(S, 'or', '|', [rfReplaceAll, rfIgnoreCase]);
S := StringReplace(S, ' ', '', [rfReplaceAll]);
S := UpperCase(S);
end;


procedure SplitText(Text, Delimiter: string; Ret: TStrings);
var
prev, next: integer;
begin
if not Assigned(Ret) then
Exit;


Ret.Clear;


prev := 1;
repeat
next := PosEx(Delimiter, Text, prev);
if next = 0 then
Break;


Ret.Add(Copy(Text, prev, next -prev));
prev := next +length(Delimiter);
until false;
end;


procedure PickTokens(var S: string; TokenList: TStringList);
var
T: string;
i: integer;
begin
T := S;
T := StringReplace(T, '&', ',', [rfReplaceAll]);
T := StringReplace(T, '|', ',', [rfReplaceAll]);
T := StringReplace(T, '(', '', [rfReplaceAll]);
T := StringReplace(T, ')', '', [rfReplaceAll]);
T := T + ',';


TokenList.Sorted := true;
TokenList.Duplicates := dupIgnore;
SplitText(T, ',', TokenList);


for i := 0 to TokenList.Count -1 do
S := StringReplace(S, TokenList[i], '<' + IntToHex(i, 4) + '>', [rfReplaceAll]);
end;


function Evaluate(S: string): boolean;
function SingleEval(S: string): boolean;
begin
while pos('T&T', S) > 0 do S := StringReplace(S, 'T&T', 'T', [rfReplaceAll]);
while pos('T&F', S) > 0 do S := StringReplace(S, 'T&F', 'F', [rfReplaceAll]);
while pos('F&T', S) > 0 do S := StringReplace(S, 'F&T', 'F', [rfReplaceAll]);
while pos('F&F', S) > 0 do S := StringReplace(S, 'F&F', 'F', [rfReplaceAll]);


while pos('T|T', S) > 0 do S := StringReplace(S, 'T|T', 'T', [rfReplaceAll]);
while pos('T|F', S) > 0 do S := StringReplace(S, 'T|F', 'T', [rfReplaceAll]);
while pos('F|T', S) > 0 do S := StringReplace(S, 'F|T', 'T', [rfReplaceAll]);
while pos('F|F', S) > 0 do S := StringReplace(S, 'F|F', 'F', [rfReplaceAll]);


Result := S = 'T';
end;


var
f, t: integer;
begin
repeat
t := pos(')', S);
if t = 0 then
Break;


f := t;
while (f > 0) and (S[f] <> '(') do
dec(f);


S := Copy(S, 1, f -1) + IfThen(SingleEval(Copy(S, f +1, t -f -1)), 'T', 'F') + Copy(S, t +1, length(S));
until false;


Result := SingleEval(S);
end;


function DemorganSimplify(S: string; Cnt: integer): string;
var
i, j: integer;
T, X: string;
B: boolean;
begin
Result := '';


for i := 0 to Floor(Power(2, Cnt)) -1 do
begin
T := S;
X := '';
for j := 0 to Cnt -1 do
begin
B := (i and Floor(Power(2, j))) > 0;
X := X + IfThen(j > 0, ' AND ', '') + IfThen(B, '', ' NOT ') + '<' + IntToHex(j, 4) + '>';
T := StringReplace(T, '<' + IntToHex(j, 4) + '>', IfThen(B, 'T', 'F'), [rfReplaceAll]);
end;


if Evaluate(T) then
Result := Result + IfThen(Result <> '', #13' OR '#13, '') + '(' + X + ')';
end;
end;


procedure PutTokens(var S: string; TokenList: TStringList);
var
i: integer;
begin
for i := 0 to TokenList.Count -1 do
S := StringReplace(S, '<' + IntToHex(i, 4) + '>', '(' + TokenList[i] + ')', [rfReplaceAll]);
end;


procedure TForm1.Button1Click(Sender: TObject);
var
S: string;
lst: TStringList;
begin
lst := TStringList.Create;


S := Edit1.Text;
StandardizeExpression(S);
PickTokens(S, lst);
S := DemorganSimplify(S, lst.Count);
PutTokens(S, lst);


Memo1.Lines.Text := S;


lst.Free;
end;


end.




همون طور که می بینید تابع محدودیتی از نظر تعداد نداره. خروجی کاملا بسط یافته، و دقیقا هم ارز ورودی اولیه است. می تونید جملات اضافی رو با توابع ساده دیگه حذف کنید اما نتیجه فرقی نخواهد کرد.

روش کار تقریبا مثل کامپایلره:
پیدا کردن متغیرها
جایگذاری متغیرها
شمارش دودویی
تست شمارش در جدول ارزش متغیرها
ارزش دهی این تست
قرار دادن این جمله در خروجی در صورت مثبت بودن تست ارزش دهی
جایگذاری معکوس متغیرها

موفق و موید باشید.

HosseinSaberi
جمعه 29 فروردین 1393, 10:49 صبح
سلام
ممنون از بذل توجهتون، اما من نتونستم از تابع شما استفاده کنم و در حقیقت خروجی اون تهی بود. یه مثال هم درست کردم که ضمیمه میکنم تا خودتون در صورت امکان مشکل رو رفع کنید چون من کدهایی که نوشته بودید رو تا وسطش بیشتر نخوندم در حقیقت من از کدهایی هم که خودم مینویسم چیزی سر در نمیارم چه برسه به کدهایی که شما نوشتید (که کامنت هم نداره).:قهقهه:
ضمناً تابعی که من نوشتم مال خیلی وقت پیشه (تقریباً دوسال و نیم پیش) ولی تا اونجایی که یادمه خلاصه کار تابعی که من نوشتم اینه که And های بعد از پرانتز رو بر میداره و تبدیل میکنه به And داخل پرانتز . در حقیقت ترانسپوزه یک ماتریس رو محاسبه میکنه که اون محدودیت 5 ستون هم در حقیقت بعد ماتریس هست. ماتریسی که بین هر ردیف با ردیف دیگه یک And وجود داره و تابع من ماتریس رو ترانهاده میکنه و ANd ها رو هم تبدیل میکنه به Or. من چون رشتم حسابداریه به مسائل ریاضی خیلی وارد نیستم ولی تا حالا فکر میکردم قوانین دمورگان مربوط به مجموعه هاست (اجتماع و اشتراک و ...)

یوسف زالی
جمعه 29 فروردین 1393, 12:40 عصر
پرانتز گذاریتون یکی کم داشت.
همون طور که می بینید کنترل خطایی در سیستم وجود نداره. دلیلش هم این بود که روی کل کار و تحلیل و پیاده سازیش بیش از یک ساعت وقت نذاشتم.
نمونه برنامتون رو اصلاح کردم. نکته ای که وجود داره اینه که در حال حاضر برنامه فاصله میان علی اصغر رو از بین می بره. برای این کار باید تابع استانداردایز رو یکم تغییر بدید تا توکن های درون '' رو دست نزنه.
این برنامه رو برای درک ساز و کار این جور کارها نوشتم نه این که بصورت عملیاتی ازش استفاده بشه.
برای این کار باید یکم اصلاحات و کنترل خطا اضافه بشه که می گذارم به عهده خودتون.
دلیل اون NOT ها رو هم که می دونید دیگه.. خروجی رو می شه به راحتی ساده تر از این کرد اما قاعدتا باید همین خروجی هم در فیلتر کار کنه.
تبدیل AND به OR هم این جوره:

A AND B = NOT(NOT A OR NOT B) // one of de morgn rules

HosseinSaberi
جمعه 29 فروردین 1393, 13:05 عصر
دلیل اون NOT ها رو هم که می دونید دیگه..

نه متاسفانه نمیدونم
به نظرم زیادی اومد

یوسف زالی
جمعه 29 فروردین 1393, 14:49 عصر
نمی دونم در رشته شما تا چه حد این چیزا رو درس می دن، اما این اساس جدولیه به نام جدول ارزش دهی بولی برای یک جمله بولی با n متغیر.
می تونید در ساده سازی حذفشون کنید اما لزومی بر این کار نیست.
موفق باشید.

HosseinSaberi
جمعه 29 فروردین 1393, 15:21 عصر
پس من حق داشتم خیلی روی این تابع وقت بذارم
چون واقعاً هیچ کدوم از این مبانی رو نمیدونستم ولی تابع رو درست کردم (همش رو ذهنی) و فکر کنم تقریباً دو روزی طول کشید.
به هرحال ممنون