doridoridoriand’s diary

主に技術的なことを書いていく予定(たぶん)

C#のyieldに関して

まあなんか書くことなかったんだけど笑 C#のyieldを目にする機会が多かったので、ちょっと調べてみました。Microsoftのドキュメントを読んでみると

ステートメントで yield キーワードを使用した場合、メソッド、演算子、または get アクセサーが反復子であることを示します。 yield を使用して反復子を定義すると、カスタム コレクション型の IEnumerable および IEnumerator パターンを実装するときに明示的な余分なクラス (列挙の状態を保持するクラス。たとえば IEnumerator を参照) が不要になります。

フーン

まあコード書いてこのyieldの利益をえてみましょう。まずyieldを使っていない書き方から

class YieldTest {
  static void Main(String[] args) {
    foreach (var item in GenDataset()) {
      Console.WriteLine(item);
    }
  }

  static List<String> GenDataset() {
    List<String> dataset = new List<String>();

    for (int i = 0; i < 100; i++) {
      dataset.Add(i.ToString());
    }
    return dataset;
  }
}

まあ単純に0から99までの数字を配列に入れているだけです。次にyieldを使った書き方に変更してみます

class YieldTest {
  static void Main(String[] args) {
    var dataSet = GenDataset();
    foreach (var item in dataSet) {
      Console.WriteLine(item);
    }
  }

  static IEnumerable<String> GenDataset() {
    for (int i = 0; i < 100; i++) {
      yield return i.ToString();
    }
  }
}

ListからIEnumerableというインターフェースに変更して、かつ内部のListの変数宣言などが不要になっています

正直この例だと簡単過ぎてメリットを感じないのですが笑

