Doubly-linked list/Definition: Difference between revisions

Content added Content deleted
Line 553: Line 553:


</lang>
</lang>

=={{header|ATS}}==

{{trans|Scheme}}


The example is broken into an interface ("static") file ''dllist.sats'', an implementation ("dynamic") file ''dllist.dats'', and a demonstration program ''dllist-demo.dats''. I broke up the example this way because the abstraction thus introduced is important; the implementation is a linear type, whereas the user sees it as a nonlinear type. The interface file completely hides the linear typing.

The reason for this complication is that, in ATS, a linear object can be treated as "mutable", without resorting to embedded C code. One cost of this is that an object of a linear type has to be freed explicitly. However, if it is cast to an "ordinary" (nonlinear) type, a garbage collector to handle freeing the object. Such casting is what I have done.

Here is ''dllist.sats''.
<lang ATS>(********************************************************************)
(* The public interface *)

typedef dllist_t (t : t@ype+, is_root : bool) = ptr
typedef dllist_t (t : t@ype+) = [b : bool] dllist_t (t, b)

(* Make a new dllist_t, consisting of a root node. *)
fun {t : t@ype}
dllist_t_make () : dllist_t (t, true)

(* Is this the root node, with no element stored? *)
fun {t : t@ype}
dllist_t_is_root
{is_root : bool}
(dl : dllist_t (t, is_root)) :
[b : bool | b == is_root]
bool b

(* Is this a non-root node, with an element stored? *)
fun {t : t@ype}
dllist_t_isnot_root
{is_root : bool}
(dl : dllist_t (t, is_root)) :
[b : bool | b == ~is_root]
bool b

(* Return the root node of the list. *)
fun {t : t@ype}
dllist_t_root (dl : dllist_t t) : dllist_t (t, true)

(* Return the previous node. *)
fun {t : t@ype}
dllist_t_previous (dl : dllist_t t) : dllist_t t

(* Return the next node. *)
fun {t : t@ype}
dllist_t_next (dl : dllist_t t) : dllist_t t

(* Insert an element before the given node. *)
fun {t : t@ype}
dllist_t_insert_before (dl : dllist_t t, elem : t) : void

(* Insert an element after the given node. *)
fun {t : t@ype}
dllist_t_insert_after (dl : dllist_t t, elem : t) : void

(* Remove the given node. It is an error to call this on the root
node. *)
fun {t : t@ype}
dllist_t_remove (dl : dllist_t t) : void

(* Return a copy of the stored element. It is an error to call this on
the root node. *)
fun {t : t@ype}
dllist_t_element (dl: dllist_t t) : t

fun {t : t@ype}
dllist_t_make_generator (dl: dllist_t t, direction : int) :
() -<cloref1> Option t

fun {t : t@ype}
dllist_t_to_list
(dl : dllist_t t) : [n : nat] list (t, n)

fun {t : t@ype}
list_to_dllist_t
{n : int}
(dl : list (t, n)) : dllist_t t

(********************************************************************)</lang>


Here is ''dllist.dats''.
<lang ATS>#define ATS_DYNLOADFLAG 0

#include "share/atspre_staload.hats"

staload "dllist.sats"
staload UN = "prelude/SATS/unsafe.sats"

(********************************************************************)
(* The implementation in terms of linear types. *)

absprop NODEPTR (t : t@ype+, is_root : bool, p : addr)

vtypedef nodeptr_vt (t : t@ype+, is_root : bool, p : addr) =
@(NODEPTR (t, is_root, p) | ptr p)
vtypedef nodeptr_vt (t : t@ype+, p : addr) =
[is_root : bool] nodeptr_vt (t, is_root, p)
vtypedef nodeptr_vt (t : t@ype+, is_root : bool) =
[p : addr] nodeptr_vt (t, is_root, p)
vtypedef nodeptr_vt (t : t@ype+) =
[is_root : bool] [p : addr] nodeptr_vt (t, is_root, p)

datavtype node_vt (t : t@ype+, is_root : bool) =
| node_vt_object (t, is_root) of
(bool is_root, (* Is it the root node? *)
nodeptr_vt t, (* previous*)
nodeptr_vt t, (* next *)
t) (* element *)
vtypedef node_vt (t : t@ype+) =
[is_root : bool] node_vt (t, is_root)

