Home > Blockchain >  How do I call my binary search function in swift?
How do I call my binary search function in swift?

Time:08-16

I'm an old C programmer. I'm trying to implement some code in Swift and needed a binary search algorithm that would return the closest element if an exact match isn't available.

I searched for a bit and either the code was invoking so many Swift concepts at once as to be opaque to my C eyes or it wasn't suitable for my use case.

I decided to port some old C code to get the job done and wrote the following:

public func bsearch(arr:[Any], target:Double, bcompare:(Any,Double)->Int)-> Int{
    // v is array being searched
    // target is what we're looking for
    // bcompare is a pointer to user-supplied comparison function
    //   bcompare returns -1 if left element < right element, 0 if =, 1 if >
    // target must be same type as v element being compared
   
    // returns index of array element that is closest to <= target
    // note! indexed returned may not match target value
    
    var lo, hi, mid:Int
   
    hi = v.count-1
    if hi <= 0 {return -1}

    lo = 0
    while ((hi-lo) > 1) {
        mid = (hi lo)/2
        if( bcompare(v[mid], target) == -1){
            lo = mid   1
        }
        else{
            hi = mid
        }
    }
    if bcompare(v[hi],target) == 0{
        return hi
    }
    return lo
    
}
func eleCompare(left:locArrayele,right:Double)->Int{
    if right < left.degrees{
        return -1
    }
    else if right == left.degrees{
        return 0
    }
    else {
        return 1
    }
}

In C, you can pass search functions pointers to structures and tell the compiler how to interpret the memory blocks in the comparison functions that you also pass to the search function. The comparison function reference is just another memory pointer and no parameters are needed.

I assumed a Swift "Any" declaration was equivalent to a pointer reference and wrote the above code with that idea in mind. The compiler complained about declaring the search target as an Any when the comparison function referred to the target as a double. To satisfy the compiler, I declared the target as a double and the code compiled fine.

My problem now is to actually test the code. No matter how I try to invoke the search function, the compiler is unhappy. This test snippet is the closest I could get to satisfying the compiler.

       class locArrayele{
            public var degrees  = CLLocationDegrees()       
            public var ix = Int()
            init( degrees:CLLocationDegrees, i:Int){
                self.degrees = degrees
                self.ix = i
            }
        }
       public var latArray : [locArrayele] = [] 
       .
       .
       .
       ix = bsearch(v: latArray, target:35.0, bcompare: eleCompare )
       print(" when lat is 35, closest ix is \(ix))

Apparently, the compiler wants me to supply parameters to eleCompare, a task I expect bsearch to do as it executes.

How do I invoke the code? I realize I'm c-ifing Swift but I'm just trying to get something to work. Elegance can come later as I get comfortable in the language.

CodePudding user response:

You need to make your bsearch() generic. You have two types that can vary: the first type is the one that the array v contains, and the other is the target's type.

Change your first line to:

public func bsearch<T, U>(v: [T], target: U, bcompare: (T, U) -> Int) -> Int {

You don't have to use 2 different types when you call it, but you can.


Example: String and Int

This example has an array of words of type [String] and it is searching for the word with 5 letters, so the target in an Int.

let words = ["a", "to", "the", "seven", "butter"]

func compareNameLength(left: String, right: Int) -> Int {
    if left.count < right {
       return -1
    } else if left.count == right {
       return 0
   } else {
       return 1
   }
}

// search for the 5 letter word
let i = bsearch(v: words, target: 5, bcompare: compareNameLength)
print(words[i])
seven

Example: Int and Int

This example has an [Int] containing prime numbers, and it is searching for a prime closest to a number without going over, so the target is an Int.

let primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]

func compareInt(left: Int, right: Int) -> Int {
    if left < right {
        return -1
    } else if left == right {
        return 0
    } else {
        return 1
    }
}

// search for closest prime to 8
let p = bsearch(v: primes, target: 8, bcompare: compareInt)
print(primes[p])
7
  • Related