@blog.justoneplanet.info

日々勉強

CakePHPでCSVのダウンロードをする

ベタに書いてもいいんだけどアレなんでヘルパーを使う。

■ヘルパー

vi app/views/helpers/csv.php

CSV Helper (PHP 5)のコードをペーストする。

コントローラ

App::import('Helper', 'Csv');
var $helpers = array(
    'Csv'
);

■データ挿入

Configure::write('debug', 0);
$this->layout = false;
$line = array('hogehoge', 'fugafuga', 'piyopiyo'); 
$csv->addRow($line);

■出力

echo $csv->render('data.csv', 'sjis', 'utf-8');

Cで文字列を使ってみる

まぁ、簡単にまとめておく。

■文字変数

Cには文字列変数はなく文字変数のみが存在する。他の言語よりも扱いにくいけど低レイヤーを感じられる。

int main(int argc, char *argv[])
{
    char c = 'a';
    printf("%c", c);// a
}

当然、日本語は扱えない。低レイヤー万歳!

int main(int argc, char *argv[])
{
    char c = 'a';
    c++;
    printf("%c", c); // b
}

上述のようにするとASCIIでaの次にあるbが表示される。

■文字列

文字変数を並べれば文字列になるという考え方で配列によって表現される。

int main(int argc, char *argv[])
{
char str[] = "this is a pen.";
printf("%s", str);
}
[/sourcecode]

一応、配列なので以下のように書くこともできる。

int main(int argc, char *argv[])
{
char str[] = {‘t’, ‘h’, ‘i’, ‘s’, ‘ ‘, ‘i’, ‘s’, ‘ ‘, ‘a’, ‘ ‘, ‘p’, ‘e’, ‘n’, ‘.’, ‘\0’};
printf("%s", str);
}
[/sourcecode]

末端文字などの文化はObjective-Cにも受け継がれていたりする。

Cで関数を使ってみる

まぁ、とりあえず書いておく。

■定義

void hoge() {
    printf("%s", "hello");
}

以下のようにして引数を定義する。

void hoge(char str[]) {
    printf("%s", str);
}

まとめると以下のようになる。

戻り値型 関数名(引数の宣言) {
    処理
}

ちなみにC言語では配列を返すことができない。

また、Javaでは可能だがCでは以下のようにすることはできない。

int hoge(int a){
    return a;
}
double hoge (double a){
    return a;
}

プロトタイプ宣言

以下のように記述した場合の処理は実装次第である。

int main(int argc, char *argv[])
{
    printf("%d", hoge(6));
}
int hoge(int a){
    return a;
}

従って以下のようにプロトタイプ宣言をする。

int hoge(int a);
int main(int argc, char *argv[])
{
    printf("%d", hoge(6));
}
int hoge(int a){
    return a;
}

プロトタイプ宣言はヘッダファイルに記述すると良い。というかincludeしたファイルにはプロトタイプ宣言が記述されている。

CakePHPのページャーにクエリを付加する

以下のようにoptionsに記述する。

$this->paginate = array(
    'page'   => 1,// 初期選択ページ
    'limit'  => 100,// 1ページあたりの件数
    'order'  => array('created' => 'desc'),// 順序
    'fields' => 'id, name, birthday, created',
    'joins'  => array(
        array(
            'type'       => 'LEFT',
            'alias'      => 'Data',
            'table'      => 'datas',
            'conditions' => "User.id = Data.users_id"
        ),
    ),
    'options'  => array(
        '?' => array(
            'param' => 'hogehoge'
        )
    )
);

CakePHPでデータを保存する

CakePHPでデータをDBに保存するときは新規登録でも更新でもsaveメソッドを使用する。

■新規登録

新規登録の際は以下のようにcreateメソッドを用いる。

$this->User->create();
$this->User->save($this->data, true, array('name', 'password'));

■更新

更新する際は以下のようにidに更新するレコードの値をセットする。

$this->User->id = 32;
$this->User->save($this->data, true, array('name', 'password'));

CakePHPとMySQLのON UPDATE CURRENT_TIMESTAMPを使う

CakePHPにおいてON UPDATE CURRENT_TIMESTAMPを使用すると、文字列としてCURRENT_TIMESTAMPを格納しようとするので、MySQL側でTIMESTAMPとして処理できない。以下のようにすることで対処できる。

■CURRENT_TIMESTAMP

modifiedというカラム名をつければCakeが自動的に更新日時をセットするので必ずしもON UPDATE CURRENT_TIMESTAMPをセットする必要はない。

