Conversation
| int numIslands(vector<vector<char>>& grid) { | ||
| row_size = size(grid); | ||
| column_size = size(grid[0]); | ||
| recognized_island.assign(row_size, vector<char>(column_size, 0)); |
There was a problem hiding this comment.
0 か '1' が入っているということに違和感ですね。
1 がきっと入ったら混乱しますね。
bool のほうが間違えないのでは。
There was a problem hiding this comment.
ちなみに、'1'は49ですね。
https://www.asciitable.com/
There was a problem hiding this comment.
ありがとうございます。
step4でboolに修正しました。
適切な型を使うことを心がけます。
liquo-rice
left a comment
There was a problem hiding this comment.
スペースの使い方を直してください。early returnを適所に使っている点は良さそうでした。
| ```c++ | ||
| class Solution { | ||
| private: | ||
| int row_size,column_size; |
There was a problem hiding this comment.
Add a space after the comma
int row_size, column_size;
There was a problem hiding this comment.
丁寧にコードを見て頂きありがとうございます。
step4で修正しました。
以下を少しづつ見ていけたらと思っています。
https://google.github.io/styleguide/cppguide.html
| class Solution { | ||
| private: | ||
| int row_size,column_size; | ||
| int num_island=0; |
There was a problem hiding this comment.
Add spaces around the equal sign
int num_island = 0;
| private: | ||
| int row_size,column_size; | ||
| int num_island=0; | ||
| vector<vector<char>> recognized_island; //既に島として認識されている点を1とする。 |
| int num_island=0; | ||
| vector<vector<char>> recognized_island; //既に島として認識されている点を1とする。 | ||
|
|
||
| //start_pointと同じ島である点を調べ上げる。 |
There was a problem hiding this comment.
Indent and align the comment to the function below
Add a space after "//"
// start_pointと同じ島である点を調べ上げる。
void check_same_island (pair<int, int> start_point, vector<vector<char>>& grid) {
| vector<vector<char>> recognized_island; //既に島として認識されている点を1とする。 | ||
|
|
||
| //start_pointと同じ島である点を調べ上げる。 | ||
| void check_same_island (pair<int, int> start_point, vector<vector<char>>& grid) { |
| tie(x_connected, y_connected) = seen.front(); | ||
| recognized_island[x_connected][y_connected] = '1'; | ||
| seen.pop(); | ||
| for (int i = 0; i < size(connected_point); i++) { |
| ```c++ | ||
| class Solution { | ||
| private: | ||
| int row_size,column_size; |
There was a problem hiding this comment.
Add a trailing underscore to differentiate between member and local variables
e.g. row_size_
| int x,y; | ||
| x = x_connected + connected_point[i].first; | ||
| y = y_connected + connected_point[i].second; | ||
| if( x < 0 || y < 0 || x >= row_size || y >= column_size){ |
There was a problem hiding this comment.
Fix spacing
if(x < 0 || y < 0 || x >= row_size || y >= column_size) {
| if( x < 0 || y < 0 || x >= row_size || y >= column_size){ | ||
| continue; | ||
| } | ||
| if (grid[x][y] == '0' || recognized_island[x][y] == '1'){ |
There was a problem hiding this comment.
Fix spacing
if (grid[x][y] == '0' || recognized_island[x][y] == '1') {
| if (grid[i][j] == '0' || recognized_island[i][j] == '1') { | ||
| continue; | ||
| } | ||
| check_same_island ({i,j}, grid); |
There was a problem hiding this comment.
Fix spacing
check_same_island({i, j}, grid);
| int row_size,column_size; | ||
| int num_island=0; | ||
| vector<vector<char>> recognized_island; //既に島として認識されている点を1とする。 |
There was a problem hiding this comment.
ここ、クラスメンバーにしておくと、numIslands を複数のスレッドから呼んだときにうまく動かなくなります。
スレッドセーフでない、などといいます。スレッドセーフでないときにはコメントが欲しい気持ちはありますね。
HTML Parser とかだったら、HTML 一つにつきインスタンス一つという設計も自然でしょうが、うーん、この程度の機能でスレッドセーフでないのか、という感覚はあります。
https://discord.com/channels/1084280443945353267/1237649827240742942/1293905886506385438
There was a problem hiding this comment.
@oda thread safetyに疎くて申し訳ないのですが、この場合どのように記述することが正解なのでしょうか?
There was a problem hiding this comment.
https://source.chromium.org/search?q=%2F%2F.*thread.%3Fsafe%20file:%5C.cc$
好きに書けばいいですが、たとえばこんなのです。
There was a problem hiding this comment.
メンバ変数ではなくて、関数内のローカル変数にするのが良いのかなと思います。メンバ変数で持ってしまうと、Soluionインスタンスが複数のスレッドから同時に呼ばれるとメンバ変数が共有されているため意図せず書き換わってしまいます。
There was a problem hiding this comment.
ありがとうございます。
使ってくれる人がわかりやすいように書こうとはしているつもりですが、
意識できていない部分でした....
step4としては、hayashi-ayさんの案のとおり、ローカル変数にして、
スレッドセーフとなるように修正しました。
(ローカル変数だと、それぞれのスレッドに対して別々に用意されるスタックに
格納されるので、同時に別のスレッドからインスタンスが呼びだれてもスレッド競合は起きないと理解しています。)
https://www.divx.co.jp/media/techblog-220627
| //start_pointと同じ島である点を調べ上げる。 | ||
| void check_same_island (pair<int, int> start_point, vector<vector<char>>& grid) { | ||
| queue<pair<int, int>> seen; | ||
| vector<pair<int ,int >> connected_point = {{1,0}, {-1,0}, {0,1}, {0,1}}; |
There was a problem hiding this comment.
これは定数なので、以下のようにclass member としてrefactorしても良いのかなと思いました
private:
constexpr static int _connected_points[4][2] {
{0, 1}, {0, -1}, {1, 0}, {-1, 0}
};
There was a problem hiding this comment.
ありがとうございます。
vectorにconstexprを使おうとしたのですが、動的配列なので、ダメなのですね...
https://stackoverflow.com/questions/33241909/cannot-create-constexpr-stdvector
vectorに対してconstは使えるとのことなので、constで定数化してみました。
const vector<pair<int ,int >> deltas = {{1,0}, {-1,0}, {0,1}, {0,-1}};
| ```c++ | ||
| class Solution { | ||
| private: | ||
| int row_size,column_size; |
There was a problem hiding this comment.
私個人の意見ですが、class memberとlocal variableを区別しやすくするために、class memberには "_"を付けるのが良いかと思います。
ex:
int row_size_, column_size_
or
int _row_size, _column_size
因みに_を先につけると、IDEで_を打つだけで変数を見つけてくれるメリットがあります!
There was a problem hiding this comment.
https://google.github.io/styleguide/cppguide.html#Variable_Names
Data members of classes, both static and non-static, are named like ordinary nonmember variables, but with a trailing underscore.
Google Style Guide もそれやってますね。これも趣味の範囲です。(自分一人ならば趣味の問題だが、チームで開発するときには周りに合わせるよ、という態度を取れれば良いということです。)
| while (!seen.empty()) { | ||
| int x_connected, y_connected; | ||
| tie(x_connected, y_connected) = seen.front(); | ||
| recognized_island[x_connected][y_connected] = '1'; |
There was a problem hiding this comment.
1とだけみてもなんのことなのか分からないので、定数化してあげると良いと思います。
There was a problem hiding this comment.
ありがとうございます。
こちらの変数については、visited_islandという変数名に変えて、
bool型に変えることで、意味がわかるようにしてみました。
| //start_pointと同じ島である点を調べ上げる。 | ||
| void check_same_island (pair<int, int> start_point, vector<vector<char>>& grid) { | ||
| queue<pair<int, int>> seen; | ||
| vector<pair<int ,int >> connected_point = {{1,0}, {-1,0}, {0,1}, {0,1}}; |
| while (!seen.empty()) { | ||
| int x_connected, y_connected; | ||
| tie(x_connected, y_connected) = seen.front(); | ||
| recognized_island[x_connected][y_connected] = '1'; |
There was a problem hiding this comment.
実際に訪れてからしかrecognized_islandを書き換えていないので、seenに同一のセルが複数個入るケースがありそうです。
There was a problem hiding this comment.
step4で、queueに訪問予定のセルが入ったタイミングで、書き換えるように変更しました
| //start_pointと同じ島である点を調べ上げる。 | ||
| void check_same_island (pair<int, int> start_point, vector<vector<char>>& grid) { | ||
| queue<pair<int, int>> seen; | ||
| vector<pair<int ,int >> connected_point = {{1,0}, {-1,0}, {0,1}, {0,1}}; |
There was a problem hiding this comment.
connected_point という変数名から、特定の位置を意味しているように感じました。ここでは今見ているポイントからの移動方向ということかと思うので、directions とかがわかりやすそうです。また単数形だと配列であることがわかりにくそうです。
|
|
||
| seen.push(start_point); | ||
| while (!seen.empty()) { | ||
| int x_connected, y_connected; |
There was a problem hiding this comment.
x, y を使うと座標軸と混ざってややこしいので、個人的には2次元配列では避けるようにしています。
There was a problem hiding this comment.
ありがとうございます。
一つ前のレビューでいただいた指摘も含めて、
step4では変数名がわかりやすいように修正しました。
| int x,y; | ||
| x = x_connected + connected_point[i].first; | ||
| y = y_connected + connected_point[i].second; | ||
| if( x < 0 || y < 0 || x >= row_size || y >= column_size){ |
There was a problem hiding this comment.
「範囲外である」ことを表す場合、「範囲内でない」という書き方のほうが個人的には理解しやすいです。
またこれも個人的な感覚かもしれませんが、x について確認してから y について見るといった順番で、かつ1つの変数に対して2つの条件がある場合は、数直線のように小さい方から順に書く順番がわかりやすく感じます。
| if( x < 0 || y < 0 || x >= row_size || y >= column_size){ | |
| if (!(0 <= x && x < row_size && 0 <= y && y < column_size)) { |
There was a problem hiding this comment.
step4で否定形の形に書き換えました。
変数の向きについては、私的には変数を左にしたほうが読みやすく感じでおります。
seal-azarashi/leetcode#17 (comment)
| int numIslands(vector<vector<char>>& grid) { | ||
| row_size = size(grid); | ||
| column_size = size(grid[0]); | ||
| recognized_island.assign(row_size, vector<char>(column_size, 0)); |
| int _row_size, _column_size; | ||
| int _num_island = 0; |
There was a problem hiding this comment.
これらの変数がオブジェクト内で共有されているのでまだスレッドセーフではないです。
また、numIslands()が複数回呼ばれるとどうなりますか?
There was a problem hiding this comment.
ありがとうございます。
変な質問かもしれず申し訳ないのですが、
メンバ関数のなかで、メンバ変数にアクセスするとスレッドセーフな関数にはならないと思うのですが、
そうなるとメンバ変数の利点は何なのでしょうか...
私としては、引数にせずに、クラスの様々なメンバ関数からアクセスできるから、コードが書きやすくなると思っていました。
There was a problem hiding this comment.
例えば、複数回の関数呼び出しに渡って状態を保存したいなら必要です。スレッドセーフにするには、ロックを使う方法などがあります。https://github.com/haniwachann/leetcode/pull/3/files/a799a8e7598e51d24022fd17ca1eeee8ad11406e#r1841490546
ここでは、int _row_size, _column_size;はgridから取り出せるので、私ならメンバーにしません。_num_islandはそもそもnumIslands()内でしか使われていないです。
| vector<vector<bool>> visited_island; // 既に島として認識されている点を1とする。 | ||
| _row_size = grid.size(); | ||
| _column_size = grid[0].size(); | ||
| visited_island.assign(_row_size, vector<bool>(_column_size, 0)); |
| }; | ||
| ``` | ||
|
|
||
| # step4 |
<問題>
https://leetcode.com/problems/number-of-islands/