-
Notifications
You must be signed in to change notification settings - Fork 0
Create 8. String to Integer (atoi).md #59
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
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,256 @@ | ||
| # 8. String to Integer (atoi) | ||
| ## STEP1 | ||
| - 何も見ずに解いてみる | ||
| - 言われていることを実装すれば良いが細かいところで引っかかった。 | ||
| - 空白判定は要件としては" "と比較すれば良いが、string.whitespace を使う方がよい場面もありそう。 | ||
| - Rounding は Python だと簡単。C++だとどうするんでしょう。 | ||
| - _read_integer に index = len(s) を入れても大丈夫だが、事前にチェックした方がわかりやすいかもしれない。 | ||
| - 各メソッドに s を渡しているのは微妙かもしれない。len(s) の方がよい? | ||
| ```python | ||
| import string | ||
|
|
||
|
|
||
| class Solution: | ||
| def myAtoi(self, s: str) -> int: | ||
| index = 0 | ||
| # Ignore any leading whitespace | ||
| index = self._skip_white_space(s, index) | ||
| if index == len(s): | ||
| return 0 | ||
|
|
||
| # Determine the sign | ||
| is_positive, index = self._read_sign(s, index) | ||
|
|
||
| # Read the integer | ||
| integer = self._read_integer(s, index) | ||
|
|
||
| # Rounding | ||
| rounded_integer = self._rounding_to_32bit_signed_integer(integer, is_positive) | ||
| return rounded_integer | ||
|
|
||
| def _skip_white_space(self, s, start: int) -> int: | ||
| for i in range(start, len(s)): | ||
| if s[i] != " ": | ||
| return i | ||
| return len(s) | ||
|
|
||
| def _read_sign(self, s, index: int) -> tuple[bool, int]: | ||
| if s[index] == "+": | ||
| return True, index + 1 | ||
| if s[index] == "-": | ||
| return False, index + 1 | ||
| return True, index | ||
|
|
||
| def _read_integer(self, s, index: int) -> int: | ||
| result = 0 | ||
| while index < len(s): | ||
| if s[index] != "0": | ||
| break | ||
| index += 1 | ||
| else: | ||
| return 0 | ||
|
|
||
| for i in range(index, len(s)): | ||
| if s[i] in string.digits: | ||
| result = 10 * result + int(s[i]) | ||
|
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. 今回の問題の出題意図は |
||
| continue | ||
| break # non digit exists | ||
| return result | ||
|
|
||
| def _rounding_to_32bit_signed_integer(self, integer: int, is_positive: bool) -> int: | ||
|
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. 確かに、_round_とした方が良いですね。コメントありがとうございます。 |
||
| MAX_INT = (1 << 31) - 1 | ||
| MIN_INT = -(1 << 31) | ||
| print(integer) | ||
| if is_positive: | ||
| if integer > MAX_INT: | ||
| return MAX_INT | ||
| return integer | ||
| else: | ||
| integer *= -1 | ||
| if integer < MIN_INT: | ||
| return MIN_INT | ||
| return integer | ||
| ``` | ||
|
|
||
| ## STEP2 | ||
| ### プルリクやドキュメントを参照 | ||
| - https://github.com/olsen-blue/Arai60/pull/60/files | ||
| - 各処理を関数化した方がわかりやすく感じる (下で少し思い直しました)。追加の要件が来たらべた書きの場合対応が難しそう。ただ上の STEP1 で書いた index と s を関数間で取り回しているコードがわかりやすいかは微妙。関数内関数でも良いが読みにくさはありそう。 | ||
| - 一桁の数字に対しても int を使わないことが想定されているかも。ord で変換できる。 | ||
| - https://docs.python.org/3/library/functions.html#ord | ||
| > If the argument is a one-character string, return the Unicode code point of that character. | ||
| - https://docs.python.org/3/library/stdtypes.html#str.isdigit | ||
| - 想定範囲より広い概念の数字をTrueと判定する。 | ||
| - MAX_INT = (1 << 31) - 1 は MAX_INT = 0x7FFF_FFFF でも良いかも。MIN_INT = -MAX_INT - 1 | ||
| - https://docs.python.org/3/library/functions.html#divmod | ||
| - a % b は 0 でない場合 b と同符号 | ||
| - オーバーフロー判定で MAX_INT, MIN_INT が 10 の倍数である場合にも対応しようとすると複雑。対応する必要があるのか。この関数は基盤になりそうだからどんな入力でも正しく動いてほしいとは思う。 | ||
| - https://github.com/fhiyo/leetcode/pull/57/files#r1730005383 | ||
| - 拡張性について。hex や oct への拡張は結構大変そう。 | ||
| - string.hexdigits, string.octdigits はある。 | ||
| - https://docs.python.org/3/reference/expressions.html#operator-precedence | ||
| - シフト演算子の優先順位は +, - より低い | ||
| ```python | ||
| import string | ||
|
|
||
|
|
||
| MAX_INT = 0x7FFF_FFFF | ||
| MIN_INT = -MAX_INT - 1 | ||
|
|
||
|
|
||
| class Solution: | ||
| def myAtoi(self, s: str) -> int: | ||
| index = 0 | ||
| # Ignore any leading whitespace | ||
| index = self._skip_whitespace(s, index) | ||
| if index == len(s): | ||
| return 0 | ||
|
|
||
| # Determine the sign | ||
| sign, index = self._parse_sign(s, index) | ||
|
|
||
| # Read the integer, rounding on overflow. | ||
| integer = self._parse_integer(s, index, sign) | ||
| return integer | ||
|
|
||
| def _skip_whitespace(self, s, index: int) -> int: | ||
| for i in range(index, len(s)): | ||
| if s[i] != " ": | ||
| return i | ||
| return len(s) | ||
|
|
||
| def _parse_sign(self, s, index: int) -> tuple[int, int]: | ||
| if s[index] == "+": | ||
| return 1, index + 1 | ||
| if s[index] == "-": | ||
| return -1, index + 1 | ||
| return 1, index | ||
|
|
||
| def _parse_integer(self, s, index: int, sign: int) -> int: | ||
| value = 0 | ||
| for i in range(index, len(s)): | ||
| if s[i] not in string.digits: | ||
| break # non digit exists | ||
| digit = ord(s[i]) - ord("0") | ||
| if self._is_overflow(sign, value, digit): | ||
| return MAX_INT if sign == 1 else MIN_INT | ||
| value = 10 * value + digit * sign | ||
| return value | ||
|
|
||
| def _is_overflow(self, sign: int, value: int, digit: int) -> bool: | ||
| if sign == 1: | ||
| if value > MAX_INT // 10: | ||
| return True | ||
| if value == MAX_INT // 10 and digit > MAX_INT % 10: | ||
| return True | ||
| else: | ||
| if value < (MIN_INT + 9) // 10: | ||
| return True | ||
| if value == (MIN_INT + 9) // 10 and digit > (10 - MIN_INT % 10) % 10: | ||
| return True | ||
| return False | ||
| ``` | ||
| - あまり関数化せずに書き直した。これでも意外とわかりやすい。 | ||
| ```python | ||
| import string | ||
|
|
||
|
|
||
| MAX_INT = 0x7FFF_FFFF | ||
| MIN_INT = -MAX_INT - 1 | ||
|
|
||
|
|
||
| class Solution: | ||
| def myAtoi(self, s: str) -> int: | ||
| index = 0 | ||
| # Ignore any leading whitespace | ||
| while index < len(s) and s[index] == " ": | ||
| index += 1 | ||
| if index == len(s): | ||
| return 0 | ||
|
|
||
| # Determine the sign | ||
| sign = 1 | ||
| if s[index] == "+": | ||
| index += 1 | ||
| elif s[index] == "-": | ||
| sign = -1 | ||
| index += 1 | ||
|
|
||
| # Read the integer, rounding on overflow. | ||
| value = 0 | ||
| for i in range(index, len(s)): | ||
| if s[i] not in string.digits: | ||
| break # non digits exists | ||
| digit = ord(s[i]) - ord("0") | ||
| if self._is_overflow(sign, value, digit): | ||
| return MAX_INT if sign == 1 else MIN_INT | ||
| value = 10 * value + digit * sign | ||
| return value | ||
|
|
||
| def _is_overflow(self, sign: int, value: int, digit: int) -> bool: | ||
| if sign == 1: | ||
| if value > MAX_INT // 10: | ||
| return True | ||
| if value == MAX_INT // 10 and digit > MAX_INT % 10: | ||
| return True | ||
| else: | ||
| if value < (MIN_INT + 9) // 10: | ||
| return True | ||
| if value == (MIN_INT + 9) // 10 and digit > (10 - MIN_INT % 10) % 10: | ||
| return True | ||
| return False | ||
| ``` | ||
| ## STEP3 | ||
| ### 3回ミスなく書く | ||
| ```python | ||
| import string | ||
|
|
||
|
|
||
| MAX_INT = 0x7FFF_FFFF | ||
| MIN_INT = -MAX_INT - 1 | ||
|
|
||
|
|
||
| class Solution: | ||
| def myAtoi(self, s: str) -> int: | ||
| index = 0 | ||
| # Skip whitespace | ||
| while index < len(s) and s[index] == " ": | ||
| index += 1 | ||
| if index == len(s): | ||
| return 0 | ||
|
Comment on lines
+219
to
+220
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. "Read the integer" のところは while の条件でどうせ拾われるので、sign のところの if でチェックしてもよいかと思います。好みの範囲かと思います。 |
||
|
|
||
| # Determine the sign | ||
| sign = 1 | ||
| if s[index] == "+": | ||
| index += 1 | ||
| elif s[index] == "-": | ||
| sign = -1 | ||
| index += 1 | ||
|
Comment on lines
+223
to
+228
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. if s[index] in "+-":
if s[index] == "-":
sign = -1
index += 1などでもよいですかね。 |
||
|
|
||
| # Read the integer | ||
| value = 0 | ||
| while index < len(s): | ||
| if s[index] not in string.digits: | ||
|
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. 一応以下のような違いを想定して書いてます。 import string
# 半角
print("1".isdigit()) # True
print("1" in string.digits) # True
# 全角
print("1".isdigit()) # True
print("1" in string.digits) # False
# カローシュティー数字
print("੭".isdigit()) # Truehttps://docs.python.org/3/library/stdtypes.html#str.isdigit
https://docs.python.org/3/library/string.html#string.digits
|
||
| break | ||
| digit = ord(s[index]) - ord("0") | ||
| if self._is_overflow(sign, value, digit): | ||
| return MAX_INT if sign == 1 else MIN_INT | ||
| value = 10 * value + digit * sign | ||
| index += 1 | ||
| return value | ||
|
|
||
| def _is_overflow(self, sign: int, value: int, digit: int) -> bool: | ||
| if sign == 1: | ||
| if value > MAX_INT // 10: | ||
| return True | ||
| if value == MAX_INT // 10 and digit > MAX_INT % 10: | ||
| return True | ||
| else: | ||
| if value < (MIN_INT + 9) // 10: | ||
| return True | ||
| if value == (MIN_INT + 9) // 10 and digit > (10 - MIN_INT % 10) % 10: | ||
| return True | ||
| return False | ||
|
Comment on lines
+243
to
+253
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. is_overflow = result > INT_MAX // 10 or (result == INT_MAX // 10 and digit > INT_MAX % 10)などとまとめることはできますね。意図的に分けたのかもしれませんが、個人的には特にマイナスのときが読みづらく感じました。。
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. ご提案としては sign == 1 の場合に記載いただいた is_overflow を返り値にできるということであっていますか? |
||
| ``` | ||
|
|
||
| 7分,6分,5分で3回Accept | ||
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.
私ならメソッド名は
_convert_to_integerにします。