Haskell Recursive HashMap data structure of arbitrary depth - haskell

With this Python function:
def mut_add_to_tree(text, tree):
tree_ = tree
for i, c in enumerate(text):
if c in tree_:
tree_[c][0] += 1
tree_ = tree_[c][1]
else:
for c_ in text[i:]:
tree_[c_] = [1, {}]
tree_ = tree_[c_][1]
break
is created a data structure of nested dicts like this:
In [15]: tree = {}
In [16]: mut_add_to_tree("cat", tree)
In [17]: tree
Out[17]: {'c': [1, {'a': [1, {'t': [1, {}]}]}]}
In [18]: mut_add_to_tree("car", tree)
In [19]: tree
Out[19]: {'c': [2, {'a': [2, {'t': [1, {}], 'r': [1, {}]}]}]}
In [20]: mut_add_to_tree("bat", tree)
In [21]: tree
Out[21]:
{'c': [2, {'a': [2, {'t': [1, {}], 'r': [1, {}]}]}],
'b': [1, {'a': [1, {'t': [1, {}]}]}]}
In [22]: mut_add_to_tree("bar", tree)
In [23]: tree
Out[23]:
{'c': [2, {'a': [2, {'t': [1, {}], 'r': [1, {}]}]}],
'b': [2, {'a': [2, {'t': [1, {}], 'r': [1, {}]}]}]}
How can this behaviour be replicated in Haskell?
More generally, how are nested HashMaps of arbitrary depth created and inserted into?
I've experimented with the following:
type NestedHashMap k v = HashMap Char (Int,(HashMap Char v))
toNestedHashMap :: String -> HashMap Char (Int, HashMap Char v)
toNestedHashMap [] = fromList []
toNestedHashMap (x:xs) = fromList [(x, (1, toNestedHashMap xs))]
but already here the compiler tells me
Couldn't match type ‘v’ with ‘(Int, HashMap Char v0)’
‘v’ is a rigid type variable bound by
the type signature for:
toNestedHashMap :: forall v.
String -> HashMap Char (Int, HashMap Char v)
at WordFuncs.hs:48:1-63
Expected type: HashMap Char (Int, HashMap Char v)
Actual type: HashMap
Char (Int, HashMap Char (Int, HashMap Char v0))
Any help appreciated. Thanks.

This is basically an infinite type. Map Char (Int, Map Char (Int, Map Char (... ¿()?)...))) is what the type synonym would have to unroll, to allow the stuff you're doing in Python.
Haskell doesn't allow infinite types per se, but it does allow you to create the structure of such a type. For this, it's not sufficient to make a type synonym, you need a newtype, which in this case means to the compiler “I shouldn't bother recursing this, it's a known, distinguishable type that has already been checked”.
newtype NestedHashMap k v = NestedHashMap -- N.B. the `v` argument is unused
{ getNestedHashMap :: HashMap k (Int, NestedHashMap k v) }
toNestedHashMap :: String -> NestedHashMap Char ()
toNestedHashMap [] = NestedHashMap $ fromList []
toNestedHashMap (x:xs) = NestedHashMap $ fromList [(x, (1, toNestedHashMap xs))]

Related

how can i write a groupBy function for list elements in haskell?

