これが魔法が起こる場所です:
return (s[0] == s[-1]) and isPal(s[1:-1])
(括弧を追加したので、演算子の優先順位は完全に明確です)。Python の AND ステートメントは最初の引数を評価し、それがTrue
である場合にのみ 2 番目の引数を評価することを知っておくことが重要です。最初が だった場合、を呼び出さずにすぐにFalse
戻ります。これは短絡評価として知られています。Blenderは、彼の投稿でこれの良い例を示しています。False
isPal()
s[0]
は文字列の最初の文字、 は文字列s[-1]
の最後の文字です。まず、最初と最後の文字が同じかどうかを確認します。それらが異なる場合、文字列が回文になる方法はありません。それらが同じ場合は、それらを無視して文字列の内側に移動できます。
s[1:-1]
最初と最後の文字を切り取り、それをisPal()
関数に渡します。これはたまたま現在使用している関数です。これを再帰と呼びます。関数に再度「再帰」すると、新しいスタックが作成され、新しいローカル変数が作成され、関数への入力引数は前の呼び出しのものとは異なります。
最初と最後の文字をチェックし続け、単語の中心まで再帰します。この時点で、1 文字または 0 文字が残っており、そこまで到達した場合は、回文が見つかったことがわかります。
この最終的な内部から戻るとisPal()
、戻り値への呼び出し (True
回文が見つかった場合) が呼び出し元の関数に渡され、呼び出し元の関数にisPal()
返されます。最初の呼び出し元に結果を返します。このプロセスを「スタックの展開」と呼びます。
次に例を示します。
s = 'racecar'
s[0] == s[-1] # True - both letters are 'r'
s = s[1:-1] # s becomes 'aceca'
s[0] == s[-1] # True - both letters are 'a'
s = s[1:-1] # s becomes 'cec'
s[0] == s[-1] # True - both letters are 'c'
s = s[1:-1] # s becomes 'e'
len(s) <= 1 # This statement is now True, so the stack will start to unwind
視覚的には、呼び出しは次のようになります。
'racecar' ---------
|
V 'aceca'
True <--- isPal( ) ----
^ |
True | V 'cec'
---- isPal( ) ----
^ |
True | V 'e'
---- isPal( ) ----
^ |
True | V
---- isPal( ) # We get all the way down here,
# and then we start to return
回文がない場合は、次のようになります。
'race2car' --------
|
V 'ace2ca'
False <--- isPal( ) ----
^ |
False | V 'ce2c'
---- isPal( ) ----
^ |
False | V 'e2'
---- isPal( ) ----
^ |
False | V
---- isPal( ) # Here the outer two letters
# do not match, so we start to
# return False