配列要素取り出し接尾演算子
配列を返す関数を呼んで、その中の特定の要素だけが欲しいことがありますが、Fortran ではいったんテンポラリな配列にいれて、行を変えて添え字で抜き出す必要があります。
ここでは配列の後ろにつけて、特定の要素を取り出すユーザー定義演算子を定義してみます。
ソース・プログラム
python に習って、負の引数の時には配列のお尻から数えた要素を返すことにします。(しかし Fortran は添え字が 1 から始まるので 0 の扱いに困ります。)
module sub_m
implicit none
interface operator (.o.)
module procedure :: take
end interface
contains
pure real function take(d, i)
real , intent(in) :: d(:)
integer, intent(in) :: i
if (i > 0) then
take = d(i)
else if (i < 0) then
take = d(size(d) + i + 1)
else
take = 0.0
end if
end function take
end module sub_m
program oOo
use sub_m
implicit none
real :: a(10)
integer :: i
a = [(i * 1.0, i = 1, 10)]
print *, a .o. 2
print *, a .o. -1
print *, test(22.0/7.0) .o. 1
print *, test(22.0/7.0) .o. 2
print *, test(22.0/7.0) .o. -1
contains
function test(x)
real, intent(in) ::x
real, allocatable :: test(:)
test = [sin(x), cos(x), tan(x)]
end function test
end program oOo
実行結果
2.000000
10.00000
-1.2644208E-03
-0.9999992
1.2644219E-03
続行するには何かキーを押してください . . .
一応、望みの結果が得られましたが、部分配列を取り出すことなどは出来ないので、改良の余地はあると思います。
文字列と文字配列の変換
Fortran では、文字列と文字の配列は別物ですが、メモリー上の並びは同じになっていて、transfer 関数で相互に変換できます。
文字列の方が様々な点で使い勝手がいいのですが、配列の場合 elemental な関数を使って、文字全体を一括で処理することが出来る利点があります。
以下の例では、文字列を文字配列に変換して、英小文字だけを大文字に変化して、また文字列に戻します。また Fortran 2003 から、文字列長を再割り付けで自動的に調節できるので、その機能も使っています。
ソース・プログラム
Fortran の組み込み関数 iachar/achar はどちらも要素型の関数なので、配列を引数に入れると、MAP 演算のように各要素に作用した結果を配列で返してくれます。
なお Fortran には achar/iachar と char/ichar という、極めて似通った関数のセットがありますが、これはかつては IBM の EBCDIC 文字コード系をはじめとして各処理系ごとに独自の文字コードを用いていたため、ASCII 文字コード用の関数と、各処理系独自の文字コード用の関数の二つが用意されたことによると思われます。
program arai_choo
implicit none
character(*), parameter :: text = 'This is a pen.'
character, allocatable :: chars(:)
character(:), allocatable :: string
chars = transfer(text, chars) ! transfer(text, ' ', size = len(text))
print '(a)', text, chars
where (chars >= 'a' .and. chars <= 'z') chars = achar(iachar(chars) + iachar('A') - iachar('a'))
string = transfer(chars, repeat(' ', size(chars)))
print '(a)', string
end program arai_choo
実行結果
This is a pen.
T
h
i
s
i
s
a
p
e
n
.
THIS IS A PEN.
続行するには何かキーを押してください . . .
1 から N までの番号付け
Fortran では配列は 1 から番号付けが始まります。これは普通の行列などのテキストと一致するので至極自然で、自然数が 1 から始まるという定義も、もっともだなと感じられます。
一方で計算機上では、自然数は 0 から始まるほうが自ずから然りという事情もあって、Fortran でたまに困ることがあります。
私の場合、ある正整数 i を、1 から n の自然数に類別したいことが時々あります。(グラフの色を選ぶとか色々)この時、n の余りを取ればいいですが、その場合 1 から n ではなく、0 から n-1 の値が得られてしまします。+1 で誤魔化す場合もありますが、余りに意味がある場合もあって、できれば 0 だけが n になって欲しいことがあります。
これを簡単に実現する方法として、modulo 関数を使う方法があります。modulo 関数は mod 関数とは異なるもので、より数学的に性質がいいものとして Fortran 90 で導入されました。 mod 関数は引数が正負をまたぐとき、原点 0 に関して正負に対称になっていますが、modulo 関数は原点に関わらず並進的に結果を与えます。
ソース・プログラム
program numbering
implicit none
integer :: i, n = 7
do i = 0, 10
print *, mod(i, n), modulo(i, -n) + n
end do
end program numbering
実行結果
以下のように、望みの結果が得られます。ただし引数は正を仮定しています。
0 7
1 1
2 2
3 3
4 4
5 5
6 6
0 7
1 1
2 2
3 3
続行するには何かキーを押してください . . .
配列から重複要素を抜いて一覧表を作る (ダブりを削る)
データ読み込みなどをしたとき、重複したラベルを持つ要素を処理しなければならない時があります。また集合のように、二つの配列を合体させて、重複要素は1個と見なしたい場合もあります。
この時、クイックソートの流用で、ソートしつつダブりを消すことが出来ます。
以下では、文字列配列から重複要素を削って、出現文字列の一覧表を出力します。
ソース・プログラム
三種類の文字列が重複している大きさ 10 の文字配列から、重複要素を除いたものを出力しています。
Fortran 90 から、ASCII コード順での文字列の順序の比較が <,> 演算子で出来るようになりました。FORTRAN77 にも固有文字コード[訂正] ASCII コードでの比較関数がありましたが。
program dup
implicit none
character(len = 5) :: buff(10)
character(:), allocatable :: list(:)
buff( 1) = 'ABCDE'
buff( 2) = '12345'
buff( 3) = '@#$%%'
buff( 4) = '@#$%%'
buff( 5) = '12345'
buff( 6) = '12345'
buff( 7) = '@#$%%'
buff( 8) = 'ABCDE'
buff( 9) = '12345'
buff(10) = 'ABCDE'
!
list = deduplicate(buff)
!
print '(a)', list
contains
recursive function deduplicate(texts) result(res) ! skew-quicksort
character(*), intent(in) :: texts(:)
character(:), allocatable :: res(:)
integer :: m
m = size(texts) / 2
if (m < 1) then
res = texts
else
res = [ deduplicate(pack(texts, texts < texts(m))), &
texts(m), &
deduplicate(pack(texts, texts > texts(m))) ]
end if
end function deduplicate
end program dup
実行結果
12345
@#$%%
ABCDE
続行するには何かキーを押してください . . .