February 2007
Sun Mon Tue Wed Thu Fri Sat
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28
Search


Sections
Oracle Article Summary
Categories
Apple   [2 items]
Cinema   [82 items]
Lambda   [2 items]
Oracle   [12 items]
Podcasting   [1 items]
Archives
February 2006
January 2006
December 2005
November 2005
October 2005
September 2005
July 2005
June 2005
May 2005
April 2005
March 2005
February 2005
January 2005
December 2004
November 2004
October 2004
September 2004
August 2004
July 2004
June 2004
May 2004
Links
robios.org -annex-
鮎の詰め合わせ
ぞうパラ
Jasi's Blog
ぱんでもにぅむ・ぶろぐ
へべれけ
Recent Entries
キング・アーサー - King Arthur
Stored Outlineの活用 実践編
Stored Outlineの活用
スパイダーマン2 - Spider-Man 2
SchemeによるRaytracer
運命の逆転 - Reversal of Fortune
無名関数の再帰
スイミング・プール - Swimming Pool
Recent TrackBacks
Versace Perfume
(Versace Perfume, Feb 7)

Xanax overdose.
(Xanax side effects., Feb 7)

Rhinoplasty Recovery Time
(Rhinoplasty Recovery Time, Feb 6)

Acdsee Software
(Acdsee Software, Feb 5)

4250 Toner Cartridge
(4250 Toner Cartridge, Feb 3)

Hardside Luggage
(Hardside Luggage, Jan 29)

Headboard Bookcase
(Headboard Bookcase, Jan 26)

Samsonite Wheeled
(Samsonite Wheeled, Jan 26)

Tinkerbell Myspace Backgrounds
(Tinkerbell Myspace Backgrounds, Jan 24)

Breitling Chrono Avenger
(Breitling Chrono Avenger, Jan 23)

Syndicate
Syndicate this site (XML)

キング・アーサー - King Arthur

July 29, 2004

キング・アーサーを観た。

Yahoo!ムービーによる解説:
有名な伝説「アーサー王と円卓の騎士」をスペクタクル感あふれる映像で描く歴史アドベンチャー大作。アーサー王を『すべては愛のために』のクライヴ・オーウェンが演じ、やがて王妃となるグイナヴィアに『パイレーツ・オブ・カリビアン』のキーラ・ナイトレイが扮している。彼女の見事な弓矢さばは要チェック。

イングランドの伝説の王のお話。アーサー王になる前の話でした。

Jerry Bruckheimerが手がけた、最近はやりの騎士もの(というか中世ものというか)。映画として純粋に楽しめるが、主題はあまりはっきりとはしない。ストーリー展開も非常に早い。編集しすぎたのかな?もう少しプロットを丁寧にすれば、もっとよかっただろうなぁ。

それでも、アーサー王と円卓の騎士や、グイナヴィアのキャラクターはうまく描かれていて、よい。マーリン率いる部族は(?_?)だったけど。

監督: Antoine Fuqua
製作: Jerry Bruckheimer
出演: Clive Owen, Ioan Gruffudd, Keira Knightley, 他

Stored Outlineの活用 実践編

July 28, 2004

前述した、Stored OutlineのHackを実践してみる。

まずは実験用テーブルを作る。

ora817@robios.org> create table hoge_transactions
  2  as
  3  select rownum as transaction_id, 'HOGE' as data,
  4  4 as status_code
  5  from all_objects
  6  where rownum <= 100000;

表が作成されました。

経過: 00:00:20.04
ora817@robios.org> create index hoge_transactions_n1
  2  on hoge_transactions (status_code);

索引が作成されました。

経過: 00:00:00.08
ora817@robios.org> update hoge_transactions
  2  set status_code = 1
  3  where transaction_id between 99000 and 99099;

100行が更新されました。

経過: 00:00:00.01
ora817@robios.org> commit;

コミットが完了しました。

