Fortran で array list を作ってみた

## 動機

前回は Fortran で linked list を実装しましたが、私が Java で普段よく使うのは ArrayList の方なので、今回は array list を実装します (前回記事 [Fortran で linked list を作ってみた](https://qiita.com/ue1221/items/1df930f4cd59ca8fd7e4)) 。配列が優秀な Fortran では array list の方が実装しやすいと思います。

## array list とは

array list は要素を配列としてもつリストのことです。長さが固定されている配列とは異なり、長さを自由に変えることができるのが特徴です。 linked list と比較すると、要素の追加や削除は遅い $O(N)$ ですが、要素の検索は高速 $O(1)$ です。

## 環境

macOS Sierra (10.12.6)
GNU Fortran (GCC) 6.3.0

## プログラム

実際に実装したのが以下のものです。

```fortran:array_list.f08
module mod_array_list
  implicit none

  type t_array_list
    integer :: num = 0
    integer, pointer :: arr(:) => null()
  end type t_array_list

contains

  subroutine add(list,item)
    implicit none
    type(t_array_list), intent(inout) :: list
    integer, intent(in) :: item
    integer, allocatable :: tmp(:)

    if (.not.associated(list%arr)) allocate(list%arr(1))
    if (list%num == size(list%arr)) then
      allocate(tmp(list%num))
      tmp = list%arr
      deallocate(list%arr)
      allocate(list%arr(2*list%num))
      list%arr(1:list%num) = tmp
      deallocate(tmp)
    end if

    list%num = list%num+1
    list%arr(list%num) = item
    return
  end subroutine add

  subroutine clear(list)
    implicit none
    type(t_array_list), intent(inout) :: list

    if (associated(list%arr)) deallocate(list%arr)
    list%num = 0
    return
  end subroutine clear

  function get(list,i) result(item)
    implicit none
    type(t_array_list), intent(inout) :: list
    integer, intent(in) :: i
    integer :: item

    item = list%arr(i)
    return
  end function get

  function remove(list,i) result(item)
    implicit none
    type(t_array_list), intent(inout) :: list
    integer, intent(in) :: i
    integer :: item

    item = list%arr(i)
    list%arr(i:) = list%arr(i+1:)
    list%num = list%num-1
    return
  end function remove

  subroutine replace(list,i,item)
    implicit none
    type(t_array_list), intent(inout) :: list
    integer, intent(in) :: i
    integer :: item

    list%arr(i) = item
    return
  end subroutine replace

  function first_index_of(list,item) result(idx)
    implicit none
    type(t_array_list), intent(inout) :: list
    integer, intent(in) :: item
    integer :: idx, i

    idx = -1
    do i = 1, list%num
      if (list%arr(i) == item) then
        idx = i
        return
      end if
    end do
    return
  end function first_index_of

  function last_index_of(list,item) result(idx)
    implicit none
    type(t_array_list), intent(inout) :: list
    integer, intent(in) :: item
    integer :: idx, i

    idx = -1
    do i = list%num, 1, -1
      if (list%arr(i) == item) then
        idx = i
        return
      end if
    end do
    return
  end function last_index_of

  subroutine show_all(list)
    implicit none
    type(t_array_list), intent(inout) :: list
    integer :: i

    if (.not.associated(list%arr)) return
    write(*,'(i0)',advance='no') list%arr(1)
    do i = 2, list%num
      write(*,'(x,i0)',advance='no') list%arr(i)
    end do
    write(*,*)
    return
  end subroutine show_all

end module mod_array_list
```

また、実装できているか確認するために以下のプログラムも書きました。

```fortran:test_array_list.f08
program test_array_list
  use mod_array_list
  implicit none
  type(t_array_list) :: list
  integer :: i, n = 3

  do i = 1, n
    call add(list,i)
    write(*,'("add: ",i0," size: ",i0)') i, list%num
  end do
  do i = 1, n
    call add(list,i)
    write(*,'("add: ",i0," size: ",i0)') i, list%num
  end do

  call show_all(list)

  write(*,'("first_index_of 2: ",i0," size: ",i0)') first_index_of(list,2), list%num

  write(*,'("last_index_of 2: ",i0," size: ",i0)') last_index_of(list,2), list%num

  write(*,'("remove at 3: ",i0," size: ",i0)') remove(list,3), list%num

  call show_all(list)

  call replace(list,3,9)
  write(*,'("replace at 3, size: ",i0)') list%num

  call show_all(list)

  write(*,'("get at 3: ",i0," size: ",i0)') get(list,3), list%num

  call show_all(list)

  call clear(list)
  write(*,'("clear, size: ",i0)') list%num

  call show_all(list)

  call add(list,10)
  write(*,'("add: ",i0," size: ",i0)') 10, list%num

  call show_all(list)

  stop
end program test_array_list
```

実行結果は以下のとおりです。

```
add: 1 size: 1
add: 2 size: 2
add: 3 size: 3
add: 1 size: 4
add: 2 size: 5
add: 3 size: 6
1 2 3 1 2 3
first_index_of 2: 2 size: 6
last_index_of 2: 5 size: 6
remove at 3: 3 size: 5
1 2 1 2 3
replace at 3, size: 5
1 2 9 2 3
get at 3: 9 size: 5
1 2 9 2 3
clear, size: 0
add: 10 size: 1
10
```

linked list よりも簡潔に実装できました。

## 追記 2019/08/11

不必要な部分を削り、よりオブジェクト指向な書き方に変更しました ([ソースコード](https://github.com/ue1221/fortran-utilities/blob/master/fortran08/array_list.f08)) 。