トップページへ

Haskell言語のプログラミングスタイル

Access Count : 1474

Copyright © 2021 TAKEHANA TADASHI
著作日時: 2021.03.01. 11:55:00 著作者、竹花 忠
Haskell言語のプログラミングスタイル:
 終了条件が訪れるまで再帰呼び出しを続ける。その再帰呼び出しの繰り返しにおいて、適宜、中間結果を更新する。その中間結果はパラメーターに保持することもあれば、あるいは、再帰呼び出しの記述の左にリスト構築子を置いて、その左に中間結果を登録する方式を採ることもある。
 そのようにして、再帰呼び出しの際に、中間結果を蓄積していったり中間結果を連結していったりする。
 やがて、終了条件の到来によって、パラメーターに蓄積していた値の完成なりリストの完成なり、を迎えて、返り値が確定し関数の処理が終了する。
 そのような変遷展開による関数によってプログラムを構成してゆくのが、Haskell言語によるプログラミングのスタイルの一例というか二例というかである。

 あるデーター・ある対象、を使用しての変遷を展開するためには、その必要なデーター・その必要な対象、を一堂に揃えることがまず必要である。
 まず、関数が内部に、恒常的に保持する値・不変で保持する値・常時一定値で保持している値、が、その関数では取り扱える。
 さらに、関数呼び出しの都度、変更され得るはずの値については、パラメーターでわたすことになる。
 関数呼び出しの都度、変更され得る値は、パラメーターによってしかわたしようがないのだから。
 なお、関数内に記述された関数、その定義についても、それは、呼び出しごとに変動しない、恒常的なものである。差し替わる記述を含んでいるにしても、どう差し替わるかが、不変で保持されている。であるから、そういう意味で、関数内に記述されている関数もまた、恒常的なものである。
 必要なデーター・必要な対象、のうち、恒常的でないもの・常時一定値で保持されないもの、は、パラメーターでわたす。
 以上で、関数内に一堂に、必要なデーター・必要な対象、を取り揃えられた。
 あとは、それらを使用して、変遷を展開して、目的の値を・結果値を、手に入れればいい。そしてそれを返り値として、関数の外に出してやる。
 目的の値・結果値、が直ちに得られないときには、再帰呼び出しによって、目的の値・結果値、を追求してゆく・へと接近してゆく。
 その途上の値は、そのためのにパラメーターを用意して、そのパラメーターに、保持・蓄積、してゆく。関数の呼び出しにおいてその関数が受け取れるものは、パラメーターの値だけなのだから、前回までの途中経過・前回までの中間結果、を引き継ぐにはそうするしかない。
 あるいは、再帰呼び出しの記述の左にリスト構築子を置いて、その左に随時の結果を登録してゆく。それによって、再帰呼び出しにつれて随時その時の結果が登録されて、返り値となるリストが完成してゆく。
 また、中間結果をパラメーターに蓄積していた場合には、終了条件を迎えた時の関数呼び出しの定義の位置に、そのパラメーターに必要な処理を施した記述を行っておけばいい。そうすれば、そのパラメーター値に必要な処理が施された値が、その関数の返り値となる。
 以上のいずれかの構成による関数の作成が、Haskell言語によるプログラミングスタイルの一例というか二例というか、である。
 下記に、簡単に、その具体例を示す。
 一例目は、get_keyw_no_ato_cnt_moji関数である。
 コードのあとに、コードの説明が付記してある。

例1.
  前略
parts = T.splitOn (T.pack keyw) (T.pack contents)
stringparts = lines . T.unpack $ T.unlines parts
(result, zanyoparts) = get_keyw_no_ato_cnt_moji (drop 1 stringparts) keyw cnt ""

