Skip to content

Conversation

@haniwachann
Copy link
Owner

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));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 か '1' が入っているということに違和感ですね。
1 がきっと入ったら混乱しますね。
bool のほうが間違えないのでは。

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ちなみに、'1'は49ですね。
https://www.asciitable.com/

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oda さんと同じ感想を持ちました。bool がよさそうです

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。
step4でboolに修正しました。
適切な型を使うことを心がけます。

Copy link

@liquo-rice liquo-rice left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

スペースの使い方を直してください。early returnを適所に使っている点は良さそうでした。

```c++
class Solution {
private:
int row_size,column_size;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a space after the comma
int row_size, column_size;

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

丁寧にコードを見て頂きありがとうございます。
step4で修正しました。
以下を少しづつ見ていけたらと思っています。
https://google.github.io/styleguide/cppguide.html

class Solution {
private:
int row_size,column_size;
int num_island=0;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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とする。

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vector<vector<bool>> visited_

int num_island=0;
vector<vector<char>> recognized_island; //既に島として認識されている点を1とする。

//start_pointと同じ島である点を調べ上げる。

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const 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++) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for (auto [dx, dy] : deltas) {

```c++
class Solution {
private:
int row_size,column_size;
Copy link

@liquo-rice liquo-rice Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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){

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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'){

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix spacing

check_same_island({i, j}, grid);

Comment on lines +15 to +17
int row_size,column_size;
int num_island=0;
vector<vector<char>> recognized_island; //既に島として認識されている点を1とする。
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここ、クラスメンバーにしておくと、numIslands を複数のスレッドから呼んだときにうまく動かなくなります。
スレッドセーフでない、などといいます。スレッドセーフでないときにはコメントが欲しい気持ちはありますね。

HTML Parser とかだったら、HTML 一つにつきインスタンス一つという設計も自然でしょうが、うーん、この程度の機能でスレッドセーフでないのか、という感覚はあります。
https://discord.com/channels/1084280443945353267/1237649827240742942/1293905886506385438

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oda thread safetyに疎くて申し訳ないのですが、この場合どのように記述することが正解なのでしょうか?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://source.chromium.org/search?q=%2F%2F.*thread.%3Fsafe%20file:%5C.cc$
好きに書けばいいですが、たとえばこんなのです。

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

メンバ変数ではなくて、関数内のローカル変数にするのが良いのかなと思います。メンバ変数で持ってしまうと、Soluionインスタンスが複数のスレッドから同時に呼ばれるとメンバ変数が共有されているため意図せず書き換わってしまいます。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。
使ってくれる人がわかりやすいように書こうとはしているつもりですが、
意識できていない部分でした....

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}};
Copy link

@austyhooong austyhooong Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これは定数なので、以下のようにclass member としてrefactorしても良いのかなと思いました

private:
constexpr static int _connected_points[4][2] {
{0, 1}, {0, -1}, {1, 0}, {-1, 0}
};

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。
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;
Copy link

@austyhooong austyhooong Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

私個人の意見ですが、class memberとlocal variableを区別しやすくするために、class memberには "_"を付けるのが良いかと思います。

ex:
int row_size_, column_size_
or
int _row_size, _column_size

因みに_を先につけると、IDEで_を打つだけで変数を見つけてくれるメリットがあります!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1とだけみてもなんのことなのか分からないので、定数化してあげると良いと思います。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。
こちらの変数については、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}};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

よく見ると、{0,1}が2つありますね。

while (!seen.empty()) {
int x_connected, y_connected;
tie(x_connected, y_connected) = seen.front();
recognized_island[x_connected][y_connected] = '1';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

実際に訪れてからしかrecognized_islandを書き換えていないので、seenに同一のセルが複数個入るケースがありそうです。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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}};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

connected_point という変数名から、特定の位置を意味しているように感じました。ここでは今見ているポイントからの移動方向ということかと思うので、directions とかがわかりやすそうです。また単数形だと配列であることがわかりにくそうです。


seen.push(start_point);
while (!seen.empty()) {
int x_connected, y_connected;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

x, y を使うと座標軸と混ざってややこしいので、個人的には2次元配列では避けるようにしています。

tarinaihitori/leetcode#17 (comment)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。
一つ前のレビューでいただいた指摘も含めて、
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){

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

「範囲外である」ことを表す場合、「範囲内でない」という書き方のほうが個人的には理解しやすいです。

またこれも個人的な感覚かもしれませんが、x について確認してから y について見るといった順番で、かつ1つの変数に対して2つの条件がある場合は、数直線のように小さい方から順に書く順番がわかりやすく感じます。

Suggested change
if( x < 0 || y < 0 || x >= row_size || y >= column_size){
if (!(0 <= x && x < row_size && 0 <= y && y < column_size)) {

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oda さんと同じ感想を持ちました。bool がよさそうです

continue;
}
check_same_island ({i,j}, grid);
++num_island;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここで前置インクリメントしているのには理由がありますか?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

他の箇所と統一がなかったです。
step4では前置のインクリメントで統一しました。

Comment on lines +260 to +261
int _row_size, _column_size;
int _num_island = 0;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これらの変数がオブジェクト内で共有されているのでまだスレッドセーフではないです。

また、numIslands()が複数回呼ばれるとどうなりますか?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。

変な質問かもしれず申し訳ないのですが、
メンバ関数のなかで、メンバ変数にアクセスするとスレッドセーフな関数にはならないと思うのですが、
そうなるとメンバ変数の利点は何なのでしょうか...

私としては、引数にせずに、クラスの様々なメンバ関数からアクセスできるから、コードが書きやすくなると思っていました。

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

例えば、複数回の関数呼び出しに渡って状態を保存したいなら必要です。スレッドセーフにするには、ロックを使う方法などがあります。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));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0, 1ではなく、true/falseを使ってください。

};
```

# step4

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

スペースの空け方がまだところどころおかしいです。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants