なんか考えてることとか

変な人が主にプログラミング関連で考えていることをまとめる。

文字列結合に(+)演算子を使うことへの違和感の正体

私はソフトウェア開発をしていて、ずっと違和感があったところがある。それが一部の言語で見られる文字列結合演算子(+)である。
今回は、この違和感について、正体がわかってきたので書いていくことにする。

TL;DR

一般に(+)演算子は加法群において用いられるものであり、整数・実数においては加法群なのでこれに当てはまるが、文字列においては加法群ではないので、これには当てはまらない。

はじめに

私の感じた違和感とは、(+)演算子が整数・実数においては「加法」、文字列においては「結合」を意味しており、これらが異なった演算に見えたことである。
私は数学者ではないので、この違和感を最初感じた当時は説明できなかったのだが、今回これを説明する際に群の定義を少しかじった結果、説明できるのではないかと思ったので、(+)演算子が文字列結合として使われることの違和感を説明してみることにした。

ちなみに、保険をかけておくが、私は数学者では断じてないので、この解釈は間違っている可能性があることは念頭に置いておいてほしい。また、型は本来集合と見なすべきではないが、この記事では型を集合と見なして考える。

1. 加法群の定義

さて、今回の記事を書いていくうえで必要となるのが、加法群と呼ばれる群だが、この加法群は以下のように定義される。加法群であることを説明するのに一から定義してたらキリがないので、あまり深くは掘り下げない。

集合Gと二項演算{\bullet}: G{\times}G \to Gについて

  • (定義1.1) {\forall}a, b, c \in G, (a \bullet b) \bullet c = a \bullet (b \bullet c)結合律といい、結合律を満たす組(G, \bullet)半群という
  • (定義1.2) (定義1.1)に加えて{\exists}e{\forall}a \in G, e \bullet a = a = a \bullet eを満たすとき、e単位元といい、この半群モノイドという
  • (定義1.3) (定義1.2)に加えて{\forall}a{\exists}e \in G, a \bullet a^{-1} = a^{-1} \bullet a = e(ただしe単位元)を満たすとき、a^{-1}a逆元であるといい、このモノイドをという

そして皆さんお待ちかね加法群は以下のように定義される。

  • (定義1.4) (定義1.3)に加えて{\forall}a, b \in G, a \bullet b = b \bullet a交換律といい、交換律を満たす群を可換群という。加法演算+を使った可換群(G, +)を、特に加法群という

つまりまとめると、結合律と交換律を満たし、単位元と逆元が存在する集合と加法演算の組は加法群であるということだ。

2. 整数・実数の前準備

整数・実数と加法演算+が群を成すことを示すために、いくつか公理、定理を書いておく。

  • (公理2.1) 単位元{\exists}e \in \mathbb{R}0である
  • (公理2.2) {\forall}a \in \mathbb{R}の逆元a^{-1}-aである
  • (定理2.3) {\forall}a, b, c \in \mathbb{R}, (a + b) + c = a + (b + c)
  • (定理2.4) {\forall}a \in \mathbb{R}, a + 0 = a = 0 + a
  • (定理2.5) {\forall}a \in \mathbb{R}, a + -a = -a + a = 0
  • (定理2.6) {\forall}a, b \in \mathbb{R}, a + b = b + a

これらは整数\mathbb{Z}においても同様である。

3. 文字列の前準備

文字列と言うと、数学的にはあまり馴染みがないと思われるので、以下のようにざっくりと定義、公理を書いておく。

  • (定義3.1) 文字列の集合Sとは、文字の列(c_n)_{n ∈ \mathbb{N}}の集合をいう
  • (公理3.2) 単位元{\exists}e \in Sは空の文字の列\varnothingとする
  • (公理3.3) 逆元は存在しないとする
  • (公理3.4) {\forall}a \in Sに対して、...aのように記述することで文字列の展開ができる。すなわち(...a) = aである
  • (定義3.5) {\forall}a, b \in S, a \bullet b = (...a, ...b)が成り立つ二項演算{\bullet}: S{\times}S \to S文字列結合演算という

文字列結合演算には以下の定理があると考えられる。

  • (定理3.6) {\forall}a, b, c \in S, (a \bullet b) \bullet c = a \bullet (b \bullet c)
  • (定理3.7) {\forall}a \in S, a \bullet \varnothing = a = \varnothing \bullet a
  • (定理3.8) {\forall}a, b \in S, a = \varnothing \lor b = \varnothing \Rightarrow a \bullet b = b \bullet a
  • (定理3.9) {\forall}a, b \in S, a \neq \varnothing \land b \neq \varnothing \Rightarrow a \bullet b \neq b \bullet a

