したがって、初心者にとっては、シンプルだが実際の例は〜2000ファイルプロジェクトです。 使用される環境変数のリストを作成する必要があります。 つまり、行 "getenv(...)"および "GetVariable(...)"(これがラッパーです)の出現箇所を見つけて、それらからパラメーターを削除します。
タスクは簡単で、ディレクトリトラバーサルさえ行わないC ++プログラムによって長い間解決されてきましたが、Unixの「find」を呼び出すだけで、マスクによってファイルのリストを生成し、リスト上のファイルをシャベルします。 2000ファイルでは、1つのスレッドで数秒間動作します。
さあ、アーラン。 ここで、シーケンシャルなファイルトラバーサルよりもカーリーなものをかき立てたいと思います。 MapReduceはトピックに関するものです-ファイルのリストを作成し、各ファイルを並行して分析し(Map)、見つかった変数名を蓄積し、最後に受信したすべてのエントリを処理します(Reduce)。この場合、各変数の出現回数をカウントします。
実際、私のコードは「 Programming Erlang 」の例を繰り返し、同じ本のphofs(並列高階関数)モジュールを使用しています。
-module(find_variables). -export([main/0, find_variables_in_file/2, process_found_variables/3]). -define(PATH, "/Projects/interesting_project"). -define(MASK, "\\..*(cpp|c)"). main() -> io:format("Creating list of files...~n", []), % . - % , . Files = filelib:fold_files(?PATH, ?MASK, true, fun(N, A) -> [N | A] end, []), io:format("Found ~b file(s)~n", [length(Files)]), F1 = fun find_variables_in_file/2, % Map F2 = fun process_found_variables/3, % Reduce % MapReduce benchmark, % . benchmark(fun() -> L = phofs:mapreduce(F1, F2, [], Files), io:format("Found ~b variable(s)~n", [length(L)]) end, "MapReduce"). benchmark(Worker, Title) -> {T, _} = timer:tc(fun() -> Worker() end), io:format("~s: ~f sec(s)~n", [Title, T/1000000]). -define(REGEXP, "(getenv|GetVariable)\s*\\(\s*\"([^\"]+)\"\s*\\)"). % Map. . find_variables_in_file(Pid, FileName) -> case file:open(FileName, [read]) of {ok, File} -> % . {_, RE} = re:compile(?REGEXP), % % . CallBack = fun(Var) -> Pid ! {Var, 1} end, find_variable_in_file(File, RE, CallBack), file:close(File); {error, Reason} -> io:format("Unable to process '~s', ~p~n", [FileName, Reason]), exit(1) end. % Reduce. . % MapReduce % , . % {VarName, 1}. VarName % , . % . process_found_variables(Key, Vals, A) -> [{Key, length(Vals)} | A]. % . find_variable_in_file(File, RE, CallBack) -> case io:get_line(File, "") of eof -> void; Line -> scan_line_in_file(Line, RE, CallBack), find_variable_in_file(File, RE, CallBack) end. % ( ), % CallBack % . scan_line_in_file(Line, RE, CallBack) -> case re:run(Line, RE) of {match, Captured} -> [_, _, {NameP, NameL}] = Captured, Name = string:substr(Line, NameP + 1, NameL), CallBack(Name); nomatch -> void end.
プログラムをビルドするには、 phofsモジュールが必要です 。 これは、特定のMapおよびReduce機能とは独立した、普遍的なものです。
念のため、Makefile:
target = find_variables all: erlc $(target).erl erlc phofs.erl erl -noshell -s $(target) main -s init stop clean: -rm *.beam *.dump
大きく。 すでに述べたように、C ++プログラムは、マシン上の「find」呼び出しの時間とともに1〜2秒間実行されます。 Erlang'eバージョンは約20秒動作します。 悪い? それはあなたがどのように見えるかに依存します。 各ファイルの分析が長くなる場合(つまり、プログラムがディレクトリのクロールではなくファイルの分析にほとんどの時間を費やす場合)、ファイルの数と分析の複雑さを増やす場合に、どのソリューションがより実用的かは完全には明らかではありません。
私はErlangが初めてなので、コードの批判に感謝します。
関連記事: