Prologでreduce(l|r)

というわけでPrologで書いてみた。

reducel(Pred, [X|List], Result) :-
	reducel(Pred, X, List, Result).
reducel(_, X, [], X).
reducel(Pred, X, [Y|List], Result) :-
	call(Pred, X, Y, Z),
	reducel(Pred, Z, List, Result).

reducer(_, [X], X).
reducer(Pred, [X|List], Result) :-
	reducer(Pred, List, Y),
	call(Pred, X, Y, Result).

20分くらいかかっちゃってます。reducerの方が簡単なんですよね。まだまだPrologは慣れないなぁ。
で、テスト。

?- reducel(pair, [1,2,3,4], R).

R = [[[1, 2], 3], 4] ;

No
?- reducer(pair, [1,2,3,4], R).

R = [1, [2, [3, 4]]] ;

No

大丈夫みたい。pair/3の定義は以下。

pair(X, Y, [X, Y]).

あと、無名関数っぽくかけると楽なので、こんなの書いてみた。

:- op(1150, xfx, =>).
=>(Vars, Term, Arg1, Arg2, Arg3) :-
	copy_term((Vars, Term), (Vs, T)),
	Vs = (Arg1, Arg2, Arg3),
	call(T).

さっきの例はこんな感じになる。

?- reducel((X,Y,Z => Z = [X,Y]), [1,2,3,4], R).

R = [[[1, 2], 3], 4] ;

No
?- reducer((X,Y,Z => Z = [X,Y]), [1,2,3,4], R).

R = [1, [2, [3, 4]]] ;

No

ぜんぜんPrologっぽくないけど気にしない気にしない。

PrologでふつうにFizzBuzz

またFizzBuzz問題の話です。昨日はなるべく短く書こうってことだったけど今度は普通に書いてみます。と言ってもPrologで何が普通かなんでよく知らないので適当です。そして処理系はSWI-Polog。
とりあえず述語fizzbuzzを定義。

fizzbuzz(N, 'FizzBuzz') :- N mod 15 =:= 0, !.
fizzbuzz(N, 'Fizz') :- N mod 3 =:= 0, !.
fizzbuzz(N, 'Buzz') :- N mod 5 =:= 0, !.
fizzbuzz(N, N).

あとは、例えば1行ずつ出力するならこんなかんじ。

?- between(1,100, N), fizzbuzz(N, X), writeln(X), fail.

リストがほしいときはfindallを使うのかな。

?- findall(X, (between(1,100,N), fizzbuzz(N, X)), L).

もしくはこうか。

?- numlist(1, 100, Ns), maplist(fizzbuzz, Ns, L).

剰余を使わないでやるにはどうしようかと思ったけど、循環リストで書いてみた。

fizzbuzz_list(N, L) :-
	Fs = ['', '', 'Fizz' |Fs],
	Bs = ['', '', '', '', 'Buzz' |Bs],
	fizzbuzz_sub(0, N, Fs, Bs, L).
fizzbuzz_sub(N, N, _, _, []) :- !.
fizzbuzz_sub(I, N, [F|Fs], [B|Bs], [X|Xs]) :-
	I1 is I + 1,
	atom_concat(F, B, S),
	(S == '' -> X = I1 ; X = S),
	fizzbuzz_sub(I1, N, Fs, Bs, Xs).

ってPrologで循環リストって使っていいんだっけ?

PrologでFizzBuzz Golf

今更ながらFizzBuzz。最近はじめたProloganarchy golf - FizzBuzzに初挑戦。おそらくPrologでショートコーディングしようなんて人はあまりいないでしょう。
まず最初に書いたもの。

m:-between(1,100,N),(N mod 15=:=0->write('FizzBuzz');N mod 3=:=0->write('Fizz');N mod 5=:=0->write('Buzz');write(N)),nl,fail.

次はそれを短くしたもの。これで104byte。

m:-between(1,100,N),(N mod 15<1->X='FizzBuzz';N mod 3<1->X='Fizz';N mod 5<1->X='Buzz';X=N),\+writeln(X).

その後91byteまで短くしたもの。

m:-put(49),between(2,100,N),nl,((D=3,S='Fizz';D=5,S='Buzz'),N mod D<1*->[];S=N),\+write(S).

絶対もっと短くできる気がします。

GoogleカレンダーにGoogleトランジットへのリンクを追加するGoreasemonkeyスクリプト

毎度おなじみGoogleカレンダー用のGreasemonkeyスクリプトです。場所の指定がある予定のポップアップにGoogleトランジットへのリンクを追加します。

ただ、予定が重なっていたりすると無視されてしまいます。場所が見つからないときはデフォルトの場所からになるので、スクリプトdefaultLocation変数に自宅の住所とかを入れておくといいかもしれません。

prototype.jsの$$で:containsとその拡張を使えるようにしてみる

