はじめに
AtCoderの問題では、縦$H$行、横$W$列からなるマス目の状態を文字列で与えられます。これを受け取るプログラムをワンライナーで書こうとしましたが僕の力では出来ませんでした。
入力の仕様(例)
整数$W$,$H$と、$H$個の長さ$W$の文字列$S_i (1 \le i \le H)$が与えられます。$S_i$の$j$文字目の文字は上から$i$行目、左から$j$列目を表しています。
W H
S_1
S_2
:
S_h
入力例
5 3
abcde
fghij
klmno
自然な実装
まずは自然な受け取り方を考えます。入力の仕様から考えても、「長さ$W$の文字列要素を$H$個持つ配列」を用意すれば良さそうです。
program main
use,intrinsic :: iso_fortran_env
implicit none
integer(int32):: w,h,i
character(:),allocatable:: s(:)
read*, w,h
allocate(character(w):: s(h))
read*, (s(i), i=1,h)
end program main
出来ました。これで十分な気がします。readしてる行の(..., i=1,h)
というのはimplied doと言うやつです。[(i, i=1,10)]
で、要素が1 2 3 4 5 6 7 8 9 10
の配列を返します。Pythonの内包表記のような感じです。
欠点
$i$行$j$列目の要素にアクセスするときにちょっと面倒です。
print*, s(i)(j:j) ! i行j列目の文字を表示
あと行と列でデータの持ち方が違う(文字列と文字配列)というのも気持ちが悪いですし、Row-majorなのも腹が立ちます。
いい感じにデータをもたせる
program main
use,intrinsic :: iso_fortran_env
implicit none
integer(int32):: w,h,i
character(1),allocatable:: s(:,:)
character(:),allocatable:: lines(:)
read*, w,h
allocate(character(w):: lines(h))
allocate(s(w,h))
read*, (lines(i), i=1,h)
s(:,:) = reshape([(transfer(lines(i),s(:,i)), i=1,h)], [w,h])
end program main
このように受け取るといい感じです。先程のデータの持ち方はlines
にやらせており、implied doとtransferとreshapeを使った前衛的なコードによって、1文字を2次元の配列で持つs
にわたしています。
$i$行$j$列目の要素にアクセスするときはこのように書けます。
print*, s(j,i) ! i行j列目の文字を表示
Column-majorでいい感じです。
欠点
先程より複雑になってしまいました。いっそのことライブラリにしてしまうのはどうでしょうか。
function read_grid(w,h) result(s)
integer(int32),intent(in):: w,h
character(1):: s(w,h)
character(w):: lines(h)
integer(int32):: i
read*, (lines(i), i=1,h)
s = reshape([(transfer(lines(i),s(:,i)), i=1,h)], [w,h])
end function
allocateしなくて良い分やや楽
使い方
program main
use,intrinsic :: iso_fortran_env
implicit none
integer(int32):: w,h
character(1),allocatable:: s(:,:)
read*, w,h
s = read_grid(w,h)
end program main
なかなか便利なライブラリじゃないでしょうか。
まとめ
いい感じの受け取り方を紹介+ライブラリ化をしました