2010年8月30日月曜日

表計算 (Excel, OpenOffice Calc) における移動系のショートカットキー

シートがたくさんある表を見る場合、移動系のショートカットキーを覚えた方が操作が楽になる。

基本 Ctrl キーを押しながら…

  1. Page Down , Page Up : シートの移動
  2. Home, End               : 左端・右端へ
  3. 矢印キー                   : 連続するデータのジャンプ

08-29-20104

詳しくは、Excel のショートカット キーとファンクション キーについて - Excel - Microsoft Office より

シートの移動

Ctrl + PageDown キーを押すと、ブック内で次のシートに移動します。

Ctrl + PageUp キーを押すと、ブック内で前のシートに移動します。

ワークシート内の左端・右端へ

Ctrl + Home キーを押すと、ワークシートの先頭に移動します。

Ctrl + End キーを押すと、ワークシートの最も下の行の右端の列にある最後のセルに移動します。カーソルが数式バーにあるときは、文字列の末尾にカーソルを移動します。

連続するデータの移動

Ctrl キーを押しながら方向キーを押していくと、ワークシート内の現在のデータ範囲 (データ範囲: データが入力されていて、周囲が空白セルまたはシートの端で囲まれているセル範囲。) の先頭行、末尾行、左端列、または右端列に移動します。

Java で型変数を利用して Cons , Nil クラスによるリスト表現 – map, filter, foldr, foldl の実装

Haskell でリストに対する関数を考えるときの視点」 のつづき

Haskell のリストに対応した Java クラス

前回は Haskell のリストに対応したクラスを定義し、リストの大きさを返すメソッド length を実装した。

08-29-20102

このクラスを使い、map, filter, foldr, foldl の実装を考える。

ここで予め Main クラスで利用するリストを作成しておく。以降この変数 list を使う。

IList<Integer> list = new Nil<Integer>().cons(4).cons(3).cons(2).cons(1);
System.out.println(list);    // 1 2 3 4 

 

累積変数を使った length メソッド

まずは、前回実装した length 関数を、末尾再帰で定義したときのような格好の実装を追加してみる。

Cons クラス...

    public int length(int acc) {
        return this.xs.length(acc + 1);
    }

累積変数 acc を導入し、そこへ要素の数を累積させる。

リストの末尾の Nil クラスでは、累積変数を返す。

    public int length(int acc) {
        return acc;
    }

Main クラスでこれを使ってみる。

        System.out.println(list.length(0));  // 4

 

map

次に、リストの要素に対して関数を適用する map 関数に相当するメソッドを定義する。

Haskell での map 関数の型は、

map :: (a -> b) -> [a] -> [b]

第1引数は、要素を別の型の値へ変換する関数。

Java では 「関数」 を関数の引数へ渡すことができないので、オブジェクトを代わりに渡し Strategy パターンで対応。オブジェクトの実装するインターフェイスを IUnaryOp として以下のように宣言し、利用するときはインターフェイスを実装した匿名クラスを作成する。

public interface IUnaryOp<A,B> {
    public B apply(A a);
}

apply 関数は、引数に A 型の値を与えると B 型の値が返ってくる。これは Haskell の map 関数の第1引数 (a –> b) に相当。

IList インターフェイスに map メソッドの宣言を追加。

    public <B> IList<B> map(IUnaryOp<A, B> f);

上記の型変数 B は、このインターフェイスを実装したクラスを生成するときではなく、このメソッドを使うときに決まるので、メソッドで型変数を宣言する。 ( cf. Java の型変数 とイレイジャ )

上記に伴ない Cons クラスで map メソッドを実装。

    public <B> IList<B> map(IUnaryOp<A, B> f) {
        return this.xs.map(f).cons(f.apply(this.x));
    }

Nil クラスにも map メソッドを実装。

    public <B> IList<B> map(IUnaryOp<A, B> f) {
        return new Nil<B>();
    }

これを Main クラスで使用する。

// map : 要素を 2 倍する
System.out.println(list.map(
        new IUnaryOp<Integer, Integer>() {
            public Integer apply(Integer a) {
                return a * 2;
            }
        }));     // 2 4 6 8

 

filter

filter 関数は述語を与え、リストから要素を抽出する関数。

Haskell の filter 関数の型は、

filter :: (a -> Bool) -> [a] -> [a]

第1引数は、ある型の値を渡すと Bool 型を返す述語

先ほどの map メソッドのように、filter 関数の第1引数に相当する述語に対応したメソッドを持つオブジェクトのインターフェイスを IPredicate とし、メソッドを宣言。

public interface IPredicate<A> {
    public boolean apply(A a);
}

apply メソッドは、引数に型 A の値を与えると boolean が返る。これは Haskell の filter 関数の第1引数 (a –> Bool) に相当する。

IList インターフェイスに filter メソッドの宣言を追加。

    public IList<A> filter(IPredicate<A> p);

上記に伴ない Cons クラスで filter メソッドを実装。

    public IList<A> filter(IPredicate<A> p) {
        return p.apply(this.x) == true
                ? this.xs.filter(p).cons(this.x)
                : this.xs.filter(p);
    }

Nil クラスにも filter メソッドを実装。

    public IList<A> filter(IPredicate<A> p) {
        return new Nil<A>();
    }

Main クラスでこれを使う。

// filter : 偶数を抽出
System.out.println(list.filter(new IPredicate<Integer>() {
    public boolean apply(Integer p) {
        return p % 2 == 1;
    }
}));     // 1 3

 

foldr

forldr はリストの右側から二項演算子により特定の型の値に畳み込む関数。

Haskell の foldr の型は、

foldr :: (a -> b -> b) -> b -> [a] -> b

これまでと同様、foldr の第1引数の関数に相当するメソッドを持つオブジェクトを生成するためにインターフェイス IBinaryOp1 を宣言。今回は二項演算子なので、引数が一つ増える。

public interface IBinaryOp1<A, B> {
    public B apply(A a, B b);
}

apply メソッドの引数には型 A の値と型 B の値を与えると型 B の値が返る。これは Haskell の foldr 関数の第1引数の型 (a –> b –> b) に相当する。

IList インターフェイスに foldr メソッドの宣言を追加。このとき Haskell の foldr の型と比較しながら宣言を考えるとよい。

    public <B> B foldr(IBinaryOp1<A, B> f, B z);

上記に伴ない Cons クラスに foldr メソッドを実装。

    public <B> B foldr(IBinaryOp1<A, B> f, B z) {
        return f.apply(this.x, this.xs.foldr(f, z));
    }

Nil クラスにも foldr メソッドを実装。

    public <B> B foldr(IBinaryOp1<A, B> f, B z) {
        return z;
    }

これを Main クラスで使う。

// foldr : 要素を足し合わせる
System.out.println(list.foldr(new IBinaryOp1<Integer, Integer>() {
    public Integer apply(Integer a, Integer b) {
        return a + b;
    }
}, 0));                 // 10

// foldr : リストのコピー
System.out.println(list.foldr(
        new IBinaryOp1<Integer, IList>() {
            public IList apply(Integer a, IList b) {
                return b.cons(a);
            }
        },
        new Nil()));    // 1 2 3 4

 

foldl

foldr が右から畳み込むのに対して、foldl は左から畳み込む関数。

Haskell の foldl 関数の型は、

foldl :: (a -> b -> a) -> a -> [b] –> a

よく見ると foldr と型が少し違うのがわかる。

foldr :: (a -> b -> b) -> b -> [a] -> b

foldr のときと同様に、第1引数の関数に相当するメソッドを持つオブジェクトのインターフェイスを IBinaryOp2 とする。

public interface IBinaryOp2<A, B> {
    public A apply(A a, B b);
}

apply メソッドは引数型 A の値と型 B の値を与えると、型 A の値が返る。これは Haskell の foldl の第1引数 (a –> b –> a) に相当する。

IList インターフェイスに foldr メソッドを追加。ただし、foldr のときのように Haskell の型と完全に同じようになっていないことに注意。理由は、Cons クラスで実装したところを見るとわかる。

    public <B> B foldl(IBinaryOp2<B, A> f, B z);

上記に伴ない Cons クラスに foldr メソッドを実装。

    public <B> B foldl(IBinaryOp2<B, A> f, B z) {
        return this.xs.foldl(f, f.apply(z, this.x));
    }

特に第1引数が

IBinaryOp2<A, B>

ではなく、

IBinaryOp2<B, A>

であることに気をつける。

後者の場合、IBinaryOp2 インターフェイスに照らし合わせると、メソッド apply の型は次のようになる。

public B apply(B b, A a)

このように型変数名が変わっても本質的な意味は同じ。

