Exchanging multiple pairs of characters in a Haskell string - string

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:
import Data.List
import Data.List.Split
sub = foldl' swap ['A'..'Z'] . splitOn "." . reverse
where
swap az [a,b] = map (\x -> if x == a then b else if x == b then a else x) az
Drop the reverse if you don't care whether EB or RB is swapped first.
If you'd want to replace instead of a swap:
import Data.List
import Data.List.Split
replace needle replacement haystack =
intercalate replacement (splitOn needle haystack)
rep = foldl' replace' ['A'..'Z'] . splitOn "."
where
replace' az [a,b] = replace [a] [b] az

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
import Data.Maybe (mapMaybe)
swap = undefined -- Your current implementation is fine,
-- although you could rewrite it using
-- a local function instead of a lambda
-- |Parses the swap specification string into a list of
-- of characters to swap as tuples
parseSwap :: String -> [(Char, Char)]
parseSwap = mapMaybe toTuple . splitOn "."
where
toTuple (first:second:_) = Just (first, second)
toTuple _ = Nothing
-- |Takes a list of characters to swap and applies it
-- to a target string
sub :: [(Char, Char)] -> String -> String
sub charsToSwap = foldr (.) id (map (uncurry swap) charsToSwap)
The equivalent to your sub function would be
sub swapSpec = foldr (.) id (map (uncurry swap) $ parseSwap swapSpec)
But 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'.

