はじめに
「a, b, cは全て異なる値かどうか」という条件に遭遇したので、いろいろ実験したメモです。
やりたいこと
変数a, b, c 全ての数値が異なる場合は「全て異なる値です」と出力し、そうでない場合は「同じ値が含まれています」と出力するプログラムを考えます。
実験1: a!=b!=c
と書く
a = 0
b = 1
c = 0 # a=cのため、同じ値が含まれている
if a != b != c:
print("全て異なる値です")
else:
print("同じ値が含まれています")
# 出力 -> "全て異なる値です"
「全て異なる→ a==b==c
の逆なのでa≠b≠c
」と書いてしまうかもしれませんが、上記の通り、正しく判別することはできません。
Pythonの公式ドキュメントを見てみましょう。
Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).
「 "x < y <= z" は "x < y and y <= z" と等しい」と書いてあります。
つまり、a!=b!=c
はa!=b and b!=c
の判定を行なっているだけでは (≠では推移律が成り立たないので) 、a!= c
が成り立っているかどうか は保証されないということです。
(実際に試してみたメモ)
a, b, c = 0, 0, 1
if a != b != c:
print("Yes")
else:
print("No")
# No
# 0 != 0 が偽になるため。
# 一方で、
a, b, c = 0, 1, 0
if a != b != c:
print("Yes")
else:
print("No")
# Yes
# 0 != 1 は真であり、かつ、1 != 0 も真であるため、論理式は真になる。
先程のa = 1, b = 0, c = 1
を例にすると、$a≠b$ かつ $b≠c$であることは確認しているけれども、「$a≠c$であるかどうか」までは確認していないことになります。
そもそも、$a=b=c$, $a < b ≦ c$ はあっても、 $a≠b≠c$ という式の書き方は、数学や論理式の文脈でグローバルに定義されているものではありません。このような書き方は、たとえ他の言語で運良く意図通りに動いたとしても、可読性の観点から好ましくはないと考えられます。
実験2. not (a==b==c)
a = 0
b = 1
c = 0
if not (a == b == c):
print("全て異なる値です")
else:
print("同じ値が含まれています")
# 出力 -> "全て異なる値です"
論理式を把握されている方からすれば当然と思われるでしょうが、これも「全て異なる」の判定には使えません。
「全て等しい」の否定は「少なくとも1つ異なる」と等しいため、1つだけでも異なるものがあれば真と判定されるためです。
では、どう書けば「全て異なる」を正しく判定できるのでしょうか。
正しい書き方
if a!=b and b!=c and c!=a:
#省略
素直に「全て異なる」という条件の中身を展開して、端折らずに書くのが最善と考えられます。
if a!=b!=c!=a:
#省略
短く書きたいならば、先述したPythonにおける比較の定義を利用し、 a!=b!=c!=a
と書くことで、上と同じ判定を行うこともできます(共有ありがとうございます)。
ちなみに、3個以上の比較でも同様に定義されていることは、公式ドキュメントに記載されています。
Formally, if a, b, c, …, y, z are expressions and op1, op2, …, opN are comparison operators, then a op1 b op2 c ... y opN z is equivalent to a op1 b and b op2 c and ... y opN z, except that each expression is evaluated at most once.
先述したように、数学や論理式として定義されたものではありませんが、可読性はそこまで損なわれないと思います。
ただ、これらの書き方は、4つ以上の要素がある場合には使えません。a!=b and b!=c and c!=d and d!=a
だけでは、a!=c
とb!=d
を判定できないためです。
その場合には、以下のような方法が考えられます。
s = [a,b,c]
if len(set(s)) == len(s):
set
型 (集合) を利用して、「set
型にlist
の[a,b,c]
を入れ、重複がなければset
の要素数がlist
の要素数と等しい」という書き方です(共有ありがとうございます)。
これならば4つ以上でも簡潔に判定できます。
set型についてはこちらに分かりやすく解説されています。