HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Getting the index (x,y) of a char in a "2D" list in Haskell

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
thechargettinghaskellindexlist

Problem

I have this working function circuitFind that I really don't find nice because I feel there is a better and simpler way to accomplish what it does.

circuitFind :: Circuit -> Tile -> Maybe Position
circuitFind c t = unpack . find (\ p -> fst p /= Nothing) . enumerate . map findTile $ c
    where findTile = findIndex (==t)
          enumerate = flip zip [0..]
          unpack a = case a of 
             Just (Just x, y) -> Just (x,y) 
             _ -> Nothing


Basically, the function works like this: (ie: we search for the char '3')

["1stline", "2ndline", "3rdline"]
              | 
        map findTile '3'
              | 
              v

[Nothing, Nothing, Just 0]
              |
          enumerate
              |
              v

[(Nothing, 0), (Nothing, 1), (Just 0, 2)]
              |
       find (\ p -> fst p /= Nothing) 
              |
              v

      Just (Just 0, 2)
              |
           unpack
              |
              v 

     ––  Just (0,2) ––


Is there a better way to make this function more readable/shorter/... ?

All the relevant code here:

import Data.List

type Vec2D = (Int, Int)
type Position = Vec2D
type Circuit = [[Tile]]
type Tile = Char

basicCircuit :: Circuit
basicCircuit = ["------------",
                "-b        e-",
                "------------"]

circuitFind :: Circuit -> Tile -> Maybe Position
circuitFind c t = unpack . find (\ p -> fst p /= Nothing) . enumerate . map findTile $ c
    where findTile = findIndex (==t)
          enumerate = flip zip [0..]
          unpack a = case a of 
             Just (Just x, y) -> Just (x,y) 
             _ -> Nothing


Some tests to illustrate the usage:

Main> circuitFind basicCircuit 'e'
Just (10,1)
Main> circuitFind basicCircuit 'o'
Nothing

Solution

Disclaimer: I'm rusty with Haskell, there may be a cleaner way to do this.

Code review: your code looks fine to my eyes, it's just that circuitFind is overly complex, as you suspected. Also note that findIndex (==t) can be written as elemIndex t (source).


"Let us see whether we could, by chance, conceive some other general
problem that contains the original problem and is easier to solve." –
Leibnitz.

We start by solving the more general problem of finding all indices of t in c.

Let's look at the skeleton of our solution.

[ (x,y) | ??? ]


We can get y using zip [0..] as you have done.

[ (x,y) | (y,line) <- zip [0..] c, ??? ]


Now we can find x using elemIndices.

[ (x,y) | (y,line) <- zip [0..] c, x <- elemIndices t line ]


Thanks to lazy evaluation, and Data.Maybe, we get our solution

circuitFind :: Circuit -> Tile -> Maybe Position
circuitFind c t = listToMaybe [ (x,y) | (y,line) <- zip [0..] c, x <- elemIndices t line ]

Code Snippets

[ (x,y) | ??? ]
[ (x,y) | (y,line) <- zip [0..] c, ??? ]
[ (x,y) | (y,line) <- zip [0..] c, x <- elemIndices t line ]
circuitFind :: Circuit -> Tile -> Maybe Position
circuitFind c t = listToMaybe [ (x,y) | (y,line) <- zip [0..] c, x <- elemIndices t line ]

Context

StackExchange Code Review Q#58954, answer score: 3

Revisions (0)

No revisions yet.