珟実䞖界のHaskell

このブログはすでにHaskell蚀語自䜓に぀いお倚くのこずを曞いおおり、その実甚的な応甚に関するいく぀かの蚘事がありたす。 ここで、本番環境での蚀語のもう1぀の実際の応甚に぀いお、感動的に語りたす。



デヌタの説明



私はテレコムで働いおいたす固定電話、むンタヌネット、IPテレフォニヌ。 タスクには、PBX、IPテレフォニヌサヌバヌからのトラフィックの凊理、珟圚の課金のサポヌト、゜フトりェアの䜜成、ネットワヌク、ドメむンの管理が含たれたすはい、「すべおの取匕のゞャック」。 最近、組織に新しい機噚がむンストヌルされ、たったく異なる原理で動䜜し、それに応じおトラフィックも倉曎されたした。 珟圚、叀いステヌションず新しいステヌションの2぀のバヌゞョンがありたす。 新しいトラフィックは、バむナリ圢匏ずテキスト圢匏で耇補されたす。 バむナリはたったく私たちには向いおいたせんただし、䞀郚の人は知らないうちに「なぜそこにいるのですか。それを自分で請求したす」。 叀い機噚からのトラフィックはただ進行䞭であり、ロヌカルDBMSのサヌビスを䜿甚しお、確立されたスキヌムを䜿甚しおパヌトナヌず䞀緒に投げおいたす。 新しいステヌションからのトラフィックに同じサヌビスを蚭定するこずは可胜ですが、いく぀かの機胜が発芋されたした。 新しいステヌションのトラフィックは、15分ごずに個別のファむルに曞き蟌たれたす。 合蚈で、月に玄3,000個のそのようなファむルが取埗されたす理想的には-31日間で2976、30日間で2880。 狂気のために、各ファむルを個別にむンポヌトするこずはありたせん。 それらはすべおテキストであり、コヌルレコヌドは行ごずに配眮されるため、それらを1぀にマヌゞするこずも可胜です。 手動マヌゞは次のようになりたす。先月のみファむルを遞択し、コマンドラむンで最も単玔なマヌゞスクリプトに远加したす。 ファむル名の圢匏は固定されおいるため、合䜵を自動化できたす。幎ず月を解析するだけです。 Linuxは、 Bash 、 Perl、たたはPythonのいずれかを䜿甚したす。安䟡で陜気ですが、1回の操䜜でWindowsマシンにむンストヌルするのではなく、 PowerShellでも同様です。 そしおcmdは、私たちがよく知っおいる倒錯です。 ;最埌に、DBMSツヌルを䜿甚しおマヌゞおよびむンポヌトした埌でも、倚くの手動SQL䜜業が必芁だったため、トラフィック自䜓にも驚きが芋぀かりたした。 䞀般に、これらの芁因はどうにかしおHaskellのタスクにうたく発展したした。そのずき2011幎4月から5月、研究を始めたした。



したがっお、1か月あたり玄3,000の15分ファむル。 機噚では、間隔を倉曎し、15分ではなく、5、10、30、45などの他の倀を蚭定できたす。ファむルの数ずサむズはそれぞれ倉曎されたす。 ファむル名の䟋2011幎5月9日09:30:00



  999992011050909300081.txt
 99999-機噚に瞫い付けられた識別子明らかな理由により、私はそれを9぀に眮き換えたした
      2011-幎
          05-月
            09-日
              09-時間
                30分
                  00-秒
                    81-おそらく10分の1秒の乱数。 




サブスクラむバヌが増え、各ファむルのサむズは着実に増加しおいたす。 珟圚、ファむルごずに平均240行がありたすが、倏季の季節的な沈䞋があり、䌑暇に出かけた人は少なくなりたした。 9月には、1.5〜2倍のアクティビティの増加が芋蟌たれたす。



通話蚘録は倚様です。 フィヌルドの数が異なるレコヌドにはいく぀かの異なるタむプがありたす。 R210のような゚ントリは非垞にたれであり、その意味を理解できたせんでした以降、トラフィックデヌタはランダムデヌタに眮き換えられたす。



  | R210 | 2011-06-24 214353 | 2011-06-24 014352 | 1 | 




ご芧のずおり、レコヌドタむプ識別子、開始日、終了日ISO 8601 / SQL圢匏、および䜕らかの理由で1぀のフィヌルドのみがありたす。 フィヌルドは、レコヌドの先頭ず末尟にある垂盎バヌで区切られおいるため、実際にはさらに1぀のフィヌルド、぀たり5がありたす。むンデックス0のフィヌルドが最初の垂盎バヌの前にあるず想定するず䟿利です。 その埌、重芁なフィヌルドのカりントダりンは1から始たりたす。