get_keyw_no_ato_cnt_moji :: [String] -> String -> Int -> String -> (String, [String])
get_keyw_no_ato_cnt_moji [] _ _ ato = (ato, [])
get_keyw_no_ato_cnt_moji stringparts@(x:xs) keyw cnt ato
| length x < cnt = get_keyw_no_ato_cnt_moji xs keyw cnt (ato ++ keyw ++ x)
| otherwise = (ato ++ keyw ++ (take cnt x), stringparts)

 T.packは、String型のデーターをT.Text型のデーターに型変換している。
 T.splitOnは、T.Text型のデーターに対する関数で、第2引数のテキストデーターを、第1引数のテキストデーターの位置で切り分けて、その切り分けられたテキストデーターから成るリストを返す関数である。
 つまり、T.splitOn (T.pack "TT") (T.pack "abTTcdTTef")なら、[T.Text]型の["ab", "cd", "ef"]が返される。
 T.unlinesは、T.Text型のデーターに対するunlines関数である。したがって、T.Text型の"ab\ncd\nef\n"が返される。
 T.unpackは、T.Text型のデーターをString型のデーターに変換する関数である。従って、通常の"ab\ncd\nef\n"が返される。
 linesは、String型のデーターに対して、\nをセパレーターとして切り分けを行い、その切り分けられた文字列からなるリストを返す関数である。従って、[String]型の["ab", "cd", "ef"]が返される。
 つまり、この2行のコードで、contentsをkeywをセパレータとして切り分けて、その切り分けられた文字列からなるリストを得ている。ただし、それをT.Text型を経由して行ったものである。
 String型に対するsplitOnが使用できなかったので以上のようにT.Text型を経由する手間をかけた。

 (result, zanyoparts) = get_keyw_no_ato_cnt_moji (drop 1 stringparts) keyw cnt ""にて、get_keyw_no_ato_cnt_mojiの呼び出しをしている。
 処理対象である部分文字列たちをわたす第1パラメーターに、先頭の部分文字列を削除したものをわたす。先頭の部分文字列は、キーワードの直後の部分文字列ではないからである。
 get_keyw_no_ato_cnt_mojiは、キーワードの直後からの部分文字列たちを第1パラメーターに受け取って処理をする関数である。
 第2パラメーターにはキーワードkeyw、第3パラメーターにはキーワードの直後から何文字抽出するかの個数cnt、を配置している。
 第4パラメーターは、中間結果を保持しておくためのものである。初期値として、""を配置してある。
 以上のバラメーターの設定で、get_keyw_no_ato_cnt_mojiを呼び出すことで、下記が実行される。
 get_keyw_no_ato_cnt_mojiは、キーワードの後のcnt個の文字を抽出し、また、抽出した位置の部分文字列とそれ以降の部分文字列たちからなるリストとを、タプルで返す。
 ただし、cnt個未満内にキーワードが見つかった場合は、つまり、先頭の部分文字列xのサイズがcntよりも小さかった場合は、これまでに保持している抽出済みの文字列atoにキーワードkeywを連結しさらにcnt個に達しなかった先頭の部分文字列xを連結する。そしてそれを第4パラメーターに配置する。なお、処理対象となる第1パラメーターは、先頭の部分文字列xを削除した残余の部分文字列たちxsに差し替え。第2パラメーターのキーワードkeywと第3パラメーターの抽出文字の個数cntはそのまま。以上のパラメーターの設定で、再度、get_keyw_no_ato_cnt_mojiを呼び出す。
 ですから、その時先頭に位置していた部分文字列xのサイズが抽出文字の個数cntよりも小さいことが続けば、第4引数に保持される文字列はどんどん長くなってゆく。
 さて、otherwiseであるが、これは、先頭に位置していた部分文字列xのサイズが、抽出文字の個数cnt以上であった時である。この時には、これまで第4パラメーターに保持してきた文字列atoにキーワードkeywを連結し、さらに、cnt以上のサイズであったxからcnt個だけ文字を抽出して連結する。それをタプルの第1要素としている。第2要素には、第1パラメーターにわたされたきた処理対象であった部分文字列たち全体stringpartsを配置する。その2要素のタプルが返り値として返される。


-------
例2.
  前略

listing_keywbun :: String -> String -> [String]
listing_keywbun [] _ = []
listing_keywbun zenbun keyw =
let
(bun, zanyobun) = bun_And_zanyobun zenbun
in
case (isInfixOf keyw bun) of
True -> (bun ++ "\n\n\n\n") : listing_keywbun zanyobun keyw
False -> listing_keywbun zanyobun keyw


 bun_And_zanyobunは、引数の文字列を、その先頭の1文の文字列と残余の文字列に分離して、それをタプルで返す関数である。
 listing_keywbunは、キーワードを含む文の文字列を、その末尾に改行文字4個を付加して、ことごとくリストアップしたリストを返す関数である。
 キーワードが先頭の1文の中に含まれていたら、その1文に改行文字4個を付加した文字列を、結果リストが構築される位置に登録する。そして処理対象文字列をzanyobunに差し替えて、再度、listing_keywbunを呼び出す。
 キーワードを先頭の1文が含んでいなかったら、結果リストが構築される位置への登録はせずに、処理対象文字列をやはりzanyobunに差し替えて、再度、listing_keywbunを呼び出す。
 listing_keywbunの呼び出しが、第1引数に[]を配置して行われると、その時には、[]が返される。これによって、結果リストを構築してきていた、データーとリスト構築子の並びの右端に[]が配置される。これにより、結果リストが完成されて、このリストが返り値として返される。


 以上のような、パターン・スタイル、が、Haskell言語でのプログラミングの代表的なバリエーションであると思われる。