Quantcast
Channel: XibXor » variadic
Viewing all articles
Browse latest Browse all 2

Functional OR in PHP and Obj C

$
0
0

Javascript’s OR operator, ||, is extremely convenient. It returns the first true value, otherwise the last false value.

That is, (3 || 0 || 1) would evaluate to 3.
And (0 || 0 || null) would evaluate to null

An OR operator that doesn’t coalesce its return value into a boolean is extremely useful for things like default text.
You might want to do something like this: UsernameField.value = (username || "No Username");

Javascript can get away with the dynamic behavior of the OR operator quite easily because it has the concept of object falseness for some objects like empty strings. In stronger-typed languages, object references are true if not pointing to nil.
In strongly typed compiled languages like C, the OR operator evaluates to a boolean because otherwise it would have to be overloaded and account for a ton of different types.

One would then wonder why a language like PHP, dynamically typed, with the concept of object falseness, fails to have this *extremely* convenient behavior?

I didn’t find one on the web, so I’ve implemented a non-coalescing OR for PHP. The name ‘OR’ is reserved in PHP, T_LOGICAL_OR, so I use _or instead. It’s a variadic function, meaning it takes a variable number of args.

function _or(){
  for ($i = 0, $n = func_num_args(); $i < $n; ++$i){
    $arg = func_get_arg($i);
    if ($arg) return $arg;
   

  return $arg;
}

PHP has a function called func_get_args() which returns an array of the arguments. I’m not sure if the internal storage of the arguments is an array, or there is some transmutation overhead in pulling them all out at once and doing a foreach.
I just used what is safer performance-wise, func_get_arg, fetching each one seperately.
If you’d like to do some performance benchmarks on func_get_arg vs func_get_args, that’d be awesome! Please post your findings in the comments. I am all about performance!

In newer versions of PHP > 5.3, PHP has the ?: operator
(“” ?: “2nd” ?: “3rd”) would evaluate to “2nd”
(“1st” ?: “2nd” ?: “3rd”) would evaluate to “1st”


On to Objective C…

Objective C is a bit harder to implement for. Everything is an Object that responds to messages, well except for primitives, structs, and pure data (we can ignore those.) It can be done with a little elbow grease.
Variadics, methods that take an indeterminate number of arguments, have to have their arguments nil terminated in the C family of languages. I suppose you can say PHP wins here..but hell, this might be the only time PHP wins so celebrate sparingly.
Nil-termination is problematic for us because we may want to pass a nil reference to our OR function and have it consider it ‘empty’. Instead, we will make our or function take a numArgs parameter, and create a macro to make it easy to call.

First we write an isEmpty function. By checking for nil references, count of 0, or length of 0, we should cover almost all objects that follow standard naming convention.

static inline BOOL isEmpty(id thing) {
    return thing == nil
    || thing == [NSNull null]
    || ([thing respondsToSelector:@selector(length)]
        && [thing length] == 0)
    || ([thing respondsToSelector:@selector(count)]
        && [thing count] == 0);
}

Next we write our fairly simple variadic or function:

static inline id or(int numArgs, ...)
{
    id obj;
    va_list args;
    
    for (va_start(args, numArgs); numArgs; --numArgs)
        if (!isEmpty(obj = va_arg(args, id))) break;
    
    va_end(args);
    
    return obj;
}

To make it easier to call our ‘or’ function, without having to pass the argument count, we can make a macro.

#define __VA_NARGS__(...)  (sizeof((id[]){__VA_ARGS__})/sizeof(id))

#define OR(...) or(__VA_NARGS__(__VA_ARGS__), __VA_ARGS__)

__VA_NARGS__ is just a clever macro to get the argument count. We call `or` with the number of arguments, followed by the arguments. In C/ObjC there is no built-in macro for getting argument count at compile-time. __VA_NARGS__ is a clever savior that simply divides the size of a compile-time temporary id[] array by sizeof(id) to calculate the argument count.

Example usage of the functional or:

NSLog(@"%@", OR(nil,@"", @{@"foo": @"bar"}, @[]));
// outputs {foo = bar;}

NSLog(@"%@", OR(nil,@"", @{}, @[]));
// outputs []

NSLog(@"%@", OR(nil,@"", @[], @"", @{}));
// outputs {}

NSLog(@"%@", OR(nil,@"", @[], @"", nil));
// outputs (null)

NSLog(@"%@", OR(nil,@"", @[], @"", [NSNull null]));
// outputs <null>

NSLog(@"%@", OR(nil,[NSNull null], @[], @"", @"", @"woot"));
// outputs "woot"

Functions like this are plenty fast when using the ‘inline’ function modifier, so feel free to use ‘OR’ even where performance is necessary.

As with all code I post, the above functions are public domain.

Download FunctionalOr from Github

EDIT (5/5/2013) Please see my newer, more efficient code for a functional OR macro in Objective C: Non-Coalescing OR operator for Objective C


Viewing all articles
Browse latest Browse all 2

Latest Images

Trending Articles





Latest Images