How can I write like "x == either 1 or 2" in a programming language? [duplicate]
Asked Answered
R

22

25

Possible Duplicate:
Why do most programming languages only have binary equality comparison operators?

I have had a simple question for a fairly long time--since I started learning programming languages.

I'd like to write like "if x is either 1 or 2 => TRUE (otherwise FALSE)."

But when I write it in a programming language, say in C,

( x == 1 || x == 2 )

it really works but looks awkward and hard to read. I guess it should be possible to simplify such an or operation, and so if you have any idea, please tell me. Thanks, Nathan

Rellia answered 6/10, 2010 at 6:35 Comment(9)
I had thought such a function might have been available in any language since this sort of operation was done very often.Rellia
develop your own programming language?Meteoritics
It's a necessary part of learning a programming language, you need to learn to read code and understand these constructs at a glance. Boolean logic is just the tip of the iceburg. The essential problem is as people do not speak logic notation, for instance it is common to say: "if x = a or b then..." where we should say: "if x=a or a=b".Raisin
should be community wiki: it's poll/discussionPiquet
Dumb question (at least for the case given) where x equals 1 or x equals 2 is no more clumsy than x is either 1 or 2. Its a more interesting question where the set of possible values is larger and there are some sensible answers.Biographer
@Murph: So a better scenario would be (devsLike == "pizza" || devsLike == "cake") ?Indifferentism
@Ryan - maybe I've just been doing this for too long... being a programmer is, in part, about being explicit about meaning and devsLike either pizza or cake is just a contraction of devsLike pizza or devsLike cake that works in english and if you can't see that both ways then, its seems to me, that as a developer you're in trouble. That's probably overly aggressive... but it goes toward mindset.Biographer
@Murph, Not that dumb... The more concise code, the more time you save. If the language supports that tools, then the coder owns his style, not the language makes itself worst.Wrinkle
@haibison have you ever seen any APL? That's concise... its also fairly close to "write only" code, likewise forth. And I was careful to note that looking at an "IN" relationship is a bit more interestingBiographer
Z
37

Python allows test for membership in a sequence:

if x in (1, 2):
Zolazoldi answered 6/10, 2010 at 6:37 Comment(18)
Wow, it works! But is it normal to use an array?Rellia
That's not an array, it's a structure called a tuple. And it is normal to use one for up to 5 or so values; beyond that it's more efficient to use a set (containment testing in a sequence is O(n), whereas containment testing in a set is O(1)).Zolazoldi
I see. I learned many great things from you today. Thank you!Rellia
One of the many examples showing python gives you beautiful code compared to some of the more traditional languages :).Ithaca
@Ignacio Vazquez-Abrams: how can it be O(1) with sets? We were taught it'll be O(log n) at best...Dele
@progo: Because sets are represented as hashtables, which have constant average lookup time.Leitmotif
@Matajon: oh, Python never ceases to amaze me :)Dele
Well in Python a tuple is just an immutable list.Prate
But wouldn't creating the set be O(n)?Olivero
So if I wrote a piece of code (forgive me for not knowing the set syntax) if x is set(1, 2, 3, 4, 5, 6, 7, 8, 9, 10):, it would create the set every time it got to that line, so a tuple would have been quicker anyway. At least so it seems to me.Olivero
You create the set ahead of time, and refer to it by name each time it's needed.Zolazoldi
Same kind of syntax in Pascal, plus you can have either a set (1, 2, 3) or an interval (1..3). That was one of the major letdowns of C for me when learning it after Pascal, the power of switch/case, versus case/of, which in Pascal allows testing in sets and intervals.Emission
I don't know any python, but I suppose it's O(1) on average, if you test many values over a constant set. If you're testing a single value it's still O(n) because you have to create the set...Elidiaelie
@configurator: In Python 3, you could use a set by writing if x in {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}.Mindymine
Kobi: The access is very close to O(1) even when testing a single value - but the cost of creating the set is ignored so very often.Olivero
Hash-table lookup isn't O(1), it's O(k); i.e. it depends on the particular hash-table and the hash function rather than number of items in the table.Rogovy
If the Python compiler was smart it would build a constant set once during compile/parse and keep it around forever. However, is it that smart?Amylo
@Zan: It can't know whether set refers to __builtins__.set or some module/local object, so it does the call at runtime.Zolazoldi
M
18

