IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

サーバーサイド jQuery をやってみる!

最近、社内で PHP Spidermonkey が流行って(?)いるようです><!

Cybozu Inside Out: SpiderMonkeyを使ってPHPでサーバーサイドJavaScript
id:ama-ch さすがです><

というわけで

僕も、 PHPSpidermonkey でどのくらいのことが出来るのか試してみました><

まず、 Hello, world!

<?php

// new して
$js = new JSContext();

// print 関数作って
$js->registerFunction(function($v) { print $v; }, 'print');

// こんにちはこんにちは!
$js->evaluateScript('print("Hello, world!!")');
Hello, world!

おおお、簡単!

次は、 id:m-hiyama さんが作った minidom.js を読み込んでみる!

minidom.js は Pure JavaScript による DOM の実装です!
詳しくはこちら
100% JavaScriptによる、簡易なDOM/SAXの実装 - 檜山正幸のキマイラ飼育記

<?php

$js = new JSContext();
$js->registerFunction(function($v) { print $v; }, 'print');

// minidom.js を読み込んで
$js->evaluateScript(file_get_contents('http://www.chimaira.org/tools/minidom.js.txt'));

// div 要素を作ってみる!
$js->evaluateScript('print(document.createElement("div").toString(0))');
<div
></div>

なんかでたー!
おおお! minidom.js ++

調子に乗って、 John Resig さんが作った htmlparser.js を読み込んでみる!

htmlparser は Pure JavaScript で書かれた HTML パーサーです!
詳しくはこちら
John Resig - Pure JavaScript HTML Parser

<?php

$js = new JSContext();
$js->registerFunction(function($v) { print $v; }, 'print');
$js->evaluateScript(file_get_contents('http://www.chimaira.org/tools/minidom.js.txt'));

// htmlparserjs を読み込む!
$js->evaluateScript(file_get_contents('http://ejohn.org/files/htmlparser.js'));

// JavaScript を用意!
$script = <<< END
    HTMLParser("<html><body>foo<div>bar</div>baz</body></html>", {
        start: function(tag) {
            print(tag + "(start)\\n");
        },
        chars: function(text) {
            print(text + "(text)\\n");
        },
        end: function(tag) {
            print(tag + "(end)\\n");
        }
    })
END;

// 実行!!1
$js->evaluateScript($script);

どきどき

html(start)
body(start)
foo(text)
div(start)
bar(text)
div(end)
baz(text)
body(end)
html(end)

おおおお、ちゃんとパースできたあああ

パースして DOM を構築してみる

こんどは、 htmlparser でパースした HTML を minidom.js の DOM 実装に乗せてみる

<?php

$js = new JSContext();
$js->registerFunction(function($v) { print $v; }, 'print');
$js->evaluateScript(file_get_contents('http://www.chimaira.org/tools/minidom.js.txt'));
$js->evaluateScript(file_get_contents('http://ejohn.org/files/htmlparser.js'));

// ちょっと htmlparser と minidom を拡張
$js->evaluateScript(file_get_contents('http://amachang.sakura.ne.jp/misc/php_jquery/fix.js'));

// html を読み込んでみる!
$js->assign('html', file_get_contents('http://amachang.sakura.ne.jp/misc/php_jquery/pasee.html'));

// パース実行!
$js->evaluateScript('parseHtml(html, document);');

// toSource で DOM 構造を見て見る!
print $js->evaluateScript('document.documentElement.toSource()');
#2={_root:#1={_root:#1#, ownerDocument:#1#, parentNode:null, nodeType:9, nodeName:"#document", _name:"main", _idMap:{}, childNodes:[#2#], firstChild:#2#, lastChild:#2#, documentElement:#2#, implementa ....

おおお、なんかパースできてそう!!

jQuery を使ってみる!

でっきるかなー!

<?php

$js = new JSContext();
$js->registerFunction(function($v) { print $v; }, 'print');
$js->evaluateScript(file_get_contents('http://www.chimaira.org/tools/minidom.js.txt'));
$js->evaluateScript(file_get_contents('http://ejohn.org/files/htmlparser.js'));
$js->evaluateScript(file_get_contents('http://amachang.sakura.ne.jp/misc/php_jquery/fix.js'));
$js->assign('html', file_get_contents('http://amachang.sakura.ne.jp/misc/php_jquery/pasee.html'));
$js->evaluateScript('parseHtml(html, document)');

// jquery を読み込む!
$js->evaluateScript(file_get_contents('http://code.jquery.com/jquery.js'));

// JavaScript を実行!
print $js->evaluateScript('Array.join($(".hoge"), "\n")');
<div class="hoge"
>foo</div>
<div class="hoge"
>bar</div>
<div class="hoge"
>baz</div>

きたーヽ(;'□')ノ

というわけで