経過: 00:00:00.01
ora817@robios.org> analyze table hoge_transactions
  2  compute statistics
  3  for table
  4  for all indexes
  5  for all indexed columns;

表が分析されました。

経過: 00:00:02.02

これで、Cardinalityが非常に低いコラムを索引対象したテーブルが作成された。ヒストグラムが作成されているため、STATUS_CODE = 1で検索すると、索引検索が行われる。

ora817@robios.org> select *
  2  from hoge_transactions
  3  where status_code = 1;

TRANSACTION_ID DATA STATUS_CODE
-------------- ---- -----------
         99000 HOGE           1
         99001 HOGE           1
         99002 HOGE           1

- 途中省略 -

         99097 HOGE           1
         99098 HOGE           1
         99099 HOGE           1

100行が選択されました。

経過: 00:00:03.02

実行計画
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=100 Bytes=160
          0)

   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'HOGE_TRANSACTIONS' (Cost
          =2 Card=100 Bytes=1600)

   2    1     INDEX (RANGE SCAN) OF 'HOGE_TRANSACTIONS_N1' (NON-UNIQUE
          ) (Cost=1 Card=100)





統計
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         17  consistent gets
          5  physical reads
          0  redo size
       2759  bytes sent via SQL*Net to client
        384  bytes received via SQL*Net from client
          9  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
        100  rows processed

しかし、バインド変数を使うと、索引は使用されなくなる。(→理由についてはCBOにおける索引の使用判断 - コストの計算例を参照)

ora817@robios.org> variable status number;
ora817@robios.org> exec :status := 1;

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.00
ora817@robios.org> select *
  2  from hoge_transactions
  3  where status_code = :status;

TRANSACTION_ID DATA STATUS_CODE
-------------- ---- -----------
         99000 HOGE           1
         99001 HOGE           1
         99002 HOGE           1

- 途中省略 -

         99097 HOGE           1
         99098 HOGE           1
         99099 HOGE           1

100行が選択されました。

経過: 00:00:03.02

実行計画
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=39 Card=50000 Bytes=
          800000)

   1    0   TABLE ACCESS (FULL) OF 'HOGE_TRANSACTIONS' (Cost=39 Card=5
          0000 Bytes=800000)





統計
----------------------------------------------------------
          0  recursive calls
         12  db block gets
        257  consistent gets
        247  physical reads
          0  redo size
       2759  bytes sent via SQL*Net to client
        407  bytes received via SQL*Net from client
          9  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
        100  rows processed

さて、このようなSQLがOracle ApplicationのProcedureの中にあり、手が出せないとする(実際にこのようなSQLはよくある)。先のOutline Hackでパフォーマンス改善を行う。

まずはProcedureを作る。

ora817@robios.org> create or replace procedure process_hoge_transactions is
  2    unprocessed_status constant number := 1;
  3  begin
  4    for cur in (select *
  5                from hoge_transactions
  6                where status_code = unprocessed_status)
  7    loop
  8      dbms_output.put_line(cur.transaction_id || ', ' || cur.data);
  9    end loop;
 10  end;
 11  /

プロシージャが作成されました。

経過: 00:00:00.01
ora817@robios.org> alter session set sql_trace=true;

セッションが変更されました。

経過: 00:00:00.01
ora817@robios.org> exec process_hoge_transactions;
99000, HOGE
99001, HOGE
99002, HOGE

- 途中省略 -

99097, HOGE
99098, HOGE
99099, HOGE

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.08
ora817@robios.org> alter session set sql_trace=false;

セッションが変更されました。

経過: 00:00:00.01

トレースの結果を示す。

********************************************************************************

SELECT *
FROM
 HOGE_TRANSACTIONS  WHERE STATUS_CODE = :b1


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch      101      0.06       0.07        247        350         12         100
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total      103      0.06       0.07        247        350         12         100

Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 183     (recursive depth: 1)
********************************************************************************

まず、ターゲットSQLのOutlineを作る。CREATE_STORED_OUTLINESフラグを立てて、Procedureを実行する。