通垞の呌び出しは、R200などのレコヌドに蚘録されたす。 すでに152のフィヌルドがあり、これは機噚䞊で再構成できたす。䞀郚のフィヌルドを远加、䞀郚を削陀、その他はフォヌマットを倉曎できたす。



  | R200 | 99999 | 111111 | CR、CS、AM | 1 | 1 | 3022 | 222222 | 333333 ||| 2011-06-23 113358 | C | 2011-06-23 113422 | S | 0 | 16 | 1 |||||| 1 | 1 |||||| 3 | 162 | 17 | 1 | 12 | 24 ||||||||||| 16 | 0 |||| || 192.168.1.172 || 192.168.1.12 |||||| 8 | 8 | 20 | 20 | 64 | 64 | 20 | 0 | 0 | OS | 7777 | 8888 | 555555 | 666666 | 0 | 8 | 9 | ||| OS | 19 ||| 30 | 10 | 42 | 43 |||||||||||||| 1 ||||||| 1 | 1 | 0 | 3 || 222222 ||||| || 2 | 1 || 333333 ||||||||||||||||||||||||||||||||||||| 




むンデックス[ 7、8、9、12、14、36、112、122 ]のフィヌルドに興味があり、最終的には、DBMSにむンポヌトしすぎないように、䞍芁なものをすべお陀倖したいず思いたす。 生デヌタから必芁なものだけを遞択するず、次の行が埗られたす。



 蚘録3022 | 222222 | 333333 | 2011-06-23 113358 | 2011-06-23 113422 | 24 | 222222 | 333333
むンデックス7 | 8 | 9 | 12 | 14 | 36 | 112 | 122

むンデックス| 説明
 ---------------------------
 7 | 知倚の垂倖局番
 8、112 | 発信番号
 9、122 | 着信番号
 12 | 䌚話の開始日時
 14 | 終了日時
 36 | 秒単䜍の通話時間 




他のすべおのフィヌルドは特に必芁ありたせん。 あなたが芋るように、いく぀かは䞀般に空ですが、他の意味は䞍明です。 倉曎されたIPアドレスが、RTPトラフィックが通過する電話ネットワヌクむンフラストラクチャの2぀のボヌドに属しおいない限り。 これらのフィヌルドをむンポヌトするこずにより、ボヌドの負荷を調査できたす。 おそらく将来的には䟿利になるでしょう。



トラフィック゚ントリは1行ず぀タむトです。 他の皮類のレコヌドもあるかもしれたせんが、それらは私たちにずっお興味深いものではありたせん。 関皎に぀いおは、タむプR200のレコヌドのみで十分です。 しかし、トラフィックの芖芚的な調査䞭に、別の興味深い事実が明らかになりたした。 コヌルは、同じ番号から開始され、同時に開始されるこずもありたすが、通話時間は異なりたした。 最初は、情報が䞍完党な状況で、ある皮のグリッチだず思った。人が同じ番号から同時に電話をかけるこずはできないからだ。 その埌、パタヌンが芋え始め、最終的に、私は問題が䜕であるかを理解したした。 1぀の電話番号に察するこのようなレコヌドの䟋を次に瀺したす。わかりやすくするために、䜙分なフィヌルドはすべお捚おたした。



  | 7 | 8 | 9 | 12 | 14 | 36 |
 | 3022 | 222222 | 333333 | 2011-05-23 130754 | 2011-05-23 133754 | 1800 |
 | 3022 | 222222 | 333333 | 2011-05-23 130754 | 2011-05-23 135940 | 3106 |

 | 3022 | 444444 | 555555 | 2011-05-23 145352 | 2011-05-23 152352 | 1800 |
 | 3022 | 444444 | 555555 | 2011-05-23 145352 | 2011-05-23 155352 | 3600 |
 | 3022 | 444444 | 555555 | 2011-05-23 145352 | 2011-05-23 160050 | 4018 |

 | 3022 | 666666 | 777777 | 2011-05-23 191555 | 2011-05-23 194554 | 1800 |
 | 3022 | 666666 | 777777 | 2011-05-23 191555 | 2011-05-23 201554 | 3600 |
 | 3022 | 666666 | 777777 | 2011-05-23 191555 | 2011-05-23 204554 | 5400 |
 | 3022 | 666666 | 777777 | 2011-05-23 191555 | 2011-05-23 204717 | 5483 | 




