月別アーカイブ: 2012年8月

[C#]ListをRemoveAll( c => !c.MoveNext());する意味のまとめ

※いろいろと間違っていたのを修正。

数ヶ月前に参加したUnity仙人のセミナー、Unityで覚えるC#のサンプルで挙動がよくわからなかったもののまとめ。

サンプルは前述のスライドの56ページ。

ここで何が起こってるのかをまとめる。

まずListの言語仕様を整理

参考:http://www.atmarkit.co.jp/fdotnet/special/generics01/generics01_02.html

ListGenericClassは内容物の型を宣言する形式のArrayListクラス(のようなもの)。

Listという形で使用する。

ArrayListもListGenericも共に.Add()で内容を追加する。

この処理速度はListGenericの方が上。

理由はArrayListの場合はオブジェクトに変換するボックス化処理が挟まるから。

ジェネリッククラスの記述方法

参考:http://www.atmarkit.co.jp/fdotnet/special/generics01/generics01_03.html

山括弧内の型指定をTにすることで型を後付けにして汎用化できる。

ただ、複数の型を混在させる事が出来るわけではない。これをしたいのであればDictionaryClassを使用する。

Whereキーワード

型指定はTにしたいけれどもクラスであることは確実だったり、構造体であることが確実であったりと条件を幾分追加するキーワードで先の参考に示したページにその一覧が記述されている。

class MyGenericClass1 where T : struct {
  // 制約:Tは構造体
}

class MyGenericClass2 where T : class {
  // 制約:Tはクラス
}

class MyGenericClass3 where T : new() {
  // 制約:Tはインスタンス化可能
}

class MyGenericClass4 where T : MyOtherClass {
  // 制約:TはMyOtherClassクラスを継承
}

class MyGenericClass5 where T : IMyInterface {
  // 制約:TはIMyInterfaceインターフェイスを実装
}

class MyGenericClass6 where T : U {
  // 制約:Tは別の型パラメータUを継承
}

//////////////////////////////////////////////////
class MyOtherClass {
  // とあるクラス
}

interface IMyInterface {
  // とあるインターフェイス
}

また、複数の制約も可能ということなので試してみました。

class MyGenericClass where T:class,new()

こんな風にコロンで連結すれば良いです。

ゲームだと、出現したゴブリンとホフゴブリンとゴブリンアーチャー全員がゴブリンパンチ打てるよーって状況で全員がゴブリンパンチを採択するとかで使える。

例が限定的すぎるけれども。

ジェネリックに限らず、こういった書き方ができるのはC#の強み。

  public T this[int index] {
    get {
      return _list[index];
    }
    set {
      _list[index] = value;
    }
  }

で、ジェネリックについて整理したけどList型と必ずしも関連しないという事を注意したい。

List型はIEnumerableなのでforeachが使えるけれども、

前述した例だと独自実装なのでIEnumeratorを実装しておらず、foreachが使えない。

なんということでしょう・・・。

ListクラスのRemoveAllを理解する

RemoveAllメソッドは、特定の条件に一致する要素だけ削除する際に使用するメソッド。

RemoveAllの引数として、条件を判別するメソッドのハンドラ渡し、Trueが返る場合は削除を行う。

そこまでのサンプル。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class genericRemoveAll : MonoBehaviour {

     // Use this for initialization
     void Start () {
          List<orgString> stringList = new List<orgString>();

          for( int i =0 ; i < 10; i++){
               orgString tmp = new orgString( "string:"+i );
               stringList.Add( tmp );
          }

          stringList.RemoveAll( Method );
          foreach(orgString o in stringList)
          {
                o.Method();
          }

     }
     bool Method(orgString o){
          return o.str == "string:3";
     }
     // Update is called once per frame
     void Update () {

     }
     public class orgString{
          public string str;
          public orgString(string s){
               this.str = s;
          }
          public void Method()
          {
               Debug.Log("at method :"+this.str);
          }
     }
}

相変わらず起動確認はUnityで。

RemoveAllの中身をラムダ式にするとこうなる。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class genericRemoveAll : MonoBehaviour {

     // Use this for initialization
     void Start () {
          List<orgString> stringList = new List<orgString>();

          for( int i =0 ; i < 10; i++){
              orgString tmp = new orgString( "string:"+i );
              stringList.Add( tmp );
          }
          stringList.RemoveAll( s => s.str == "string:3" );
          foreach(orgString o in stringList)
          {
                o.Method();
          }

     }
     public class orgString{
          public string str;
          public orgString(string s){
               this.str = s;
          }
          public void Method()
          {
               Debug.Log("at method :"+this.str);
          }
     }
}

参考:http://www.atmarkit.co.jp/fdotnet/dotnettips/815listremove/listremove.html

ラムダ式をおさらい

ラムダ式はEventHandlerを省略したデリゲートをさらに省略した記述方式。

なのでラムダ式には左辺と右辺があり、左辺はdelegateとして呼び出されるメソッドのparameterが入り、右辺には式か文が入る。

左辺の記述は()にくくられていなければならないが、parameterが単一の場合は省略可能。

右辺の記述が式の場合、セミコロンで閉じない。

右辺の記述が文の場合、{ 文; 文; 文; }と記述し、{}にくくられていなければならないし、各文の最後に;が入ってなければ成らない。

(a, b) => {
a.transform.position = new Vector3( 0,0,0 );
b.transform.position = new Vector3( 1,1,1 );
}

また、以下の様な特徴もある。

http://d.hatena.ne.jp/seraphy/20120609

  • ラムダ式は括弧の外もスコープの対象。
  • スナップショットではなく、生きた参照。

まとめ

というわけで、RemoveAll()のparameterで記述されている件は…

列挙された内容をcというオブジェクトで代用し、MoveNext()が失敗する場合論理値を反転させTrueにし、RemoveAllの対象とする、という意味になる。

以下のサンプルが良い例になるだろうか。

         A a = new A ();
         a.Do ();
         a.Do ();
         a.Do ();
         a.Do ();
         a.Do ();

         public class A
         {
             List<IEnumerator> ien;
             public A ()
             {
                 this.ien = new List<IEnumerator>();
                 this.ien.Add (IenMethod1());
                 this.ien.Add (IenMethod2());
             }
             public void Do()
             {
                 this.ien.RemoveAll( c => !c.MoveNext() );
             }
             IEnumerator IenMethod1()
             {
                 Debug.Log ("befor1");
                 yield return null;
                 Debug.Log ("after1");
             }
             IEnumerator IenMethod2()
             {
                 Debug.Log ("befor2");
                 yield return null;
                 Debug.Log ("after2");
             }
         }

実行結果:

befor1
befor2
after1
after2

あーやっと終わった。スッキリ!

MacOS Lion + XAMPP + OpenPNE

インストール

各種インストーラDLして入れておく
現在ではOpenPNE-3.6.4が最新?

※3.8.Xがリリースされてます。

セキュリティ

同じ様なテーマで作業ログ残してるブログを参考にすすめてみる。

参考:http://d.hatena.ne.jp/s_kaneda/20090603/1244016722

上述の参考URI上にあるリンクのXAMPPのインストールから設定まわりは古過ぎて参考にならず。

MacOS LionにはUtilityにDirectory なんちゃらってのが無い。

ターミナルからsudoでセキュリティの設定を行う。

参考:http://www.youtube.com/watch?v=BPjpRzdrug8

XAMPPのファイルをターミナルからいじる

作業はすべてsudoで行う

sudo vim /Applications/XAMPP/etc/httpd.conf

ファイルを開いたらset numberで行数を表示。
可能な限り何行目にどんな設定があるのかを把握しておく。

最後尾のコメントアウトを削除。vim は Ctrl + d でページ送り

vhostで設定したのディレクトリがちゃんとhtdocs下で一致している事を確認してからxamppのapacheを再起動。

エラーがあるとxamppがalert投げて知らせてくれる。

openPneのデータベースをインストール

データベースは予め作成しておかなければならない。でないとCreatingTableでこける。

phpmyadminからDBを作成。

DB名はopenpne。

照合順序もutf8_unicode_ci

参考:http://technology.rey-net.com/?eid=1155855

ターミナルから以下を入力する。

sudo php /Applications/XAMPP/htdocs/OpenPNE-3.6.4/symfony openpne:install

require_onceでエラーが出たら、次の事をやってない可能性が高い。OpenPNE-3.6.4/config/ProjectConfiguration.class.php.sampleをコピーしてファイル名から.sampleを抜いたものを用意しておく。

インストールが開始されると青い箱で質問攻め。質問内容は参考URLを参考に。

あれ?インストール中どうしても
PDO Connection Error: SQLSTATE[HY000] [2002] No such file or directory
で止まる。

PDO Connection Error: SQLSTATE[HY000] [2002] No such file or directoryの対策

php.iniの最終行に”pdo_mysql.default_socket=/var/mysql/mysql.sock”を追加してインストール処理を試す。

sudo vim でphp.iniを開く。参考URLではpdo_mysql.default_socketの項目があるようだけど俺のところには無かった。無かった!

結果・・・失敗。

参考:http://yasuwagon.blogspot.jp/2010/12/openpne3-pdo-connection-error.html

mysql.sockを探してそれをインストール時に聞かれるdatabase socket pathに入れる方法

まずソケットの場所を探す。

find / -name "*sock"

ずらずらっと出て来るはずなのでそこからそれっぽいものを採択。

/Applications/XAMPP/xamppfiles/var/mysql/mysql.sock

これをインストール時に聞かれるType database socket path (optional)に入力。

結果・・・成功。

参考:http://weble.org/2011/05/07/openpne-mac-hemtel

成功すると
http://localhost/OpenPNE-3.6.4/web/
からアクセスできるようになる。

途中、手を付けたもの

/config/OpenPNE.yml.sample -> /config/OpenPNE.yml
/config/ProjectConfiguration.class.php.sample -> /config/ProjectConfiguration.class.php

ここまででインストール完了と管理画面へのログインはできたものの、ユーザー側のフロントエンドにはいけず。

なので色々見直したところ、OpenPNE.ymlのベースURLが間違っているっぽい事が分かったのでそこを修正してみる。

OpenPNE.ymlのbase_urlまわりを修正

6行目

base_url: "http://example.com"
->
base_url: "http://localhost/OpenPNE-3.6.4/web/"
  pc_frontend: "https://example.com"
  mobile_frontend: "https://example.com"
  pc_backend: "https://example.com"
->
  pc_frontend: "http://localhost/OpenPNE-3.6.4/web"
  mobile_frontend: "http://localhost/OpenPNE-3.6.4/web"
  pc_backend: "http://localhost/OpenPNE-3.6.4/web"

結果・・・以下のエラーがでてAdmin側もアクセス不可能に…。

現在、サーバが混み合っているか、メンテナンス中です。
ご迷惑をおかけいたしますが、しばらく時間を空けて再度アクセスしてください。

Currently, our service is too busy or is under maintenance.
We're sorry and please retry accessing later.

ここからbase_urlの最後のスラッシュだけ排除してもう一度インストール

結果・・・アクセス可能に!

しかし、相変わらずログインはできない。

テーブルのPrefixも変更

table_prefix: ""
->
table_prefix: "op_"

これは推奨、ということなのでやらなくてもいいっぽいけどやっとく。

最有力の.htaccessを修正する

参考:http://redmine.openpne.jp/issues/201

XAMPPにmcryptを入れる。

mcrypt:暗号化用関数。なぜかxamppのmac版には入ってない。

http://www.weblio.jp/content/Mcrypt+%E6%9A%97%E5%8F%B7%E5%8C%96%E9%96%A2%E6%95%B0

入ってるか確認。

http://forum.tsukaeru.net/viewtopic.php?p=24183

mcryptはhomebrew経由でinstallした。

同様にAPC拡張モジュールを入れる

APC拡張モジュールって何?

apcはhomebrew経由でのinstall方法が良くわからず…。

その後、発見するもhomebrewからだとダメらしい。

http://d.hatena.ne.jp/ken-ton/20111108/1320732105

そしてAPCをinstallするためにのphpizeが機能しない。

これはphp-develをインストールする必要があるらしい…。

日を跨いでるので一度整理。

現在の現象の種類

  • ログイン後のページが404
  • 管理側は閲覧可能

解決策、原因と思われるものの種類

  • APCとmcryptを入れれば解決するかも!
    • http://blog.bmoon.jp/diary/tips/server/openpne-3.php
    • mcryptはbrew経由でインストール済み
  • .htaccessを編集すれば解決するかも!

公式の手順を読み返したら実際はなにも編集しなくても
http://example.com/pc_frontend_dev.php
でアクセス出来た。

やっとできた…。
でも相変わらずdev抜きURLではアクセスできないけれどもPluginを製作するにあたっては問題無いね。きっと。

OpenPNE

[C#][ObjC]列挙子

Enumerator=調査員。なんだか不穏な感じ。納税状態とかチェックされちゃいそう。
ちゃんと調べてみます。

C#だと

列挙子はGetEnumeratorメソッドを持っていればforeachで使うコレクションにできるIEnumeratorを取得できる。
このメソッドを持ってる=IEnumerableってこと。

IEnumeratorを実装しないとforeach文で列挙できない。
というか、IEnumeratorはforeachの為にある。かのようにMSDNには記述されている。

IEnumerable.GetEnumerator

http://msdn.microsoft.com/ja-jp/library/system.collections.ienumerable.getenumerator(v=vs.80).aspx

IEnumeratorの最初の位置、Reset直後はundefinedが入ってる。というかC#だと何も入ってない?
いずれにせよ最初に動作させるにはMoveNext()を一発かませとな。
コレクションの要素が変更追加削除されると無効になり、これは回復不能。
コレクションの全ての処理が終わるまでロックすることが推奨される。

以上、MSDNより。

ロックってどうやんの?

Unityだと

内容は以下のリンクのp55。
http://www.slideshare.net/lucifuges/unityc
つまり、MoveNextメソッド内の処理に、yield が挟まる間隔単位でcaseが設けられたswitch構文入りの処理をメンバに持つクラスが生成されるとのこと。
newされる度にクラス変数がカウントアップし、返す内容がシフトする仕組み。

Objective-Cだと

http://www.atmarkit.co.jp/fcoding/articles/objc/05/objc05d.html

NSEnumeratorクラス。

NSArrayやNSDictionaryクラス(これを継承したNSMutable**も?)列挙子を内包しているので、for-in使える。

Objective-Cでの使い方

.MoveNextにあたるのが nextObject。
取得する要素が無いとnilを返す。

for( id obj in array )
{
     NSLog( @"value : %@" , obj );
}
for (id key in dic)
{
     NSLog( @"key : %@ nvalue : %@n", key , [dic objectForKey:key] );
}

列挙子の取得から列挙。

NSEnumerator *en = [dic objectEnumerator];
for ( id obj in en)
{
     NSLog( @"value : %@n ", obj );
}

これで中身をズラズラっと取得。

NSDictionaryはkeyの列挙できるkeyEnumeratorがある。

NSEnumerator *en = [dic keyEnumerator];
id key;
while (key = [en nextObject]) {
     NSLog(@"key: %@, value: %@n", key, [dic objectForKey:key]);
}

サンプル

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
    NSLog(@"ApplicationStart");
    
    NSLog(@"NSArray");
    NSArray *arr = [NSArray arrayWithObjects:@"arr0",@"arr1",@"arr2",@"arr3",nil];
    for(id obj in arr)
    {
        NSLog(@"value:%@",obj);
    }
    
    NSLog(@"NSDictionary");
    NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:
                         @"value1",@"key1",
                         @"value2",@"key2", nil];
    for(id key in dic)
    {
        NSLog(@"key:%@, value:%@",key,[dic objectForKey:key]);
    }
    
    NSEnumerator *en = [dic keyEnumerator];
    id key;
    while(key = [en nextObject])
    {
        NSLog(@"en::key:%@, valu:%@",key,[dic objectForKey:key]);
    }
}