It's signature has to look like this:
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
My function should group the input list's elements to output lists based on whether the elements are equal or there's a strictly monotonous incrementing partlist in it.
Examples for termination:
-- groupBy (==) groups the equal elements
groupBy (==) [0, 0, 1, 1, 2, 2] == [[0, 0], [1, 1], [2, 2]]
groupBy (==) [0, 1, 2] == [[0], [1], [2]]
-- groupBy (<) returns the strictly monotonous incrementing partlists
groupBy (<) [0, 1, 2, 1, 2, 3] == [[0,1,2],[1,2,3]]
groupBy (<) [3, 4, 5] == [[3, 4, 5]]
groupBy (>=) [3, 3, 1, 5] == [[3,3,1],[5]] --- monotonous decrementing
-- partlists, where the consecutive elements' difference is 1:
groupBy (\x y -> abs (x - y) == 1) [0, 1, 3, 4] == [[0, 1], [3, 4]]
groupBy (\x y -> abs (x - y) == 1) [1, 2, 3, 2, 1, 10, 11] == [[1,2,3,2,1],[10,11]]
Thanks for your help in advance :)
Look at the comparisons that have to happen here. You're providing a function f :: a -> a -> Bool that will group each item as long as it remains true, in other words:
groupBy (>=) [3, 3, 1, 5, 4]
[3]
3 >= 3 = True [3, 3] -- add RHS value
3 >= 1 = True [3, 3, 1]
1 >= 5 = False -- new group!
[5]
5 >= 4 = True [5, 4]
This looks like recursion to me. Your inner function should keep an accumulator, look at each element of the list and decide to either A) include it in the current accumulator or B) give the accumulator back and start a new one based on the results of f.
It's probably helpful to define groupBy as a wrapper around some helper function that does the real work.
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy f (x:xs) = go f [x] xs
where
-- base case: no values left to check
go _ acc [] = [acc]
-- recursive case
go f acc#(y:_) (x:xs)
| y `f` x = go f (x:acc) xs -- add to current group
| otherwise = acc : go f [x] xs -- start a new group
Note that the above has a subtle bug that I left in for illustrative purposes (and also because I wrote it this way first, so you might, too!)

How to combine two lists in Haskell

I want to make a list of lists like this:
[1,2,3] [4,5,6] -> [[1,2,3], 4, 5, 6]
This what i have by now:
combine :: [a] -> [a] -> [[a]]
combine xs ys = [xs,ys]
But this code gives me: [[1, 2, 3], [4, 5, 6]] and is not what I need.
As m0nhawk writes in the comments, you can't directly have a Haskell List of both lists of integers and integers. There are several alternatives, though.
One alternative is indeed to use a list of lists of integers ([[1, 2, 3], [4], [5], [6]]), like this:
combine:: [Int] -> [Int] -> [[Int]]
combine xs ys = [xs] ++ [[y] | y <- ys]
main = do
putStrLn $ show $ combine [1, 2, 3] [4, 5, 6]
(running this indeed prints [[1, 2, 3], [4], [5], [6]]).
Another alternative is to use algebraic data types:
Prelude> data ScalarOrList = Scalar Int | List [Int] deriving(Show)
Prelude> [List [1, 2, 3], Scalar 4, Scalar 5, Scalar 6]
[List [1,2,3],Scalar 4,Scalar 5,Scalar 6]
There are such things as heterogeneous lists in Haskell, but they're not particularly trivial or beginner-friendly:
https://hackage.haskell.org/package/hvect-0.4.0.0/docs/Data-HVect.html
This is also a good read: https://wiki.haskell.org/Heterogenous_collections
You're probably best off trying to see if you can make your own data type that encapsulates both simple values and lists of values:
data IntOrList = AnInt Int | AList [Int]
But then you'll have to unwrap your values, which might be an added layer that you don't want to deal with. At least they'll all be able to share a list though: someList = [AnInt 5, AnInt 7, AList [1, 2, 5, 8], AnInt 2]

alternative permutation of two lists

I'm trying to get a alternative permutation of two list with no fixed length for example:
x = ["a","b","c"]
y = [1,2,3]
should return:
[(a,1),(b,2),(c,3)] , [(a,2),(b,3),(c,1)], [(a,3),(b,1),(c,2)]
with list comprehensions I was able to get this:
[(x,y) | x<-x, y<-y ]
[("a",1),("a",2),("a",3),("b",1),("b",2),("b",3),("c",1),("c",2),("c",3)]
which is not exactly what I want
You only need to permute one of your two lists, then pair up each permutation with the other list in the original order:
import Data.List (permutations)
bijections :: [a] -> [b] -> [[(a, b)]]
bijections xs ys = map (zip xs) (permutations ys)
What this does is it generates all permutations of ys (so that would be [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1] for your example) and then for each of them, it pairs them up with xs in its original order.

