I started to study Haskell. I found a code that, every few characters (which we set with a parameter), inserts a given character (also a parameter). But I can't figure out how it works.
func1 :: [a] -> Int
func1 [] = 0
func1 (_:xs) = 1 func1 xs
func2 :: Int -> [a] -> [a]
func2 _ [] = []
func2 n xs@(_:xs')
| n > 0 = func2 (n-1) xs'
| otherwise = xs
func3 :: [a] -> Int -> a -> [a]
func3 xs 0 y = xs
func3 [] n y = []
func3 xs n y
| func1 xs < n = xs
| otherwise = n `take` xs [y] func3 (drop n xs) n y
main :: IO ()
main = do
print(func3 "1234567" 2 '@')
I understand what, for example, this func1 :: [a] -> Int does, but what does this part of the code, I do not?
func2 n xs@(_:xs')
| n > 0 = func2 (n-1) xs'
| otherwise = xs
Or this one?
func3 xs 0 y = xs
func3 [] n y = []
func3 xs n y
| func1 xs < n = xs
| otherwise = n `take` xs [y] func3 (drop n xs) n y
Could you explain this to me? Thanks
CodePudding user response:
I will try to go through the functions one by one.
func1 xs
calculates the length of xs
. []
has length 0
, and the length of x : xs
is one more than the length of xs
.
func2 n xs@(_:xs')
is not called by any other function, but I will try to explain what it would do if it were called. The pattern xs@(_:xs')
might seem unusual, so lets break it down. xs@pat
binds the variable xs
to the pattern pat
. In this case, the pattern is _:xs
. The underscore matches any value and discards it. xs'
matches any list. If you called func2 3 [1, 2]
, then this pattern would be matched. Inside the scope of the function, xs
would be equal to [1, 2]
and xs'
would be equal to [2]
. If n > 0
, then func2
calls itself, where xs
has been replaced by the tail of xs
, and n
has been replaced by n - 1
. This continues until n = 0
, at which point xs
is just kept.
Therefore, func2 n xs
does essentially the same thing as drop n xs
. At least as long as length xs >= n
.
As you said, func3
inserts y
at every n
th position of xs
. There are several cases:
n = 0
is an edge case with no obvious behaviour, so func3
does nothing. If xs = []
, there is no n
th position in xs
so func3
again does nothing. The last pattern distinguishes two cases using guards. This is necessary because there are no patterns for "a list of length at least n
", unless n
is some constant known at compile-time. In the first case, the length of xs
(calculated using func1
) is less than n
. Again, nothing to insert since there is no position n
.
The second guard is where the magic happens. otherwise
is literally just True
and therefore this is a 'catch-all' guard. The result may be described by the following procedure: First we take the first n
elements of xs
. Then we append (
) the one-element list [y]
. Then we append the result of func2
applied to the remaining list.
This is the first time I posted an answer on SO, so feel free to ask if something was unclear!