Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions 20ValidParentheses.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
### Step 1
- stackのカテゴリの問題であるというヒントもあり、すぐに方針は立った
- 実装も割とすぐにできた
- 他のArai60の問題はgolangで解いてきたが、golangでの文字列処理は面倒なのでPythonを使うことにした
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[質問]
Goに詳しくないので質問させてください
Goのどのような部分が文字列処理を面倒にしていると思いますか??

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

goでは文字列に対してインデックスで一文字取得するとbyte型で、rangeでループするとrune型という別の型になります。
for i, c := range sとしてループの中でs[i]とcを出力すると、例えば'('の場合、どちらも41という出力結果になります。しかし、型が違うのでs[i] == cはエラーになってしまいます。一方、Pythonだとsもs[i]も同じstr型なので文字列の処理の際に余計なことを考えずに済むので今回はPythonを選びました。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

もう一つgoではなくpythonを選んだ大きな理由として、goにはスタックを自分で実装する必要があるからです。今回だとスタックからのpopを以下のようにやらないといけません。

l := len(openBracketsStack)
if l == 0 {
    return false
}
top := openBracketsStack[l-1]
openBracketsStack = openBracketsStack[:l-1]

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

以上二つの理由から今回はgoを避けました。というより、面接で同じ問題が出されてgoを選んだらその時点で知識とセンスを疑われるような気がしたことが理由です。
以前勉強会で、面接で使う言語はその会社で使っている言語と必ずしも同じでなくて良いと伺いましたが、与えられた問題に対してどの言語を選ぶかというセンスは見られているでしょうか。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

言語を切り替えられないこともありますし、何だと書きやすいという話をすれば十二分だと思いますね。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

なるほど〜
標準ライブラリにデータ構造が乏しいのはGoにGenericsがなかったのも関係してるんですかね
サードパーティのライブラリ見てるとinterfaceでデータ構造を抽象化してるみたいですね
こっちが主流になっちゃって標準ライブラリのサポートがないままって感じなんでしょうか

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

「サードパーティが主流になっちゃった」というより最初から標準で用意するつもりがなかったと思います。goはミニマリスト思考が大きなコンセプトになるので

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

G 社内であんまり困った記憶ないんですよね。ちょっと複雑なことをすると、RPC を飛ばすことになっていたかと思います。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

G 社内であんまり困った記憶ないんですよね。ちょっと複雑なことをすると、RPC を飛ばすことになっていたかと思います。

理解の確認をさせてください
これって、ある程度複雑なデータ処理は他のサービスをcallして任せていたからGo側の標準ライブラリにデータ構造が乏しくても困らなかったってことであってますか??


```Python3
class Solution:
def isValid(self, s: str) -> bool:
brackets = {
")": "(",
"}": "{",
"]": "["
}
stack: List[str] = []
for i in range(len(s)):
if s[i] in brackets.keys():
if len(stack) == 0:
Copy link
Copy Markdown

@sakzk sakzk Jul 23, 2024

Choose a reason for hiding this comment

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

好みの問題だと思いますが、以下のように条件文をひとつづきにするのも自然に感じます。
(スタックが空か、もしくはスタックの頂上が目当てのものでないなら False )

if len(stack) == 0 or stack.pop() != brackets[s[i]]:
    return False

return False
c = stack.pop()
if brackets[s[i]] != c:
return False
else:
stack.append(s[i])
return len(stack) == 0
```

### Step 2
- コードを整えつつ、discordで先人達のコードも参考にした
- close_to_openのマップよりopen_to_closeを使っているコードを多く見たのでそちらを試してみる
- if文をネストさせずに済んだのでこっちの方が良いだろう
- Pythonの好きでない点として、同じことをやろうとしても選択肢が多すぎるという点を挙げられるが、今回もまさしくそう
- スタックを普通のリストを使って実現するか、deque()を使うか、LifoQueueを使うか、、
- 下記コードのようにLifoQueueを使うと、宣言時にこれはスタックであるということをリスト名にstackという言葉を入れなくてもわかるのが利点。欠点としては、メソッドがput, getでスタックぽくない
- 逆にリストやdeque()を使うと、スタックであるということを別の方法(命名やコメント)で伝えないといけなかったり、誤ってスタックではしない動作をさせてしまう可能性があるのが難点

