目次
サンプル・プログラム
比較演算子
\( a < b \) のように、演算子の左右にあるオペランドの大小を比較するのが比較演算子である。if文や for文のほか、次章で説明する while文でよく使う。
Python には下表に掲げる比較演算子がある。
Python には下表に掲げる比較演算子がある。
演算子 | 意味 | 使用例 |
---|---|---|
== | 等価 | a == b |
!= | 不等価 | a != b |
> | より大きい | a > b |
>= | 以上 | a >= b |
< | より小さい | a < b |
<= | 以下 | a <= b |
is | 同一性 | a is a |
is not | 非同一性 | a is not b |
in | 包含 | 2 in [1, 2, 3] |
オペランドにオブジェクトを求める is, is not, in が存在しないプログラミング言語も多いが、これらは Pythonを特徴付ける演算子である。
演算誤差対策
さて、小数の比較演算では、演算誤差や2進数誤差が発生し、期待したような演算結果にならないことがある。
a = 6 - 5.6
b = 0.4
print(str(a) + " == " + str(b))
print(a == b)
たとえばこのプログラムは、演算結果が True になることが期待するが、実際には False になってしまう。\( 6 - 5.6 \) の計算結果が2進数の循環小数になってしまうためだ。
そこで Python には近似比較を行う組み込み関数 math.isclose が用意されている。
そこで Python には近似比較を行う組み込み関数 math.isclose が用意されている。
import math
a = 6 - 5.6
b = 0.4
print(str(a) + " == " + str(b))
print(math.isclose(a, b))
math.isclose関数は、許容誤差範囲内で引数が等しいかどうかを比較する。デフォルトの許容誤差は \(\displaystyle 1 \times 10^{-9} \) だ。この許容誤差は、引数rel_tolやabs_tolで変更することができる。
文字列の比較
次に、文字列の比較を見てみよう。
a = "123"
b = "567"
print(a < b)
a = "あいうおえ"
b = "あいう"
print(a < b)
文字列に対しては、辞書順と長さで比較する。これは予想通りの結果であろう。
a = 123
b = "123"
print(a == b)
数値と数字が等しいかどうかを比較しようとすると、False になる。これも予想通りだろう。
a = 123
b = "456"
print(a < b)
問題は、数値と数字の大小比較を行った場合で、これはエラーになってしまう。
こうしたエラーによりプログラムが異常終了してしまうことを防ぐのに、比較演算の際にいちいちオペランドのバリデーションをやっていたのではプログラムサイズが大きくなってしまうから、データを受け取る(ユーザー入力およびデータベースや他システムからの取得)時点でバリデーションをやっておかなければならない。
こうしたエラーによりプログラムが異常終了してしまうことを防ぐのに、比較演算の際にいちいちオペランドのバリデーションをやっていたのではプログラムサイズが大きくなってしまうから、データを受け取る(ユーザー入力およびデータベースや他システムからの取得)時点でバリデーションをやっておかなければならない。
等価と同一性
等価 == と同一性 is の関係について見ておこう。
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b)
print(a is b)
print(a is not b)
a = 123
b = 123
print(a == b)
print(a is b)
print(a is not b)
前半は "a = [1, 2, 3]" と "b = [1, 2, 3]" の比較を行う。各々の数値リストそのものは等しいから、等価 == の結果は True になる。一方、変数aとbが格納されているメモリ領域は異なるから、同一性 is は False になる。リストについては後述するが、aとbは非同一であるから、bに要素を追加しても、aには影響を及ぼさない。
厄介なのは、後半の数値の方である。前半の変数aとbが格納されているメモリ領域は異なるものの、同じ123という値を示していることから、同一性 is は True になる。
これらを図示すると下のようになる。
厄介なのは、後半の数値の方である。前半の変数aとbが格納されているメモリ領域は異なるものの、同じ123という値を示していることから、同一性 is は True になる。
これらを図示すると下のようになる。
ブール演算子(論理演算子)
\( a \ \& \ b \) のように、オペランドのブール演算(論理演算)を行うのがブール演算子(論理演算子)である。if文や for文のほか、次章で説明する while文でよく使う。
Python には下表のブール演算子がある。
Python には下表のブール演算子がある。
演算子 | 意味 | 使用例 |
---|---|---|
and | 論理積 | a and b |
or | 論理和 | a or b |
not | 否定 | not a |
^ | 排他的論理和(xor) | a ^ b |
論理積は、変数がある範囲に収まっているかどうかを判定するときによく使う。
# a ≦ x ≦ b かどうか
a = -2
b = 3
x = 1
y = (a <= x) and (x <= b)
print(y)
このプログラムは \( a \leq x \leq b \) の範囲に変数xが収まってれば True を、そうでなければ False を返す。
aやbの値を変更し、範囲を変えてみよう。
ここで、a <= x と書くべきか、x >= a と書くべきかは議論が分かれるところだが、 ここでは、\( a \leq x \leq b \) に近い形を目指し、上記のような式にした。
aやbの値を変更し、範囲を変えてみよう。
ここで、a <= x と書くべきか、x >= a と書くべきかは議論が分かれるところだが、 ここでは、\( a \leq x \leq b \) に近い形を目指し、上記のような式にした。
# a ≦ x ≦ b かどうか
a = -2
b = 3
x = 1
y = a <= x <= b
print(y)
Python には比較演算子の結合という機能が備わっており、論理演算子を用いなくても数の範囲を判定することができる。条件演算子を結合した a <= x <= b は (x >= a) and (x <= b) と同じ意味を持っており、より数学の式 \( a \leq x \leq b \) に近い表記になっている。ただし、他のプログラミング言語にはない機能なので、移植性を重視するときには使わない方がいいだろう。
論理和を使って、変数が整数または小数であるかどうかを調べるプログラムをつくってみる。
import cmath # 複素数関数モジュール
# 整数
a = 2
y = isinstance(a, int) or isinstance(a, float)
print(y)
# 小数
a = cmath.pi
y = isinstance(a, int) or isinstance(a, float)
print(y)
# 複素数
a = cmath.sqrt(-3)
y = isinstance(a, int) or isinstance(a, float)
print(y)
isinstance関数は、第1引数のデータ型(後述)が第2引数であるときには True を、そうでないときには False を返す組み込み関数だ。データ型は文字列のようにクォーテーションで囲まれていないところに留意したい。
cmathモジュールは、mathモジュールを複素数に拡張したものである。ここでは、負の数の平方根を得たかった(結果が複素数になる)ので、cmath.sqrt関数を使っている。
cmathモジュールは、mathモジュールを複素数に拡張したものである。ここでは、負の数の平方根を得たかった(結果が複素数になる)ので、cmath.sqrt関数を使っている。
否定はオペランドが1つだけだ。
# not演算子
for i in range(-3, +4):
y = (i == 0)
print(not y)
このプログラムは、整数-3以上+4未満の整数に対し、それが0がどうかを表示する。
ショートカット演算
Python の論理演算子にはショートカット演算という副作用がある。
たとえば a or b という論理和では、aが True のときにはbを計算しないが、aが False のときにはbを計算する。論理和の働きからして当然の動きなのだが、これを利用すると、次のようなプログラムを書くことができる。
たとえば a or b という論理和では、aが True のときにはbを計算しないが、aが False のときにはbを計算する。論理和の働きからして当然の動きなのだが、これを利用すると、次のようなプログラムを書くことができる。
# ショートカット演算
a = 3
b = 2
y = (b == 0) or a / b
print(y)
a = 3
b = 0
y = (b == 0) or a / b
print(y)
このプログラムは、被除数がゼロの時には(b ==0 が True のときには)除算を行わないというゼロ除算エラー対策になっている。
練習問題
次回予告
次回は、IoT機器などの組み込みプログラミングでよく使われるビット演算子とシフト演算子について学ぶ。
コラム:集合と論理
かつて中学の数学の学習範囲だった「集合と論理」は高校の数学I(または数学A)に移動した。義務教育の範囲では、比較演算子と論理演算子の組み合わせができないというのは困ったものである。
ここでは、真理値表とベン図を掲載する。どうか高校数学の参考書の該当箇所を併読いただきたい。
ここでは、真理値表とベン図を掲載する。どうか高校数学の参考書の該当箇所を併読いただきたい。
コラム:等価の定義
集合に関わる話題としてもう1つ――本文で比較演算子として等価 == を取り上げたが、数学では等価の定義が曖昧だという。
イギリスの数学者ケビン・バザードさんは論文「Grothendieck’s use of equality」の中で、「現状、数学者は等式の概念を曖昧に使っており、近年のコンピュータプログラムによる証明(形式化)においてその曖昧さが障害になっている」と指摘している。
多くのプログラミング言語では、基本型同士の比較 "1 == 1" で等価が成り立つことは確実なのだが、集合のような派生型では言語によって結果が異なる。
ここに、{1,2,3} と {3,2,1} という2つの集合がある。
数学では、\( \{1,2,3\} \equiv \{3,2,1\} \) が成り立つ(集合の相等)。
Python で集合を表すブレース {...} を使うと、{1,2,3} と {3,2,1} は等価になる。
C++ の std::setコンテナは集合を表すが、こちらも等価になる。
ところが、JavaScriptで集合を表す Setオブジェクトでは等しくならない。
プログラムの条件分岐をするのに等価条件は欠かせない。使用しているプログラミング言語の等価条件が数学と異なったり曖昧だったりするときは、ユーザー関数やメソッドを用意した方がいいだろう。
イギリスの数学者ケビン・バザードさんは論文「Grothendieck’s use of equality」の中で、「現状、数学者は等式の概念を曖昧に使っており、近年のコンピュータプログラムによる証明(形式化)においてその曖昧さが障害になっている」と指摘している。
多くのプログラミング言語では、基本型同士の比較 "1 == 1" で等価が成り立つことは確実なのだが、集合のような派生型では言語によって結果が異なる。
ここに、{1,2,3} と {3,2,1} という2つの集合がある。
数学では、\( \{1,2,3\} \equiv \{3,2,1\} \) が成り立つ(集合の相等)。
Python で集合を表すブレース {...} を使うと、{1,2,3} と {3,2,1} は等価になる。
C++ の std::setコンテナは集合を表すが、こちらも等価になる。
ところが、JavaScriptで集合を表す Setオブジェクトでは等しくならない。
プログラムの条件分岐をするのに等価条件は欠かせない。使用しているプログラミング言語の等価条件が数学と異なったり曖昧だったりするときは、ユーザー関数やメソッドを用意した方がいいだろう。
コラム:ブール代数
「コラム:乗算と除算ができるようになるまで」に書いたが、コンピュータは算術演算をブール演算(論理演算)で行っている。
1847年、イギリスの数学者ジョージ・ブールが『論理の数学的分析』という小冊子の中でブール代数の体系を紹介する。これがコンピュータ科学の基礎理論のひとつとなった。
ブール代数は、真(true)と偽(false)を対象にした論理代数で、記号論理学を数学的に表すことを目的としている。代数演算を真と偽の2値で表現することが可能で、四則演算の代わりに、論理積(AND)、論理和(OR)、論理否定(NOT)という3つの論理演算を用いる。
1847年、イギリスの数学者ジョージ・ブールが『論理の数学的分析』という小冊子の中でブール代数の体系を紹介する。これがコンピュータ科学の基礎理論のひとつとなった。
ブール代数は、真(true)と偽(false)を対象にした論理代数で、記号論理学を数学的に表すことを目的としている。代数演算を真と偽の2値で表現することが可能で、四則演算の代わりに、論理積(AND)、論理和(OR)、論理否定(NOT)という3つの論理演算を用いる。
真を1、偽を0という2進数に置き換えることによって、デジタル・コンピュータで代数演算が実現できることを保証する。20世紀に入って登場するリレー式計算機や半導体計算機は、ブール代数を基に回路設計された。
Python のブール演算子は、ブール代数に由来する。厳密には、次回紹介するビット演算子がブール代数そのものである。
Python のブール演算子は、ブール代数に由来する。厳密には、次回紹介するビット演算子がブール代数そのものである。
(この項おわり)
今回は、比較演算子と、これも if文で使うことが多いブール演算子(論理演算子)について学ぶ。