-
Notifications
You must be signed in to change notification settings - Fork 0
20. Valid Parentheses #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b02e347
2a2aed0
8a197ff
9ee3225
d9b24ea
e671954
b24bea8
cbea1b2
cb778a8
96aa98b
38c934f
06b0afe
807fd0d
f9ece5c
18dbce1
6c05c9f
cad2d1c
ee3aaa5
8aeb275
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,373 @@ | ||
| # 20. Valid Parentheses | ||
|
|
||
| ## STEP 1 | ||
|
|
||
| ### 手作業でやってみる。 | ||
|
|
||
| * `{`, `[`, `(`, `}`, `]`, `)` と書いた紙が一列に並んでいる | ||
| * 一番最初の紙から作業をしていく。 | ||
| * 紙が`{`, `[`, `(` だったら、机の上に縦に積んでいく。 | ||
| * 紙が`}`, `]`, `)` だったら、机の上の一番上から紙を取り、同じ種類の紙かを確認する。 | ||
| * 作業が完了して、机の上に何も残っていない場合には、終了する。 | ||
| * 机の上に何か残っている場合には、異常を報告する。 | ||
|
|
||
| ```javascript | ||
| var isValid = function(characters) { | ||
| const container = [] | ||
| const open_bracket_chars = ["(", "{", "["] | ||
| for (const character of characters) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. bracketなどの命名でもいいのかなと思いました。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 予約語を変数名として避けるという意識ができておりませんでした。ありがとうございます。 @nodachipのコメントだと、C++の予約語を避けて、charを避けて ch, c, characterを選ぶというコメントを見つけました。言語によってもよく使う変数名があるというのが気付きでした。
以下がJavasciprtの予約語だそうです。
https://262.ecma-international.org/#sec-keywords-and-reserved-words Javaだと charも予約語なのですね。 |
||
| if (open_bracket_chars.includes(character)) { | ||
| container.push(character) | ||
| } | ||
| if (character === ")") { | ||
| const last_character = container.pop() | ||
| if (last_character !== "(") { | ||
| return false | ||
| } | ||
| } | ||
| if (character === "]") { | ||
| const last_character = container.pop() | ||
| if (last_character !== "[") { | ||
| return false | ||
| } | ||
| } | ||
| if (character === "}") { | ||
| const last_character = container.pop() | ||
| if (last_character !== "{") { | ||
| return false | ||
| } | ||
| } | ||
| } | ||
| if (container.length === 0) { | ||
| return true | ||
| } | ||
| return false | ||
| }; | ||
| ``` | ||
| ## STEP 2 | ||
|
|
||
| ### やったこと | ||
| * 関数を分けた. | ||
|
|
||
| ```javascript | ||
| function doesMatchBracket(candidate, close_bracket) { | ||
| if (candidate === "[" && close_bracket === "]") { | ||
| return true | ||
| } | ||
| if (candidate === "{" && close_bracket === "}") { | ||
| return true | ||
| } | ||
| if (candidate === "(" && close_bracket === ")") { | ||
| return true | ||
| } | ||
|
|
||
| return false | ||
| } | ||
| var isValid = function(characters) { | ||
| const container = [] | ||
| const open_bracket_chars = ["(", "{", "["] | ||
|
|
||
| for (const character of characters) { | ||
| // 開き括弧の場合 | ||
| if (open_bracket_chars.includes(character)) { | ||
| container.push(character) | ||
| continue | ||
| } | ||
|
|
||
| // 閉じ括弧の場合 | ||
| const open_bracket_candidate = container.pop() | ||
| const close_bracket = character | ||
| if (!doesMatchBracket(open_bracket_candidate, close_bracket)) { | ||
| return false | ||
| } | ||
| } | ||
| if (container.length === 0) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. return container.length === 0でいいかなと思いました。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 確かにその方が読みやすいですね。 ありがとうございます。 |
||
| return true | ||
| } | ||
|
|
||
| return false | ||
| }; | ||
| ``` | ||
|
|
||
| ## STEP 3 | ||
|
|
||
| ```javascript | ||
| function doesMatchBracket(candidate, close_bracket_character) { | ||
| if (candidate === "(" && close_bracket_character === ")") { | ||
| return true | ||
| } | ||
| if (candidate === "[" && close_bracket_character === "]") { | ||
| return true | ||
| } | ||
| if (candidate === "{" && close_bracket_character === "}") { | ||
| return true | ||
| } | ||
| return false | ||
| } | ||
| var isValid = function(characters) { | ||
| const container =[] | ||
| const open_bracket_characters = ["(", "[", "{"] | ||
|
|
||
| for (const character of characters) { | ||
| if (open_bracket_characters.includes(character)) { | ||
| container.push(character) | ||
| continue | ||
| } | ||
| const open_bracket_candidate = container.pop() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ご自身でご指摘の通り、
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 確かに早めにreturnする方が脳の負荷が減りますね。 |
||
| const close_bracket_character = character | ||
| if (!doesMatchBracket(open_bracket_candidate, close_bracket_character)) { | ||
| return false | ||
| } | ||
| } | ||
|
|
||
| if (container.length === 0) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 僕も同感です! |
||
| return true | ||
| } | ||
| return false | ||
| }; | ||
| ``` | ||
|
|
||
| ## 感想 | ||
|
|
||
| ### 他の人のコードを読んで | ||
|
|
||
|
|
||
| * BumbuShoji のPR https://github.com/BumbuShoji/Leetcode/pull/7 | ||
| * 開き括弧と閉じ括弧の対応関係を表すMapを用意することも可能。 (`*1`で解法を追加) | ||
|
|
||
| * はじめに、閉じ括弧があるケースを想定できていなかった。 | ||
| * 配列が要素数0の時に、pop()で、undefinedを返すため、たまたま上手く行った。 | ||
| 参考 : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop#return_value | ||
|
|
||
| ```javascript | ||
| // BEFORE | ||
| const open_bracket_candidate = container.pop() | ||
| ``` | ||
|
|
||
| ```javascript | ||
| // AFTER | ||
| const open_bracket_candidate = container.pop() || "" | ||
| const open_bracket_candidate = container.length > 0 ? container.pop() : "" | ||
| ``` | ||
|
|
||
| * 最後のif文は、`return container.length === 0`、`return !arr.length` でも良い | ||
|
|
||
| ```javascript | ||
| // BEFORE | ||
| if (container.length === 0) { | ||
| return true | ||
| } | ||
| return false | ||
| ``` | ||
|
|
||
| ```javascript | ||
| // AFTER | ||
| return container.length === 0 | ||
| ``` | ||
|
|
||
| * Odaのコメント https://discord.com/channels/1084280443945353267/1225849404037009609/1231646037077131315 | ||
| * 不正な入力に対して、エラーハンドリングをどうするかという視点がなかった。 | ||
| https://github.com/lilnoahhh/leetcode/pull/7#discussion_r1948110757 | ||
|
|
||
| > open_to_close でデータは持ちたいです。文字列にカッコ以外が来たときに落ちないで欲しいからです。 | ||
|
|
||
| * 括弧以外の文字が入ってきた際のあるべきは、エラーログを吐き、exit 1で異常終了することと考えた。 https://discord.com/channels/1084280443945353267/1225849404037009609/1231648833914802267 | ||
| * 異常終了、続行する(例外を投げる、特殊な値を返す)の選択肢を意識して、選べると良い。 | ||
| * 異常終了 | ||
| * エラーに気づきやすい。 https://github.com/mura0086/arai60/pull/11#discussion_r1986104852 | ||
| * 重要なエラー | ||
| * 続行 | ||
| * 重要でないエラー | ||
|
|
||
| 参考 : https://discord.com/channels/1084280443945353267/1226508154833993788/1227171332131786774 | ||
|
|
||
| * 異常な入力への対応を頭に入れてコードを書く。 | ||
| 参考 : https://discord.com/channels/1084280443945353267/1316770883729100810/1335077966954369095 | ||
|
|
||
| * 質問されたら、結果と選択肢と理由を回答できる状態にする。 | ||
|
|
||
| * lilnoahhhのPR https://github.com/lilnoahhh/leetcode/pull/7 | ||
| * Stringで判定する方法がある。 | ||
|
|
||
| ```javascript | ||
| // BEFORE | ||
| const open_bracket_chars = ["(", "{", "["] | ||
| if (open_bracket_chars.includes(character)) { | ||
| // | ||
| } | ||
| ``` | ||
|
|
||
| ```javascript | ||
| // AFTER | ||
| const open_brackets = "{([" | ||
| if (open_brackets.includes(character)) { | ||
| // | ||
| } | ||
| ``` | ||
|
|
||
|
|
||
| * SanakoMeine のPR https://github.com/SanakoMeine/leetcode/pull/7 | ||
| * 簡潔で読みやすい. | ||
|
|
||
| ## その他の解法 | ||
|
|
||
| * `*1` 括弧の対応関係を表すMapを使う方法 | ||
|
|
||
| ```javascript | ||
| var isValid = function(characters) { | ||
| const open_to_close = new Map() | ||
| open_to_close.set("(", ")") | ||
| open_to_close.set("{", "}") | ||
| open_to_close.set("[", "]") | ||
|
|
||
| const container =[] | ||
| for (const character of characters) { | ||
| if (open_to_close.has(character)) { | ||
| container.push(character) | ||
| continue | ||
| } | ||
| const converted_last_character = open_to_close.get(container.pop()) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 変数名と内容が合致していなかったですね! 以下の変数名もあるよと、ChatGPTが教えてくれました。
|
||
| const close_bracket_character = character | ||
| if (converted_last_character !== close_bracket_character) { | ||
| return false | ||
| } | ||
| } | ||
| return container.length === 0 | ||
| }; | ||
| ``` | ||
|
|
||
| * `*2` 番兵をおく方法 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Python の場合は、空であったときに pop すると undefined が返らずに IndexError になります。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. コメントありがとうございます。 今後は、スクリプト言語のPythonの仕様も確認して、言語間の違いも意識しようと思います。 |
||
|
|
||
| * 以下の方法でさらに簡潔にかける. | ||
|
|
||
| ```javascript | ||
| const bracket_pairs = new Map([ | ||
| ["{", "}"], | ||
| ["(", ")"], | ||
| ["[", "]"], | ||
| ["*", ""], | ||
| ]) | ||
| const container = ["*"] | ||
| ``` | ||
|
|
||
| ```javascript | ||
| function doesMatchBracket(candidate, close_bracket_character) { | ||
| if (candidate === "(" && close_bracket_character === ")") { | ||
| return true | ||
| } | ||
| if (candidate === "[" && close_bracket_character === "]") { | ||
| return true | ||
| } | ||
| if (candidate === "{" && close_bracket_character === "}") { | ||
| return true | ||
| } | ||
| return false | ||
| } | ||
| var isValid = function(characters) { | ||
| const container =[] | ||
| const open_bracket_characters = ["(", "[", "{"] | ||
| container.push("SENTINEL") | ||
|
|
||
| for (const character of characters) { | ||
| if (open_bracket_characters.includes(character)) { | ||
| container.push(character) | ||
| continue | ||
| } | ||
| const open_bracket_candidate = container.pop() | ||
| const close_bracket_character = character | ||
| if (!doesMatchBracket(open_bracket_candidate, close_bracket_character)) { | ||
| return false | ||
| } | ||
| } | ||
| return container.length === 1 && container[container.length-1] === "SENTINEL" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. このようなやり方もあるんですね、興味深いです 💡
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mapを使った場合には、Mapのkeyでundefinedが入るケースを検討しないといけなかったのでreturn earlyが有効だと思ったのですが、 今回だとdoesMatchBracketに SENTINELが渡されるケースでfalseが返却されることは自明なので、僕の意見としてはどちらでも良いのかなと思いました。 |
||
| }; | ||
| ``` | ||
|
|
||
| * `*3` 不正な入力のエラーハンドリングを行う解法 | ||
| * 正常終了(エラーログ)と異常終了の選択肢のうち、異常終了を選択する。 | ||
|
|
||
| ```javascript | ||
| const isValid = function(characters) { | ||
| const open_to_close = new Map([ | ||
| ["{", "}"], | ||
| ["[", "]"], | ||
| ["(", ")"] | ||
| ]) | ||
| const container = [] | ||
| const expected_characters = ["(", ")", "{", "}", "[", "]"] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 些細ですが、エラーメッセージで"Invalid character"と書いてあるので、変数名もvalid_charactersでよいのかなと思いました!
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. invalidかどうかの判定をするための配列なので、valid_charactersの方がexpected_charactersよりも読みやすいですね! |
||
| for (const character of characters) { | ||
| if (!expected_characters.includes(character)) { | ||
| throw new Error(`Invalid character. Expected : ${expected_character.join(",")}. Current : ${character}`) // UPDATE | ||
| } | ||
| if (open_to_close.has(character)) { | ||
| container.push(character) | ||
| continue | ||
| } | ||
| const expected_close_bracket = open_to_close.get(container.pop()) | ||
| const close_bracket_character = character | ||
| if (expected_close_bracket !== close_bracket_character) { | ||
| return false | ||
| } | ||
| } | ||
| return container.length === 0 | ||
| }; | ||
| ``` | ||
|
|
||
| ### レビューを受けて | ||
|
|
||
| #### レビューコメント1 | ||
|
|
||
| * 変更前 | ||
|
|
||
| ```javascript | ||
| const isValid = function(characters) { | ||
| const open_to_close = new Map([ | ||
| ["{", "}"], | ||
| ["[", "]"], | ||
| ["(", ")"] | ||
| ]) | ||
| const container = [] | ||
| const expected_characters = ["(", ")", "{", "}", "[", "]"] | ||
| for (const character of characters) { | ||
| if (open_to_close.has(character)) { | ||
| container.push(character) | ||
| continue | ||
| } | ||
| const expected_close_bracket = open_to_close.get(container.pop()) | ||
| const close_bracket_character = character | ||
| if (expected_close_bracket !== close_bracket_character) { | ||
| return false | ||
| } | ||
| } | ||
| return container.length === 0 | ||
| }; | ||
| ``` | ||
|
|
||
| * 変更後 (conatiner.pop()をする際にundefinedとstringの可能性を考慮する必要がある。) | ||
|
|
||
| ```javascript | ||
| const isValid = function(characters) { | ||
| const open_to_close = new Map([ | ||
| ["{", "}"], | ||
| ["[", "]"], | ||
| ["(", ")"] | ||
| ]) | ||
| const container = [] | ||
| const expected_characters = ["(", ")", "{", "}", "[", "]"] | ||
| for (const character of characters) { | ||
| if (open_to_close.has(character)) { | ||
| container.push(character) | ||
| continue | ||
| } | ||
| // 箱の中身が空で、閉じ括弧が挿入された際には、falseを返す。 | ||
| if (container.length === 0) { | ||
| return false | ||
| } | ||
| const expected_close_bracket = open_to_close.get(container.pop()) | ||
| const close_bracket_character = character | ||
| if (expected_close_bracket !== close_bracket_character) { | ||
| return false | ||
| } | ||
| } | ||
| return container.length === 0 | ||
| }; | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"aaa"みたいな文字列が入力された際に
trueを返しそうですね。LeetCodeではそこまで予期せぬ入力について考えなくても良いでしょうし、JavaScriptのベストプラクティスはよくわからないのですが、予期せぬ値が来た時にtrueorfalseを返してしまうと、本当は意図しない動作なのに関数を呼び出した側からしたらうまくいったように見えてわかりづらいので、業務ではエラーを吐いた方が逆に親切な気がします。Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
38c934f でエラーハンドリングのコードを追加しました