Thursday, August 3, 2017

Method to Convert Depth-First-Search From Recursion To Tail-Recursion in Scala

The earth is divided into multiple grid-like region which is represented by GeoHash. The following will start searching from a grid, whose path will be one of the (north, south, east, west) grids. The border will be distance between current grid and the starting grid.
def expand_neighbors_impl(ghCenter: GeoHash, ghCur: GeoHash, buffer: collection.mutable.Set[GeoHash]): Unit = {
    // MARK: DP: check whether it's iterated already or not
    if(buffer contains ghCur)  {
    return
    }
    buffer += ghCur

    for(ghAround <- get4GeoHashAround(ghCur))  {
    if(distanceBetweenGeohash(ghCenter, ghAround) <= radius)  {
        expand_neighbors_impl(ghCenter, ghAround, buffer)
    }
    }
}

def get4GeoHashAround(gh: GeoHash): Array[GeoHash] = {
    Array(gh.getNorthernNeighbour, gh.getSouthernNeighbour, gh.getWesternNeighbour, gh.getEasternNeighbour)
}

def distanceBetweenGeohash(gh1: GeoHash, gh2: GeoHash) = {
    haversine(gh1.getBoundingBoxCenterPoint.getLatitude, gh1.getBoundingBoxCenterPoint.getLongitude, gh2.getBoundingBoxCenterPoint.getLatitude, gh2.getBoundingBoxCenterPoint.getLongitude)
}
But the above code needs recursion, which may throw StackOverflowError. One of the way to solve it is by setting -Xss256m -Xmx4096m parameters when launching. But the more decent way to do is transfer this depth-first-search to tail-recursion:
@tailrec
def expand_neighbors_impl(ghCenter: GeoHash, toGoThrough: List[GeoHash], buffer: Set[GeoHash] = Set()): Set[GeoHash] = {
    toGoThrough.headOption match {
    case None => buffer
    case Some(ghCur) =>
        if (buffer contains ghCur) {
        expand_neighbors_impl(ghCenter, toGoThrough.tail, buffer)
        }
        else {
        val neighbors = get4GeoHashAround(ghCur).filter(distanceBetweenGeohash(ghCenter, _) <= radius)
        expand_neighbors_impl(ghCenter, neighbors ++: toGoThrough, buffer + ghCur)
        }
    }
}

def expand_neighbors(ghCenter: GeoHash, ghCur: GeoHash): Set[GeoHash] = expand_neighbors_impl(ghCenter, List(ghCur))

No comments:

Post a Comment