TIM Labs

1024によるエントリー一覧

RAII の話題は C++ の例が多いが、それもそのはず、ここ最近は GC が面倒を見てくれることがほとんどなので、 RAII の需要そのものが減っている。とはいえネットワークコネクションやらファイルハンドルやらと、あまりリソース解放のタイミングを遅らせたくないものに関しては、 RAII パターンで寿命を明示しておきたいのは GC があったとしても同じこと。

Ruby もそういったリソースに関してはブロックを渡すことで解放もしてくれる RAII パターンを実装したインターフェースが提供されているので、積極的に使っていきたいところだ。そういったリソースがひとつだけなら、素直に書けばいいだけのことなのであまり考える必要はない。

open(...) do |fp|
  something(fp)
end # ここでファイルハンドルが解放される

ところが、配列になっているものに対し、すべての要素を開き、同時に使いたいとなると意外と面倒なことに気付く。配列 arr = [a1, a2, a3, ...] があったとき、意味的にはこういうことがしたい。

open(a1) do |b1|
  open(a2) do |b2|
    open(a3) do |b3|
      ...
      something([b1, b2, b3, ...])
    end
  end
end

が、配列の要素数がわからない以上、この方針でネストを続けていくわけにはいかない。 果て...。どうすればいいだろう?

巷では「ズンドコキヨシ」というプログラミング課題が流行っているらしい。そりゃなんぞや、というところだが、要点としては《「ズン」「ドコ」のいずれかをランダムで出力し続け、「ズン」「ズン」「ズン」「ズン」「ドコ」の配列が出たら「キ・ヨ・シ!」と出力して終了するプログラムを作成せよ》ということになるだろうか。既に多くの方が挑戦しているようで、以下にまとめられている。

ズンドコキヨシまとめ

すっかり波には乗り遅れている感じではあるが、それはともかくとしてもプログラミング課題としてとてもセンスが良い。ざっと思いつくプログラミング上のポイントは

  • 文字列出力
  • 乱数利用
  • 状態管理
  • 繰り返し、およびその中断

あたりだろうか。ツボを押さえたとても良い課題である。

というわけで、是非やってみたい。そう、敢えて、「手続き型」 Haskell で。

と、こんな表題で書くからには当然、「等価ではないこともある」と言いたいわけだが、その前に一般的な話をおさらいしておこう。

Ruby では表題の通り、 method1 { |e| e.method2 }method1(&:method2) は基本的に等価だとされる。こんな感じだ:

[1,2,3].map { |e| e.to_s } # => ["1", "2", "3"]
[1,2,3].map(&:to_s)        # => ["1", "2", "3"]

どちらの書き方が良いのか、というのは人やプロジェクトなどによりけりではあるが、後者のメリットのひとつとして「ブロック内の変数を命名しなくて良い」というものがある。我々ソフトウェアエンジニアにとって命名とは一般的に重労働であり、その労働力はできればもっと重要な箇所で使いたい。そんなわけで我々としてはできれば後者の記法を利用していきたいわけである。

ところが表題の通り、この二つは必ずしも同じ挙動になるとは限らない。実際に二つほど発見したので紹介しよう。

早速だが、コードを見てほしい。ネットワークデータを一旦ローカルの一時ファイルに受け取り、後でゆっくり読み書きしよう、という意図のコードだ。呼び出し元はファイルハンドルを受けとり、シークを含めた読み書きができる、というか、したい。

def create_tempfile(*)
  network_source = ... # 省略
  Tempfile.new("example").binmode.tap do |file|
    IO.copy_stream(network_source, file)
    file.rewind
  end
end

ところが、このコードには問題がある。普段何も問題なく動くのに、時折このメソッドから受け取るファイルに対して操作をしようとしたら IOError が発生することがある。しかもこのエラーは環境や時間、扱うデータによって発生したりしなかったりするし、スタックトレースはなにやら所謂ライブラリ類の奥深く。そもそも問題の原因がここであることを突き止めるにも一苦労があったわけだが、えーっと、現場の苦労話はさておきだ、とにかく「問題の原因はこのコード」、「発生している例外が持つメッセージは "closed stream"」である。

どう直せば解決するか、分かっただろうか?

先日 GHOST と呼ばれる glibc の脆弱性が発表された。なんでも、「リモートから任意のコードを実行できる可能性がある」らしいではないか。しかも様々なプログラムで利用されているライブラリ部分の問題とあって、影響範囲がとても広い。なかなか厄介なことである。

