• No results found

PROCESSING LINKED LISTS RECURSIVELY

In document Fortran Array and Pointer Techniques (Page 119-123)

Maintaining a Large Ordered List

5.3 PROCESSING LINKED LISTS RECURSIVELY

The actual argument to a recursive linked list procedure is a pointer to the remainder of a list (initially Root_NP, the pointer to the entire list). The procedure processes the current node and then calls itself recursively to process the remainder (unless the remainder is empty). While processing the current node, the procedure may modify the dummy argument pointer. Such modifications affect the actual linkage of the list, and not merely a copy of the argument pointer.

The following example shows how to insert a new item into an ordered linked list. Arguments in the initial call to the recursive subroutine are Root_NP and the data to be inserted.

The outer if construct tests whether the current node pointer is associated (i.e., whether it has an actual target).

• If the current node pointer is not null, the inner if construct compares the Key in the current node, referred to as Arg_NP % Info % Key, with the Key component of the given item. This comparison has three possible outcomes:

1. If the keys match, information in the current node is to be modified.

2. If Arg_NP % Info % Key is larger, no matching key will be found — the search has passed the point where a match would have occurred — so the given item is inserted ahead of the given node. The procedure Insert_Target discussed earlier performs this operation and correctly modifies the current input pointer.

3. Otherwise, Arg_NP % Info % Key is smaller than the search key, so the procedure is called recursively to search the remainder of the list.

• If the current node pointer is null, the else block of the outer if construct is executed and the given item is inserted at the end of the list.

recursive subroutine Look_Up( Arg_NP, Item ) type (Node_Type), pointer :: Arg_NP

type (Info_Type), intent(in) :: Item

! start subroutine Look_Up

if (associated( Arg_NP )) then

if (Item % Key == Arg_NP % Info % Key) then call Modify_Target( Arg_NP, Item )

else if (Arg_NP % Info % Key > Item % Key) then

call Insert_Target( Arg_NP, Item ) ! Insert before next node.

else

call Look_Up( Arg_NP % Next_NP ) ! Keep looking.

end if else

call Insert_Target( Arg_NP, Item ) ! Insert at end of linked list.

end if return

end subroutine Look_Up

This recursive approach is very elegant, but the time and space overhead imposed by recursion leave some scholars unconvinced of its merits. A new activation record (see Section 3.1) is created at each recursive call — that is, for each node encountered by the linked list operation. For a very long linked list, the amount of extra storage occupied by these activation records may be intolerable. On the other hand, a long list will itself necessarily occupy a great deal of storage unless the amount of data in each node is trivial.

5.2 OPERATIONS ON WHOLE LINKED LISTS

Nevertheless, for long lists it is well to minimize the space occupied by each activation record. In particular, the procedure Look_Up has an argument Item that might be a large data structure in an actual application; it would be well to avoid storing redundant copies of this Item. A solution is to con-struct the subroutine Look_Up as a nonrecursive “wrapper.” The application program calls Look_Up, which in turn calls a recursive internal procedure R_Look_Up that inherits Item from the wrapper. The argument Item is stored only in the activation record for the wrapper; the activation record for R_Look_Up is now quite small because there are no local variables and the only argument is a pointer.

Say It with Fortran

Example 14

! Example 14. Linked List with Recursion module D14_M

implicit none

public :: Look_Up, Print_List, Delete_List integer, parameter, public :: S_LEN = 20 type, public :: Info_Type

character (len = S_LEN) :: Key

integer, dimension(2) :: Data = (/ 0, 1 /) end type Info_Type

type, private :: Node_Type type (Info_Type) :: Info

type (Node_Type), pointer :: Next_NP => Null( ) end type Node_Type

type (Node_Type), pointer, private :: Root_NP => Null( ) contains

subroutine Look_Up( Item )

type (Info_Type), intent(in out) :: Item

! start subroutine Look_Up call R_Look_Up( Root_NP ) return

contains

recursive subroutine R_Look_Up( Arg_NP ) type (Node_Type), pointer :: Arg_NP

! start subroutine R_Look_Up if (associated ( Arg_NP )) then

