大きいGoogle Mapsが見たかったので、タブレットをたくさん買いました

コンセプトはこんな感じです。

タブレットを並べるとこんな感じになります(結局のところ、普段使っているディスプレイのほうが大きいGoogle Mapsが見られるのですが)。

お試しサイトとソースコードをあとで公開します。

追記:お試しサイト1 (AppFog)・お試しサイト2 (Heroku)を公開しました。

追記:コードはここからたどれます。

お約束ですが、Google Mapsをプログラムで操作する方法を基本的なところから学びたいという場合は、拙著『Webアプリケーション構築入門』を参照してください。

Wolfram CDF PlayerをMathematicaとして使う方法

2017年12月の時点で、ここで紹介する方法は実現困難になっているようです。

http://www.unfindable.net/umm3/

計算可能ドキュメント形式(Computable Document Format, CDF)を閲覧するためのソフトウェアWolfram CDF PlayerとMathematicaの関係は、Portable Document Format (PDF)を閲覧するためのソフトウェアAdobe ReaderとAcrobatの関係に似ています。Wolfram CDF Playerで閲覧可能なCDF文書を作るにはMathematicaが、Adobe Readerで閲覧可能なPDF文書を作るにはAcrobatが必要です。

しかし、CDFとPDFには大きな違いがあります。PDF文書は内容が固定された静的な文書であるのに対して、CDF文書は内容を変化させられる動的な文書です。下はCDF文書の簡単な例です。Wolfram CDF Playerがインストールされている環境なら、aの値を変えながらSin[a x]をプロットしてみることができます。CDF文書の内容は計算によって変化するのです。


Sin[a x]のaの値を変えられるなら、もっと大胆に「Sin[a x]」という式全体を変えられるのではないかと考えるのは自然でしょう。Mathematicaの式を処理できるCDF文書、それはMathematicaとして使えるCDF文書です。使い勝手は多少悪くても、「Mathematicaを使いたいけれど高すぎて買えない」という人にとっては有用でしょう。みんな大好きWolframAlphaも、Mathematicaのすべての機能を使えるわけではありませんし。

残念ながら、直接的な方法はうまくいきません。CDF文書に入力できるのは数値だけであり、「Sin[a x]」のような文字列は入力できないからです。しかし、コンピュータ上で表現されるすべてのものは、メモリ上では数で表現されています。「Sin[a x]」のような式ももちろん数で表現されています(メモリのことがよくわからない人は、ゲーデル数を思い出してもいいでしょう)。ですから、Mathematicaの式を一度数値に変換してからCDF文書に入力し、CDF文書内でそれを元に戻すというような工夫をすれば、CDF文書で式を扱えます。このアイディアを実現したのが、以前紹介したUniversal Mathematica Manipulator (UMM)です。

UMMには、Mathematicaの式を変換してできる数値が長大なため入力に手間がかかるという問題がありました。Wolfram CDF Playerには、文書上でクリップボードからの貼り付けができないという制約があるため、長大になる数値をCDF文書上で入力するための面倒な仕掛け(VBScriptやAppleScript)が必要でした。(Mathematicaに付属するCDF Playerなら貼り付けられます。このように仕様がばらばらなことが後で混乱を生まないことを祈ります。)

ここでは別の方法を紹介します。

まず、Wolfram CDF Playerをインストールします。これがなくては始まりません。

次に、PHPを使えるウェブサーバをlocalhostに用意します。WindowsならXAMPPを導入するのが簡単でしょう。

下のような内容のexpression.phpをhttp://localhost/umm3/expression.phpというURLで呼び出せるようにしておきます。ディレクトリumm3は、ウェブサーバから書き込めるようにしてください。

<?php

$file = 'expression.txt';

if (isset($_GET['body'])) {
  file_put_contents($file, $_GET['body']);
  echo $_GET['callback'].'()';
} else {
  if (is_file($file)) echo file_get_contents($file);
  echo '(* OK *)';
}

PHPのmagic_quotes_gpcがOffであることを確認します。XAMPPの場合はデフォルトでOffになっています。Macの場合はphp.iniを編集してApacheを再起動する必要があるかもしれません。

http://www.unfindable.net/umm3/にアクセスします。

お約束ですが、上の手順がよくわからない人は、拙著『Webアプリケーション構築入門』などを参照してください。

以上の準備ができたらhttp://www.unfindable.net/umm3/にアクセスします。ピタゴラス3体問題のような比較的大きなプログラムも実行できることを、Wolfram CDF Player 8.0.4(Win, Mac)で確認しています。

機械翻訳を使って日本語→英語→日本語…(Bing版)

「翻訳に不動点はあるか—我々は生活の中で美しいを破壊している by Google」では、Google Translate APIを使って「日本語→英語→日本語」や「英語→日本語→英語」を1クリックでできるようにしていました。Google Translate APIが終了してしまうということなので、作り直さなければなりません。

