Haskell supports some basic operations for recursing through lists, like head
, tail
, init
and last
. I'm wondering, internally, how Haskell is representing its list data? If it's a singly-linked list, then init
and last
operations could become costly as the list grows. If it's a doubly-linked list, all four operations could be made O(1)
quite easily, albeit at the expense of some memory. Either way, it's important for me to know, so I can write appropriate code. (although, the ethos of functional programming seems to be one of "ask what it does, not how it does it").
There are five different ways to construct lists in Haskell: Square-bracket syntax: This is the simplest and most recognisable way. Colon operator: This is very similar to the cons function from Lisp-like languages. It adds a single element to the beginning of a list (and returns a new list).
Recall earlier that Haskell has many different kinds of types, such as * for value-containing types, [*] for type-level lists, etc. Haskell also has a special kind called Symbol from the module GHC.
If your lists have always same size then just A == B . Also if your lists don't have the same size, just as == bs tells you if they are equal. @Ingo @mort's "working" solution treats, for example, [1,2,3] and [1,2,3,4] as equal, there (==) would not.
Lists are represented as ... singly linked lists. The definition is given by:
data [] a = [] | a : [a]
which you could write as:
data List a = Empty | Cons a (List a)
The memory layout is entirely defined by this.
So you end up with something like this:
So head
is O(1) on this structure, while last
or (++)
is O(n)
There is no magic to data structures in Haskell - their straight-forward definition makes entirely clear what the complexity will be (modulo laziness). If you need different complexity, use a different structure (such as IntMap, Sequence, HashMap, Vector etc)...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With