塩が䜕であるかを芋るこずができるようになった埌、これらの蚘録を䜕千もの類䌌の蚘録の䞭、䜙分なフィヌルド、文字、数字の䞭から芋぀けるのは簡単ではありたせんでした。 䞀般的に、それは運、盎芳、たたは魔法のいずれかでした。 :)そしお答えは簡単です。30分1800秒ごずの機噚は、䜕かが起こった堎合に「䌚話のマむルストヌン」をマヌクしたす。 䌚話の最埌の29分が䜕らかの圢で倱われたずしおも、信頌性を高めるために、過去3時間の䌚話党䜓が䜕床も蚘録されたした。 最埌のレコヌドには、実際のデヌタがありたす。 おそらく、機噚では、マむルストヌンの期間を䜕らかの方法で倉曎できたすが、これたでのずころ、その数は次のようになっおいたす。 [1800、3600、5400、7200、9000、9000以䞊。]結果レコヌドのフィヌルド。 おそらく、䞍必芁なものをより良い方法で陀倖するために、将来これを怜蚎する䟡倀があるかもしれたせんが、今のずころ、このシリヌズの期間を持぀すべおのレコヌドを単玔に砎棄するこずにしたした。 理論的には、ここでは通垞の通話の䞀郚が倱われたすが、このためには、1秒に正確に30分間話す必芁がありたす。 この確率は非垞に小さく、ボリュヌムはそれほど倧きくないので、倧きな数の法則が䜕らかの圢でサンプルに圱響を䞎えたす。 この珟象を創造的に「1800秒の問題」ず呌びたした。



プログラムに぀いお簡単に



合蚈で、必芁なファむルをマヌゞしお有甚な情報を陀倖する4぀のプログラムを䜜成したした。 最初は、パヌサヌずマヌゞャヌでした。2぀のプログラムは別々に䜜成され、マヌゞず呌ばれる3番目のプログラムに結合されおいたした。 それらはすべお非垞にゆっくりず動䜜し、倚くのメモリを消費したしたが、タスクに察凊したした。 500 MBのメモリが消費されおいる状態でスワップが機胜したため、さらに10分間1か月のデヌタ240行の3000ファむル= 720000行を凊理できたした。 ずおも、ずおも怖い結果です。 たた、タスクは月に1回行う必芁がありたしたが、私のパヌトナヌはHaskellを軜したした。 確かに、 Haskellはそれずは䜕の関係もありたせん。 私はプログラムでこれを初心者の機胜䞻矩者の倚くの兞型的な間違いにしたが、これは曲がったアルゎリズムが非垞にひどくうたく機胜しなかったためである。 しかし、圌らは働いた さらにプログラムおよびそれらの最倧のものはわずか150の有甚な行を取るは、コマンドラむンから蚭定するこずができたす。 利甚可胜な機胜は次のずおりです。



1.パラメヌタヌなしのモヌドで䜜業したす。 デフォルトではフィヌルドが取埗され、先月のファむルが取埗されたす。

2.パラメヌタヌを䜿甚したモヌドで䜜業したす。

-フィヌルド[<フィヌルドむンデックスのリスト>]-取埗するフィヌルドパヌサヌフィヌルド[1、24、55];

-yyyy、mm-凊理する月合䜵2011、5;

-W-「埅機」ずいう蚀葉からワヌクアりト埌にコン゜ヌルりィンドりを閉じないでください。

3.結果は3぀のファむルでした。

-yyyy.mm.txt-今月の生のトラフィックを持぀すべおのファむルがマヌゞされたした。

-processed.txt-今月に必芁なフィヌルドのみを含むファむル。

-yyyy.mm.txt.log-生の未加工ファむルがリストされ、芁玄情報が曞き蟌たれるログファむル行数、ファむル、日付範囲。

4.プログラムは、凊理されたトラフィックの統蚈ず䟋を衚瀺したす。



䜕回か䜿甚したしたが、もちろん、プログラムを最初から曞き盎したした。 叀いコヌドには、倚くの曲がったコヌド、䞍必芁な自転車、愚かなアルゎリズム、奇劙な解決策がありたした。 その結果、同じ機胜ず同じデヌタセットを持぀4番目のプログラムNgnParserは10分間ではなく10秒間動䜜し、10 mbのメモリしか消費したせん。 速床では、差はほが2桁で、少なくずも1぀はメモリにありたす プログラムを遅くするような混乱は䜕だったのでしょうか 私ず同じ熊手を螏んだ人がいるず思いたす。曲がった手よりも蚀語の遅れを信じおいる人がいるず思いたす-理由もなく、理由もなくむンタヌネット䞊で非垞に倚くの悲鳎がありたす... Haskellは玠晎らしい蚀語です。 これらのプログラムを曞くのは簡単でした。 それぞれに぀いお、2営業日しかかかりたせんでした。 そしお、私は倚くの喜びを埗たたびに。 Cで同じこずをした堎合、どれほどの苊痛があるか想像できたせん。



最初のプログラム



最初のプログラムは簡単なものから始めたした。 ずりあえず、コマンドラむンツヌルを䜿甚しお必芁なファむルを1぀merged.txtにマヌゞしたした。パヌサヌのタスクは、トラフィックを解析し、識別子R200で必芁なレコヌドを陀倖するこずでした 倧量のテキストを凊理するには、特別なByteString文字列型を䜿甚する方が䟿利です。 ただし、通垞の文字列型ほど䟿利ではありたせん。 ByteStringが文字列自䜓の最適化された実装である堎合、 Stringは文字のリスト、぀たり[Char]です。 Stringはその性質䞊非垞に䟿利ですが、ビッグデヌタではリストのパフォヌマンスが倧幅に䜎䞋したす。 プログラムの最初のバヌゞョンでは、匷力なブレヌキず倧きなメモリのむさがりを匕き起こしたのは、 文字列ずいく぀かの愚かな決定でした。 しかし、その埌、生産性ではなく、開発の速床が心配になりたした。 プログラムのプロトタむプを非垞に迅速に䜜成したした。 倖芳は次のずおりですリビゞョン2



replaceChars :: Char- > Char- > Char- > Char

replaceChars whatC withC c = if c == whatC then withC else c



interestFields :: [ 文字列 ] -> [ Int ] -> [ 文字列 ]

interestFieldsのtakeWhat = undefined- スタブ



isR200 :: [ 文字列 ] -> Bool

isR200 s =  head s  == "R200"



processLine :: 文字列 -> 文字列

processLine s = if isR200 sInWords then unwords  interestFields sInWords [1、2、3]  else [ ] -[1,2,3]-フィヌルドのテスト

ここで、 sInWords = words  map  replaceChars ' | ' ''  s 



processString :: 文字列 -> [ 文字列 ]

processString s = map processLine  lines $ s 



メむン:: IO  

メむン= 行う

str < -readFile "merged.txt"

putStrLn 挿入" \ r \ n "  processString $ str  




タむプChar- > Char- > Char- > Charの replaceCharsずいう奇劙な関数にすぐに気付くこずができたす。 アむデアはこれでしたラむンレコヌドを取り、垂盎バヌ「|」を眮き換えたす スペヌスを䜿甚し、 words関数を䜿甚しお文字列を単語に分割したす。



sInWords = words  map  replaceChars ' | ' ''  s 




sInWordsを実行した結果、次の倉換が埗られたす。



"| R200 | 99999 | 111111 | CR、CS、AM | 1 | 1 | 3022 | 222222 | 333333 ||| 2011-06-23 113358 |" ->

「R200 99999 111111 CR、CS、AM 1 1 3022 222222 333333 2011-06-23 11:33:58」 ->

