From: Brian Hurt <brian.hurt@qlogic.com>
To: Ocaml Mailing List <caml-list@inria.fr>
Subject: [Caml-list] Probably FAQ: Why is list-append (list :: elem) so expensive?
Date: Wed, 25 Sep 2002 10:33:10 -0500 (CDT) [thread overview]
Message-ID: <Pine.LNX.4.33.0209250958520.1974-100000@eagle.ancor.com> (raw)
Hello, all. I'm new to Ocaml and have what is probably a frequently asked
question. It's actually one of the faqs which caused me question:
From:
http://caml.inria.fr/FAQ/FAQ_EXPERT-eng.html#couts
I get that the cost of list concatenation is proportional to the length of
the first list. So (elem :: list) is O(1) no matter what the length of
list is. But (list :: elem) is O(n), with n being the length of the list.
Why doesn't Ocaml keep a pointer to the last element of the list, as well
as the first element? This would make all list concatenation (in
situations where you don't have to duplicate a list) an O(1) operation.
At the cost of increasing the size of a list object.
An example of where this would come in usefull. Consider the merge
portion of a merge sort (yes, I know I'm duplicating code that is in the
List module- I'm doing it so that I can learn the language. Plus, here we
all understand the problem). The current implementation I have (probably
chock full of inefficiencies) is:
let merge a b =
(* merge sorted lists a and b into one big list *)
let reverse_list lst =
(* again, duplicating List module functionality for clarity *)
let rec reverse_list_int lst accum =
match lst with
[] -> accum
| x :: [] -> (x :: accum)
| x :: tail -> reverse_list_int tail (x :: accum)
in
reverse_list_int lst []
in
let rec merge_int a b accum =
match (a, b) with
( [], [] ) -> accum
| ( ahead :: [], [] ) -> (ahead :: accum)
| ( ahead :: atail, [] ) ->
merge_int atail b (ahead :: accum)
| ( [] , bhead :: [] ) -> (bhead :: accum)
| ( [] , bhead :: btail ) ->
merge_int a btail (bhead :: accum)
| ( ahead :: atail, bhead :: btail) ->
if (ahead < bhead) then
(* should replace this test with a call to cmp *)
merge_int atail b (ahead :: accum)
else
merge_int a btail (bhead :: accum)
in
reverse_list (merge_int a b [])
;;
Note that I've had to add a whole second function (reverse_list) which is
O(n) just to allow me to prepend instead of append to the accumulation
list. The natural way to do this would be:
let merge a b =
(* merge sorted lists a and b into one big list *)
let rec merge_int a b accum =
match (a, b) with
( [], [] ) -> accum
| ( ahead :: [], [] ) -> (accum :: ahead)
| ( ahead :: atail, [] ) ->
merge_int atail b (accum :: ahead)
| ( [] , bhead :: [] ) -> (accum :: bhead)
| ( [] , bhead :: btail ) ->
merge_int a btail (accum :: bhead)
| ( ahead :: atail, bhead :: btail) ->
if (ahead < bhead) then
(* should replace this test with a call to cmp *)
merge_int atail b (accum :: ahead)
else
merge_int a btail (accum :: bhead)
in
merge_int a b []
;;
Were appends O(1) instead of O(N), the second version of this code would
be signifigantly faster, as I don't have to do the O(N) reversal of the
list at the end. However, since appends aren't O(1), the second version
of the code is O(N^2)- diaster!
My first inclination would be to make all lists include a tail pointer.
Then, at a later point, the compiler could detect those lists which don't
need the tail pointer, and switch back to the old style lists.
Unless there's something I'm missing. I've only been playing with this
language for ~2 days or so. I am definately still a newbie. Pointers
and/or explanations would be greatly appreciated.
Brian
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
next reply other threads:[~2002-09-25 15:28 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2002-09-25 15:33 Brian Hurt [this message]
2002-09-25 15:39 ` Noel Welsh
2002-09-25 15:42 ` Oleg
2002-09-25 16:17 ` Markus Mottl
2002-09-25 18:44 ` Brian Hurt
2002-09-25 19:22 ` Markus Mottl
2002-09-26 7:10 ` Florian Hars
2002-09-26 14:44 ` Kontra, Gergely
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=Pine.LNX.4.33.0209250958520.1974-100000@eagle.ancor.com \
--to=brian.hurt@qlogic.com \
--cc=caml-list@inria.fr \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox