Efficiently compare an integer against a static list of integers in Delphi?
Asked Answered
S

4

5

Similarly to any way to compare an integer variable to a list of integers in if statement but in Delphi, how can I test whether an integer variable is contained in a static "list" (loosely speaking - a set that is predefined) of integers (maybe a list of consts, or a list of explicit values) in an if statement?

Now I would do something like this:

if (x = 1) or
   (x = 2263) or
   (x = 263553) or
   (x = whatever_int) then
begin
  //do something;
end;

But I'm trying to avoid lenghty sequences of or conditions.

I'm using Delphi XE6, so if there's something new to achieve this in a clean way, please advise me.

Stav answered 13/5, 2015 at 10:47 Comment(15)
How do you currently store this list of integers? How large is the list? Do you care about runtime efficiency more than clarity of code?Monumental
Since you require the compaison to take place in an if statement (even marked in bold), I don't dare to suggest a case statement, which I would use.Champ
@TomBrunberg If that works depends on whatever_int being a const or not.Berthoud
@Stefan Yes, I should have mentioned that.Champ
@Stefan whatever_int is just a placeholder, not real code, it means "here the list continues". Maybe the question isn't so clear (sorry, I'm not native English speaker). I'm trying to solve the problem of comparing an integer variable to a known "list" (not a Delphi TList) of values (maybe they're consts, maybe they're explicit numbers).Stav
BTW, I've edited my question to better explain I'm talking of comparing an integer to a certain number of known values. These values are NOT an array... maybe I have to put them in an array, but then this should be an answer ;-) My question is not a true duplicate of #29697932Stav
Unfortunately I can't post as answer, but if, as you say, all values you check against are known at compiletime, you can use a case statement: case x of /n 1, 2263, 263553, declared_const, other_declared_const, 1048000:/n // do domething/n else // optionally do something else/n end;/n,,, The characters /n means new line for normal pascal formattingChamp
I did an edit to help demonstrate why this isn't a dupe of the post David pointed to.Jackboot
@TomBrunberg Yes maybe this is the way to go. Thanks. David, what about deleting duplicate flag?Stav
Perhaps you could make clear, once and for all, in what form you hold this collection of potential integers. Is it a list of values that is known at compile time? It would help if you would answer the three questions that I asked you in the very first comment.Monumental
@DavidHeffernan yes, please read the question now that GreenAsJade edited it to be clearer. As for your questions: the list is now "stored" as simple values, as my example clearly demonstrates. The list can be from 2 to 10 or so values. Maybe some situations will require runtime efficiency, maybe others will not, so maybe a couple of suggestions for the two possible situations? Thanks.Stav
Please will you do what I ask. Then I will be happy to reopen. You have failed to define "efficient". And I still have not seen a statement from you as to how many values you have.Monumental
@DavidHeffernan we're just chasing ourselves, you know, this may happen on the web :-) please read my last (not this!) comment.Stav
@TomBrunberg, would you please post your comment as an answer, if you like to? I'll upvote it. Thanks.Stav
@Stav No point anymore that David already posted it, he also makes a few good points regarding terminology (efficient). Another time, thanks.Champ
M
4

Your current approach is good. The compiler will produce efficient code. If efficiency is of the utmost importance, then you can choose the order of the comparisons so that the most common values are checked first. This requires knowledge of the distribution of your input data.

Another syntactical approach is the case statement. For instance:

case x of
1, 2263, 263553, whatever_int:
  // do other stuff
end;

This results in similar (in fact I suspect identical) code to the if statement. But it is perhaps more concise and easier to read. And in some scenarios the compiler is able to produce a jump table.

If efficiency is of the utmost importance to you then do make sure that you inspect the code that the compiler emits, for release compiler options, and perform profiling.

I do wonder what you really mean by efficient. In programming that term has a specific meaning relating to runtime performance. But I wonder if you are actually more concerned with writing clear and concise code, in which case efficient is the wrong term. The fact that your question talks about avoiding repeated or operators makes me doubt that efficiency is what matters to you.

On the other hand, if runtime performance is what matters then consider giving up on code clarity and implementing an indexed jump table. But don't ever optimise code without first identifying that it is a bottleneck, and profiling any changes you make.

Monumental answered 13/5, 2015 at 13:22 Comment(4)
The case sometimes beats even ordered ifs... Don't ask me why, I haven't checked the produced code from that answer.Myotome
@Myotome Compiler sometimes produces jump tables. I think.Monumental
I often prefer the case version for tests such as this; in the checks I've done, the generated code is almost identical, and I think the readability and maintainability of the code is considerably improved.Vaginismus
Sorry for my poor English, actually by efficient I mean clear, readable. So the case solution will probably be what I was searchin for.Stav
P
2

You can make the code much cleaner to read by writing a little function:

//NOTE: This function is written in a way that would be compatible with any version of Delphi.
//Since you're using XE6, there are some improvement options available to you:
// E.g. You can make the function inline, use "for in", use a record helper, use generics
function ContainsInteger(const AValue: Integer; const AArray: array of Integer): Boolean;
var
  I: Integer;
begin
  Result := False;
  for I := Low(AArray) to High(AArray) do
  begin
    if (AArray[I] = AValue) then
    begin
      Result := True;
      Exit;
    end;
  end;
end;

Now you can call your function as follows:

if ContainsInteger(x, [1, 2263, 263553, whatever_int]) then
begin
  //do something;
end;

NOTE: This would be slower than a case statement as described in David's answer, but it has the potential to be significantly faster for longer lists of numbers.

If it's likely this function will be used frequently with longer lists:

  • You can specify a requirement that the input array be given in ascending order. (Behaviour would be undefined otherwise.)
  • And modify the function to perform a binary search.
  • Then for example, a list of 32 numbers would require at most 5 comparisons to find a match using binary search; whereas it would need an average of 16 for the case statement.

NB: As always when fine-tuning optimisations like this:

  1. Use profiling to confirm whether you need it here.
  2. Use profiling to confirm whether you get the benefit you expect.
Publicist answered 13/5, 2015 at 14:28 Comment(1)
+1 Thanks for sharing this code! I've always missed this in Delphi 7. I would like to suggest to rename it to IntInList(const whatToFind: Integer; const inThese ...). Shorter, more international. (Programmers with less english knowledge can not always recall the word: contains.) PS: Once we have made measurements about running 4096 cycles of for-next compared to a recursive balanced tree with "only 12 steps": the first was 20x faster.Pinole
I
1

given the use case, I would use TList<Integer>, and its methods:

  • procedure AddRange(const Values: array of T);
  • Contains(const Value: T): Boolean;
Ileac answered 17/1, 2020 at 14:41 Comment(0)
C
0

Why not just simple:

x : integer;
begin
  if x in [1,2,5] then
  begin
    showmessage('x in 1,2,5');
  end;
end;
Casket answered 17/1, 2020 at 15:52 Comment(2)
The in operator can only be used for elements from zero to 255. The question exemplifies with values larger than that.Hughs
Please explain how this works and why it helps. Since somebody commented what they consider a shortcoming of your solution you might want to show the result make plausible that it works.Slobbery

© 2022 - 2024 — McMap. All rights reserved.