このプログラムを改修するにあたっていくつか質問したいです
解決したいこと
paiza というサイトの問題集で以下の問題を以下のプログラムで解きました。
そして、問題自体はクリアできたのですが、気になったことがあったので質問させてください。
ソースコードの詳細
using System;
using System.Runtime.CompilerServices;
// n が縦の長さで、インデックスは i を割り当ててる
// m が横の長さで、インデックスは j を割り当ててる
namespace Island {
static class Program {
static void Main ( ) {
Read.InitNum(out int n, out int m); // 初期化数の読み込み
int[,] map = new int[n, m]; // 初期化
Read.Island(map, n, m); // map データの読み込み
int count = Search.Bigin(map, n, m); // 走査して、島の数を格納
Console.WriteLine(count); // 表示
}
}
// 読み込みメソッドをまとめたクラス
static class Read {
public static void InitNum ( out int n, out int m ) {
string[] strs = Console.ReadLine().Split();
m = int.Parse(strs[0]);
n = int.Parse(strs[1]);
}
public static void Island ( int[,] map, int n, int m ) {
for ( int i = 0; i < n; i++ ) {
string[] strs = Console.ReadLine().Split();
for ( int j = 0; j < m; j++ ) {
map[i, j] = int.Parse(strs[j]);
}
}
}
}
// 走査とカウントに必要なメソッドをまとめたクラス
static class Search {
public static int Bigin ( int[,] map, int n, int m ) {
// 島コード:陸地がない場合は 0 、陸地がある場合は 1 で map が読み込まれているので
// 2, 3, 4, 5 と分類コードを振り分けていく
int code = 2;
// map 全体を走査して map[i, j] が未分類コード 1 の時だけ
// 再帰的に連続する陸地を同じ分類コードに塗っていく
for ( int i = 0; i < n; i++ ) {
for ( int j = 0; j < m; j++ ) {
if ( map[i, j] == 1 ) {
Recursive(code, map, i, j, n, m);
code++;
}
}
}
// 最後の分類コードから初期値 2 を引いた数が島の数になる
return code - 2;
}
// 上下左右でメソッドを分けて効率化を図るか
// このままにしてすっきりしたコードを保つか
private static void Recursive ( int code, int[,] map, int i, int j, int n, int m ) {
if ( i >= 0 && j >= 0 && i < n && j < m && map[i, j] == 1 ) {
map[i, j] = code;
Recursive(code, map, i + 1, j, n, m ); // 下
Recursive(code, map, i - 1, j, n, m ); // 上
Recursive(code, map, i, j + 1, n, m ); // 右
Recursive(code, map, i, j - 1, n, m ); // 左
}
}
}
}
質問 関数を呼び出すたびに同じ変数を引き渡してていいのか?
map, n, m
をどこでも使っているので、C言語で使われるグローバル変数のようにしていちいち引数で渡す手間を省きたい。
そこで静的なクラスpublic static class GV
を作ってGV.
と参照する。
実装した静的なクラスの詳細
public static class GV { // GlobalVariables
public static int[,] Map { get; set; }
public static int N { get; set; }
public static int M { get; set; }
}
のもいいですが、いちいちGV.
をつけるのも面倒です。
ほかの方法を調べると{ get; set; }
を使えばフィールドを設定できるらしい?ということで、試しに書いてみました。
class Read
とclass Search
にそれぞれ以下のコード
追加したプロパティの詳細
public static int[,] Map {
get { return GV.Map; }
set { GV.Map = value; }
}
public static int N {
get { return GV.N; }
set { GV.N = value; }
}
public static int M {
get { return GV.M; }
set { GV.M = value; }
}
を書き加えて以下のようなソースコードにしました。
改修したソースコードの詳細
using System;
using System.Runtime.CompilerServices;
namespace Island {
static class Program {
static void Main ( ) {
Read.InitNum();
Read.Island();
int count = Search.Bigin();
Console.WriteLine(count);
}
}
// グローバル変数のように扱うクラス
public static class GV {
// N が縦の長さで、インデックスは i を割り当ててる
// M が横の長さで、インデックスは j を割り当ててる
public static int[,] Map { get; set; }
public static int N { get; set; }
public static int M { get; set; }
}
// 読み込みメソッドをまとめたクラス
static class Read {
public static int[,] Map {
get { return GV.Map; }
set { GV.Map = value; }
}
public static int N {
get { return GV.N; }
set { GV.N = value; }
}
public static int M {
get { return GV.M; }
set { GV.M = value; }
}
public static void InitNum ( ) {
string[] strs = Console.ReadLine().Split();
M = int.Parse(strs[0]);
N = int.Parse(strs[1]);
Map = new int[N, M];
}
public static void Island ( ) {
for ( int i = 0; i < N; i++ ) {
string[] strs = Console.ReadLine().Split();
for ( int j = 0; j < M; j++ ) {
Map[i, j] = int.Parse(strs[j]);
}
}
}
}
// 走査とカウントに必要なメソッドをまとめたクラス
static class Search {
public static int[,] Map {
get { return GV.Map; }
set { GV.Map = value; }
}
public static int N {
get { return GV.N; }
set { GV.N = value; }
}
public static int M {
get { return GV.M; }
set { GV.M = value; }
}
public static int Bigin ( ) {
// 島コード:陸地がない場合は 0 、陸地がある場合は 1 で map が読み込まれているので
// 2, 3, 4, 5 と分類コードを振り分けていく
int code = 2;
for ( int i = 0; i < N; i++ ) {
for ( int j = 0; j < M; j++ ) {
if ( Map[i, j] == 1 ) {
// map 全体を走査して map[i, j] が未分類コード 1 の時だけ
// 再帰的に連続する陸地を同じ分類コードに塗っていく
Recursive(code, i, j);
code++;
}
}
}
// 最後の分類コードから初期値 2 を引いた数が島の数になる
return code - 2;
}
// 上下左右でメソッドを分けて効率化を図るか
// このままにしてすっきりしたコードを保つか
private static void Recursive ( int code, int i, int j ) {
if ( i >= 0 && j >= 0 && i < N && j < M && Map[i, j] == 1 ) {
Map[i, j] = code;
Recursive(code, i + 1, j );
Recursive(code, i - 1, j );
Recursive(code, i, j + 1 );
Recursive(code, i, j - 1 );
}
}
}
}
しかしこれだと、class
の数が増えるたびに同じコードを書き加えなきゃいけないのと、そもそも、とりあえず動いてはいるけど{ get; set; }
の使い方がこれで正しいのか分からないです。
ということで、{ get; set; }
の使い方が正しいか、
class GV
にする、{ get; set; }
を上記のように使う、以外で何か方法があるか知りたいです。