先日で紹介したMicrosoft Translator V2がここでも使えます。翻訳APIを使えるようになるとまず、日本語→英語→日本語…みたいなことを試したくなるものなので、練習にもちょうどいいでしょう。

Open Questions

翻訳エンジン自体が不変のものではないので結論を出せるものではないかもしれませんが、以下のそれぞれに該当するものはあるでしょうか。

  1. 収束しない(測定可能な周期での振動もしない)
  2. 測定可能な周期で振動する

お約束ですが、ボタンを押した時の動作の実装方法やAPIを使う基本的な方法については、拙著『Webアプリケーション構築入門』に書いてあります。

Bingを使ってTwitter上のさまざまな言語のつぶやきをひとつずつ翻訳するユーザーサイドスクリプト

2011年11月末でサービスを終了するGoogle Translate API。これを利用していたアプリその他の対応に追われるという話、その2です。

ある言語で書かれたウェブページをまとめて翻訳するというような場合には、その1で紹介したTranslate Elementで何とかなりそうです(ページのコンテンツが動的に変更される場合には対応できませんが)。

しかし、Twitterのタイムラインに対しては、Translate Elementは無力です。Twitterのタイムラインには複数の言語のつぶやきが同時に存在するため、「ある決まった言語の文書を別の言語に翻訳する」という仕掛けでは対応できないのです。

APIを使ってつぶやきを一つずつ翻訳するというのがよい解決策で、Google Translate APIを使ってそれを実装したのがTwitter上のさまざまな言語のつぶやきをひとつずつ翻訳するユーザーサイドスクリプトでした。一つのつぶやきの中に複数の言語の文章があるときはダメでしたが、たいていの場合はこれで十分でした。

Google Translate APIが使えなくなるということなので、代わりのAPIとしてMicrosoft Translator V2を試すことにします。

GoogleのAPIと違って、まずBing Developer CenterでBing AppIDを取得しなければなりませんが、それさえ済めば。あとはAPIリファレンスを見ながら問題なく使えるでしょう。(お約束ですが、APIを使う基本的な方法については、拙著『Webアプリケーション構築入門』に書いてあります。)

というわけで、Microsoft Translator V2を使ってつぶやきを一つずつ翻訳するユーザーサイドスクリプトを書きました。

FirefoxとChrome 13 betaで動作を確認しています。Chrome 12では動作しません。

インストール方法

  1. Bing Developer CenterでBing AppIDを取得する
  2. (Firefoxの場合)Greasemonkeyをインストールする
  3. Twitter Translator (Bing)をインストールする
  4. Twitterにアクセスする
  5. (最初に利用する時のみ)Bing AppIDを入力する
  6. (最初に利用する時のみ)ブラウザ(あるいはタブ)を一度閉じ、再度Twitterにアクセスする

Bing AppIDをリセットしたい時は、twitter.comの「Bing_AppID」というcookieを削除してください。

Bing AppIDを設定しなければならないので、ちょっと面倒なスクリプトになってしまいましたが、とりあえずはこれでよしとしましょう。

2011/12/10 Twitterの新しいUIに対応しました。

Twitter時代の文字の数え方

正確には、「Unicode 3.1時代の文字の数え方」なのでしょうが、Unicodeの最新バージョンが6.0の今、それではぱっとしないので。

はじめに

一言で言えば、「𠮷野家」は3文字なのか4文字なのか、という話です。これはもちろん3文字で、Twitterの1回のつぶやきに46個含められます。しかし、4文字だとみなされるケースがたくさんあるのです。

30過ぎて数も数えられない、なんてことになるとは思っていなかったのですが、文字を数えるのはけっこう難しいです。とはいえ、Twitterがこれだけ普及しているのに、「難しい」と言って済ますわけにもいかないので、ちょっとまとめておきましょう(拙著『Webアプリケーション構築入門』で使ったもの+αで)。

注意:ちゃんとやるためには、Unicode正規化の話とか、もしかしたらIVSの話とか、いろいろやらなければいけないのかもしれませんが、ここでは話を少し単純にしています。

まとめのまとめ

Unicodeの登録文字数が少ない時代に作られたシステムが、登録文字数の多い時代に対応できていないという問題があります。例えば「𠮷」(U+20BB7)はもちろん1文字ですが、UTF-8・UTF-16ともに、想定を超える4バイトで表されるため、2文字とみなされることがあります(UTF-8のナイーブな想定は3バイト、UTF-16のそれは2バイト)。

𠮷
コードポイント U+20BB7 U+91CE U+5BB6
UTF-8 F0 A0 AE B7 E9 87 8E E5 AE B6
4バイト 3バイト 3バイト
UTF-16 D842 DFB7 91CE 5BB6
4バイト 2バイト 2バイト