■saveメソッド

CakePHP 2系からどうも挙動が変わったようで、1.3系の手法では上手くいかなくなった。以下のように、アップデートして欲しくないカラムにfalseを指定することで対応できる。

$data['modified'] = false;

CakePHP 1.3系

付けたい場合、saveメソッドで保存するカラムを指定することで対処できる。

$this->User->save($this->data, true, array('name', 'birthday'));

上述のようにすることで$this->dataにあらゆるキーとそれに紐付く値が格納されていても、保存されるのはnameカラムとbirthdayカラムのみである。これはアプリケーションをセキュアにする目的でも使用できるので、常時使用することをお勧めする。

GCとメモリ

  • Java VMは参照されているオブジェクトがメモリに存在することを保証する
  • Java VMは参照されなくなったオブジェクトをGCによって回収しメモリ領域を解放する

■GC

オブジェクトはnewを使用して生成されるが、freeやdeallocなど削除を明示しない。

  • 他のオブジェクトを参照するように変更する
  • nullを参照するように変更する
  • メソッドが戻るなどして参照がなくなる

上述のタイミングでGCはオブジェクトを回収する。

GCの実装について

参照カウンタ方式

循環参照に対して機能しない事から殆どのGCの実装は使用していない。

マークアンドスイープ
  1. 直接到達可能なオブジェクトの集合rootを決定する(ローカル変数から参照されているオブジェクトなど)
  2. rootから参照可能なオブジェクトを到達可能とマークする
  3. マークされていない到達可能なオブジェクトがなくなるまで続く
  4. マークされていないオブジェクトを回収する

基本的にはマーク中にプログラムの実行を停止する必要がある。また、仮想マシンの実装により複数のコレクションアルゴリズムから選択される。

注意

  • 新たなオブジェクトのために常にメモリがあることを保証しない(OutOfMemory)
  • 必要のない参照を残すことによってメモリリークを起こす可能性がある

意識しなくて良い事と存在しない事は別の話である。

■ファイナライズ

finalize記述するする必要がないようにするべきである。

protected void finalize() throws Throwable
GCが回収するべきと判断されたときに実行される。どのスレッドで実行されるか分からず実行自体も何ら保証してはいない

明示して記述する場合

記述する場合は以下のように注意する必要がある。

public class ProcessFile {
    private FileReader file;
    public ProcessFile(String path) throws FileNotFoundException {
        file = new FileReader(path);
    }
    public synchronized void close() throws IOException {
        if (file != null) {
            file.close();
            file = null;
        }
    }
    protected void finalize() throws Throwable {
        try {
            close();
        }
        finally {
            super.finalize();// 忘れないように呼び出す!
        }
    }
}

finalizeはコールされることがない可能性を考慮しfinalizeによってコールされるcloseは2回以上コールされても動作する必要がある。finalizeはプログラマーがリソースの解放を忘れた時の安全対策にしかならないと考える必要がある。

■GCを呼び出す

public void gc()
オブジェクトの回収を仮想マシンに要求する
public void runFinalization()
到達不可能なオブジェクトでfinalizeが実行されていないオブジェクトのfinalizeの実行を要求する
public long freeMemory()
システムメモリの空きバイト数を返す
public long totalMemory()
システムメモリ内の全バイト数を返す
public long maxMemory()
仮想マシンが使用するメモリの最大バイト数を返す

以下のようにして

System.gc();
Runtime.getRuntime().gc();

GCを明示して呼び出すことには以下のようなメリットがある。

  • できるだけメモリがフリーの状態で実行できる
  • 処理中にGCが動作する可能性を低減できる

■参照オブジェクト

public T get()
参照オブジェクトのリファレントオブジェクトを返す
public void clear()
参照オブジェクトをクリアして、リファレントオブジェクトを持たないようにする
public boolean enqueue
参照オブジェクトが登録されている参照キューがあれば、キューに参照オブジェクトを追加する。入れられたらtrueを返し、登録されたキューが存在しないか既にキューに入れられていたらfalseを返す
public boolean isEnqueued
参照オブジェクトがキューに入れられていたらtrueを返す

提供されている参照オブジェクトの種類は以下のとおりである。

  • SoftRefernce<T>
  • WeekRefernce<T>
  • PhantomRefernce<T>

用例

以下のようにしてWeakReferenceを使用することができる。