[ 「R200」 、 「99999」 、 「111111」 、 「CR、CS、AM」 、 「1」 、 「1」 、 「3022」 、 「222222」 、 「333333」 、 「2011-06-23」 、 「 11:33:58 " ]




残念ながら、日時フィヌルドも2぀の別々のフィヌルドに分割されたす。 埌で、これを回避するために、蚭蚈をさらに耇雑にし、最初にこのフィヌルドのスペヌスをアスタリスクに眮き換えおから返したす。 その結果、ラむンレコヌドはさらに倚くの倉換を経たした。



眮換 ''-> '*';

「|」を眮き換える -> '';

単語機胜による単語分割。

フィヌルドのリストを凊理し、目的のむンデックスを持぀フィヌルドを取埗したす。

unwords関数を䜿甚しおフィヌルドをマヌゞしたす。

眮換 ''-> '|'

眮換 '*'-> ''



さらに、これらのひどい倉換の埌、受信したフィヌルドの数は゜ヌスフィヌルドの数ず䞀臎したせんでした。空のフィヌルドが最終的に完党に消えたためです䞊蚘の倉換䟋を参照。 すべおのレコヌドで、空のフィヌルドたたは塗り぀ぶされたフィヌルドが同じ堎所にあるず䟿利です。そうしないず、䞍快なアヌティファクトが発生したす。 ご芧のずおり、コヌドは冗長であるだけでなく、芋苊しいものです。 私はその挞近的な耇雑さを評䟡するこずすら想定しおいたせん。 圌女はOn ^ 2をはるかに超えおいたず思いたす。 さらに、リビゞョン12に近づくず、二重の瞊線が原因で倱われたフィヌルドに察しお䜕かを行う必芁があるこずに気付きたした。 そしお別の倉換を远加したした



-「||」ずマヌクされた空のフィヌルドが正しく凊理されるように、それらの間にスペヌスが挿入されたす。

refieldDoubles :: 文字列 -> 文字列

refieldDoubles [ ] = [ ]

refieldDoubles  ' | ' [ ]  = "|"

refieldDoubles  ' | ' ' | 'ss  = "| |" ++  refieldDoubles  ' | 'ss  

refieldDoubles  s [ ]  = [ s ]

refieldDoubles  sss  = s  refieldDoubles ss 




したがっお、各行に別の完党なパッセヌゞを远加したした たあ、真実-猿の劎働。 ただし、かなりの䜜業を行う必芁がありたした。 代わりにData.String.Utilsモゞュヌルのsplit関数を䜿甚するか、独自のバヌゞョンを䜜成したす。 次に、レコヌド行に沿っお1回パスするだけで、正しいフィヌルドの内蚳を取埗できたす。



「|」を分割したす "| R200 | 99999 | 111111 | CR、CS、AM | 1 | 1 | 3022 | 222222 | 333333 ||| 2011-06-23 113358 |" ->

[ "" 、 "R200" 、 "99999" 、 "111111" 、 "CR、CS、AM" 、 "1" 、 "1" 、 "3022" 、 "222222" 、 "333333" 、 "" 、 "" 、 "2011-06-23 11:33:58" 、 "" ]


私は䜕を蚀うこずができたす... facepalm蚀葉ではなく、感情だけ。



どのような改善が可胜ですか



経隓豊富なHaskellistsは、コヌドがばらばらのスタむルを䜿甚しおいないこずにすでに気づいおおりその時点ではただ感じおいたせんでした、ケヌスの構築、サンプルずの比范、たたはあらゆる皮類のセキュリティ衚珟はほずんどありたせん。 結果ずしお、そのような単玔なコヌドでさえ、堎所で読むこずは困難です。 いく぀かの関数を曞き留めるより良い方法は次のずおりです。



replaceSymbols s = map  replaceChar ' | ' ''   map  replaceChar '' ' * '  s 

-->

replaceSymbols = map  replaceChar ' | ' ''。replaceChar '' ' * ' 





isR200 s =  head s  == "R200"

-->

isR200  "R200"  _  = True

isR200 _ = False





replaceChars whatC withC c = if c == whatC then withC else c

-->

replaceChars whatC withC c | c == whatC = withC

| それ以倖の堎合 = c





processLine s = if isR200 sInWords then unwords  interestFields sInWords [ 1、2、3 ]  else [ ]

ここで、 sInWords = words  map  replaceChars ' | ' ''  s 

-->

processLine s | isR200 sInWords = unwords  interestFields sInWords [ 1、2、3 ] 

| それ以倖の堎合 = [ ]

ここで、 sInWords = words 。 map  replaceChars ' | ' ''  $ s





processString s = map processLine  lines $ s 

-->

processString = map processLine 。 行




通垞、 むンタヌカレヌト関数"\ r \ n"はunlinesに眮き換える必芁がありたした。 より短く、より明確になり、テストでは、 unlinesは優れたパフォヌマンスを瀺したした-少なくずも30



  ItemsCnt testUnlinesnstestIntercalatensパヌセント
 10 23.84 34.05 29.9
 100 22.70 34.62 34.4
 1000 23.28 35.48 34.3
 10000 22.17 35.48 37.5
 50,000 22.13 33.26 33.4
 100000 21.06 35.47 40.6
 200000 22.70 34.05 33.3 




しかし、ただ経隓の浅い間、私は䞍芁な自転車をブロックしたため、 プレリュヌドモゞュヌルからでも暙準機胜を知りたせんでした。 ただし、今でも、最小限の劎力で、芁玠のリストから目的のむンデックスを持぀芁玠を遞択する方法を実際には理解しおいたせん。 叀いプログラムず新しいプログラムのコヌドを比范したす。



-叀いコヌド

-匕数1-生の食品のリスト

-匕数2-必芁なむンデックスのリスト

takeInterest :: [ 文字列 ] -> [ Int ] -> [ 文字列 ]

takeInterest _ [ ] = [ ]

takeInterest ss  nns  = [ ss !! n ] ++ takeInterest ss ns



-新しいコヌド

-匕数1-バッテリヌ

-匕数2-必芁なむンデックスのリスト

-匕数3-生フィヌルドのリスト

collectFields :: Int- > [ Int ] -> [ 文字列 ] -> [ 文字列 ]

collectFields _ _ [ ] = [ ]

collectFields idx fis  sss  | idx ` elem` fis = scollectFields  idx + 1  fis ss

collectFields idx fis  sss  | それ以倖の堎合 = collectFields  idx + 1  fis ss




最初のケヌスでは、むンデックスのリストを反埩凊理し、安党でない関数を䜿甚しお芁玠を匕き出したす!! 。 2番目のケヌスでは、バッテリヌを䜿甚しおフィヌルドのリストを繰り返し凊理し、むンデックスのリストでバッテリヌが芋぀かった堎合、コレクションのバッテリヌむンデックスで珟圚のフィヌルドを取埗したす。 そこず末尟の再垰がありたす。 ただし、テストによるず、新しいコヌドは40遅くなりたした。 おそらくこの堎合、叀いコヌドを返す䟡倀がありたす。新しいプログラムをさらに高速化するためです。



  ItemsCnt takeInterestnscollectFieldsnsパヌセント
 10 17.33 36.84 52.9
 100 20.58 36.84 44.1
 1000 21.67 37.92 42.8
 10000 21.13 36.84 42.6
 50,000 21.67 37.92 42.8 




進む



次のプログラムである合䜵は、先月のすべおのテキストファむルを1぀に結合するこずになっおいたす。 毎回手動でやりたくはありたせんでした。ご存知のように、怠lazは進歩の原動力です。質問が発生したした先月の予定をどうやっお知るのですか はい、䞀般的には簡単です。珟圚の日付を取埗しお1か月を差し匕きたす。 このプログラムは月の初めにのみ䜿甚されるこずになっおいたため、問題は予想されたせんでした。 珟圚の日付ず、䞀般に日付ず時刻の操䜜は、 時間モゞュヌルにありたす。 ファむルシステム構造の操䜜は、 System.Directoryモゞュヌルにありたす。 どちらの関数も䞻にIOモナドで機胜したすが、その時点ではただモナドコヌドを組み合わせる方法を実際には知りたせんでしたが、最終的には䞍気味に芋えたすマヌゞ、リビゞョン14。



メむン:: IO  

メむン= 行う

args < -getArgs

curDir < -getCurrentDirectory

dirContents < -getDirectoryContents curDir

curTime < -T. getClockTime

monthAgoTime <- $ Tを返したす 。 addToClockTime  T. TimeDiff 0  -1  0 0 0 0 0  curTime

calendarMonthAgoTime < -T. toCalendarTime monthAgoTime

倚分DateRange = case args

 ab _  -> readDateRange  unwords [ a 、 b ] 

_- >ちょうど$ defaultDateRange calendarMonthAgoTime

ケヌス maybeDateRange of

ただdr- > do

let fsToMerge = filesToMerge dirContents dr

let fsToMergeCountStr = $ length fsToMergeを衚瀺

let mergeLog =  newFileName dr ++ ".log" 

dateRangeMsg = "DateRange" ++に drを衚瀺 させたす

fsContents < -merge fsToMerge

writeFile  newFileName dr   fsContentsの展開

writeFile mergeLog  fsToMerge ++ printfのむンラむン化を解陀したす \ n s \ n合蚈ファむルs " dateRangeMsg fsToMergeCountStr 

putStrLn  fsContentsのむンラむン化を解陀

putStrLn dateRangeMsg

--putStrLn "マヌゞするファむル" ++ fsToMergeのむンラむン展開を解陀

putStrLn  printf "ファむルの数s。ファむルリストに぀いおはsを参照しおください。" fsToMergeCountStr mergeLog 

䜕もない-> putStrLn  "無効な日付範囲。" 




このコヌドが䜕をするのか、私もそれに入るこずはお勧めしたせん...しかし、叀いプログラムの最新バヌゞョンが膚倧な量のメモリを食い蟌んだため、将来の巚倧な゚ラヌの芜が忍び蟌んだのはここです。 このコヌドから呌び出されるマヌゞ関数を考えおみたしょう。



merge :: [ 文字列 ] -> IO [ 文字列 ]

merge fsToMerge = mapM readFile fsToMerge




制埡するファむルのリストを受け取り、それらを読み取り、その内容のリストを返したす。 コヌドには次の2行がありたす。



する

...

fsContents < -merge fsToMerge

writeFile  newFileName dr   fsContentsの展開

...




重芁な点は、 fsContentsのむンラむン展開です。 ぀たり、すべおのファむルのすべおの生のコンテンツが1回にたずめられ、結果ファむルに詰め蟌たれたした。 埌に、パヌサヌず合䜵が組み合わされたずき、パヌサヌパヌツの凊理に転送されたのはこの膚倧な量のデヌタでした。芚えおいるのは、倧量の眮換、パス、その他のオヌバヌヘッドがあるこずです。 パヌサヌずマヌゞからコンパむルされた叀いプログラムでは、このコヌドは次のようになりたす。



する

...

fsContents < -readFilesToMerge fsToMerge

let mergedContents = fsContentsのラむンを解陀したす

writeFile  newFileName dr  mergedContents

let writtenContentStr = unlines $ processData nedeedFields mergedContents

...




そしお、それは単に面倒なだけでなく、デヌタフロヌの抂念に察する重倧な違反です。 次のようになりたす。



 スキヌム1
        _____ _____ _____ 
       |  |  |  |  |  |
 A1-> | FA1|  -> B1-> | GB1|  -> C1-> | HC1|  ->結果1->保存
       | _____ |  | _____ |  | _____ |

        _____ _____ _____
       |  |  |  |  |  |
 A2-> | FA2|  -> B2-> | GB2|  -> C2-> | HC2|  ->結果2->保存
       | _____ |  | _____ |  | _____ |

        _____ _____ _____ 
       |  |  |  |  |  |
 A3-> | FA3|  -> B3-> | GB3|  -> C3-> | HC3|  ->結果3->保存
       | _____ |  | _____ |  | _____ |


 ...-> ...-> ...-> ...-> ...-> ...->結果n->保存 




そしお、それは次のようになりたした



 スキヌム2
     ____________________ ____________________ ____________________
    |  |  |  |  |  |
    |  |  |  |  |  |
    |  |  |  |  |  |
 A-> |  FA|-> B-> |  GB|-> C-> |  HC|->結果->保存
    |  |  |  |  |  |
    |  |  |  |  |  |
    | ____________________ |  | ____________________ |  | ____________________ | 




郚品の順次凊理ずすべおの凊理の違いをすぐに感じたした。 今、私は知っおいたす䞀床にすべおの䜜業を行う䟡倀はありたせん。デヌタをバッチで発行するメカニズムを䜜成する方がよいでしょう。メモリ内で効果的か぀高速になりたす。 そしお、他のすべおに加えお、2番目のスキヌムに埓っお䜜成されたプログラムは䞊列化できたせん。 ホラヌ、䞀蚀で蚀えば...



埌で、ほずんどすべおの堎所でStringをByteStringに眮き換えお、叀いプログラムを最適化しようずしたした。 もちろん、ゲむンはわずかでしたが、霧のファンを分散させるこずはできたせん。



蚀うたでもなく、私は新しいプログラムであるNgnTrafficを䜜成したした。NgnTrafficはすでに通垞の知識を持っおいたすか 倚くのこずが芳察されおいたす。 プログラムは、 Main 、 Constants 、 DataProcess 、 FileListProces 、 Options 、 Tools 、 Typesのモゞュヌルに分かれおいたす。 もちろん、スキヌム1が䜿甚され、 文字列の代わりに、 ByteString型厳密なバリアントが最初に䜿甚されたした。 コヌドは、手間がかからないスタむルでさえ、よりコヌミングされおいたす。 そしお、最も重芁なこずは、行動の原則が倉わったこずです。 最初に、凊理されるファむルのリストが収集されたす-この郚分は、叀いプログラムの類䌌郚分に䌌おいたす。 ただし、ファむルは䞀床に1぀の倧きな倉数に読み蟌たれるのではなく、それぞれ個別に読み蟌たれたす。 その内容はすぐに凊理され、行が解析され、レコヌドがフィルタリングされ、必芁なフィヌルドが取り出されたす。 結果は結果ファむルに远加されたす。 その結果、「ディスクからの読み取り FAn -文字列の凊理 GBn -ディスクぞの結果の曞き蟌み HCn 」ずいうサむクルがあり、ファむルの数が非垞に倚くなりたす。 さらに、もちろん、ある文字を別の文字に眮き換えるこずはありたせんが、 Data.ByteString.Char8モゞュヌルの単玔な分割関数がありたす。これは、レコヌド文字列を1぀のパスでフィヌルドに分割し、それ自䜓がパフォヌマンスの途方もないシェアを獲埗したす。 スキヌム1を満たす関数は次のずおりです。



プロセス ' :: ResFilePath- > FieldIndexes- > FilePath- > IO  

プロセスのresFile fis targetFile = do

fileContents < -C. readFile targetFile

let processResult = processData fis述語fileContents

C. appendFile resFile processResult



プロセス:: ResFilePath- > [ FilePath ] -> FieldIndexes- > IO String

process _ [ ] _ = 「凊理するファむルがありたせん」を 返す

プロセスresFile fs fieldIndexes = do

C. writeFile resFileC 。 空っぜ

mapM_ プロセスのresFile fieldIndexes  fs

「すべおOK」を 返し たす




ここで、processは結果ファむルの名前、凊理するファむルのリスト、および必須フィヌルドのむンデックスを取りたす。 むンデックスはコマンドラむンから取埗できるため、この関数の匕数ずしお出力されたす。 processは、各ファむル名を順番に取埗し、関数process 'を適甚したす行mapM_process' resFile fieldIndexesfs 。 圌女はすでにファむルの䞻な仕事をしおいたす。 processData関数は、ファむルのコンテンツの凊理を行いたす。 R200レコヌドのフィルタリングに加えお、新しいプログラムで述語メカニズムが䜜成されたこずは興味深いこずです。どのレコヌドも利甚可胜な述語ず照合でき、述語自䜓に必芁なものを簡単に远加できたす。 これたでに2぀のこずが行われたした。むンデックスxのフィヌルドは、フィヌルドのリストに属し、そのリストに属しおいたせん。 これは、述郚のタむプです。



デヌタ述語= NotInList [ C. ByteString ]

| InList [ C. ByteString ]

タむプ PredicateMap = [  FieldIndex 、 Predicate  ]




そしお、これらは䞎えられた定数述語です



述語:: PredicateMap

述語= [  1 、 InList [ C. pack "R200" ]  、

 36 、 NotInList  map C. pack [ "1800" 、 "3600" 、 "5400" 、 "7200" 、 "9000" 、 "10800" 、 "12600" 、 "14400" 、 "16200" 、 "18000" 、 「19800」 、 「21600」 、 「23400」 ]   ]




フィヌルド1が「R200」に等しく、フィヌルド36が「問題1800秒」のリストにないレコヌドのみが述語を満たすず掚枬するのは簡単です。 スクリヌニングはcheckPredicateおよびexamineFields関数で発生したす 。



checkPredicate ::述語-> C ByteString- > Bool

checkPredicate  NotInList l  str =  not。elem str  l

checkPredicate  InList l  str = elem str l



examineFields :: Int- > PredicateMap- >フィヌルド-> Bool

examineFields _ _ [ ] = True

examineFields idx preds  sss  = case L. ルックアップ IDX

ちょうどpred ->  checkPredicate pred s  &&  examineFields  idx + 1  preds ss 

なし-> examineFields  idx + 1  preds ss




examineFields関数はfoldrを介しお実行できるこずがわかりたしたが、それはささいなこずです。



䞀般的に、完了した䜜業に満足しおいたす。 これはたれです-しかし、私はNgnTrafficコヌドが奜きです。 そしお、私は特に、異なるプログラムで、異なる時間に、異なる知識で曞かれた同じプログラムの䟋で目に芋えるようになった進歩が奜きです。 「さらに特別な」私は、これが実際の生産で䜿甚されるHaskellの実際のプログラムであるずいう事実が奜きです。



圌らが䜕ず蚀っおも、 Haskellは玠晎らしい蚀語であり、私がこれたで働いた䞭で最高のものです。



リビゞョンのパヌサヌ゜ヌス [2] 、 [6] 、 [12]

合䜵の゜ヌス合䜵統合プログラムを含む [13] 、 [14] 、 [16] 、 [23]

NgnTrafficの゜ヌス



PS Yet Another Monad Tutorialの次の郚分の翻蚳も近日䞭に利甚可胜になりたす。

PPS同志steckのおかげでプログラムぞのリンクが修正されたした 。

PPPS同志のgoderずjack128のおかげで、いく぀かの゚ラヌを修正したした 。



All Articles