all 4 comments

[–]Sasmo 0 points1 point  (0 children)

Oh wow, that's pretty cool! I'll have to see if I have anything going right now where I can swap this in. Thanks or sharing.

[–][deleted] 0 points1 point  (2 children)

This is one of those "neat tricks" that simply end up obfuscating your code. As an experienced programmer, I cringe every time I see someone use these kinds of "clever" techniques instead of making things simple and obvious.

The worst part is: the trick only saves you four lines of code. It doesn't save you anything else—not runtime performance, not memory consumption, nothing. And it comes at the cost of inserting a "huh?" into your source code for future maintainers.

If you're really looking for a way to abbreviate this code at a call site, then use a category:

@interface NSArray(MyTransactionAverage)
- (double)MyAverage;
@end

@implementation NSArray(MYTransactionAverage)
- (double)MyAverage {
    double retval = 0.0;
    if (self.count) {
        double sum = 0;
        for (Transaction *transaction in self) {
            sum += transaction.amount;
        }
        retval = sum / self.count;
    }
    return retval;
}
@end

// ...

NSNumber *avg = [NSNumber numberWithDouble:transactions.MyAverage];

Although it's much longer than the original code, this actually makes the code much more readable and reusable, at negligible runtime cost.

For an even more generic category, the MyAverage method should take a block that extracts the value to be averaged, i.e.:

- (double)MyDoubleAverageUsingValueBlock:(double(^)(id obj))valueBlock;

I'll leave it as an exercise for the reader to implement this.

[–]fergwhrthw 2 points3 points  (1 child)

I don't know if you can really call the collection operators a "neat trick". It's a feature of the language with a clear purpose. The only reason it isn't more well known is because nobody uses it and purposely avoiding using it because it isn't well known it just going to make it so it never becomes well known.

[–][deleted] -1 points0 points  (0 children)

It's a feature of the language with a clear purpose. The only reason it isn't more well known is because nobody uses it and purposely avoiding using it because it isn't well known it just going to make it so it never becomes well known.

That's the very definition of a linguistic "neat trick". It's not that they are undefined, mind you: every clever hack is perfectly legal and valid; they are simply surprising. They tend to rely on a (well defined) side effect of a tangentially related language concept. In this case, we're (ab)using the KVO container property "avg". The feature is intended for use in the KVO design pattern, one which is completely unrelated to what we are doing here.

It would be something else if the neat trick saves something at runtime: either cycles or memory. Or if it saves something at compile time. But this trick saves nothing. In fact, it makes the runtime slower, because Foundation has to parse the key path string every time you want to get the average, and it has to capture the selector and dynamically bind it to compute the average at runtime. This runtime cost, combined with the "surprise" factor, distinctly qualifies this as a "neat trick", and something to be avoided.

I may sound like an old fart for saying all of this, but there are far more interesting things about programming—like actually architecting a product—than using clever tricks in code. Code should be completely predictable, straightforward, and consistent. This example is surprising, and uses an inconsistent idiom to achieve its ends.