Home > Software design >  Switching elements in a list (Haskell)
Switching elements in a list (Haskell)

Time:10-26

I am trying to create a program that takes in a list of strings. The strings contains of spacing and n chars, either '*', '#', or '|', depending on the lists. So, given two lists, e.g..

["     #     ","    # #   " ,"   # # #  " ,"  # # # # " ," # # # # #" ] 
["     |     ","     |     ","     |     ","     |     ", "     |    "]

My switch method would find the first string which contains '#' in the first list, and put it in front of the other '#'-containing strings in the other list. And if there were none, then we put the '#'-containing string behind all the '|' strings in the list.

In other words this models the Towers of Hanoi move from one pole to another where '|' shows an empty pole and '#' stands for a disk.

Thus the goal here is to move the top-most disk from the first pole onto the top-most disk on the other pole, if any; or otherwise put the moved disk on the bottom.

My code:

-- Switches elements from two lists, returns a list with the two new lists inside
switch :: [String] -> [String] -> [[String]]
switch [] [] = []
switch xs ys =
  let
    e1 = fromJust (findStr '#' xs)            -- Find the first string in list that contains '#'
    e2 = fromJust (findStr '|' $ reverse ys)  -- Find the first string in the reversed list that contains '|' 
    newX = e2 : tail xs                       -- New list with its head replaced by e2
    newY = tail ys : [[e1]]                   -- New list with its last string which contains '|' replaced by e1
    in
      newX : newY 

--Finds and returns the string to be switched
findStr :: Char -> [String] -> Maybe String
findStr c xs = listToMaybe [s | s <- xs, c `elem` s]

When I run it, I get this actual output:

switch ["     #    " ,"    # #   " ,"   # # #  " ,"  # # # # " ," # # # # #"] 
       ["     |     ","     |     ","     |     ","     |     ","     |     "]

ghci> [["     |     ","    # #   " ,"   # # #  " ,"  # # # # " ," # # # # #"],
       ["     |     ","     |     ","     |     ","     |     "],
       ["     #    "]]

Desired output:

switch ["     |     ","    # #   " ,"   # # #  " ,"  # # # # " ," # # # # #"] 
       ["     |     ","     |     ","     |     ","     |     ","     #    "]

ghci> [["     |     ","     |    " ,"   # # #  " ,"  # # # # "   ," # # # # #"],
       ["     |     ","     |     ","     |     ","     # #     ","     #    "]]

If I try to write it like this:

newY = tail ys : e1  -- New list with its last string which contains '|' replaced by e1

(Removed the brackets surrounding e1)I get an error saying:

Couldn't match typeChar’ with ‘[String]’
  Expected: [[String]]
    Actual: String

What am I doing wrong here? All help is appreciated!

CodePudding user response:

This code solves the issue I had:

-- Switches elements from two lists, returns a list with the two new lists inside
switch :: [String] -> [String] -> [[String]]
switch [] [] = []
switch xs ys =
  let
    rs = reverse ys
    (e1, n1) = findStr '#' xs                                -- Find the first string in list that contains '#'
    (e2, n2) = findStr '|' rs                                -- Find the first string in the reversed list that contains '|' 
    newX = e2 : (take (n1-1) xs    drop n1 xs)               -- New list with its head replaced by e2
    newY = [reverse (take (n2-1) rs    [e1]    drop n2 rs)]  -- New list with its last string which contains '|' replaced by e1
    in
      newX : newY

--Finds and returns the string to be switched
findStr :: Char -> [String] -> (String, Int)
findStr c xs = head [s | s <- zip xs [1..], c `elem` fst s]

CodePudding user response:

Regardless of your issues with the specific code you posted, a simple way to solve your problem is to use span (or break), then combine the two resulting tuples' components with :

move :: [String] -> [String] -> Maybe ([String], [String])
move xs ys 
   | ( (a,h:b) , (c,d) ) <- (break (elem '#') xs,
                             break (elem '#') ys)
       = Just (a    b, c    [h]    d)
   | otherwise
       = Nothing

So that now we have,

> move [" # "," # # "," # # # "] [" | "," | "," | "]
Just ([" # # "," # # # "],[" | "," | "," | "," # "])

> move [" | "," # "," # # # "] [" | "," | "," | "," # # "]
Just ([" | "," # # # "],[" | "," | "," | "," # "," # # "])

> move [" | "," | "," | "] [" # "," # # "," # # # "]
Nothing

This code uses pattern guards.

I've also changed the output type.

  • Related