fortran-lang / stdlib

Fortran Standard Library
https://stdlib.fortran-lang.org
MIT License
1.05k stars 164 forks source link

[Proposal] add `swap` subroutines: swap the values of two variables of the same type ? #462

Open zoziha opened 3 years ago

zoziha commented 3 years ago

Description

Does stdlib need simple swap routines as follows? (Wait for #449 )

interface swap
    ...
end interface swap

!! Swap the values of two variables of the same type.
!! `type & kind` description: logical, integer, real, complex, character, string_type
pure elemental subroutine swap_${t1[0]}$${k1}$(a, b)
    ${t1}$, intent(inout) :: a, b
    ${t1}$ :: c
    c = a
    a = b
    b = c
end subroutine swap_${t1[0]}$${k1}$

call [[stdlib_math(module):swap(interface)]](a, b)

The use of swap already in stdlib:

  1. https://github.com/fortran-lang/stdlib/pull/189#discussion_r426192657
  2. https://github.com/fortran-lang/stdlib/blob/888b5d343e5579a392273755b66394404fd361f9/src/stdlib_sorting_sort.fypp#L216~L219
aman-godara commented 3 years ago

swap using move_alloc for string_type (or in combination with allocatable character (character(len=:), allocatable)) might be a useful addition.

zoziha commented 3 years ago

swap using move_alloc for string_type (or in combination with allocatable character (character(len=:), allocatable)) might be a useful addition.

Hello @Aman-Godara , for string_type, is there any significant difference in efficiency when swap uses move_alloc or assignment(=)?

aman-godara commented 3 years ago

a = string_type( "... a very long string ..." ) b = string_type( "... another long string ..." ) Now if you want to swap a and b, using assignment operator your approach will be something like this:

c = a ! This will create a deep copy (since the underlying `raw` variable is `allocatable` and NOT `pointer`) of `a` and assign it to `c`)
a = b
b = c

Using move_alloc it will be like this:

move_alloc( a, c ) ! happens in O(1) time since no new copy has been created
move_alloc( b, a )
move_alloc( c, b )

So move_alloc is faster and takes O(1) extra space whereas assignment operator takes O(m) extra space and executes in O(m + n). Here m and n are the lengths of the input a and b

zoziha commented 3 years ago

Thank you for your reply. I just tested it and found that string_type%raw is of a private attribute, so we can't directly access allocatable raw using move_alloc.

    type :: string_type
        ! Use the sequence statement below as a hack to prevent extending this type.
        ! It is not used for storage association.
        sequence
        private
        character(len=:), allocatable :: raw  !! Component 'raw' is a PRIVATE component of 'string_type'
    end type string_type

And move_alloc requires its arguments to be allocatable.

type(string_type) :: a, b !! They are not allocatable.
a = string_type( "... a very long string ..." )
b = string_type( "... another long string ..." )
call move_alloc( b, a )   !! Error: `move_alloc` requires its arguments to be `allocatable`.

I also found that move_alloc is indeed more efficient than assignment operation. 👍

aman-godara commented 3 years ago

you cannot access raw variable outside of stdlib_stringtype.f90 file. Yes you are right move_alloc requires it arguments to be allocatable. Excuse me if I gave you wrong impression above. First we need to create a function which does equivalent of move_alloc for string_type and then use it in swap function.

In my comment above, by move_alloc( a, c ) I meant call a subroutine which does equivalent of move_alloc for string_type. Look at this PR to know more.

zoziha commented 3 years ago

Thank you very much!😘 Understood!