In C, you can assign two variables in one line with
b = a = sqrt(10);
In Delphi
b := a := Sqrt(10);
is not allowed.
With IfThen, there is an "alternative" for the ternary operator ?: as discussed in Delphi - Equivalent to C#'s ternary operator? In summary, IfThen doesn't seem absolutely necessary.
So maybe there is also something like this:
function AssignAndReturn(var LHS: Integer; RHS: Integer): Integer;
begin
LHS := RHS;
Result := RHS;
end;
(...)
var
a, b: Integer;
begin
b := AssignAndReturn(a, Round(Sqrt(10)));
I'm not trying to "make everything look like C". I just noticed, that sometimes it would be nice to "reuse" the right hand side of an assignment in the same line again. (See Lazarus/Free Pascal: How to improve coding style (to avoid using endless loop) of a while loop whose boolean expression is reassigned every pass for example.)
An assignment statement is not an expression. It does not, and will never, yield a value. The only way to assign to multiple variables in one statement is via a function.
Based on this question and your previous question I would say that you are mistaken in trying to fight against the language. Code into the language you are using. In C you might write
b = a = sqrt(10);
But in Pascal the language wants you to write it like this
a := sqrt(10);
b := a;
Do it this way. Don't create obscure functions just so that you can cram it all onto a single line. The two lines above could not be clearer to read. Stop trying to force other languages into Pascal. They don't fit.
The short answer is:
No. What you want is not possible. And arguably would be of little if any benefit in Delphi.
First, let's discuss a more practical example. To be frank, a = b = f(x); is a rather poor example.
It's rare that you arbitrarily want the same value assigned to 2 variables.
Order of operation above has to break the left to right convention otherwise it would first resolve a = b then ? = f(x).
So there's little justification to favour that over b = f(x); a = b;.
Basically it's good for byte count (code golf) not readability.
So instead, let's consider the following, more practical scenario:
//Option 1
if ((b = f(x)) > 42)
//use b
There's no Delphi equivalent to the above, but it's at least as readable/maintainable to write:
//Option 2
b := f(x);
if (b > 42) then
//use b
There is another option to consider; especially if Option 1 is sitting in the middle of a larger function. Remember small functions are more maintainable and easier for a compiler to optimise. So consider:
//Option 3
...
ProcessRule42(f(x));
...
//Where Rule42 is is implemented as:
procedure ProcessRule42(b: Integer);
begin
if (b > 42) then
//use b
end;
Delphi doesn't really seem to be suffering due to not being able to write: if (b := f(x)) > 42 then //use b.
Is there any real benefit to Option 1?
If the Option 2 is at least as good, why would one ever bother with Option 1. The benefit is seen when you consider scoping rules.
//b does not exist
if ((var b = f(x)) > 42) {
//use b
}
//b does not exist
// Using the Delphi approach, this becomes:
//b does not exist
var b = f(x);
if (b > 42) {
//use b
}
//b still exists
Delphi simply does not have the same scoping concerns. Delphi variables are all declared at the beginning of the method, and available for the whole method.
Conclusion
This brings us back to what others have been trying to explain. Delphi is a different language with slightly different approaches to some problems. While I applaud you for examining other options and considering what concepts can be borrowed from other languages: be careful of trying to force-fit some concepts where they don't belong.
If you break too many Delphi paradigms, your code may become unnecessarily difficult for Delphi programmers to maintain.
Related
if exp1 and exp2 and exp3 then
//something
In Delphi is the order of evaluation exp1, exp2 and exp3 (of course they are all Boolean) is defined or random?
The order of evaluation is only defined when Boolean Short-Circuit Evaluation is enabled. Then, as the documentation in Complete Versus Short-Circuit Boolean Evaluation explains, evaluation is left to right.
If Boolean Short-Circuit Evaluation is not enabled, the order is undefined. The following code demonstrates this:
{$APPTYPE CONSOLE}
function A: boolean;
begin
Result := True;
Write('A ');
end;
function B: string;
begin
Result := '';
Write('B ');
end;
function C: Integer;
begin
Result := 0;
Write('C ');
end;
begin
{$O+}
Writeln('short circuit on');
{$B-}
if A and (B = '') and (C = 0) then Writeln;
Writeln('short circuit off');
{$B+}
if A and (B = '') and (C = 0) then Writeln;
end.
For the first one, you get printed A B C, and for the second one you get B A C.
And that was compiled for Win32 - to make this spicy, and bring home the point of this being Undefined, lets run it on Win64 where we get A B C in both cases.
You might say: "Ok, but maybe just B was called first but the evaluation of the boolean expression B = '' is evaluated in the correct order." Let's take a look at the assembler code that gets executed:
if A and (B = '') and (C = 0) then Writeln;
0040B17C 8D45E8 lea eax,[ebp-$18]
0040B17F E864EAFFFF call B
0040B184 837DE800 cmp dword ptr [ebp-$18],$00
0040B188 0F94C3 setz bl
0040B18B E81CEAFFFF call A
0040B190 22D8 and bl,al
0040B192 E891EAFFFF call C
0040B197 85C0 test eax,eax
0040B199 0F94C0 setz al
0040B19C 22D8 and bl,al
Nope:
call B, compare to ''
call A, and it with the result of the string comparison
call C, compare with 0, and it with the result of the previous and
Which translates to (written in left to right order):
((B = '') and A) and (C = 0)
Addendum: Because I saw this mentioned in another answer and discussed in the comments. Parentheses are no solution to force order of evaluation of operands. You can only group operations but the compiler might still decide to evaluate the right operand first.
In the code above there is no way to put any parentheses to get A B C simply because the compiler flipped the first two operands. It then however executed the operators from left to right and that is something to easily confuse - the question was not whether the first or the second and was executed first but the order of the boolean expressions - the operands.
The documentation will guide you of course.
I have found that in more complex if statements the compiler does not do what even experienced programmers expected when they wrote the code, often because they are including logical nots and mixing boolean ands and ors. Remembering the precedence and association rules is not always easy.
My tip is to be explicit about what you want and put brackets around the terms. The compiler won't mind and it makes it clear what you intended when you or someone else reviews the code.
With compound if statements laying out the logical parts together can aso help humans to understand what was meant.
For example:
if( ((pObjectRef<>nil) And (pObjecRef.Property=SomeValue)) Or
((pAlternateObject<>nil) And
((pAlternatObject.Property=AnotherValue) Or
(pAlternatObject.Property=YetAnotherValue))) ) then
begin
...
end
Is there a way to remove part of condition in IF statement depending if I need it or not. Next code is example, because in my code there are a lot of user defined functions and procedures in my language:
IF A THEN
Q := TQuery.Create(Application);
IF B AND C AND D AND E AND Q.FieldByName('Example').AsInteger = 1 then
BEGIN
...
END
So, let's say after creating TQuery that I have imported some data into it (I didn't write that part of code here). Is there a way to remove part with Q in the second IF statement if the Q wasn't created (because the condition A was not satisfied) or do I have to writhe the whole IF statement again just without Q part?
I was thinking using something like CASE to check if Q is created and if it is not, just to skip that part of sentence. If it is possible, how can I skip it?
Quick and dirty way
IF B AND C AND D AND E AND (NOT A OR Q.FieldByName('Example').AsInteger = 1) then
As a note, try to keep your if conditions simpler.
EvaluationRequested := A AND C ...
QueryNeedsAval := NOT A OR ...
if EvaluationRequested AND QueryNeedsAval then
begin
...
end;
The intuitive answer would be that the loop is never entered. And this seems to be case in all tests I could come up with. I'm still anxious and always test it before entering the loop.
Is this necessary?
No, it is not necessary.
The documentation clearly states :
for counter := initialValue to finalValue do statement
or:
for counter := initialValue downto finalValue do statement
...
If initialValue is equal to finalValue, statement is executed exactly once. If initialValue is greater than finalValue in a for...to statement, or less than finalValue in a for...downto statement, then statement is never executed.
There is no need for anxiety.
If we want to examine further what happens, let's make a few examples. Consider first :
program Project1;
{$APPTYPE CONSOLE}
var
i : integer;
begin
for i := 2 to 1 do WriteLn(i);
end.
This produces a compiler hint:
[dcc32 Hint] Project1.dpr(6): H2135 FOR or WHILE loop executes zero times - deleted
So the compiler will simply throw away a loop with constants that produce no loop iterations. It does this even with optimizations turned off - no code is produced for the loop at all.
Now let's be a bit more clever :
program Project1;
{$APPTYPE CONSOLE}
var
i, j, k : integer;
begin
j := 2;
k := 1;
for i := j to k do WriteLn(i);
end.
This actually compiles the loop. The output is as below:
Project1.dpr.8: for i := j to k do WriteLn(i);
004060E8 A1A4AB4000 mov eax,[$0040aba4] {$0040aba4 -> j = 2}
004060ED 8B15A8AB4000 mov edx,[$0040aba8] {$0040aba8 -> k = 1}
004060F3 2BD0 sub edx,eax {edx = k - j = -1}
004060F5 7C2E jl $00406125 {was k-j < 0? if yes, jmp to end.}
004060F7 42 inc edx {set up loop}
004060F8 8955EC mov [ebp-$14],edx
004060FB A3A0AB4000 mov [$0040aba0],eax
00406100 A118784000 mov eax,[$00407818] {actual looped section}
00406105 8B15A0AB4000 mov edx,[$0040aba0]
0040610B E8E8D6FFFF call #Write0Long
00406110 E8C3D9FFFF call #WriteLn
00406115 E8EECCFFFF call #_IOTest
0040611A FF05A0AB4000 inc dword ptr [$0040aba0] {update loop var}
00406120 FF4DEC dec dword ptr [ebp-$14]
00406123 75DB jnz $00406100 {loop ^ if not complete}
Project1.dpr.9: end.
00406125 E88EE1FFFF call #Halt0
So, the very first thing a loop does is to check whether it needs to execute at all. If the initial is greater than the final (for a for..to loop) then it skips straight past it entirely. It doesn't even waste the cycles to initialize the loop counter.
There are some edge-cases in which you may be surprised to discover that the code does unexpectedly enter the loop. And still other cases where you may be tempted to pre-check whether to call the loop. But before I get into those details, I want to try impress on you the importance of not pre-checking your loop with an if condition.
Every line of code, no matter how easy to understand draws attention. It's more to read and more to confirm is correct. So if it's not important, or if it's technically redundant: it's best left out.
A for loop is conceptually translated as follows:
Initialise loop index to starting value.
If iteration constraint is valid (e.g. Index <= EndValue in case of forward loop):
Perform iteration (code within loop block/statement)
Perform loop control operations (increment loop index)
Repeat 2
Otherwise continue at first instruction after loop.
The way in which Step 2 is checked, makes an extra if condition before the loop completely redundant.
So if you (or another developer) is later maintaining code with a redundant if condition, they're left to wonder:
Is the line correct?
It seems redundant; is there a special condition it's trying to handle?
If it currently serves no purpose, perhaps it was intended to prevent calling the loop on a different condition?
In simple case, redundant lines of code can create some confusion. In more complex cases, they can result in whole new sections of irrelevant code being developed; that tries to cater for irrelevant scenarios implied by legacy redundant code.
Recommendation: Stamp out redundant code as much as possible. Including redundant pre-checks for "should the loop execute at all".
The most important benefit of stamping out redundant code is that: it correctly draws attention to peculiar cases whenever special handling actually is required.
There are 2 potential pitfalls, and the first is the more dangerous one as it deals with implicit type conversion. So it may not always be easy to detect. The following code was tested on rextester using fpc, but I have verified the same issue on Delphi 2007 / 2009 in the past.
//fpc 2.6.2
program UnexpectedForIteration;
{$MODE DELPHI}
{ Ensure range-checking is off. If it's on, a run-time error
prevents potentially bad side-effects of invalid iterations.}
{$R-,H+,W+}
var
IntStart, IntEnd, IntIndex: Integer;
UIntStart, UIntEnd, UIntIndex: Cardinal;
IterCount: Integer;
begin
Writeln('Case 1');
IntStart := High(Integer) - 1;
IntEnd := -IntStart;
UIntStart := Cardinal(IntStart);
UIntEnd := Cardinal(IntEnd);
{This gives a clue why the problem occurs.}
Writeln('From: ', IntStart, ' To: ', IntEnd);
Writeln('From: ', UIntStart, ' To: ', UIntEnd, ' (unsigned)');
Writeln('Loop 1');
IterCount := 0;
for IntIndex := IntStart to IntEnd do Inc(IterCount);
Writeln(IterCount);
Writeln('Loop 2');
IterCount := 0;
{ The loop index variable is a different type to the initial &
final values. So implicit conversion takes place and:
IntEnd **as** unsigned is unexpectedly bigger than IntStart }
for UIntIndex := IntStart to IntEnd do Inc(IterCount);
Writeln(IterCount, ' {Houston we have a problem}');
Writeln();
Writeln('Case 2');
UIntStart := High(Cardinal) - 2;
UIntEnd := 2;
IntStart := Integer(UIntStart);
IntEnd := Integer(UIntEnd);
{This gives a clue why the problem occurs.}
Writeln('From: ', UIntStart, ' To: ', UIntEnd);
Writeln('From: ', IntStart, ' To: ', IntEnd, ' (signed)');
Writeln('Loop 3');
IterCount := 0;
for UIntIndex := UIntStart to UIntEnd do Inc(IterCount);
Writeln(IterCount);
Writeln('Loop 4');
IterCount := 0;
{ The loop index variable is a different type to the initial &
final values. So implicit conversion takes place and:
UIntStart **as** signed is unexpectedly less than UIntEnd }
for IntIndex := UIntStart to UIntEnd do Inc(IterCount);
Writeln(IterCount, ' {Houston we have a problem}');
end.
The output is as follows:
Case 1
From: 2147483646 To: -2147483646
From: 2147483646 To: 2147483650 (unsigned)
Loop 1
0
Loop 2
5 {Houston we have a problem}
Case 2
From: 4294967293 To: 2
From: -3 To: 2 (signed)
Loop 3
0
Loop 4
6 {Houston we have a problem}
In many cases the problem is resolved by ensuring the same types are used for loopIndex, initialValue and finalValue. As this means there won't be an implicit type conversion, and the loop will reliably iterate as the initialValue and finalValue would suggest.
It would be easier if the compiler emits appropriate warnings for implicit type conversion in for loops. Unfortunately fpc didn't; I don't recall whether Delphi 2007/2009 does; and have no idea whether any recent versions do.
However, the preferred approach would be to favour container iteration syntax (pushing responsibility for 'correct' iteration on the enumerators). E.g.: for <element> in <container> do ...;. This should not iterate empty containers provided the enumerator's methods are implemented correctly.
The only time I'd say a pre-check is worth considering is:
when for in is not feasible for some reason
and the loop index needs to be zero-based
and support large unsigned integers (High(Integer) < index < High(Cardinal))
because this leaves no space for a reliable sentinel less than all possible initial values.
Even in this case, consider using an Int64 loop index instead of if (initialValue <= finalValue) then for ....
The second pitfall involves what I would in any case consider to be a design flaw. So the problem can be avoided entirely by rather being aware of this design consideration. It is demonstrated in code that looks as follows:
if Assigned(AnObject) then
for LIndex := 0 to AnObject.Count - 1 do ...;
In this case the if condition may in fact be necessary as a result of dubious design. Certainly, if AnObject hasn't been created, you do not want to access its Count property/method. But the dubious aspect of the design is the fact that you're uncertain whether AnObject exists. Yes, you may have employed a lazy-initialisation pattern. But it doesn't change the fact that in the above code, there's no way to differentiate between: "zero iterations" because AnObject doesn't exist or because AnObject.Count = 0.
I'd like to point out that when code has many redundant if Assigned(AnObject) then (or similar) lines, it leads to one of the problems I described in section 1. Local code caters for 2 possibilities. And by extension, client code also caters for 2 possibilities. And by induction, this problem eventually leaks throughout the code-base.
The solution is to first and foremost limit the cases where existence of AnObject is uncertain.
It's much easier to ensure an empty object with Count = 0 is guaranteed to be created (typically only affecting a small number of places in code).
It's far more work to deal with the ripple effects of a large number of places where the object might not exist yet; yielding 2 possible states and code paths.
If lazy-initialisation is required, try to ensure the code surface where existence is optional is kept as small as possible.
I'm writing custom variant PhysUnit which is something like VarConv but more advanced, allowing not only adding and subtracting, but also multiplying and dividing units, with real or complex quantities, it works fine, but extremely slow.
Problem is, this custom variant wraps around some other variant (simple types like integer or double, or another custom like VarComplex), so when performing DoAdd, DoSubtract, it firstly checks if both operands have same family (length for example), and then adds quantities converting one of them if neccesary:
Left:=Left + Right*multiplier;
something like that, here Left and Right are variants.
Compiler turns this line into series of calls:
_varCopy(tmp,Left);
_varAdd(tmp,Right*multiplier);
_varClear(Left);
_varCopy(Left,tmp);
_varClear(tmp);
while in fact, it would be enough of _varAdd, without allocating/deallocating memory for temporary variant and all these workarounds.
The sad part is: I can't just write _varAdd(Left,Right), it's not linked in VCL.
So the question is: is it possible to call it anyway and make it as "clean" as possible, without nasty calls to direct memory address which might change when compiled with different options or other libraries added?
You can't call the underscored functions because the compiler turns the underscore into # making it impossible to use it as an identifier.
But assembler functions are allowed to call them. You can use the original declaration and change the TVarData into Variant so you don't have to cast the Variants all the time.
procedure _VarAdd(var Left: Variant; const Right: Variant);
asm
jmp System.Variants.#VarAdd
end;
procedure _VarSub(var Left: Variant; const Right: Variant);
asm
jmp System.Variants.#VarSub
end;
begin
_VarAdd(Left, Right);
end;
But using Variants if you want to be fast, isn't the right way. They are very slow and don't have that much help from the compiler's optimizer like integer arithmetic, where i := i + 1; is compiled without the need for a temporary variable/cpu-register.
You can make Variants faster by using special handing for common cases:
if TVarData(Left).VType = TVarData(Right).VType then
begin
case TVarData(Left).VType of
varSingle:
begin
TVarData(Left).VSingle := TVarData(Left).VSingle + TVarData(Right).VSingle * M;
Exit;
end;
varDouble:
begin
TVarData(Left).VDouble := TVarData(Left).VDouble + TVarData(Right).VDouble * M;
Exit;
end;
end;
end;
// use the slow function for all other cases
_VarAdd(Left, Right * M);
If you want to use clean syntax like:
Left := Left + Right*multiplier;
then you have to let the compiler generate the code. And since you are using variants, the generated code will perform appallingly badly.
If you want to call a function directly to perform the operation, then there's no point in using variants. You can create a type, presumably a record, add some methods to the type, and call those methods directly.
However, if you want to make a type that supports mathematical operators, and you care about performance, then you should use operator overloading on records.
1) Does anyone know if it is possible to loop through an irregular enumeration in Delphi (XE)?
Looping over a normal enumeration is ok. From Delphi Basics:
var
suit : (Hearts, Clubs, Diamonds, Spades);
begin
// Loop 3 times
For suit := Hearts to Diamonds do
ShowMessage('Suit = '+IntToStr(Ord(suit)));
end;
But, if 'suit' instead is declared as
var
suit : (Hearts=1, Clubs, Diamonds=10, Spades);
it loops 10 times. Not suprising, but I would like to loop 3. The only solution I've found so far is converting an enumeration to a set and use the 'for ... in'-loop like on delphi.about.com.
So, if answer to question 1) is no, then:
2) How to convert from enumeration to set in Delphi?
The context I am using it in is a component array of edit-boxes (TEdit) that has an irregular numbering (edit1, edit5, edit7, edit3, ...). While it is possible to reorder all the edit-boxes, it removes the reason of using enumeration as a flexible way to allow addition of an edit-box in the middle of the enumeration.
I do not have a Delphi compiler at hand right now, but I tink that gabr's approach can be rather significantly improved by doing
type
TSuit = (Hearts = 1, Clubs, Diamonds = 10, Spades);
const
Suits: array[0..3] of TSuit = (Hearts, Clubs, Diamonds, Spades);
Who knows, maybe it doesn't even compile.
type
TSuit = (Hearts=1, Clubs, Diamonds=10, Spades);
var
suit: TSuit;
suitEnum: array [1..4] of TSuit;
//initialization
suitEnum[1] := Hearts;
suitEnum[2] := Clubs;
suitEnum[3] := Diamonds;
suitEnum[4] := Spades;
for suit in suitEnum do
DoSomething(suit);
I always use
var
s: TSuit;
begin
for s := Low(TSuit) to High(TSuit) do
{something};
end;
Loop using Ord(Hearts) to Ord(Spades) ?
A dirty option, useful for small enumerations:
type
TSuit = (Hearts = 1, Clubs, Diamonds = 10, Spades);
var
Suit: TSuit;
begin
for Suit in [Hearts, Clubs, Diamonds] do
WriteLn(Ord(Suit));
Works nice in Delphi 2007. Don't know about older versions. Be aware, using for Suit in [Hearts..Diamonds] do has the same problem as your loop.Btw, I use WriteLn() because I tested this in a console application. :-)
It should be understood (and often isn't) that the moment you put hard ordinal assignments into an enumeration, it ceases for all intents to be a Pascalian enumerated type - it just becomes a "bag of constants", which is not the same thing. This is what C-programmers call enumerations. However, a Pascalian enumerated type is ORDINAL in all criterion: It has discrete consecutive values that respond meaningfully to the base operations ORD, PRED, SUCC. Enumerations in C don't do this, and neither do enums in Pascal once you force the ordinals apart.
THIS is the reason that Delphi's RTTI basically refuses to return type information once this has been done. To all intents the type is essentially a tkUnknown, and has to be treated as a 'bag' of compile-time constants. It is only because it still plays lip service to being an ordinal and has (sometimes shaky) support for use in sets that people are led into believing it should still behave like a proper enumerated type. It's better to just understand it for what it really is: a nod to enumerated values in C. Avoid mixing the coding metaphor!
If you do this, then your solution becomes obvious: you use an enumerated type (a proper one) to index a corresponding array of CONSTANTS. Then you can make the ordinals whatever you want, and the enums retain their full RTTI definitions as a proper enumeration. So: your ENUMERATED TYPE contains proper unchanged ordinal values. You get your funny numbers by indexing the constants array using the enumeration -ergo array [MyEnums] of byte = (1,3,8,22,99,whatever)