extern castfn
node2nodeptr_consuming :
{t : t@ype}
{is_root : bool}
node_vt (t, is_root) -<> nodeptr_vt (t, is_root)

extern castfn
nodeptr2node_consuming :
{t : t@ype}
{is_root : bool}
nodeptr_vt (t, is_root) -<> node_vt (t, is_root)

extern castfn
node2nodeptr_preserving :
{t : t@ype}
{is_root : bool}
(!node_vt (t, is_root) >> _) -<> nodeptr_vt (t, is_root)

extern castfn
nodeptr2node_preserving :
{t : t@ype}
{is_root : bool}
(!nodeptr_vt (t, is_root) >> _) -<> node_vt (t, is_root)

fn {t : t@ype}
node_refcopy {is_root : bool}
(node : !node_vt (t, is_root)) :
node_vt (t, is_root) =
nodeptr2node_preserving (node2nodeptr_preserving node)

fn {t : t@ype}
make_root () : node_vt (t, true) =
(* Create a node that is marked as a root, points to itself, and has
no actual element stored in it. *)
let
var fake_elem : t?
prval _ = $UN.castview2void_at{t} (view@ fake_elem)

val node = node_vt_object (true,
$UN.castvwtp0 the_null_ptr,
$UN.castvwtp0 the_null_ptr,
fake_elem)

val prev_val = node2nodeptr_preserving node
val next_val = node2nodeptr_preserving node

val+ @ node_vt_object (_, prev, next, _) = node
val _ = prev := prev_val
val _ = next := next_val
prval _ = fold@ node
in
node
end

fn {t : t@ype}
is_root {is_root : bool}
(node : !node_vt (t, is_root)) :
[b : bool | b == is_root] bool b =
case+ node of
| node_vt_object (is_root, _, _, _) => is_root

fn {t : t@ype}
isnot_root {is_root : bool}
(node : !node_vt (t, is_root)) :
[b : bool | b == ~is_root] bool b =
~is_root<t> node

fn {t : t@ype}
find_root (node : !node_vt t) : node_vt (t, true) =
let
fun
loop (node : !node_vt t) : node_vt (t, true) =
case+ node of
| node_vt_object (true, _, _, _) => node_refcopy node
| node_vt_object (false, prev, _, _) =>
let
val prev_node = nodeptr2node_preserving prev
val retval = loop prev_node
val _ = $UN.castvwtp0{void} prev_node
in
retval
end
in
loop node
end

fn {t : t@ype}
get_prev (node : !node_vt t) : node_vt t =
let
val+ @ node_vt_object (_, prev, _, _) = node
val prev_node = nodeptr2node_preserving prev
prval _ = fold@ node
in
prev_node
end

fn {t : t@ype}
get_next (node : !node_vt t) : node_vt t =
let
val+ @ node_vt_object (_, _, next, _) = node
val next_node = nodeptr2node_preserving next
prval _ = fold@ node
in
next_node
end

fn {t : t@ype}
set_prev (node : !node_vt t, prev_val : !node_vt t) : void =
{
val+ @ node_vt_object (_, prev, _, _) = node
val _ = prev := node2nodeptr_preserving prev_val
prval _ = fold@ node
}

fn {t : t@ype}
set_next (node : !node_vt t, next_val : !node_vt t) : void =
{
val+ @ node_vt_object (_, _, next, _) = node
val _ = next := node2nodeptr_preserving next_val
prval _ = fold@ node
}

fn {t : t@ype}
insert_before (node : !node_vt t, elem : t) : void =
{
val prev_node = get_prev<t> node
val new_node =
node_vt_object (false, node2nodeptr_preserving prev_node,
node2nodeptr_preserving node, elem)
val _ = set_next<t> (prev_node, new_node)
val _ = set_prev<t> (node, new_node)
val _ = $UN.castvwtp0{void} prev_node
val _ = $UN.castvwtp0{void} new_node
}

