Reputation: 365
I'm new to Haskell and I'm trying to shift a character by one to the right and that the last character will always be first, and that it would return a string.
For example: shiftString "abcd" = "dabc"
but the compiler doesn't agree with me, can anybody help me fix this?
shiftString :: String -> String
shiftString x = take (length x) $ iterate rot1 x
where rot1 = drop 1 x ++ take 1 x
Also, I want to check after this on two Strings and returns True if one of them is a result of applying shiftString one or more times, how can I use this to do that? as in:
isShifted :: String -> String -> Bool
Upvotes: 0
Views: 109
Reputation: 28
-- shiftString "abcd" == "dabc"
-- shiftString "hello" == "ohell"
-- shiftString "orange" == "eorang"
shiftString :: [a] -> [a]
shiftString lst = [ head (reverse lst)] ++ (init lst)
-- Test Cases
-- isShifted "abcdefg" "gabcdef" == True
-- isShifted "hello" "ohell" == True
-- isShifted "orange" "eorange" == False
isShifted :: String -> String -> Bool
isShifted str1 str2
| shiftString str1 == str2 = True
| otherwise = False
Upvotes: 0
Reputation: 52290
A String
in Haskell is just a list of Char
s - so you can do this using simple pattern-matching:
shiftString' :: String -> String
shiftString' (firstChar:restChars) = restChars ++ [firstChar]
shiftString' [] = []
now if you try this it'll do the opposite of what you want - but there is a trick: we reverse the string twice:
shiftString :: String -> String
shiftString = reverse . shiftString' . reverse
that should do the trick
fix yours:
I think you only need the rot1
(it does the same as my shiftString'
) together with the reverse trick from above
Or - to fix yours more:
I guess you tried with the iterate to rot1
more times and then drop the rest - that works too:
shiftS cs = head $ drop (length cs - 1) $ iterate rot1 cs
rot1 cs = drop 1 cs ++ take 1 cs
note that iterate
gives you a list of strings (with more and more rotations) - I drop the first n-1
(where n
= length of the input) which gives me a list again (one type-error on your version) and take the head of that because I only want the first String
in this list.
for your isShifted
you can reuse the iterate
with rot1
(no need to care about order) - this time take length cs
from this and then check if your other input is an element of this list:
isShifted cs cs' = cs `elem` take (length cs) (iterate rot1 cs')
Example:
Prelude> isShifted "abcd" "cdab"
True
Prelude> isShifted "abdc" "cdab"
False
note that you cannot just use iterate rot1 cs'
without the take - think about it and then try what happens ;) (hint: it'll work when your inputs are shifted - but you'll get into trouble if they are not - why?)
Upvotes: 1