偽物

2009/05/26

「主筆」のビューとドキュメントの分割に手を着ける前に、とりあえずフォルダ構成を見直しておくことにした。現時点でも、現在公開している第20版には存在しない共有ライブラリが増えているし、フォルダ名がいくつか気に入らない奴が有るため、今の内にきれいにしておくことにしたのだ。

また、生成された共有ライブラリと、その共有ライブラリを使用するために必要となるヘッダファイルが、その共有ライブラリを開発するためのフォルダ内に格納されたままだと何かと都合が悪いため、別途includeとlibというフォルダを用意して、コンパイル後にそのフォルダにシンボリックリンクを作成するようにしておいた。

つまり、たとえばTaEdit/FOOというフォルダを作って、その中でlibFOO.soというライブラリを構築するようにしたとする。そうすると、libFOO.soそのものと、それを使うためのヘッダファイル、たとえばfoo.hは、TaEdit/FOOフォルダの中に存在する事になる。となると、そのlibFOO.soを使うプログラムは、コンパイル時にヘッダファイルとライブラリの検索パスとして、TaEdit/FOOというフォルダのパス名を指定してあげなければならなくなる。

とりあえず、使用するライブラリが1つだけであれば、それは大した事ではない。だが、そういったライブラリがだんだん増えてくると、コンパイル時に指定しなければならなくなるパス名が増えてくることになる。まぁ、だとしても、指定すればいいだけの話だと言ってしまえばそれまでだが、やはりそれでは美しくないため、外部に公開する奴は「公開されるためのフォルダ」を作って、そこに格納するようにしたほうがいい。そうしておけば物事がわかりやすくなるから、コンパイルするときにヘッダファイルが見つからないだの、リンク時にシンボルが見つからないだのと言われる可能性を減らすことができるようになる。

フォルダ構成の整理は、基本的にはMakefileをいじるだけだからそう大した話ではない。もっとも、複数人で開発している場合は、逆にほとんど不可能に等しい荒技ということになるのだが。

今現在は、COMの偽物の仕様ついて考えている。WindowsのCOMの様なことをやりたいとはいっても、しかし、COMそのものを作るつもりはないし、COMと張り合おうというつもりもない。ただ、自分が作ったアプリケーションの範囲内だけでつかわれる、それっぽくモジュールを分割できるような仕掛けが有ればそれでいいのだ。

そうすると、もっともストイックな実装としては、ただ、純粋仮想クラスを継承したオブジェクトを生成して、そのインスタンスを返してよこすだけのライブラリというのが考えられる。すなわち、そのライブラリで提供される関数を一回呼び出してやれば、IDocumentとか何とか、そんな感じの純粋仮想クラスのポインタを返す、そういうライブラリにしておくということだ。

だが、それではちょっとありがたみが薄い。どうせなら、そのライブラリのインタフェース仕様(つまり関数の仕様)や、オブジェクトのインタフェースとなる純粋仮想クラスに一定の規約を当てはめ、その規約を強制するようなライブラリを一枚かぶせるようにした方がいい。そもそも、共有ライブラリの動的ロードや実行時に関数を検索する処理など、ライブラリや実装するオブジェクトによらずみんな同じになるのだから、どうせなら処理を統合した方がいい。

とすると、その規約なりライブラリなりで、どこまで何をサポートするのかが問題となる。とりあえず、純粋仮想クラスを継承したオブジェクトを生成するという機能と、ライブラリ自体がエクスポートする関数は完全にC言語の仕様に準拠していること等と言った物事は、そもそもの目的であることからして外すわけには行かない。

だが、IUnknown::AddRefやIUnknown::Releaseといったメソッドにより提供される、インスタンス管理の機能は必要なのだろうか? 難しいことをやりたいわけでもなし、とりあえずオブジェクトを使う側で間違えなければ、わざわざインスタンス管理の機能をここで実装してやる必要はないものと思われる。もし、参照カウンタなりガーベッジコレクタなりでメモリ管理を行う様にしたいので有れば、それは別の機能として実現すればいいまでの話だ。

また、オブジェクトが提供する別のインタフェースを取得する機能も必要ではない。そんなものは適用にやればいい。COMではGUIDによりインタフェースなどに一意な名前を採番するようにしているが、それも簡略化していいはずだ。どうで、1アプリケーションに閉じた世界の話なのだ。世界中で一意にに識別できなければならない必要性などない。

