Objective and Motivation
ある整数/浮動小数点数/文字列が何らかの集合の要素であるかを調べたいときがよくありますよね.
そのときに,if
文を使って
my_value_is_in_my_list=.false.
do i=1,number_of_element
if(my_list(i)==my_value)then
my_value_is_in_my_list=.true.
endif
end do
のように条件分岐するのが常道ですが,これは少なくとも3つのバグの温床となります.
- イテレータをイテレーション中に誤って変更してしまう
- 条件文を書き忘れる,ないし書き誤る
- 戻り値の初期化を忘れる
さらに,集合の側をどう定義するかも問題で,有限個であれば列挙したリストを作成しておくのもよいですが,任意の開集合や閉集合について判定したいときなどはまた書き方を変える必要があります.
こうした苦労なしに,
print *, my_value .in. [10,11,12,12]
print *, my_value .in. [-1.0d0,2.0d0,3.0d0]
print *, my_value .in. to_range(20,30)!暗黙的に,範囲は閉区間
!以下,閉区間/開区間を明示
print *, my_value .in. to_range('[',20,30,']')!閉区間
print *, my_value .in. to_range('(',20,30,')')!開区間
print *, my_value .in. to_range('(',20,30,']')!半開区間
! 文字列
print *, my_value .in. "ABCDEFG"
のように呼び出せたらいいなと思いました.
実装方針としては,
- Fortranの組み込み関数
range
との衝突を避ける. -
type(Range_)
を定義しておいて,そこで最小値と最大値をメンバ変数に持たせておく -
interface
文を使い,演算子オーバーロードにより値がinteger/real/characterのいずれであっても適当な関数を呼び出せるようにする.
以下,実装の一部です.
module RangeClass
type :: Range_
real(real64) :: x_range(1:2)=[0.0d0 , 0.0d0]
contains
interface to_range
module procedure :: to_range_int32, to_range_real64
end interface
interface operator(.in.)
module procedure :: in_detect_by_range_int32,in_detect_by_range_real64,
end interface
function in_detect_by_range_int32(intval,in_range) result(ret)
integer(int32),intent(in) :: intval
type(Range_),intent(in) :: in_range
logical :: ret
ret = (in_range%x_range(1) <= dble(intval) ) .and. (dble(intval) <= in_range%x_range(2) )
end function
function in_detect_by_range_real64(intval,in_range) result(ret)
real(real64),intent(in) :: intval
type(Range_),intent(in) :: in_range
logical :: ret
ret = (in_range%x_range(1) <= intval ) .and. (intval <= in_range%x_range(2) )
end function
function to_range_int32(from,to) result(ret_range)
type(Range_) :: ret_range
integer(int32),intent(in) :: from,to
ret_range%x_range(1:2) = dble([from,to])
end function
function to_range_real64(from,to) result(ret_range)
type(Range_) :: ret_range
real(real64),intent(in) :: from,to
ret_range%x_range(1:2) = [from,to]
end function
...
end module RangeClass
実際に使ってみます.
program main
use RangeClass
implicit none
print *, 300 .in. to_range(200,400) ! True
end program
補遺
.in.
の設計についてベストプラクティスを模索中です.
type::range_
を定義しておいて,to_range()
で.in.
に渡すことで,ユーザーにtype::range_
の存在を意識させないのはよいかと思いますが,
to_range()
引数がwordyな気もします.
このような.in.
の設計がよいのでは,というコメントを歓迎します.この記事がきっかけとなり議論が深まることを期待しています.
また,実装は今しがた着手して上記機能のみ実装したのですが,まだ全てを実装できていません.すみません.
character
のところまで実装できましたら追記します.