Conversation
| if (grid.isEmpty || grid[0].isEmpty) { | ||
| return 0; | ||
| } |
There was a problem hiding this comment.
正常に動作して何らかの結果(ここでは0)を返すのか、例外を上げるのかの選択肢は検討しても良さそうです。
ちなみに、自分なら後者を選びます。というのも、自分がこの関数のユーザーであることを想像すると、グリッド(や行)が空であることの確認は呼び出し元でやりそうで、この分岐に引っかかるのはそのチェックをすり抜けてしまった場合なので、それなら例外で止めてもらったほうがバグに気が付きやすいからです。
There was a problem hiding this comment.
レビューありがとうございます。
確かに 0 で返していると、デバッグ時に問題に気づくのが大変になりますね。参考になります!
| for (var row = 0; row < grid.length; row++) { | ||
| for (var col = 0; col < grid[0].length; col++) { | ||
| if (grid[row][col] != land || seenLands.contains((row, col))) { | ||
| continue; | ||
| } | ||
|
|
||
| var area = calcAreaOfIsland(row, col); | ||
| maxArea = max(maxArea, area); | ||
| } | ||
| } | ||
|
|
||
| return maxArea; |
There was a problem hiding this comment.
好みの範囲だと思いますが、calcAreaOfIsland のようなinner functionは別なメソッドとして括りだして、このメインループだけにしたいです。メソッドの行数が長くなって、コードを読むときに脳内のスタックに積んでおく情報が増えるのが、個人的には負荷が大きいです。何に使うかわからない関数が出てきた上でメインループが出てくるより、メインループが先に出てきて、じゃあサブルーチンで何をやってるのか見に行くか、という順番のほうがコードを読む心理的ハードルが下がる、というのもあります。
isLand くらいまでがinner functionにしても良い(個人的な)限度です。
| var smaller = find(key1); | ||
| var larger = find(key2); | ||
|
|
||
| if (smaller == larger) { | ||
| return; | ||
| } | ||
|
|
||
| if (_sizes[smaller] > _sizes[larger]) { | ||
| final tmp = smaller; | ||
| smaller = larger; | ||
| larger = tmp; | ||
| } |
There was a problem hiding this comment.
頭から読んだとき、以下のことが気になりました。
- key1の親のほうが小さいという保証があるのか?
- →実際にはあとでサイズを見て入れ替える
smaller == largerってどういうことだ?サイズに大小あることを想定しているが、同じになる場合がある?- →この段階ではサイズは関係ない
大きさのことを気にするのは、親が違うことを確認してからでどうでしょうか。
| var smaller = find(key1); | |
| var larger = find(key2); | |
| if (smaller == larger) { | |
| return; | |
| } | |
| if (_sizes[smaller] > _sizes[larger]) { | |
| final tmp = smaller; | |
| smaller = larger; | |
| larger = tmp; | |
| } | |
| final parent1 = find(key1); | |
| final parent2 = find(key2); | |
| if (parent1 == parent2) { | |
| return; | |
| } | |
| final smaller = (_sizes[parent1] <= _sizes[parent2]) ? parent1 : parent2; | |
| final larger = (_sizes[parent1] <= _sizes[parent2]) ? parent2 : parent1; |
(smaller, larger の代入の方法はこれがベストかは自信がありません。素直に var で入れてスワップの方がいいのかも)
There was a problem hiding this comment.
ご指摘の通りだと思います。「親が同じであれば早期終了する」という文脈ではサイズは全く関係ないですね。ありがとうございます。
代入の方法は私も悩みます…。LLM にあげてもらったのだと以下のような候補がありそうです。
3 の var でスワップは、大きい方なのに smaller に入る矛盾するケースがあり、ここは割とデメリットなのかなと思っています。そのため、個人的にはそのデメリットがなく、一番シンプルな 1 がわかりやすく好みでした。ただ、二項演算子はあまり好みではにという方もいらっしゃるので、チームの好みに合わせるのが良さそうですね。
// 1. 二項演算子で一回でどちらにも代入
final (smaller, larger) = _sizes[parent1] <= _sizes[parent2]
? (parent1, parent2)
: (parent2, parent1);// 2. if で長めに明確に書く
late final int smaller;
late final int larger;
if (_sizes[parent1] <= _sizes[parent2]) {
smaller = parent1;
larger = parent2;
} else {
smaller = parent2;
larger = parent1;
}// 3. var でスワップ
var smaller = parent1;
var larger = parent2;
if (_sizes[smaller] > _sizes[larger]) {
(smaller, larger) = (larger, smaller) // この方式でも書けるの知らなかった。
}// 4. sort を使う
final sorted = [parent1, parent2]
..sort((a, b) => _sizes[a].compareTo(_sizes[b]));
final smaller = sorted[0];
final larger = sorted[1];There was a problem hiding this comment.
私は 1. でも違和感ありませんでした。文句を言うような人がいるなら 2. を選びます。
ところで late final なんて便利な機能もあるんですね。
|
些末なところをコメントしましたが、全体的にとても読みやすかったです。 |
| final row = land.$1; | ||
| final col = land.$2; | ||
|
|
||
| for (var offset in offsets) { |
There was a problem hiding this comment.
インデントで2space/4spaceが混在していそうなのが少し気になりました。dartの一般的なルールを把握していないので、一般的に問題なければ大丈夫です。(ここのwhileが2spaceなのがずれているだけ?)
There was a problem hiding this comment.
レビューありがとうございます。
最近 formatter を使うのを忘れており、意図せず混在していました。ご指摘ありがとうございます。混在は一般的ではないと思います。
| import 'dart:math'; | ||
|
|
||
| class SolutionBFSTuple { | ||
| static const int land = 1; |
There was a problem hiding this comment.
大きな問題ではないですが、後でループ変数のlandと名前が被っています。どちらかを別の名前にした方が良いかもしれません。
| for (var col = 0; col < grid[0].length; col++) { | ||
| final place = grid[row][col]; | ||
|
|
||
| if (place == water || seenLands.contains((row, col))) { |
There was a problem hiding this comment.
大した理由ではないのですが、isLand 関数は、ここの判定を書いた後に作成したため、isLand で置き換えられることに気づいていなかったという理由になります。コードを書いた順番としては、
- 二重 for ループで、やりたいメインの処理を先に記載
- calcAreaOfIsland の関数の中身を記述
- calcAreaOfIsland の重複部分を isLand にまとめる
でした。
解いた問題:https://leetcode.com/problems/max-area-of-island/description/
次に解く問題:https://neetcode.io/problems/count-connected-components/question