質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

537閲覧

2つの線形リストが同一か調べる。

poteboy

総合スコア5

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

1グッド

1クリップ

投稿2020/03/02 19:47

前提・実現したいこと

初投稿です。
Pythonで、2つの同一な線形リストを作り、それが同一であるか調べる関数is_sameをつくりました。
returnで再帰を行っているのですが、

if x.val == y.val: return is_same(x.next, y.next)

だとTrueになるのですが、

if x == y: return is_same(x.next, y.next)

だとFalseになります。
xもyも同じ値が入っていて、ポインターも同じ値を指しているから同一であるはずなのに、なぜ上のコードと下のコートで実行結果が異なるのでしょうか。
(想定していた出力は上のほうです)

発生している問題・エラーメッセージ

エラーメッセージ

該当のソースコード

Python

1class ListNode: 2 def __init__(self, x): 3 self.val = x 4 self.next = None 5 6def is_same(x,y) -> bool: 7 if x is None and y is None: 8 return True 9 if x is None and y is not None or x is not None and y is None: 10 return False 11 if x.val != y.val: 12 return False 13 if x.val == y.val: 14 return is_same(x.next, y.next) 15 16if __name__ =='__main__': 17 head = ListNode(0) 18 node = head 19 20 for n in range(1, 100): 21 node.next = ListNode(n) 22 node = node.next 23 24 head2 = ListNode(0) 25 node2 = head2 26 27 for n in range(1, 100): 28 node2.next = ListNode(n) 29 node2 = node2.next 30 31 print(is_same(head, head2)) 32 33

試したこと

ここに問題に対して試したことを記載してください。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

hayataka2049👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

Pythonで==演算子を使うということは、オブジェクトの__eq__メソッド(特殊メソッドの一種)を呼び出すことです。もう少し噛み砕いて言うと、a == bという式はa.__eq__(b)と原則等価です。

3. データモデル — Python 3.8.2 ドキュメント

さて、この__eq__ですが、すべてのオブジェクトにデフォルトで存在してはいます。すべてのクラスの基底クラスであるobjectクラスですでに実装されているからです。ただし、これは抽象メソッド的に置いてあるだけで、実用的に使える訳ではありません(念の為に:「抽象メソッド的」というのは比喩的な意味です。最低限の実装は以下に述べるように存在しています)。デフォルトではis比較(同一性の比較)と同等の処理のみ行います。

全ての型は (直接的あるいは間接的に) object のサブクラスとなっているので、デフォルトの比較の振る舞いを object から継承しています。 基本的なカスタマイズ で解説されているように、型を使って rich comparison methods である __lt__() などのメソッドを実装することで、 比較の振る舞いをカスタマイズできます。

等価比較 (== および !=) のデフォルトの振る舞いは、オブジェクトの同一性に基づいています。 従って、同一のインスタンスの等価比較の結果は等しいとなり、同一でないインスタンスの等価比較の結果は等しくないとなります。 デフォルトの振る舞いをこのようにしたのは、全てのオブジェクトを反射的 (reflexive つまり x is y ならば x == y) なものにしたかったからです。
6. 式 (expression) — Python 3.8.2 ドキュメント

以上のことを雑に言うと、同一のオブジェクトでなければobject.__eq__に従う==比較はFalseになります。

もう少し踏み込んで説明すると、以下のようになります。

a,bobjectの異なるインスタンスとして、a == bではまずa.__eq__(b)が試され、これはNotImplementedを返します。こうなると処理系はb.__eq__(a)も試しますが、こちらも同様の結果になります。両方NotImplementedになったときは、結果はFalseとして取り扱われます。


同値性の比較というのはけっこう高級な処理です。属性ぜんぶ同値なら同値とすればいい? 本質的ではない属性は無視して取り扱いたいかもしれません。あるいは、異なる型の間でも同値性を確認したいかもしれません(1 == 1.0Trueです)。numpyなど、配列に==演算子を使うと要素と比較してくれますが(np.array([1, 2, 3]) == 1array([True, False, False])になります)、これはnumpyの開発者がそうなるように__eq__などのメソッドをすべて実装しているからです(+演算子に対応する__add__など、ほぼすべての演算子に対して同様のことをしています)。演算子オーバーロードというか演算子に対応するメソッドのオーバーライドなのですが、このあたりを柔軟に書き換えられるのが動的言語の強みです。

逆に言うと、自作オブジェクトで意味のある==を使いたいのなら、自分で書く必要があります。

たとえばこんな感じのメソッドを定義すればいいのではないでしょうか。

python

1 def __eq__(self, other): 2 return (self.val is other.val) and (self.next is other.next)

コードのロジックまでは追っていないので、やりたいことと合っているかはわかりません。valnext属性が同一のListNodeオブジェクトは同値であるとみなすという私の信念にもとづくコードです。気に入らなければ質問者さんの責任で書き換えてください。

やりたいことがx.val == y.valならそれを__eq__に書いておけばいいのですが(引数の名前に変えた上で)、私の感覚ではこれはリストのコンスセルの同値性としては「緩すぎる」と感じます。なので私なら、==演算子には委ねずに素直に外側でx.val == y.valを判定するように実装するでしょう。

投稿2020/03/02 21:17

編集2020/03/02 21:23
hayataka2049

総合スコア30935

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問