見出し画像

関数型プログラミング事始め (4) 配列の苦難

関数型プログラミングがはじめての方へ贈る入門の書
前節:変数の苦難 次節:再帰の苦難
参考書:
・五味 弘「はじめてのLisp関数型プログラミング」技術評論社(2016)
・大山口 通夫、五味 弘「プログラミング言語論」コロナ社(2008)
・五味 弘「関数型プログラミングと数学(ITと数学)」技術評論社(2021)

副作用のないプログラミングは難しいものです。変数や代入文にも警戒する必要があります。それだけでなく、状態を変更する操作には警戒した方がいいでしょう。例えば配列の値を変更する操作にも目を配る必要があります。でもこの縛りプレイでのプログラミングができれば、関数型の王道楽土に行けます。

(2) 配列が使えない? - ミュータブルの呪い

配列は要素が変更可能なミュータブル(mutable)なデータタイプです。ミュータブルとは変更可能という意味で、値が変更可能なことを言います。
例えば a[1] = 100 のように配列の要素が変更できます。つまり配列の状態を変更でき、これが副作用の原因になります。

関数型プログラミングではイミュータブル(immutable)なデータタイプを使います。イミュータブルとは不変と言う意味で、値が変更不可能なデータタイプです。

イミュータブルなデータタイプを使えば、そこからは副作用は生じません。安全安心なプログラムが使えます。

ところで配列はミュータブルです。ということは配列は使えないのでしょうか。答えは「はい」なのですが、でも安心してください。回避できる方法はあります。これは後述します。

(例) Javaの文字列はイミュータブルで、Cはミュータブルで

例えばJavaのStringクラスはイミュータブルです。一方、Cのchar[]はミュータブルです。

例.
Java: String x = "abc";  // x に格納されている"abc"は不変
C: char[] x = "abc"; // x[1] = 'B’を実行すると"abc"は変更され、"aBc"になる

Cではxの値がどこかで誰かが変更されているかの恐怖にびくびくしながら、プログラミングしなければなりません。デバッグのときに「誰が勝手に値を変えたんだ!」という悲鳴が挙がります。

Javaでは文字列の値は不変で、誰にも変更を許していません。もし何らかの理由で変更したいときは、オリジナルの文字列をコピーして変更することになり、オリジナルの文字列には影響を与えません。

このように最近のプログラミング言語ではイミュータブルなデータタイプが導入されるようになり、副作用の危険性を減らすようになってきています。
これも関数型プログラミングの影響です、きっと、たぶん。

(弁解と対策) 配列を使いたい

配列はミュータブルで副作用の原因になります。でも配列はあまりにも便利です。使いたいです。使えないと不便です。

でもJavaのStrinigはイミュータブルです。副作用は生じません。これはどのように実装されているのでしょうか。答えは簡単で変更操作(破壊的操作)を許していないだけです。変更操作をするときは暗黙的か明示的にかは別にして、コピーして変更するようにします。

つまり配列を使いたいときは値を変更するときはコピーをしてオリジナルの配列に影響を与えないようにするようにします。

このための実装は色々と考えられます。1回ごとにコピーするという原始的な方法から、キャッシュを設ける方法などがあります。この実装については別の節で紹介することにします。

ということで今回の結論「配列はコピーして変更」以上です。

(次回予告) (3) 再帰プログラミングの苦難

次回は関数型プログラミングでよく使われる再帰プログラミングが難しいという苦難です。

参考:プログラミング言語はどれがお得?(前編)|五味弘 (note.com)
参考:プログラミング言語はどれがお得?(後編)|五味弘 (note.com)

よろしければサポートをお願いします!