こんなセレクタが CSS にあれば便利なのにと思うセレクタ - lucky bag」を読んで、妄想してるだけでもなんなのでprototype.jsの$$で使えるようにして遊んでみました。ちなみに超やっつけですので絶対どっかおかしいと思います。一応IE6,Firefox2ではちょろっと確認しています。
prototype.jsを読み込んだ後に以下のソースを読み込みます。prototype.jsのバージョンは1.5.1_rc3です。1.5.0じゃ動きません。

Selector.patterns.pseudo = /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not|contains)(\((.*?)\))?(\b|$|\s)/;
Selector.pseudos['contains'] = function(nodes, selector, root) {
  nodes = $A(nodes);
  var m;
  if (m = selector.match(/^\s*(["'])(.*)\1\s*$/)) {
    return nodes.select(function(node) {
      var text = [];
      (function collectText(node) {
        var childs = node.childNodes;
        for (var i = 0, il = childs.length; i < il; ++i) {
          if (childs[i].nodeType == 3) {
            text.push(childs[i].nodeValue);
          } else if (childs[i].nodeType == 1) {
            collectText(childs[i]);
          }
        }
      })(node);
      return text.join('').include(m[2]);
    });
  } else {
    var s = new Selector(selector);
    return nodes.select(function(node) {
      return s.findElements(node).length > 0;
    });
  }
};
Selector.xpath.pseudos['contains'] = function(m) {
  var e = m[6], p = Selector.patterns,
      x = Selector.xpath, le, m, v;
  if (m = e.match(/^\s*(["'])(.*)\1\s*$/)) {
    return "[contains(.,'" + m[2] + "')]";
  }
  var exclusion = [];
  while (e && le != e && (/\S/).test(e)) {
    le = e;
    for (var i in p) {
      if (m = e.match(p[i])) {
        v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m);
        exclusion.push("(" + v.substring(1, v.length - 1) + ")");
        e = e.replace(m[0], '');
        break;
      }
    }
  }
  return "[.//*[" + exclusion.join(" and ") + "]]";
};
Selector.findChildElements = function(element, expressions) {
  var exprs = expressions.join(','), expressions = [];
  exprs.scan(/(([\w#:.~>+\s-]+|\*|\[.*?\]|\(.*?\))+)\s*(,|$)/, function(m) {
    expressions.push(m[1].strip());
  });
  var results = [], h = Selector.handlers;
  for (var i = 0, l = expressions.length, selector; i < l; i++) {
    selector = new Selector(expressions[i].strip());
    h.concat(results, selector.findElements(element));
  }
  return (l > 1) ? h.unique(results) : results;
};

ちなみに、例に出てた

li:contains(img) {
  list-style-type: none;
}

を適用しようと思ったら、たとえば以下のように書かないといけません。

(function(css) {
  Event.observe(window, 'load', function() {
    for (selector in css)
      $$(selector).each(function(e) { e.setStyle(css[selector]); });
  });
})({
  'li:contains(img)':{
    'list-style-type': 'none'
  }
});

:containsの引数には任意のセレクタが使えるようにしてあるので、もっと複雑な事も出来ると思います。もちろん:contains("foo")という形も使えます。たぶん。

[2007-04-26 00:04追記] e-luckさんが「セレクタと引数が同じだとループしちゃうのかな」って言っていたのですが、このコードだと例えばli:contains(li)は「li要素を子孫に持つli要素」を指します。そういう意味じゃないのかな? 私の認識がちょっと違ったみたいです。

[2007-04-26 10:05追記] 1.5.1_rc3用にしたかも。

Google Calendar を広く使うための Gresemonkey スクリプト

ノートの小さい画面で作業していると出来るだけ広く表示したくなるわけで、 Google Calendar のナビゲーションバーとかヘッダーとか邪魔なときがあります。
Googleカレンダーをもっと便利に使うための13のツール&Tips | POP*POP でも紹介されていた Greasemonkey 用スクリプト - Hide Google Calendar Navi を使うとナビゲーションバーを消せるので便利です。それに加えて、

  • ヘッダーも(ナビゲーションバーとは別に)消したい
  • 前回の表示状態を復元したい
  • サイドバーを消したときに予定の表示がずれるのを何とかしたい

と思ったので自分で Greasemonkey スクリプトを書いて使ってたのですが、せっかくなので公開してみました。

右上の黒いボタンからインストール。
インストールすると三角の矢印が上と左の中央に出てきます。

それをクリックするとヘッダーやナビゲーションバーが出たり消えたりします。下は両方とも非表示にしたところ。

しかし Google Calendar 用の Greasemonkey スクリプト書くのは骨が折れます。バージョンや言語によって関数名が変わるのは勘弁してよ。

Google Calendarに現在時刻のラインを表示するGreasemonkeyスクリプト

Google Calendarを日表示や週表示にしたときに現在時刻のあたりに赤いラインを表示するGreasemonkeyスクリプトです。次の予定までどのくらいとかぱっと見で分かり易くなるかも。要するにこんなかんじ。

インストールは以下から。右上の黒いボタンを押すとインストールできます。

書いてから思ったけど今日以外のところにラインを表示してもしょうがないな。(2007-03-17追記)と言うわけで現在の日付のところだけに表示するように変更してみた。