4. 整数・実数における加法群の証明

まずは整数・実数と(+)演算子について加法群を成すことを、証明してみる。

  • (命題4.1) 組(\mathbb{R}, +)は加法群である
    • (命題4.1.1) 組(\mathbb{R}, +)半群である
      (定義1.1)より、半群{\forall}a, b, c \in G, (a + b) + c = a + (b + c)つまり結合律があるので、G = \mathbb{R}としたときにも結合律があることをcについての数学的帰納法を用いて示す。
      • (a) c = 1のとき
      (定理2.3)より、a + (b + 1) = (a + b) + 1。したがって(a + b) + c = a + (b + c)
      • (b) {\forall}k \in \mathbb{R}, (a + b) + k = a + (b + k) \Rightarrow c = k + 1のとき
       \begin{align}a + (b + {(k + 1)}) &= a + ({(b + k)} + 1) & \text{... (定理2.3)より} \\&= (a + {(b + k)}) + 1 & \text{... (a)より} \\&= ({(a + b)} + k) + 1 & \text{... 帰納法の仮定より} \\&= (a + b) + (k + 1) & \text{... (定理2.3)より}\end{align}
      となるので、(a + b) + c = a + (b + c)
    • (命題4.1.2) 組(\mathbb{R}, +)はモノイドである
      (命題4.1.1)より、半群であることは示されている。これに加えて(定義1.2)より、モノイドは{\exists}e{\forall}a \in G, e + a = a = a + eが成り立つので、G = \mathbb{R}としたときにも成り立つことを、aについての数学的帰納法を用いて示す。
      • (a) a = 1のとき
      (定理2.4)より、1 = 0 + 1 \land 1 + 0 = 0 + 1。したがってe + a = a = a + e
      • (b) {\forall}k \in \mathbb{R}, 0 + k = k = k + 0 \Rightarrow a = k + 1のとき
       \begin{align}k + 1 &= (0 + k) + 1 &\text{... 帰納法の仮定より} \\&= 0 + (k + 1) &\text{... (定理2.3)より} \\(k + 1) + 0 &= k + (1 + 0) &\text{... (定理2.3)より} \\&= (0 + k) + (1 + 0) &\text{... 帰納法の仮定より}\\&= (0 + k) + 1 &\text{... (a)より}\\&= 0 + (k + 1) &\text{... (定理2.3)より}\end{align}
      となるので、e + a = a = a + e
    • (命題4.1.3) 組(\mathbb{R}, +)は群である
      (命題4.1.2)より、モノイドであることは示されている。これに加えて(定義1.3)より、群は{\forall}a{\exists}e \in G, a + a^{-1} = a^{-1} + a = eが成り立つので、G = \mathbb{R}としたときにも成り立つことを、aについての数学的帰納法を用いて示す。
      • (a) a = 1のとき
      (定理2.5)より、-1 + 1 = 1 + -1 \land 0 = 1 + -1。したがってa + a^{-1} = a^{-1} + a = e
      • (b) {\forall}k \in \mathbb{R}, k + -k = -k + k = 0 \Rightarrow a = k + 1のとき
       \begin{align}-(k + 1) + (k + 1) &= (-(k + 1) + k) + 1 &\text{... (定理2.3)より} \\&= (k + -(k + 1)) + 1 &\text{... (定理2.6)より} \\&= 1 + (k + -(k + 1)) &\text{... (定理2.6)より} \\&= (1 + k) + -(k + 1) &\text{... (定理2.3)より} \\&= (k + 1) + -(k + 1) &\text{... (定理2.6)より} \\0 &= (k + 1) + -(k + 1) &\text{... (定理2.5)より}\end{align}
      となるので、a + a^{-1} = a^{-1} + a = e
    • (命題4.1.4) 組(\mathbb{R}, +)は可換群である
      (命題4.1.3)より、群であることは示されている。これに加えて(定義1.4)より、可換群は{\forall}a, b \in G, a + b = b + aつまり交換律があるので、G = \mathbb{R}としたときにも結合律があることをbについての数学的帰納法を用いて示す。
      • (a) b = 1のとき
      (定理2.6)より、1 + a = a + 1。したがってa + b = b + a
      • (b) {\forall}k \in \mathbb{R}, a + k = k + a \Rightarrow b = k + 1のとき
       \begin{align}(k + 1) + a &= k + (1 + a) & \text{... (定理2.3)より} \\&= k + (a + 1) & \text{... (a)より} \\&= (k + a) + 1 & \text{... (定理2.3)より} \\&= (a + k) + 1 & \text{... 帰納法の仮定より}\\&= a + (k + 1) & \text{... (定理2.3)より}\end{align}
      となるので、a + b = b + a
    可換群(\mathbb{R}, +)の二項演算+は加法であるので、組(\mathbb{R}, +)は加法群である\square