if (Item % Key == Arg_NP % Info % Key) then call Modify_Target( Arg_NP, Item )

else if (Arg_NP % Info % Key > Item % Key) then

call Insert_Target( Arg_NP, Item ) ! Insert ahead of next node.

else

call R_Look_Up( Arg_NP % Next_NP ) ! Keep looking.

end if else

call Insert_Target( Arg_NP, Item ) ! Insert at end of linked list.

end if return

end subroutine R_Look_Up

111 subroutineModify_Target(Arg_NP)

type(Node_Type),pointer::Arg_NP

! startsubroutineModify_Target

Arg_NP%Info%Data(2)=Arg_NP%Info%Data(2)+1 return

endsubroutineModify_Target

subroutineInsert_Target(Arg_NP,Item) type(Node_Type),pointer::Arg_NP,Temp_NP type(Info_Type),intent(in)::Item

! startsubroutineInsert_Target allocate(Temp_NP)

Temp_NP%Info=Item

Temp_NP%Next_NP=>Arg_NP Arg_NP=>Temp_NP

return

endsubroutineInsert_Target endsubroutineLook_Up

subroutinePrint_List()

! startsubroutinePrint_List callR_Print_List(Root_NP) return

contains

subroutineR_Print_List(Arg_NP) type(Node_Type),pointer::Arg_NP

! startsubroutineR_Print_List if(associated(Trav_NP))then

callPrint_Target(Trav_NP)

callR_Print_List(Arg_NP%Next_NP) !Advancetonextnode endif

return

endsubroutineR_Print_List subroutinePrint_Target(Arg_NP)

type(Node_Type),pointer::Arg_NP

! startsubroutinePrint_Target print*,"Print:",Arg_NP%Info return

endsubroutinePrint_Target endsubroutinePrint_List subroutineDelete_List()

! startsubroutineDelete_List callR_Delete_List(Root_NP) return

contains

5.3 PROCESSING LINKED LISTS RECURSIVELY

subroutineR_Delete_List()

! startsubroutineR_Delete_List if(associated(Root_NP))then

print*,"Deleted:",Delete_Target(Root_NP) Delete at root: Next call deletes at the same point.

call R_Delete_List( ) end if

return

end subroutine R_Delete_List

function Delete_Target( Arg_NP ) result(Item) type (Node_Type), pointer :: Arg_NP, Temp type (Info_Type) :: Item

! start function Delete_Target Temp_NP => Arg_NP

Item = Arg_NP % Info

Arg_NP => Arg_NP % BOX % Next_NP deallocate( Temp_NP )

end function Delete_Target end subroutine Delete_List end module D14_M

program D14 use D14_M implicit none integer :: EoF

type (Info_Type) :: Item

! start program D14

open (1, file = "dxf.txt", status = "old", action = "read", &

position = "rewind") do

read (1, *, iostat = EoF) Temp_NP % Key if (EoF < 0) exit

Item % Data(1) = Item % Data(1) + 1 call Look_Up( Item )

end do

call Print_List( ) call Delete_List( ) stop

end program D14

As mentioned earlier, a linked list may be viewed as a recursive data structure: Each node contains data as well as a pointer to the remainder of the list. Besides its philosophical elegance, recursive linked list processing fortuitously circumvents a syntactic shortcoming of Fortran and of some other program-ming languages. The recursive technique passes the pointer argument (at each recursive call) by refer-ence: what is actually transmitted is a pointer to the argument pointer. Pointers to pointers are needed, and recursion provides this facility in a (syntactically) simple manner. This philosophical elegance and syntactic simplicity are achieved at the expense of extra space and time overhead imposed by the mecha-nism of recursion, although with some care the space overhead can be minimized. Converting the tail recursion in this program to iteration without introducing significant syntactic complication seems im-possible.

113 Johnson

Predecessor Node

Remainder of List Lincoln

Current Node Traveling Pointer

Root Pointer

Washington Predecessor Node

Traveling Pointer Root Pointer

5.4 LINKED LISTS WITH POINTERS TO

In document Fortran Array and Pointer Techniques (Page 119-123)