An extension version in C#

step 1: create an extension method

public static class ObjectExtensions
{
    public static bool Either(this object value, params object[] array)
    {
        return array.Any(p => Equals(value, p));
    }
}

step 2: use the extension method

if (x.Either(1,2,3,4,5,6)) 
{
}
else
{
}
Meteoritics answered 6/10, 2010 at 9:5 Comment(4)
@Piers Myers: Why create a new List<T>?Askja
@Piers Myers Just use (Array.IndexOf(values, value) > -1) so you don't have to create a new list.Mussulman
Wow, I like this a lot! Extension methods are so flippin' awesome!Hawks
@Andrew Koester - yep that's even faster, thanks - it's always good to learn something new.Antiperistalsis
N
17

While there are a number of quite interesting answers in this thread, I would like to point out that they may have performance implications if you're doing this kind of logic inside of a loop depending on the language. A far as for the computer to understand, the if (x == 1 || x == 2) is by far the easiest to understand and optimize when it's compiled into machine code.

Nebiim answered 6/10, 2010 at 7:57 Comment(3)
But this depends on the language. I would expect the code generated by Python for x in (1, 2) to be at least as well optimized as that generated by Python for x == 1 or x == 2 Of course in C the equivalent opposite is true.Banda
True, but as mentioned by intuited this would come down to the underlying implementation. But I don't think that answers the OP's question, even if it's true and worth mentioning.Northern
@Banda - just FYI: In Python 3.1.2, the performance for both is nearly the same. I just time d it, and I get 0.042 seconds for in and 0.047 seconds for or if x is 1, and 0.08 seconds for both if x is 2 or 0. So if you're doing microoptimization in Python, look at the order of the conditions you're testing...Argal
A
11

When I started programming it seemed weird to me as well that instead of something like:

(1 < x < 10)

I had to write:

(1 < x && x < 10)

But this is how most programming languages work, and after a while you will get used to it.

So I believe it is perfectly fine to write

( x == 1 || x == 2 )

Writing it this way also has the advantage that other programmers will understand easily what you wrote. Using a function to encapsulate it might just make things more complicated because the other programmers would need to find that function and see what it does.

Only more recent programming languages like Python, Ruby etc. allow you to write it in a simpler, nicer way. That is mostly because these programming languages are designed to increase the programmers productivity, while the older programming languages' main goal was application performance and not so much programmer productivity.

Apocynaceous answered 6/10, 2010 at 8:55 Comment(1)
You can do the first example in Python!Tressatressia
N
9

It's Natural, but Language-Dependent

Your approach would indeed seem more natural but that really depends on the language you use for the implementation.

Rationale for the Mess

C being a systems programming language, and fairly close to the hardware (funny though, as we used to consider a "high-level" language, as opposed to writing machine code), it's not exactly expressive.

Modern higher-level languages (again, arguable, lisp is not that modern, historically speaking, but would allow you to do that nicely) allow you to do such things by using built-in constructs or library support (for instances, using Ranges, Tuples or equivalents in languages like Python, Ruby, Groovy, ML-languages, Haskell...).

Possible Solutions

Option 1

One option for you would be to implement a function or subroutine taking an array of values and checking them.

Here's a basic prototype, and I leave the implementation as an exercise to you:

/* returns non-zero value if check is in values */
int is_in(int check, int *values, int size);

However, as you will quickly see, this is very basic and not very flexible:

  • it works only on integers,
  • it works only to compare identical values.

Option 2

One step higher on the complexity ladder (in terms of languages), an alternative would be to use pre-processor macros in C (or C++) to achieve a similar behavior, but beware of side effects.

Other Options

A next step could be to pass a function pointer as an extra parameter to define the behavior at call-point, define several variants and aliases for this, and build yourself a small library of comparators.

The next step then would be to implement a similar thing in C++ using templates to do this on different types with a single implementation.

And then keep going from there to higher-level languages.


Pick the Right Language (or learn to let go!)

Typically, languages favoring functional programming will have built-in support for this sort of thing, for obvious reasons.