Some things I noticed from reading your code (I haven't tried to rewrite it). My first suggestion involves separation of concerns:
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
That means the a more natural type for your function would be:
sub :: [(Char, Char)] -> String -> String
Or, using Data.Map for more efficient lookups:
sub :: Map Char Char -> String -> String
Which is a lot more precise than taking a string with dot-separated pairs. You can then generate the associations between Chars in a separate step:
parseCharPairs :: String -> Map Char Char
Ideally you should also handle invalid inputs (e.g. AB.CDE) and empty input strings.
my swap helper function (which I only use here)
Then you probably should define it in a where clause. I would also avoid the name swap, as there is a relatively common function in Data.Tuple with the same name (swapLetters might be a nice choice).
sub n = foldr (.) id -- etc.
foldr (.) id (fmap f xs) y is the same thing as foldr f y xs. I'm almost certain this can be rewritten in a simpler way.

Related

Should I use foldr or foldl' to build a String in Haskell?

Assuming that foldr should be used to build data structures and foldl' if the result is supposed to be a single value, I'm not sure what to use for Strings. On the one hand it is a data structure, but on the other hand a String is usually only used as a whole, meaning that short-circuiting isn't very relevant. To answer this question, it's probably crucial how functions like putStrLn use Strings, isn't it? Or am I on a completely wrong track?
EDIT: So I want my function to turn something like [(5, 's'), (1, ’a'), (3, 'd')] into sssssaddd (following an exercise from https://en.m.wikibooks.org/wiki/Haskell) and I have to choose one from those two functions:
decode :: [(Int, Char)] -> String
decode = foldr ff []
where
ff (l, c) xs = replicate l c ++ xs
decode' :: [(Int, Char)] -> String
decode' = foldl' ff []
where
ff xs (l, c) = xs ++ replicate l c
You're on the completely wrong track. The only correct way to decide what fold to use involves knowing what the fold will do. Knowing only the output type is not enough.
A String is just an alias of [Char], so a list. If you use foldl or foldl' with append ((++)) or cons ((:)), it will fold with foldl as:
(("hell" ++ "o") ++ " ") ++ "world"
Concatenating takes linear time in the length of the left operand. So if you eacn time concatenate a single character, then constructing a string of n characters will take O(n2) time.
Another problem that might arise if you have an infinite list, in that case, foldl will get stuck in an infinite loop. Whereas in foldr you can "consume" the output if that happens in a generator-like approach.
But as #chepner says, using Strings for large amounts of text is not effective: it requires a cons per character, so it blows up in memory. Text allows one to have a more compact and efficient way to store text, in an unboxed type, and often the algorithms are more efficient than what one can do with a String.

How to iterate over a list of characters and manipluate the characters in Haskell?

I am trying to go through a list of characters in a list and do something to the current character. My java equivalent of what I am trying to accomplish is:
public class MyClass {
void repeat(String s) {
String newString = "";
for(int i = 0; i < s.length(); i++) {
newString += s.charAt(i);
newString += s.charAt(i);
}
public static void main(String args[]) {
MyClass test = new MyClass();
test.repeat("abc");
}
}
One of the nicest thing about functional programming is that patterns like yours can be encapsulated in one higher-order function; if nothing fits, you can still use recursion.
Recursion
First up, a simple recursive solution. The idea behind this is that it's like a for-loop:
recursiveFunction [] = baseCase
recursiveFunction (char1:rest) = (doSomethingWith char1) : (recursiveFunction rest)
So let's write your repeat function in this form. What is the base case? Well, if you repeat an empty string, you'll get an empty string back. What is the recursion? In this case, we're doubling the first character, then recursing along the rest of the string. So here's a recursive solution:
repeat1 [] = []
repeat1 (c:cs) = c : c : (repeat1 cs)
Higher-order Functions
As you start writing more Haskell, you'll discover that these sort of recursive solutions often fit into a few repetitive patterns. Luckily, the standard library contains several predefined recursive functions for these sort of patterns:
fmap is used to map each element of a list to a different value using a function given as a parameter. For example, fmap (\x -> x + 1) adds 1 to each element of a list. Unfortunately, it can't change the length of a list, so we can't use fmap by itself.
concat is used to 'flatten' a nested list. For example, concat [[1,2],[3,4,5]] is [1,2,3,4,5].
foldr/foldl are two more complex and generic functions. For more details, consult Learn You a Haskell.
None of these seem to directly fit your needs. However, we can use concat and fmap together:
repeat2 list = concat $ fmap (\x -> [x,x]) list
The idea is that fmap changes e.g. [1,2,3] to a nested list [[1,1],[2,2],[3,3]], which concat then flattens. This pattern of generating multiple elements from a single one is so common that the combination even has a special name: concatMap. You use it like so:
repeat3 list = concatMap (\x -> [x,x]) list
Personally, this is how I'd write repeat in Haskell. (Well, almost: I'd use eta-reduction to simplify it slightly more. But at your level that's irrelevant.) This is why Haskell in my opinion is so much more powerful than many other languages: this 7-line Java method is one line of highly readable, idiomatic Haskell!
As others have suggested, it's probably wise to start with a list comprehension:
-- | Repeat each element of a list twice.
double :: [x] -> [x]
double xs = [d | x <- xs, d <- [x, x]]
But the fact that the second list in the comprehension always has the same number of elements, regardless of the value of x, means that we don't need quite that much power: the Applicative interface is sufficient. Let's start by writing the comprehension a bit differently:
double xs = xs >>= \x -> [x, x] >>= \d -> pure d
We can simplify immediately using a monad identity law:
double xs = xs >>= \x -> [x, x]
Now we switch over to Applicative, but let's leave a hole for the hard part:
double :: [x] -> [x]
double xs = liftA2 _1 xs [False, True]
The compiler lets us know that
_1 :: x -> Bool -> x
Since the elements of the inner/second list are always the same, and always come from the current outer/first list element, we don't have to care about the Bool:
double xs = liftA2 const xs [False, True]
Indeed, we don't even need to be able to distinguish the list positions:
double xs = liftA2 const xs [(),()]
Of course, we have a special Applicative method, (<*), that corresponds to liftA2 const, so let's use it:
double xs = xs <* [(),()]
And then, if we like, we can avoid mentioning xs by switching to a "point-free" form:
-- | Repeat each element of a list twice.
double :: [x] -> [x]
double = (<* [(),()])
Now for the test:
main :: IO ()
main = print $ double [1..3]
This will print [1,1,2,2,3,3].
double admits a slight generalization of dubious value:
double :: Alternative f => f x -> f x
double = (<* join (<|>) (pure ()))
This will work for sequences as well as lists:
double (Data.Sequence.fromList [1..3]) = Data.Sequence.fromList [1,1,2,2,3,3]
but it could be a bit confusing for some other Alternative instances:
double (Just 3) = Just 3

How to implement the inverse of "intercalate" (splitting a string into pieces on a character) using functions from "base"?

I wanted to split a string on newlines and I was surprised that I could not find the inverse function to intercalate "\n". That is, a function that splits a string into pieces on new lines (or according to some other predicate).
Note that lines and words do something different. For example
intercalate "\n" (lines "a\n") == "a"
There is a similar function function splitOn in the split library. I could also write such a function myself directly:
splitOn :: (a -> Bool) -> [a] -> [[a]]
splitOn p = map reverse . g []
where
g rs [] = [rs]
g rs (x:xs) | p x = rs : g [] xs
| otherwise = g (x : rs) xs
but I wonder if it could be constructed more easily using only functions from base.
As Nikita Volkov points out, the restriction "only Prelude functions" doesn't make it very easy, but here's one option:
splitWhen p [] = [[]]
splitWhen p l = uncurry (:) . fmap (splitWhen p . drop 1) . break p $ l
This uses the Functor instance for (,) a as an alternative to Control.Arrow.second (to avoid a messier lambda expression), which works without importing anything (ghci says "defined in 'GHC.Base'"), but I'm not sure if that actually belongs in the Prelude, since I can't find it in the Haskell Report.
Edit: Being allowed to use other functions from base doesn't even help me that much. In any case I would use second instead of fmap because I think it adds a little clarity. With unfoldr, using a Maybe for the seed to distinguish the end of the string from an empty part (or empty line in the example):
import Control.Applicative ((<$>))
import Control.Arrow (second)
import Data.List (unfoldr)
splitWhen p = unfoldr (second check . break p <$>) . Just
where
check [] = Nothing
check (_:rest) = Just rest
-- or cramming it into a single line with 'Data.Maybe.listToMaybe'
splitWhen' p =
unfoldr (second (\rest -> tail rest <$ listToMaybe rest) . break p <$>) . Just
If the composition of more primitive functions is what you're looking for, then I can think of no other way than something based on unfoldr and break, but unfoldr is not in Prelude.
Anyway, I think you're well aware that Prelude is far from what many of us want it to be, so there's no surprise that it can't solve even such a seemingly trivial problem. The general problem with Prelude seems to be that it's mispurposed: the module aims to provide things that are enough to play with Haskell as part of an introduction, as even its name suggests, but since there is no standard "base" module, Haskellers tend to see it as one.

Character & strings

I am new in haskell and I have a problem (aka homework).
So, I have a list with a tuple – a string and an integer:
xxs :: [([Char], Integer)]
I need to know how many of the strings in xxs start with a given character.
Let me exemplify:
foo 'A' [("Abc",12),("Axx",34),("Zab",56)]
Output: 2
foo 'B' [("Abc",12),("Bxx",34),("Zab",56)]
Output: 1
My best attempt so far:
foo c xxs = length (foldl (\acc (x:xs) -> if x == c then c else x) [] xxs)
But, of course, there's something VERY wrong inside the lambda expression.
Any suggestion?
Thanks.
You can use a fold, but I would suggest another way, which breaks the problem in three steps:
transform the input list to the list of first letters. You can use map for this
filter out all elements not equal to the given Char
take the length of the remaining list
Obviously the first step is the hardest, but not as hard as it looks. For doing it you just have to combine somehow the functions fst and head, or even easier, map twice.
You can write this as a simple one-liner, but maybe you should start with a let:
foo c xxs = let strings = map ...
firstLetters = map ...
filteredLetters = filter ...
in length ...
There are a few problems with your attempt:
You plan to use foldl to construct a shorter list and then to take its length. While it is possible, filter function is much better suited for that task as #landei suggests
foldl can be used to accumulate the length without constructing a shorter list. See the answer of #WuXingbo - his answer is incorrect, but once you realize that length is not needed at all with his approach, it should be easy for you to come with correct solution.
Somewhat contradictory to common sense, in a lazy language foldr is faster and uses less memory than foldl. You should ask your teacher why.
I would rewrite foo as
foo :: Char -> [(String, Int)] -> Int
foo c = length . filter ((==c).head.fst)
fst fetches the first element of a two-element tuple.
(==c) is a one-argument function that compares its input with c (see http://www.haskell.org/tutorial/functions.html paragraph 3.2.1 for better explanation).

Can you use pattern matching to bind the last element of a list?

Since there is a way to bind the head and tail of a list via pattern matching, I'm wondering if you can use pattern matching to bind the last element of a list?
Yes, you can, using the ViewPatterns extension.
Prelude> :set -XViewPatterns
Prelude> let f (last -> x) = x*2
Prelude> f [1, 2, 3]
6
Note that this pattern will always succeed, though, so you'll probably want to add a pattern for the case where the list is empty, else last will throw an exception.
Prelude> f []
*** Exception: Prelude.last: empty list
Also note that this is just syntactic sugar. Unlike normal pattern matching, this is O(n), since you're still accessing the last element of a singly-linked list. If you need more efficient access, consider using a different data structure such as Data.Sequence, which offers O(1) access to both ends.
You can use ViewPatterns to do pattern matching at the end of a list, so let's do
{-# LANGUAGE ViewPatterns #-}
and use reverse as the viewFunction, because it always succeeds, so for example
printLast :: Show a => IO ()
printLast (reverse -> (x:_)) = print x
printLast _ = putStrLn "Sorry, there wasn't a last element to print."
This is safe in the sense that it doesn't throw any exceptions as long as you covered all the possibilities.
(You could rewrite it to return a Maybe, for example.)
The syntax
mainFunction (viewFunction -> pattern) = resultExpression
is syntactic sugar for
mainFunction x = case viewFunction x of pattern -> resultExpression
so you can see it actually just reverses the list then pattern matches that, but it feels nicer.
viewFunction is just any function you like.
(One of the aims of the extension was to allow people to cleanly and easily use accessor functions
for pattern matching so they didn't have to use the underlying structure of their data type when
defining functions on it.)
The other answers explain the ViewPatterns-based solutions. If you want to make it more pattern matching-like, you can package that into a PatternSynonym:
tailLast :: [a] -> Maybe ([a], a)
tailLast xs#(_:_) = Just (init xs, last xs)
tailLast _ = Nothing
pattern Split x1 xs xn = x1 : (tailLast -> Just (xs, xn))
and then write your function as e.g.
foo :: [a] -> (a, [a], a)
foo (Split head mid last) = (head, mid, last)
foo _ = error "foo: empty list"
This is my first day of Haskell programming and I also encountered the same issue, but I could not resolve to use some kind of external artifact as suggested in previous solutions.
My feeling about Haskell is that if the core language has no solution for your problem, then the solution is to transform your problem until it works for the language.
In this case transforming the problem means transforming a tail problem into a head problem, which seems the only supported operation in pattern matching. It turns that you can easily do that using a list inversion, then work on the reversed list using head elements as you would use tail elements in the original list, and finally, if necessary, revert the result back to initial order (eg. if it was a list).
For example, given a list of integers (eg. [1,2,3,4,5,6]), assume we want to build this list in which every second element of the original list starting from the end is replaced by its double (exercise taken from Homework1 of this excellent introduction to Haskell) : [2,2,6,4,10,6].
Then we can use the following:
revert :: [Integer] -> [Integer]
revert [] = []
revert (x:[]) = [x]
revert (x:xs) = (revert xs) ++ [x]
doubleSecond :: [Integer] -> [Integer]
doubleSecond [] = []
doubleSecond (x:[]) = [x]
doubleSecond (x:y:xs) = (x:2*y : (doubleSecond xs))
doubleBeforeLast :: [Integer] -> [Integer]
doubleBeforeLast l = ( revert (doubleSecond (revert l)) )
main = putStrLn (show (doubleBeforeLast [1,2,3,4,5,6,7,8,9]))
It's obviously much longer than previous solutions, but it feels more Haskell-ish to me.

Resources