Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursive Block in Swift

I've been trying to convert this block of code from Objective-C (taken from https://gist.github.com/mikeash/1254684) to Swift. I've used it successfully to repeat a block of code based on results from API calls. Is there a better method to do this in Swift?

dispatch_block_t recursiveBlockVehicle(void (^block)(dispatch_block_t recurse)) {
    return ^{
        block(recursiveBlockVehicle(block));
    };
}

Any help is appreciated.

like image 522
XCool Avatar asked Mar 16 '23 05:03

XCool


1 Answers

Here’s a straight translation of your obj-c version:

func recursiveBlockVehicle(block: @escaping (()->Void)->Void) -> ()->Void {
    return { block(recursiveBlockVehicle(block: block)) }
}

// test usage
var i = 5
let block = recursiveBlockVehicle { recurse in
    if i > 0 {
        print("\(i--) ")
        recurse()
    }
    else {
        println("blastoff!")
    }
}

block()  // prints 5 4 3 2 1 blastoff!

(I’ve subbed out the dispatch_block_t since it doesn’t feel necessary in the Swift version, but you can use it instead of ()->Void instead if you prefer)

For the 1-parameter version, you can use generics rather than the obj-c id approach, so it can create type-safe single-parameter recursive blocks:

func recursiveBlockVehicle<T>(block: @escaping (T, (T)->Void)->Void) -> (T)->Void {
    return { param in block(param, recursiveBlockVehicle(block: block)) }
}

let block1: (Int)->Void = recursiveBlockVehicle { i, recurse in
    if i > 0 {
        print("\(i) ")
        recurse(i-1)
    }
    else {
        print("blastoff!")
    }
}


block1(5)  // prints 5 4 3 2 1 blastoff!

(and Swift overloading means you don’t have to give them different names)

...and if you want your recursive function to return a value:

func recursiveBlockVehicle<T,U>(block: @escaping (T, (T)->U)->U) -> (T)->U {
    return { (param: T)->U in block(param, recursiveBlockVehicle(block: block)) }
}

let factorial: (Int)->Int = recursiveBlockVehicle { i, factorial in
    return i > 1 ? i*factorial(i-1) : 1
}

factorial(4)  // returns 24

Note, this last one is really the only one you need – since T and U can be Void, it serves the purposes of the zero-parameter non-returning one too, though you have to write your closures to take two parameters (the first one of which you can ignore, since it’ll be void), i.e.:

let block: ()->() = recursiveBlockVehicle { _, recurse in

If you like this sort of stuff you should check out the 2014 WWDC Advanced Swift video which as an example of a memoizing recursive function caller.

like image 143
Airspeed Velocity Avatar answered Mar 28 '23 06:03

Airspeed Velocity