Or just learn to accept that some languages can do things that others cannot, and that depending on the job and environment, that's just the way it is. It mostly is syntactic sugar, and there's not much you can do. Also, some languages will address their shortcomings over time by updating their specifications, while others will just stall.

Maybe a library implements such a thing already and that I am not aware of.

Northern answered 6/10, 2010 at 8:34 Comment(0)
D
7

that was a lot of interesting alternatives. I am surprised nobody mentioned switch...case - so here goes:

switch(x) {
case 1:
case 2:
  // do your work
  break;
default:
  // the else part
}
  1. it is more readable than having a bunch of x == 1 || x == 2 || ...
  2. more optimal than having a array/set/list for doing a membership check
Devilkin answered 6/10, 2010 at 8:27 Comment(5)
I don't agree that it looks more readable. x==1 || x==2 is pretty straight forward compared to a switch in my opinion. It may be more optimal than an array check, but it's probably not more optimal than the original statement.Blanket
it may not be that obvious when you have two comparisons, but when you have say 10 comparisons then doing a x == 1 || ... x == 10 can get overwhelming and error prone also. although you are comparing the same var "x" you repeat it 10 times - and you need to go through all 10 of htem to make sure one of them is not a "y"Devilkin
Then I would still definitely prefer an implementation iterating over an array instead of a switch case, which is going to take a lot of LOCs every time for no significant advantage in either readability or maintainability, while being prone to errors every time your re-implement it.Northern
the "iterate over array" solution is readable only in scripting languages. if you think about it, it is that readable in C, C++ or Java. you will find swithc...case more readable instead. Also switch...case almost directly translates into machine code (jump instructions) and hence better performance than iterate over lists.Devilkin
It's hardly more readable or terse; even less so for a short list of elements.Northern
I
6

Err, what's wrong with it? Oh well, if you really use it a lot and hate the looks do something like this in c#:

#region minimizethisandneveropen
public bool either(value,x,y){       
    return (value == x || value == y);
}
#endregion

and in places where you use it:

if(either(value,1,2))
   //yaddayadda 

Or something like that in another language :).

Ithaca answered 6/10, 2010 at 7:52 Comment(5)
You could avoid the condition in your function: return (value == x || value == y);Cacuminal
woops, so true haha. Its early ;)Ithaca
A small comment, if I may - this is a little confusing. I'd expect value to go first (but that's just me), and x is used twice, once as value and once as the first argument.Elidiaelie
@dark_charlie: Aarh, I'm unable to think how :( Please hint me!Singlehanded
well, it was just to show the idea, but I fixed them for you :).Ithaca
M
6

I doubt I'd ever do this, but to answer your question, here's one way to achieve it in C# involving a little generic type inference and some abuse of operator overloading. You could write code like this:

if (x == Any.Of(1, 2)) {
    Console.WriteLine("In the set.");
}

Where the Any class is defined as:

public static class Any {
    public static Any2<T> Of<T>(T item1, T item2) {
        return new Any2<T>(item1, item2);
    }
    public struct Any2<T> {
        T item1;
        T item2;
        public Any2(T item1, T item2) {
            this.item1 = item1;
            this.item2 = item2;
        }
        public static bool operator ==(T item, Any2<T> set) {
            return item.Equals(set.item1) || item.Equals(set.item2);
        }
        // Defining the operator== requires these three methods to be defined as well:
        public static bool operator !=(T item, Any2<T> set) {
            return !(item == set);
        }
        public override bool Equals(object obj) { throw new NotImplementedException(); }
        public override int GetHashCode() { throw new NotImplementedException(); }
    }
}

You could conceivably have a number of overloads of the Any.Of method to work with 3, 4, or even more arguments. Other operators could be provided as well, and a companion All class could do something very similar but with && in place of ||.

Looking at the disassembly, a fair bit of boxing happens because of the need to call Equals, so this ends up being slower than the obvious (x == 1) || (x == 2) construct. However, if you change all the <T>'s to int and replace the Equals with ==, you get something which appears to inline nicely to be about the same speed as (x == 1) || (x == 2).

Moskowitz answered 6/10, 2010 at 9:45 Comment(1)
+1: because I doubt I'd do that, but it's a pretty nice answer.Northern
K
3