対策方法は以下の通りです。

  • HTML5のフォーム検証:あきらめる
  • JavaScript:注意が必要(ふつうの方法ではダメ)
  • PHP:問題なし
  • Java:注意が必要(ふつうの方法ではダメ)
  • MySQL:5.1以前ではあきらめる。5.5以降ではutf8mb4を使う
  • Perl 5.?:問題なし(コメントでの指摘)
  • C#:注意が必要(ふつうの方法ではダメ。正規表現も要注意)
  • Visual Basic:注意が必要(ふつうの方法ではダメ。正規表現も要注意)
  • ASP.NET:注意が必要(ふつうの方法ではダメ。正規表現も要注意)

PerlやPHPは問題ないとは言っても、MySQLを使うようなアプリケーションでは問題が起こることに注意してください。

この結論に至る理由を、少し詳しく説明します。


クライアント側

HTML5

HTML5のフォームでは入力されたデータの検証ができるようになっているのですが、この機能を使って文字数をチェックするのは、やめたほうがよさそうです。

maxlength属性

HTML5のフォームでは、input要素やtextarea要素において、入力できる文字数をmaxlength属性で指定できるようになっています。たとえば、次のようなinput要素では、3文字までしか入力できません。

<input type="text" maxlength="3" />

ところが、ブラウザによっては、ここに「𠮷野家」という3文字を入力できません。そもそもこの3文字を正しく表示できないブラウザもあるようなので、手元のWindows上のブラウザで調べてみました(環境に依ります。たとえば、MacのSafariでは表示が○になります)。

ブラウザ 表示 入力
Chrome 12.0.742.100
Safari 5.0.5 ×
Firefox 21.0 ×
IE 8 ×
IE 9 ×
IE 10
Opera 12.15 × ×

入力「×」のブラウザでは、「𠮷」が2文字とみなされるため、2文字目まで、つまり「𠮷野」までしか入力できません。

Mozillaの文書には、Unicode code pointsで数えると書いてあるので、そのうち改善されるのかもしれませんが、現時点ではTwitterのために「maxlength="140"」を使うことはできません。

pattern属性

Firefox 21とChrome 27、IE 10、Opera 12.15は、「pattern=".{0,3}"」(任意の文字からなる0から3文字)のような正規表現を使った検証にも対応していますが、やはり「𠮷野家」は4文字とみなされてしまいます。


JavaScript

追記:javascript – でBMP以外のUnicode文字をきちんと扱う(404 Blog Not Found)

JavaScriptでは、文字列strの長さをstr.lengthで取得できることになっていますが、残念ながらこの方法では、上記のすべてのブラウザで「𠮷野家」は4文字とみなされてしまいます。