ora817@robios.org> alter session set create_stored_outlines=true;

セッションが変更されました。

経過: 00:00:00.00
ora817@robios.org> exec process_hoge_transactions;
99000, HOGE
99001, HOGE
99002, HOGE

- 途中省略 -

99097, HOGE
99098, HOGE
99099, HOGE

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.09
ora817@robios.org> alter session set create_stored_outlines=false;

セッションが変更されました。

経過: 00:00:00.00
ora817@robios.org> select ol_name, sql_text
  2  from outln.ol$;

OL_NAME                        SQL_TEXT
------------------------------ ----------------------------------------
SYS_OUTLINE_040729043824572    SELECT *   FROM HOGE_TRANSACTIONS
                               WHERE STATUS_CODE = :b1

経過: 00:00:00.02

次に、ターゲットSQLにヒントを埋め込み、索引が正しく使われるようにする。そしてそのSQLのOutlineを作成する。

ora817@robios.org> select /*+ RULE */ *
  2  from hoge_transactions
  3  where status_code = 1;

TRANSACTION_ID DATA STATUS_CODE
-------------- ---- -----------
         99000 HOGE           1
         99001 HOGE           1
         99002 HOGE           1

- 途中省略 -

         99097 HOGE           1
         99098 HOGE           1
         99099 HOGE           1

100行が選択されました。

経過: 00:00:03.03

実行計画
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: RULE
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'HOGE_TRANSACTIONS'
   2    1     INDEX (RANGE SCAN) OF 'HOGE_TRANSACTIONS_N1' (NON-UNIQUE
          )





統計
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         17  consistent gets
          3  physical reads
          0  redo size
       2680  bytes sent via SQL*Net to client
        396  bytes received via SQL*Net from client
          9  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
        100  rows processed

ora817@robios.org> create outline temp on
  2  select /*+ RULE */ *
  3  from hoge_transactions
  4  where status_code = 1;

アウトラインが作成されました。

経過: 00:00:00.01

先ほどのOutlineと、今作成したOutlineを入れ替える。OL$のHINTCOUNTも忘れずに(値は前もって確認)。

ora817@robios.org> update outln.ol$hints
  2  set ol_name = decode(ol_name, 'SYS_OUTLINE_040729043824572', 'TEMP',
  3                                'TEMP', 'SYS_OUTLINE_040729043824572')
  4  where ol_name in ('SYS_OUTLINE_040729043824572', 'TEMP');

13行が更新されました。

経過: 00:00:00.01
ora817@robios.org> update outln.ol$
  2  set hintcount = decode(ol_name, 'SYS_OUTLINE_040729043824572', 7,
  3                                  'TEMP', 6)
  4  where ol_name in ('SYS_OUTLINE_040729043824572', 'TEMP');

2行が更新されました。

経過: 00:00:00.01
ora817@robios.org> commit;

コミットが完了しました。

経過: 00:00:00.00

これで完了である。試しに、USE_STORED_OUTLINESフラグをオンにし、ヒント埋め込みバージョンを実行してみる。うまくいっていれば、全スキャンが行われるはずである。

ora817@robios.org> alter session set use_stored_outlines=true;

セッションが変更されました。

経過: 00:00:00.00
ora817@robios.org> select /*+ RULE */ *
  2  from hoge_transactions
  3  where status_code = 1;

TRANSACTION_ID DATA STATUS_CODE
-------------- ---- -----------
         99000 HOGE           1
         99001 HOGE           1
         99002 HOGE           1

- 途中省略 -

         99097 HOGE           1
         99098 HOGE           1
         99099 HOGE           1

100行が選択されました。

経過: 00:00:03.04

実行計画
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: RULE (Cost=39 Card=100 Byte
          s=1600)

   1    0   TABLE ACCESS (FULL) OF 'HOGE_TRANSACTIONS' (Cost=39 Card=1
          00 Bytes=1600)





