【C#】Enumの爆速ForEach

Enumをforeachで回すと遅いよと聞いて
自前で書いてみたら爆速ForEachができた

// 以下のenum定義を使用する
enum Days {Sat=10, Sun=20, Mon, Tue, Wed, Thu, Fri};

まず結果(1,000,000回を回す)

// 1回目
foreach : 1328ms
EnumExtension.ForEach : 1283ms
EnumExtension.ForEachList : 482ms
EnumForEach<T>.Exec : 56ms

// 2回目
foreach : 1370ms
EnumExtension.ForEach : 1357ms
EnumExtension.ForEachList : 635ms
EnumForEach<T>.Exec : 61ms

// 3回目
foreach : 1342ms
EnumExtension.ForEach : 1360ms
EnumExtension.ForEachList : 488ms
EnumForEach<T>.Exec : 62ms

実装詳細

foreach

普通のforeach

foreach(Days days in Enum.GetValues(typeof(Days))) {
}

EnumExtension.ForEach

Enum(自前)拡張メソッド

public static class EnumExtension
{
    public static void ForEach<T>(Action<T> action) where T : struct, IConvertible
    {
        var type = typeof(T);
        foreach(T v in Enum.GetValues(type))
        {
            action((T)v);
        }
    }
}
// 使い方
EnumExtension.ForEach<Days>( days => {} );

速度がほぼforeachと変わらない

EnumExtension.ForEachList

Enum(自前)拡張メソッド:Listキャスト版

public static class EnumExtension
{
    public static void ForEachList<T>(Action<T> action) where T : struct, IConvertible
    {
        var type = typeof(T);
        var values = Enum.GetValues(type).Cast<T>().ToList();
        var count = values.Count;
        for(int i = 0 ; i < count ; ++i) {
            action((T)values[i]);
        }
    }
}
// 使い方
EnumExtension.ForEachList<Days>( days => {} );

Enum.GetValues(...)してListにキャストする方が遅いと思いきや
まさかかかった時間は半分以下に、ランダムアクセス早い!

EnumForEach.Exec

ジェネリッククラス版:static変数を持ち、反則のチートしてる

public class EnumForEach<T> where T : struct, IConvertible
{
    static List<T> Values = Enum.GetValues(typeof(T)).Cast<T>().ToList();
    static int Count = Values.Count;
        
    public static void Exec(Action<T> action)
    {
        for(int i = 0 ; i < Count ; ++i) {
            action((T)Values[i]);
        }
    }
}
// 使い方
EnumForEach<Days>.Exec( days => {} );

爆速!かかった時間はforeachの20分の1以下

最後に

  • 測定方法が間違ってないかしら?怖いorz
  • 疑念点
    • foreachみたいにループ中のbreak脱出はできない
    • メモリ消費を考えてない(それほど心配ではないが)
  • 俺は普通のforeachを使う(常識を考え百万回を回さないよね)

