# Codewars - Land Perimeter

The Land-Perimeter challenge from Codewars is slightly different from the others I've mentioned; it effectively needs you to produce results from a list of lists, where the relationship between a given item and its peers is important. I tried this one in Haskell first, since the approach in that language is somewhat new to me.

One more round of spoilers?

```landPerimeter :: [String] -> String
landPerimeter xs = (++) "Total land perimeter: "
(show \$ sum \$ map (\p -> pointPerimeter (fst p) (snd p) xs) \$ points xs)

-- | Produce the X or O character of a given x,y coordinate and islands map.
--   Defaults to O if the coordinate is not on the map.
geography :: Int -> Int -> [String] -> Char
geography x y islands
| y < 0 || x < 0 = 'O'
| y >= length islands || x >= length (head islands) = 'O'
| otherwise = (islands !! y) !! x

-- | Produce the perimeter of a given point on an island map.
pointPerimeter :: Int -> Int -> [String] -> Int
pointPerimeter x y islands
| geography x y islands == 'O' = 0
| otherwise = length . filter (\g -> g == 'O')
\$ zipWith (\x y -> geography x y islands)
[(x - 1), (x + 1), x, x]
[y, y, (y - 1), (y + 1)]

-- | Produce a list of possible (x, y) coordinates for an islands map.
points :: [String] -> [(Int, Int)]
points xs = go 0 0 xs where
go _ _ [] = []
go x y (a:as) = map (\x -> (x, y)) [0 .. (length a) - 1] ++ go 0 (y + 1) as
```

It was tricky to get the coordinate traversal right. My first pass tried to incorporate it as part of the landPerimeter function - it didn't work until I made it a separate procedure and thought it through with independent tests. There may be cleaner ways to get at what amounts to a nested for loop.

# Python

```def land_perimeter(islands):
"""Given a map of islands, return the perimeter of land among them."""
total = 0
for y in range(len(islands)):
for x in range(len(islands[y])):
total += perimeter(x, y, islands)

return "Total land perimeter: %s" % (total,)

def geography(x, y, islands):
"""Given an x, y position and island map, return the X or O character at the
position.  Default to O for positions out of range."""

if y < 0 or x < 0: return "O"
if y >= len(islands) or x >= len(islands): return "O"
return islands[y][x]

def perimeter(x, y, islands):
"""Given an x, y positon and island map, return the perimeter of position."""

if geography(x, y, islands) == "O": return 0

neigbors = [geography(g, g, islands) for g in [(x, y - 1), (x, y + 1),
(x - 1, y), (x + 1, y)]]
return neigbors.count("O")
```

Prior to learning about functional programming I might have implemented this as one long-ish function.

# Other solutions ...

... were okay; there are a few in each language that require far fewer lines of code, but for once I'm not taken with any of them. Typically I find several that show me ideas I want to learn from, but in this case the "code golf" answers left me thinking "good luck to whomever has to maintain that."

# Comparison

Still much easier time writing the Python, though it feels like function composition in Haskell is becoming more familiar.

Bottom line - it feels like there is a lot of depth to be explored in Haskell; it's also been pretty challenging to bring it up to speed with what I can handle in Python. Chances are good I'll keep exploring it, though it's starting to feel like time to move off of exercises and think about where I could use it for real projects.