しかし、これを Cons クラスのメソッドのシグニチャに用いると意味がそれぞれ異なる。理由は、Cons クラスが持つ型変数が A であり、この A と上記 apply メソッドの型変数名 A が一致するので、これによりメソッド全体において型変数 A の意味が定まるから。 (多分… ^^; 色々と試して動いたのがこれだったので、後付けで理由を考えた。)

Nil クラスにも foldr メソッドを定義。

    public <B> B foldl(IBinaryOp2<B, A> f, B z) {
        return z;
    }

これを使ってみる。

// foldl : 要素を足し合わせる
System.out.println(list.foldl(
        new IBinaryOp2<Integer, Integer>() {
            public Integer apply(Integer a, Integer b) {
                return a + b;
            }
        },
        0));       // 10

// foldl : 要素を掛け合せる
System.out.println(list.foldl(
        new IBinaryOp2<Integer, Integer>() {
            public Integer apply(Integer a, Integer b) {
                return a * b;
            }
        },
        1));       // 2

 

メソッドチェーン

最後に上記 map, filter, foldr でメソッドチェーン。

// メソッドチェーン : filter => map => foldr :
System.out.println(list.filter(new IPredicate<Integer>() {
    public boolean apply(Integer p) {
        return p % 2 == 0;     // 偶数を抽出
    }
}).map(new IUnaryOp<Integer, Integer>() {
    public Integer apply(Integer a) {
        return a * 3;          // 3 をかける
    }
}).foldr(new IBinaryOp1<Integer, Integer>() {
    public Integer apply(Integer a, Integer b) {
        return a * b;          // 掛け合わせる
    }
}, 1));     // 72

結論は、こんなゴチャゴチャしたコードは書きたくないということで。(+_+)

型を書くのが面倒なので型推論してくれぇ~。

てゆうか、これからの実用所はやっぱ Scala っすか ?

 

ここまでのコード

クラス図。(メソッドにおける型変数を無理矢理書いてしまったけれど。)

08-29-20103

 

関連記事

Haskell でリストに対する関数を考えるときの視点 - オブジェクト指向からの類推

Haskell のリストに対する関数の定義

データコンストラクタによる値の生成

Haskell ではリストが特別扱いされている。(3.7 リスト)

リストを生成したいときは、

[0,1,2,3]

のように書く。

他の代数的データ型と違い、組込みの構文を使って表現できるため、最初リストにデータコンストラクタがあることに気がつかなかった。

データコンストラクタを使って書くなら、

0:1:2:3:[]

しかし、これまた随分長いこと

(:)

がデータコンストラクタであるという認識なし。二項演算子 (:) は、要素とリストをくっつけるために標準で用意されている一般的な関数だと思っていた。 ^^; (cf. Haskell の cons (コンス) )

更に言えば、

[]

もデータコンストラクタだと思わず。なんだかよくわからないけれど、要素のないリストを表わすのに [] を使うと覚えていただけ。

大学で情報系を専攻していたら、Lisp や Scheme などの所謂王道 (?) を学んだろうから、リストを生成するための cons は馴染深く、上記のような疑問を抱かなかったはず。いかんせん心理学だったもので、 Lisp は認知系の教科書の片隅にあった AI に関したコラムで見たのみ。その頃、括弧だらけの変態プログラミング言語だと思っていた。

 

関数を定義するときの場合分けはなぜ?

ところで、リストに関する関数の定義を見ると、

  • 空の場合
  • 要素がある場合

に場合分けるすることが多い。

例えば、リストの長さを返す関数を定義したいなら、

length []     = 0
length (x:xs) = 1 + length xs

「空リスト」 と 「空ではないリスト」 の場合に定義が分けられている。

リストの先頭要素を返す Prelude の head 関数 を見ても同じ。

head                    :: [a] -> a
head (x:_)              =  x
head []                 =  badHead

badHead :: a
badHead = errorEmptyList "head"

「空リスト」 に head を適用するとエラーを返すだけなのだけれど、定義がキチンとされている。

もちろん、定義してなくても動作しないことはない。その場合、

Non-exhaustive patterns in function head

というように、パターンマッチで失敗したことが通知される。

しかし、実装では空リストに対して head を適用すると、以下のエラーが返される。

*** Exception: Prelude.head: empty list

length 関数の定義に話を戻す。

はじめてこの定義を見たとき、どうも腑に落ちなかった。直感的に理解できないと言うか、定義が十分であることを肌で感じ取れないというか…。 (+_+)

命令型の言語にどっぷり浸っていたので、for 文のような繰り返しのための制御文で大きさを数えられないのかな?と最初思ったり。

 

宣言的という意味は?

関数型言語の説明としてよく聞くのが、

… 処理方法ではなく対象の性質などを宣言することでプログラミングする…

( 宣言型プログラミング - Wikipedia より )

A program that describes what computation should be performed and not how to compute it

( Declarative programming - Wikipedia より、太字は引用者による )

というもの。これが何を意味しているのかはさて置き、額面通り素直に受け取るなら、lenght の空リストに対する定義は、

length []     = 0

次のように解釈できる。

空リストの 「大きさ」 という性質は 0

これに対し、「空ではないリスト」 の定義はどう解釈したら性質を宣言していると言えるのだろう?

length (x:xs) = 1 + length xs

例えば、

リストの大きさは、先頭要素以外のリストの「大きさ」に 1 足した値

一見わかりずらい日本語。

ここで最初に気になったのは、パターンマッチでリストを 「先頭」 と 「残りの要素」 に分けて考えている処理。何となく what というよりは how を意識しているような気がするので、果たしてこれが性質を表わしていると言えるのか。…てゆうか、そもそも何をもって what による定義であると見なせるのかわからない。