統計
----------------------------------------------------------
         48  recursive calls
         20  db block gets
        264  consistent gets
        252  physical reads
        600  redo size
       2727  bytes sent via SQL*Net to client
        292  bytes received via SQL*Net from client
          8  SQL*Net roundtrips to/from client
          3  sorts (memory)
          0  sorts (disk)
        100  rows processed

全スキャンが行われている。次に本題のProcedureを実行し、トレースを取ってみる。

********************************************************************************

SELECT *
FROM
 HOGE_TRANSACTIONS  WHERE STATUS_CODE = :b1


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.01          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch      101      0.01       0.00          3        202          0         100
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total      103      0.01       0.01          3        202          0         100

Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 183     (recursive depth: 1)
********************************************************************************

索引が使用され、Disk IOが大幅に減ったのが分かっていただけると思う。

その他:

  • PL/SQLで、定数として変数を宣言し、それを検索のキーとして使うのは避けるべきである。定数と宣言してもバインド変数となり、せっかくのヒストグラムが使用されない。上記Procedureで、STAUTS_CODE = 1と素直に書けば、ヒストグラムが使用され、索引検索が行われる。
  • ヒント埋め込みバージョンSQLの作成は、今回の場合はヒストグラムがある為あえてRULEヒントを加える必要はない。
  • 今回作成した表の、STATUS_CODEのようなCardinalityが非常に低い項目に索引を作成する場合、通常の索引ではなくビットマップ索引を作成すると効果的である。しかし、ビットマップ索引の場合、近いレコードへの同時更新が発生すると、ロックが発生するため注意が必要。
  • USE_STORED_OUTLINESフラグを立てた場合、ハード解析時に、そのSQLのOutlineがOL$に定義されているかどうかを調べるため、OL$をHash Valueで検索する。そのため、多少のオーバーヘッドが生じることになる。オーバーヘッドはRecursive Callという形で現れる。このオーバーヘッドについての検証はいずれ。

Stored Outlineの活用

July 25, 2004

手を入れたいがどうしても入れることができないSQLが多くある。ヒント文を入れるだけで、コストがうんと下がるのであれば、この方法は有効だ。

Oracle 8iよりStored Outlineという、あまり知られていない、しかし非常に有用な機能が追加された。

Stored Outlineとは、Ask Tomのある一稿から引用すると、「the ability for a developer to save an outline, a set of “hints to the server” for executing a specific SQL statement in the database」、とあり、日本語にすれば、「あるSQLを実行する際Oracleが参考とするヒント文、すなわちOutlineを保存する機能」となる。

この機能を使うことで、SQLが実行される際のヒント(というかむしろ実行計画そのもの)を、テーブルに保存することができる。また、USE_STORED_OUTLINESフラグを立てることで、その後は同SQLはそのテーブルに保存されたヒントを基に問い合わせが実行される。

ヒントが保存されるテーブルは、下記の2つのテーブル:
OUTLN.OL$
OUTLN.OL$HINTS

Outlineは、CREATE OUTLINE文で作成できる。作成されるOutlineに名前をつけたい場合は、CREATE OUTLINE Outline名 ON〜とすればよい。省略した場合は、Oracleが勝手に名前をつける。

CREATE OUTLINE ON
SELECT CODE_COMBINATION_ID
FROM GL.GL_CODE_COMBINATIONS
WHERE SEGMENT1 = :SEGMENT1;

これで、GL_CODE_COMBINATIONSをSEGMENT1で検索するSQLのOutlineが作成された。ただ、これだけでは今作成したOutlineは使用されない。Outlineの使用には、USE_STORED_OUTLINESフラグを立てる必要がある。フラグはSYSTEMかSESSIONで立てる。

ALTER SESSION SET USE_STORED_OUTLINES = TRUE;