class DataHandler {
    private File lastFile;
    //private WeakReference<File> lastFile;
    private WeakReference<byte[]> lastData;// フィールドで保持
    byte[] readFile(File file) {
        byte[] data;
        if (file.equals(lastFile)) {// ...(a)最後に開いたファイルと同じの場合
            data = lastData.get();
            if (data != null) {// ...(b)最後に返されてからデータがGCされていないばあい
                return data;
            }
        }
        // (a)ファイルを開いていない場合
        // (b)最後に返されてからGCされている場合
        // 新しくファイルを読み込んでlastFileとlastDataに保持する
        data = readBytesFromFile(file);
        lastFile = file;
        //lastFile = new WeakReference<File>(file);
        lastData = new WeakReference<byte[]>(data);
        return data;
    }
}

仮に強い参照でdataを保持した場合、DataHandlerオブジェクトが到達可能な限りdataは回収されない。WeakReferenceを使用することで時々データをディスクから読み出す。IOのコストは増加するがメモリの占有率は低下させることができる。

■参照キュー

Referenceクラスのサブクラスは次の形式のコンストラクタを提供する。

public SoftReference(T referent, ReferenceQueue q)
public WeakReference(T referent, ReferenceQueue q)
public PhantomReference(T referent, ReferenceQueue q)
指定されてたリファレントで新たな参照オブジェクトを作成し、指定されたQueueに登録する

ReferenceQueueクラスはキューから参照を取り除くため、以下の3つのメソッドを提供している。

public Reference poll()
キューから次の参照オブジェクトを取り除いて返す。空ならばnullを返す
public Reference remove() throws InterruptedException
キューから次の参照オブジェクトを取り除いて返す。参照オブジェクトが取り出せるようになるまで待つ
public Reference remove(long timeout) throws InterruptedException
キューから次の参照オブジェクトを取り除いて返す。参照オブジェクトが取り出せるようになるまで待つがtimeoutする

以下のようにリソースのインターフェースを定義する。

interface Resource {
    void use(Object key, Object... args);
    void release();
}

インターフェースを実装しResourceImplを以下のように実装する。

private static class ResourceImpl implements Resource {
    int keyHash;
    boolean needsRelease = false;
    ResourceImpl(Object key) {
        keyHash = System.identityHashCode(key);
        // ...外部リソースの設定
        needsRelease = true;
    }
    public void use(Object key, Object... args) {
        if(System.identityHashCode(key) != keyHash) {
            throw new IllegalArgumentException("wrong key");
        }
        // ..外部リソースの使用
    }
    public synchronized void release() {
        if(needsRelease) {
            needsRelease = false;
        }
        // ..外部リソースの解放
    }
}

以下のようにResourceManagerを実装する。

public final class ResourceManager {
    final ReferenceQueue<Object> queue;
    final Map<Reference<?>, Resource> refs;
    final Thread reaper;
    boolean shutdown = false;
    public ResourceManager() {
        queue = new ReferenceQueue<Object>();
        refs = new HashMap<Reference<?>, Resource>();
        reaper = new ReaperThread();
        reaper.start();
        // ...リソースの初期化
    }
    public synchronized void shutdown() {
        if(!shutdown) {
            shutdown = true;
            reaper.interrupt();
        }
    }
    public synchronized Resource getResource(Object key) {
        if(shutdown) {
            throw new IllegalStateException();
        }
        Resource resource = new ResourceImpl(key);
        Reference<?> reference = new PhantomReference<Object>(key, queue);//
        refs.put(reference, resource);
        return resource;
    }
    class ReaperThread extends Thread {
        @Override
        public void run() {
            while(true) {
                try {
                    Reference<?> ref = queue.remove();
                    Resource resource = null;
                    synchronized(ResourceManager.this) {
                        resource = refs.get(ref);
                        refs.remove(ref);
                    }
                    resource.release();
                    ref.clear();
                }
                catch (InterruptedException e) {
                    break;
                }
            }
        }
    }
}

JavaScriptでベルマンフォード法

■実装

グラフは以下のようにして実装した。グラフは前回のものと同様だがベルマンフォード法では負の重みも存在しうるので、経路が存在しない部分はnullとした。

// Graph //前回のグラフと同じグラフを使用
var vertexes = [
     [null, 1,    3,    null, null, null],// vertex s
     [null, null, 1,    4,    null, null],// vertex 1
     [null, null, null, 1,    null, null],// vertex 2
     [null, null, null, null, 1,    10],//   vertex 3
     [null, null, null, null, null, 1],//    vertex 4
     [null, null, null, null, null, null] // vertex g
];