fn {t : t@ype}
insert_after (node : !node_vt t, elem : t) : void =
{
val next_node = get_next<t> node
val new_node =
node_vt_object (false, node2nodeptr_preserving node,
node2nodeptr_preserving next_node, elem)
val _ = set_next<t> (node, new_node)
val _ = set_prev<t> (next_node, new_node)
val _ = $UN.castvwtp0{void} next_node
val _ = $UN.castvwtp0{void} new_node
}

fn {t : t@ype}
remove (node : !node_vt t) : void =
{
val _ = assertloc (isnot_root<t> node)
val prev_node = get_prev<t> node
val next_node = get_next<t> node
val _ = set_next<t> (prev_node, next_node)
val _ = set_prev<t> (next_node, prev_node)
val _ = $UN.castvwtp0{void} prev_node
val _ = $UN.castvwtp0{void} next_node
}

fn {t : t@ype}
get_element (node : !node_vt t) : t =
case+ node of
| node_vt_object (is_root, _, _, elem) =>
begin
assertloc (~is_root);
elem
end

(********************************************************************)
(* Implementation of the public interface. *)

(* The public interface is "nonlinear"; that is, its types are
"ordinary", and will have to be managed by a garbage collector, if
you do not want them to leak freely. The need to free the linear
types is bypassed by these interface template functions.

This, of course, is the situation with a great many programming
languages, and with more of them all the time. So it is nothing
extraordinary.

The usual garbage collector to use with ATS2 (Postiats) is Boehm
GC. *)

implement {t}
dllist_t_make () =
$UN.castvwtp0 (make_root<t> ())

implement {t}
dllist_t_is_root {is_root} dl =
let
val node = $UN.castvwtp0{node_vt (t, is_root)} dl
val retval = is_root<t> node
val _ = $UN.castvwtp0{void} node
in
retval
end

implement {t}
dllist_t_isnot_root {is_root} dl =
let
val node = $UN.castvwtp0{node_vt (t, is_root)} dl
val retval = isnot_root<t> node
val _ = $UN.castvwtp0{void} node
in
retval
end

implement {t}
dllist_t_root dl =
let
val node = $UN.castvwtp0{node_vt t} dl
val root = find_root<t> node
val _ = $UN.castvwtp0{void} node
in
$UN.castvwtp0 root
end

implement {t}
dllist_t_previous dl =
let
val node = $UN.castvwtp0{node_vt t} dl
val prev = get_prev<t> node
val _ = $UN.castvwtp0{void} node
in
$UN.castvwtp0 prev
end

implement {t}
dllist_t_next dl =
let
val node = $UN.castvwtp0{node_vt t} dl
val next = get_next<t> node
val _ = $UN.castvwtp0{void} node
in
$UN.castvwtp0 next
end

implement {t}
dllist_t_insert_before (dl, elem) =
let
val node = $UN.castvwtp0{node_vt t} dl
val next = insert_before<t> (node, elem)
val _ = $UN.castvwtp0{void} node
in
end

implement {t}
dllist_t_insert_after (dl, elem) =
let
val node = $UN.castvwtp0{node_vt t} dl
val next = insert_after<t> (node, elem)
val _ = $UN.castvwtp0{void} node
in
end

implement {t}
dllist_t_remove dl =
let
val node = $UN.castvwtp0{node_vt t} dl
val next = remove<t> node
val _ = $UN.castvwtp0{void} node
in
end

implement {t}
dllist_t_element dl =
let
val node = $UN.castvwtp0{node_vt t} dl
val elem = get_element<t> node
val _ = $UN.castvwtp0{void} node
in
elem
end

implement {t}
dllist_t_make_generator (dl, direction) =
if isltz direction then
let
val node_ref = ref (dllist_t_previous<t> (dllist_t_root<t> dl))
val p_node = $UN.castvwtp0{ptr} node_ref
in
lam () =>
let
val node_ref = $UN.castvwtp0{ref (dllist_t t)} p_node
val node = !node_ref
in
if dllist_t_is_root<t> node then
None ()
else
let
val elem = dllist_t_element<t> node
in
!node_ref := dllist_t_previous<t> node;
Some elem
end
end
end
else
let
val node_ref = ref (dllist_t_next<t> (dllist_t_root<t> dl))
val p_node = $UN.castvwtp0{ptr} node_ref
in
lam () =>
let
val node_ref = $UN.castvwtp0{ref (dllist_t t)} p_node
val node = !node_ref
in
if dllist_t_is_root<t> node then
None ()
else
let
val elem = dllist_t_element<t> node
in
!node_ref := dllist_t_next<t> node;
Some elem
end
end
end

