I'm trying to write a Haskell function that takes a string of pairs of letters, and exchanges the letters of the pair in a string of all letters, but what I've come up with feels awkward and unidiomatic.
I have
swap a b = map (\x-> if x == a then b else if x == b then a else x)
sub n = foldr (.) id (zipWith swap (head <$> splitOn "." n) (last <$> splitOn "." n)) ['A'..'Z']
which works well enough giving
> sub "RB.XD.EU.ZM.IJ"
"ARCXUFGHJIKLZNOPQBSTEVWDYM"
and
> sub "YC.LU.EB.TZ.RB.XD.IJ"
"ARYXBFGHJIKUMNOPQESZLVWDCT"
but I'm new to Haskell and feel like my approach — especially my swap
helper function (which I only use here) — is more elaborate than it needs to be.
Is there a better, more idiomatic, approach to this problem; especially one that takes advantage of a language feature, builtin, or library function that I've missed?
Doing a left fold over the substitution list makes the code shorter:
Drop the reverse if you don't care whether
EB
orRB
is swapped first.If you'd want to replace instead of a swap:
Some things I noticed from reading your code (I haven't tried to rewrite it). My first suggestion involves separation of concerns:
That means the a more natural type for your function would be:
Or, using
Data.Map
for more efficient lookups:Which is a lot more precise than taking a string with dot-separated pairs. You can then generate the associations between
Char
s in a separate step:Ideally you should also handle invalid inputs (e.g.
AB.CDE
) and empty input strings.Then you probably should define it in a
where
clause. I would also avoid the nameswap
, as there is a relatively common function inData.Tuple
with the same name (swapLetters
might be a nice choice).foldr (.) id (fmap f xs) y
is the same thing asfoldr f y xs
. I'm almost certain this can be rewritten in a simpler way.I'd break the problem down a bit more. It's important to remember that shorter code is not necessarily the best code. Your implementation works, but it's too compact for me to quickly understand. I'd recommend something more like
The equivalent to your
sub
function would beBut the former is probably easier to understand for most haskellers. You could also do more transformations more easily to your swap specification as a list of tuples making it more powerful overall. You essentially decouple the representation of the swap specification and the actual swapping. Even for small programs like this it's important to maintain loose coupling so that you develop a habit for when you write larger programs!
This implementation also avoids recalculating
splitOn
for the swap specification string.(I wasn't able to execute this code because I'm on a computer without Haskell installed, if anyone notices any bugs please edit to fix.)Tried it out in FPComplete, output matches @raxacoricofallapatorius'.