また、再帰的な定義は、性質 (what) を記述しているのか、それとも処理方法 (how) を示しているのかどうかも判断できず。 (@_@;

ここまでをまとめると、以下の 3 点が不明。

  • リストに対する関数で場合分けをすること
  • how ではなく what により計算を表現すること
  • 再帰的な定義が上記 2 点から見て何を意味するか

 

Control flow

まずは、宣言的なプログラミングについてもう少し調べる。

Declarative programming - Wikipedia によると、

declarative programming is a programming paradigm that expresses the logic of a computation without describing its control flow.[1]

上記 Control flow - Wikipedia とは、

control flow (or alternatively, flow of control) refers to the order in which the individual statements, instructions, or function calls of an imperative or a declarative program are executed or evaluated.

宣言的なプログラミングとは、「あれやってから、これやって…」 というように順序を記述することなく、計算のロジックを表現する方法だと。確かに例として挙げられている SQL の SELECT は、順序を指定することなく欲しいデータを取得できる。

Control flow に関しては、Neil Mitchell's Haskell Blog: Functional Flow Control の説明もわかりやすい。

In normal programming languages, there are many keywords for flow control, such as for, while etc. These flow control keywords encode a particular pattern of iteration, such as looping over a range (in the case of for) or continuing until some condition holds (while). Imperative programming languages continue to add more iteration keywords: both C#/Java have introduced some form of for-each; Python/C# have yield; Ada has many variants.

( 太字は引用者による。)

for や while などの control flow を表わすキーワードは、特別な計算のパターンを符号化していると。

計算のパターンに関して、CiNii 論文 -  関数プログラミングの実際 (<特集>関数型プログラミングと計算の基礎) では次のように述べられている。

構造的プログラミングは... 洗練された制御構造とデータ構造を用いてプログラムを設定・作成すべきであるという教えであり...
手続き型言語の文の構造化機構 ( if e then c1 else c2 や while e do c など ) とデータ構造化機構 ( array[T1] of T2 や record...end など) によって、文やデータの構成要素をより大きな構成単位に組み上げるという機能を利用している。つまり、これらは構成要素を結びつける糊(glue)の役割を果たしている...

関数プログラミングにおいては、関数を組み合わせる関数がプログラムの構成要素を結合する糊になる...

手続き型の糊の弱さのひとつに、そこでは構成要素を結合する際の計算のパターン、たとえば if e then c1 else c2 が制御の流れしか捉えていないということがある。

関数プログラミングの考え方は計算のパターンを抽象化して捉え、そこで成り立つ法則を用いてプログラムを合成したり、その性質を調べたりすることが特徴的である。... 計算パターンの性質は個々の構成要素とは独立しており、それ自体としても関数としての存在意義をもっている。...
関数 map のように、計算パターンを関数によって抽象することができる...
内包表記を map とほかのいくつかの関数を用いた式に変換することもできる...
左右のそれぞれの「たたみ込み(fold)」を行う計算パターンも一般的である。...
計算パターンを表わす関数の形式的な定義は再帰的に行う...

つまり、関数プログラミングでは、制御の流れも関数により表現し、そういった計算のパターンは再帰的に定義することができると。

先ほど 「リストの大きさを知りたいとき、for ループで数え上げればいいじゃん」 と反射的に思ったのは、「制御の流れを示す flow control を使う」という命令型の考え方であると言える。

ついでなので、length 関数を foldr や map などの計算のパターンを表現した関数を使って定義するなら、

length = foldr (\_ acc -> acc+1)  0

length = sum . map (\_ -> 1) 

数え上げるという意味では、末尾再帰により特定の変数にカウンタの役割を持たせた方が自然なのかも。

length xs = length' xs 0
    where
      length' [] acc     = acc
      length' (x:xs) acc = length' xs (acc+1)

話を元に戻して、上記から考えると、

length (x:xs) = 1 + length xs

の定義は、「あの計算をしてから、この計算をする」 ということが明示的に述べらていないことから宣言的な定義であると言えそうな気もするし、イヤイヤ、再帰的な定義そのものが実際には制御の流れを統制している計算のパターンだから、この部分が計算の how を表現しているとも言えそうな気がする。

それにしても、このままwhat と how について考えても length 関数の直感的な理解へと進みそうにない。また、制御の流れを意識しない定義は、定義としては満足かもしれないけれど、どうしてもイメージとして府に落ちないし、しっくり来ない。これは命令型脳であることの証左なのか。。

ともあれ、先ほど挙げた疑問の内、what / how について考えるのは諦め、以下の 2 点について直感的なイメージができるようにしたい。

  • リストに対する関数の場合分けをする意味
  • how ではなく what により計算を表現すること
  • 再帰的な定義が何を意味するか

 

Java で cons 相当のクラスを定義して Haskell のリストを再考

自分の場合、直感的な理解ができたと感じるのは、オブジェクト指向からの類推で何が何に相当するのか対応付けができたとき。もう少し正確に言うなら、計算を担当する小人さんを想定し、小人たちのコラボレーションをイメージできた場合。 独立したモジュールを想定し、影響を与える範囲をほどほどに限定して、各々に役割を与え詳細を隠蔽。 「あれして、これして」 と各々に頼み、自分が知っている情報を用いて計算を行ってもらうというモデルは素朴でわかりやすい。

 

Cons クラスを元にリストを定義

では、Java で Haskell のリストのような構造を定義する場合、どのように考えればいいのだろう?Haskell のリストは特別に組込まれているが、代数的データ型で定義できるとしたら以下の通り。

data  [a]  =  [] | a : [a]  deriving (Eq, Ord)

( 6.1.3 リスト より )

2 つのデータコンストラクタからリスト型の値が作られる。

  1. 空リスト
  2. 型 a である要素 と リスト型をフィールドに持つ値

A Gentle Introduction to Haskell: Classes によると、

Haskell のクラスは大筋では Java のインタフェースに類似している。 Java のインタフェース宣言とおなじように、Haskell のクラス宣言はオブジェクトそのものを定義するのではなく、オブジェクトを使う手順を定義 している。

よって Java で定義するなら、リスト型をインターフェイスと見なし、それを実装したクラスが二つ。

  1. 空リストに対応したクラス : Nil
  2. 先頭要素 と 残りの要素に対する参照をフィールドとして持つクラス : Cons

全体として Composite パターン。

 img02-16-2010[1]

リストの大きさを返す length メソッドを実装するなら、IList インターフェイスの定義は、

public interface IList {

    public int length();
}

空リストに相当するクラス Nil は、

public class Nil implements IList {

    /**
     * リストの要素数を返す
     * @return 空リストなので 0
     */
    public int length() {
        return 0;
    }

    @Override
    public String toString() {
        return "Nil";
    }
}

(:) に相当する Cons クラスは、

public class Cons<T> implements IList {

    private T a;        // 先頭要素
    private IList l;    // 残りの要素

    public Cons(T a, IList l) {
        this.a = a;
        this.l = l;
    }

    /**
     * このリストの先頭に要素を追加する
     * @param a 先頭に追加する要素
     * @return 先頭に要素を追加したリスト
     */
    public Cons cons(T a) {
        return new Cons<T>(a, this);
    }

    /**
     * このリストの要素数を返す
     * @return リストの要素数
     */
    public int length() {
        return 1 + this.l.length();
    }

    @Override
    public String toString() {
        return this.a.toString() + "," + this.l.toString();
    }
}

試してみる。

public class Main {

    public static void main(String[] args) {
        Cons<Integer> l = new Cons<Integer>(3, new Nil()).cons(2).cons(1);
        System.out.println(l);
        System.out.println(l.length());
    }
}

結果は、

1,2,3,Nil
3

 

他の実装方法

下図のように Node クラスのオブジェクトを一方向につなぎ、次の Node オブジェクトを指し示していないオブジェクトを末尾と見なすという実装も考えられる。(cf. gist: 307278 - GitHub)

img02-20-2010[2]

 

Haskell の代数的データ型の定義と Java のクラス定義を対比して

しかし、Haskell のリストをオブジェクト指向から類推するとき、むしろ先ほどのように型をインターフェイスと見なし、各々のデータコンストラクタをクラスと対応させる方がしっくり来る。なぜなら、

同じ型だけれど、それぞれが違うもの

ということが印象付けられ、各々の型がどう振る舞うべきかを考えるよう自然に動機付けられるから。つまり、IList インターフェイスにメソッドの宣言を追加したら、それを実装する Nil クラスと Cons クラスにメソッドを実装しなくてはならないことをコンパイラによって嫌でも意識させられる。

img02-16-2010[1] (3)

同じように Haskell で [a] 型の値に対する関数を定義するとき、上記に基いて考えると、データコンストラクタ [] と (:) によって生成される値に対する関数を定義する必要があることに思いが至る。

img02-21-2010[1]

ただし、Haskell の場合、以下のように関数の型を宣言しても、必ずしも [] と (:) に対して定義しなければコンパイル時にエラーが出るわけではないことに注意。

length :: [a] -> Integer
length []     = 0
-- length (x:xs) = 1 + length xs

 

オブジェクト指向で実装するときのイメージ

ところで、先ほどの Haskell と Java の length の実装を比較するとよく似ている。

length []     = 0
length (x:xs) = 1 + length xs

Java の Nil クラス …

    public int length() {
        return 0;
    }

Cons クラス …

    public int length() {
        return 1 + this.l.length();
    }

Java で実装したときの頭の中のイメージは下図の通り。

img02-16-2010[1][5]

  • Nil クラスは要素を持たない。 よって、length の問い合わせで 0 を返す。
  • Cons クラスは、
    • 自分が直接持っている要素は 1 つ。
    • リストに対する参照を持っており、これに対し length の問い合わせができる。その結果に自分が持っている要素の数を 1 つ足して、最終的な lenght の問い合わせに答える。

08-26-20101

この実装方法は、極めて自然に考えることができる。特に、再帰的な関数の呼び出しが、連鎖しているオブジェクトの連続的な呼び出しとして表現されるので、動作しているイメージをしやすい。

 

Haskell をオブジェクト指向的に見ると…

この見方をしたとき、Haskell のデータコンストラクタによるパターンマッチの動作と、再帰的な定義の意味が違って見えるようになった。

ここでリストの値がデータコンストラクによって生成されることを明確に意識するために、リストを代数的データ型で定義する。

data List a = Nil 
            | Cons a (List a) 
              deriving Show

これで Java で定義したインターフェイスとクラスに対応させて考えやすくなる。

List a 型に対して、リストの大きさを返す関数を定義。

length             :: List a -> Int
length Nil         = 0
length (Cons x xs) = 1 + length xs

これに対して次のような見方をする。

img02-18-2010[3]

  1. パターンマッチにおけるデータコンストラクタ Cons をクラスと見なす。
  2. データコンストラクタにより分解されたフィールドの値は、Cons クラスのプライベート変数。
  3. x は Cons クラスが直接持っている値で、xs は List a 型の値への参照。

length 関数を Cons クラスのメソッドと見なし、右辺は Cons クラスのフィールドにアクセスできると考える。関数の適用する対象をオブジェクトと見なすなら、

length (Cons x xs)   =>   (Cons x xs).length()

length xs   =>   xs.length()

のように見立てると対応付けやすい。

Java で型変数を利用して Cons , Nil クラスによるリスト表現 – map, filter, foldr, foldl の実装 」 につづく

 

関連記事

2010年8月24日火曜日

Javascript から見る Ruby のイテレータ - Enumerable

JavaScript のクロージャ と オブジェクト指向 」のつづき

Ruby の Enumerable

Ruby ではコンテナの役割を持つクラスに Enumerable モジュールをインクルードし、その要素を順にブロックに与えるメソッド each を定義することにより、要素に対する便利なメソッドがいくつか使えるようになる。

例えば、「人」は `名前' と `年齢' を属性として持ち、 「グループ」 に所属しているとする。

# 人
class Person
  attr_reader :name, :age
  def initialize(name, age)
    @name, @age = name, age
  end
  def to_s
    @name + " " + @age.to_s
  end
end

# グループ
class Group
  include Enumerable
  def initialize
    @persons = []
  end
  def add(ps)
    @persons << ps; self
  end
  def to_s
    @persons.join(",")
  end
  def each
    @persons.each{|ps| yield ps}
  end
end

g = Group.new.
  add(Person.new("Hanako", 15)).
  add(Person.new("Jiro", 15)).
  add(Person.new("Tarou", 21))

g.each{|ps| p ps}
p g.map{|ps| ps.age * 2}                      #=> [30, 30, 42]
p g.select{|ps| ps.age < 20}                  #=> [Hanako 15, Jiro 15]
p g.inject(0){|result, ps| result + ps.age}   #=> 51
p g.inject(""){|result, ps| result + ps.name} #=> "HanakoJiroTarou"

ここで使われている yield とは、

自分で定義したブロック付きメソッドでブロックを呼び出すときに使います。 yield に渡された値はブロック記法において || の間にはさまれた変数(ブロックの引数)に代入されます。

(メソッド呼び出し - Rubyリファレンスマニュアル より)

この yield の動作はイメージしにくい。理由は、ブロックの呼び出しをするために特別な構文が用意されていることと、当の呼び出し先のブロックの名前がメソッドの定義に現れていないことにより、一体 yield がどこに何を渡しているのかわかりずらいため。

 

Javascript で enumerable 関数を定義

ところで、前回は 「JavaScript のクロージャ と オブジェクト指向」 について見た。今回はこの延長線上で、上記 Ruby の Enumerable のような動作をする関数を定義してみる。

まずは、「人」 と 「グループ」 オブジェクトを生成する関数を定義。はじめは 「グループ」 に 「人」 を追加する関数を追加。

/**
 * 人
 */
var person = function(spec){
    var that = {};
    
    var toString = function(){
        return spec.name + " " + spec.age.toString();
    };
    that.toString = toString;
    
    var getName = function(){
        return spec.name;
    };
    that.getName = getName;
    
    var getAge = function(){
        return spec.age;
    };
    that.getAge = getAge;
    
    return that;
};

/**
 * グループ
 */
var group = function(spec){
    var that = {};
    var persons = [];
    
    var add = function(ps){
        persons[persons.length] = ps;
        return that;
    };
    that.add = add;
    
    var toString = function(){
        return persons.join(",");
    };
    that.toString = toString;
    
    return that;
};

var g = group({}).add(person({
    name: "Hanako",
    age: 15
})).add(person({
    name: "Jiro",
    age: 15
})).add(person({
    name: "Tarou",
    age: 21
}));

console.log(g.toString());   // Hanako 15,Jiro 15,Tarou 21

 

group 関数が返すオブジェクトに enumerable 関数を定義

次に Ruby でコンテナクラスに要素を順にブロックへと渡すメソッド each を定義したのと同様に、 「グループ」 オブジェクトに関数 each を定義してみる。

group 関数内…

    var each = function(f){
        var i;
        for (i = 0; i < persons.length; i++) {
            f(persons[i]);
        }
    };
    that.each = each;

Ruby での定義は以下の通りだった。

  def each
    @persons.each{|ps| yield ps}
  end

違いは Javascript では関数 f が明示されており、この f が Ruby のブロックに相当。 yield が関数 f の呼び出しに当たる。このように比較すると、Ruby の yield の意図が汲み取りやすく、呼び出しの関係もイメージしやすい。

先ほど定義した変数 g で each 関数を使ってみる。

g.each(function(ps){
    console.log(ps.toString());
});

結果は、

Hanako 15
Jiro 15
Tarou 21

 

map, filter, fold の定義

上記 each 関数を使い、map, filter, fold を goup 関数内に定義する。

    var map = function(f){
        var result = [];
        each(function(ps){
            result[result.length] = f(ps);
        });
        return result;
    };
    that.map = map;
    
    var filter = function(p){
        var result = [];
        each(function(ps){
            if (p(ps)) {
                result[result.length] = ps
            }
        });
        return result;
    };
    that.filter = filter;
    
    var fold = function(z, f){
        var acc = z;
        each(function(ps){
            acc = f(acc, ps);
        });
        return acc;
    };
    that.fold = fold;

これを使うと、

var g2 = g.map(function(ps){
    return ps.getAge() * 2;
});
console.log(g2.toString());          // 30,30,42

var g3 = g.filter(function(ps){
    return ps.getAge() < 20;
});
console.log(g3.toString());          // Hanako 15,Jiro 15

var g4 = g.fold(0, function(acc, ps){
    return acc + ps.getAge();
});
console.log(g4.toString());          // 51

var g4 = g.fold("", function(acc, ps){
    return acc + ps.getName();
});
console.log(g4.toString());          // HanakoJiroTarou

ここまでのコード

 

enumerable 関数に map, filter, fold を移す

map, filter, fold は group 関数に特有のものではない。Ruby のモジュールのように enumerable 関数を新たに作りその中 に 3 つの関数を移す。これにより、他のコンテナオブジェクトを生成する関数で共通に利用できるようになる。

以下の enumerable 関数の引数 that は、3 つの関数を追加するオブジェクトを表わす。

/**
* Enumerable - コンテナが要素に対して行う処理。
* コンテナで each メソッドが定義されていることを要求する。
*/
var enumerable = function(that){
    var map = function(f){
        var result = [];
        that.each(function(e){
            result[result.length] = f(e);
        });
        return result;
    };
    that.map = map;
    
    var filter = function(p){
        var result = [];
        that.each(function(e){
            if (p(e)) {
                result[result.length] = e
            }
        });
        return result;
    };
    that.filter = filter;
    
    var fold = function(z, f){
        var acc = z;
        that.each(function(e){
            acc = f(acc, e);
        });
        return acc;
    };
    that.fold = fold;
    
    return that;
};

group 関数内では、最終的に返すオブジェクト that を enumerable 関数に渡し、that のプロパティに map, filter, fold メソッドを追加してもらうように変更する。 Ruby でモジュールをインクルードすることに相当。

var group = function(spec){
    var that = {};
    that = enumerable(that);

ここまでのコード。

 

関連記事

2010年8月23日月曜日

JavaScript のクロージャ と オブジェクト指向

1. JavaScript における重要な概念

久しぶりに JavaScrip を書こうと思ったら、ほとんど頭の中から抜けている。 (+_+)

this とか prototype って何だっけ?というレベル。てゆうか考えてみたら、その辺読んだけど何かごちゃごちゃしていて頭に入らなったので面倒くさくなって言語仕様読むのやめたんだった。 ^^; シンプルなもの以外理解も記憶もできない。

4873113911

ところで、以前に JavaScript のコードを書くときに参考にした本は、

JavaScript に関する本は、これしかまともに読んだことがない。

特に以下の部分が、JavaScript を使う上で参考になった。

  • 3.9  グローバル領域の利用を減らす, p28
  • 4.10 クロージャ, p43
  • 5.2   オブジェクト指定子, p57
  • 5.4   関数型, p59

上記の内容を思い出すために、例を考えながら、復習することに。

以下のコードは、 Aptana で書いて、 Firebug 上で実行した。

 

2. クロージャの意味

クロージャとは、レキシカルな環境において、束縛された自由変数を含む関数

まずはクロージャから。 Closure – Wikipedia によると、

… a closure is a first-class function with free variables that are bound in the lexical environment. Such a function is said to be "closed over" its free variable.

上記の First-class function とは、First-class function - Wikipedia によると、言語が以下の機能をサポートしていることを意味する。

  • constructing new functions during the execution of a program,
  • storing them in data structures,
  • passing them as arguments to other functions,
  • returning them as the values of other functions.

JavaScript は上記の要件を満たしているので、関数は first-class function 。この関数がレキシカルに束縛される自由変数を伴なっているものがクロージャ。

レキシカル」 とは、Scope (programming) - Wikipedia によると、

With lexical scope, a name always refers to its (more or less) local lexical environment.

実行時のことを考えなくても書かれているコードを見れば、名前が何を指し示しているのかわかる。

自由変数」 とは、Free variables and bound variables - Wikipedia によると、

… a free variable is a variable referred to in a function that is not a local variable or an argument of that function.

関数において、ローカル変数でも引数でも変数。

こういった関数がどのような状況で生じるかと言えば、先ほどの Closure – Wikipedia に戻り、

In some languages, a closure may occur when a function is defined within another function, and the inner function refers to local variables of the outer function.

関数の中で関数を定義し、内側の関数が外側の関数のローカル変数を参照する。

(cf. Ruby におけるクロージャの例 : Proc オブジェクトを返すメソッド )

 

関数を返す関数

例えば、「初期値」 と 「増分」 を持つ 「カウンター」 をモデル化するのにクロージャを利用してみる。次のことを念頭に置いて関数を定義。

  • 関数の引数は 「カウンター」 の 「初期値」 と 「増分」。
  • 返り値は、関数を呼出すごとに値を増分だけインクリメントする関数。
  • 最初に与えた初期値を保持する変数を、インクリメントするたびに現在の値に更新する。

オブジェクト指向からの類推で言えば、

  1. 関数の引数がプライベートなインスタンス変数
  2. 返される関数がメソッド
var counter = function(val, step){
    return function(){
        val += step;
        return val;
    };
};

これを実行すると、

var c1 = counter(0, 1);
console.log(c1());        // 1
console.log(c1());        // 2
console.log(c1());        // 3

var c2 = counter(100, 10)
console.log(c2());        // 110
console.log(c2());        // 120
console.log(c2());        // 130

 

3. オブジェクト指定子とは

上記の関数における引数は、必要となる値を直接渡していた。 しかし、JavaScript: The Good Parts の 「5.2 オブジェクト指定子」 (p57) にはこのデメリットについて、次のように述べられている。

コンストラクタが受け取るパラメータの数が非常に多くなってしまうことは、よくあることだ。しかしそうなると、引数の順番を覚えるのがとても大変になってしまい、トラブルの原因にもなりやすい。

これに従い、オブジェクトを関数の引数として与えるように変更する。

var counter = function(spec){
    return function(){
        spec.val += spec.step;
        return spec.val;
    };
};

カウンターを生成する部分を変更。

var c1 = counter({val:0, step:1});

var c2 = counter({val:100, spec:10});

生成するときに、与える変数の順番を気にしなくていいところが便利。また、与える変数を増やす場合も、関数側で定義する引数を変更する手間が省ける。

 

4. オブジェクトを返す関数

上記では関数を返す関数を定義した。今度はオブジェクトを返す関数を定義してみる。ただし、上記と同様にクロージャが生成されるような構成にする。

関数を返す関数はメソッドが一つのオブジェクト。オブジェクトを返す関数は複数のメソッドがオブジェクトに詰め込まれているというイメージ。

例えば、「名前」 をフィールドに持つ 「人」 をモデル化したオブジェクトを返す関数を定義してみる。ここで生成される 「人」 オブジェクトは、メソッドとして 「名前」 を取得する関数と設定する関数を持つとする。

var person = function(spec) {
    return { getName : function(){ return spec.name; }
           , setName : function(n){ spec.name = n; }
    };
};

これを使い、

var tarou = person({name: "Tarou"});
console.log(tarou.getName());        // Tarou

tarou.setName("太郎");
console.log(tarou.getName());        // 太郎

 

アクセス制御

重要な点は、以下のように person 関数によって生成されたオブジェクトの内部的なプロパティにアクセスできないということ。 (cf. JavaScript: The Good Parts, p59)

console.log(tarou.name);             // undefined

これに対して new を用いて関数を呼出した場合は、クラスにおけるプライベート変数のような用い方ができない。

var Person = function(name){
    this.name = name;
};
var tarou = new Person("Tarou");
console.log(tarou.name);          // プロパティに直接アクセス
tarou.name = "太郎";               // プロパティを直接変更
console.log(tarou.name);

this が指すものは、呼出されたメソッドをプロパティとして持つオブジェクトであり、そのプロパティは外部からアクセス可能。

 

オブジェクトを返す関数 と クラス指向の言語におけるコンストラクタ

ところで、典型的なクラス指向の言語では、クラスを雛形とし、 new 演算子によりクラスに定義されたコンストラクタが呼出されオブジェクトが生成される。これに対して、上記で定義した関数 person はオブジェクトを返す普通の関数だった。

この点を考えると、クラス指向の言語でオブジェクトを生成する方法は、関数 person と同じくオブジェクトを生成するための手段であり、オブジェクトを生成することを意味的に明確にするための方便であることがわかる。

その意味で Douglas Crockford が勧めている「関数型」 (JavaScript: The Good Parts, p59) と呼んでいる 「クロージャを使ってオブジェクト生成する方法」 は、関数という手段のみが使われているシンプルな方法。わざわざ  new 演算子を導入する必要がない。

JavaScript で new 演算子と併用する this や prototype チェーンを使う前に、関数だけで書ける事柄は余計なものを使わずに書く方が頭の中がスッキリして良さげ。

 

プライベートメソッド

先ほどの person 関数は返すオブジェクトの中にメソッドが定義されていた。JavaScript: The Good Parts (p.61) ではこれを、

  1. 関数の定義
  2. 関数をオブジェクトのプロパティとして設定

の 2 段階に分けて書くことが推奨されている。

var person = function(spec) {
    var that = {};
    
    var getName = function(){ return spec.name; };
    that.getName = getName;
    
    var setName = function(n){ spec.name = n; };
    that.setName = setName;       // これを削除
    
    return that;
};

これにより、setName 関数を that のプロパティに設定しなければ、setName はプライベートメソッドになる。

 

5. 継承

次にオブジェクト指向の拡張に相当するものを書いてみる。(cf. JavaScript: The Good Parts , p60)

その前に準備として、「人」 オブジェクトを以下のように変更。

  • 年齢をフィールドに持つ
  • 年齢を問い合わせても答えない

ただし、「人」 を継承したオブジェクトは年齢を答えるものとする。また、継承したオブジェクトから利用できるメソッドを 「人」 オブジェクトは持つものとし、これを defaultGetAge 関数とするなら、

var person = function(spec, my){
    my = my || {};
    var that = {};
    
    var getName = function(){
        return spec.name;
    };
    that.getName = getName;
    
    var defaultGetAge = function(){
        return "私の年齢は" + spec.age + "です";
    };
    my.defaultGetAge = defaultGetAge;
    
    return that;
};

上記 my の使われ方は、person 関数を拡張した関数を見てからの方が理解しやすい。少なくとも、that のプロパティに defaultGetAge 関数が設定されてないので、「人」 オブジェクトは年齢に関して答えることができないことはわかる。

「人」を継承した 「羽の生えた人」 と 「ヒレのある人」 を想定する。

img08-23-2010[1]

/**
 * 羽の生えた人
 */
var personWithWings = function(spec, my){
    my = my || {};
    var that = person(spec, my);
    
    var fly = function(){
        return "飛んでるぅ~";
    };
    that.fly = fly;
    
    var getAge = function(){
        return my.defaultGetAge();
    }
    that.getAge = getAge;
    
    return that;
}

/**
 * ヒレのある人
 */
var personWithFin = function(spec, my){
    my = my || {};
    var that = person(spec, my);
    
    var swim = function(){
        return "泳いでるぅ~";
    };
    that.swim = swim;
    
    var getAge = function(){
        return my.defaultGetAge();
    };
    that.getAge = getAge;
    
    return that;
}

拡張した関数から、拡張元の関数に変数 my を渡し、そこへ未公開の関数を詰め込んでもらうイメージ。

これを使ってみる。

var hanako = person({
    name: "Hanako",
    age: 15
});

var jiro = personWithWings({
    name: "Jiro",
    age: 15
});

var tarou = personWithFin({
    name: "Tarou",
    age: 21
});

console.log(hanako.getName());     // Hanako

console.log(jiro.getAge());        // 私の年齢は15です
console.log(jiro.fly());           // 飛んでるぅ~

console.log(tarou.getAge());       // 私の年齢は21です
console.log(tarou.swim());         // 泳いでるぅ~

Javascript から見る Ruby のイテレータ – Enumerable」 ヘつづく。

 

関連記事

参考

2010年8月22日日曜日

カイサドラム と ハングドラムの澄んだ音色に感動

1. 鍋ではなくて、楽器だった

とあるお店で食事をしていたら、外人の客の一人が UFO のような形をした奇妙なものを取り出した。あぐらをかき、ひざの上にその金属製の土鍋のようなものを置く。

何か食事をすることと関係があるのかと見ていたら、突然その円形の上を軽やかに手が舞った。スチールドラムのような柔らかくて、丸みのある深い澄んだ音色。奇妙な形とは対照的な民族的なミニマルなリズムと旋律。 体の奥にまで響く重厚なサウンド。

 

2. カイサドラムとハングドラムの違い

家に帰り調べてみると、良く似た形の楽器が紹介されていた。

ハンドスチールドラムの販売 - 商品 - カイサドラム によると、

カイサという今年2008年に発表されたばかりの新しい楽器です。膝の上に乗せたりスタンドに取りつけて演奏します。円盤にぐるりと等間隔にならんだ凹みを素手で叩くとコーンコーンと、カリブのスチールドラムのような東南アジアのガムランのような音がします。五音階、バリ島音階など、音階によってずいぶん違う雰囲気になります。

Hang Drumに似たハンドスティールドラム : Caisa Drum:■ 音楽方丈記 ■

Hang Drum (ハングドラム)に似ていますが構造が少し違っていて使われるのは表側のドームだけで、木製(または金属)の底面と6箇所のワイヤーで繋がって固定されています。

動画を検索してみると、非常に美しい音色が奏でられている。

Caisa from Josue Arias on Vimeo.

YouTube - "Caisa drum" で他にもいくつか動画を見ることができる。

また、上記ハングドラムについても調べてみると、カイサドラムより硬質な音色がするようだ。

YouTube - "hang drum" より、

生で演奏をしていた人も割と簡単そうに演奏していたので、自分でもできそうに思えるけれど、結構難しいんだろうなぁ。

Ubiquity で不正な IP を調べる

以下のサイトでは、不正な IP がデータベース化されている。

DroneBL

DroneBL is a realtime monitor of abusable IPs, which has the goal of stopping abuse of infected machines.

EFnet RBL

The EFnet IRC Network RBL is comprised of a collection of data from several sources with the purpose of providing a "one stop shop" for IRC admins to block a multitude of undesirable clients.

Ubiquity でこれらのデータベースに IP アドレスを問い合わせ、何れかに登録されているかどうか調べたい。

インストールはこちらから。

使い方 : IP アドレスをブラウザ上で選択してから checkIP コマンドを実行。いづれかのデータベースに登録されている場合は true、そうでなければ false がプレビューに表示される。

 

ソースコードについて

上記二つのデータベースの問い合わせでは、単純にサイトの URL と IP を組み合わせてアクセスすると結果が HTML で返される。その中に含まれる文字列をチェックすることにより登録状態をチェック。

新たにデータベースを増やしたい場合は、問い合わせ先の URL と、問い合わせの結果が返されたら IP が存在することをチェックする述語 existp を記述して、dbSet に追加する。

CmdUtils.CreateCommand({
    names: ["checkIP"],
    preview: function preview(pblock, args){
        // プレビューをクリア
        pblock.innerHTML = "";
        
        /**
        * DB は IP を検索するサイト。
        *
        * {spec.url サイトの URL,
        * spec.existp サイトでの検索結果から、対象がサイトに存在するか判定する述語}
        *
        * @param {Object} spec
        */
        var db = function(spec){
            var that = {};
            
            // public --------------------------------------------
            
            /**
            * 与えられた IP が存在することを確認し、その結果に対して関数 f を適用する。
            * @param {Object} ip IP アドレス
            * @param {Function} f コールバック関数
            */
            var exists = function(ip, f){
                jq(ip, function(page){
                    f(spec.existp(page));
                });
            };
            that.exists = exists;
            
            /**
            * 与えられた IP に対する存否の結果ページを取得し、その結果に対して関数 f を適用する。
            * @param {Object} ip IP アドレス
            * @param {Function} f コールバック関数
            */
            var getHTML = function(ip, f){
                jq(ip, function(page){
                    f(page);
                });
            };
            that.getHTML = getHTML;
            
            // private --------------------------------------------
            
            /**
            * jQuery.get をラップした関数
            * @param {Object} ip
            * @param {Function} f
            */
            var jq = function(ip, f){
                jQuery.get(spec.url + ip, null, function(page){
                    f(page);
                });
            };
            
            return that;
        };
        
        /**
        * DB SET は検索対象の DB を集約。
        *
        * {spec.dbs db の配列}
        *
        * @param {Object} spec
        */
        var dbSet = function(spec){
            var that = {};
            
            /**
            * DB における IP アドレスの存否の結果を HTML で取得し、それに関数 f を適用する。
            * @param {Object} ip
            * @param {Function} f
            */
            var getHTML = function(ip, f){
                each(function(db){
                    db.getHTML(ip, function(page){
                        f(page);
                    });
                });
            };
            that.getHTML = getHTML;
            
            /**
            * いずれかの DB に与えられた IP が存在するか確認し、その結果に対して関数 f を適用する。
            *
            * @param {Object} ip
            * @param {Function} f
            */
            var exists = function(ip, f){
                var result = [];

                var allChecked = function(){
                    var b = false;
                    if (result.length === spec.dbs.length) {
                        for (var i = 0; i < spec.dbs.length; i++) {
                            b = b || result[i];
                        }
                        f(b);
                    }
                };
                
                each(function(db){
                    db.exists(ip, function(exists){
                        var idx = result.length;
                        exists ? result[idx] = true : result[idx] = false;
                        allChecked();
                    });
                });
            };
            that.exists = exists;
            
            // private ----------------------------------------------
            
            /**
            * 各 DB に対して関数 f を適用する
            * @param {Function} f
            */
            var each = function(f){
                for (var i = 0; i < spec.dbs.length; i++) {
                    f(spec.dbs[i]);
                }
            };
            
            return that;
        };
        
        // 各 DB の設定
        var dronebl = db({
            url: "http://dronebl.org/lookup_branded.do?ip=",
            existp: function(page){
                var msg = /This means that your IP is not listed in DroneBL/;
                return page.match(msg) ? false : true;
            }
        });
        
        var efnetrbl = db({
            url: "http://rbl.efnetrbl.org/?i=",
            existp: function(page){
                var msg = /was found in the database/;
                return page.match(msg) ? true : false;
            }
        });
        
        // ブラウザで選択された IP アドレス
        var ip = CmdUtils.getSelection("").match(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/);
               
        // DB を集約
        dbSet({
            dbs: [dronebl, efnetrbl],
        }).exists(ip, function(b){
            pblock.innerHTML += b;
        });
    },
});

 

関連記事

2010年8月21日土曜日

Firefox, Google Chrome で USTREAM のチャットをブロック

USTREAM のチャットは IRC を利用している

Ustream の配信を見ていると、映像の右横に Twitter の投稿とリアルタイムのチャットが表示されることがある。

このチャットの仕組みは「Ustreamチャット - UstreamまとめWiki」によると、

# 他の人のチャンネルを見ると(...)、FlashでIRCチャットに参加できます。

上記 IRC というのは、
正式名称はインターネット・リレー・チャット(Internet Relay Chat)です。世界中に張られたIRCサーバのネットワークを利用し、世界中の人々とリアルタイムで会話ができるようになっています。

... IRCサーバは互いにリンクされ、広大なIRCネットワークを形成しています。

ユーザーはこのサーバにつなぐためのソフト(...)を使用し て、IRCサーバに接続します。

(IRC普及委員会/IRCのしくみ より)

 

LimeChat の設定

サーバに接続するためのクライアントとして定番なのが LimeChat のようだ。

サーバの接続設定は「IRCクライアントでUstreamチャット - UstreamまとめWiki」を参考にした。

「サーバ」→「サーバを追加」 から、UstreamのIRCサーバを追加します。

  • 「ホスト名」は、chat1.ustream.tv
  • 接続用の「ポート番号」は、6667
  • 「ログイン名」と「パスワード」は、Ustreamに登録したログインID・パスを使用(コレで管理者権限がGETできます)
  • 私のように表示名を変えたい人は、「ニックネーム」を変えればOK

 

USTREAM のチャンネルと IRC のチャンネル

サーバに接続したら、次に Ustream の配信画面にあるチャットに接続してみる。

IRC ではサーバの中に「チャンネル」が存在している。このチャンネルと Ustream の各「チャンネル」が対応しているとのこと。

Ustreamでは、showsのページアドレス(http://www.ustream.tv/channel/*****)のうち最後の、*****の前に#を付ける事で、それがIRCの「チャンネル」に相当します。つまり#*****です。

(びるのブログ 今更ながら・・・ より)

上記の説明に従いチャンネルを追加。

 

ユーザ情報の表示

チャンネルに入ると、そこへ接続しているユーザ名が表示される。

ircの危険性 によると、

まず何が一番気になることかというと、IRCの特徴としてIPアドレスやホスト名が表示されるというところにあります。

確かに特定のユーザを右クリック > 「ユーザ情報」 を見るとホスト名 が表示された。これではブラウザで匿名接続したとしても、場合によっては半ば識別子としての価値を持ってしまう。

 

CSS でチャットを表示させない

これを避けるにはチャットに接続しないようにすれば良い。

ブラウザ上でチャット画面を表示しなければ IRC に接続されないようなので、この要素を CSS でブロックする。

Firefox, Google Chrome を使っている場合、アドオン Stylish をそれぞれ予めインストールしておく。

その後、以下の install with Stylish ボタンでインストール。

http://userstyles.org/styles/35330

USTREAM - Block IRC | userstyles.org via kwout

Ubiquity で特定のサイトをプレビューに表示

Firefox のアドオン Ubiquity を使い、そのプレビューに特定のサイトの内容を表示したい。

予め下記のサイトの解説を読んでおき、基本的な書き方を確認。

API の解説は Ubiquity のコマンドで help 。 ハッキング にある 「コマンド API ドキュメント」 を参照。

 

Ubiquity とその他アプリケーションの設定

Ubiquity を使う前に、コマンド help > 各種設定 > 言語設定 において 「Parse 2 を使用」 のチェックをはずす。これがあるとコマンドが思ったように実行されなかった。

「ぴたすちお」 のようなマウスホイールの回転を 「カーソルの下のウィンドウに送る」設定がされている場合、Ubiquity 内のプレビューでスクロールができないので設定をはずしておく必要がある。

 

コマンドの編集

コマンド Command-Editor により、ブラウザ上でコードを編集できる。

コマンドテンプレートを挿入」 し、不必要なコードを削除。ここではコマンド名の設定とプレビューの表示関数だけ残す。

「ファイルに保存」 すると保存した場所が Ubiquity に記録されるので、後はエディタで直接編集するのが楽。

 

プレビューの表示

例えば、コマンド test を実行して、 Apple のサイトをプレビューに表示させたいとする。

iframe で外部サイトを表示させるなら、

CmdUtils.CreateCommand({
    names: ["test"],
    preview: function preview(pblock, args){
        pblock.innerHTML = '<iframe width="100%" height="475px" src="http://www.apple.com" />';
    },
});

preview 関数が Ubiquity のプレビュー表示に対応しており、その第1引数がプレビュー領域を指し示す。

 

テンプレートの使用

テンプレート機能を使うなら、「API ドキュメント」 より以下の関数を使う。

CmdUtils.renderTemplate(template, data)

Renders a template by substituting values from a dictionary. The templating language used is trimpath, which is defined at http://code.google.com/p/trimpath/wiki/JavaScriptTemplates.

renderTemplate 関数は第一引数がテンプレートで、第二引数は埋め込むデータ。

テンプレート内では、

${dataのプロパティ}

と記述することにより、値が置き換えられる。(詳しくは上記サイトを参照)

CmdUtils.CreateCommand({
    names: ["test"],
    preview: function preview(pblock, args){
        var data = {
            width: "100%",
            height: "475px",
            src: "http://www.apple.com"
        };
        var template = '<iframe width=${width} height=${height} src=${src} />';
        pblock.innerHTML = CmdUtils.renderTemplate(template, data);
    },
});

 

jQuery でアクセス

5 Documentation > Labs/Ubiquity/Bundled Libraries - MozillaWiki によると、

Ubiquity currently includes the following JavaScript libraries:

  • jQuery -- the awesome jQuery library
  • DateJS -- fantastic date natural language parser

Labs/Ubiquity/Ubiquity 0.1 Author Tutorial - MozillaWiki

Because we include jQuery with Ubiquity, it is simple to perform Ajax calls as well as parse returning data.

Ubiquity から他のサイトにアクセスするは jQuery を使えばいいと。

jQuery.get() – jQuery API によると

Description: Load data from the server using a HTTP GET request.

jQuery.get( url, [ data ], [ callback(data, textStatus, XMLHttpRequest) ], [ dataType ] )

  • url A string containing the URL to which the request is sent.
  • data A map or string that is sent to the server with the request.
  • callback(data, textStatus, XMLHttpRequest) A callback function that is executed if the request succeeds.
  • dataType The type of data expected from the server.

これを使うなら、

CmdUtils.CreateCommand({
    names: ["test"],
    preview: function preview(pblock, args){
        jQuery.get("http://www.apple.com", null, function(page){
            pblock.innerHTML = page;
        });
    },
});

 

選択された文字列の取得

URL を表わす文字列`http://XXXXX.XX.XXX’ があった場合、XXXXX.XX.XXX の部分を選択した後、コマンドを入力したらそのサイトをプレビューに表示するように変更したい。

ブラウザ中の選択した文字列を取得するには、API の CmdUtils より、

CmdUtils inherits ContextUtils. The methods are wrapped so that the context argument isn't needed. (i.e. CmdUtils.getSelection("") is equivalent to ContextUtils.getSelection(context, ""))

CmdUtils は ContextUtils を継承しているということなので、API の ContextUtils を見ると、

ContextUtils.getSelection(context, joint = "\n\n")

Returns a string containing the text and just the text of the user's current selection, i.e. with HTML tags stripped out.

joint is an optional string to join multiple selections.

これを使い、

CmdUtils.CreateCommand({
    names: ["test"],
    preview: function preview(pblock, args){
        jQuery.get("http://" + CmdUtils.getSelection(""), null, function(page){
            pblock.innerHTML = page;
        });
    },
});

 

jQuery.get の代替となる関数

CmdUtils には jQuery.get の代替となる  関数が定義されている。違いは、

The difference is that previewGet()/previewPost() is designed to handle command previews, which can be cancelled by the user between the time that it's requested and the time it displays. If the preview is cancelled, the given callback will not be called.

(API のドキュメントより)

これを用いるなら、

CmdUtils.CreateCommand({
    names: ["test"],
    preview: function preview(pblock, args){
        pblock.innerHTML = "";
        CmdUtils.previewGet(pblock, "http://" + CmdUtils.getSelection(""), null, function(page){
            pblock.innerHTML = page;
        });
    },
});

2010年8月20日金曜日

OS を Vista から XP に変更したら、バックアップしておいたマイドキュメントが消えた

D ドライブへバックアップ

これまで Windows Vista を使っていたけれど、最近調子が悪いのでこれを機に XP に戻すことにした。バックアップ対象のフォルダを選定するのが面倒だったので、C ドライブにあるホームフォルダをごっそり Dドライブへコピー。

 

インストール後…

Windows XP のインストールでは、 C ドライブのみフォーマットしてからインストール。
さて、いざバックアップした中から必要なものを取り出そうと思ったら、

マイドキュメントが消えている (@_@;)

バックアップした後に各フォルダがちゃんとコピーされているか確認しなかったことを後悔。エラーが表示されなかったので、間違いなく全部バックアップされていると思い込んでいた。 (o_ _)o~†

 

見えなくなっていたマイドキュメント

半ば放心状態で、バックアップしたホームフォルダの中の「最近使ったファイル」を眺めていたら、マイドキュメント以下に存在していたフォルダが表示されている。開いてみたら中身が存在していることが確認できた。アドレスを見ると、

D:backup日付\ユーザ名\Documents\XXXXXX

バックアップしたホームフォルダで再び Documents を探したけれど、表示されていない。フォルダオプションで 「全ての隠しフォルダ・ファイル、保護されたシステムファイル」 を表示させているにも関わらずエクスプローラ上で見ることができない。ただし、コマンドライン上では ` Documents' と表示されていた。

ちゃんとバックアップされていたのでほっと一安心。 ^^

同じような症状の人がいないか探したら、「Vista の My Documents フォルダは頑固だった」で同様なことが述べられている。`caclsコマンドでACLを編集する - @IT' を参考に Documents フォルダのアクセス権を変更したけれど相変わらず。

とりあえず、データを元に戻せたから良しとするか。

それにしても、表示は綺麗ではないけれど、XP は Vista に比べるとレスポンスが良いなぁ。アプリの互換性の面から考えても当分は XP で行こうかな。

2010年8月12日木曜日

USTREAM のダッシュボードにおける配信動画のサイズを大きくする

USTREAM のダッシュボードに表示される動画のサイズは小さい。これをスタイルシートで以下のように大きくする。

image2826-2

Firefox, Google Chrome を使っている場合、アドオン Stylish をそれぞれ予めインストールしておく。

その後、以下の install with Stylish ボタンでインストール。

2010年8月11日水曜日

Wibiya でブログに Facebook の 「いいね!」 ボタンを設置

1. ブログ下部にツールバーを配置

の記事を目にして以来、ブログ下部に表示されるボタンの一群を導入している。

「あると邪魔かな?」

と思いつつ、Blogger での設定は、Wibiya に登録した後、ボタン一つでできるというお手軽さが好印象だったため、そのまま使い続けている。ブログテンプレートを直接いじらないで済むので、後々のメンテナンスが楽。

Blogger 以外なら、上記のサイトに設置方法が書かれている。

 

2. Facebook とは

ところで、最近よく記事を目して気になる SNS は Facebook

米SNS最大手Facebook、アクティブユーザーが5億人突破 | ネット | マイコミジャーナル (Yoichi Yamashita 2010/07/22) によると、

Facebookは2004年に大学生向けSNSとして始まり、2006年に一般(13歳以上)へのサービス提供を開始した。当時は米SNS市場において MySpaceに差を付けられた2位だったが、2007年のFacebook APIの公開で風向きが変わった。開発パートナーの増加と共にユーザー数が急増し始め、2009年4月に2億ユーザー、同年9月に3億ユーザーを達成。設立6周年の今年2月に4億ユーザーを突破し、それから6カ月とかからずに5億ユーザーのマイルストーンに到達した。

 

a. mixi との比較

しかし、日本国内では利用者が少ない。

現在、世界中に5億人を超えるユーザーを持つ世界最大のSNSである。そのうち日本国内のユーザー数は約100万人。

(Facebook – Wikipedia より)

日本では今のところ mixi が圧倒しており、会員数で見ると桁が違う。 “『mixi』のユーザー数が2,000万人に « 株式会社ミクシィ” (2010年04月14日) によると、

本日、ソーシャル・ネットワーキング サービス(SNS) 『mixi』のユーザー数が、2,000万人を超えたことをお知らせいたします。

ただし、自分は 「足あと」 機能が好きではないので使わなくなった。はじめたときはおもしろくて誰が来たか見ていたけれど、ソーシャルグラフが生長するに連れて足枷になる。

 

b. 国内における Facebook の注目状況

世界のSNSマップ最新版。Facebookが131ヵ国中111ヶ国でトップ:in the looop:ITmedia オルタナティブ・ブログ」(2010/06/15) で紹介されている SNS の勢力図を見ると、アジア地域は Facebook の勢力に飲み込まれないで健闘していると見るべきか、鎖国してると言うべきか…。

Google トレンド で他の日本の有力な SNS 「GREE, モバゲー」を加えて比較してみると、世界的に見れば当然ながら facebook が圧倒的。

日本に限って見ても、2010年を境に facebook の勢いが止まらない。

興味深いのは、「詳細地域」を見ると facebook が注目されているのは都市部ではなく東京に一極集中という感じ。

日本でも3ー5年でFacebookがトップSNSに?=Zuckerberg氏【湯川】 : TechWave」 (2010年06月24日)  によると、

どの国でもまず、外国人の友人がいる人がFacebookを使い始め、やがて自国の友人も招待し始める。そして自国の友人関係の数が、外国人との友人関係の数を上回ると、あとはその国でFacebookが一気に広がるのだという。…

日本を含む残りの国について、そのtipping pointが半年や1年以内にくることはないが、3年から5年以内には可能性が十分ある、というのが同氏の考えだ。

と述べられていることより、地方でも注目されるレベルになったときには、会員数もすごいことになっているのかな?

 

c. Facebook になじめるのか?

グローカル化 – Wikipedia という言葉がある。この意味は、

    1. 地球規模/多地域での展開を目指しながらも、地域の法律や文化に応じる形で提供される製品やサービス。
    2. インターネットなどの電子コミュニケーション技術を活用し、地球規模/多地域の基準の下で提供される地域限定のサービス。
    3. 地域の文化や需要に応じるために、世界的な企業が設立する現地法人、など。

img08-11-2010[2]-vert果たして、facebook は日本向けに上手くローカライズされるのだろうか。もしくは日本の文化圏でソーシャルグラフが生長できる土壌たりえるのか。加えて、馴染んだ SNS から乗り換える気にさせるプラットフォームなのか。

ちょっと使ってみた感じでは、機能の全容を把握しずらい。特にプライバシーとアプリケーションの設定で戸惑った。

メインコンテンツとおぼしき 「掲示板」 も、言語を英語に設定すると `Wall’ と表示されるが、果たしてこれは 「掲示板」 と呼ぶに相応しいものなのだろうか?

 デジタルネイティブ ならぬ Facebook ネイティブなる世代が誕生し、「こういうのが掲示板だ」 と最初から認識していない限り、違和感を感じるに違いない。慣れるまでに時間がかかりそう。 Twitter のときと同じようにはいかない気がする。 (+_+) その分、いじるのおもしろそうだけれど。

個人的には 「足あと」 機能がないという理由だけでこちらを使いたい。そんなものまで可視化してソーシャルグラフに加える必要はない。

 

d. 目新しインターフェイスに慣れるのは骨が折れる

直感的に操作できないものは、それの善し悪しに関わらず、必要性に迫られる人以外には支持されない。

家の親父は、若いとき割と新しもの好きだった。会社では一早くワープロを使って文書を作成し、上司には

「手書きでキチンと書かれていないような文書はダメだ!」

と言われ、頭にきたと聞いたことがある。同年代の中ではインターネットにも親しんでいる方で、自分でホームページを作成している。しかし、頭の中は古いままで、昔ながらのシンプルな掲示板 & メールやメルマガによる同報通信というモデルを頑なに堅持。 Twitter を勧めても関心がないようで、いわんや Facebook をや。

寄る年波には勝てないなと横目で見つつ、翻って自分のことを考えてみれば、以前よりも新しいものに対する免疫力が低下していると感じることがある。例えば、昨日まで地デジのリモコンに

`d データ’ ボタン

があることに気づいてなかった。加えて、その操作が「カーソルと決定、4色のボタン」というシンプルな構成にも関わらず、「これ押すとどうなるんだ?あれ…? え?ん…?」と操作する気が失せた。 (+_+)

万人に受ける操作モデルとインターフェイスはいかに難しいかと実感。

 

3. 「いいね!」 ボタンの設定

話を戻して Facebook 。このサービスの中で一番シンプルでわかりやすいのが 「いいね!」 ボタン。

Facebookの”like”ボタンは立ち上げから24時間で10億回押されるって」によると、

“like”ボタンの機能はほかのサイトの上でも … ユーザがそのページのコンテンツを気に入ったシルシとしてボタンを押す。気に入った理由などメモも書ける

この「いいね!」ボタンを Wibiya のツールバーに追加してみる。

まずは Web Application Bank | Wibiya にアクセス。

img08-11-2010[4]

Facebook Like Button を選択して install を押す。

img08-11-2010[3]

後は表示する位置を右上の Rearrange Toolbar ボタンを使って調整。

これで以下のようにブログ下部に表示された。

img08-11-2010[5]

 

Facebook での表示

「いいね!」 ボタンを押すと、Facebook の掲示板の「最近のアクティビティ」に表示される。

img08-11-2010[6]

もう一度「いいね!」ボタンを押したら、「最近のアクティビティ」から消えた。

2010年8月10日火曜日

Firefox で Google スプレッドシートの日本語入力がおかしい

家のばぁちゃんの体調が悪くなり、Google スプレッドシートで簡単な体調記録を付けることにした。 Google スプレッドシートなら家族で共有できるので、誰でも必要なとき印刷をして病院に持っていける。

 

入力が滅茶苦茶

Firefox で久しぶりに Google スプレッドシートを使ったら、どうも日本語の入力がおかしい。例えば、「本日は晴天なり」と入力をはじめたら、

img08-10-2010[4]

となり、終いには滅茶苦茶になった。

img08-10-2010[6]

ただし、IE8, Google Chrome では問題ない。

また、Firefox でも Google スプレッドシートで「旧バージョン」に戻せばちゃんと入力できる。

img08-10-2010[11]

 

入力する前に Enter キーを押す

これを回避するには、入力する前に一度 Enter キーを押して、入力可能な状態にした後、

img08-10-2010[9]

日本語を入力すればよい。

img08-10-2010[10]

(※ 上図で入力フィールドがピンク色になっているのは、アドオン IMEStatus をインストールしてあるため。「編集」ボタンが表示されているのは It's All Text! をインストールしてあるから。)

しかし、面倒だなぁ。。 (+_+) 素直に Chrome 使っておこうかな。

Facebook のまとめ

更新日:2012/04/25

投稿

いいね!