```Python3
from queue import LifoQueue


class Solution:
def isValid(self, s: str) -> bool:
open_to_close = {
"(": ")",
"{": "}",
"[": "]"
}
open_brackets = LifoQueue()

for i in range(len(s)):
if s[i] in open_to_close:
open_brackets.put(s[i])
continue
if open_brackets.empty():
return False
c = open_brackets.get()
if s[i] != open_to_close[c]:
return False

return open_brackets.empty()
```

### Step 3
- `for i in range(len(s))`を`for c in s`に変えて、Pythonらしくシンプルさを追求した
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

これ今回はどちらでもいいですが、例えばsがdeque等だった場合を考えると、ランダムアクセスがO(1)で出来ないので、場合によっては気をつけた方が良さそうです。

- `if c in open_to_close.keys()`もより短く`if c in open_to_close`に変えた

```Python3
from queue import LifoQueue
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LifoQueueはスレッドセーフな設計になっていて、ロックを取得するためパフォーマンス面では劣りそうです。ユースケースによって使い分けるといいと思います。https://docs.python.org/3/library/queue.html



Comment on lines +68 to +69
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ここの空白は1行にした方がいいと思います。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

PEP 8的には2行ですね。

Surround top-level function and class definitions with two blank lines.

https://peps.python.org/pep-0008/#blank-lines

class Solution:
def isValid(self, s: str) -> bool:
open_to_close = {
"(": ")",
"{": "}",
"[": "]"
}
open_brackets = LifoQueue()

for c in s:
if c in open_to_close:
open_brackets.put(c)
continue
if open_brackets.empty():
return False
top = open_brackets.get()
if c != open_to_close[top]:
return False

return open_brackets.empty()
```
32 changes: 32 additions & 0 deletions go.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
- goでも解いてみました

```Go
func isValid(s string) bool {
openToClose := map[rune]rune{
'(': ')',
'{': '}',
'[': ']',
}
openBracketsStack := []rune{}

for _, c := range s {
if _, exist := openToClose[c]; exist {
openBracketsStack = append(openBracketsStack, c)
continue
}

l := len(openBracketsStack)
if l == 0 {
return false
}

top := openBracketsStack[l-1]
openBracketsStack = openBracketsStack[:l-1]
if c != openToClose[top] {
return false
}
}

return len(openBracketsStack) == 0
}
```
79 changes: 79 additions & 0 deletions step4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
### Step 4
- step1~3ではスタックを自作する必要のあるGoを避けてPythonを使ったが、他の問題はGoで解いてきたのでGoでやることに。
- スタックも自作した

```Go
type runeStack []rune

func (s runeStack) Empty() bool { return len(s) == 0 }

func (s *runeStack) Push(r rune) {
*s = append(*s, r)
}

func (s *runeStack) Pop() (rune, error) {
l := len(*s)
if l == 0 {
return rune(0), errors.New("Empty Stack")
}

last := (*s)[l-1]
*s = (*s)[:l-1]
return last, nil
}

func isValid(s string) bool {
openToClose := map[rune]rune{
'(': ')',
'{': '}',
'[': ']',
}
openBracketStack := &runeStack{}

for _, c := range s {
if _, exist := openToClose[c]; exist {
openBracketStack.Push(c)
continue
}

last, err := openBracketStack.Pop()
if err != nil || c != openToClose[last] {
return false
}
}

return openBracketStack.Empty()
}
```

- ちなみにスタックを自作せずスライスを使うと下のようになる。
- スタックを自作すると手間はかかるが、可読性は増すということを実感した

```Go
func isValid(s string) bool {
openToClose := map[rune]rune{
'(': ')',
'{': '}',
'[': ']',
}
openBracketStack := []rune{}

for _, c := range s {
if _, exist := openToClose[c]; exist {
openBracketStack = append(openBracketStack, c)
continue
}
l := len(openBracketStack)
if l == 0 {
return false
}
last := openBracketStack[l-1]
openBracketStack = openBracketStack[:l-1]
if c != openToClose[last] {
return false
}
}

return len(openBracketStack) == 0
}
```