[Unity][C#]StopCoroutineが動かない

Coroutineの挙動が不信だったので調べてみたのでメモ。

当初の状況

あれ?yield return www;してるからwwwインスタンスがデータ取得してからでないと次進まないはず

にも関わらずwww.textよめねーぞって怒られる。なんでだ…?

Reset()もできずハマる。

今回の問題点と目的

  1. coroutineはStopCortoutineした後に同じメソッドをStartCoroutineするとStopしたところから再開するんじゃ?疑惑の解消
  2. Start/Stopの動きを先ず確認
  3. 進む原因は?

ダメなソース

using UnityEngine;
using System.Collections;

public class coroutine : MonoBehaviour {
	
	// Use this for initialization
	IEnumerator Start () {
		
		StartCoroutine(Test());
		Debug.Log("StartCoroutine 1");
		
		yield return new WaitForSeconds(2f);
		
		StopCoroutine("Test");
		Debug.Log("StopCoroutine");
		
		
		yield return new WaitForSeconds(2f);
		
		StartCoroutine(Test());
		Debug.Log("StartCoroutine 2");
		
	}
	
	IEnumerator Test() {
		
		Debug.Log("Test Start");
		
		yield return new WaitForSeconds(1f);
		Debug.Log("1 second");
		yield return new WaitForSeconds(1f);
		Debug.Log("2 second");
		yield return new WaitForSeconds(1f);
		Debug.Log("3 second");
		yield return new WaitForSeconds(1f);
		Debug.Log("4 second");
		yield return new WaitForSeconds(1f);
		Debug.Log("5 second");
		
		
		Debug.Log("Test End");
		
	}
	
	
	// Update is called once per frame
	void Update () {
	
	}
}

結果

コンソールにはこんな表示が。
StopCoroutine doesnt work

StopCoroutine()した後も元気に走り続けるTest()の姿が!

原因は、文字列ではなくハンドラを直接StartCoroutine()に渡していた事。
StopCoroutine()は文字列で受取った指定のCoroutineしか参照できず、あたかも処理を完遂したかの様にエラーも出さなきゃ警告も出ない。

修正

using UnityEngine;
using System.Collections;
using System.Collections.Generic;


public class coroutine : MonoBehaviour {
	
	// Use this for initialization
	IEnumerator Start () {
		
		StartCoroutine("Test");
		Debug.Log("StartCoroutine 1");
		
		yield return new WaitForSeconds(2f);
		
		StopCoroutine("Test");
		Debug.Log("StopCoroutine");
		
		
		yield return new WaitForSeconds(2f);
		
		StartCoroutine("Test");
		Debug.Log("StartCoroutine 2");
		
	}
	
	IEnumerator Test() {
		
		Debug.Log("Test Start");
		
		yield return new WaitForSeconds(1f);
		Debug.Log("1 second");
		yield return new WaitForSeconds(1f);
		Debug.Log("2 second");
		yield return new WaitForSeconds(1f);
		Debug.Log("3 second");
		yield return new WaitForSeconds(1f);
		Debug.Log("4 second");
		yield return new WaitForSeconds(1f);
		Debug.Log("5 second");
		
		
		Debug.Log("Test End");
		
	}
	
	
	// Update is called once per frame
	void Update () {
	
	}
}

これだと以下のように動く。

StopCoroutine work

ちゃんとStopCoroutineで止まってくれた。

そして疑惑だった途中再開もされていない。文字列で指定すればStopCoroutine()もちゃんと機能するということも分かった。

おまけ

  • Startが今回のサンプルの様にIEnumeratorを返すタイプなのにStopAllCoroutines()をするとStartまで止めてしまうので注意。
  • Resetは必要無い。
  • IEnumeratorにparameterを渡す場合は第二引数に入れて上げれば機能する。

参考

C#本