微妙なのは、インタフェースを実装するライブラリをどこかに登録しておいて、オブジェクト生成時に、自動的に目的となるライブラリを検索できるようにする仕掛けがいるかどうかだ。COMではインタフェースに割り当てられたGUIDとDLLのファイル名をレジストリに登録しておいて、オブジェクトを生成する際にはDLLのファイル名や物理的な場所については意識しなくてもいいようになっている。だが、今回俺が作ろうとしているものには、そのような機能は必要であろうか?

ビューの方から本物のライブラリ名を指定してオブジェクトを生成させるようにしたところで、あまり実害はなさそうな気がする。だが、ライブラリのファイル名とインタフェース名を事前に何かの設定ファイルに登録しておくようにすれば、アプリケーションでライブラリのファイルを保持する必要が無くなり、何か便利になるような気がする。

だが、それ以上の具体的な利点が見えない。必要だろうか? 不要だろうか? どっちでも良さそうな気がするが、きめなきゃ物事が進まない。どうしようか。

一刀両断

2009/05/22

正規表現の最適化なんか考えている場合ではない。技術士の勉強をしなければならないはずである。とりあえず、ここ数日は毎日論文の問題を解くようにしてはいる。だが、そもそも正解というものがない論文は、勉強したところでできるようになるものなのだろうか? それに、手元にある問題数が少ないため、もうやるべきものが無くなってしまった。 とりあえず、漢字の勉強でもしておいた方が良いのだろうか?

「主筆」のウインドウ幅で文字列を折り返して表示する対応に、そろそろ手を付けたいと考えている。だが、それをやるためには文字列の編集と表示という、最もコアな部分に手を入れなければならない。

大変残念なことに、文字列の編集に関わる処理や文字列の表示に関する部分は、「主筆」の中でも最も古いコードに類し、かなりいい加減な作りになっている。特に気に入らないのは、文字列の操作や編集を行うモジュールと、画面の表示を行うモジュールが強く結合していることだ。つまり、ビューがドキュメントの内部構造に立ち入った様な処理になっているということだ。

どう考えてもおかしいこの作りは、開発当初、モチベーションを維持することを目的に、とにかく動作するものをでっち上げようとしたことに起因する。過去の経験から、フリーソフトを作り上げるためにはモチベーションを持続させることが重要だと考え、その為に設計の基本ですら無視した開発を行ったのだ。

その甲斐あってか、とりあえずはテキストエディタとしての体をなしたモノを作り上げることができた。そういう意味ではその戦略は正解だったといえる。だが、今やその目的は消え、当初無視した設計上の不良がここに来てボディブローのように効いてきている。

コアな部分には手を入れずに、塩漬けにしたまま使い続けるのであれば、別に設計がどうであろうが問題はない。しかし、大規模に手を加えようとするとなると、このままではどうにも都合が悪い。だから、そろそろドキュメントとビューをスパッと切断するような大手術が必要になるものと思われる。

できれば、インタフェースを明確に定め、独立した共有ライブラリとして切り出すようにしたい。つまり、Windowsで言うところのCOMコンポーネントのようにしたいと考えている。まぁ、IDLがどうだとかGUIDがどうだのということをやるつもりはない。とりあえず、インタフェースとして純粋仮想クラスを定義し、その純粋仮想クラスを実装するオブジェクトを生成する、という程度のことはやりたい。そうすれば、主筆の本体部分がほぼ真っ二つに切断されることになり、かなり事態が単純化するはずである。

しかし、その為にはまたしてもかなりの作り込みが必要になる。それも相当に難しく、一度踏み出したら容易には後戻りできない危険な迷宮に足を踏み入れることになるものと予測される。それを、技術士の勉強をしなければならないと言っているような、今この時期に始めるべきか否か。

それが問題だ。

正規表現の最適化2

2009/05/09

答えから書いてしまうと、「α{a,b}{c,d}」という正規表現はc(b-a)>=a-1もしくはc=dが成立する場合に限り「α{a*c,b*d}」に変換することができる。

証明といえるような内容になっているかどうか判らないが、俺なりに考えた結果を書いてみる。

まず、そもそも論として、「α{a,b}{c,d}」を「α{a*c,b*d}」に変換できるとはどういう意味なのだろうか?