(\mathbb{Z}, +)は加法群であることは、(命題4.1)と同様に示せるので、整数・実数と(+)演算子については加法群を成すと言える。

5. 文字列における加法群でないことの証明

本題となる、文字列と(+)演算子について加法群を成さないことの証明である。

  • (命題5.1) 組(S, +)は加法群ではない
    加法群は半群、モノイド、群、そして可換群でもあるので、それらを示そうとすることで、逆説的に加法群ではないことを示す。
    • (公理5.1.1) 文字の集合はCとする。
    • (命題5.1.2) 組(S, +)半群である
      (定義1.1)より、半群{\forall}a, b, c \in G, (a + b) + c = a + (b + c)つまり結合律があるので、G = Sとしたときにも結合律があることをcについての構造的帰納法を用いる。
      • (a) c = \varnothingのとき
       \begin{align}a + (b + \varnothing) &= a + b & \text{... (定義3.5)より} \\&= (a + b) + \varnothing & \text{... (定理3.7)より}\end{align}
      となるので、(a + b) + c = a + (b + c)
      • (b) {\forall}s \in S, (a + b) + s = a + (b + s) \Rightarrow c_1 \in C, c = (c_1) + sのとき
       \begin{align}a + (b + {({(c_1)} + s)}) &= a + ({(b + {(c_1)})} + s) & \text{... (定理3.6)より} \\&= (a + {(b + {(c_1)})}) + s & \text{... (定理3.6)より} \\&= ({(a + b)} + {(c_1)}) + s & \text{... (定理3.6)より} \\&= (a + b) + ({(c_1)} + s) & \text{... (定理3.6)より}\end{align}
      となるので、(a + b) + c = a + (b + c)
    • (命題5.1.3) 組(S, +)はモノイドである
      (命題5.1.2)より、半群であることは示されている。これに加えて(定義1.2)より、モノイドは{\exists}e{\forall}a \in G, e + a = a = a + eが成り立つので、G = Sとしたときにも成り立つことを、aについての構造的帰納法を用いて示す。
      • (a) a = \varnothingのとき
      e = \varnothingであるため自明。
      • (b) {\forall}s \in S, \varnothing + s = s = s + \varnothing \Rightarrow c_1 \in C, a = (c_1) + sのとき
       \begin{align}(c_1) + s &= (c_1) + (\varnothing + s) &\text{... 帰納法の仮定より} \\&= ({(c_1)} + \varnothing) + s &\text{... (定理3.6)より} \\&= (\varnothing + {(c_1)}) + s &\text{... (定理3.7)より} \\&= \varnothing + ({(c_1)} + s) &\text{... (定理3.6)より} \\({(c_1)} + s) + \varnothing &= (c_1) + (s + \varnothing) &\text{... (定理3.6)より} \\&= (c_1) + s &\text{... 帰納法の仮定より}\\&= (\varnothing + {(c_1)}) + s &\text{... (定理3.7)より} \\&= \varnothing + ({(c_1)} + s) &\text{... (定理3.6)より} \end{align}
      となるので、e + a = a = a + e
    • (命題5.1.4) 組(S, +)は群である
      (公理3.3)より、逆元が存在しないので、群であることを示すことが不可能である。
    群でなければ、かつ(定理3.9)より、交換律を満たせないため、可換群であることを示すことも不可能であるので、組(S, +)は加法群ではない\square

以上から、文字列結合演算子(+)加法としては見れないことがわかる。

終わりに

(S, +)は「加法群」ではないため、加法として見なすことは不可能であることを証明した。

ここからは私のお気持ち表明になるのだが、加法と文字列結合を同一視したことによって、定数でない値a, bに対して、a + bの意味が二通りになってしまっているし、特に、JavaScript/TypeScriptなどにおいては、暗黙型変換により、バグの原因にもなっていることを考えると、結果論ではあるが、文字列結合演算子(+)は失敗作だったと感じる。

しかもモダンなプログラミング言語では文字列結合演算子(+)を用いずとも、それよりも可読性の高い表記(ECMAScript2015のテンプレートリテラルなど)があるので、今ではわざわざこの演算子を使う必要性が感じられない(悲しいことに、それがサポートされていない言語であれば諦めるしかないが・・・)。