これで今後同セッションでは、先ほど作成されたOutlineが、先ほどと同じSQLを実行する場合において、必ず使用されることになる。気をつけなければならないことは、実行するSQLは、CREATE OUTLINEを行ったSQLと一言一句異なってはならない。なぜなら、SQL Hash ValueをキーにOL$を検索するからである。スペース1つ違うだけで違うHash値となってしまい、Outlineは使用されなくなってしまう。

他にも、作成するOutlineをグループ分けし、使用の際グループ単位で使用/未使用を決定する方法などあるがここでは割愛する。次回にでも。

さて、ここまでは普通の解説。これからはOracleがコメントしていない部分を多少Hackする。ここから先はAt Your Own Riskで。何があっても私は責任をもてません。笑。

作成されたOutlineは、OL$とOL$HINTSに保存される。OL$は親情報で、SQL文やHash Valueなどを持つ。OL$HINTSはOL$の子供で、SQLのヒント文を持つ。このOL$HINTSの中身こそが、実行計画そのものである。

さて、Oracle 9iには、Diagnostic Packの中にOutline Editorという、Outlineの中身を変更するGUIツールがあるのだが、8iにはない、、、。こうなれば、直接OL$HINTSを変更するまでである。

とはいうものの、HINT_TEXTの項目に関するOracleのReferenceはない。そこで、別のSQLにてOutlineを作成し、それをオリジナルの物と置き換えるという荒業にて対応することにする。

1) 実行計画を変更したいSQL(ターゲットSQL)のOutlineを作る

先ほど述べたように、SQLは一言一句異なってはならない。Outlineを作成したいSQLをPL/SQL等からコピーし、SQL*PlusにペーストしCREATE OUTLINEする方法は通常あまりうまくいかない。このような場合有効な方法は、CREATE_STORED_OUTLINESフラグの使用である。このフラグを立てた場合、そのセッション(またはシステム)で実行されたSQL全てのOutlineが作成される。このフラグを立て、PL/SQL等の、「ターゲットSQL」が含まれているアプリケーションを実行する。

CREATE_STORED_OUTLINESのフラグの立て方:

ALTER SESSION SET CREATE_STORED_OUTLINES = TRUE;

これで、ターゲットとなるSQLのOutlineが作成されたはずである。(ごみOutlineも沢山できたはず)

2) ターゲットSQLのヒント文埋め込みバージョンを作る

次に、ターゲットSQLにヒント文を埋め込み、好みの実行計画になるよう調整する。この段階で、ヒント文をいくら埋め込んだ所で好みの実行計画にならない場合、その実行計画にはどこか無理がある。Outlineはあくまでもヒントであり、Oracleは実行時に使用判断を行い使用しないという選択もとり得る。

3) ヒント文埋め込みバージョンのOutlineを作る

ステップ2で作成したSQLのOutlineを、CREATE OUTLINEで作成する。名前は適当に。つけなくてもよい。

4) OL$HINTSの交換

1)で作成したOutlineのOL$HINTSの内容を、3)で作成したものと交換する。1)と3)のOL$HINTSの行数が異なる場合は、OL$のHINTCOUNTの更新も忘れずに。更新に臨んで、USE_STORED_OUTLINESフラグが立っていれば、落とすのが懸命だろう。

交換の方法は、SYSユーザーで下記SQLを実行する。1)で作成したOutlineの名前を「ORIGINALOL」、3)で作成したOutlineの名前を「MODIFIEDOL」とする。

UPDATE OL$HINTS
SET OL_NAME = DECODE(OL_NAME, 'ORIGINALOL', 'MODIFIEDOL',
                              'MODIFIEDOL', 'ORIGINALOL')
WHERE OL_NAME IN ('ORIGINALOL', 'MODIFIEDOL');

5) USE_STORED_OUTLINESのフラグを立てる

あとは、変更したOutlineが使用されるように、USE_STORED_OUTLINESのフラグを立てるのみ。同一セッションでターゲットSQLが実行されるならALTER SESSIONでいいが、ほとんどの場合は他のセッションのはずなので、ALTER SYSTEMでフラグを立てる。