はて、しかし一体全体どうやってリモートから任意のコードを実行しようというのだろう? 話を聞くに、たかが数バイトの情報を範囲外のメモリに書き込める可能性があるだけだという。実際それだけのことでサーバーの乗っ取りなどできるものなのだろうか。そんなわけで、その疑問に答えるべく、本記事では以下の URL で解説されている実際の攻撃方法を若干端折って紹介してみようと思う。

http://www.openwall.com/lists/oss-security/2015/01/27/9

なお、本記事はこの脆弱性そのものに対する緊急度などについて言及するものではないし、実際に攻撃を行うことを推奨するものでもない。くれぐれも攻撃に用いたりしないようご注意願いたい。また、脆弱性そのものに対する適切な対応などについては、専門機関や専門家の指示や勧告に従って欲しい。

こんな感じにデータを持ってる sample_table があるとする。

+----+----------+------------+---------+
| id | group_id | updated_at | comment |
+----+----------+------------+---------+
|  1 |        1 | 2013-12-01 | C       |
|  2 |        2 | 2013-12-01 | A       |
|  3 |        1 | 2013-12-02 | B       |
|  4 |        2 | 2013-11-30 | D       |
+----+----------+------------+---------+

MySQL で同じデータを作成したければ、以下の SQL で再現できる。

CREATE TABLE sample_table (
    id int(11) NOT NULL,
    group_id int(11) NOT NULL,
    updated_at date NOT NULL,
    comment varchar(60) NOT NULL
);
INSERT INTO sample_table VALUES
    (1, 1, '2013-12-01', 'C'),
    (2, 2, '2013-12-01', 'A'),
    (3, 1, '2013-12-02', 'B'),
    (4, 2, '2013-11-30', 'D');

こんなデータのとき、同じグループ内であるフィールドが最大(あるいは最小)のレコードを取得したい、ということがたまにある。例では、group_id それぞれについて、 updated_at が最新のレコード、即ち comment が A および B のレコードを取得したいとしよう。

さて、どんな SQL を書けばよいだろう?

Rails のバリデーションには特定のコンテキストのときだけ実行させることができる on オプションが存在している。

validates :field1, presence: true
validates :field2, presence: true
validates :field3, presence: true, on: :admin

こうすると、 valid?(:admin) のときだけは全てのフィールドが必須入力となるが、そうでない場合は field1field2 のみ必須となり、 field3 は任意入力になる。なるほど、便利だ。

ところが、これを逆転させたいとき、即ち :admin コンテキストのとき「だけ」field3 を任意入力にしたいとなると、途端に面倒なことになる。

validates :field1, presence: true
validates :field2, presence: true
validates :field3, presence: true, on: [:create, :update, :context_foo, :context_bar]

おおう。これ、 :context_baz が増えたら、ここもメンテナンスせなあかんのか? ちょっとそれはなくない? このコードからは「:admin コンテキストの時だけ任意入力」という意図が全く読み取れない(そもそも :admin が出てこないではないか)ので、適切にメンテナンスするのは無理がある。

前回、無理数の取り扱いを複素数的な考えで取り扱ってフィボナッチ数列の一般項を求めた。意味的にはさほど遅くないはずの実装ではあるが、それでも 100,000,000 番目を求めようとすると結構時間がかかってしまう。実際に計測するとこんな感じだ。

90.67s user 0.21s system 99% cpu 1:30.94 total

チューニングの余地があるのは恐らく素朴に書いた累乗計算、fibExp だ。素朴と言っても一応アルゴリズム的な考慮はしたつもりだが、とはいえ言語について詳しく知っているわけでもなし、色々素人が小手先のチューニングを施すよりも、標準ライブラリに任せたほうがこういうものは余程パフォーマンスがいいものと相場が決まっている。

Haskell での累乗計算は (^) を使う。GHCi に聞いてみると、

