Mark
Mark

Reputation: 12525

How to write this recursive function in Swift?

I have this recursive function that works fine in JavaScript:

var distributeSpacings = function (cols, maxSpacing) {
  if (cols == 1) {
      return [maxSpacing];
  }

  var results = [];

  for (var i = 0; i <= maxSpacing; i++) {
    var subSpacings = distributeSpacings(cols - 1, maxSpacing - i);
    for (var j = 0; j < subSpacings.length; j++) {
      var subSpacing = subSpacings[j];
      results.push([i].concat(subSpacing));
    }
  }

  return results;
}

console.log(distributeSpacings(3, 3));

// prints:
// [ [ 0, 0, 3 ],
//   [ 0, 1, 2 ],
//   [ 0, 2, 1 ],
//   [ 0, 3, 0 ],
//   [ 1, 0, 2 ],
//   [ 1, 1, 1 ],
//   [ 1, 2, 0 ],
//   [ 2, 0, 1 ],
//   [ 2, 1, 0 ],
//   [ 3, 0, 0 ] ]

I want to write it in Swift. The type safety makes this difficult - this is what I have so far:

extension Array {
    func concat(toAdd: Any) -> [Element] {
        if let _ = toAdd as? Int {
            return self + ([toAdd as! Element])
        } else {
            return self + (toAdd as! [Element])
        }
    }
}

func permutateSpacings (columns: Int, maxSpacing: Int) -> Any {
    if columns == 1 {
        return [maxSpacing]
    }

    var results = [Any]()

    for (var i = 0; i <= maxSpacing; i++) {
        var subSpacings = permutateSpacings(columns - 1, maxSpacing: maxSpacing - 1) as! [Int] // I suspect the problem is with this line
        for (var j = 0; j < subSpacings.count; j++) {
            let subSpacing = subSpacings[j]
            results.append([i].concat(subSpacing))
        }
    }
    return results
}

print(permutateSpacings(3, maxSpacing: 3) as! [[Int]])
// prints:
// Could not cast value of type 'Swift.Array<protocol<>>' (0x1175ef0d8) to 'Swift.Array<Swift.Int>' (0x1175ef028).

Upvotes: 2

Views: 321

Answers (2)

R Menke
R Menke

Reputation: 8391

With thanks to sunshine for finding 1 -> i. I got a different result and thought I messed up the algorithm.


No need for your Array extension, but it would be correctly written like so:

extension Array {
    func concat(toAdd: [Element]) -> [Element] {
        return self + toAdd
    }
    func concat(toAdd: Element) -> [Element] {
        return self + [toAdd]
    }
}

No need for Any at all. The type is always known.

for spacing in 0...maxSpacing for subSpacing in subSpacings Swiftier syntax for iterations.

func permutateSpacings(columns: Int, maxSpacing: Int) -> [[Int]] {
    if columns == 1 {
        return [[maxSpacing]]
    }

    var results : [[Int]] = [] // var/let : Type = Value

    for spacing in 0...maxSpacing {
        let subSpacings = permutateSpacings(columns - 1, maxSpacing: maxSpacing - spacing)
        for subSpacing in subSpacings {
            results.append(([spacing] + subSpacing))
        }
    }
    return results
}

Upvotes: 1

sunshinejr
sunshinejr

Reputation: 4854

So I managed to fix your function with few tweaks.

First problem was that your function returned Any, instead of [Any]. You then casted Any to [Int] and well, the crash happened. Second problem was your implicit casting as! [Int]. It is a bad practice using ! without guard/if let (in places where it can crash, and here it surely can), and you really don't need that cast anyways. Third problem was in the line:

var subSpacings = permutateSpacings(columns - 1, maxSpacing: maxSpacing - 1)

You have 1 instead of needed i (from your JS algorithm). Working function below:

extension Array {
    func concat(toAdd: Any) -> [Element] {
        if let _ = toAdd as? Int {
            return self + ([toAdd as! Element])
        } else {
            return self + (toAdd as! [Element])
        }
    }
}

func permutateSpacings (columns: Int, maxSpacing: Int) -> [Any] {
    if columns == 1 {
        return [maxSpacing]
    }

    var results = [Any]()

    for (var i = 0; i <= maxSpacing; i++) {
        var subSpacings = permutateSpacings(columns - 1, maxSpacing: maxSpacing - i)
        for (var j = 0; j < subSpacings.count; j++) {
            let subSpacing = subSpacings[j]
            results.append([i].concat(subSpacing))
        }
    }
    return results
}

print(permutateSpacings(3, maxSpacing: 3))

Upvotes: 3

Related Questions