ここでメソッドから帰ってきた配列の型を確認してみましょう。普通に書いた方は System.Collections.Generic.List1[System.String]`

ですが、yieldで書いた方は YieldTest+<GenDataset>c__Iterator0

という形で返ってきています。イテレータとなっていることから、List型ではなく、反復子として処理されたことがわかります。なので、内容はList型のような参照を行うことが出来ません(配列要素をカウントするCountなどをメソッドチェインすることは不能です)

。。結論として何を言いたいんだって感じになってしまいましたが、まあRubyにもYieldあったなあって思って色々試しただけですw

久しぶりにまとまらない話になりましたorz

C#の開発環境をMac上に整えてみる

C#面白いけどUnity上でしか触っていないので、コンソールから叩けるようにしようと思い、インストールしてみました

C#マイクロソフトが開発した言語で、.NET Framework上で動くように設計されています。 しかし、マイクロソフトが.NETに関する仕様を公開したのに伴って、オープンソースによる.NET Frameworkと互換性のあるフレームワークが作成されるようになりました。まあ詳しくはWikipediaでも見といてください
(ぉ

まあ長々と説明していてもしかたがないので、さくっとインストール方法を書いていきます。
といっても大変簡単で、Monoプロジェクトの本家のサイト に懇切丁寧な説明がありました。Macにインストールしていきます

・・・

まずインストールパッケージをダウンロードしましょう。MonoのサイトからDownloadに行き、Download Mono MDKをクリックして.pkgファイルをダウンロードします

ダウンロードしたファイルをダブルクリックすると以下のような画面が出てくると思います

あとはインストーラーにしたがってインストールを完了させてください

実際にインストールされたかを確認するには以下のコマンドを実行してください

$ mcs --version

僕の環境で実行したところ次のようになりました

では使えるか試してみましょう。以下のコードを適当なcsファイル内部にコピペしてください

using System;

class MonoTest {
  static void Main(String[] args) {
    Console.WriteLine("うぇぇいww");
  }
}

作成したファイルをコンパイルして実行しましょう。以下のコマンドで行うことが出来ます

$ mcs hogehoge.cs
$ mono hogehoge.exe

ここで$ mono hogehoge.exeとしている理由ですが、MonoのC#コンパイラがexeファイルを出力するためです。exeファイルの実行にはmonoコマンドを利用します

無事に実行できました。次回はUbuntu上で実行出来るようにします

Cocos2d-xをGenymotionで実行出来るようにする

Cocosで開発するときは、わざわざNexus7をUSBでつないで実機で動くか試していたのですが、いちいち持ち歩いているの嫌だなってこととUSBケーブルの調子が悪くてlogcatがブツブツ切れていたりして、地味にストレスだったので、エミュレーターで動くように変更しました(USBケーブル買い直せってツッコミは無しの方向で)

※既にGenymotionは入っている前提で行います

まずCocos2d-x本体側のコードの変更を行います。Cocosをインストールしたディレクトリに移動して、次の場所のファイルを編集してください

cocos2dx/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxActivity.java

このファイルにあるisAndroidEmulator()を探してください。次のような関数になっているかと思われます

private final static boolean isAndroidEmulator() {
  String model = Build.MODEL;
  Log.d(TAG, "model=" + model);
  String product = Build.PRODUCT;
  Log.d(TAG, "product=" + product);
  boolean isEmulator = false;
  if (product != null) {
    isEmulator = product.equals("sdk") || product.contains("_sdk") || product.contains("sdk_");
  }
  Log.d(TAG, "isEmulator=" + isEmulator);
  return isEmulator;
}

ここのor条件分岐が連なっているところでエミュレーターの種別を判断しているようです。Genymotionは裏でVirtualboxを動かしているので、エミュレーターの種類に含めてやる必要があります。よって分岐を以下のように書き換えます

isEmulator = product.equals("sdk") || product.contains("_sdk") || product.contains("sdk_") || product.contains("vbox");

これで保存してCocos本体側の変更は終了です。次にプロジェクトファイルの変更を行います。 以下のファイルを変更します
対象のプロジェクトフォルダ/proj.android/jni/Application.mk

このファイルの一行目にAPP_STL := gnustl_staticという記述があるので、この下にAPP_ABI := armeabi x86を追記してあげましょう。これで以下のように変更されたと思われます

APP_STL := gnustl_static
APP_ABI := armeabi x86

APP_CPPFLAGS := -frtti -DCC_ENABLE_CHIPMUNK_INTEGRATION=1 -std=c++11 -fsigned-char
APP_LDFLAGS := -latomic

これをし終わったら、再度コマンドラインから、プロジェクトフォルダで cocos run -p android を実行してみてください。無事にGenymotionで動くようになります

参考にしたサイト

フニゲの開発日記

Unityで大量のオブジェクトをランダムに生成して更にそれぞれランダムに行動させる

Unityロゴあればいいやと思ってタイトル無しにした笑
それにしてもUnityのDocumentationわかりやすくてよかった

とりあえず空間上に大量の障害物などをまき散らして、 更に自立して行動させたいとかってしたくなると思います。どうやるかちょっと悩んだので備忘録がてら

まず転がしたいプレハブちゃんを用意します。まあ適当に3Dオブジェクトを作りましょう

とりあえずUnity公式のチュートリアルのやつを持ってきました。 ここからプレハブを生成します。まずからのGameObjectをとりあえず新規で追加します

ヒエラルキー(hierarchy)にこんな感じで生成できたと思います↓

次にこの空のオブジェクトに対してmodelから対象となるものを子要素としてアタッチしてあげましょう

選択して…

ドラッグアンドドロップで子要素として追加

で、GameObjectだと普通にわかりづらいので、わかりやすい名前にリネームします。とりあえずRotationRockとでもしておきます

次に、このオブジェクト自身を動かすためのコードを書きます。とりあえず回転させながら適当な方向に運動でもしてもらいましょう

先ほど子要素にしたモデルを選択して、

Inspectorから、[Add Component] -> [New Script] -> 名づけたいクラス名を入力&追加します。とりあえずRotatorとしました

すると、C#ファイルが生成されたかと思います。これを編集していきます。右上のギアアイコンをクリックし、Edit Scriptを選択します

以下のようなコードを書きます

using UnityEngine;
using System.Collections;

public class Rotator : MonoBehaviour {

  // オブジェクト生成時に初速度を与えて等速直線運動をさせる
  void Start() {
    GetComponent<Rigidbody>().velocity = transform.forward;
  }

  // オブジェクトを(x, y, z)すべての成分をランダムにして回転運動をさせる
  void Update() {
    transform.Rotate(new Vector3(Random.Range(0, 180),
                                 Random.Range(0, 180),
                                 Random.Range(0, 180)
                                ) * Time.deltaTime);
  }
}

以上のコードを書き終わったら、子要素にしたモデルのInspectorをから[Add Component] -> [Physics] -> [Rigidbody] を追加してください。これを追加することによって物理的な要素を与えることが出来ます

ここで試しに実行してみましょう

これで1つの時が出来るようになりました。次はこれをランダムに生成しようと思います。ひとまずRotationRockをプレハブとして保持するようにします。
ヒエラルキーに入っているRotationRockをドラッグアンドドロップでAssets/Prefabs配下に持っていきます

次に空のGameoOjectを作成しましょう(Create Emptyです)
とりあえずRocksとでもリネームしましょうか。ここで先ほどのようにInspectorからRigidbodyを追加します。 また、新たにコードが必要になるので適当な名前(個人的にはRandomObjectsとしました)で新規に追加します
Inspectorは以下のようになっているかと思います

ではコードを書いていきましょう。以下のようなコードになります

using UnityEngine;
using System.Collections;

public class RandomObjectGenerator : MonoBehaviour {

  public GameObject targetGameObject;
  public float MinRange, MaxRange;

  // 今はとりあえず決め打ちで500個生成しています
  void Start () {
    for (int i = 0; i <= 500; i++) {
      Instantiate(targetGameObject, new Vector3(Random.Range(MinRange, MaxRange),
            Random.Range(MinRange, MaxRange),
            Random.Range(MinRange, MaxRange)),
            Quaternion.Euler(Random.Range(0, 180),
                             Random.Range(0, 180),
                             Random.Range(0, 180)));
    }
  }
}

Instantiateは、実行時にオブジェクトを複製する関数です。これに対象となるGameObjectと初期座標、初期のオイラー角を入れてあげればランダムに生成されます

ちなみにpublic float MinRange, MaxRangeで初期座標部分に関しての上限値と下限値はInspectorから入力可能にしています

実行してみると次のような結果が得られるかと思います

あまり生成数が多いと重くなるかもしれませんが、普通に面白いので色々試してください
久しぶりにめっちゃ書いたなw

Cocos2d-xの個人的な備忘録

久しぶりの投稿になってしまいました。半年後の自分は赤の他人という心構えでなるべく継続して書いていこうと思います(意識高い)

とりあえずCocos久しぶりに触ったらコマンド普通に忘れていたのでメモがてら

Cocosプロジェクトの作成
$ cocos new アプリ名 -d アプリを置きたいディレクトリの絶対パス -p パッケージ名 -l プロジェクトで使用したい言語
ex. cocos new SampleGame -d ~/Documents -p com.hogehoge.com -l cpp
Cocosプロジェクトの実行
$ cocos run -s アプリのプロジェクトのディレクトリ -p 実行したいプラットフォーム
ex. cocos run -s ~/Documents/SampleGame/ -p android

プロジェクトの実行に関しては、最初にビルドが走るため実際に実行されるまでに時間がかかります

しばらくCocosやUnityの話題が続きますが、よろしくお願い致します。
UnityはC#だけど、C#書きやすいね

色々な言語でフィボナッチ

なんとなく色々な言語でフィボナッチワンライナーを実装してみました。

Ruby
p 0; a = 1; b = 0; while a < 10000 do p a; a += b; b = a - b; end
Perl
print 0; $a = 1; $b = 0; while ($a < 10000) {print "$a\n"; $a += $b; $b = $a - $b}
Python
a = 1; b= 0
while < 10000: print str(a); a+= b; b= a- b

Pythonがうまく一行に出来なかったので誰か教えてください。なんとなくgenerator使わないといけない気がする。。。

2GBを超えるメモリを1つのプロセスで確保したい場合

C++で大量の変数を使って計算したい時に、たくさん変数確保するようなことがあるかと思います。 例えば以下のようなコードとか

const int REPEAT_TIMES = 10000;
double num_a[REPEAT_TIMES][REPEAT_TIMES], num_b[REPEAT_TIMES][REPEAT_TIMES], num_c[REPEAT_TIMES][REPEAT_TIMES];
double num_d[REPEAT_TIMES][REPEAT_TIMES], num_e[REPEAT_TIMES][REPEAT_TIMES], num_f[REPEAT_TIMES][REPEAT_TIMES];
double num_g[REPEAT_TIMES][REPEAT_TIMES], num_h[REPEAT_TIMES][REPEAT_TIMES], num_i[REPEAT_TIMES][REPEAT_TIMES];
double num_j[REPEAT_TIMES][REPEAT_TIMES], num_k[REPEAT_TIMES][REPEAT_TIMES], num_l[REPEAT_TIMES][REPEAT_TIMES];
double num_m[REPEAT_TIMES][REPEAT_TIMES], num_n[REPEAT_TIMES][REPEAT_TIMES], num_o[REPEAT_TIMES][REPEAT_TIMES];
double num_p[REPEAT_TIMES][REPEAT_TIMES], num_q[REPEAT_TIMES][REPEAT_TIMES], num_r[REPEAT_TIMES][REPEAT_TIMES];

まあウンコードなのは気にしないでくださいw
しかしこのようなコードを書いて、いざコンパイルしようとした時次のようなエラーが出てくることがあります

/tmp/ccEo3zaI.o: In function `_Z11calc_vectorv.omp_fn.2':
calcMultiThreadManyDataDouble.cpp:(.text+0x3ba): relocation truncated to fit: R_X86_64_32S against symbol `num_d' defined in .bss section in /tmp/ccEo3zaI.o
calcMultiThreadManyDataDouble.cpp:(.text+0x3e7): relocation truncated to fit: R_X86_64_32S against symbol `num_e' defined in .bss section in /tmp/ccEo3zaI.o
calcMultiThreadManyDataDouble.cpp:(.text+0x414): relocation truncated to fit: R_X86_64_32S against symbol `num_f' defined in .bss section in /tmp/ccEo3zaI.o
calcMultiThreadManyDataDouble.cpp:(.text+0x441): relocation truncated to fit: R_X86_64_32S against symbol `num_g' defined in .bss section in /tmp/ccEo3zaI.o
calcMultiThreadManyDataDouble.cpp:(.text+0x46e): relocation truncated to fit: R_X86_64_32S against symbol `num_h' defined in .bss section in /tmp/ccEo3zaI.o
calcMultiThreadManyDataDouble.cpp:(.text+0x49b): relocation truncated to fit: R_X86_64_32S against symbol `num_i' defined in .bss section in /tmp/ccEo3zaI.o
calcMultiThreadManyDataDouble.cpp:(.text+0x4c8): relocation truncated to fit: R_X86_64_32S against symbol `num_j' defined in .bss section in /tmp/ccEo3zaI.o
calcMultiThreadManyDataDouble.cpp:(.text+0x4f5): relocation truncated to fit: R_X86_64_32S against symbol `num_k' defined in .bss section in /tmp/ccEo3zaI.o
calcMultiThreadManyDataDouble.cpp:(.text+0x522): additional relocation overflows omitted from the output
collect2: ld returned 1 exit status

relocation truncated to fit

再配置はサイズに合うように切り捨てられました??ん??どういうことだ?

色々調べていたら次のサイトに巡りあうことが出来ました

シングルプロセッサプログラムの最適化

つまるところ、64ビットコンパイラでは、デフォルトでは32ビットと互換性を保つために2GB制限をかけているけど、コンパイルオプションを追加してやることで64ビットアプリケーションとしてコンパイル出来るようになるようです

よってコンパイル時に

$ g++ ファイル名.cpp -o 出力したい名前 -mcmodel=large

としてやればコンパイル出来るようになります。
実行結果を見てみると

この計算機はメモリが24GB積んであるのでこのプロセスだけで13.7GB程度消費しています。 …やばいな笑