> :i (^)
(^) :: (Num a, Integral b) => a -> b -> a       -- Defined in `GHC.Real'
infixr 8 ^

ということなので、a が Num クラスに属してさえいれば、a はなんだっていい。ということは、前回の FibNum を Num クラスのインスタンスにしてしまえばいいわけだ。それさえできれば標準の (^) が独自の型にも適用できるようになる。素晴らしい。

フィボナッチ数列という有名な数列がある。 1 1 2 3 5 8 ... と続くアレだ。漸化式でいうと、

\begin{eqnarray*} F_1 &=& 1 \\ F_2 &=& 1 \\ F_{n+2} &=& F_n + F_{n+1} \ \ (1 \leq n) \end{eqnarray*}

っていうこいつである。

で、こいつの一般項というのが知られている。それは以下の通りである。...らしい。

$$ F_n = \frac{1}{\sqrt{5}}\biggl\{\biggl(\frac{1 + \sqrt{5}}{2}\biggr)^n - \biggl(\frac{1 - \sqrt{5}}{2}\biggr)^n\biggr\} $$

え、えー? いやちょっと待ってよだってほら整数の数列だし一般項に無理数入ってるとか大分意味わかんないんですけどこれ大丈夫なの本当に?

うーん。確認してみよう。

fibDouble :: Integer -> Double
fibDouble n = ( ((1 + sqrt 5)/2)^n - ((1 - sqrt 5)/2)^n ) / sqrt 5

main :: IO ()
main = mapM_ (print . fibDouble) [1..10]

runghc すると:

1.0
1.0
2.0
3.0
5.0
8.0
13.0
21.0
34.0
54.99999999999999

うん。あって...る。微妙に誤差があるのだがまあ四捨五入すれば十分使えるレベルのハズ。誤差が怪しかったひとつ前の 9 番目からを改めてやってみよう。

fibDouble :: Integer -> Integer
fibDouble n = round $ ( ((1 + sqrt 5)/2)^n - ((1 - sqrt 5)/2)^n ) / sqrt 5

main :: IO ()
main = mapM_ (print . fibDouble) [9..15]

えいや

34
55
89
144
233
377
610

良いのでは?

一般項を使っているので、 n 番目一個だけを知りたい、という要求においてはこの実装は確かにそれなりに速い。ただ内部の計算が浮動小数なので、100 番目くらいを求めようとするともうもはや求まらない。

main = print $ fibDouble 100

実行すると

354224848179261800448

あってる? いいえ、残念。100 番目の本当のフィボナッチ数は 354224848179261915075 なのだ。(※ちなみにこの数を Google さんで検索すると、本記事執筆時点で 7,000 件以上ひっかかってそれなりに有名な数であることがわかる。)

もう浮動小数なんかダメだ。信用ならない。まったく、たかが 100 番目くらいで正しい答えを得られないようでは"なってない"といわざるを得ないではないか。やっぱりここは誤差なしで扱えるイケメン、じゃなかった、有理数に限る。

でも、どうやって? 元の式に無理数が含まれている以上、有理数だけで計算するなんてできっこないじゃないか。

えっ、世間ではHTML5の話題でいっぱいなのに、今更XHTMLの話? XHTML 2.0は頓死したって聞いたよ?

まあ、その点についてはその通り。

しかし未だにIE6は爆発せず、 SafariにもOperaにもChromeにもFirefoxにも対応する必要があり、 IEに至っては何故か4バージョン(!)分も対応しなきゃならない現状を鑑みれば、 まだあと数年、場合によっては十数年はXHTMLも現役であることは十分に想定されるわけで、 そういう意味では正しいXHMLの知識というのは無駄にはならないはずだ。 それに、より正則なXHTMLの方が、HTML5に移行するときも楽に違いない。

さて、このトピックで扱うのは、要素の包含関係についてだ。 ただのXMLとしてみればきちんと入れ子になり、閉じタグがあって問題ないとしても、 XHTMLとしては不整なことはよくある。 良いことなのか余計なお世話なのか、 殆どのブラウザでは多少不整であってもどうにか「それっぽく」表示してしまうので、 開発中は気付かないことが多いのだ。 そして、後でこうなる。

「なんかさ、IE8で見るとこの画面オカシイんだけどどうなってんの?」

あーあ。他のブラウザだとちゃんと(?)表示されるのにね。困ったことだ。

XHTMLのValidatorは世に存在しているので、本トピックで扱うようなことは本来それで十分検出できるはずなのだが、 実際にはなにかと「XHTMLっぽいがXHTMLではなく、テンプレートエンジンを通すと最終的にはXHTMLが出てくるハズの何か」 になっていて、上手くValidatorを通せないことも多いのがWeb開発の現実だ。 もちろん、理想を言えば、テンプレートエンジンを通した後の成果物に対してはValidatorを掛けておくべきなのだが...。

そんなわけで、出来得る限り書く時点で気を付けるに越したことはないのだ。 本トピックでは、実際に見かけた不整なパターン 7 個を紹介する。 細かい点や使用頻度の低いタグについては、直接仕様書にあたられたい。

このアーカイブについて

このページには、1024が最近書いたブログ記事が含まれています。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。