In php you can use

$ret = in_array($x, array(1, 2));

Kim answered 6/10, 2010 at 7:42 Comment(2)
Holy moly: a memory allocation, object construction, function call, linear search, destruction, and garbage collect to do something equivalent to two compare-and-branch opcodes. PHP fulfills all my expectations yet again.Graphomotor
@Graphomotor Surprising last sentence from the likes of a gentleman of such highness as yourself.Keheley
S
3

As far as I know, there is no built-in way of doing this in C. You could add your own inline function for scanning an array of ints for values equal to x....

Like so:

inline int contains(int[] set, int n, int x)
{ 
  int i;
  for(i=0; i<n; i++)
    if(set[i] == x)
      return 1;

  return 0;
}

// To implement the check, you declare the set
int mySet[2] = {1,2};
// And evaluate like this:
contains(mySet,2,x) // returns non-zero if 'x' is contained in 'mySet'
Semiquaver answered 6/10, 2010 at 7:42 Comment(0)
C
3

In .Net you can use Linq:

int[] wanted = new int{1, 2};

// you can use Any to return true for the first item in the list that passes
bool result = wanted.Any( i => i == x );

// or use Contains
bool result = wanted.Contains( x );

Although personally I think the basic || is simple enough:

bool result = ( x == 1 || x == 2 );
Cleon answered 6/10, 2010 at 8:19 Comment(1)
Oooooh, you changed the code without updating the comment. Naughty =)Tetzel
T
3

In T-SQL

where x in (1,2)
Thermos answered 6/10, 2010 at 10:43 Comment(0)
M
3

In COBOL (it's been a long time since I've even glanced briefly at COBOL, so I may have a detail or two wrong here):

IF X EQUALS 1 OR 2
...

So the syntax is definitely possible. The question then boils down to "why is it not used more often?"