「α{a,b}{c,d}」というのは、昨日も書いたが「とある正規表現αがa回以上b回以下繰り返す」というものがc回以上d回以下繰り返す、という指定である。また「α{a*c,b*d}」とは、とある正規表現αがa*c回以上b*d回以下繰り返すという意味である。

でもって、「α{a,b}{c,d}」を「α{a*c,b*d}」に変換できるというのは、「α{a,b}{c,d}」が実質的に「α{a*c,b*d}」と同じ、すなわち、「α{a,b}{c,d}」という指定により繰り返されるαの個数が、a*c~b*dの範囲内に収まり、かつ、a*c~b*dの全ての値を取るということを意味している。

例えば、「α{2,3}{4,5}」という正規表現を考えてみる。

αの数が最も少ないパターンでは、内側の繰り返しが全部2回で、かつ、外側の繰り返しが4回の場合、合計で8回繰り返す場合である。つまり下記のようになる場合である。

 αα αα αα αα (半角空白は判りやすくするために入れた)

次に少ないのは、内側の繰り返しが2回の奴が3つと3回の奴が1つ、でもって、外側の繰り返しが4回の場合である。つまり、下記のようなパターンである。

 ααα αα αα αα
    あるいは
 αα ααα αα αα
    あるいは
 αα αα ααα αα
    あるいは
 αα αα αα ααα

同様に、最も多くなるのは内側の繰り返しが全部3回で、かつ、外側の繰り返しが5回になる場合である。すなわち、下記のようなパターンである。

 ααα ααα ααα ααα ααα

こうやって考えていくと、「α{a,b}{c,d}」と指定された場合、全体の繰り返し数の最小は必ずa*c回、最大の繰り返し数はb*d回になると言うことができる。

ということは、「α{a,b}{c,d}」による繰り返し回数が、a*c回~b*d回の全てのパターンを取りうるのであれば、「α{a*c,b*d}」に変換できると言うことになる。

ちなみに、「α{2,3}{4,5}」の取りうる繰り返し回数を列挙すると下記のようになる。

外側の繰り返し回数内側の繰り返し回数合計
42,2,2,28
42,2,2,39
42,2,3,29
42,2,3,310
42,3,2,29
42,3,2,310
42,3,3,210
42,3,3,311
43,2,2,29
43,2,2,310
43,2,3,210
43,2,3,311
43,3,2,210
43,3,2,311
43,3,3,211
43,3,3,312
52,2,2,2,210
52,2,2,2,311
52,2,2,3,211
52,2,2,3,312
52,2,3,2,211
52,2,3,2,312
52,2,3,3,212
52,2,3,3,313
52,3,2,2,211
52,3,2,2,312
52,3,2,3,212
52,3,2,3,313
52,3,3,2,212
52,3,3,2,313
52,3,3,3,213
52,3,3,3,314
53,2,2,2,211
53,2,2,2,312
53,2,2,3,212
53,2,2,3,313
53,2,3,2,212
53,2,3,2,313
53,2,3,3,213
53,2,3,3,314
53,3,2,2,212
53,3,2,2,313
53,3,2,3,213
53,3,2,3,314
53,3,3,2,213
53,3,3,2,314
53,3,3,3,214
53,3,3,3,315


重複はあるが、最小の8回(=2*4)から最大の15回(=3*5)の全ての値を取るため、「α{2,3}{4,5}」は「α{8,15}」と同じであると言うことができる。

では、いかなる条件が成立した場合に、「α{a,b}{c,d}」による繰り返し回数がa*c回~b*d回の全ての値を取るのだろうか?

それを明らかにするために、まずは外側の数量子の値が同じだった場合、すなわち「α{a,b}{x,x}」という指定だった場合について考えてみる。つまり、

 命題:{a,b}{x,x}が指定されたとき、任意のax~bxの繰り返し回数を取りうる

の、証明を試みる。

何か泥臭いが、帰納的に証明する。

まず、x=1の場合、すなわち{a,b}{1,1}の場合にa~bの任意の値を取りうるか否かについて考える。{a,b}の指定は、a~b回の任意回の繰り返しであることを示すのだから、{a,b}{1,1}がa~bの任意回の値を取れるのは明らかである。

次に、x=2の場合、すなわち{a,b}{2,2}の場合について考えてみる。