[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

【C++】マジックインクリメント

PHPには文字列のインクリメントがあるらしく、C++で実装してみた

マジックインクリメントとは

(以下はネットで調べて理解したことです。間違ったら指摘していただければ幸いです)

英数字の文字列をインクリメントする

++"a" = "b"
++"0" = "1"

桁上げも可能です

++"az" = "ba"
++"19" = "20"

英数字だけの文字列だと

++"z9" = "aa0"
++"Z9" = "AA0"
++"9z" = "10a"
先頭の桁上げは最初の文字種類によって変わります。

英数字以外の文字を含む場合

++"Z!9" = "Z!0"
英数字以外の桁上げはしない

実装

#include <cctype>
#include <string>

std::string& operator++(std::string& s)
{
  if(s.empty()) {
    s = "1";
    return s;
  }
  auto begin = s.rbegin();
  auto end = s.rend();
  while(begin != end) {
    if(*begin == '9') {
      *begin = '0';
    } else if(*begin == 'z') {
      *begin = 'a';
    } else if(*begin == 'Z') {
      *begin = 'A';
    } else if(std::isalnum(*begin)) {
      ++(*begin);
      return s; // 桁上げの必要ありません
    } else {
      return s; // 英数字以外は終了する
    }
    ++begin;
  }
  // 桁上げ
  if(std::isdigit(s.front())) {
    s = "1" + s;
  } else if(std::islower(s.front())) {
    s = "a" + s;
  } else if(std::isupper(s.front())) {
    s = "A" + s;
  }
  return s;
}
std::string operator++(std::string& s,int)
{
  std::string tmp = s;
  ++s;
  return tmp;
}

使い方

#include <iostream>
int main()
{
  std::string s = "a";
  for(int i = 0 ; i < 26 ; i++) {
    std::cout << s++ << " ";
  }
}
// 出力
a b c d e f g h i j k l m n o p q r s t u v w x y z 

何かうれしいの?

連続の名前をアクセスしたいとき

#include <iostream>
int main()
{
  for(std::string s = "IMG_001a" ; s <= "IMG_001e" ; s++) {
    std::cout << s << std::endl;
  }
// 出力
IMG_001a
IMG_001b
IMG_001c
IMG_001d
IMG_001e

C++11のstd::iotaを使うとき(使うかしら...)

#include <iostream>
#include <numeric>
#include <vector>
int main()
{
  std::vector<std::string> v(5);
  std::iota(std::begin(v), std::end(v),std::string("IMG_001a"));
  for(const auto& s : v) {
    std::cout << s << std::endl;
  }
}
// 出力
IMG_001a
IMG_001b
IMG_001c
IMG_001d
IMG_001e

【C++】UTF8

UTF8文字列処理のメモです。

いまだにUTF8を使ってサーバーとやり取りしてます。
たまに、表示文字数をカウントしたり、文字数が多すぎる場合途中から"..."を表示したり
結構困ります。。。

C++11からstd::u32stringを追加されて意外と便利です。
std::codecvt_utf8と組み合わせば、もう怖くない〜

例えば

文字数をカウントしたり

#include <iostream>
#include <codecvt>

size_t strlen(const std::string& s)
{
    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> conv;
    return conv.from_bytes(s).size();
}
int main()
{
    std::string utf8 = u8"2015年1月24日";
    std::cout << "size:" << strlen(utf8) << std::endl; // size:10
}

文字列の一部分取得したり

#include <iostream>
#include <codecvt>

std::string substr(const std::string& s,size_t pos = 0, size_t len = std::string::npos)
{
    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> conv;
    auto u32string = conv.from_bytes(s); // UTF8 -> UTF32
    auto sub = u32string.substr(pos,len); // 部分取得
    return conv.to_bytes(sub); // UTF32 -> UTF8にして返す
}
int main()
{
    std::string utf8 = u8"𠮷野家牛丼";
    std::cout << substr(utf8,0,3) << std::endl; // 𠮷野家
}

サロゲートペアも無問題、めでたしめでたし〜

追記

2015/12/06
VisualStudio2015 では コンパイルエラーになると報告がありました
検証環境が用意できたので結果を報告します
結論から言うと、この記事のサンプルコードは問題ありません。

VS 2015 RC linker std::codecvt error
上記リンクによると、これはVisualStudio2015既知の不具合です
回避策として char32_t を int32_t に変更すればコンパイルが通ります

修正確認したらまた追記する

milkcocoaを使ってみた

この記事はmilkcocoa Advent Calendar 2014の13日目の記事です。

簡単にリアルタイム通信できると聞いて興味が湧いたので思わず参加しました。

とりあえず、作成したもの=>GO!

みんなに紹介したいこと

1つDataStoreを使用して異なるコマンドを送受信してみたので
その対応方法を書いてみたいと思います
※以下ソースコードはTypeScriptを使用しています。

それでは、説明していきましょう~

1.異なるコマンドを分岐するために列挙を定義する

enum Command {
  Born,   // 生まれる(キャラ作成)
  Alive,  // 生存しているアピール
  Move,   // 移動(クリックした座標を送信)
}

2.送信用パッケージ定義する

class Package {
  // cmd : パッケージコマンド
  // data : 追加データ(コマンド毎に異なる)
  constructor(public cmd: Command, public data: any) { }
}

3.送信ヘルパー関数

実行したいコマンド+必要なデータを1つオブジェクトにして送信しています

var ds = milkcocoa.dataStore("message");
function send(command: Command, data: any) {
  ds.send(new Package(command,data));
}

4.受信部

何か受信したら、data.valueから送信したオブジェクトを取得できます

var ds = milkcocoa.dataStore("message");
ds.on("send", (data:any)=>{
  var p = <Package>data.value; // オブジェクトをPackageにキャスト
  switch (p.cmd) {
    case Command.Born:
    // MEMO:誰か生まれた
    break;
    case Command.Alive:
    // MEMO:誰か生存しているアピールした
    break;
    case Command.Move:
    // MEMO:誰か移動した
    break;
  }
});

使用例:移動先の座標を送信する場合

send(Command.Move,{x:10,y:20});

最後に

書き終わって振り返ってみたら、

milkcocoa成分が少なすぎる、本当にいいのか・・

と思っちゃいます、これもmilkcocoaの強みと言えるでしょう。

駆け足ですが以上で終わります。ではでは~

おまけ

サンプルのソースコード=> DL
Visual Studio Community 2013で動作確認

milkcocoaのTypeScript用型定義ファイル=> GitHub

必要な方どうぞ~

3分でTypeScriptの定義ファイルの作成方法を覚えよう

この記事はTypeScript Advent Calendar 2014の9日目の記事です。

TypeScriptの定義ファイル(d.ts)の作成方法について書いてみます(自分用メモを兼ねて・・)
まだTypeScript歴は浅いので、間違ったことがありましたら指摘していただければ幸いです

説明に使用するJavaScriptライブラリ:milkcocoa

JavaScript1行でバックエンドを提供します」と面白そうなので、
このライブラリを使って説明していきます

簡単!?3分で作成方法を覚えられる

リファレンスが揃えれば、簡単に変換できます(全手動ですが・・・)

  • ステップ1:クラスのレイアウト作成
  • ステップ2:メソッド(&パラメータ)をコピペ
  • ステップ3:パラメータの型を決める

ステップ1:クラスのレイアウト作成

以下はレイアウトのテンプレートです

declare module <ライブラリ名> {
    export class <クラス名> {
        // ...メンバの実装...
    }
}
// 外部アクセスのために定義、なくても可
import <クラス名> = <ライブラリ名>.<クラス名>;

milkcocoaは以下三つクラスがあります。リファレンス

  • MilkCocoa
  • DataStore
  • Query

以下はテンプレートから変換したレイアウト

declare module milkcocoa {
    export class MilkCocoa {
    }
}
declare module milkcocoa {
    export class DataStore {
    }
}
declare module milkcocoa {
    export class Query {
    }
}
import MilkCocoa = milkcocoa.MilkCocoa;
import DataStore = milkcocoa.DataStore;
import Query = milkcocoa.Query;

ステップ2:メソッド(&パラメータ)をコピペ

MilkCocoaクラスのメソッド一覧

  • new MilkCocoa()
  • dataStore()
  • addAccount()
  • login()
  • logout()
  • getCurrentUser()

そのままコピペしましょう。(見やすいように整形しましょう

export class MilkCocoa {
  constructor(host);  //new MilkCocoa()の代わりにコンストラクタを使用する
  dataStore(path);
  addAccount(email, password, options, callback);
  login(email, password, callback);
  logout(callback);
  getCurrentUser(callback);
}

ステップ3:パラメータの型を決める

リファレンスを読みながらパラメータの型を決めていきます
※リファレンスを読んでも分からなかった場合、設定しなくても問題ありません

例1:new MilkCocoa(host)

引数:MilkCocoaのデータストアのホストへのURLをString型で渡します
戻り値:通信が確立しているMilkCocoaのインスタンスを取得することができます
※コールバック関数は引数に入れても入れなくても動作します

// コールバックを提供しない場合
constructor(host: string);
// コールバックを提供する場合
constructor(host: string, cb?: () => void);

例2:dataStore(path)

引数:データストアへのパスを指定できます
戻り値:指定したパスへのDataStoreオブジェクトを返します

dataStore(path:string) : DataStore;


説明は以上です
他のメソッドも同じようにひたすら作成していきましょう。

最後に

定義ファイルを読んだことある方にも知っていますが、いろんな書き方があります。
個人的覚えやすいと思う手順を紹介しました。

今回作成した定義ファイルはGitHubにアップしています。
作成時のご参考になるようでしたら幸いです。

ではでは~