// initialize
var dist    = [];
var pred    = [];
for(var i = 0; i < vertexes.length; i++){
    dist[i] = Number.POSITIVE_INFINITY;
    pred[i] = -1;
}
dist[0] = 0;// start

// 計算
(function(){
    var n = vertexes.length;
    for(var i = 1; i <= n; i++){
        // i=nの時
        // destinationと自身が同じであるにも関わらず
        // 後述のnewLenの方が短い場合、
        // 負の閉路が存在していることを示す
        failOnUpdate = (i === n);
        leaveEarly   = true;
        for(var u = 0; u < n; u++){
            for(var ci = 0; ci < n; ci++){
                if(vertexes[u][ci] === null){continue;}//経路が存在しない
                var newLen = dist[u] + vertexes[u][ci];//頂点uを経由したciへの距離
                if(newLen < dist[ci]){// 頂点u経由の方が短い場合はアップデートする
                    if(failOnUpdate){
                        throw new Error('Graph has negative cycle');
                    }
                    dist[ci] = newLen;
                    pred[ci] = u;
                    leaveEarly = false;
                }
            }
        }
        if(leaveEarly){
            break;
        }
    }
})();

// 結果表示
console.log("最短経路");
console.log(pred);// [-1, 0, 1, 2, 3, 4]
console.log("最短距離");
console.log(dist);// [0, 1, 2, 3, 4, 5]

但し、上述の場合では負の重みが存在しない。

負の重み

従って以下のデータでテストしてみる。

var vertexes = [
     [null, 1,    3,    null, null, null],// s
     [null, null, 1,    4,    null, null],// 1
     [null, null, null, 1,    null, null],// 2
     [null, null, null, null, 1,    -10],// 3
     [null, null, null, null, null, 1],// 4
     [null, null, null, null, null, null] // g
];

以下のような結果となる。

console.log("最短経路");
console.log(pred);// [-1, 0, 1, 2, 3, 3]
console.log("最短距離");
console.log(dist);// [0, 1, 2, 3, 4, -7]

3→gの距離が4を経由するよりも近くなり、s〜gの重みは-7となった。

負の閉路

以下のように閉路(ループ)で総和が負の値になるような経路を用意する。

var vertexes = [
     [null, -1,   3,    null, null, null],// s
     [null, null, -10,  4,    null, null],// 1
     [5,    null, null, 1,    null, null],// 2
     [null, null, null, null, 1,    -10],// 3
     [null, null, null, null, null, 1],// 4
     [null, null, null, null, null, null] // g
];

以下のようなエラーが発生する。

//Uncaught Error: Graph has negative cycle

ベルマンフォード法は負の重みがあった場合でも処理が可能であるが、総和が0より小さい負の閉路が存在していた場合は使用できない。但し、そのような場合は最短経路自体が意味を成さない。ちなみに密グラフには向かないアルゴリズムであるので、多次元配列(行列)でのグラフ生成は良くないかもしれない。(●´⌓`●)

overflowがscrollの時にアンカーにsmooth scrollできるjQueryプラグインを作ってみた

久しぶりのjQueryでのちょっとしたスクリプト。


■クライアントコード

// a[href="#target"]をクリックすると#targetに#mainがスムーズスクロールする。
$('a[href="#target"]').click(function(e){
    e.preventDefault();
    $('#main').boxScroll($("#target-wrapper"), $('#target'));
});

こんな感じで使う。気が向いたらブラッシュアップする。

3Dにおける頂点と行列

頂点を表すベクトルに対して乗算する事で以下のような操作ができる行列である。

■移動行列

 1,  0,  0, 1
 0,  1,  0, 0
 0,  0,  1, 0
dx, dy, dz, 1

■拡大縮小行列

zx,  0,  0, 0
 0, zy,  0, 0
 0,  0, zz, 0
 0,  0,  0, 1

■回転行列

XX(1 - cosθ) +  cosθ,  XY(1- cosθ) - Zsinθ,  XZ(1 - cosθ) + Ysinθ, 0
XY(1 - cosθ) + Zsinθ,  YY(1- cosθ) -  cosθ,  YZ(1 - cosθ) + Xsinθ, 0
XZ(1 - cosθ) + Ysinθ,  ZY(1- cosθ) + Xsinθ,  ZZ(1 - cosθ) +  cosθ, 0
                   0,                    0,                     0, 1