かつて、Twitterのウェブサイトではこの方法で文字数を数えていたと思われます。APIでならつぶやける文字列が、ウェブサイトからでは文字数超過でつぶやけなかったのです。Twitterに報告して直してもらいましたが(参考(https://support.twitter.com/tickets/1279115)リンク切れ)、「𠮷野家」を4文字とみなすクライアントはまだたくさんあるでしょう(手元のHootSuiteとついっぷるはそうでした)。

ちなみに、「.{0,3}」という正規表現で検証することもできません。「𠮷野家」はやはり4文字だとみなされます。

次のような関数を使えば、JavaScriptでも文字列の長さを正しく測ることができます(Stringオブジェクトをそのまま拡張する方法が、「サロゲート・ペアに対応した文字列操作関数を書いてみた」で紹介されています)。

function strlen(str) {
  var i = 0, len = str.length, result = 0;
  while (i < len) {
    result++;
    var x = str.charCodeAt(i++);
    if (0xD800 <= x && x < 0xDC00) i++;
  }
  return result;
}

この方法で数えた文字数が140以下なら、Twitterでつぶやくことができるでしょう(サーバ側も自分で作るときは、サーバ側でのチェックも必要です)。


サーバ側

PHP

mb_strlen()

関数mb_strlen()で正しく長さを測れます。ただし、クライアントから送信されたデータを処理する際には、関数mb_check_encoding()を使って、文字以外の不正なものがないことをまず確認するといいでしょう(拙著『Webアプリケーション構築入門』でも触れました)。

正規表現

長さがある範囲にあることをチェックするだけなら、徳丸浩『体系的に学ぶ 安全なWebアプリケーションの作り方』(ソフトバンククリエイティブ, 2011)で紹介されている方法が洒落ています。

if (preg_match('/\A\P{Cc}{1, 140}\z/u', $str) == 1) {
  // OK

Java

codePointCount()

Javaでは、文字列strの長さをstr.length()で取得できることになっていますが、残念ながらこの方法では「𠮷野家」は4文字とみなされてしまいます(JavaScriptの場合と同じです)。

拙著でも紹介したように、長さを正しく測りたい場合は、「str.codePointCount(0, str.length())」としなければなりません。ただし、クライアントから送信されたデータを処理する際には、条件「str.indexOf(0xFFFD) < 0」をチェックして、文字以外の不正なものがないことをまず確認するといいでしょう。

正規表現

長さがある範囲にあることをチェックするだけなら、徳丸浩『体系的に学ぶ 安全なWebアプリケーションの作り方』(ソフトバンククリエイティブ, 2011)で紹介されている方法が洒落ています(PHPの場合と同じです)。

if (str.matches("\\P{Cc}{1,140}")) {
  // OK

MySQL

MySQL 5.1以前

UTF-8で4バイトになるような文字を文字として扱うようにはなっていないので、文字列はbinaryとして保管し、文字列の長さを関数char_length()で測ったりするのはあきらめましょう(PHPやJavaなど、MySQLの外で測るのが簡単)。n文字まで格納したいなら、VARCHAR(4n)にしなければなりませんが、使う文字によってはn文字より多く格納できてしまうのが困りものです(全部ASCIIなら4n文字)。

MySQL 5.5以降

UTF-8で4バイトになるような文字を文字として扱うためのutf8mb4が導入されたので、これを使うのが簡単です。文字列の長さは関数char_length()で正しく測れます。n文字まで格納したいときも、VARCHAR(n)で大丈夫です。


おわりに

「𠮷野家」は単なる例です。「𠮷野家」でも「吉野家」を出してくるGoogleは偉いとは思いますが、拙著でも述べたように、それでも「吉野家」と書くべきだと私は思います。しかし、ここで述べたような話は、絵文字が大量に導入されたUnicode 6.0の普及が進めば、深刻な問題になるかもしれません。Javaが虐げられる理由が増えなければいいのですが。

想定外(あるいは無視された想定)の津波が原子力発電所を襲うほどの深刻度ではありませんが、ITの世界では、過去に2000年問題がありましたし、現在もこういう問題が発生していますし、将来もおそらく似た問題は起こるのでしょう(例えば2038年問題とか)。

先祖を笑えるようにはならず、先祖からは笑われ続けるのでしょうか。

文字を数えるのはけっこう難しいです。


追記

Perl

Perlは問題ないということをコメントで教えてもらいました。しかし、まさにここで紹介した問題のせいでコメントを壊してしまったので、本文にコードを追記します。

$ perl -v
This is perl 5, version 14, subversion 0 (v5.14.0) built for darwin-2level

$ perl -Mutf8 -E 'say length(𠮷野家)'
3

.NET Framework 4.0

追記

.NET Framework 4.0で文字列の長さを知りたい時は、StringInfo.LengthInTextElementsを使います。String.Lengthではダメです。PHPやJavaでは有効だった正規表現による長さの検査が使えないことにも注意してください。参考:田丸健三郎『UnicodeによるJIS X0213実装入門』(日経BPソフトプレス, 2008)

C#

String str = "𠮷野家";
System.Console.WriteLine(str.Length);
//4

StringInfo si = new StringInfo("𠮷野家");
System.Console.WriteLine(si.LengthInTextElements);
//3

Regex pattern = new Regex("^\\P{Cc}{4}$");
System.Console.WriteLine(pattern.IsMatch("𠮷野家"));
//True(「4文字」にマッチ)

Visual Basic

Dim str As String = "𠮷野家"
Console.WriteLine(str.Length)
'4

Dim si As New StringInfo("𠮷野家")
Console.WriteLine(si.LengthInTextElements)
'3

Dim pattern As New Regex("^\P{Cc}{4}$")
Console.WriteLine(pattern.IsMatch("𠮷野家"))
'True(「4文字」にマッチ)

ASP.NET

拙著『Microsoft Visual Web Developer 2008 Express Edition入門』で紹介した、正規表現による検証コントロールであるRegularExpressionで「.{0,3}」のような正規表現を設定しても、文字列の長さを正しく検証することはできません。「𠮷野家」は4文字とみなされてしまうからです。

PHPやJavaで有効だった「\P{Cc}{0,3}」という正規表現は機能しないようです(機能したとしても、上述のC#やVisual Basicの結果を見ると役には立たなそうですが)。

そこで、CustomValidatorを使うことになります。しかし、MSDNで公開されている「方法 : ASP.NET サーバー コントロールをカスタム関数で検証する」はダメです。文字列の長さを正しく測れません。正しく検証するためには、次のようなコードを使うといいでしょう。


StringInfo si = new StringInfo(args.Value);
args.IsValid = si.LengthInTextElements <= 3; [/csharp] クライアント側で処理したいときも、MSDNの方法はダメで、次のようなJavaScriptが必要になるでしょう(関数strlen()の定義は上述)。

function validateLength(oSrc, args) {
    args.IsValid = (strlen(args.Value) <= 3);
}