kazumalab tech log

流行りとリラックマと嵐が大好きです。技術的ログ。

Unity C#でHaskellのmapっぽいものを実装したお話

かずまです。

最近、すごいHaskell楽しく学ぼう! を読み始めました。

おもしろおかしく書かれているところもあるので読み行ってしまいます。
なので、寝る前に少し読む、みたいなスタイルで。

Haskellの高層関数

Haskellには高層関数と呼ばれるものがあり、マッピング(map)や畳込みなどが例に挙げれらます。
その中でも今回はmapを実装してみたいと思います。

mapとは?

map関数はリストを返す関数で、引数にfの関数を取り、引数2つめのリストの中身にその関数を適用してくれるという優れもの。
例は以下のように書きます。

map (2 *) [1, 2, 3, 4, 5]
-- [2, 4, 6, 8, 10]

Haskellを僕も今回初めて読み書きしましたがこのように書くようです。
(2 *)は関数で引数としてintを一つ受取り2倍するというもの。

初見では何してるの!?これって感じでした。笑
今回はmapという関数を作って、関数とリストを投げて、マッピングして適用させます。

C#でのFunctional的に書く方法

  • Lambda式を使う

C#でもLambda式が使えるのでそれを利用します。
無名関数とも言いますね!

公式ドキュメントはこちら。
Lambda Expressions (C# Programming Guide)

  • 引数でデリゲートを使う

Lambda式を受けるデリゲートは

Func(T, TResult) Delegate (System)

ココらへんを使えば良さそうです。

** Func

これは一つ目のTは引数の型、TResultは返り値の型をGenericで指定するようです。

なんと、この2つを使えばできるはずですね!!

クラスとメソッドの作成

とりあえず、Funcというクラスを作って、staticでListを返すメソッドを用意してみます。

このような感じで書いてみました。
メソッドをGenericにするのはうーん、どうなのかな。
ClassをGenericにするのもいい気がするけど今はこっちでかきました。

使い方

先程のクラスとは別にFunctorTestクラスを用意します。
以下のように記述してみました。

public class FunctorTest : MonoBehaviour {

	private List<int> list = new List<int>(){ 1, 2, 3, 4, 5 };

	private void Start () {
		System.Func<int, int> f =  (int arg) => {
			return arg * 2;
		};

		list = Functor.map (f, list);

		print (list);
	}
}

追記 2/7

Lambda式をまとめるのもありです。

public class FunctorTest : MonoBehaviour {

	private List<int> list = new List<int>(){ 1, 2, 3, 4, 5 };

	private void Start () {

		list = Functor.map ((int arg) => {return arg * 2;}, list);
		print (list);
	}
}

このコードではHaskellと同じように2倍するLambda式を書いて、それをmapの第一引数として渡すだけ!
簡単ですね!ただHaskellと違ってタイプする数は多いのでスマートじゃない...

これぐらいなら許容範囲!


mapはGenericだけど型を指定しなくても良さそう

mapはGenericな型を要求するので実際は

list = Functor.map<int> (f, list);

と指定するのがいいのか、と想定していました。
ところがTestクラスを書いた時にmapの後ろに型の指定を忘れていたのです。

でもコンパイルエラーも出なく、きちんと出力されていたので
自動的に型を認識するのかと感動してしまいました。

f:id:kazumalab:20170130235140p:plain

きちんとint型だと認識していますね。
公式のドキュメントにもきちんとかいてありました。
Generic Methods (C# Programming Guide)

便利だしHaskellに一歩近づいた?笑

出力結果

f:id:kazumalab:20170130235534p:plain

素晴らしい、ちゃんと出てますね!

Stringでも試してみよう!

Listのintが出来たのでstringでも同様に使えるはずです。

	private List<string> slist = new List<string>() { "Hello", "World" };

	private void Start () {

		System.Func<string, string> fs =  (string arg) => {
			return arg + "(ΦωΦ)";
		};

		slist = Functor.map (fs, slist);

		print (slist);
	}

この様にHelloとWorldの後ろに(ΦωΦ)を付け足すスクリプトです。

結果

f:id:kazumalab:20170131003311p:plain

にゃーと猫がでてきましたね。

まとめ

Haskellはまだまだ初心者ですので間違ってる解説などありましたらご指摘ください。
あとこう書いたほうがいいよ〜みたいなのもぜひ!

今回はHaskellっぽくC#で書いてみよう!みたいな感じでしたが、
おもったより便利だと思います。

追記

C# やるなら LINQ を使おう | プログラマーズ雑記帳

LINQを使えば畳み込みも写像も出来るみたい。
次にそのことを書きたいではある。

Global Game Jam 2017に参加しました。

かずまです。

21 ~ 22日で行われた(他の会場では20日もあった)Global Game Jamに参加してきました。
参加だけでなく僕達IGDA琉球大学、琉ゲ部では沖縄会場の運営も行っておりました。

Global Game Jam(GGJ)とは

Game Jamは、ゲーム開発を行うハッカソンです。
その中でも、Global Game Jam (以下、GGJ) は、ギネス記録を持つ世界最大のGame Jamです。

全世界にハッカソン会場が設けられており、全世界で同じ日に同じテーマでゲームを開発します。GGJは、2009年の初開催以降年々規模を大きくしており、2016年1月のGGJでは、世界93の国と地域で、36,000人以上が参加し、約6,800の作品が制作されました(世界の会場一覧はここから見られます)。

引用URL

Global Game Jam JAPAN | Innovation, Collaboration, Experimentation! Jan. 20~22, 2017

沖縄会場の参加者

毎回少ない沖縄会場ですが、23名の参加がありました。
ほぼ学生ですが、企業の方も数名きていただきました。

最近は

琉球大学
KBC(専門学校)
ITカレッジ

の3校からの参加が多いです。

制作物

今回運営チームとして

evilonedeath.blog.fc2.com

彼と2人で作成しました。

もちろんゲームエンジンはUnityです。
担当はHookを飛ばす、引き寄せる部分を彼に担当してもらい、その他(剣のモデルを含める)は僕が担当しました。

タイトル

「Hook VR」

コンセプト

VRで試したことない体験したい、ということで敵にHookを引っ掛けて、近くに引き寄せて剣で切る、というもの。

プレイ動画


データ配布

HookVR.exe - Google ドライブ

上記URLからダウンロードしてください。
GPU GTX 970以降搭載のPC、HTC Viveが必要になります。

Asset

使ったアセットは

assetstore.herokuapp.com

こちらになります。
やっぱり$10は安い。

第2回 技術的ログ Twitterから

前回の記事はこちら。
blog.kazumalab.com

Twitterでの技術エントリー(?)をまとめただけになります。

技術本の大賞

学生が作ったUE4の作品

めっちゃレベル高い。
ゲームの気持ちいい部分を抜き出しててすごくやりたい。

ねこが可愛い...

たまには癒やしも必要ですよん。

RapidAPIを使えばすべて同一のコーディングで利用できるらしい。

Oculus TouchでもSteamVR Pluginが使えるらしい @Unity

The Labのやつができるみたい。
Steam強い。

写真から木を作成SpeedTree

UE4でも動くのすごくいい。

コードの書く際に見直すべきサイト

Gitからコーディングスタイル、ガイドラインまで、幅広い。

UnityのResourcesからLoadする際のキャストの件

かずまです。

UnityでResourcesからLoadするときになにもキャストしない場合Object型で返ってきます。
ただそれではGameObjectとして使えないのでキャストする必要があります。
今回はas演算子もキャストとして扱っています。
どの方法が一番はやく処理ができるのか、気になったので今回はそれを計測してメモしておきたいと思います。

キャスト方法

現在僕が知っている限りResourcesからのGameObjectへの型の変換方法は3つです。

asを使う

GameObject obj = Resources.Load ("Prefabs/Box/Wood") as GameObject;

予想ではasは早いと聞いたのでこれが早いかと!

(GameObject) 対象物

GameObject obj = (GameObject) Resources.Load ("Prefabs/Box/Wood");

これだとInvalidCastExceptionが投げられる場合があるので2つめの引数として型を指定します。

(GameObject)対象物 + typeof(GameObject)

GameObject obj = (GameObject) Resources.Load ("Prefabs/Box/Wood", typeof(GameObject));

二段階で念入りではありますが、これ実は2回も型変換しているので遅いのでは?と思っています。

Genericで指定

GameObject obj = Resources.Load<GameObject> ("Prefabs/Box/Wood");

Genericはコードを書く文にはキャストし忘れ、ということは減るはずなのでいいのですが、はたしてas演算子とどう変わるかといったところですね。

速度計測方法

今回は上記コードを1万回Loadし、そのたびに速度(時間)を計測します。
それを100回試行し、平均を取ります。

計測に使うのは
C#のStopwatchです。
Stopwatch クラス (System.Diagnostics)

プログラムのサンプルはこちら。

// asでの型変換の計測
for(int count = 0; count < times; count++) {
			Stopwatch st = new Stopwatch ();
			st.Start ();
			for (int i = 0; i < 10000; i++) {
				GameObject obj = Resources.Load ("Prefabs/Box/Wood") as GameObject;
			}
			st.Stop ();
			total_as += st.Elapsed.TotalMilliseconds;
}

PCの性能

OS : macOS Sierra 10.12.2
CPU : 2.9 GHz Intel Core i5
メモリ : 8 GB 2133 MHz

Unityと.Net Framework

Unity 5.5.0f3 Personal (64bit)
.NET Framework 4.5

こんな感じです。

計測結果

以下の結果になりました。
f:id:kazumalab:20170115015326p:plain

表に早い順番に並べるとこのような感じ。

方法 実行時間(100回平均)
as演算子 9.47781ms
(GameObject)対象物 + typeof 9.74216ms
(GameObject)対象物 9.99564ms
Generic 10.02538ms

予想ではas演算子が早いということでしたが、ほんとに早かったです。
as演算子を使う場合は少し制約があるのでそこも考えないと行けないです。

nullが返ってくる

キャストが出来なかった場合、InvalidCastExceptionをthrowされますが、
as演算子ではnullが返ってきます。

ufcpp.net

予想より早い(GameObject)対象物 + typeof(GameObject)

今回は早い結果となりましたが、何回か再生をポチポチした時にtypeofがない方が早いという結果になることも。
一概には早かったとはいえないけど、そこまで遅くないので使うのは良さそうですね。

Genericでは他に比べると遅いのかな、という印象でした。
高速に使うならas演算子、確実にキャストするなら(GameObject)対象物 + typeof(GameObject)を使うのが良さそうですね。

もし、もっとこうしたほうが良い、ホントはこうだと思うことがあればどんどんコメントでください。
絶賛C#マスター中ですのでご指摘、議論は嬉しいです。

よろしくお願いします。

初の学会発表に臨んだ件。

かずまです。

1/6 ~ 1/8まで静岡県伊東市にある、
ラフォーレ伊東にてプログラミングシンポジウムに参加、発表してきました。
これが人生初の学会というものです。

毎年この時期恒例だそうです。

発表のもろもろ

専門はデータベースを主としています。
今回の発表もデータベースについての内容でした。
結果としては

図が絶対パスになっていて表示されなかった

内容の良さがあまり伝わってなかった

質問の受け答えが微妙

でした。
発表の際に原稿を一応書いて練習してたはずですが、テンパったあまり真っ白に。
あちゃーです。

反省点

プロジェクターの端子を事前に確認しておく

これは発表前に「HDMIの端子がなくDVIしかない!」となってしまいました。
つまり、新しいMacを画面出力するのに変換ケーブルを持っていたのですがHDMIしか出力できず、あっけなく断念。
先輩のPCを使うことになり、Pathが違うので画像が表示されないという問題に。
相対パスにするべきだ...と反省、卒論は気をつけよう。

用語の意図をきちんと伝える

今回問題になったのはインピーダンスミスマッチ。

インピーダンスミスマッチに関してはここあたりの論文を参考にするのが良いかと。
ci.nii.ac.jp

次回こそはいい発表したいところですが今年3月で学生が終わるので
リベンジは社会人になってからですね。

ところで、こんなツイートが。

いやー確かにこの通りめっちゃ緊張して、ガチガチでした!悔しい!

食べ物、温泉、お酒

学会といえば論文発表もメインですが、それに引けを取らないご飯と温泉とお酒とボドゲです。
夜の部があったのでそちらにも参加しました。

これシンプルなのですが面白かった、オススメです。
f:id:kazumalab:20170110020824j:plain

旅館の近くに美味しい海鮮丼もあります!
f:id:kazumalab:20170110020918j:plain
最高だった。

旅館のご飯も美味しい!
f:id:kazumalab:20170110021042j:plain

さらに部屋には温泉が備え付けられているのでいつでも入り放題!


学会にでると知らないことがまだまだあって、参考になりますし、
トータル最高でした。

発表(記憶分)

3DプリンタのデータをSchemeで書く
対戦型2048の最強AIを作ってきた
中級者向け補完機能
Ocamlによる対話式デバッグ
Rubyでの型推論
オートマトン最高
個人情報(匿名、行動パターンから個人の特定できるか?みたいな)

などなど。