implement {t}
dllist_t_to_list dl =
let
var lst : List0 t = list_nil ()
var xopt : Option t
val gen = dllist_t_make_generator<t> (dl, ~1)
in
for (xopt := gen (); option_is_some xopt; xopt := gen ())
case+ xopt of
| Some x => lst := list_cons (x, lst);
lst
end

implement {t}
list_to_dllist_t lst =
let
val root = dllist_t_make<t> ()
var dl : dllist_t t = root
var p : List t
in
for (p := lst; isneqz p; p := list_tail p)
begin
dllist_t_insert_after<t> (dl, list_head p);
dl := dllist_t_next<t> dl
end;
root
end

(********************************************************************)</lang>


And here is ''dllist-demo.dats''.
<lang ATS>#include "share/atspre_staload.hats"

staload "dllist.sats"
staload _ = "dllist.dats"

(* Using macdefs as follows, rather than implementing compiled
functions in terms of the templates, means the templates will be
expanded each time you call the macro. You may or may not wish
this. You *might* get big code that optimizes well. *)

typedef dl_t = dllist_t int

macdef dlmake = dllist_t_make<int>

macdef insert_before = dllist_t_insert_before<int>
macdef insert_after = dllist_t_insert_after<int>
macdef remove = dllist_t_remove<int>

macdef get_root = dllist_t_root<int>
macdef get_prev = dllist_t_previous<int>
macdef get_next = dllist_t_next<int>

macdef is_root = dllist_t_is_root<int>
macdef isnot_root = dllist_t_isnot_root<int>

macdef get_element = dllist_t_element<int>

macdef make_generator = dllist_t_make_generator<int>
macdef dl2list = dllist_t_to_list<int>
macdef list2dl = list_to_dllist_t<int>

fn
print_forwards (dl : dl_t) =
let
val gen = make_generator (dl, 1)
var xopt : Option int
var separator : string = ""
in
for (xopt := gen (); option_is_some xopt; xopt := gen ())
case+ xopt of
| Some x =>
begin
print! separator;
print! x;
separator := " "
end
end

fn
print_backwards (dl : dl_t) =
let
val gen = make_generator (dl, ~1)
var xopt : Option int
var separator : string = ""
in
for (xopt := gen (); option_is_some xopt; xopt := gen ())
case+ xopt of
| Some x =>
begin
print! separator;
print! x;
separator := " "
end
end

implement
main0 () =
{
val dl = list2dl ($list{int} (10, 20, 30, 40, 50))
val _ = print! ("doubly linked list: ")
val _ = print_forwards dl
val _ = println! ()

val _ = print! ("conversion to a regular list: ")
val _ = println! (dl2list dl)

val _ = print! ("traversal backwards: ")
val _ = print_backwards dl
val _ = println! ()

val _ = print! ("traversal forwards, given a non-root node: ")
val _ = print_forwards (get_prev (get_prev dl))
val _ = println! ()

val _ = print! ("traversal backwards, given a non-root node: ")
val _ = print_backwards (get_prev (get_prev dl))
val _ = println! ()

val _ = print! ("insertion after the root: ")
val _ = insert_after (dl, 5)
val _ = print_forwards dl
val _ = println! ()

val _ = print! ("insertion before the root: ")
val _ = insert_before (dl, 55)
val _ = print_forwards dl
val _ = println! ()

val _ = print! ("insertion after the second element: ")
val _ = insert_after (get_next (get_next dl), 15)
val _ = print_forwards dl
val _ = println! ()

val _ = print! ("insertion before the second from last element: ")
val _ = insert_before (get_prev (get_prev dl), 45)
val _ = print_forwards dl
val _ = println! ()

val _ = print! ("removal of the element 30: ")
val _ =
let
var p : dl_t
in
for (p := get_next dl; get_element p <> 30; p := get_next p)
();
remove p
end
val _ = print_forwards dl
val _ = println! ()
}</lang>




=={{header|AutoHotkey}}==
=={{header|AutoHotkey}}==