x=2つまり外側の繰り返し回数が2回なので、内側の(a~b回)を2回繰り返す、すなわち、

 (a~b回)+(a~b回)=(2a~2b回)

が成立することを考える。

ここでまた泥臭いが、1回目の内側の繰り返し回数が、a回からb回に変化するのを順番に追っていってみる。

 (a+0回)+(a~b回)=((a+0)+a~(a+0)+b回)=(2a~a+b回)
 (a+1回)+(a~b回)=((a+1)+a~(a+1)+b回)=(2a+1~a+b+1回)
 (a+2回)+(a~b回)=((a+2)+a~(a+2)+b回)=(2a+2~a+b+2回)
  ・・・
 (b-2回)+(a~b回)=((b-2)+a~(b-2)+b回)=(a+b-2~2b-2回)
 (b-1回)+(a~b回)=((b-1)+a~(b-1)+b回)=(a+b-1~2b-1回)
 (b-0回)+(a~b回)=((b-0)+a~(b-0)+b回)=(a+b~2b回)

1つめの式により、2a回からa+b回の任意の繰り返し回数が生成できることが判る。また、それ以降の式の最大繰り返し回数が、a+bから2bまで1つずつ順番に増加して行くことから、結果として、2a回~2b回の任意の繰り返し回数を取ることができることが判る。すなわち、(a~b回)+(a~b回)=(2a~2b回)は成立する。

次に、x=3の場合、すなわち{a,b}{3,3}の場合について考えてみる。

x=3つまり外側の繰り返し回数が3回なので、内側の(a~b回)を3回繰り返す、すなわち、

 (a~b回)+(a~b回)+(a~b回)=(3a~3b回)

が成立することを考える。

ここで、(a~b回)+(a~b回)=(2a~2b回)が成立することを利用して、上記の式を下記のように変形する。

 (a~b回)+(2a~2b回)=(3a~3b回)

ここでx=2の場合と同じように、1回目の内側の繰り返し回数が、a回からb回に変化するのを順番に追っていってみる。

 (a+0回)+(2a~2b回)=((a+0)+2a~(a+0)+2b回)=(3a~a+2b回)
 (a+1回)+(2a~2b回)=((a+1)+2a~(a+1)+2b回)=(3a+1~a+2b+1回)
 (a+2回)+(2a~2b回)=((a+2)+2a~(a+2)+2b回)=(3a+2~a+2b+2回)
  ・・・
 (b-2回)+(2a~2b回)=((b-2)+2a~(b-2)+2b回)=(2a+b-2~3b-2回)
 (b-1回)+(2a~2b回)=((b-1)+2a~(b-1)+2b回)=(2a+b-1~3b-1回)
 (b-0回)+(2a~2b回)=((b-0)+2a~(b-0)+2b回)=(2a+b~3b回)

x=2の場合と同様である。1つめの式により、3a回からa+2b回の任意の繰り返し回数が生成でき、また、それ以降の式の最大繰り返し回数が、a+2bから3bまで1つずつ順番に増加して行くことから、結果として、3a回~3b回の任意の繰り返し回数を取ることができることが判る。すなわち、(a~b回)+(a~b回)+(a~b回)=(3a~3b回)は成立する。

一般化してn回の場合も同様に、

 (a~b回)1+・・・+(a~b回)(n-1)=((n-1)a~(n-1)b回)

が成立することから、

 (a~b回)1+・・・+(a~b回)n=(na~nb回)

は、

 (a~b回)+((n-1)a~(n-1)b回)=(na~nb回)

となり、今までと同じように、

 (a+0回)+((n-1)a~(n-1)b回)=((a+0)+(n-1)a~(a+0)+(n-1)b回)=(na~a+(n-1)b回)
 (a+1回)+((n-1)a~(n-1)b回)=((a+1)+(n-1)a~(a+1)+(n-1)b回)=(na+1~a+(n-1)b+1回)
 (a+2回)+((n-1)a~(n-1)b回)=((a+2)+(n-1)a~(a+2)+(n-1)b回)=(na+2~a+(n-1)b+2回)
  ・・・
 (b-2回)+((n-1)a~(n-1)b回)=((b-2)+(n-1)a~(b-2)+(n-1)b回)=((n-1)a+b-2~nb-2回)
 (b-1回)+((n-1)a~(n-1)b回)=((b-1)+(n-1)a~(b-1)+(n-1)b回)=((n-1)a+b-1~nb-1回)
 (b-0回)+((n-1)a~(n-1)b回)=((b-0)+(n-1)a~(b-0)+(n-1)b回)=((n-1)a+b~nb回)

