6

To simplify the problem I'm facing, assume this data type:

data Item = X Int | Y Char deriving (Eq, Show..)

and two lists

let a = [X 1, Y 'g']
let b = [X 2]

I need to replace all items in a with the first (or any) item in b with the same constructor. So the result would be: [X 2, Y 'g'].

Any suggestions?

dan
  • 43,914
  • 47
  • 153
  • 254

4 Answers4

2

Borrowing ideas from an answer by Petr Pudlák you could try:

{-# LANGUAGE DeriveDataTypeable #-}

import Data.Data
import Data.Function (on)
import Data.List (nubBy, find)
import Data.Maybe (fromMaybe)

data Item = X Int | Y Char
  deriving (Eq, Show, Typeable, Data)

a = [X 1, Y 'g']
b = [X 2]

main :: IO ()
main = print $ map replace a
  where b' = nubBy (on (==) toConstr) b -- remove duplicates
        replace x = fromMaybe x $ find (on (==) toConstr x) b'

You can also skip removing the duplicates in b and use b instead of b' in the last line.

Community
  • 1
  • 1
j.p.
  • 1,031
  • 1
  • 18
  • 25
0

You can just make a function to replace the items:

specialReplace :: Item -> Item -> Item
specialReplace (X x1) (X x2) = (X x1)
specialReplace (Y y1) (Y y2) = (Y y1)
specialReplace _ a = a

and then:

foldr (\el list -> map (specialReplace el) list) a b

will run trough your a list and apply the correlated substitutions in b accordingly. Of course if more Xs or Ys are introduced in the b list, then last one will be used at the end.

Your live exampleAnother live example

Shoe
  • 74,840
  • 36
  • 166
  • 272
0

First you'll need a way to determine what constructor has been used:

isX :: Item -> Bool
isX (X _) = True
isX _ = False

-- If you have more than 2 constructors you'll have to write them all like isX
isY :: Item -> Bool
isY = not . isX

And a method to get the first value of each kind of constructor

import Data.Maybe

firstX :: [Item] -> Maybe Item
firstX = listToMaybe . filter isX

firstY :: [Item] -> Maybe Item
firstY = listToMaybe . filter isY

Then a way to replace items

replaceItem :: Item -> Maybe Item -> Item
replaceItem = fromMaybe

replaceItems :: [Item] -> Maybe Item -> Maybe Item -> [Item]
replaceItems [] _ _ = []
replaceItems (item:items) x y =
    (if isX item
        then replaceItem item x
        else replaceItem item y) : replaceItems items x y

But since this is just a map:

replaceXY :: Item -> Maybe Item -> Maybe Item -> [Item]
replaceXY item x y =
    if isX item
        then replaceItem item x
        else replaceItem item y

replaceItems items x y = map (\item -> replaceXY item x y) items

And finally you just have to combine this with firstX and firstY:

replaceFrom :: [Item] -> [Item] -> [Item]
replaceFrom a b =
    let x = firstX b
        y = firstY b
    in replaceXY a x y
bheklilr
  • 53,530
  • 6
  • 107
  • 163
0

For a given Item and a list to be replaced, consider,

replace' :: Item -> [Item] -> [Item]
replace' _ [] = []
replace' (X i) ((X j):xs) = (X i) : replace' (X i) xs
replace' (Y i) ((Y j):xs) = (Y i) : replace' (Y i) xs
replace' r (x:xs) = x : replace' r xs

in which each pattern related to each type in Item. In this approach the latest occurrence of each type will be kept in the replaced list.

elm
  • 20,117
  • 14
  • 67
  • 113