ALTER SYSTEM SET USE_STORED_OUTLINES = TRUE;

以上である。あとは、1)でできたゴミOutlinesをDROPすればなお良い。

ちなみに、Outlineが実際に使われているかを確認する方法は、V$SQLを見ればよい。OUTLINE_CATEGORYという項目の値が、OL$のCATEGORYの値と一致すれば、Outlineは正しく使用されている。ブランクであれば、使用されていない。

以上の方法で、手を出したくても出せないSQL(Oracle Applicationやら)の実行計画を、SQLそのものは全く変更することなく、修正することが可能となる。

一番良いのは、そのような修正の必要があるSQLを生み出さないことである、、、。

追記:

同様の記事を探したところ、DBAzine.comに見つけた

その記事で知ったのだが、Metalinkに同様のNoteがポストされている(Oracleはコメントしていないと思っていた)。92202.1である。(ただ、このNoteでは、OL$のHINTCOUNTの更新について漏れている。)

しかしやはりサポート外であることには変わりはない。

スパイダーマン2 - Spider-Man 2

July 24, 2004

スパイダーマン2を観た。

Yahoo!ムービーによる解説:
世界中を熱狂させた前作から約2年、待望の続編。本作では、スパイダーマンとして活躍する一方、普通の青年としての日々に、もがき悩む等身大のヒーロー、ピーター・パーカーの前に、新たなる強敵“ドック・オク”が現れる。主演を務めるのは前作同様、トビー・マグワイア。そして、“ドック・オク”役には『フリーダ』の名優アルフレッド・モリーナがあたる。最新VFXを駆使しパワーアップした映像だけでなく丁寧に描いた奥深い人間ドラマの行方に注目。

最近楽しみにしていた映画。なかなかよかった!

ヒーローの私生活というか、現実社会における貧しい生活の描写がおもしろい。そりゃ、スパイダーマンやって生計立てているわけじゃないんだから、貧しくて当たり前。笑。子供達の目にはどう映るのだろうか?

さらに、ヒーローの恋愛についての描画もまたおもしろい。言いたいのに、危険にさらすわけにはいかないから言えない。相手はどんどん遠ざかっていく。葛藤がいいね。

まさしく、「With Great Power Comes Great Responsibility」なお話でした。

ラストのあたりが、次回作を期待させるような感じだったので、ちょっと調べてみたら、ありました。2007年公開予定だそうです。

監督: Sam Raimi
出演: Tobey Maguire, Kirsten Dunst, Alfred Molina, 他

SchemeによるRaytracer

July 15, 2004

ANSI Common Lispの簡易レイトレーサをScheme (DrScheme)へ移植してみました。

dolistとpushのマクロを定義しています。

(define sq
  (lambda (x)
    (* x x)))

(define mag
  (lambda (x y z)
    (sqrt (+ (sq x) (sq y) (sq z)))))

(define unit-vector
  (lambda (x y z)
    (let ((d (mag x y z)))
      (values (/ x d) (/ y d) (/ z d)))))

(define-struct point (x y z))

(define distance
  (lambda (p1 p2)
    (mag (- (point-x p1) (point-x p2))
         (- (point-y p1) (point-y p2))
         (- (point-z p1) (point-z p2)))))

(define minroot
  (lambda (a b c)
    (if (zero? a)
        (/ (- c) b)
        (let ((disc (- (sq b) (* 4 a c))))
          (if (negative? disc)
              #f
              (let ((discrt (sqrt disc)))
                (min (/ (+ (- b) discrt) (* 2 a))
                     (/ (- (- b) discrt) (* 2 a)))))))))

(define-struct surface (color))

(define *world* null)

(define eye (make-point 0 0 200))

