Function signature

A closure insists on having a function with the signature:


(_) -> _


I don't understand what this means. I thought it would be the same as (Any) -> Any, but the compiler won't accept the function I'm trying to use, which looks like (T) -> Range<T> . The generic T has been declared as Comperable.

Accepted Reply

Seems you have found a working solution, but creating a `Dictionary<Range<T>, [T]>` and then convert it to `[Range<T>, Int]` seems to be inefficient.


You can write something like this:

struct GenericDataDistibutor2<T: Comparable & Hashable> {
    var data: [T]
    var dataRanges: [Range<T>]
    var dataBins: [(Range<T>, Int)] {
        guard data.count > 0 && dataRanges.count > 0 else {return []} //<- This line may not be needed
        return dataRanges.map {r in (r, data.reduce(0) {$0 + (r.contains($1) ? 1 : 0)})}
    }
}


Testing code:

var testData = (1...1000).map {_ in Int.random(in: 1...9)}
let ranges = [1..<4, 4..<7, 7..<10]
let distribution = GenericDataDistibutor(data: testData, dataRanges: ranges)
let distribution2 = GenericDataDistibutor2(data: testData, dataRanges: ranges)
for bins in [distribution.dataBins, distribution2.dataBins] {
    print("--")
    for item in bins {
        print(item.0.lowerBound, item.0.upperBound, item.1)
    }
}


Output example:

--

1 4 320

4 7 324

7 10 356

--

1 4 320

4 7 324

7 10 356


Some differences.

- Your code may crash, when `data` contains any value out of ranges.

- My code may generate non-sorted `dataBins` when `dataRanges` is not sorted.

Replies

Could you show the code itself with the declarations and the complete error message ?

Swift shows such messages when it cannot collect enough info about the types of `_`. (The types of the two `_` may be different.)


You should better take it as there's some type-related error near a closure, the actual cause of the error may not be the closure itself.

Please show your code, if you want to know the actual reason causing the issue.

This is the code:


struct GenericDistibution {
  var data: [T]
  var dataRanges: [Range]
  var dataBins: [(Range, Int)] {
    let groups = Dictionary(grouping: data) { (t) -> Range in
      for r in dataRanges {
        if r.contains(t) {return r}
      }
      return dataRanges.first!
    }
    return Array(groups.mapValues {$0.count}).sorted {$0.key.lowerBound < $1.key.lowerBound}
  }
}


And this is the error: Cannot convert value of type '(_) -> Range<T>' to expected argument type '(_) -> _'


But when I changed line #1 to this:


struct GenericDistibution {


the error disappeared. The code now compiles and runs, and even produces the results I want. Here's some code I used to test it:


var testData = [Int]()
for _ in 1...1000 {testData.append(Int.random(in: 1...9))}
let ranges = [1..<4, 4..<7, 7..<10]
let distribution = GenericDistibution(data: testData, dataRanges: ranges)
for item in distribution.dataBins {
  print(item.0.lowerBound, item.0.upperBound, item.1)
}


It produces this output:

1 4 327

4 7 335

7 10 338


(Of course the numbers in the third column change with each run.)


I suspect there's a way of replacing my loop in lines #6-9 with something more elegant.

This seems to do everything I want:


struct GenericDataDistibutor<T: Comparable & Hashable> {
  var data: [T]
  var dataRanges: [Range<T>]
  var dataBins: [(Range<T>, Int)] {
    guard data.count > 0 && dataRanges.count > 0 else {return []}
    let groups = Dictionary(grouping: data) { (t) -> Range<T> in
      return dataRanges.first(where: {$0.contains(t)})!
    }
    return Array(groups.mapValues {$0.count}).sorted {$0.key.lowerBound < $1.key.lowerBound}
  }
}

In the previous version, T was not defined, that could not work.


In addition, Range alone is not accepted, you have to define the type on which you take a Range.


So, great, don't forget to mark your own answer as correct.

Seems you have found a working solution, but creating a `Dictionary<Range<T>, [T]>` and then convert it to `[Range<T>, Int]` seems to be inefficient.


You can write something like this:

struct GenericDataDistibutor2<T: Comparable & Hashable> {
    var data: [T]
    var dataRanges: [Range<T>]
    var dataBins: [(Range<T>, Int)] {
        guard data.count > 0 && dataRanges.count > 0 else {return []} //<- This line may not be needed
        return dataRanges.map {r in (r, data.reduce(0) {$0 + (r.contains($1) ? 1 : 0)})}
    }
}


Testing code:

var testData = (1...1000).map {_ in Int.random(in: 1...9)}
let ranges = [1..<4, 4..<7, 7..<10]
let distribution = GenericDataDistibutor(data: testData, dataRanges: ranges)
let distribution2 = GenericDataDistibutor2(data: testData, dataRanges: ranges)
for bins in [distribution.dataBins, distribution2.dataBins] {
    print("--")
    for item in bins {
        print(item.0.lowerBound, item.0.upperBound, item.1)
    }
}


Output example:

--

1 4 320

4 7 324

7 10 356

--

1 4 320

4 7 324

7 10 356


Some differences.

- Your code may crash, when `data` contains any value out of ranges.

- My code may generate non-sorted `dataBins` when `dataRanges` is not sorted.

Very nice! It will take me awhile to understand exactly why it works, but I'm sure it will be worth the effort.

I have found another difference.

- My code may include zero-count etnries.


You know how to use `filter` if you do not like that.

No, I want zero-count entries to be included. It's an important thing to know whether a data set contains any values within the ranges selected.


Once again, it's an extremely elegant line of code. I think I now understand how it works. I've set up a playground just to fiddle with the reduce function!