Haskell List Comprehension for [q1,..,qn]

Is there a way to generate something like:
list n = [[q1,..,qn]|q1 <- [1..5], .., qn <- [1..5]]
?
I have a set, for example [1..5] and a number n, that determines how long is my list to work on. What I want is a list of this lists of length n, where each position ranges over my given set.
For example:
list 2 = [[1,1],[1,2],[1,3],[1,4],[1,5],[2,1],...]
list 3 = [[1,1,1],[1,1,2]...]
If I understand correct, what you want is a function which, given n lists, takes their cross product. For instance,
cross [[1, 2, 3], [10, 20, 30]] = [[1, 10], [2, 10], [3, 10]
,[1, 20], [2, 20], [3, 20]
,[1, 30], [2, 30], [3, 30]]
Interestingly enough, this function already exists, and it's called sequence. For instance:
Prelude> sequence [[1, 2, 3]]
[[1],[2],[3]]
Prelude> sequence [[1, 2, 3], [10, 20, 30]]
[[1,10],[1,20],[1,30],[2,10],[2,20],[2,30],[3,10],[3,20],[3,30]]
Prelude> sequence [[1, 2, 3], [10, 20, 30], [100, 200, 300]]
[[1,10,100],[1,10,200],[1,10,300],[1,20,100],[1,20,200],[1,20,300],[1,30,100],[1,30,200],[1,30,300],[2,10,100],[2,10,200],[2,10,300],[2,20,100],[2,20,200],[2,20,300],[2,30,100],[2,30,200],[2,30,300],[3,10,100],[3,10,200],[3,10,300],[3,20,100],[3,20,200],[3,20,300],[3,30,100],[3,30,200],[3,30,300]]
What's going on? Well, remember that the List type [] is a monad, and sequence has type sequence :: [m a] -> m [a]; essentially, it combines the monadic values using the monadic bind. When m is the list type, this becomes [[a]] -> [[a]]. To see how it works, consider the following code:
do
a <- [1, 2, 3]
b <- [10, 20, 30]
return [a, b]
This is equivalent to what sequence would do for only two input lists. List bind (>>=) is just flip concatMap and return just wraps the given value in a list, so this is equivalent to
let aList = [1, 2, 3]
bList = [10, 20, 30]
flippedConcatMap = flip concatMap in
aList `flippedConcatMap` (\a ->
bList `flippedConcatMap` (\b -> [[a, b]]))
This is essentially a nested for loop. For each value in aList, we apply the function that takes a. This function takes each value in bList, and applies the function that takes the b, which just returns a list with the single element [a, b]. Then, both "for loops" (concatMaps) take all the elements they generate and stick them into a single list, so all of the [a, b] lists get conglomerated into one, which yields what you see in GHCi when evaluating sequence [[1, 2, 3], [10, 20, 30]].

operation on list of lists | how

When I've seen concat function in Haskell book, I wonder how I can flatten the list below in Haskell. In Python, I can do that because I can check its type in function. But in Haskell I couldn't. How can I flatten the list below?
input: [[1, 2], [[2, 3], 5], [[[2, 3], [4, 5]], [2, 3]]]
output: [1, 2, 2, 3, 5, 2, 3, 4, 5, 2, 3]
As already pointed out, you can't have arbitrary nested lists in Haskell. The closest thing (without dirty type class hacks using fancy pragmas) would be something like:
data Nested a = L a | B [Nested a]
flatten :: Nested a -> [a]
flatten (L x) = [x]
flatten (B xs) = concatMap flatten xs
print $ flatten $ B[B[L 1,L 2],B[B[L 2,L 3],L 5],B[B[B[L 2,L 3],B[L 4, L 5]],B[L 2,L 3]]]
--[1,2,2,3,5,2,3,4,5,2,3]
You cant create a list with different depth in haskell. It won't t typecheck. [[a]] are not the same type as [[[a]]]. This function will solve your question but only on list with the same depth.
flat::[[a]] -> [a]
flat [] = []
flat l:ls = l ++ flat ls

Resources