Create 0008-string-to-integer-atoi.md - #60
Conversation
| class Solution: | ||
| def myAtoi(self, s: str) -> int: | ||
| whole_match = re.match("\s*([\+\-]?)([0-9]+)", s) | ||
| if whole_match: |
There was a problem hiding this comment.
マッチしない場合は None が返ってくるようなので、ここは if not whole_match: か if whole_matchi is None: のときに return 0 して if 文の中の2行を外に出した方が個人的な好みかなと思いました。ただそうすると空行を入れる場所が難しいなと思いました。
https://docs.python.org/3/library/re.html#re.match
Return None if the string does not match the pattern; note that this is different from a zero-length match.
There was a problem hiding this comment.
ありがとうございます。
同意です。
先にearly returnするnot matchから消しておけば読み手は残りの状況だけを考えれば良いので、可読性が高いと感じます。
There was a problem hiding this comment.
空行の位置は、下の実装2、実装4のように、if not match: return 0の直後にだけ入れるイメージです。(気持ち的に)
There was a problem hiding this comment.
確かに、実装2と4の if not match の直後の空行に個人的に違和感は感じなかったのでそこがいいかもしれません。参考になります。
| - https://discord.com/channels/1084280443945353267/1237649827240742942/1354495583494082580 | ||
| - > 漢字などでも True になるのは Python の isdigit の話ですね。 | ||
| - `str.isdecimal()`, `str.isdigit()`, `str.isnumeric()`があるらしい。 | ||
| - `isdecimal()`: 最も厳密。Unicode の一般カテゴリ"Nd"(Number, Decimal unit)、つまり全角も含めたアラビア数字(十進数字という)だけを許す。 |
| sign = 1 | ||
| i += 1 | ||
| else: | ||
| sign = 1 |
There was a problem hiding this comment.
"+" や "-" がない場合についてのコメントを残して、以下のようにするのも選択肢としてよさそうかなと思いました。
# Determine the sign. Assume '+' if it is not specified.
sign = 1
if s[i] == "-":
sign = -1
i += 1
elif s[i] == "+":
i += 1| return 0 | ||
|
|
||
| signed_integer = int(sign + digits) | ||
| if signed_integer < -2**31: |
There was a problem hiding this comment.
signed_integer = max(signed_integer, -2**31)
signed_integer = min(signed_integer, 2**31 - 1)のほうがシンプルに感じました。
|
|
||
| class Solution: | ||
| def myAtoi(self, s: str) -> int: | ||
| whole_match = re.match("\s*([\+\-]?)([0-9]+)", s) |
There was a problem hiding this comment.
正規表現は読み手に取って認知負荷が高くなる場合があると思います。どのような文字列にマッチするパターンなのか、コメントで説明するとよいかもしれません。
| sign = 1 | ||
|
|
||
| if sign == -1: | ||
| abs_limit = -self.INT_MIN |
There was a problem hiding this comment.
念の為ですが、この値は32bitをこえていますよね。Python なのでそんなに気にすることではないかもしれませんが。
There was a problem hiding this comment.
ありがとうございます。
気づいてなかったです。お察しの通り、ここは他言語互換で32bit整数のつもりで書いていたので設計上のミスです。
abs_limitを使うのはやめて、単に、
# check overflow
if sign == -1:
if value < (self.INT_MIN + digit) // 10:
return self.INT_MIN
else:
if value > (self.INT_MAX - digit) // 10:
return self.INT_MAXとする方が良さそうですね。
There was a problem hiding this comment.
前提として value は符号付きの値ですよね。
負の数の割り算における切り捨ての影響で正しく動かないと思います。digit にオーバーフローすべき大きい値が来た時、self.INT_MIN + digit の十の位は 1 小さくなりますが、//10 によりその影響が消されてしまいます。
ご参考として私は以下のように上の桁と追加する digit をそれぞれ比較するように書きました。
https://github.com/tokuhirat/LeetCode/pull/59/files#diff-ed1cf3d46cfd4bddc1808bc5157b0cc7596de0a5858c3d1da82c0d1b1006874cR242
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 FalseThere was a problem hiding this comment.
ありがとうございます。
おっしゃるとおりでした... 鋭いご指摘をありがとうございます。
10 * valueとdigitを別々に扱う処理も大変参考になります。
一方で、元のoverflowの条件式自体は単一の不等式になるはずなので、丸めを同値変形を維持して行うことで、単一式での判定を維持するべきだと考えます。
sign == -1のとき、10 * value - digit_abs < self.INT_MIN、つまりvalue < (self.INT_MIN + digit_abs) / 10がoverflow条件です。ここで天井関数、つまりmath.ceil()とするのが簡単ですが、これは商を一時的に浮動小数点数で表すことになるので避けます。これを// のような切り捨て除算、つまり床関数で表現するためには、非除数を9足しておけばいいと考えました。
たとえば、まず[-100, -99, -98, ..., -91, -90]を/10してmath.ceil()とすると[-10, -9, -9, ..., -9, -9]となります。
これを、被除数を先に+9して//10するとしたら、[-91, -90, -89, ..., 82, -82] -> [-10, -9, -9, ..., -9, -9]と同じ結果を得られます。
# read digits
value = 0
while i < n and s[i].isdecimal():
digit_abs = int(s[i])
# check overflow
if sign == -1:
# value < math.ceil((self.INT_MIN + digit_abs) / 10)
if value < (self.INT_MIN + digit_abs + 10 - 1) // 10:
return self.INT_MIN
else:
# value > math.floor((self.INT_MAX - digit_abs) / 10)
if value > (self.INT_MAX - digit_abs) // 10:
return self.INT_MAX
value = value * 10 + sign * digit_abs
i += 1There was a problem hiding this comment.
私が書いたコードは冗長に感じていましたので、お書きいただいたコードはシンプルで良いなと思いました。
この問題:https://leetcode.com/problems/string-to-integer-atoi/
次の問題:https://leetcode.com/problems/zigzag-conversion/