8

Is there an operator in SML which allows me to append to a list without having to create a new list? For example

I cant do this:

 [1,2,3]::1

but I can do this:

 [1,2,3]@[1]

which is odd as I have to create a list with 1 in it. Is there a better way of doing this?

Har
  • 3,727
  • 10
  • 41
  • 75
  • 2
    As @sepp2k said, appending to the end of a list is expensive. One of the slowest ways to build up a list is by appending elements one by one to the end of it since any such approach is at best quadratic in the length of the list. For this reason, SML programmers sometimes write function which create lists in a backwards order (appending to the front rather than the back, even in situations where appending to the back seems more natural) and then run the resulting list through `rev` (which is implemented in a clever way so that it is `O(n)`) to get the desired list. – John Coleman Feb 10 '17 at 04:27
  • Is there no tail pointer to append to? that would also be O(1) – Har Feb 10 '17 at 11:09
  • 2
    @Har Appending to a tail pointer (if there even was one, which there isn't) would mutate the original list. SML lists are not mutable, so this is out of the question. – sepp2k Feb 10 '17 at 12:10
  • @Har that is correct, there is no tail pointer to append to. – vijrox Feb 10 '17 at 19:10

2 Answers2

10

You have to create a list with a one in it either way. The tail of the tail of the tail of [1,2,3,1] is [1]. So if you have [1,2,3,1] in memory, you also have [1] somewhere in memory.

So even if there were operator like [1,2,3] @:: 1, it wouldn't make a difference since it still needs to create a list with a one in it.

PS: The real issue with xs @ [x] isn't the creation of the list, but the fact that its runtime is in O(n) (as opposed to x :: xs, which is in O(1)). This is also an issue inherent in the nature of immutable singly linked lists and thus can't be helped, but it's why you generally should avoid appending to the end of a list like that.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • 1
    1. how else would you append to a list? 2. Do they not store a tail pointer to which you would append to? 3. What does x::xs do exactly? Create a new list or append to the head pointer? – Har Feb 10 '17 at 09:45
  • 1
    Are lists in SML implemented as linked structures or as arrays? – Har Feb 10 '17 at 11:09
  • 1
    @Har SML list are *immutable*, singly linked lists. They're exactly equivalent to `datatype list 'a = Cons of 'a * 'a list | Nil` with `Cons` taking the place of `::` and `Nil` taking the place of `[]`. So they a non-empty list has a head value and another list that is its tail. Neither of those can be changed. `x :: xs` creates a new list with `x` as the head value and `xs` as the tail. – sepp2k Feb 10 '17 at 12:08
  • 1
    "how else would you append to a list?" If it can be avoided, I wouldn't. Instead I would prepend using `::`. For example if you're trying to append multiple times, it makes more sense to prepend multiple times and then reverse the list. – sepp2k Feb 10 '17 at 12:08
4

[1,2,3]::1 tries to append the int list [1,2,3] into the integer value 1, which is not possible as an integer cannot be appended to.
1::[1,2,3] however, is completely valid as the list [1,2,3] supports the appending operation. The evaluation rules for :: require t1 :: t1 list, where t1 is a value from type 1, and t1 list is a list of type 1, what you're doing is t1 list :: t1.
[1,2,3] @ [1] is valid because you're appending a list into a list.

Selhar
  • 136
  • 2
  • 10