なんとか PHPSpidermonkeyjQuery を動かすことが出来ました!ぱちぱちー
でも、適当かつ無理やり動かしてるので jQuery でも動かない機能が多々あると思います><
ごめんなさいごめんなさい><
まあ、ともあれ JavaScript がサーバーサイドで動くってーのはめっちゃ楽しいですね!
ではではーヽ(`・ω・´;)ノ

Java でアスキーアート画像を生成する

要は

文字列を指定された *.ttf ファイルを使って画像化したい!!
こんな画像作りたい

と言う訳で書いてみました

"-D" オプションでいろいろ設定できます。

import java.lang.String;
import java.lang.System;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.File;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.awt.GraphicsEnvironment;
import java.awt.Font;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.RenderingHints;
import javax.imageio.ImageIO;

public class CreateImage {

  private static String P(String name, String def) {
    return System.getProperty(name, def);
  }
 
  public static void main(String[] args) throws Exception {

    final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in, P("charset", "UTF-8")));
    final Collection<String> list = new LinkedList<String>();

    final int width = Integer.parseInt(P("width", "600"));
    final int height = Integer.parseInt(P("height", "400"));

    GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
    Font font = Font.createFont(Font.TRUETYPE_FONT, new File(P("fontpath", "ipagp-mona.ttf")));
    env.registerFont(font);

    String line;
    while ((line = reader.readLine()) != null) {
      list.add(line);
    }   
    final int lineLength = list.size();
    final float fontHeight = height / lineLength;

    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    Graphics2D g = image.createGraphics();
    g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VBGR);
    g.setColor(Color.WHITE);
    g.fillRect(0, 0, image.getWidth(), image.getHeight());
    g.setColor(Color.BLACK);
    g.setFont(font.deriveFont(fontHeight));

    Iterator<String> it = list.iterator();
    for (int i = 0; it.hasNext(); i++) {
      g.drawString(it.next(), 10, i * fontHeight + fontHeight);
    }   

    ImageIO.write(image, "png", new File(P("output", "hoge.png")));
  }
}

Jython がおもしろい

仕事で Jython を使う機会があって

ほぼ、初めて Jython を触ったんですけど、めっちゃおもしろい
Java のクラスが何も考えずに使えちゃう。
たとえば、 HTML (not XHTML) をパースして XPath で取得するコードとかを nekohtml と xalan で以下のように書ける

from java.io import FileInputStream
from org.xml.sax import InputSource
from org.cyberneko.html.parsers import DOMParser
from org.apache.xpath import XPathAPI

# input
source = InputSource(FileInputStream('test.html'))
source.setEncoding('UTF-8')

# parse
parser = DOMParser()
parser.parse(source)
doc = parser.getDocument()

# xpath evaluate
print XPathAPI.selectNodeList(doc, '/HTML/BODY/*').item(0)

ほんと楽に Java のクラスが使えて感動した。
てか、このサンプル java のクラス以外使ってないっていう。
後は id:nishiohirokazuJython 本を買えば完璧ですね!

Java でラムダ

λ... λ... ちょっととおりますよ

はじめに

C++ Template の勉強をしていて、気がついた。

  • ネストした(内側から外側が見える)名前-値の空間が存在し
  • 値から名前-値の空間を生成することが可能で
  • その空間を生成するための情報を値として扱え
  • 名前-値の空間の中の一つ以上の値を取り出せると

ラムダが出来る。

という訳で Java でラムダを作ってみた

import static java.lang.System.out;

public class Hoge {
    public static void main (String args[]) {

        // チャーチ数 0
        final λ zero = new λ () { λ call (final λ f) {
            return new λ () { λ call (final λ x) {
                return x;
            }};
        }};

        // チャーチ数の succ
        final λ succ = new λ () { λ call (final λ n) {
            return new λ () { λ call (final λ f) {
                return new λ () { λ call (final λ x) {
                    return f.call(n.call(f).call(x));
                }};
            }};
        }};

        // チャーチ数の plus
        final λ plus = new λ () { λ call (final λ n) {
            return new λ () { λ call (final λ m) {
                return new λ () { λ call (final λ f) {
                    return new λ () { λ call (final λ x) {
                        return m.call(f).call(n.call(f).call(x));
                    }};
                }};
            }};
        }};

        out.println(
            // ((0++)++)+((0++)+((0++)++))
            // 2+(1+2)
            // 2+3
            // 5
            plus.call(succ.call(succ.call(zero)))
                .call(
                    plus.call(succ.call(zero))
                    .call(succ.call(succ.call(zero))))

                    // int 化
                    .call(new λ () { λ call (final λ b) { 
                        return new V () { int v() { return b.v() + 1; }};
                     }})
                    .call(new V() { int v() { return 0; }})
        );

        // y combinator
        final λ y = new λ () { λ call (final λ f) {
            return new λ () { λ call (final λ g) {
                return new λ () { λ call (final λ m) {
                    return f.call(g.call(g)).call(m);
                }};
            }}.call(new λ () { λ call (final λ g) {
                return new λ () { λ call (final λ m) {
                    return f.call(g.call(g)).call(m);
                }};
            }});
        }};

        // fibonatti
        final λ fib = y.call(new λ () { λ call (final λ fib) {
            return new λ () { λ call (final λ a) {
                return (a.v() < 2) ? a : new V () { int v() {
                    return fib.call(new V() {int v() {
                            return a.v() - 1;
                        }}).v() + 
                        fib.call(new V() { int v() {
                            return a.v() - 2;
                        }}).v();
                }};
            }};
        }});

        out.println(
            // fib(10) = 55
            fib.call(new V () { int v() { return 10; }})
        );
    }
}

abstract class λ {
    abstract λ call(λ a);
    λ call(V a) { return this.call((λ)a); }
    int v() { throw new RuntimeException(); }
}

abstract class V extends λ {
    λ call(λ a) { throw new RuntimeException(); }
    abstract int v();
    public String toString() {
        return "" + this.v();
    }
}

Java のλの特徴

見ての通り、無名クラスはそのスコープの final な変数を扱えるのでそれを使ってネストした名前-値の空間が出来る。
しかも、 final しか扱えないのでインスタンスフィールドを使わなければ参照透明だ。
ただ、 a = λx.a(x -1) のように再帰的なことを書こうと思うと、コンパイル時に「a がまだ初期化されてない」と怒られるので y コンビネータが必要。

(追記) 更なる勉強

コメント欄で id:alohakun に教えて貰った 結論:結局、Javaはクロージャを使えるの? - lethevert is a programmer のコメント欄がめちゃめちゃ面白いです。

Java 5 の型推論が分からない

なんぞこれ><

Javaの型推論Utilsクラス - yukobaのブログ
ってことでやってみた

これは OK

import java.util.ArrayList;
import static java.lang.System.out;

public class Main {

    public static <S, T> T cast(S o) { return (T) o; }

    public static void main(String[] args) {
        ArrayList a = new ArrayList();
        ArrayList<String> b = cast(a);
        b.add("hoge");
        b.add("fuga");
        out.println(b);

        Hoge c = cast(a); // キャストできない場合は実行時例外になる
    }
}

class Hoge {}
$ javac -Xlint:unchecked Main.java && java Main
Main.java:7: warning: [unchecked] unchecked cast
found   : S
required: T
    public static <S, T> T cast(S o) { return (T) o; }
                                                  ^
1 warning
[hoge, fuga]
Exception in thread "main" java.lang.ClassCastException: java.util.ArrayList
	at Main.main(Main.java:22)

でもなんでこれはダメ?

import java.util.ArrayList;
import static java.lang.System.out;

public class Main {

    public static <S, T> T cast(S o) { return (T) o; }

    public static void hoge(ArrayList<String> a) {
        a.add("piyo");
    }

    public static void main(String[] args) {
        ArrayList a = new ArrayList();

        hoge(cast(a));

        out.println(a);
    }
}
$ javac -Xlint:unchecked Main.java && java Main
Main.java:6: warning: [unchecked] unchecked cast
found   : S
required: T
    public static <S, T> T cast(S o) { return (T) o; }
                                                  ^
Main.java:15: hoge(java.util.ArrayList<java.lang.String>) in Main cannot be applied to (java.lang.Object)
        hoge(cast(a));
        ^
1 error
1 warning

hoge の引数の型 ArrayList が 型変数 T に与えられて、 a の型 ArrayList が型変数 S に与えられて OK なんじゃないの><?

まとめ

わからない><

わーい \(^o^)/ JS で書ける IRC ボットライブラリできたよー

どうしても IRC ボットを JavaScript で作りたくて

Rhino を勉強したよ!><

できたよー\(^o^)/

使い方

  1. まずここから js.jar (Rhino) を拾ってくる -> http://www.mozilla-japan.org/rhino/download.html
  2. 次に pircbot.jar (PircBot) を拾ってくる -> PircBot - Java IRC Bot Framework (Java IRC API for Bots)
  3. 次に これ を org/coderepos/botchang/ ってディレクトリに入れて javac でコンパイルする!
  4. で、以下みたいな JS のファイルを作る
defineClass('org.coderepos.botchang.Botchang');

// ニックネームとエンコードの名前
var botchang = new Botchang('botchang', 'utf-8');

// verbose (デバッグ用
botchang.verbose = true;

// 接続
botchang.connect('irc.freenode.net');

// チャンネルに join する
botchang.joinChannel('#1981');

// メッセージハンドラ
botchang.onMessage = function(channel, sender, login, hostname, message) {
    if (message.match(/^botchang/)) {
        botchang.sendMessage(channel, 'My name is botchang');
    }
    else if (message.match(/^change nick:(.*)$/)) {
        botchang.nick = RegExp.$1; // dynamic change nick
    }
};

// たとえば、 Notice をハンドリングしたかったら以下のように関数追加
botchang.onNotice = function(channel, sender, login, hostname, notice) {
//...
};

で、最後に org.mozilla.javascript.tools.shell.Main を java コマンドで実行してこの JS を渡してやるだけ!><*1

java -cp ".:js.jar:pircbot.jar" org.mozilla.javascript.tools.shell.Main sample.js

ちなみに

onMessage や onNotice や sendMessage や nick の変更だけじゃなくて、いろいろできます><
短いからソース読んでみて><

あと、

ぜんぜんテストしてないよー! CodeRepos だからみんなテストとかしてしてー

たのしー

\(^o^)/

*1:classpath に注意してね