(define tracer
  (lambda (pathname res)
    (let ((p (open-output-file pathname 'replace)))
      (fprintf p "P2 ~A ~A 255 ~n" (* res 100) (* res 100))
      (let ((inc (/ res)))
        (do ((y -50 (+ y inc)))
          ((< (- 50 y) inc))
          (do ((x -50 (+ x inc)))
            ((< (- 50 x) inc))
            (fprintf p "~A ~n" (color-at x y)))))
      (close-output-port p))))

(define color-at
  (lambda (x y)
    (call-with-values
     (lambda ()
       (unit-vector (- x (point-x eye))
                    (- y (point-y eye))
                    (- 0 (point-z eye))))
     (lambda (xr yr zr)
       (inexact->exact
        (round (* (sendray eye xr yr zr) 255)))))))

(define sendray
  (lambda (pt xr yr zr)
    (call-with-values 
     (lambda () (first-hit pt xr yr zr))
     (lambda (s int)
       (if (null? s)
           0
           (* (lambert s int xr yr zr) (surface-color s)))))))

(define-syntax dolist 
  (syntax-rules ()
    ((dolist (el list) body1 body2 ...)
     (do ((to-do list (cdr to-do)))
       ((null? to-do))
       (let ((el (car to-do)))
         body1
         body2
         ...)))
    ((dolist (el list res) body1 body2 ...)
     (do ((to-do list (cdr to-do)))
       ((null? to-do) res)
       (let ((el (car to-do)))
         body1
         body2
         ...)))))

(define first-hit
  (lambda (pt xr yr zr)
    (let ((surface null) (hit null) (dist null))
      (dolist (s *world*)
              (let ((h (intersect s pt xr yr zr)))
                (when h
                  (let ((d (distance h pt)))
                    (when (or (null? dist) (< d dist))
                      (set! surface s)
                      (set! hit h)
                      (set! dist d))))))
      (values surface hit))))

(define lambert
  (lambda (s int xr yr zr)
    (call-with-values
     (lambda () (normal s int))
     (lambda (xn yn zn)
       (max 0 (+ (* xr xn) (* yr yn) (* zr zn)))))))

(define-struct (sphere surface) (radius center))

(define-syntax push
  (syntax-rules ()
    ((push item list)
     (set! list (cons item list)))))

(define defsphere
  (lambda (x y z r c)
    (let ((s (make-sphere c r (make-point x y z))))
      (push s *world*)
      s)))

(define intersect
  (lambda (s pt xr yr zr)
    (cond
      ((sphere? s) (sphere-intersect s pt xr yr zr)))))

(define sphere-intersect
  (lambda (s pt xr yr zr)
    (let* ((c (sphere-center s))
           (n (minroot 
               (+ (sq xr) (sq yr) (sq zr))
               (* 2 (+ (* (- (point-x pt) (point-x c)) xr)
                       (* (- (point-y pt) (point-y c)) yr)
                       (* (- (point-z pt) (point-z c)) zr)))
               (+ (sq (- (point-x pt) (point-x c)))
                  (sq (- (point-y pt) (point-y c)))
                  (sq (- (point-z pt) (point-z c)))
                  (- (sq (sphere-radius s)))))))
      (if n
          (make-point 
           (+ (point-x pt) (* n xr))
           (+ (point-y pt) (* n yr))
           (+ (point-z pt) (* n zr)))
          #f))))

(define normal
  (lambda (s pt)
    (cond
      ((sphere? s) (sphere-normal s pt)))))

(define sphere-normal
  (lambda (s pt)
    (let ((c (sphere-center s)))
      (unit-vector (- (point-x c) (point-x pt))
                   (- (point-y c) (point-y pt))
                   (- (point-z c) (point-z pt))))))

(define ray-test
  (lambda (res)
    (set! *world* null)
    (defsphere 0 -300 -1200 200 .8)
    (defsphere -80 -150 -1200 200 .7)
    (defsphere 70 -100 -1200 200 .9)
    (do ((x -2 (add1 x)))
      ((> x 2))
      (do ((z 2 (add1 z)))
	((> z 7))
        (defsphere (* x 200) 300 (* z -400) 40 .75)))
    (tracer "spheres.pgm" res)))

実行は、(ray-test 1)。生成されるファイルはpgm形式なので、Win/Mac系ではビューワが必要。

Common LispとSchemeのベンチマークはおもしろそうなので次回。実装に依存しまくりだろうけど。

運命の逆転 - Reversal of Fortune

July 10, 2004

運命の逆転を観た。

Amazon.co.jpによる解説:
富豪の妻(グレン・クローズ)を殺害しようとし、植物人間にさせた罪で起訴された夫のフォン・ビューロー(ジェレミー・アイアンズ)。彼の弁護を引き受けたアラン(ロン・シルヴァー)はプロジェクトチームを作り、誰がどう見ても有罪にしかなりえない裁判に臨んでいくが……。
1980年に実際に起きた事件を、『バーフライ』などの俊英バーベット・シュローダー監督が冷徹な視線で映画化した社会派ドラマの秀作。上流階級の内幕や、依頼人に疑念を抱きつつも、あくまでビジネスとして無罪を得ようと腐心する弁護チームなど、現代社会の黒い構図がサスペンスフルに展開していく。J・アイアンズの不気味なたたずまいも実に素晴らしく、それゆえに事件の真相を欲しようと観る者はますます画面に釘付けになるのは必至である。(的田也寸志)

1990年度主演男優賞受賞作品。ジェレミー・アイアンズの演技は人を惹きこむところがある。

導入部は、世間一般の見解と同様、明らかに黒な雰囲気が漂うが、話が進むにつれ白なのではないかという見方へ自然とシフトさせられる。解説にあるように、監督が実際に冷徹な視線の元ストーリーを展開したのならば、この作品のスタンスは、むしろ世間とは反対側なのでは?

ここで仮に白だとすれば、子供達も母親の薬漬けについては承知の上なはずで、母親の財産を守るために検察官を雇ってまで事件をでっち上げたのか?

黒だとすれば、それはもう間違いなく財産目当てと愛人のためなのだろう。

上流階級はどろどろしてて大変だね、、、。

監督: Barbet Schroeder
出演: Glenn Close, Jeremy Irons, Ron Silver

無名関数の再帰

July 07, 2004

無名関数の再帰。

((lambda (function l)
   (cond 
     ((null? l) 0)
     (else (+ (car l) (function function (cdr l))))))
 (lambda (function l)
   (cond 
     ((null? l) 0)
     (else (+ (car l) (function function (cdr l))))))
 '(1 2 3 4 5))

美しい、、、。

スイミング・プール - Swimming Pool

July 02, 2004

スイミング・プールを観た。

Yahoo!ムービーによる解説:
フランスの気鋭監督、フランソワ・オゾンが彼の新旧のミューズである、リュディヴィーヌ・サニエとシャーロット・ランプリングを主役に迎えて贈る、刺激的な推理劇。『ピーター・パン』のティンカーベル役でハリウッド・デビューを飾った、サニエの若く挑発的な魅力と、ランプリングの成熟した大人の女の色香の調和も素晴らしい。南仏プロヴァンスの美しい風景を舞台に繰り広げられる、何ともゴージャスで薫り高き一本。

これはミステリーじゃないね。シックス・センスみたいな感じ。

だけど、シックス・センスほどにすっきりするわけではなく、観終わった後は混乱してしまった。問題は、どこまでが現実で、どこからが仮想世界か、ということなのかな。監督が自著したノベルには、「公的」な種明かしがあるらしい。ただ、フィクションは、とりわけこの手のフィクションには何が「正しい」かなんてあるわけではなく、観た人それぞれが自分なりに解釈して楽しめばいいのだ。実際ネット上のレビューには、いろいろな解釈がある。

ま、でも、本屋で見かけたらつい種明かしを読んじゃいそうだけどね。。。

監督: François Ozon
出演: Charlotte Rampling, Ludivine Sagnier

Creative Commons License This weblog is licensed under a Creative Commons License.