C vs Python vs Ruby vs Haskell(ナンバリングdeベンチマーク)

前回の記事が一応の決着を見せたので、次に行こうと思います。
私が思ったよりもHaskellがずっと速かったようで、Haskellは実用的な言語ではないと言う風潮を払拭すると言う目標の前途は明るそうで何よりです。

さて、今回のお題はファイルを読み込んで、行毎に数字を割り振っていくナンバリング処理です。
Linux等のUNIX系OSではcatと呼ばれる便利なコマンドでファイルの中身を表示するだけでなく、オプションで指定すればナンバリングも出来る訳ですが、Windowsでのcatコマンドに対応するtypeコマンドは、ファイルの内容を表示する以外の機能を持ちません。
そこで、numberingコマンドを作ってしまえ!と言うのがHaskellでこのコマンドを作った発端なのですが、ついでにこの間の言語でも作ってみました。

そして、今日はmethaneさんから教えて頂いたスーパーpre記法とやらを使ってみたいと思います!!

まずはC言語

#include <stdio.h>

int main(int argc,char *argv[])
{
	FILE *fp;
	char c;
	int i, a;

	for(i = 1; i < argc; i++)
	{
		if((fp = fopen(argv[i], "r")) == NULL)
		{
			fprintf(stderr, "No such file or directory:\"%s\"\n", argv[i]);
			return 1;
		}
		else
		{
			printf("%s\n%d",argv[i],a = 1);
			while((c = fgetc(fp)) != EOF)
			{
				putchar(c);
				if(c == '\n')
				{
					printf("%d",++a);
				}
			}
			puts("\n");
			fclose(fp);
		}
	}
	return 0;
}

Python

import sys

for arg in sys.argv[1:]:
    print(arg)
    fin = open(arg, 'r')
    for a, line in enumerate(fin):
        print(a, line, end = '')
        print("\n")
    fin.close()

Ruby

ARGV.each do|fname|
	puts fname
	i = 0
	File.open(fname).each{|line| print i += 1, line }.close
	puts "\n" * 2
end

Haskell

import System.Environment

numbering = unlines.(map (\(x,y) -> show x ++ y)).(zip [1..]).lines

main = getArgs >>= \args -> mapM readFile args >>= return.map numbering >>= 
				(mapM_ (\(x,y) -> putStrLn $ x ++ "\n" ++ y)).zip args


では、各言語の実行時間をば・・・
読み込ませるファイルは、青空文庫からDL出来るテキストファイルを3つ。
タイトルは、吾輩は猫である・心・リスタートです。


C言語

difftime numbering_c wagahaiwa_nekodearu.txt kokoro.txt restart.txt
(表示が長すぎるため、実行結果は省略)

11.8566451s

Python

difftime python numbering.py wagahaiwa_nekodearu.txt kokoro.txt restart.txt
(表示が長すぎるため、実行結果は省略)

12.0017503s

Ruby

difftime ruby numbering.rb wagahaiwa_nekodearu.txt kokoro.txt restart.txt
(表示が長すぎるため、実行結果は省略)

11.8665878s

Haskell
difftime numbering_hs wagahaiwa_nekodearu.txt kokoro.txt restart.txt
(表示が長すぎるため、実行結果は省略)

11.8646347s


例によって、こうすればもっと速くなるよ!と言うコメント受け付けます。
しかし、すでに十分速いうえに誤差レベルの違いしかないですね・・・。

ちなみに、吾輩は猫であるは2372行。心は1595行。リスタートは2615行です。
今回は、fgetsを使っても誤差程度の差しか無かったので1行の長さを気にしなくて良いと言う利点からfgetcを使ってますが、C言語でfgetsを使う際には1行分のバッファが足りないと、行数が違って来ますので気を付けてください。