となり、(a~b回)1+・・・+(a~b回)n=(na~nb回)が成立する。

以上のことから、「α{a,b}{x,x}」は、常に「α{a*x,b*x}」に変換できると言うことができる。

次に、外側の繰り返し回数に幅がある場合、すなわち「α{a,b}{c,d}」の場合に、ac~bd回の任意の繰り返し回数を生成できるか否かについて考えてみる。

ここで、以下のような複数の変換について考えてみる。

 「α{a,b}{c+0,c+0}」を「α{a(c+0),b(c+0)}」にする
 「α{a,b}{c+1,c+1}」を「α{a(c+1),b(c+1)}」にする
 「α{a,b}{c+2,c+2}」を「α{a(c+2),b(c+2)}」にする
  ・・・
 「α{a,b}{d-1,d-1}」を「α{a(d-1),b(d-1)}」にする
 「α{a,b}{d-0,d-1}」を「α{a(d-0),b(d-0)}」にする

つまり、外側の繰り返し回数を固定にして、一つずつ増やしていった場合について、それぞれの変換について考えてみる。上記の変換は、外側の繰り返し回数が同じであるため、今までの証明から全て成立するということができる。

ここで、変換後の「α{ac,bc}」「α{a(c+1),b(c+1)}」・・・「α{ad,bd}」の繰り返し回数の範囲が互いに重複しているのであれば、「α{a,b}{c,d}」の繰り返し回数が、ac~bd回の全ての繰り返し回数の値を取ることができると言うことができるはずである。

数直線で書くと、こんな感じになるだろうか。



式で書くと、b(c+0)+1>=a(c+1) かつ b(c+1)+1>=a(c+2) かつ ・・・ かつ b(d-1)+1>=a(d-0) という条件式になる。表現を変えれば、下記のようになる。



上記の式は、下記のように書き換えられる。



ここで、iは必ず正の値であり、かつ、単調増大であること、及び、aとbの値は変わらないことから、iがもっとも小さいとき、すなわちi=cの場合にi(b-a)>=a-1が成立すれば、iがcからdまで変化する場合の、全ての場合について成立すると言うことができる。

以上のことから、c(b-a)>=a-1が成立する、あるいは、c=dが成立するならば、「α{a,b}{c,d}」=「α{a*c,b*d}」が成立すると言うことができる。


と、こんなもんでどうだろうか? あっているだろうか?

正規表現の最適化1

2009/05/07

下らないことを考えてみた。

正規表現で、繰り返しを表す記号(数量子とか言うらしい)には、*とか、?とか、+とか、{a,b}とか、いろいろある。また、その中にも最長一致とか最短一致とか強欲なマッチとかいろいろな種類がある。

とりあえず、最長・最短・強欲の種別は無視するとして、*・?・+・{a,b}の指定に関して、正規表現を最適化することについて考えてみた。

*は0回以上の繰り返しを表す。すなわち機械的に{0,}に置き換えることができる。同様に、+は{1,}に、?は{1,0}に置き換えることができる。また、{a,b}の形式でbが省略された場合は、bに十分大きな値(例えばINT_MAX)が指定されたと考えれば、全ての数量子は{a,b}の形に正規化することができる。

そこまではいい。問題はここから。

α{a,b}{c,d}と指定されたら何が起きるのか、ということについて考えてみる。この指定の直接的な意味としては、正規表現αが、最低a回・最大b回繰り返し、かつ、それが最低c回・最大d回繰り返すということになる。つまり二重の繰り返しになるということだ。

だが、素直に考えるとこれは無駄である。例えば、α{0,1}{0,INT_MAX}と指定された場合は、内側の{0,1}の指定は全く意味が無くただの無駄以外の何物でもない。すなわち、α{0,INT_MAX}と最適化してあげるのが妥当である。

同じように、α{2,3}{4,5}と指定された場合にはα{8,15}としてあげることができるはずである。そうすれば、マッチを行う際の処理効率が大幅に向上するはずだ。(まぁ、元よりこんなアホな指定をする人間が悪いという考え方もあるのだが)

そう考えると、一般的にα{a,b}{c,d}と指定された場合は、α{a*c,b*d}と変換してあげることができるような気がする。

しかし、よく考えるとこの変換には無理がある。例えば、α{20,21}{2,3}という指定について考えてみる。この場合、先の変換公式に従うとα{40,63}という形になるのだが、実はα{20,21}{2,3}とα{40,63}とは等しくないのである。

すなわち、α{40,63}とした場合は、正規表現αが最低40回・最大63回繰り返すと言うことを意味しているのだが、α{20,21}{2,3}とした場合は、αが40回・41回・42回・60回・61回・62回・63回のいずれかの繰り返しになると言うことを意味する。よって、この変換は成立しない。

よくよく考えると、ある特定の条件が成立した場合だけα{a,b}{c,d}をα{a*c,b*d}に変換できるということに気がついた。だが、その条件については、今日はもう疲れたからまた今度書くことにする。

あな

2009/05/01

スーツを新調した。

別に、この年になってから性に目覚めておしゃれに気を遣うようになったためでもなければ、季節の変わり目だからという訳でもない。単に、スーツのズボンに穴が空いたためである。

いつも人に笑われるが、俺は外出する際には常に鞄は肩から提げるようにしている。典型的なオタク臭の漂うあのスタイルである。それは通勤の際も同様であり、必ず鞄は肩にかけている。その都合上、鞄の生地とスーツの腿のところがこすれて、気を付けてないとズボンに穴が空いてしまうのだ。

俺的には穴が空こうがズボンをはいていなかろうが、別にどうでも良いとしか思っていないのだが、しかし、曲がりなりにも客商売である以上、最低限まともな格好をしている必要がある。例えよれていようが型が古かろうが、最低限、穴や破れの無いスーツっぽい布きれでチンコとすね毛を隠さねばならない。これはサラリーをもらう社会人として必須である。

そういうことで、手取りが少なくなり、ますます生活が苦しくなりつつあるこの状況下でありながらも、思い切ってスーツを買ってしまった。

だがそもそも、穴が空かなければ無駄な支出を削減することができるのだから、例の腐臭漂うスタイルをやめればいいのではないかとも思うのだが、しかしこれはそう簡単な問題ではない。あの格好はいろいろな条件を勘案するともっとも安全であるという結果が導き出されるスタイルのなである。

まず第一に楽である。わざわざ手で鞄を持つ必要がないため、疲れにくい。第二に、置き忘れる心配がない。手で持っているとついどこかに置きたくなるが、そうすると置き忘れる危険性がある。手放さなければ置き忘れる心配はない。第三に、痴漢に間違われるリスクを軽減することができる。電車内で、鞄で股間を護り、両手を上に上げていることで、あらぬ言いがかりを付けられるスキをなくすことができる。

というか、むしろ鞄を手に持って満員電車に乗っている奴らの方が不思議である。あれはわざとやっているのだろうか? 鞄を手に持ち下におろしていると、ちょうど手がケツの部分にあたる。それを判った上で、女のケツを触るべくしてわざとそうやっているのか、さもなくば著しく頭の働きが鈍く、現実が認識できていないのか、そのどちらかとしか思えない。そういった連中がいる限り、世の中から痴漢が無くなることはないだろうし、同時に、痴漢の冤罪が無くなることもないだろう。

いずれにせよ、いろいろな意味でのセキュリティ的観点から言って、鞄は手に持つものではなく肩に提げておくべきものであるという結論が導き出される。

だがこのスタイルにはいくつかの重大な欠点がある。まず、先にも書いたがスーツに穴があくという問題が挙げられる。ジーンズの生地ですらこすれて薄くなり、ついには貫通するぐらいなのだから、スーツのペラペラな布きれなどたちどころにやられてしまう。次に、このスタイルは自ら公衆に対してオタク・マニアの類、すなわち、被差別階級の所属であることを公言しているようなものだということである。大多数の人は、おそらくこのレッテルを背負うことを恐れ、忌み嫌うのだろうが、俺はもはやそのような問題は超越している。マニアは、他者からマニアであると蔑まれることは気にしない。

しかし、余分な出費が嵩む現実は残る。合理性を妥協するつもりはないが、穴が空く問題には何らかの対処が必要であろう。