Well, the thing is, parsing expressions like that is a bit of a bitch. Not when standing alone like that, mind, but more when in compound expressions. The syntax starts to become opaque (from the compiler implementer's perspective) and the semantics downright hairy. IIRC, a lot of COBOL compilers will even warn you if you use syntax like that because of the potential problems.

Mesitylene answered 6/10, 2010 at 12:38 Comment(2)
And, when the syntax starts becoming opaque from the compiler implementor's perspective, it also becomes opaque from the programmer's perspective. Typically, if a reasonable parser can parse something, a human can also.Sharilyn
Humans can parse more complicated expressions before breaking down. Evidence: pretty much all of natural language. :DMesitylene
R
2

Thanks Ignacio! I translate it into Ruby:

[ 1, 2 ].include?( x )

and it also works, but I'm not sure whether it'd look clear & normal. If you know about Ruby, please advise. Also if anybody knows how to write this in C, please tell me. Thanks. -Nathan

Rellia answered 6/10, 2010 at 6:52 Comment(5)
For C you create an array with a sentinel at the end and you iterate through the array looking for either a match or the sentinel. If the resultant index contains the sentinel then return false, otherwise return true.Zolazoldi
That's completely unreadable!Algophobia
@UpTheCreek: Welcome to Ruby. Power of Python, legibility of Perl.Zolazoldi
I probably wouldn't do it this way for only two numbers but it's certainly not unreadable for a Ruby programmer.Enarthrosis
Well, there's no syntactic sugar for it in Ruby, but it is readable...Solanum
T
2

Perl 5 with Perl6::Junction:

use Perl6::Junction 'any';
say 'yes' if 2 == any(qw/1 2 3/);

Perl 6:

say 'yes' if 2 == 1|2|3;

This version is so readable and concise I’d use it instead of the || operator.

Tedmann answered 6/10, 2010 at 8:10 Comment(0)
N
2

Pascal has a (limited) notion of sets, so you could do:

if x in [1, 2] then

(haven't touched a Pascal compiler in decades so the syntax may be off)

Naughty answered 6/10, 2010 at 15:52 Comment(1)
It is however limited to ordinal types with <= 256 possible values. The Delphi compiler implements sets as one or more 32 bit bitmasks. In then uses binary ORs to check for membership.Orobanchaceous
F
1

In java:

 List list = Arrays.asList(new Integer[]{1,2});
 Set set = new HashSet(list);
 set.contains(1)
Frig answered 6/10, 2010 at 8:31 Comment(3)
You can do it in one line: if (new HashSet<Integer>(Arrays.asList(1, 2, 3)).contains(2))Predella
Nice, I wish I was using 1.5>= version with variable number of parameters.Frig
@mavro you can use even less code, skip the HashSet: if (Arrays.asList(1, 2, 3).contains(2))Doc
I
1

A try with only one non-bitwise boolean operator (not advised, not tested):

if( (x&3) ^ x ^ ((x>>1)&1) ^ (x&1) ^ 1 == 0 )

The (x&3) ^ x part should be equal to 0, this ensures that x is between 0 and 3. Other operands will only have the last bit set.

The ((x>>1)&1) ^ (x&1) ^ 1 part ensures last and second to last bits are different. This will apply to 1 and 2, but not 0 and 3.

Irving answered 6/10, 2010 at 8:50 Comment(0)
P
1

You say the notation (x==1 || x==2) is "awkward and hard to read". I beg to differ. It's different than natural language, but is very clear and easy to understand. You just need to think like a computer.

Also, the notations mentioned in this thread like x in (1,2) are semantically different then what you are really asking, they ask if x is member of the set (1,2), which is not what you are asking. What you are asking is if x equals to 1 or to 2 which is logically (and semantically) equivalent to if x equals to 1 or x equals to 2 which translates to (x==1 || x==2).

Paraglider answered 6/10, 2010 at 9:15 Comment(0)
D
0

I have a macro that I use a lot that's somewhat close to what you want.

#define ISBETWEEN(Var, Low, High) ((Var) >= (Low) && (Var) <= (High))

ISBETWEEN(x, 1, 2) will return true if x is 1 or 2.

Duester answered 6/10, 2010 at 13:31 Comment(0)
D
0

Neither C, C++, VB.net, C#.net, nor any other such language I know of has an efficient way to test for something being one of several choices. Although (x==1 || x==2) is often the most natural way to code such a construct, that approach sometimes requires the creation of an extra temporary variable:

  tempvar = somefunction(); // tempvar only needed for 'if' test:
  if (tempvar == 1 || tempvar == 2)
    ...

Certainly an optimizer should be able to effectively get rid of the temporary variable (shove it in a register for the brief time it's used) but I still think that code is ugly. Further, on some embedded processors, the most compact and possibly fastest way to write (x == const1 || x==const2 || x==const3) is:

  movf  _x,w             ; Load variable X into accumulator
  xorlw const1           ; XOR with const1
  btfss STATUS,ZERO      ; Skip next instruction if zero
   xorlw const1 ^ const2 ; XOR with (const1 ^ const2)
  btfss STATUS,ZERO      ; Skip next instruction if zero
   xorlw const2 ^ const3 ; XOR with (const2 ^ const3)
  btfss STATUS,ZERO      ; Skip next instruction if zero
   goto NOPE

That approach require two more instructions for each constant; all instructions will execute. Early-exit tests will save time if the branch is taken, and waste time otherwise. Coding using a literal interpretation of the separate comparisons would require four instructions for each constant.

If a language had an "if variable is one of several constants" construct, I would expect a compiler to use the above code pattern. Too bad no such construct exists in common languages.

(note: Pascal does have such a construct, but run-time implementations are often very wasteful of both time and code space).

Damning answered 6/10, 2010 at 23:46 Comment(0)
V
-2

return x === 1 || x === 2 in javascript

Vanya answered 6/10, 2010 at 8:13 Comment(3)
Not sure why this has been down-voted, though some more explanation would help. In this case the use of === would make sure that 1 or 2 passed, but that "1" or "2" would fail, while == would implicitly covert the strings and compare the values. You could want either way of doing it in a dynamically typed language.Cleon
This doesn't really answer the question at all.Dumond
I think it's downvoted because it doesn't answer the question at all. The OP doesn't want to know about ways to do the equivalent statement in other languages, but ways to write it more expressively.Northern

© 2022 - 2024 — McMap. All rights reserved.