diff --git a/docs/image/vol1-cover.jpg b/docs/image/vol1-cover.jpg new file mode 100644 index 0000000..0b21509 Binary files /dev/null and b/docs/image/vol1-cover.jpg differ diff --git a/docs/image/vol2-cover.jpg b/docs/image/vol2-cover.jpg new file mode 100644 index 0000000..e357ddd Binary files /dev/null and b/docs/image/vol2-cover.jpg differ diff --git a/index.xhtml b/docs/index.xhtml similarity index 100% rename from index.xhtml rename to docs/index.xhtml diff --git a/docs/style/book-style.css b/docs/style/book-style.css new file mode 100755 index 0000000..c5ee51b --- /dev/null +++ b/docs/style/book-style.css @@ -0,0 +1,315 @@ +@charset "UTF-8"; +@import "style-reset.css"; +@import "style-standard.css"; +@import "style-advance.css"; +@import "style-karc.css"; +@import "style-kadokawa.css"; + +/* ------------------------------------------------------------- +Windows でチェックするときは以下の指定を利用 +※チェックが済んだら必ず削除かコメントアウトすること +@import "style-check.css"; +---------------------------------------------------------------- */ + + + +/* ファイル情報 +---------------------------------------------------------------- +【内容】 +外部 CSS の一括読み込み と 作品別カスタマイズ指定 + +【CSSファイルバージョン】 +ver.1.1.1 + +【当ファイル更新時の電書協EPUB 3 制作ガイドバージョン】 +ver.1.1.3 + +【細目】 +・外部 CSS の import +・作品別カスタマイズ領域 + +【更新履歴】 +2014/11/01 ver.1.1.1 +・「特殊リンク指定」を追加 +・「注釈リンクの下線と色」を変更 + +2012/12/07 ver.1.1.0 +・ファイル更新時の電書協EPUB 3 制作ガイドバージョン表記を追加 + +2012/08/21 ver.1.0b1 +・公開版 +---------------------------------------------------------------- */ + + +/* ------------------------------------------------------------- + * 作品別カスタマイズ領域 + * ------------------------------------------------------------- */ +@import "style-ascii-dwango.css"; + + +/* 見出しのデフォルト書体指定 +---------------------------------------------------------------- */ +/* 横組み用 */ +.hltr h1, +.hltr h2, +.hltr h3, +.hltr h4, +.hltr h5, +.hltr h6 { + /* font-family: serif-ja, serif; */ + font-family: sans-serif-ja, sans-serif; +} +/* 縦組み用 */ +.vrtl h1, +.vrtl h2, +.vrtl h3, +.vrtl h4, +.vrtl h5, +.vrtl h6 { + font-family: serif-ja-v, serif-ja, serif; +} + + +/* リンク指定 +---------------------------------------------------------------- */ +/* 基本設定(上:横組み 下:縦組み) */ +.hltr a { +} +.vrtl a { +} +/* 未訪問リンク */ +a:link { +} +/* 訪問済みリンク */ +a:visited { +} +/* マウスオーバー時 */ +a:hover { +} +/* フォーカス時 */ +a:focus { +} +/* アクティブ時 */ +a:active { +} + + +/* 特殊リンク指定 +---------------------------------------------------------------- */ +/* 基本設定(上:横組み 下:縦組み) */ + +/* .link-01 +-------------------- */ +.hltr a.link-01 { +} +.vrtl a.link-01 { +} +/* 未訪問リンク */ +a.link-01:link { +} +/* 訪問済みリンク */ +a.link-01:visited { +} +/* マウスオーバー時 */ +a.link-01:hover { +} +/* フォーカス時 */ +a.link-01:focus { +} +/* アクティブ時 */ +a.link-01:active { +} + +/* .link-02 +-------------------- */ +.hltr a.link-02 { +} +.vrtl a.link-02 { +} +/* 未訪問リンク */ +a.link-02:link { +} +/* 訪問済みリンク */ +a.link-02:visited { +} +/* マウスオーバー時 */ +a.link-02:hover { +} +/* フォーカス時 */ +a.link-02:focus { +} +/* アクティブ時 */ +a.link-02:active { +} + +/* .link-03 +-------------------- */ +.hltr a.link-03 { +} +.vrtl a.link-03 { +} +/* 未訪問リンク */ +a.link-03:link { +} +/* 訪問済みリンク */ +a.link-03:visited { +} +/* マウスオーバー時 */ +a.link-03:hover { +} +/* フォーカス時 */ +a.link-03:focus { +} +/* アクティブ時 */ +a.link-03:active { +} + + +/* 注釈リンクの下線と色(注釈参照側) +---------------------------------------------------------------- */ +/* 基本設定(上:横組み 下:縦組み) */ +.hltr a.noteref { +} +.vrtl a.noteref { +} +/* 未訪問リンク */ +a.noteref:link { +} +/* 訪問済みリンク */ +a.noteref:visited { +} +/* マウスオーバー時 */ +a.noteref:hover { +} +/* フォーカス時 */ +a.noteref:focus { +} +/* アクティブ時 */ +a.noteref:active { +} + + +/* 注釈リンクの下線と色(注釈内容側) +---------------------------------------------------------------- */ +/* 基本設定(上:横組み 下:縦組み) */ +.hltr a.note { +} +.vrtl a.note { +} +/* 未訪問リンク */ +a.note:link { +} +/* 訪問済みリンク */ +a.note:visited { +} +/* マウスオーバー時 */ +a.note:hover { +} +/* フォーカス時 */ +a.note:focus { +} +/* アクティブ時 */ +a.note:active { +} + + +/* 見出しの指定(上:横組み 下:縦組み) +---------------------------------------------------------------- */ +/* 扉見出し */ +.hltr .tobira-midashi { +} +.vrtl .tobira-midashi { +} +/* 大見出し */ +.hltr .oo-midashi { +} +.vrtl .oo-midashi { +} +/* 中見出し */ +.hltr .naka-midashi { +} +.vrtl .naka-midashi { +} +/* 小見出し */ +.hltr .ko-midashi { +} +.vrtl .ko-midashi { +} + + +/* カバーページ +---------------------------------------------------------------- +描画領域の余白をゼロに +デフォルトで左右中央揃えに +---------------------------------------------------------------- */ +body.p-cover { + margin: 0; + padding: 0; + text-align: center; +} +body.p-cover .main { +} + + +/* 画像のみのページ +---------------------------------------------------------------- +描画領域の余白をゼロに +デフォルトで左右中央揃えに +---------------------------------------------------------------- */ +body.p-image { + margin: 0; + padding: 0; + text-align: center; +} +body.p-image .main { +} + + +/* テキスト中心のページ +---------------------------------------------------------------- */ +body.p-text { +} +body.p-text .main { +} + + +/* 本扉ページ +---------------------------------------------------------------- */ +body.p-titlepage { +} +body.p-titlepage .main { +} + + +/* 奥付ページ +---------------------------------------------------------------- */ +body.p-colophon { +} +body.p-colophon .main { +} + + +/* 目次ページ +---------------------------------------------------------------- */ +body.p-toc { +} +body.p-toc .main { +} + + +/* 電子版用の注意書きページ +---------------------------------------------------------------- */ +body.p-caution { +} +body.p-caution .main { +} + + +/* 広告ページ +---------------------------------------------------------------- */ +body.p-ad { +} +body.p-ad .main { +} + + diff --git a/docs/style/fixed-layout-jp.css b/docs/style/fixed-layout-jp.css new file mode 100755 index 0000000..ad6ad57 --- /dev/null +++ b/docs/style/fixed-layout-jp.css @@ -0,0 +1,12 @@ +@charset "UTF-8"; + +html, +body { + margin: 0; + padding: 0; + font-size: 0; +} +svg { + margin: 0; + padding: 0; +} diff --git a/docs/style/style-advance.css b/docs/style/style-advance.css new file mode 100755 index 0000000..9844912 --- /dev/null +++ b/docs/style/style-advance.css @@ -0,0 +1,1566 @@ +@charset "UTF-8"; + + +/* ファイル情報 +---------------------------------------------------------------- +【内容】 +全作品共通の基本スタイル(論理方向指定・組み方向の入れ子対策用) + +【CSSファイルバージョン】 +ver.1.3.1 + +【当ファイル更新時の電書協EPUB 3 制作ガイドバージョン】 +ver.1.1.3 + +【細目】 +・【組み方向の入れ子対策】リンク指定 +・【組み方向の入れ子対策】外字画像 +・【組み方向の入れ子対策】画像のページフィット指定 +・【組み方向の入れ子対策】小書き文字 +・【組み方向の入れ子対策】区切り線 +・【組み方向の入れ子対策】傍線 +・【論理方向指定】罫線 +・【組み方向の入れ子対策】突き出しインデント(ぶら下がりインデント) +・【組み方向の入れ子対策】字下げ・字上げ指定 +・【論理方向指定】外側の余白(マージン)指定 +・【論理方向指定】内側の余白(パディング)指定 +・【論理方向指定】行長方向のサイズ +・【論理方向指定】行長方向の最大サイズ +・【論理方向指定】行幅方向のサイズ +・【論理方向指定】行幅方向の最大サイズ + +【更新履歴】 +2014/11/01 ver.1.3.1 +・「線位置【二重線】」を修正 + +2012/12/07 ver.1.3.0 +・ファイル更新時の電書協EPUB 3 制作ガイドバージョン表記を追加 + +2012/10/29 ver.1.2b1 +・「傍線」を修正 + +2012/10/03 ver.1.1b1 +・「傍線」を修正 + +2012/08/21 ver.1.0b1 +・公開版 +---------------------------------------------------------------- */ + + +/* 【組み方向の入れ子対策】リンク指定 +---------------------------------------------------------------- */ +/* 横組み:下線 縦組み:右線 */ +.vrtl .hltr a { + text-decoration: underline; +} +.hltr .vrtl a { + text-decoration: overline; +} + + +/* 【組み方向の入れ子対策】外字画像 +---------------------------------------------------------------- */ +/* 外字画像のベースライン */ +.vrtl .hltr img.gaiji, +.vrtl .hltr img.gaiji-line, +.vrtl .hltr img.gaiji-wide { + vertical-align: text-bottom; +} +.hltr .vrtl img.gaiji, +.hltr .vrtl img.gaiji-line, +.hltr .vrtl img.gaiji-wide { + vertical-align: baseline; +} + + +/* 【組み方向の入れ子対策】画像のページフィット指定 +---------------------------------------------------------------- */ +/* 画像のベースライン */ +.vrtl .hltr .fit { + vertical-align: top; +} +.hltr .vrtl .fit { + vertical-align: baseline; +} + + +/* 【組み方向の入れ子対策】小書き文字 +---------------------------------------------------------------- */ +/* 【横組み】左下 */ +.vrtl .hltr .kogaki { + padding: 0 0.15em 0 0.1em; + vertical-align: baseline; +} +/* 【縦組み】右上 */ +.hltr .vrtl .kogaki { + padding: 0.1em 0 0.15em 0; + vertical-align: super; +} + + +/* 【組み方向の入れ子対策】区切り線 +---------------------------------------------------------------- */ +/* 【横組み】水平線 */ +.vrtl .hltr hr { + margin: 0.5em 0; + border-style: solid none none none; +} +/* 【縦組み】垂直線 */ +.hltr .vrtl hr { + margin: 0 0.5em; + border-style: none solid none none; +} + + +/* 【組み方向の入れ子対策】傍線 +---------------------------------------------------------------- */ +/* 【横組み】下線 【縦組み】右線 */ +.vrtl .hltr .em-line { + text-decoration: underline; +} +.hltr .vrtl .em-line { + text-decoration: overline; +} +/* 【横組み】上線 【縦組み】左線 */ +.vrtl .hltr .em-line-outside { + text-decoration: overline; +} +.hltr .vrtl .em-line-outside { + text-decoration: underline; +} + + +/* 【論理方向指定】罫線 +---------------------------------------------------------------- */ +/* 線種【実線】 */ +.k-solid-start, +.k-solid-before, +.k-solid-end, +.k-solid-after, +.k-solid-startend, +.k-solid-beforeafter { + border-width: 1px; + border-color: #000000; +} +/* 線位置【実線】 */ +/* 横組み用 */ +.hltr .k-solid-start, .vrtl .hltr .k-solid-start { border-style: none none none solid; } +.hltr .k-solid-before, .vrtl .hltr .k-solid-before { border-style: solid none none none; } +.hltr .k-solid-end, .vrtl .hltr .k-solid-end { border-style: none solid none none; } +.hltr .k-solid-after, .vrtl .hltr .k-solid-after { border-style: none none solid none; } +.hltr .k-solid-startend, .vrtl .hltr .k-solid-startend { border-style: none solid none solid; } +.hltr .k-solid-beforeafter, .vrtl .hltr .k-solid-beforeafter { border-style: solid none solid none; } +/* 縦組み用 */ +.vrtl .k-solid-start, .hltr .vrtl .k-solid-start { border-style: solid none none none; } +.vrtl .k-solid-before, .hltr .vrtl .k-solid-before { border-style: none solid none none; } +.vrtl .k-solid-end, .hltr .vrtl .k-solid-end { border-style: none none solid none; } +.vrtl .k-solid-after, .hltr .vrtl .k-solid-after { border-style: none none none solid; } +.vrtl .k-solid-startend, .hltr .vrtl .k-solid-startend { border-style: solid none solid none; } +.vrtl .k-solid-beforeafter, .hltr .vrtl .k-solid-beforeafter { border-style: none solid none solid; } + +/* 線種【点線】 */ +.k-dotted-start, +.k-dotted-before, +.k-dotted-end, +.k-dotted-after, +.k-dotted-startend, +.k-dotted-beforeafter { + border-width: 2px; + border-color: #000000; +} +/* 線位置【点線】 */ +/* 横組み用 */ +.hltr .k-dotted-start, .vrtl .hltr .k-dotted-start { border-style: none none none dotted; } +.hltr .k-dotted-before, .vrtl .hltr .k-dotted-before { border-style: dotted none none none; } +.hltr .k-dotted-end, .vrtl .hltr .k-dotted-end { border-style: none dotted none none; } +.hltr .k-dotted-after, .vrtl .hltr .k-dotted-after { border-style: none none dotted none; } +.hltr .k-dotted-startend, .vrtl .hltr .k-dotted-startend { border-style: none dotted none dotted; } +.hltr .k-dotted-beforeafter, .vrtl .hltr .k-dotted-beforeafter { border-style: dotted none dotted none; } +/* 縦組み用 */ +.vrtl .k-dotted-start, .hltr .vrtl .k-dotted-start { border-style: dotted none none none; } +.vrtl .k-dotted-before, .hltr .vrtl .k-dotted-before { border-style: none dotted none none; } +.vrtl .k-dotted-end, .hltr .vrtl .k-dotted-end { border-style: none none dotted none; } +.vrtl .k-dotted-after, .hltr .vrtl .k-dotted-after { border-style: none none none dotted; } +.vrtl .k-dotted-startend, .hltr .vrtl .k-dotted-startend { border-style: dotted none dotted none; } +.vrtl .k-dotted-beforeafter, .hltr .vrtl .k-dotted-beforeafter { border-style: none dotted none dotted; } + +/* 線種【二重線】 */ +.k-double-start, +.k-double-before, +.k-double-end, +.k-double-after, +.k-double-startend, +.k-double-beforeafter { + border-width: 4px; + border-color: #000000; +} +/* 線位置【二重線】*/ +/* 横組み用 */ +.hltr .k-double-start, .vrtl .hltr .k-double-start { border-style: none none none double; } +.hltr .k-double-before, .vrtl .hltr .k-double-before { border-style: double none none none; } +.hltr .k-double-end, .vrtl .hltr .k-double-end { border-style: none double none none; } +.hltr .k-double-after, .vrtl .hltr .k-double-after { border-style: none none double none; } +.hltr .k-double-startend, .vrtl .hltr .k-double-startend { border-style: none double none double; } +.hltr .k-double-beforeafter, .vrtl .hltr .k-double-beforeafter { border-style: double none double none; } +/* 縦組み用 */ +.vrtl .k-double-start, .hltr .vrtl .k-double-start { border-style: double none none none; } +.vrtl .k-double-before, .hltr .vrtl .k-double-before { border-style: none double none none; } +.vrtl .k-double-end, .hltr .vrtl .k-double-end { border-style: none none double none; } +.vrtl .k-double-after, .hltr .vrtl .k-double-after { border-style: none none none double; } +.vrtl .k-double-startend, .hltr .vrtl .k-double-startend { border-style: double none double none; } +.vrtl .k-double-beforeafter, .hltr .vrtl .k-double-beforeafter { border-style: none double none double; } + +/* 線種【破線】 */ +.k-dashed-start, +.k-dashed-before, +.k-dashed-end, +.k-dashed-after, +.k-dashed-startend, +.k-dashed-beforeafter { + border-width: 1px; + border-color: #000000; +} +/* 線位置【破線】 */ +/* 横組み用 */ +.hltr .k-dashed-start, .vrtl .hltr .k-dashed-start { border-style: none none none dashed; } +.hltr .k-dashed-before, .vrtl .hltr .k-dashed-before { border-style: dashed none none none; } +.hltr .k-dashed-end, .vrtl .hltr .k-dashed-end { border-style: none dashed none none; } +.hltr .k-dashed-after, .vrtl .hltr .k-dashed-after { border-style: none none dashed none; } +.hltr .k-dashed-startend, .vrtl .hltr .k-dashed-startend { border-style: none dashed none dashed; } +.hltr .k-dashed-beforeafter, .vrtl .hltr .k-dashed-beforeafter { border-style: dashed none dashed none; } +/* 縦組み用 */ +.vrtl .k-dashed-start, .hltr .vrtl .k-dashed-start { border-style: dashed none none none; } +.vrtl .k-dashed-before, .hltr .vrtl .k-dashed-before { border-style: none dashed none none; } +.vrtl .k-dashed-end, .hltr .vrtl .k-dashed-end { border-style: none none dashed none; } +.vrtl .k-dashed-after, .hltr .vrtl .k-dashed-after { border-style: none none none dashed; } +.vrtl .k-dashed-startend, .hltr .vrtl .k-dashed-startend { border-style: dashed none dashed none; } +.vrtl .k-dashed-beforeafter, .hltr .vrtl .k-dashed-beforeafter { border-style: none dashed none dashed; } + +/* 線幅 */ +.k-0px { border-width: 0; } +.k-1px { border-width: 1px; } +.k-2px { border-width: 2px; } +.k-3px { border-width: 3px; } +.k-4px { border-width: 4px; } +.k-5px { border-width: 5px; } +.k-6px { border-width: 6px; } +.k-7px { border-width: 7px; } +.k-8px { border-width: 8px; } +.k-thin { border-width: thin; } +.k-medium { border-width: medium; } +.k-thick { border-width: thick; } + +/* 1C用の線色 */ +.k-black { border-color: #000000; } +.k-dimgray { border-color: #696969; } +.k-gray { border-color: #808080; } +.k-darkgray { border-color: #a9a9a9; } +.k-silver { border-color: #c0c0c0; } +.k-gainsboro { border-color: #dcdcdc; } +.k-white { border-color: #ffffff; } + +/* 基本色 */ +.k-red { border-color: #ff0000; } +.k-blue { border-color: #0000ff; } +.k-cyan { border-color: #00ffff; } +.k-magenta { border-color: #ff00ff; } +.k-orangered { border-color: #ff4500; } + + +/* 【組み方向の入れ子対策】突き出しインデント(ぶら下がりインデント) +---------------------------------------------------------------- */ +/* .hltr .vrtl [class|="h-indent"] { padding-left: 0; } */ +.hltr .vrtl .h-indent-1em, .hltr .vrtl .h-indent-2em, .hltr .vrtl .h-indent-3em, +.hltr .vrtl .h-indent-4em, .hltr .vrtl .h-indent-5em, .hltr .vrtl .h-indent-6em, +.hltr .vrtl .h-indent-7em, .hltr .vrtl .h-indent-8em, .hltr .vrtl .h-indent-9em, +.hltr .vrtl .h-indent-10em { + padding-left: 0; +} + +/* .vrtl .hltr [class|="h-indent"] { padding-top: 0; } */ +.vrtl .hltr .h-indent-1em, .vrtl .hltr .h-indent-2em, .vrtl .hltr .h-indent-3em, +.vrtl .hltr .h-indent-4em, .vrtl .hltr .h-indent-5em, .vrtl .hltr .h-indent-6em, +.vrtl .hltr .h-indent-7em, .vrtl .hltr .h-indent-8em, .vrtl .hltr .h-indent-9em, +.vrtl .hltr .h-indent-10em { + padding-top: 0; +} + + +/* 【組み方向の入れ子対策】字下げ・字上げ指定 +---------------------------------------------------------------- */ +/* 字下げ */ +/* .hltr .vrtl [class|="start"] { margin-left: 0; } */ +.hltr .vrtl .start-0em25, .hltr .vrtl .start-0em50, .hltr .vrtl .start-0em75, +.hltr .vrtl .start-1em, .hltr .vrtl .start-1em25, .hltr .vrtl .start-1em50, +.hltr .vrtl .start-1em75, .hltr .vrtl .start-2em, .hltr .vrtl .start-2em50, +.hltr .vrtl .start-3em, .hltr .vrtl .start-4em, .hltr .vrtl .start-5em, +.hltr .vrtl .start-6em, .hltr .vrtl .start-7em, .hltr .vrtl .start-8em, +.hltr .vrtl .start-9em, .hltr .vrtl .start-10em { + margin-left: 0; +} +/* .vrtl .hltr [class|="start"] { margin-top: 0; } */ +.vrtl .hltr .start-0em25, .vrtl .hltr .start-0em50, .vrtl .hltr .start-0em75, +.vrtl .hltr .start-1em, .vrtl .hltr .start-1em25, .vrtl .hltr .start-1em50, +.vrtl .hltr .start-1em75, .vrtl .hltr .start-2em, .vrtl .hltr .start-2em50, +.vrtl .hltr .start-3em, .vrtl .hltr .start-4em, .vrtl .hltr .start-5em, +.vrtl .hltr .start-6em, .vrtl .hltr .start-7em, .vrtl .hltr .start-8em, +.vrtl .hltr .start-9em, .vrtl .hltr .start-10em { + margin-top: 0; +} + +/* 字上げ */ +/* .hltr .vrtl [class|="end"] { margin-right: 0; } */ +.hltr .vrtl .end-0em25, .hltr .vrtl .end-0em50, .hltr .vrtl .end-0em75, +.hltr .vrtl .end-1em, .hltr .vrtl .end-1em25, .hltr .vrtl .end-1em50, +.hltr .vrtl .end-1em75, .hltr .vrtl .end-2em, .hltr .vrtl .end-2em50, +.hltr .vrtl .end-3em, .hltr .vrtl .end-4em, .hltr .vrtl .end-5em, +.hltr .vrtl .end-6em, .hltr .vrtl .end-7em, .hltr .vrtl .end-8em, +.hltr .vrtl .end-9em, .hltr .vrtl .end-10em { + margin-right: 0; +} +/* .vrtl .hltr [class|="end"] { margin-bottom: 0; } */ +.vrtl .hltr .end-0em25, .vrtl .hltr .end-0em50, .vrtl .hltr .end-0em75, +.vrtl .hltr .end-1em, .vrtl .hltr .end-1em25, .vrtl .hltr .end-1em50, +.vrtl .hltr .end-1em75, .vrtl .hltr .end-2em, .vrtl .hltr .end-2em50, +.vrtl .hltr .end-3em, .vrtl .hltr .end-4em, .vrtl .hltr .end-5em, +.vrtl .hltr .end-6em, .vrtl .hltr .end-7em, .vrtl .hltr .end-8em, +.vrtl .hltr .end-9em, .vrtl .hltr .end-10em { + margin-bottom: 0; +} + + +/* 【論理方向指定】外側の余白(マージン)指定 +---------------------------------------------------------------- */ +/* 行頭マージン:横組み用 */ +.hltr .m-start-auto { margin-left: auto; } +.hltr .m-start-0, +.hltr .m-start-0em, +.hltr .m-start-000per { margin-left: 0; } + +/* %指定 */ +.hltr .m-start-005per { margin-left: 5%; } +.hltr .m-start-010per { margin-left: 10%; } +.hltr .m-start-015per { margin-left: 15%; } +.hltr .m-start-020per { margin-left: 20%; } +.hltr .m-start-025per { margin-left: 25%; } +.hltr .m-start-030per { margin-left: 30%; } +.hltr .m-start-033per { margin-left: 33%; } +.hltr .m-start-040per { margin-left: 40%; } +.hltr .m-start-050per { margin-left: 50%; } +.hltr .m-start-060per { margin-left: 60%; } +.hltr .m-start-067per { margin-left: 67%; } +.hltr .m-start-070per { margin-left: 70%; } +.hltr .m-start-075per { margin-left: 75%; } +.hltr .m-start-080per { margin-left: 80%; } +.hltr .m-start-090per { margin-left: 90%; } + +/* 文字数指定 */ +.hltr .m-start-0em25 { margin-left: 0.25em; } +.hltr .m-start-0em50 { margin-left: 0.50em; } +.hltr .m-start-0em75 { margin-left: 0.75em; } +.hltr .m-start-1em { margin-left: 1.00em; } +.hltr .m-start-1em25 { margin-left: 1.25em; } +.hltr .m-start-1em50 { margin-left: 1.50em; } +.hltr .m-start-1em75 { margin-left: 1.75em; } +.hltr .m-start-2em { margin-left: 2.00em; } +.hltr .m-start-2em50 { margin-left: 2.50em; } +.hltr .m-start-3em { margin-left: 3.00em; } +.hltr .m-start-4em { margin-left: 4.00em; } +.hltr .m-start-5em { margin-left: 5.00em; } +.hltr .m-start-5em25 { margin-left: 5.25em; } + +/* 行頭マージン:縦組み用 */ +.vrtl .m-start-auto { margin-top: auto; } +.vrtl .m-start-0, +.vrtl .m-start-0em, +.vrtl .m-start-000per { margin-top: 0; } + +/* %指定 */ +.vrtl .m-start-005per { margin-top: 5%; } +.vrtl .m-start-010per { margin-top: 10%; } +.vrtl .m-start-015per { margin-top: 15%; } +.vrtl .m-start-020per { margin-top: 20%; } +.vrtl .m-start-025per { margin-top: 25%; } +.vrtl .m-start-030per { margin-top: 30%; } +.vrtl .m-start-033per { margin-top: 33%; } +.vrtl .m-start-040per { margin-top: 40%; } +.vrtl .m-start-050per { margin-top: 50%; } +.vrtl .m-start-060per { margin-top: 60%; } +.vrtl .m-start-067per { margin-top: 67%; } +.vrtl .m-start-070per { margin-top: 70%; } +.vrtl .m-start-075per { margin-top: 75%; } +.vrtl .m-start-080per { margin-top: 80%; } +.vrtl .m-start-090per { margin-top: 90%; } + +/* 文字数指定 */ +.vrtl .m-start-0em25 { margin-top: 0.25em; } +.vrtl .m-start-0em50 { margin-top: 0.50em; } +.vrtl .m-start-0em75 { margin-top: 0.75em; } +.vrtl .m-start-1em { margin-top: 1.00em; } +.vrtl .m-start-1em25 { margin-top: 1.25em; } +.vrtl .m-start-1em50 { margin-top: 1.50em; } +.vrtl .m-start-1em75 { margin-top: 1.75em; } +.vrtl .m-start-2em { margin-top: 2.00em; } +.vrtl .m-start-2em50 { margin-top: 2.50em; } +.vrtl .m-start-3em { margin-top: 3.00em; } +.vrtl .m-start-4em { margin-top: 4.00em; } +.vrtl .m-start-5em { margin-top: 5.00em; } +.vrtl .m-start-5em25 { margin-top: 5.25em; } + +/* 組み方向の入れ子対策 */ +/* .hltr .vrtl [class|="m-start"] { margin-left: 0; } */ +.hltr .vrtl .m-start-auto, +.hltr .vrtl .m-start-005per, .hltr .vrtl .m-start-010per, .hltr .vrtl .m-start-015per, +.hltr .vrtl .m-start-020per, .hltr .vrtl .m-start-025per, .hltr .vrtl .m-start-030per, +.hltr .vrtl .m-start-033per, .hltr .vrtl .m-start-040per, .hltr .vrtl .m-start-050per, +.hltr .vrtl .m-start-060per, .hltr .vrtl .m-start-067per, .hltr .vrtl .m-start-070per, +.hltr .vrtl .m-start-075per, .hltr .vrtl .m-start-080per, .hltr .vrtl .m-start-090per, +.hltr .vrtl .m-start-0em25, .hltr .vrtl .m-start-0em50, .hltr .vrtl .m-start-0em75, +.hltr .vrtl .m-start-1em, .hltr .vrtl .m-start-1em25, .hltr .vrtl .m-start-1em50, +.hltr .vrtl .m-start-1em75, .hltr .vrtl .m-start-2em, .hltr .vrtl .m-start-2em50, +.hltr .vrtl .m-start-3em, .hltr .vrtl .m-start-4em, .hltr .vrtl .m-start-5em, +.hltr .vrtl .m-start-5em25 { + margin-left: 0; +} +/* .vrtl .hltr [class|="m-start"] { margin-top: 0; } */ +.vrtl .hltr .m-start-auto, +.vrtl .hltr .m-start-005per, .vrtl .hltr .m-start-010per, .vrtl .hltr .m-start-015per, +.vrtl .hltr .m-start-020per, .vrtl .hltr .m-start-025per, .vrtl .hltr .m-start-030per, +.vrtl .hltr .m-start-033per, .vrtl .hltr .m-start-040per, .vrtl .hltr .m-start-050per, +.vrtl .hltr .m-start-060per, .vrtl .hltr .m-start-067per, .vrtl .hltr .m-start-070per, +.vrtl .hltr .m-start-075per, .vrtl .hltr .m-start-080per, .vrtl .hltr .m-start-090per, +.vrtl .hltr .m-start-0em25, .vrtl .hltr .m-start-0em50, .vrtl .hltr .m-start-0em75, +.vrtl .hltr .m-start-1em, .vrtl .hltr .m-start-1em25, .vrtl .hltr .m-start-1em50, +.vrtl .hltr .m-start-1em75, .vrtl .hltr .m-start-2em, .vrtl .hltr .m-start-2em50, +.vrtl .hltr .m-start-3em, .vrtl .hltr .m-start-4em, .vrtl .hltr .m-start-5em, +.vrtl .hltr .m-start-5em25 { + margin-top: 0; +} + + +/* 行末マージン:横組み用 */ +.hltr .m-end-auto { margin-right: auto; } +.hltr .m-end-0, +.hltr .m-end-0em, +.hltr .m-end-000per { margin-right: 0; } + +/* %指定 */ +.hltr .m-end-005per { margin-right: 5%; } +.hltr .m-end-010per { margin-right: 10%; } +.hltr .m-end-015per { margin-right: 15%; } +.hltr .m-end-020per { margin-right: 20%; } +.hltr .m-end-025per { margin-right: 25%; } +.hltr .m-end-030per { margin-right: 30%; } +.hltr .m-end-033per { margin-right: 33%; } +.hltr .m-end-040per { margin-right: 40%; } +.hltr .m-end-050per { margin-right: 50%; } +.hltr .m-end-060per { margin-right: 60%; } +.hltr .m-end-067per { margin-right: 67%; } +.hltr .m-end-070per { margin-right: 70%; } +.hltr .m-end-075per { margin-right: 75%; } +.hltr .m-end-080per { margin-right: 80%; } +.hltr .m-end-090per { margin-right: 90%; } + +/* 文字数指定 */ +.hltr .m-end-0em25 { margin-right: 0.25em; } +.hltr .m-end-0em50 { margin-right: 0.50em; } +.hltr .m-end-0em75 { margin-right: 0.75em; } +.hltr .m-end-1em { margin-right: 1.00em; } +.hltr .m-end-1em25 { margin-right: 1.25em; } +.hltr .m-end-1em50 { margin-right: 1.50em; } +.hltr .m-end-1em75 { margin-right: 1.75em; } +.hltr .m-end-2em { margin-right: 2.00em; } +.hltr .m-end-2em50 { margin-right: 2.50em; } +.hltr .m-end-3em { margin-right: 3.00em; } +.hltr .m-end-4em { margin-right: 4.00em; } +.hltr .m-end-5em { margin-right: 5.00em; } +.hltr .m-end-5em25 { margin-right: 5.25em; } + +/* 行末マージン:縦組み用 */ +.vrtl .m-end-auto { margin-bottom: auto; } +.vrtl .m-end-0, +.vrtl .m-end-0em, +.vrtl .m-end-000per { margin-bottom: 0; } + +/* %指定 */ +.vrtl .m-end-005per { margin-bottom: 5%; } +.vrtl .m-end-010per { margin-bottom: 10%; } +.vrtl .m-end-015per { margin-bottom: 15%; } +.vrtl .m-end-020per { margin-bottom: 20%; } +.vrtl .m-end-025per { margin-bottom: 25%; } +.vrtl .m-end-030per { margin-bottom: 30%; } +.vrtl .m-end-033per { margin-bottom: 33%; } +.vrtl .m-end-040per { margin-bottom: 40%; } +.vrtl .m-end-050per { margin-bottom: 50%; } +.vrtl .m-end-060per { margin-bottom: 60%; } +.vrtl .m-end-067per { margin-bottom: 67%; } +.vrtl .m-end-070per { margin-bottom: 70%; } +.vrtl .m-end-075per { margin-bottom: 75%; } +.vrtl .m-end-080per { margin-bottom: 80%; } +.vrtl .m-end-090per { margin-bottom: 90%; } + +/* 文字数指定 */ +.vrtl .m-end-0em25 { margin-bottom: 0.25em; } +.vrtl .m-end-0em50 { margin-bottom: 0.50em; } +.vrtl .m-end-0em75 { margin-bottom: 0.75em; } +.vrtl .m-end-1em { margin-bottom: 1.00em; } +.vrtl .m-end-1em25 { margin-bottom: 1.25em; } +.vrtl .m-end-1em50 { margin-bottom: 1.50em; } +.vrtl .m-end-1em75 { margin-bottom: 1.75em; } +.vrtl .m-end-2em { margin-bottom: 2.00em; } +.vrtl .m-end-2em50 { margin-bottom: 2.50em; } +.vrtl .m-end-3em { margin-bottom: 3.00em; } +.vrtl .m-end-4em { margin-bottom: 4.00em; } +.vrtl .m-end-5em { margin-bottom: 5.00em; } +.vrtl .m-end-5em25 { margin-bottom: 5.25em; } + +/* 組み方向の入れ子対策 */ +/* .hltr .vrtl [class|="m-end"] { margin-right: 0; } */ +.hltr .vrtl .m-end-auto, +.hltr .vrtl .m-end-005per, .hltr .vrtl .m-end-010per, .hltr .vrtl .m-end-015per, +.hltr .vrtl .m-end-020per, .hltr .vrtl .m-end-025per, .hltr .vrtl .m-end-030per, +.hltr .vrtl .m-end-033per, .hltr .vrtl .m-end-040per, .hltr .vrtl .m-end-050per, +.hltr .vrtl .m-end-060per, .hltr .vrtl .m-end-067per, .hltr .vrtl .m-end-070per, +.hltr .vrtl .m-end-075per, .hltr .vrtl .m-end-080per, .hltr .vrtl .m-end-090per, +.hltr .vrtl .m-end-0em25, .hltr .vrtl .m-end-0em50, .hltr .vrtl .m-end-0em75, +.hltr .vrtl .m-end-1em, .hltr .vrtl .m-end-1em25, .hltr .vrtl .m-end-1em50, +.hltr .vrtl .m-end-1em75, .hltr .vrtl .m-end-2em, .hltr .vrtl .m-end-2em50, +.hltr .vrtl .m-end-3em, .hltr .vrtl .m-end-4em, .hltr .vrtl .m-end-5em, +.hltr .vrtl .m-end-5em25 { + margin-right: 0; +} +/* .vrtl .hltr [class|="m-end"] { margin-bottom: 0; } */ +.vrtl .hltr .m-end-auto, +.vrtl .hltr .m-end-005per, .vrtl .hltr .m-end-010per, .vrtl .hltr .m-end-015per, +.vrtl .hltr .m-end-020per, .vrtl .hltr .m-end-025per, .vrtl .hltr .m-end-030per, +.vrtl .hltr .m-end-033per, .vrtl .hltr .m-end-040per, .vrtl .hltr .m-end-050per, +.vrtl .hltr .m-end-060per, .vrtl .hltr .m-end-067per, .vrtl .hltr .m-end-070per, +.vrtl .hltr .m-end-075per, .vrtl .hltr .m-end-080per, .vrtl .hltr .m-end-090per, +.vrtl .hltr .m-end-0em25, .vrtl .hltr .m-end-0em50, .vrtl .hltr .m-end-0em75, +.vrtl .hltr .m-end-1em, .vrtl .hltr .m-end-1em25, .vrtl .hltr .m-end-1em50, +.vrtl .hltr .m-end-1em75, .vrtl .hltr .m-end-2em, .vrtl .hltr .m-end-2em50, +.vrtl .hltr .m-end-3em, .vrtl .hltr .m-end-4em, .vrtl .hltr .m-end-5em, +.vrtl .hltr .m-end-5em25 { + margin-bottom: 0; +} + + +/* 行前方マージン:横組み用 */ +.hltr .m-before-auto { margin-top: auto; } +.hltr .m-before-0, +.hltr .m-before-0em, +.hltr .m-before-000per { margin-top: 0; } + +/* %指定 */ +.hltr .m-before-005per { margin-top: 5%; } +.hltr .m-before-010per { margin-top: 10%; } +.hltr .m-before-015per { margin-top: 15%; } +.hltr .m-before-020per { margin-top: 20%; } +.hltr .m-before-025per { margin-top: 25%; } +.hltr .m-before-030per { margin-top: 30%; } +.hltr .m-before-033per { margin-top: 33%; } +.hltr .m-before-040per { margin-top: 40%; } +.hltr .m-before-050per { margin-top: 50%; } +.hltr .m-before-060per { margin-top: 60%; } +.hltr .m-before-067per { margin-top: 67%; } +.hltr .m-before-070per { margin-top: 70%; } +.hltr .m-before-075per { margin-top: 75%; } +.hltr .m-before-080per { margin-top: 80%; } +.hltr .m-before-090per { margin-top: 90%; } + +/* 文字数指定 */ +.hltr .m-before-0em25 { margin-top: 0.25em; } +.hltr .m-before-0em50 { margin-top: 0.50em; } +.hltr .m-before-0em75 { margin-top: 0.75em; } +.hltr .m-before-1em { margin-top: 1.00em; } +.hltr .m-before-1em25 { margin-top: 1.25em; } +.hltr .m-before-1em50 { margin-top: 1.50em; } +.hltr .m-before-1em75 { margin-top: 1.75em; } +.hltr .m-before-2em { margin-top: 2.00em; } +.hltr .m-before-2em50 { margin-top: 2.50em; } +.hltr .m-before-3em { margin-top: 3.00em; } +.hltr .m-before-4em { margin-top: 4.00em; } +.hltr .m-before-5em { margin-top: 5.00em; } +.hltr .m-before-5em25 { margin-top: 5.25em; } + +/* 行前方マージン:縦組み用 */ +.vrtl .m-before-auto { margin-right: auto; } +.vrtl .m-before-0, +.vrtl .m-before-0em, +.vrtl .m-before-000per { margin-right: 0; } + +/* %指定 */ +.vrtl .m-before-005per { margin-right: 5%; } +.vrtl .m-before-010per { margin-right: 10%; } +.vrtl .m-before-015per { margin-right: 15%; } +.vrtl .m-before-020per { margin-right: 20%; } +.vrtl .m-before-025per { margin-right: 25%; } +.vrtl .m-before-030per { margin-right: 30%; } +.vrtl .m-before-033per { margin-right: 33%; } +.vrtl .m-before-040per { margin-right: 40%; } +.vrtl .m-before-050per { margin-right: 50%; } +.vrtl .m-before-060per { margin-right: 60%; } +.vrtl .m-before-067per { margin-right: 67%; } +.vrtl .m-before-070per { margin-right: 70%; } +.vrtl .m-before-075per { margin-right: 75%; } +.vrtl .m-before-080per { margin-right: 80%; } +.vrtl .m-before-090per { margin-right: 90%; } + +/* 文字数指定 */ +.vrtl .m-before-0em25 { margin-right: 0.25em; } +.vrtl .m-before-0em50 { margin-right: 0.50em; } +.vrtl .m-before-0em75 { margin-right: 0.75em; } +.vrtl .m-before-1em { margin-right: 1.00em; } +.vrtl .m-before-1em25 { margin-right: 1.25em; } +.vrtl .m-before-1em50 { margin-right: 1.50em; } +.vrtl .m-before-1em75 { margin-right: 1.75em; } +.vrtl .m-before-2em { margin-right: 2.00em; } +.vrtl .m-before-2em50 { margin-right: 2.50em; } +.vrtl .m-before-3em { margin-right: 3.00em; } +.vrtl .m-before-4em { margin-right: 4.00em; } +.vrtl .m-before-5em { margin-right: 5.00em; } +.vrtl .m-before-5em25 { margin-right: 5.25em; } + +/* 組み方向の入れ子対策 */ +/* .hltr .vrtl [class|="m-before"] { margin-top: 0; } */ +.hltr .vrtl .m-before-auto, +.hltr .vrtl .m-before-005per, .hltr .vrtl .m-before-010per, .hltr .vrtl .m-before-015per, +.hltr .vrtl .m-before-020per, .hltr .vrtl .m-before-025per, .hltr .vrtl .m-before-030per, +.hltr .vrtl .m-before-033per, .hltr .vrtl .m-before-040per, .hltr .vrtl .m-before-050per, +.hltr .vrtl .m-before-060per, .hltr .vrtl .m-before-067per, .hltr .vrtl .m-before-070per, +.hltr .vrtl .m-before-075per, .hltr .vrtl .m-before-080per, .hltr .vrtl .m-before-090per, +.hltr .vrtl .m-before-0em25, .hltr .vrtl .m-before-0em50, .hltr .vrtl .m-before-0em75, +.hltr .vrtl .m-before-1em, .hltr .vrtl .m-before-1em25, .hltr .vrtl .m-before-1em50, +.hltr .vrtl .m-before-1em75, .hltr .vrtl .m-before-2em, .hltr .vrtl .m-before-2em50, +.hltr .vrtl .m-before-3em, .hltr .vrtl .m-before-4em, .hltr .vrtl .m-before-5em, +.hltr .vrtl .m-before-5em25 { + margin-top: 0; +} +/* .vrtl .hltr [class|="m-before"] { margin-right: 0; } */ +.vrtl .hltr .m-before-auto, +.vrtl .hltr .m-before-005per, .vrtl .hltr .m-before-010per, .vrtl .hltr .m-before-015per, +.vrtl .hltr .m-before-020per, .vrtl .hltr .m-before-025per, .vrtl .hltr .m-before-030per, +.vrtl .hltr .m-before-033per, .vrtl .hltr .m-before-040per, .vrtl .hltr .m-before-050per, +.vrtl .hltr .m-before-060per, .vrtl .hltr .m-before-067per, .vrtl .hltr .m-before-070per, +.vrtl .hltr .m-before-075per, .vrtl .hltr .m-before-080per, .vrtl .hltr .m-before-090per, +.vrtl .hltr .m-before-0em25, .vrtl .hltr .m-before-0em50, .vrtl .hltr .m-before-0em75, +.vrtl .hltr .m-before-1em, .vrtl .hltr .m-before-1em25, .vrtl .hltr .m-before-1em50, +.vrtl .hltr .m-before-1em75, .vrtl .hltr .m-before-2em, .vrtl .hltr .m-before-2em50, +.vrtl .hltr .m-before-3em, .vrtl .hltr .m-before-4em, .vrtl .hltr .m-before-5em, +.vrtl .hltr .m-before-5em25 { + margin-right: 0; +} + + +/* 行後方マージン:横組み用 */ +.hltr .m-after-auto { margin-bottom: auto; } +.hltr .m-after-0, +.hltr .m-after-0em, +.hltr .m-after-000per { margin-bottom: 0; } + +/* %指定 */ +.hltr .m-after-005per { margin-bottom: 5%; } +.hltr .m-after-010per { margin-bottom: 10%; } +.hltr .m-after-015per { margin-bottom: 15%; } +.hltr .m-after-020per { margin-bottom: 20%; } +.hltr .m-after-025per { margin-bottom: 25%; } +.hltr .m-after-030per { margin-bottom: 30%; } +.hltr .m-after-033per { margin-bottom: 33%; } +.hltr .m-after-040per { margin-bottom: 40%; } +.hltr .m-after-050per { margin-bottom: 50%; } +.hltr .m-after-060per { margin-bottom: 60%; } +.hltr .m-after-067per { margin-bottom: 67%; } +.hltr .m-after-070per { margin-bottom: 70%; } +.hltr .m-after-075per { margin-bottom: 75%; } +.hltr .m-after-080per { margin-bottom: 80%; } +.hltr .m-after-090per { margin-bottom: 90%; } + +/* 文字数指定 */ +.hltr .m-after-0em25 { margin-bottom: 0.25em; } +.hltr .m-after-0em50 { margin-bottom: 0.50em; } +.hltr .m-after-0em75 { margin-bottom: 0.75em; } +.hltr .m-after-1em { margin-bottom: 1.00em; } +.hltr .m-after-1em25 { margin-bottom: 1.25em; } +.hltr .m-after-1em50 { margin-bottom: 1.50em; } +.hltr .m-after-1em75 { margin-bottom: 1.75em; } +.hltr .m-after-2em { margin-bottom: 2.00em; } +.hltr .m-after-2em50 { margin-bottom: 2.50em; } +.hltr .m-after-3em { margin-bottom: 3.00em; } +.hltr .m-after-4em { margin-bottom: 4.00em; } +.hltr .m-after-5em { margin-bottom: 5.00em; } +.hltr .m-after-5em25 { margin-bottom: 5.25em; } + +/* 行後方マージン:縦組み用 */ +.vrtl .m-after-auto { margin-left: auto; } +.vrtl .m-after-0, +.vrtl .m-after-0em, +.vrtl .m-after-000per { margin-left: 0; } + +/* %指定 */ +.vrtl .m-after-005per { margin-left: 5%; } +.vrtl .m-after-010per { margin-left: 10%; } +.vrtl .m-after-015per { margin-left: 15%; } +.vrtl .m-after-020per { margin-left: 20%; } +.vrtl .m-after-025per { margin-left: 25%; } +.vrtl .m-after-030per { margin-left: 30%; } +.vrtl .m-after-033per { margin-left: 33%; } +.vrtl .m-after-040per { margin-left: 40%; } +.vrtl .m-after-050per { margin-left: 50%; } +.vrtl .m-after-060per { margin-left: 60%; } +.vrtl .m-after-067per { margin-left: 67%; } +.vrtl .m-after-070per { margin-left: 70%; } +.vrtl .m-after-075per { margin-left: 75%; } +.vrtl .m-after-080per { margin-left: 80%; } +.vrtl .m-after-090per { margin-left: 90%; } + +/* 文字数指定 */ +.vrtl .m-after-0em25 { margin-left: 0.25em; } +.vrtl .m-after-0em50 { margin-left: 0.50em; } +.vrtl .m-after-0em75 { margin-left: 0.75em; } +.vrtl .m-after-1em { margin-left: 1.00em; } +.vrtl .m-after-1em25 { margin-left: 1.25em; } +.vrtl .m-after-1em50 { margin-left: 1.50em; } +.vrtl .m-after-1em75 { margin-left: 1.75em; } +.vrtl .m-after-2em { margin-left: 2.00em; } +.vrtl .m-after-2em50 { margin-left: 2.50em; } +.vrtl .m-after-3em { margin-left: 3.00em; } +.vrtl .m-after-4em { margin-left: 4.00em; } +.vrtl .m-after-5em { margin-left: 5.00em; } +.vrtl .m-after-5em25 { margin-left: 5.25em; } + +/* 組み方向の入れ子対策 */ +/* .hltr .vrtl [class|="m-after"] { margin-bottom: 0; } */ +.hltr .vrtl .m-after-auto, +.hltr .vrtl .m-after-005per, .hltr .vrtl .m-after-010per, .hltr .vrtl .m-after-015per, +.hltr .vrtl .m-after-020per, .hltr .vrtl .m-after-025per, .hltr .vrtl .m-after-030per, +.hltr .vrtl .m-after-033per, .hltr .vrtl .m-after-040per, .hltr .vrtl .m-after-050per, +.hltr .vrtl .m-after-060per, .hltr .vrtl .m-after-067per, .hltr .vrtl .m-after-070per, +.hltr .vrtl .m-after-075per, .hltr .vrtl .m-after-080per, .hltr .vrtl .m-after-090per, +.hltr .vrtl .m-after-0em25, .hltr .vrtl .m-after-0em50, .hltr .vrtl .m-after-0em75, +.hltr .vrtl .m-after-1em, .hltr .vrtl .m-after-1em25, .hltr .vrtl .m-after-1em50, +.hltr .vrtl .m-after-1em75, .hltr .vrtl .m-after-2em, .hltr .vrtl .m-after-2em50, +.hltr .vrtl .m-after-3em, .hltr .vrtl .m-after-4em, .hltr .vrtl .m-after-5em, +.hltr .vrtl .m-after-5em25 { + margin-bottom: 0; +} +/* .vrtl .hltr [class|="m-after"] { margin-left: 0; } */ +.vrtl .hltr .m-after-auto, +.vrtl .hltr .m-after-005per, .vrtl .hltr .m-after-010per, .vrtl .hltr .m-after-015per, +.vrtl .hltr .m-after-020per, .vrtl .hltr .m-after-025per, .vrtl .hltr .m-after-030per, +.vrtl .hltr .m-after-033per, .vrtl .hltr .m-after-040per, .vrtl .hltr .m-after-050per, +.vrtl .hltr .m-after-060per, .vrtl .hltr .m-after-067per, .vrtl .hltr .m-after-070per, +.vrtl .hltr .m-after-075per, .vrtl .hltr .m-after-080per, .vrtl .hltr .m-after-090per, +.vrtl .hltr .m-after-0em25, .vrtl .hltr .m-after-0em50, .vrtl .hltr .m-after-0em75, +.vrtl .hltr .m-after-1em, .vrtl .hltr .m-after-1em25, .vrtl .hltr .m-after-1em50, +.vrtl .hltr .m-after-1em75, .vrtl .hltr .m-after-2em, .vrtl .hltr .m-after-2em50, +.vrtl .hltr .m-after-3em, .vrtl .hltr .m-after-4em, .vrtl .hltr .m-after-5em, +.vrtl .hltr .m-after-5em25 { + margin-left: 0; +} + + +/* 【論理方向指定】内側の余白(パディング)指定 +---------------------------------------------------------------- */ +/* 行頭パディング:横組み用 */ +.hltr .p-start-0, +.hltr .p-start-0em, +.hltr .p-start-000per { padding-left: 0; } + +/* %指定 */ +.hltr .p-start-005per { padding-left: 5%; } +.hltr .p-start-010per { padding-left: 10%; } +.hltr .p-start-015per { padding-left: 15%; } +.hltr .p-start-020per { padding-left: 20%; } +.hltr .p-start-025per { padding-left: 25%; } +.hltr .p-start-030per { padding-left: 30%; } +.hltr .p-start-033per { padding-left: 33%; } +.hltr .p-start-040per { padding-left: 40%; } +.hltr .p-start-050per { padding-left: 50%; } +.hltr .p-start-060per { padding-left: 60%; } +.hltr .p-start-067per { padding-left: 67%; } +.hltr .p-start-070per { padding-left: 70%; } +.hltr .p-start-075per { padding-left: 75%; } +.hltr .p-start-080per { padding-left: 80%; } +.hltr .p-start-090per { padding-left: 90%; } + +/* 文字数指定 */ +.hltr .p-start-0em25 { padding-left: 0.25em; } +.hltr .p-start-0em50 { padding-left: 0.50em; } +.hltr .p-start-0em75 { padding-left: 0.75em; } +.hltr .p-start-1em { padding-left: 1.00em; } +.hltr .p-start-1em25 { padding-left: 1.25em; } +.hltr .p-start-1em50 { padding-left: 1.50em; } +.hltr .p-start-1em75 { padding-left: 1.75em; } +.hltr .p-start-2em { padding-left: 2.00em; } +.hltr .p-start-2em50 { padding-left: 2.50em; } +.hltr .p-start-3em { padding-left: 3.00em; } +.hltr .p-start-4em { padding-left: 4.00em; } +.hltr .p-start-5em { padding-left: 5.00em; } +.hltr .p-start-5em25 { padding-left: 5.25em; } + +/* 行頭パディング:縦組み用 */ +.vrtl .p-start-0, +.vrtl .p-start-0em, +.vrtl .p-start-000per { padding-top: 0; } + +/* %指定 */ +.vrtl .p-start-005per { padding-top: 5%; } +.vrtl .p-start-010per { padding-top: 10%; } +.vrtl .p-start-015per { padding-top: 15%; } +.vrtl .p-start-020per { padding-top: 20%; } +.vrtl .p-start-025per { padding-top: 25%; } +.vrtl .p-start-030per { padding-top: 30%; } +.vrtl .p-start-033per { padding-top: 33%; } +.vrtl .p-start-040per { padding-top: 40%; } +.vrtl .p-start-050per { padding-top: 50%; } +.vrtl .p-start-060per { padding-top: 60%; } +.vrtl .p-start-067per { padding-top: 67%; } +.vrtl .p-start-070per { padding-top: 70%; } +.vrtl .p-start-075per { padding-top: 75%; } +.vrtl .p-start-080per { padding-top: 80%; } +.vrtl .p-start-090per { padding-top: 90%; } + +/* 文字数指定 */ +.vrtl .p-start-0em25 { padding-top: 0.25em; } +.vrtl .p-start-0em50 { padding-top: 0.50em; } +.vrtl .p-start-0em75 { padding-top: 0.75em; } +.vrtl .p-start-1em { padding-top: 1.00em; } +.vrtl .p-start-1em25 { padding-top: 1.25em; } +.vrtl .p-start-1em50 { padding-top: 1.50em; } +.vrtl .p-start-1em75 { padding-top: 1.75em; } +.vrtl .p-start-2em { padding-top: 2.00em; } +.vrtl .p-start-2em50 { padding-top: 2.50em; } +.vrtl .p-start-3em { padding-top: 3.00em; } +.vrtl .p-start-4em { padding-top: 4.00em; } +.vrtl .p-start-5em { padding-top: 5.00em; } +.vrtl .p-start-5em25 { padding-top: 5.25em; } + +/* 組み方向の入れ子対策 */ +/* .hltr .vrtl [class|="p-start"] { padding-left: 0; } */ +.hltr .vrtl .p-start-005per, .hltr .vrtl .p-start-010per, .hltr .vrtl .p-start-015per, +.hltr .vrtl .p-start-020per, .hltr .vrtl .p-start-025per, .hltr .vrtl .p-start-030per, +.hltr .vrtl .p-start-033per, .hltr .vrtl .p-start-040per, .hltr .vrtl .p-start-050per, +.hltr .vrtl .p-start-060per, .hltr .vrtl .p-start-067per, .hltr .vrtl .p-start-070per, +.hltr .vrtl .p-start-075per, .hltr .vrtl .p-start-080per, .hltr .vrtl .p-start-090per, +.hltr .vrtl .p-start-0em25, .hltr .vrtl .p-start-0em50, .hltr .vrtl .p-start-0em75, +.hltr .vrtl .p-start-1em, .hltr .vrtl .p-start-1em25, .hltr .vrtl .p-start-1em50, +.hltr .vrtl .p-start-1em75, .hltr .vrtl .p-start-2em, .hltr .vrtl .p-start-2em50, +.hltr .vrtl .p-start-3em, .hltr .vrtl .p-start-4em, .hltr .vrtl .p-start-5em, +.hltr .vrtl .p-start-5em25 { + padding-left: 0; +} +/* .vrtl .hltr [class|="p-start"] { padding-top: 0; } */ +.vrtl .hltr .p-start-005per, .vrtl .hltr .p-start-010per, .vrtl .hltr .p-start-015per, +.vrtl .hltr .p-start-020per, .vrtl .hltr .p-start-025per, .vrtl .hltr .p-start-030per, +.vrtl .hltr .p-start-033per, .vrtl .hltr .p-start-040per, .vrtl .hltr .p-start-050per, +.vrtl .hltr .p-start-060per, .vrtl .hltr .p-start-067per, .vrtl .hltr .p-start-070per, +.vrtl .hltr .p-start-075per, .vrtl .hltr .p-start-080per, .vrtl .hltr .p-start-090per, +.vrtl .hltr .p-start-0em25, .vrtl .hltr .p-start-0em50, .vrtl .hltr .p-start-0em75, +.vrtl .hltr .p-start-1em, .vrtl .hltr .p-start-1em25, .vrtl .hltr .p-start-1em50, +.vrtl .hltr .p-start-1em75, .vrtl .hltr .p-start-2em, .vrtl .hltr .p-start-2em50, +.vrtl .hltr .p-start-3em, .vrtl .hltr .p-start-4em, .vrtl .hltr .p-start-5em, +.vrtl .hltr .p-start-5em25 { + padding-top: 0; +} + + +/* 行末パディング:横組み用 */ +.hltr .p-end-0, +.hltr .p-end-0em, +.hltr .p-end-000per { padding-right: 0; } + +/* %指定 */ +.hltr .p-end-005per { padding-right: 5%; } +.hltr .p-end-010per { padding-right: 10%; } +.hltr .p-end-015per { padding-right: 15%; } +.hltr .p-end-020per { padding-right: 20%; } +.hltr .p-end-025per { padding-right: 25%; } +.hltr .p-end-030per { padding-right: 30%; } +.hltr .p-end-033per { padding-right: 33%; } +.hltr .p-end-040per { padding-right: 40%; } +.hltr .p-end-050per { padding-right: 50%; } +.hltr .p-end-060per { padding-right: 60%; } +.hltr .p-end-067per { padding-right: 67%; } +.hltr .p-end-070per { padding-right: 70%; } +.hltr .p-end-075per { padding-right: 75%; } +.hltr .p-end-080per { padding-right: 80%; } +.hltr .p-end-090per { padding-right: 90%; } + +/* 文字数指定 */ +.hltr .p-end-0em25 { padding-right: 0.25em; } +.hltr .p-end-0em50 { padding-right: 0.50em; } +.hltr .p-end-0em75 { padding-right: 0.75em; } +.hltr .p-end-1em { padding-right: 1.00em; } +.hltr .p-end-1em25 { padding-right: 1.25em; } +.hltr .p-end-1em50 { padding-right: 1.50em; } +.hltr .p-end-1em75 { padding-right: 1.75em; } +.hltr .p-end-2em { padding-right: 2.00em; } +.hltr .p-end-2em50 { padding-right: 2.50em; } +.hltr .p-end-3em { padding-right: 3.00em; } +.hltr .p-end-4em { padding-right: 4.00em; } +.hltr .p-end-5em { padding-right: 5.00em; } +.hltr .p-end-5em25 { padding-right: 5.25em; } + +/* 行末パディング:縦組み用 */ +.vrtl .p-end-0, +.vrtl .p-end-0em, +.vrtl .p-end-000per { padding-bottom: 0; } + +/* %指定 */ +.vrtl .p-end-005per { padding-bottom: 5%; } +.vrtl .p-end-010per { padding-bottom: 10%; } +.vrtl .p-end-015per { padding-bottom: 15%; } +.vrtl .p-end-020per { padding-bottom: 20%; } +.vrtl .p-end-025per { padding-bottom: 25%; } +.vrtl .p-end-030per { padding-bottom: 30%; } +.vrtl .p-end-033per { padding-bottom: 33%; } +.vrtl .p-end-040per { padding-bottom: 40%; } +.vrtl .p-end-050per { padding-bottom: 50%; } +.vrtl .p-end-060per { padding-bottom: 60%; } +.vrtl .p-end-067per { padding-bottom: 67%; } +.vrtl .p-end-070per { padding-bottom: 70%; } +.vrtl .p-end-075per { padding-bottom: 75%; } +.vrtl .p-end-080per { padding-bottom: 80%; } +.vrtl .p-end-090per { padding-bottom: 90%; } + +/* 文字数指定 */ +.vrtl .p-end-0em25 { padding-bottom: 0.25em; } +.vrtl .p-end-0em50 { padding-bottom: 0.50em; } +.vrtl .p-end-0em75 { padding-bottom: 0.75em; } +.vrtl .p-end-1em { padding-bottom: 1.00em; } +.vrtl .p-end-1em25 { padding-bottom: 1.25em; } +.vrtl .p-end-1em50 { padding-bottom: 1.50em; } +.vrtl .p-end-1em75 { padding-bottom: 1.75em; } +.vrtl .p-end-2em { padding-bottom: 2.00em; } +.vrtl .p-end-2em50 { padding-bottom: 2.50em; } +.vrtl .p-end-3em { padding-bottom: 3.00em; } +.vrtl .p-end-4em { padding-bottom: 4.00em; } +.vrtl .p-end-5em { padding-bottom: 5.00em; } +.vrtl .p-end-5em25 { padding-bottom: 5.25em; } + +/* 字下げ:組み方向の入れ子対策 */ +/* .hltr .vrtl [class|="p-end"] { padding-right: 0; } */ +.hltr .vrtl .p-end-005per, .hltr .vrtl .p-end-010per, .hltr .vrtl .p-end-015per, +.hltr .vrtl .p-end-020per, .hltr .vrtl .p-end-025per, .hltr .vrtl .p-end-030per, +.hltr .vrtl .p-end-033per, .hltr .vrtl .p-end-040per, .hltr .vrtl .p-end-050per, +.hltr .vrtl .p-end-060per, .hltr .vrtl .p-end-067per, .hltr .vrtl .p-end-070per, +.hltr .vrtl .p-end-075per, .hltr .vrtl .p-end-080per, .hltr .vrtl .p-end-090per, +.hltr .vrtl .p-end-0em25, .hltr .vrtl .p-end-0em50, .hltr .vrtl .p-end-0em75, +.hltr .vrtl .p-end-1em, .hltr .vrtl .p-end-1em25, .hltr .vrtl .p-end-1em50, +.hltr .vrtl .p-end-1em75, .hltr .vrtl .p-end-2em, .hltr .vrtl .p-end-2em50, +.hltr .vrtl .p-end-3em, .hltr .vrtl .p-end-4em, .hltr .vrtl .p-end-5em, +.hltr .vrtl .p-end-5em25 { + padding-right: 0; +} +/* .vrtl .hltr [class|="p-end"] { padding-bottom: 0; } */ +.vrtl .hltr .p-end-005per, .vrtl .hltr .p-end-010per, .vrtl .hltr .p-end-015per, +.vrtl .hltr .p-end-020per, .vrtl .hltr .p-end-025per, .vrtl .hltr .p-end-030per, +.vrtl .hltr .p-end-033per, .vrtl .hltr .p-end-040per, .vrtl .hltr .p-end-050per, +.vrtl .hltr .p-end-060per, .vrtl .hltr .p-end-067per, .vrtl .hltr .p-end-070per, +.vrtl .hltr .p-end-075per, .vrtl .hltr .p-end-080per, .vrtl .hltr .p-end-090per, +.vrtl .hltr .p-end-0em25, .vrtl .hltr .p-end-0em50, .vrtl .hltr .p-end-0em75, +.vrtl .hltr .p-end-1em, .vrtl .hltr .p-end-1em25, .vrtl .hltr .p-end-1em50, +.vrtl .hltr .p-end-1em75, .vrtl .hltr .p-end-2em, .vrtl .hltr .p-end-2em50, +.vrtl .hltr .p-end-3em, .vrtl .hltr .p-end-4em, .vrtl .hltr .p-end-5em, +.vrtl .hltr .p-end-5em25 { + padding-bottom: 0; +} + + +/* 行前方パディング:横組み用 */ +.hltr .p-before-0, +.hltr .p-before-0em, +.hltr .p-before-000per { padding-top: 0; } + +/* %指定 */ +.hltr .p-before-005per { padding-top: 5%; } +.hltr .p-before-010per { padding-top: 10%; } +.hltr .p-before-015per { padding-top: 15%; } +.hltr .p-before-020per { padding-top: 20%; } +.hltr .p-before-025per { padding-top: 25%; } +.hltr .p-before-030per { padding-top: 30%; } +.hltr .p-before-033per { padding-top: 33%; } +.hltr .p-before-040per { padding-top: 40%; } +.hltr .p-before-050per { padding-top: 50%; } +.hltr .p-before-060per { padding-top: 60%; } +.hltr .p-before-067per { padding-top: 67%; } +.hltr .p-before-070per { padding-top: 70%; } +.hltr .p-before-075per { padding-top: 75%; } +.hltr .p-before-080per { padding-top: 80%; } +.hltr .p-before-090per { padding-top: 90%; } + +/* 文字数指定 */ +.hltr .p-before-0em25 { padding-top: 0.25em; } +.hltr .p-before-0em50 { padding-top: 0.50em; } +.hltr .p-before-0em75 { padding-top: 0.75em; } +.hltr .p-before-1em { padding-top: 1.00em; } +.hltr .p-before-1em25 { padding-top: 1.25em; } +.hltr .p-before-1em50 { padding-top: 1.50em; } +.hltr .p-before-1em75 { padding-top: 1.75em; } +.hltr .p-before-2em { padding-top: 2.00em; } +.hltr .p-before-2em50 { padding-top: 2.50em; } +.hltr .p-before-3em { padding-top: 3.00em; } +.hltr .p-before-4em { padding-top: 4.00em; } +.hltr .p-before-5em { padding-top: 5.00em; } +.hltr .p-before-5em25 { padding-top: 5.25em; } + +/* 行前方パディング:縦組み用 */ +.vrtl .p-before-0, +.vrtl .p-before-0em, +.vrtl .p-before-000per { padding-right: 0; } + +/* %指定 */ +.vrtl .p-before-005per { padding-right: 5%; } +.vrtl .p-before-010per { padding-right: 10%; } +.vrtl .p-before-015per { padding-right: 15%; } +.vrtl .p-before-020per { padding-right: 20%; } +.vrtl .p-before-025per { padding-right: 25%; } +.vrtl .p-before-030per { padding-right: 30%; } +.vrtl .p-before-033per { padding-right: 33%; } +.vrtl .p-before-040per { padding-right: 40%; } +.vrtl .p-before-050per { padding-right: 50%; } +.vrtl .p-before-060per { padding-right: 60%; } +.vrtl .p-before-067per { padding-right: 67%; } +.vrtl .p-before-070per { padding-right: 70%; } +.vrtl .p-before-075per { padding-right: 75%; } +.vrtl .p-before-080per { padding-right: 80%; } +.vrtl .p-before-090per { padding-right: 90%; } + +/* 文字数指定 */ +.vrtl .p-before-0em25 { padding-right: 0.25em; } +.vrtl .p-before-0em50 { padding-right: 0.50em; } +.vrtl .p-before-0em75 { padding-right: 0.75em; } +.vrtl .p-before-1em { padding-right: 1.00em; } +.vrtl .p-before-1em25 { padding-right: 1.25em; } +.vrtl .p-before-1em50 { padding-right: 1.50em; } +.vrtl .p-before-1em75 { padding-right: 1.75em; } +.vrtl .p-before-2em { padding-right: 2.00em; } +.vrtl .p-before-2em50 { padding-right: 2.50em; } +.vrtl .p-before-3em { padding-right: 3.00em; } +.vrtl .p-before-4em { padding-right: 4.00em; } +.vrtl .p-before-5em { padding-right: 5.00em; } +.vrtl .p-before-5em25 { padding-right: 5.25em; } + +/* 組み方向の入れ子対策 */ +/* .hltr .vrtl [class|="p-before"] { padding-top: 0; } */ +.hltr .vrtl .p-before-005per, .hltr .vrtl .p-before-010per, .hltr .vrtl .p-before-015per, +.hltr .vrtl .p-before-020per, .hltr .vrtl .p-before-025per, .hltr .vrtl .p-before-030per, +.hltr .vrtl .p-before-033per, .hltr .vrtl .p-before-040per, .hltr .vrtl .p-before-050per, +.hltr .vrtl .p-before-060per, .hltr .vrtl .p-before-067per, .hltr .vrtl .p-before-070per, +.hltr .vrtl .p-before-075per, .hltr .vrtl .p-before-080per, .hltr .vrtl .p-before-090per, +.hltr .vrtl .p-before-0em25, .hltr .vrtl .p-before-0em50, .hltr .vrtl .p-before-0em75, +.hltr .vrtl .p-before-1em, .hltr .vrtl .p-before-1em25, .hltr .vrtl .p-before-1em50, +.hltr .vrtl .p-before-1em75, .hltr .vrtl .p-before-2em, .hltr .vrtl .p-before-2em50, +.hltr .vrtl .p-before-3em, .hltr .vrtl .p-before-4em, .hltr .vrtl .p-before-5em, +.hltr .vrtl .p-before-5em25 { + padding-top: 0; +} +/* .vrtl .hltr [class|="p-before"] { padding-right: 0; } */ +.vrtl .hltr .p-before-005per, .vrtl .hltr .p-before-010per, .vrtl .hltr .p-before-015per, +.vrtl .hltr .p-before-020per, .vrtl .hltr .p-before-025per, .vrtl .hltr .p-before-030per, +.vrtl .hltr .p-before-033per, .vrtl .hltr .p-before-040per, .vrtl .hltr .p-before-050per, +.vrtl .hltr .p-before-060per, .vrtl .hltr .p-before-067per, .vrtl .hltr .p-before-070per, +.vrtl .hltr .p-before-075per, .vrtl .hltr .p-before-080per, .vrtl .hltr .p-before-090per, +.vrtl .hltr .p-before-0em25, .vrtl .hltr .p-before-0em50, .vrtl .hltr .p-before-0em75, +.vrtl .hltr .p-before-1em, .vrtl .hltr .p-before-1em25, .vrtl .hltr .p-before-1em50, +.vrtl .hltr .p-before-1em75, .vrtl .hltr .p-before-2em, .vrtl .hltr .p-before-2em50, +.vrtl .hltr .p-before-3em, .vrtl .hltr .p-before-4em, .vrtl .hltr .p-before-5em, +.vrtl .hltr .p-before-5em25 { + padding-right: 0; +} + + +/* 行後方パディング:横組み用 */ +.hltr .p-after-0, +.hltr .p-after-0em, +.hltr .p-after-000per { padding-bottom: 0; } + +/* %指定 */ +.hltr .p-after-005per { padding-bottom: 5%; } +.hltr .p-after-010per { padding-bottom: 10%; } +.hltr .p-after-015per { padding-bottom: 15%; } +.hltr .p-after-020per { padding-bottom: 20%; } +.hltr .p-after-025per { padding-bottom: 25%; } +.hltr .p-after-030per { padding-bottom: 30%; } +.hltr .p-after-033per { padding-bottom: 33%; } +.hltr .p-after-040per { padding-bottom: 40%; } +.hltr .p-after-050per { padding-bottom: 50%; } +.hltr .p-after-060per { padding-bottom: 60%; } +.hltr .p-after-067per { padding-bottom: 67%; } +.hltr .p-after-070per { padding-bottom: 70%; } +.hltr .p-after-075per { padding-bottom: 75%; } +.hltr .p-after-080per { padding-bottom: 80%; } +.hltr .p-after-090per { padding-bottom: 90%; } + +/* 文字数指定 */ +.hltr .p-after-0em25 { padding-bottom: 0.25em; } +.hltr .p-after-0em50 { padding-bottom: 0.50em; } +.hltr .p-after-0em75 { padding-bottom: 0.75em; } +.hltr .p-after-1em { padding-bottom: 1.00em; } +.hltr .p-after-1em25 { padding-bottom: 1.25em; } +.hltr .p-after-1em50 { padding-bottom: 1.50em; } +.hltr .p-after-1em75 { padding-bottom: 1.75em; } +.hltr .p-after-2em { padding-bottom: 2.00em; } +.hltr .p-after-2em50 { padding-bottom: 2.50em; } +.hltr .p-after-3em { padding-bottom: 3.00em; } +.hltr .p-after-4em { padding-bottom: 4.00em; } +.hltr .p-after-5em { padding-bottom: 5.00em; } +.hltr .p-after-5em25 { padding-bottom: 5.25em; } + +/* 行後方パディング:縦組み用 */ +.vrtl .p-after-0, +.vrtl .p-after-0em, +.vrtl .p-after-000per { padding-left: 0; } + +/* %指定 */ +.vrtl .p-after-005per { padding-left: 5%; } +.vrtl .p-after-010per { padding-left: 10%; } +.vrtl .p-after-015per { padding-left: 15%; } +.vrtl .p-after-020per { padding-left: 20%; } +.vrtl .p-after-025per { padding-left: 25%; } +.vrtl .p-after-030per { padding-left: 30%; } +.vrtl .p-after-033per { padding-left: 33%; } +.vrtl .p-after-040per { padding-left: 40%; } +.vrtl .p-after-050per { padding-left: 50%; } +.vrtl .p-after-060per { padding-left: 60%; } +.vrtl .p-after-067per { padding-left: 67%; } +.vrtl .p-after-070per { padding-left: 70%; } +.vrtl .p-after-075per { padding-left: 75%; } +.vrtl .p-after-080per { padding-left: 80%; } +.vrtl .p-after-090per { padding-left: 90%; } + +/* 文字数指定 */ +.vrtl .p-after-0em25 { padding-left: 0.25em; } +.vrtl .p-after-0em50 { padding-left: 0.50em; } +.vrtl .p-after-0em75 { padding-left: 0.75em; } +.vrtl .p-after-1em { padding-left: 1.00em; } +.vrtl .p-after-1em25 { padding-left: 1.25em; } +.vrtl .p-after-1em50 { padding-left: 1.50em; } +.vrtl .p-after-1em75 { padding-left: 1.75em; } +.vrtl .p-after-2em { padding-left: 2.00em; } +.vrtl .p-after-2em50 { padding-left: 2.50em; } +.vrtl .p-after-3em { padding-left: 3.00em; } +.vrtl .p-after-4em { padding-left: 4.00em; } +.vrtl .p-after-5em { padding-left: 5.00em; } +.vrtl .p-after-5em25 { padding-left: 5.25em; } + +/* 組み方向の入れ子対策 */ +/* .hltr .vrtl [class|="p-after"] { padding-bottom: 0; } */ +.hltr .vrtl .p-after-005per, .hltr .vrtl .p-after-010per, .hltr .vrtl .p-after-015per, +.hltr .vrtl .p-after-020per, .hltr .vrtl .p-after-025per, .hltr .vrtl .p-after-030per, +.hltr .vrtl .p-after-033per, .hltr .vrtl .p-after-040per, .hltr .vrtl .p-after-050per, +.hltr .vrtl .p-after-060per, .hltr .vrtl .p-after-067per, .hltr .vrtl .p-after-070per, +.hltr .vrtl .p-after-075per, .hltr .vrtl .p-after-080per, .hltr .vrtl .p-after-090per, +.hltr .vrtl .p-after-0em25, .hltr .vrtl .p-after-0em50, .hltr .vrtl .p-after-0em75, +.hltr .vrtl .p-after-1em, .hltr .vrtl .p-after-1em25, .hltr .vrtl .p-after-1em50, +.hltr .vrtl .p-after-1em75, .hltr .vrtl .p-after-2em, .hltr .vrtl .p-after-2em50, +.hltr .vrtl .p-after-3em, .hltr .vrtl .p-after-4em, .hltr .vrtl .p-after-5em, +.hltr .vrtl .p-after-5em25 { + padding-bottom: 0; +} +/* .vrtl .hltr [class|="p-after"] { padding-left: 0; } */ +.vrtl .hltr .p-after-005per, .vrtl .hltr .p-after-010per, .vrtl .hltr .p-after-015per, +.vrtl .hltr .p-after-020per, .vrtl .hltr .p-after-025per, .vrtl .hltr .p-after-030per, +.vrtl .hltr .p-after-033per, .vrtl .hltr .p-after-040per, .vrtl .hltr .p-after-050per, +.vrtl .hltr .p-after-060per, .vrtl .hltr .p-after-067per, .vrtl .hltr .p-after-070per, +.vrtl .hltr .p-after-075per, .vrtl .hltr .p-after-080per, .vrtl .hltr .p-after-090per, +.vrtl .hltr .p-after-0em25, .vrtl .hltr .p-after-0em50, .vrtl .hltr .p-after-0em75, +.vrtl .hltr .p-after-1em, .vrtl .hltr .p-after-1em25, .vrtl .hltr .p-after-1em50, +.vrtl .hltr .p-after-1em75, .vrtl .hltr .p-after-2em, .vrtl .hltr .p-after-2em50, +.vrtl .hltr .p-after-3em, .vrtl .hltr .p-after-4em, .vrtl .hltr .p-after-5em, +.vrtl .hltr .p-after-5em25 { + padding-left: 0; +} + + +/* 【論理方向指定】行長方向のサイズ +---------------------------------------------------------------- +行長方向と行幅方向のサイズ指定は、固定値、最大値とも +同じ要素内では同時に利用できないので注意 + +※以下のように入れ子で対応は可能 + +
+
+

内容

+
+
+---------------------------------------------------------------- */ +.measure-auto { height: auto; width: auto; } + +/* %指定 */ +/* 横組み用 */ +.hltr .measure-010per, .vrtl .hltr .measure-010per { height: auto; width: 10%; } +.hltr .measure-020per, .vrtl .hltr .measure-020per { height: auto; width: 20%; } +.hltr .measure-025per, .vrtl .hltr .measure-025per { height: auto; width: 25%; } +.hltr .measure-030per, .vrtl .hltr .measure-030per { height: auto; width: 30%; } +.hltr .measure-033per, .vrtl .hltr .measure-033per { height: auto; width: 33%; } +.hltr .measure-040per, .vrtl .hltr .measure-040per { height: auto; width: 40%; } +.hltr .measure-050per, .vrtl .hltr .measure-050per { height: auto; width: 50%; } +.hltr .measure-060per, .vrtl .hltr .measure-060per { height: auto; width: 60%; } +.hltr .measure-067per, .vrtl .hltr .measure-067per { height: auto; width: 67%; } +.hltr .measure-070per, .vrtl .hltr .measure-070per { height: auto; width: 70%; } +.hltr .measure-075per, .vrtl .hltr .measure-075per { height: auto; width: 75%; } +.hltr .measure-080per, .vrtl .hltr .measure-080per { height: auto; width: 80%; } +.hltr .measure-090per, .vrtl .hltr .measure-090per { height: auto; width: 90%; } +.hltr .measure-100per, .vrtl .hltr .measure-100per { height: auto; width: 100%; } +/* 縦組み用 */ +.vrtl .measure-010per, .hltr .vrtl .measure-010per { height: 10%; width: auto; } +.vrtl .measure-020per, .hltr .vrtl .measure-020per { height: 20%; width: auto; } +.vrtl .measure-025per, .hltr .vrtl .measure-025per { height: 25%; width: auto; } +.vrtl .measure-030per, .hltr .vrtl .measure-030per { height: 30%; width: auto; } +.vrtl .measure-033per, .hltr .vrtl .measure-033per { height: 33%; width: auto; } +.vrtl .measure-040per, .hltr .vrtl .measure-040per { height: 40%; width: auto; } +.vrtl .measure-050per, .hltr .vrtl .measure-050per { height: 50%; width: auto; } +.vrtl .measure-060per, .hltr .vrtl .measure-060per { height: 60%; width: auto; } +.vrtl .measure-067per, .hltr .vrtl .measure-067per { height: 67%; width: auto; } +.vrtl .measure-070per, .hltr .vrtl .measure-070per { height: 70%; width: auto; } +.vrtl .measure-075per, .hltr .vrtl .measure-075per { height: 75%; width: auto; } +.vrtl .measure-080per, .hltr .vrtl .measure-080per { height: 80%; width: auto; } +.vrtl .measure-090per, .hltr .vrtl .measure-090per { height: 90%; width: auto; } +.vrtl .measure-100per, .hltr .vrtl .measure-100per { height: 100%; width: auto; } + +/* 文字数指定 */ +/* 横組み用 */ +.hltr .measure-0em25, .vrtl .hltr .measure-0em25 { height: auto; width: 0.25em; } +.hltr .measure-0em50, .vrtl .hltr .measure-0em50 { height: auto; width: 0.50em; } +.hltr .measure-0em75, .vrtl .hltr .measure-0em75 { height: auto; width: 0.75em; } +.hltr .measure-1em, .vrtl .hltr .measure-1em { height: auto; width: 1.00em; } +.hltr .measure-1em25, .vrtl .hltr .measure-1em25 { height: auto; width: 1.25em; } +.hltr .measure-1em50, .vrtl .hltr .measure-1em50 { height: auto; width: 1.50em; } +.hltr .measure-1em75, .vrtl .hltr .measure-1em75 { height: auto; width: 1.75em; } +.hltr .measure-2em, .vrtl .hltr .measure-2em { height: auto; width: 2.00em; } +.hltr .measure-2em50, .vrtl .hltr .measure-2em50 { height: auto; width: 2.50em; } +.hltr .measure-3em, .vrtl .hltr .measure-3em { height: auto; width: 3.00em; } +.hltr .measure-4em, .vrtl .hltr .measure-4em { height: auto; width: 4.00em; } +.hltr .measure-5em, .vrtl .hltr .measure-5em { height: auto; width: 5.00em; } +.hltr .measure-5em25, .vrtl .hltr .measure-5em25 { height: auto; width: 5.25em; } +.hltr .measure-6em, .vrtl .hltr .measure-6em { height: auto; width: 6.00em; } +.hltr .measure-7em, .vrtl .hltr .measure-7em { height: auto; width: 7.00em; } +.hltr .measure-8em, .vrtl .hltr .measure-8em { height: auto; width: 8.00em; } +.hltr .measure-8em75, .vrtl .hltr .measure-8em75 { height: auto; width: 8.75em; } +.hltr .measure-9em, .vrtl .hltr .measure-9em { height: auto; width: 9.00em; } +.hltr .measure-10em, .vrtl .hltr .measure-10em { height: auto; width: 10.00em; } +.hltr .measure-11em, .vrtl .hltr .measure-11em { height: auto; width: 11.00em; } +.hltr .measure-12em, .vrtl .hltr .measure-12em { height: auto; width: 12.00em; } +.hltr .measure-13em, .vrtl .hltr .measure-13em { height: auto; width: 13.00em; } +.hltr .measure-14em, .vrtl .hltr .measure-14em { height: auto; width: 14.00em; } +.hltr .measure-15em, .vrtl .hltr .measure-15em { height: auto; width: 15.00em; } +.hltr .measure-20em, .vrtl .hltr .measure-20em { height: auto; width: 20.00em; } +.hltr .measure-30em, .vrtl .hltr .measure-30em { height: auto; width: 30.00em; } +.hltr .measure-40em, .vrtl .hltr .measure-40em { height: auto; width: 40.00em; } +/* 縦組み用 */ +.vrtl .measure-0em25, .hltr .vrtl .measure-0em25 { height: 0.25em; width: auto; } +.vrtl .measure-0em50, .hltr .vrtl .measure-0em50 { height: 0.50em; width: auto; } +.vrtl .measure-0em75, .hltr .vrtl .measure-0em75 { height: 0.75em; width: auto; } +.vrtl .measure-1em, .hltr .vrtl .measure-1em { height: 1.00em; width: auto; } +.vrtl .measure-1em25, .hltr .vrtl .measure-1em25 { height: 1.25em; width: auto; } +.vrtl .measure-1em50, .hltr .vrtl .measure-1em50 { height: 1.50em; width: auto; } +.vrtl .measure-1em75, .hltr .vrtl .measure-1em75 { height: 1.75em; width: auto; } +.vrtl .measure-2em, .hltr .vrtl .measure-2em { height: 2.00em; width: auto; } +.vrtl .measure-2em50, .hltr .vrtl .measure-2em50 { height: 2.50em; width: auto; } +.vrtl .measure-3em, .hltr .vrtl .measure-3em { height: 3.00em; width: auto; } +.vrtl .measure-4em, .hltr .vrtl .measure-4em { height: 4.00em; width: auto; } +.vrtl .measure-5em, .hltr .vrtl .measure-5em { height: 5.00em; width: auto; } +.vrtl .measure-5em25, .hltr .vrtl .measure-5em25 { height: 5.25em; width: auto; } +.vrtl .measure-6em, .hltr .vrtl .measure-6em { height: 6.00em; width: auto; } +.vrtl .measure-7em, .hltr .vrtl .measure-7em { height: 7.00em; width: auto; } +.vrtl .measure-8em, .hltr .vrtl .measure-8em { height: 8.00em; width: auto; } +.vrtl .measure-8em75, .hltr .vrtl .measure-8em75 { height: 8.75em; width: auto; } +.vrtl .measure-9em, .hltr .vrtl .measure-9em { height: 9.00em; width: auto; } +.vrtl .measure-10em, .hltr .vrtl .measure-10em { height: 10.00em; width: auto; } +.vrtl .measure-11em, .hltr .vrtl .measure-11em { height: 11.00em; width: auto; } +.vrtl .measure-12em, .hltr .vrtl .measure-12em { height: 12.00em; width: auto; } +.vrtl .measure-13em, .hltr .vrtl .measure-13em { height: 13.00em; width: auto; } +.vrtl .measure-14em, .hltr .vrtl .measure-14em { height: 14.00em; width: auto; } +.vrtl .measure-15em, .hltr .vrtl .measure-15em { height: 15.00em; width: auto; } +.vrtl .measure-20em, .hltr .vrtl .measure-20em { height: 20.00em; width: auto; } +.vrtl .measure-30em, .hltr .vrtl .measure-30em { height: 30.00em; width: auto; } +.vrtl .measure-40em, .hltr .vrtl .measure-40em { height: 40.00em; width: auto; } + + +/* 【論理方向指定】行長方向の最大サイズ +---------------------------------------------------------------- */ +.max-measure-none { max-height: none; max-width: none; } + +/* %指定 */ +/* 横組み用 */ +.hltr .max-measure-010per, .vrtl .hltr .max-measure-010per { max-height: none; max-width: 10%; } +.hltr .max-measure-020per, .vrtl .hltr .max-measure-020per { max-height: none; max-width: 20%; } +.hltr .max-measure-025per, .vrtl .hltr .max-measure-025per { max-height: none; max-width: 25%; } +.hltr .max-measure-030per, .vrtl .hltr .max-measure-030per { max-height: none; max-width: 30%; } +.hltr .max-measure-033per, .vrtl .hltr .max-measure-033per { max-height: none; max-width: 33%; } +.hltr .max-measure-040per, .vrtl .hltr .max-measure-040per { max-height: none; max-width: 40%; } +.hltr .max-measure-050per, .vrtl .hltr .max-measure-050per { max-height: none; max-width: 50%; } +.hltr .max-measure-060per, .vrtl .hltr .max-measure-060per { max-height: none; max-width: 60%; } +.hltr .max-measure-067per, .vrtl .hltr .max-measure-067per { max-height: none; max-width: 67%; } +.hltr .max-measure-070per, .vrtl .hltr .max-measure-070per { max-height: none; max-width: 70%; } +.hltr .max-measure-075per, .vrtl .hltr .max-measure-075per { max-height: none; max-width: 75%; } +.hltr .max-measure-080per, .vrtl .hltr .max-measure-080per { max-height: none; max-width: 80%; } +.hltr .max-measure-090per, .vrtl .hltr .max-measure-090per { max-height: none; max-width: 90%; } +.hltr .max-measure-100per, .vrtl .hltr .max-measure-100per { max-height: none; max-width: 100%; } +/* 縦組み用 */ +.vrtl .max-measure-010per, .hltr .vrtl .max-measure-010per { max-height: 10%; max-width: none; } +.vrtl .max-measure-020per, .hltr .vrtl .max-measure-020per { max-height: 20%; max-width: none; } +.vrtl .max-measure-025per, .hltr .vrtl .max-measure-025per { max-height: 25%; max-width: none; } +.vrtl .max-measure-030per, .hltr .vrtl .max-measure-030per { max-height: 30%; max-width: none; } +.vrtl .max-measure-033per, .hltr .vrtl .max-measure-033per { max-height: 33%; max-width: none; } +.vrtl .max-measure-040per, .hltr .vrtl .max-measure-040per { max-height: 40%; max-width: none; } +.vrtl .max-measure-050per, .hltr .vrtl .max-measure-050per { max-height: 50%; max-width: none; } +.vrtl .max-measure-060per, .hltr .vrtl .max-measure-060per { max-height: 60%; max-width: none; } +.vrtl .max-measure-067per, .hltr .vrtl .max-measure-067per { max-height: 67%; max-width: none; } +.vrtl .max-measure-070per, .hltr .vrtl .max-measure-070per { max-height: 70%; max-width: none; } +.vrtl .max-measure-075per, .hltr .vrtl .max-measure-075per { max-height: 75%; max-width: none; } +.vrtl .max-measure-080per, .hltr .vrtl .max-measure-080per { max-height: 80%; max-width: none; } +.vrtl .max-measure-090per, .hltr .vrtl .max-measure-090per { max-height: 90%; max-width: none; } +.vrtl .max-measure-100per, .hltr .vrtl .max-measure-100per { max-height: 100%; max-width: none; } + +/* 文字数指定 */ +/* 横組み用 */ +.hltr .max-measure-0em25, .vrtl .hltr .max-measure-0em25 { max-height: none; max-width: 0.25em; } +.hltr .max-measure-0em50, .vrtl .hltr .max-measure-0em50 { max-height: none; max-width: 0.50em; } +.hltr .max-measure-0em75, .vrtl .hltr .max-measure-0em75 { max-height: none; max-width: 0.75em; } +.hltr .max-measure-1em, .vrtl .hltr .max-measure-1em { max-height: none; max-width: 1.00em; } +.hltr .max-measure-1em25, .vrtl .hltr .max-measure-1em25 { max-height: none; max-width: 1.25em; } +.hltr .max-measure-1em50, .vrtl .hltr .max-measure-1em50 { max-height: none; max-width: 1.50em; } +.hltr .max-measure-1em75, .vrtl .hltr .max-measure-1em75 { max-height: none; max-width: 1.75em; } +.hltr .max-measure-2em, .vrtl .hltr .max-measure-2em { max-height: none; max-width: 2.00em; } +.hltr .max-measure-2em50, .vrtl .hltr .max-measure-2em50 { max-height: none; max-width: 2.50em; } +.hltr .max-measure-3em, .vrtl .hltr .max-measure-3em { max-height: none; max-width: 3.00em; } +.hltr .max-measure-4em, .vrtl .hltr .max-measure-4em { max-height: none; max-width: 4.00em; } +.hltr .max-measure-5em, .vrtl .hltr .max-measure-5em { max-height: none; max-width: 5.00em; } +.hltr .max-measure-5em25, .vrtl .hltr .max-measure-5em25 { max-height: none; max-width: 5.25em; } +.hltr .max-measure-6em, .vrtl .hltr .max-measure-6em { max-height: none; max-width: 6.00em; } +.hltr .max-measure-7em, .vrtl .hltr .max-measure-7em { max-height: none; max-width: 7.00em; } +.hltr .max-measure-8em, .vrtl .hltr .max-measure-8em { max-height: none; max-width: 8.00em; } +.hltr .max-measure-8em75, .vrtl .hltr .max-measure-8em75 { max-height: none; max-width: 8.75em; } +.hltr .max-measure-9em, .vrtl .hltr .max-measure-9em { max-height: none; max-width: 9.00em; } +.hltr .max-measure-10em, .vrtl .hltr .max-measure-10em { max-height: none; max-width: 10.00em; } +.hltr .max-measure-11em, .vrtl .hltr .max-measure-11em { max-height: none; max-width: 11.00em; } +.hltr .max-measure-12em, .vrtl .hltr .max-measure-12em { max-height: none; max-width: 12.00em; } +.hltr .max-measure-13em, .vrtl .hltr .max-measure-13em { max-height: none; max-width: 13.00em; } +.hltr .max-measure-14em, .vrtl .hltr .max-measure-14em { max-height: none; max-width: 14.00em; } +.hltr .max-measure-15em, .vrtl .hltr .max-measure-15em { max-height: none; max-width: 15.00em; } +.hltr .max-measure-20em, .vrtl .hltr .max-measure-20em { max-height: none; max-width: 20.00em; } +.hltr .max-measure-30em, .vrtl .hltr .max-measure-30em { max-height: none; max-width: 30.00em; } +.hltr .max-measure-40em, .vrtl .hltr .max-measure-40em { max-height: none; max-width: 40.00em; } +/* 縦組み用 */ +.vrtl .max-measure-0em25, .hltr .vrtl .max-measure-0em25 { max-height: 0.25em; max-width: none; } +.vrtl .max-measure-0em50, .hltr .vrtl .max-measure-0em50 { max-height: 0.50em; max-width: none; } +.vrtl .max-measure-0em75, .hltr .vrtl .max-measure-0em75 { max-height: 0.75em; max-width: none; } +.vrtl .max-measure-1em, .hltr .vrtl .max-measure-1em { max-height: 1.00em; max-width: none; } +.vrtl .max-measure-1em25, .hltr .vrtl .max-measure-1em25 { max-height: 1.25em; max-width: none; } +.vrtl .max-measure-1em50, .hltr .vrtl .max-measure-1em50 { max-height: 1.50em; max-width: none; } +.vrtl .max-measure-1em75, .hltr .vrtl .max-measure-1em75 { max-height: 1.75em; max-width: none; } +.vrtl .max-measure-2em, .hltr .vrtl .max-measure-2em { max-height: 2.00em; max-width: none; } +.vrtl .max-measure-2em50, .hltr .vrtl .max-measure-2em50 { max-height: 2.50em; max-width: none; } +.vrtl .max-measure-3em, .hltr .vrtl .max-measure-3em { max-height: 3.00em; max-width: none; } +.vrtl .max-measure-4em, .hltr .vrtl .max-measure-4em { max-height: 4.00em; max-width: none; } +.vrtl .max-measure-5em, .hltr .vrtl .max-measure-5em { max-height: 5.00em; max-width: none; } +.vrtl .max-measure-5em25, .hltr .vrtl .max-measure-5em25 { max-height: 5.25em; max-width: none; } +.vrtl .max-measure-6em, .hltr .vrtl .max-measure-6em { max-height: 6.00em; max-width: none; } +.vrtl .max-measure-7em, .hltr .vrtl .max-measure-7em { max-height: 7.00em; max-width: none; } +.vrtl .max-measure-8em, .hltr .vrtl .max-measure-8em { max-height: 8.00em; max-width: none; } +.vrtl .max-measure-8em75, .hltr .vrtl .max-measure-8em75 { max-height: 8.75em; max-width: none; } +.vrtl .max-measure-9em, .hltr .vrtl .max-measure-9em { max-height: 9.00em; max-width: none; } +.vrtl .max-measure-10em, .hltr .vrtl .max-measure-10em { max-height: 10.00em; max-width: none; } +.vrtl .max-measure-11em, .hltr .vrtl .max-measure-11em { max-height: 11.00em; max-width: none; } +.vrtl .max-measure-12em, .hltr .vrtl .max-measure-12em { max-height: 12.00em; max-width: none; } +.vrtl .max-measure-13em, .hltr .vrtl .max-measure-13em { max-height: 13.00em; max-width: none; } +.vrtl .max-measure-14em, .hltr .vrtl .max-measure-14em { max-height: 14.00em; max-width: none; } +.vrtl .max-measure-15em, .hltr .vrtl .max-measure-15em { max-height: 15.00em; max-width: none; } +.vrtl .max-measure-20em, .hltr .vrtl .max-measure-20em { max-height: 20.00em; max-width: none; } +.vrtl .max-measure-30em, .hltr .vrtl .max-measure-30em { max-height: 30.00em; max-width: none; } +.vrtl .max-measure-40em, .hltr .vrtl .max-measure-40em { max-height: 40.00em; max-width: none; } + + +/* 【論理方向指定】行幅方向のサイズ +---------------------------------------------------------------- */ +.extent-auto { height: auto; width: auto; } + +/* %指定 */ +/* 横組み用 */ +.hltr .extent-010per, .vrtl .hltr .extent-010per { height: 10%; width: auto; } +.hltr .extent-020per, .vrtl .hltr .extent-020per { height: 20%; width: auto; } +.hltr .extent-025per, .vrtl .hltr .extent-025per { height: 25%; width: auto; } +.hltr .extent-030per, .vrtl .hltr .extent-030per { height: 30%; width: auto; } +.hltr .extent-033per, .vrtl .hltr .extent-033per { height: 33%; width: auto; } +.hltr .extent-040per, .vrtl .hltr .extent-040per { height: 40%; width: auto; } +.hltr .extent-050per, .vrtl .hltr .extent-050per { height: 50%; width: auto; } +.hltr .extent-060per, .vrtl .hltr .extent-060per { height: 60%; width: auto; } +.hltr .extent-067per, .vrtl .hltr .extent-067per { height: 67%; width: auto; } +.hltr .extent-070per, .vrtl .hltr .extent-070per { height: 70%; width: auto; } +.hltr .extent-075per, .vrtl .hltr .extent-075per { height: 75%; width: auto; } +.hltr .extent-080per, .vrtl .hltr .extent-080per { height: 80%; width: auto; } +.hltr .extent-090per, .vrtl .hltr .extent-090per { height: 90%; width: auto; } +.hltr .extent-100per, .vrtl .hltr .extent-100per { height: 100%; width: auto; } +/* 縦組み用 */ +.vrtl .extent-010per, .hltr .vrtl .extent-010per { height: auto; width: 10%; } +.vrtl .extent-020per, .hltr .vrtl .extent-020per { height: auto; width: 20%; } +.vrtl .extent-025per, .hltr .vrtl .extent-025per { height: auto; width: 25%; } +.vrtl .extent-030per, .hltr .vrtl .extent-030per { height: auto; width: 30%; } +.vrtl .extent-033per, .hltr .vrtl .extent-033per { height: auto; width: 33%; } +.vrtl .extent-040per, .hltr .vrtl .extent-040per { height: auto; width: 40%; } +.vrtl .extent-050per, .hltr .vrtl .extent-050per { height: auto; width: 50%; } +.vrtl .extent-060per, .hltr .vrtl .extent-060per { height: auto; width: 60%; } +.vrtl .extent-067per, .hltr .vrtl .extent-067per { height: auto; width: 67%; } +.vrtl .extent-070per, .hltr .vrtl .extent-070per { height: auto; width: 70%; } +.vrtl .extent-075per, .hltr .vrtl .extent-075per { height: auto; width: 75%; } +.vrtl .extent-080per, .hltr .vrtl .extent-080per { height: auto; width: 80%; } +.vrtl .extent-090per, .hltr .vrtl .extent-090per { height: auto; width: 90%; } +.vrtl .extent-100per, .hltr .vrtl .extent-100per { height: auto; width: 100%; } + +/* 文字数指定 */ +/* 横組み用 */ +.hltr .extent-0em25, .vrtl .hltr .extent-0em25 { height: 0.25em; width: auto; } +.hltr .extent-0em50, .vrtl .hltr .extent-0em50 { height: 0.50em; width: auto; } +.hltr .extent-0em75, .vrtl .hltr .extent-0em75 { height: 0.75em; width: auto; } +.hltr .extent-1em, .vrtl .hltr .extent-1em { height: 1.00em; width: auto; } +.hltr .extent-1em25, .vrtl .hltr .extent-1em25 { height: 1.25em; width: auto; } +.hltr .extent-1em50, .vrtl .hltr .extent-1em50 { height: 1.50em; width: auto; } +.hltr .extent-1em75, .vrtl .hltr .extent-1em75 { height: 1.75em; width: auto; } +.hltr .extent-2em, .vrtl .hltr .extent-2em { height: 2.00em; width: auto; } +.hltr .extent-2em50, .vrtl .hltr .extent-2em50 { height: 2.50em; width: auto; } +.hltr .extent-3em, .vrtl .hltr .extent-3em { height: 3.00em; width: auto; } +.hltr .extent-4em, .vrtl .hltr .extent-4em { height: 4.00em; width: auto; } +.hltr .extent-5em, .vrtl .hltr .extent-5em { height: 5.00em; width: auto; } +.hltr .extent-5em25, .vrtl .hltr .extent-5em25 { height: 5.25em; width: auto; } +.hltr .extent-6em, .vrtl .hltr .extent-6em { height: 6.00em; width: auto; } +.hltr .extent-7em, .vrtl .hltr .extent-7em { height: 7.00em; width: auto; } +.hltr .extent-8em, .vrtl .hltr .extent-8em { height: 8.00em; width: auto; } +.hltr .extent-8em75, .vrtl .hltr .extent-8em75 { height: 8.75em; width: auto; } +.hltr .extent-9em, .vrtl .hltr .extent-9em { height: 9.00em; width: auto; } +.hltr .extent-10em, .vrtl .hltr .extent-10em { height: 10.00em; width: auto; } +.hltr .extent-11em, .vrtl .hltr .extent-11em { height: 11.00em; width: auto; } +.hltr .extent-12em, .vrtl .hltr .extent-12em { height: 12.00em; width: auto; } +.hltr .extent-13em, .vrtl .hltr .extent-13em { height: 13.00em; width: auto; } +.hltr .extent-14em, .vrtl .hltr .extent-14em { height: 14.00em; width: auto; } +.hltr .extent-15em, .vrtl .hltr .extent-15em { height: 15.00em; width: auto; } +.hltr .extent-20em, .vrtl .hltr .extent-20em { height: 20.00em; width: auto; } +.hltr .extent-30em, .vrtl .hltr .extent-30em { height: 30.00em; width: auto; } +.hltr .extent-40em, .vrtl .hltr .extent-40em { height: 40.00em; width: auto; } +/* 縦組み用 */ +.vrtl .extent-0em25, .hltr .vrtl .extent-0em25 { height: auto; width: 0.25em; } +.vrtl .extent-0em50, .hltr .vrtl .extent-0em50 { height: auto; width: 0.50em; } +.vrtl .extent-0em75, .hltr .vrtl .extent-0em75 { height: auto; width: 0.75em; } +.vrtl .extent-1em, .hltr .vrtl .extent-1em { height: auto; width: 1.00em; } +.vrtl .extent-1em25, .hltr .vrtl .extent-1em25 { height: auto; width: 1.25em; } +.vrtl .extent-1em50, .hltr .vrtl .extent-1em50 { height: auto; width: 1.50em; } +.vrtl .extent-1em75, .hltr .vrtl .extent-1em75 { height: auto; width: 1.75em; } +.vrtl .extent-2em, .hltr .vrtl .extent-2em { height: auto; width: 2.00em; } +.vrtl .extent-2em50, .hltr .vrtl .extent-2em50 { height: auto; width: 2.50em; } +.vrtl .extent-3em, .hltr .vrtl .extent-3em { height: auto; width: 3.00em; } +.vrtl .extent-4em, .hltr .vrtl .extent-4em { height: auto; width: 4.00em; } +.vrtl .extent-5em, .hltr .vrtl .extent-5em { height: auto; width: 5.00em; } +.vrtl .extent-5em25, .hltr .vrtl .extent-5em25 { height: auto; width: 5.25em; } +.vrtl .extent-6em, .hltr .vrtl .extent-6em { height: auto; width: 6.00em; } +.vrtl .extent-7em, .hltr .vrtl .extent-7em { height: auto; width: 7.00em; } +.vrtl .extent-8em, .hltr .vrtl .extent-8em { height: auto; width: 8.00em; } +.vrtl .extent-8em75, .hltr .vrtl .extent-8em75 { height: auto; width: 8.75em; } +.vrtl .extent-9em, .hltr .vrtl .extent-9em { height: auto; width: 9.00em; } +.vrtl .extent-10em, .hltr .vrtl .extent-10em { height: auto; width: 10.00em; } +.vrtl .extent-11em, .hltr .vrtl .extent-11em { height: auto; width: 11.00em; } +.vrtl .extent-12em, .hltr .vrtl .extent-12em { height: auto; width: 12.00em; } +.vrtl .extent-13em, .hltr .vrtl .extent-13em { height: auto; width: 13.00em; } +.vrtl .extent-14em, .hltr .vrtl .extent-14em { height: auto; width: 14.00em; } +.vrtl .extent-15em, .hltr .vrtl .extent-15em { height: auto; width: 15.00em; } +.vrtl .extent-20em, .hltr .vrtl .extent-20em { height: auto; width: 20.00em; } +.vrtl .extent-30em, .hltr .vrtl .extent-30em { height: auto; width: 30.00em; } +.vrtl .extent-40em, .hltr .vrtl .extent-40em { height: auto; width: 40.00em; } + + +/* 【論理方向指定】行幅方向の最大サイズ +---------------------------------------------------------------- */ +.max-extent-none { max-height: none; max-width: none; } + +/* %指定 */ +/* 横組み用 */ +.hltr .max-extent-010per, .vrtl .hltr .max-extent-010per { max-height: 10%; max-width: none; } +.hltr .max-extent-020per, .vrtl .hltr .max-extent-020per { max-height: 20%; max-width: none; } +.hltr .max-extent-025per, .vrtl .hltr .max-extent-025per { max-height: 25%; max-width: none; } +.hltr .max-extent-030per, .vrtl .hltr .max-extent-030per { max-height: 30%; max-width: none; } +.hltr .max-extent-033per, .vrtl .hltr .max-extent-033per { max-height: 33%; max-width: none; } +.hltr .max-extent-040per, .vrtl .hltr .max-extent-040per { max-height: 40%; max-width: none; } +.hltr .max-extent-050per, .vrtl .hltr .max-extent-050per { max-height: 50%; max-width: none; } +.hltr .max-extent-060per, .vrtl .hltr .max-extent-060per { max-height: 60%; max-width: none; } +.hltr .max-extent-067per, .vrtl .hltr .max-extent-067per { max-height: 67%; max-width: none; } +.hltr .max-extent-070per, .vrtl .hltr .max-extent-070per { max-height: 70%; max-width: none; } +.hltr .max-extent-075per, .vrtl .hltr .max-extent-075per { max-height: 75%; max-width: none; } +.hltr .max-extent-080per, .vrtl .hltr .max-extent-080per { max-height: 80%; max-width: none; } +.hltr .max-extent-090per, .vrtl .hltr .max-extent-090per { max-height: 90%; max-width: none; } +.hltr .max-extent-100per, .vrtl .hltr .max-extent-100per { max-height: 100%; max-width: none; } +/* 縦組み用 */ +.vrtl .max-extent-010per, .hltr .vrtl .max-extent-010per { max-height: none; max-width: 10%; } +.vrtl .max-extent-020per, .hltr .vrtl .max-extent-020per { max-height: none; max-width: 20%; } +.vrtl .max-extent-025per, .hltr .vrtl .max-extent-025per { max-height: none; max-width: 25%; } +.vrtl .max-extent-030per, .hltr .vrtl .max-extent-030per { max-height: none; max-width: 30%; } +.vrtl .max-extent-033per, .hltr .vrtl .max-extent-033per { max-height: none; max-width: 33%; } +.vrtl .max-extent-040per, .hltr .vrtl .max-extent-040per { max-height: none; max-width: 40%; } +.vrtl .max-extent-050per, .hltr .vrtl .max-extent-050per { max-height: none; max-width: 50%; } +.vrtl .max-extent-060per, .hltr .vrtl .max-extent-060per { max-height: none; max-width: 60%; } +.vrtl .max-extent-067per, .hltr .vrtl .max-extent-067per { max-height: none; max-width: 67%; } +.vrtl .max-extent-070per, .hltr .vrtl .max-extent-070per { max-height: none; max-width: 70%; } +.vrtl .max-extent-075per, .hltr .vrtl .max-extent-075per { max-height: none; max-width: 75%; } +.vrtl .max-extent-080per, .hltr .vrtl .max-extent-080per { max-height: none; max-width: 80%; } +.vrtl .max-extent-090per, .hltr .vrtl .max-extent-090per { max-height: none; max-width: 90%; } +.vrtl .max-extent-100per, .hltr .vrtl .max-extent-100per { max-height: none; max-width: 100%; } + +/* 文字数指定 */ +/* 横組み用 */ +.hltr .max-extent-0em25, .vrtl .hltr .max-extent-0em25 { max-height: 0.25em; max-width: none; } +.hltr .max-extent-0em50, .vrtl .hltr .max-extent-0em50 { max-height: 0.50em; max-width: none; } +.hltr .max-extent-0em75, .vrtl .hltr .max-extent-0em75 { max-height: 0.75em; max-width: none; } +.hltr .max-extent-1em, .vrtl .hltr .max-extent-1em { max-height: 1.00em; max-width: none; } +.hltr .max-extent-1em25, .vrtl .hltr .max-extent-1em25 { max-height: 1.25em; max-width: none; } +.hltr .max-extent-1em50, .vrtl .hltr .max-extent-1em50 { max-height: 1.50em; max-width: none; } +.hltr .max-extent-1em75, .vrtl .hltr .max-extent-1em75 { max-height: 1.75em; max-width: none; } +.hltr .max-extent-2em, .vrtl .hltr .max-extent-2em { max-height: 2.00em; max-width: none; } +.hltr .max-extent-2em50, .vrtl .hltr .max-extent-2em50 { max-height: 2.50em; max-width: none; } +.hltr .max-extent-3em, .vrtl .hltr .max-extent-3em { max-height: 3.00em; max-width: none; } +.hltr .max-extent-4em, .vrtl .hltr .max-extent-4em { max-height: 4.00em; max-width: none; } +.hltr .max-extent-5em, .vrtl .hltr .max-extent-5em { max-height: 5.00em; max-width: none; } +.hltr .max-extent-5em25, .vrtl .hltr .max-extent-5em25 { max-height: 5.25em; max-width: none; } +.hltr .max-extent-6em, .vrtl .hltr .max-extent-6em { max-height: 6.00em; max-width: none; } +.hltr .max-extent-7em, .vrtl .hltr .max-extent-7em { max-height: 7.00em; max-width: none; } +.hltr .max-extent-8em, .vrtl .hltr .max-extent-8em { max-height: 8.00em; max-width: none; } +.hltr .max-extent-8em75, .vrtl .hltr .max-extent-8em75 { max-height: 8.75em; max-width: none; } +.hltr .max-extent-9em, .vrtl .hltr .max-extent-9em { max-height: 9.00em; max-width: none; } +.hltr .max-extent-10em, .vrtl .hltr .max-extent-10em { max-height: 10.00em; max-width: none; } +.hltr .max-extent-11em, .vrtl .hltr .max-extent-11em { max-height: 11.00em; max-width: none; } +.hltr .max-extent-12em, .vrtl .hltr .max-extent-12em { max-height: 12.00em; max-width: none; } +.hltr .max-extent-13em, .vrtl .hltr .max-extent-13em { max-height: 13.00em; max-width: none; } +.hltr .max-extent-14em, .vrtl .hltr .max-extent-14em { max-height: 14.00em; max-width: none; } +.hltr .max-extent-15em, .vrtl .hltr .max-extent-15em { max-height: 15.00em; max-width: none; } +.hltr .max-extent-20em, .vrtl .hltr .max-extent-20em { max-height: 20.00em; max-width: none; } +.hltr .max-extent-30em, .vrtl .hltr .max-extent-30em { max-height: 30.00em; max-width: none; } +.hltr .max-extent-40em, .vrtl .hltr .max-extent-40em { max-height: 40.00em; max-width: none; } +/* 縦組み用 */ +.vrtl .max-extent-0em25, .hltr .vrtl .max-extent-0em25 { max-height: none; max-width: 0.25em; } +.vrtl .max-extent-0em50, .hltr .vrtl .max-extent-0em50 { max-height: none; max-width: 0.50em; } +.vrtl .max-extent-0em75, .hltr .vrtl .max-extent-0em75 { max-height: none; max-width: 0.75em; } +.vrtl .max-extent-1em, .hltr .vrtl .max-extent-1em { max-height: none; max-width: 1.00em; } +.vrtl .max-extent-1em25, .hltr .vrtl .max-extent-1em25 { max-height: none; max-width: 1.25em; } +.vrtl .max-extent-1em50, .hltr .vrtl .max-extent-1em50 { max-height: none; max-width: 1.50em; } +.vrtl .max-extent-1em75, .hltr .vrtl .max-extent-1em75 { max-height: none; max-width: 1.75em; } +.vrtl .max-extent-2em, .hltr .vrtl .max-extent-2em { max-height: none; max-width: 2.00em; } +.vrtl .max-extent-2em50, .hltr .vrtl .max-extent-2em50 { max-height: none; max-width: 2.50em; } +.vrtl .max-extent-3em, .hltr .vrtl .max-extent-3em { max-height: none; max-width: 3.00em; } +.vrtl .max-extent-4em, .hltr .vrtl .max-extent-4em { max-height: none; max-width: 4.00em; } +.vrtl .max-extent-5em, .hltr .vrtl .max-extent-5em { max-height: none; max-width: 5.00em; } +.vrtl .max-extent-5em25, .hltr .vrtl .max-extent-5em25 { max-height: none; max-width: 5.25em; } +.vrtl .max-extent-6em, .hltr .vrtl .max-extent-6em { max-height: none; max-width: 6.00em; } +.vrtl .max-extent-7em, .hltr .vrtl .max-extent-7em { max-height: none; max-width: 7.00em; } +.vrtl .max-extent-8em, .hltr .vrtl .max-extent-8em { max-height: none; max-width: 8.00em; } +.vrtl .max-extent-8em75, .hltr .vrtl .max-extent-8em75 { max-height: none; max-width: 8.75em; } +.vrtl .max-extent-9em, .hltr .vrtl .max-extent-9em { max-height: none; max-width: 9.00em; } +.vrtl .max-extent-10em, .hltr .vrtl .max-extent-10em { max-height: none; max-width: 10.00em; } +.vrtl .max-extent-11em, .hltr .vrtl .max-extent-11em { max-height: none; max-width: 11.00em; } +.vrtl .max-extent-12em, .hltr .vrtl .max-extent-12em { max-height: none; max-width: 12.00em; } +.vrtl .max-extent-13em, .hltr .vrtl .max-extent-13em { max-height: none; max-width: 13.00em; } +.vrtl .max-extent-14em, .hltr .vrtl .max-extent-14em { max-height: none; max-width: 14.00em; } +.vrtl .max-extent-15em, .hltr .vrtl .max-extent-15em { max-height: none; max-width: 15.00em; } +.vrtl .max-extent-20em, .hltr .vrtl .max-extent-20em { max-height: none; max-width: 20.00em; } +.vrtl .max-extent-30em, .hltr .vrtl .max-extent-30em { max-height: none; max-width: 30.00em; } +.vrtl .max-extent-40em, .hltr .vrtl .max-extent-40em { max-height: none; max-width: 40.00em; } + + diff --git a/docs/style/style-ascii-dwango.css b/docs/style/style-ascii-dwango.css new file mode 100755 index 0000000..f4f0c51 --- /dev/null +++ b/docs/style/style-ascii-dwango.css @@ -0,0 +1,1126 @@ +@charset "UTF-8"; + +/* +Ascii-Dwango customized e-pub style +---------------------------------------------------------------- */ +@page { margin: 1em 2em; } +body { margin: 5%; text-align: justify; font-size: 100%; } + +/* 見出し +---------------------------------------------------------------- */ +/* 部: part */ +h1{ + font-size: 180%; margin-top: 15pt; margin-bottom: 12pt; + font-weight: bold; text-align: center; + page-break-after: avoid; + color: #06287e; +} +/* 章: section */ +h2{ + font-size: 180%; margin-top: 15pt; margin-bottom: 25pt; + font-weight: bold; text-align: left; + break-after: avoid; + color: #06287e; +} +/* 節: subsection */ +h3{ + font-size: 120%; margin-top: 20pt; margin-bottom: 0pt; + font-weight: bold; text-align: left; + break-after: avoid; + color: #06287e; + text-indent: -2em; margin-left: 2em; +} +/* 項: subsubsection */ +h4{ + font-size: 100%; margin-top: 15pt; margin-bottom: 0pt; + font-weight: bold; text-align: left; + break-after: avoid; + color: #06287e; + text-indent: -2em; margin-left: 2em; +} +/* 小項 */ +h5{ + font-size: 95%; margin-top: 12pt; margin-bottom: 0pt; + font-weight: bold; text-align: left; + break-after: avoid; + color: #06287e; + text-indent: -2em; margin-left: 2em; +} +/* 小小項 */ +h6{ + font-size: 100%; margin-top: 9pt; margin-bottom: 0pt; + font-weight: bold; text-align: left; + break-after: avoid; + color: #06287e; + text-indent: -2em; margin-left: 2em; +} +/* section */ +section{ break-before: avoid; break-after: avoid; } +/* 見出しの泣き別れを避ける */ +.heading{ break-before: avoid; break-after: avoid; } + +/* Info +---------------------------------------------------------------- */ +.cover{ + margin-top: 2pt; margin-bottom: 2pt; text-align: center; +} +.maintitle{ + margin-top: 100pt; margin-bottom: 10pt; text-align: left; + font-size: 180%; font-weight: bold; + font-family: sans-serif-ja, sans-serif; + break-after: avoid; +} +.subtitle{ + margin-top: 5pt; margin-bottom: 10pt; text-align: left; + font-size: 120%; + break-after: avoid; +} +.edition{ + margin-top: 15pt; margin-bottom: 15pt; text-align: left; +} +.author{ + margin-top: 150pt; margin-bottom: 15pt; text-align: right; + font-weight: bold; +} +.publisher{ + margin-top: 200pt; margin-bottom: 2pt; text-align: right; +} +.address{ + margin-top: 30pt; margin-bottom: 4pt; text-align: left; +} +.copy{ + margin-top: 15pt; margin-bottom: 4pt; + font-size: 90%; +} +.ex-title{ + margin-top: 9pt; margin-bottom: 8pt; text-align: left; + font-weight: bold; + font-family: sans-serif-ja, sans-serif; + break-after: avoid; +} + +/* Table Of Contents +---------------------------------------------------------------- */ +.toc-part{ + margin-top: 14pt; margin-bottom: 5pt; + text-indent: 0.002pt; + text-align: left; +} +.toc-chapter{ + margin-top: 10pt; margin-bottom: 5pt; + text-indent: 0.002pt; + text-align: left; +} +.toc-section{ + margin-top: 5pt; margin-bottom: 4pt; + text-indent: 0.002pt; + text-align: left; +} +.toc-section1{ + margin-top: 4pt; margin-bottom: 4pt; margin-left: 20pt; + text-align: left; +} +.toc-section11{ + margin-top: 4pt; margin-bottom: 4pt; margin-left: 14pt; + text-align: left; +} +.toc-section2{ + margin-top: 4pt; margin-bottom: 4pt; margin-left: 52pt; + text-align: left; +} +.toc-section21{ + margin-top: 4pt; margin-bottom: 4pt; margin-left: 46pt; + text-align: left; +} + +/* Footnote +---------------------------------------------------------------- */ +.footnote{ + margin-top: 4pt; margin-bottom: 8pt; + margin-left: 8em; text-indent: -1em; + font-size: 0.8em; +} +.footnotes{ + margin-top: 4pt; margin-bottom: 4pt; + font-size: 0.88em; +} + +/* Index +---------------------------------------------------------------- */ +.index{ + font-size: 0.8em; +} +.indexmain{ + margin-top: 2pt; margin-bottom: 2pt; + text-align: left; text-indent: 0.024pt; + font-size: 0.88em; +} +.indexmain1{ + margin-top: 14pt; margin-bottom: 2pt; + text-align: left; text-indent: 0.024pt; + font-size: 0.88em; +} +.indexsub{ + margin-top: 2pt; margin-bottom: 2pt; + margin-left: 25pt; + text-align: left; + font-size: 0.88em; +} + +/* 強調 +---------------------------------------------------------------- */ +span.k{/* ゴシック */ + font-family: sans-serif-ja, sans-serif; + font-style: normal; +} +span.kb{/* 太ゴシック */ + font-family: sans-serif-ja, sans-serif; + font-style: normal; font-weight: bold; +} +span.it{/* イタリック */ + font-style: italic; +} +span.bl{/* イタリックボールド */ + font-style: normal; font-weight: bold; +} +span.link{/* リンク */ + color: #06287e; +} +a.footnote-ref{/* 脚注 */ + vertical-align: super; +} +/* ml font */ +span.b { font-style: normal; font-weight: bold; } +span.tvr { font-family: sans-serif-ja, sans-serif; font-style: italic; } +span.cd { font-family: sans-serif-ja, sans-serif; font-style: normal; } +span.id { font-family: sans-serif-ja, sans-serif; font-style: normal; } +span.kw { font-family: sans-serif-ja, sans-serif; font-style: normal; font-weight: bold; text-transform: lowercase; font-size: 90%;} +span.sc { font-family: sans-serif-ja, sans-serif; font-variant-caps: small-caps; } +span.vr { font-family: sans-serif-ja, sans-serif; font-style: italic; } +span.con { font-family: sans-serif-ja, sans-serif; font-variant-caps: small-caps; } +span.tcon { font-family: sans-serif-ja, sans-serif; font-style: normal; font-variant-caps: small-caps; } +span.modsc { font-family: Courier; } +span.errmsg { font-family: Courier; } + +/* フォントサイズ +---------------------------------------------------------------- */ +span.fontsmall { font-size: 75%; } +span.fonttiny { font-size: 65%; } + +/* 数式 +---------------------------------------------------------------- */ +span.f{/* Italic */ + font-family: Georgia; + font-style: italic; + margin-left: 1pt; margin-right: 1pt; +} +span.fb{/* Italic, bold */ + font-family: Georgia; + font-style: italic; font-weight: bold; + margin-left: 1pt; margin-right: 1pt; +} +span.r{/* Regular */ + font-family: Georgia; + font-style: normal; + margin-left: 1pt; margin-right: 1pt; +} +span.sum{/* Symbol, sum */ + font-style: normal; + font-size: 160%; + vertical-align: middle; + margin-left: 0pt; margin-right: 0pt; +} +span.cup{/* Symbol, cup */ + font-style: normal; + font-size: 190%; + vertical-align: middle; + margin-left: 0pt; margin-right: 0pt; +} + +/* 2倍ダーシ ―― +---------------------------------------------------------------- */ +span.emdash { letter-spacing:-.2em; width:2em; margin-left:.5em; margin-right:.5em; } + +/* Text Indentation +---------------------------------------------------------------- */ +/* Indent */ +.in { text-indent: 1em; } +.ih { text-indent: 0.5em; } +/* No-Indent */ +.ni { text-indent: 0em; } + +/* Indent: words */ +.i1w { margin-left: 1em; } +.i2w { margin-left: 2em; } +.i3w { margin-left: 3em; } +.i4w { margin-left: 4em; } +/* Indent: index */ +.in2 { text-indent: 2em; } +.in4 { text-indent: 4em; } +/* Indent: hangindent */ +.hangindent { text-indent: -1em; margin-left: 1em; } +.hangindentl { text-indent: -1em; margin-left: 2em; } +.hangalpha { text-indent: -2em; margin-left: 4em; } +.hangcase { text-indent: -3em; margin-left: 3em; } + +/* chapter title */ +span.chap-title{ + font-size: 120%; font-weight: bold; font-style: italic; + font-family: Georgia; color: #979393; +} +span.chap-num{ + font-size: 200%; font-weight: bold; font-style: italic; + font-family: Georgia; color: #979393; + margin-left: 4px; +} + +/* description */ +div.description{ + margin-top: 1.5em; margin-bottom: 1.5em; padding-left: 1em; +} +p.ditem{ + text-indent: 0em; + font-family: sans-serif-ja, sans-serif; font-style: normal; +} +p.din{ + margin-left: 2em; + text-indent: 1em; +} +p.dni{ + margin-left: 2em; + text-indent: 0em; +} + +/* horizontal line */ +hr.chap-hr{ + border-top: 4px solid; + border-color: #979393; +} +hr.hr-gray{ + border-top: 2px solid; + border-color: #979393; +} +hr.hr-blue{ + border-top: 2px solid; + border-color: #0000BB; +} +hr.hr-green{ + border-top: 2px solid; + border-color: #006600; +} + +/* Interview */ +.hang-interview{ + text-indent: -2em; margin-left: 2em; +} +p.iin{ + margin-left: 2em; + text-indent: 1em; +} + +/* Cover page +---------------------------------------------------------------- */ +ul.cover { + display: flex; + justify-content: space-around; + list-style: none; + text-align: center; +} +ul.cover li { + width: 40%; +} +ul.cover img { + width: 80%; +} + +/* Division +---------------------------------------------------------------- */ +div.blockquote{/* 引用 */ + margin-top: 1.0em; margin-bottom: 1.0em; + margin-left: 2em; +} +/* column */ +div.column{/* column, body */ + margin-top: 1.5em; margin-bottom: 1.5em; + padding-left: 2em; padding-right: 2em; padding-top: 4pt; padding-bottom: 4pt; + border:solid 1px #000000; + -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; background-color: #F2F2F2; +} +p.column-title{/* column, title */ + font-family: sans-serif-ja, sans-serif; + font-weight: bold; + padding-top: 4pt; padding-bottom: 4pt; +} +/* newlead */ +div.newlead{ + margin-top: 1.5em; margin-bottom: 1.5em; + padding-left: 1em; padding-right: 1em; padding-top: 4pt; padding-bottom: 4pt; + border:solid 1px #000000; + -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; background-color: #F2F2F2; +} +/* lead */ +div.lead{ + margin-top: 1.5em; margin-bottom: 1.5em; + padding-left: 1.5em; padding-right: 1.5em; padding-top: 4pt; padding-bottom: 4pt; + font-family: sans-serif-ja, sans-serif; + font-style: normal; + background-color: #F2F2F2; +} + +/* Note */ +div.note { margin-top: 12pt; margin-bottom: 20pt; font-size: 85%; break-before: avoid; break-after: avoid; break-inside: avoid; } + +/* Enumerate */ +div.enum { margin-top: 10pt; margin-bottom: 10pt; } + + +/* List +---------------------------------------------------------------- */ +/* none */ +ul{ + margin-top: 0.5em; + margin-bottom: 0.5em; +} +ul.none{ + list-style: none; + padding-left: 2em; +} +/* parentheses */ +ol.list_parentheses{ + padding:0 0 0 2em; margin:0; + margin-top: 0.5em; margin-bottom: 0.5em; +} +ol.list_parentheses li{ + list-style-type:none; list-style-position:inside; + counter-increment: cnt; + text-indent:-1.5em; margin-left:1em; +} +ol.list_parentheses li:before{ + display: marker; + content: "(" counter(cnt) ") "; +} +/* ul left 1em */ +ul.left{ + margin-left: 2em; + padding-left: 0em; +} +/* minus */ +.minus{ padding-left: 2em; list-style-type: "− "; } +/* circle */ +.circle{ padding-left: 2em; list-style-type: circle; } +/* ref */ +.ref{ padding-left: 1em; } +/* loweralpha */ +ol.liststyletype_loweralpha{ + list-style-type: lower-alpha; + padding-left: 4em; padding-right: 1em; +} +/* num */ +div.listnum{ + margin-top: 0.5em; margin-bottom: 0.5em; + margin-left: 2em; +} + +/* Border +---------------------------------------------------------------- */ +.bordersource{ + margin-top: 4pt; margin-bottom: 4pt; + border-top: solid 1px #000000; border-bottom: solid 1px #000000; +} +.borderconsole{ + margin-top: 0.5em; margin-bottom: 0.5em; + border:solid 1px #000000; + -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; +} +.borderterminal{ + margin-top: 0.5em; margin-bottom: 0.5em; + padding: 2px 0em; + border:solid 1px #000000; + -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; +} +.bordergrammar{ + margin-top: 10pt; margin-bottom: 10pt; + padding: 2px 0em; + border:solid 1px #000000; + background-color: #EEEEEE; +} +.borderfigure{ + margin-top: 4pt; margin-bottom: 4pt; + border: solid 1px #000000; + padding-left: 1em; +} +.borderlist{ + margin-top: 1.0em; margin-bottom: 1.0em; + border:solid 1px #000000; + padding-right: 1em; + background-color: #EEEEEE; + -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; +} + +/* Program list +---------------------------------------------------------------- */ +code{ + font-family: Courier; + font-size: 100%; +} +pre{ + padding-left: 2em; padding-right: 2em; + padding-top: 0pt; padding-bottom: 0pt; + margin-top: 0.5em; margin-bottom: 0.5em; + font-family: Courier; + font-size: 95%; font-style: normal; + line-height: 150%; + white-space: pre-wrap; +} +pre.tabbing{ + margin: 0 auto 0 0; + line-height: 1.75; + font-family: serif-ja, serif; + padding-left: 0em; padding-right: 0em; + font-size: 100%; font-style: normal; +} +pre span.kwd{ + color: #3366CC; +} +pre span.cmt{ + color: #993333; +} +pre span.str{ + color: #006633; +} +pre span.mcm{ + color: #9933CC; +} +div.plist{/* Program list */ + margin-top: 0.5em; margin-bottom: 0.5em; + break-before: avoid; break-after: avoid; + padding-left: 0em; +} +div.plistc{/* Program list, caption */ + margin-top: 1.5em; margin-bottom: 1.5em; + break-before: avoid; break-after: avoid; + padding-left: 0em; +} +div.plistd{/* Program list, description */ + margin-top: 0.5em; margin-bottom: 0.5em; + break-before: avoid; break-after: avoid; + padding-left: 2em; +} +p.lst-caption{/* Program list caption */ + margin-left: 0em; padding-left: 1em; + margin-top: 4pt; margin-bottom: 2pt; + font-family: sans-serif-ja, sans-serif; font-style: normal; + font-size: 85%; text-align: left; + break-before: avoid; break-after: avoid; + background-color: #545252; color: white; +} + + +/* Caption +---------------------------------------------------------------- */ +/* other */ +.caption { font-family: sans-serif-ja, sans-serif; font-style: normal; margin-top: 10pt; margin-bottom: 10pt; font-size: 85%; text-align: center; break-before: avoid; break-after: avoid; } +.filename { font-family: sans-serif-ja, sans-serif; font-style: italic; margin-top: 4pt; margin-bottom: 10pt; font-size: 85%; text-align: right; break-before: avoid; break-after: avoid; } + +/* Text position +---------------------------------------------------------------- */ +/* 下線 */ +.underline{ text-decoration: underline; } +/* 中央 */ +.center { text-align: center; } +.center-border { text-align: center; border: thin solid #000000; } +/* 右寄せ */ +.right { text-align: right; } +/* 左寄せ */ +.left { text-align: left; margin-left: 4em; } +/* 両端揃え */ +.lleft { text-align: left; float: left; } +.lright { text-align: right; } + +/* Navigation +---------------------------------------------------------------- */ +/* パンくずリスト */ +.navigation-top { font-family: sans-serif-ja, sans-serif; font-style: normal; font-size: 85%;} +/* 右寄せ */ +.navigation-right { text-align: right; font-family: sans-serif-ja, sans-serif; font-style: normal; font-size: 85%;} + +/* Text color +---------------------------------------------------------------- */ +.gray { color: #C0C0C0; } + + +/* Equation +---------------------------------------------------------------- */ +.eqnParent { display: table; width: 100%; margin-top: 8pt; margin-bottom: 8pt; } +.eqnEqn { display: table-cell; text-align: center; } +.eqnEqnl { display: table-cell; text-align: left; } +.eqnNum { display: table-cell; text-align: right; vertical-align:middle; } +.eqnCenter { margin-top: 8pt; margin-bottom: 8pt; text-align: center; } +/* Equation: number */ +table.eqntbl { width: 100%; max-width: 100%; border-collapse: collapse; border-spacing: 0; margin-top: 8pt; margin-bottom: 8pt; border: none; } +table.eqntbl td { padding: 0px 0px; font-size: 100%; vertical-align: middle; white-space: normal; } +table.eqntbl tr td:first-child { width: 90%; text-align: center; } +table.eqntbl tr td:last-child { width: 10%; text-align: right; } + +/* Image and Figure +---------------------------------------------------------------- */ +/* Figure */ +div.fig{ + margin-top: 1.5em; margin-bottom: 1.5em; + text-align: center; + break-inside: avoid; +} +div.figconsole{ + margin-top: 1.5em; margin-bottom: 1.5em; + text-align: left; + break-inside: avoid; +} +p.fig-caption{/* caption */ + margin-top: 4pt; margin-bottom: 2pt; + margin-left: 0em; padding-left: 1em; + font-family: sans-serif-ja, sans-serif; font-style: normal; + font-size: 85%; text-align: left; + background-color: #545252; color: white; + break-before: avoid; break-after: avoid; +} + +/* Image */ +div.image img { max-width: 99%; max-height: 99%; } + +img { text-indent: 0pt; } +img.icon { width: 1.4em; height: 1.6em; margin-top: 0.5em; margin-left: 0.8em; float: right; } +img.middleicon { vertical-align: middle; width: 1.4em; height: 1.4em; } + +.image img { margin-top: 10pt; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; } +.image10 img { width: 10%; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image15 img { width: 15%; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image20 img { width: 20%; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image25 img { width: 25%; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image30 img { width: 30%; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image35 img { width: 35%; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image40 img { width: 40%; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image50 img { width: 50%; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image60 img { width: 60%; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image70 img { width: 70%; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image80 img { width: 80%; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image90 img { width: 90%; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image100 img { width: 100%; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } + +.image200 img { width: 200px; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image300 img { width: 300px; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image350 img { width: 350px; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image400 img { width: 400px; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image450 img { width: 450px; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image500 img { width: 500px; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image600 img { width: 600px; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image700 img { width: 700px; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } +.image800 img { width: 800px; height: auto; margin-left: 0em; margin-right: 0em; text-align: center; break-inside: avoid; object-fit: contain; } + +.middle img { vertical-align: middle; } +.middle10 img { height: 1.0em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle12 img { height: 1.2em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle14 img { height: 1.4em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle15 img { height: 1.5em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle16 img { height: 1.6em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle18 img { height: 1.8em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle20 img { height: 2em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle24 img { height: 2.4em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle25 img { height: 2.5em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle30 img { height: 3em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle35 img { height: 3.5em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle40 img { height: 4em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle45 img { height: 4.5em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle50 img { height: 5em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle55 img { height: 5.5em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle60 img { height: 6em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle70 img { height: 7em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle80 img { height: 8em; width: auto; vertical-align: middle; text-align: center; object-fit: contain;} +.middle90 img { height: 9em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle100 img { height: 10em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle110 img { height: 11em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle120 img { height: 12em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle130 img { height: 13em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle140 img { height: 14em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle150 img { height: 15em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle160 img { height: 16em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle170 img { height: 17em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle180 img { height: 18em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle190 img { height: 19em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle200 img { height: 20em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle210 img { height: 21em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle220 img { height: 22em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle230 img { height: 23em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle240 img { height: 24em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle250 img { height: 25em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle260 img { height: 26em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle270 img { height: 27em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle280 img { height: 28em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle290 img { height: 29em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle300 img { height: 30em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle310 img { height: 31em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } +.middle320 img { height: 32em; width: auto; vertical-align: middle; text-align: center; object-fit: contain; } + +/* Table +---------------------------------------------------------------- */ +div.table {/* Table */ + margin-top: 1.5em; margin-bottom: 1.5em; + break-before: avoid; break-after: avoid; +} +p.tbl-caption{/* table caption */ + margin-left: 0em; padding-left: 1em; + font-family: sans-serif-ja, sans-serif; font-style: normal; + margin-top: 4pt; margin-bottom: -4pt; + font-size: 85%; + text-align: left; + background-color: #545252; color: white; + break-before: avoid; break-after: avoid; +} +table{/* table, common */ + border-collapse: collapse; border-spacing: 0; + margin-top: 1em; margin-bottom: 1em; + font-family: sans-serif-ja, sans-serif; font-style: normal; + font-size: 85%; + break-inside: avoid; +} + +/* tbl00 */ +table.tbl00 th{ + white-space: nowrap; + padding-left: 4pt; padding-right: 4pt; + background-color: #000000; color: #ffffff; + font-weight: normal; + border-right: 1px #ffffff solid; +} +table.tbl00 td{ + white-space: normal; + padding: 4px 8px; border-width: 0 0 2px 2px; + text-align: left; vertical-align: top; +} +table.tbl00 tr:first-child{ + white-space: nowrap; + border-top: 1px #000000 solid; +} +table.tbl00 tr:last-child{ + border-bottom: 1px #000000 solid; border-right: none; + white-space: wrap; +} + +/* tbl01 */ +table.tbl01 th{ + white-space: nowrap; + padding-left: 4pt; padding-right: 4pt; + background-color: #000000; color: #ffffff; + font-weight: normal; + border-right: 1px #ffffff solid; +} +table.tbl01 td { + white-space: normal; + padding: 4px 8px; border-width: 0 0 2px 2px; + text-align: left; vertical-align: top; + border-top: 1px #000000 solid; border-bottom: 1px #000000 solid; + border-left: none; border-right: 1px #000000 solid; +} +table.tbl01 tr td:nth-child(odd){ + white-space: nowrap; + background-color: #C0C0C0; +} +table.tbl01 tr td:last-child{ + white-space: wrap; + border-right: none; +} + +/* tbl02 */ +table.tbl02 td{ + white-space: normal; + padding: 4px 8px; border-width: 0 0 2px 2px; + text-align: left; vertical-align: top; + border-top: 1px #000000 solid; border-bottom: 1px #000000 solid; + border-left: none; border-right: none; +} +table.tbl02 tr:first-child{ + white-space: nowrap; + background-color: #C0C0C0; +} +table.tbl02 tr td:last-child{ + white-space: wrap; + border-right: none; +} + +/* tbl03 */ +table.tbl03 td{ + white-space: normal; + padding: 4px 8px; border-width: 0 0 2px 2px; + text-align: center; vertical-align: top; + border-left: none; border-right: none; +} +table.tbl03 tr:first-child{ + white-space: nowrap; + background-color: #C0C0C0; + border-top: 1px #000000 solid; border-bottom: 1px #000000 solid; +} +table.tbl03 tr:last-child{ + white-space: wrap; + border-right: none; border-bottom: 1px #000000 solid; +} + +/* tbl04 */ +table.tbl04 th{ + white-space: nowrap; + background-color: #000000; color: #ffffff; + font-weight: normal; + border-right: 1px #ffffff solid; + padding-left: 4pt; padding-right: 4pt; +} +table.tbl04 td{ + white-space: normal; + padding: 4px 8px; border-width: 0 0 2px 2px; + text-align: left; vertical-align: top; + border-top: 1px #000000 solid; border-bottom: 1px #000000 solid; + border-left: none; border-right: 1px #000000 solid; +} +table.tbl04 tr td:nth-child(odd){ + white-space: nowrap; + background-color: #C0C0C0; + text-align: center; +} +table.tbl04 tr td:last-child{ + white-space: wrap; + border-right: none; +} + +/* tbl05 */ +table.tbl05 th{ + white-space: nowrap; + background-color: #000000; color: #ffffff; + font-weight: normal; + border-right: 1px #ffffff solid; + padding-left: 4pt; padding-right: 4pt; +} +table.tbl05 td{ + white-space: normal; + padding: 4px 8px; border-width: 0 0 2px 2px; + text-align: right; vertical-align: top; + border-top: 1px #000000 solid; border-bottom: 1px #000000 solid; + border-left: none; border-right: 1px #000000 solid; +} +table.tbl05 tr td:first-child{ + white-space: nowrap; + background-color: #C0C0C0; + text-align: center; +} +table.tbl05 tr td:last-child{ + white-space: wrap; + border-right: none; +} + +/* tbl06 */ +table.tbl06 th{ + white-space: nowrap; + padding-left: 4pt; padding-right: 4pt; + background-color: #000000; color: #ffffff; + font-weight: normal; + border-right: 1px #ffffff solid; +} +table.tbl06 td{ + padding: 4px 8px; border-width: 0 0 2px 2px; + text-align: left; vertical-align: top; + white-space: normal; + border-top: 1px #000000 solid; border-bottom: 1px #000000 solid; + border-left: none; border-right: 1px #000000 solid; +} +table.tbl06 tr td:first-child{ + white-space: nowrap; + background-color: #C0C0C0; + text-align: center; +} +table.tbl06 tr td:nth-child(2){ + white-space: nowrap; + background-color: #EEEEEE; + text-align: center; +} +table.tbl06 tr td:last-child{ + white-space: wrap; + border-right: none; +} + +/* tbl07 */ +table.tbl07 th{ + white-space: nowrap; + padding-left: 4pt; padding-right: 4pt; + background-color: #000000; color: #ffffff; + font-weight: normal; + border-right: 1px #ffffff solid; +} +table.tbl07 td{ + white-space: normal; + padding: 4px 8px; border-width: 0 0 2px 2px; + text-align: center; vertical-align: top; + border-top: 1px #000000 solid; border-bottom: 1px #000000 solid; + border-left: none; border-right: 1px #000000 solid; +} +table.tbl07 tr td:first-child{ + white-space: nowrap; + background-color: #C0C0C0; + text-align: center; +} +table.tbl07 tr td:last-child{ + white-space: wrap; + border-right: none; +} + +/* tbl08 */ +table.tbl08 th{ + white-space: nowrap; + padding-left: 4pt; padding-right: 4pt; + background-color: #000000; color: #ffffff; + font-weight: normal; + border-right: 1px #ffffff solid; +} +table.tbl08 td{ + white-space: normal; + padding: 4px 8px; border-width: 0 0 2px 2px; + text-align: center; vertical-align: top; + border-top: 1px #000000 solid; border-bottom: 1px #000000 solid; + border-left: none; border-right: 1px #000000 solid; +} +table.tbl08 tr td:nth-child(odd){ + white-space: nowrap; + background-color: #C0C0C0; +} +table.tbl08 tr td:first-child{ + white-space: nowrap; + background-color: #444444; color: #ffffff; + text-align: center; +} +table.tbl08 tr td:nth-child(2){ + white-space: nowrap; + background-color: #666666; color: #ffffff; + text-align: center; +} +table.tbl08 tr td:last-child{ + white-space: wrap; + border-right: none; +} + +/* tbl09 */ +table.tbl09 th{ + white-space: nowrap; + padding-left: 4pt; padding-right: 4pt; + background-color: #000000; color: #ffffff; + text-align: center; + font-weight: normal; + border-right: 1px #ffffff solid; border-top: 1px #ffffff solid; +} +table.tbl09 td{ + white-space: normal; + padding: 4px 8px; border-width: 0 0 2px 2px; + text-align: center; vertical-align: top; + border-top: 1px #000000 solid; border-bottom: 1px #000000 solid; + border-left: none; border-right: 1px #000000 solid; +} +table.tbl09 tr td:first-child{ + white-space: nowrap; + background-color: #C0C0C0; + text-align: right; +} +table.tbl09 tr td:nth-child(3){ + white-space: nowrap; + background-color: #EEEEEE; + text-align: center; +} +table.tbl09 tr td:nth-child(4){ + white-space: nowrap; + background-color: #C0C0C0; + text-align: center; +} +table.tbl09 tr td:nth-child(6){ + white-space: nowrap; + background-color: #EEEEEE; + text-align: center; +} +table.tbl09 tr td:nth-child(7){ + white-space: nowrap; + background-color: #C0C0C0; + text-align: center; +} +table.tbl09 tr td:last-child{ + white-space: wrap; + border-right: none; +} + +/* tbl10 */ +table.tbl10 td { + white-space: normal; + padding: 4px 8px; border-width: 0 0 2px 2px; + text-align: left; vertical-align: top; + border-top: 1px #000000 solid; border-bottom: 1px #000000 solid; + border-left: none; border-right: 1px #000000 solid; +} +table.tbl10 tr td:nth-child(1){ + white-space: nowrap; + background-color: #C0C0C0; + border-right: none; +} +table.tbl10 tr td:nth-child(2){ + white-space: nowrap; + background-color: #C0C0C0; +} +table.tbl10 tr td:last-child{ + white-space: wrap; + border-right: none; +} + +/* tbl11 */ +table.tbl11 th{ + white-space: nowrap; + padding-left: 4pt; padding-right: 4pt; + background-color: #000000; color: #ffffff; + text-align: center; + font-weight: normal; + border-right: 1px #ffffff solid; border-top: 1px #ffffff solid; +} +table.tbl11 td { + white-space: normal; + padding: 4px 8px; border-width: 0 0 2px 2px; + text-align: left; vertical-align: top; + border-top: 1px #000000 solid; border-bottom: 1px #000000 solid; + border-left: none; border-right: 1px #000000 solid; +} +table.tbl11 th:nth-child(1){ + white-space: nowrap; + background-color: #000000; color: #ffffff; + border-right: none; +} +table.tbl11 tr td:nth-child(1){ + white-space: nowrap; + background-color: #C0C0C0; + border-right: none; +} +table.tbl11 tr td:nth-child(3){ + white-space: wrap; + background-color: #C0C0C0; +} +table.tbl11 tr td:nth-child(4){ + white-space: wrap; + background-color: #C0C0C0; +} +table.tbl11 tr td:last-child{ + white-space: wrap; + border-right: none; +} + +/* tbl12 */ +table.tbl12 td{ + white-space: normal; + padding: 4px 8px; border-width: 0 0 2px 2px; + text-align: left; vertical-align: top; + border-top: 1px #000000 solid; border-bottom: 1px #000000 solid; + border-left: none; border-right: 1px #000000 solid; +} +table.tbl12 tr:first-child{ + border-top: 1pt solid; +} +table.tbl12 tr:last-child{ + border-bottom: 1pt solid; +} +table.tbl12 tr td:nth-child(1){ + white-space: nowrap; + background-color: #444444; color: #ffffff; + text-align: left; + border-top: none; border-bottom: none; +} +table.tbl12 tr td:nth-child(2){ + white-space: nowrap; + background-color: #C0C0C0; + text-align: center; +} +table.tbl12 tr td:nth-child(4){ + white-space: nowrap; + background-color: #444444; color: #ffffff; + text-align: left; + border-top: none; border-bottom: none; +} +table.tbl12 tr td:nth-child(5){ + white-space: nowrap; + background-color: #C0C0C0; + text-align: center; +} +table.tbl12 tr td:last-child{ + white-space: wrap; + border-right: none; +} + +/* tbl13 */ +table.tbl13 th{ + white-space: nowrap; + padding-left: 4pt; padding-right: 4pt; + background-color: #000000; color: #ffffff; + font-weight: normal; + border-right: 1px #ffffff solid; +} +table.tbl13 td { + white-space: normal; + padding: 4px 8px; border-width: 0 0 2px 2px; + text-align: left; vertical-align: top; + border-top: 1px #000000 solid; border-bottom: 1px #000000 solid; + border-left: none; border-right: 1px #000000 solid; +} +table.tbl13 tr:nth-child(6){ + white-space: nowrap; + border-top: 1px solid; border-bottom: none; +} +table.tbl13 tr:last-child{ + white-space: nowrap; + border-bottom: 1px solid; +} +table.tbl13 tr td:nth-child(1){ + white-space: nowrap; + background-color: #C0C0C0; + border-top: none; border-bottom: none; +} +table.tbl13 tr td:nth-child(3){ + white-space: nowrap; + background-color: #C0C0C0; +} +table.tbl13 tr td:last-child{ + white-space: wrap; + border-right: none; +} + +/* tbl14 */ +table.tbl14 td { + white-space: normal; + padding: 4px 8px; border-width: 0 0 2px 2px; + text-align: left; vertical-align: top; + border-left: none; border-right: 1px #000000 solid; +} +table.tbl14 tr:first-child{ + white-space: nowrap; + border-top: 1px solid; +} +table.tbl14 tr:last-child{ + white-space: nowrap; + border-bottom: 1px solid; +} +table.tbl14 tr:nth-child(odd){ + border-bottom: none; +} +table.tbl14 tr:nth-child(even){ + border-bottom: 1px solid; +} +table.tbl14 tr td:nth-child(1){ + white-space: nowrap; + background-color: #C0C0C0; + border-top: none; border-bottom: none; + border-right: none; +} +table.tbl14 tr td:nth-child(2){ + white-space: nowrap; + background-color: #EEEEEE; + border-bottom: 1px solid; +} +table.tbl14 tr td:last-child{ + white-space: wrap; + border-right: none; + border-bottom: 1px solid; +} diff --git a/docs/style/style-check.css b/docs/style/style-check.css new file mode 100755 index 0000000..04a7288 --- /dev/null +++ b/docs/style/style-check.css @@ -0,0 +1,88 @@ +@charset "UTF-8"; + + +/* ファイル情報 +---------------------------------------------------------------- +【内容】 +チェック用スタイル + +【CSSファイルバージョン】 +ver.1.1.1 + +【当ファイル更新時の電書協EPUB 3 制作ガイドバージョン】 +ver.1.1.3 + +【細目】 +・Windows 用フォント指定 +・チェック用領域 + +【更新履歴】 +2014/11/01 ver.1.1.1 +・「特定RS対策用」指定を削除 + +2012/12/07 ver.1.1.0 +・ファイル更新時の電書協EPUB 3 制作ガイドバージョン表記を追加 + +2012/08/21 ver.1.0b1 +・公開版 +---------------------------------------------------------------- */ + + +/* ------------------------------------------------------------- + * Windows 用フォント指定 + * ------------------------------------------------------------- */ + +/* フォントセット指定 +---------------------------------------------------------------- */ +/* 横組み用 */ +@font-face { + font-family: "serif-ja"; + src: local("MS 明朝"); +} +@font-face { + font-family: "sans-serif-ja"; + src: local("MS ゴシック"); +} +/* 縦組み用 */ +@font-face { + font-family: "serif-ja-v"; + src: local("@MS 明朝"); +} +@font-face { + font-family: "sans-serif-ja-v"; + src: local("@MS ゴシック"); +} + + +/* 組み方向のデフォルトフォント指定 +---------------------------------------------------------------- +@付きフォント指定が不要になるまでは、 +組み方向が変わるとき明朝で上書き(書体を継承しない) +---------------------------------------------------------------- */ +.hltr { font-family: serif-ja, serif; } +.vrtl { font-family: serif-ja-v, serif-ja, serif; } + +/* 組み方向の入れ子対策 */ +.vrtl .hltr { font-family: serif-ja, serif; } +.hltr .vrtl { font-family: serif-ja-v, serif-ja, serif; } + + +/* 書体指定 +---------------------------------------------------------------- */ +/* 横組み用 */ +.hltr .mfont { font-family: serif-ja, serif; } +.hltr .gfont { font-family: sans-serif-ja, sans-serif;} +/* 縦組み用 */ +.vrtl .mfont { font-family: serif-ja-v, serif-ja, serif; } +.vrtl .gfont { font-family: sans-serif-ja-v, sans-serif-ja, sans-serif; } + +/* 組み方向の入れ子対策 */ +/* 縦組み中の横組み用 */ +.vrtl .hltr .mfont { font-family: serif-ja, serif; } +.vrtl .hltr .gfont { font-family: sans-serif-ja, sans-serif; } +/* 横組み中の縦組み用 */ +.hltr .vrtl .mfont { font-family: serif-ja-v, serif-ja, serif; } +.hltr .vrtl .gfont { font-family: sans-serif-ja-v, sans-serif-ja, sans-serif; } + + + diff --git a/docs/style/style-kadokawa.css b/docs/style/style-kadokawa.css new file mode 100755 index 0000000..bd9ea8f --- /dev/null +++ b/docs/style/style-kadokawa.css @@ -0,0 +1,319 @@ +@charset "UTF-8"; + + +/* ファイル情報 +---------------------------------------------------------------- +【内容】 +KADOKAWA リフロー型標準スタイル + +【バージョン】 +ver.1.3.0 + +【細目】 +・既存クラス上書き指定 +・ページ設定 + +【更新履歴】 +2019/09/25 +・奥付のEPUB ファイルバージョン表記指定「.release-version」を追加。 + あわせて上の行のデジタル発行日「.release-date」の margin-top の値を変更 + +・奥付の「連絡先(サポート)情報」に + 注意書き用の「.support-note-01」~「.support-note-04」を追加 + +2018/06/01 ver.1.2.0 +・既存クラス上書きのリンク指定を削除 +・奥付(追加要素)の「.p-colophon2」を追加 + +2017/06/01 ver.1.1.0 +・奥付に「.p-colophon .support」とその配下のクラスを追加 +・ロゴ用クレジット表記は .publisher-data の外に移動(別
となる) +・これまでの奥付との互換用に、今回変更された従来のクラスをファイル末に記載 + +2016/05/01 ver.1.0.1 +・奥付に「.p-colophon .publish-reception」を追加 +・奥付に「.p-colophon .publish-phone-editorial」を追加 + +2015/08/01 ver.1.0.0 +・公開版 +---------------------------------------------------------------- */ + + +/* ------------------------------------------------------------- + * ページ設定 + * ------------------------------------------------------------- */ + + +/* 本扉(標準) +---------------------------------------------------------------- */ +/* ページ全体の設定 */ +.p-titlepage { +} +.p-titlepage .main { + margin: 0 auto 0 auto; + padding: 4em 1em 1.5em 1em; + max-width: 22em; + text-align: center; + line-height: 1.6; +} +/* 作品名 */ +.p-titlepage .book-title { + margin: 0; + padding: 0; +} +.p-titlepage .book-title-before { + margin: 0; + padding: 0; + font-size: 0.85em; +} +.p-titlepage .book-title-main { + margin: 0; + padding: 0; + font-size: 1.5em; +} +.p-titlepage .book-title-after { + margin: 0; + padding: 0; + font-size: 0.85em; +} +.p-titlepage .book-title-demo { + margin: 1.5em 0 0 0; + padding: 0; + font-size: 0.85em; +} +/* 著者名 */ +.p-titlepage .author { + margin: 1.5em 0 3em 0; + padding: 1.5em 0 0 0; + font-size: 0.85em; + border-top: 1px solid black; +} +.p-titlepage .author p { + margin: 0.5em 0 0 0; + padding: 0; +} +/* レーベルロゴ部分 */ +.p-titlepage .label { + margin: 0; + padding: 0.4em 0 0.3em 0; + line-height: 1.2; +} +/* レーベルロゴ部分のロゴ画像 */ +.p-titlepage .label-logo img { + height: 2em; +} +/* レーベルロゴ部分のレーベル名 */ +.p-titlepage .label-name { + margin: 0; + padding: 0; + font-size: 0.75em; + font-family: sans-serif-jp, sans-serif; +} + + +/* 奥付(標準) +---------------------------------------------------------------- */ +/* ページ全体の設定 */ +.p-colophon { +} +.p-colophon .main { + margin: 0 auto 0 auto; + padding: 2em 1em 1em 1em; + max-width: 22em; + text-align: left; + line-height: 1.6; +} +/* 作品名 */ +.p-colophon .book-title { + margin: 0; + padding: 0; +} +.p-colophon .book-title-before { + margin: 0; + padding: 0; + font-size: 0.85em; +} +.p-colophon .book-title-main { + margin: 0; + padding: 0; + font-size: 1.2em; +} +.p-colophon .book-title-after { + margin: 0; + padding: 0; + font-size: 0.85em; +} +/* 著者名 */ +.p-colophon .author { + margin: 0.6em 0 0.9em 0; + padding: 0 0 0.4em 0; + font-size: 0.9em; + border-bottom: 1px solid black; +} +/* レーベルロゴ部分 */ +.p-colophon .label { + margin: 0; + padding: 0 0 0.3em 0; + line-height: 1; +} +/* レーベルロゴ部分のロゴ画像 */ +.p-colophon .label-logo img { + height: 2em; +} +/* レーベルロゴ部分のレーベル名 */ +.p-colophon .label-name { + margin: 0; + padding: 0; + font-size: 0.75em; + font-family: sans-serif-jp, sans-serif; +} +/* デジタル発行日 */ +.p-colophon .release-date { + margin: 1em 0 0 0; + padding: 0; + font-size: 0.8em; +} +/* EPUB ファイルバージョン表記 */ +.p-colophon .release-version { + margin: 0; + padding: 0; + font-size: 0.7em; +} +/* (C)表記 */ +.p-colophon .copyright { + margin: 0.6em 0 0 0; + padding: 0; + font-size: 0.7em; + line-height: 1.0; +} +/* 制作底本情報 */ +.p-colophon .original-books { + margin: 1em 0 0 0; + padding: 0; + font-size: 0.7em; + line-height: 1.0; +} +.p-colophon .original-title { + margin: 0.5em 0 0 0; + padding: 0; +} +.p-colophon .original-first-edition { + margin: 0; + padding: 0; +} +.p-colophon .original-used-edition { + margin: 0; + padding: 0; +} +/* 発行者情報 */ +.p-colophon .publisher-data { + margin: 1em 0 0 0; + padding: 0; + font-size: 0.7em; + line-height: 1.0; +} +.p-colophon .publish-person { + margin: 0; + padding: 0; +} +.p-colophon .publish-company { + margin: 0; + padding: 0; +} +.p-colophon .publish-address { + margin: 0; + padding: 0; +} +/* 連絡先(サポート)情報 */ +.p-colophon .support { + margin: 0.5em 0 0 0; + padding: 0; + font-size: 0.7em; + line-height: 1.0; +} +.p-colophon .support-phone-editorial { + margin: 0; + padding: 0; +} +.p-colophon .support-desk { + margin: 0; + padding: 0; +} +.p-colophon .support-contact { + margin: 0; + padding: 0; +} +.p-colophon .support-note { + margin: 0; + padding: 0; +} +.p-colophon .support-note-01 { + margin: 0; + padding: 0; +} +.p-colophon .support-note-02 { + margin: 0; + padding: 0; +} +.p-colophon .support-note-03 { + margin: 0; + padding: 0; +} +.p-colophon .support-note-04 { + margin: 0; + padding: 0; +} +/* ロゴ用クレジット表記 */ +.p-colophon .logo-credit { + margin: 2em 0 0 0; + padding: 0; + font-size: 0.7em; + line-height: 1.0; +} +/* 奥付中の断り書き */ +.p-colophon .kotowarigaki { + margin: 2em 0 0 0; + padding: 0; + font-size: 0.7em; + line-height: 1.0; +} + + +/* 奥付(追加要素) +---------------------------------------------------------------- */ +/* ページ全体の設定 */ +.p-colophon2 { +} +.p-colophon2 .main { + margin: 0 auto 0 auto; + padding: 2em 1em 1em 1em; + max-width: 22em; + text-align: left; + line-height: 1.6; +} + + +/* 旧奥付(リフロー型制作仕様 ver.1.0.1)互換用の連絡先情報 +---------------------------------------------------------------- */ +/* 発行者情報中の連絡先情報 */ +.p-colophon .publish-phone-editorial { + margin: 0; + padding: 0; +} +.p-colophon .publish-phone { + margin: 0; + padding: 0; +} +.p-colophon .publish-reception { + margin: 0; + padding: 0; +} +.p-colophon .publish-url { + margin: 0.5em 0 0 0; + padding: 0; +} +/* ロゴ用クレジット表記 */ +.p-colophon .publish-logo-credit { + margin: 2em 0 0 0; + padding: 0; +} diff --git a/docs/style/style-karc.css b/docs/style/style-karc.css new file mode 100755 index 0000000..481da27 --- /dev/null +++ b/docs/style/style-karc.css @@ -0,0 +1,68 @@ +@charset "UTF-8"; + + +/* ファイル情報 +---------------------------------------------------------------- +【内容】 +KADOKAWA リフロー型共通スタイル + +【バージョン】 +ver.1.1.0 + +【細目】 +・リンク指定 +・ページ別スタイル +・ツール類に自動追加されるスタイル指定等 + +【更新履歴】 +2018/06/01 ver.1.1.0 +・リンク指定「黒文字用」を追加 + +2015/08/01 ver.1.0.0 +・公開版 +---------------------------------------------------------------- */ + + +/* リンク指定 +---------------------------------------------------------------- */ +/* 青文字用 */ +a.link-blue:link { color: #0000ff; } +a.link-blue:visited { color: #0000ff; } +a.link-blue:hover { color: #0000ff; } +a.link-blue:focus { color: #0000ff; } +a.link-blue:active { color: #0000ff; } + +/* 赤文字用 */ +a.link-red:link { color: #ff0000; } +a.link-red:visited { color: #ff0000; } +a.link-red:hover { color: #ff0000; } +a.link-red:focus { color: #ff0000; } +a.link-red:active { color: #ff0000; } + +/* 黒文字用(マウスオーバー時は灰色) */ +a.link-black:link { color: #000000; } +a.link-black:visited { color: #000000; } +a.link-black:hover { color: #696969; } +a.link-black:focus { color: #696969; } +a.link-black:active { color: #696969; } + + +/* 下線[表示] */ +.hltr a.link-line { text-decoration: underline; } +.vrtl a.link-line { text-decoration: overline; } + +/* 下線[非表示] */ +.hltr a.link-noline { text-decoration: none; } +.vrtl a.link-noline { text-decoration: none; } + + +/* ページ別スタイル +---------------------------------------------------------------- */ +/* 電子版断り書き(デジタル追加文言)ページの全体設定 */ +.p-caution { + font-size: 0.9em; +} + + +/* etc +---------------------------------------------------------------- */ \ No newline at end of file diff --git a/docs/style/style-reset.css b/docs/style/style-reset.css new file mode 100755 index 0000000..8498d57 --- /dev/null +++ b/docs/style/style-reset.css @@ -0,0 +1,104 @@ +@charset "UTF-8"; + + +/* ファイル情報 +---------------------------------------------------------------- +【内容】 +CSS リセット + +【CSSファイルバージョン】 +ver.1.2.1 + +【当ファイル更新時の電書協EPUB 3 制作ガイドバージョン】 +ver.1.1.3 + +【細目】 +・css reset + +【更新履歴】 +2014/11/01 ver.1.2.1 +・「-epub(webkit)-text-align-last」を削除 +・「text-underline-position」を「-epub-text-underline-position」に変更 + +2012/12/07 ver.1.2.0 +・ファイル更新時の電書協EPUB 3 制作ガイドバージョン表記を追加 +・「text-underline-position: below left;」の「below」部分を「under」に変更 + +2012/10/29 ver.1.1b1 +・body に text-underline-position を指定 + ※ページ内すべての overline を縦組み時に右線(傍線)、 + underline を左線として設定 + +2012/08/21 ver.1.0b1 +・公開版 +---------------------------------------------------------------- */ + + +/* css reset +---------------------------------------------------------------- */ +body { + margin: 0; + padding: 0; + font-size: 100%; + vertical-align: baseline; + line-height: 1.75; + background: transparent; + + word-spacing: normal; + letter-spacing: normal; + white-space: normal; + word-wrap: break-word; + text-align: justify; + + -webkit-line-break: normal; + -epub-line-break: normal; + + -webkit-word-break: normal; + -epub-word-break: normal; + + -webkit-hyphens: auto; + -epub-hyphens: auto; + + -webkit-text-underline-position: under left; + -epub-text-underline-position: under left; +} +div,p { + display: block; + width: auto; + height: auto; + margin: 0; + padding: 0; +} +body,div,p { + text-indent: 0; +} +body > p, +div > p { + text-indent: inherit; +} +h1,h2,h3,h4,h5,h6 { + display: block; + margin: 0; + padding: 0; + font-size: 100%; + font-weight: inherit; + background: transparent; +} +img { + width: auto; + height: auto; + margin: 0; + padding: 0; + border: none; + vertical-align: baseline; + background: transparent; +} +a { + font-style: inherit; + font-weight: inherit; + text-decoration: inherit; + color: inherit; + background: transparent; +} + + diff --git a/docs/style/style-standard.css b/docs/style/style-standard.css new file mode 100755 index 0000000..df43e78 --- /dev/null +++ b/docs/style/style-standard.css @@ -0,0 +1,1675 @@ +@charset "UTF-8"; + + +/* ファイル情報 +---------------------------------------------------------------- +【内容】 +全作品共通の基本スタイル + +【CSSファイルバージョン】 +ver.1.3.1 + +【当ファイル更新時の電書協EPUB 3 制作ガイドバージョン】 +ver.1.1.3 + +【細目】 +・組み方向指定 +・html指定 +・ボックスの種類 +・改ページ指定 +・行揃え +・ブロック要素の位置揃え +・インライン要素の位置揃え +・リンク指定 +・注釈 +・外字画像 +・画像のページフィット指定 +・縦中横 +・文字の向き +・上付文字・下付文字 +・小書き文字 +・訓点(返り点) +・圏点・傍点 +・行高 +・文字の間隔 +・フォント指定 +・太字 +・斜体 +・文字色 +・背景色 +・文字色の白黒反転 +・圏点・傍点の色指定 +・区切り線 +・打ち消し線 +・傍線 +・囲み罫 +・罫線 +・罫線色 +・行頭インデント +・突き出しインデント(ぶら下がりインデント) +・字下げ・字上げ指定 +・外側の余白(マージン)指定 +・内側の余白(パディング)指定 +・高さ +・高さの最大値 +・幅 +・幅の最大値 +・最大サイズ +・禁則処理のルール +・自動改行のルール +・長い単語の改行ルール +・【参考】回り込み + +【更新履歴】 +2014/11/01 ver.1.3.1 +・「行揃え」の「-epub(webkit)-text-align-last」を削除 +・「縦中横」を修正 +・「文字の向き」の「.upright-1」を修正 +・「文字の向き」の「.sideways」を修正 + +2012/12/07 ver.1.3.0 +・ファイル更新時の電書協EPUB 3 制作ガイドバージョン表記を追加 + +2012/10/29 ver.1.2b1 +・「傍線」を修正 + +2012/10/03 ver.1.1b1 +・「行揃え」の位置揃え用の空白数を調整 +・「文字の向き」の「.sideways」を修正 +・「傍線」を修正 + +2012/08/21 ver.1.0b1 +・公開版 +---------------------------------------------------------------- */ + + + +/* 組み方向指定 +---------------------------------------------------------------- */ +/* 横組み用 */ +html, +.hltr { + -webkit-writing-mode: horizontal-tb; + -epub-writing-mode: horizontal-tb; +} +/* 縦組み用 */ +.vrtl { + -webkit-writing-mode: vertical-rl; + -epub-writing-mode: vertical-rl; +} +/* +.vltr { + -webkit-writing-mode: vertical-lr; + -epub-writing-mode: vertical-lr; +} +*/ + + +/* html指定 +---------------------------------------------------------------- +デフォルトフォントのみ指定 +---------------------------------------------------------------- */ +html { + font-family: serif-ja, serif; +} + + +/* ボックスの種類 +---------------------------------------------------------------- */ +.display-none { + display: none; +} +.display-inline { + display: inline; +} +.display-inline-block { + display: inline-block; +} +.display-block { + display: block; +} + + +/* 改ページ指定 +---------------------------------------------------------------- +端末の処理能力等に配慮する意味もあり、 +改ページは、原則としてファイルを替えることで実現する + +ただし、ページ全体のスタイル指定の変更が不要で、 +かつページ数が少ない場合は、ファイル数の増加を防ぐため、 +下記の指定を利用する可能性もある(短い随筆やコラム的な文章の連続など) +---------------------------------------------------------------- */ +/* 指定したブロックの直後で改ページ */ +.pagebreak { + page-break-after: always; +} +/* 指定したブロックの直前で改ページ */ +.pagebreak-before { + page-break-before: always; +} +/* 指定したブロックの前後で改ページ */ +.pagebreak-both { + page-break-before: always; + page-break-after: always; +} + + +/* 行揃え +---------------------------------------------------------------- +本文は原則として「text-align: justify;」 +ただし、両端が揃うのが好ましくない場合は手動で以下の「align-start」を利用 +行末まで均等揃えは今回含めない +---------------------------------------------------------------- */ +/* 行頭揃え */ +.align-left, +.align-start { + text-align: left; +} +/* 行中揃え */ +.align-center { + text-align: center; +} +/* 行末揃え */ +.align-right, +.align-end { + text-align: right; +} +/* 両端揃え(行末は行頭揃え) */ +.align-justify { + text-align: justify; +} + + +/* ブロック要素の位置揃え +---------------------------------------------------------------- */ +/* 絶対方向(横組みでは左右、縦組みでは上下方向のみ利用可) */ +.block-align-left { margin: 0 auto 0 0; } +.block-align-center { margin: 0 auto; } +.block-align-right { margin: 0 0 0 auto; } +.block-align-top { margin: 0 0 auto 0; } +.block-align-middle { margin: auto 0; } +.block-align-bottom { margin: auto 0 0 0; } + +/* 論理方向(行頭-行中-行末) */ +/* 横組み用 */ +.hltr .block-align-start { margin: 0 auto 0 0; } +.hltr .block-align-center { margin: 0 auto; } +.hltr .block-align-end { margin: 0 0 0 auto; } + +/* 縦組み用 */ +.vrtl .block-align-start { margin: 0 0 auto 0; } +.vrtl .block-align-center { margin: auto 0; } +.vrtl .block-align-end { margin: auto 0 0 0; } + + +/* インライン要素の位置揃え +---------------------------------------------------------------- */ +.valign-inherit { vertical-align: inherit; } +.valign-baseline { vertical-align: baseline; } +.valign-sub { vertical-align: sub; } +.valign-super { vertical-align: super; } +.valign-top { vertical-align: top; } +.valign-text-top { vertical-align: text-top; } +.valign-middle { vertical-align: middle; } +.valign-bottom { vertical-align: bottom; } +.valign-text-bottom { vertical-align: text-bottom; } + + +/* リンク指定 +---------------------------------------------------------------- */ +/* 基本設定 */ +/* 横組み:下線 縦組み:右線 */ +.hltr a { + text-decoration: underline; +} +.vrtl a { + text-decoration: overline; +} +/* リンク文字色(デフォルトは青) */ +a:link, +a:visited, +a:hover, +a:focus, +a:active { + color: #0000ff; +} + + +/* 注釈 +---------------------------------------------------------------- */ +/* 注釈記号の文字サイズ */ +.key, +.ref { + font-size: smaller; + vertical-align: super; +} + + +/* 外字画像 +---------------------------------------------------------------- */ +img.gaiji, +img.gaiji-line, +img.gaiji-wide { + display: inline-block; + margin: 0; + padding: 0; + border: none; + background: transparent; +} +img.gaiji { + width: 1em; + height: 1em; +} +img.gaiji-line { + width: 1em; + height: auto; +} +img.gaiji-wide { + width: auto; + height: 1em; +} +/* 外字画像のベースライン */ +.hltr img.gaiji, +.hltr img.gaiji-line, +.hltr img.gaiji-wide { + vertical-align: text-bottom; +} +.vrtl img.gaiji, +.vrtl img.gaiji-line, +.vrtl img.gaiji-wide { + vertical-align: baseline; +} + + +/* 画像のページフィット指定 +---------------------------------------------------------------- +「img.fit」を用いること +サイズ指定上書きの都合上、CSS ファイル上では img.fit と記述しない + +

+---------------------------------------------------------------- */ +.fit { + display: inline-block; + page-break-inside: avoid; + max-height: 100%; + max-width: 100%; +} + +/* 画像のベースライン */ +.hltr .fit { + vertical-align: top; +} +.vrtl .fit { + vertical-align: baseline; +} + + +/* 縦中横 +---------------------------------------------------------------- */ +.tcy { + -webkit-text-combine: horizontal; + -webkit-text-combine-upright: all; + text-combine-upright: all; + -epub-text-combine: horizontal; +} + + +/* 文字の向き +---------------------------------------------------------------- +【WebKit対策】半角1文字の直立はセンターが揃わないので縦中横を利用 +---------------------------------------------------------------- */ +.upright-1 { + -webkit-text-combine: horizontal; + -webkit-text-combine-upright: all; + text-combine-upright: all; + -epub-text-combine: horizontal; +} +.upright { + -webkit-text-orientation: upright; + -epub-text-orientation: upright; +} +.sideways { + -webkit-text-orientation: sideways; + -epub-text-orientation: sideways; +} + + +/* 上付文字・下付文字 +---------------------------------------------------------------- */ +/* 上付文字 */ +.super { + font-size: smaller; + vertical-align: super; +} +/* 下付文字 */ +.sub { + font-size: smaller; + vertical-align: sub; +} + + +/* 小書き文字 +---------------------------------------------------------------- +通常の文字を「ぁゃっ」のような小書き文字に見せるための指定 +---------------------------------------------------------------- */ +.kogaki { + font-size: 0.75em; +} +/* 【横組み】左下 */ +.hltr .kogaki { + padding: 0 0.15em 0 0.1em; + vertical-align: baseline; +} +/* 【縦組み】右上 */ +.vrtl .kogaki { + padding: 0.1em 0 0.15em 0; + vertical-align: super; +} + + +/* 訓点(返り点) +---------------------------------------------------------------- +縦組み時、上付・下付文字の上下端のスペースは調整しない +必要があれば、class を上書きすること +---------------------------------------------------------------- */ +/* 記号(縦組みでは左上付) */ +.kunten { + vertical-align: sub; + font-size: 0.67em; +} +/* 送り仮名(縦組みでは右上付) */ +.kunten-okuri { + vertical-align: super; + font-size: 0.67em; +} + + +/* 圏点・傍点 +---------------------------------------------------------------- */ +.em-sesame { + -webkit-text-emphasis-style: filled sesame; + -epub-text-emphasis-style: filled sesame; +} +.em-sesame-open { + -webkit-text-emphasis-style: open sesame; + -epub-text-emphasis-style: open sesame; +} +.em-dot { + -webkit-text-emphasis-style: filled dot; + -epub-text-emphasis-style: filled dot; +} +.em-dot-open { + -webkit-text-emphasis-style: open dot; + -epub-text-emphasis-style: open dot; +} +.em-circle { + -webkit-text-emphasis-style: filled circle; + -epub-text-emphasis-style: filled circle; +} +.em-circle-open { + -webkit-text-emphasis-style: open circle; + -epub-text-emphasis-style: open circle; +} +.em-double-circle { + -webkit-text-emphasis-style: filled double-circle; + -epub-text-emphasis-style: filled double-circle; +} +.em-double-circle-open { + -webkit-text-emphasis-style: open double-circle; + -epub-text-emphasis-style: open double-circle; +} +.em-triangle { + -webkit-text-emphasis-style: filled triangle; + -epub-text-emphasis-style: filled triangle; +} +.em-triangle-open { + -webkit-text-emphasis-style: open triangle; + -epub-text-emphasis-style: open triangle; +} + + +/* 行高 +---------------------------------------------------------------- */ +.line-height-normal { line-height: normal; } +.line-height-1em { line-height: 1.00; } +.line-height-1em50 { line-height: 1.50; } +.line-height-1em75 { line-height: 1.75; } +.line-height-2em { line-height: 2.00; } +.line-height-2em50 { line-height: 2.50; } +.line-height-3em { line-height: 3.00; } +.line-height-3em50 { line-height: 3.50; } +.line-height-4em { line-height: 4.00; } +.line-height-4em50 { line-height: 4.50; } +.line-height-5em { line-height: 5.00; } + + +/* 文字の間隔 +---------------------------------------------------------------- +本文中では四分アキ[25%]刻み以外はなるべく使わない方向で +---------------------------------------------------------------- */ +/* 標準 */ +.lspacing-normal { letter-spacing: normal; } + +/* クリア */ +.lspacing-0, +.lspacing-0em { letter-spacing: 0; } + +/* 文字数指定 */ +.lspacing-0em10 { letter-spacing: 0.10em; } +.lspacing-0em20 { letter-spacing: 0.20em; } +.lspacing-0em25 { letter-spacing: 0.25em; } +.lspacing-0em30 { letter-spacing: 0.30em; } +.lspacing-0em33 { letter-spacing: 0.33em; } +.lspacing-0em40 { letter-spacing: 0.40em; } +.lspacing-0em50 { letter-spacing: 0.50em; } +.lspacing-0em60 { letter-spacing: 0.60em; } +.lspacing-0em67 { letter-spacing: 0.67em; } +.lspacing-0em70 { letter-spacing: 0.70em; } +.lspacing-0em75 { letter-spacing: 0.75em; } +.lspacing-0em80 { letter-spacing: 0.80em; } +.lspacing-0em90 { letter-spacing: 0.90em; } +.lspacing-1em { letter-spacing: 1.00em; } +.lspacing-1em25 { letter-spacing: 1.25em; } +.lspacing-1em50 { letter-spacing: 1.50em; } +.lspacing-1em75 { letter-spacing: 1.75em; } +.lspacing-2em { letter-spacing: 2.00em; } +.lspacing-2em25 { letter-spacing: 2.25em; } +.lspacing-2em50 { letter-spacing: 2.50em; } +.lspacing-2em75 { letter-spacing: 2.75em; } +.lspacing-3em { letter-spacing: 3.00em; } +.lspacing-3em25 { letter-spacing: 3.25em; } +.lspacing-3em50 { letter-spacing: 3.50em; } +.lspacing-3em75 { letter-spacing: 3.75em; } +.lspacing-4em { letter-spacing: 4.00em; } +.lspacing-4em25 { letter-spacing: 4.25em; } +.lspacing-4em50 { letter-spacing: 4.50em; } +.lspacing-4em75 { letter-spacing: 4.75em; } +.lspacing-5em { letter-spacing: 5.00em; } + + +/* フォント指定 +---------------------------------------------------------------- */ +/* 明朝 */ +.hltr .mfont, +.vrtl .mfont { + font-family: serif-ja, serif; +} + +/* ゴシック */ +.hltr .gfont, +.vrtl .gfont { + font-family: sans-serif-ja, sans-serif; +} + +/* フォントサイズ(%指定) */ +.font-050per { font-size: 50%; } +.font-060per { font-size: 60%; } +.font-070per { font-size: 70%; } +.font-075per { font-size: 75%; } +.font-080per { font-size: 80%; } +.font-085per { font-size: 85%; } +.font-090per { font-size: 90%; } +.font-100per { font-size: 100%; } +.font-110per { font-size: 110%; } +.font-115per { font-size: 115%; } +.font-120per { font-size: 120%; } +.font-130per { font-size: 130%; } +.font-140per { font-size: 140%; } +.font-150per { font-size: 150%; } +.font-160per { font-size: 160%; } +.font-170per { font-size: 170%; } +.font-180per { font-size: 180%; } +.font-190per { font-size: 190%; } +.font-200per { font-size: 200%; } +.font-250per { font-size: 250%; } +.font-300per { font-size: 300%; } + + +/* フォントサイズ(文字数指定) */ +.font-0em50 { font-size: 0.50em; } +.font-0em60 { font-size: 0.60em; } +.font-0em70 { font-size: 0.70em; } +.font-0em75 { font-size: 0.75em; } +.font-0em80 { font-size: 0.80em; } +.font-0em85 { font-size: 0.85em; } +.font-0em90 { font-size: 0.90em; } +.font-1em { font-size: 1.00em; } +.font-1em10 { font-size: 1.10em; } +.font-1em15 { font-size: 1.15em; } +.font-1em20 { font-size: 1.20em; } +.font-1em30 { font-size: 1.30em; } +.font-1em40 { font-size: 1.40em; } +.font-1em50 { font-size: 1.50em; } +.font-1em60 { font-size: 1.60em; } +.font-1em70 { font-size: 1.70em; } +.font-1em80 { font-size: 1.80em; } +.font-1em90 { font-size: 1.90em; } +.font-2em { font-size: 2.00em; } +.font-2em50 { font-size: 2.50em; } +.font-3em { font-size: 3.00em; } + + +/* 太字 +---------------------------------------------------------------- */ +/* 太字 */ +.bold { + font-weight: bold; +} +/* 太字解除 */ +.font-weight-normal { + font-weight: normal; +} + + +/* 斜体 +---------------------------------------------------------------- */ +/* 斜体 */ +.italic { + font-style: italic; +} +/* 斜体解除 */ +.font-style-normal { + font-style: normal; +} + + +/* 文字色 +---------------------------------------------------------------- */ +/* 1C用文字色 */ +.color-black { color: #000000; } +.color-dimgray { color: #696969; } +.color-gray { color: #808080; } +.color-darkgray { color: #a9a9a9; } +.color-silver { color: #c0c0c0; } +.color-gainsboro { color: #dcdcdc; } +.color-white { color: #ffffff; } +.color-transparent { color: transparent; } + +/* 基本色 */ +.color-red { color: #ff0000; } +.color-blue { color: #0000ff; } +.color-cyan { color: #00ffff; } +.color-magenta { color: #ff00ff; } +.color-orangered { color: #ff4500; } + + +/* 背景色 +---------------------------------------------------------------- */ +/* 1C用背景色 */ +.bg-black { background-color: #000000; } +.bg-dimgray { background-color: #696969; } +.bg-gray { background-color: #808080; } +.bg-darkgray { background-color: #a9a9a9; } +.bg-silver { background-color: #c0c0c0; } +.bg-gainsboro { background-color: #dcdcdc; } +.bg-white { background-color: #ffffff; } +.bg-transparent { background-color: transparent; } + +/* 基本色 */ +.bg-red { background-color: #ff0000; } +.bg-blue { background-color: #0000ff; } +.bg-cyan { background-color: #00ffff; } +.bg-magenta { background-color: #ff00ff; } +.bg-orangered { background-color: #ff4500; } + + +/* 文字色の白黒反転 +---------------------------------------------------------------- */ +.inverse { + color: #ffffff; + background: #000000; +} + + +/* 圏点・傍点の色指定 +---------------------------------------------------------------- +インラインで .inverse を用いたとき、行間に表示される傍点色が白になり、 +白背景では傍点が見えなくなってしまうことへの対処用 +---------------------------------------------------------------- */ +.em-black { + -webkit-text-emphasis-color: #000000; + -epub-text-emphasis-color: #000000; +} + + +/* 区切り線 +---------------------------------------------------------------- */ +hr { + border-width: 1px; + border-color: #000000; +} +/* 【横組み】水平線 */ +.hltr hr { + margin: 0.5em 0; + border-style: solid none none none; +} +/* 【縦組み】垂直線 */ +.vrtl hr { + margin: 0 0.5em; + border-style: none solid none none; +} + + +/* 打ち消し線 +---------------------------------------------------------------- */ +.line-through { + text-decoration: line-through; +} + + +/* 傍線 +---------------------------------------------------------------- */ +/* 【横組み】下線 【縦組み】右線 */ +.hltr .em-line { + text-decoration: underline; +} +.vrtl .em-line { + text-decoration: overline; +} + +/* 【横組み】上線 【縦組み】左線 */ +.hltr .em-line-outside { + text-decoration: overline; +} +.vrtl .em-line-outside { + text-decoration: underline; +} + + +/* 囲み罫 +---------------------------------------------------------------- +線幅の指定には、罫線と同じものを使用 +---------------------------------------------------------------- */ +/* 上から実線、点線、二重線、破線 */ +.k-solid { border-style: solid solid solid solid; border-width: 1px; border-color: #000000; } +.k-dotted { border-style: dotted dotted dotted dotted; border-width: 2px; border-color: #000000; } +.k-double { border-style: double double double double; border-width: 4px; border-color: #000000; } +.k-dashed { border-style: dashed dashed dashed dashed; border-width: 1px; border-color: #000000; } + +/* 線色付き囲み罫(画像枠などに利用) */ +.k-solid-black { border-style: solid solid solid solid; border-width: 1px; border-color: #000000; } +.k-solid-gray { border-style: solid solid solid solid; border-width: 1px; border-color: #808080; } +.k-solid-silver { border-style: solid solid solid solid; border-width: 1px; border-color: #c0c0c0; } +.k-solid-white { border-style: solid solid solid solid; border-width: 1px; border-color: #ffffff; } + + +/* 罫線 +---------------------------------------------------------------- +線種や線幅など、細かな調整が必要なときは、 +無理に既存のクラスを用いず新たにクラスを作成すること +---------------------------------------------------------------- */ +/* 線種【実線】 */ +.k-solid-top, +.k-solid-right, +.k-solid-bottom, +.k-solid-left, +.k-solid-topbottom, +.k-solid-rightleft { + border-width: 1px; + border-color: #000000; +} +/* 線位置【実線】 */ +.k-solid-top { border-style: solid none none none; } +.k-solid-right { border-style: none solid none none; } +.k-solid-bottom { border-style: none none solid none; } +.k-solid-left { border-style: none none none solid; } +.k-solid-topbottom { border-style: solid none solid none; } +.k-solid-rightleft { border-style: none solid none solid; } + +/* 線種【点線】 */ +.k-dotted-top, +.k-dotted-right, +.k-dotted-bottom, +.k-dotted-left, +.k-dotted-topbottom, +.k-dotted-rightleft { + border-width: 2px; + border-color: #000000; +} +/* 線位置【点線】 */ +.k-dotted-top { border-style: dotted none none none; } +.k-dotted-right { border-style: none dotted none none; } +.k-dotted-bottom { border-style: none none dotted none; } +.k-dotted-left { border-style: none none none dotted; } +.k-dotted-topbottom { border-style: dotted none dotted none; } +.k-dotted-rightleft { border-style: none dotted none dotted; } + +/* 線種【二重線】 */ +.k-double-top, +.k-double-right, +.k-double-bottom, +.k-double-left, +.k-double-topbottom, +.k-double-rightleft { + border-width: 4px; + border-color: #000000; +} +/* 線位置【二重線】 */ +.k-double-top { border-style: double none none none; } +.k-double-right { border-style: none double none none; } +.k-double-bottom { border-style: none none double none; } +.k-double-left { border-style: none none none double; } +.k-double-topbottom { border-style: double none double none; } +.k-double-rightleft { border-style: none double none double; } + +/* 線種【破線】 */ +.k-dashed-top, +.k-dashed-right, +.k-dashed-bottom, +.k-dashed-left, +.k-dashed-topbottom, +.k-dashed-rightleft { + border-width: 1px; + border-color: #000000; +} +/* 線位置【破線】 */ +.k-dashed-top { border-style: dashed none none none; } +.k-dashed-right { border-style: none dashed none none; } +.k-dashed-bottom { border-style: none none dashed none; } +.k-dashed-left { border-style: none none none dashed; } +.k-dashed-topbottom { border-style: dashed none dashed none; } +.k-dashed-rightleft { border-style: none dashed none dashed; } + +/* 線幅 */ +.k-0px { border-width: 0; } +.k-1px { border-width: 1px; } +.k-2px { border-width: 2px; } +.k-3px { border-width: 3px; } +.k-4px { border-width: 4px; } +.k-5px { border-width: 5px; } +.k-6px { border-width: 6px; } +.k-7px { border-width: 7px; } +.k-8px { border-width: 8px; } +.k-thin { border-width: thin; } +.k-medium { border-width: medium; } +.k-thick { border-width: thick; } + +/* 1C用の線色 */ +.k-black { border-color: #000000; } +.k-dimgray { border-color: #696969; } +.k-gray { border-color: #808080; } +.k-darkgray { border-color: #a9a9a9; } +.k-silver { border-color: #c0c0c0; } +.k-gainsboro { border-color: #dcdcdc; } +.k-white { border-color: #ffffff; } + +/* 基本色 */ +.k-red { border-color: #ff0000; } +.k-blue { border-color: #0000ff; } +.k-cyan { border-color: #00ffff; } +.k-magenta { border-color: #ff00ff; } +.k-orangered { border-color: #ff4500; } + + +/* 行頭インデント +---------------------------------------------------------------- */ +.indent-0, +.indent-0em { text-indent: 0; } +.indent-1em { text-indent: 1em; } +.indent-2em { text-indent: 2em; } +.indent-3em { text-indent: 3em; } +.indent-4em { text-indent: 4em; } +.indent-5em { text-indent: 5em; } +.indent-6em { text-indent: 6em; } +.indent-7em { text-indent: 7em; } +.indent-8em { text-indent: 8em; } +.indent-9em { text-indent: 9em; } +.indent-10em { text-indent: 10em; } + + +/* 突き出しインデント(ぶら下がりインデント) +---------------------------------------------------------------- +「h-」は「hanging」の略 +---------------------------------------------------------------- */ +/* 横組み用 */ +.hltr .h-indent-0, +.hltr .h-indent-0em { text-indent: 0; padding-left: 0; } +.hltr .h-indent-1em { text-indent: -1em; padding-left: 1em; } +.hltr .h-indent-2em { text-indent: -2em; padding-left: 2em; } +.hltr .h-indent-3em { text-indent: -3em; padding-left: 3em; } +.hltr .h-indent-4em { text-indent: -4em; padding-left: 4em; } +.hltr .h-indent-5em { text-indent: -5em; padding-left: 5em; } +.hltr .h-indent-6em { text-indent: -6em; padding-left: 6em; } +.hltr .h-indent-7em { text-indent: -7em; padding-left: 7em; } +.hltr .h-indent-8em { text-indent: -8em; padding-left: 8em; } +.hltr .h-indent-9em { text-indent: -9em; padding-left: 9em; } +.hltr .h-indent-10em { text-indent: -10em; padding-left: 10em; } + +/* 縦組み用 */ +.vrtl .h-indent-0, +.vrtl .h-indent-0em { text-indent: 0; padding-top: 0; } +.vrtl .h-indent-1em { text-indent: -1em; padding-top: 1em; } +.vrtl .h-indent-2em { text-indent: -2em; padding-top: 2em; } +.vrtl .h-indent-3em { text-indent: -3em; padding-top: 3em; } +.vrtl .h-indent-4em { text-indent: -4em; padding-top: 4em; } +.vrtl .h-indent-5em { text-indent: -5em; padding-top: 5em; } +.vrtl .h-indent-6em { text-indent: -6em; padding-top: 6em; } +.vrtl .h-indent-7em { text-indent: -7em; padding-top: 7em; } +.vrtl .h-indent-8em { text-indent: -8em; padding-top: 8em; } +.vrtl .h-indent-9em { text-indent: -9em; padding-top: 9em; } +.vrtl .h-indent-10em { text-indent: -10em; padding-top: 10em; } + + +/* 字下げ・字上げ指定 +---------------------------------------------------------------- */ +/* 字下げ:横組み用 */ +.hltr .start-0, +.hltr .start-0em { margin-left: 0; } +.hltr .start-0em25 { margin-left: 0.25em; } +.hltr .start-0em50 { margin-left: 0.50em; } +.hltr .start-0em75 { margin-left: 0.75em; } +.hltr .start-1em { margin-left: 1.00em; } +.hltr .start-1em25 { margin-left: 1.25em; } +.hltr .start-1em50 { margin-left: 1.50em; } +.hltr .start-1em75 { margin-left: 1.75em; } +.hltr .start-2em { margin-left: 2.00em; } +.hltr .start-2em50 { margin-left: 2.50em; } +.hltr .start-3em { margin-left: 3.00em; } +.hltr .start-4em { margin-left: 4.00em; } +.hltr .start-5em { margin-left: 5.00em; } +.hltr .start-6em { margin-left: 6.00em; } +.hltr .start-7em { margin-left: 7.00em; } +.hltr .start-8em { margin-left: 8.00em; } +.hltr .start-9em { margin-left: 9.00em; } +.hltr .start-10em { margin-left: 10.00em; } + +/* 字下げ:縦組み用 */ +.vrtl .start-0, +.vrtl .start-0em { margin-top: 0; } +.vrtl .start-0em25 { margin-top: 0.25em; } +.vrtl .start-0em50 { margin-top: 0.50em; } +.vrtl .start-0em75 { margin-top: 0.75em; } +.vrtl .start-1em { margin-top: 1.00em; } +.vrtl .start-1em25 { margin-top: 1.25em; } +.vrtl .start-1em50 { margin-top: 1.50em; } +.vrtl .start-1em75 { margin-top: 1.75em; } +.vrtl .start-2em { margin-top: 2.00em; } +.vrtl .start-2em50 { margin-top: 2.50em; } +.vrtl .start-3em { margin-top: 3.00em; } +.vrtl .start-4em { margin-top: 4.00em; } +.vrtl .start-5em { margin-top: 5.00em; } +.vrtl .start-6em { margin-top: 6.00em; } +.vrtl .start-7em { margin-top: 7.00em; } +.vrtl .start-8em { margin-top: 8.00em; } +.vrtl .start-9em { margin-top: 9.00em; } +.vrtl .start-10em { margin-top: 10.00em; } + +/* 字上げ:横組み用 */ +.hltr .end-0, +.hltr .end-0em { margin-right: 0; } +.hltr .end-0em25 { margin-right: 0.25em; } +.hltr .end-0em50 { margin-right: 0.50em; } +.hltr .end-0em75 { margin-right: 0.75em; } +.hltr .end-1em { margin-right: 1.00em; } +.hltr .end-1em25 { margin-right: 1.25em; } +.hltr .end-1em50 { margin-right: 1.50em; } +.hltr .end-1em75 { margin-right: 1.75em; } +.hltr .end-2em { margin-right: 2.00em; } +.hltr .end-2em50 { margin-right: 2.50em; } +.hltr .end-3em { margin-right: 3.00em; } +.hltr .end-4em { margin-right: 4.00em; } +.hltr .end-5em { margin-right: 5.00em; } +.hltr .end-6em { margin-right: 6.00em; } +.hltr .end-7em { margin-right: 7.00em; } +.hltr .end-8em { margin-right: 8.00em; } +.hltr .end-9em { margin-right: 9.00em; } +.hltr .end-10em { margin-right: 10.00em; } + +/* 字上げ:縦組み用 */ +.vrtl .end-0, +.vrtl .end-0em { margin-bottom: 0; } +.vrtl .end-0em25 { margin-bottom: 0.25em; } +.vrtl .end-0em50 { margin-bottom: 0.50em; } +.vrtl .end-0em75 { margin-bottom: 0.75em; } +.vrtl .end-1em { margin-bottom: 1.00em; } +.vrtl .end-1em25 { margin-bottom: 1.25em; } +.vrtl .end-1em50 { margin-bottom: 1.50em; } +.vrtl .end-1em75 { margin-bottom: 1.75em; } +.vrtl .end-2em { margin-bottom: 2.00em; } +.vrtl .end-2em50 { margin-bottom: 2.50em; } +.vrtl .end-3em { margin-bottom: 3.00em; } +.vrtl .end-4em { margin-bottom: 4.00em; } +.vrtl .end-5em { margin-bottom: 5.00em; } +.vrtl .end-6em { margin-bottom: 6.00em; } +.vrtl .end-7em { margin-bottom: 7.00em; } +.vrtl .end-8em { margin-bottom: 8.00em; } +.vrtl .end-9em { margin-bottom: 9.00em; } +.vrtl .end-10em { margin-bottom: 10.00em; } + + +/* 外側の余白(マージン)指定 +---------------------------------------------------------------- +字下げ・字上げと同じ要素で同時には使えないので注意 +【NG例】
+     →字下げを内側にして
の入れ子とする +---------------------------------------------------------------- */ +/* 四方 */ +.m-auto { margin: auto; } +.m-0, +.m-0em, +.m-000per { margin: 0; } + +/* %指定 */ +.m-005per { margin: 5%; } +.m-010per { margin: 10%; } +.m-015per { margin: 15%; } +.m-020per { margin: 20%; } +.m-025per { margin: 25%; } +.m-030per { margin: 30%; } +.m-033per { margin: 33%; } +.m-040per { margin: 40%; } +.m-050per { margin: 50%; } +.m-060per { margin: 60%; } +.m-067per { margin: 67%; } +.m-070per { margin: 70%; } +.m-075per { margin: 75%; } +.m-080per { margin: 80%; } +.m-090per { margin: 90%; } + +/* 文字数指定 */ +.m-0em10 { margin: 0.10em; } +.m-0em20 { margin: 0.20em; } +.m-0em25 { margin: 0.25em; } +.m-0em30 { margin: 0.30em; } +.m-0em40 { margin: 0.40em; } +.m-0em50 { margin: 0.50em; } +.m-0em60 { margin: 0.60em; } +.m-0em70 { margin: 0.70em; } +.m-0em75 { margin: 0.75em; } +.m-0em80 { margin: 0.80em; } +.m-0em90 { margin: 0.90em; } +.m-1em { margin: 1.00em; } +.m-1em25 { margin: 1.25em; } +.m-1em50 { margin: 1.50em; } +.m-1em75 { margin: 1.75em; } +.m-2em { margin: 2.00em; } +.m-2em50 { margin: 2.50em; } +.m-3em { margin: 3.00em; } +.m-4em { margin: 4.00em; } +.m-5em { margin: 5.00em; } + + +/* 画面上側(縦組み:行頭/横組み:行前方) */ +.m-top-auto { margin-top: auto; } +.m-top-0, +.m-top-0em, +.m-top-000per { margin-top: 0; } + +/* %指定 */ +.m-top-005per { margin-top: 5%; } +.m-top-010per { margin-top: 10%; } +.m-top-015per { margin-top: 15%; } +.m-top-020per { margin-top: 20%; } +.m-top-025per { margin-top: 25%; } +.m-top-030per { margin-top: 30%; } +.m-top-033per { margin-top: 33%; } +.m-top-040per { margin-top: 40%; } +.m-top-050per { margin-top: 50%; } +.m-top-060per { margin-top: 60%; } +.m-top-067per { margin-top: 67%; } +.m-top-070per { margin-top: 70%; } +.m-top-075per { margin-top: 75%; } +.m-top-080per { margin-top: 80%; } +.m-top-090per { margin-top: 90%; } + +/* 文字数指定 */ +.m-top-0em25 { margin-top: 0.25em; } +.m-top-0em50 { margin-top: 0.50em; } +.m-top-0em75 { margin-top: 0.75em; } +.m-top-1em { margin-top: 1.00em; } +.m-top-1em25 { margin-top: 1.25em; } +.m-top-1em50 { margin-top: 1.50em; } +.m-top-1em75 { margin-top: 1.75em; } +.m-top-2em { margin-top: 2.00em; } +.m-top-2em50 { margin-top: 2.50em; } +.m-top-3em { margin-top: 3.00em; } +.m-top-4em { margin-top: 4.00em; } +.m-top-5em { margin-top: 5.00em; } +.m-top-5em25 { margin-top: 5.25em; } + + +/* 画面左側(縦組み:行後方/横組み:行頭) */ +.m-left-auto { margin-left: auto; } +.m-left-0, +.m-left-0em, +.m-left-000per { margin-left: 0; } + +/* %指定 */ +.m-left-005per { margin-left: 5%; } +.m-left-010per { margin-left: 10%; } +.m-left-015per { margin-left: 15%; } +.m-left-020per { margin-left: 20%; } +.m-left-025per { margin-left: 25%; } +.m-left-030per { margin-left: 30%; } +.m-left-033per { margin-left: 33%; } +.m-left-040per { margin-left: 40%; } +.m-left-050per { margin-left: 50%; } +.m-left-060per { margin-left: 60%; } +.m-left-067per { margin-left: 67%; } +.m-left-070per { margin-left: 70%; } +.m-left-075per { margin-left: 75%; } +.m-left-080per { margin-left: 80%; } +.m-left-090per { margin-left: 90%; } + +/* 文字数指定 */ +.m-left-0em25 { margin-left: 0.25em; } +.m-left-0em50 { margin-left: 0.50em; } +.m-left-0em75 { margin-left: 0.75em; } +.m-left-1em { margin-left: 1.00em; } +.m-left-1em25 { margin-left: 1.25em; } +.m-left-1em50 { margin-left: 1.50em; } +.m-left-1em75 { margin-left: 1.75em; } +.m-left-2em { margin-left: 2.00em; } +.m-left-2em50 { margin-left: 2.50em; } +.m-left-3em { margin-left: 3.00em; } +.m-left-4em { margin-left: 4.00em; } +.m-left-5em { margin-left: 5.00em; } +.m-left-5em25 { margin-left: 5.25em; } + + +/* 画面右側(縦組み:行前方/横組み:行末) */ +.m-right-auto { margin-right: auto; } +.m-right-0 +.m-right-0em +.m-right-000per { margin-right: 0; } + +/* %指定 */ +.m-right-005per { margin-right: 5%; } +.m-right-010per { margin-right: 10%; } +.m-right-015per { margin-right: 15%; } +.m-right-020per { margin-right: 20%; } +.m-right-025per { margin-right: 25%; } +.m-right-030per { margin-right: 30%; } +.m-right-033per { margin-right: 33%; } +.m-right-040per { margin-right: 40%; } +.m-right-050per { margin-right: 50%; } +.m-right-060per { margin-right: 60%; } +.m-right-067per { margin-right: 67%; } +.m-right-070per { margin-right: 70%; } +.m-right-075per { margin-right: 75%; } +.m-right-080per { margin-right: 80%; } +.m-right-090per { margin-right: 90%; } + +/* 文字数指定 */ +.m-right-0em25 { margin-right: 0.25em; } +.m-right-0em50 { margin-right: 0.50em; } +.m-right-0em75 { margin-right: 0.75em; } +.m-right-1em { margin-right: 1.00em; } +.m-right-1em25 { margin-right: 1.25em; } +.m-right-1em50 { margin-right: 1.50em; } +.m-right-1em75 { margin-right: 1.75em; } +.m-right-2em { margin-right: 2.00em; } +.m-right-2em50 { margin-right: 2.50em; } +.m-right-3em { margin-right: 3.00em; } +.m-right-4em { margin-right: 4.00em; } +.m-right-5em { margin-right: 5.00em; } +.m-right-5em25 { margin-right: 5.25em; } + + +/* 画面下側(縦組み:行末/横組み:行後方) */ +.m-bottom-auto { margin-bottom: auto; } +.m-bottom-0, +.m-bottom-0em, +.m-bottom-000per { margin-bottom: 0; } + +/* %指定 */ +.m-bottom-005per { margin-bottom: 5%; } +.m-bottom-010per { margin-bottom: 10%; } +.m-bottom-015per { margin-bottom: 15%; } +.m-bottom-020per { margin-bottom: 20%; } +.m-bottom-025per { margin-bottom: 25%; } +.m-bottom-030per { margin-bottom: 30%; } +.m-bottom-033per { margin-bottom: 33%; } +.m-bottom-040per { margin-bottom: 40%; } +.m-bottom-050per { margin-bottom: 50%; } +.m-bottom-060per { margin-bottom: 60%; } +.m-bottom-067per { margin-bottom: 67%; } +.m-bottom-070per { margin-bottom: 70%; } +.m-bottom-075per { margin-bottom: 75%; } +.m-bottom-080per { margin-bottom: 80%; } +.m-bottom-090per { margin-bottom: 90%; } + +/* 文字数指定 */ +.m-bottom-0em25 { margin-bottom: 0.25em; } +.m-bottom-0em50 { margin-bottom: 0.50em; } +.m-bottom-0em75 { margin-bottom: 0.75em; } +.m-bottom-1em { margin-bottom: 1.00em; } +.m-bottom-1em25 { margin-bottom: 1.25em; } +.m-bottom-1em50 { margin-bottom: 1.50em; } +.m-bottom-1em75 { margin-bottom: 1.75em; } +.m-bottom-2em { margin-bottom: 2.00em; } +.m-bottom-2em50 { margin-bottom: 2.50em; } +.m-bottom-3em { margin-bottom: 3.00em; } +.m-bottom-4em { margin-bottom: 4.00em; } +.m-bottom-5em { margin-bottom: 5.00em; } +.m-bottom-5em25 { margin-bottom: 5.25em; } + + +/* 内側の余白(パディング)指定 +---------------------------------------------------------------- */ +/* 四方 */ +.p-0, +.p-0em, +.p-000per { padding: 0; } + +/* %指定 */ +.p-005per { padding: 5%; } +.p-010per { padding: 10%; } +.p-015per { padding: 15%; } +.p-020per { padding: 20%; } +.p-025per { padding: 25%; } +.p-030per { padding: 30%; } +.p-033per { padding: 33%; } +.p-040per { padding: 40%; } +.p-050per { padding: 50%; } +.p-060per { padding: 60%; } +.p-067per { padding: 67%; } +.p-070per { padding: 70%; } +.p-075per { padding: 75%; } +.p-080per { padding: 80%; } +.p-090per { padding: 90%; } + +/* 文字数指定 */ +.p-0em10 { padding: 0.10em; } +.p-0em20 { padding: 0.20em; } +.p-0em25 { padding: 0.25em; } +.p-0em30 { padding: 0.30em; } +.p-0em40 { padding: 0.40em; } +.p-0em50 { padding: 0.50em; } +.p-0em60 { padding: 0.60em; } +.p-0em70 { padding: 0.70em; } +.p-0em75 { padding: 0.75em; } +.p-0em80 { padding: 0.80em; } +.p-0em90 { padding: 0.90em; } +.p-1em { padding: 1.00em; } +.p-1em25 { padding: 1.25em; } +.p-1em50 { padding: 1.50em; } +.p-1em75 { padding: 1.75em; } +.p-2em { padding: 2.00em; } +.p-2em50 { padding: 2.50em; } +.p-3em { padding: 3.00em; } +.p-4em { padding: 4.00em; } +.p-5em { padding: 5.00em; } + + +/* 画面上側(縦組み:行頭/横組み:行前方) */ +.p-top-0, +.p-top-0em, +.p-top-000per { padding-top: 0; } + +/* %指定 */ +.p-top-005per { padding-top: 5%; } +.p-top-010per { padding-top: 10%; } +.p-top-015per { padding-top: 15%; } +.p-top-020per { padding-top: 20%; } +.p-top-025per { padding-top: 25%; } +.p-top-030per { padding-top: 30%; } +.p-top-033per { padding-top: 33%; } +.p-top-040per { padding-top: 40%; } +.p-top-050per { padding-top: 50%; } +.p-top-060per { padding-top: 60%; } +.p-top-067per { padding-top: 67%; } +.p-top-070per { padding-top: 70%; } +.p-top-075per { padding-top: 75%; } +.p-top-080per { padding-top: 80%; } +.p-top-090per { padding-top: 90%; } + +/* 文字数指定 */ +.p-top-0em25 { padding-top: 0.25em; } +.p-top-0em50 { padding-top: 0.50em; } +.p-top-0em75 { padding-top: 0.75em; } +.p-top-1em { padding-top: 1.00em; } +.p-top-1em25 { padding-top: 1.25em; } +.p-top-1em50 { padding-top: 1.50em; } +.p-top-1em75 { padding-top: 1.75em; } +.p-top-2em { padding-top: 2.00em; } +.p-top-2em50 { padding-top: 2.50em; } +.p-top-3em { padding-top: 3.00em; } +.p-top-4em { padding-top: 4.00em; } +.p-top-5em { padding-top: 5.00em; } +.p-top-5em25 { padding-top: 5.25em; } + + +/* 画面左側(縦組み:行後方/横組み:行頭) */ +.p-left-0, +.p-left-0em, +.p-left-000per { padding-left: 0; } + +/* %指定 */ +.p-left-005per { padding-left: 5%; } +.p-left-010per { padding-left: 10%; } +.p-left-015per { padding-left: 15%; } +.p-left-020per { padding-left: 20%; } +.p-left-025per { padding-left: 25%; } +.p-left-030per { padding-left: 30%; } +.p-left-033per { padding-left: 33%; } +.p-left-040per { padding-left: 40%; } +.p-left-050per { padding-left: 50%; } +.p-left-060per { padding-left: 60%; } +.p-left-067per { padding-left: 67%; } +.p-left-070per { padding-left: 70%; } +.p-left-075per { padding-left: 75%; } +.p-left-080per { padding-left: 80%; } +.p-left-090per { padding-left: 90%; } + +/* 文字数指定 */ +.p-left-0em25 { padding-left: 0.25em; } +.p-left-0em50 { padding-left: 0.50em; } +.p-left-0em75 { padding-left: 0.75em; } +.p-left-1em { padding-left: 1.00em; } +.p-left-1em25 { padding-left: 1.25em; } +.p-left-1em50 { padding-left: 1.50em; } +.p-left-1em75 { padding-left: 1.75em; } +.p-left-2em { padding-left: 2.00em; } +.p-left-2em50 { padding-left: 2.50em; } +.p-left-3em { padding-left: 3.00em; } +.p-left-4em { padding-left: 4.00em; } +.p-left-5em { padding-left: 5.00em; } +.p-left-5em25 { padding-left: 5.25em; } + + +/* 画面右側(縦組み:行前方/横組み:行末) */ +.p-right-0 +.p-right-0em +.p-right-000per { padding-right: 0; } + +/* %指定 */ +.p-right-005per { padding-right: 5%; } +.p-right-010per { padding-right: 10%; } +.p-right-015per { padding-right: 15%; } +.p-right-020per { padding-right: 20%; } +.p-right-025per { padding-right: 25%; } +.p-right-030per { padding-right: 30%; } +.p-right-033per { padding-right: 33%; } +.p-right-040per { padding-right: 40%; } +.p-right-050per { padding-right: 50%; } +.p-right-060per { padding-right: 60%; } +.p-right-067per { padding-right: 67%; } +.p-right-070per { padding-right: 70%; } +.p-right-075per { padding-right: 75%; } +.p-right-080per { padding-right: 80%; } +.p-right-090per { padding-right: 90%; } + +/* 文字数指定 */ +.p-right-0em25 { padding-right: 0.25em; } +.p-right-0em50 { padding-right: 0.50em; } +.p-right-0em75 { padding-right: 0.75em; } +.p-right-1em { padding-right: 1.00em; } +.p-right-1em25 { padding-right: 1.25em; } +.p-right-1em50 { padding-right: 1.50em; } +.p-right-1em75 { padding-right: 1.75em; } +.p-right-2em { padding-right: 2.00em; } +.p-right-2em50 { padding-right: 2.50em; } +.p-right-3em { padding-right: 3.00em; } +.p-right-4em { padding-right: 4.00em; } +.p-right-5em { padding-right: 5.00em; } +.p-right-5em25 { padding-right: 5.25em; } + + +/* 画面下側(縦組み:行末/横組み:行後方) */ +.p-bottom-0, +.p-bottom-0em, +.p-bottom-000per { padding-bottom: 0; } + +/* %指定 */ +.p-bottom-005per { padding-bottom: 5%; } +.p-bottom-010per { padding-bottom: 10%; } +.p-bottom-015per { padding-bottom: 15%; } +.p-bottom-020per { padding-bottom: 20%; } +.p-bottom-025per { padding-bottom: 25%; } +.p-bottom-030per { padding-bottom: 30%; } +.p-bottom-033per { padding-bottom: 33%; } +.p-bottom-040per { padding-bottom: 40%; } +.p-bottom-050per { padding-bottom: 50%; } +.p-bottom-060per { padding-bottom: 60%; } +.p-bottom-067per { padding-bottom: 67%; } +.p-bottom-070per { padding-bottom: 70%; } +.p-bottom-075per { padding-bottom: 75%; } +.p-bottom-080per { padding-bottom: 80%; } +.p-bottom-090per { padding-bottom: 90%; } + +/* 文字数指定 */ +.p-bottom-0em25 { padding-bottom: 0.25em; } +.p-bottom-0em50 { padding-bottom: 0.50em; } +.p-bottom-0em75 { padding-bottom: 0.75em; } +.p-bottom-1em { padding-bottom: 1.00em; } +.p-bottom-1em25 { padding-bottom: 1.25em; } +.p-bottom-1em50 { padding-bottom: 1.50em; } +.p-bottom-1em75 { padding-bottom: 1.75em; } +.p-bottom-2em { padding-bottom: 2.00em; } +.p-bottom-2em50 { padding-bottom: 2.50em; } +.p-bottom-3em { padding-bottom: 3.00em; } +.p-bottom-4em { padding-bottom: 4.00em; } +.p-bottom-5em { padding-bottom: 5.00em; } +.p-bottom-5em25 { padding-bottom: 5.25em; } + + +/* 高さ +---------------------------------------------------------------- */ +.height-auto { height: auto; } + +/* %指定 */ +.height-010per { height: 10%; } +.height-020per { height: 20%; } +.height-025per { height: 25%; } +.height-030per { height: 30%; } +.height-033per { height: 33%; } +.height-040per { height: 40%; } +.height-050per { height: 50%; } +.height-060per { height: 60%; } +.height-067per { height: 67%; } +.height-070per { height: 70%; } +.height-075per { height: 75%; } +.height-080per { height: 80%; } +.height-090per { height: 90%; } +.height-100per { height: 100%; } + +/* 文字数指定 */ +.height-0em25 { height: 0.25em; } +.height-0em50 { height: 0.50em; } +.height-0em75 { height: 0.75em; } +.height-1em { height: 1.00em; } +.height-1em25 { height: 1.25em; } +.height-1em50 { height: 1.50em; } +.height-1em75 { height: 1.75em; } +.height-2em { height: 2.00em; } +.height-2em50 { height: 2.50em; } +.height-3em { height: 3.00em; } +.height-4em { height: 4.00em; } +.height-5em { height: 5.00em; } +.height-5em25 { height: 5.25em; } +.height-6em { height: 6.00em; } +.height-7em { height: 7.00em; } +.height-8em { height: 8.00em; } +.height-8em75 { height: 8.75em; } +.height-9em { height: 9.00em; } +.height-10em { height: 10.00em; } +.height-11em { height: 11.00em; } +.height-12em { height: 12.00em; } +.height-13em { height: 13.00em; } +.height-14em { height: 14.00em; } +.height-15em { height: 15.00em; } +.height-20em { height: 20.00em; } +.height-30em { height: 30.00em; } +.height-40em { height: 40.00em; } + + +/* 高さの最大値 +---------------------------------------------------------------- */ +.max-height-none { max-height: none; } + +/* %指定 */ +.max-height-010per { max-height: 10%; } +.max-height-020per { max-height: 20%; } +.max-height-025per { max-height: 25%; } +.max-height-030per { max-height: 30%; } +.max-height-033per { max-height: 33%; } +.max-height-040per { max-height: 40%; } +.max-height-050per { max-height: 50%; } +.max-height-060per { max-height: 60%; } +.max-height-067per { max-height: 67%; } +.max-height-070per { max-height: 70%; } +.max-height-075per { max-height: 75%; } +.max-height-080per { max-height: 80%; } +.max-height-090per { max-height: 90%; } +.max-height-100per { max-height: 100%; } + +/* 文字数指定 */ +.max-height-0em25 { max-height: 0.25em; } +.max-height-0em50 { max-height: 0.50em; } +.max-height-0em75 { max-height: 0.75em; } +.max-height-1em { max-height: 1.00em; } +.max-height-1em25 { max-height: 1.25em; } +.max-height-1em50 { max-height: 1.50em; } +.max-height-1em75 { max-height: 1.75em; } +.max-height-2em { max-height: 2.00em; } +.max-height-2em50 { max-height: 2.50em; } +.max-height-3em { max-height: 3.00em; } +.max-height-4em { max-height: 4.00em; } +.max-height-5em { max-height: 5.00em; } +.max-height-5em25 { max-height: 5.25em; } +.max-height-6em { max-height: 6.00em; } +.max-height-7em { max-height: 7.00em; } +.max-height-8em { max-height: 8.00em; } +.max-height-8em75 { max-height: 8.75em; } +.max-height-9em { max-height: 9.00em; } +.max-height-10em { max-height: 10.00em; } +.max-height-11em { max-height: 11.00em; } +.max-height-12em { max-height: 12.00em; } +.max-height-13em { max-height: 13.00em; } +.max-height-14em { max-height: 14.00em; } +.max-height-15em { max-height: 15.00em; } +.max-height-20em { max-height: 20.00em; } +.max-height-30em { max-height: 30.00em; } +.max-height-40em { max-height: 40.00em; } + + +/* 幅 +---------------------------------------------------------------- */ +.width-auto { width: auto; } + +/* %指定 */ +.width-010per { width: 10%; } +.width-020per { width: 20%; } +.width-025per { width: 25%; } +.width-030per { width: 30%; } +.width-033per { width: 33%; } +.width-040per { width: 40%; } +.width-050per { width: 50%; } +.width-060per { width: 60%; } +.width-067per { width: 67%; } +.width-070per { width: 70%; } +.width-075per { width: 75%; } +.width-080per { width: 80%; } +.width-090per { width: 90%; } +.width-100per { width: 100%; } + +/* 文字数指定 */ +.width-0em25 { width: 0.25em; } +.width-0em50 { width: 0.50em; } +.width-0em75 { width: 0.75em; } +.width-1em { width: 1.00em; } +.width-1em25 { width: 1.25em; } +.width-1em50 { width: 1.50em; } +.width-1em75 { width: 1.75em; } +.width-2em { width: 2.00em; } +.width-2em50 { width: 2.50em; } +.width-3em { width: 3.00em; } +.width-4em { width: 4.00em; } +.width-5em { width: 5.00em; } +.width-5em25 { width: 5.25em; } +.width-6em { width: 6.00em; } +.width-7em { width: 7.00em; } +.width-8em { width: 8.00em; } +.width-8em75 { width: 8.75em; } +.width-9em { width: 9.00em; } +.width-10em { width: 10.00em; } +.width-11em { width: 11.00em; } +.width-12em { width: 12.00em; } +.width-13em { width: 13.00em; } +.width-14em { width: 14.00em; } +.width-15em { width: 15.00em; } +.width-20em { width: 20.00em; } +.width-30em { width: 30.00em; } +.width-40em { width: 40.00em; } + + +/* 幅の最大値 +---------------------------------------------------------------- */ +.max-width-none { max-width: none; } + +/* %指定 */ +.max-width-010per { max-width: 10%; } +.max-width-020per { max-width: 20%; } +.max-width-025per { max-width: 25%; } +.max-width-030per { max-width: 30%; } +.max-width-033per { max-width: 33%; } +.max-width-040per { max-width: 40%; } +.max-width-050per { max-width: 50%; } +.max-width-060per { max-width: 60%; } +.max-width-067per { max-width: 67%; } +.max-width-070per { max-width: 70%; } +.max-width-075per { max-width: 75%; } +.max-width-080per { max-width: 80%; } +.max-width-090per { max-width: 90%; } +.max-width-100per { max-width: 100%; } + +/* 文字数指定 */ +.max-width-0em25 { max-width: 0.25em; } +.max-width-0em50 { max-width: 0.50em; } +.max-width-0em75 { max-width: 0.75em; } +.max-width-1em { max-width: 1.00em; } +.max-width-1em25 { max-width: 1.25em; } +.max-width-1em50 { max-width: 1.50em; } +.max-width-1em75 { max-width: 1.75em; } +.max-width-2em { max-width: 2.00em; } +.max-width-2em50 { max-width: 2.50em; } +.max-width-3em { max-width: 3.00em; } +.max-width-4em { max-width: 4.00em; } +.max-width-5em { max-width: 5.00em; } +.max-width-5em25 { max-width: 5.25em; } +.max-width-6em { max-width: 6.00em; } +.max-width-7em { max-width: 7.00em; } +.max-width-8em { max-width: 8.00em; } +.max-width-8em75 { max-width: 8.75em; } +.max-width-9em { max-width: 9.00em; } +.max-width-10em { max-width: 10.00em; } +.max-width-11em { max-width: 11.00em; } +.max-width-12em { max-width: 12.00em; } +.max-width-13em { max-width: 13.00em; } +.max-width-14em { max-width: 14.00em; } +.max-width-15em { max-width: 15.00em; } +.max-width-20em { max-width: 20.00em; } +.max-width-30em { max-width: 30.00em; } +.max-width-40em { max-width: 40.00em; } + + +/* 最大サイズ +---------------------------------------------------------------- */ +.max-size-none { max-height: none; max-width: none; } + +/* %指定 */ +.max-size-005per { max-height: 5%; max-width: 5%; } +.max-size-010per { max-height: 10%; max-width: 10%; } +.max-size-020per { max-height: 20%; max-width: 20%; } +.max-size-025per { max-height: 25%; max-width: 25%; } +.max-size-030per { max-height: 30%; max-width: 30%; } +.max-size-033per { max-height: 33%; max-width: 33%; } +.max-size-040per { max-height: 40%; max-width: 40%; } +.max-size-050per { max-height: 50%; max-width: 50%; } +.max-size-060per { max-height: 60%; max-width: 60%; } +.max-size-067per { max-height: 67%; max-width: 67%; } +.max-size-070per { max-height: 70%; max-width: 70%; } +.max-size-075per { max-height: 75%; max-width: 75%; } +.max-size-080per { max-height: 80%; max-width: 80%; } +.max-size-090per { max-height: 90%; max-width: 90%; } +.max-size-100per { max-height: 100%; max-width: 100%; } + +/* 文字数指定 */ +.max-size-0em25 { max-height: 0.25em; max-width: 0.25em; } +.max-size-0em50 { max-height: 0.50em; max-width: 0.50em; } +.max-size-0em75 { max-height: 0.75em; max-width: 0.75em; } +.max-size-1em { max-height: 1.00em; max-width: 1.00em; } +.max-size-1em25 { max-height: 1.25em; max-width: 1.25em; } +.max-size-1em50 { max-height: 1.50em; max-width: 1.50em; } +.max-size-1em75 { max-height: 1.75em; max-width: 1.75em; } +.max-size-2em { max-height: 2.00em; max-width: 2.00em; } +.max-size-2em50 { max-height: 2.50em; max-width: 2.50em; } +.max-size-3em { max-height: 3.00em; max-width: 3.00em; } +.max-size-4em { max-height: 4.00em; max-width: 4.00em; } +.max-size-5em { max-height: 5.00em; max-width: 5.00em; } +.max-size-5em25 { max-height: 5.25em; max-width: 5.25em; } +.max-size-6em { max-height: 6.00em; max-width: 6.00em; } +.max-size-7em { max-height: 7.00em; max-width: 7.00em; } +.max-size-8em { max-height: 8.00em; max-width: 8.00em; } +.max-size-8em75 { max-height: 8.75em; max-width: 8.75em; } +.max-size-9em { max-height: 9.00em; max-width: 9.00em; } +.max-size-10em { max-height: 10.00em; max-width: 10.00em; } +.max-size-11em { max-height: 11.00em; max-width: 11.00em; } +.max-size-12em { max-height: 12.00em; max-width: 12.00em; } +.max-size-13em { max-height: 13.00em; max-width: 13.00em; } +.max-size-14em { max-height: 14.00em; max-width: 14.00em; } +.max-size-15em { max-height: 15.00em; max-width: 15.00em; } +.max-size-20em { max-height: 20.00em; max-width: 20.00em; } +.max-size-30em { max-height: 30.00em; max-width: 30.00em; } +.max-size-40em { max-height: 40.00em; max-width: 40.00em; } + + +/* 禁則処理のルール +---------------------------------------------------------------- */ +.line-break-auto { + -webkit-line-break: auto; + -epub-line-break: auto; +} +.line-break-loose { + -webkit-line-break: loose; + -epub-line-break: loose; +} +.line-break-normal { + -webkit-line-break: normal; + -epub-line-break: normal; +} +.line-break-strict { + -webkit-line-break: strict; + -epub-line-break: strict; +} + + +/* 自動改行のルール +---------------------------------------------------------------- */ +.word-break-normal { + -webkit-word-break: normal; + -epub-word-break: normal; +} +.word-break-break-all { + -webkit-word-break: break-all; + -epub-word-break: break-all; +} +.word-break-keep-all { + -webkit-word-break: keep-all; + -epub-word-break: keep-all; +} + + +/* 長い単語の改行ルール +---------------------------------------------------------------- */ +.word-wrap-normal { + word-wrap: normal; +} +.word-wrap-break-word { + word-wrap: break-word; +} + + +/* 【参考】回り込み +---------------------------------------------------------------- */ +/* 行頭方向に回り込み */ +.float-left, +.float-start { + float: left; +} +/* 行末方向に回り込み */ +.float-right, +.float-end { + float: right; +} +/* 回り込みなし */ +.float-none { + float: none; +} +/* 回り込み解除 */ +.float-clear { + clear: both; +} +/* 行頭方向の回り込み解除 */ +.float-clear-left, +.float-clear-start { + clear: left; +} +/* 行末方向の回り込み解除 */ +.float-clear-right, +.float-clear-end { + clear: right; +} + + diff --git a/docs/vol1/image/ch01/1-1.jpg b/docs/vol1/image/ch01/1-1.jpg new file mode 100644 index 0000000..db1571f Binary files /dev/null and b/docs/vol1/image/ch01/1-1.jpg differ diff --git a/docs/vol1/image/ch01/1-2.jpg b/docs/vol1/image/ch01/1-2.jpg new file mode 100644 index 0000000..946d6e7 Binary files /dev/null and b/docs/vol1/image/ch01/1-2.jpg differ diff --git a/docs/vol1/image/ch02/2-1.jpg b/docs/vol1/image/ch02/2-1.jpg new file mode 100644 index 0000000..793de15 Binary files /dev/null and b/docs/vol1/image/ch02/2-1.jpg differ diff --git a/docs/vol1/image/ch02/2-2.jpg b/docs/vol1/image/ch02/2-2.jpg new file mode 100644 index 0000000..796cfa1 Binary files /dev/null and b/docs/vol1/image/ch02/2-2.jpg differ diff --git a/docs/vol1/image/ch02/2-3.jpg b/docs/vol1/image/ch02/2-3.jpg new file mode 100644 index 0000000..4a02ba3 Binary files /dev/null and b/docs/vol1/image/ch02/2-3.jpg differ diff --git a/docs/vol1/image/ch03/3-1.jpg b/docs/vol1/image/ch03/3-1.jpg new file mode 100644 index 0000000..cfce514 Binary files /dev/null and b/docs/vol1/image/ch03/3-1.jpg differ diff --git a/docs/vol1/image/ch03/3-2.jpg b/docs/vol1/image/ch03/3-2.jpg new file mode 100644 index 0000000..f5aeffd Binary files /dev/null and b/docs/vol1/image/ch03/3-2.jpg differ diff --git a/docs/vol1/image/ch07/07-1.jpg b/docs/vol1/image/ch07/07-1.jpg new file mode 100644 index 0000000..148c657 Binary files /dev/null and b/docs/vol1/image/ch07/07-1.jpg differ diff --git a/docs/vol1/image/ch07/07-2.jpg b/docs/vol1/image/ch07/07-2.jpg new file mode 100644 index 0000000..9ea74d5 Binary files /dev/null and b/docs/vol1/image/ch07/07-2.jpg differ diff --git a/docs/vol1/image/ch08/08-1.jpg b/docs/vol1/image/ch08/08-1.jpg new file mode 100644 index 0000000..c4654ce Binary files /dev/null and b/docs/vol1/image/ch08/08-1.jpg differ diff --git a/docs/vol1/image/ch08/08-2.jpg b/docs/vol1/image/ch08/08-2.jpg new file mode 100644 index 0000000..edda6ac Binary files /dev/null and b/docs/vol1/image/ch08/08-2.jpg differ diff --git a/docs/vol1/image/ch08/08-3.jpg b/docs/vol1/image/ch08/08-3.jpg new file mode 100644 index 0000000..5b0eef7 Binary files /dev/null and b/docs/vol1/image/ch08/08-3.jpg differ diff --git a/docs/vol1/image/ch08/08-4.jpg b/docs/vol1/image/ch08/08-4.jpg new file mode 100644 index 0000000..6edd753 Binary files /dev/null and b/docs/vol1/image/ch08/08-4.jpg differ diff --git a/docs/vol1/image/ch09/09-1.jpg b/docs/vol1/image/ch09/09-1.jpg new file mode 100644 index 0000000..47c1526 Binary files /dev/null and b/docs/vol1/image/ch09/09-1.jpg differ diff --git a/docs/vol1/image/ch09/09-2.jpg b/docs/vol1/image/ch09/09-2.jpg new file mode 100644 index 0000000..1b35124 Binary files /dev/null and b/docs/vol1/image/ch09/09-2.jpg differ diff --git a/docs/vol1/image/ch10/10-1.jpg b/docs/vol1/image/ch10/10-1.jpg new file mode 100644 index 0000000..a2ca6c7 Binary files /dev/null and b/docs/vol1/image/ch10/10-1.jpg differ diff --git a/docs/vol1/image/ch13/13-1.jpg b/docs/vol1/image/ch13/13-1.jpg new file mode 100644 index 0000000..987df22 Binary files /dev/null and b/docs/vol1/image/ch13/13-1.jpg differ diff --git a/docs/vol1/image/ch13/13-2.jpg b/docs/vol1/image/ch13/13-2.jpg new file mode 100644 index 0000000..556f63e Binary files /dev/null and b/docs/vol1/image/ch13/13-2.jpg differ diff --git a/docs/vol1/image/ch13/13-3.jpg b/docs/vol1/image/ch13/13-3.jpg new file mode 100644 index 0000000..30d4dd2 Binary files /dev/null and b/docs/vol1/image/ch13/13-3.jpg differ diff --git a/docs/vol1/image/ch13/13-4.jpg b/docs/vol1/image/ch13/13-4.jpg new file mode 100644 index 0000000..8900579 Binary files /dev/null and b/docs/vol1/image/ch13/13-4.jpg differ diff --git a/docs/vol1/image/ch14/14-1.jpg b/docs/vol1/image/ch14/14-1.jpg new file mode 100644 index 0000000..dacada0 Binary files /dev/null and b/docs/vol1/image/ch14/14-1.jpg differ diff --git a/docs/vol1/image/ch14/14-2.jpg b/docs/vol1/image/ch14/14-2.jpg new file mode 100644 index 0000000..5443a4c Binary files /dev/null and b/docs/vol1/image/ch14/14-2.jpg differ diff --git a/docs/vol1/image/ch15/15-0.jpg b/docs/vol1/image/ch15/15-0.jpg new file mode 100644 index 0000000..bdc58f3 Binary files /dev/null and b/docs/vol1/image/ch15/15-0.jpg differ diff --git a/docs/vol1/image/ch15/15-1.jpg b/docs/vol1/image/ch15/15-1.jpg new file mode 100644 index 0000000..529e2f4 Binary files /dev/null and b/docs/vol1/image/ch15/15-1.jpg differ diff --git a/docs/vol1/image/ch15/15-2.jpg b/docs/vol1/image/ch15/15-2.jpg new file mode 100644 index 0000000..8f23fcd Binary files /dev/null and b/docs/vol1/image/ch15/15-2.jpg differ diff --git a/docs/vol1/image/ch15/15-4.jpg b/docs/vol1/image/ch15/15-4.jpg new file mode 100644 index 0000000..e07fca4 Binary files /dev/null and b/docs/vol1/image/ch15/15-4.jpg differ diff --git a/docs/vol1/image/ch15/15-5.jpg b/docs/vol1/image/ch15/15-5.jpg new file mode 100644 index 0000000..a95a55b Binary files /dev/null and b/docs/vol1/image/ch15/15-5.jpg differ diff --git a/docs/vol1/image/ch15/15-6.jpg b/docs/vol1/image/ch15/15-6.jpg new file mode 100644 index 0000000..1de46f9 Binary files /dev/null and b/docs/vol1/image/ch15/15-6.jpg differ diff --git a/docs/vol1/image/ch16/16-1.jpg b/docs/vol1/image/ch16/16-1.jpg new file mode 100644 index 0000000..0f2ca50 Binary files /dev/null and b/docs/vol1/image/ch16/16-1.jpg differ diff --git a/docs/vol1/image/ch16/16-2.jpg b/docs/vol1/image/ch16/16-2.jpg new file mode 100644 index 0000000..cefcb75 Binary files /dev/null and b/docs/vol1/image/ch16/16-2.jpg differ diff --git a/docs/vol1/image/ch16/16-3.jpg b/docs/vol1/image/ch16/16-3.jpg new file mode 100644 index 0000000..6338ad3 Binary files /dev/null and b/docs/vol1/image/ch16/16-3.jpg differ diff --git a/docs/vol1/image/ch16/16-5.jpg b/docs/vol1/image/ch16/16-5.jpg new file mode 100644 index 0000000..e8127f6 Binary files /dev/null and b/docs/vol1/image/ch16/16-5.jpg differ diff --git a/docs/vol1/image/ch16/16-6.jpg b/docs/vol1/image/ch16/16-6.jpg new file mode 100644 index 0000000..d440092 Binary files /dev/null and b/docs/vol1/image/ch16/16-6.jpg differ diff --git a/docs/vol1/image/ch16/16-7.jpg b/docs/vol1/image/ch16/16-7.jpg new file mode 100644 index 0000000..bfb57ca Binary files /dev/null and b/docs/vol1/image/ch16/16-7.jpg differ diff --git a/docs/vol1/image/ch18/18-1.jpg b/docs/vol1/image/ch18/18-1.jpg new file mode 100644 index 0000000..a83b3d5 Binary files /dev/null and b/docs/vol1/image/ch18/18-1.jpg differ diff --git a/docs/vol1/image/ch18/18-2.jpg b/docs/vol1/image/ch18/18-2.jpg new file mode 100644 index 0000000..92ddd46 Binary files /dev/null and b/docs/vol1/image/ch18/18-2.jpg differ diff --git a/docs/vol1/image/ch19/19-1.jpg b/docs/vol1/image/ch19/19-1.jpg new file mode 100644 index 0000000..2ef7182 Binary files /dev/null and b/docs/vol1/image/ch19/19-1.jpg differ diff --git a/docs/vol1/image/ch19/19-2.jpg b/docs/vol1/image/ch19/19-2.jpg new file mode 100644 index 0000000..f1d080f Binary files /dev/null and b/docs/vol1/image/ch19/19-2.jpg differ diff --git a/docs/vol1/image/ch22/22-1.jpg b/docs/vol1/image/ch22/22-1.jpg new file mode 100644 index 0000000..61d7551 Binary files /dev/null and b/docs/vol1/image/ch22/22-1.jpg differ diff --git a/docs/vol1/image/ch22/22-2.jpg b/docs/vol1/image/ch22/22-2.jpg new file mode 100644 index 0000000..7bfb30b Binary files /dev/null and b/docs/vol1/image/ch22/22-2.jpg differ diff --git a/docs/vol1/image/ch23/23-1.jpg b/docs/vol1/image/ch23/23-1.jpg new file mode 100644 index 0000000..621cdb2 Binary files /dev/null and b/docs/vol1/image/ch23/23-1.jpg differ diff --git a/docs/vol1/image/ch23/23-2.jpg b/docs/vol1/image/ch23/23-2.jpg new file mode 100644 index 0000000..d0644a8 Binary files /dev/null and b/docs/vol1/image/ch23/23-2.jpg differ diff --git a/docs/vol1/image/ch24/24-1.jpg b/docs/vol1/image/ch24/24-1.jpg new file mode 100644 index 0000000..1a93549 Binary files /dev/null and b/docs/vol1/image/ch24/24-1.jpg differ diff --git a/docs/vol1/image/ch25/25-1.jpg b/docs/vol1/image/ch25/25-1.jpg new file mode 100644 index 0000000..3e0a03e Binary files /dev/null and b/docs/vol1/image/ch25/25-1.jpg differ diff --git a/docs/vol1/image/ch28/28-1.jpg b/docs/vol1/image/ch28/28-1.jpg new file mode 100644 index 0000000..6671712 Binary files /dev/null and b/docs/vol1/image/ch28/28-1.jpg differ diff --git a/docs/vol1/image/ch29/29-1.jpg b/docs/vol1/image/ch29/29-1.jpg new file mode 100644 index 0000000..33d7dc5 Binary files /dev/null and b/docs/vol1/image/ch29/29-1.jpg differ diff --git a/docs/vol1/image/ch30/30-1.jpg b/docs/vol1/image/ch30/30-1.jpg new file mode 100644 index 0000000..dd46b29 Binary files /dev/null and b/docs/vol1/image/ch30/30-1.jpg differ diff --git a/docs/vol1/image/ch30/30-2.jpg b/docs/vol1/image/ch30/30-2.jpg new file mode 100644 index 0000000..9b1cf17 Binary files /dev/null and b/docs/vol1/image/ch30/30-2.jpg differ diff --git a/docs/vol1/image/ch30/30-3.jpg b/docs/vol1/image/ch30/30-3.jpg new file mode 100644 index 0000000..ce8f226 Binary files /dev/null and b/docs/vol1/image/ch30/30-3.jpg differ diff --git a/docs/vol1/image/cover.jpg b/docs/vol1/image/cover.jpg new file mode 100644 index 0000000..8914af0 Binary files /dev/null and b/docs/vol1/image/cover.jpg differ diff --git a/docs/vol1/image/tobira.jpg b/docs/vol1/image/tobira.jpg new file mode 100644 index 0000000..33eb9ac Binary files /dev/null and b/docs/vol1/image/tobira.jpg differ diff --git a/docs/vol1/index.xhtml b/docs/vol1/index.xhtml new file mode 100755 index 0000000..8b71683 --- /dev/null +++ b/docs/vol1/index.xhtml @@ -0,0 +1,59 @@ + + + + + +Matz Essays Volume 1 + + + + +

Matz Essays Volume 1

+ + +
+

はじめに

+

1999〜2003

+

第1章 言語にとって美とは何か?: Rubyにみるスクリプト言語の実装技法

+

第2章 Free Language Report: オブジェクト指向スクリプト言語Ruby

+

第3章 UNIX系OS間の移植性について

+

第4章 連載 スクリプト言語: スクリプト言語の歴史

+

第5章 特集 開発ツールをもっと便利に使おう: 最強の開発環境を求めて: Ruby最古のユーザーとしての開発環境

+

第6章 初等Ruby講座: はじめの一歩

+

第7章 初等Ruby講座: 条件判断とループ

+

第8章 初等Ruby講座: オブジェクトと変数

+

第9章 初等Ruby講座: 配列

+

第10章 初等Ruby講座: ハッシュ(または連想配列)

+

第11章 初等Ruby講座: 文字列の操作

+

第12章 初等Ruby講座: パターンマッチ

+

第13章 初等Ruby講座: 入出力

+

第14章 初等Ruby講座: 数と電卓

+

第15章 初等Ruby講座: CGI

+

第16章 初等Ruby講座: Rubyで作るCGI

+

第17章 初等Ruby講座: CGIの道具箱

+

第18章 初等Ruby講座: ファイル処理

+

第19章 初等Ruby講座: ネットワークプログラミング

+

第20章 初等Ruby講座: 番外編: Rubyカンファレンスレポート

+

第21章 初等Ruby講座: プロセスとフォーク

+

第22章 初等Ruby講座: スレッド(その1)

+

第23章 初等Ruby講座: スレッド(その2)

+

第24章 初等Ruby講座: データの保存

+

第25章 初等Ruby講座: XMLとYAML

+

第26章 初等Ruby講座: XMLとYAML(その2)

+

第27章 初等Ruby講座: エクストリーム・プログラミング

+

第28章 初等Ruby講座: 独習Ruby

+

第29章 初等Ruby講座: 再入門オブジェクト指向

+

第30章 初等Ruby講座: 最終回: ここからのRuby

+

初出一覧

+

索引

+

奥付

+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-001.xhtml b/docs/vol1/xhtml/p-001.xhtml new file mode 100755 index 0000000..30941db --- /dev/null +++ b/docs/vol1/xhtml/p-001.xhtml @@ -0,0 +1,28 @@ + + + + + +Volume 1 + + + + +

Matz Essays Volume 1

+ + +
+ +

1999〜2003

+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-002.xhtml b/docs/vol1/xhtml/p-002.xhtml new file mode 100755 index 0000000..71db8fe --- /dev/null +++ b/docs/vol1/xhtml/p-002.xhtml @@ -0,0 +1,508 @@ + + + + + +第1章 Rubyにみるスクリプト言語の実装技法 + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay1 +
+

+言語にとって美とはなにか?
+Rubyにみるスクリプト言語の
実装技法 +

+
+

[TransTECH, 1999年8月号]

+
+

『ドクター・ドブズ・ジャーナル日本版』から改装された雑誌『TransTECH』1999年8月号の大特集「言語にとって美とはなにか?」に掲載された原稿です。掲載誌は今でも記念として私の本棚を飾っていますが、「美とはなにか」という大げさなタイトルの割に、この特集の中で「言語の美」を直接扱った記事は、私のものを含めて存在しなかったように思います。『TransTECH』はなかなか骨太な雑誌で、記事の内容も好みのものが多かったのですが、残念ながら翌年2000年4月号で休刊しています。本家『Dr. Dobb’s Journal』も休刊してしまうし、紙の雑誌は受難ですね。

+

記事の内容としては1999年当時のRubyの実装や言語実装に関するテクニックを解説しています。YARV仮想マシンが導入される1.8以前を対象とした解説ではありますが(当時のバージョンは1.2)、20年以上経った現在でも有効な内容が多いと思います。言語処理系を作りたい人の参考になるかもしれません。でも、今だったらRuby 1.8のようなtree walking interpreterではなくてVMを実装することをお勧めします。当時はまだ20世紀で、その頃はtree walkingは珍しくなかったんですけどねえ。Ruby自身は本文中「やらないと思っている」と書いた「VM」「世代別GC」や「JITコンパイラ」なども実装していて、隔世の感があります。

+

それから、他の記事もそうですが、記事中のリンクが、Ruby自身のものを含めて切れているのがインターネット時代にはかなさを感じます。むしろデジタルデータのほうが消滅しやすいですね。ただ、「GC FAQ」だけは2024年現在も健在ですばらしいと思います。

+
+
+

はじめに

+
+

最近のコンピュータの普及に従ってプログラミング人口はますます増加しています。ほとんどの場合プログラミングには何らかのプログラミング言語を使うことになります。それらの言語の中にはCやJava, Pascalのようなメジャーなものから、各種アプリケーションに組み込みのいわゆるマクロ言語や簡易言語と呼ばれるものまでさまざまなものがあります。

+
+

このようにプログラマーとは切っても切れない関係のプログラミング言語も、実際にその処理系を実装したことのある人はそれほど多くはありません。自分のプログラミング言語を設計したことがある人はもっと少ないでしょう。

+

しかし、コンピュータがこの世に登場してからこのかた数多くのプログラミング言語が設計されました。その中には世の人々にほとんど知られることなく衰退してしまったものも数知れずあります。The Language List <http://cuiwww.unige.ch/langlist> はそのようなプログラミング言語を集めたカタログですが、ここに収録されているある程度知られた言語に限っても2400くらいあります。知られていない言語の数がいったいどのくらいになるのかとても想像がつきません。

+

なぜこのように数多くのプログラミング言語が存在するのでしょうか? 確かな理由は推測するしかありませんが、プログラミング言語の設計と実装にはプログラマーを引きつける「何か」があるのは確かです。私自身がその魅力に取りつかれた人間の一人ですから、間違いありません。

+

プログラミング言語の設計と実装はある意味でソフトウェア工学の総決算ともいえます。少し考えただけでも表1.1のような分野を包含しています。

+
+

表1.1●ソフトウェア工学の総決算

+ + + + + + + + + + + + + + + + + + + + + + + + + +
人間工学モデルとアルゴリズムの記法
言語理論コンパイラ設計、最適化
アルゴリズム文字列処理、記号処理、数値処理
データ構造リスト、配列、ハッシュ
OSインターフェイスシステムコール、ネットワーク、動的リンク
高速化アルゴリズムの選択、チューンアップ
+
+

私の経験からいうとおもちゃのような言語を実装するだけでも相当いろいろ考えることがあり、これほど楽しいプロジェクトはなかなかないと思います。

+

一方、機能・性能の面から「使いものになる」インタプリタを作るにはただ楽しいだけでは済まないいろいろな工夫が必要になります。この記事ではプログラミング言語の設計者・実装者の立場から、オブジェクト指向スクリプト言語であるRubyの実装を例にしながら、プログラミング言語の実装技法を解説します。

+
+
+

Rubyとは

+
+

本題に入る前に、Rubyについてご存じない方もいらっしゃるでしょうから、このオブジェクト指向スクリプト言語についてひととおり紹介しておきましょう。

+

Rubyは、私「まつもと ゆきひろ」によって設計・開発されたスクリプト言語です。Rubyは1993年から開発が始まりました。ライバルであるPerlやPythonよりもやや若い言語です。実際、Perl(真珠は6月の誕生石)を意識して、Ruby(ルビーは7月の誕生石)と名付けられています。

+

その主要な特徴は、

+ +

などがあります。RubyはPerlが使えるような分野にはほとんど適用できます。具体的にはテキスト処理、CGIなどのWWWプログラミング、システム管理、ネットワークプログラミング、GUIプログラミングなどです。しかも、Perlよりもプログラムが暗号のようになりにくい傾向があるので、まだまだマイナーであることを除けば、Perlの代替として十分機能します。

+

しかし、作者の目から見るとこの言語は単なるスクリプト言語ではありません。というのも、Rubyはオブジェクト指向とプログラミング言語に魅せられたプログラマーの理想の追求の結果だからです。やや大げさですが。

+

私はいわゆる簡易言語が大嫌いです。簡易言語の「簡易」とは「簡単に使える言語」ではなく、「簡単に作れる言語」を意味しています。要するに言語仕様における手抜きです。言語仕様とはプログラミング言語の使いやすさを決める最も重要な要素です。Rubyはある意味理想の追求ですから、このような部分においては妥協できません。もっとも、本質に関係ない部分では結構手抜きがあるのですが。

+

さて、本稿の主題は言語処理系の実装についてですから、これ以上Ruby言語そのものについては解説しません。参考までにRubyによる簡単なプログラムのいくつかをリスト1.1に示しておきます。

+
+

リスト1.1●Rubyのサンプルプログラム

+
# 由緒正しい Hello World.
+print "Hello World\n"
+
+# 入力ファイルの"From "を含む行を印刷する
+while line = gets()
+  if line =~ /^From /
+    print line
+  end
+end
+
+# 引数で指定したファイル名を小文字にそろえる
+ARGV.each {|path| File.rename(path, path.downcase)}
+
+# TkによるGUI
+require 'tk'
+TkButton.new(nil, 'text'=>'hello', 'command'=>'exit').pack
+Tk.mainloop
+
+
+ +

より詳しくはRubyホームページ <http://www.netlab.co.jp/ruby/jp> を参照してください。

+
+
+

性能向上の原則

+
+

さて、実装の話に移る前にプログラムの性能を向上させる場合一般に適用できる原則について紹介しておきます。

+

プログラムの性能にはいくつかの種類があります。空間効率(少ないメモリで動作する)、実行効率(処理が速く完了する)、操作性(使いやすい)、開発効率(プログラムが速く完成する)などがあります。が、プログラムの性能というと普通は実行効率(まれに空間効率)を意味するでしょう。

+

プログラムの実行効率を向上させるにはやみくもにプログラムをいじってもあまり効果がありません。むしろバグを増やすのに貢献するのみです。狙いを定めて対応する必要があるのです。

+

プログラムの性能の問題はほとんどの場合はプログラム全体から見れば小さな部分に存在しています。よく80 : 20則あるいは90 : 10則と呼ばれるのですが、プログラムの実行時間の8割はプログラム全体の2割のコードで消費されている(あるいは9割を1割で消費している)ことが多いという経験則が知られています。性能の改善にはこのようなボトルネックの部分を見つけて対処することが肝心です。

+

プログラムのある部分を苦労して改善して半分の時間で処理が終了するように効率化したとしても、その部分が全体の実行時間の2%しか消費していなかったら、プログラム全体としては1%、ほとんど誤差のレベルでしか実行効率が改善しません。これでは苦労するだけ無駄です。

+

ですから、実行効率の改善を図る前には必ずどこがボトルネックであるのか分析する必要があります。このような分析を「プロファイルを取る」といいます。プロファイルを取るツールをプロファイラといいます。

+

UNIXでは伝統的にprofgprofというプロファイラが使えます。このツールはどの関数が実行中に何回呼ばれて、それぞれの消費時間はどのくらいであったかを測定します。商用のツールになりますが、日本ラショナルのQuantifyはより細かく行単位でボトルネックの検査ができる優れたツールです。

+

ボトルネックを見つけると今度は実際の対策にかかるわけですが、その場合には以下の優先順位で対応すべきです。

+
    +
  1. アルゴリズムの改善
    +もっと効率のよいアルゴリズムを選択する。最も有効な対策。これに手を付けないで小手先の対応は意味がない。

  2. +
  3. データ構造の改善
    +たとえばデータをヒープに持つか、スタックに持つかとか、無駄なデータを省くとか一本化するとか。

  4. +
  5. 小手先の改善
    +関数のインライン展開とか、ループの展開や置き換えなど。

  6. +
+

決して逆の順序で行ってはいけません。というのも、小手先の改善では数割、うまくいっても数倍しか改善できない場合がほとんどですが、アルゴリズムを変える場合には、ときには100倍以上の劇的な改善が可能な場合もあるからです。

+
+

プログラミング言語における型

+

プログラミング言語の性質を決める重要な要素に型があります。プログラミング言語における型には大きく分けると以下の2種類があります。

+
    +
  • 静的な型

  • +
  • 動的な型

  • +
+

静的な型とはプログラムの字面上に登場する型です。たとえばCプログラムの変数宣言、

+
+
int i
+
+

iという変数がint(整数)型であることを示しています。int型の変数は整数データの格納場所になります。静的な型は変数だけでなく式にも適用されます。

+

動的な型とはプログラムの字面には登場せず、実行時に決まる「値の型」です。Cにはこのような概念は(言語仕様としては)登場しないのですが、たとえばJavaには、

+
+
Object obj = new String;
+
+

のように変数の型とは違う「オブジェクトの型」が存在します。上の例では変数の型はObjectであり、その変数の値の型はStringです。変数objの型(静的な型)を見ただけでは実際にその変数の値の型はわかりません。

+

プログラミング言語をこの型の観点から分類すると以下のようになります。

+
    +
  • 型のない言語     AWK, BCPL, アセンブラ
  • +
  • 静的な型のみの言語  C, Pascal
  • +
  • 両方ある言語     Java, Eiffel, Perl
  • +
  • 動的な型のみの言語  Lisp, Smalltalk, Ruby
  • +
+

静的な型のある言語は型のチェックをコンパイル時に行うことができるので、エラー検出が早いタイミングでできることと、型情報を使ってプログラム解析をより詳細に行うことができる(それによってより最適化できる)ことが利点です。

+

一方、動的な型のみの言語はエラー検出が遅れるというデメリットはありますが、変数の型を厳密に考えなくても気軽にプログラミングできる、オブジェクトに対する扱いに柔軟性が高いという利点があります。

+
+
+
+ +

Rubyの実装

+
+

さて、ようやくRubyの実装を見てみることにしましょう。Rubyインタプリタの構成は図1.1のようになっています。

+
+ +
+ fig0101 +
+

図1.1●Rubyインタプリタの構成

+
+
+

コンパイラ

+

Rubyはインタプリタ型の言語ですが、実際には最初にプログラムを全部読み込んで、コンパイラ部が構文解析を行います。構文解析の結果である構文木を後段の構文木インタプリタが逐次解釈するわけです。このようにまずコンパイラでプログラムを内部構造へ変換する方式をコンパイラ・インタプリタ方式と呼び、最近のスクリプト言語では主流の構成になっていて、PerlやPythonなどでも採用されています。

+

古いタイプのインタプリタ言語ではバージョン7.6以前のTclのようにプログラムを文字列として扱ったり、あるいはある種のBASICのように予約語を1バイトの記号に置き換えた中間言語を使ったりしていました。このような方式のインタプリタに比べるとコンパイラ・インタプリタ方式は、

+
    +
  • 構文解釈のコストを削減できる

  • +
  • 文法エラーを実行前に発見できる

  • +
+

というメリットがあります。

+

Rubyのコンパイラ部(パーサーと呼ぶこともあります)は、UNIXでの一般的なコンパイラ生成ツールであるyaccを使っています。

+

yaccはBNFと呼ばれる言語の文法の記述と結果を出力するプログラム(の断片)から、パーサーを自動生成してくれる優れもののツールです。yaccはRubyを始めとする多くの言語処理系で採用されています。

+

パーサーの出力は言語によってまちまちです。たとえばRubyやPerlでは構文木と呼ばれる構造体のリンクですし、PythonやSmalltalkやバージョン8.0以降のTclではバイトコードと呼ばれる一種の仮想機械のマシン語を出力します。

+

構造体のリンクである構文木は柔軟で実装が容易であるというメリットがあり、一方バイトコードはコンパクトでメモリ効率がよいというメリットがあります。

+

コンパイラ部の効率を改善する方法としては出力をそのまま書き出して2回目以降のコンパイルを回避するというものがあります。この方法はPythonやEmacsなどで用いられています。Pythonはスクリプトを読み込んだときにディレクトリが書き込み可であればコンパイル結果のバイトコードを書き込んでしまうという大胆な方法を採用しています。

+ +

Rubyでもかつてこのコンパイル結果を書き出す方法を採用するかどうかの検討を行ったことがあります。しかし、プロファイルを取ってみるとコンパイル部の実行時間は全体にほとんど影響を与えていないことが明らかになりました。コンパイルはプログラムの実行直前に一度しか行われず、かつ内部構造へのコンパイルは十分に高速なので、ボトルネックになりにくいのでした。Rubyはもともと書き出しを行いにくい構文木方式を採用していたこともあって、この手法は割に合わないということで採用を見送りました。おそらく今後も採用されることはないでしょう。

+

結論としてはコンパイラ部は現状でもかなり高速なうえ、時間をあまり消費していないので、速度の点ではあまり改善の余地がないといえるでしょう。

+
+
+

最適化

+

コンパイラの実装の中で重要な分野として「最適化」と呼ばれるものがあります。最適化とはプログラムの実行効率を向上させる処理です。たとえば無駄な処理を省いたり、先に計算できるものをあらかじめ計算しておくなどの「小手先」の改善をコンパイラに行わせるものです。歴史的な事情から「最適化」と呼ばれていますが、実際には「最適」にすることなどできるはずもなく、本来ならいわば「ちょっとマシ化」とでも呼ぶべきものです。

+

コンパイラ型の言語ではこれらの最適化は非常に有効で、たとえばCコンパイラの最適化オプションを付けるかどうかで処理時間が相当変化することはざらです。

+

具体的な最適化にはたとえば以下のようなものがあります。

+
    +
  • 式展開(x**2x*xなど)

  • +
  • 定数畳み込み(2+13, "a"+"b""ab"など)

  • +
  • 共通部分式の除去

  • +
  • 不要コードの除去

  • +
  • コードの並べ替え

  • +
  • ループ展開

  • +
  • インライン展開

  • +
+

しかし、残念なことにRubyのような動的な性質の強い言語ではこの最適化はほとんど適用できません。

+

まず、第一にRubyには変数や式に型がないので、型情報を用いた式展開などができません。x+yという式を見てもそれぞれの変数に数値が入っているのか、文字列が入っているのかによって実際の処理が異なるので、あらかじめ計算することができないのです。

+

また、演算子も一種のメソッドでプログラム中で再定義することが可能なので(その気になれば整数の加算なども置き換えられる)、その点からも最適化は困難です。副作用がないことがはっきりわかっていれば、共通部分式や不要コードの除去やコードの並べ替え、ループ展開などが可能ですが、そのことを保証する方法も提供されていません。

+

プログラムの解析によって実際に起動される処理を決めることができないのでインライン展開も行えません。

+
+

Rubyの動的で柔軟な性質は、プログラムの書きやすさや使いやすさにつながりますが、プログラムの実行効率、特に最適化という点からはやや厳しいものがあります。

+
+
+

C言語へのコンパイル

+

最近のマシンの高速化によって実行速度に不満を感じる機会は以前より少なくなりましたが、やはりインタプリタ型の言語はコンパイラ型の言語よりも10倍から100倍程度実行速度が遅い場合がほとんどです。

+

「インタプリタは遅い」というとたいていの人がすぐに思い付くのは、「コンパイルして実行形式にすれば高速化できる」ということでしょう。

+

しかし、世の中はそうは甘くないものです。Rubyにはまだありませんが、同じ分野のスクリプト言語であるPerlやPythonにはプログラムをC言語に変換して、それをコンパイルすることにより実行形式を得ることができるツールがすでに開発されています。しかし、やや意外なことではありますが、実行速度はほとんどの場合、10倍どころか数割改善されれば上出来という程度のようです。

+

というのも、動的な性質を持つスクリプト言語では、コンパイル時の最適化が難しいのと同様の理由でC言語に変換することによるメリットを受けにくいことが原因です。

+

また、実行時間の多くはコンパイルすることで短縮されるインタプリタによる制御部分ではなく、もともとC言語で記述されコンパイラ型言語の速度で実行されるライブラリルーチンの中で消費されることも関係しているようです。

+

結局、実行形式にコンパイルすることによってインタプリタの制御の部分を直接コンパイルされたスピードで実行できても、それは全体の実行時間から見ると数割にすぎません。その数割のためにかなり複雑になるC言語への変換ツールを作るかと問われるとかなり悩むのが正直なところです。なにしろコンピュータは放っておいてもどんどん高速化しているのですから。

+
+
+

インタプリタ

+

コンパイラによる改善に限界があるとすれば、実際の処理を実行するインタプリタ部はどうでしょうか。実際にプロファイルを取ると、インタプリタを構成する関数はたいてい時間を消費する関数のトップテンに入っているようです。

+

Rubyのインタプリタ部はコンパイラ部が生成した構造体(ノード)のリンクである構文木を解釈します。インタプリタ部の本体はrb_eval()という関数で、その本体はノードの種別ごとに各種の処理を行う巨大なswitch文になっています。

+

現状の構文木インタプリタは以下のような高速化のための工夫を行っています。

+
    +
  • シンボル

  • +
  • allocaの利用

  • +
  • メソッドキャッシュ

  • +
+

これらの工夫を具体的に見てみましょう。

+
+

シンボル

+

プログラムに登場する識別子(変数やメソッドなどの名前)は、コンパイラによってそれと1対1対応するシンボルと呼ばれる整数値に変換されます。インタプリタ内部では文字列としての名前は扱いません。これにより識別子の比較や検索が大幅に高速化されています。また、文字列を管理する必要がないので、メモリの節約と同時にメモリ管理の単純化にも貢献しています。

+
+

allocaの利用

+

ヒープ上に領域を割り当てるmalloc()に対して、スタック上に領域を割り当てるのがalloca()です。alloca()で割り当てられた領域はその関数を終了するまで有効で、関数が終了すると(スタックですから)自動的に解放されます。サイズを自由に指定できるローカル配列のようなものだと考えることができるでしょう。alloca()のもう1つの特徴は、例外もありますが、ほとんどのマシンではmalloc()よりも高速に割り当てを行うことです。Rubyインタプリタではalloca()を利用して高速化を図っています。ただし、alloca()は多用するとスタックを過剰に消費してしまう欠点がありますので、1つの関数の中で繰り返し領域を割り当てたりする用途には使わないようにしなければなりません。

+

メソッドキャッシュ

+

オブジェクト指向言語ではメソッドの呼び出し対象になったオブジェクトによって実際に起動されるメソッドが決まります。ですから、どのオブジェクトに対してどの名前のメソッドが呼ばれたかという情報によって、メソッド呼び出しごとにテーブルを検索して実際の処理を選択する必要があります。

+

Rubyのような純粋オブジェクト指向言語では、ほとんどあらゆる処理がメソッドによって処理されますから、このメソッド検索のコストは非常に大きくなります。

+

そこでRubyではメソッドキャッシュという手法を使ってこの検索コストを低減させています。具体的には、メソッド検索を行った場合、このオブジェクトに対してこの名前のメソッドを呼び出したら、実際にはこの処理を行ったという情報を、クラスとメソッド名をキーにしてメソッドキャッシュと呼ばれるテーブルに保存します。メソッド検索が発生するとまずメソッドキャッシュを調べ、そこで見つからなかったときに初めてオブジェクト内部のテーブルを検索します。

+

このメソッドキャッシュは単純ですが、非常にヒット率が高く、ある調査では95%のメソッド呼び出しでキャッシュヒットしています。これによりかなりの効率改善が行われます。

+
+

インタプリタの効率改善にはこの他にも以下のようなものがあります。

+
+

インラインメソッドキャッシュ

+

これはSmalltalkの一部の処理系で採用されている方法で、上記のメソッドキャッシュのようなグローバルなテーブルではなく、呼び出しコードの中に前回の呼び出し情報を保存しておくというものです。

+

この方法は同じ場所でのメソッド呼び出しはほとんどの場合同じオブジェクトに対して行われるという経験即に基づくものです。

+

実は、以前実際にこのインラインメソッドキャッシュをRubyに実装したことがあるのですが、結果はほとんど実行結果に影響を与えませんでした。すでに採用していたメソッドキャッシュが十分な効果をあげていたからのようです(あるいは私の実装がまずかったのかもしれませんが)。

+
+

threaded code

+

threaded code(縫い糸コードとも呼ばれる)は構文木のノードにノードの種別の代わりに実際にそのノードの処理を行う関数へのポインタを埋め込むものです。これによりswitch文の分岐のコストを削減することができるはずです。

+

これも実装してみましたが、目に見える効果はなく、むしろやや遅くなる傾向がありました。はっきりとした原因は不明ですが、おそらくは最近の最適化されたCコンパイラの生成するswitch文の分岐は十分に高速で、threaded codeにより関数呼び出しが増えてしまうことによるデメリットを補えなかったのではないかと推測します。

+

JIT(Just In Time compile)

+

JITは実行する時点で動的にマシン語に変換し、それを実行するという手法で、Javaでよく用いられています。しかし、この方法は(当然ですが)各CPUに固有の方法になってしまい、同じインタプリタのソースコードをいろいろなマシンで使う移植性を維持するのが難しくなります。

+

やればある程度(数割くらい?)の効果はありそうですが、将来のRubyインタプリタでこの方法が採用される可能性は低いでしょう。

+

型仮定

+

コンパイル部で最適化できない理由の1つは動的な言語ではプログラムに型情報がないからと言いました。だったら型情報に依存しないルーチンと、ある型を仮定して、その条件が成立するときだけ高速で実行するルーチンの両方を用意すればよいというのがこのアプローチです。この手法は非常に興味深いので、後述のHotSpotと合わせて将来いつか挑戦してみたいと考えています。

+

HotSpot

+

HotSpotはSunが提出しているJavaの高速化手法の名称です。詳細は不明ですが、基本的な原理は実行時に統計情報(一種のプロファイル)を集め、それを元にボトルネックの部分で集中的に動的最適化を行うもののようです。

+

統計用のコストがどのくらいの割合になるのか不明ですが、実行時に情報収集して、それを元に高速化を行うというのは面白いアイデアだと思います。

+
+

このようにインタプリタの改善にはまだまだできることがありそうです。

+
+
+

メモリ管理

+

オブジェクト指向言語、特に純粋オブジェクト指向言語では、配列、文字列、数値に至るまでのあらゆるデータがオブジェクトなので、プログラムの実行中に大量のオブジェクトを割り当てる傾向があります。

+

これらのオブジェクトをすべて管理することをプログラマーに要求するとしたら、大変なことになります。プログラマーはどのオブジェクトがいつまで生きているべきか1つ1つ管理して、用事が終われば1つ残らず解放しなければなりません。もし解放するのを忘れたり、あるいはまだ使っているのに解放してしまったりしたら、深刻でなおかつ非常に発見しにくいバグの原因になります。

+

しかし、実際にC++というオブジェクト指向言語ではこのようなことを要求しています。まったく信じられないような話です。

+

一方、C++よりは「ちゃんと」設計された言語であるJavaではこのような問題に対しての対抗策が用意されています。それはガベージコレククション(GC)と呼ばれる機能です。ガベージコレクションはもう使われなくなったオブジェクトを自動的に検出し、解放処理を行ってくれます。もちろん、Rubyにもこの機能があります。

+
+

C++の賛同者の中にはプログラマーが明示的にメモリ管理を行ったほうが(たとえ少々問題が発生しやすくても)、結果的によい性能が出るという主張があるようです。しかし、最近の研究ではコンピュータの性能向上もあいまって、ほとんどの場合には自動的にメモリ管理を行うGCを使ったほうが性能がよいという結果も出ています。

+

ここではまずGCの理論について簡単に説明してから、RubyでのGCの実装について見てみましょう。

+

GCはプログラミング言語の中でも重要なテーマです。最近のほとんどのプログラミング言語は何らかの形でオブジェクトを操作する機能を持ち、C++のような冷酷な言語を除けばオブジェクト操作機能を支援するために何らかのGC機能を持つものが珍しくありません。

+

ガベージコレクションの手法としては以下のようなものがあります。

+
+

リファレンスカウント

+

リファレンスカウント(reference count)はオブジェクトを回収する手法の中では最も「手軽」でPerl, Python, Tcl(8.0以降)を始めとする多くの言語で採用されています。リファレンスカウント方式では、あるオブジェクトが参照されている数をいつも把握しています(図1.2(a))。この参照されている数(これをリファレンスカウントと呼びます)がゼロになったオブジェクトはどこからも参照されていないので、回収することができるというわけです(図1.2(b))。

+
+ +
+ fig0102 +
+

図1.2●リファレンスカウント

+
+ +

この方式のメリットは実装が簡単なことと、オブジェクトが使われなくなったらすぐに回収されることが期待できる点です。この特長は他のGC手法ではまず実現できません。

+
+
f = open("/path/to/file", "r")
+  ... fを使った処理 ...
+f = nil        # ここでfの解放処理が期待できる
+
+

別の特長としてはオブジェクトの回収処理がプログラムの本来の処理の中で少しずつ行われるので、GC処理による中断がほとんどないということがあります。

+

この方式のデメリットはサイクルのあるデータを回収できないことです(図1.2(c))。サイクルのあるデータは全体として参照されなくなっても、個々のオブジェクトの参照数がゼロになりませんから、回収されずゴミになります。

+

オブジェクト指向プログラミングではたくさんのオブジェクトがお互いに参照しあうので、サイクルのあるデータ構造はしばしば発生すると考えられます。この場合ゴミにならないためには用事が済んだ時点で明示的にサイクルを切る必要があります。これはプログラマーにとってはかなり面倒な要求です。

+

また、実装者の立場からいうと参照数の増減を忘れてしまうバグに悩まされやすいというのもデメリットです。

+

マーク・アンド・スイープ

+

マーク・アンド・スイープ(mark and sweep)は、最も古く考案され、最も広く使われているGC手法です。

+

マーク・アンド・スイープでは、ルート(root = 根)と呼ばれるオブジェクトから芋づる式にたぐれるオブジェクトにすべてマークを付け、マークの付いていないオブジェクトはもう使われていないものとみなして回収するという方法です。

+

マーク・アンド・スイープは長らく「古い」という理由であとで説明するストップ・アンド・コピーやその変形であるジェネレーション・スキャベンジングに押されていましたが、単純さとスレッドなどの並行動作との相性のよさから見直されてきています。

+

マーク・アンド・スイープは全オブジェクトをスキャンするので、GC中に処理が停止するという欠点があります。これがかつてリファレンスカウントが人気であった理由の1つだったのですが、しかし、最近のコンピュータの性能向上とアルゴリズムの改善のおかげでそのことが問題になることはほとんどなくなりました。

+

ストップ・アンド・コピー

+

ストップ・アンド・コピー(stop and copy)はオブジェクトのためのメモリを切り出してくるための領域を2面持ち、一方がいっぱいになると、ルートからたぐることのできる「生きている」オブジェクトを別の面にコピーする方法です。別の面へのコピーが終わればすべての「生きている」オブジェクトがそちらに移動していますから、それ以上の回収作業は不要です。

+

ストップ・アンド・コピーは、GCの処理時間がオブジェクトの総数ではなく、「生きているオブジェクト」の数で決定されるというメリットがあります。また、参照される可能性のあるオブジェクトがコピーによって集められるので(コンパクションと呼びます)、一度にアクセスするメモリ空間を小さくできます。このプログラムが同時にアクセスするメモリ空間をワーキングセットと呼ぶこともあります。ワーキングセットが小さいと、OSのページング機能にかける負荷が減り、性能が向上する可能性があります。

+ +

しかし、領域が2面必要になるので割り当てた領域の半分しか活用できず無駄が多いのと、個別のオブジェクトの回収処理を行わないので、メモリ以外の資源の後始末(ファイルのクローズとか)が難しいという欠点もあります。

+

ジェネレーション・スキャベンジング

+

ジェネレーション・スキャベンジング(generation scavenging)は、ストップ・アンド・コピーの変形で「無駄な領域」という欠点を少し改善したうえで、オブジェクトの回収効率を向上させています。

+

オブジェクトの振る舞いの一般的な傾向として、寿命の短いオブジェクトは徹底的に短く、寿命の長いオブジェクトはかなり長いということがいえます。この傾向を利用して、オブジェクトを世代別に分類し、若い世代は頻繁に、古い世代はときどきスキャンするというのがジェネレーション・スキャベンジングの基本原理です。

+

ただし、ジェネレーション・スキャベンジングは実装が非常に難しいという欠点があります。

+
+

それぞれ利点と欠点のあるGC手法ですが、Rubyはこの中でマーク・アンド・スイープ方式を採用しています。理由はリファレンスカウント方式のデメリットが妥協できなかったことと、ストップ・アンド・コピー方式、およびその後継のジェネレーション・スキャベンジング方式では必然的にオブジェクトの移動が発生するので、インタプリタの実装が飛躍的に難しくなるからです。

+

RubyのGCの特徴はルートとして、C言語の大域変数領域とCPUのスタックとレジスタも使う点です。もちろんCPUはどの値がポインタで、どの値が整数かという情報は記録されていませんが、たまたまオブジェクトのアドレスと同じ整数値が存在した場合には、ポインタであるかもしれないので安全側に倒して参照されているとみなすことにしています。

+

このようなGCを、安全側に倒すことから「保守的」という意味のコンサバティブ(conservative)GCと呼びます。コンサバティブGCの利点は、C言語のローカル変数に格納されたRubyのオブジェクトへの参照をGCに明示的に教えてやらなくても自動的に検出できることです。これはインタプリタの開発者(つまり私)の開発効率を飛躍的に向上させます。また、明示的な指定を忘れてしまうことによるバグがなくなることにより、間接的にユーザーも恩恵を受けることになります。

+

コンサバティブGCはポインタと整数値の区別が付けられないので、ポインタの付け替えを伴うストップ・アンド・コピーなどには適用できません。Rubyがストップ・アンド・コピーを使っていないのはそのことも理由の1つです。

+

RubyのコンサバティブGCでは大域変数はユーザーに明示的に指定してもらうことにしています。スタックの範囲はローカル変数のアドレスから計算し、その領域内から参照されている(ように見える)すべてのオブジェクトにマークします。レジスタの内容はCのライブラリ関数であるsetjmp()がレジスタの状態を保存するjmp_buf構造体から参照します。

+

オブジェクトでないメモリ領域、たとえば文字列の内容などは単純にmalloc()free()を使って管理しています。というのも任意長のメモリ領域については、下手に自前で管理するよりもOSが提供している関数を使ったほうが効率が高いことが多いからです。

+
+

Emacsでは文字列領域をプールと呼ばれる領域から割り当て、GCのときに使われていない領域を詰めてコンパクションを行っています。これはワーキングセットを小さくすることを目的としていると思われますが、最近はコンピュータに搭載されているメモリ容量が増えたこともあって、それほど劇的な変化はないようです。

+

参照されなくなってゴミになるのはなにもメモリだけとは限りません。たとえばオープンしっぱなしで誰からも参照されなくなったファイルなどもゴミになります。このようなメモリ以外の資源についてもGCのタイミングで解放してやる必要があります。Rubyはこれもサポートしていますから、たとえば、

+
+
txt = open("/path/to/file", "r").readline
+
+

のように明示的なクローズを省いてしまってもちゃんと動作します。ま、きちんとクローズしたほうがお行儀が良いとは思いますけどね。

+
+
+

組み込みクラスライブラリ

+

構文木インタプリタはプログラムの解釈実行を行いますが、実際の処理の多くの部分はクラスライブラリで提供されるメソッドによって実行されます。

+

組み込みクラスライブラリのメソッドはCで記述されています。ですからインタプリタの負荷と関係なくコンパイルされた速度で実行されます。インタプリタによる解釈・実行がコンパイラ型言語による実行の1000倍遅くても(マシン語の命令数で比較するなら最低でもそれくらいは違う)、全体の実行速度がそれほどまでは差がつかないのは、これらの組み込みメソッドが同等の速度で動作しているからです。

+

しかも、組み込みクラスライブラリはデータ構造、アルゴリズムともに時間と手間をかけて実装されていますから、インタプリタの負荷を除けば、下手なCプログラムよりもはるかに高速に動作していることになります。

+
+
+

文字列/配列/ハッシュ

+

文字列、配列、ハッシュはRubyにおける基本的なデータ型です。これらのデータ構造に対する処理の種類はPerlを参考に決められています。長い経験のあるPerlでの処理を参考にしたうえで、クラスという単位で機能を分類することにより、使いやすいクラスライブラリを実現しています。

+

これらのデータに対するメソッドは多少実装に手間がかかっても高速なものを選択しています。これらのメソッドはあらゆる局面で繰り返し使われるので、手間をかけることが報われるからです。

+

その他のこれらデータ構造における効率上の工夫といえば、たとえば文字列におけるコピー・オン・ライト(copy on write)をあげることができます。

+

Rubyにおいて文字列は参照されるたびに新しく生成されます。これは文字列の内容を変更しても、元の文字列を保存するためですが(リスト1.2)、文字列は非常に多用されますから、参照されるたびに毎回メモリ領域を割り当てて、文字列の内容をコピーしていたのでは効率が悪すぎます。そこで、新しい文字列オブジェクトを生成したあと、文字列の内容を共有することにします。変更が行われるときに初めてメモリ領域の割り当てと内容のコピーを行うわけです。

+ +
+

リスト1.2●文字列の参照

+
def foo
+  "abc"
+end
+
+a = foo()
+a[0] = "b"
+print a, "\n"       # => "bbc"
+
+print foo(), "\n"   # => "abc"のままであってほしい
+
+
+

この方法によって無駄なコピーをかなりの割合で削減できています。

+
+
+

数値計算

+

コンピュータはもともとは電子計算機と呼ばれていたくらいですから、本来は計算が得意です。Rubyは実行効率の観点から数値処理を高速に行うよりも、むしろ文字列を操作することなどのスクリプト向きの仕事のほうが得意なのですが、それでも数字を取り扱うことはしばしばあります。

+

特に整数は非常に頻繁に登場するので、整数オブジェクトの取り扱いは効率に大きく影響します。そこでRubyではいくつかのオブジェクトにはメモリ領域を割り当てず、変数の中に直接格納するようにしています。このように直接変数の中に格納されるオブジェクトは小さな(31ビット幅程度の)整数Fixnumnil, true, falseです。

+

Fixnumの場合、変数(Cレベルで見るとポインタ)の内容の1ビット目(LSB)が1ならば、その変数の中身は参照ではなく、Fixnumの値が格納されているものとします。変数の中に格納されたFixnumから整数を取り出すには全体を1ビット右シフトさせます。

+

この手法により、整数の操作に伴うオブジェクトの生成を避けることが可能になっています。また、Fixnumと整数のやりとりは単純なシフト演算によって行われますから、これも高速に処理できます。

+

一方、Pythonでは整数処理の高速化に対して別のアプローチを取っています。Pythonの作者は変数の中に埋め込む特殊なオブジェクトの存在を嫌って、整数もすべて「普通のオブジェクト」にすることにしました。しかし、整数1つ1つに毎回オブジェクトを生成するのは効率上つらい面もあるので、以下の工夫を行っています。

+
    +
  • 整数オブジェクト専用の割り当てルーチンを用意して、オブジェクトをできるだけ再利用する

  • +
  • −1〜100までの範囲の整数はあらかじめ生成してテーブルに登録しておく

  • +
+

Pythonについて正確な統計を取ったことはありませんが、これらの工夫もかなりの効果をあげているようです。

+
+
+ +

Bignum ―― 無限多倍長整数

+

RubyではFixnumで表現される小さな整数の他にBignumと呼ばれる多倍長演算を行うクラスがあります。このクラスのおかげでたとえば400の階乗のような非常に大きな数も(効率を気にしなければ)自由に計算できます。

+

もともとRubyにBignumを導入した動機というのが、Fixnumの範囲に収まらない整数を取り扱いたかったという単純なもので「単純なニーズの割に大げさなものを導入してしまったなあ」というのが正直な感想です。しかし、「まあ、便利だからいいか」と思っているのも事実です。

+

Bignumはその値をshort(多くは16ビット)の配列として格納しています。計算は単純に繰り上がり、繰り下がりを考慮しつつshortを対象に行います。ただ、割算だけは簡単なアルゴリズムがなく、自分でも十分に理解しきれない手順に悩まされました。

+

Rubyの整数は計算結果がFixnumで表現できるときにはFixnumを、その範囲を超えるときは自動的にBignumを返します。ですから、プログラミングのレベルでは、ほとんどの場合大きさの限界を意識することなく整数を取り扱うことができます。

+
+
+

正規表現

+

正規表現とは文字列のパターンの表現方法です。Rubyを始めとするスクリプト言語は正規表現がしばしば重要な役割を果たします。

+

Rubyの正規表現ルーチンは、もともとEmacsのために開発され、gawkで用いられていたものがベースになっています。このルーチンを採用した理由は、

+
    +
  • フリー(GPL, 現在はLGPL)

  • +
  • パターン、文字列に \0を含むことができる

  • +
  • 8ビットクリーン

  • +
  • 日本語化パッチ(EUC, SJIS)が入手できた

  • +
+

というものです。その後、Rubyで使うために以下のかなり大規模な改造を行っています。

+
+

さて、すでに述べたように正規表現はスクリプト言語ではしばしば多用されますから、正規表現ルーチンの高速化は処理全体の実行効率に関わってきます。Rubyの利用している正規表現ルーチンは以下の最適化を行っています。

+
+

fastmap(Emacsからある)

+

パターンの先頭にマッチし得る文字のテーブルをあらかじめ計算しておき、マッチ対象の文字列からマッチし得る部分を素早く検索するために使われます。

+

固定文字列による絞り込み

+

正規表現がrubyという文字列を必ず含む場合、その文字列を含まない文字列は最初から検索対象になりません。あらかじめ必ずマッチする文字列を計算して、BMサーチという高速文字列検索アルゴリズムを使ってマッチ処理の前に絞り込みを行っています。

+

その他

+

たとえば、正規表現の先頭が .* であった場合、先頭でマッチが失敗すれば、そのあとのチェックは不要であるとか、細々とした最適化を行っています。

+
+

なお、これらの正規表現の最適化は『詳説 正規表現』という書籍からの情報を参考にしています。

+

実装者の観点からいうと、正規表現は非常に奥が深く、面白く、かつつらい分野です。特にRubyの正規表現ルーチンはもともと自分で書いたものではないうえに、原形をとどめないほどの改造を行っているので、バグが出ると悲しくなってしまいます。

+
+
+

スレッド

+

Rubyには1つのプログラムの中で複数の制御の流れが存在するスレッド機能を持っていて、これが言語の特徴の1つになっています。

+

スレッド機能の実装には、OSの協力を必要とするカーネルレベルスレッドと、プログラムが全部自前で実装するユーザーレベルスレッドがあります。

+

カーネルレベルスレッドはOSの協力を受けるので、マルチCPUの活用ができる一方、OSがスレッド機能に対応してくれなければ使えないという問題があります。最近のUNIXの多くや、NTなどでは使えますが、DOSなど古いOSでは使えないということです。

+

ユーザーレベルスレッドはスレッドの切り替えなどを全部自前で実現しますから、カーネルレベルスレッドのようなマルチCPUの活用などは不可能です。その代わりOSに依存しない移植性の高い実装が可能です。

+

Rubyはユーザーレベルスレッドを採用しています。最大の理由はDOSやMacのような古いOSや、カーネルレベルスレッドを採用していないUNIX系OSでも同様のスレッドの使い勝手を提供するためです。残念ながらカーネルレベルスレッドはどこででも使えるわけではありませんし、たとえ使えたとしても各OSごとに仕様や挙動が違うというのが現実ですから。

+
+

RubyのスレッドはRubyが動くところならどこでも同じように動作します。DOSでスレッドプログラミングを気軽にできるのは、ある意味感動ものでもあります。

+

Rubyのスレッドはユーザーレベルスレッドですから、マルチCPUを活用できませんし、スレッドの制御の切り替えのコストがありますから、全体の実行時間は決して短くなりません。また、後述する理由により制御の切り替えのコストもあまり軽くはありません。

+
+
+

スレッドの実装

+

では、Rubyがスレッドをどのように実装しているかを簡単に解説しましょう。詳細はRubyのソースコードを見てください。

+

プログラムの実行状態は大まかにいうと、大域変数とスタック(関数の呼び出し階層とローカル変数)とレジスタ(ローカル関数、作業領域)に格納されています。スレッドの場合、大域変数は共有しますから、スタックとレジスタの状態を保存して、切り替えることができればよいということになります。

+

Cにはsetjmp/longjmpという関数があってレジスタなどの状態をjmp_bufという構造体に書き出す機能があります。このjmp_bufはコンサバティブGCにも使われていましたね。スレッドの実装にもこの機能を利用します。

+

Rubyでは以下の手順でスレッドを切り替えます。

+
    +
  1. setjmp()を使って、現在のレジスタなどの状態をjmp_bufに書き出します。この保存された状態は現在スレッドに制御を返すときに使われます
  2. +
  3. 現在のスタックの状態をヒープにコピーします
  4. +
  5. 切り替え先のスレッドが保存しているスタック状態をスタックに書き戻します
  6. +
  7. 切り替え先のスレッドに保存してあったjmp_bufを使ってlongjmp()します。するとアラ不思議、setjmp()でセーブした時点から実行を再開します
  8. +
+

ここで(2)や(3)の手順でローカル変数が使えないことに注意してください。スタックを壊してしまいます。関数呼び出しも使えません。

+

この方式だとスレッドの切り替えのたびにスタック領域のコピーと書き戻しが発生するので正直なところ効率はあまりよくありません。他のユーザーレベルスレッドの実装の中ではスタック領域をあらかじめ固定領域に割り当てることで、このコピーを行わない実装もあります。しかし、そのような実装には以下のデメリットがあります。

+
    +
  • スタック領域を割り当てるためにはCPUのスタックポインタを書き換える必要があるが、移植性のある方法では実現できない。OS, CPUごとに対応する必要がある

  • +
  • あらかじめ割り当てられたスタック領域の大きさを拡張できない。この領域を超えるとインタプリタ全体が異常終了する

  • +
+

これらのデメリットはRubyのポリシーから考えるといずれも致命的に思えます。そこでRubyでは少々効率が悪くてもコピーする方法を採用しました。

+
+

このようにRubyのスレッド機能の実装は効率よりもむしろ安全性や移植性に重点を置いて設計されています。

+

スレッドを使うと実行時間の総計はスレッド切り替えのコストでかえって長くなるということですし、プログラムの性能向上には貢献しないように思えます。

+

しかし、全体の処理時間は短縮しなくても、時間のかかる処理をスレッドに任せてバックグラウンドで実行することにより、プログラムの応答性が改善するなどのメリットはあります。

+

Rubyではどこでも動くスレッドがあるので安心してスレッドが使えるという点も見過ごしにできません。

+
+
+

拡張ライブラリ

+

さて、ここまででRuby言語の処理系の基本的な部分について簡単に見てきました。Rubyインタプリタはプログラミングに便利なたくさんの機能を組み込みで持っていますが、あらゆる機能を提供しているわけではありません。そんなことは原理的に不可能です。

+

しかし、Rubyには拡張ライブラリという形でインタプリタにあとから機能を追加することができるようになっています。しかも、多くのOSではインタプリタそのものを再コンパイルあるいは再リンクする必要なしに実行時にロードすることができます。

+

実際にネットワークソケット機能やデータベースアクセス機能などが拡張ライブラリとして提供されています。

+

拡張ライブラリはRubyインタプリタに機能を追加するためにも使われますが、実はプログラムの実行効率向上のためにも使うことができます。実行のボトルネックになる部分をCで記述したメソッドとして拡張ライブラリ化してしまうことで、処理の重い部分はCで高速に、それ以外の部分はRubyで快適にプログラミングすることが可能になるのです。これこそ適材適所ですね。

+

このような拡張ライブラリと、その動的ロードの機能はRubyのライバルであるPerlやPythonでももちろん提供されています。これらの拡張ライブラリと比較した場合、Rubyのほうには以下の利点があります。

+
    +
  • コンサバティブGCによりオブジェクトの参照数の管理が不要
    +他言語ではリファレンスカウントを採用しているのでオブジェクトの参照数を管理する必要があります。これを忘れると面倒なバグの原因になります。

  • +
  • Rubyでできることをほぼストレートに記述できる高レベルなAPI
    +CとRubyの言語の差による必然的な違い以外には、クラス定義、メソッド定義などについてほぼ同様の機能を提供するC言語用APIを用意しています。当り前のことのようですが、他言語ではCからのオブジェクトの操作などに制限があり、基本的な部分だけCで記述し、その外側にスクリプト言語で皮をかぶせるような方法がよく取られます。Rubyではそのような方法が必要になることはほとんどありません。

  • +
+

実はまだまだ拡張ライブラリの資産の蓄積という点でライバルたちに負けているのですが、今後ユーザーの増加とともに追いつきたいと考えています。その場合にはこの「拡張ライブラリの書きやすさ」が武器になるはずです。

+
+
+
+ +

まとめ

+
+

本稿ではRuby言語の実装とそこに使われている手法を特に実行効率の向上という点を中心に解説しました。しかし、Rubyはソースコードの規模が5万行を超えるかなり大規模なソフトウェアですから、ここでは簡単にしか説明できなかったことばかりです。

+

興味のある人は実際にRubyを入手し、使ってみて、あるいはソースコードを眺めてみてください。Rubyは入手も利用も無料です。そして、できればプログラミング言語の実装の楽しみに目覚めていただければと願っています。

+

Happy Hacking!

+
+

参考文献

+

書籍:

+

『オブジェクト指向スクリプト言語Ruby(仮題)』, まつもと ゆきひろ・石塚圭樹共著, アスキー(近刊)

+

『詳説 正規表現』, Jeffrey E.F. Friedl著, オライリー・ジャパン

+


+

URL:

+

http://www.netlab.co.jp/ruby/jp/

+

Rubyホームページ。Rubyに関することなら何でもここから参照できる(はず)。

+

http://ruby.freak.ne.jp/

+

Unofficial Ruby Home Page。本家Rubyホームページよりもはるかに出来がよい。
+メーリングリストのトピックのまとめが秀逸。

+

http://www.jin.gr.jp/~nahi/link-Ruby.html

+

Linkavailable: Ruby。Ruby関連のページの更新状況がひと目でわかる。

+

http://cuiwww.unige.ch/langlist

+

The Language List(英語)。古今東西のコンピュータ言語に関する情報が集められている。ここにはRubyは登録されておらず、Rubyという名の別の言語(ハードウェア記述言語)が登録されている。

+

http://www.iecc.com/gclist/GC-faq.html

+

GC FAQ(英語)。ガベージコレクションに関する「よくある質問とその答」集。歴史的な事情からGCはよく誤解されている(遅い、重い、使えないとか)ので、その誤解に対する答を提供している。

+
+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-003.xhtml b/docs/vol1/xhtml/p-003.xhtml new file mode 100755 index 0000000..bed5f33 --- /dev/null +++ b/docs/vol1/xhtml/p-003.xhtml @@ -0,0 +1,675 @@ + + + + + +第2章 オブジェクト指向スクリプト言語Ruby + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay2 +
+

+Free Language Report
+オブジェクト指向スクリプト言語
Ruby +

+
+

[C Magazine, 1999年8月号]

+
+

第1章とほぼ同じ時期の記事です。こちらは内部実装ではなく、言語そのものを紹介する記事ですね。実際にRubyを使っている人のインタビューがあるのもいいですね。

+

興味深いのは「楽しいプログラミング」というスローガンがすでに登場しているところです。1998年に開催されたPerl Conference Japanでゲストスピーカーとして講演したときにはまだこのスローガンは登場していませんから、Rubyの代名詞と言えるこのスローガンが固まったのは1999年頃であると考えてよいでしょう。ちょうどこの頃は、同じ年の秋に出版されたRubyを取り扱った最初の書籍になる『オブジェクト指向スクリプト言語Ruby』(アスキー)の原稿を執筆中だったはずですから、その中で思いついたスローガンだったのではないかと思います。もう20年以上前のことなのですっかり忘れてしまっていますが。

+
+
+

読者の方はどんな開発環境を使っているのでしょうか? もしかしたら今使っている開発環境よりもすばらしい環境がネットワーク上で配布されているかもしれません。本連載では、そのときの旬ともいうべきフリーソフトウェア/シェアウェアの開発環境を1つ取り上げ、その特徴と実際にどのようなプログラムを作ることができるかを深く解説していきます。今回はスクリプト言語にオブジェクト指向を取り入れたRubyについて解説します。

+
+
+

Rubyとは?

+
+

「Ruby」とは筆者によって開発された比較的新しいオブジェクト指向スクリプト言語です。Rubyは ’93年に開発が始まりました。いわば ’80年代生まれのPerlやPythonよりも若い言語です。実際、Perl(真珠は6月の誕生石)を意識して、Ruby(ルビーは7月の誕生石)と名付けられています。

+ +

Rubyはプログラミング言語とオブジェクト指向の大好きな人間(筆者のこと)が、多数の言語の特徴を踏まえつつ、自分好みの理想の言語の追求の結果、生まれた言語です。そのため、スクリプト言語を作ろうとしてできた言語ではなく、理想の言語を追求したら結果としてスクリプト言語になったのいうのが実情です。

+
+

表2.1●Rubyの概要

+ + + + + + + + + + + + + + + + + + + + + + + + + +
名称Ruby
バージョン1.3.4(開発版)/ 1.2.6(安定版)
形態オブジェクト指向スクリプト言語
作者まつもとゆきひろ
主な配布先http://www.netlab.co.jp/ruby/jp/
対応プラットホームLinux, FreeBSD, BSD/OS, Solaris, AIX, HP/UX, その他多くのUNIX系OS, BOW, Windows 95/98/NT, MS-DOS, Mac, BeOS, OS/2, NEXTSTEP, その他
+
+
+

Rubyの特徴

+

主要な特徴としては、

+
    +
  • フリーソフトウェア

  • +
  • インタプリタによる手軽さ

  • +
  • 強力な組み込み機能

  • +
  • オブジェクト指向による統一感

  • +
  • どこでも動くポータビリティ

  • +
  • 小さなプログラムから大きなプログラムまでカバーするスケーラビリティ

  • +
+

があります。もう少し細かくRubyの特徴をあげると、

+
    +
  • 型宣言が不要

  • +
  • 書きやすく読みやすい文法

  • +
  • イテレータ

  • +
  • 例外処理機能

  • +
  • システムプログラミング

  • +
  • ネットワークプログラミング

  • +
  • メモリ管理が不要

  • +
  • Cによる拡張が容易

  • +
  • ダイナミックローディング

  • +
  • スレッド機能

  • +
+

などがあります。

+

RubyはもともとUNIX生まれで、開発は主にLinux上で行われています。しかし、現在では多くのプラットフォームに移植されています。現在動作が確認されているプラットフォームは表2.1のとおりです。

+
+
+ +

Rubyのインストール

+

UNIX系のOSの場合、ソースコードを展開したあと、

+
+
configure
+make
+make install
+
+

の手順でインストールできます。他の環境では、それぞれのドキュメントを見てインストールしてください。なお、原稿執筆時点での最新バージョンは1.3.4です(付録CD-ROMに収録)。また、安定版の最新バージョンは1.2.6です(付録CD-ROMに収録)。

+
+
+
+

Rubyの役割

+
+

Rubyはオブジェクト指向スクリプト言語としての前述した特徴により、「お手軽プログラミング」を応援します。具体的なRubyの適用分野としては次のようなものが考えられます。もっともこれらはほんの一例にすぎません。

+
+

テキスト処理

+

テキスト処理はスクリプト言語の本来の目的と言っても過言ではありません。Rubyは強力なファイルクラス、文字列クラスと組み込みの正規表現機能によってテキストを自由自在に操作できます。それにRubyは国産ですから、日本語(EUC, SJIS, UTF-8)の操作も自然にできます。

+
+
+

CGI

+

インタラクティブなWebページを提供するCGI(Common Gateway Interface)プログラムは現在Perlの独擅場と言っても過言ではないでしょう。しかし、CGIプログラムの本質はテキスト処理であり、もともとRubyに向いた分野であるといえます。現在では普及の度合いでPerlに負けているとはいえ、スクリプト言語としての性質と、書きやすさ、読みやすさからCGIにおけるRubyの将来は明るいと信じています。

+
+
+

ネットワークプログラミング

+

Rubyの持つソケットクラスライブラリは非常に簡単にネットワークプログラミングを実現できます。従来ならばCなどによる複雑な手続きによって実現されていたものを簡単なスクリプトで書けるのです。あとで紹介しますが、特別なライブラリを使わずにそこそこちゃんとしたHTTPクライアントをわずか20行程度で記述できるのはなかなか感動モノです。最新のRubyでは、まだ実験段階ですがIPv6ソケット機能も提供されています。

+
+
+ +

GUI

+

Tcl/Tkの成功により、GUI(Graphical User Interface)がスクリプト言語に向いていることはすでに証明されたと思います。GUIはせいぜい人間の反応速度に追いつけば十分なので、それほど実行速度を要求しないからです。

+

RubyはTkインターフェイス、Gtkインターフェイスを始めとするGUIライブラリを持っていて、お手軽GUIプログラミングを支援しています。このGUIライブラリを利用してIMAP4メールリーダーSGmail(後述)のようなかなり規模の大きなものも開発されています。

+
+
+

XMLプログラミング

+

XML(eXtensible Markup Language)はスタイルではなく、データ構造を表現するためのマークアップ言語です。XMLの処理は基本的にテキスト処理なのでスクリプト言語に向いた領域です。XMLはUnicodeを扱うことができなければならないと決められていますが、Rubyは日本語の標準的なエンコーディングであるEUCとSJISに加えてUTF-8を標準でサポートしていますから、そういう観点からもXML向きといえます。

+

RubyのXML対応としては、吉田正人氏によってXMLパーサーの「expat」に対するインターフェイスライブラリが開発されています。なお、RubyのUTF-8対応も吉田氏によるものです。

+

RubyによるXMLプログラミングやexpatインターフェイスについては、吉田さんのページ(http://www.yohidam.net/)を参照してください。

+
+
+

プロトタイピング

+

スクリプト言語は小さめのプログラムを対象にすることが多いですが、決して小さいプログラム専用というわけではありません。特にRubyはオブジェクト指向機能によるモジュール化機能などのおかげで、他のスクリプト言語では無理がある大きいプログラムでも自然にプログラムできます。これによりどのようなプログラムでもインタプリタ言語の生産性をそのまま生かして開発することができます。

+

この生産性により本格的なプログラムのプロトタイプの開発に用いられることもありますし、実行速度などの事情が許せば、そのまま本番のアプリケーションとして採用できることも多いと思います。速度的に問題のある部分だけをC言語などで記述するというのもよく取られるアプローチです。

+
+
+

プログラミング教育

+

Rubyはとっつきやすく、またプログラムをすぐに実行できますから、プログラミング教育用の言語としても適しています。ただし、「『教育用にも使える』とあれば教育用にしか使えない」というマーフィーの法則はRubyには当てはまりません:-)。

+

教育用プログラミング言語としてRubyを見る場合、オブジェクト指向やスレッドプログラミングなどのやや目新しい機能まで手軽に使える点が重要です。改めて考えてみると、オブジェクト指向機能やスレッド機能が自由に使えるお手軽な言語というのはなかなかないようです。オブジェクト指向機能を持つスクリプト言語はあっても、多くはその機能は後付けで統一感に欠ける場合が多いようです。Rubyはそのようなところもカバーします。

+
+
+
+ +

Rubyの設計思想「楽しいプログラミング」

+
+

ここで機能や役割ではなく、Rubyを真に特徴付けている「設計思想」について語りましょう。それは「楽しいプログラミング」です。

+

本来、プログラミングは楽しいものです。無から有を創り出す創造的な活動であるというだけでなく、問題を解決するパズル的な楽しみをも提供してくれます。しかし、コンピュータが融通の効かない堅物であることを反映して、プログラミングには面倒な約束事がつきものです。決まりきった設定や、事細かな宣言などは「気軽なプログラミング」には不要です。Rubyはこのようなプログラミングの面倒な側面をできるだけ取り除くことで、楽しい側面に集中できることを目指しています。

+

たとえば、

+
    +
  • 思ったことがすぐに実行に移せる

  • +
  • 重要でないことはしなくてよい

  • +
  • 気を付けることが少なくてよい

  • +
  • よくやることは簡単に

  • +
  • やろうと思えば何でもできる

  • +
+

などは、Rubyが持つ性質の一部です。Rubyがインタプリタ型の言語なのも、変数に宣言が不要なのもみなこの目的のためです。

+

このような「使いやすさ」というのは人間の心理的性質を反映しています。人間の心というのは難しいもので、単純でも複雑でも使いにくいと感じるようです。結局は重要なのはバランスであり、Rubyは「設計者が使いやすいと感じるバランス」に基づいて設計されています。そのバランスに共感できる方は、Rubyを使っていて楽しいと感じられることでしょう。

+
+
+

Rubyプログラミング入門

+
+

さて、一般的な紹介はこのくらいにして実際のRubyのプログラムを見てみましょう。まずは定番のhello worldです(リスト2.1)。

+
+

リスト2.1●“hello world”を表示するスクリプト

+
# ハローワールド
+print "hello world\n"
+
+
+

このままでは、あまり面白くありませんね。言語の特徴もそれほど出ていません。実際このプログラムをPerlに読ませるとそのまま実行してしまいます。

+

もう少しだけ複雑な例を見てみましょう。以下はUNIXの標準的なコマンドであるgrepのサブセットです(リスト2.2)。grepは第1引数で指定した正規表現のパターンにマッチする行を出力します。

+ +
+

リスト2.2●“grep”と同じ処理をするスクリプト

+
# 4行で書くgrep
+pat = Regexp.compile(ARGV.shift)
+for line in ARGF
+  print line if pat.match(line)
+end
+
+
+

リスト2.2は小さなプログラムですが、これくらいでもRubyの特徴を知ることができます。たとえば、制御構造がendで終わることがわかります。これはややPascalを思い出させる文法ですね。他にも以下のような文法の特徴がわかると思います。

+
    +
  • 変数に宣言がいらない

  • +
  • Perl譲りの後置ifがある

  • +
  • スクリプト引数はARGVに入っている

  • +
  • 引数を連結した内容はARGFで読み込める

  • +
  • pat.match(line)でメソッド呼び出し

  • +
+

Rubyは組み込みクラスである文字列(String)、正規表現(Regexp)、配列(Array)、ハッシュ(Hash)、数値(Numeric)、入出力(IO)の各クラスが非常に強力で、これらだけでかなりの仕事ができます。

+

スクリプト言語の得意分野であるこのような小さなプログラムをいくつか紹介しましょう(リスト2.3)。

+
+

リスト2.3●ファイルに行番号を付けるスクリプト

+
# ファイルに行番号をつける
+while gets()
+  print $., "", $_
+end
+
+# πの計算
+print Math.atan2(1,1)*4, ""
+
+# 入力ファイルの"From "を含む行を印刷する
+ARGF.each{|line| print line if /^From / =~ line}
+
+# 引数で指定したファイル名を小文字にそろえる
+ARGV.each{|path| File.rename(path, path.downcase)}
+
+# 文字コードを自動判別してJISに変換するフィルタ
+require "kconv"
+ARGF.each{|line| print Kconv.tojis(line)}
+
+
+ +

リスト2.3では、

+
ARGF.each{...}
+

という表現が出てきました。これはARGFの各要素(各行)に対して繰り返しを行うという意味です。このようなメソッドは「イテレータ」と呼ばれ、繰り返す処理を抽象化しています。つまり、{...} という形で与えられた処理(ブロックと呼ぶ)を各要素に対して評価することで、要素の取り出しや終了チェックなど処理をメソッドにすべて任せてしまうことができるわけです。Rubyはこのようなイテレータを自由に定義できることが特徴の1つとしています。

+
+

Rubyを使ったソケット操作

+

さて、次にはもう少しだけ大きめのスクリプトを見てみましょう。リスト2.4に示すのは、URLの存在チェックを行うHTTPクライアントです。

+
+

リスト2.4●HTTPページチェッカー

+
# 最小のHTTPページチェッカー
+require 'socket'
+
+url = "http://localhost/"
+host, port, path = "localhost", 80, "/"
+if %r#http://(.*?)(?::(\d+))?(/.*)# =~ ARGV[0]
+  url = ARGV[0]
+  host = $1
+  port = Integer($2) if $2
+  path = $3
+end
+
+begin
+  s = TCPsocket::open(host, port)
+  s.print "HEAD #{path} HTTP 1.0\r\n\r\n"
+  if /^HTTP.*? 200 OK/i =~ s.read
+    printf "URL<%s> exists\n", url
+    exit
+  end
+rescue
+end
+printf "URL<%s> does not exist\n", url
+
+
+

このスクリプトをURLを引数に実行するとHTTPサーバーにアクセスして、そのページが存在するかどうかを判定してくれます。ただし、現状ではHTTP以外のプロトコルには対応していないので、任意のURLというわけにはいきませんが、これでも結構役に立つと思います。

+ +

このスクリプトは正規表現を使ってURLの解析を行い、ソケットクラスを使ってHTTPサーバーに接続しています。あとはHTTPサーバーの反応を正規表現でチェックして、ページが存在するかどうかを表示することになります。

+

Rubyではプログラムの実行中に発生したエラーは例外という形で報告されます。例外が発生するとプログラムの実行は中断されます。またrescue節があると、そこで捕捉することができます。HTTPページチェッカーでは、beginからrescueまでの範囲内で発生するすべての例外をまとめて捕捉しています。これにより、たとえば、

+
    +
  • 実行したマシンがネットワークにつながってない

  • +
  • 指定したホストが存在しない

  • +
  • ホストでHTTPサーバーが動作していない

  • +
+

などの問題が発生すると直ちにrescue節にジャンプします。リスト2.4のその部分には何もないので、そのまま処理の続きを実行することになります。このような問題が発生する場合には、「指定されたページは存在しない」とみなすことができるので、そのまま「存在しない」と表示すればOKです。

+

このようにエラーに対して例外が発生することによって個別の問題に対してユーザーがいちいちチェックする必要がなくなっています。

+
+
+

RubyのGUI操作

+

次にRubyではGUIも手軽であるところを見てみましょう。リスト2.5はボタンを1つだけ持つGUIプログラムです。

+
+

リスト2.5●GUI操作サンプルスクリプト

+
# Ruby/Tk
+require 'tk'
+TkButton.new(nil, 'text'=>'hello', 'command'=>'exit').pack
+Tk.mainloop
+
+
+

これまでに説明してきたソケットやTkインターフェイスは、Rubyインタプリタに組み込みの機能ではなく、拡張ライブラリと呼ばれる追加機能です。Rubyではこのような拡張ライブラリを用いてインタプリタに機能を追加することができます。ソケットやTkインターフェイス以外にも次のような拡張ライブラリがあります(ほんの一部です)。

+ +
+
+
+

Rubyにおけるオブジェクト指向とは?

+
+

Rubyはオブジェクト指向スクリプト言語だといいながら、ここまでの説明ではオブジェクト指向プログラミングにつきものの「クラス定義」だとか「継承」がまったく登場しなかったのにお気付きでしょうか。

+

もちろん、Rubyにもクラス定義や継承の機能はありますし、それらを活用したオブジェクト指向プログラミングはRubyの得意分野でもあります。しかし、Rubyのオブジェクト指向プログラミングの本質は「扱うデータがすべてオブジェクトである」ことにあります。あらゆるデータを統一的にオブジェクトとして扱うことができることが重要なのです。Rubyのプログラムは、一見通常の非オブジェクト指向言語のプログラムと同様に見える場合が多いのですが、その実態は「データはすべてオブジェクト」「手続きはすべてメソッド」とかなり徹底してオブジェクト指向しています。このような「すでに定義されているクラスを活用するオブジェクト指向プログラミング」はRubyの支援するプログラミングスタイルです。Rubyに組み込みの主要なクラスライブラリを図2.1に示します。

+
+ +
+ fig0201 +
+

図2.1●Rubyの主要クラスライブラリ

+
+

強力なクラスライブラリを利用した手軽なオブジェクト指向プログラミングの例として、時間の計算をあげてみましょう。2000年問題を意識して「2000年1月1日は何曜日か」という問題を考えてみましょう。多くの言語では何十行ものプログラミング必要になりそうですが、Rubyなら、

+
+
p Time.mktime(2000,1,1)
+
+

の1行だけです。実行すると、“Sat Jan 01 00:00:00 JST 2000”と表示され、土曜日であることがわかります。ここでは時刻表現のためのTimeクラスが面倒な計算を引き受けてくれています。

+

既存のクラスを利用するオブジェクト指向プログラミングの次の段階として、クラスやモジュールを定義するより高度なオブジェクト指向プログラミングが待っているわけです。

+ +

ここではより高度なオブジェクト指向プログラミングについてはこれ以上は説明しませんが、クラス定義の実例は紹介しておきます(リスト2.6)。あまり実用的な例ではありませんが、基本的な仕組みはわかるはずです。

+
+

リスト2.6●Rubyによるクラス定義

+
class Person
+  def initialize(name)
+    @name = name
+  end
+  def say
+    print "I'm #{@name}.\n"
+  end
+end
+
+# Personを継承したStudentの定義
+class Student<Person
+  def study
+    print "studying...\n"
+  end
+end
+
+
+
+
+

Rubyの「気持ち良さ」の秘密

+
+

ここまでで、ある程度Rubyのプログラムの雰囲気をつかめていただけましたでしょうか?

+

限られた誌面でまったく新しいプログラミング言語を紹介することは難しいことです。Ruby言語の詳しい説明はRubyホームページにあるリファレンスマニュアル(http://www.netlab.co.jp/ruby/jp/man-1.3)を参照していただくとして、ここからは再びRubyの設計思想に注目して、その「使いやすさ」「楽しさ」「気持ち良さ」の理由を追求してみたいと思います。

+

もともとプログラミング言語のできることは機能という観点から考えると、特定の目的に限定されたものを除けば、実行効率などといった以外の点ではどれも大差はありません。ですから、言語の違いはその「できること」ではなく、むしろその「使い勝手」によって発生します。そして、多くの場合にはほんのわずかの言語仕様の違いが大きな使い勝手の違いを生みます。ここでは、その「違い」について説明しましょう。

+
+

インタプリタ型言語

+

Rubyはインタプリタ型の言語ですから、コンパイルやリンクなどの手順なしにプログラムをいきなり実行できます。バグを修正したらすぐに再実行できます。プログラミング、実行、修正、再実行のサイクルを非常に素早く回せるのは、古き良きBASICの時代のような快適さです。

+ +

インタプリタ型言語の欠点はコンパイル型の言語に比べて実行速度が遅いことですが、幸いなことにコンピュータの実行速度はどんどん向上しています。今や一昔前のスーパーコンピュータなみのCPUパワーが手元にある時代です。もはやほとんどの分野で実行速度の遅さは問題にならなくなっています。

+
+
+

高機能言語

+

ほとんどの場合、プログラマーは事細かなデータ構造や些細ささいなアルゴリズムを考えるよりももっと仕事の本質に集中できれば、と考えていると思います。

+

Rubyは日常生活のプログラミングで最もよく使うと思われる機能が、クラスライブラリという形で組み込まれています。多彩な文字列処理機能や正規表現を含めて、数値、配列、ハッシュなどのRubyの組み込み機能は非常に強力で、やりたい仕事を簡単に片付けることができます。

+

また、もともとRubyには組み込まれていない機能でも、拡張ライブラリを使ってどんどん取り込んでいくことができます。拡張ライブラリによってRubyはますます強力に、ますます使いやすくなります。

+
+
+

簡潔な表現

+

Rubyはプログラミングが簡潔になる言語です。変数の宣言は必要ありませんし、組み込みのクラスライブラリが高機能であることも手伝って、多くの場合「やりたいこと」だけに集中できます。たとえば、引数で指定されたファイル群の各行を逆順でソートして出力したければ、

+
+
print ARGF.readlines.sort.reverse
+
+

だけで済みます。これは「ARFの内容を読み込んで(readline)、ソートして(sort)、反転(reverse)する」と読みます。日本語の語順で連ねて記述できるのが気持ち良いですね。

+

プログラミングの「約束事」に縛られず、プログラミングの本質に集中できるのは気持ちの良いことです。よくアルゴリズムの説明にいわゆる約束事を省略した疑似コードを使うことがありますが、Rubyはその疑似コードと同じような高いレベルの記述をそのまま実行することができます。また、コンパクトなプログラムが書けるので、手軽に問題を片付けるのにぴったりです。

+

しかし、簡潔な表現は度が過ぎるとプログラムが暗号になってしまうという問題もはらんでいます。簡潔な表現によってプログラムを圧縮しすぎると読めないプログラムを大量生産してしまうことになります。このことはPerlユーザーがよく経験していることではないでしょうか。

+

Rubyは次に述べるバランスのとれた文法によってこの問題に対応しています。

+
+
+

シンプルでバランスのとれた文法

+

プログラミング言語の文法は言語設計者の最大の関心事です。文法は言語の性質や使い勝手を決める重要な要素です。

+

プログラミング言語はいろいろな局面で使われますから、あらゆる状況に対応するために文法が肥大する傾向があります。言語設計者としてはよさそうな機能をどんどん取り込みたいという気持ちは理解できます。しかし、文法が大きすぎると今度は破綻します。古くはPL/IやAda、最近ではC++のような言語がこの症状に陥っています。大きすぎる文法によって全容を把握できないような言語は結果として使いにくくなってしまいます。

+
+

覚えることは少ないほうがよいですから、小さめの文法のほうが望ましいのですが、小さければよいというわけではありません。たとえばアセンブラは非常に小さな文法を持つ言語ですが、これが使いやすいという人はそんなにいないと思います。また、LispやSmalltalkは文法の小さい言語として知られていますが、単純すぎるせいで、かえって特異な文法となり、人間にとって自然な表記でなくなって、なじみにくかったり、熱狂的なファン以外には広く受け入れられなかったりしています。

+

スクリプト言語は歴史的には用途限定の簡易言語であったので、文法にあまり気を使わなかったり、あるいは特定の目的に特化した文法を持つことが多かったようです。目的に特化した文法は確かにその目的には便利ですが、逆にその言語の適用範囲を狭めることになりがちです。

+

そこで、結論としては、文法もバランスが大切だということです。大きく複雑すぎる文法も、小さく単純すぎる文法も結局は使いにくい言語となってしまいます。適度に高機能で、適度に単純な言語こそが本当に使いやすい言語になり得るのだと思います。もっとも使いやすさには多分に主観が入るので難しいのですが。

+

Rubyでは、

+
    +
  • 覚えることが少なくて済む単純な(ように見える)文法

  • +
  • やりたいことがすっきり書ける程度の高機能な文法(演算子とか正規表現など)

  • +
  • 将来の拡張に備えた汎用的な文法

  • +
+

などに気を使っています。

+
+
+

メモリ管理からの解放

+

オブジェクト指向言語は実行に伴い、大量のオブジェクトを割り当てます。これらのオブジェクトを管理して、いつ不要になるかを検出するのは苦痛が伴います。はっきりいうとそんなことを気にするよりもプログラマーにはもっと他にやりたいことがたくさんあります。

+

Rubyはガベージコレクトと呼ばれるメモリ管理機能を持っています。この機能により、もうどこからも参照されなくなったオブジェクトはインタプリタが自動的に検出して、解放してくれます。これで機械的で非人間的なメモリ管理から解放されます。

+
+
+

エラーチェックからの解放

+

Rubyの「面倒からの解放」はメモリ管理にとどまりません。Rubyは例外機能によって、エラーチェックからもプログラマーを解放してくれます。

+

例外についてはすでに説明しましたが、この機能により、個別のエラーチェックを省略することができて楽ちんなだけでなく、エラーを検出しないまま異常な処理を行ってしまう事態を自動的に避けられます。それでいてエラーに対処したいときにはきちんと対処できる柔軟性も持ちあわせています。

+
+
+ +

オブジェクト指向による統一感

+

Rubyではあらゆるデータがオブジェクトです。文字列や数値あるいは配列のような基本的なデータ構造を特別扱いする言語が多い中で、Rubyでは言語全体がオブジェクトというモデルで統一されています。

+

人間の意識は例外を嫌います。たとえば文字列の扱いが特殊であれば一瞬「えーと、どうだったっけな」と悩むことになります。この微細な「悩み」がプログラマーへのストレスとなり、「気持ち良さ」を阻害します。Rubyが純オブジェクト指向言語であるということはそういう観点からも「使いやすさ」を支援しています。

+
+
+

抽象化

+

「抽象化」とは「詳細を隠蔽いんぺいして、より高いレベルで処理する」ことで、プログラミング上の重要な概念の1つです。たとえば一連の手続きを関数としてまとめることは「手続きの抽象化」と呼ぶことができます。「手続きの抽象化」の具体的な例として、階乗を求める手続きは、

+
+
def fact(n)
+  if n == 1
+    1
+  else
+    n*fact(n-1)
+  end
+end
+
+

のように定義できます。このようにしておけば、あとは階乗をどのように計算するのかその方法を忘れてしまっても、

+
+
fact(10)
+
+

とすることで計算できます。

+

Rubyは抽象化を支援することに力を注いでいます。たとえば、

+
    +
  • メソッド(関数)による手続きの抽象化

  • +
  • オブジェクトによるデータ構造の抽象化

  • +
  • 例外によるエラー処理の抽象化

  • +
  • ブロックによるループの抽象化(イテレータ)

  • +
+

などがそうです。

+

抽象化において重要なのは「詳細を上手に隠すこと」です。つまり、抽象化機能で詳細を隠蔽されたものは、あたかもその機能が最初から存在していたかのように自然に内部を隠すことが望ましいということです。

+

具体的にはユーザー定義のクラスやオブジェクトに対して不自然な区別や制限を与えないことです。いくつかの言語、特にスクリプト言語ではこの部分が不十分で、ユーザー定義の手続きの呼び出し方が組み込みの手続きと違っていたり、ユーザー定義オブジェクトの機能が組み込みオブジェクトに対して劣っていたりします。Rubyの設計は、組み込みの機能とユーザー定義の機能とのアクセス手段の違いができるだけ起きないようにして、このような制限がないように気を使っています。

+
+
+ +

拡張ライブラリが簡単

+

Rubyはインタプリタにもともと組み込まれている機能だけでなく、拡張ライブラリを取り込んで機能を強化することができます。しかも、多くのOSでは、この機能を実行時に取り込むことができます。もっとも、この機能はRubyの専売特許ではなく、最近のスクリプト言語であるPerlやPythonなどでも同様の機能があります。

+

これら他言語と比べてRubyの拡張ライブラリ機能の優れた点は、大きくいうと2つあります。

+

まず第一の点は拡張ライブラリの中でのメモリ管理が自動化されていることです。Rubyは今使われているオブジェクトを自動的に検出するガベージコレクタを採用していますから、拡張ライブラリの中で明示的にメモリ管理を行う必要がありません。他言語ではオブジェクトの参照数の管理などが発生しますし、それを忘れると面倒なエラーの原因になりますから、これは非常にうれしい点です。

+

もう1つの点は、Cで記述された拡張ライブラリ内で(言語による記法の違いはともかく)ほぼ同等以上のことができることです。拡張ライブラリでできることに制約のある言語が多い中、この点は当り前のようで当り前でない利点です。

+

このような利点により「楽に」拡張ライブラリを開発できることで、Rubyの「楽さ」と「気軽さ」はますます強められます。

+
+

コラム1: Ruby利用者インタビュー/
+鴫原厚博(しぎはらあつひろ)さん
+聞き手: まつもとゆきひろ

+

実際にRubyを使ってプログラムを開発している人の生の声を聞いてみましょう。最初はIMAP4 MUA(Mail User Agent)である鴫原さんです。

+


+

— まずは自己紹介をお願いします。

+

鴫原厚博といいます。アサカネットというプロバイダのネットワーク管理を生業なりわいにしています。

+

— SGmailとはどのようなものですか?

+

SGmailは、Ruby/Tkで書いたIMAPをサポートしたXのGUIで動くMUAです(図2.2, 図2.3、付録CD-ROMに収録)。基本的には、WindowsやMacのMUAと同じような機能を持っていますが、

+ +

などを特徴としています。IMAPの良さを生かした作りにしようと思っています。また、SSHでトンネリングする機能は他のMUAにはない機能だと思っています。

+
+ +
+ fig0202 +
+

図2.2●SGmailの起動画面

+
+
+ +
+ fig0203 +
+

図2.3●SGmailの実行画面

+
+

SGmailはGPLライセンスのもとで配布を行っています。ソースコードなどは、

+
    +
  • http://www.sgmail.org/

  • +
+

のホームページから入手できます。メーリングリストもあり、

+
    +
  • sgmail-list@sgmail.org

  • +
+

で最新の情報を流しています。興味のある方はぜひ参加してください。

+

— なぜRubyを選んだのですか?

+

Tcl/Tk, Perl/Tk, Ruby/Tkを比較検討した結果、

+
    +
  • Perlに言語のコンセプト(お手軽というところ^^;)が似ている

  • +
  • Perl, Tclより大規模なプログラミングに向いている(ように思った)

  • +
  • 日本語環境が比較的良好

  • +
+

ということで、Ruby/Tkで作成することにしました。また、初めての言語でチャレンジしてみよう、という気持もありました。

+

— Rubyプログラミングで気付いたことはありますか?

+

Rubyを使ってみて「オブジェクト指向もこういう言語なら使いやすいものになるんだな」と再認識しました。

+

また、手軽にRubyで書いたアプリケーションを動かすという意味で、Ruby/Gtkコンパイラがあるとよいですね。

+

— Rubyの良い点、困った点はありますか?

+

やはり、「柔軟で手軽な、しかし本格的なオブジェクト指向」「プロトタイピングに優れている」点が良いです。

+

困った点としては、やはりTk関係のドキュメントが少ないことがありますね。

+
+

— 今後の構想などはありますか?

+

現在、実験版の開発を行っていて、

+
    +
  • POP3モード

  • +
  • MIME送信

  • +
  • PGP/MIME

  • +
  • Plug-in

  • +
+

などを実装する予定です。

+

Tkを使うと一部Tclを使う部分がありますので、できればGtkで動くようにしたいと思っています。

+

また、SGmailだけではなく、SGftpとか、SGircとか、GUIを使ったアプリケーションファミリーを構成していきたいと思っています。UNIX以外のOSでの計画として、

+
    +
  • Windows版のフル機能化

  • +
  • MacOS版

  • +
  • BeOS版

  • +
+

なども考えています。

+

MacOS版に関しては、SGmail仲間に協力してもらっていますが、MacRuby、Mac版Tcl/Tkなどとの絡みもあり、思うようにいっていません。Mac版の開発に協力していただける方を探しています。

+

Rubyとともにより多くのOSで動作するようにしたいと思っていますので「私の環境でも動かしたい」という方がいらっしゃいましたら、ぜひSGmailの開発に参加していただきたいです。

+

— これからRubyを始めようとする人に先輩として一言お願いします。

+

Rubyを通して、プログラミングの楽しさや、面白さを発見してもらいたいです。そしてRubyの良さをじっくりと「味わって」みてください。

+
+
+

コラム2: Ruby利用者インタビュー/
+松尾尚典(まつおひさのり)さん
+聞き手: まつもとゆきひろ

+

次のインタビューは、Webを使った会合調整サーバーであるMeeting2000の作者の松尾さんのお話をうかがいました。

+


+

— まずは自己紹介をお願いします。

+

松尾尚典といいます。世間的には男、32歳、独身、メーカー勤務、趣味ゴルフ、宴会好きなどという属性を持ちます。インターネット業界的には、ForUs(http://www.forus.or.jp/)の会長をやっています。またそのメンバーとして、いくつかのフリーウェアを出してきました。

+

ご連絡などのメールは、

+
+
    +
  • matsuo@present.forus.or.jp

  • +
+

へお願いします。

+

— Meeting2000とはどのようなものですか?

+

会合調整サーバー「Meeting2000」は、会合の幹事にとって最も難しい作業の1つである、参加者の確定および日程や場所の決定作業を支援するWeb上のアプリケーションシステム(付録CD-ROMに収録)です。そのためにシステムは、

+
    +
  • 幹事として、参加予定者の都合を収集したい会合を登録する機能

  • +
  • 参加予定者が、自分の都合を入力する機能

  • +
  • これらの情報を一覧表示する機能

  • +
  • 幹事が、参加予定者の都合に合わせて会合の設定を変更する機能

  • +
+

を備えています。このシステムを利用すれば、会合をより容易に開催することができるようになります。特に会合を開くことの多いコミュニティの方は、ぜひお試しください。作った僕がいうのもなんですが、正直、恐ろしく便利です^^。

+

— なぜRubyを選んだのですか?

+

生産性が(僕にとっては)最も高いというのが大きな理由です。

+

Ruby以前にはPerl 4を使っていました。Perl 4も生産性がかなり高い言語だと思います。僕もフリー物ではMeeting2000の前身のadj_meeting他、Nifty4UというのもPerl 4で書きました。実際Perl 4を気に入っていたのですが、Perl 5が出てからはあえて4を使うのもなんだし、かといって5は何だかなぁという感じで、使いたいと思う言語がありませんでした。Javaもそこそこいいんですけどね、まぁそこそこ。

+

そんなときにRubyと出合い、その書きやすさ、気持ち良さに惚れてしまったわけです。特に僕がアプリケーションを作る道具としての必要十分さ加減が、ちょうどいい感じなのです。書きたいところだけ書けて、あまり手をかけたくないなぁと思うところは書かなくて済む。それが僕にとって書きやすく、気持ちよいと感じるところだと思います。

+

Web上でドキュメントを眺めたときに、ビビビッ(すでに死語)ときましたね。

+

実のところ、私はコードを書くのがそんなに好きなわけではありません。ニーズを見つけ、要求を定義し、分析するまでが好きなのです。実装は誰か他の人がやってくれる、というのが一番^^; そんな私が選ぶぐらいのRubyの生産性の高さがすばらしいと思います。

+

— Rubyの良い点、困った点はありますか?

+

まつもとさんが一人で開発されている、というのがRubyの良い方、悪い方の両方に出ていると思います。

+

良い点は、まつもとさんが一人で開発されているので言語として一貫している、というところです。コードを書いていても言語のせいで迷うことがないし、まつもとさんの言語設計、実装基準を信頼しているという意味で、まつもとさんが開発している限り将来も付き合っていける安心感があります。

+
+

悪い点は、まつもとさんが一人で開発されているので、まつもとさんに何かあったらどうなるんだ、っていう心配です。この前のRuby Conference(’99年3月に行われた宴会)でお聞きしたのですが、そのときはどなたかがあとを継ぐ、ということになってはいるそうです。でもねぇ……。

+

他には現時点では本がないとか、メーリングリストの流量が大きすぎるとか、が困ったところです^^;

+

— 今後の展開などをお聞かせください。

+

Meeting2000は僕にとっては便利なアプリケーションで、今の自分が必要とする機能はあらかた盛り込んであります。それゆえにMeeting2000としての大幅な機能強化は実はあまり考えていません。「英語化」リクエストがいくつか、まつもとさんからもあがっているので、それだけはいつか何とかするつもりです。

+

これだけではなんですので、もうちょっと述べますと、Meeting2000も含めた将来構想として「グループウェアサーバー」を作るかもしれません。これは、Meeting2000などのWeb上で動作するグループウェアのプラットホームとなるものです。これがあるとグループウェアを作るのが楽だよ、みたいな感じです。

+

グループウェアを書きたい方はこの構想にご協力くださいませんか? ruby-listメーリングリスト(本文参照)にて連絡をお待ちします。

+
+
+
+
+

Rubyに関する情報/文献

+
+

Rubyに関する情報の入手法は以下のようなものがあります。

+
+
Rubyホームページ
+

http://www.netlab.co.jp/ruby/(英語)
+http://www.netlab.co.jp/ruby/jp/(日本語)

+

本家本元です。開発者(私)が直接メンテしています。Rubyに関するあらゆる情報がここから入手できます。

+
+
+
Ruby Application Archive
+

http://www.netlab.co.jp/ruby/jp/raa.html

+

Rubyで書かれたアプリケーションやライブラリなどの情報が集まっています。

+
+
+
Unofficial Ruby Home Page
+

http://ruby.freak.ne.jp/

+

「Unofficial」とはいいながら充実した情報がウリのページです。特に過去のメーリングリストのトピックが整理されているのが便利です。

+
+
+ +
FTPサイト(ソースコード入手先)
+

最新のソースは表2.2から入手できます。回線が遅い場合にはミラーサイトをご利用ください。付録CD-ROMには、最新版が収録されています。

+
+

表2.2●Rubyの配布先

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
場所URL
一次配布場所ftp://ftp.netlab.co.jp/pub/lang/ruby/
東京インターネットの
ミラーサイト
ftp://ftp.tokyonet.ad.jp/pub/misc/ruby/
IIJのミラーサイトftp://ftp.iij.ad.jp/pub/lang/ruby/
長岡技科大のミラーサイトftp://blade.nagaokaut.ac.jp/pub/lang/ruby/
熊本地域ネットワーク
(IIJ下流)のミラーサイト
ftp://ftp.krnet.ne.jp/pub/ruby/
名古屋商科大学の
ミラーサイト
ftp://mirror.nucba.ac.jp/mirror/ruby
http://mirror.nucba.ac.jp/mirror/ruby
+
+
+
+
CVSリポジトリ
+

:psrever:anonymous@cvs.netlab.co.jp(パスワードはguest)

+

CVSを使って最新のソースコードを入手できます。場合によってはまだリリースされていない「本当の最新」が入手できることもあります。開発中のソースコードはバグを含んでいる場合もありますから、やや上級者向けです。

+
+
+
CVSWEB
+

http://cvs.netlab.co.jp/cgi-bin/cvsweb/ruby/

+

CVSリポジトリの内容をWebから参照できます。

+
+
+
メーリングリスト
+

メーリングリストはRubyに関するあらゆる情報の宝庫です。表2.3のようにジャンルによって分かれています。非常に活発に質問や議論が行われています。参加するためには、

+
    +
  • ruby-xxx-ctl@netlab.co.jp

  • +
  • (xxxにはそれぞれlist, dev, ext, talkが入る)

  • +
+

に本文に、subscribeと書いたメールを出します。

+
+

表2.3●Rubyに関するメーリングリストの種類

+ + + + + + + + + + + + + + + + + + + + + +
メールアドレス内容
ruby-list@netlab.co.jp一般向け
ruby-dev@netlab.co.jp開発者向け
ruby-ext@netlab.co.jp拡張ライブラリ開発者向け
ruby-talk@netlab.co.jp英語
+
+
+
+ +
ネットニュース
+

fj.comp.lang.ruby

+

Rubyのことを話し合うためのニュースグループも存在します。もっともメーリングリストに流量がとられてしまって、それほど記事は流れていません。もっと活用されてもよいのにな、と思います。

+
+
+
書籍
+

現在執筆中です。年内に数冊出版される予定です。

+
+
+
+

まとめ

+
+

今回はオブジェクト指向スクリプト言語Rubyを簡単に紹介し、さらにその根底にある思想について語りました。しかし、Rubyの「楽しさ」や「使いやすさ」は単なる説明ではとても伝わりません。実際に使ってみることで実感できると思います。どうぞRubyを使ってみて自分の経験でRubyの良さを理解してください。そして、「楽しいプログラミング」を。Happy Hacking.

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-004.xhtml b/docs/vol1/xhtml/p-004.xhtml new file mode 100755 index 0000000..17caf0c --- /dev/null +++ b/docs/vol1/xhtml/p-004.xhtml @@ -0,0 +1,386 @@ + + + + + +第3章 UNIX系OS間の移植性について + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay3 +
+

+UNIX系OS間の
移植性について +

+
+

[TransTECH, 1999年9月号]

+
+

「Rubyの父」としてではなく、歴戦のCプログラマーとして執筆した記事。同じ「UNIX系」と分類されるOSでもそれぞれ違いがあって、移植性のためには考慮すべきことがたくさんあり、それに対応する手段(特にautoconf)について紹介する記事。

+

UNIX系OSは事実上LinuxとmacOSだけと考えてもあまり問題のなくなった現代と違って、1990年代には本当に数多くのUNIX系OSがありました。コンピュータメーカーも今よりもずっと数が多く、それぞれの会社が独自のUNIX系OSを提供していました(表3.1)し、フリーソフトウェアに限っても、LinuxとFreeBSDがいい勝負で、どちらを選ぶか難しいという時代でした。この記事の背景にはそんな「UNIX系OS戦国時代」があります。当時のフリーソフトウェアはみんなこんなに面倒なことをして各種OSに対応していたんですよ。今は移植性を維持するのはずっと簡単になりました。いい時代になりましたね。

+
+

表3.1●コンピューターメーカーとUNIX系OS(代表的なもののみ)

+ + + + + + + + + + + + + + + + + + + + + +
メーカーOS
SunSolaris
IBMAIX
HPHP-UX
DECUltrix
+
+
+
+

はじめに

+
+

人間というのは自分の周りの環境が普遍的なものだと思い込んでしまう傾向があるようです。プログラミングにおいてもそれは例外ではありません。

+

Windows系プログラマーは下手にOS・アーキテクチャともに統一された環境にいるのでますますその傾向が強いようです。

+

一方、UNIX系OSではひとくちにUNIXといってもアーキテクチャも癖もさまざまなOSの集合ですから、このような分野における苦労を積んできています。複数のアーキテクチャで動作するプログラムを書くのはなかなかに大変なことです。各OSに独自の機能を使わなければそれで済むかといえば必ずしもそうでもありません。

+ +

本記事では、複数のOS・アーキテクチャで動作することを考慮したプログラムの開発において気を付けるべき点を簡単に紹介します。

+
+
+

整数のサイズ

+
+

C言語にはいくつかのサイズの整数があります。小さい方から順にchar, short, int, longです(long longがある処理系もあります)。

+

C言語の規格にはそれぞれのサイズは、

+
+
char <= short <= int <= long
+
+

であると定義してあります。逆にいうと具体的なサイズはまったく定義されていないということです。intは16ビットであると思い込むのはDOS時代を生きていた、すでに古いプログラマーですが、intは32ビットであると思い込むのは、同じくらい愚かなことです。

+

かつて汎用機にあったという36ビットとかいうようなヘンなアーキテクチャはとりあえず考慮しないにしても、ありふれた組み合わせだけでも表3.2のようなものがあります。

+
+

表3.2●整数とポインタのサイズ

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
charshortintlongvoid*
816163216
816323232
816326464
816646464
832646464
+
+

これらのサイズに暗黙の仮定を行っていると違うアーキテクチャに持っていくと破綻します。特に64ビット整数はこれからCPUの64ビット化に伴い出合うことが増えていくでしょう。

+

私の経験で結構ハマったのは、ポインタのサイズに対する仮定です。以前、あまり意識せずにintとポインタのサイズが同じであること、キャストによって相互変換できることを仮定してプログラミングしていました。

+

ところが、そのプログラムを違うCPU上の同じOSでコンパイルするととたんにクラッシュしました。そのマシンではintのサイズは32ビットですが、ポインタは64ビットでした。キャストすると情報が落ちてしまうのです。

+

結局えらく苦労してサイズへの依存性をなくしましたが、気を付けているようでも意外と暗黙の仮定をしているものです。

+
+
+

エンディアン

+
+

整数のサイズと並んではまる原因になるのはエンディアンです。アメリカ先住民……などとお約束をいうつもりはありませんが、エンディアンとはCPU上でデータをどのように表現するかの決まりです(図3.1)。

+
+ +
+ fig0301 +
+

図3.1●エンディアン

+
+

整数の最上位データをアドレスの最上位に置くのがビッグエンディアンで、その逆がリトルエンディアンです。よく使われるインテル系CPUはリトルエンディアンですし、SunやMotrolaはビッグエンディアンです。中にはMIPSやPowerPCのように設定次第でビッグエンディアンとリトルエンディアンどちらにでもなるというCPUもあります。

+ +

Cレベルではエンディアンが問題になることはほとんどないので、普段はあまりエンディアンを意識することはありませんが、ネットワークやファイルに生のバイナリデータを書き出そうとすると問題になります。リトルエンディアンでの1234という32ビット整数データをファイルに直接書き込んで、ビッグエンディアンのマシンで読み込むと3523477504という値になります。

+

ネットワークプログラミングでは伝統的にビッグエンディアンを使うことがしきたりになっています。このことからビッグエンディアンのことをネットワークバイトオーダーと呼ぶこともあります。

+
+
+

アラインメント

+
+

ほとんどのCPUではある特定の数の倍数のアドレスに対するアクセスが高速になっています。たとえばインテルのx86では、奇数アドレスのデータのアクセスよりも、偶数アドレスのデータのアクセスのほうが高速です。CPUによってはそもそも奇数アドレスに対するアクセスを許さないものもあります(そういう場合には偶数アドレスにアクセスして、シフトする)。

+

そのことを反映して、構造体の各メンバーはその先頭アドレスが特定の数の倍数になるように場合に応じてすき間が入れられることがあります(4または8バイト境界に置かれることが多いようです)。このようなアドレス配置のことをアラインメントと呼びます。アラインメントもプラットフォームごとに異なるので、エンディアン同様構造体を直接書き出したりする場合には注意が必要です。

+

エンディアンとアラインメントについては苦い思い出があります。以前、私が関わっていたプロジェクトで、データ転送の効率化のために、ネットワーク経由で直接生の構造体をやりとりするプロトコルが採用されたことがありました。出力には構造体の内容を直接書き出し、入力には読み込んできたデータを構造体としてキャストするわけです。この方法ではデータを展開したり、解析する必要がまったくないのでプログラミングは簡単で、通信コストも最小で済むということでした。

+

しかし、実際には整数サイズやエンディアンの問題もありますし、そもそも構造体のアラインメントはプラットフォームによってまちまちなので、そのようなプロトコルは危険だと何度か進言しましたが、このプロジェクトではネットワーク全体で同じ機種を用いるのでエンディアンの問題などは将来にわたって発生しないとのことでした。

+

実際には、数年後にその仮定は反故ほごになり、システムに対する別機種の導入が決定され、担当者は通信プロトコル処理部分の総取り換えという大変な目にあうことになります。

+

このことから未来のことは誰にもわからないことと、誰かが断言してもあまり簡単に信じてはいけないことがわかります。ですから、システムの設計にはある程度将来を見越した設計が必要であることもいえると思います。

+
+
+ +

ANSI C/POSIX

+
+

結局、移植性のための大原則は「決まっている以上のことを前提にしない」です。で、その「決まっていること」といえば、やはり規格ということになるでしょう。ここで重要な規格はANSI CとPOSIXです。

+

ANSI CはつまりC言語の仕様です。Cの文法は当然としても、ライブラリルーチンの多くもここで定義されています。この規格の範囲内であれば、UNIX系に限らず、ほぼあらゆるプラットフォームで動くわけですから、ぜひ押えておく必要があります。

+

しかし、ANSI Cには2つの点で限界があります。1つはいまだにANSI Cに対応していない処理系が存在することです。このような処理系にも対応したければ、より古い(そしてより厳密でない)仕様のCを使う必要があります。このような「古いC」をCのバイブルである『プログラミング言語C』という書物の愛称からK&Rと呼ぶことがあります。実際にはK&Rの第2版ではANSI Cを解説していますから、名前だけが慣習として残っていることになります。もっとも、最近ではANSIを満たしていない処理系は少なくなってきましたし、OS付属のCがANSI Cに対応していなくても、gccが使える場合がほとんどですから、この問題は次第に重要ではなくなってきています。

+

もう1つの問題は、世の中のプログラムはANSI Cだけで記述できるプログラムばかりではないことです。ANSIでは入出力を中心とした基本的な処理しか定義されていません。多くのプログラムはOSの提供するさまざまな機能や、より高度なライブラリを要求するでしょう。

+

そのような場合に役に立つのがPOSIXです。POSIXはIEEEという団体が定義したUNIX的OSに関する規格です。POSIXはシステムコールの挙動から、各種ツールの仕様までOSのほぼあらゆる分野を定義しています。ただ、問題は規格が大きすぎて完全に満たしているOSがなかなかないことです。ただ、ライブラリ/システムコールレベルでは多くのOSが満たしていますから、POSIXが重要であることに変わりはありません。

+
+
+

規格を超えて

+
+

規格を押えることはもちろん非常に重要ですが、それだけですべての問題が解決するわけではありません。たとえば、ライブラリの仕様がANSI CやPOSIXで定義されているものとは微妙に異なっているプラットフォームも数多く存在します。

+

歴史的にUNIXには亜流がたくさん存在し、しばしばそれぞれが微妙に異なる仕様を持っていました。POSIXはそれらを統一することが1つの目的でしたが、歴史的な事情から(あるいは別の理由から)、POSIXに準拠していないプラットフォームはいくらでもあります。

+

規格に従っているプラットフォームだけを相手にするのも1つの選択肢ですが、より多くのプラットフォームで動作させたいのであれば、ANSI CやPOSIXなどの規格は結局は参考程度にしか使えない場合が多いでしょう。

+

UNIX系OSでの問題に限っても、すでに述べた整数のサイズやエンディアン以外にも以下のような問題があります。

+
    +
  1. 同機能を持つ別関数
    +たとえば文字列から文字を探す関数はindex()strchr()の両方があり、どちらかしか持たないプラットフォームもあります。

  2. +
  3. 同名で仕様の違う関数
    +たとえばシステムコールのgetpgrp()はOSによって2引数を取る場合と、引数を1つも取らない場合があります。

  4. +
  5. ヘッダーファイルがない/名前が違う
    +たとえばmalloc用のヘッダーファイルとして <malloc.h> というヘッダーファイルがあるプラットフォームも、<stdlib.h> などでカバーするプラットフォームもあります。名前が違う例としては <string.h><strings.h> をあげておきます。

  6. +
+

古い時代のフリーソフトウェアでは、自分のプラットフォームに合わせてソースコードやMakefileの書き換えを要求したり、あるいはリスト3.1のようにプリプロセッサを使ってプラットフォームごとの個別の対応を追加したりしていました。

+
+

リスト3.1●個別の対応

+
#if defined(__hp9000s300) || (defined(__NetBSD__) && (!defined(__alpha__) &&
+ !defined(__mips__))) || defined(__BORLANDC__) || (defined(__FreeBSD__) && 
+__FreeBSD__ < 3) || defined(NeXT) || defined(__WATCOMC__) || defined(__APPLE__)
+...
+#endif
+
+
+
+
+

賢い対応(autoconf)

+
+

では、こんな不毛な対応よりもマシな方法はないのでしょうか。

+

もちろん、あります。

+

ここではこのような問題を解決を支援するツールであるGNU autoconfを紹介します。

+

フリーソフトウェアをコンパイルしたことのある人ならば、GNUソフトウェアを始めとする最近のさまざまなフリーソフトウェアのコンパイル・インストールが、

+
+
./configure
+make
+make install
+
+

という簡単な3ステップで行えることに気付いていることでしょう。このconfigureというプログラム(シェルスクリプト)が、そのプラットフォームで使える機能を確認して、その情報を取り出してくれるので、各プラットフォームの持つ機能に応じたコンパイルを行うことができます。

+

このconfigureというプログラムを生成してくれるツールが、autoconfです。

+ +

autoconfを引数なしで起動すると、カレントディレクトリにあるconfigure.inというファイルからconfigureプログラムを生成します(図3.2(a))。

+

configure.inに限らず、autoconfではファイルを生成する元になるファイルに .inという拡張子を付けることになっています。

+

このconfigureが実行されると、さまざまなチェックを行い、そのプラットフォームにおいてどのような機能を使うことができるかを検出してくれます。そして、その検出結果を反映したファイルをテンプレートから生成します(図3.2(b))。生成されるのは普通はMakefileです。

+
+ +
+ fig0302 +
+

図3.2●autoconf

+
+
+

autoconfによるチェック

+

configureでは以下のようなさまざまなチェックを行うことができます。

+
    +
  • 関数の存在チェック

  • +
  • ライブラリの存在チェック

  • +
  • ヘッダーファイルの存在チェック

  • +
  • 整数や各種データ型のサイズチェック

  • +
  • エンディアンのチェック

  • +
  • 各種ツール(コンパイラ・リンカなど)の挙動チェック

  • +
  • alloca/signalなど代表的なプラットフォームでの相違

  • +
  • その他、ほぼあらゆるチェック

  • +
+

具体的なconfigure.inの記述の抜粋をリストリスト3.2に示します。

+
+

リスト3.2●configure.inの記述(抜粋)

+
AC_INIT(main.c)       # 初期化(必ず存在するファイル名を指定)
+
+AC_PROG_CC            # コンパイラのチェック
+AC_PROG_INSTALL       # インストールプログラムのチェック
+AC_PROG_YACC          # yaccのチェック
+
+AC_C_CONST            # constがないコンパイラのチェック
+AC_C_INLINE           # inlineがないコンパイラのチェック
+AC_C_BIGENDIAN        # エンディアンのチェック
+
+AC_CHECK_SIZEOF(int)  # 整数のサイズチェック
+
+AC_TYPE_GETGROUPS     # 典型的な互換性チェック
+AC_FUNC_ALLOCA
+
+AC_HAVE_HEADERS(string.h)
+      # 関数の存在チェック
+AC_CHECK_FUNCS(strtoul)
+
+AC_OUTPUT(Makefile)   # 雛型(Makefile.in)からMakefileの生成
+
+
+ +

configure.inの中ではリストリスト3.2の中に登場するものを始めとして、さまざまなチェックを行うことができます。実際、ポータブルなプログラムを記述するために必要なほとんどあらゆるチェックを網羅しているといえます。詳細はここでは説明しきれませんので、autoconfのドキュメントを参照してください。

+

ここではautoconfの多彩なチェック機能のうち、特徴的だと思われる実際にプログラムのコンパイルを行うことによるチェックについて紹介します。

+

リスト3.3に、実際にコンパイルを行ってチェックを行う例を示します。これはCプリプロセッサマクロでの識別子の連結にANSIスタイルで新しく定義された ## を使うスタイルか、それとも古いK&Rで使われていた空のコメントを使うスタイルのどちらが使えるかを判定するものです。

+
+

リスト3.3●コンパイルによるチェック例

+
AC_TRY_COMPILE([#define paste(a,b) a##b],
+           [int xy = 1; return paste(x,y);],
+           rb_cv_tokenpaste=ansi,
+           rb_cv_tokenpaste=knr)
+if test "$rb_cv_tokenpaste" = ansi; then
+  AC_DEFINE(TOKEN_PASTE(x,y),[x##y])
+else
+  AC_DEFINE(TOKEN_PASTE(x,y),[x/**/y])
+fi
+
+
+

この指定により、プログラムではコンパイラの種類にかかわらず、TOKEN_PASTE(x,y)というマクロを使うことで、識別子の連結を行うことができます。

+
+
+

Makefile.in

+

configureの実行によって生成されるMakefileの雛型になるMakefile.inの例をリスト3.4に示します。

+
+

リスト3.4●Makefile.in

+
CC = @CC@
+YACC = @YACC@
+
+CFLAGS = @DEFS@
+OBJ = main.o
+
+main:   $(OBJ)
+    $(CC) $(LDFLAGS) $(OBJ) -o $@
+
+
+ +

Makefile.inの中に@CC@とか@DEFS@という文字列が登場します。この@〜@で囲まれた文字列がconfigureの検出結果に置き換わってMakefileが生成されます。生成されたMakefileリスト3.5に示します。

+
+

リスト3.5●生成されたMakefile

+
# Generated automatically from Makefile.in by configure.
+CC = gcc
+YACC = bison -y
+
+CFLAGS =  -DSIZEOF_INT=4 -DGETGROUPS_T=gid_t -DHAVE_ALLOCA_H=1 -DHAVE_ALLOCA=1 -DHAVE_STRING_H=1 -DHAVE_STRTOUL=1 
+OBJ = main.o
+
+main:   $(OBJ)
+    $(CC) $(LDFLAGS) $(OBJ) -o $@
+
+
+

@CC@はCコンパイラの名前、@DEFS@configureによって検出された各機能がプリプロセッサ用の定数として定義されていることがわかるでしょう。

+

プログラムのほうではこれらの定数を参照して、移植性の高いソフトウェアを開発することができるわけです。

+

autoconfを使っても違いを検出できるだけで、検出した結果を利用して移植性のあるプログラムを書くことは自分で行わなければなりません。残念なことにこの作業は(まだ)自動化できませんが、各種のOSで確実にコンパイルできるソフトウェアの記述はもはやautoconfなしには考えられません。

+

どのような条件を検出し、どのように対応するかは、それだけでも難しい問題です。しかし、幸いたくさんのプログラムのソースコードが公開されていますから、それらを参考にすることで多くのことを学ぶことができます。

+
+
+

configure.inの自動生成(autoscan)

+

configure.inの記述はなかなか複雑です。このような点をカバーする方法としてautoscanというプログラムが提供されています。autoscanを引数なしで起動すると、起動されたディレクトリに存在するソースコードをスキャンして、最低限必要と思われるチェックを含むconfigure.inの記述をconfigure.scanという名前で生成してくれます。このファイルに少々手を加えてconfigure.inという名前に置き換えれば、とりあえずautoconfを使ってconfigureを生成することができます。

+ +

autoscanの生成するconfigure.inは完全なものではありませんが、最初のとっかかりとしては十分なものを生成してくれます。あとは、これに手直しをしていくことで比較的容易にautoconf対応に移行することができます。

+
+
+

autoconfのインストール

+

autoconfの最新バージョンは2.13です。autoconfの実行にはマクロプロセッサのm4がインストールされている必要があります。

+

autoconfのインストールも、

+
+
./configure
+make
+make install
+
+

で完了します。autoconf自身がconfigureを利用しているのは面白いですね。

+

autoconfautoscanはそれ自身はGPL(GNU General Public License)が適用されるGNUソフトウェアですが、autoconfなどを使って生成したconfigureスクリプトは自分の望むライセンスで配布することができますから、GPLが使えない局面でもautoconfを利用することができます。

+
+
+

autoconfの仲間

+

誌面の関係から詳しくは説明できませんが、autoconfの仲間には、Makefile.inの生成を支援し、make時の標準的なルールを自動生成してくれるautomake, そして、種々のプラットフォームにおけるそれぞれに異なるダイナミックリンクライブラリの生成法を統一的に扱えるlibtoolがあります。

+

これらのツールおよびautoconfの詳細については

+
    +
  • http://www.gnu.org/software/autoconf/autoconf.html

  • +
  • http://www.gnu.org/software/automake/automake.html

  • +
  • http://www.gnu.org/software/libtool/libtool.html

  • +
+

を参照してください(英文)。

+
+
+
+

非UNIXへの移植

+
+

autoconfを用いても、各種プラットフォームに対応した移植性の高いプログラムを書くことはそれなりに大変ですが、ここまで対応すれば、かなりの確率で非UNIX環境でもコンパイルできます。

+

各種非UNIXプラットフォームにはUNIX系ソフトウェアのコンパイルを支援する処理系やツールが提供されています(表3.3)。これらを利用すれば、かなりのプログラムを動作させることができます。この中でも特にCygwin32は優れもので、シェルが提供されていますからconfigureスクリプトをそのまま動かすことも可能ですし、普通に考えればUNIX環境に固有に思えるXウィンドウシステムを使ったプログラムまでコンパイルできます。

+ +
+

表3.3●UNIXソース用ツール

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
DOSDJGPPコンパイラ + 32ビットエクステンダ + ライブラリ
WindowsCygwin32コンパイラ + ライブラリ + 各種ツール
MacMachTENコンパイラ + ライブラリ + 各種ツール
MacMWPコンパイラ + ライブラリ + 各種ツール
OS/2EMXコンパイラ + ライブラリ
+
+
+
+

まとめ

+
+

本記事では移植性を妨げる問題と、それに対する対応について駆け足で説明しました。限られた誌面では十分な解説はできませんでしたが、何かのきっかけになればと望んでいます。

+

移植性について学ぶ最善の方法は、実際に各種プラットフォームで動作するプログラムのソースコードを眺めることです。現在では各種フリーソフトウェアのソースコードが比較的容易に入手できます。移植性という観点からフリーソフトウェアを見てみるのも面白いかもしれません。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-005.xhtml b/docs/vol1/xhtml/p-005.xhtml new file mode 100755 index 0000000..f2b519c --- /dev/null +++ b/docs/vol1/xhtml/p-005.xhtml @@ -0,0 +1,227 @@ + + + + + +第4章 スクリプト言語の歴史 + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay4 +
+

+連載 スクリプト言語
+スクリプト言語の歴史 +

+
+

[bit, 2001年2月号]

+
+

私が高校生の頃の憧れの雑誌、『bit』への寄稿です。近所の本屋には置いてなくて、わざわざ取り寄せたのですが、アカデミックな雰囲気の『bit』は当時の私には手に負えなかった記憶があります。その10数年後にはその雑誌に原稿を書くまでになるとは。

+

この記事では、Rubyの作者という立場を離れて、スクリプト言語全般の流れをまとめています。当然のようにRubyの紹介部分は自画自賛満載ですね。

+

これは『bit』の「スクリプト言語」特集の連載の一部でした。当時はPerl, Python, Rubyと言った「スクリプト言語」がかなり注目されていたんですね。このすぐあとに、これまでの「スクリプト言語」という概念を置き換えるようなLL(Lightweight Language)という単語も登場して、注目はますます高まります。この記事の中でも、スクリプト言語の未来について予想していますが、「スクリプト言語の適用分野がテキスト処理を超えてますます広がる」という予想は、かなり当たったのではないかと思います。もっとも、JavaScriptを含めたスクリプト言語は、テキスト処理言語というよりもむしろ、Webアプリケーション向け言語と認識される未来は想像を超えましたし、さらにこのジャンルの多くの言語が(PHPやPython, TypeScriptのように)積極的に静的型宣言を受け入れるとはまったく予想できませんでした。

+
+
+

スクリプト言語の歴史とは?

+
+

前回のスクリプト言語座談会1 に参加したいきがかり上、「スクリプト言語の歴史」について記事を書いてほしいとの依頼を受けた。「私は若いから歴史は知りません」と答えたのだが、押し切られてしまった。額に「お人好し」とポストイットが貼ってあるらしい。

+

引き受けたはいいが、知らないものは本当に知らないのだからしょうがない。ここは、実体験に基づかない、言語オタクの耳学問から歴史を再構成することとしよう。

+
+
+ +

スクリプト言語の3世代分類

+
+

ここで前回の座談会でも紹介した「スクリプト言語の3世代分類」を改めて紹介しておこう。個人的な見解だが、いわゆるスクリプト言語と呼ばれるものは、実は3つの世代に分類できると考えている(表4.1)。

+
+

表4.1●スクリプト言語の3世代分類

+ + + + + + + + + + + + + +
第1世代処理に制御構造を導入するためのもの。アドホックな文法。できないことは外部ツールに任せちゃう。「シェル」「awk」など
第2世代とりあえず、何でも自分でできる程度に機能強化。第1世代のアドホックさを残している。「Perl」
第3世代第2世代を踏まえつつ、もっと「普通」のプログラミング言語になったもの。「Python」「Ruby」
+
+

これはどういうことかと考えるに、おそらく、玩具やツールボックスのたくさんある1つのツールにすぎなかった第1世代のスクリプト言語が、そのよい性質のゆえにだんだんと機能が強化され、普通のプログラミング言語のレベルに(そのよい性質を保ったまま)成長してきた、ということではないかと思う。

+

そこでスクリプト言語の本質を見極めるためにも、進化論よろしく、スクリプト言語の進化をたどることにしよう。ただし、先にも言い訳したとおり、筆者はこの歴史を体験してはいないので、適当に自分に都合のいいように歴史を再構成していることは了承していただきたい。

+
+
+

スクリプト言語の出発点: シェル

+
+

スクリプト言語にとっての原点となる存在は、やはりシェルだろう。シェルと、それ以前のJCL(job control language)との違いは、きっと「気軽に実行できるかどうか」ではないだろうか。バッチ時代のJCLを気軽に実行できるリッチな人は、あまりたくさんはいなかったのではないだろうか。

+

もう1つ、シェルのスクリプト言語たる理由は、柔軟なデータ構造の存在ではないかと考えている。といっても、それはシェルの場合、単なる「バイト列のストリーム」なのだが、この単純かつ柔軟なデータ形式が、ツールをつなげたり、データを加工したりするのに大変な威力を発揮したのだ。UNIX使いは今でも日々お世話になっている。

+

筆者の分類では、シェルは第1世代のスクリプト言語なので、あまり大規模プログラムや、当初の目的を超えた汎用のプログラムを作るような目的には向かない。向かないはずだが不可能ではないので、超巨大なプログラムを書く人も多々いたらしい。私はそんなプログラムにお目にかかりたくはないが、いにしえの超人にはそんなものは屁でもないらしい。

+

UNIX上のシェルにはsh(いわゆる、Bシェル)とcsh(Cシェル)の双方があるが、個人的にはcshは言語として謎が多すぎると思っている。大概はあとから出たもののほうが優れているのに、これは珍しい例外である。Cシェルの問題については以下のURLを当たられたい。

+ +

これを読むとcshは使いたくなくなるのではないだろうか。少なくとも、私はそうだった。

+
+
+

スクリプト言語のもう1つの原点: awk

+
+

初期のスクリプト言語で重要な位置を占めるものとしては、シェルの他にawkがあげられる。awkほど世代を反映する言語も珍しい。実際あちこちで現役で働いていて、ちょっとしたシェルスクリプトの中にはたいてい、こっそり隠れていたりする。UNIX黎明れいめい期(といってもさほど昔ではないが)を経験した人々はPerlよりもついawkを使ってしまったりするらしい。しかし、最近はLinuxのシェルスクリプトなどでは、awkの代わりにPerlが隠れていたりするので、どうも最近の人はawkにあまり遭遇する機会がないらしい。

+

今、awkはスクリプト言語の歴史的にみて重要な位置を占めていると述べたが、その理由は2つある。1つは、ちょっと複雑なフィルタを書くのに最適な言語であったことである。awkはgrepとsedとCを足して4で割ったような2 言語であるが、入力を加工して出力するという単一の目的に対して非常に特化している。

+

もう1つの理由は、awkが「シェルよりもちゃんとした言語」であったことであろう。あとでより詳しく述べるが、スクリプト言語の進化の歴史は「ちゃんとした言語」への進化の道である。awkが「よりちゃんとした言語」であるPerlに主役の座を譲ったのはむしろ歴史的な必然であったのだろう。

+
+
+

スクリプト言語の転換点: Perl

+
+

このようにして発生したと考えられるスクリプト言語の転換点はやはりPerlであろう。Larry WallによるPerlはawkの後継あるいは代替として開発が開始されている。

+
+

Larryは最初に考えた。「awkを使おう。」しかし困ったことに、当時のawkはファイルから得られた情報を基に、複数のファイルをオープン、クローズすることはできなかった。Larryは、ある目的にしか使えないツールを書きたくなかったので、結果として新しい言語が誕生したのである。

+

(『プログラミングPerl』8.7節より引用)

+
+

ということは、当時からnawkがあればPerlは発生しなかったということである。

+

Perlはawkと比較して以下の点で画期的であった。

+
+

awkのルールベースのプログラム構造から解放された

+

awkは標準入力に対してパターンごとに処理を登録するという、ある種のルールベースのプログラム構造が強要されていた。Perlはそれを捨て、より「普通」のプログラム言語になっている。これは、フィルタ用言語としての役割を捨てることで、結果として言語の適用範囲が広がったと考えられる。

+ +

たくさんの機能を取り込んだ

+

Perlは“feeping creaturism”3 とまで呼ばれるほど、インタプリタに機能強化を繰り返した。これは「小さいことはよいことだ」をモットーに、小さいツールを組み合わせるというUNIXの美徳(ツールボックスアプローチ)を捨てることとなった。この考え方はUNIX的ではないが、Emacsのような前例はある。事実なかなか便利なやり方だ。

+

関数、ライブラリのロード、パッケージなどの機能が導入された

+

これもまた、より「普通」の言語に近づいたということだが、これにより「スクリプト言語は小さいプログラム用」という前提を捨て、大規模なプログラムにも適用されるようになったと考えられる。もっとも、これらの機能にもかかわらず、Perlの大規模プログラムは多くの場合やはり悲劇のようだ。

+
+

つまり、awkにあったいくつかの前提あるいは思い込み(「特定目的」「単機能ツール」「小規模プログラム向け」)を捨てることにより、Perlは新境地に達したと考えられる。Larry Wallがこれらの点をどれだけ意識したかはともかく、結果としてPerlがこれらのことを捨てたことにより、現在のスクリプト言語があると考えられる。ほぼすべてのオブジェクト指向言語が何らかの形でSmalltalkの影響を受けているように、ほぼすべてのスクリプト言語が何らかの形でPerlの影響を受けているというのは言い過ぎではないと思われる。

+

Perlは確かに便利だ。UNIXにおけるテキスト処理やシステム管理に必要な機能を不足なく提供していることや、コンパイラ型言語による最高速ではないものの、そこそこ高速な処理などから、それ以前のツール(シェルやawk)はちょっと使う気にならないくらいだ。

+

しかし一方で、Perlは多くの批判を受ける言語でもある。Perlの困った点は「利用者を迷わせる」ということだ。PerlのモットーであるTMTOWTDI(There’s More Than One Way To Do It — やり方はいくつもある)ということは、それはそれでかまわないのだが「ありそうないくつかのやり方のうち、(諸般の理由により)いくつかは受け付けられない」というのが一番つらい。「記号が多くてぱっと見ただけではプログラムの意味がわからなくなる」のと相まって、Perlのプログラムについては、昨日の自分は別人だと思ってかからねばならない。

+

これは、Perlが、まだ「普通」の言語から遠かった第1世代スクリプト言語の特徴を引き継いでいるせいではないかと考えられる。また、たび重なる仕様強化で一貫性を失ったのかもしれない。Perlは開発が進むたびに(ある種アドホックに)機能を加えられ、バージョン5においてはとうとうオブジェクト指向機能さえ身につけている。

+

しかし、この批判の原因はそれだけでなく、Larry Wallの言語仕様に対する考え方が、他の言語設計者と一風変わっていることにあるだろう。彼は自然言語をモデルにしたと述べている。どうも大学時代に言語学を専攻したそうで、そのへんも影響しているのかもしれない。「自然言語の文法はもっと複雑であいまいだが、毎日使うのに不自由はしない」といわれると、一瞬納得しそうになる。が、たとえ納得したとしてもPerlのプログラムを読むのがつらいという事実は少しも変わらないのだった。

+
+
+ +

新たな適用分野: Tcl

+
+

Tclという言語がある。“Tool Command Language”の略だが、たびたびスクリプト言語に分類されているようだ。スクリプト言語という単語は間口が広いので、そう分類することには別に何の問題もない。言語仕様だけを見ると、Tclは第1世代としようか、第2世代にしようか、迷うレベルである。データ型は文字列しかないし、機能もそれほどない。しかし、文法構造は単純だが拡張性があるのでなんとか第2世代にしてもよいか、という感じにとらえている。もともとアプリケーションへの共通組み込み言語として開発されたのだから無理もない。当初の目的には合致しているといえよう。

+

しかし、Tclの注目すべき点はそこではない。Tk(ToolKitの略らしい)と呼ばれるGUIライブラリである。Tclが今まで生き残っているのも、組み込み言語ではなく、スクリプト言語と呼ばれるのも、みんなこのTkが原因であろう。Tkは当時としては性質のよいGUIライブラリであったが、画期的な点は、インタプリタ型言語と一体になったGUIライブラリがこれほど使いやすいということを世の中に広く知らしめた点である。

+

もちろん、Lispなどインタプリタのある言語からアクセスできるGUIライブラリは以前からあったのだが、庶民にはやや高根の花という印象があった。Tclは誰にでも簡単に入手できて、「使えるツール」という印象を与えたのが成功の秘訣ひけつだろう。しかし、Tcl以前にもWinterpのような、インタプリタ型言語(Winterpの場合はxlisp)から、GUIライブラリ(Xt)を操作できる処理系はあったのだが、なぜ広まらなかったのだろうか。やはりLispは(かっこが)嫌われるのだろうか。

+

しかし、考えてみればGUIというのはインタプリタ型処理系に向いた分野である。人間の反応速度はインタプリタで対応できるほど十分に遅いし、しかも、よいインターフェイスの実現にはある程度試行錯誤が要求されるので、手早い開発サイクルが重宝される。一時GUIビルダがもてはやされた時期があったが、インタプリタ言語とGUIライブラリの組み合わせは、WYSIWYG性には劣るかもしれないが、それに勝る開発効率を提供する。

+

かくしてTkは愛され、さまざまな言語(Perl, Python, Rubyなど)からのインターフェイスが提供されることとなった。TkはいまだにTclと不可分であるにもかかわらず、使いもしないTclインタプリタを一緒にリンクすることになってでも、各言語に対するTkインターフェイスを提供しようという熱意は、Tkに対するあこがれというよりは、Tclに対する不満が原動力になっているような気がする。

+

Tclは、やはり組み込み言語として作られたがゆえに、特にデータ型が文字列しかない点でどうにも使いにくいからだ。Tclは、一時の勢いは感じられないものの、現在でもファンを維持している。

+
+
+

より「普通」の言語に: Python

+
+

スクリプト言語の進化の方向が「ちゃんとした言語」への進化であるとするならば、Pythonはある意味その究極の位置にいると考えることができる。Pythonは、インタプリタ型、変数・式に型がない、変数宣言がほぼ不要、というスクリプト言語伝統の特徴をかろうじて備えているが、それらを除けば、ほとんど「普通」のプログラミング言語である。PythonはModulaなどに見られるようなモジュールシステムや例外処理機能を備え、オブジェクト指向機能も最初のバージョンから提供している。

+
+

このような性質は、Lispのような伝統的にはスクリプト言語と見なされないが、インタプリタが主体として提供される言語とまったく差がない。むしろ、制御構造などについてはより近代的(≒最新流行に近い)といえる。

+

Perlに至るまでのスクリプト言語には、UNIX的というか、C的というか、楽観的で自己責任の原則に従う傾向があるのだが、Pythonの設計思想はなんとなくPascalのような「正しさ」を強調しているような気がする。

+

Pythonの面白い点は、ブロック構造をインデントによって表現することだ。これはスタイルの多様性を減らす目的があるということだ。むやみに多様性を強調するPerlとは違う道を歩むつもりらしい。他にブロック構造をインデントで表現する言語といえばOccamがある。イギリス出身のOccamといい、スイス出身のPascalといい、オランダ生まれのPythonはヨーロッパ生まれの言語の影響を大きく受けているのだろうか。

+

Pythonについて、歴史的に重要なのは、スクリプト言語の領域に初めてオブジェクト指向を大々的に取り込んだという点である。Pythonのオブジェクト指向機能は、すべてのデータがインスタンスでない、という若干の制約があるものの、全般としてかなり筋がよい。一説にはPerl 5のオブジェクト指向機能にも影響を与えたのだということだ。Python以後のスクリプト言語と呼ばれるものが、ほとんど例外なくオブジェクト指向機能を備えるのは、多くの場合、直接あるいは間接的にPythonの影響であるといえるのかもしれない。

+

Pythonは「Perlよりマシな言語」として、現在でも人気上昇中である。特に海外での評判は高く、infoseek.comやyahoo.comで使われていたという話も聞くが、最近はパフォーマンス上の理由からC(またはC++)で置き換えられたらしい。国内では、あとに述べる「ある理由」により、さほど人気は出ていない。

+
+
+

オブジェクト指向スクリプト言語: Ruby

+
+

さて、その「Pythonが人気が出ないある理由」について説明しよう。

+

それは「Pythonキラー」とでも呼ぶべき言語が存在するからである。プログラミング言語にしては珍しく、日本で設計され、日本から広がり始めたその言語はRubyという名前である。

+

と、もったいぶって紹介したが、実はRubyは筆者によって開発されたスクリプト言語である。開発者自身による解説などというものは客観性がなく信じるに足らないものであることが多いのだが、この文も例外ではない。以下は眉に唾をつけて読んでいただきたい。そして、Rubyを入手して本当かどうかご自分で確認していただけるとありがたい。

+

Rubyの開発開始は1993年である。Pythonが1989年、Perlが1980年代半ばであることを考えるとかなり新しい。実際にRubyを作り始めたときには、PerlのこともPythonのことも知っていて、それでもまだ不満に感じた部分があったのが最初のきっかけである。もっとも、作り出した原動力になったのは既存の言語に対する不満というよりも、「自分の言語を作りたい」という創造に対する意欲だったように思う。

+

Rubyのほうがあとから開発が始まったので、Rubyの仕様にはPerlやPythonに対するアンチテーゼとなる部分が散見される。目指すはBetter Perl Than Perlであり、Better Python Than Pythonである。

+

これらの言語と比較したRubyの特長は、以下のとおりである。

+
+ +

変数に型情報を持たない

+

Perlは変数のプリフィックスよって変数のデータ型を指定している(スカラー型、配列型、ハッシュ型、グロブ型)。一方、Rubyも変数名にプリフィックスを使っているが、指定しているのは型ではなく、スコープである。データ型は変数ではなくすべてオブジェクトが持つ。Pythonはこのようなプリフィックスをいっさい持たない。

+

すべてのデータがオブジェクト(インスタンス)

+

Perlは、それぞれのデータ型ごとに挙動が異なる。Perl 5以降ではデータに対するリファレンスとして、ある程度統一的に扱えるようになったが、デリファレンス時に悪夢が再現する。

+

Pythonはすべてのデータはオブジェクトであるが、組み込みのデータ型のほとんどはオブジェクトであっても、クラスのインスタンスでない。このことによってオブジェクトの統一的な扱いができない場合がある。もっとも、Python陣営はこの点を認識していて、未来のバージョンであるPython 3000では、すべてを本物のオブジェクトに統一しようと計画しているらしい。

+

Rubyは、すでにすべてのデータが本物のオブジェクトである。すべてのオブジェクトは、何らかのクラスに所属するインスタンスであり、すべてのクラスはObjectを頂点とするクラス階層を持つ。

+

使える継承機能

+

PerlもPythonも、そのオブジェクト指向機能によって多重継承をサポートしている。一方、Rubyは少々意外だが単純継承しかサポートしていない。これはRubyのほうが劣っているというわけではない。PerlやPythonが深さ優先の検索を行う、事実上あまり使いものにならない多重継承を提供しているのに対して、Rubyのほうは単純継承とMix-inと呼ばれる補助的な継承機能を組み合わせることで、実質、多重継承相当の「使える」継承機能を提供している。

+

ほどほどのスクリプト言語らしさ

+

ここまで、スクリプト言語の進歩は「普通の言語」への道であり、Pythonに至ってある点まで行き着いたように感じられる。Rubyでは少々スクリプト言語的要素を取り戻している。たとえば、組み込みの正規表現であり、バッククオートによるコマンド出力である。

+
+

筆者は成功していると自画自賛しているが、読者の皆さんはどう感じられるであろうか。

+

さあ、宣伝はもう十分だろう。次に行こう。

+
+
+

スクリプト言語の性質

+
+

ここまで、スクリプト言語と呼ばれるいくつかの言語をほぼ時系列に眺めてきたわけだが、共通する性質が見出せただろうか。筆者なりにまとめると、以下のようになる。

+
+

インタプリタ型

+

厳密には、インタプリタであるかコンパイラであるかは、言語の問題ではなく処理系の問題なのだが、変数や式に型がないとか、evalがあるなどの動的な性質の多い言語は、インタプリタ向けの仕様といえないことはない。スクリプト言語と呼ばれる言語は例外なく、主要な処理系がインタプリタである。

+
+

テキスト処理

+

スクリプト言語の多くは、テキスト処理向けの機能を持つ。たとえば、強力な文字列操作関数や正規表現など。が、これは絶対的な条件ではなく、たくさんの例外がある。

+

高級なデータ構造

+

シェルのバイトストリームを高級と呼ぶのは少々はばかられるが、柔軟でありメモリ管理が不要である点から、ここでは高級と分類する。ほとんどのスクリプト言語は、動的で「高級な」データ構造を持つ。たとえば、awkには連想配列があり、データ型として文字列しかもたないTclでも、それをリストや数値として自在に操作できる。

+

外部との積極的な関わり

+

個人的に、スクリプト言語の性質としてかなり重要だと思っているのが、この外部との関わりである。たとえば、OSの諸機能を呼び出したり、外部のプロセスと通信したりすることで、システムをつなぐグルー(のり)の働きを行ったり、システムの一部に取り付いて、システムにプログラマブルな性質を付与したりする働きは、スクリプト言語の重要な役割であろう。

+

イメージ

+

なにより重要なのは「スクリプト言語」というイメージだろう。なんとなく、かっこいい感じがしないだろうか。結局、ある言語がスクリプト言語かどうかを決めるのは、設計者がこのはスクリプト言語だというメッセージを言語にどれだけ込めるかに依存するような気さえする。

+
+
+
+

スクリプト言語でなさそうなもの

+
+

スクリプト言語と同じような性質を持っていても、必ずしもスクリプト言語と呼ばれないものもある。

+
+

Lisp

+

Lispはインタプリタが主体となる言語であり、原則的に変数や式に型がなく、動的な性質も持っていて、スクリプト言語として十分な性質を持っているように思われるが、なぜかスクリプト言語とは呼ばれない。GuileやScsh(Scheme Shell)などは結構がんばってると思うのだが、まだ定着しない。

+

なぜかと考えるに、歴史的に、テキスト処理や外部との関わりの点で弱かったのと、やはりあの大量のかっこが庶民に好かれていないのが原因ではないかと思われる。それと現時点ではスクリプト言語には「新参もの」というイメージがあり、なんだか太古からあるLispはスクリプト言語らしくない気がする。いや、思い込みなのだが、スクリプト言語はイメージなのだ。

+

BASIC

+

インタプリタ型の言語といえば、BASICもそうだ。これもまたスクリプト言語と呼ばれないのは、歴史的に、外部との関わりの点とデータ構造が単純すぎる点に問題があったからだろう。庶民的すぎてもスクリプト言語にはならないらしい。現代のBASICではそれらの点はすでに改善されていても、まだイメージはつきまとうものだ。

+

Smalltalk

+

SmalltalkもLisp同様だ。フリーの処理系が長らくなかったことも影響しているかもしれない。

+
+
+
+ +

傍流スクリプト言語

+
+

ここまでが、「私の考えるスクリプト言語」である。

+

しかし、先月号の座談会を見てもわかるとおり、違う見解を持つ人も多いらしい。たとえば、JavaScriptやVBScriptのようなものもスクリプト言語と呼ばれる。これは上記のスクリプト言語の性質のうち、多くを満たしていないが、インタプリタ型であり、外部(この場合はブラウザなど)と積極的に関わろうというという点は満たしている。そしてなにより名前がスクリプト言語であると主張している。

+

これらの言語はテキスト処理などを主眼とした旧来のスクリプト言語とは毛色は違っているが、スクリプト言語の今後の主流になるかもしれない。しかし、これらの言語には、拡張性などの点で、できればもうちょっと「普通の言語」らしくなってほしいものだ。

+
+
+

スクリプト言語の未来

+
+

結論として、スクリプト言語の未来を予想してと思ったのだが、まったく想像力が働かない。現状で満足してしまっているのだろうか。人間の中身はそれほど速くは進化しないので、未来の言語の言語もやはり今と大差ないような気がする。ただ、スクリプト言語のイメージはこれまでも変わってきたように今後も変わるだろう。適用分野も現在のテキスト処理中心からあらゆる分野に進出するのではないだろうか。

+

望むのは「手軽なプログラミング」が行える言語が広まって、どこでも自由にプログラミングが行える未来だ。そんなプログラマー天国が実現するのだろうか。するといいなあ。

+
+
+
+
+
    +
  1. +

    編集部注: 『bit』2001年1月号。「連載 スクリプト言語第1回【座談会】スクリプト言語とは」のこと。 +

    +
  2. +
  3. +

    「3で割る」ではなく「4で割る」のは、awkの仕様はかなり小さめなので、3で割っては大きすぎるから。 +

    +
  4. +
  5. +

    “creeping featurism”のもじり。放っておくとどんどん機能が増殖することを正当化する言葉。 +

    +
  6. +
+
+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-006.xhtml b/docs/vol1/xhtml/p-006.xhtml new file mode 100755 index 0000000..76b91a6 --- /dev/null +++ b/docs/vol1/xhtml/p-006.xhtml @@ -0,0 +1,46 @@ + + + + + +第5章 Ruby最古のユーザーとしての開発環境 + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay5 +
+

+特集 開発ツールをもっと便利に使おう
+最強の開発環境を求めて
+Ruby最古のユーザーとしての
開発環境 +

+
+

[C Magazine, 2001年11月号]

+
+

著名な開発者に開発環境を聞こうという企画の一環で私の開発環境を紹介する記事。他にどなたがいらっしゃったのか覚えてませんねえ。結局は、EmacsとUNIX系OS(Linux)というのが答えでした。また、デスクトップ環境はFVWMにEmacsとKtermとNetscapeを重ね合わせるというシンプルな構成でした。20年後はどうかというと、相変わらずLinuxを使い、デスクトップ環境こそFVWMでなくXFCEですが、全画面サイズでEmacsとxfce4-terminalとFirefoxを重ね合わせる構成は大して進歩していないのでした。

+
+

私は自分をRubyの最古のユーザーであると考えています。ま、そりゃそうでしょう。私が作ったんだから。でも、実際には時間としては「Rubyを」開発している時間が圧倒的に長く、「Rubyで」開発している時間はそれほどでもありません。読者の皆さんの中では私よりはるかに長い時間Rubyで開発しておられる方がいらっしゃるのではないでしょうか?

+

そういう私の開発環境はやはりEmacsです。私はEmacsがなければ何もできません。EmacsでC言語のプログラムを書き、コンパイルし、gdbを使い、CVSにコミットし、メールを読み、返事を書き、ニュースを読み、原稿を書き、プレゼンテーション資料を用意します。でも、Webを見るときにはNetscapeです。

+

世の中にはEmacsとViのどちらがよいかというテーマの論争が長く続けられていると聞きますが、私には信じられません。この2つは違うものです。Viはエディタですが、Emacsは環境です。私だってViも使います。rootになって設定ファイルを修正するときには主にViを使います。それから自分が普段使っていないマシンにログインしたときもViを使います。環境としてのEmacsが不要のときに単なるエディタとしてEmacsを使う必要はないでしょう。両方使い分ければよいのです。

+

というか、私のEmacsは .emacsなどでコテコテにカスタマイズされているので、環境としてのEmacsが使えない上記のような状況で素のEmacsを使うのは、かえって苦痛なのです。発狂しそうになります。私は日本語入力のキー配列すらカスタマイズしているので、ローマ字入力さえ苦痛です。逆に私のEmacsは他の人には使えないんでしょうね、きっと。開発環境にこだわりがあるので自分好みにカスタマイズできないツールは、使い込めません。

+
+

開発環境といえば、プログラマーとしてOSも重要です。私はDebian/GNU Linuxを使っていますが、重要なのは「ちゃんとしたPOSIX APIを持っているか」です。個別のツールに関してはOSに関係ないのですが、システムコールやライブラリとその背景にある思想はPOSIX(というかUNIX)でないと生きていけません。あとは特にLinuxにこだわっているわけではないのですが、歴史的な事情でLinuxを使っています。

+

どうやら私は典型的なオールドタイプに属する人間のようです。そういえば、スクリーンには巨大なEmacsとKtermとNetscapeが重ね合わせて置いてあるだけだし、ウィンドウマネージャはfvwmだし。絶滅間近か。

+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-007.xhtml b/docs/vol1/xhtml/p-007.xhtml new file mode 100755 index 0000000..95cc750 --- /dev/null +++ b/docs/vol1/xhtml/p-007.xhtml @@ -0,0 +1,293 @@ + + + + + +第6章 はじめの一歩 + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay6 +
+

+初等Ruby講座
+はじめの一歩 +

+
+

[Linux magazine, 2001年11月号]

+
+

私にとって初めての初心者向けの連載初回になります。これまでは中級者以上を対象にした文章しか書いてこなかったこともあって、自信のなさが文面からにじみ出ていますね。苦肉の策として、初心者向け、中級者向け、現状報告の3部構成にしたのも、作者の不安を反映していますね。

+

興味深いのは、JAOO訪問記です。デンマークのオーフス市で開催されたこのカンファレンスへの出席は私にとって大変刺激的でした。インターネットでしか会話したことのなかった人たちや、著名な開発者などと直接交流できる日が来るとは、それまでの私には想像もできなかったことです。特に世界最初のオブジェクト指向言語と呼ばれるSimulaの開発者であるKristien Nygaard教授は「気のいいおじいさん」という感じで、気さくに話してくださいました。なかには冷戦時代の公開するとヤバそうな話も。とてもその年のチューリング賞受賞者とは思えない気さくさ。

+
+
+

Ruby知ってますか。Rubyは便利なんです。それになにより気持ちがいいんです。今回から始まるこの連載では、Ruby作者自らが、Rubyを知らない人でもRubyを使えるようになるような解説を試みます。乞うご期待。

+
+
+

ごあいさつ

+
+

はじめまして。まつもと ゆきひろと申します。一部では“Matz”としても知られていますが、Rubyを作っている人です。

+

私は頼まれるとなかなかイヤといえない性格なので、以前からお付き合いのあったLinux magazineの編集の人からの「Ruby入門の連載をお願いします」という依頼に、つい「はい」と答えてしまいました。開発者としてRubyとはそもそもの初めからの付き合いですし、それも今年でもう8年にもなります。正直なところ、初心はすっかり忘れてしまいました。

+
+

問題は初心を忘れているという点だけではなくて、そもそもちゃんとした初心者向けの文章を書いたことがないってことのほうが重要な気がします。そういえば、出たばっかりの『Rubyを256倍使う本 太陽編』じゃなかった『黄道編』でも、私の書いたREADME.EXT.jpという文書(拡張ライブラリの書き方を解説している)に対して

+
+

Rubyの内部構造を知っている人が書いている

+
+

と断言されていました。一瞬「そりゃ内部構造を知らなきゃREADME.EXT.jpは書けないでしょ」とツっこんだのですが、要するに「内部構造を知っている人にしか読めない文書だ」という意味なんでしょう。やはり初心者向けの連載を引き受けたのは無謀であったか……。

+

というわけで(なにが「というわけ」なんだか)、入門向けのわかりやすい文章を書く自信はまったくないのですが、それなりに努力しますのでよろしくお願いします。

+

皆さんの評判がよければ、連載が続くかもしれません。

+
+
+

連載の構成

+
+

本連載は毎月以下の3部構成にしようと考えています。

+
+

Ruby入門

+

この連載の本文です。できるだけRubyのことをまったく知らない人も学べるような内容にしようと思っています。皆さんにとっても一番優しく、私にとっては一番難しい部分です。

+

知られざるRuby

+

見かけは単純でも奥が深いことが多いのがRubyです。このパートではRubyの一部を取り上げて、かなり突っ込んだ内容まで解説します。初心者を卒業した人にも役に立つ内容にしたいと考えています。

+

Ruby開発日記

+

せっかく開発者自らによる連載なのですから、開発者でないと書けないことも載せたいと思って作ったのが、この「Ruby開発日記」です。Ruby開発の最新トピックや、私自身の近況を毎月少しずつ紹介しようと思います。

+
+
+
+

Rubyとは

+
+

前振りはここまでにして、ここからが本文です。

+

今回は初回ですから、そもそもRubyとはいったい何かという点から始めたいと思います。一言で言ってしまうとRubyは、

+
+

オブジェクト指向プログラミング言語

+
+

なのですが、これではわかる人にしかわかりませんから、もうちょっとていねいに説明します。

+

まず「オブジェクト指向」はとりあえず置いておきましょう。今月は「オブジェクト指向」の話はしません。で、残りの「プログラミング言語」のほうですが、こちらは「プログラムを作るときに使う言語」です。

+
+

コンピュータは人間に代わっていろいろな仕事を速く正確に行ってくれますが、正直なところあまり頭は良くありません。SF映画に出てくるような柔軟な発想で人間を助けてくれるパートナーなんてまだまだ夢のまた夢です。現在のコンピュータが仕事をしてくれるのは、誰か人間がその仕事の手順を正確にコンピュータに教えてあげてくれたからです。その手順書を「プログラム」と呼びます。

+

繰り返しになりますが、コンピュータはあまり頭が良くありませんので、この手順書を人間の言葉で書いても読めません。ですから、コンピュータにもわかる言語で書いてやる必要があります。これが「プログラミング言語」です。

+

プログラミング言語にはいろいろな種類があります。たとえば、BASIC, FORTRAN, C, Pascal, Lisp, Perl, Smalltalkなどがあげられます。いくつかは聞いたことがあるのではないでしょうか?

+

Rubyはそれらと同じプログラミング言語です。ですから、Rubyを使うということは、プログラムを作る、つまりコンピュータに仕事の手順を教えてやるということなのです。

+
+
+

なぜRuby

+
+

世の中にプログラミング言語がいくつあるのか正確に知っている人は誰もいません。以前、数千はあるのではという文章を読んだことがあります。まあ誰も知らないマイナーなものはこの際置いておくとしても、相当メジャーなものに限っても両手では足りないくらいあるでしょう。

+

その中で、なぜRubyを使うのか、あるいはなぜRubyを学ぶのか、という疑問が出てくるのは当然だと思います。ここで「学ぶ必要はない」と答えてしまうと、私のRubyの作者としての面目は丸つぶれですし、この連載も初回にして最終回になってしまいます。それは私にとって個人的に非常にまずいので、とりあえず「学ぶ必要はある」として考えましょう。

+

他の言語に比べて、Rubyが優れている理由はいくつかあるのですが、代表的なものを列挙しておきます。

+
+

手軽に実行できる

+

Rubyのプログラムは書いてすぐに実行できます。これはRuby言語を解釈するプログラム(これをインタプリタと呼びます)が、プログラムを読み込んですぐに実行するからです。一方、C言語などの場合では、プログラムはまずコンピュータが直接実行できる形式に変換するプログラム(これをコンパイラと呼びます)が、C言語で書かれたプログラムを解釈しますから、実際に実行する前に1ステップ必要になります。このコンパイルと呼ばれるステップには、数分から場合によっては数時間も必要になりますから、すぐ実行というわけにはいきません。

+

機能が強力

+

あなたが自分でプログラムを書いてコンピュータにやらせたい仕事はなんでしょうか? 人によってそれぞれだと思いますが、Linux magazineの読者である皆さんならば、テキスト処理やファイル操作、システム管理などの仕事が多いのではないでしょうか。Rubyはそういう仕事が大の得意です。Rubyはこのあたりを先輩に当たる言語であるPerlから学びました。

+
+

わかりやすい

+

わかりやすいと感じるかどうかは人によって違うと思うので、この「わかりやすい」という長所には主観がたっぷり入っています。少なくとも私はわかりやすいプログラムが書きやすいようにRubyを設計しました。もっともプログラムがわざわざわかりにくくなるように言語を設計する人はあんまりいないと思います。いや、世の中にはBrainf*ckという言語のような「わかりにくいことそのものが存在意義」というプログラミング言語もあるにはありますけど。

+

Rubyは文法がシンプルで同じ仕事を比較的簡潔に書けるという点、充実したライブラリのおかげで自分で書かなくてはいけないことが少ない点、それから今後説明するオブジェクト指向という考え方に基づいて設計されているので言語に一貫性がある点などが、わかりやすさの原因になっていると考えています。

+

フリーソフトウェア

+

私にとってあまりに当たり前なのでつい忘れてしまうのですが、人によっては重要な特徴であると思います。Rubyはフリーソフトウェア、あるいはオープンソースソフトウェアであるため、利用に際して費用がかかりません。開発者である私に対しても謝礼や代金を払う必要はいっさいありません。

+
+
+
+

サンプルRubyプログラム

+
+

くどくどと説明ばかりを読んでいてもイメージがわかないと思いますので、ここで実際のRubyプログラムをいくつか見てみましょう。

+

まずは超簡単なものから。

+
+
puts 1+2
+
+

これは1+2を計算して出力するものです。putsは1行出力のための手続きです。別にRubyだから特別というふうには見えません。実際にはこのようなプログラムでも背後で「オブジェクト」がいろいろ動いているのですが、その話は来月以降ゆっくり説明したいと思います。

+

次のものはもうちょっと複雑です。以下のプログラムはコマンドライン引数として指定したファイルのそれぞれの行数を調べます。

+
+
 1 total = 0
+ 2 nfile = 0
+ 3 for file in ARGV
+ 4   n = 0
+ 5   File::foreach(file) do
+ 6     n += 1
+ 7   end
+ 8   printf "file %s has %d lines\n", file, n
+ 9   total += n
+10   nfile += 1
+11 end
+12 printf "total %d files %d lines\n", nfile, total
+
+
+
+

1行目と2行目は変数の初期化です。総行数を保存するtotalという変数とファイル数を保存するnfileという変数を両方とも0にしています。

+

ARGVはコマンドライン引数です。for文で各ファイルに対して繰り返しています(3行目)。引数に指定したファイル名がそれぞれ変数fileに入って繰り返されます。

+

File::foreach(file)fileで指定したファイルの各行に対して繰り返す手続きです(5行目)。今回は行数を数えるだけですから、ファイルごとの行数を変数nに足していくだけです。

+

各行に対する繰り返しが終了したら、printf手続きを使って、そのファイルの行数を出力し(8行目)、トータルの行数とファイル数を増やしておきます(9, 10行目)。最後にトータルのファイル数と行数を出力して終わりです(12行目)。

+

他の言語でプログラムを書いたことがある(あるいは読んだことがある)人ならば、あまり説明しなくてもかなり推測できるのではないかと思います。この「常識が通用する」部分もRubyの長所の1つです。

+

せっかくのプログラムですから、実行してみましょう。とりあえず上記のプログラム(行番号を除く)をエディタを使ってファイルにします。ここではcount.rbという名前だとします。これをRubyインタプリタの引数にします。それから行数を数えたいファイル名を続けて並べます。ですから、たとえばカレントディレクトリのヘッダーファイル(拡張子が".h"のファイル)の行数を数えたければ、

+
+
% ruby count.rb *.h
+
+

と実行します。出力例を図6.1に示します。

+
+
+
file config.h has 118 lines
+file defines.h has 94 lines
+file dln.h has 31 lines
+file env.h has 60 lines
+file intern.h has 414 lines
+file node.h has 358 lines
+file re.h has 41 lines
+file regex.h has 228 lines
+file rename2.h has 307 lines
+file ruby.h has 623 lines
+file rubyio.h has 66 lines
+file rubysig.h has 90 lines
+file st.h has 46 lines
+file util.h has 53 lines
+file version.h has 4 lines
+total 15 files 2533 lines
+
+

図6.1●Rubyプログラムの実行例

+
+

どうです? そんなに難しくないでしょう?

+ +

実はRubyを使わなくてもLinuxならwc -l *.hとすれば行数は簡単に求まるのですが、ここでは「自分でプログラムしたのだ」という満足感を得たのだということにしておきましょう(笑)。これは最初の一歩なのですから。そのうち、既存のコマンドでは簡単には済まないこともできるようになります。

+
+
+

はじめに下準備を

+
+

さて、Rubyを使ってみたくなりましたか? え、ならない? では、念力を送らなければ。……。さあ、使いたくなりましたか?

+

では、Rubyを使ってみたくなったところで、お手元のマシンでRubyが使えるかどうか確かめてみましょう。Linux magazineの読者ですから、マシンにはきっとLinuxがインストールしてあると思います。

+

最近の多くのLinuxでは最初からRubyがインストールしてあります。試してみましょう。xtermなどの画面から、シェルプロンプトに対して以下のように入力します(“%”はシェルプロンプトです)。

+
+
% ruby -v
+ruby 1.6.4 (2001-08-06) [i386-linux]
+
+

などと表示されたらRubyが使えます。バージョンや日付は異なっているかもしれません。使える人はラッキーでした。使えない人はなんとかしましょう。

+
+
+

インストール

+
+

というわけで、使えなかった人はRubyをインストールする必要があります。インストール手順はRuby公式サイトのhttp://www.ruby-lang.org/ja/install.htmlなどを参考にするとよいとありますが、若干情報が古いようです(最新は1.4.5とか書いてあるし)。

+

どうせですから、最新をインストールしましょう。Windows使いならともかくLinuxなら楽勝です。実際、私もLinuxでRubyを開発してますから。

+

まず、ソースコードを入手します。「超」最新安定版であるstable-snapshotがよいでしょう(「超」は「最新」にかかります)。

+
    +
  • ftp://ftp.ruby-lang.org/pub/ruby/stable-snapshot.tar.gz

  • +
+

を手に入れます。同じディレクトリにあるruby-1.6.4.tar.gzがリリース安定版です。たぶん、今月号が出る頃にはruby-1.6.5.tar.gzに置き換わっていると思いますが。

+

ソースを入手したら、それを展開します。適当なディレクトリで以下のコマンドを実行します。

+
+
% tar zxvf stable-snapshot.tar.gz
+
+

つらつらとメッセージが出てrubyディレクトリができているはずです。あとはコンパイルです。以下の手順でコマンドを実行してください。

+
+
+
% cd ruby
+% ./configure
+% make
+
+

これでコンパイルは完了です。実際のインストールの前にテストしておきましょう。

+
+
% make test
+test succeeded
+
+

と出れば、成功です。Linuxではコンパイルもテストも失敗しないはずです。後はインストールです。rootになって、

+
+
% su
+# make install
+
+

とすれば、インストールは完了です。もしあなたがroot権限をお持ちであれば、このようにLinuxでのインストールは簡単です。もし、rootになれないのであれば、つまりそのマシンには他に管理者がいるということですから、その管理者の方にお願いするのが一番手っ取り早いでしょう。

+
+

「ねえ、Rubyをインストールして」

+
+

もちろん、自分のホームディレクトリにインストールするという方法もあるにはあるのですが、やはりここはそのマシンを使うすべての人の幸せのため管理者の方にお願いするのが正解でしょう。

+

また、もしあなたが何らかの事情でWindowsでRubyしたいと強く望んでいるのでしたら、

+
    +
  • http://www.pragmaticprogrammer.com/ruby/downloads/ruby-install.html

  • +
+

からバイナリをダウンロードしたほうがよいかもしれません。ただし、私はWindowsのことはなんにもわかりませんから、くれぐれも私には質問しないでくださいね。お願いします。

+
+
+

Rubyの使い方

+
+

とりあえずRubyインタプリタの使い方を説明しておきましょう。といっても全然難しくなんかありません。

+
+
% ruby プログラム
+
+

「プログラム」の部分にはエディタなどで入力したRubyプログラムのファイル名が入ります。これでRubyプログラムが実行できます。Rubyプログラムファイルには、たとえばsample.rbというように".rb"という拡張子を付けるのが慣習になっていますが、別に付けなければならないというものではありません。

+
+

実際には、Rubyインタプリタはたくさんのコマンドラインオプションがあるのですが、とりあえず今回は初回ですから、必要はないでしょう。興味のある人は、

+
+
% ruby -h
+
+

と実行すると一覧を見ることができます(リスト6.1)。

+
+

リスト6.1●Rubyインタプリタのコマンドラインオプション

+
Usage: ruby [switches] [--] [programfile] [arguments]
+  -0[octal]       specify record separator (\0, if no argument)
+  -a              autosplit mode with -n or -p (splits $_ into $F)
+  -c              check syntax only
+  -Cdirectory     cd to directory, before executing your script
+  -d              set debugging flags (set $DEBUG to true)
+  -e 'command'    one line of script. Several -e's allowed. Omit [programfile]
+  -Fpattern       split() pattern for autosplit (-a)
+  -i[extension]   edit ARGV files in place (make backup if extension supplied)
+  -Idirectory     specify $LOAD_PATH directory (may be used more than once)
+  -Kkcode         specifies KANJI (Japanese) code-set
+  -l              enable line ending processing
+  -n              assume 'while gets(); ... end' loop around your script
+  -p              assume loop like -n but print line also like sed
+  -rlibrary       require the library, before executing your script
+  -s              enable some switch parsing for switches after script name
+  -S              look for the script using PATH environment variable
+  -T[level]       turn on tainting checks
+  -v              print version number, then turn on verbose mode
+  -w              turn warnings on for your script
+  -x[directory]   strip off text before #!ruby line and perhaps cd to directory
+  --copyright     print the copyright
+  --version       print the version
+
+
+
+
+

irbの使い方

+
+

もう1つ便利なツールirbも紹介しておきましょう。Rubyインタプリタがインストールされていれば、たぶんirbもインストールされていると思います。irbはinteractive ruby(対話型Ruby)の略です。irbを使えばプログラムを対話的に入力できるので、手軽に実験することができます。irbの起動にはただirbコマンドを実行するだけでかまいません。

+ +
+
% irb
+irb(main):001:0>
+
+

起動するとプロンプトが出てきますから、それに向かって実行したいRubyのプログラムを入力します。

+
+
irb(main):001:0> 1+1
+2
+irb(main):002:0>
+
+

複数の行に渡るプログラムもそのまま入力できます。

+
+
irb(main):002:0> def fact(n)
+irb(main):003:1>  if n == 0
+irb(main):004:2>    1 
+irb(main):005:2>  else
+irb(main):006:2*   n*fact(n-1)
+irb(main):007:2>  end
+irb(main):008:1> end
+nil
+irb(main):009:0> fact(10)
+3628800
+irb(main):010:0>
+
+

プロンプトの最初の数字は入力した行番号、次の数字はネストのレベルです。

+

irbを使えば、Rubyの挙動を簡単に確認できるので非常に便利です。やはり、実際に使ってみるのが一番勉強になります。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-008.xhtml b/docs/vol1/xhtml/p-008.xhtml new file mode 100644 index 0000000..c7662f1 --- /dev/null +++ b/docs/vol1/xhtml/p-008.xhtml @@ -0,0 +1,180 @@ + + + + + +第6章 はじめの一歩 + + + + +

Matz Essays Volume 1

+ + +
+

◆ 知られざるRuby ◆ open

+
+

今回のテーマはopenです。

+

入出力をするためにファイルをオープンするのがopenです。openKernelモジュールで定義されているメソッドで、Kernelモジュールはすべてのクラスの親であるObjectにインクルードされているので、openはどのクラスでも用いることができます。たとえば、このように使います。

+
+
f = open("/usr/share/dict/words")
+f.grep(/Ruby/){|line| print line}
+
+

これは/usr/share/dict/wordsにある辞書ファイルからRubyという単語のある行を探して出力する小プログラムです。

+ +

openメソッドの引数は以下のようになっています。

+
+
open(path[, mode[, perm]])
+
+

pathはオープンするファイルのパス、modeはファイルの入出力モードを決定する文字列、または整数フラグ、permopenにより新しいファイルが作られたときのパーミッション(アクセス権)を整数で指定します。カギかっこ“[]”でくくられた部分は省略が可能です。

+

modeを文字列で指定するには、表6.1の入出力モードを指定します。

+
+

表6.1●openで指定する入出力モードの補助指定

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
指定する文字列入出力モード
"r"読み込みモード。ファイルが存在しなければエラーになる
"r+"読み込み・書き込みモード。ファイルが存在しなければエラーになる
"w"書き込みモード。ファイルがなければ作る。あればサイズ0にする
"w+"読み込み・書き込みモード。ファイルがなければ作る。あればそのファイルのサイズ0にする
"a"追加書き込みモード。ファイルがなければ作る。ファイル末尾にseekする
"a+"追加読み込み・書き込みモード。ファイルがなければ作る。ファイル末尾にseekする
+
+

文字列によるモード指定では2文字目に“b”を追加することができます。これはバイナリモードでの読み込みを意味しますが、UNIXのような、もともとバイナリモードという概念がないOSでは“b”を指定しても単に無視されます。

+

整数フラグの場合、基本的な動作モードを表6.2の定数から指定します。それを補助する動作として表6.3の定数を、“|”(ビットOR)で複数指定することができます。これらの定数は、Fileクラスで定義されていますので、たとえば、

+
+
open(path, File::RDWR|File::CREAT, 0666)
+
+

のように指定するという意味です。

+ +
+

表6.2●openで指定する入出力モード(文字列の場合)

+ + + + + + + + + + + + + + + + + +
指定する定数入出力モード
RDONLY読み込みモード
WRONLY書き込みモード
RDWR読み込み・書き込みモード
+
+
+

表6.3●openで指定する入出力モードの補助指定

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
orで指定する定数入出力モード
CREAT指定したpathにファイルが存在しなかった場合は作成する。つまり、逆にいえばこの定数が指定されない場合には、ファイルが存在していなければエラーになる
EXCLCREATと一緒に使用された場合、ファイルがすでに存在した場合にエラーとなる
NOCTTY指定したpathがterminalデバイスを参照していて、かつプロセスが制御端末を持たない場合でも、そのterminalが制御端末にならない
TRUNCファイルがすでに存在し、通常ファイルであり、書き込み可モード(RDWRまたはWRONLY)が指定されている場合、そのファイルの長さは0に切り詰められる
APPENDファイルを追加モードでオープンし、ファイル末尾にseekする
NONBLOCK
NDELAY
ファイルをnonblockingモードでオープンする。openを始めとする、ファイル・ディスクリプタに対するすべてのシステムコールは、ブロックしない。ブロックが発生しそうな場合にはシステムコールが失敗する
SYNCファイルを同期I/Oモードでオープンする。ファイル・ディスクリプタに対するすべてのwriteは、実際に書き込みが完了するまで終了しない
+
+

デフォルトの入出力モードは読み込みモード(“r”またはRDONLY)です。

+

新規に作るファイルのパーミッションを指定する第三引数permを省略した場合には0666を指定したことになります。0666というのは「獣の数」ではなくて、パーミッションを指定する8進数です。最初の0はこの数字が8進数であることを示します。続く3つの数字は、左からそれぞれ「ファイルの所有者」「ファイルの所有グループに属するユーザー」「それ以外のユーザー」に対するアクセス許可権を指定しています。8進数の一桁は3ビットなのですが、それの各ビットで「読み込み権」「書き込み権」「実行権」を表現します。6は2進数で表現すると110つまり、「読み込み権と書き込み権は許可するが実行権は許可しない」という意味になります。

+

これをまとめると0666はファイルの所有者(自分が新規に作成するのだからプログラムの実行者と同じ)、ファイルの所有グループに所属するユーザー、その他のユーザーの誰もに対して、読み込みと書き込みを許可し、実行を許可しないパーミッションという意味になります。

+

ただし、この値がそのまま使われるわけではなく、プロセス単位のパーミッションマスク(umask)で修正され、(perm&umask)が実際の値になります。この値は新規にファイルを作るときにだけ有効で、すでに存在するファイルにpermを指定してオープンしたとしても、ファイルのパーミッションは変化しません。

+

以上のパーミッションの挙動はUNIX系OSでは共通のものです。では、そのようなファイルのアクセス権を持たないOSではどうなるのでしょうか? それはそのOSでのPOSIXエミュレーション層の実装に依存するのですが、基本的には、ない袖は振れないので、存在しないアクセス権を指定しても無視するというのが基本です。

+

ファイルをオープンしたopenFileクラスのオブジェクトを返します。

+

openにはファイルだけでなく、コマンドを指定することができます。openpathが“|”で始まるときには、それに続く文字列をコマンドとして起動し、そのコマンドの標準入出力に対してパイプラインをつなぎ、それに対応するIOクラスのオブジェクトを返します。pathが“|”で始まる場合には第3引数を指定するとエラーになります。

+
+

Perlを使っている人は注意する必要があるのですが、読み込みでも書き込みでも“|”は先頭に来ます。

+

さらにコマンド名が“-”であるとき、つまりpathに“|-”を指定した場合には、Rubyインタプリタをforkして子プロセスを作り、その子プロセスの標準入出力との間にパイプラインをつなぎます。

+

openがブロックとともに呼び出されたとき、openはファイルをオープンして、そのオープンしたファイルをパラメータとしてブロックを実行し、ブロックの実行が終了するとファイルをクローズします。

+

つまり、以下のようになります。

+
+
open(path, mode) do |f|
+   ... 
+end
+
+

これは以下のコードとほぼ同じ動作をします。

+
+
f = open(path, mode)
+begin
+   ... 
+ensure
+  f.close
+end
+
+

これはつまりブロック付きでopenを呼び出した場合、途中でエラーが起きたり、例外などで実行を中断したとしても必ずファイルがクローズされるということです。この形式の利用は積極的に推奨されています。

+

この呼び出し方をした場合にはopenの戻り値はファイルのオブジェクトではなく、ブロックで一番最後に評価した式の値になります。これは、もうクローズしてしまったファイルオブジェクトよりもブロックの評価値のほうが使い道があるだろうという配慮からそうなっています。

+

openはファイルとコマンドを両方開くことができる高機能なメソッドですが、コマンドではなくファイルしか開かないことが明らかな場合、Fileクラスのメソッドを使うことができます。

+
+
File::new(path[, mode[, perm]])
+File::open(path[, mode[, perm]])
+
+

これらのメソッドの引数の意味はopenメソッドと同様ですが、pathの先頭の“|”をチェックしません。ですから(あまりないことですが)、ファイル名の先頭に“|”が付いていてもオープンすることができます。また、これらのメソッドはFileクラスのオブジェクトを返します。

+

それから、File::newはブロックを取りませんが(与えられても無視します)、File::openopen同様にブロック付きで呼び出すことができます。動作もopenと同じです。これはnewはオブジェクトの生成に専念するというルールからで、他のクラスのメソッド、たとえばDir::newDir::openでも同じような関係が成立しています。

+

ファイルを開くことだけが目的のFile::openとは逆に、コマンドを開くことが明らかな場合にはIO::popenが使えます。

+
+
IO::popen(cmd[, mode])
+
+

こちらにはcmdの先頭に“|”は付きません(付けてはいけません)。IO::popenopenメソッド同様にブロックを取ります。IO::popenの戻り値はIOクラスのオブジェクトです。

+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-009.xhtml b/docs/vol1/xhtml/p-009.xhtml new file mode 100644 index 0000000..52c451d --- /dev/null +++ b/docs/vol1/xhtml/p-009.xhtml @@ -0,0 +1,49 @@ + + + + + +第6章 はじめの一歩 + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ デンマーク訪問記

+
+

ちょっといってきました、デンマークへ。

+

今回のトピックは、9月10日から14日までデンマークのオーフス市で開催されたJAOOのリポートです。

+

JAOOは毎年デンマークで開催されている国際的なJavaとオブジェクト指向のカンファレンスで、今年で5回目になるのだそうです(ちなみに国際カンファレンスということで、すべてのプレゼンテーションはデンマーク語ではなく、英語で行われました)。JAOOは何かの略かとスタッフに尋ねたところ、やっぱりJava and OOの略だそうです。なんか安易ですね。

+

ただ、カンファレンスは安易どころではなく、スピーカーにはアメリカから来た有名人がずらりとそろっています。たとえば、『リファクタリング』のMartine Fowlerや、Wikiの提唱者Ward Cunningham、Java方面でも知られているDoug Leaなど、どこかで名前は聞いたという人がたくさんです(敬称略)。私はUML方面の人はあまり知らないのですが、そちらでも知られた人が来ていたようです。その中でもAlistair Cockburnという人がパワフルで印象的した。なんか有名人と直接会えて感動してしまいました。なんだかミーハーですね。

+

私たちRuby関係者にとっての有名人といえば、Programming Rubyの著者であるPragmatic ProgrammersことDave ThomasとAndy Huntのふたりもスピーカとして参加していました。昨年のJAOOでは、彼らはRubyを紹介するプレゼンテーションを行い好評だったのだそうです。今年もプログラミング一般に関してのプレゼンテーションと、Rubyのチュートリアルとを担当していました。

+

実は昨年の彼らのプレゼンテーションが好評だったので、彼らの推薦もあって今年はRubyの作者を招待してのプレゼンテーションを、という話になったのだそうです。そうなんです。今回のデンマーク訪問は単なる参加者ではなく、講師としてプレゼンテーションするためだったのです。

+

英語も怪しいのに大丈夫だろうかという不安もよそに、とにかく引き受けてしまったのだからしょうがない、と覚悟を決めて出かけてきました。悪天候のため飛行機のスケジュールが狂いまくりで、不安なものもありましたが、10日深夜に無事オーフスにたどりつきました。

+

私の出番は11日の午前のパネルセッション「The Role of Scripting Language」と午後のプレゼンテーション「Object-Oriented Scripting in Ruby」でした。パネルセッションでは、私、Practical Programming in Tcl and Tkの著者であるBrent Welch、それとPerlの偉い人Michel G. Schwernの3人で「スクリプティングとは何か」というテーマで討論しました。といってもわずか45分しかありませんでしたから、それぞれの(言語の)立場を紹介しただけで終わってしまいました。私としては「スクリプティングとはたんなる短いプログラムのことではない、人間指向のプログラミングだ」とかいうようなことを言いたかったのですが、伝わったかどうかはかなり疑問です。ああ、もっと英語が話せたらなあ。でも、まあ「度胸で話せばなんとかなる」という妙な自信を強めたことも確かです。

+
+

午後のプレゼンテーション「Object-Oriented Scripting in Ruby」は、Rubyの思想や背景を踏まえて、スクリプティングとは何かという午前中のパネルの続きのような内容を紹介しました。つたない説明でしたが、ある程度興味をもってもらえたようです。このプレゼンテーションの資料は

+
    +
  • http://www.ruby-lang.org/en/jaoo2001/

  • +
+

で公開しています。実はデンマーク人は(日本人のように?)とても礼儀正しくおとなしいので、どのセッションでもほとんど質問がでなかったのですが(質問するのはアメリカ人ばかり……)、なぜか私のプレゼンテーションに対しては、ある程度活発な質問が出ました。内容がよかったせいか、私の英語があまりに下手だったせいかはわかりませんが、後者でなかったらよいなあと思っています。

+

プレゼンテーションが終わると、みんながざわざわしていました。ちょうどその頃例の同時多発テロのニュースが報道されたからです。JAOOの講師はほとんどがアメリカ人なのですが、彼らは自分の国が攻撃されたショックと、飛行機の飛行が禁止されて帰れるかどうかわからない不安からかなり動揺していました。その後の連絡でPragmatic Programmersはうちに帰り着くのが3日くらい予定より遅れたのだそうです。大変でした。

+

私にとっての今回のJAOOでの収穫といえば、たくさんの人に直接会うことができた点でしょう。たとえば、Dave ThomasとAndy Huntに初めて会いました。彼らとはすでに数えきれないくらいメールやIRCでやりとりをしていたのですが、直接会ったのは初めてでした。

+

Martin Fowler, Ward Cunningham, Doug Leaなどと直接言葉を交わせたのはかなり感激ものでした。Kent Beckは今年は不参加だそうで会えませんでした、残念。

+

他にも一緒にパネルに参加した人たちとの会話も刺激的でした。特にBrent Welchは波長があって、RubyとTclはお互いに参考にできるところはないかとかいろいろと話をしました。正直Tclを見直しました。それから、オブジェクト指向言語の父Simulaを開発したというOslo大のKristien Nygaard教授にも会ったのも貴重な経験でした。彼にとってはすべてのオブジェクト指向言語は、いわば孫か曽孫のようなものだそうです。クラスとか継承とかの概念はみんなSimulaが発明したのですから、確かにそうかもしれません。

+

私は、飛行機のチケットの関係で私は木曜日に出発しなければならなかったのですが、向こうにいたのが54時間だけで、移動には58時間かかったというなんだか強行軍でした。なかなか珍しい経験をしたと思います。

+

次回はLinux Conference Japanをレポートする予定です。あるいはフロリダ州タンパで開かれる第1回 Ruby Conferenceについてレポートできるかもしれません。

+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-010.xhtml b/docs/vol1/xhtml/p-010.xhtml new file mode 100755 index 0000000..1ee4137 --- /dev/null +++ b/docs/vol1/xhtml/p-010.xhtml @@ -0,0 +1,369 @@ + + + + + +第7章 条件判断とループ + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay7 +
+

+初等Ruby講座
+条件判断とループ +

+
+

[Linux magazine, 2001年12月号]

+
+

Rubyはブロックの終わりをendで表現するという現代では珍しくなってしまった文法を採用しているのですが、「知られざるRuby」のコーナーでは、その背景を説明しています。言語をデザインするときに、どんなことを考えながらデザインしているかうかがえる記事になっているのではないでしょうか。

+

「開発日記」では「Linux Conference 2001」のことを紹介しています。ここでは実験的なVMと世代別GCについて発表されました。この実装が現在のRubyに直接取り込まれたわけではありませんが、このような研究があったからこそ、その後のRuby実装の改善が実現したと言えるでしょう。この頃から考えると、20年の間に信じられないほどの投資と技術的改善が行われました。

+
+
+

プログラムはコンピュータに対する手順書だと考えることができます。今回はその手順書の「読み進め方」に当たる手順の制御方法について学びます。かなり基本的な部分ですので、プログラムの知識・経験のある方は雑学の部分だけを楽しんでください。

+
+
+

おことわり

+
+

最初に白状しておきますが、私は数学がダメです。昔から全然ダメなんです。よくコンピュータは理系的といわれますが、私は学校は理系にいましたが(コンピュータがどうしてもやりたかったので)、頭の中身は文系でした。大学時代は周りのみんなについていけずに悩んだものでした。

+

なぜわざわざこんなことをいうかというと、今回の記事中に何度か「証明」という単語が(数学的な意味で)出てくるからです。もちろん私に何かを証明することができるはずもなく、これは誰かが証明したと言っているのをそのまま真に受けているという意味です。まあ、間違ってはいないと思いますが……。

+
+
+ +

プログラミング基礎の基礎

+
+

昔々、アラン・チューリングという名前の天才がいました。この人はイギリスで第二次世界大戦中の暗号解読に大きな成果をあげた人ですが、コンピュータ関係の基礎を打ち立てた人でもあります。彼の考え出したことが今のコンピュータの理論的背景になっているのです。

+

たとえば、彼の考え出したことの1つに「チューリングテスト」というものがあります。これは「機械が知性を持つかどうか判定する」というテストです。これは自分の相手が完全に見えない状態、たとえばスクリーンを経由して、対話を行うというものです。相手が人間であるかコンピュータであるか教えられずに、ある程度の時間対話したうえで区別することができるかどうかをテストします。もし、対話を通して人間とコンピュータを区別することができなければ、それはコンピュータが十分に知性を持つことを意味しているとしてもよいというのが彼の結論です。なんだかカンプフ・フォークトテスト1 みたいですね。

+

彼は「チューリングマシン」という概念も考え出しました。チューリングマシンは、左右に無限の長さを持つテープと1つの読み書きヘッドによって構成される機械で、ヘッドがテープからデータを読み取り、それに従って記号操作を行い、その結果をテープに書き込むことを繰り返すことによって一種の計算を行います(図7.1)。

+
+ +
+ fig0701 +
+

図7.1●チューリングマシン

+
+

これは(まだ当時はコンピュータは存在していませんでしたが)、一種の仮想的なコンピュータで、彼はこの単純な機械によってあらゆるアルゴリズムが表現可能であることを証明したのです。いや、その証明は私には理解できませんが(笑)。

+

チューリングマシンは非常に原始的な「コンピュータ」なので、あらゆるアルゴリズムが表現可能であっても、実際にこれでプログラムを行うのは現実的ではありません。しかし、あらゆる「アルゴリズムが表現可能である」ためにはそれほどたいした機能が必要ではないということがわかります。

+

チューリングマシンの単純さは、プログラミング言語がその本来の目的を果たすために必要な最低限の機能がとても小さいことを示しています。たとえばRubyの場合、そのリファレンスであるヤギ本(『Rubyデスクトップリファレンス』オライリー、ISBN4–87311–023–8)で、言語そのものを解説した部分が全138ページ中、たったの24ページしかないことからもうかがえます。

+

チューリングマシンについてもっと知りたい人は、

+
    +
  • http://www.jaist.ac.jp/~u-ryo/notes/class/ida/CognitiveScience/turing.html

  • +
+

などからリンクをたどってみてください。

+
+
+ +

プログラミングの基礎

+
+

さて、役に立つのか立たないのかよくわからない基礎はまだ続きます。かなり時代が下がるとエドガー・ダイクストラが登場します。

+

彼は“GO TO Statement Considered Harmful”という論文でプログラミング言語には以下の制御の流れがあれば、あらゆるアルゴリズムが表現できると証明しました。

+
    +
  • 逐次実行

  • +
  • 条件判断

  • +
  • ループ

  • +
+

かつてはプログラミング言語の制御構造には任意の場所にジャンプするGOTO文が必須であると考えられていました。ダイクストラの論文はそんなものは不要で、この3つのわかりやすい制御構造だけですべてが記述できると断言したことで当時としては画期的でした。

+

もちろん、私にはその証明は理解できません。が、結論は信じることにします。

+

逐次実行とは、命令を順番に実行していくことです。プログラミング言語は普通、並んでいる行を順番に実行します。これが逐次実行です。これを制御と呼ぶのは変な気もしますが、中には各命令の直後に次はどの命令を実行するか指定する変な言語もありますし、さらにひどいことに逐次実行が進む方向(上下左右)を変えることができる2次元プログラミング言語というものもあります。どちらも異様に使いにくいですけどね。後者はジョークとして設計されたものですが、前者は本気だというのが恐いものがあります。

+

条件判断とは、ある条件が成立しているときだけ一連の命令を実行するものです。if文のことですね。

+

ループは、ある条件が成立するまで実行を繰り返すものです。while文のことですね。

+
+
+

if文

+
+

Rubyで条件分岐といえばifです。いや、Rubyでなくても普通のプログラミング言語ではどれもifですね。

+

ifの最も単純な形式は以下のようなものです。

+
+
if 式
+  文
+end
+
+

ifは式の結果が「真」であれば文を(複数の文があればそれを順番に)実行し、「偽」であれば何も実行しません。

+

ifを1行に書こうと思えば、式と文の間を明示するために、thenかセミコロン(;)を入れる必要があります。

+
+
ifthenend
+if 式 ; 文 end
+
+
+

ですから、

+
+
if true
+  puts "hello"
+end
+
+

は式(true)の値が真ですから、文である、

+
+
puts "hello"
+
+

が実行されます。必ず実行されるんじゃ、条件分岐の意味ないですが。

+

では、Rubyでは、どのような値が「真」で、どのような値が「偽」なのでしょうか。定義からいえば、

+
    +
  • falsenilが「偽」

  • +
  • 「偽」でない値はすべて「真」

  • +
+

です。ですから、数字の0も文字列の""も含めて、falsenil以外のすべての値は「真」になります。

+

false」が英語で「偽」ですから、偽なのは当然だと思われます。ではnilとはなんでしょう? 英和辞典によればnilとは、

+
+

無、零、ない、存在しない

+
+

という意味なのだそうです。ですから、Rubyにおいてもnilは「存在しないことを示す値」です。たとえば、代入されていない変数の中身はnilですし、配列の範囲を超えて要素を取り出そうとしたときに得られる値もnilです。

+

また、Rubyでは一般的に

+
    +
  • 真としてtrueを返す手続きは偽としてfalseを返す

  • +
  • 真としてtrue以外を返す手続きは偽としてnilを返す

  • +
+

という慣習があります。たとえば、「defined?()」はある式が定義されていれば、その種別を示す文字列を返しますが、定義されていなければnilを返します。ですから、

+
+
defined?(FOOBAR)
+
+

は(FOOBARが定義されていなければ)nilを返しますが、

+
+
defined?(ARGV)
+
+

は文字列"constant"を返します。やや、余談になりますが、Rubyでは「defined?」のような真偽値を返すもの(述語と呼びます)は名前の末尾に「?」を付ける習慣があります。

+

「偽」としてfalsenilの両方があることは、ときどき、

+
+

真 – 偽 – 未定義

+
+

という3つの状態を表現するのに使われることがあります。nilは偽というよりも「そもそも条件が成立しない」ということを表現していると考えることができます。

+
+

さて、真偽値についてはここまでにして、またif文に戻りましょう。先ほどは「条件が成立するときにこれを実行する」というif文の一番簡単な形式について説明しましたが、if文にはまだいくつかの形式があります。

+

「条件が成立するときにはこれを、成立しないときにはこっちを」というタイプの条件判断がほしい場合があります。その場合にはelseの付いたif文を使います。

+
+
if 式
+  文1
+else
+  文2
+end
+
+

これは式の値が真であれば文1を、偽であれば文2を実行します。まあ、Rubyに限らず他の言語でも似たような形式ですよね。

+

次に「この条件が成立するときにはこれを、別の条件が成立するときにはこっちを、すべての条件が成立しない場合にはこれを」というタイプの条件判断にはelsifの付いたif文を使います。

+
+
if 式1
+  文1
+elsif 式2
+  文2
+else
+  文3
+end
+
+

これは式1の値が真であれば文1を、式2の値が真であれば文2を、すべての式が偽であれば文3を実行します。elsifの部分は実際には好きなだけ繰り返すことができますし、elseの部分は省略できます。

+

ここで、他の言語の経験がある人はelsifがCのようなelse ifでも、Pythonのようなelifでもないことに気を付けてください。PerlやAdaが得意な人は間違えないと思います。もしわからなくなったら、「えるすいふ」と素早く発音すると「えるしふ」になると思い出してください。私がelsifを選んだのはまさにそういう理由からですから。

+

RubyにはPerlから引き継いだif文のもう1つの形式があります。それが「if修飾子」です。if修飾子は「後置if」とも呼ばれていて、文の後ろにifを付けるものです。形式は、

+
+
if
+
+

のようなものです。たとえば、

+
+
puts "hello" if a == b
+
+

abの値が等しければ"hello"を出力します。ifは文の後ろに付いていますが、条件チェックは先に行われます。考えてみれば当然のことですが。

+
+
ifthenend
+
+
+

と、

+
+
if
+
+

はまったく同じ意味です。好きなように使えばよいのですが、お勧めの使い分けとしては、条件を強調するときには普通のifを、文を強調するときにはif修飾子を使うというものがあります。

+

たとえば、変数$DEBUGがセットされているときにはデバッグ用のメッセージを出力したい場合には、

+
+
if $DEBUG
+  puts "this is debug message"
+end
+
+

とするよりも、

+
+
puts "this is debug message" if $DEBUG
+
+

としたほうが簡潔な印象があります。

+
+
+

unless文

+
+

条件分岐にはもう1つunless文もあります。unless文はif文のちょうど反対で、条件が成立していないときに文を実行するものです。

+
+
unless 式
+  文
+end
+
+
+

unlessif notと同じ意味ですから、主に意図を明確にするために用いられます。

+

unless文にもif文同様にelseを付けることができます。

+
+
unless 式
+  文1
+else
+  文2
+end
+
+

これは式の値が偽であれば文1を、真であれば文2を実行します。でもこれは、

+
+
if 式
+  文2
+else
+  文1
+end
+
+

と同じですからあんまり意味がないですよね。ifにはelsifがありますが、unlessにはunlesifのようなものはありません。ifを使ってください。

+

unlessifの反対ですから、当然ifと同様に修飾子形式が使えます。

+
+
unless
+
+

これは式の結果が偽のときだけ文を実行します。

+
+
+

while文

+
+

条件分岐の次はループです。ループの最も基本的なものはwhileです。whileの形式は以下のとおりです。

+
+
while 式
+  文
+end
+
+

whileは式の結果が「真」である間、文を(複数の文があればそれを順番に)繰り返し実行し、「偽」になれば繰り返しを終了します。最初に式を評価した結果が「偽」であれば、文はまったく実行しません。

+

whileを1行に書こうと思えば、式と文の間を明示するために、doかセミコロン(;)を入れる必要があります。

+
+
whiledoend
+while 式 ; 文 end
+
+

whileにも修飾子形式があります。形式は、

+
+
while
+
+

になり、これは、

+
+
while 式
+  文
+end
+
+

とまったく同じ意味です。まったく同じ意味ということは、修飾子形式では、後ろのほうに登場していても、式のほうが先に評価されるということです。

+

ただ、ループの場合本体を毎回実行して、繰り返しの終了条件のチェックはループの最後に行いたい場合があります。そのためにPascalではrepeat .. untilという文がありますし、Cにもdo .. whileがあります。

+

Rubyでは、そういう場合には後述のbreakを使う方法もありますが、beginwhile修飾子というものもあります。

+
+
begin
+  文
+end while
+
+
+

という形式の場合、まず文を実行し、次に式を評価します。beginにはrescueensureを付けてはいけません2

+
+
+

until文

+
+

ifに対してunlessがあるように、whileに対してはuntilがあります。

+
+
until 式
+  文
+end
+
+

untilは式が偽である間、文を繰り返します。また、while同様にuntilにも修飾子形式があり、begin文に付加されると式の評価の前に文を実行する点も同じです。

+
+
+

ループの中断

+
+

先ほど説明したとおり、逐次実行、条件分岐、ループの3つがあれば任意のアルゴリズムが記述できるのですが、とはいえ途中でジャンプできると便利なこともあります。

+

ジャンプといえばgotoですが、残念なことに(または幸いなことに)Rubyにはgotoはありません。その代わりもうちょっと「高級な」ジャンプがいくつかあります。今回はそのうちループに関するものを紹介しましょう。

+

ループに関するジャンプは以下の3つです。

+
+

break

+

現在実行中のループを中断します。これを使えば最後に条件チェックするループも簡単に記述できます。

+
+
while true
+  文
+  break if 式
+end
+
+

ここではwhile trueで無限ループを表現しています。で、文を実行したあとで条件をチェックして、それが成立していればbreakで繰り返しを中断するわけです。あるいは、beginwhile修飾子の組み合わせよりもこっちのほうがわかりやすいかもしれませんね。

+ +

next

+

ループ本体の実行を中断して末尾までジャンプします。主に以下のような使い方をします。

+
+
while line = gets()   # 1行ずつ読み込み
+  next if line == ""  # 空行なら処理しない
+  文                  # lineに対する処理
+end
+
+

C言語のcontinueに相当します。continueという名前でないのはnextのほうが短いからです。Perlでもnextですね。

+

redo

+

ループ本体の実行を最初からやり直します。つまり条件チェックの直前にジャンプします。C言語にも相当するジャンプがないことからもわかるようにあまり使われません。

+
+

これらのジャンプをまとめると、図7.2のようになります。

+
+ +
+ fig0702 +
+

図7.2●ループを中断するジャンプ

+
+
+
+

for文

+
+

配列などの要素それぞれに対して繰り返すためには、whileのような単純なループよりもforのほうが便利です。たとえば、配列要素を出力するのであれば、while文を使うなら、

+
+
i=0
+while i<ary.size
+  puts ary[i]
+  i+=1
+end
+
+

のように、やや面倒になりますが、forを使えば、

+
+
for i in ary
+  puts i
+end
+
+

と単純になります。forは実際にはこのあと説明するイテレータを使って実現されています。

+
+
+

イテレータ

+
+

イテレータは奥が深いので、この先連載1回分まるまる使って説明しようと思いますが、簡単にいうとメソッドの一種で「ユーザーが定義できる制御構造」です。

+

先ほどのforも内部的にイテレータを使って実現されています。ここでは仕組みは説明せず、いくつか便利なイテレータを紹介するだけにとどめます。

+
+
loop do        # loopは無限ループするイテレータです
+  文
+end
+
+3.times do     # 文を3回繰り返します
+  文
+end
+
+ary.each do|i| # forは内部的にこれと同じことをしています
+  文
+end
+
+
+
+
+
+
    +
  1. +

    映画「ブレードランナー」でレプリカントを区別するためのテスト。 +

    +
  2. +
  3. +

    でもねえ、あるので紹介しますけど、あんまりわかりやすい文法じゃなかったですよねえ。失敗か(苦笑)。 +

    +
  4. +
+
+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-011.xhtml b/docs/vol1/xhtml/p-011.xhtml new file mode 100644 index 0000000..6ec6ad5 --- /dev/null +++ b/docs/vol1/xhtml/p-011.xhtml @@ -0,0 +1,179 @@ + + + + + +第7章 条件判断とループ + + + + +

Matz Essays Volume 1

+ + + +
+

◆ 知られざるRuby ◆ end

+
+

前回はファイルをオープンするメソッドopenについて探求しましたが、今回は制御構造ということで、Rubyの制御構造を構成する重要なキーワードendについて考えてみます。

+

文のまとまりを表現する方法はプログラミング言語ごとにそれぞれあります。たとえば、Cであれば、

+
+
{ 文; }
+
+

でしょうし、Pascalであれば、

+
+
beginend
+
+

でしょう。また、Pythonのように「インデントの深さで決める」という変わり種もあります。Pythonでは、

+
+
if a == b:
+  foo
+  bar
+baz
+
+

というふうにして文のまとまりの範囲を決めます。上の例をRuby的に書けば、

+
+
if a == b
+  foo
+  bar
+end
+baz
+
+

という感じでしょうか。簡潔ですし(Rubyより1行少ないですね)、この形式を好む人もいます。

+
+

文のまとめ方はそれぞれですが、実は大きく分けると、

+
    +
  • 複文派

  • +
  • ブロック派

  • +
+

に分かれるのだということをご存じでしたでしょうか。複文派とは「複数の文を文かっこでくくったものは1つの文である」とする言語です。たとえば {} を文かっことするCや、begin, endを文かっこにするPascalは複文派です。この派の言語では、単文が置けるところにはどこにでも複文が置けるというルールがあります。ですから、たとえばCの条件式で、

+
+
if (a == b)
+  foo;
+
+

と書いたものは、fooの部分を複数の文に置き換えたければ、{} でくくった複数の文を置けばよいわけです。

+
+
if (a == b) {
+  foo;
+  bar;
+}
+
+

このやり方には2つほど問題があります。1つは単文から複文への書き換えが面倒なことです。たとえば上記の変更では、ただbarという文が追加したいだけなのに、fooの前に { を、barの後ろに } を追加する必要があります。

+

もう1つの問題は「ぶらさがりelse」という問題です。またまたCの例になりますが、

+
+
if (a == b)
+  if (c == 5)
+    foo;
+else
+  bar
+
+

と書いてあった場合、これをどう解釈しますか?

+
+
if (a == b) {
+  if (c == 5) {
+    foo;
+  }
+}
+else {
+  bar;
+}
+
+

と解釈したくなるでしょうが、実際にはインデントに関係なく、

+
+
if (a == b) {
+  if (c == 5) {
+    foo;
+  }
+  else {
+    bar;
+  }
+}
+
+
+

と解釈されます。

+

もう一方のブロック派は複文を取る文はいつもブロック(文のまとまり)を取ることが決まっている言語です。ブロック派言語には上記の2つの問題は存在しません。たとえばRubyであれば、

+
+
if a == b
+  foo
+end
+
+

barを追加したければ、ただfooの後ろの行に追加するだけです。他の部分を変更する必要はありません。「ぶらさがりelse問題」についても

+
+
if a == b
+  if c == 5
+    foo
+  end
+else
+  bar
+end
+
+

と、

+
+
if a == b
+  if c == 5
+    foo
+  else
+    bar
+  end
+end
+
+

は明らかに異なりますから、混乱を招く可能性はありません。このようにユーザーにとっての使いやすさの観点からみれば、ブロック派言語の圧勝といえるでしょう。最近開発された言語ではブロック派が主流になっているように感じられます。たとえば、Rubyと同じスクリプト言語に分類されるものに限っても、

+
    +
  • まとまりはいつも {} でくくる必要があるPerl

  • +
  • まとまりはインデントで表すPython

  • +
  • まとまりの終わりをendで表すRuby

  • +
+
+

はいずれもブロック派言語です。上記の3つの言語では同じブロック派でも記法がそれぞれまったく異なるのが面白いですね。

+

Rubyを初めて見た人はendによる文区切りが結構異様に感じられるようです。endを使う言語の中で最も有名なPascalの人気が下降する一方ですから無理もないのかもしれません。

+

では、Rubyはなぜendを文の区切りに選んだのでしょうか。

+

それにはいくつかの理由があります。

+
+

endはAlgolから続く伝統的な文区切り

+

Rubyは比較的「保守的」な部分があります。最近優勢なC, C++, Javaとは違いますが、endにはここ数十年文区切りとして使われてきた伝統があります。

+

begincaseがきれい

+

たとえば現在のRubyではbegin文は以下のような構文になります。

+
+
begin
+  文
+rescue 例外クラス
+  文
+ensure
+  文
+end
+
+

仮にこの構文を {} で表現しようと思うと以下のようになるでしょう。

+
+
begin {
+  文
+}
+rescue(例外クラス) {
+  文
+}
+ensure {
+  文
+}
+
+

なんだか間が抜けているようですし、あまりきれいではありません。case文でも同じような状況があります。ま、主観や趣味の問題かもしれませんが。

+

オートインデントが可能

+

Emacsであればruby-modeを使ってオートインデントができます。予約語を使った言語のオートインデントは記号を使ったものよりも難しいのですが、ruby-modeによってオートインデントが可能になる見通しがついたことでendを採用する決心がついたというのは正直な話です。

+
+

とはいえendに欠点がないわけでもないです。

+
+

文区切りに別の行が必要となるので行数が増える

+

しかし、逆に文の並びの終わりがはっきりわかってよいという側面もありますから、行が増えることがただちに問題であるということにはなりません。

+

文区切りが識別子と区別がつきにくく埋没しがち

+

最近のエディタは予約語に色が付けられますから、この点はさして問題にならないかもしれません。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-012.xhtml b/docs/vol1/xhtml/p-012.xhtml new file mode 100644 index 0000000..9e67c6e --- /dev/null +++ b/docs/vol1/xhtml/p-012.xhtml @@ -0,0 +1,58 @@ + + + + + +第7章 条件判断とループ + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ Linux Conference 2001

+
+

9月26日から9月28日まで、東京の明治記念館においてLinux Conference 2001が開催されました。例年、Linux Conferenceはプログラム委員から講師候補に直接コンタクトがあったのですが、今年はCall for Paper(論文応募)方式に変更されました。正直、論文を用意するのも大変なので、今年は発表しないつもりでいたのですが、広島市立大の木山さんが応募した論文が2本も通ったので、セッションを1つ作るために「埋め草」として話しませんか、と誘われたので引き受けることにしました。頼まれるとなかなかイヤとはいえないのです。

+

ということで、スピーカー特権で、26日から参加してきました。プログラムは、

+
    +
  • http://lc.linux.or.jp/

  • +
+

から参照できます。

+

26日には、『Rubyを256倍使うための本 無道編』の著者である青木峰郎さんによるチュートリアル『Ruby活用〜オブジェクトは恐くない』に参加しました。青木さんのチュートリアルは、オブジェクト指向設計に注目して、いかに共有を推し進めるか、いかに「よい」プログラムを書くかということについて解説がありました。発表資料は、

+
    +
  • http://www.loveruby.net/~aamine/ja/lc2001/

  • +
+

から入手できます。とはいえ、青木さんはこの手の発表は初めてだったようで、かなり緊張していました。『無道編』や『紅玉制覇編』でのなめらかな語り口は聞かれませんでした。ちょっと残念。また、チュートリアルでは実際に段階的に「よい」プログラムを開発していくという形式だったのですが、「よい」プログラムになっているはずなのに、なんとなくだんだん複雑になっていくという皮肉な現象も観測されました。

+

27日午後には「プラットフォーム: Ruby」と名付けられたRuby関連セッションが開催されました。内容は木山さんによる「オブジェクト指向スクリプト言語Rubyへの世代別ごみ集め実装手法の改良とその評価」、木山さんと同じ大学の佐原大輔さんによる「RubyのVMに向けて」、それから私による「Rubyの心理学」の3つの発表が行われました。

+

木山さんの世代別GC(ガベージコレクション)の実装については、その基本的実装について昨年11月のLinux Confereneでも発表されましたし、私は実際に一緒に開発も行いましたので、私にとっては耳新しいものではありませんでしたが、GCとは何か、というところから始まって、世代別手法がどのような仕組みで効率を改善するのかという点と、また実際にどの程度の改善が行われるかについて、よくまとまった発表でした。実際に全体の実行時間が39.3%、GC実行時間が88.7%も削減したという話でした。

+
+

もちろんすべてのケースでここまで高速化されるわけではないのですが、GCの実行速度は普段はあまり問題にならず、問題になるような場合というのは大量のオブジェクトを取り扱うケースで、そのような局面において世代別GCが有効であること、また、今年に入ってからの改善でさらに高速化されたことが示されました。

+

ただ、木山さんにとって気になる点がいくつか残っているそうで、今のRubyに取り込むよりも、将来のVMバージョンに組み込んだほうがよいのではないかと言っていました。

+

佐原さんのVMの実装の話はなかなか興味深いものでした。VMというのは仮想機械(Virtual Machine)の略で、プログラムを仮想的なCPUの機械語(よくバイトコードと呼ばれます)に変換して、実行しようとするものです。また、この仮想的なCPUのエミュレータのことをVMと呼びます。

+

私はこの実装については何もタッチしていません。まだプロトタイプレベルで、Rubyプログラムを直接実行するレベルには達していないとはいえ、独自にここまで開発を行う人が出てくるというだけも、私にとっては驚異です。実際、Rubyは処理系開発者よりも、プログラマーに対して優しいように設計されているので、処理系を開発するのは結構大変なのです。

+

さて、今回実装されたVMは、まだ構文解析を行うパーサーはなくて、アセンブラしか解釈できないうえに、クラスライブラリも数値しかないのだそうですが、それでもフィボナッチ関数(fib)や竹内関数(tak)の実行は、現在のRubyと比較して3倍近く高速化されるのだそうです。

+

また、目標は現状よりも10倍速い処理系を実装することだそうです。本人も難しいだろうとは自覚しているようですが。

+

Ruby実装者の立場から聞くと、静的言語向けのVMであるJava VMをベースに設計したのはどうだろうか、とかいろいろ疑問があるにはあるのですが、なかなか頼もしい発表でした。

+

私のものは前2つと比較すると恥ずかしいくらい不真面目な内容でした。要するにRubyってのはどういうふうに人間向けにデザインされているかとかいうような内容です。簡単にまとめると、プログラミング言語は実はマン・マシン・インターフェイスであり、ヒューマンインターフェイスの原則が適用されるということと、そしてその結果、言語設計は人間に注目して行われるべきことを紹介したつもりです。

+

私の発表資料は、

+
    +
  • http://www.ruby-lang.org/ja/lc2001/

  • +
+

で公開しています。

+

セッション終了後、明治記念館の一室を借りて、Ruby BOFが開かれました。BOF とはBird Of a Featherの略で、1つのことに興味がある人が集まってワイワイやる集会のことです。今回も日本にはRubyユーザーグループがない話から、VMの実装の話まで多岐にわたって話が制限時間いっぱいまで行われました。

+

28日にもライトニングトークを始めとして、Rubyの話がいくつかあったようなのですが、この日は娘の誕生日なのであきらめて帰りました。家族からこれ以上不評を買ってもいけませんから。

+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-013.xhtml b/docs/vol1/xhtml/p-013.xhtml new file mode 100755 index 0000000..7d96b6b --- /dev/null +++ b/docs/vol1/xhtml/p-013.xhtml @@ -0,0 +1,278 @@ + + + + + +第8章 オブジェクトと変数 + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay8 +
+

+初等Ruby講座
+オブジェクトと変数 +

+
+

[Linux magazine, 2002年1月号]

+
+

変数についての説明です。そういえばこの頃は「変数は入れ物」という発想をする人が多くて、たとえば複数のローカル変数が同じ配列を指しているときに、片方の変数経由で配列要素を書き換えるともう1つの変数も変わってしまって驚いた、などという話を頻繁に聞きました。そういう誤解がこの記事を書く動機になったのですが、最近はそういう誤解は減ったように感じます。やはり、JavaScriptやPythonのようなメジャーな言語もRubyと同じ変数モデルを採用しているからでしょうか。

+

「開発日記」はアメリカで開催された第1回RubyConfについての話題です。自分の作った言語に関するカンファレンスが(勝手開催のOOPSLAの前座とはいえ)、アメリカで開催されるのは私にとって感激でした。ここでは出席者は70名くらいと書いてあるんですが、別の資料には34名とあって矛盾しています。私の記憶は信用できないんで、本当は34名だったんでしょう、たぶん。

+
+
+

Rubyはオブジェクト指向言語ですから、オブジェクトという単語がいつもつきまといます。しかし、これがわかりにくい原因なのだという意見もあります。今回はこのオブジェクトの基礎を解説しようと思います。けれど、いわゆるオブジェクト指向プログラミングでよく使われる継承とかカプセル化のような難しげな概念には踏み込みません。

+
+
+

オブジェクトとは

+
+

オブジェクト(object)とは英語で「モノ」という意味です。Rubyで扱うものは、すべてこれオブジェクトです。ですから、整数の1も、文字列の"abc"もオブジェクトです。Rubyプログラムで表現するものは、現実世界の具体的な存在を表したもの(たとえば犬)も、抽象的なもの(たとえば集合)もすべてオブジェクトです。

+
+

Rubyではオブジェクトのことを値と呼ぶこともあります。これは重要な点ですので覚えておいてください。

+
+

Rubyでは値とはオブジェクトのことである

+
+

世の中の種々雑多なモノがあるように、オブジェクトもさまざまです。しかし、1つだけ決まっていることがあります。それは「すべてのオブジェクトは命令に反応する」という点です。オブジェクトに命令すれば、オブジェクトは自分が知っている命令であれば、それに対応する仕事を実行しますし、知らない命令であれば「そんな命令は知らない」と返事します。たとえば文字列にはその長さを求めるように命令することができ、犬にはえるように命令することができます。

+

つまり、どのようなオブジェクトであっても、そのオブジェクトがどんな命令を受け付け、それに対してどんな仕事をしてくれるかを知っていれば、使いこなすことができます。現実世界と違ってオブジェクトは知っている命令には完全服従します。ただし、「正しくプログラムされていれば」ですが。これがオブジェクト指向の最低限の決まりごとです。

+

この命令のことを「メッセージ」、命令に対応する仕事のことを「メソッド」と呼びます。でも、この2つはあまり区別されないで、単にメソッドと呼ばれることも多いです。

+
+
+

メソッド

+
+

メソッドを呼び出すためには、Rubyでは以下のような文法を使います。

+
+
obj.x()
+
+

obj」の部分が「オブジェクト」です。この部分にはオブジェクトを指定する式が書けます。このオブジェクトはメッセージを受け取るオブジェクトですから、「レシーバ」と呼ばれることがあります。

+

x」の部分が「メッセージ」です。これでレシーバのxという名前の「メソッド」を呼び出しているわけです。()の部分は今回は空ですが、コンマ(,)で区切った任意の数の式が書けます。これはメソッドの「引数」(「ひきすう」と読みます)と呼ばれ、メソッドに与える追加的な情報になります。たとえば文字列の長さを求めるためには文字列にlengthメッセージ を送ります。言い換えれば、文字列のlengthメソッドを呼び出すということです。

+
+
"abc".length() # 3が得られる
+
+

メソッド引数のためのかっこは原則的に省略できますから、上の命令は、

+
+
"abc".length
+
+

と書いても同じです。ただし、あまり省略しすぎるとプログラムが読みにくくなるかもしれません。特に引数がある場合には混乱のもとです。やりすぎは禁物です。

+

メソッド呼び出しには、メッセージの受け手であるオブジェクトを省略した形式もあります。その場合の形式は、

+
+
x(arg1, arg2)
+
+

のようになります。xがメッセージで、arg1arg2が引数です。引数が2個なのは単なる例で、任意の数が指定できます。この場合のメッセージの受け手は、実行中のメソッドのレシーバになります。

+
+

メソッドの外側でも、レシーバ用に用意されたオブジェクトが設定されていて、受け手が省略されたメッセージを受け取ってくれます。Rubyのプログラムにおいては常に何らかのオブジェクトがレシーバ、あるいは別の言い方をすると省略時のメッセージの受け手として控えています。レシーバは、いつもselfという変数(正確には「擬似変数」と呼びます)によって参照することができます。

+

今話した「その辺のオブジェクト」がなにもので、どのような機能と振る舞いを持つかについては、今後詳しく説明することにしましょう。

+

ですから、先月までにすでに出ていた、

+
+
puts "hello"
+
+

なども、この「その辺のオブジェクト」がputsメソッドを実行してくれていたわけなんですね。この例の場合、putsが「メッセージ」で、"hello"が引数になります。引数の周りのかっこが省略されていますね。

+

メソッドの呼び出しはRubyの基本的な機能の1つです。先月学んだ制御構造などを除いては、Rubyのほとんどの機能はこのメソッド呼び出しを使って利用することができます。

+
+
+

クラス

+
+

オブジェクトはおおむね種別ごとに分類できます。手あかのついたような例ですが、ここにポチというオブジェクトがあった(いた)場合、私たちはそれを犬であると認識するわけです。現実世界に犬というモノがあるわけではなく、犬というのはあくまでも抽象的な種別にすぎないわけです。この種別のことを「クラス」と呼びます。ですから、ポチは犬クラスに属するオブジェクトということになるわけです。クラスに属することを強調するときにはオブジェクトのことをインスタンスと呼ぶことがあります。ですから、犬クラスに属するオブジェクトであるポチは、犬クラスのインスタンスと呼ぶわけです。

+

同じクラスに属するオブジェクトは原則として同じメッセージに反応し、同じメソッドを持ちます。しかし、ポチは同じ犬クラスのオブジェクトであっても別の犬であるタロやジロとは性質は似ているものの微妙に違うように、同じメソッドといえどもいつも同じ結果を返すわけではありません。具体的には毛皮の色が違ったりするわけです。

+

クラスを定義するためにはclass文を使います。

+
+
class Foo
+ ...
+end
+
+

また、他のクラスの定義を元に新しいクラスを作るときには、元になるクラスを指定します。元になるクラスのことをスーパークラスと呼びます。

+
+
class Bar<Foo
+ ...
+end
+
+
+

クラスの中ではメソッドなどを定義します。メソッド定義はdef文を使います。

+
+
class Dog
+  def bark(how)    # 「吠える」メソッド
+    puts how
+  end
+end
+
+

これでDogクラスに属するオブジェクトは吠えることができるようになりました。

+
+
pochi = Dog.new     # Dogオブジェクトはこうして作る
+pochi.bark("わんっ") # 吠えさせてみよう、わんっ
+
+

クラスの定義については、今回はここまでにしておきます。詳細は今後きちんと説明することにします。

+
+
+

オブジェクト基礎のまとめ

+
+

ここでここまで学んだことをまとめておきましょう。重要な用語は以下のとおりです。

+
+

オブジェクト

+

オブジェクトは「モノ」です。オブジェクトにはそれぞれ機能があり、命令すると仕事をしてくれます。Rubyで取り扱うあらゆるものはオブジェクトです。ですから、Rubyにおいて「値」とはオブジェクトのことです。

+

メッセージ

+

オブジェクトに命令するときの命令の名前がメッセージです。

+

メソッド

+

メッセージを受けたオブジェクトが実際に行う作業のことをメソッドと呼びます。慣習としてメッセージよりもメソッドという単語をよく使います。ですから、「文字列にlengthメッセージを送る」よりも「文字列のlengthメソッドを呼び出す」という言い方のほうが頻繁に見かけます。

+

クラス

+

オブジェクトの「種別」がクラスです。同じクラスに属するオブジェクトは(まだ説明していない例外を除いて)、同じメソッドを持ち、同じメッセージに反応します。

+
+
+
+

オブジェクトの指定

+
+

で、このオブジェクトを指定する方法は3つあります。それは、「リテラル」「変数」「式」の3つです。

+

ある種のオブジェクトはたびたび用いられるので、プログラムの中で直接表現する方法が用意されています。たとえば、プログラムの中で数字の1が登場すれば、それは整数の1オブジェクトという意味ですし、"abc" は文字列オブジェクトを意味します。

+

変数はオブジェクトに付けるラベルのようなものです。オブジェクトにラベルを付けておけば、あとでそのラベルでオブジェクトを使うことができます。たとえば、

+
+
a = "abc"
+
+
+

という形で"abc"という文字列にaという名前を付けておくと、

+
+
a.length
+
+

という形でaという名前を付けたオブジェクトのlengthメソッドを呼び出すことができます。

+

そして、メソッド呼び出しや1+1のようなものが式です。上のlengthメソッドの呼び出しも式です。これらの組み合わせでオブジェクトを表現することができます。

+
+
+

変数の種別

+
+

変数には以下の種類があります。

+
    +
  • ローカル変数

  • +
  • グローバル変数

  • +
  • クラス変数

  • +
  • インスタンス変数

  • +
  • 定数

  • +
  • 擬似変数

  • +
+
+

ローカル変数

+

ローカル変数はメソッドの中だけで有効な変数です。ローカル変数の名前はアルファベットの小文字またはアンダーライン('_')で始まります。

+
+
+

グローバル変数

+

グローバル変数とはつまりプログラムのどこからでもアクセスできる変数です。どこからでもアクセスできるってことは、誰がいつ変更したのかわからなくなりやすいってことでもあるので、これを使うのはあんまりお勧めじゃありません。

+

グローバル変数の名前は $ から始まります。多用するとプログラムが醜くなりますね。これもお勧めしない理由の1つです。グローバル変数の例はこんな感じです。

+
+
$foo
+$_
+$1
+
+

なお、グローバル変数の一部はローカル変数と同じように振る舞うものがあります(たとえば$_, $~, $1 など)。ですから、グローバル変数というよりもむしろ「スペシャル変数」というような名前のほうが適切なのかもしれません。呼び名を変えようかなあ。

+
+
+ +

インスタンス変数

+

インスタンス変数はオブジェクトごとの変数です。「犬の名前」とか「毛皮の色」とかオブジェクトごとに違う状態を表現するための変数です。インスタンス変数は名前が、@ で始まります。こんな感じです。

+
+
@foo
+
+

インスタンス変数は実行中のメソッドのレシーバに属します。

+
+
+

定数

+

変数なのに定数とはなにごとかと、毎回説明してて思うんですが、こればっかりはしょうがありません。

+

定数とはクラスに属する固定された(ほんとはちょっと違うんですが)値のことです。クラスが違えばアクセスできる定数も変わります。

+

定数の名前は大文字で始まります。定数の例はこんな感じです。

+
+
ARGV
+RUBY_VERSION
+
+

それから、クラス名も定数です。

+
+
String
+Array
+
+

値が設定されていない定数は存在しませんから、アクセスはエラーになります。

+
+
+

クラス変数

+

定数にちょっと似てるのがクラス変数です。クラス変数は名前が、@@ で始まります。クラス変数はこんな感じです。

+
@@foo
+

定数と同様に値が設定されていないクラス変数は存在しませんから、アクセスはエラーになります。しかし、クラス変数は「変数」ですから、値を自由に変えることができます。

+
+
+

擬似変数

+

別の意味で定数に似ているのが擬似変数です。擬似変数というのは、見かけはローカル変数と同じなのに値が固定で代入できないものです。擬似変数に代入しようとすると文法エラーになります。

+

擬似変数には表8.1の6つがあります。

+
+

表8.1●擬似変数

+ + + + + + + + + + + + + + + + + + + + + + + + + +
true真(条件が成立していること)を示す値
false偽(条件が成立していないこと)を示す値
nil未定義(初期化されてないとか)を示す値
self実行中のメソッドのレシーバ
__FILE__実行中のファイル名
__LINE__実行中の行番号
+
+
+
+
+ +

代入

+
+

代入とは変数(など)にオブジェクトを割り当てる操作です。言い方を変えればオブジェクトに名前を付けると言ってもよいでしょう。決して間違えてはいけないのは「変数はオブジェクトを『指す』もので、オブジェクトの容器ではない」ことです(図8.1)。

+
+ +
+ fig0801 +
+

図8.1●複数の変数が同じオブジェクトを指す

+
+

ここではとりあえず「変数は容器である」という考え方を「Rubyでない考え方」、「変数は参照である」という考え方を「Rubyの考え方」と呼ぶことにします。普段はあまり問題にならないのですが、ここを押さえておかないと複数の変数が同じオブジェクトを指しているときに混乱します(図8.2)。

+
+ +
+ fig0802 +
+

図8.2●複数の変数が同じオブジェクトを指す

+
+

「Rubyでない考え方」では文字列の一部を変更した場合、元の文字列は変化しますが、代入先は変化しません。一方、「Rubyの考え方」では、指している先が変化しますから、変数aの値と変数bの値も同時に変化するように見えます。これはつまり、変数aから参照されるオブジェクトも、変数bから参照されるオブジェクトも同じであるということを意味しています。

+

「なんで?」と思う人もいるかもしれません。実は、これは「値」という言葉をどうとらえるかにかかっているのです。

+

最初のほうで説明したように、Rubyでは「値」とはいつも「オブジェクト」のことです。ですから、変数aの値が "abc" だとすると、その「値」を変数bに代入するということは、"abc" という文字列オブジェクトが変数bからも参照できるようになるということを意味します(図8.2(2))。代入に当たってオブジェクトのコピーは発生しません。

+ +

一方、「Rubyでない考え方」では「値」は「ある種の状態」です。ですから、この考え方のもとでは "abc" という「値」は「文字a, b, cが順に並んでいる状態」のことです。これを代入するということは、変数bの格納している状態が変数aと同じになるということです(図8.2(1))。一度代入されてしまえば、変数aの値と変数bの値は無関係ですから、変数aの値を変化させても変数bには影響されません。

+

BASICやPerlなど多くの言語では「変数は容器」という「Rubyでない考え方」を採用しています。それらの言語の知識がある人はこの点に引っかかることが多いようです。しかし、Lisp, Smalltalkなどの言語ではRubyと同じ「変数は参照」という考え方を採用しています。一度慣れてしまえば、Rubyのような考え方のほうが一貫性があって理解しやすいと思います。Cなどでは構造体変数などは「容器」として振る舞いますが、ポインタは「参照」として振る舞うのでますます混乱します。そういえば、Perl 5にも「参照」というポインタのようなものがありますね。

+
+
+

まとめ

+
+

今回はオブジェクトと変数について説明してみました。今回説明した部分は基本的すぎてあまり具体的な実例が出せませんでした。しかし、ここは基礎として重要ですから我慢してください。

+

次回はより具体的なクラスについて学ぼうと思います。

+
+
+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-014.xhtml b/docs/vol1/xhtml/p-014.xhtml new file mode 100644 index 0000000..9c01dd5 --- /dev/null +++ b/docs/vol1/xhtml/p-014.xhtml @@ -0,0 +1,205 @@ + + + + + +第8章 オブジェクトと変数 + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ 知られざるRuby ◆ 変数

+
+

入門編でも変数について学びましたが、こちらではより詳細に変数について見てみましょう。変数のルールは、詳細に見ると結構いろいろ複雑です。でも、普通に使うぶんにはあまり問題にならないように設計したつもりです。本当です、信じてください。

+
+

ローカル変数

+

ローカル変数で注意すべき点は、スコープ(有効範囲)と宣言です。

+

変数はある特定の範囲だけで有効な変数です。特定の範囲とは、以下のいずれかです。

+
    +
  • def文(defからendまで)

  • +
  • class文(classからendまで)

  • +
  • module文(moduleからendまで)

  • +
  • トップレベル(上記のすべての外側、ファイル末尾まで)

  • +
  • ブロック(doからendまで、{ から } まで)

  • +
+

この範囲内で代入されたローカル変数はそのスコープの終わりまで存在します。代入はメソッドの引数やブロックのパラメータとして値が設定されることを含みます。その範囲の実行が終わるとローカル変数は自動的に消滅します。他の言語に慣れた人は、begin文が新しい変数スコープを作らないことに注意する必要があります。

+

ブロックによるスコープを例外として、外側のスコープのローカル変数は見えません。ブロックによるスコープの場合には外側のスコープのローカル変数も参照することができます。

+

このブロックのルールは少々ひっかかりやすくて、たとえば、

+
+
["foo", "bar", "baz", "quux"].each do |x|
+   if /^q/ =~ x
+     len = x.length
+   end
+   break
+end
+puts len
+
+

というプログラムで、qから始まる要素の長さを求めようと考えた場合、結果を代入したlenという変数が、ブロック内部で初出のため、結果を出力しようとブロック終了後で参照した場合、すでに消えてなくなっている、というような目にときどき出くわします。

+

Rubyは変数の宣言のない言語として知られていますが、明示的な宣言文がないというだけで、変数の種別によっては代入が変数宣言的な役割を果たしています。ローカル変数もスコープの中での最初の代入が宣言の役割を果たします。しかたがないのでこのような場合には、ブロックの前にローカル変数lenを初期化してやる必要があります。

+
+

個人的には、この設計はあんまりよくなかったなあと思うときもあって、できることならば直したいと思っているのですが、コトはそんなに簡単ではないので将来直せるかどうかはわかりません。

+

この仕様は別に誰かを悩ませるためではなくて、それなりに目的があるのです。この仕様が役に立つ場合もいくつかはあるのです。たとえばProcを使って名前のない手続きを作ろうとしたときに、この仕様によってブロック内部の変数をその手続きのローカル変数として使えます。

+
+
fact = Proc.new{|n|    # nはブロック内ローカル変数
+  if n == 0
+    1
+  else
+    n * fact.call(n - 1)
+  end
+}
+puts fact.call(5)
+
+

また、スレッドではスレッドローカル変数としても役に立ちます。

+
+
Thread.new do
+  for i in data        # iは外部から影響を受けない
+    ....
+  end
+end
+
+

代入が行われていないローカル変数の参照は、引数のないメソッド呼び出しとみなされます。たとえ同じスコープの後ろのほうで変数に代入していてもです。ということは、やろうと思えば、

+
+
puts      # 引数のないputsは改行を出力する
+puts = 5  # putsというローカル変数ができる
+puts      # ローカル変数アクセス、改行出力されない
+
+

ということもできてしまうわけです。が、こういうのは当然よくない例であり、やるべきではありません(よね)。

+

ローカル変数はコンパイル時に決定されます。ですから、変数が定義されているかどうかを判定するdefined? はその場所でローカル変数が定義されているかどうかだけを判定します。

+
+
2.times do          # 2回ループするんだけど...
+  puts defined?(a)  # ここではaは定義されてないからnil
+  a = 25            # ここからendまでaが有効
+  puts a            # 25を出力
+end
+
+

また、実行時にevalで定義されたローカル変数はプログラムからは見えません。

+
+
eval("a = 25")
+puts a              # 見えない、エラー
+
+
+
+ +

グローバル変数

+

グローバル変数はどこからでも見えるという非常に単純なルールがありますので、有効範囲について心配する必要はありません。

+

グローバル変数について注意すべき点は、アクセス(参照や代入)によって特別な処理が行われる変数があるという点です。たとえば、$_$1 などの特殊変数ではグローバル変数のような見かけをしているがローカル変数のようにメソッドごとに独立の値を持つものがあります。また、$SAFEという変数はスレッドごとに独立した値を持ちます。

+

グローバル変数にはRubyレベルでも代入が行われるときの処理を定義できます。処理の定義はtrace_varメソッドで行います。

+
+
trace_var 変数名 {|v| 手続き}
+
+

ブロックには新しい値がパラメータとして渡されます。たとえば、以下のように使います。

+
+
$foo = 1
+trace_var(:$foo) {|v| puts "$foo is #{v}"}
+$foo = 42    # "$foo is 42" と出力
+$foo = "hi"  # "$foo is hi" と出力
+
+
+
+

インスタンス変数

+

インスタンス変数には他の変数たちほど隠された働きはありません。ただ単に代入された値が設定されるだけです。インスタンス変数は最初に代入されたときからそのオブジェクト内に存在するようになります。代入によって初期化されていないインスタンス変数をアクセスするとその値はnilになります。インタプリタに -wオプションが指定されている場合には、未初期化のインスタンス変数のアクセスに対して警告が出力されます。

+

インスタンス変数が代入によって生じるということは、同じクラスのインスタンスでもそれぞれインスタンス変数が異なる場合がありえるということでもあります。これはC++やJavaなど他のクラス定義の中にインスタンス変数の定義が含まれるオブジェクト指向言語に慣れた眼には奇異に映るかもしれません。これはRubyの動的な性質を表しているといえると思います。

+
+
+

クラス変数

+

クラス変数への代入はメソッドの外側では定義としての働きがあり、メソッドの内側では値の更新を行います。

+

メソッドの内側でクラス変数への代入を行う場合、もしそのクラス定数がすでに外側で代入されていればそのまま変数の値を更新します。もし、そのクラス変数への代入が行われていなければ、エラーになります。

+
+
class Foo
+  @@foo = 1
+  def foo
+     @@foo = 2  # @@fooの値が変わる
+     @@bar = 3  # 未定義、エラー!
+  end
+end
+
+
+

メソッドの外側でクラス変数へ代入を行うとき、もしそのクラス変数がすでに定義されていれば、クラス変数の名前が重複してクラス変数の有効範囲がわかりにくくなります。そこで、そういう場合には警告が出力されます(Ruby 1.7.xで -wオプションを指定した場合)。たとえば、以下のようなプログラムの場合、

+
+
module M1
+  @@foo=1
+end
+module M2
+  @@foo=2
+end
+class Foo
+  include M1
+  include M2
+  p @@foo
+end
+
+

クラスFooではモジュールM1から受け継いだ @@fooとモジュールM2から受け継いだ @@fooの両方が存在することになります。挙動としては後から引き継いだM2@@fooが有効になるのですが、あいまいであるので、

+
+
warning: class variable @@foo of M1 is overridden by M2
+
+

という警告が出力されます。

+

なお、クラス変数はそのクラスやモジュールを継承したすべてのクラスやモジュールからアクセスできるため、スコープを限定したグローバル変数といってもよいので利用には注意が必要です。

+
+
+

定数

+

定数の挙動を詳細まで見ると少々混乱するかもしれません。定数の振る舞いはRubyの中でもなかなか複雑になっています。まず、最初にRubyの定数というものは以下の性質があります。

+
+
+ +
+ fig0803 +
+

図8.3●定数の有効範囲

+
+ +

定数への代入はメソッドの中ではできませんから、定数の代入というのは定義とか宣言という意味合いが強くなります。

+

メソッドの外であれば、定数への代入は自由にできます。このとき、代入を行ったクラスで同じ名前の定数がすでに定義されていた場合、警告を出したうえで定数の値を書き換えてしまいます。

+

一方、代入を行ったクラスには同じ名前の定数がなくて、スーパークラスで定数が定義されている場合、現在のクラスにおいてだけ、その定数の値が置き換わります。元のクラスの定数の値には影響がないことに注意してください。

+
+
class Foo
+  CONST1 = 1
+  CONST2 = 2
+  CONST1 = 3  # 警告のうえ、書き換え
+end
+
+class Bar<Foo
+  CONST2 = 4  # Barにおいてのみ値が置き換わる
+end
+
+

最後にRubyの定数について誤解を招きやすい2つの点を注意しておきます。1つはすでに述べたように実際にはやろうと思えば定数の書き換えができる点です。警告はされるものの厳密には定数でないという点には注意が必要です。

+

もう1つの点は、定数というのはその値、つまり指しているオブジェクトが変わらないのであって、指しているオブジェクトの状態が変化しないわけではないという点です。つまり、以下のようなプログラムにおいて、

+
+
ARRAY = [1,2,3,4]
+
+

定数の参照する配列の要素を置き換えると、

+
+
ARRAY[0] = 10
+
+

定数ARRAYの指す配列は[10,2,3,4]になります。しかし、これは値が変わったわけではないという点に注意してください。配列を変化させたくなければ、freezeするという手があります。

+
+
ARRAY.freeze
+ARRAY[0] = 20  # 内容の変更、エラー!
+
+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-015.xhtml b/docs/vol1/xhtml/p-015.xhtml new file mode 100644 index 0000000..4f39627 --- /dev/null +++ b/docs/vol1/xhtml/p-015.xhtml @@ -0,0 +1,111 @@ + + + + + +第8章 オブジェクトと変数 + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ RubyConf.new (2001)

+
+

同時多発テロ以来キナくさいアメリカへ行くのはホンキかと、周囲の心配もよそに、行ってきました、フロリダへ。今回、フロリダ州タンパでRuby ConferenceとOOPSLAが開かれたのに参加してきたのです。

+

いやあ、炭疸たんそ菌の騒ぎやアフガニスタン空爆まで始まって一時はどうなることかと思いましたが、厳重な警備体制を乗り越えて無事に行って帰ってきました。空港では毎回何度も何度もパスポートをチェックされたり、自動小銃を抱えた軍人さん(州兵でしょうね)を何人も見かけたりしました。

+

で、結局タンパに着いたのはカンファレンス当日の早朝(というか前日深夜というか)で、時差のせいかカンファレンスには遅刻しちゃいました。朝ご飯も食べ損ねたし。

+

さて、それはともかく記念すべき第1回Ruby Conferenceのプログラムは表8.2です。

+
+

表8.2●Ruby Conferenceプログラム

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Friday October 12
08:30Continental breakfast
09:15Dave Thomas: Welcome address
09:30Andy Hunt: Ruby Insurgency
Pete McBreen: Using Ruby to Teach OO programming
12:00Lunch
13:00Emil Ong: Parallel programming with MPI Ruby
Avi Bryant: Web Development with IOWA
Lyle Johnson: Developing GUIs with Fox and Ruby
Ken Rubotzky: Scouting Japanese Resources
18:00Dinner, followed by
Matz: Keynote
Saturday October 13
08:30Continental breakfast
09:00Nathaniel Talbott: esreveR nI gnitseT
Ron Jeffries and Chet Hendrickson:
Pairing Fishbowl, or how Ruby works with XP
12:00Lunch
13:00David Alan Black: Ruby Behaviors
Ryan Leavengood: Ruby Gems
14:30General brainstorming session:
Let’s design RAA.succ
+
+

Ruby Conferenceには7カ国から参加があったそうです。私は6カ国(アメリカ、カナダ、日本、スウェーデン、ドイツ、ベネズエラ)までしか数えられなかったのですが、Dave Thomasは7カ国だと強く主張していました。もしかしたら、彼(イギリス出身、テキサス在住)は自分をイギリス人であると数えたのかもしれません。参加人数は70人くらいだったかなあ。

+

発表資料はwww.rubyconf.orgから入手できますから、見ていただきたいのですが、皆なかなか真面目かつ真剣で、8年前気楽な気持ちで開発を始めたRubyがこんなことになるとは予想もできませんでしたし、なんだか不思議な気持ちでした。

+

印象的だったのは、日本以上にXP、それもTest First Programmingが注目されている点でした。13日にはXPに関するセッションも用意されていましたし、XP界からRon JefferiesとChet Hendricksonが参加してRuby開発環境を作るというテーマでペアプログラミングを実演してくれました。なんだか掛け合い漫才のように見えたというのは彼らには秘密にしておいてくださいね。あ、当然ながら開発環境はほんのサワリしか実現しませんでした、念のため。

+

私はもともとテストとかあんまり好きなほうじゃないんですが、これを機会にTest First Programmingに宗旨替えしようかなあなんて思いました。まだ、思っただけで実践してないんですが。

+

他にもホットな話題や議論はたくさんあったのですが、とても書ききれません。「次世代RAAを作ろう」というRAA.succのブレインストームなどやけどしそうな熱さでした。総括した印象としては、今回のカンファレンスはこじんまりとはしていますが、参加者は非常に熱意があり、参加していて気分のいいカンファレンスでした。参加者はみな一同に来年もやろうと話していました。

+ +

参加者には図8.4のようなTシャツが配られました。日本では3人しかもってないレアものです。いいでしょう?

+
+ +
+ fig0804 +
+

図8.4●RubyConf Tシャツ(モデルはmatz)

+
+

Ruby Conferenceの翌週には同じ町でOOPSLAが開かれました。OOPSLAとは「Object-oriented Programming, System, Language, and Applications」の略でACM(計算機械学会)の主催するオブジェクト指向に関する世界最大のカンファレンスです。OOPSLAが始まった80年代の後半は私も学生だったので論文集とかを一生懸命読んだ記憶がありますが、実際に参加するのは初めてです。

+

OOPSLAでは、9月にデンマークのJAOOで会ったWard Cunningham, Martin Fowlerに再会しましたし、他にもSmalltalkの実装で有名で、Ghostscriptの開発者でもあるPeter Deutcheなどにも会いました。尊敬する人たちに会ってかなり緊張しました。

+

しかし、しばらくOOPSLAについてはチェックしていなかったのですが、まるでJavaのカンファレンスのようにJavaばかりが話題になっていました。教育についても、開発手法についても、また処理系の実装についてもJava一色という感じでした。ちょっとだけSmalltalkの話をする人たちがいて、さらにわずかな人たちがRubyの話をしてました。

+ +

アメリカから帰国後、私は高熱を出して寝込んでしまい、ヤバい病気ではないか、やっぱり行かせるべきではなかったと周囲を当惑させましたが、大事には至らず今は元気です。ご心配なく。

+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-016.xhtml b/docs/vol1/xhtml/p-016.xhtml new file mode 100755 index 0000000..6e1c8d9 --- /dev/null +++ b/docs/vol1/xhtml/p-016.xhtml @@ -0,0 +1,485 @@ + + + + + +第9章 配列 + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay9 +
+

+初等Ruby講座
+配列 +

+
+

[Linux magazine, 2002年2月号]

+
+

配列とソートについて解説しています。この辺の基本的な部分は何年経っても変わりませんね。

+

「開発日記」はRuby 1.8リリース直前の様子を紹介しています。当時開発中だったRuby 1.7というバージョンは、開発版として結局正式にリリースされることはありませんでした。この頃はバージョン番号の2桁目が奇数は開発版、偶数は安定版というルールを採用していました。ですから、1.3, 1,5, 1,7は開発のみでリリースされず、1.4, 1.6, 1.8は安定版としてリリースされました。同じ時期にはPerlもLinuxも同様のルールでしたから、一種の流行だったんでしょうね。このルールはRuby 1.9で見直され、1.9.0を開発版、1.9.1以降を安定版とするルールに変更されました。モチベーションは限りあるバージョン番号という「リソース」を節約するためです。Rubyのバージョン番号は各桁数字1つと決めているので余裕が少ないんですよね。

+

さて、本文中、「2002年初頭までには1.8がリリースされていることでしょう」とありましたが、実際にRuby 1.8がリリースされたのは2003年8月4日でした。実に1年半以上遅れましたね。この頃はまだ定期リリースという概念がなく、ずるずると遅くなりがちだったのを覚えています。

+
+
+

先月まではRubyの本当の基本である文法などについて見てきましたが、今回からRubyの胴体であるクラスライブラリについて紹介します。今回は配列について学びます。

+
+
+

配列とは

+
+

皆さんは単数と複数について悩まれたことはおありですか? 職業柄(というか海外にユーザーがたくさんいるソフトウェアの開発者という立場上)、大量の英語のメールを受け取って、それらに返事をしなければならない私は、モノの単数と複数についてときどき間違えてしまいます。日本語には単語の複数形という概念も、主語が単数形か複数形かで動詞が変化するというルールもないので、ついうっかりしちゃうんですよね。最初は単数だった主語を複数にしたのに動詞はそのままだったとかしょっちゅうです。ああ、英語をしゃべる国に生まれたらラクだったのになあ。いや、日本語が世界言語というのもよいな。

+
+

まあ、愚痴はここまでにして、Rubyの話に戻りましょう。Rubyも言語には違いありません。Rubyには単数形や複数形があるのでしょうか。

+

人間の使う自然言語よりもずっと単純なRubyには、実は複数形はありません。単数形だけです。では、複数のものを取り扱うときにはどうするのでしょう。

+

魚屋に行ってイワシを3匹買った場合をRubyで取り扱おうと思えば、イワシ個別に名前を付ける(鰯一郎、鰯次郎、鰯花子とかでしょうか)というのが一番簡単な解法です。でも、これでは融通がききませんし、あらかじめ決まっていない数の魚を取り扱えません。そういうときには複数の魚を載せるトレーのようなオブジェクトを使います。このようなオブジェクトのことを、「コンテナ」「コレクション」などと呼びます。

+

今回説明する配列(Array)もこのコレクションの一種です。

+

Arrayとはもともとは「整然と並んだもの」という意味ですが、実体も似たようなものです。たとえば、整数1, 2, 3を含む配列を図で表現すると図9.1のようになります。

+
+ +
+ fig0901 +
+

図9.1●配列(イメージ図)

+
+

整数が配列の格子の中にないことに注目してください。先月学んだ「Rubyの考え方(変数は参照)」の原則は、配列にも適用されます。ですから、今月は「配列はオブジェクトを『指す』もので、オブジェクトの容器ではない」と覚えてください。

+

配列を作る方法はたくさんありますが、一番簡単で一般的な方法は「配列式」を用いることです。配列式は[ ]で囲まれてコンマで区切られた式の並びです。たとえば、図9.1で示した配列であれば、

+
+
[1, 2, 3]
+
+

という表現になります。文字列の配列であれば、

+
+
["鰯一郎", "鰯次郎", "鰯花子"]
+
+

ですね(鰯はイワシです、念のため)。文字列の配列については省略形が用意されていて、同じ配列を、

+
+
%w(鰯一郎 鰯次郎 鰯花子)
+
+

と記述することもできます。

+
+
+

配列要素の参照

+
+

配列は0個以上の要素をまとめるオブジェクトで、その要素はそれぞれ順番付けられています。ですから、先頭の要素、次の要素、そのまた次の要素とそれぞれ取り出すことができます。

+
+
a = [1, 2, 3]
+a[0]       # => 1
+
+b = %w(鰯一郎 鰯次郎 鰯花子)
+puts b[1]  # => 「鰯次郎」を出力
+
+ +

忘れてはいけない重要な点は先頭の要素を表す番号は1ではなく、ゼロだということです。先頭の要素のことは普段「1番目の要素」と呼びますから、うっかり間違えないようにしてください。また、要素番号(インデックス)は負の数も指定できます。この場合は一番最後の要素が−1番目になります(図9.2)。

+
+ +
+ fig0902 +
+

図9.2●配列のインデックス

+
+

1つの要素を取り出すだけでなく、配列の一部分を範囲を指定して別の配列として取り出すこともできます。範囲の指定は、

+
+
[n,m]    先頭の位置と長さ
+[n..m]   先頭の位置と末尾の位置(末尾を含む)
+[n...m]  先頭の位置と末尾の位置(末尾を含まない)
+
+

の3種類があります。「..」と「...」の意味の違いに注意してください。

+
+
a = [1, 2, 3]
+a[1,2]    # => [2,3] 1番の要素から2要素
+a[1..2]   # => [2,3] 1番の要素から2番の要素まで
+a[1...2]  # => [2]   1番の要素から2番の要素の前まで
+
+
+
+

配列要素の変更

+
+

配列はその要素の一部を変更することができます。要素の変更には、配列要素の参照を左辺(代入先)にした代入によって行います。

+
+
b = %w(鰯一郎 鰯次郎 鰯花子)
+b[0] = "鮒太郎"
+# ["鮒太郎", "鰯次郎", "鰯花子"] になる
+
+

もちろん左辺は範囲を指定した要素参照も指定することができます。この場合、右辺には配列を指定します。

+
+
# 1番の要素の位置から長さゼロを置き換える
+# つまり、1番の要素の前に挿入する
+b[1,0] = ["鯉之助", "鯰三郎"]
+# ["鮒太郎", "鰯次郎", "鯉之助", "鰯次郎", "鰯花子"]
+# 長さは自動的に調整される
+
+# 4番の要素から4番の要素までを置き換える
+# 結局4番の要素を置き換える
+b[4..4] = ["鯨之進"]
+# ["鮒太郎", "鰯次郎", "鯉之助", "鰯次郎", "鯨之進"]
+
+# 3番の要素から4番の要素の直前までを置き換える
+# 結局3番だけが置き換わる
+b[3...4] = ["鮭造"]
+# ["鮒太郎", "鰯次郎", "鯉之助", "鯨之進", "鮭造"]
+
+ +

しかし魚偏の漢字ってたくさんありますね。

+

先ほど右辺には配列を指定すると解説しましたが、もし配列でない右辺を指定したらどうなるのでしょう? これはエラーにはならず、その範囲全体がその値に置き換わります。

+
+
# 0番から5要素分を置き換える
+# もとの配列が5要素しかないのでこの場合全部置き換え
+b[0,5] = "鯱助"
+# ["鯱助"]
+
+
+
+

配列の入手法

+
+

配列は非常に基本的なデータですから、さまざま局面で登場します。すでに紹介した配列式で明示的に作る以外にも、たとえば、

+
    +
  • IO.readlines(path)pathで指定したファイルの内容を1行1文字列を要素とする配列として返します。

  • +
  • Dir.glob(pat)patで指定したワイルドカードにマッチするファイル名を要素とする配列を返します。

  • +
  • local_variablesメソッドはローカル変数の名前を文字列の配列として返します。

  • +
+

これらはごくごく一部の例です。配列はほんとうにあちこちで登場します。見えにくいところでは、

+
+
a, b = b, a
+
+

のような代入(abの値を交換します)でも中で、

+
+
tmp = [b, a]
+a = tmp[0]
+b = tmp[1]
+
+

のような配列を用いた処理を行っています。

+
+
+ +

メソッドリスト

+
+

配列がどのようなことができるかを示すために配列オブジェクトの持つメソッド表9.1に示します。たくさんありますね。それぞれのメソッドの具体的な機能や使い方についてはマニュアルを参照してください。

+
+

表9.1●配列オブジェクトのメソッド

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
メソッド説明メソッド説明
&配列の共通要素index要素検出
|いずれかに含まれる要素indexesindex取得
*配列の繰り返しindicesindex取得
+配列の結合join文字列に結合
-配列の差last最後の要素
<<配列への追加length配列サイズ
<=>大小比較mapcollectと同じ
[]要素の取得map!collect!と同じ
[]=要素の設定max最大要素
assoc簡易連想配列member?include?と同じ
at要素の取得min最小要素
clear要素のクリアnitemsnilでない要素数
collect処理結果の収集packバイナリ文字列化
collect!処理結果の収集pop末尾要素を取り出す
compactnilを取り除くpush末尾に追加
compact!nilを取り除くrassoc簡易連想配列
concat配列の追加reject条件による削除
delete要素の削除reject!条件による削除
delete_at位置による削除replace配列内容の置換
delete_if条件による削除reverse逆順化
detect要素の検出reverse!逆順化
each繰り返しreverse_each逆順繰り返し
each_indexindexによる繰り返しrindex逆順の要素検出
each_with_indexindex付き繰り返しselect条件に合う全要素検出
empty?空配列チェックshift先頭要素を取り出す
fill要素セットsize配列サイズ
finddetectと同じslice部分配列
find_allselectと同じslice!部分配列の切り出し
first最初の要素sortソート
flatten平滑化sort!ソート
flatten!平滑化uniq重複の削除
grep要素の選択uniq!重複の削除
include?メンバーチェックunshift先頭に追加
+
+
+
+ +

Bangメソッド

+
+

表9.1のメソッド一覧を見ると、名前の最後に「!」の付いたメソッドがいくつかあります。これは「Bangメソッド」と呼ばれます(「ばんめそっど」と発音してください)。Bangメソッドは、レシーバを変更します。逆順に並べ替えるreverseメソッドについて「!」が付いているものと、付いていないものを比較してみましょう。

+
+
ary = [0,1,2,3,4,5]
+ary.reverse     # [5,4,3,2,1]を返す
+                # aryは元のまま
+ary.reverse!    # これも[5,4,3,2,1]を返すが、
+                # aryも[5,4,3,2,1]になる
+
+

!」の付いていないreverseメソッドでは要素を逆順にしたコピーを作るのに対して、reverse!では配列の要素を直接逆順に並べ替えていることがわかるでしょう。Bangメソッドはコピーを行わないため、特に要素数が大きくなったり、呼び出し回数が頻繁になると実行性能を向上させることができます。

+

その一方、レシーバを直接書き換えてしまうために1つの配列が複数の変数などから参照されていると、思いがけない変更が発生することになりかねません。注意する必要があります。

+

もう1つ注意するべき点があります。それはメソッド名の「!」は「付いているものはレシーバを書き換える、付いていないものは書き換えない」という意味では「ない」ということです。

+

配列の場合「!」の付いているメソッドはすべてレシーバを書き換えますが、逆に「!」が付いていなくてもレシーバを変更するメソッドはいくつもあります。たとえば、deleteメソッドはメソッド名に「!」が付いていませんが、引数として指定した配列の要素を取り除きます。

+

ですから、メソッド名の「!」は、付いているものと付いていないものの両方が提供されていて、「!」が付いているもののほうが使用上より注意が必要という程度の意味にとらえてください。

+
+
+

繰り返し

+
+

配列に典型的な処理といえば、各要素に対して順に処理を行うことでしょう。C的に表現すると、

+
+
for (i=0; i<len; i++) {
+  ... a[i] を参照する ..
+}
+
+
+

という感じでしょうか。しかし、Rubyではもうちょっとマシな方法で各要素に対する処理が行えます。

+

一番典型的なのがfor文です。for文は以下のような文法を持ちます。

+
+
for 変数 in 式
+  文
+end
+
+ +

式の部分に配列(を返す式)を指定すると、その配列の要素が順番に変数に代入されfor文の本体が順に実行されます。配列の長さチェックや要素の取り出しなどはfor文が面倒を見てくれます。

+

その他にも繰り返しを行ってくれるメソッドがたくさんあります。

+
+

配列.each{|変数| 文}

+

変数に要素が順に代入され、文が実行されます。eachfor文と同じ働きをするメソッドです。実際にはfor文の処理はこのeachメソッドを使って実現されています。

+

配列.reverse_each{|変数| 文}

+

reverse_eacheachを逆順に実行します。

+
+

それから「-ect兄弟」と通称されるメソッド群があります。これらはみな「ect」で終わる名前を持ち、配列の各要素に対して繰り返して処理を行います。

+
+

配列.collect{|変数| 式}

+

式の結果を集めた配列を返します。mapという別名があります。

+

配列.select{|変数| 式}

+

配列の要素のうち、式が成立する(真になる)要素を集めて配列として返します。find_allという別名があります。

+

配列.detect{|変数| 式}

+

配列の要素を先頭から順番にチェックし、式が成立する(真になる)最初の要素を返します。findという別名があります。

+
+
+
+

ソート

+
+

ソート(sort)とは配列の要素を順番に並べ替えることです。たとえば、

+
+
[5,2,3,1,4]
+
+

+
+
[1,2,3,4,5]
+
+

に並べ替えることをソートといいます。配列の要素をソートするにはsortメソッドを使います。

+
+
ary = [5,2,3,1,4]
+ary.sort    # [1,2,3,4,5]を返す
+
+

sortメソッドは元の配列と要素がソートされた同じ大きさの配列を返します。配列が大きくなると、この配列のコピー処理が負担になる場合があります。その場合には配列の中身を直接ソートするsort!メソッドがあります。

+

sort!メソッドのほうがコピーが避けられるぶんsortメソッドよりも高速なのですが、破壊的なメソッドなので、配列そのものを書き換えるときのリスクがあることに注意してください。

+
+
+ +

Ruby 1.7の追加機能

+
+

Ruby 1.7では配列も機能強化されていて、表9.1に示したメソッド以外にも、下の6つのメソッド追加されています。

+
+

配列.all?{|変数| 式}

+

各要素に対して評価した式がすべて成立したときに真を返します。どれか1つでも式が成立しない要素があればそこで繰り返しを止めてしまいます。

+

配列.any?{|変数| 式}

+

各要素に対して評価した式のいずれかが成立したときに真を返します。式が成立する要素に出合うとそこで繰り返しを止めてしまいます。

+

配列.fetch(i,ifnone)

+

i番の要素を返します。iが配列の範囲を超えるときにはifnoneで指定した値を返します(ifnoneを省略したときにはnil)。

+

配列.insert(i,v)

+

iで指定した位置(i番の要素の直前)に引数で指定した値を挿入します。複数の値が指定されれば、それらをすべて挿入します。

+

配列.inject(n){|変数1, 変数2| 式}

+

injectも「-ect兄弟」の1つです。injectを実行すると、各要素ごとに式を評価します。変数1には前回の式の結果が入ります。最初の要素に対しては「前回の式の結果」はありませんから、injectの引数として渡した初期値を使います。変数2には配列の要素が代入されます。injectの値は最後に評価した式の値です。

+

injectの典型的な利用法は各要素の合計の計算です。以下にinjectを用いた配列要素の合計を示します。

+
+
ary = [1,1,2,3,5,8,13]
+p ary.inject(0) {|n,i| n + i} # => 33
+
+

配列.sort_by|変数| 式

+

式の結果に基づいてソートします。sort_byについては、のちほど詳しく説明します。

+
+
+
+

まとめ

+
+

ここまで駆け足で配列について見てきました。配列はRubyにおいてかなり重要なオブジェクトです。いろいろ試してみて、使いこなしてください。

+
+
+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-017.xhtml b/docs/vol1/xhtml/p-017.xhtml new file mode 100644 index 0000000..f9bc299 --- /dev/null +++ b/docs/vol1/xhtml/p-017.xhtml @@ -0,0 +1,210 @@ + + + + + +第9章 配列 + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ 知られざるRuby ◆ ソート

+
+

入門編で説明したように、ソートは配列の要素を順番に並べ替えることなのですが、これをどのような手順(アルゴリズム)で行うかというのはソフトウェアの世界では重要なテーマでもあります。今回はソートについて少々深く見てみることにしましょう。

+
+

アルゴリズムのいろいろ

+

ソートのアルゴリズムは数々ありますが、ここでは代表的なものだけを紹介しておきます。

+
+
バブルソート
+

最も単純なソートアルゴリズムです(リスト9.1)。端から隣り合う要素を比較して左のほうが大きければ交換するという処理を繰り返します。1回配列の全要素に対して繰り返すたびに最も大きな要素が確定します。バブルソートは単純なのですが、比較回数が要素数の2乗に比例するので配列が大きくなると極端に遅くなります。

+
+

リスト9.1●バブルソート

+
def bsort(a)
+  len = a.size - 1
+  while len > 0
+    i=0
+    while i < len
+      if (a[i] <=> a[i+1]) > 0       # 左が大きい
+        a[i], a[i+1] = a[i+1], a[i]  # 要素交換
+      end
+      i += 1
+    end
+    len -= 1
+  end
+  a
+end
+
+a = [5,2,3,1,4]
+p bsort(a)
+
+
+
+
+ +
クイックソート
+

バブルソートは要素数が増えるとあまりにも性能が悪くなるので、よりマシなソートアルゴリズムが探し求められました。その代表的なものがクイックソートです。クイックソートはその名のとおりほぼ最速のアルゴリズムです。

+

クイックソートは要素の中から1つを選び、要素をその要素より大きいグループとそうでないグループに分割し、それぞれのグループに対して再帰的に分割を繰り返すことによってソートを行うアルゴリズムです。

+

ただ、クイックソートには弱点もあります。

+
    +
  • 要素が少ないときは逆に遅い
    +クイックソートはグループの分割や再帰などを含むため、要素が少ないときには優位性が出ない場合があります。

  • +
  • 基準値の選び方で性能が変化する
    +クイックソートアルゴリズムではある要素を基準に配列をグループに分割するのですが、基準になる要素がちょうど平均的な値でグループが均等に分割されることが望ましいです。逆の場合には効率が下がってしまうことになります。

  • +
+

実際のソートルーチンでは、クイックソートを基本に他の手法を組み合わせてこの弱点を克服しようとしています。

+

Rubyのsortメソッドもこのクイックソートアルゴリズムを採用しています。

+
+
+
+

sortメソッド

+

すでに説明したようにソートはsortメソッドを用いて行います。ここまでに紹介したアルゴリズムのことなど何も考えないでも、Rubyが適切にソートしてくれます。この辺がRubyらしい便利さです。

+

ただ、これだけでは思うようにいかない場合があります。たとえば以下のような点を考慮に入れる必要があります。

+
+

降順

+

sortは配列要素を小さいものから大きいものへ並べますが(昇順と呼びます)、逆に大きいものから小さいものへ並べたい場合もあります。もちろん、ソート後逆順にするとか、配列の末尾からアクセスという手もありますが、直接降順にできればそれに越したことはありません。

+

比較条件

+

sortは要素同士を <=> 演算子を使って比較しますが、<=> 演算子の提供する標準の比較方法とは違う方法で並べたい場合もあります。たとえば、大文字小文字を無視して文字列を比較したいとか、一部の属性だけで比較したいとかです。

+
+

このような場合のためにsortメソッドは比較する方法を指定することができます。sortメソッドにはブロックとして比較条件式を指定できます。こんな感じです。

+
+
ary = [5,2,3,1,4]
+ary.sort{|a,b|b<=>a}  # [5,4,3,2,1]を返す
+
+
+

これはabの2要素を <=> 演算子で比較していますが、abの順序を置き換えているため、結果は降順にソートされます。降順にソートするためには、この他にも、

+
+
ary.sort.reverse      # [5,4,3,2,1]を返す
+
+

という手もあります。実際にはほとんどのケースでこちらのほうが速いかもしれません。

+

ただ単に降順にするという単純なケースはともか、比較条件を自由に指定できるということはソートの自由度を高めます。たとえば、文字列を大文字小文字を無視して比較する場合には以下のようにできます。

+
+
ary.sort{|a,b|a.upcase <=> b.upcase}
+
+

では、仮に以下のデータが1行ずつ配列に入っているとして、それを末尾の数字を基準にしてソートしたければどのようにするのがよいでしょうか。

+
+
eir      11   9   2    6    3    1     1  81%  63%    13
+oos      10   6   4    3    3    0     4  60%  70%    25
+hrh      10   6   4    5    1    2     2  60%  70%    15
+spp      10   6   4    3    3    1     3  60%  60%    14
+
+

ブロックを使って一番簡単に実現するには以下のようになります。

+
+
ary.sort{|a,b|a.split[-1].to_i <=> b.split[-1].to_i}
+
+

splitで分割し、末尾の数字を取り出し、整数化して比較しているわけです。結果はこんな感じです。

+
+
eir      11   9   2    6    3    1     1  81%  63%    13
+spp      10   6   4    3    3    1     3  60%  60%    14
+hrh      10   6   4    5    1    2     2  60%  70%    15
+oos      10   6   4    3    3    0     4  60%  70%    25
+
+

ただ、問題があります。sortのために比較が行われる回数はソートする要素数よりもずっと多くなるのです。表9.2はランダムな整数を含む配列のソートに必要な比較回数の測定結果を示しています。

+
+

表9.2●配列のソートに必要な比較回数

+ + + + + + + + + + + + + + + + + + + + + + + + + +
要素数比較回数
56
1022
100531
10009427
10000129879
+
+ +

これだけの回数ブロックを評価することは性能上の問題になる可能性があります。たとえば、上で示した

+
+
ary.sort{|a,b|a.split[-1].to_i <=> b.split[-1].to_i}
+
+

の例では同じ文字列の分割や整数化を繰り返し繰り返し行うことになります。1万要素であれば分割と整数化がそれぞれ24万回以上も行われるわけです。これは無駄以外のなにものでもありません。このようなときに有効なテクニックがシュウォーツ変換(Schwarzian transform)です。シュウォーツ変換はこのテクニックを最初に考案した(のだと思う)Perl界の著名人Randal Schwarzにちなんで名付けられました。シュウォーツ変換は、ソートを行う前にまず比較するためのキーを計算しておきます。

+

具体的にはこのようになります。

+
+
ary.map{|i|[i.split[-1].to_i,i]}.sort.map{|j|j[-1]}
+
+

順番に解説すると、

+
+
ary.map{|i|[i.split[-1].to_i,i]}
+
+

で要素を元に比較の対象となる値(i.split[-1].to_i)を計算し、要素そのものと一緒に配列に入れておきます。それを、

+
+
.sort
+
+

でソートし、

+
+
.map{|j|j[-1]}
+
+

で元の配列の要素だけ取り出し、ソート結果を得ます。比較値の計算は要素の回数分だけしか行われませんから、配列の要素数が大きいときには大きな差が出ます。

+

もっともシュウォーツ変換では余計な配列の生成を伴いますから、比較条件の計算が「軽い」処理であった場合にはかえって遅くなってしまう可能性もあります。

+
+
+

sort_by

+

上で説明したシュウォーツ変換はなかなか魅力的ですが、mapを2度も呼ぶ必要があり、面倒でもあります。Ruby 1.7ではこれを簡単に行ってくれるsort_byメソッドが提供されています。

+
+
ary.sort_by{|i|i.split[-1].to_i}
+
+

明示的にシュウォーツ変換を行った場合の

+
+
ary.map{|i|[i.split[-1].to_i,i]}.sort.map{|j|j[-1]}
+
+

に比べてずいぶん簡潔になっています。

+
+
+

「安定」なソート

+

ソートには安定(stable)なソートとそうでないソートがあります。ソートが安定であるとは、比較した結果同じであると判定された要素が複数あった場合、それらの要素が元に並んでいた順序が保存されることです。

+
+

Rubyのソートは安定ではありません。普通は同じ要素は区別がつかないはずなので、安定でないことが問題になることはあまりありませんが、たとえば大文字小文字を無視した比較を行った場合、

+
+
ary.sort_by{|i|i.upcase}
+
+

の結果の大文字小文字を区別した順序が予想できないというのはうれしくない場合もあります。

+
+
ary = %w(foo Foo fOo FOo fOO FOO bar)
+ary.sort_by{|i|i.upcase}  # 結果のfooの並びは?
+# => ["bar", "FOo", "fOo", "Foo", "fOO", "FOO", "foo"]
+
+

大文字小文字を無視して同じとみなされる文字列同士で元の順序が保存されるためには、比較するキーにインデックスを含めて比較する必要があります。

+
+
n = 0
+ary.sort_by{|x|[x.upcase,n+=1]}
+
+

余談ですが、大文字小文字を無視して文字列が等しかった場合、そろえる前の文字列を第2キーとしてソートしたい場合には以下のようにsort_byメソッドを使います。

+
+
ary = %w(foo Foo fOo FOo fOO FOO bar)
+p ary.sort_by{|x|[x.upcase,x]}
+# => ["bar", "FOO", "FOo", "Foo", "fOO", "fOo", "foo"]
+
+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-018.xhtml b/docs/vol1/xhtml/p-018.xhtml new file mode 100644 index 0000000..a729bdd --- /dev/null +++ b/docs/vol1/xhtml/p-018.xhtml @@ -0,0 +1,86 @@ + + + + + +第9章 配列 + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ Ruby 1.8

+
+

Ruby 1.6.0が2000年9月19日にリリースされてからはやくも1年以上が過ぎ、そろそろ次の安定版である1.8はどうだろうかという話が流れるようになってきました。CVSで開発されている1.8候補の1.7系でもなかなか積極的な開発が行われているようです。

+

個人的には2001年中に1.8をリリースするつもりですから、もしかすると皆さんがこれを読んでいるときにはすでにリリースされているかもしれません。もっともRubyの開発においてリリースが遅れるのはさほど珍しいことではないので、期待させといてまだってことも十分にありえるのですが。

+

では、実際にリリースされることになるRuby 1.8の新機能というのはいったいどのようなものになるのでしょうか?

+

今年の5月に本誌の出版社であるアスキーを会場に開催されたYet Another Ruby/Perl Conference 19101(YARPC19101)におけるプレゼンテーションで、私はRuby 1.8には以下の項目が新たに追加されると発表しました。

+
    +
  • メソッドの追加

  • +
  • 世代別GC

  • +
  • 多言語対応

  • +
+

このときの発表資料は、

+
    +
  • http://www.ruby-lang.org/ja/yarpc01/index.html

  • +
+

で読めます。

+

しかし、非常に残念なことに重要で期待されていた後者2つは1.8には取り込まれないことになりそうです。

+
+

世代別GC

+

広島市立大の木山さんが基礎部分を開発した世代別GCは当時のGC性能を非常に向上させたので、将来取り込まれることが相当期待されていた機能です。

+

RubyのGC(ゴミ集め)機能は普段は非常に優秀に働いてくれるのですが、大量のオブジェクトを保持するパターンのプログラムにおいて、もう使われなくなったゴミを集めるためのコストが実際の処理を圧迫する結果になり、十分な性能が出ないケースがありました。

+

ところが、世代別GCとは別に行ってきたGCの改善の結果、現在の世代別GCの実装に対する以下の2つの点が明らかになったのです。

+
    +
  • GC性能が向上し、世代別方式を使わなくても性能にほとんど差が出ない

  • +
  • 改善が世代別GCと相性が悪く世代別GCによる性能向上を妨げる

  • +
+
+

この結果から、現在の実装のまま世代別GCを導入することによるメリットが開発当初よりもかなり下がってしまいました。

+

しかし、将来にわたって世代別GCがダメだというわけではありません。世代別GCは最近の性能を要求される処理系では広く用いられている方式で、方式そのものの有効性には疑問の余地はありません。今回の開発の知識と経験を生かし、次世代Rubyにはぜひとも世代別GCを取り込みたいと考えています。

+
+
+

多言語対応

+

多言語対応はCVSのruby_m17nブランチとして公開されています。この実装は、

+
    +
  • ASCII

  • +
  • SJIS

  • +
  • EUS

  • +
  • UTF-8

  • +
  • Latin1 (ISO-8859-1)

  • +
+

の各種エンコーディングをそのまま取り扱えるうえ、わずかなCのコードを動的ライブラリとして追加するだけで、他のエンコーディングにも対応できるようになるという優れものです。

+

個人的にはこれこそが多言語対応のあるべき姿(の1つ)だと信じています。では、その多言語対応が1.8に取り込まれない(だろう)というのはどういうことなのでしょうか。

+

理由は大きく分けると2つあります。1つは使い勝手を含めた機能の問題です。つまり、各種エンコーディングをそのまま扱うコアになる部分は実装したものの、それをどのようにユーザーに見せれば、多言語対応、ひいては国際化までを支援できるかということについて詰めきれなかった点です。これは多くの経験ある意見の集約が必要だと思われますが、なかなか時間内にまとめきれませんでした。この点にこだわってこれ以上1.8のリリースを遅らせるのは得策ではないと考えました。

+

2つめは最初のものとも関連するのですが、互換性についてです。国際化対応は文字列を文字単位で扱うので、現在のバイト単位で扱っている文字列とは一部互換性がなくなります。明示的にバイト列であると指定することで互換性の問題を回避することは可能なのですが、今度は多言語対応に制約が出る場合があります。この部分の仕様についてももっと詰める必要があったのですが、これまた時間切れです。

+
+
+

先送り

+

しかし、1.8.0にこれらが含まれないからといって、これらを捨てたりあきらめたりするわけではありません。今後、別の機会に取り込みたいと考えています。もしかすると、それは思ったよりも早いかもしれません。1.8.2とか。

+
+
+

さらにその次

+

YARPC19101での発表にもあったように、その次も待っています。

+

1.8のリリースができたら(そして私の心に余裕ができたら)、そのときこそ心機一転、次のメジャーリリースになるインタプリタの開発に取り掛かりたいと思っています。Riteというコードネームを持つこのインタプリタは今まで9年近いRubyの開発で学んできたことを踏まえて、今までの不満や問題を(できる限り)解消したものにしたいと考えています。Riteはまだ実体のないベーパーウェアにすぎませんが、ぜひ早く取り掛かりたいものです。

+
+
+ +

次回予告(?)

+

というわけで、2002年初頭までには1.8がリリースされていることでしょうから、(うまくいけば)来月あたり1.8の新機能を紹介できればと考えています。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-019.xhtml b/docs/vol1/xhtml/p-019.xhtml new file mode 100755 index 0000000..db4f4b8 --- /dev/null +++ b/docs/vol1/xhtml/p-019.xhtml @@ -0,0 +1,552 @@ + + + + + +第10章 ハッシュ(または連想配列) + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay10 +
+

+初等Ruby講座
+ハッシュ(または連想配列) +

+
+

[Linux magazine, 2002年3月号]

+
+

前半はHashクラスについてです。Rubyでは、Perlでそう読んでいるのを継承してハッシュテーブルのクラスをHashと名付けていますが、考えてみれば「ハッシュ関数を用いて高速アクセスを可能にしたテーブル」のことを「ハッシュ」と呼ぶのは本質的な部分が脱落しているという批判があります。Lua言語のようにテーブルと呼ぶべきだったかもしれません。しかし、自然言語の名付けでは「ケータイ」「スーパー」「コンビニ」のように本質的な部分が省略されるケースも多く、そんなに悪くないのではないかとも思っています。余談ですが、私の住む地方では「スーパーマーケット」のことを「マーケット」と呼ぶ年配の方々がいらっしゃいます。一種の方言かもしれません。

+

Hashクラスの本質的な部分はこの頃から変化していませんので、記事の内容は今でも有効です。ただ、要素の順序は不定となっていた部分は、このあと1.9から挿入順とする仕様になりました。この仕様はこのあとPythonなど他の言語にも受け継がれています。

+

後半はオープンソースな生活についてです。自分の書いた記事ではありますが、どうやって生活を維持していたのか書いていない点は不満ではありますね。

+
+
+

子供の頃、四角は豆腐、豆腐は白い、などと言葉をつなげて遊びませんでしたか? これは連想遊びですが、今月はその連想とも関係があるハッシュについて学びます。

+
+
+

はじめに

+
+

作者自らがRubyを解説するというこの連載も早くも5回目になります。「Ruby入門の連載を」と依頼されたときにはいったいどうなることかと思いましたが、とりあえずここまではなんとかなっているようです(自画自賛)。しかし、正直なところ進み方が遅いなという気もします。5回かけても、Rubyの全体像から考えるとほんのさわりの部分しか解説できていません。「知られざるRuby」とか「Ruby開発日記」などにページを割いてるからなんですが、それがないと面白くないし、考えようによってはゆっくりていねいに解説する連載にも価値があるのではないでしょうか(またしても自画自賛)。

+
+
+ +

ハッシュとは

+
+

で、今日の本題のハッシュです。ハッシュは連想配列(associative array)とか辞書(dictionary)とも呼ばれ、オブジェクトとオブジェクトの関係を表現します。たとえば、日本語と英語の曜日の名前の対応は、

+
+
日曜 => Sunday
+月曜 => Monday
+火曜 => Tuesday
+水曜 => Wednesday
+木曜 => Thursday
+金曜 => Friday
+土曜 => Saturday
+
+

になります。この関係を表現するのがハッシュです。

+

上記の関係はあえていえば和英辞書のようなものです。ハッシュが「辞書」と呼ばれるのはそういう理由です。また、「日曜」から連想されるのが「Sunday」であり、そういう連想関係を並べたものであるので「連想配列」と呼ばれます。その他にもこのようなデータ構造に対しては「ハッシュテーブル」「テーブル」「マッピング」などの呼び名があります。

+

ハッシュは単方向の関連を表現します。つまり上の関係は和英ではあり、英和ではないということです。日本語から英語を引くことはできますが、逆は基本的にできません。訳語の部分を1つ1つ調べていけば別ですが、紙の辞書ではそんなことやりたくはないですよね。辞書における見出し語に当たるものを「キー」、訳語に当たる部分を「値」と呼びます。

+

ハッシュが連想配列と呼ばれるのには、わけがあります。「連想関係を並べたもの」だということはすでに述べましたが、それ以外にも、配列を整数(オフセット)から値への対応であると考えると、配列は連想配列のキーを整数に制限したものであると解釈できます。事実、AWKという言語では配列と連想配列は同じもので、たまたま整数以外をインデックスにした場合のことを連想配列と呼んでいるだけです。ですから、Rubyでもハッシュはインデックスが整数でない配列だと考えることができます。

+

Rubyでも整数をキーに取るハッシュは配列によく似た振る舞いをしますし、要素を飛び飛びにしか使わない配列であれば、かえってハッシュのほうが効率がよい場合もあります。

+

RubyのハッシュとAWKの配列の大きな違いは、AWKの配列は数値以外には文字列しか取らないのに対して(AWKには数値以外のデータ型は文字列しかありません)、Rubyでは任意のオブジェクトをキーにすることができることです。ただし、中にはキーにするのには向かないタイプのオブジェクトもあります。どのようなオブジェクトがキーに向かないかについてはあとで説明しましょう。文字列に限定されないとはいえ、文字列はやはり最もキーに向いたオブジェクトです。Rubyではキーばかりでなく値にも任意のオブジェクトが使えます。

+

あ、そうそう。もう1つ重要なことがありました。インデックスの整数の順番に要素が並ぶと決まっている配列に対して、ハッシュは順序が決まっていません。ハッシュから要素を取り出す場合には、内部実装の都合で決まる予測が難しい順序で取り出されます。

+
+

では、なぜハッシュは「ハッシュ」と呼ばれるのでしょう。それはこの対応関係を表現するデータ構造がハッシュ法というアルゴリズムを用いて実装されているからです。ハッシュ法は巧妙なアルゴリズムで、キーから値を高速に検索します。ハッシュアルゴリズムについてはあとでもう少し詳しく解説します。

+

ハッシュは配列、文字列と並んでRubyの基本的なデータ構造の1つです。もっとも、自分で自由にデータ構造を定義できるRubyにとっては、あくまでも「標準で提供される使い勝手のよいクラス」の1つにすぎないわけですが。

+
+
+

ハッシュの生成

+
+

ハッシュを生成するにはハッシュ式を用います。ハッシュ式は、「{」と「}」の間に、キーと値を「=>」でつなげたものをコンマで区切って並べます。

+
+
{"apple" => "red", "banana" => "yellow"}
+
+

また、Hash[]という形式でも生成できます。

+
+
Hash["apple", "red", "banana", "yellow"]
+
+

Hash[]の場合には => を使わず、奇数番目の要素がキーになり、偶数番目の要素が値になります。

+

要素が1つも入っていないハッシュは中身のない {} またはHash.newによって生成できます。中身のないハッシュを何に使うかというと、もちろん将来キーと値の組み合わせを登録するためにあらかじめ用意しておくのです。

+
+
+

メソッド一覧

+
+

ハッシュがどのようなことができるかを示すためにハッシュオブジェクトの持つメソッド表10.1に示します。それぞれのメソッドの具体的な機能や使い方についてはマニュアルを参照してください。

+ +
+

表10.1●ハッシュオブジェクトのメソッド

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
メソッド説明メソッド説明
[]値の取得invert逆ハッシュ(値=>キー)
[]=値の設定key?キー存在チェック
clearハッシュを空にkeysキーの配列
collect処理結果の収集lengthエントリ数
defaultデフォルト値mapcollectと同じ
default=デフォルト値設定map!collect!と同じ
deleteエントリの削除member?include?と同じ
delete_if条件による削除rehashハッシュ再計算
detectエントリの検出reject条件による削除
each繰り返しreject!条件による削除
each_keyキーについて繰り返しreplaceハッシュ内容の置換
each_paireachの別名select条件に合う全エントリ検出
each_value値について繰り返しshift1エントリを取り出す
empty?空であるかsizeエントリ数
fetch値の取得sortソート
finddetectと同じstore値の設定
find_allselectと同じupdateハッシュの更新
include?key?と同じvalue?値存在チェック
indexes値(複数)の取得values値の配列
indicesindexesと同じ
+
+
+
+

アクセス

+
+

ハッシュは本来キーから値への対応を表現するものですから、最も基本的な操作はキーを与えて値を得ることです。

+

hをハッシュオブジェクトが格納されている変数、keyがキーであるとすると、

+
+
h[key]
+
+

とすることで、キーに対応する値を取り出すことができます。もし仮にキーに対応する値がなかった場合、普通はnilが返ります。この「存在しなかったときの値」のことをハッシュのデフォルト値と呼びます。このハッシュのデフォルト値は変更することができます。

+

キーが存在しなかったときの制御をもっと細かく行いたい人向けには、値取得用のfetchという別のメソッドがあります。fetchメソッドの一番基本的な呼び出し方は以下のようになります。

+
+
h.fetch(key)
+
+

この呼び出し方ではfetchメソッドではキーに対応する値が存在しなかった場合にはエラーになります。とはいえ、いつもエラーでは面倒なので、fetchはそういう場合の値の計算方法を指定できます。

+
+
h.fetch(key, default)
+
+

この呼び出し方をすると、キーに対応する値がなかった場合にはdefaultの値を返します。もう1つの呼び出し方は、

+
+
h.fetch(key) { expr }
+
+

です。こちらはキーに対応する値がなかった場合に {} の中の式を計算します。引数に指定すると値があってもなくてもデフォルト値を計算する必要がありますが、ブロックを指定するこちらの呼び出し方では、キーが存在しないときに初めて計算されますから、コストのかかる計算や無駄なオブジェクトの割り当てを避けることができます。

+
+
+ +

更新

+
+

ハッシュは本物の辞書と違ってもっと柔軟です。ハッシュにはいつでも自由にキーと値を登録することができます。

+

hをハッシュオブジェクトが格納されている変数、keyがキー、valが値であるとすると、

+
+
h[key] = val
+
+

とすることで、キーに対応する値を設定することができます。ですからこれ以降、

+
+
h[key]
+
+

とすればvalが得られるというわけです。h[key]fetchがあったように、h[key]=valにも対応するメソッドがあります。それはstoreです。

+
+
h.store(key, val)
+
+

fetchにはデフォルト値に関するいろいろなバリエーションがありましたが、storeにはそんなものはありません。シンプルなものです。

+

追加ができるのですから、削除もできます。

+
+
h.delete(key)
+
+

で、キーと対応する値がハッシュから取り除かれます。

+

それから、ハッシュを丸ごと更新することができます。

+
+
h.update(h2)
+
+

とすると、別のハッシュh2に含まれるキーと値の対応をハッシュに追加できます。もし、hh2に同じキーが含まれていた場合には、h2の値のほうが優先になります。一方、

+
+
h.replace(h2)
+
+

では、ハッシュの現在のデータはすべて捨ててしまって、h2に含まれるキーと値の対応でハッシュの内容が置き換えられます。

+
+
+

検査

+
+

ハッシュそのものに関する情報を得る手段も用意されています。

+
+
h.key?(key)
+
+

ではハッシュがそのキーを含むときに真を返します(Rubyでは真か偽を判定するメソッドには ? が付きます)。

+
+
h.value?(value)
+
+
+

はハッシュがその値を(1つでも)含むときに真を返します。

+
+
h.empty?
+
+

はハッシュが空っぽであるときに真を返します。それから、

+
+
h.size
+
+

はハッシュがいくつ連想対応を持っているかを返します。sizeには別名としてlengthがあります。

+
+
+

エントリの処理

+
+

ハッシュの各エントリに対して繰り返し処理を行う方法はいくつかあります。一番最初に思い付くのは全部のキーを含む配列を取り出して、それに対して繰り返すことでしょう。全部のキーを配列として取り出すメソッドはkeysです。

+

hをハッシュオブジェクトが格納されている変数だとすると、

+
+
for k in h.keys
+  ....
+end
+
+

で各キーごとに処理を行うことができます。すでに述べたようにハッシュは順序が予測できませんから、keysが返す配列の要素の順序もバラバラです。ちなみに全部の値を配列として取り出すのはvaluesメソッドです。

+

しかし、この方法には少々問題があります。というのも、keysメソッドは内部でまず配列を作り、それにハッシュのキーを1つずつ追加していく処理を行うので、ハッシュのサイズが大きくなると、キーを含む配列も大きくなり無駄なコピーが生じるので、記憶容量の点からも実行速度の点からも不利になるのです

+

Rubyには無駄な配列を作らずにハッシュの各エントリに対して繰り返すことができます。それには以下のようにします。

+
+
h.each {|k, v|
+  ...
+}
+
+

こうすると各エントリのキーと値がそれぞれkvという変数に代入されて、残りのブロックが実行されます。要素の取り出しなどの詳細はeachメソッドの中で行われますので、使う側は何も気にする必要がありません。eachメソッドでは内部的に配列を割り当てたりせず、ハッシュの内部構造から直接キーと値を取り出します。このような繰り返しを制御するメソッドをRubyでは「イテレータ」と呼びます。

+

ハッシュに対するイテレータは、eachの他にもeach_pair(単なるeachの別名。keyvalueのペアを与えるから)、each_key(キーについて繰り返す)、each_value(値について繰り返す)があります。

+

たとえば、ハッシュの各エントリを出力するには、

+
+
+
h.each {|k,v
+  printf "%s => %s\n", k, v
+}
+
+

というふうに書けます。

+
+
+

削除

+
+

ハッシュからの削除にはdeleteメソッドを使うと先ほど説明しました。これはくどくどと言い換えると「ハッシュの連想対応のうち、指定したキーと等しい」という条件に合致するものを削除することです。

+

しかし、削除したい条件がいつもこんなに簡単とは限りません。たとえば、「キーに対応する値の数値がある一定以下」という条件で削除するということだってありえます。つまり、一定の成績に満たない人を不合格とする入試の合格判定のような場合ですね

+

Rubyではこのような場合にも合致するようなメソッドが提供されています。

+
+
h.delete_if{|k,v| ...}
+
+

では各エントリに対して、kにキー、vに値が代入されてブロックが実行されて、その結果が真であれば、そのエントリが削除されます。delete_ifにはreject!という別名があります。元のハッシュをとっておきたい場合には、rejectメソッドを使います。

+
+
h.reject{|k,v| ...}
+
+

は条件に合うエントリを取り除いた新しいハッシュを返しますが、元のハッシュは変更しません。

+

先ほどの入試判定の例を実際にプログラムにすると、ハッシュmathに数学のテストの成績が入っていて、合格ラインが仮に60点だとすると、

+
+
math = {"matz" => 50, "shugo" => 80, "gotoken" => 100}
+math.delete_if{|k,v| v < 60}
+
+

というプログラムの実行すれば、ハッシュmathは、

+
+
{"gotoken" => 100, "shugo" => 80}
+
+

になり、めでたく不合格者は取り除かれることになります……って、私が不合格になってますね(泣)。

+

Ruby以外の多くのプログラミング言語では、このような処理はここまで直感的ではありません。おそらくハッシュの各エントリに対してループを回して条件に合うものを削除する以下のような処理を行うことでしょう。

+
+
for k in math.keys
+  if math[k] < 60
+    math.delete(k)
+  end
+end
+
+

もちろん、プログラムを読めばやってることは明らかなんですが、上記のdelete_ifを使ったものに比べると「やりたいこと」がイマイチ直接的に表現されていない気がしませんか?

+
+

この「やりたいことを明確に簡潔に表現できるメソッドがそろっている」という事実が、Rubyを使いやすい、あるいは気持ちがよい言語だと感じさせてくれる原因ではないかと思っています。このことは、また同時にこの連載がなかなか進まなかったり、本来は薄いはずの「デスクトップリファレンス」がこれ以上ないってくらい分厚くなってしまったりする原因でもあるのですが。

+

他にもエントリの削除に関係するメソッドがいくつかあります。

+

clearメソッドはハッシュのデータを全部捨てて空にします。shiftメソッドはハッシュからエントリを1つだけ削除し、そのキーと値を2要素の配列として返します。どのエントリが取り出されるのか予測するのは困難ですから、あまり使う局面の思い付かないメソッドなんですが、1つずつ空になるまで取り出して繰り返すというようなときにでも使うのでしょうか。

+
+
+

ハッシュの出力

+
+

Rubyはオブジェクトを適当に文字列化してから出力してくれます。これはハッシュに限らずすべてのオブジェクトが持つto_sメソッドが担当しているのですが、ハッシュのto_sメソッドはあまり人間にとってうれしい形式とはいえません。

+
+
h = {"ayaka" => 9, "ruka" => 7, "takuto" => 4}
+puts h
+
+

とした場合の出力は、

+
+
ayaka9ruka7takuto4
+
+

です。とても読みやすいとはいえません。人間向けの文字列化を行ってくれるのはinspectメソッドです。inspectメソッドは、人間が読んでそのデータの意味がわかりやすいような表現の文字列を返します。

+

inspectメソッドを使って出力してくれるのがpメソッドです。これはinspectの出力をそのままputsと同様に出力するので、デバッグ時などに値を確認するのに役立ちます。

+
+
h = {"ayaka" => 9, "ruka" => 7, "takuto" => 4}
+p h
+
+

の出力は、

+
+
{"ayaka"=>9, "ruka"=>7, "takuto"=>4}
+
+

です。ほら、こっちのほうがずっと人間向きでしょう? pメソッドは今までこの連載で使ってきたputsとほぼ同じように、あらゆるオブジェクトに対して使えます。便利なので覚えておくとよいでしょう。

+
+
+

ハッシュの反転

+
+

ハッシュはキーから値への対応を表現するものです。では、値からキーを見つけるのはどうしたらよいでしょう。これにはいくつかの方法があります。

+
+

まず最初に考えられるのはループを使う方法です。ハッシュをh、対応するキーを探す値をvalueとすると、

+
+
key = nil  # あらかじめローカル変数を用意
+h.each {|k,v|
+  if v == value
+    key = k
+  end
+}
+
+

とすると、変数keyに対応するキーが代入されます。

+

しかし、直接ループを使うのはちょっと格好悪い気がします。Rubyではこういう単純なループを必要とする場合には、たいてい何か別の方法があるものです。この場合にも確かにあります。それはdetectを使う方法です。

+
+
key, = h.detect{|k,v| v == value}
+
+

detectはキーと値のペアを返すので、多重代入でキーだけを変数keyに取り出しています。多重代入の部分が多少トリッキーですが、ループを使うよりも簡潔ではないでしょうか。

+

これらは1回だけ値からキーを取り出すことを考えた方法ですが、逆の検索がたびたび発生するようならば、値からキーを得る逆方向のハッシュ、いわば逆ハッシュを作ったほうが効率がよい場合もありえます。そういう場合に逆ハッシュを求めるinvertメソッドがあります。

+
+
h2 = h.invert
+h2[value]  # valueに対応するキーが得られる
+
+

これが一番簡潔な気がします。

+

さて、値からキーを求める場合には、上記のいずれの方法を用いる場合にも、気を付けなければならないことがあります。それは「値に対応するキーは1つとは限らない」という点です。たとえば、名前からテストの点への対応を表現するハッシュでは、値(たとえば「100点」)から、名前を得ようとしても、100点を取った人が一人ではないために、対応する名前は1つではないかもしれません。

+

上記の方法ではいずれも一番最初に見つかったキーを返します。そしてハッシュでのエントリの登場順は予想困難ですから、1つの値が複数のキーに対応するケースでは、あまり使いものにならないでしょう。

+

そういう場合の対応方法もまたいくつもあるのですが、ここでは1つだけ紹介しておきましょう。上記のdetectメソッドの仲間(先月紹介した「-ect兄弟の1つ」)であるselectを使う方法です。

+
+
values = h.select{|k,v| v == value}
+
+

これで、変数valuesには条件を満たすペアの配列が代入されます。つまり、

+
+
math = {"matz" => 0, "shugo" => 100, "gotoken" => 100}
+values = math.select{|k,v| v == 100}
+
+

とすればvaluesには、

+
+
[["gotoken", 100], ["shugo", 100]]
+
+

が代入されるというわけです。あとは必要な部分を切り出すだけですね。

+
+

たとえば名前だけを取り出すには以下のようにします。

+
+
values.collect{|v| v[0]}
+
+
+
+

ハッシュのソート

+
+

さっきからしつこいくらい繰り返していますが、ハッシュのエントリが登場する順序は不定(というか、予想困難)です。しかし、現実にはある特定の順序で処理したい場合はたびたびあるわけです。そういう場合に用いられるテクニックは、キーの配列を取り出してソートしてから処理することです。

+
+
for i in h.keys.sort
+  # ソートされた順序でキーの処理
+end
+
+

もちろん、このテクニックにはハッシュが巨大になったときに非効率であるという欠点があるのですが、順に処理することが必須である場合には避けられません(前号で説明したsort!を使うことでちょっとだけ効率が上げられます)。

+

ハッシュオブジェクトにはsortメソッドがありますが、これは、

+
    +
  • まずハッシュを[[key, value],[key,value]..]という配列に変換し

  • +
  • それからその配列をソートしてその結果を返す

  • +
+

というものですから、実用性は低いかもしれません。

+
+
+

ハッシュのデフォルト値

+
+

ハッシュにはデフォルト値があると説明しました。このデフォルト値の使い方を説明しておきましょう。

+

デフォルト値の設定されたハッシュを作るためにはHash.newを使います。Hash.newに引数を指定するとその値がデフォルト値になるのです。

+
+
h = Hash.new(0)
+h["foo"]        # 0が返る
+
+

または、default属性でもデフォルト値を設定できます。

+
+
h = {"matz" => 50, "shugo" => 80, "gotoken" => 100}
+h.default = 0   # 存在しない場合は0
+h["yukihiro"]   # => 0
+
+

ただ、ハッシュのデフォルト値には落とし穴があります。それはデフォルト値のオブジェクトはコピーされないというものです。たとえば以下のプログラムを見てみましょう。

+
+
h = Hash.new([])
+h["foo"] = ["foo"]
+names = ["foo", "bar", "baz"]
+for name in names
+  ary = h[name]  # 配列を得る
+  ary.push name  # 名前を追加
+end
+
+for name in names
+  p h[name]      # 配列を出力
+end
+
+
+

この結果は

+
+
["foo", "foo"]
+["bar", "baz"]
+["bar", "baz"]
+
+

になります。"bar""baz"が混ざってしまっています。つまり、pushでデフォルト配列そのものを変更してしまったため、予想外の結果になったというわけです。

+

これを避けるためにはfetchを使うのがよいでしょう。

+
+
h = Hash.new            # デフォルト値なし
+h["foo"] = ["foo"]
+names = ["foo", "bar", "baz"]
+for name in names
+  ary = h.fetch(name){  # 配列を得る
+    h[name] = []        # 配列を設定しておく
+  }
+  ary.push name         # 名前を追加
+end
+
+for name in names
+  p h[name]             # 配列を出力
+end
+
+

こちらの結果は期待どおり、

+
+
["foo", "foo"]
+["bar"]
+["baz"]
+
+

になります。

+
+
+ +

データ出現回数の計算

+
+

たとえば配列があって、その要素それぞれの出現回数が知りたいとすると、以下のような方法が使えます。要素の出現回数を数える配列をaryだとすると、

+
+
h = Hash.new(0)
+for item in ary
+  h[item] += 1
+end
+
+

これでハッシュhには要素からその登場回数への対応が設定されます。

+

Rubyの配布パッケージに含まれるサンプルプログラムのうちoccur.rbは、ほぼ同じ処理を行っています。

+
+
+

環境変数

+
+

Linuxを含むUNIXには環境変数というものがあって(いや、Windowsにもあるんですけど)、RubyからはENVという定数に格納されたハッシュとしてアクセスできます。

+
+
ENV["USER"]   # ユーザー名(たとえば"matz")
+ENV["PATH"]   # コマンドパス
+
+ENV.each{|k,v|
+  print k, "=", v, "\n"
+}
+
+

厳密にはENVはハッシュオブジェクトではなくて、ハッシュそっくりに振る舞うように用意されたオブジェクトなのですが(なんとENVのクラスはObjectです)、ほんのわずか違いがあって、ENVではキーも値も文字列のみという制限があります。

+
+
+

まとめ

+
+

実は、今月の記事を書くためにちょっと調べてみたのですが、ハッシュについての解説は意外と少ないようです。どの本もせいぜい2, 3ページくらいしか割いていません。もしかすると、今回の解説は貴重かもしれません。この解説が皆さんがよりハッシュを理解する一助になれば幸いです。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-020.xhtml b/docs/vol1/xhtml/p-020.xhtml new file mode 100644 index 0000000..f19387c --- /dev/null +++ b/docs/vol1/xhtml/p-020.xhtml @@ -0,0 +1,103 @@ + + + + + +第10章 ハッシュ(または連想配列) + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ 知られざるRuby ◆ ハッシュアルゴリズム

+
+

ハッシュのような対応関係の検索を一番簡単に行うことができるのは線形検索です。配列には線形検索を行うメソッドassocがあります。assocは、

+
+
[["matz", 50],["shugo",80],["gotoken",100]]
+
+

というような配列から対応関係を検索します。

+
+
ary.assoc("matz")  # => ["matz",50]
+
+

しかし、線形検索ではエントリ数が増えたときに最大でそのエントリ数に等しい回数だけ比較を行う必要があります。これはあまりにも非効率です。このような処理はエントリ数nに比例するのでO(n)と呼ばれます。配列がソートされていれば、二分検索を用いることができますから、O(log n) になりますが、連想検索のようなケースではソートされていることは期待できません。

+

そこでハッシュも用いていて、名前の由来にまでなっているハッシュ法が登場します。ハッシュ法は実にO(1)、つまり要素数が増えても原理的には効率が変化しないアルゴリズムです。

+

ハッシュ法では、まず格納するキーに対してハッシュ値という整数を計算します。ハッシュ値はオブジェクトの状態に応じた整数で、等しいオブジェクトに対してハッシュ値を計算するとは同じ値にならなければなりません。また、なるたけバラツキがあるのが望ましいといわれています。

+

そして、登録用のテーブル(配列)にハッシュ値をインデックスとしてアクセスします(図10.1)。

+
+ +
+ fig1001 +
+

図10.1●ハッシュ法

+
+ +

仮に文字列に対してハッシュ値を計算する関数がhashだったとします。そのうえで、文字列 "matz" をキーとする値を取り出すには、まずハッシュ値を計算します。この例ではhash("matz")1です。そこでテーブルのインデックス1の位置を見ると値は50であるとわかります。

+

存在しないキーに対しては、ハッシュ値インデックスの位置を検査することで、存在しないということがわかります。図10.1の例では "keiju" のハッシュ値4に対する値は存在しませんから、登録されたキーではないと判定できます。

+

ハッシュ値の計算も配列のインデックスによるアクセスも要素数には関係ありませんから、ハッシュ法では原理的にはO(1) が達成できるというわけです。

+

ただし、これは相当単純化された説明です。現実の実装はそう簡単ではありません。

+

ハッシュ法では同じオブジェクトは同じハッシュ値を持つことが保証される必要がありますが、違うオブジェクトが同じハッシュ値を持ってはいけないわけではありません。ですから、ときには異なるオブジェクトに対して同じハッシュ値が得られ、インデックスが衝突する可能性があります。ハッシュテーブルの実装では衝突した場合の処理についてそれぞれに工夫をしています。Rubyのハッシュテーブルはチェーン法と呼ばれる手法を使って実装されています。

+

ハッシュ法についての詳細はアルゴリズムについて解説した書籍を参考にしてください。書名に「アルゴリズム」とあればハッシュ法が載っていないことはないでしょう。それほど有名なアルゴリズムです。Rubyではハッシュ法は組み込みライブラリとして提供されているので、実際にこのアルゴリズムを自分で作ることはまずないでしょうが、知っておいて損はないと思います。

+
+

eql?とhash

+

Rubyではハッシュの値の計算はhashメソッド、キーとして等しいかどうかの判定はeql? メソッドで行われます。

+
+
"foo".hash         # => 876516207
+"bar".hash         # => 841490416
+
+"foo".eql?("foo")  # => true
+"foo".eql?("bar")  # => false
+
+

ですから、自分で定義したオブジェクトをハッシュのキーとして適切に振る舞わせたいと思えば、この2つのメソッドを定義すればよいわけです。再定義しなければまったく同じオブジェクトだけが等しいとみなされます。

+
+
class Point
+  def initialize(x,y)
+    @x = x
+    @y = y
+  end
+  def x; @x; end
+  def y; @y; end
+end
+
+h={}               # hはハッシュ
+p1 = Point.new(1,1)
+h[p1] = 5
+p2 = Point.new(1,1)
+p h[p2]  # => nil
+         # 構造や内容は同じでも
+         # 等しいとはみなされない
+
+# eql?とhashの定義を追加する
+class Point
+  def eql?(other)
+    @x == other.x and @y == other.y
+  end
+  def hash
+    @x ^ @y
+  end
+end
+
+h[p1] = 5
+p h[p2]  # => 5
+         # 等しくみなされるように
+
+ +

キーのハッシュ値は格納するときに計算されます。しかし、配列やハッシュなどは要素が変化すればそのハッシュ値も変化してしまいます。そうなれば、ハッシュ法が正常に動作しませんから、ハッシュ値が変化してしまったオブジェクトはハッシュから取り出せなくなります。

+

そうなった場合にはrehashメソッドで全キーのハッシュ値を再計算するとまた取り出せるようになります。

+

文字列もハッシュ値が内容によって変化するのですが、文字列はハッシュのキーとしてたびたび用いられるので特別扱いされています。ハッシュは文字列がキーとして与えられると、その文字列を内部的にコピーし、更新不可に設定してからキーとして登録します。ですから、キーとなっている文字列をkeysメソッドなどで取り出して、それを変更しようとするとエラーになります。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-021.xhtml b/docs/vol1/xhtml/p-021.xhtml new file mode 100644 index 0000000..9299d42 --- /dev/null +++ b/docs/vol1/xhtml/p-021.xhtml @@ -0,0 +1,70 @@ + + + + + +第10章 ハッシュ(または連想配列) + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ オープンソースな生活

+
+

もう9年近くRubyなんてものを開発してると、喧伝するするつもりはなくても、あちこちで「Rubyの開発者」として認識されるようになってきます。ま、たいていは肯定的な評価のようなので、そんなに悪い気分ではないのですが、ときどき困るのが「なんで無料で配っちゃうんですか」という質問です。

+

コンピュータなるものがだんだん世の中に広まってきて、昔のようにやっているだけで変人扱いということはなくなりましたが、今度は「せっかく開発したソフトウェアを無料で配っている変人」とみなされるようになってしまったようです。結局は変人なのね。

+

まあ、好きなことだけやってるんですから、マイノリティ扱いはある程度しょうがないにしても、やはり理解していただく努力は必要ではないだろうか、と感じるようになりました。

+

ということで、今月はRubyの開発者まつもとのオープンソースな生活をちょっとだけ公開しましょう。

+
+

出会い

+

そもそもソフトウェアビジネスのようなものが成熟していない昔は、ソフトウェアというのは、あまりお金のかかるものではありませんでしたし、ソースコードを開示することも珍しくありませんでした。ハードウェアのおまけだからという認識だったのかもしれませんが、少なくとも自由でした。私と近い世代の人は雑誌に掲載されたプログラムリストを打ち込んだり、読んで勉強したりした記憶があるのではないでしょうか。そういう原体験にある私にとっては「オープンソース」はその単語が生まれるはるか前から当然だったというわけです。

+
+
+

恩義

+

大学に入ってから、私はたくさんのソースコードを読んでプログラムやアルゴリズムについて学びました。そのほぼすべてはフリーソフトウェア(オープンソースソフトウェア)で、私のソフトウェア技術(というものがもしあるとして)は、それらから学んだものによってのみ構成されていると断言しても過言ではありません。

+

つまり、フリーソフトウェアなかりせばプログラマーとしての今の私は存在しなかったわけです。これは恩義を感じないわけにはいかないでしょう。

+
+
+

自由

+

私は自分の使うツールは苦労してでも手になじむものを選ぶタイプです。また、それがソフトウェアであれば自作することもいといません。本当はソフトウェアでなくても自作したいんですが、天性の不器用がそれを許さないという悲しい現実があります。

+

ですから、自分が日常的に使うツールが使いにくければ、徹底的にカスタマイズします。カスタマイズの範囲内で解決しなければ、そのソースコードを修正してでも直そうとします。実際にそうやって手を入れたソフトウェアは数知れずあります。私の最初のフリーソフトウェアであるcmailもそうやってできたものです。

+
+

これは自分の快適さを追求する自由です。この自由には多くの場合ソースコードが必要です。

+

また、ソースコードはアルゴリズムを始めとするプログラミングのあらゆる知識の源です。この学ぶ自由のためにもソースコードが必要です。

+

つまり、ソースコードが入手できるということは、ただお金がかからないということ以上に自由のために必須であるということです。

+
+
+

否定?

+

とはいえ、ソフトハウス、つまりソフトウェアを売って商売する会社に勤めていた以上、ビジネスとしてのソフトウェアについても全否定するものではありません。その辺がFSFのStallmanとはちょっと違う点ですね。

+

ただ、ソフトウェアは人に奉仕するものであり、人の自由を制約することはソフトウェアの存在目的に反すると感じている点では同じかもしれません。

+
+
+

動機

+

では、自分の開発したソフトウェアをなぜフリーソフトウェア(オープンソースソフトウェア)として公開したのかというと、実は理由はそんなに深いものではないのです。

+

まず、第一に自分の開発したものがそれほどたいしたものだとは思えなかったという点です。つまり、「これをベースにビジネスを」とかいうような野望を持つタイプのソフトウェアではなかったということです。もっともソフトウェアのタイプというよりも、私自身がそういう野望を持つタイプではないということのほうが大きいかもしれませんが。

+

次に、フリーソフトウェアからこれだけの恩義を受けている以上、自分の開発したソフトウェアも公開して「お返し」をするのが当たり前に感じたという点です。

+

最後に、自分が自由がほしいのであれば、他人にもそれを許すべきだと感じたことです。それは結局はまわりまわって自分のためでもあるのですから。

+

そして、これは理由ではないのですが、cmailにしても、Rubyにしても、もし仮にフリーソフトウェアにしなかったとしたら、今ごろは誰にも使われずに消え去ってしまっていたことでしょう。

+

どちらも公開したての最初のバージョンは決して最高のツールではありませんでした。しかし、コミュニティのみんなが協力してよいものにしていったことにより発展してきたのです。

+
+
+

環境

+

という私のプログラミング環境ですが、OSはLinux、エディタはEmacs、コンパイラはgcc、インタプリタはRuby、ブラウザはmozilla(w3mも)とフリーソフトウェアのみで固めています。

+

最近Palmを使うようになったのですが、そちらではソースを公開したソフトウェアがほとんどないので閉口しています。フリーソフトウェアといえば無料ソフトウェアのことですし。うーむ。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-022.xhtml b/docs/vol1/xhtml/p-022.xhtml new file mode 100755 index 0000000..95fe5b7 --- /dev/null +++ b/docs/vol1/xhtml/p-022.xhtml @@ -0,0 +1,717 @@ + + + + + +第11章 文字列の操作 + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay11 +
+

+初等Ruby講座
+文字列の操作 +

+
+

[Linux magazine, 2002年4月号]

+
+

前半はRubyの基本的データ構造の1つ、文字列についての解説です。また、あまり使われないpack/unpack機能についても解説しています。この辺も大きな変化はないですね。メソッドは増えていますが。

+

後半は国際感覚についてです。20年前には、海外で働く日本人といえば大企業の駐在員とかのイメージで、ソフトウェアエンジニアが海外のカンファレンスに出席したり、あるいは海外企業に就職するなどめったにないことだったように思います。それを思うとこの20年の間に(ソフトウェア業界にいる)日本人の国際感覚はずいぶん変化したように思います。私の知人にも海外企業で働く人たちがいっぱいいますし、中にはRubyのおかげで海外進出した人さえ数多くいます。時代は変わりましたが、良い方向の変化だと思います。

+
+
+

Rubyは「スクリプト言語」と呼ばれることが多いのですが、その理由の1つは今月解説する文字列処理機能が充実していることにあります。今月は文字列クラスStringの機能を学んで、文字列処理の基本を押さえましょう。

+
+
+

はじめに

+
+

文字列とは読んで字のごとく「文字の列」です。プログラミングにおいて文字列は頻繁に登場します。ファイルから読み込んできたデータも、CGIの入出力、データベースのやりとりなど、Rubyにやらせたいほとんどあらゆる処理は文字列を経由します。

+
+
+ +

文字列の生成

+
+

文字列を作る方法はいくつもありますが、一番基本的な方法はプログラムに直接書き込む方法です。プログラム中での文字列表現のことを「リテラル」と呼びます。文字列リテラルには以下の4つの形式があります。

+
    +
  • ""(ダブルクォート)

  • +
  • ''(シングルクォート)

  • +
  • %形式

  • +
  • ヒアドキュメント

  • +
+

それからフォーマットした文字列を得るためにはsprintfメソッドを使います。

+
    +
  • sprintf

  • +
+

ここからは、それぞれの方法について少し詳しく説明しようと思います。

+
+
+

ダブルクォート

+
+

一番基本的な文字列表現は""(ダブルクォート)で囲むことです。たとえば以下は「まつもと」という内容の文字列です。

+
+
"まつもと"
+
+

ダブルクォートで囲んだ文字列の中では、表11.1のような \(バックスラッシュ、フォントによっては円マーク)による特殊表現が使えます。これを「バックスラッシュ表現」と呼びます。

+ +
+

表11.1●バックスラッシュ表現

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
表記説明
\tタブ(0x09
\n改行(0x0a
\rキャリッジリターン(0x0d
\f改ページ(0x0c
\bバックスペース(0x80
\aベル(0x07
\eエスケープ(0x1b
\s空白
\nnn8進数表記(nは0-7)
\xnn16進数表記(nは0-9, a-f)
\cx, \C-xコントロール文字(xはASCII文字)
\M-xメタxc | 0x80
\M-\C-xメタ コントロールx
\<ch><ch>が上記の文字でないとき,文字<ch>そのもの
+
+

バックスラッシュ表現は、ダブルクォート文字列中以外にも、正規表現など他のところでも使えます。

+

ダブルクォートで囲んだ文字列の中では、それ以外に式展開というものが使えます。式展開は、文字列中にRubyの式を埋め込むことができるものです。

+
+
a = 124
+b = 2
+"a+b = #{a}+#{b} = #{a+b}"  # a+b = 124+2 = 126
+
+

#{}」の部分は式の結果を文字列に変換して置き換えられます。

+

ダブルクォートに限らず、文字列リテラルは登場するたびに新しいオブジェクトが作られます。ということは、文字列リテラルのオブジェクトの内容を変更しても、次の実行のときに文字列が変わってしまうことはありません。たとえば、

+
+
2.times do
+  a = "foo"
+  puts a
+  a[0] = "b"      # "boo" になる
+  puts a
+end
+
+

の実行結果は、

+
+
foo
+boo
+foo
+boo
+
+

です。

+
+
+

シングルクォート

+
+

もう1つの文字列リテラルはシングルクォートで囲んだものです。

+
+
'まつもと'
+
+

シングルクォートで囲んだ文字列の中では、ダブルクォート文字列のような式展開は行われません。バックスラッシュ表現もシングルクォートの中で使えるものは、\'(シングルクォートをエスケープ)と \\(バックスラッシュをエスケープ)だけです。

+
+
+ +

%形式

+
+

文字列の中に「"」や「'」がたくさん登場する場合には、バックスラッシュによるエスケープが増えて読みにくくなります。

+
+
"He said \"I'm fine\". And I said \"me too\"."
+
+

そんなときに便利なのが「%形式」です。%形式は%で始まる文字列表現で、次の文字で種別が、続く文字で区切り文字が決まります。%Qで始まるものはダブルクォート相当、%qで始まるものはシングルクォート相当の扱いになります。区切り文字はもう一度同じ文字が登場するまでが文字列の範囲になりますが、「(」「[」「{」「<」だけは相当する閉じかっこ(それぞれ「)」「]」「}」「>」)が終わりの区切り文字になります。

+
+
%Q(He said "I'm fine". And I said "me too".)
+
+

%形式は文字列「Q」「q」の他にも、正規表現「r」や文字列の配列「w」、それからコマンド入力「x」などにも使われます。

+
+
+

ヒアドキュメント

+
+

最後の文字列リテラルがヒアドキュメントです。ヒアドキュメントはシェル由来の記法で、プログラムの中に複数行にわたるテキストを埋め込む方法です。

+

ヒアドキュメントは「<< 識別子」または「<< 文字列」という形式で、その次の行から識別子、または文字列の内容に正確に一致する行までの内容がその内容になります。「<<」の直後には空白を置けません。

+

文字列によって引用終了を指定した場合にはその文字列の種別がヒアドキュメントの種別になります。ですから、たとえば終了記号が「""」でくくってある場合には、ヒアドキュメント中でバックスラッシュ記法および式展開が使えます。文字列の代わりに識別子を指定した場合は,その識別子を「""」でくくった場合と同じになります。

+
+
print <<FOO            # ヒアドキュメント
+this is a test
+FOO
+
+print <<"FOO"          # 上とまったく同じ
+this is a test
+FOO
+
+print <<"FOO"          # 式展開も行われます
+this is the test
+print #{10+5}
+FOO
+
+print <<'FOO'          # こちらでは式展開は行われません
+this is the test
+print #{10+5}
+FOO
+
+print <<`BAR`          # コマンド入力
+ls -l
+BAR
+
+print <<"FOO", <<"BAR" # 連ねられます
+I said foo.
+FOO
+I said bar.
+BAR
+
+
+

<<」の直後に「-」を置くことで、引用終了マークをインデントすることができます。これによってプログラム中にヒアドキュメントをより自然な形で置くことができます。

+
+
if need_define_foo
+  eval <<-EOS          # 区切り文字列をインデントできます
+    def foo
+      print "foo\n"
+    end
+  EOS
+end
+
+
+
+

sprintf

+
+

「文字列の生成」として結構便利なのがC由来のsprintfメソッドです。sprintfは数値を文字列化したり、表示幅を指定して文字列を整形したりするのに便利です。

+
+
printf("%dは8進で%oです\n", 16, 16)
+
+

を実行すれば、

+
+
"16は8進で20です\n"
+
+

という出力を得ます。%dが10進整数、%oが8進整数を意味します。sprintfのフォーマット指定は詳細はほぼCのprintfの指定と同じですが、以下の違いがあります。

+
    +
  • %*s」のような表示幅を引数で指定する方法が提供されない

  • +
  • %b」という2進表示の指定が追加されている

  • +
+

Rubyではsprintf%という文字列のメソッドにもなっていますので、上記の例は以下のように書くこともできます。

+
+
+
"%dは8進で%oです\n" % [16, 16]
+
+

どっちがわかりやすいかというと微妙ですが。

+
+
+

メソッド一覧

+
+

文字列がどのようなことができるかを示すために文字列オブジェクトの持つメソッド表11.2に示します。それぞれのメソッドの具体的な機能や使い方についてはマニュアルを参照してください。

+ +
+

表11.2●文字列オブジェクトのメソッド

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
メソッド説明メソッド説明
%sprintfnext「次」の文字列
*文字列の繰り返しnext!「次」の文字列
+文字列の結合oct8進整数化
<<文字列の追加replace文字列内容の置換
=マッチreverse逆順化
[]文字列一部の取得reverse!逆順化
[]=文字列一部の更新rindex後からのindex
capitalize先頭文字の大文字化rjust右揃え
capitalize!先頭文字の大文字化scan繰り返しマッチ
center中央揃えsizelengthと同じ
chomp末尾改行削除slice部分文字列切り出し
chomp!末尾改行削除slice!部分文字列切り出し
chop末尾文字削除split文字列分割
chop!末尾文字削除squeeze文字列圧縮
concat文字結合squeeze!文字列圧縮
count文字数カウントstrip両端の空白文字削除
crypt暗号化strip!両端の空白文字削除
delete文字削除sub文字列置換
delete!文字削除sub!文字列置換
downcase小文字化succnext
downcase!小文字化succ!next!
dump非表示文字のエスケープsumチェックサム
each各行繰り返しswapcase大文字小文字の交換
each_byteバイト単位繰り返しswapcase!大文字小文字の交換
each_lineeachと同じto_f浮動小数点数化
empty?空文字列かto_i整数化
hex16進整数化tr文字置換
include?文字列包含チェックtr!文字置換
index部分文字列検索tr_s文字置換と圧縮
internシンボル化tr_s!文字置換と圧縮
gsub文字列置換unpackバイナリ展開
gsub!文字列置換upcase大文字化
length長さupcase!大文字化
ljust左揃えupto「次」の文字列の繰り返し
+
+
+
+

部分文字列

+
+

文字列の一部を切り出すには[]メソッドを使います。[]メソッドには、配列と似た以下の4パターンの呼び出し方があります。文字列sから一部を切り出すには次のようにします。

+
    +
  • s[n]     n番目の文字(バイト)
  • +
  • s[n,len] nバイト目からlenバイトの文字列
  • +
  • s[n..m]  nバイト目からmバイト目まで(末尾含む)
  • +
  • s[n...m] nバイト目からmバイト目まで(末尾含まない)
  • +
+

位置を表すインデックス(上の例ではnm)はCなどと同様に先頭が0バイト目になります。また、インデックスが負の数であった場合には、文字列の後ろから数えます(末尾が−1になる)。

+

s[n]」は文字コードを表す整数を返し、それ以外は文字列を返すことに注意してください。同じように[]= メソッドでその一部を変更することができます。

+
+
s = "foobarbaz"
+s[0]               # => fの文字コード(102)
+s[0,3]             # => "foo"
+s[4,2] = "oo"      # "fooboobaz"になる
+s[-3..-1] = "hoo"  # "fooboohoo"になる
+
+

部分文字列の取り出しには、その他にsliceメソッドがあります。sliceメソッドは[]と同じような引数を取り、部分文字列を返します。slice!メソッドは元の文字列から指定された部分を取り除き、取り除かれた文字列を返します。

+
+
s = "foobarbaz"
+s.slice(0,3)       # => "foo"
+s.slice!(3,3)      # => "bar"
+                   # 文字列は"foobaz"になる
+
+

Rubyの文字列は、文字列とはいいながらその実体はバイト列であることに注意してください。つまり、上記のメソッドなどで「n文字目からm文字目」というふうに指定したつもりでも、実際には「nバイト目からmバイト目」までと解釈されます。また、漢字のようなマルチバイト文字列の途中でも遠慮なく切り取ってくれます。日本語を扱う場合には注意してください。

+
+

将来のRubyではこの部分が文字単位に変更される予定ですが、とりあえず現状では「Stringクラスの処理はバイト単位」と覚えておいてください。現状のRubyで文字単位の処理を行うためには、次回で説明する予定の「正規表現」を使います。

+
+
+

文字列の長さを得る

+
+

文字列の長さを知るメソッドはlengthメソッドです。この長さは文字列のバイト単位の長さで文字列に何文字含まれているかではありません。lengthメソッドにはsizeという別名があります。また、文字列が空文字列(長さがゼロ)であることを判定するためには、

+
+
s.length == 0
+
+

とする代わりに、

+
+
s.empty?
+
+

と書くこともできます。

+
+
+

文字列を逆順にする

+
+

文字列を逆順にするメソッドも用意されています。それはreverseメソッドです。reverseメソッドは逆順にされた文字列を返します。また、reverse!は元の文字列を逆順に書き換えます。

+
+
s = "reverse"
+s.reverse  # => "esrever"
+s          # "reverse"のまま
+s.reverse! # => "esrever"
+s          # "esrever"に変わっている
+
+
+
+

文字単位に処理する

+
+

上で紹介したlengthメソッドもreverseメソッドもバイト単位で処理を行いますから、日本語テキストの処理には向きません。実際のところ、ほとんどの場合、日本語テキストの処理はバイト単位で間に合うのですが、なかにはどうしても文字単位での処理が必要な場合もあります。そのような場合のために、以下のテクニックを覚えておいてください。

+

splitメソッドに空の正規表現(//)を渡すと文字列を文字単位に分割してくれます。

+
+
s = "まつもと"
+s.split(//)      # => ["ま","つ","も","と"]
+
+

これで文字単位の処理ができます。分割された文字の配列を文字列に戻すには配列のjoinメソッドを使います。

+
+
s = "まつもと"
+s2 = s.split(//)
+
+s2[1]            # => "つ" (1番の文字)
+s2.length        # => 4 (文字単位)
+s2.reverse.join  # => "ともつま"
+
+
+

ね、仕掛けがわかってしまえば簡単でしょう? もちろん毎回文字列を配列に分割して、また元に戻すのはコストがかかる処理ですが、あまり頻繁に使わなければ問題になることはないでしょう。

+
+
+

文字列を分割する

+
+

文字単位の処理に登場したsplitのもともとの目的は文字列を分割することです。splitは特定のパターンに合致する部分で文字列を分割します。たとえば、テストの成績データが、

+
+
matz:国語:100
+matz:数学:40
+junko:国語:80
+junko:英語:100
+
+

のように記録されているとします。この場合の区切り文字は「:」ですね。

+

この最初の行をsplitしてみましょう。

+
+
s = "matz:国語:100"
+s.split(":")
+
+

そうすると、この文字列は、

+
+
["matz","国語","100"]
+
+

という配列に分割されます。

+

一方、固定幅で分割する場合にはunpackメソッドを使います。

+
+
s = "国語100"
+s.unpack("a4a3")  #=> ["国語", "100"]
+
+

unpackメソッドは他にもいろいろ使えるメソッドなのですが、詳しくはあとで説明します。

+
+
+

整数化

+
+

Rubyはデータの種別に厳格な側面がありますから、文字列と整数は区別されます。

+
+
1 + 1    # => 2
+1 + "2"  # error!
+
+

文字列から整数に変換したい場合に使われるのが、以下のメソッドです。

+
+
    +
  • s.to_i     10進整数化
  • +
  • s.hex      16進整数化
  • +
  • s.oct      8進整数化
  • +
  • Integer(s) 整数化
  • +
+

注意すべき点は以下のとおりです。

+
    +
  • to_i, hex, octは数値として解釈できない文字列に対してゼロを返す

  • +
  • oct"0x", "0b" などのプレフックスがあるとそれぞれ文字列を16進、2進とみなして変換する(Perl由来)

  • +
  • Ruby 1.7 ではto_iに何進数で変換するか引数で指定できる(省略値は当然10進)。現時点では指定できるのは2, 8, 10, 16だけ

  • +
  • 関数形式のInteger()は数値として解釈できない文字列はエラーになる

  • +
  • Integer"0x", "0", "0b" などのプレフックスがあるとそれぞれ文字列を16進、8進、2進とみなして変換する

  • +
+

浮動小数点数に変換するときには以下のメソッドを使います。

+
    +
  • s.to_f   浮動小数点数化
  • +
  • Float(s) 浮動小数点数化
  • +
+

Integer()同様、Float()も数値として解釈できない文字列はエラーになります。

+
+
+

大文字小文字の変換

+
+

英語を扱う場合には大文字と小文字の変換が発生する場合があります。そのときに使うのは以下のメソッドです。

+
+
s = "FooBarBaz"
+s.capitalize    # => "Foobarbaz"
+s.downcase      # => "foobarbaz"
+s.swapcase      # => "fOObARbAZ"
+s.upcase        # => "FOOBARBAZ"
+
+

これらのメソッドにはそれぞれ「!」の付いたバージョンがあります。その違いは、

+
    +
  • 元の文字列を書き換える

  • +
  • 書き換えが起きなかった場合にnilを返す

  • +
+

の2点です。

+
+
s = "foo"
+s.downcase!     # => nil (変化がなかったので)
+
+
+
+ +

比較する

+
+

2つの文字列が同じかどうか判定するためには、ごく普通に「==」で比較します。

+

また、文字列は大小比較も行うことができます。

+
+
s1 = "abc"
+s2 = "def"
+s1 > s2     # => false
+s1 >= s2    # => false
+s1 < s2     # => true
+s1 <= s2    # => true
+s1 <=> s2   # => -1
+
+

<=>」演算子は左辺が小さいとき負の整数、等しいときゼロ、大きいとき正の整数を返します。

+

文字列の大小判定は文字コード順に行われます。文字コードはアルファベットはABC順に、ひらがなやカタカナはあいうえお順になっていますから、辞書の順番に近い判定ができます。

+

ただし、以下の点に気を付けてください。

+
    +
  • アルファベットは大文字のABCに続いて小文字のabcがくる。すなわち、「Z」のほうが「a」よりも小さい

  • +
  • 仮名ではひらがなに続いてカタカナがくる。すなわち「ん」は「ア」よりも小さい

  • +
  • 辞書では濁点は無視されるが、文字コード順では濁点のある文字は「次の文字」とみなされる。つまり「が」は「か」の次である

  • +
+

また、Ruby 1.7にはアルファベットの範囲において大文字小文字を無視して比較するcasecmpメソッドがあります。casecmpメソッドは、大文字小文字の差を無視して比較する点以外は「<=>」と同様に動作します。

+
+
"a".casecmp("A")  # => 0
+
+
+
+

繰り返し

+
+

配列と同様に文字列に対しても繰り返しメソッドが定義されています。ただ、文字列の場合には配列ほど繰り返しが役に立ちません。

+

eachメソッドは文字列の各行に対して繰り返します。

+
+
s = <<END
+foo
+bar
+baz
+END
+s.each{|x| puts x}  # foo, bar, bazを出力する
+
+
+

eachメソッドにはeach_lineという別名があります。

+

各行ごとではなく、各文字より正確には各バイトごとに繰り返すのはeach_byteメソッドです。

+

もうちょっと面白い繰り返しはuptoメソッドです。これは「次」の文字列を順番に返します。

+
+
"a".upto("z") {|x|
+  puts x    # a, b, c..zを出力
+}
+"aa".upto("zz") {|x|
+  puts x    # aa, ab, ac,..zy, zzを出力
+}
+
+

この「次」の文字列はnextメソッドで得ることができます。

+
+
"a".next   # => "b"
+"az".next  # => "ba"
+
+
+
+

シンボル

+
+

「シンボル」というのは、識別子に対応する値です。「foo」とか「$bar」とか、Rubyプログラム中に現れる個々の「名前」には対応するシンボルが存在します。シンボルは「:名前」という形式のリテラルがあります。

+

シンボルはRubyのプログラムの中で「名前」を指定するためにたびたび用いられます。たとえば、

+
+
attr_reader :foo, :bar
+
+

によって、「foo」と「bar」という属性読み出し用メソッドを定義することができます。

+

文字列からシンボルを得るためのメソッドがinternです。internメソッドを使えば、通常Rubyの識別子としては許されない、たとえばスペースを含むような名前を持つシンボルを得ることができます。

+
+
"foo".intern    # => :foo
+
+

シンボルからその文字列表現を取るためには、to_sメソッドを使います。

+
+
:foo.to_s       # => "foo"
+
+
+
+

まとめ

+
+

今月は文字列処理の基本ということで、文字列クラスについて学びました。文字列クラスはRubyの中でも最もメソッドの多いクラスです。それだけRubyは文字列処理に本気だということができるでしょう。来月は文字列処理のもう1つの主役である正規表現について学ぶ予定です。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-023.xhtml b/docs/vol1/xhtml/p-023.xhtml new file mode 100644 index 0000000..2ad3152 --- /dev/null +++ b/docs/vol1/xhtml/p-023.xhtml @@ -0,0 +1,280 @@ + + + + + +第11章 文字列の操作 + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ 知られざるRuby ◆ packとunpack

+
+

「文字列の分割」のときに使ったunpackメソッドは、本来配列のpackメソッドと対になって使われるためのメソッドです。packは配列に格納されている各要素を指定されたテンプレートに従って文字列(というかバイト列)に詰め込むメソッドで、unpackはその逆、つまりテンプレートに従ってバイト列の内容からデータを取り出し配列として返します。

+

今回はPerl譲りのこのpackunpackについて考えてみましょう。Perlエキスパートの人には退屈な話かもしれませんね。

+
+

Perlエキスパートのために

+

Perlのpackunpackについてよくご存じの方のために、PerlとRubyのpackunpackの違いを紹介しておきます。

+
+

引数が違う

+

Perlがそれぞれpack, unpack関数であるのに対して、Rubyでは配列クラスのpackメソッドと文字列クラスのunpackメソッドです。呼び出し方もPerlでは、

+
+
pack(template, val, val, val);
+unpack(template, str);
+
+

であるのに対して、Rubyでは、

+
+
[val, val, val].pack(template)
+str.unpack(template)
+
+

になります。

+

64bit整数のQ, qテンプレートがない

+

将来はできるかもしれませんが、当面は64bit整数は扱えません。もっともPerlでもすべてのプラットフォームで使えるわけではありませんから、さして問題にはならないと思います(※この原稿を書いている間に1.7に追加しちゃいました)。

+

Rubyのpackにはチェックサム機能がない

+

文字列のチェックサムのためには文字列クラスのsumメソッドを使います。

+
+
+
+

packテンプレート

+

packunpackに指定するテンプレート文字の一覧を表11.3に示します。テンプレート文字は後ろに「長さ」を表す数字を続けることができます。「長さ」の代わりに‘*’とすることで「残りすべて」を表すこともできます。

+ +
+

表11.3●テンプレート文字の意味の違い

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
文字説明文字説明
aASCII文字列(nul文字を詰める)vリトルエンディアンのunsigned short
AASCII文字列(スペースを詰める)Vリトルエンディアンのunsigned long
bビットストリング(下位ビットから上位ビット)f単精度浮動小数点数(機種依存)
Bビットストリング(上位ビットから下位ビット)d倍精度浮動小数点数(機種依存)
h16進文字列(下位ニブルが先)eリトルエンディアンの単精度浮動小数点数(機種依存)
H16進文字列(上位ニブルが先)Eリトルエンディアンの倍精度浮動小数点数(機種依存)
cchargビッグエンディアンの単精度浮動小数点数(機種依存)
Cunsigned charGビッグエンディアンの倍精度浮動小数点数(機種依存)
sshortpナル終端の文字列へのポインタ
Sunsigned shortP構造体(固定長文字列)へのポインタ
iintuuuencodeされた文字列
Iunsigned intUutf-8
llongwBER圧縮整数
Lunsigned longxナルバイト/1バイト読み飛ばす
mbase64された文字列X1バイト後退
Mquoted-printable encodingされた文字列Znull終端文字列
nビッグエンディアンのunsigned short@絶対位置への移動
Nビッグエンディアンのunsigned long
+
+

テンプレート文字の間には空白を入れて見やすくすることができます。

+

下記の説明の中でshortlongのサイズはシステムによらずそれぞれ2, 4バイトとみなされます。s, S, i, I, l, Lの各テンプレート文字は直後に「_」または「!」を続けた場合にはサイズはシステム依存のshort, longのサイズになります。

+

a, A, Zの各テンプレート文字はpackunpackで少しだけ意味が違います(表11.4)。

+
+

表11.4●テンプレート文字の意味の違い

+ + + + + + + + + + + + + + + + + + + + + +
文字packunpack
anul文字を詰める後続するnull文字やスペースを残す
Aスペースを詰める後続するnull文字やスペースを削除
Znul文字を詰める後続するnull文字を削除
+
+
+
+

主な使い道

+

packunpackの使い方として主なものは以下のとおりです。

+ +

このうち、すでに説明した固定幅での文字列の分割以外の使い方について見てみましょう。

+
+
数値の配列を文字列化
+
+
[82, 117, 98, 121].pack("cccc") # => "Ruby"
+[82, 117, 98, 121].pack("c4")   # => "Ruby"
+[82, 117, 98, 121].pack("c*")   # => "Ruby"
+
+
+
+
文字列を文字コードの配列に
+
+
"Ruby".unpack('C*')             # => [82, 117, 98, 121]
+
+
+
+
文字コードとUTF-8文字列との相互変換
+

Cでは結局ASCII文字列しか扱えませんが、Uテンプレートを使うとUTF-8文字列を処理できます。入力がUTF-8の場合、

+
+
"Ruby".unpack('U*')             # => [82, 117, 98, 121]
+"ああ".unpack('U*')             # => [12354, 12354]
+[12354, 12354].pack(U2)         # => "ああ"
+
+

となります。

+
+
+
uuencode, base64のエンコード、デコード
+

uuencodeはかつてメールなどでバイナリデータを送るときに用いられたエンコーディング手法です。base64は同じ目的のために最近よく用いられるエンコーディング手法です。直接見ることは少ないかもしれませんが、メールの添付ファイルはたいていこの方法でエンコーディングされています(base64の他にquoted printable形式というものも使われることがあります)。

+

strというバイナリデータをエンコードするためには、

+
+
[str].pack("u")
+[str].pack("M")
+
+

デコードするためには、

+
+
str.unpack("u")
+str.unpack("M")
+
+
+

とします。なお、quoted printable形式にはmテンプレート文字を用います。

+
+
+
エンディアンの変換
+

マシンによって整数のデータは位置が違うことを「エンディアン」といいます。Intel製のx86は小さい桁を先に置く「リトルエンディアン」で、SunのSparcやMotorolaの68000などは大きい桁を先に置く「ビッグエンディアン」です。packunpackを用いれば、ビッグエンディアンとリトルエンディアンの相互変換も簡単にできます。

+
+
[1].pack("N").unpack("V")  # => [16777216]
+[1].pack("n").unpack("v")  # => [256]
+
+
+
+
C構造体のバイナリ操作
+

SocketクラスはCのsocket APIを構造体を含めてそのまま受け取る非常に原始的なインターフェイスを提供しています。そのためにstruct sockaddr構造体を文字列にpackする必要があります。

+
+
[2, 7, 127, 0, 0, 1].pack("snC4x8")
+
+

これは「AF_INET, 'echo', 127.0.0.1」を意味します。IPアドレスの取得もunpackを使います。

+
+
Socket.gethostbyname("localhost")[3].unpack("C4").join(".")
+# => "127.0.0.1"
+
+
+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-024.xhtml b/docs/vol1/xhtml/p-024.xhtml new file mode 100644 index 0000000..9b3afaa --- /dev/null +++ b/docs/vol1/xhtml/p-024.xhtml @@ -0,0 +1,66 @@ + + + + + +第11章 文字列の操作 + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ 国際感覚

+
+

ちょうどこの原稿を書いているタイミングで、冬季オリンピックが開催されています。今年は私の通ってる教会の本部があるソルトレークシティが会場ともあって注目しています。

+

実は普段はスポーツにはほとんど関心がなく、野球中継も見たことがない私ですが、今年はついつい引き込まれて夜更かしをして中継を見てしまっています。もっとも私の場合オリンピックとは関係なくいつも夜更かしをしてるんですが(笑)。

+

そんななかで、思わず日本人選手の応援をしている自分を発見すると国際感覚ってなんだろうって考えてしまいます。もともと、海外で事故や事件があったときに「日本人の被害はありませんでした」などとわざわざ断言するニュース番組の姿勢は嫌いだったのですが、オリンピックで競技そのものよりも日本人の成績だけを気にして、応援する姿勢っていうのは実際のところ大差はないなあなどと感じてしまいます。

+

ま、それはそれとして、海外に積極的に出ていって活躍している人々の姿を見るのは気持ちのいいものですね。

+

もともと田舎に引きこもって、海外旅行の経験もほとんどなく、東京にさえめったに行かない私にとって、長らくなかなか「日本の外」を意識する機会はあまりなかったのですが、最近になってそういう機会が増えてきました。気の付いた順にいくつかあげてみましょう。

+
+

時差

+

当然といえば当然ですが、地球は丸く、太陽は地球から見ると東から西に移動しているわけです。というわけで、世界には時差があります。日本はロンドン時間である世界標準時よりも9時間早く、逆にオリンピック会場であるソルトレークシティは日本よりも16時間遅いわけです。

+

オリンピックでもない限り、普段はあまり気にならないような時差ですが、最近はメールの来る時間や、IRCでチャットする場合の話し相手のタイムゾーンなどでより頻繁に感じるようになりました。

+

そういえば普段から不規則な生活をしているのが幸いして、海外旅行に行ってもあまり時差ぼけには悩まされませんでした。これってよいこと? そういえば、Perlの作者Larry Wallも日本に来たときおんなじようなことを言ってたなあ。

+
+
+

言葉の壁

+

これまた当然ですが、日本人でない人はほとんど日本語を話しません。

+

私の知り合いには日本語を話す外国人が何人かいますが、彼らのほとんどは驚嘆の目で見られるか、「変なガイジン」と呼ばれるかのいずれか、あるいは両方の経験をしているようです。まあ、日本語を母国語にしないのに日本語を話す人の数を考えるとしかたがない面もあるのかもしれません。

+

一方、海外に出かけるとほとんどは英語を使ってコミュニケートすることになるわけです。スペイン語を母国語とするホンデュラス人と日本語を母国語とする日本人が、共通言語が英語しかないので、たどたどしく話すのは、一生懸命ながらどこかユーモラスな気もします。

+
+

まあ、言葉の壁を言い訳に日本に閉じこもるよりは100倍望ましいので、私自身も英語を積極的に使って、苦しみながらも、英語のメールを読んだり書いたり、あるいは場合によっては英語でプレゼンテーションをしたりするわけです。

+

しかし、とはいうものの、アメリカ人やイギリス人のように英語を母国語にする人々はこんな苦労は感じることはないわけで、心の片隅では「こんなの不公平だ」とか「世界の共通言語は日本語であるべきでは」などと考えたりもするわけです。現実にはそんなことは夢にすぎないわけですが。厳しいなあ。

+
+
+

心理的障壁

+

「言葉の壁」は、英語を話せないから伝えたいことも伝えられない、というある種わかりやすい障壁なのですが、なんとなくもっと別の障壁があるような気がします。

+

ここではとりあえず心理的障壁と呼んでみましたが、なんとなく自分の心の中に「異質なものへの恐れ」のようなものがあって、それが「外国人」あるいは「(典型的)日本人でない人」に向かっていることがたびたびあったような気がするのです。

+

今回のRubyに関連して何人もの「外国人」と数え切れないほどメールのやりとりをしました。彼らとは国籍も文化も宗教も違うことが多いわけですが、それにも関らず、驚くほど共感できる経験がたびたびありました。もちろん、これはRubyという共通の関心があったからこそですが、逆にいえば共通の関心事さえあれば、同じ人間として、背景の違いはさして問題ではない、恐れることはないということでもあります。

+

こうやって少しずつお互いにわかりあえることによって、「違い」を克服できるのではないか、できたらいいなあと思うのでした。

+
+
+

国産主義

+

Rubyが世に登場して以来、たびたび「国産言語」と呼ばれてきました。まあ、日本人が開発して海外でも一般に使われている言語としては珍しい(最初かも)のは確かですが、とはいえなんか違和感を感じるのも事実です。

+

別に日本人が作ったから日本人の感覚に合っているとか、日本人に使いやすいとか、逆に外国人には使いにくいとかがあるわけもないのですから。メリットといえば、たまたま作者と英語ではなく日本語でメールが書けることくらいでしょうか。こんなのが重視されてしまうのも一種の心理的障壁なのでしょうか。

+
+
+

おわりに

+

なんとなく、自分の国際感覚のなさを露呈しただけの駄文のような気もしないでもないのですが、これが私が感じる日本の壁ということで。これが「馬鹿なこと言ってる」と思われるのが当たり前の世の中が来てほしいと思う今日この頃です。

+

あと、今後Rubyの知識を生かして積極的に海外展開しようという人など現れてくれないだろうかなあ、とも期待しているのですが、読者の皆さんはいかがです?

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-025.xhtml b/docs/vol1/xhtml/p-025.xhtml new file mode 100755 index 0000000..6f51ba6 --- /dev/null +++ b/docs/vol1/xhtml/p-025.xhtml @@ -0,0 +1,875 @@ + + + + + +第12章 パターンマッチ + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay12 +
+

+初等Ruby講座
+パターンマッチ +

+
+

[Linux magazine, 2002年5月号]

+
+

現代においてはパターンマッチと言えば、Ruby 2.7から導入された構文のことを連想する人のほうが多そうですが、こちらは正規表現による文字列のパターンマッチです。当時のEmacsベースの正規表現エンジンは、鬼雲おにぐもに置き換わってしまいましたが、基本的な部分は同じです。

+

また、Riteという次世代インタプリタ構想も披露しています。記事の末尾に「ぐずぐずしてたら、他の人に負けちゃうかもしれません」と予想していたとおり、Rubyのインタプリタコアの置き換えは笹田さんのYARVによって達成されてしまいました。しかし、Rite構想は無駄になったわけではなく、こちらはmrubyのコアになりました。今でもmrubyのソースコードに何箇所かRiteという表現が残っています。

+
+
+

先月学んだ文字列クラスに続いて、今月はRubyの文字列処理のもう1つの鍵である正規表現クラスRegexpについて学んで、文字列処理の神髄に迫りましょう。

+
+
+

パターンとは

+
+

文字列を処理する場合に、たとえば「"Ruby"という文字列を含む」というのは比較的簡単ですが、もうちょっとインテリジェントなルールを記述したい場合もあります。たとえば、「Pで始まってlで終わる」とかです。でも、コンピュータは(Rubyも)人間の言葉はわかりませんから、コンピュータにでもわかる言葉でこのルールを表現してやらなくてはいけません。このようなルールが「正規表現」です。Rubyではスラッシュ「/」で囲まれた部分が正規表現になります。

+
+
/Ruby/   # 「"Ruby"という文字列を含む」
+/P.*l/   # 「Pで始まってlで終わる」
+
+

後者は少々難しいルールを表現していますね。この細部の意味についてはあとで詳しく説明します。

+
+

正規表現は文字列のパターンを表現するための、いわばミニ言語ですから、文法があります。外側の言語(Ruby)とは違う文法を持つ独立した言語ですから、ときおり混乱の元になることもありますが、文法をきちんと押さえてしまえばこれほど便利なものはありません。

+
+
+

パターンマッチのパワー

+
+

正規表現によるパターンマッチのパワーでなにができるのか、その能力の一端をご紹介しましょう。たとえば、パターンマッチ機能を使えば、次のようなことは朝飯前です。

+
    +
  • ログファイルから「日付のようなもの」を取り出す
    +しかも、日本式、米国式、欧州式のそれぞれに対応できます。

  • +
  • HTMLファイルから「URLのようなもの」を取り出す
    +URLを抽出すればリンク集やブックマークをデータとして加工できます。

  • +
  • 典型的なスペルミスを探す
    +日本語では「私はは」のような助詞の連結のミスが多いのですが、それらもパターンで表現できます。

  • +
+

「のようなもの」というあたりがパターンマッチ以外の方法ではなかなか実現できないのですが、逆にいえばパターンマッチの最も得意とする領域ということです。

+

これらの具体的な方法についてはあとで紹介します。

+
+
+

パターンの文法

+
+

正規表現は「メタ文字」と呼ばれる特殊機能を持つ文字とそれ以外の文字から構成されます。メタ文字はプログラミング言語における制御構造のようなものです。正規表現のメタ文字を表12.1に示します。

+ +
+

表12.1●正規表現のメタ文字

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
表記説明表記説明
.任意の一文字(改行以外)m,n?m回からn回の繰り返し(非よくばり型)
[][a-z]aからzまでのいずれか^行頭
[^][^a-z]aからz以外の文字$行末(改行があればその直前)
\w単語を構成する文字\A文字列の先頭
\W単語を構成する文字以外\Z文字列の末尾(改行があればその直前)
\s空白文字。[\t\n\r\f]と同じ\z文字列の末尾
\S非空白文字\bバックスペース(0x08)([]内)
\d数字。[0-9]と同じ\b語境界文字([]外)
\D非数字。[^0-9]と同じ\B非語境界文字
*0回以上の繰り返し|選択
+1回以上の繰り返し( )表現のグループ化(後方参照あり)
?0または1回の繰り返し\1,\2後方参照(n番目のかっこに対応する)
m,nm回からn回の繰り返し(?: )表現のグループ化(後方参照なし)
*?0回以上の繰り返し(非よくばり型)(?= )パターンによる位置指定(幅を持たない)
+?1回以上の繰り返し(非よくばり型)(?! )パターンの否定による位置指定(幅を持たない)
??0または1回の繰り返し(非よくばり型)(?# )コメント
+
+

メタ文字以外の文字はその文字そのものと一致します。ですから、/FOO/FOOという文字の並びと一致します。

+

前に出てきた例 /P.*l/ は以下のように解釈します。

+
    +
  • P」は通常文字なので文字「P」そのものとマッチ

  • +
  • .」は任意の1文字とマッチ

  • +
  • *」は直前のパターン(「.」)の任意回数の繰り返し

  • +
  • l」は文字「l」そのものとマッチ

  • +
+

ですから、これを人間の言葉に直すと「Pで始まってlで終わる」パターンになり、「Perl」とか「Pascal」とマッチします。「.*」は0回を含みますから、上記のパターンは「Pl」にもマッチします。人間はよく0回のことを忘れてしまいますから注意が必要です。コンピュータは忘れないんですよねえ。

+
+
+

通常文字

+
+

では、正規表現の文法をちょっと詳しく見てみましょう。まずは、表12.2に示したメタ文字以外の「普通」の文字はその文字自身とマッチします。ですから、「a」は「a」自身とマッチします。ですから、メタ文字を含まない文字列のパターンは、その文字列自身です。たとえば、「Ruby」にマッチするパターンは「Ruby」です。これだけでは全然難しくないですね。

+
+
+

文字クラス

+
+

[」と「]」で囲まれたものは「文字クラス指定」で、[] の間に含まれる文字のいずれかにマッチします。たとえば「[abcdef]」はアルファベット小文字のabcdefのいずれかにマッチします。文字クラス指定の中では -(ハイフン)でつなぐことによって範囲を指定できます。ですから、「[abcdef]」の代わりに「[a-f]」と書くことができます。このほうがわかりやすいですね。

+

文字クラス指定の最初の文字が「^」であった場合には、条件が反転して今度は[]に含まれない文字にマッチします。

+

では、文字クラスで「^」「-」「]」を指定したい場合にはどうしたらよいのでしょう?

+

正解は、「^」は先頭以外に、「-」「]」は先頭に置けばよい、でした。また、直前にバックスラッシュ(\またはY)を置く方法もあります。

+
+
+ +

任意の1文字

+
+

次は任意の1文字を表現するパターンです。これは「.」になります。ただし、「.」は改行とはマッチしません(特別に指定した場合を除く)。ということは、「.」はabCなどにマッチします。日本語の文字についてですが、Rubyの正規表現は日本語対応ですから、文字コードについての情報を与えてやれば、「.」が「あ」や「い」などに1文字としてマッチします。

+

たとえば、「Pから始まって、任意の2文字の後、l」というパターンは「P..l」になります。「.」はたびたびこの後説明する「繰り返し」とともに使われます。

+
+
+

繰り返し

+
+

さて、パターンの文法には制御構造も必要です。正規表現の制御構造は「繰り返し」です。正規表現には上限と下限によって複数の繰り返し記法があります(表12.2)。いずれも直前のパターンに対して繰り返しを行います。

+
+

表12.2●繰り返し記法

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
表記下限上限
a*0なし
a+1なし
a?01
an,mnm
annn
an,nなし
+

ただし、n, mは整数

+
+

ですから、「a*」は「aの0回以上の繰り返し」「a+」は「aの1回以上の繰り返し」「a?」は「aの0回または1回の繰り返し」言い換えれば「a」または無にマッチします。

+

ここで注意すべき点が2つあります。

+

1つは、マッチは「最左最長一致」であり、パターンは一番左側で一番長い部分にマッチすることです。たとえば「a*」というパターンを "aabbabaaa" という文字列にマッチさせると先頭の "aa" にマッチします。aの1個の並びでなく2個の並びにマッチすることと、文字列中で最も長いaの並びである末尾の "aaa" にマッチするわけでないことに注目してください。

+

もう1つは、0回の繰り返しは間違いやすいことです。「a*bb」というパターンを "bbaabb" という文字列にマッチさせると、マッチするのは先頭の "bb" の部分です。つまり、「a*」は0回マッチし、その後の「bb」が先頭でマッチするのです。混乱しそうなときには「*」でなく1回以上の繰り返しである「+」を使うべきでしょう。

+
+
+

「よくばり」と「なまけもの」

+
+

さて、最左最長一致では困る場合がときどきあります。たとえば、

+
    +
  • http://www.ruby-lang.org:80/ja/

  • +
+

という文字列の先頭の「http:」までの部分を取り出すために「.*:」というパターンとマッチさせると、最長一致により実際には「http://www.ruby-lang.org:」までマッチしてしまいます。これは正規表現の繰り返しが「よくばり」で、パターンに合致する一番長い文字列を探すからです。

+

繰り返し系メタ文字のすぐ後ろに「?」を付けると「よくばり」の代わりに「なまけもの」のマッチを行います。「なまけもの」マッチは最初にパターンをマッチした時点で繰り返しを止めてしまいます。ですから、上記の文字列と「.*?」というパターンをマッチさせると、めでたく「http:」を取り出すことができます。

+ +

しかし、「よくばり」か、そうでなければ「なまけもの」とは、人間だったらいずれにしても嫌われちゃいますね(笑)。

+
+
+

グルーピング

+
+

繰り返しは直前の正規表現を対象にしますから、「ma+」は「mとそれに続くaの1回以上の繰り返し」という意味になります。「maの1回以上の繰り返し」のためにはかっこでくくって「(ma)+」とします。このパターンをまとめる機能「グルーピング」と呼びます。

+

正規表現中のかっこにマッチした文字列は \1などで表現できます。つまり「(.)\1」というパターンは同じ文字が2つ続いたパターンを表現します。これを後方参照と呼びます。

+

かっこは後方参照のためにデータを内部的にセーブするので、少々効率が落ちます。ただ単にグルーピングだけのためにはパターンを「(?:」と「)」でくくります。

+
+
+

選択

+
+

複数のパターンのいずれかを選択するのがメタ文字「|」です。たとえば「yes|no」のような感じです。

+

選択の「|」は他の正規表現の構成要素に比べて優先順位が低いため、「yes|no」は「"yes" または "no"」と解釈され、「"ye" に続く "s" または "n"、さらに "o" が続いたもの」とは解釈されません。そういうパターンはグルーピングを使って「ye(s|n)o」と表現されます。

+
+
+

アンカー

+
+

今まで紹介したパターンは文字(の並び)とマッチするものでしたが、正規表現にはその他にも位置を特定するだけで文字とはマッチしないパターンがあります。それらをアンカーと呼びます。正規表現のアンカーを表12.3に示します。

+
+

表12.3●繰り返し記法

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
表記マッチ位置
^行の先頭
$行の末尾(改行があればその前)
\A文字列の先頭
\Z文字列の末尾(改行があればその前)
\z文字列の末尾
\b語境界文字([]外)
(?= )パターンによる位置指定
(?! )パターンの否定による位置指定
+
+
+
+ +

オプション

+
+

正規表現の末尾の/の後ろにはその正規表現のためのオプションを指定できます。

+
+
/ruby/i     # 大文字小文字無視
+/a=#{a}/o   # 一度だけ展開
+/あい/e     # 文字コードはEUC
+/iあい/ei   # 複数指定できる
+
+

指定できるオプションを表12.4に示します。

+
+

表12.4●正規表現オプション一覧

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
オプション説明
i大文字小文字を区別しない
m複数行マッチ、改行も通常文字とみなす
o式展開を最初の一度だけ行う
x正規表現中の空白を無視。コメントを有効に
eEUC文字列だと仮定してマッチを行う
sSJIS文字列だと仮定してマッチを行う
uUTF-8文字列だと仮定してマッチを行う
nバイト列だとみなしてマッチを行う
+
+

iオプションを使うと正規表現マッチの際にアルファベットの大文字と小文字を区別しなくなります。iはignore caseのiです。

+

oオプションは式展開を一度しか行いません。正規表現でも文字列同様に式展開を行うことができるのですが、oオプションを付けるとこの展開を最初の一度だけに限定させることができます。oはonceのoです。

+

mオプションは複数行マッチモードです。通常、「.」は改行にマッチしませんし、「^」や「$」は文字列途中の改行にもマッチしますが、mオプションを使うと文字列全体が1つの行であると見なして、改行を特別扱いしなくなります。ですから、「.」は改行にマッチし、「^」は文字列先頭と、「$」は文字列末尾とだけマッチします。mはmulti-lineのmです。

+

xオプションは、正規表現に意味上の区切りに空白を入れたり、コメントを付けたりすることを許します。この拡張正規表現で、読みにくい正規表現を比較的わかりやすく記述することができます。

+
+
/\d{4}-?    # 年
+\d{1,2}-?   # 月
+\d{1,2}     # 日
+/x
+
+

xオプションを付けた正規表現中に空白を入れたいときには「\s」を使います。xはextendedのxです。

+

残りのe, s, u, nは正規表現の文字コードを表します。それぞれEUC, SJIS, UTF-8, NONEを意味します。

+
+
+ +

読解・正規表現

+
+

では練習としていくつかの正規表現の意味を読解してみましょう。

+
+

/FOO/

+

FOO」つまり「F」「O」「O」という文字の並び。

+

/[A-Z][a-zA-Z1-9]*/

+

アルファベット大文字で始まり、英数字が続く、Rubyの定数の識別子のパターン。

+

/(Ruby)+/

+

Ruby」の1回以上の繰り返し、つまり、「Ruby」や「RubyRuby」や「RubyRubyRuby」……。

+

/Dec,?/

+

Dec」の後「,」の0または1回の繰り返し。つまり、「Dec」または「Dec,」。

+

/Sun|Mon|Tue|Wed|Thu|Fri|Sat/

+

(英語の)曜日にマッチ。選択(|)は結合強度が弱いので、直前の文字でなく文字列が対象。

+
+
+
+

メソッド一覧

+
+

さて、毎回恒例ですから、正規表現オブジェクトの持つメソッドを表12.5に示します。今まで紹介してきた配列、ハッシュ、文字列に比べてはるかに少ないメソッドしかないのが目立ちますね。6つしかありません。おまけにそのうち3つは同じメソッドの別名ですし。結局、正規表現オブジェクトの本質はマッチすることだけにあるということでしょう。

+
+

表12.5●正規表現オブジェクトのメソッド

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
メソッド説明
re === strmatch
re =~strmatch
casefold?大文字小文字を無視するか
kcode対応する文字コード
match(str)マッチ
source正規表現の文字列表現
+
+

一方、正規表現のクラスメソッドは表12.6のとおりです。これも少ないですね。

+
+

表12.6●正規表現クラスのメソッド

+ + + + + + + + + + + + + + + + + + + + + + + + + +
メソッド説明
compile(str)正規表現
escape(str)メタ文字のエスケープ
last_match最後のマッチ情報
new(str)compile
quote(str)escape
+
+
+
+ +

正規表現の生成

+
+

Rubyでは正規表現オブジェクトを得る方法は、正規表現リテラルを使う方法と、正規表現クラスのクラスメソッドを使う方法とがあります。

+
+
/P.*l/                 # 正規表現リテラル
+Regexp.compile("P.*")  # クラスメソッド
+
+

前者は正規表現オブジェクトの生成がプログラムの読み込み時に一度しか行われないので、毎回文字列から正規表現を作り出す後者に比べて少々効率がよいです。しかし、プログラムの中で組み立てた文字列から正規表現を作るには後者のほうが自然です。

+

正規表現の元になる文字列の中にメタ文字が含まれている可能性があり、メタ文字に特別な意味を与えたくない場合には、メタ文字をエスケープして特別な意味をなくす必要があります。

+
+
Regexp.escape("P.*") => "P\\.\\*"
+
+

この文字列から正規表現を作れば、文字列「P.*l」そのものとマッチする正規表現が得られます。

+
+
+

正規表現マッチ

+
+

正規表現マッチは =~ メソッドかmatchメソッドを使います。=~ メソッドはマッチが成功したとき、マッチした位置を示す整数を返します(文字列先頭がゼロ)。一方、matchメソッドは成功したとき、マッチ情報を示すMatchDataオブジェクトを返します。マッチしなかった場合にはいずれのメソッドもnilを返します。

+
+
/P.*l/ =~ "Perl"      # => 0
+/P.*l/.match("Perl")  # => #<MatchData:0x401b8488>
+
+

マッチに使われる =~ メソッドは文字列クラスにも定義されているので、右辺と左辺を取り換えても同じ意味を持ちます。

+
+
"Perl" =~ /P.*l/      # => 0
+
+

ここで、注意すべき点は =~ の両辺が文字列だった場合には、右側がパターンとして解釈される点です。

+
+
"Perl" =~ "P.*l"      # => 0=
+
+
+
+

MatchData

+
+

matchメソッドの戻りであるMatchDataからは、マッチの位置、部分マッチ(かっこでくくられた部分)、そのオフセット、マッチした部分の前後の文字列などを取り出すことができます。

+

MatchDataクラスのメソッドを表12.7に示します。

+ +

実際の使い方の例は以下のようになります。

+
+
m = /a(.)(.)/.match("abc")
+m[0]         # => "abc" (マッチ全体)
+m[1]         # => "b"   (最初のかっこ)
+m.begin(0)   # => 0     (マッチ全体)
+m.begin(1)   # => 1     (最初のかっこ)
+m.offset(2)  # => [2,2] (2番目のかっこ)
+
+
+
+

特殊変数

+
+

RubyはPerl由来の特殊変数を持っています。「$」で始まるこれらの変数はプログラムを醜くする傾向があるのでしばしば嫌われますが、一度使えば書き捨てるような短いプログラムでは、記述がコンパクトになるメリットもあります。正規表現関係の特殊変数を表12.8に示します。

+
+

表12.8●正規表現関連特殊変数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
表記説明
$~最後のMatchDataRegexp.last_matchと同じ)
$&最後のマッチ文字列
$`マッチより前の文字列
$'マッチより後ろの文字列
$+最後のかっこにマッチした文字列
$nn番目のかっこに対応する文字列(nは1, 2, 3 ... )
+
+
+
+ +

文字列と正規表現

+
+

正規表現は文字列とマッチするものですから、文字列との関わりがすべてです。前回解説した文字列オブジェクトにも正規表現を使うメソッドがたくさんあります。前回はあまり説明できなかった文字列オブジェクトで正規表現を使うものをここで解説しましょう。

+

文字列オブジェクトの正規表現関連メソッドは表12.9のとおりです。

+
+

表12.9●文字列オブジェクトの正規表現関連メソッド

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
メソッド説明メソッド説明
s=~reマッチrindex(re)後ろからのindex
s[re]文字列一部の取得scan(re)繰り返しマッチ
s[re]=v文字列一部の更新split(re)文字列分割
index(re)部分文字列検索sub(re,str)文字列置換
gsub(re,str)文字列置換sub!(re,str)文字列置換
gsub!(re,str)文字列置換
+
+

index, rindexは部分文字列を探すメソッドですが、部分文字列の代わりに正規表現を指定することもできます。[][]=は文字列の一部を取り出すメソッドですが、位置の指定に正規表現を使うことができます。

+
+
+

splitの神髄

+
+

文字列の分割メソッドとして先月も紹介したsplitメソッドですが、実は正規表現と組み合わせることによってよりいろいろなことができるパワーを秘めているのです。

+

先月紹介したのは固定文字列(1文字)で分割する機能でした。

+
+
"a,b,c".split(",")   # => ["a","b","c"]
+
+

しかし、正規表現でも分割できます。

+
+
"a,b:c".split(/[,:]/) # => ["a","b","c"]
+
+

これは、「,」または「:」のいずれかで分割するという意味になります。もちろんもっと複雑なパターンもOKです。

+
+
str.split(/<.*?>/)
+
+

これはHTML(またはXML)のタグの部分で分割し、地の文を配列として得ることができます。タグの部分を除いてしまうのではなく、そのまま残したければパターンをかっこでくくります。splitはかっこでくくった部分を配列に含めるからです。

+
+
str = "<ul><li>a<li>b</ul>"
+str.split(/<.*?>/) # 以下のように分割される
+# ["","<ul>","","<li>","a","<li>","b","</ul>"]
+
+
+
+ +

文字列のscan

+
+

splitは分割する区切りに正規表現を使いましたが、scanのほうはマッチした部分を取り出すメソッドです。

+
+
"foo".scan(/./)   # => ["f", "o", "o"]
+
+

正規表現がかっこを含む場合は、マッチごとにそれぞれのかっこにマッチした文字列の配列を追加するので、配列の配列を返します。

+
+
"foo".scan(/(.)(.)/)  # => [["f","o"]]
+
+

ブロックを指定した場合には、マッチごとにその要素に対してブロックを実行します。

+
+
"foobarbazfoobarbaz".scan(/ba./) {|s| p s}
+# 以下の行を出力:
+# "bar"
+# "baz"
+# "bar"
+# "baz"
+
+

これは

+
+
"foobarbazfoobarbaz".scan(/ba./).each {|s| p s}
+
+

と同じ動作ですが、無駄な配列を生成しないぶん少々効率がよくなっています。

+
+
+

置換

+
+

文字列のパターンに合致する部分を置き換えるためには置換のためのメソッドを使います。置換のためのメソッドは4つあって用途に応じて使い分けます(表12.10)。

+
+

表12.10●置換メソッド

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
メソッド複数置換文字列の書き換え更新なしでnil
sub×××
sub!×
gsub××
gsub!
+
+ +

つまり、subメソッドはパターンに合致する最初の部分だけを置き換え、gsubgはglobalのgです)メソッドは文字列のうちパターンに合致する部分すべてを置き換えます。また、メソッド名に「!」に付いたものは文字列そのものを置き換え、またパターンがマッチしなければnilを返します。メソッド名に「!」が付かないものは、置換を行った文字列を返し、元の文字列は変更しません。

+
+
a = "abcabc"
+a.sub(/b/, 'B')   # => "aBcabc"(aは変化なし)
+a.sub!(/b/, 'B')  # => "aBcabc"(aも更新)
+a.sub!(/d/, 'D')  # => nil(マッチしないから)
+
+a = "abcabc"
+a.gsub(/b/, 'B')  # => "aBcaBc"
+
+

さて、パターンはたいていの場合固定的な文字列ではなく、いろいろな文字列にマッチします。そのような場合、マッチの結果に応じて置換する文字列を決めたいと考えるのは自然なことです。置換メソッドはそういうニーズに対して2つの方法で対応します。

+

1つは置換文字列の中でマッチ結果を表す一種のメタ文字を使う方法です。置換文字列リプレース中の \&\0はマッチした部分文字列に、\1\9n番目のかっこの内容に置き換えられます。これらはそれぞれ「$」(マッチより前の文字列)、「$'」(マッチより後ろの文字列)、「$+」(最後のかっこに対するマッチ)に対応します。

+
+
x = "abcabc"
+x.sub(/[bc]/, '(\&)')   # => "a(b)(c)abc"
+
+

もう1つはブロックを使う方法です。置換メソッドの第2引数(置換文字列)を省略して、代わりにブロックを指定すると、正規表現にマッチした部分がそのブロックを実行した結果で置き換えられます。

+
+
"foobarbazfoobarbaz".gsub(/ba./){|s|s.upcase}
+# => "fooBARBAZfooBARBAZ"
+
+

置換文字列については $1などの特殊変数を使えません。この文字列を評価する時点ではまだマッチが行われていないからです。また、「"」でくくった文字列の中では「\」はエスケープしなければならないので、置換文字列は「'」でくくることをお勧めします。

+
+
# 第二引数の指定でよくある間違い
+'abbbcd'.gsub(/a(b+)/, "#{$1}")       # これは間違い
+'abbbcd'.gsub(/a(b+)/, "\1")          # これも間違い
+'abbbcd'.gsub(/a(b+)/, "\\1")         # これは正解
+'abbbcd'.gsub(/a(b+)/, '\1')          # これも正解
+'abbbcd'.gsub(/a(b+)/, '\\1')         # これも正解
+'abbbcd'.gsub(/a(b+)/) { $1 }         # これも正解
+
+

最初のものは、式展開はgsubの呼び出し前に行われることが間違いです。展開されるのはマッチ前の $1の値です。2番目のものは置換文字列を「"」でくくったため、「\1」が解釈されて「\001」、つまりバイト値の1に解釈されてしまいます。

+
+
+ +

日付を取り出す

+
+

ひとくちに「日付」といってもいろいろなフォーマットがあります。欧米でよく用いられる形式については、正規表現を使うよりもparsedateライブラリを使うほうがよいでしょう。

+
+
require "parsedate"
+
+puts ParseDate.parsedate(str)
+# [年,月,日,時,分,秒,タイムゾーン,曜日]の配列を返す
+
+

しかし、parsedateは日本でよく用いられる「年月日」形式には対応していませんから、それに対応したものを書いてみましょう。

+
+
num = "(?:[0-9]|[0-9])+"
+re = Regexp.new("#{num}年 ?#{num}月 ?#{num}日", 0, "euc")
+p re
+ARGF.each do |line|
+  p line
+  m = re.match(line)
+  puts m[0] if m
+end
+
+

注意すべき点は、日本語文字列の数字には全角と半角の両方があることと、文字コードが違えばマッチしないことです。ここでは全角半角両方の数字にマッチするパターンnumを埋め込むことで、両方に対応し、Regexp.compileの引数として文字コード名 "euc" を指定しています。

+

これで、

+
+
2002年4月8日
+2002年4月8日
+2002年4月8日
+
+

などを日付として認識できます。

+
+
+

HTMLファイルから「URLのようなもの」を取り出す

+
+

HTMLファイルからURLのように見える部分を切り出すと、簡単なリンク集が作れます。以下のスクリプトは引数で指定したファイルからURLのように見える部分を切り出します。

+
+
ARGF.read.scan(/(?:http|ftp):[^ ")]+/) do |match|
+  puts match
+end
+
+
+
+ +

典型的なスペルミスを探す

+
+

文章を編集しているうちに助詞(「てにをは」のこと)が並んでしまうことがよくあります。たとえば、

+
+
シンプルな文法
+
+

という文章の「シンプル」を「簡潔」に変更するときに、まず「シンプル」を削除した後、思わず「簡潔な」と「な」まで含めて入力してしまうと、

+
+
簡潔なな文法
+
+

という文章が残ります。こういうミスをチェックするのが以下のスクリプトです。

+
+
ARGF.each do |line|
+  print ARGF.file.path, " ", 
+        ARGF.file.lineno, ":", 
+        line if line.gsub!(/([あ-ん])\1/e, '[[\&]]')
+end
+
+

このスクリプトは引数に与えたファイルをチェックして、助詞の重なりのように見える部分を[[ ]]でくくって教えてくれます。行の長さの関係で折り返していますが、特に折り返す必要はありません。これを今月の原稿に適用するとこうなります(図12.1)。

+
+
+
200205.rd 32:後者は少々難しいルール[[をを]]表現していますね。この細部の
+200205.rd 59:     日本語では「私[[はは]]」のような助詞の連結のミスが
+200205.rd 177:後説明する「繰り返し」[[とと]]もに使われます。
+200205.rd 202:[[ここ]]で注意すべき点が2つあります。
+200205.rd 208:なく2個の並びにマッチするこ[[とと]]、文字列中でもっとも
+200205.rd 248:で[[くく]]って「(ma)+」とします。
+200205.rd 413:置、部分マッチ(かっこで[[くく]]られた部分)、そのオフセッ
+200205.rd 461:ものを[[ここ]]で解説しましょう。
+200205.rd 510:  簡潔[[なな]]文法
+200205.rd 686:のうち99%くらいはその[[まま]]動作するよう
+200205.rd 728:とはいうも[[のの]]、長い間「いつかやろう」
+200205.rd 729:と思い[[つつ]]、取り掛からなかったことには
+
+

図12.1●実行結果

+
+

32行目に間違いがありますね。直しとかなきゃ。「ここ」とか「そのまま」など間違いでないものも多数引っかかっていますが、原稿を目で見てチェックするのに比べれば、ずいぶん効率的です。

+
+
+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-026.xhtml b/docs/vol1/xhtml/p-026.xhtml new file mode 100644 index 0000000..e4ac1fa --- /dev/null +++ b/docs/vol1/xhtml/p-026.xhtml @@ -0,0 +1,71 @@ + + + + + +第12章 パターンマッチ + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ 知られざるRuby ◆ 文字コード

+
+

今月はページ数の関係で「知られざるRuby」はコンパクト版でお届けします。

+

誰でも使えるASCIIの範囲内だけで表現できる英語はともかく、他の言語、特に日本語を使う場合には文字コードについてなかなか難しい問題があります。

+

文字コードというのは、おおざっぱにいうと文字に対応する数値(の集合)と、それをデータとしてどのように表現するかという決まりです。コンピュータ上ではすべてのデータは数値の羅列で表現されますから、この「決まり」がわからないと正しく文字を扱うことはできません。ときどき見かける「文字化け」という現象は、送り手と受け手で想定している文字コードが違っていることから発生します。

+

日本で広く使われている文字コードは以下の4つです。

+
    +
  • シフトJIS(SJIS)
    +Windows, Macintosh, Palmなど。

  • +
  • EUC-JP
    +UNIXで広く用いられている。

  • +
  • JIS(ISO-2022-JP)
    +電子メール、ネットニューズなど。

  • +
  • UTF-8
    +XMLなど。

  • +
+

「文字コード」は俗称で、正確には、どの文字がどの整数(コードポイント)に対応するかというマッピングである「文字集合」とその数値をバイト列に変換する「符号化方式」の組み合わせになります。

+

ですから、厳密な話をすればシフトJIS, EUC-JP, JISの3つは文字集合はJIS(JIS x0208)で、それぞれ別の符号化方式を採用しているということです。また、UTF-8は符号化方式の名前で文字集合はUnicodeという規格になります。

+

文字集合と符号化方式は独立した概念ではありますが、ばらばらに組み合わせることはないので、あまり厳密な話をしないときにはほとんどの場合、両方を一緒にして文字コードと呼ぶようです。

+
+

Rubyでの文字コードの扱い

+

さて、Rubyの話に戻りましょう。Rubyは上記の文字コードのうち、SJIS, EUC-JP, UTF-8を扱うことができます。Rubyの各種文字コードの扱いは正規表現がそれらの文字コードにおけるマルチバイト文字を文字として扱うということを意味します。

+

文字コードの指定は以下の4つの方法で行います。

+
+
+
+

未来の文字コードの扱い

+

Rubyの文字コードの扱いには以下の制限があります。

+
    +
  • プログラム全体で1つの文字コードしか扱えない

  • +
  • 決められた文字コードしか扱えない

  • +
+

そこで現在Ruby M17Nという複数文字コードを自由に扱うプロジェクトが進行中です。M17Nはmultilingualization(多言語化)のMとNの間に17文字あることから作られた省略形です。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-027.xhtml b/docs/vol1/xhtml/p-027.xhtml new file mode 100644 index 0000000..d1f6439 --- /dev/null +++ b/docs/vol1/xhtml/p-027.xhtml @@ -0,0 +1,79 @@ + + + + + +第12章 パターンマッチ + + + + +

Matz Essays Volume 1

+ + +
+

◆ Ruby開発日記 ◆ Riteと鬼車の話

+
+

実は先日とうとうRite の開発を始めました。Riteというのは、前にも話したような気がしますが、Ruby 2.0のコードネームです。1993年に0.01から始めて、1996年に1.0、そして現在1.6.8までリリースしたのですが(おや、そういえばまだ1.8.0が出ていませんね(苦笑))、だんだん問題が出てきました。

+
+

ソフトウェアの老化

+

皆さん当然ご存じのように、経年劣化によってだんだん傷んでくるハードウェアと違って、ソフトウェアはただ時間が経ったからといって壊れたりはしません。実際、ソフトウェアの寿命は思ったより長くて、たとえばしばらく前に話題になった2000年問題は、ソフトウェアが開発当時には当然リプレースされているだろうと思われた時期になっても生き残っていたことが原因の一部です。

+

ところが、そういうソフトウェアでも老化するのです。ソフトウェアの老化の原因は大きく分けると以下の2つです。

+ +

開発開始以来9年を経過したRubyは、典型的な後者の症状を呈しています。最初から全部の仕様がわかっていればよかったのでしょうが、少しずつ機能を追加して現在まできているので、あちこちがからまって非常に複雑です。いや、全部ではないんですが、何箇所か大変なところがあるんです。

+
+
+

そこでRite

+

いやあ、それもこれも私にもっと将来的な展望とか、プログラマーとしての才能とかがあればよかったのですが、そもそもない袖は振れません。

+

そこで、長年かけてきて「Rubyとはなんぞや」ということがほぼ明らかになった現時点で、改めて再実装することによって老化を解消することを考えました。これがRuby 2.0、通称Riteです。

+

全体を再実装することから、たぶんRuby 2.0は現在のRubyとはかなり変化することになると思います。それで、Riteという別の名前も用意したわけです。変化するとはいっても、現在存在する大量のRubyプログラムのうち99%くらいはそのまま動作するようにしようと思ってはいますが。

+

まあ、とにかく開発を開始したわけです。で、どこから取り掛かろうかと考えた末、まずは正規表現ルーチンから始めようと考えました。正規表現ルーチンはRubyの中でも複雑すぎる(いわば老化した)箇所の1つです。

+
+
+

正規表現ルーチンの問題

+

現在のRubyの正規表現ルーチンはもともとはGNU Emacsに付属していたもので、それを谷本さんという方が(たしかjgawkのために)マルチバイト化され、それを私が大量に手を加えてPerl 5互換の正規表現を追加し、さらに、よしだむさんがUTF-8対応を追加したという代物です。とりあえず十分動いているのですが、少しだけ問題があります。

+
    +
  • 機能追加を重ねて誰も細部を理解していない

  • +
  • よって、バグが発見されても、なかなか直せない

  • +
  • Rubyのソースの中で正規表現ルーチンだけLGPLでライセンスが違う

  • +
+

これらの問題を一気に解決するために、「いつかは正規表現ルーチンを書き換えよう」とずっと思っていたので、Riteにやる気になったこの機会に正規表現から取り掛かろうと考えました。もう1つの老化箇所であるインタプリタのコアの部分は、正規表現ルーチン以上に迷路のような構成になっていますから、それを避けたというものあるのですが。

+
+
+ +

正規表現ルーチンの実装

+

とはいうものの、長い間「いつかやろう」と思いつつ、取り掛からなかったことにはそれなりに理由があったのです。実際に実装する段になって、自分がいかに正規表現について無知であるかについて思い知らされました。

+

もともと正規表現は正則集合という比較的数学に近い概念から発生していることもあって、数学の能力に欠けている私には困難の極みです。

+
+
+

正規表現ルーチンの勝負

+

そんなこんなでぐずぐずしていたら、こっちは半分もできていないのに、ソフネックの小迫さんから「鬼車」なるコードネームの正規表現ルーチンがリリースされてしまいました。この鬼車は

+
    +
  • Rubyの正規表現互換

  • +
  • M17Nにも対応済み

  • +
  • いくつか直していなかったバグが存在しない

  • +
  • 現在の正規表現ルーチンを置き換えるためのRubyへのパッチも提供されている

  • +
  • Rubyにはなかった(難しくて実装できなかった)look behindが実装されている

  • +
+

などなどとてもかないません。素直に負けを宣言します。Riteの正規表現ルーチンはきっとこの「鬼車」ベースになるでしょう。いや、この完成度ならばもっと早く、1.8で置き換えることもできそうです。

+

というわけで、出来かけの私の正規表現ルーチンは永遠に封印されることになりそうです。残念。とはいえ、これで懸案のLGPLの問題も解消され、私は楽しみを失った代わりに、苦労をしなくて済んだのでよかったと思うべきなんでしょう。

+

しょうがないので、私は残るもう1つ、インタプリタコアに挑戦することにしましょう。こっちもぐずぐずしてたら、他の人に負けちゃうかもしれません。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-028.xhtml b/docs/vol1/xhtml/p-028.xhtml new file mode 100755 index 0000000..df77269 --- /dev/null +++ b/docs/vol1/xhtml/p-028.xhtml @@ -0,0 +1,594 @@ + + + + + +第13章 入出力 + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay13 +
+

+初等Ruby講座
+入出力 +

+
+

[Linux magazine, 2002年6月号]

+
+

今回は入出力についてです。Rubyが主にスクリプト言語として使われていた頃は超重要な機能でしたが、Webアプリケーション開発が中心になった現在では、昔ほどは使われませんね。

+

後半は、その超重要な入出力機能を「ちゃんと」実装しようとすると移植性が問題になるという話です。当時は今よりもずっとOSのバリエーションが多く、POSIX規格も充実していなかったので、OSごとの細かな挙動の差が大きかったのです。移植性のある処理系を実装しようとすると、いろいろなところを自前で実装せざるをえません。Rubyは挙動が統一されていない標準ライブラリ関数の一部は再実装しています。現代では、OSの種別はLinuxなどのUNIX系と、かなりUNIXふうのmacOSと、Windowsくらいでほとんどカバーできてしまうので、だいぶ楽になりました。もっとも、macOSはUNIXふうなのに妙なところで独自性を出してくれるし、Windowsは根本的なところでOSの作りが違うので、バリエーションの少なさの割に楽になっていないとも言えます。

+
+
+

普通のプログラムは入力を受け取り、出力を行って初めて意味のある処理を行います。いわば入出力はプログラムの本質に非常に近い部分だといえるでしょう。今月はそのプログラムの重要な要素である入出力について学びます。

+
+
+

入力と出力

+
+

ほとんどのプログラムは入力を受け付けます。入力のないプログラムはごくわずかでしょう。たとえば、乱数によって占いを表示するプログラムなどには入力はないでしょうが、そういうのはごく少数でしょう。

+

プログラムの入力元には以下のようなものがあります。

+
+
    +
  • コンソール入力(キーボード)

  • +
  • ファイル入力

  • +
  • ネットワーク(ソケット)

  • +
  • GUI(ボタン、テキストフィールド、メニュー、etc.)

  • +
+

このうちGUIからの入力を除けば、UNIXおよびその影響を受けたOSでは、入力は「ストリーム」として統一的に操作されます。ストリームとはバイト列(文字列と言い換えてもよいです)を次々読み込むことのできるものです。

+

一方、プログラムの出力先といえば、以下のようなものでしょう。

+
    +
  • コンソール出力

  • +
  • ファイル出力

  • +
  • ネットワーク(ソケット)

  • +
  • GUI

  • +
+

これらも(GUIを除けば)同じストリームで表現できます。出力ストリームは入力ストリームのちょうど逆で、文字列を次々書き出すことができるものです。

+
+
+

ストリーム

+
+

ストリーム(stream)は「流れ」という意味です。文字が次々と送り出されていく様子を表現しているわけです。たとえば、キーボードからの入力を受け、コンソールに出力するということは図13.1のようなイメージになるわけです。

+
+ +
+ fig1301 +
+

図13.1●コンソール入出力

+
+

ストリームは単なる文字の流れですから、ストリームから入力を受け、ストリームに出力するプログラムならいくらでもつなげることができます(図13.2)。

+
+ +
+ fig1302 +
+

図13.2●ストリームの連鎖

+
+ +

このようなストリームに対して入出力を行うプログラムをフィルタと呼びます。流れを加工するイメージですね。また、フィルタプログラムの連鎖を「パイプライン」あるいは省略して「パイプ」と呼びます。

+

ストリームはUNIX系OSでの入出力の基本になります。ストリームのすごいところは、入力元や出力先の種別にかかわらず同じインターフェイスが使えることです。入力しているのが人間であるのか、ファイルであるのか、あるいはネットワークの先のプログラムであるのか、そのようなことにはまったく関係なしにバイトの流れという形式でデータを取り扱えます。なんとなくオブジェクト指向をほうふつとさせます。

+
+
+

標準入出力

+
+

さて、このようなプログラム間でのストリームのつなぎ換えを行うためには、それぞれのプログラムがある種の決まりごとに従う必要があります。その決まりごととは「入出力を標準入出力に対して行う」というものです。

+

標準入出力とはOSが各プログラムのために用意している入出力先です。すべてのプログラムには以下の3つのストリームが用意されています。

+
+

stdin

+

標準の入力元です。通常はキーボードからの入力を受け付けるようになっています。Rubyでは定数STDINで参照することができます。

+

stdout

+

標準の出力先です。通常はコンソール画面に向けられています。Rubyでは定数STDOUTで参照することができます。

+

stderr

+

標準のエラーメッセージの出力先です。標準出力同様、通常はコンソール画面に向けられています。Rubyでは定数STDERRで参照することができます。

+
+

標準入力からデータを受け取り、標準出力にデータを出力するプログラムはフィルタになることができます。フィルタの詳しい作り方についてはのちほど説明します。

+
+
+

IOクラス

+
+

Rubyでストリームを表現するクラスがIOです。まず、IOクラスのオブジェクトが持つメソッドの一覧を表13.1に示します。そのサブクラスとしてファイルとのIOを表現するFileクラスもあります。Fileクラスのオブジェクトが持つメソッドの一覧を表3.2に示します。

+ +
+

表13.1●IOオブジェクトのメソッド

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
メソッド説明メソッド説明
<<writepos=ファイルポインタの設定
binmodeバイナリモードprint出力
closeクローズprintfフォーマット付き出力
close_read読み込みのクローズputc1文字出力
close_write書き込みのクローズputs1行出力
closed?クローズされているかread固定長入力/全読み込み
eacheach_linereadchar1文字入力
each_byte各バイトに対する繰り返しreadline1行入力
each_line各行に対する繰り返しreadlines全ファイルを配列として読み込み
eof?ファイルの終端に達したかreopen再オープン
fcntlfcntl(2)rewindファイルポインタを先頭に移動
filenoファイルディスクリプタ番号seekファイルポインタの移動
flush出力のフラッシュstatファイル情報の取得
getc1文字入力sync自動フラッシュモードの状態
gets1行入力sync=自動フラッシュモードの設定
ioctlioctl(2)sysreadread(2)
lineno行番号syswritewrite(2)
lineno=行番号の設定tellファイルポインタの位置
pidプロセスIDtty?接続先がttyか
posファイルポインタの位置write文字列出力
+
+
+

表13.2●Fileオブジェクトのメソッド

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
メソッド説明
atime最終アクセス時間
chmodモード変更
chownオーナー変更
ctime最終i-node更新時間
flockロック
lstatシンボリックリンク情報
mtime最終更新時間
pathファイルのパス
truncateファイルの切り詰め
+
+

IOにはその他にもネットワークとのアクセスを行うサブクラス(群)があります。IOとそのサブクラスを図13.3に示します。

+
+ +
+ fig1303 +
+

図13.3●IO 関係クラス

+
+
+
+ +

ファイルのオープン

+
+

標準入出力以外のストリームを使うには、ストリームをオープンしてIOオブジェクトを入手する必要があります。ストリームをオープンする基本的なメソッドがopenメソッドです。openメソッドについては、この連載の第1回(2001年11月号)で詳しく取り上げたので、ここでは簡単に説明します。

+

openメソッドは次のように呼び出します。

+
+
open(path[, mode])
+
+

openpathに指定されたファイルをオープンして対応するIOオブジェクトを返します。より正確にはファイルのパスを指定した場合にはIOクラスのサブクラスであるFileクラスのオブジェクトを返すのですが、実用的にはIOオブジェクトだと思って扱ってかまいません。

+

modeには以下のうちのいずれかの文字列を指定します。

+
    +
  • "r" 読み込み用
  • +
  • "w" 書き込み用
  • +
  • "a" 追加書き込み用
  • +
+

モード文字列に "b" を追加するとバイナリモードになります。でも、UNIX系OSでは通常モードとバイナリモードの間に差はありません。Windowsとスクリプトを共有する場合の互換用と考えればよいでしょう。

+

モード文字列に "+" を追加すると、読み書きモードになります。ただし、読み書きモードでは、読み出しと書き出しの間にflushを入れないと正しく入出力されません。

+

openメソッドでは、pathの先頭が "|" であった場合、ファイルではなくプログラムとの入出力になるのですが、そのような特殊な動きを避けたい場合にはFile.openメソッドを使ってください。こちらのメソッドではpathは必ずファイルへのパスと解釈されます。

+
+
+ +

入力メソッド

+
+

まず、一番基本的な入力メソッドは1行入力と1文字入力でしょう。1行入力を行うメソッドはgetsreadlineです。なぜ2つあるのかというと、これらはファイルの終端に達したときの動作が違うのです。getsはファイル終端でnilを返しますが、readlineは例外を返します。ですから、使い方はそれぞれ、

+
+
# getsの場合
+while line = io.gets
+   ... line に対する処理 ...
+end
+
+# readlineの場合
+begin
+  loop do
+    line = io.readline
+    ... line に対する処理 ...
+  end
+rescue EOFError
+  ...終了処理、必要なければ空 ...
+end
+
+

という形になります。多くのRubyユーザーは(短いので)getsを好むようです。

+

1文字入力メソッドにもgetcgetcharの2つがあります。これらも違いはファイル終端で例外を発生するかどうかです。

+

場合によっては、ファイル全体を一度に読み込んできたほうが楽に処理できます。特にファイルのサイズがあまり大きくない場合にはかえって高速になることさえあります。その目的のメソッドがreadlinesです。readlinesはファイルを行単位で読み込んできて、全体を配列に入れて返します。ですから、ファイルの5行目だけがほしければ、

+
+
io.readlines[4]  # 0から始まるので4
+
+

で得られるわけです。このやり方だとファイルの内容を全部読み込んできちゃうんで、ちょっともったいないですけどね。

+

同じファイルの内容を全部読むのでも、行単位ではなく1つの大きな文字列として読み込んできたい場合もあります。その場合にはreadメソッドがあります。

+
+
io.read         # 全体を文字列として返す
+
+

また、readメソッドに引数を指定するとその長さだけ読み込んできます。

+
+
io.read(10)     # 10バイトだけ読む
+
+
+

入力処理にはブロックを使う方法もあります。行単位、文字(バイト)単位の処理をメソッドが用意されています。

+
+
# each_lineは行単位で読み込む
+io.each_line {|line|
+  ... line に対する処理 ...
+}
+
+# eachはeach_lineの別名
+io.each {|line|
+  ... line に対する処理 ...
+}
+
+# each_byteはバイト単位で読み込む
+io.each_byte {|byte|
+  ... byte に対する処理 ...
+}
+
+

最後に、readシステムコールを直接呼ぶメソッドsysreadを紹介しておきます。

+
+
io.sysread(5)   # 5バイト読み込む
+
+

sysreadメソッドはIOオブジェクトが内部で行っているバッファリングなどをいっさい経由しないで直接readシステムコールを呼び出します。ですから、他の入力メソッドとは混在できません。他の入力メソッドを使ったストリームに対してsysreadを行うとエラーになります。

+
+
+

出力メソッド

+
+

出力メソッドも豊富に用意されています。まず、最もよく使われるのがprintメソッドです。printメソッドは引数に与えられたオブジェクトを順に出力します。文字列でないオブジェクトは、それぞれのto_sメソッドを使って文字列化されます。ただし、なぜかnilだけは特別扱いされてnilが出力されます(nil.to_s"" を返すのに)。

+
+
io.print "abc\n"    # => abc<改行>
+io.print nil, "\n"  # => nil<改行>
+
+

書式付き出力にはC言語同様printfを使います。イメージとしてはC言語のfprintfの第1引数がRubyのprintfメソッドのレシーバになるという感じでしょうか。

+
+
# Cのfprintf
+fprintf(stderr, "%d\n", 5);
+
+# Rubyのprintf
+STDERR.printf("%d\n", 5)
+
+
+

1文字出力にはputcを使います。そういえば、Rubyのプログラムではあまりputcが使われているのを見ませんね。1行出力にはputsを使います。putsメソッドは出力の末尾に改行を付けます。

+
+
io.puts("abc")
+# 出力:
+#  abc
+
+io.puts("abc", "def")
+# 出力:
+#  abc
+#  def
+
+io.puts("abc\n")  # 改行は一度だけ
+# 出力:
+#  abc
+
+

出力の中で一番基本となるのがwriteメソッドです。writeメソッドは引数の文字列をそのまま出力します。writeにはC++ファンのために“<<”という別名が用意されています。

+
+
io.write("abc")
+io << "abc"
+
+

最後に、入力にsysreadがあったように、出力にもシステムコールを直接呼び出すsyswriteがあります。sysread同様、syswriteも他の出力メソッドと混在してはいけません。他の出力メソッドを使ったストリームに対してsyswriteを行うと警告を受けます。

+
+
+

クローズ

+
+

オープンしたファイルを使い終わったらクローズします。クローズするにはcloseメソッドを使います。

+
+
io.close
+
+

ただし、IOオブジェクトは、プログラムの終了時と、もうどこからも参照されなくなったときに自動的にクローズされます。

+

では、クローズする必要はないのでしょうか。残念ながらそうはいかない場合もあります。クローズしないとバッファの内容が実際に書き込まれていない可能性があります。フラッシュという操作(あとで説明します)で、バッファの内容を書き出す必要があります。もちろん、クローズしないでフラッシュだけするっていうのもありえますが、それもなんだか気持ち悪いですよね。わざわざフラッシュするくらいならクローズすればよいわけですし。

+

自動的にクローズする方法はもう1つあります。openメソッドはブロックを取ると、ブロックの実行が終了したときにクローズしてくれます。

+
+
+
open(path) do |f|
+  ...
+end
+
+

IOがクローズされているかどうかはclosed? メソッドで判定できます。

+
+
+

バッファリングとフラッシュ

+
+

ストリームへの実際の書き込みは(UNIX系OSでは)writeというシステムコールで行われます。また、読み込みはreadというシステムコールで行われます。

+

これらのシステムコールは実はかなりコストの高い処理なのです。UNIXのようにカーネルとユーザープログラムで異なるメモリ空間で動作しているOSでは、カーネル空間とユーザー空間とでメモリ空間の切り替えが毎回発生しますし、引数となるデータを空間を超えてコピーする必要があります。

+

そこでこのコストのかかるシステムコールの呼び出しを削減する必要があります。それがバッファリングです。読み込みも書き込みも一度バッファを経由します。

+

IOからの読み込みが発生すると、まずバッファに一度に読み込みます。そして個々の読み込みに対して、バッファから少しずつ読み出します。バッファが空になるとまた一度に読み込みを行います。これによってreadシステムコールの削減を行うことができるわけです。

+

書き込みの場合には個々の書き込みをバッファに書き込みます。バッファからの実際の書き込みはある程度まとめて「適当な」タイミングで行われます。

+

読み込みは必要に応じて、OSから読んでくるのであまり問題が起きにくいのですが、書き込みのほうは「適当な」タイミングが予想と食い違う場合がありえます。書き出したはずのデータが届いてないということもありえるわけです。そこでバッファの内容を書き出す方法も提供されています。

+

バッファの内容を明示的に書き出すためには、2つの方法があります。1つはIOをクローズすることです。ただ、クローズしてしまうと(当然ですが)これ以上の書き込みはできません。もう1つの方法はflushメソッドを使うことです。

+
+
io.print "abc"  # バッファへの追加
+io.flush        # バッファの内容を書き出し
+
+
+
+

ランダムアクセス

+
+

ストリームは順に読み込んでくるだけですから、シーケンシャル(順次)アクセスと呼ばれます。しかし、ときどきファイルの場所を指定して読みたいケースもあります。

+

各ストリームには現在読み込んでいる場所を示す「ファイルポインタ」があります。そのポインタを操作することで、読み込み位置を移動させてファイルをランダムアクセスすることができます。

+

現在のファイルポインタを取得するためにはtellメソッド、またはposメソッドを使います。

+
+
io.tell   # 現在のファイルポインタ
+io.pos    # 同上
+
+
+

特定の位置にファイルポインタを移動させるためには、pos= メソッド、またはseekメソッドを使います。

+
+
p = io.pos    # ファイルポインタのセーブ
+... ioに対する処理 ...
+io.pos = p    # pos=メソッドの呼び出し
+              # 元の位置への復旧
+
+

seekメソッドはpos= メソッドと同じように使えますが、第2引数として、移動の基点を指定できます。移動の基点は定数で指定し、その意味は、

+
+
IO::SEEK_SET  先頭から
+IO::SEEK_CUR  現在位置から
+IO::SEEK_END  ファイル末尾から
+
+

になります。

+

ファイルポインタを先頭に移動させるためにはrewindメソッドを使います。

+
+
io.rewind                # 先頭へ巻き戻し
+io.pos=0                 # 同じ意味
+io.seek(0)               # 同じ意味
+io.seek(0,IO::SEEK_SET)  # 同じ意味
+
+

ランダムアクセスの利用例はあまり見かけないので、実例を紹介しておきましょう。以下のプログラムはn番目の固定長レコードの内容を変更します。レコードサイズは定数RECORD_SIZEに定義されているとします。

+
+
io.fseek(n*RECORD_SIZE)
+rec = io.read(RECORD_SIZE)
+... recの内容を修正; サイズを変えないこと ...
+io.fseek(n*RECORD_SIZE)
+io.print rec    # recを出力
+io.close        # ファイルをクローズ
+
+
+
+

フィルタ

+
+

さて、先に標準入力から読み込み、標準入力に書き出すプログラムはフィルタと呼ばれると書きました。フィルタというのはPerl以前AWKやシェルの頃からスクリプト言語の重要な適用分野でした。ここでフィルタについて少々詳しく説明しておきましょう。

+

UNIXにおいては、ほとんどのフィルタは以下の性質を持っています。

+
+

たとえば、フィルタの代表格catは、

+
+
cat
+
+

では、標準入力から読み込み、標準出力に書き出します(だから、あまり意味がない)。複数の引数を指定すると、

+
+
cat foo bar baz
+
+

それらのファイルの内容を順番に標準出力に書き出します。オプションを付けると、ちょっとだけ動作が変化します。

+
+
cat -n foo bar baz
+
+

で行番号を付けて出力します。

+

Rubyではこのようなフィルタの性質を支援する機能が提供されています。それがARGFです。ARGFは「引数で指定したファイルから構成される仮想的なファイル」です。引数が指定されなかったときはARGFは標準入力から読み込みます。ほら、フィルタの性質をそのまま実現してくれてますよね。

+

ARGFは厳密にはIOクラスのオブジェクトではありませんが、IOクラスのメソッドのほとんどを持っています。先ほどのcatの動作は、

+
+
ARGF.each{|line| print line}
+
+

で実現できます。行番号を付けたければ、

+
+
ARGF.each{|line| printf "%6d  %s", ARGF.lineno, line}
+
+

です。簡単でしょう?

+
+
+

ファイルのロック

+
+

複数のプロセスが同時に同じファイルにアクセスしようとする場合、トラブルが発生する場合があります。図13.4に示したのは典型的なWebのカウンタの処理です。

+
+ +
+ fig1304 +
+

図13.4●Webカウンタの処理と競合

+
+

カウンタプログラムはCGIとして起動されますから、複数同時に起動されることもありえます。さて、ここで仮にカウンタファイルには数値「10」が書き込まれているとしましょう。カウンタプログラムが起動され、(b) の処理で10が読み込まれます。さて、この瞬間に誰か別の人がこのページにアクセスしカウンタプログラムが起動されたとすると、最初のプログラムが (e) の処理で11を書き込む前に、こちらも10を読み込んでしまう可能性があります (h)。そうすると後のプログラムは前のプログラムが書き込んだ11を上書きしてしまい (k)、最初のアクセスはなかったことになってしまいます。

+

この問題を避けるためには、数値を読み込んでから新しい値を書き込む間は、他のプログラムに読み込みを待ってもらう必要があります。それを実現するのがflockメソッドです。

+ +

Fileクラスのflockメソッドを実行すると、そのファイルがロックされます。ロックされたファイルを別のプログラムがロックしようとすると、ロックが解放されるまでそのプログラムは停止します。

+
+
f = open("/var/lib/www/count.dat", "r+")
+f.flock(File::LOCK_EX)  # ロック
+n = f.read.to_i + 1     # 読み込みと1増加
+f.rewind                # 先頭に巻き戻し
+f.print n               # 出力
+f.flock(File::LOCK_UN)  # アンロック
+
+

これで上記の問題は解決です。

+
+
+

アクセス権

+
+

さて、ここまでで基本的な入出力操作は説明してきましたが、最後にファイルのもう1つの性質であるアクセス権について説明しておきましょう。

+

まず、各ファイルにはオーナー(所有者)とグループ(所属グループ)という属性があります。それから、誰がアクセスできるかというアクセス権という属性があります。

+

アクセス権は、ファイルにアクセスするユーザーを、そのファイルの所有者、そのファイルのグループに所属するユーザー、それ以外のユーザーの3種類に分類し、それぞれに対して、読める、書ける、実行できるの3種類の操作に対して許可したり、禁止したりできます。たとえば、あるファイルを読み出すためには、ファイルのアクセス権について以下の条件のいずれかが成立している必要があります。

+
    +
  • オーナーに対する読み出し権が設定されていて、自分がそのファイルのオーナーである

  • +
  • グループに対する読み出し権が設定されていて、自分がそのファイルのグループに所属している

  • +
  • その他のユーザーに対する読み出し権が設定されている

  • +
+
+

アクセス権を設定できるのは3種類のユーザーに対してそれぞれ3種類の操作なので3×3で9通りの許可があります。この許可状態を表現するために、

+
+
rwxr-x---
+
+

のように表現することがあります(たとえばlsの出力)。これは3文字ずつグループ化して、オーナーに対しては読み込み(r)、書き込み(w)、実行(x)のそれぞれを許可するが、グループのメンバーに対しては読み込みと実行だけ、それ以外のユーザーに対しては何も許可しないというふうに読みます。

+

よりコンパクトな表現として、8進数を使って同じ許可状態を、

+
+
750
+
+

とも表現します。これは8進数の数字1桁が3ビットであることを利用しています。1番目の数字はオーナーに対するアクセス権を意味します。7は2進数でいえば111でこれはすべての操作が許可されていることを示します。2番目の数字5は2進数で101で、これはグループのメンバーに対して読み込みと実行だけが許可されていることを示します。3番目の数字0は当然000ですからその他のユーザーに対してはすべての操作が禁止されていることを示します。

+

Rubyにおいてアクセス権を調べたり、変更したりするメソッドは以下のとおりです。

+
+
stat = f.stat   # アクセス権を含む属性を得る
+stat.mode       # アクセス権
+stat.readable?  # 読み込めるか
+stat.owned?     # 自分のファイルか
+f.chmod(0750)   # アクセス権を変更
+
+

誰からでも書き込みできるファイルは信頼できないユーザーによって書き換えられているかもしれません。そこで、重要な情報を読み込むときにはそのファイルのアクセス権が安全かどうかをチェックするとよいでしょう。

+
+
# pathからファイル情報を得る
+stat = File.stat(path)
+# 以下の条件のいずれかが成立するときには安全でない
+if !stat.owned? or  # 自分のファイルでない、
+   stat.mode & 022  # 自分以外から書き込み可
+
+   ... 安全でないのでエラーにする ...
+end
+
+
+
+

まとめ

+
+

今月は入出力を司るIOクラスについて説明しました。だんだんRubyプログラミングの基礎部分についての解説が進んできました。そろそろ、応用的プログラミングについても説明できるかもしれません。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-029.xhtml b/docs/vol1/xhtml/p-029.xhtml new file mode 100644 index 0000000..12ef392 --- /dev/null +++ b/docs/vol1/xhtml/p-029.xhtml @@ -0,0 +1,29 @@ + + + + + +第13章 入出力 + + + + +

Matz Essays Volume 1

+ + +
+

◆ 知られざるRuby ◆

+
+

今月の「知られざるRuby」はお休みです。

+
+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-030.xhtml b/docs/vol1/xhtml/p-030.xhtml new file mode 100644 index 0000000..1eb546d --- /dev/null +++ b/docs/vol1/xhtml/p-030.xhtml @@ -0,0 +1,67 @@ + + + + + +第13章 入出力 + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ stdioの憂鬱ゆううつ

+
+

今回取り上げたIOクラスはstdioをベースに実装されています。stdioはC言語の規格で定義された入出力ですから、これを使っていればC言語のフルセットが使えるところではどこでも動作するわけです。組み込み環境などではstdioが提供されない場合もありますが、それはしょうがない話です。

+

ところが、なかなか世の中は甘くないんですよね。Rubyのような複雑なソフトウェアを実現しようとなるとstdioにもいろいろな問題があるのです。

+

まず第一に問題になるのが機能が足りないという点です。Rubyではユーザーレベルスレッド機能を提供していますが、そのためにIOのバッファに読み込まれたデータがあるかどうかを判定する必要があります。ところがこの機能はstdioには定義されていません。しかたがないので、Rubyではプラットフォームごとにバッファが空かどうかを判定するルーチンを用意しています。

+

次の問題が、OSごとに微妙に動作が異なる点です。たとえば、読み込みと書き込み両方のモード("r+"など)でオープンされたIOに対して、読み込みと書き込みの間にflushを行わないと問題があるOS(HP-UXとか)と問題ないOS(Linuxとか)があるのです。どこでもできるだけ同じように動作してほしいスクリプト言語としてはできるだけ動作をそろえたいところです。

+

最後の問題が、これも動作の違いの一部ですが、ロケール(国際化)の対応がまちまちだということです。Ruby自身はロケールを使っていないので、実はロケール対応はないほうがよいのですが、なかなかそうもいきません。たとえばヨーロッパの一部では小数点に","を使い、数字の3桁ごとの区切りに"."を使います。日本やアメリカのちょうど反対ですね。

+
+
printf "%f\n", 1000.5
+
+

を実行して

+
+
1.000,5
+
+

などと書かれても、動揺してしまいます。

+
+ +

fnmatchの悪夢

+

このような問題はstdio以外にもたくさんあります。その一例はfnmatchです。fnmatchというのはワイルドカードのマッチングを行うライブラリ関数ですが、実はこの関数の挙動はOSごとに微妙に違います。たとえばSolarisでは[^T]のようなTでない文字を表現するパターンが使えません。

+

そこでとうとうRubyでは、わたなべひろふみさん(ebanさん)作のfnmatchを自分で持つことにしました。

+

これで、どのOSでも同じ挙動のワイルドカードマッチが使えてハッピーなのですが、なんとなくこのままほとんどの関数について独自に提供していくことになってしまうのではないだろうか、と考えると少々恐くなってしまうのです。

+
+
+

移植性の実現

+

UNIX系OSの宿命として、ソフトウェアの移植性というのは重要な課題でした。UNIXとひとくちにいってもベンダーが違えばCPUもまちまちでしたから、コンパイルしたバイナリを配布するというわけにはいきません。そこでソフトウェアの配布は当然のようにソースコードが中心になりました。個人的にはUNIXでフリーソフトウエアあるいはオープンソースソフトウェアが発展した理由の1つは、このソースコードでの配布ではないかと思っています。いや、それともフリーソフトウエアだからこそソースコードで配布できたと考えるべきかな。ニワトリが先か、タマゴが先か。

+

それはともかく、ソースコードで配れば後は解決というわけにはいかないのが悲しいところです。OSごとにライブラリ関数があったりなかったり、名前が違ったり、あるいは最悪のケースでは同じ名前でも動作が違ったりするのです。結局は個別対応するしかありません。BSDではこの関数はある、SunOSでは名前が違うとか。

+
+
+

autoconfの光明

+

そこにさっそうと登場したのはautoconfというツールです。autoconfというのは、OSごとの違いを検出するプログラムを自動生成してくれるツールです。今まではOSの名前を見てチェックする必要があった部分を、実際にチェックを行って、この関数があれば使い、なければ別の対応を行う、といった書き方ができるようになったのです。

+

これまでのやり方では新しいOSへの対応はゼロから始める必要がありましたが、autoconfを使えば、検出プログラム(シェルで書かれています)を走らせることさえできれば、まったく新しいOSでもそのままコンパイルできる可能性が格段に高くなったのです。ちょっと幸せ。

+

というわけで、autoconfのおかげで少しだけ幸せになった移植性プログラミングなのですが、チェックする項目が減ったわけではありません。autoconfの入力となるconfigure.inというファイルがあるのですが、Rubyの場合全部で1155行あります。テスト項目はLinuxにおいて216項目にのぼります。テストはOSのチェックからヘッダーファイル、関数の存在チェック、構造体のメンバーチェックなどさまざまです。

+

こんなにテストするのーって感じですが、Rubyのような言語の場合しかたがないのかもしれません。結局はOSのほとんどの機能を呼び出しているのですから。

+
+
+ +

マルチプラットフォームのいばらの道

+

しかし、RubyはUNIX系OSだけで動作するわけではありません。WindowsやBeOS、OS/2でも動作します。また最近VMS対応が追加されました。X68000対応はずいぶん以前からあるのですが、まだ動くのでしょうか。このような私自身が使ったこともないOSにおいては、ユーザーからのフィードバックがすべてです。RubyがここまでいろいろなOSの上で動くのも皆さんのおかげなのです。みなさま、どうもありがとうございます。

+

みなさまのおかげで私はLinuxでの開発に専念できます。そうです。私はLinuxしか使っていません。私の持ってるPCはどれもLinuxしか走っていません。ときどきWordファイルを送りつけられて困っていたのですが、OpenOfficeのおかげでなんとかなりそうです。たまにはいいこともあるのね。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-031.xhtml b/docs/vol1/xhtml/p-031.xhtml new file mode 100644 index 0000000..5fc2efd --- /dev/null +++ b/docs/vol1/xhtml/p-031.xhtml @@ -0,0 +1,704 @@ + + + + + +第14章 数と電卓 + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay14 +
+

+初等Ruby講座
+数と電卓 +

+
+

[Linux magazine, 2002年7月号]

+
+

Rubyの数クラスについてです。ご存じの方もいらっしゃるでしょうが、私は数学が大変苦手です。ですから、数値計算関係の実装をするときには大変困ります。幸い、Rubyコミュニティには数学が得意な人がたくさんいるので、みなさんの助けを借りてなんとか実装しています。これは20年前も今も変わらない点です。Rubyの数クラスで自慢したい点は、coerceシステムです。Rubyの他のクラスと比べて、数クラスに特徴的なことは、クラスごとにカバーしている範囲が異なることと、混合した演算が許されていることです。既知のクラスだけならばあまり問題にならないのですが、新しい数クラスを導入したときに、既存の数クラスとの相互変換を簡潔に扱うことは困難な課題です。coerceはそれをきれいに表現できる仕組みです。普通のユーザーは新しい数クラスを定義することはありませんし、めったに見ることのない部分ではありますが、Rubyではこんな目立たない部分でも一生懸命設計されている1つの例です。coerceシステムは、書籍『オブジェクト指向スクリプト言語Ruby』の共著者である石塚圭樹のアイデアに基づいて設計されました。

+

この章では、日本語入力システムについても紹介しています。私の使っている「きゅうり改」ですが、20年経った今でも使っています。ただし、当時利用していた日本語変換システム「かんな」は使えなくなってしまいましたので、今は「fcitx + mozc」で日本語入力しています。他にユーザーはいないようですが。雑誌掲載時にはCD-ROMに設定ファイルを収録してもらいましたが、今回はクラウドへのリンクを用意しました。以下のURLからmozc.tblというファイルをダウンロードし、mozcのローマ字テーブルとして読み込むと、mozcで「きゅうり改」が使えるようになります。

+
    +
  • https://tinyurl.com/3e244k9r

  • +
+
+
+

コンピュータの本来の意味は「計算機」で、数の計算は得意中の得意です。今月はRubyにおける数の扱いを見てみましょう。

+
+
+ +

はじめに

+
+

最近、コンピュータを何に使ってますか。私の場合は、メールの読み書きやWebのブラウズ、プログラミング、それから忘れちゃいけない原稿書きが主な使い道です。おかげで、ときどきコンピュータが計算機であることを忘れて、計算するために電卓を探したりすることさえあります。かっこわるい。

+

Rubyも計算機上のプログラミング言語ですから、もちろん数の扱いは得意です。Rubyの数は図14.1のようなクラスで表現されます。

+
+ +
+ fig1401 +
+

図14.1●数のクラス階層

+
+

図14.1を見ると、クラス階層が浅めのRubyにしては継承が活用されているのがわかります。これは歴史的に数学の世界が数の構成について一種の継承のような分類を行ってきたことをある程度反映しています。数には整数(Integer)があり、それとは別に小数点以下も表現できる浮動小数点数(Float)もあります。

+

整数には一定の範囲内(2の30乗程度まで)の整数を表現するFixnumクラスと、それ以上の数を表現するBignumクラスとがありますが、これはむしろ実装の都合で、普段は気にする必要はありません。Rubyは整数の値に応じて勝手に変換してくれます。

+

ですから、たとえば2の100乗なども簡単に計算できます。

+
+
puts 2**100  # => 1267650600228229401496703205376
+
+

浮動小数点数は小数点以下も表現できます。1.5とか3.14とかですね。算数で実数と呼んでいるものに相当します。ただし、コンピュータの中では2進法で計算していたり、有効な桁数に限界があったりする関係で、誤差が発生します。電卓で1÷3×3が0.99999999になったりするようなものです。浮動小数点数を扱うときにはいつも誤差に気を付ける必要があります。絶対にしてはいけないことは、2つのFloatを「==」演算子で比較することです。同じように見えてもほんのわずかのずれで同じと判定されないことがあります。2つのFloatの差が一定以下かどうかという判定を使うか、いっそ「<」や「>」を使った別の条件に変える必要があります。

+
+
+

数の表現

+
+

Rubyのプログラムの中での数値の表現はいくつもあります(表14.1)。

+ +
+

表14.1●数値表現

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
名称
整数(10進)12341234
2進整数0b101111
8進整数01234668
16進整数0x12344660
浮動小数点数1.2341.234
指数表示浮動小数点数1.23e31230.0(1.23×10の3乗)
文字コード?a97('a'のASCIIコード)
コントロールコード?\C-a1(Control-Aのコード)
メタコード?\M-a225(aのコード97|0x80
コントロールメタコード?\C-?\M-a129(Control-Aのコード1|0x80
+
+

数値表現の数の並びにはアンダースコア('_')を含めることができます。たとえば、読みやすさのために整数を3桁ごとに区切る目的に使えます。

+
+
1_234_567   # => 1234567
+
+
+
+

算術演算

+
+

数に対する小学生の頃からおなじみの演算はもちろん提供されています。他のプログラミング言語でもだいたい同じですが、「×」の代わりに「*」が、「÷」の代わりに「/」が使われます。あと、剰余(余り)を求める演算子「%」と、べき乗を求める演算子「**」があります。

+
+
p 1 + 1   # => 2
+p 3 - 2   # => 1
+p 2 * 4   # => 8
+p 6 / 3   # => 3
+p 5 % 2   # => 1
+p 3**10   # => 59049
+
+

べき乗に「^」演算子を使う言語もありますが、Rubyでは「^」は別の意味を持ちますから、注意してください。

+

整数同士の算術演算の結果は整数、両辺いずれか(あるいは両方)が浮動小数点数である算術演算の結果は浮動小数点数です。整数同士の演算の場合、割り算の結果も整数になることに注意してください。ですから、

+
+
p 5 / 2     # => 2
+
+

になり、2.5にはなりません。2.5がほしければ、

+
+
p 5 / 2.0   # => 2.5
+
+

とする必要があります。

+

余りについても少々追加説明が必要です。余りを求めるメソッドは実は4つあります。

+
+
    +
  • x.divmod(y)    xyの商と余りの両方を返します
  • +
  • x % y          xyの余り(modulo)を返します
  • +
  • x.modulo(y)    「%」演算子と同じです。
  • +
  • x.remainder(y) xyの余り(remainder)を返します
  • +
+

divmodメソッドの返す商(q)と余り(r)の間には、

+
+
x = y * q + r   (ただしrの符号はyの符号と等しい)
+
+

という関係があります。「%」演算子とx.modulo(y)は、x.divmod(y)の余りの部分だけを返します。

+

一方、remainderrの符号がxと等しい余りを返します。ですから、remaindermoduloは負の数を含む演算で違いが現れます。

+
+
(13.modulo(4))         # =>  1
+(13.remainder(4))      # =>  1
+(13.modulo(-4))        # => -3
+(13.remainder(-4))     # =>  1
+((-13).modulo(4))      # => -3
+((-13).remainder(4))   # => -1
+((-13).modulo(-4))     # => -1
+((-13).remainder(-4))  # => -1
+
+
+
+

ビット演算

+
+

コンピュータは数を01の並び(ビット列)として扱います。たとえば、整数の5は101というビット列で表現されています。このビット列に対する操作は以下のものが用意されています。

+
    +
  • |  ビットor
  • +
  • &  ビットand
  • +
  • ^  ビットxor
  • +
  • << 左ビットシフト
  • +
  • >> 右ビットシフト
  • +
  • ~  ビット反転
  • +
+

ビット単位での論理和を求めるのがビットorで、ビット単位での論理積がビットandです。

+
+
0b101 | 0b1100
+
+

は2進数での各桁(ビット)ごとにいずれかが1であるビットが1になり、結果は0b1101(10進では13)になります。両辺のビット列の長さが違う場合には右端でそろえて演算し、足りないビットは0であるとみなします(正の数の場合)。

+

一方、ビットandでは両方ともが1であるビットだけが1になり、

+
+
0b101 & 0b1100
+
+

の結果は0b100(つまり10進の4)になります。

+
+

xorは排他的論理和(exclusive or)とも呼ばれて、両辺のビットのいずれか一方だけが1のとき1になります。

+
+
0b101 ^ 0b1100
+
+

の結果は0b1001(つまり10進の9)になります。これらの論理演算のビットの組み合わせを表14.2に示します。

+
+

表14.2●ビット論理演算

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
xyx | y (or)x & y (and)x ^ y (xor)
00000
01101
10101
11110
+
+

左ビットシフトはビット列を左側にずらします。ずらした右側にはゼロが入れられます。ですから

+
+
0b101 << 1
+
+

は、0b101が1ビットずつ左にずらされて0b1010になり、10進では10になります。整数をnビット左にシフトすることは、その整数を2のn乗倍することと同じ意味を持ちます。

+

逆に右側にシフトするのが右ビットシフトです。

+
+
0b101 >> 1
+
+

0b101が1ビットずつ右にずらされて0b10になります。はみだしたビットは捨てられます。整数をnビット右にシフトすることは、その整数を2のn乗で割ることと同じ意味を持ちます。

+

ビット列として整数を見るとき、負の数は「2の補数」で表現されていると考えることができます。「2の補数」とはコンピュータの世界での伝統的な負の数の表現方式の1つで「ビット反転して+1」した値のことです。考えやすいので、まずは整数のサイズに制限のある場合から考えましょう。整数のサイズが16ビットだったとして、0に対する2の補数は、まず0のビット反転である0xffffを計算し、それに1を足します。するとその結果はやはり0になります。0には +0 も −0 もありませんから、これはこれでよいわけです。同様に−1は0xffff, −2は0xfffeになります。

+

歴史的には「1の補数」という別の負の数の表現方式が使われていたこともあるそうです。これは単純に整数をビット反転したものです。この方式では +0 と −0 ができてしまいますね。私は実際に1の補数を使ったコンピュータを見たことはありません。

+

すでに説明したようにRubyにはBignumがありますので、整数のサイズは制限がありません。C言語では整数をビット列として扱う場合には、整数型のサイズ(16ビットとか32ビットとか64ビットとか)に制限されますが、Rubyでは100ビットだろうが1000ビットだろうが表現できます。

+
+
1 << 1000  # 1以下999ビット0が続くビット列
+
+

ですから、Rubyの整数をビット列として考えると、理論的には正の数の場合には左側に無限に0が続くと考えることができます。負の数の場合には逆に無限に1が続くことになります。まあ、実際に無限に0なり1なりが並んでいるわけではありませんが。

+

最後にビット反転はビット1の桁を0にビット0の桁を1に置き換えた数を返します。

+
+
~0    # => -1
+~1    # => -2
+
+

nのビット反転は (−n−1) と同じ意味になります。

+
+
+ +

文字列化

+
+

数値を文字列にするためにはto_sメソッドを使います。

+
+
puts 10.to_s  # => 10
+
+

putsのような出力メソッドは内部的にto_sを呼びますから、直接出力しても同じことです。

+
+
puts 10  # => 10
+
+

Rubyは普段使われる10進数の他に2進数、8進数、16進数を扱うことができます。ある数を10進以外で出力するためにはprintfを使います。また文字列化するためにはsprintfを使います(表14.3)。

+
+
a = 3456
+printf("%b\n", a)  # => 110110000000
+printf("%o\n", a)  # => 6600
+printf("%d\n", a)  # => 3456
+printf("%x\n", a)  # => d80
+printf("%X\n", a)  # => D80
+
+
+

表14.3●printfの基数指定

+ + + + + + + + + + + + + + + + + + + + + + + + + +
表記説明
%b2進数(binary)
%o8進数(octal)
%d10進数(decimal)
%x16進数(hexadecimal)
%X16進数, ただしA-Fは大文字
+
+

Ruby 1.7ではIntegerクラスのto_sメソッドが引数に基数を指定できます(基数は2, 8, 16が有効)。

+
+
a = 3456
+p a.to_s(2)     # => 110110000000
+p a.to_s(8)     # => 6600
+p a.to_s(10)    # => 3456
+p a.to_s(16)    # => d80
+
+
+
+

文字列からの変換

+
+

逆に文字列を数値化するためには、to_iメソッドまたはIntegerメソッドを使います。

+
+
puts "10".to_i      # => 10
+puts Integer("10")  # => 10
+
+

to_iメソッドとIntegerメソッドの違いは、そのスタイル以外にも、

+
    +
  • to_iは数値として解釈できない文字列に対して0を返す。Integerはエラーにする

  • +
  • Integerは数値の前のプリフィックス(0xとか0bとか)を解釈する。to_iは解釈しない

  • +
+

という違いがあります。

+

ですから、10進以外の数値表現から数値を得るためには、先頭に基数を表すプリフィックスを付けてIntegerメソッドを使います(8進数の場合は先頭に“0”を付ける)。

+ +
+
Integer("0b110110000000")  # => 3456
+Integer("06600")           # => 3456
+Integer("0xd80")           # => 3456
+Integer("0XD80")           # => 3456
+
+

Ruby 1.7ではStringクラスのto_iメソッドの引数に基数を指定できます(基数は2, 8, 16が有効)。

+
+
p "110110000000".to_i(2)  # => 3456
+p "6600".to_i(8)          # => 3456
+p "d80".to_i(16)          # => 3456
+p "D80".to_i(16)          # => 3456
+
+
+
+

数値同士の変換

+
+

Integerメソッドやto_iメソッドは浮動小数点数を整数に変換するためにも使えます。

+
+
p Integer(1.5)   # =>  1
+p 1.5.to_i       # =>  1
+p Integer(-1.5)  # => -1
+p -1.5.to_i      # =>  1
+
+

これらは小数点以下を切り捨てます。小数点以下を四捨五入するためにはroundメソッドを使います。

+
+
p 1.2.round      # =>  1
+p 1.6.round      # =>  2
+p -1.2.round     # => -1
+p -1.6.round     # => -2
+
+

その他にも繰り上げて整数化するceil、繰り下げて整数化するfloorがあります。

+
+
p 1.2.ceil       # =>  2
+p -1.2.ceil      # => -1
+p 1.2.floor      # =>  1
+p -1.2.floor     # => -2
+
+

これらのメソッドは浮動小数点数を整数化するためのものですが、逆に浮動小数点数に変換するためにはto_fメソッドまたはFloatメソッドを使います。

+
+
p 5.to_f         # => 5.0
+p Float(5)       # => 5.0
+
+

これらのメソッドは文字列に対しても有効です。

+
+
p "5.5".to_f     # => 5.5
+p Float("5.5")   # => 5.5
+
+

Integerto_iと同様に

+
+
    +
  • to_fは数値として解釈できない文字列に対して0.0を返す。Floatはエラーにする

  • +
+

という違いがあります。

+
+
+

繰り返し

+
+

たとえば熟練したCプログラマーであれば、10回繰り返したいと思えば、一瞬のうちに、

+
+
for (i=0; i<10; i++) {
+  ...
+}
+
+

という表現が頭に浮かぶかもしれません。しかし、やりたいこと(10回繰り返したい)と、実際の表現の意味にはかなりレベルの差があります。上記のCを解釈するとこうです。

+
    +
  • 変数iをまず0に初期化し、

  • +
  • それからi<10が成立するまで繰り返す、

  • +
  • 毎回の繰り返しの直後にはi++を実行する

  • +
+

ところがRubyであればこうなります。

+
+
10.times {
+  ...
+}
+
+

まさに「10回繰り返す」そのものです。直接的ですね。このtimesメソッドは整数クラスに用意されているループ用のメソッドです。このようなループ用メソッドは「イテレータ(繰り返し子)」とも呼ばれて、Rubyにはいくつも用意されています。

+

数のクラスにある繰り返し用メソッドは以下のとおりです(n, m, stepはそれぞれ数とします)。

+
+
# n回繰り返す
+10.times {
+  ...
+}
+
+# 1増やしながら繰り返す(終端を含む)
+4.upto(7) {|i|
+  p i     # 4,5,6,7を出力
+}
+
+# 1減らしながら繰り返す(終端を含む)
+4.downto(2) {|i|
+  p i     # 4,3,2を出力
+}
+
+# step数を加えながら終端を超えるまで繰り返す
+4.step(10, 2) {|i|
+  p i     # 4,6,8,10を出力
+}
+
+
+
+ +

数学関数(Math)

+
+

数、特に浮動小数点数に対してLinux(UNIX)ではlibmが提供する数学関数を使うことができます。これらの関数はMathモジュールが提供しています。これらの関数の使い方は、

+
+
p Math.sqrt(2)  # => 1.414213562
+
+

のようにMathモジュールを明示的に指定して呼び出すか、Mathモジュールをインクルードして、

+
+
include Math
+p sqrt(2)       # => 1.414213562
+
+

のように使うかのいずれかです。Mathモジュールが提供する数学関数を表14.4に示します。

+
+

表14.4●Mathモジュールの数学関数(Ruby 1.6)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
関数説明
atan2(x,y)x/yのアークタンジェント(逆正接)
cos(x)xのコサイン(余弦)
exp(x)xの指数関数
frexp(x)xの指数部と仮数部の配列
ldexp(x,e)xの2のe乗をかけた数
log(x)xの自然対数
log10(x)xの常用対数
sin(x)xのサイン(正弦)
sqrt(x)xの平方根
tan(x)xのタンジェント(正接)
+
+

三角関数のxはラジアンで表現されます。

+

その他にMathモジュールでは定数として、円周率PIと自然対数の底Eも定義されています。

+
p Math::PI    # => 3.141592654
+p Math::E     # => 2.718281828
+

Ruby 1.7ではMathモジュールの提供する数学関数が増えています。1.7で追加された数学関数を表14.5に示します。

+ +
+

表14.5●Ruby 1.7で追加された数学関数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
関数説明
acos(x)xのアークコサイン(逆余弦)
acosh(x)xのアークハイパボリックコサイン(逆双曲線余弦)
asin(x)xのアークサイン(逆正弦)
asinh(x)xのアークハイパボリックサイン(逆双曲線正弦)
atan(x)xのアークタンジェント(逆正接)
atanh(x)xのアークハイパボリックタンジェント(逆双曲線正接)
cosh(x)xのハイパボリックコサイン(双曲線余弦)
hypot(x,y)原点と点(x, y)の距離
sinh(x)xのハイパボリックサイン(双曲線正弦)
tanh(x)xのハイパボリックタンジェント(双曲線正接)
+
+

私自身はこれらの数学関数を使ったことはほとんどありませんし、「アークハイパボリックコサイン」など、正直なところそれが何を意味するのかもよくわからないのですが、使う人は使うのでしょう、きっと。

+
+
+

乱数

+
+

ランダムな数がほしい場合があります。一番ありそうなケースはゲームを作る場合でしょう。

+

Rubyで乱数を得るためにはrandメソッドを使います。randメソッドの呼び出し方は2種類あります。randメソッドに整数の引数を指定すると、その数よりも小さい整数の乱数を返します。

+
+
rand(10)  # 0から9までの乱数
+
+

randメソッドに引数を与えないと(あるいは0を与えると)、0.0から1.0の間の浮動小数点数の乱数を返します。この乱数は0.0を含み、1.0を含まない範囲になります。

+

Rubyが提供する乱数はある種の「擬似乱数」にしかすぎません。擬似乱数はある「種」となる数値を初期値として計算される、一見ばらばらに見える数列です。ですから、同じ種を与えれば「再現性のある乱数」というなんとなく矛盾したものが得られるわけです。乱数の種はsrandメソッドで指定します。

+
+
srand(0)  # 「種」の指定
+p rand    # => 0.170828036 (いつも同じ「乱数」)
+p rand    # => 0.7499019805(いつも同じ)
+
+

LinuxでないOSでは実際の乱数の値は上記とは違うかもしれませんが、何度やっても同じ乱数が得られるという点は変わりません。

+

srandを明示的に呼び出さなかった場合には、Rubyのほうで「適当な」種を用意してくれます。

+
+
+ +

有理数と複素数

+
+

Rubyは非常に拡張性のある言語なので、自分で数の階層に新しいクラスを追加することができます。そのような例としては有理数(rational)と複素数(complex)をあげておきます。

+

rationalライブラリはRubyで記述されたライブラリで、有理数クラスを定義します。有理数とは2つの整数の商の形で表現される数です。浮動小数点数では誤差を含んでしまう「割り切れない数」も正確に扱うことができます。

+
+
require 'rational'
+r = Rational.new(1,3)  # 「1/3」を定義
+
+

Rubyのすごい点は、この新しく定義した有理数が既存の数と自由に計算できる点です。

+
+
p 2 + r    # => Rational(7, 3)
+
+

複素数も同様です。

+
+
require 'complex'
+c = Complex(1,4)
+p 15 + c   # => Complex(16, 4)
+p 15 * c   # => Complex(15, 60)
+
+
+
+

複利計算

+
+

さて、少しは実用的な数の計算をしてみましょう。銀行や郵便局にお金を預けると利子が付きます。この利子は普通「年利0.5%」というふうに表現されます。これは1年間預けると元の金額の0.5%が利子として追加されるという意味です。

+

利子の計算は普通は複利計算が行われます。これはつまり前回の利子分も元本に加算されるということです。ここでは話を簡単にするため税金については考えないこととすると、複利計算の公式は以下のようになります。

+
+
受け取り額 = 元本*((1+利率)**運用年数)
+
+

この式は利子の計算を1年に1回行う(1年複利)場合の式ですが、1カ月複利や半年複利などの場合、この式の「利率」を「利率÷(1年÷複利期間)」に置き換え、「運用年数」を「運用年数÷複利期間」に置き換えます。

+

ということで、元本をx円、利率をi%、預け入れ期間をn年とし、複利期間をt年とした場合、受け取り額を求めるメソッドは以下のようになります。

+
+
def fukuri(x, i, n, t)
+  x * ((1+(i/100.0/(1/0.5)))**(n*1.0/t))
+end
+
+

「運用年数」の部分で「n*1.0」としているのは、ntを両方とも整数を指定してしまった場合に「n/t」が整数の商にならないためです。

+
+

では、実際に計算してみましょう。郵便局の半年複利の定額貯金に私のへそくり20万を預けたとしましょう。実際はそんなにありませんが。これを10年預けると税引き前でいったいいくらになるでしょう。

+

さっき郵便局で利率を確認したら、定額貯金は年利0.07%だそうです。なんでしょう、この利率は。ほとんどゼロじゃないですか。で、計算すると、

+
+
p fukuri(200000, 0.07, 10, 0.5)  # => 201404.6648
+
+

うーむ、10年で1404円ですか。お話になりませんね。これじゃ利子の意味がありません。

+

これが10年前ならどうでしょう。調べたら、10年前の定額貯金の利率は5.0%だそうです(!)。

+
+
p fukuri(200000, 5.0, 10, 0.5)   # => 327723.2881
+
+

32万7千円ですか。12万増ですか。あの頃いっぱい預けていればよかった。って、その頃は結婚したばかりでお金なかったんですけど。

+

なお、複利計算の公式については

+
    +
  • http://plaza27.mbn.or.jp/~financial/subdata/mas1.html

  • +
+

を参考にさせていただきました。

+
+
+

超高機能電卓irb

+
+

皆さんは電卓は何を使っていますか。最近はチープなものなら100円ショップでも手に入りますよね。ソーラー電池がつくと1000円くらいのものでしょうか。これらは四則演算とせいぜいレジスタが1つ2つあるくらいの単純なものです。本誌の読者ならLinux使いでしょうから、そのような低級な電卓では満足できなくて、もうちょっと高級なものを使っているかもしれません。私はHPの関数電卓を愛しているという人を何人も知っています。あるいはUNIXコマンドのbcを使うという人もいます。GUI版の単純電卓であるxcalcを愛用している人もいるかもしれません。

+

ここで紹介するのはirbというコマンドです。irbはinteractive ruby(対話的ruby)の略です。つまりRubyのプログラミングを対話的に行うためのツールです。もちろん、Rubyは単なる電卓ではないのですが、これを電卓として考えると実に高機能な電卓です。たとえば、

+
    +
  • 任意の数の変数が使える

  • +
  • 任意の桁数の整数が扱える

  • +
  • もちろん実数計算もできる

  • +
  • おまけにRubyの全機能が使える

  • +
  • さらに行編集も可能

  • +
+

といたれりつくせりです。

+

ただし、電卓として考えると「整数÷整数」の割り算の結果が整数になってしまうのはちょっと使いにくいですね。ここだけは注意する必要がありますね。

+
+

irbを起動するとプロンプトが出て入力を求められます。

+
+
irb(main):001:0>
+
+

最初の「001」は行番号、次の「0」はネストの深さを意味します。では、ちょっと計算してみましょう。

+
+
irb(main):001:0> 1 + 1
+2
+irb(main):002:0> 2 * 4
+8
+irb(main):003:0> 2 ** 10
+1024
+irb(main):004:0> 2 ** 100
+1267650600228229401496703205376
+
+

電卓としてはなかなか快調です。では、メソッドも定義してみましょう。先ほどの複利計算メソッドを定義します。

+
+
irb(main):005:0> def fukuri(x, i, n, t)
+irb(main):006:1>  x*((1+(i/100.0/(1/0.5)))**(n*1.0/t))
+irb(main):007:1> end
+nil
+
+

def文の本体の間はネストが1になっていることに注目してください。これで複利計算も使える電卓になりました。先ほどの定額貯金の計算もこのとおりです。

+
+
irb(main):008:0> fukuri(200000, 0.07, 10, 0.5)
+201404.6648
+
+

では、1年複利だとどうなるんでしょう。こういうときに行編集機能は便利です。上カーソルでさっきの行を表示させ、0.5の部分を1に直すだけです。

+
+
irb(main):009:0> fukuri(200000, 0.07, 10, 1)
+200701.1035
+
+

なんと10年で701円ですか。さみしい。では、逆に1カ月複利だと?

+
+
irb(main):010:0> fukuri(200000, 0.07, 10, 1/12.0)
+208577.3631
+
+

今度は8577円になります。このようにirbは電卓としても便利です。もちろん本来の目的であるちょっとRubyの機能を対話的に試してみたいときにもとても役に立ちます。

+
+
+

まとめ

+
+

今回は計算機の原点「数」を中心に解説しました。

+

以前から白状しているように、私は数学苦手です。学生時代から数学は落ちこぼれてました。今回、付け焼き刃の知識で解説したところもあります。もし間違い、勘違いなどがありましたら、こっそり教えてくださいませ。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-032.xhtml b/docs/vol1/xhtml/p-032.xhtml new file mode 100644 index 0000000..b8a8d3b --- /dev/null +++ b/docs/vol1/xhtml/p-032.xhtml @@ -0,0 +1,116 @@ + + + + + +第14章 数と電卓 + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ 知られざるRuby ◆ coerce

+
+

今月は数の相互変換体系であるcoerceについて解説します。

+

異なる種別の数同士の演算は自動変換されて、演算結果はより「広い」種別の数になります。「広い」とは表現できる範囲が広いという意味です。整数よりも浮動小数点数のほうが「広い」ということですね。

+

その結果、整数と浮動小数点数との演算結果はより「広い」浮動小数点数になります。

+
+
p 1 + 1.5  # => 2.5
+p 1.5 * 2  # => 3.0
+
+

問題はより広い種別にどのように変換するか、です。組み込みの数クラスだけならまだ話は簡単ですが、すでに述べた有理数や複素数のようにRubyでは後から数クラスを追加できるので、ややこしくなります。

+

また、型を考慮する必要があるのが左辺だけなら、Rubyの演算はメソッドで実行されるので、左辺のクラスに応じて自動的にメソッドが選択されるのですが、数の場合には右辺も重要です。

+

それを解決するために用意されているのがcoerceシステムです。Rubyの数クラスの演算を行うメソッドは全部以下のようなルールで設計されています。

+
    +
  • メソッドは左辺のクラスに応じて選択される

  • +
  • 各メソッドは、右辺のクラスが自分の知っているクラスなら、それに応じた処理を行う。たとえばFixnumFloatを知っているので左辺をFloatに変換してから計算する

  • +
  • 右辺のクラスが自分の知らないクラスなら、まず右辺の数オブジェクトのcoerceメソッドを左辺を引数にして呼び出す。coerceメソッドは右辺と左辺を適切なクラスに変換して[左辺, 右辺]という配列を返す

  • +
  • 右辺のクラスが左辺のクラスを知らなければ、どうやっても変換できないのでエラーになる

  • +
  • 改めて、変換された左辺と右辺に対して演算を行う。具体的には同じ名前のメソッドを呼び出す

  • +
+

この手順に従うクラスはRubyの既存の数クラスとうまく協調できます。たとえば、新しい数クラスRationalを定義してみましょう(リスト14.1)。簡単にするため、演算子は「+」だけ用意します。また、約分も行いません。きちんとしたRationalクラスの実装は標準ライブラリのrational.rbを参照してください。

+ +
+

リスト14.1●新しい数クラスRationalの定義

+
class Rational < Numeric
+  # インスタンス初期化(num=分子、den=分母)
+  def initialize(num, den)
+    @num = Integer(num)
+    @den = Integer(den)
+  end
+  # 分子、分母の読み出しメソッドの定義
+  attr_reader :num, :den
+
+  # 「+」の定義
+  def +(num)
+    case num      # numのクラスで分岐
+    when Rational # 左辺と同じクラス
+      x = @num * num.den
+      y = num.num * @den
+      Rational.new(x + y, @den * num.den)
+    when Integer
+      self + Rational.new(num, 1)
+    when Float
+      @num*1.0/@den + num
+    else          # 自分の知らないクラス
+      # coerceを行う
+      # 右辺がRationalを知らなければエラー
+      x, y = num.coerce(self)
+      x + y           # 改めて再計算
+    end
+  end
+
+  def coerce(num) # 変換メソッド
+    case num
+    when Integer  # 整数なら分母1のRationalに
+      [Rational.new(num, 1), self]
+    when Float    # Floatなら自分(右辺)をFloatに
+      [num, @num*1.0/@den]
+    else          # これら以外は知らない、エラー
+      raise TypeError, "#{num.type} can't be converted into #{self.type}"
+    end
+  end
+end
+
+
+
+

これらを実際に計算すると以下のようになります。

+
+
r = Rational.new(1,3)  # Rationalの生成
+p r + 1                # => 「4/3」相当の有理数
+# Rationalの+メソッドで処理
+
+p 1 + r                # => 「4/3」相当の有理数
+# Fixnumの+がRationalのcoerceを呼び、coerceが
+# 左辺(Fixnum)をRationalに変換
+
+p r + 1.0
+# Rationalの+メソッドで処理
+
+p 1.0 + r   # => 1.333333333
+# Floatの+がRationalのcoerceを呼び、coerceが
+# 左辺(Float)をRationalに変換
+
+

えーと、把握できましたでしょうか。一度でわからなくても無理はありません。coerceシステムはRubyで数の体系を自然に表現するために工夫されていますが、設計した本人もすぐにわからなくなります(笑)。たとえ理解できなくても、自分で新しい数クラスを設計しない限り用事はないかもしれません。coerceシステムの実際に興味を持たれた方は、Rubyのソースのnumeric.crational.rb, complex.rbなどを参照してください。

+

coerceシステムの設計にはrational.rbなどの開発者である石塚圭樹さんの多大な貢献があったことを申し添えておきます。

+

さて、本連載「初等Ruby講座」も回が進んで、知られざるRubyで解説するネタが減ってきました。もし、こんな「知られざるRuby」の一面について解説してほしいというリクエストがあれば、

+
    +
  • matz@ruby-lang.org

  • +
+

までメールをください。

+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-033.xhtml b/docs/vol1/xhtml/p-033.xhtml new file mode 100644 index 0000000..720c4b9 --- /dev/null +++ b/docs/vol1/xhtml/p-033.xhtml @@ -0,0 +1,77 @@ + + + + + +第14章 数と電卓 + + + + +

Matz Essays Volume 1

+ + +
+

◆ Ruby開発日記 ◆ インプットメソッド

+
+

プログラマーの生活を知らない人から見れば、プログラマーというのは一日中プログラムを書いているように思えるかもしれません。しかし、実際にはプログラムを書く時間よりも、日本語の文章を書いている時間のほうがよっぽど長いのです。

+ +

プログラムには仕様書やマニュアルのようなドキュメント書きは必須ですし、それ以外にも雑誌の原稿、本の原稿、Web日記など書くものはたくさんあります。なにより一番多いのはやはりメールでしょう。自分の一日を振り替えるとコンピュータに向かっている時間の7割はメールの読み書きをしているような気さえします。

+

ということは、一日のうちのかなりの時間を文章を書くことに費やしていることになります。プログラマーを始めとする技術屋は基本的に「楽になるためには苦労をいとわない」タイプの人間が多いので(私もそうです)、当然のように自分にぴったり合った入力方法を模索することになるわけです。

+
+

周辺事情

+

私の勤めている(株)ネットワーク応用通信研究所で、私の周辺の人々がどのような入力方法を使っているのか調べてみました。

+

私の隣の席に座っている前田修吾さん(mod_rubyなどの開発者ですね)は、長らくT-codeを使っていたそうです。T-codeは「無連想直接入力方式」で、特定のキーの組み合わせで直接漢字を入力できる方式です。キーの組み合わせを覚えさえすれば、変換などで思考が妨げられることなく、入力できるそうです。熱狂的なファンの多い方式ですが、覚えることが多いので、導入コストが高いのが難点です。実際、前田さんは数年がんばっても効率が上がらないので、最近Wnn+eggに移行したそうです(だめじゃん)。

+

私の上司にあたる生越昌己さん(通称: おごちゃん)はSKKの愛用者です。送りがなを指定することできめ細かな指定ができるのでお気に入りのようです。余計なことをしないのがうれしいのでしょうか。うちの会社には他にもSKK愛用者は何人もいます。

+

私の後ろの席に座っている、わが社の社長の井上浩さんは、ひらがな入力には直接入力方式の1つであるTUT-codeを使い、漢字は「かんな」による変換を行うという一風変わった入力方式です。TUT-codeでのひらがな入力はローマ字入力よりは速いとのことです。

+

変な人たちですね。

+
+
+

私の場合

+

では、私の場合はどうなのかというと、あんまり人のことはいえません。私は「かんな」のローマ字カスタマイズ機能を使って、ひらがな入力には特別の配列を使っているからです(図14.2)。

+
+ +
+ fig1402 +
+

図14.2●キーボードレイアウト「きゅうり改」

+
+

この方式は狩野宏樹さん(当時早稲田大)が考案された方式を少々カスタマイズしてできたもので、狩野さんが元の配列を「きゅうり」と呼んでいらっしゃるので、これを「きゅうり改」とします。

+ +

この方式では「さ」という文字を入力するためには、「さ行」のキー(f)に続いて母音の「あ」のキー(h)を押します。左手が子音、右手が母音を担当します。ですから、普通に日本語を入力すると左右の手が交互に動作することになり、効率が上がるというわけです。

+

「ゃ」「ゅ」「ょ」のキーは母音の代わりに使うキーです。たとえば「か行」の「ゃ」は「きゃ」になります。また、このキーを連続して打つと「ゃあ」「ゅう」「ょう」になりますから、「きょう」とか「しょうしょう」のようなよくある表現に打ちやすい同じキーの2度打ちが使えます。

+

「ぇ」のようなその他の小さい文字を入力するためには「小」キー(b)を押してからその文字を入力します。

+

たとえば、「きょうはてんきがよいです。」と入力するためには、

+
+
dooshg;ndkqhalkv;fj.
+
+

となります。アルファベットで見るとわけがわかりませんが、慣れると指が覚えてくれます。実際私は、もう何年もこの配列を使っているのにいまだにアルファベット表現からかなの列を想像できません。

+

「新しい配列を覚えるなんて」と思われる方もいらっしゃるかもしれません。しかし、この方式は子音の位置さえ覚えてしまえば、後は割と簡単です。私が数年前この方式に移行したときには、数時間でまがりなりにも打てるようになり、3日ですっかり慣れました。

+
+
+

「きゅうり改」の使い方

+

さて、「きゅうり改」を使ってみたいと思われますか。「かんな」をお使いの方は簡単な手間で移行できます。

+

付録CD-ROMにkyuri.kpdefというローマ字変換テーブルと、それをコンパイルした .kyuri.kpを収録してもらいました。まず、Disc 3のLinuxmag/Rubyディレクトリにある、.kyuri.kpをユーザーのホームディレクトリにコピーします。次にホームディレクトリの .cannaファイルに以下の行を追加します。

+
+
(setq romkana-table ".kyuri.kp")
+
+

プラットフォームの関係で再コンパイルする場合には、

+
+
kpdic < kyuri.kpdef > .kyuri.kp
+
+

を実行してください。

+

あとは、「かんな」での日本語入力がすべて「きゅうり改」で行われます。ちょっと努力すれば数日であなたの日本語入力は劇的に改善されることでしょう、たぶん。

+

どうです? 「きゅうり改」使ってみませんか?

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-034.xhtml b/docs/vol1/xhtml/p-034.xhtml new file mode 100644 index 0000000..3327705 --- /dev/null +++ b/docs/vol1/xhtml/p-034.xhtml @@ -0,0 +1,387 @@ + + + + + +第15章 CGI + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay15 +
+

+初等Ruby講座
+CGI +

+
+

[Linux magazine, 2002年8月号]

+
+

CGIについての解説回です。もはや純粋なCGIでWebアプリケーションを開発する人はいなくなってしまいましたから、原理的な部分としてしか役立たないでしょうね。時代の変化は残酷です。

+

「Ruby開発日記」では、プログラマー危機説について語っています。こちらはシステムが複雑化してしまうと、入門者がいきなり高度なものを学ぶ必要があって、それによって挫折してしまうのではないかという懸念です。幸い、現時点ではそのようなことはまだ起きていないですし、Scratchをはじめとするプログラミング初心者向けのツールも充実しています。しかし、CopilotやChatGPTのようなAIツールや、ノーコード・ローコードと言われるような開発手法が発展すると、過去の懸念がまた復活するのではないかと憂慮しています。

+
+
+

Web上でCGIプログラムを動作させることで、フォームを使用してアンケートを処理したり、動的なページを作成したりすることができます。テキスト処理を行うCGIプログラムはRubyの得意分野の1つです。

+
+
+

WWW

+
+

ちょうどRubyの開発が始まった頃の1993年にコンピュータ業界にとって革命的な発明が行われました。この発明はWWW(World Wide Web)と呼ばれるもので、当時CERNにいたTim-Burners Leeによって行われました。

+

WWWはいろんな意味で今までの常識をくつがえす発明でした。まず1つ目の非常識はすべての情報を毎回サーバーまで取りに行く点です。WWWではリンクを張ったページがたとえば地球の反対側のサーバー上にあれば、実際にその地球の反対側にあるマシンまで通信を行います。これ以前インターネット(WWW以前からインターネットはあるんですよ、念のため)では通信量を減らすことを第一に考えてあらゆるものが設計されていました。WWWのすごいところはそういう気づかいとか遠慮とかいうものをあっさり切り捨てたところです。ある意味『富豪主義』と言ってもよいでしょう。通信回線の速度や価格が飛躍的に改善された現在では当然に思えることも、過去には驚くべき発想の転換だったというわけです。

+
+

2つ目の非常識は通信内容としてプレーンテキストではなく、マークアップ言語であるHTMLを採用した点です。これ以前ではインターネットの通信はプレーンテキストが主流でした。メールもネットニュースも基本はプレーンテキストの転送でした。ところがWWWではSGMLの流れをくむHTML(Hyper Text Markup Language)を採用することによって、

+
    +
  • 他のページへのリンク(ハイパーテキスト)

  • +
  • 文章の構造の明確な表現

  • +
  • 画像や表組みなどの見栄えの表現

  • +
+

が可能になりました。最初のWWWはあくまでもテキストベースで、画像の表示などができるようになったのはだいぶ後のMosaicブラウザの登場を待つ必要があったのですが、それでもHTMLを使うという選択が現在の「インターネットといえばWWW」という状態を生んだといっても言い過ぎではないでしょう。実際うちの小学生の娘はまだインターネットとWWWの区別がついていません。おまけに5歳の息子はインターネットを使えば何でもわかると信じてます。いや、確かに最近はいろいろわかるけど。

+

3つ目の非常識はサーバーとクライアントの比重の逆転です。サーバー/クライアントモデルは昔からある形態ですが、もともとは重い仕事はサーバーで、軽い仕事はクライアントでという仕事の割り振りが行われてきました。しかし、WWWにおいてはサーバーの仕事は(少なくとも当初は)HTMLファイル内容の転送が主で、その他のHTMLファイルの解釈や表示など重い仕事がクライアントに割り振られました。このようなクライアントのほうが重い傾向はX Window Systemですでに見られましたが、WWWはそれを決定的にしました。

+

このような驚くべき(今では当然ですが)発明によりWWWはコンピュータとネットワークのあり方を一変させてしまいました。この発明がなければ、世界はまるで違ったものになっていたに違いありません。

+
+
+

CGI

+
+

発明されたばかりのWWWではサーバーはファイルに格納されたHTMLコンテンツをクライアントに転送することだけが仕事でした(図15.1)。HTTPクライアントはHTTPサーバーにURL(Unified Resource Locator)という形式でページを指定すると、サーバーは対応するHTMLファイルを読み出して、その内容をクライアントに転送します。

+
+ +
+ fig1501 +
+

図15.1●HTMLコンテンツの転送

+
+

URLはいわゆる「http://〜」ってやつですね。ホームページアドレスと呼ぶ人もいますが、実際には「ホームページ」でないものを指す場合もあるので不適切です。

+

しかし、毎回毎回同じリクエストに対して同じ返答を返すだけでなく、クライアントの要求に応じてコンテンツを生成する要求が出てくるのは、ある意味必然でした。

+

そこで登場したのがCGI(Common Gateway Interface)です。CGIでは、HTTPサーバーへの要求に対してCGIプログラムを起動します。各要求ごとにプログラムを起動してしまうところが「富豪主義」らしいといえます。クライアントからの要求の情報は主に環境変数を使ってCGIプログラムに渡されます。CGIプログラムからの出力(標準出力)はそのまま(正確には若干サーバーで加工されて)クライアントに転送されます(図15.2)。

+ +
+ +
+ fig1502 +
+

図15.2●CGIの仕組み

+
+

HTTPサーバーとCGIとの間の情報のやりとりは環境変数と標準入出力だけを使って行われますから、それらをアクセスできればどんな言語を使ってもCGIプログラムを記述できるというわけです。しかし、HTTPプロトコルもHTMLも結局はテキストですから、テキスト処理の得意な言語がCGIプログラムにも向いているということになります。ですから、RubyやPerlなどはCGI向き言語といえます。

+
+
+

動的ページでできること

+
+

では、CGIによってダイナミックに内容を生成できるとどんなことができるでしょうか。ちょっと考えただけでも、

+
+

などなどいろいろな可能性がありそうです。動的ページさえあれば、HTMLで表現できる範囲なら事実上何でも可能だと断言してもよいでしょう。

+
+
+

初めてのCGI

+
+

リスト15.1は、最も簡単なCGIプログラムです。

+
+

リスト15.1●簡単なCGI(hello.cgi)

+
#! /usr/bin/ruby -T
+
+print "Content-type: text/html; encoding=EUC_JP\n\n"
+print "<html><head><title>CGI</title></head><body>\n"
+print "こんにちは世界っ!\n"
+print "</body></html>\n"
+
+
+

このプログラムは出力に何の変化もないのでCGIである必然性もないようなプログラムですが、最初ですから。最初の行はこのプログラムがRubyで実行されることを示しています。「-T」はCGI用のおまじないです。あとで説明しますが、CGIプログラムの最初の行はこのようにしておくのがよいでしょう。あ、Rubyインタプリタのインストールしてある位置によっては、

+
+
#! /usr/local/bin/ruby -T
+
+

などに書き換える必要があるかもしれません。ファイル名はとりあえずhello.cgiとしておきます。拡張子.cgiは「このプログラムはCGIですよ」という印です。

+

このプログラムを

+
+
$ ruby hello.cgi
+
+

のように、そのまま実行してみると図15.3の出力が得られます。

+
+
+
$ ruby hello.cgi
+Content-type: text/html; encoding=EUC_JP
+
+<html><head><title>CGI</title></head><body>
+こんにちは世界っ!
+</body></html>
+
+

図15.3●簡単なCGIの出力

+
+ +

これがCGIの出力の基本的な構造です。最初の1行目はCGIの出力の本体部分に関する情報を含むもので「ヘッダー」と呼ばれます。この「Content-type」ヘッダーはデータのフォーマットが「HTMLで書かれたテキストで、文字コードはEUC-JPである」ことを意味しています。他にも追加のヘッダーを指定することはありますが、この「Content-type」ヘッダーは必須です。ヘッダーと本体の間は空行で区切られます。

+

空行から最後までが本体です。ここではHTMLを直接printしています。

+

さて、これはCGIですから、直接実行しては意味がありません。そこでHTTPサーバーから実行するように設定します。

+

この設定はHTTPサーバーによって異なるのですが、とりあえずApacheについて説明しておきます。

+

まず、CGIプログラムをApacheから見える場所に置きます。ここではとりあえず、

+
    +
  • /home/matz/public_html/hello.cgi

  • +
+

に置いたとしましょう。この場所に置いたファイルは(Apacheの設定にもよりますが)、

+
    +
  • http://ホスト名/~matz/hello.cgi

  • +
+

というURLでアクセスできるようになります。

+

次に、CGIプログラムのパーミッションを確認します。CGIプログラムはHTTPサーバーから直接起動されるため、実行ビットが設定されている必要があります。

+
+
% chmod 755 hello.cgi
+
+

としておきましょう。

+

それから、CGIが実行できるようなApacheの設定になっているかどうかを確認しましょう。とりあえず「拡張子.cgiの付いたファイルをCGIとして実行する」ためにはApacheの設定ファイルhttpd.confに、

+
+
Options ExecCGI
+AddHandler cgi-script .cgi
+
+

を(なければ)追加します。詳細はApacheのドキュメントを参照してください。これで、ブラウザで上記URLでアクセスすると、

+
+
+
こんにちは世界っ!
+
+

と表示されるはずです。表示されなかったら、

+
    +
  • URLは正しいか

  • +
  • パーミッションは設定されているか

  • +
  • #!行のパスは正しいか

  • +
  • Apacheの設定は正しいか

  • +
+

などを確認してください。

+
+
+

クライアントからの入力

+
+

しかし、変化のないCGIではHTMLファイルと変わりありません。CGIのパワーを生かすためには、ダイナミックに変化するページを作らなくてはなりません。考えてみるとダイナミックに変化するページとは、

+
    +
  • 時刻や最新のニュースなどリクエスト内容は同じで、ページ内容が刻々変化するもの

  • +
  • アンケートや検索エンジンのようにユーザーから何らかの入力を受け取り、その入力に応じたページ

  • +
+

のいずれかに分類できます。後者ではユーザーからの入力を受け付ける必要があります。ユーザーからの入力はフォームHTMLのformタグを使って指定します。

+

リスト15.2はHTMLフォームの例です。フォーム入力そのものはただのHTMLでCGIである必要はありませんが、そのフォームからの入力を処理するのがCGIになります。

+
+

リスト15.2●フォーム入力例(アンケート)

+
<html>
+<head><title>アンケート入力</title></head>
+
+<body>
+<form action='"enquiry.cgi"'>
+  <table border="0" align="center">
+  <tr><th nowrap>性別:
+  <td>
+    <input type="radio" name="gender" value="m">男性
+    <input type="radio" name="gender" value="f">女性
+  <tr><th nowrap>年齢:
+  <td>
+    <input type="radio" name="age" value="19">20歳未満
+    <input type="radio" name="age" value="20">20代
+    <input type="radio" name="age" value="30">30代
+    <input type="radio" name="age" value="40">40代
+    <input type="radio" name="age" value="50">50代以上
+  <tr><th nowrap>趣味:
+  <td>
+    <input type="checkbox" name="hobby1">コンピュータ
+    <input type="checkbox" name="hobby2">映画鑑賞
+    <input type="checkbox" name="hobby3">音楽鑑賞
+    <br>
+    <input type="checkbox" name="hobby4">工作
+    <input type="checkbox" name="hobby5">読書
+    <input type="checkbox" name="hobby6">スポーツ
+    <br>
+    <input type="checkbox" name="hobby7">アウトドア
+    <input type="checkbox" name="hobby8">旅行
+    <br>
+    その他: <input type="text" name="hobby0">
+  <tr><th><td>
+    <input type="submit" value="送信">
+    <input type="reset" value="クリア">
+  </table>
+</form>
+</body>
+</html>
+
+
+ + +

formタグではactionとしてsubmitボタンが押されたときに起動されるCGIのURLを指定します。ここではファイル名だけの相対URLを指定しています。

+

個々の入力フィールドはinputタグやtextareaタグ、selectタグなどで指定します。inputタグはtypeを使ってフィールドの種別を指定します(表15.1)。

+
+

表15.1●inputタグのtype

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
タグ説明
checkboxチェックボックス
filefileアップロード
hidden隠しフィールド
imageイメージボタン
passwordパスワード入力
radioラジオボタン
resetリセットボタン
submitサブミットボタン
textテキスト入力
+
+

これらの入力フィールドには1つ1つ、name="foo" というような名前を付ける必要があります。この名前はあとでCGIプログラムでフィールドの入力結果を取り出すために使われますから、各々別の名前を付けておきます。

+

リスト15.2のHTMLをブラウザで表示させると図15.4のようになります。

+
+ +
+ fig1504 +
+

図15.4●HTMLフォーム(HTML)

+
+
+
+

CGIプログラムへの入力

+
+

HTMLフォームへの入力はHTTPの仕様に従ってHTTPクライアントからHTTPサーバーに渡され、さらにそれはCGIの規約に従ってCGIプログラムに渡されるのですが、今回は入門編ですのでHTTPやCGIの仕様の詳細までは踏み込みません。

+

Rubyにはそのようなやりとりの解釈をしてくれるcgiライブラリがすでに存在するので、それを使うことにします。cgiライブラリを使うためにはまずrequireする必要があります。

+
+
require "cgi"
+
+

それからCGIデータを取り出すCGIオブジェクトを作ります。

+
+
cgi = CGI.new
+
+

そして各フィールドの値を取り出します。

+
+
name, = cgi["name"]
+
+

代入文の変数名の後ろの「,」は「配列の最初の要素の取り出し」を意味します。複数の同名のフィールドがあった場合のため、cgiオブジェクトはフィールドの値の配列を返します。同じ名前のフィールドが1つしかないことが確実な場合にはこのように変数名の後ろにコンマをつけるのが簡単・確実です。

+

リスト15.3はアンケート入力のHTMLフォームから、確認用にその結果を表示するCGIです。

+ +
+

リスト15.3●フォーム処理CGI

+
#! /usr/bin/ruby -T
+
+require 'cgi'            # cgiライブラリのrequire
+
+cgi = CGI.new            # cgiオブジェクトの生成
+
+gender, = cgi["gender"]  # genderフィールドの値
+age, = cgi["age"]        # ageフィールドの値
+
+# ヘッダーとHTMLの先頭部の出力
+print <<EOS
+Content-type: text/html; encoding=EUC_JP
+
+<html><head><title>アンケート入力確認</title></head><body>
+<p align="center">入力ありがとうございます</p>
+<p align="center">入力内容は以下のとおりでよろしいですか?</p>
+
+<table border="1" align="center">
+  <tr><th nowrap>性別:<td>
+EOS
+
+if gender == "f"
+  print "女性\n"
+else
+  print "男性\n"
+end
+
+print "<tr><th nowrap>年齢:<td>\n"
+
+case age
+when "19"
+  print "20歳未満"
+when "20"
+  print "20代"
+when "30"
+  print "30代"
+when "40"
+  print "40代"
+when "50"
+  print "50代以上"
+else
+  print "不明"
+end
+
+hobbies = %w(
+  nil
+  コンピュータ
+  映画鑑賞
+  音楽鑑賞
+  工作
+  読書
+  スポーツ
+  アウトドア
+  旅行)
+
+print "<tr><th nowrap>趣味:<td>\n"
+for i in 1..8
+  hobby, = cgi["hobby%d"%i]
+  if hobby
+    printf "<label>%s</label>\n", hobbies[i]
+  end
+end
+hobby, = cgi["hobby0"]
+if hobby
+  printf "<label>その他: %s</label>\n", hobby
+end
+print "</table></form></body></html>\n"
+
+
+
+

このCGIの実行結果は図15.5のようになります。

+
+ +
+ fig1505 +
+

図15.5●フォーム処理CGI実行結果

+
+
+
+

まとめ

+
+

今回はCGIプログラミングの基本の基本について説明しました。CGIプログラミングは奥が深く、今回は誌面の関係もあってさわりしか紹介できませんでした。ぜひ次回にでも、この続きを実例も交えてもっと詳しく説明したいと思います。

+
+
+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-035.xhtml b/docs/vol1/xhtml/p-035.xhtml new file mode 100644 index 0000000..86dcf00 --- /dev/null +++ b/docs/vol1/xhtml/p-035.xhtml @@ -0,0 +1,139 @@ + + + + + +第15章 CGI + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ 知られざるRuby ◆ Webセキュリティ

+
+

このようにCGIプログラムと言ってもその基本的な部分は通常のプログラムと変わりありません。環境変数や標準入力からデータを受け取り、標準出力に結果を(多くの場合はヘッダー付きHTMLとして)出力するだけです。

+

しかし、このように気軽にプログラムできるCGIには、実は落とし穴があるのです。それが、

+
    +
  • セキュリティ

  • +
+

です。CGIプログラムは通常のプログラムとは以下の点が決定的に違います。

+
    +
  • 入力が信頼できない。

  • +
+

CGIプログラムに対して入力を行うのは、ネットワークのかなたのクライアントです。クライアントはよい人ばかりとは限りません。場合によっては悪意を持つユーザーがクライアントになるかもしれません。

+

とはいうものの、それでは「信頼できない入力」のどこが危険なのかピンとこないかもしれませんね。では、どのような問題が発生し得るのかということと、その問題に対する対策を見てみましょう。

+
+

バッファオーバーラン

+

セキュリティ問題としてたびたび報告されるのがこの「バッファオーバーラン(buffer overrun)」です。これは、入力に対して固定長のバッファを確保していた場合、バッファサイズよりもはるかに大きなデータを入力することによって、プログラムを異常終了させたり、あるいはスタックのジャンプアドレスをすり替えることで、プログラムを乗っ取ったりすることができる、というものです。

+

乗っ取りの原理の詳細は説明しませんが、外部からの入力が信頼できないときには固定長のバッファは危険です。幸い、Rubyならファイルから文字列の読み込みも、文字列操作もStringクラスがメモリ割り当てを自動的に行ってくれますから、固定長のバッファを使わなくて済みます。ですから、Rubyのような「高級」な言語を使っていれば、バッファオーバーランの問題からは解放されるということです。CなどでCGIプログラムを開発する場合には気を使う必要があります。

+
+
+

信頼できない実行

+

たとえば、クライアントからの入力が文字列strに格納されていたとして、何も考えずに、

+
+
system(str)
+
+

と実行したらどうなるでしょう? もし、strの内容が、

+
+
"cat /etc/passwd"
+
+

だったり、あるいは最悪、

+
+
+
"rm -rf /"
+
+

だったら? このように外部からの入力を元に実行するのは危険です。では、基本的な部分は自分で用意するのならどうでしょう。たとえば、あるディレクトリにあるファイル名を入力させて、そのファイルのls -l出力を表示させたければ、

+
+
system "ls -l /home/matz/tmp/#{str}"
+
+

とすれば、安全なのでしょうか。一見、危険はなさそうに見えますが、strの内容が、

+
+
"../../../etc/passwd"
+
+

なら、/etc/passwdの情報が見えてしまいます。あるいは、

+
+
"foo; rm -rf /"
+
+

なら、lsの表示だけをさせるつもりだったのに、うっかり「rm -rf /」まで実行してしまいます。大変なことです。

+

systemと同じくらい危ないのがevalです。evalは文字列をプログラムとして評価します。ですから、先ほどのsystemと同じように、外部からの文字列をそのままevalに渡すと、

+
+
eval(str)
+
+

で、strの内容が、

+
+
"system 'rm -rf /'"
+
+

なんてこともできてしまいます。

+

と、考えてみると、CGIプログラムって恐いですね。そうなんです、CGIを楽しむためにはCGIの危険性をも十分に把握しておくべきだと思います。自動車は便利なものですけど、ひとたび間違うと凶器になるのでちゃんと教習所で運転を勉強するのと同じですね。

+

このようなセキュリティ上の問題への対処を簡単にするため、Rubyではいくつかのセキュリティレベルを用意しています。Rubyのセキュリティレベルは、何もチェックを行わないレベル0から、最も厳しいレベル4までありますが、今回は誌面の関係上、CGIに便利なセキュリティレベル1についてだけ説明します。

+

CGIプログラムに便利なレベル1では「汚染された値による危険な操作」が禁止されます。

+

「汚染された値」とは、いま話題にしている「信頼できない入力」のことです。普段は見えませんがRubyのすべてのオブジェクトには「信頼できるか」マークが付いています。そして、外部から入力されたデータ(たとえばファイルからの入力とか、環境変数の値とか)にはすべて「信頼できない」とマークが付けられています。また、信頼できないデータから派生したデータにも「信頼できない」マークが付きます。たとえばstrが信頼できないとき、

+
+
a = "ls -l " + str
+
+

と文字列を結合した結果aも信頼できないわけです。あるオブジェクトが信頼できないかどうかはtainted? メソッドで調べられます。

+
+
puts str.tainted?  # => true
+puts a.tainted?    # => true
+puts "a".tainted?  # => false
+
+
+

「危険な操作」とは先ほど説明したsystemevalのようなメソッドのことです。具体的には表15.2に示すメソッドは、引数に「信頼できない」マークが付いているとエラーになります。

+
+

表15.2●レベル1で禁止されるメソッド

+ + + + + + + +
test, eval, require, load, trap
File, FileTest, IO, DIRの各メソッド
+
+

何らかの内容チェック(たとえばファイル名なら「.」や「/」を含まないとか)を行って「信頼できる」と確信できたデータに対しては、明示的に「信頼できない」マークを外すことができます。

+
+
str.untaint
+
+

これでstrの文字列は「信頼できる」ことになります。実際にテストが正当かどうかはRubyは関知しませんので、くれぐれもご注意を。

+

このセキュリティレベルによって、単純なセキュリティバグを自動的に検出できるのはRubyのメリットの1つですね。Perlにも同種の機能がありますが、個人的にはRubyのもののほうが使いやすいと思います。設計者のひいき目でしょうが。

+

セキュリティレベルの指定の方法は2つあります。1つは変数 $SAFEへの代入です。

+
+
$SAFE = 1
+
+

と変数 $SAFEに整数を代入すると、それ以降の実行が指定したセキュリティレベルで行われます。$SAFEの値は現在の値よりも小さい数を指定できません。

+

もう1つの方法は、コマンドライン引数で指定する方法です。Rubyのコマンドライン引数に -T<数字> を指定するとRubyプログラムの実行がそのセキュリティレベルで行われます。数字を省略すると1を指定したと見なされます。入門編で「おまじない」として #! 行に -Tを指定しましたが、これはこのセキュリティレベル1を指定していたのです。

+
+
+

XSS

+

最後に紹介する問題はXSS(Cross Site Scripting)です。これはクライアントからの入力をそのままCGIの出力に含めてしまう危険です。たとえば、Web掲示板がユーザーの入力のタグをそのまま通してしまうと、

+
    +
  • 勝手に画像を埋め込まれたり

  • +
  • 予期しないリンクを作られたり

  • +
  • <SCRIPT> タグによりJavaScriptを埋めこまれたり

  • +
+

する場合があります。特に最後のものはクライアント側で実行されるプログラムですから、危険がいっぱいです。

+

残念ながら現在のRubyはXSSは自動検出してくれません。ユーザーからの入力を元に出力を行う場合にはHTMLタグなどをエスケープすることを忘れないようにしてください。HTMLのエスケープにはCGI.escapeHTMLメソッドが使えます(図15.6)。

+
+ +
+ fig1506 +
+

図15.6●CGI.escapeHTML

+
+
+
+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-036.xhtml b/docs/vol1/xhtml/p-036.xhtml new file mode 100644 index 0000000..0022b95 --- /dev/null +++ b/docs/vol1/xhtml/p-036.xhtml @@ -0,0 +1,65 @@ + + + + + +第15章 CGI + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ 若い世代への憂慮あるいは杞憂きゆう

+
+

私は昔SF少年でした。小・中・高と学校の図書館にあるあらゆるSF分野の本を制覇しましたし、それに飽き足らず『ペリー・ローダン』シリーズにまで手を出していました。当然のように途中で挫折しましたが。

+

最近は堕落してすっかりSFを読まなくなりました。最後に読んだのは何かなあ。J. P. ホーガンの『未来の2つの顔』かもしれません。

+

さて、その少年期に感銘を受けた作品の1つがバン・ボークトの『宇宙船ビーグル号の冒険』です。そういえば小学校の夏休みにこの作品で読書感想文を書いて、先生に変な目で見られたなあ(遠い目)。

+

とにかく、この作品中でビーグル号が訪れるある惑星の文明は「クアール」という究極生命体を作り出したものの、宇宙に進出することはできずに滅びてしまいます。彼らが宇宙に進出できなかった理由は、彼らの惑星には月がなく、かつその太陽の唯一の惑星であったため、まず月へ、それから外惑星へ、そして恒星間旅行へという段階的な宇宙開発を行うことができず、いきなり恒星間旅行を行うのは難しすぎた、というものでした。ま、確かにそういわれてみればもっともな理屈です。ステップ・バイ・ステップということですね。

+
+

ステップ・バイ・ステップ

+

ステップ・バイ・ステップといえば、コンピュータ分野においても同じことがあったように思います。私の幼い頃にはワンボードコンピュータというのが流行しました。できることといえば、せいぜい8セグメントLEDをつけたり消したり程度だったのですが、それだけでも大喜びの大人がたくさんいたのです(子供には高くて買えないおもちゃでした)。

+

次に現れたのがPC-8001やMZ-80のような「パーソナルコンピュータ」でした。これはZ80をCPUとし、16Kから32Kバイトのメモリで、BASICを実行できるコンピュータでした。もちろん、今のコンピュータとは比べものにならないくらい貧弱で、グラフィックもろくに扱えないようなものでしたが、それでもいろいろなソフトが登場しました。

+

それからPC-8801, PC-9801そしてIBM PCなどのコンピュータが次々に登場し、現在に続きます。今では、昔のコンピュータの数千、数万倍の処理能力や記憶容量を持ったPC上で、UNIX(Linuxや*BSD)を動かすことができます。というか、UNIX系OSが必要とするリソースのほうが少なかったりするんですよね。時代は変わったもんです。

+
+
+ +

憂慮

+

私自身は生まれたタイミングのおかげでこれらの変化をリアルタイムで経験してきました。しかし、それらを歴史としてしか知りようのない今の若い世代はいったいどうなんでしょう?

+

ソフトウェアに関しても、昔ならたとえばゲームでも、キャラクタが動き回るだけでも結構その気になったものです。私は今でもrogueが好きなのですが、あのノリですね。自分でもちょっと頑張れば作れそうだというのが感じられました。

+

しかし、今、PS2のゲームを目の当たりにして、「あれと同じようなものを作ってやろう」と本気で考えられる若者がどのくらいいるのでしょう。それはまさに「いきなり恒星間旅行」と同じ困難さなのではないでしょうか?

+

以前に比べてこれだけコンピュータが普及したにもかかわらず、プログラミング人口はそれほど増えていないような気がします。昔は「コンピュータやってる」は「プログラミングしてる」とほぼイコールだったのですが、今コンピュータを使ってる人でも、プログラミングする人なんてめったに見ません。

+

「まあ、母集団が増えてるんだから」ともずっと思っていたのですが、先日、母校の先生をしている人と話をしたのですが、やはり情報系学部の入学生のうちプログラム経験者の割合はむしろ減っているそうです。

+

もしかしたら、大ピンチ? パーソナルコンピュータの発展とリアルタイムに連動した私たちの世代から大きく外れた若い人たちが主流になる時代になったら、日本のプログラマーは枯渇するのでは? なんて心配したりするのです。

+
+
+

希望

+

ただ、希望がまったくないわけではないと思うのです。いくつか希望の持てる現象も見えるからです。

+

まず、第一にRubyを始めとするスクリプト言語の流行です。インタプリタ主体のスクリプト言語は往年のBASICのように、すぐプログラム、すぐ実行、すぐ修正して、また実行というお手軽開発が可能です。これはプログラム開発の敷居を低くして、より多くの人を呼び込むことができるかもしれません。

+

第二に、今回テーマにしたCGIなどのWebプログラミングです。WWWは世の中を変えましたが、プログラム環境も変えました。GUIが主体で肥大化する一方だったソフトウェアに対して、CGIプログラムはごく小規模なプログラムで実現でき、手軽に達成感を得られます。この達成感こそプログラマーの自己実現になにより必要なものだと思います。

+

第三に、WWWそのものもプログラマーを救う、かもしれません。今まで高い書籍や図書館にしかない学会誌でしか得られなかったような知識がWWWを通じてどんどん得られます。昔は論文探しに苦労したようなテーマでも、今はサーチエンジンで検索をかければあっという間に情報を収集できます。プログラミングにおいて知識は力ですから、WWWによる知識は大きな希望だと思います。

+

第四に、オープンソースソフトウェアの存在です。オープンソースという単語こそ最近作られたものですが、その考え方そのものはフリーソフトウェアとして相当古くから存在していました。しかし、これだけ世間に認知されるようになったのはやはり大きな変化でしょう。

+
+

政治家が「オープンソース的政治」などと聞くと「優しい独裁者モデルを採用する気か」と吹き出しそうになりますが、それはそれとして、プログラマーの最良の教科書はソースコードですから、オープンソースソフトウェアのソースコードはこれ以上とない情報の宝庫です。これも今の時代の希望的側面でしょう。

+
+
+

杞憂

+

というわけで、このような希望により、私の「プログラマー危機説」は杞憂かもしれません。いや、そうであればこれほどうれしいことはありません。

+

しかし、油断は禁物です。人生の先輩として、これからも機会をとらえてはプログラミングの大切さと楽しさを説いて回らねばなりません。

+

とはいうものの、こういうことを考えるようになったということは、それはすなわち歳をとったということを意味するような気がします。ああ、いやだ、いやだ。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-037.xhtml b/docs/vol1/xhtml/p-037.xhtml new file mode 100644 index 0000000..2314f3c --- /dev/null +++ b/docs/vol1/xhtml/p-037.xhtml @@ -0,0 +1,667 @@ + + + + + +第16章 Rubyで作るCGI + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay16 +
+

+初等Ruby講座
+Rubyで作るCGI +

+
+

[Linux magazine, 2002年9月号]

+
+

前章に引き続き、CGIについて解説しています。今回はサンプルとしてFAQ管理システムを作っています。現代のWebアプリケーション開発に比べるとだいぶ原始的な印象がありますね。しかし、私は昔も今もほとんどWebアプリケーション開発の経験はないはずなんですが、意外と(?)ちゃんとしたCGIアプリケーションを開発してますね。

+

「Ruby開発日記」では、フリーソフトウェアな生活について語っています。現代においてはオープンソースソフトウェアは一般的になり、知名度も高まりましたが、それぞれの開発者がどのように生活を成り立たせているかについてはあまり知られているとは言えません。20年前であればなおさらです。ここではそのうちのいくつかについて解説しています。具体的には「副業」「出版」「寄付」「パトロン」をあげています。本質的な部分は年月によっては変わらないようで、現代でも同じような収入モデルです。ただ、オープンソース開発者を支援しようという企業は昔よりははるかに増えています。ちょうどこの原稿を書いている頃に私はRubyの開発がほぼ専業になりましたが、あの頃、オープンソースソフトウェア開発だけで食べている人は(少なくとも国内には)ほとんどいませんでしたが、今ではRubyに限らずいろいろなオープンソース開発者が、それだけで食べれるようになってきています。GitHub SponsorsやPatreonの存在も大きいでしょう。

+
+
+

前回はCGIプログラミングの基本を学びました。今回は実際に使えるFAQ管理CGIプログラムを作成します。CGIプログラムを作るうえで便利なRubyライブラリも紹介します。

+
+
+

Do It Yourself

+
+

www.ruby-lang.orgにはRAA(Ruby Application Archive)と呼ばれるページがあります(http://www.ruby-lang.org/en/raa.html)。このページはRubyで書かれたプログラム、ライブラリ、ドキュメントなどなどを登録するページなのですが、現在登録プロジェクトが628エントリあって、今も増加中です。

+ +

このページは、

+
    +
  • 一覧はない

  • +
  • ダウンロード支援はない

  • +
  • 検証はない

  • +
  • 検索はない

  • +
+

と、ないないずくしなのですが、それでも結構愛用されています。それは、メンテがほぼ不要で自動化されているという点にあると思います。

+

気軽にプロジェクトを登録できる点や、プロジェクトの個々のオーナーが自分で更新・管理できるので、管理者のチェック待ちなどのタイムラグがなく、情報が常に最新に保てることなどが「使いやすさ」の秘密ではないかと考えています。もちろん、PerlのCPAN(Comprehensive Perl Archivers Network)に比べると「おもちゃ」のようなものなのですが。

+

Ruby 1.6 のドキュメント(http://www.ruby-lang.org/ja/man-1.6/)はRWikiで管理されているのですが、これも間違いや不十分な記述があれば、自分で修正できるのがなかなか便利です。

+

このように「自分でできる」ということは、主体的な参加を促したり、情報の更新が簡単だったり、管理のコストを下げたりといろいろ便利なことが多いのです。

+

そこで、今回(と次回)はCGIを利用して、このような自分でできるツールを開発してみたいと思います。

+
+
+

これってFAQ?

+
+

で、どのようなツールを作ろうか、と少し考えてみました。最初は使い勝手が悪いと評判のRAAを改善するかとも思いましたが、RAAなんてのはどこにでも必要なツールじゃありません。RAAのソースを見ても「これ使ってみよう」って人はそんなにいないでしょう。

+

そこで思い付いたのがFAQマネージャです。FAQなら、たいていの人がいろんなテーマで必要とすることもあるでしょう。ついでにRubyのFAQもこれで管理しようかなあ。

+

このFAQマネージャへの要求として以下のものをリストアップしてみました。

+
    +
  • 簡単な実装(サンプルですから)

  • +
  • 利用が簡単

  • +
  • インストールも簡単

  • +
  • 認証は行わない(誰でも書き換え可)

  • +
  • 同一サイトで複数FAQを管理できる

  • +
+

最終的にはこれだけの条件を満たすものを作るつもりです。

+
+
+ +

基本設計

+
+

このプログラム(AutoFAQ という名前にしました)の機能を表16.1に示します。

+
+

表16.1●AutoFAQプログラムの機能一覧

+ + + + + + + + + + + + + + + + +
FAQのインデックスを表示する
FAQの各エントリを表示する
FAQに質問を追加する
FAQに答えを書く
FAQを検索する
+
+

というわけで、使い勝手を決めるためにまず画面設計を行います。HTMLファイルをあれこれいじって、こういう感じはどうだろうかと用意したものが図16.1です。

+
+ +
+ fig1601 +
+

図16.1●質問の入力画面

+
+

最初の画面にはインデックス、検索、最近更新された項目、それから未回答項目の表示くらいがあればよさそうです。質問の入力画面は図16.2、回答の表示画面は図16.3です。表示をコンパクトにするために質問も回答も概要と詳細に分けてみました。

+
+ +
+ fig1602 +
+

図16.2●質問の入力画面

+
+
+ +
+ fig1603 +
+

図16.3●回答の表示画面

+
+
+
+ +

下準備

+
+

ここまで設計できれば、今度は実装に取り掛かろうと思います。

+

でも、その前に既存のライブラリを使って手抜きできるところがあるはずです。今回のプログラムで使おうと思っているライブラリには以下のものがあります。

+
    +
  • cgi    CGIインターフェイス
  • +
  • pstore オブジェクト指向データベース
  • +
  • kconv  文字コード変換
  • +
+

これらのライブラリを使えば、実装の詳細を知らなくても高度な機能を利用できます。これらライブラリについてひととおり説明しておきます。

+
+
+

cgiライブラリ

+
+

cgiについては先月学びましたが、ここで復習と補足をしておきます。cgiライブラリはCGIクラスを提供し、このクラスはCGIプロトコルを解釈し、クライアントからのリクエスト情報の取り出しを行ってくれます。

+
+
cgi = CGI.new
+name, = cgi["name"]
+
+

また、先月は説明しませんでしたが、CGIクラスはクライアントへの出力と、HTML生成の機能もあります。たとえば、cgi.printメソッドはcgi出力先(通常は標準出力)への出力を行います。また、cgi.headerメソッド はCGIの先頭に必要なヘッダー情報を生成してくれます。

+
+
cgi = CGI.new
+cgi.print cgi.header("text/html")
+
+

HTML生成機能はネストしたHTML構造をメソッドを使って表現するものです。たとえば、

+
+
<A HREF="http://www.ruby-lang.org/">Ruby</A>
+
+

は、

+
+
cgi.a("href"=>"http://www.ruby-lang.org/"){"Ruby"}
+
+

となります。変換の仕組みは、

+
    +
  • 属性はハッシュ引数

  • +
  • ボディはブロック(で指定する文字列)

  • +
+

になります。リスト16.1にAutoFAQの一部、テーブルの出力を行う部分を抜粋します。その出力結果は図16.4のようになります。

+ +
+

リスト16.1●CGIクラスによるHTML出力

+
cgi = CGI.new("html4Tr")
+.... 中略 ...
+cgi.print cgi.tr {
+  cgi.td("height"=>"100%", "valign"=>"top"){
+    q.a + cgi.div("align"=>"right"){
+      cgi.font("size"=>"-1"){"回答更新: #{q.atime}"}+cgi.br+
+        cgi.a("href"=>"#{FAQ_PROG}?mode=Entry&id=#{q.faq_id}"){"詳細を見る"}
+    }
+  }
+}
+
+
+
+
+
<TR><TD height="100%" valign="top">これは答えの例です<DIV align="right">
+<FONT size="-1">回答更新: Wed Jul 10 02:54:48 JST 2002</FONT><BR>
+<A href="autofaq?mode=Entry&amp;id=2">詳細を見る</A></DIV>
+</TD></TR>
+
+

図16.4●CGIクラスによるHTML出力結果(折り返してあります)

+
+

HTML出力を行うためには、CGIクラスのオブジェクトを作るときに出力するHTMLタイプをnewの引数として指定する必要があります。HTMLタイプは、表16.2に示すタイプのいずれかです。

+
+

表16.2●inputタグのtype

+ + + + + + + + + + + + + + + + + + + + + +
引数出力されるHTML
html3HTML 3.2
html4HTML 4.0(Strict)
html4TrHTML 4.0 Transitional
html4TrHTML 4.0 Frameset
+
+

cgiライブラリには他にもHTMLのエスケープを行うメソッド、クッキーを扱うメソッドなどがあります。

+
+
+

pstoreライブラリ

+
+

pstore(persistent store:(オブジェクトの)永続的保存)はRuby流オブジェクト指向データベースです。その特徴は以下のとおりです。

+
    +
  • marshalを使って任意のRubyオブジェクトをそのまま格納できる

  • +
  • 使いやすいインターフェイス

  • +
  • トランザクションがある

  • +
+

pstoreにはデータを一度全部メモリに読み込むので大規模データベースには向かないという欠点もあります。しかし、今回のFAQマネージャのような小規模のデータベースならあまり問題になることはないでしょう。

+ +

pstoreのデータ構造は図16.5のようになっています。

+
+ +
+ 1605 +
+

図16.5●pstoreのデータ構造

+
+

pstoreオブジェクトにはrootと呼ばれるテーブルがあって、オブジェクトに名前を付けて登録できます。登録されたオブジェクトと、そのオブジェクトから間接的に参照されているオブジェクトがデータベースに格納されます。

+

pstoreは、ほとんどあらゆるRubyオブジェクトを格納できますが、いくつか扱えないオブジェクトがあります。

+
+

IO

+

オープンしたIOオブジェクトはデータベースに入れられません。別のプロセスに持ち込めないからです。

+

拡張モジュールが定義した特殊オブジェクト

+

これもデータベースへの保存方法がわからないので保存できません。

+
+

pstoreオブジェクトの使い方は次のとおりです。

+
    +
  1. オープンする
    +まずはデータベースをオープンして、PStoreデータベースオブジェクトを得ます。
    +
    +
    db = PStore.new(path)
    +
    +pathにはデータベースファイルのパスを指定します。
  2. +
  3. トランザクションを開始する
    +トランザクション とはデータベース処理の単位です。データベースに対する処理はトランザクション単位で書き込まれますので、トランザクションの途中で、例外が発生するなど処理が失敗したときにはデータベースの状態はトランザクション開始前の状態にとどまります。トランザクションはtransactionメソッド にブロックを指定することで開始します。
    +
    +
    db.transaction {
    +  ...
    +}
    +
    +ブロックを抜けるとトランザクションが終了します。
  4. +
  5. rootの登録、取得
    +PStoreオブジェクトは一種のハッシュとして扱えます。rootへのオブジェクト登録・取得は[]メソッドを使います。
    +
    +
    db["foo"] = object  # rootの設定
    +db["bar"]           # rootの取得
    +
    +ただ、ハッシュと違って未登録のrootの取得はエラーになりますから、データベースにrootが登録されているかどうか定かでない場合には、あらかじめ確認する必要があります。データベースに登録されているすべてのroot名の一覧を得るのは
    +
    +
    db.roots         # root名一覧
    +db.root?("foo")  # root名fooがあれば真
    +
  6. +
  7. トランザクションの終了
    +transactionメソッドに指定したブロックの実行が終わればトランザクションは自動的に終了し、データベースから参照されている全オブジェクトの現在の状態がファイルに書き込まれます。
    + トランザクションの途中で強制的にトランザクションを終了させるメソッドも用意されています。commitメソッドはトランザクションを正常終了させます。transactionメソッドの実行も終了します。abortメソッドはトランザクションを強制的に中断します。データベースへの変更はファイルに書き込まれず、トランザクション開始前の状態のまま残されます。abortメソッドもtransactionメソッドの実行を中断します。
    + CGIでは複数同時に起動された場合に対応するためデータファイルへのロックが必要になるのですが、pstoreは内部で自動的にロックして排他処理してくれますので、その点でも心配せずに使えます(ただし、Ruby 1.4添付のpstoreはロックを行いません。新しいものを使ってください)。
  8. +
+

pstoreの注意として、データベース内のオブジェクトへの参照と更新は必ずトランザクション内で行ってください。途中のオブジェクトを取り出してトランザクション外で状態を変更してもエラーにはなりませんが、その変更はデータベースには反映されません。

+
+
db_data = nil
+db.transaction {
+   db["foo"] = [1,2,3]
+   db_data = db["foo"]  # 持ち出し
+}
+# トランザクション終了
+
+# データへのアクセスは可能
+p db_data[0]            # => 1
+
+# データの更新も可能だがDBには反映されず
+p db_data[0] = 42
+
+

手前みそながら、pstoreはCGIに限らず非常に使い勝手がよいので、ぜひ活用してください。先にも述べたとおり、pstoreは大規模なデータベースを扱えない(遅くて重くなる)という欠点がありますので、その場合には今度はPostgreSQLやMySQLなどのRDB(関係データベース)を使うとかする必要があります。

+

でも、pstoreはあまりにも楽に使えるので、RDBへの移行が面倒になるんですよねえ。

+

RDBアクセスなら、RubyにはDBIライブラリという各種関係データベースを統一的なAPIでアクセスできるライブラリがありますので、これを使うのもよいでしょう。

+
+
+ +

kconvライブラリ

+
+

kconvは文字コード変換用のライブラリです。歴史的な事情から日本語を表現する文字コードは複数あります。主要なものはWindows, Macなどで使われてきたシフトJIS(SJIS)、UNIXで用いられることの多いEUC(Extended UNIX Code)、メールなどで用いられるJIS(ISO-2022-JP)、それから最近いろいろ話題になっているUnicodeの表現形式の1つUTF-8があります。

+

kconvはこれらのうち、最初の3つ(SJIS, EUC, JIS)の相互変換を行います。Unicodeは変換のためにテーブルが必要ですし、まだそれほど広くは使われていないので今回は扱わないことにします。

+

kconvライブラリの提供するKconvモジュールには表16.3のメソッドがあります。また表16.4のメソッドをStringクラスに追加します。

+
+

表16.3●Kconvモジュールのメソッド

+ + + + + + + + + + + + + + + + + + + + + + + + + +
メソッド説明
guess(str)文字コードを判定
kconv(str,o[,i])文字コード変換(oからiへ)
toeuc(str)EUCに変換
tojis(str)JISに変換
tosjis(str)SJISに変換
+
+
+

表16.4●Stringクラスに追加されるメソッド

+ + + + + + + + + + + + + + + + + + + + + +
メソッド説明
kconv(o[,i])文字コード変換(oからiへ)
toeucEUCに変換
tojisJISに変換
tosjisSJISに変換
+
+

kconvメソッドには変換先の文字コードと変更元の文字コードを指定します。文字コードの指定にはKconvモジュールで定義されている定数を使います(表16.5)。

+
+

表16.5●文字コード指定の定数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
定数説明
AUTO自動変換
JISJIS(ISO-2022-JP)
EUCEUC-JP
SJISSHIFT_JIS
BINARYバイナリ(guessメソッドが返す)
NOCONV無変換(変換先として指定; 何もしない)
UNKNOWN不明(guessメソッドが返す)
+
+

たとえばある文字列をJISに変換するためには、

+
+
Kconv.kconv(str, Kconv::JIS)
+Kconv.tojis(str)
+
+

のようにします。

+
+
+ +

プログラムの構造

+
+

さて、道具の説明はこれくらいにして、本題のプログラムに戻りましょう。まずAutoFAQの機能構成を図にしておきます(図16.6)。

+
+ +
+ 1606 +
+

図16.6●AutoFAQ 機能構成

+
+

使い勝手のため、「最新項目の表示」「新質問の登録」「検索結果の表示」にはどのページからもリンクを張っておきます。また、ページの一部に質問のインデックスを用意し、「個々の項目の表示」にもジャンプできるようにしておきます。

+

インストールを簡単にしたいという要求があるので、これらを全部1つのプログラムにまとめて、機能を引数で指定することにします。具体的にはmodeというhidden fieldを用意して、その値によって切り替えることにしましょう。

+

全体の処理を擬似コードにすると図16.7のようになります。

+
+ +
+ 1607 +
+

図16.7●AutoFAQの処理の流れ

+
+
+
+

初期化とカスタマイズ

+
+

AutoFAQの初期化とカスタマイズファイルの読み込み部分をリスト16.2に示します。

+ +
+

リスト16.2●初期化とカスタマイズファイル読み込み

+
# 必要とするライブラリのrequire
+require 'cgi'
+require 'kconv'
+require 'pstore'
+
+# FAQプログラムの取り出し
+FAQ_PROG = File.basename($0).untaint
+# FAQデータディレクトリ
+FAQ_DIR = "/var/lib/www-data/"
+
+faq_conf = FAQ_DIR+FAQ_PROG+".conf"
+if File.exist?(faq_conf)
+  load faq_conf
+end
+
+FAQ_DATA = FAQ_DIR+FAQ_PROG unless defined? FAQ_DATA
+FAQ_TITLE = "AutoFAQ"       unless defined? FAQ_TITLE
+RECENT_N_ITEMS = 10         unless defined? RECENT_N_ITEMS
+
+
+

まず先頭で必要とするライブラリをrequireしています。先に説明したとおり、このプログラムではcgi, kconv, pstoreを用います。

+

それからこのプログラム名を取り出します。このためにスクリプトのパス名が格納されている変数 $0からファイル名の部分を取り出します。

+

それから設定ファイルを読み込みます。/var/lib/wwwディレクトリ以下にある、

+
+
<プログラム名>.conf
+
+

というファイルをロードします。たとえばプログラムがautofaqという名前であれば、設定ファイル名はautofaq.confになります。このファイルは通常のRubyプログラムで、その中でプログラムが参照する定数を定義することで、プログラムの設定を行うことができます。設定に使われる定数を表16.6に示します。

+
+

表16.6●AutoFAQ設定用定数

+ + + + + + + + + + + + + + + + + +
定数説明
FAQ_DATApstoreデータベースのファイル名
FAQ_TITLEFAQのタイトル
RECENT_N_ITEMS最新項目を最大いくつ表示するか
+
+

そして最後に設定ファイルの内部で定数が定義されなかった場合のためにデフォルト値を定義します。defined?はその定数が定義されていたときに真を返します。

+
+
+ +

メイン処理部

+
+

AutoFAQのメインの処理部をリスト16.3に示します。やっていることはCGIオブジェクトの生成とpstoreのトランザクション開始、それからmodeパラメータに従ってそれぞれの処理を呼び出しているだけです。Commit処理だけはデータベースの書き込みがあるので、最初に専用のトランザクションを開始して書き込みを行っています。

+
+

リスト16.3●AutoFAQメイン処理部

+
def faq_main
+  cgi = CGI.new("html4Tr")
+  mode, = cgi["mode"]
+  unless mode
+    mode = "Recent"
+  else
+    mode = mode.capitalize
+  end
+  page_header(cgi)
+  db = PStore::new(FAQ_DATA)
+  db.transaction do
+    if mode == "Commit"
+      do_commit(cgi, db)
+    end
+  end
+  db.transaction do
+    show_index(cgi, mode, db)
+    case mode
+    when "Recent"
+      show_recent(cgi, db)
+    when "Entry"
+      show_entry(cgi, db)
+    when "Commit"
+      show_commit(cgi, db)
+    when "Submit"
+      show_submit(cgi, db)
+    when "Update"
+      show_update(cgi, db)
+    when "Search"
+      show_search(cgi, db)
+    else
+      show_recent(cgi, db)
+    end
+    show_unanswered(cgi, db)
+  end
+  page_footer(cgi)
+end
+
+if __FILE__ == $0
+  faq_main
+end
+
+
+ +

プログラム最後の

+
+
if __FILE__ == $0
+  faq_main
+end
+
+

は、プログラムとして起動したときだけfaq_mainを実行するためにイディオムです。このイディオムを使うとスクリプトファイルをプログラムとしてもライブラリとしても使えるようになります。

+
+
+

データベースの構造

+
+

では、FAQのデータを格納するデータベースを設計しましょう。データベースに必要だと思われる項目を表16.7に示します。

+
+

表16.7●データベースレコード

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
項目説明
@faq_id項目のユニークID
@q質問の概要
@q2質問の詳細
@a回答の概要
@a2回答の詳細
@qtime質問時間
@atime最終回答時間
+
+

これを配列にまとめてもよいのですが、将来データ構造の変更などに対処しやすくするためにクラスとして定義します。クラス名はFAQ::Entryとします。

+
+
+ +

各機能の実装

+
+

ここまでくれば後は各機能の実装です。機能を実現するメソッドを表16.8に示します。

+
+

表16.8●機能実装メソッド

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
メソッド説明
page_headerヘッダーの表示
do_commitデータベースへの書き込み
show_indexインデックス表示(画面左側)
show_recent最新の項目表示
show_entry個別項目の詳細表示
show_commitコミット結果表示
show_submit質問入力フォーム表示
show_update回答入力フォーム表示
show_search検索結果の表示
show_unanswered未回答項目の一覧(画面右側)
page_footerフッターの表示
+
+

完成したプログラムautofaq.rbをCD-ROMに収録していただきました。各機能の実装についてはこちらを参照してください。私のプログラムらしくコメントもほとんどありませんが、割と素直に書いてあるので(短いし)読めないことはないでしょう。

+
+
+

インストール

+
+

AutoFAQのインストールと設定は以下のとおりです。root権限で行ってください。

+
    +
  1. autofaq.rbをCD-ROMからCGIの実行できる場所にコピーする
    +RedHat系ですと /var/www/cgi-binとかになるでしょう。HTTPサーバーの設定ファイル(httpd.confなど)を参照してください。ファイル名はCGIコマンドとして使いたい名前がよいでしょう(ここではfaqとします)。お使いのマシンでRubyインタプリタのパスが /usr/bin/rubyでない場合には先頭の行を書き換えてください。 +
    +
    % cp autofaq.rb /var/www/cgi-bin/faq
    +
    +複数FAQを管理したい場合にはこの手順を複数回行います。
  2. +
  3. データを置くディレクトリを用意する
    +標準では /var/lib/www-dataです。このディレクトリはCGIプログラムの実行権(ユーザーapache(RedHat系)とかwww-data(Debian)とか)で書き込みができるようにしておいてください。 +
    +
    % mkdir /var/lib/www-data
    +% chown apache.apache /var/lib/www-data
    +
  4. +
  5. (必要ならば)設定ファイルを用意する
    +なければデフォルト値が使われます。 +
    +
    % vi /var/lib/www-data/faq.conf
    +
  6. +
+

私はこのプログラムの著作権を行使しません。いかようにもお好きにお使いください。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-038.xhtml b/docs/vol1/xhtml/p-038.xhtml new file mode 100644 index 0000000..010f0de --- /dev/null +++ b/docs/vol1/xhtml/p-038.xhtml @@ -0,0 +1,71 @@ + + + + + +第16章 Rubyで作るCGI + + + + +

Matz Essays Volume 1

+ + +
+

◆ Ruby開発日記 ◆ フリーソフトウェアな生活

+
+

先日某雑誌から取材を受けて、「好きなことをして食べていけるようになった秘密ってなんですか?」と尋ねられました。そのときには「運がよかったからです」と答えたのですが、本当にそうだったのでしょうか。

+

確かに私はフリーソフトウェアの開発に自分の仕事の時間のほとんどを費やしています。「フリーは無料の意味ではない」とはよくいわれます。しかし無料の意味ではなくても、フリーソフトウェアの利用に当たってお金を払う必要がない、つまり無料であるというのも事実です。

+

つまり、どんなに優れたフリーソフトウェアを作っても、そのままでは食べていくことはできない、ということです。今回はフリーソフトウェア開発者がいかに生活を維持するかという点について考察してみましょう。

+
+

副業モデル

+

フリーソフトウェア開発者の生活のうち、一番簡単なモデルを私は「副業モデル」と呼んでいます。つまり、学生なりサラリーマンなりの安定した本来の生活があって、余暇の時間をフリーソフトウェアの開発にあてるというモデルです。フリーソフトウェアの開発はあくまでも趣味であるという立場ですね。

+

ほとんどのフリーソフトウェア開発者の立場がこれです。私自身も1997年に今の会社に転職するまではこうでした。このモデルはリスクが少なく現実的なモデルですが、3つほど欠点があります。

+

まず第一に時間は有限であるという点です。当たり前のことですが1日の長さは一人当たり24時間と決まっています。普通の生活とフリーソフトウェアの両方を行おうと思ったらどうしても時間が足りなくなります。眠る時間を少々削っても得られる時間はたかが知れています。かといって仕事をおろそかにするのはお勧めできませんし(私自身はおろそかにしていたことがあります。悪い見本です)。

+ +

第二の欠点は自己満足以外に見返りがない点です。自分の開発するフリーソフトウェアが使われるようになってくるとユーザーからの反応が得られるようになってきます。また、感謝の言葉も聞けるようになります。それはとてもとてもうれしいことなのですが、しかし、だからといって寝食を忘れてつぎ込んだ時間に対する見返りとして果たしてこれで十分だろうかという思いがよぎることもあります。

+

最後の欠点はプロジェクトの安定性が欠けることです。このモデルの開発はあくまでも余暇の時間に行われますから、開発者の生活の変化に弱くなります。たとえば、学校を卒業して就職し時間がなくなった、結婚して子供が生まれた、などなどの外部的な要因でプロジェクトが中断する例も数々あります。

+
+
+

出版モデル

+

そこで次のモデルが考えられます。それはそのフリーソフトウェアに関連する文章を書いて、その原稿料・印税によって収入を得るというものです。雑誌を買ったり、本を買ったりして、文章に対してお金を払う仕組みは長い時間をかけて確立されているので、これを利用しない手はありません。実際に私は『オブジェクト指向スクリプト言語Ruby』(アスキー)などの書籍や、この原稿のように文章を書くことでいくばくかの収入を得ています。

+

なかなかうまい手のように感じられますが、このモデルにもいくつか欠点があります。

+

最初のそして最大の欠点は、この収入はフリーソフトウェアの開発そのものではなく、その関連文書によるものだということです。フリーソフトウェア開発者はプログラマーですが、ライターではないのでなかなか思うように文章を書けません。私もこの連載で毎月苦労しています。また、関連文章は開発者でなくても書けるという点も見逃せません。

+

二番目の欠点は生活を維持するほどの収入は得られないという点です。一般書籍のように数万部とか数十万部の売り上げがあれば別ですが、コンピュータ関係の書籍はそれほどは売れないものです。印税や原稿料で生活はできません。

+

ということで、「出版モデル」は使えたとしても補完的な役割として利用することになりそうです。

+

このモデルで成功するためには、

+
    +
  • 話題になるフリーソフトウェアを作る

  • +
  • 文章力をつける

  • +
+

の2つが必要になります。あ、後者は私もそんなにはないかな。

+
+
+

寄付モデル

+

FSF(Free Software Foundation)などはこのモデルです。フリーソフトウェアによって恩恵を受けている人々や企業からの寄付を受け、その資金でフリーソフトウェアを開発するというものです。ただ、恵まれたものの当然の行為として寄付が定着しているアメリカのような国ではともかく、あまり寄付や献金が(政治献金は除いて(苦笑))定着していない日本では難しいモデルのように感じます。

+
+
+ +

パトロンモデル

+

となると、フリーソフトウェアの開発だけで食べていくにはどうしたらよいのでしょう。私の考える究極のモデルが「パトロンモデル」です。

+

昔の芸術家はお金持ちの貴族によって養われていたそうです。貴族はその芸術家の作品を自分のものにできる特権と同時に「あの芸術家を支援している」という名誉を得ることができました。

+

芸術家を開発者に置き換えると、フリーソフトウェア開発におけるパトロンモデルになります。1997年以来、私は(株)ネットワーク応用通信研究所の主任研究員として、Rubyの開発とそれからその他フリーソフトウェアを用いた開発への技術支援を仕事として雇用されています。(株)ネットワーク応用通信研究所は私を雇用する代わりに、さまざまなメリットを受けています(そのはずです)。

+

確かにこれはいうほど簡単なことではありません。まだまだ景気は思わしくなく、パトロンになりたがる企業もそれほどたくさんは見出せないかもしれません。しかし、日本におけるフリーソフトウェア開発が軌道に乗るためには、

+
    +
  • 重要なフリーソフトウェア

  • +
  • それを支援したい団体・企業

  • +
+

の両方がもっとたくさん登場する必要があると思うのです。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-039.xhtml b/docs/vol1/xhtml/p-039.xhtml new file mode 100644 index 0000000..641390a --- /dev/null +++ b/docs/vol1/xhtml/p-039.xhtml @@ -0,0 +1,521 @@ + + + + + +第17章 CGIの道具箱 + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay17 +
+

+初等Ruby講座
+CGIの道具箱 +

+
+

[Linux magazine, 2002年10月号]

+
+

CGIアプリケーションを作るときに便利なツールを紹介しています。もっとも、これらのうち、現在でも活用されているのはerubyだけですね。erubyはRailsなどでもViewの実装に使われています。時代を超えたツールですね。

+

「Ruby開発日記」は、「時代はLispに追いついたか」というテーマで話しています。Lispというのはすばらしい特質を多く持った言語ですし、私も大変大きく影響を受けました。が、どうにも人気がありません。本文中では「10年経ったら、世の中がLispを受け入れるかどうかわかる」というようなことを書いていますが、実際20年経ってもLispが当たり前の世界は来ていません。ただ、JavaScriptやRuby, PythonなどLispの特徴を一部受け継いだ言語はだいぶ一般的になりました。また、ClojureというLispベースの言語が登場したのは、以前よりもずっとLisp的な世の中が来ている証拠と言えるかもしれません。いや、やっぱり、そこまでは言えないかな。

+
+
+

今月はCGI開発の不満を解消する便利なツールをいくつか紹介しようと思います。

+
+
+

告白

+
+

白状しますが、私は普段ほとんどCGIプログラミングをしません。ですから、CGIについてはエキスパートどころかほとんど素人の隣といったレベルです。そういう人がCGIの解説記事を書いている時点ですでに問題ありのような気がしないでもないのですが、CGIはRubyを活用したい最も「ホット」な分野でしょうから解説しないわけにもいきません。もし間違いを見つけたらこっそり教えてくださいね。

+

今月はCGIプログラミングに便利な以下のツールを紹介します。

+
+
+
+

cgi/session.rb

+
+

HTTPプロトコルをベースにしたWebアプリケーションを開発するときに問題となる点は大きく分けると2つあります。それは、

+
    +
  • HTMLの表現力の限界

  • +
  • HTTPのステートレス性

  • +
+

です。前者はHTMLではインターフェイス部品がformタグのバリエーションしかないため、ちょっと気の利いたGUI的部品を使うことができないという問題です。これを解決するために古くはJava Applet, 最近はMacromedia Flashなどが活用されています。これらも面白い話題ではあるのですが、本連載の範囲を超えますので扱わないことにします。

+

で、後者の問題ですが、「ステートレス性」とはつまり「HTTPのやりとりには状態がない」という意味です。HTTPのやりとりの基本は、リクエストとそれに対するレスポンスという形になるのですが、HTTPにはこのリクエストはこのレスポンスの続きといった情報がありません。つまり、一連のやりとり(セッションといいます)に固有の状態を持たないわけです。

+

状態がないと何がまずいかというと、Webアプリケーションを構成する各ページ間での情報の共有が困難ということです。たとえば、オンラインショッピングのアプリケーションを作ったときに、ユーザーがどんな商品をいくつ注文したかという「状態」を覚えておかなければ、最後に注文を確認して精算することはできません。

+

そこでなんとかして状態を維持してセッションを実現する必要があります。HTTPを使ったやりとりでセッションを実現する方法は大きく分けると、hiddenフィールドを使ったものとクッキーを使ったものがあります。

+

hiddenフィールドとはformタグの一種で画面には表示されず、よってユーザーが入力したり修正したりすることもないformタグのことです。hiddenフィールドはsubmitボタンが押されると他のフィールドと同様にリクエストの一部としてサーバーに送られます。このhiddenフィールドにあらかじめ状態を格納することで、セッションを実現することができます。

+

一方、クッキーとはレスポンスに付加してブラウザに覚えさせておくことのできる情報です。クッキーに含まれる情報は、

+
    +
  • 有効期限(日付と時刻)

  • +
  • 有効範囲(ドメイン)

  • +
  • 名前(文字列)

  • +
  • 値(文字列)

  • +
+

です。

+

クッキー付きレスポンスを受けたブラウザはクッキーによって指定された範囲のURLに対してリクエストを送るとき、自動的にクッキーを付けて送ります。それによってサーバーは「ああ、このリクエストは以前のレスポンスに対する返答なんだな」ということを認識し、セッションを実現することができます。なお、クッキーに対応していないブラウザがあることには注意する必要があります。

+

しかし、セッションの維持のようなWebアプリケーションにほとんど必須の機能を実現するためにクッキーやらhiddenフィールドのような詳細まで立ち入りたくありません。このようなセッション処理は自動化できないものでしょうか。答えはもちろん「できます」です。そのためのライブラリがRubyに標準添付されているライブラリcgi/session.rbです。cgi/session.rbは上記のhiddenフィールドとクッキーの両方を試して、いずれかの方法でセッションを実現してくれます。

+

セッションを開始するためにはCGIオブジェクトを引数にして、CGI::Session.newを呼び出します。そのリクエストが以前開始されたセッションに所属している場合には、そのセッションに対応したセッションオブジェクトを返します。またリクエストがいずれにセッションにも所属していな場合には、新たにセッションを開始し、そのセッションに対応するセッションオブジェクトを返します。

+

CGI::Session.newにはCGIオブジェクトの他にSessionの設定を行うオプションがハッシュの形で設定できます(表17.1)。こんな感じですね。

+ +
+
session = CGI::Session.new(cgi, "tmpdir"=>"/var/tmp")
+
+

これによって、セッションのキーとして使う名前を指定したり、実際にデータを読み書きするバックエンドのクラスを指定したりできます。バックエンドにはデフォルトではファイルに対して読み書きを行うCGI::Session::FileStoreクラスが用いられます。

+
+

表17.1●CGI::Session.new のオプション

+ + + + + + + + + + + + + + + + + + + + + + + + + +
オプション説明
session_keyセッション(_session_key
session_idセッションID(自動生成)
new_session新規セッション(false
database_managerバックエンドクラス(CGI::Session::FileStore
session_pathクッキーのpath(無指定)
+
+

CGI::Session自身が解釈しなかったオプションは、そのままバックエンドに渡されます。デフォルトであるCGI::Session::FileStoreクラスは表17.2に示すオプションを受け付けます。

+
+

表17.2●CGI::Session.FileStoreのオプション

+ + + + + + + + + + + + + +
オプション説明
tmpdirファイルを置くディレクトリ(/tmp
prefixファイルのプレフィックス(なし)
+
+

sessionというセッションオブジェクトが存在するとき、

+
+
session[名前]
+
+

という形式で情報を取り出すことができます。事前にその名前で情報が保存されていない場合にはnilが返ります。情報の設定は、

+ +
+
session[名前] = 値
+
+

になります。「名前」も「値」も文字列を指定する必要があります。

+

セッションに関するアクセスを完了した場合には、

+
+
session.close
+
+

を実行します。session.closeは(実際にデータを処理するバックエンドによって異なりますが)、データの書き込みなどを行います。ほとんどの場合にはsession.closeを行わなくても、ガベージコレクタが後片付けをしてくれます。

+

もうセッションは完了した、このセッションに属する情報は使わないというときには、

+
+
session.delete
+
+

を実行します。session.deleteはこのセッションに属する情報を完全に消去してしまいますので、その情報を取り戻すことはできません。

+

cgi/session.rbの使い方の例をリスト7.1に示します。

+
+

リスト17.1●cgi/session.rbの使用例

+
#! /usr/bin/ruby
+
+require 'cgi/session'
+
+cgi = CGI.new("html4Tr")         # CGIオブジェクトを生成
+session = CGI::Session.new(cgi)  # セッションオブジェクトを生成
+if session['counter']            # counterという情報を取得
+  counter = Integer(session['counter'])
+else
+  counter = 1
+end
+
+# HTMLを出力
+cgi.out("charset"=>"euc-jp") {
+  cgi.html {cgi.head{cgi.title{"cgi/session example"}} +
+      cgi.body{cgi.h1{"cgi/session example"}+
+        cgi.p{if counter == 1 then
+          "最初のアクセスです"
+        else
+          sprintf "%d回目のアクセスです", counter
+        end
+      } + cgi.form('action'=>File::basename(cgi.script_name)) {
+        cgi.submit
+      }
+    }
+  }
+}
+session['counter'] = counter + 1 # 情報をセッションに保存
+
+
+ +

リスト17.1のプログラムはセッションにcounterという名前の情報を保存します。このCGIを以前に起動したことのないブラウザからこのCGIを起動すると「最初のアクセスです」と表示します。以後、submitボタンを押すか、URLを指定して直接アクセスすると、アクセスした回数を「◯回目のアクセス」というように表示します。

+

cgi/session.rbはデフォルトではセッションの情報を/tmpのファイルに保存します。明示的にセッションをdeleteメソッドで削除しない限りこのファイルは残ってしまいます。実運用時にはセッション情報ファイルの定期的な掃除が必要になるでしょう。あるいはファイルではなく、データベースなどに情報を格納するバックエンドを利用する方法もあります。具体的には『Rubyアプリケーションプログラム』(オーム社、ISBN4–274–06461–1)などに詳しい使い方が解説されています。この本ではPostgreSQLを使ったバックエンドの作り方も解説されています。

+

cgi/session.rbは(CGI関連のプログラムにしては珍しく)私が作ったものです。

+
+
+

mod_ruby

+
+

前々回にも説明しましたが、HTTPサーバーから見てCGIの実行は以下の手順で行われます。

+
    +
  1. HTTPリクエストを受け付ける
  2. +
  3. リクエストのURLを解析、CGIであるとわかる
  4. +
  5. CGIプログラムの起動(環境変数などを設定する)
  6. +
  7. CGIプログラムの出力の受け付け
  8. +
  9. ヘッダーの追加、調整などを行う
  10. +
  11. クライアントにレスポンスを送る
  12. +
+

この中で一番コストが高いのが実は(3)の「CGIプログラムの起動」です。新しいプロセスを起動することはOSにとって大仕事です。プログラムファイルを読み込み、新しいコンテキストを作り、プロセステーブルに登録し、スケジューリングを開始するなどやることはたくさんあります。とはいえ、普段の状態ではプロセス起動にかかる時間はコストが高いといってもコンマ1秒以下で、気になることはほとんどありません。しかし、負荷の高いサーバーでは毎秒数十から数百のCGIリクエストが発生する場合があります。このような場合にはOSにかかる負荷は相当のものがあります。

+

それを避ける方法はいくつかありますが、そのうちの1つがmod_rubyと呼ばれるものです。mod_rubyは、ApacheにRubyインタプリタを組み込んで、Rubyプログラムの実行をApacheの内部で実行するものです。これで毎回プロセスを起動する必要がなくなり、かなり負荷を軽減できます。

+ +

実際に簡単なCGIプログラムを対象にベンチマークを取ってみた結果を表17.3に示します。明らかに性能の違いがあることがわかります。ベンチマークに使ったCGIはHTML文字列を出力するだけのもので、処理そのものの負荷は非常に低いので、この数値はあくまでも一例にすぎません。

+
+

表17.3●mod_rubyの効果

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CGImod_ruby
並列度処理時間
(秒)
リクエスト/
転送レート
(バイト/秒)
処理時間
(秒)
リクエスト/
転送レート
(バイト/秒)
119.3395.1716.686.81414.6847.34
217.4115.7418.536.54915.2749.26
518.5765.3817.376.07516.4654.16
1018.0695.5317.857.22113.8545.57
+
+

いずれにせよ、アクセス数の大きなWebサーバーではmod_rubyは大きな効果がありそうだということがわかると思います。

+

mod_rubyは前田修吾さんが開発されました。

+
+
+

mod_rubyのインストール

+
+

もしあなたがDebianをお使いでしたら、それはラッキーです。Debianにはmod_rubyが標準でパッケージ化されていますので、パッケージシステムが適切にセットアップされていれば、必要なのは、

+
+
apt-get install libapache-mod-ruby
+
+

だけです。mod_rubyに限らずDebianではRuby関連のパッケージが充実しているので、たいていのものがapt-getだけでインストールできます。また、Vine Linuxでは、Vine Plusにmod_rubyのパッケージがあります。

+

もし、それ以外のシステムをお使いでしたら、自前でインストールする必要があります。自前でインストールといっても決して難しくありません。簡単な手順に従うだけです。

+
    +
  1. ダウンロード
    +現在の最新版はmod_ruby 1.0.0 です。
    +  http://www.modruby.org/archive/mod_ruby-1.0.0.tar.gz
    +を入手してください。
  2. +
  3. 展開
    +tar.gzファイルですから、tarを使って展開します。 +
    +
    % tar zxf mod_ruby-1.0.0.tar.gz
    +
    +
  4. +
  5. configureとmake
    +configure.rbがお手元のシステムの状態に合わせてコンパイル条件を決定してくれます。そのためにはapxsの位置を教えてやる必要があります。apxsはApache Extension Toolでモジュールがコンパイルできる条件を教えてくれるコマンドです。 +
    +
    % ./configure.rb --with-apxs=/usr/bin/apxs
    +% make
    +
    +
  6. +
  7. インストール
    +make installで終わりです。ただし、rootになっておく必要があります。 +
    +
    % su
    +# make install
    +
    +
  8. +
  9. mod_rubyの設定
    +インストールしただけではmod_rubyは使えません。Apacheの設定ファイルでmod_rubyを有効にする必要があります。具体的にはhttpd.conf
    リスト17.2の記述を追加する必要があります。
    + httpd.confを書き換えただけではApacheの設定は有効になりませんから、Apacheを再起動させてください。
  10. +
+
+

リスト17.2●mod_ruby用Apacheの設定

+
# モジュールのパスは環境に合わせて調整する
+LoadModule ruby_module /usr/lib/apache/1.3/mod_ruby.so
+
+<IfModule mod_ruby.c>
+  RubyRequire apache/ruby-run
+
+  # *.mrbをRubyスクリプトとして実行する
+  <Files *.mrb>
+  SetHandler ruby-object
+  RubyHandler Apache::RubyRun.instance
+  </Files>
+</IfModule>
+
+
+
+
+

mod_rubyを利用する

+
+

さて、上の設定が終わればmod_rubyを使うことができます。mod_rubyを使うに当たって注意すべき点をあげておきます。

+
+ +

NPHモード

+

mod_rubyで実行されるプログラムはNPHモードで実行されます。NPHとは(たぶん)non-parse-headerの略で、サーバー側でヘッダーの処理を行わないという意味です。NPHモードではプログラム側でクライアントに送るすべてのヘッダーを用意してやる必要があります。特にレスポンスの先頭にあるHTTPステータスラインは必須です。HTTPステータスラインは以下のような行です(取得に成功した場合)。

+
+
HTTP/1.1 200 OK
+
+

ただし、cgi.rbはプログラムがmod_rubyによって実行されていることを自動判別して、対応してくれますので、ヘッダーの出力をcgi.rbに任せていればこのことについて気にする必要はありません。

+
+
+

同一プロセス

+

mod_rubyはその動作原理上、RubyプログラムはWebサーバープロセス内で実行されます。ということは、$$ などで得られるプロセスidも同一になります。プロセスidが毎回異なることを仮定しているプログラムは動作が変わる可能性があります。

+
+
+

同一インタプリタ

+

mod_rubyは複数のRubyプログラムを同一のインタプリタで実行します。その結果、

+
    +
  • すでにrequireされたプログラムは再びrequireしない

  • +
  • グローバル変数の値が残ってしまう

  • +
  • プログラムが終了しても生き残る(GCで回収されない)オブジェクトがある

  • +
+

などの現象が起きます。

+

requireの問題などでmod_rubyを利用するとデバッグが難しくなるので、デバッグ中はCGIでテストして、ある程度安定してからmod_rubyでの実行に切り替えるというやり方が賢そうです。

+
+
+
+

CGIプログラミングの不満

+
+

先にも述べましたとおり、私はめったにCGIプログラムを作らないのですが、たまに作ると嫌になることが1つあります。それはCGIというものは基本的にHTMLを出力するものなので、プログラム中にprint文が多用されて、ロジックと混じり合ってしまうことです。特にテンプレート的なHTMLがあって、その一部だけを差し替えるようなプログラムではその傾向が顕著です。

+

プログラミングにおける不満は重要なものです。不満があれば、それを解決してよりプログラミングを快適にするツールが必要とされているという意味です。もし仮にそのようなツールがすでに存在していなければ、新しいツール開発の動機となります。不満こそはソフトウェア進化の原動力なのです。

+

この問題に対しても対応するツールがいくつかあります。

+
+
    +
  • cgi.rbによるHTML生成

  • +
  • ヒアドキュメントの利用

  • +
  • eRuby

  • +
  • IOWAやWarlusのようなフレームワーク

  • +
+

それぞれについて簡単に説明しておきます。cgi.rbによるHTML生成機能はRubyのプログラムの見かけでHTMLを作るというものです。HTML生成機能はリスト17.1でも多用されています。

+
+
cgi.html {cgi.head{cgi.title{"cgi/session example"}} +
+  cgi.body{cgi.h1{"cgi/session example"}+
+    cgi.p{sprintf "%d回目のアクセスです", counter} + 
+    cgi.form('action'=>File::basename(cgi.script_name)) {
+      cgi.submit
+    }
+  }
+}
+
+

HTML生成機能は内部で大量の文字列の生成と結合を行いますので、少々効率が悪いのが欠点です。また、記述量が増えてしまうこともあるのも欠点といえば欠点でしょう。

+

ヒアドキュメントはRubyの文字列表現の1つです。これは「<<EOF」のようなマーカーの部分が、その次の行からマーカーが示す終端行までの複数行に置き換えられるというものです。

+
+
print <<EOF
+<p>これはヒアドキュメントの例です<br>
+「<<」に続くマーカーと同じ文字列の行までが文字列にな
+ります。この中では式展開も有効です
+EOF
+
+

ヒアドキュメントによって、複数行にわたる文字列をすっきりと表現できます。大量のHTMLの出力には向いているでしょう。式展開を使って値の埋め込みが行えるのも気が利いています。

+

eRubyはembedded Ruby(埋め込みRuby)の略です。埋め込みと言っても他のプログラムにRubyを組み込むことではなくて、HTMLのようなテキストの一部にRubyコード(の断片)を埋め込むことです。ちょうど文字列の中の式展開と同じようなことができるというわけです。

+

最後のIOWAやWarlusはHTMLテンプレートとRubyコードを完全に分離してしまうWebアプリケーションフレームワークです。これらは少々規模が大きくなってしまうので、今月は解説しません。以下のURLを参照してください。

+
    +
  • IOWA:  http://beta4.com/iowa/

  • +
  • Warlus: http://www.brain-tokyo.jp/research/koutetu/walrus/

  • +
+
+
+ +

eRuby

+
+

今月解説するのはこのうちeRubyです。

+

eRubyは文書に、

+
+
<% rubyコード %>
+
+

といったタグを埋め込んで、内容がダイナミックに変化するテキストを作るためのものです。また結果の出力には、

+
+
<%= 式 %>
+
+

というタグを使います。式の結果が文字列としてテキストに埋め込まれます。タグ以外の部分はそのまま印字されます。eRubyのスクリプトはリスト17.3のようになります。

+
+

リスト17.3●eRubyサンプル

+
<%
+  # erubyではcharsetの設定が必要
+  ERuby.charset = "euc-jp"
+%>
+<HTML>
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html;charset=EUC-JP">
+<TITLE>eRuby sample</TITLE>
+</HEAD>
+<BODY>
+<H1>eRubyサンプル</H1>
+<P>
+こんにちわ<BR>
+現在の時刻は<%= Time.now %>です<BR>
+</BODY>
+</HTML>
+
+
+

ですから、eRubyは「<%」と「%>」という文法を持つ一種の言語ということができます。このeRuby言語を解釈する処理系は複数ありますが、mod_rubyと同じく前田修吾さんによって実装されたerubyについて紹介しましょう。少々ややこしいですが、ここからは「eRuby」が言語名、「eruby」がプログラム名です。

+
+
+ +

erubyのインストール

+
+

erubyのインストールもDebianなら、

+
+
apt-get install eruby
+
+

だけです。Red Hat Linux 7.3やVine Linux 2.5には、erubyのRPMパッケージが含まれています。

+

ソースからインストールする場合には

+
    +
  • http://www.modruby.net/archive/eruby-0.9.8.tar.gz

  • +
+

からダウンロードしてから展開し、configure.rbを実行してからmake、そしてmake installします。

+

インストールできたら、試しにリストリスト17.3erubyにかけてみましょう。ここではリストリスト17.3の内容がsample.rhtmlに格納されていることとします。eRubyのファイルの拡張子はrhtmlにするのが慣習のようです。

+
+
eruby sample.rhtml
+
+

実行結果はリスト17.4のようになります。

+
+

リスト17.4●eruby実行結果

+
<HTML>
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html;charset=EUC-JP">
+<TITLE>eRuby sample</TITLE>
+</HEAD>
+<BODY>
+<H1>eRubyサンプル</H1>
+<P>
+こんにちわ<BR>
+現在の時刻はSat Aug 10 15:49:07 JST 2002です<BR>
+</BODY>
+</HTML>
+
+
+

<%=」と「%>」で囲まれた部分がコードを評価した文字列と置き換えられていて、「<%」から「%>」の部分は削られていることがわかると思います。

+
+
+ +

Apacheからerubyを使う

+
+

さて、erubyのインストールができたら、今度はWebページにeRubyを使うためのApacheの設定が必要です。

+

まず .rhtmlという拡張子のファイルをeRubyで記述されたCGIとして使うための設定は、以下のとおりです。まずhttpd.confに以下の行を追加します。

+
+
AddType application/x-httpd-eruby .rhtml
+Action application/x-httpd-eruby /cgi-bin/eruby
+
+

それからeruby/usr/local/bin/erubyとか)から/cgi-bin/erubyへのシンボリックリンクを用意します。これでApacheをリスタートさせれば .rhtmlファイルはerubyで処理されるはずです。

+

実はmod_rubyにはerubyの機能が最初から組み込まれています。さすが同じ作者の作品だけのことはあります。ですから、mod_rubyをインストールすればそのままeRubyを使うこともできるわけです。mod_rubyを利用してeRubyを処理するためにはhttpd.confに以下の設定を追加します。

+
+
<IfModule mod_ruby.c>
+  RubyRequire apache/eruby-run
+
+  # *.rhtmlをeRubyファイルとして扱う。
+  <Files *.rhtml>
+  SetHandler ruby-object
+  RubyHandler Apache::ERubyRun.instance
+  </Files>
+</IfModule>
+
+

この設定を有効にしてから拡張子 .rhtmlを持つファイルにアクセスするとmod_rubyを使ってeRubyの解釈が行われます。

+
+
+

ERb

+
+

一方、咳さんのeRuby実装、ERbのほうは、

+
    +
  • http://www2a.biglobe.ne.jp/~seki/ruby/erb-1.4.3.tar.gz

  • +
+

からダウンロードして、install.rbを実行するだけです。ただ、ERbにはそのままコマンドとして実行できるファイルがないみたいなので、リスト17.5のような内容のファイルを /usr/bin/erbなどいう名前で用意するといいでしょう。

+ +
+

リスト17.5●erub

+
#! /usr/bin/ruby
+
+require 'erb/erb'
+require 'erb/main'
+
+ERbModule.run(ERb)
+
+
+

もちろん、chmodを使って実行権限を与えるのをお忘れなく。これで、

+
+
erb sample.rhtml
+
+

でeRubyファイルの展開ができるようになります。でも、先ほどのサンプルを実行する場合にはerubyにしかないERubyモジュールへのアクセスをコメントアウトしておきましょう。

+
+
+

eruby + mod_ruby ≧ PHP

+
+

PHPという言語があります。PHPという名前はPHP: Hypertext Preprocessorの略だそうです。再帰してますね。PHPの特徴は「HTMLに埋め込める」ことと「mod_phpによりApacheに組み込んで性能を稼げる」ことだそうです。このコンセプトは「eruby + mod_ruby」にそっくりではありませんか?

+

しかも、Rubyの強力なクラスライブラリやRAAに増え続ける周辺ライブラリの充実を見れば、Rubyを知っている人がPHPを覚える必要はそれほどなくなったのではないかと思います。

+
+
+

まとめ

+
+

今月はWebプログラミングを便利にするツールについていくつか紹介しました。これらのツールは個別のページの作成の支援を行いますが、これをもうちょっと推し進めて「Webサイト作成支援」とでも呼ぶべきツール、あるいはフレームワークと呼ぶようなものも存在します。今後はそのようなフレームワークについても紹介したいと思っています。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-040.xhtml b/docs/vol1/xhtml/p-040.xhtml new file mode 100644 index 0000000..b4a98b2 --- /dev/null +++ b/docs/vol1/xhtml/p-040.xhtml @@ -0,0 +1,94 @@ + + + + + +第17章 CGIの道具箱 + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ 時代はLispに追いついたか

+
+

Lispというプログラミング言語があります。

+

1958年以来、Lispはプログラミング言語の独立峰としてそびえています。それが色あせないのは、数学をベースにしているからだと思われます。Lispは広く使われることはありませんでしたが、そこで生まれた概念は「知る人ぞ知る」奥義としてコンピュータサイエンスの世界で受け継がれていたのです、ここ40年ほどの間。

+
+

みんなLispから教わった

+

Lispから始まって、やっと最近になって一般の人も知るようになった概念はたくさんあります。たとえば以下のようなものがあげられます(これらはみんなRubyにもあります)。

+
+

スタティックスコープ

+

Lisp以外の初期のプログラミング言語にはグローバル変数しかありませんでした。当時はLispだけがローカル変数(関数に閉じた変数)という概念を持っていました。

+

再帰

+

ローカル変数のない言語には関数が自分自身を呼び出す再帰の実現は不可能です。

+

多重継承

+

クラスや継承の概念を生んだのはLispではなくSimulaですが、複数のスーパークラスを持つ多重継承を発明したのは(おそらく)Lisp界の住人です。Rubyも採用しているMix-inという考え方もLispから受け継いでいます。

+

ガベージコレクション

+

使われなくなったデータを自動的に回収してくれる便利な機能、ガベージコレクションが世に広く知られるようになったのはJava以降でしょうか。しかし、Lispは1960年代からこの機能を持っていました。実に世の中は40年遅れていたわけです。

+

例外

+

これもJavaによって知られるようになった機能ですが、Lispではやはり60年代(後半?)からあったようです。

+

動的な型

+

昔の言語といえば、変数や式に型がある静的な型か、あるいは、いっそ型がないBCPLのようなものが主流でした。しかし、Lispは一貫して型は変数ではなくデータそのものにあるという考えで設計されていました。

+
+
+
+ +

世の中はLispに進むのか

+

では、世の中が40年以上をかけてLispの隠された機能に追いついてきたのならば、世の中はこのままLispの方向に進み続けるのでしょうか?

+

私はそうは思いません。

+

Lispには上で紹介しなかった「知られざる奥義」があり、そしてそれは今後も他の言語には伝わらないだろうと思うからです。その奥義は「データとプログラムの同一視」です。Lispのプログラムはこんな感じです。

+
+
(defun fact (n)
+  (if (= n 0)
+        1
+    (* n (fact (- n 1)))))
+
+

Lispのプログラムはかっこがとても多いのですが、単純な階乗のアルゴリズムですから、意味を取るのはそれほど難しくないと思います。実はこの「プログラム」はそのまま「データ」なのです。Rubyの式で書くとこんな感じでしょうか。

+
+
[:defun, :fact, [:n],
+  [:if, [:= :n 0],
+    1,
+    [:*, [:fact, [:- :n 1]]]]]
+
+

Rubyだとちょっと醜いですが、Lispではこのようにシンボルとリストからなるデータ構造でプログラムが表現されています。これにより、

+
    +
  • プログラムがプログラムを生成すること

  • +
  • プログラムの構造を解析したり、一部変更したりすること

  • +
+

の両方が容易になります。前者だけならRubyでもevalを使うことで実現できますが、後者は現実的ではありません。このプログラムとデータの同一視により、Lispにはマクロ機能という言語自身をどんどん拡張する機能があります。

+

しかし、これほど優れている(ように見える)機能も広く受け入れられることはないでしょう。私はそう感じます。

+

その理由は2つあります。

+
+

かっこが多いと見栄えが悪い

+

Lispのプログラムは非常にかっこが多用されます。この「普通でない」見栄えは人々から敬遠されてきた理由の1つです。これは今後も変わらないでしょう。外見は人気の重要な要素です。

+

マクロは強力すぎ

+

マクロは強力ですばらしい機能ですが、見方を変えると、Lispという大きな枠組みの中でいくつもの専用言語を作っていることでもあります。これは「常識がどこでも通用する」という一貫性とは逆行する性質です。かなり頭のいい人でないとついていけないと思います。言語は「普通の人」のためにあるのです。

+
+
+

さて、私の予想は当たるでしょうか。それとも参考文献に示すように世の中はLispを受け入れるのでしょうか。それはあと10年経ったら明らかになる、かもしれません。

+
+

参考文献

+

「普通のやつらの上を行け — Beating the Averages —」
+Paul Graham著, Shiro Kawai訳
+http://www.shiro.dreamhost.com/scheme/trans/beating-the-averages-j.html

+

「人気の言語を作るには — Being Popular —」
+Paul Graham著, Shiro Kawai訳
+http://www.shiro.dreamhost.com/scheme/trans/being-popular-j.html

+
+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-041.xhtml b/docs/vol1/xhtml/p-041.xhtml new file mode 100644 index 0000000..884294b --- /dev/null +++ b/docs/vol1/xhtml/p-041.xhtml @@ -0,0 +1,457 @@ + + + + + +第18章 ファイル処理 + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay18 +
+

+初等Ruby講座
+ファイル処理 +

+
+

[Linux magazine, 2002年11月号]

+
+

当時、新しいデジカメEXILIMを買ったのがうれしくて書いた記事ですね。そういえば、あのカメラはどこにやったかな。捨ててないと思うんだけど。で、記事のほうはデジカメをUSBマウントしてPCにコピーするツールを開発することを通じて、Rubyのファイル操作機能を学ぶというテーマでした。現在では、スマートフォンで写真を撮って、自動的にGoogle PhotoなりiCloudなりにバックアップされますが、当時はこういうちょっとしたツールがありがたかったものです。

+

「Ruby開発日記」は「プログラミング言語の人気の厳選は外見で決まる」という仮説の披露です。20年経っても証明はできていないのですが、相変わらず間違ってはいないのではないかと考えています。本文中、「日本語20冊、英語6冊、ドイツ語3冊、フランス語1冊」出版されているとありますが、現在では何冊あるのでしょうか。昔はRuby関連本が書かれるたびに完成した本を頂いたりしたものですが、最近はそういうこともなく、仮にそうしたとするとあっという間に本棚があふれてしまうことでしょう。当時は、Rubyを扱った本が近所の本屋に並ぶのを見るのが夢でしたが、それも早々に叶いました。

+
+
+

今月はスクリプト言語の原点に立ち返ってファイル処理について考えてみます。

+
+
+

散財自慢

+
+

愛用のデジカメが故障したのを機会に、新しいデジカメを買うことにしました。貧乏性の私にしては珍しい散財です。就職以来、貧乏はだいぶ改善されましたが、貧乏性(貧乏症)はなかなか直りません。学生時代は貧しかった。

+
+

で、買ったのはCasioのEXILIMです(図18.1)。このカメラは124万画素しかありませんが、日常のスナップには十分です。しかも、

+
    +
  • 小さい(名刺サイズ)

  • +
  • 機敏(起動1秒、シャッター0.01秒)

  • +
+

という点が気に入りました。前のカメラは300万画素クラスでしたが、オートフォーカスやらフラッシュのチャージやらにとかく時間がかかり、反応の遅さが気になっていたからです。これだけ機敏に動作すると撮ろうと思ったものをタイミングを逃さず撮ることができます。便利だ。

+

買ったその日に200万画素になったEXILIMの新機種が発表されるというオチまでついてしまいましたが、いいんです、気に入ってるんだから。

+
+ +
+ fig1801 +
+

図18.1●Casio EXILIM EX-M1

+
+
+
+

Linuxとの連携

+
+

このEXILIMというカメラにはUSBクレードルが付属していて、これに乗せると充電とPCとの接続ができます。USB接続についてはUSBマスストレージクラスに対応しているので、Linuxからも利用することができます。

+

ただし、私のマシンにはEXILIMのプロダクト番号がまだ登録されておらず、そのままではUSBコネクタを指した時点でエラーになってしまいました。USBの設定ファイルである /etc/usbmgr/usbmgr.confを見ると必要なのはベンダーIDとプロダクトIDのようです。そこで、/var/log/syslogのエラーメッセージから、

+
+
Aug 29 19:45:04 ev usbmgr[18644]: vendor:0x7cf product:0x1001
+
+

という行を見つけ出し、それを参考にして/etc/usbmgr/usbmgr.confリスト18.1の記述を追加しました。

+
+

リスト18.1●/etc/usbmgr/usbmgr.confへの追加

+
# Casio Exilim
+vendor 0x7cf product 0x1001 module scsi_mod , sd_mod , usb-storage
+
+
+

これでroot権限で、

+
+
# mount -t msdos /dev/sda1 /mnt
+
+ +

とすればファイルシステムとしてマウントできした。めでたし、めでたし。そして、/etc/fstabというファイルにリスト18.2の記述を追加しておけば、root権限がなくても一般ユーザーで、

+
+
$ mount /mnt
+
+

と実行するだけでマウントできるようになります。

+
+

リスト18.2●/etc/fstabへの追加

+
/dev/sda1 /mnt msdos defaults,user,noauto  0  0
+
+
+

私はUSB接続のデジカメに関する知識はそれほどないのですが、この方法はEXILIM以外にも富士フィルムのFinePix601でも有効でしたから、たぶん普遍的なのではないかと思います。聞くところによるとDCFというデジタルカメラのファイルシステムの規格があるのだそうです。FATファイルシステムを使うのも、ディレクトリの構成やファイル名がほぼ共通なのも、その規格に従っているのでしょう。

+
+
+

画像管理

+
+

これでデジカメのメモリがファイルシステムとして見えるようになりました。画像はマウントポイントの下にdcimというディレクトリがあって、その下に100casioというディレクトリがあり、その下にcimg0001.jpgのような名前のファイルが並んでいます。

+

要するに普通のディレクトリとファイルですから、後はcpでファイルをコピーしたり、rmで削除したりと普通のファイルと同様に操作できます。また、gimv(図18.2)のようなイメージビューアを使うのも1つの方法でしょう。

+
+ +
+ fig1802 +
+

図18.2●gimv

+
+

とはいえ、撮った画像がたまってくると管理が面倒になってきます。そこで私はリスト18.3のような簡単なシェルスクリプトを使っていました。ええ、私でもときどきはシェルも使うんです。

+ +
+

リスト18.3●画像管理シェルスクリプト

+
#! /bin/sh
+
+ls -l --time-style=long-iso /mnt/dcim/*/* | awk '{print $6" "$8}' | while read date file; do
+  test -d $date || mkdir $date
+  echo cp $file $date/`basename $file`
+  cp -p $file $date/`basename $file`
+done
+
+
+

私はこのスクリプトにimportという名前を付けていました。デジカメをマウントした状態でこのスクリプトを実行すると、写真を撮った日付のディレクトリにファイルを保存してくれるというわけです。小さいけれどなかなか便利なスクリプトです。

+

ところが、EXILIMを使うようになって困ったことが出てきました。EXILIMは今までの画像の番号を覚えていてくれないので、一度カメラから画像を削除してしまうとまたcimg0001.jpgから番号を振ります。ですから、たとえばメモリが足りなくなって画像を一度PCに転送してからカメラから削除すると、同じ名前の画像が複数できてしまうのです。前のカメラは今まで撮った番号を覚えていてくれたんだけどなあ。しかたがありません。新しい管理プログラムを書くことにしましょう。仕様は以下のようなものを考えました。

+
    +
  • マウントされていなければ自動的にマウントする

  • +
  • 扱うファイルの拡張子は .jpg, .wav, .avi, .mpg(EXILIMはmpgは扱えませんが)

  • +
  • デジカメ内のファイルを撮影日(2002-10-08のような形式)の名前の付いたディレクトリにコピー

  • +
  • ファイルにはその日のディレクトリ内で重複しない名前を付ける

  • +
  • ただし、.jpgファイルと同じファイル名(拡張子以外の部分)を持つ .wavファイルがあったとき、それは .jpgファイルに対する音声メモなので拡張子以外は同じままにしなければならない

  • +
  • デジカメの付けたファイル名は信頼できない。異なるファイルに対して番号部分は自分で割り当てる

  • +
+

このプログラムも先ほどのスクリプトの延長でシェルで書き始めたのですが、だんだんロジックが複雑になってきて、手に負えなくなってきました。この辺がシェルの限界でしょう。しかし、私たちにはシェルよりずっとよいRubyがあるじゃありませんか。

+
+

いや、今まで忘れたんだけど。

+
+

「Rubyの作者がそんなことじゃだめじゃん」という声が聞こえてくるようです。おっしゃるとおりです。すいません。

+

そこで気を取り直してRubyで書いてみることにしました。Rubyにはシェルよりもずっとマシな制御構造がありますし、道具もいろいろそろっています。意外と簡単に書けました。それでもコメントや空行も入れると90行くらいになりました。元のスクリプトが7行だったのに比べるとずいぶん複雑になったものです。

+
+
+

手品のタネ

+
+
+

今回のプログラムで一番工夫が必要なのは、要件の中にある、

+
    +
  • 異なるファイルに対して番号部分は自分で割り当てる

  • +
+

点です。

+

これを実現するためには、ファイル名に頼らないで、ファイル内容の同一性を判定する必要があります。とはいえ、127万画素の1280×960の画像ファイルはだいたい250Kバイト近くありますから、ファイルを全部読み込んですべての内容の比較をするのはコストが高すぎます。

+

同一性判定にほしいのは、ファイルの内容によって違う結果になり、

+
    +
  • 重複の危険性が無視できるくらい小さく

  • +
  • しかも値のサイズは小さく

  • +
  • できれば計算量も少ない

  • +
+

というような値です。このような値がもしあれば、それを各画像ファイルの「指紋」のように使って、比較することができます。

+

しかし、そんな都合のよい値を計算する方法がありえるものでしょうか。

+
+

ええ、あるんです。

+
+

実は上記のすべてを満たした値の計算方法が存在していて、それは一般に「ハッシュ値」と呼ばれています。「ファイルの内容を反映した短い値」という意味で「ダイジェスト値」と呼ばれることもあります。ハッシュ値を計算するアルゴリズムはいくつかあるのですが、ここではRubyに最初から添付されている「MD5」アルゴリズムを利用することにします。

+

MD5アルゴリズムはRFC 1321で定義されています。これはもともとは公開鍵暗号の署名などに使う目的で開発されました。つまり、ある人が本当にその文を書いたことを証明するために、メッセージのハッシュ値を計算し、その値をその人の秘密鍵で暗号化します。そしてその秘密鍵で暗号化されたハッシュ値をそのメッセージに対する「署名」としてメッセージと一緒に送ります。

+

メッセージと署名を受け取った人は、署名を相手の公開鍵で復号化し、得られたハッシュ値とメッセージのハッシュ値が等しいかどうかをチェックします。

+

もし、メッセージが相手を名乗った第三者からのものであれば、署名をその人の公開鍵で正しく復号化できない(のでハッシュ値は一致しない)はずです。もし、メッセージが配送の途中で改ざんされていれば、メッセージのハッシュ値と署名から得られるハッシュ値が一致しないはずです。

+

つまり、手元で計算したハッシュ値と復号化したハッシュ値が一致したということでメッセージが本人からのもので書き換えられていないことが保証されるわけです。

+

しかし、MD5がハッシュ値として持っている以下のような便利な性質は、今回の目的である同一性検査に最適です。

+
+
    +
  • 結果は16バイトの整数列

  • +
  • データが1ビットでも違うと結果がまったく異なる

  • +
  • 重複の可能性はかなり低い

  • +
  • 計算量は割と小さい

  • +
+

同一性検査以外にもMD5を使うことができます。たとえば、cgi/session.rbではユニークなセッションIDを作るために16進表記のMD5を使っています。実際には16進表記のMD5の32文字は長すぎるので前半16文字だけ使っています。半分に切り詰めても重複する可能性は十分に低いようです。

+
+
+

下準備

+
+

それでは例によって実際のプログラムを解説する前に、利用する既存のライブラリについて説明しておきます。今回使おうと思っているのは以下のものです。

+
    +
  • digest/md5

  • +
  • ftools

  • +
+

digest/md5は先ほど説明したMD5アルゴリズムを実現するクラスライブラリです。

+

一方、ftoolsはシェルで扱うようなファイル操作を強化するライブラリです。

+
+
+

digest/md5

+
+

digest/md5はアルゴリズム部分はL. Peter Deutschさんの作です。DeutschさんはGhostscriptの作者でもありますが、その他にもLispやSmalltalkの実装などで多大な業績があり、私にとっては尊敬する雲の上の人の一人です。昨年、OOPSLAというカンファレンスでご本人にお会いしたときには緊張して言葉が出なかったくらいです。Rubyのことはご存じでしたが、最近はPythonを使っているとのことでした。残念。

+

Rubyインターフェイス部分の作者はknuさんことAkinori MUSHAさんです。

+

Digest::MD5クラスのメソッドを表18.1に示します。

+
+

表18.1●Digest::MD5クラスのメソッド

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
クラスメソッド説明
Digest::MD5.digest(data)dataのハッシュ値を返す
Digest::MD5.hexdigest(data)dataの16進表記ハッシュ値
インスタンスメソッド説明
md5.update(data)dataを追加
md5 << datadataを追加
md5.digestハッシュ値
md5.hexdigest16進表記ハッシュ値
+
+

MD5値の求め方には2通りあります。1つはMD5オブジェクトにデータをどんどん追加しながらハッシュ値を計算し、最後にその計算結果を取り出す方法です。具体的には以下のようになります。

+
+
md5 = Digest::MD5.new
+md5.update(data)
+sum1 = md5.digest     # 16バイトのハッシュ値
+sum2 = md5.hexdigest  # 16進表記のハッシュ値(32文字)
+
+ +

updateは途中に何回に分けて呼んでもかまわないので、たとえばファイルを1行ずつ読み込んでupdateを呼んでも、ファイル全体を一度に呼んでupdateを呼んでも同じ結果になります。

+

もう1つのやり方は、クラスメソッドを使って一度に計算する方法です。クラスメソッドは内部的にMD5オブジェクトを生成して計算を行います。

+
+
sum = Digest::MD5.digest(data)
+sum = Digest::MD5.hexdigest(data)
+
+

このやり方では一度にハッシュを計算できます。ただ、やっていることは上記のようにMD5オブジェクトを作って計算しているだけですから、クラスメソッドを使っても1行で書けるだけで、効率に違いはありません。

+
+
+

ftools

+
+

ftoolsライブラリはシェルで扱うようなファイル操作を実現するクラスメソッドをFileクラスに追加します。ftoolsの作者はebanさんこと、わたなべひろふみさんです。

+

ftoolsが追加するクラスメソッドを表18.2に示します。

+
+

表18.2●ftoolsのクラスメソッド

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
メソッド説明
File.compare(from,to[,verbose])内容比較
File.copy(from,to[,verbose])コピー
File.chmod(files...[,verbose])モード変更
File.install(from,to[,mode[,verbose]])インストール
File.makedirs(files...)ディレクトリ作成
File.move(from,to[,verbose])移動
File.safe_unlink(files...)削除
File.syscopy(from,to)コピー
+
+

それぞれのメソッドの挙動はメソッド名から想像されるとおりなのですが、注意すべき点をいくつか説明しておきます。

+ +

これらの機能を使うと、今までシェルスクリプトで記述していたような手続きもRubyで簡単に書くことができます。ただ、Rubyにはsh -xのような今何をやっているかを表示する機能はないのがちょっと寂しいところです。

+
+
+

プログラムの構造

+
+

さて、そんなこんなで出来上がった画像管理スクリプトがリスト18.4です。

+ +
+

リスト18.4●画像管理Rubyスクリプト

+
#! /usr/bin/ruby1.7
+
+require "ftools"                 # (1)
+require "digest/md5"
+
+MOUNT = "/mnt"
+
+class DateDir                    # (2)
+  attr_reader :max
+  def initialize(date)
+    max = "0000"
+    @sizes = []
+    for f in Dir.glob("#{date}/*")
+      if /(\d+)\./.match(f)
+        if $1 > max
+          max = $1
+        end
+      end
+      unless @sizes.assoc(f)
+        sum = get_md5(f)
+        @sizes.push [f, sum]
+      end
+    end
+    @max = max.to_i
+  end
+  DATE_DIRS = {}
+  def DateDir.get(date)
+    unless dd = DATE_DIRS[date]
+      DATE_DIRS[date] = new(date)
+    else
+      dd
+    end
+  end
+  private_class_method :new
+
+  def find(file)
+    found = @sizes.rassoc(get_md5(file))
+    if found
+      return found[0]
+    else
+      return false
+    end
+  end
+  def push(file, orig)
+    @sizes.push([file, get_md5(orig)])
+    @max += 1
+  end
+
+  private
+  def get_md5(file)              # (3)
+    Digest::MD5.digest(File.read(file))
+  end
+end
+
+                                 # (4)
+if File.open("/etc/mtab").grep(%r|^/dev/sda1|).size == 0
+  system "mount #{MOUNT}"
+end
+                                 # (5)
+for file in Dir.glob("#{MOUNT}/dcim/*/*.{jpg,avi,wav,mpg}").sort_by{|x|File.mtime(x)}
+  mtime = File.mtime(file)
+  date = mtime.strftime("%Y-%m-%d")
+  fname = File.basename(file)
+  base, num, ext = /([a-z]*)(\d+)(\..*)$/.match(fname)[1,3]
+  unless File.directory? date
+    Dir.mkdir date
+    dest = "#{date}/#{base}0001#{ext}"
+  else
+    d = DateDir.get(date)        # (6)
+    dest = nil
+    if ext == ".wav"             # (7)
+      jpgfile = "#{File.dirname(file)}/#{base}#{num}.jpg"
+      if File.exist?(jpgfile) and
+        found = d.find(jpgfile)
+        dest = found.sub(/jpg$/, "wav")
+        if d.find(dest)
+          next
+        end
+      end
+    end
+    unless dest                  # (8)
+      if d.find(file)
+        next
+      end
+      d.push(dest, file)         # (9)
+      dest = "#{date}/#{base}%04d#{ext}" % d.max
+    end
+  end
+  File.copy(file, dest, true)    # (10)
+  File.utime(mtime, mtime, dest) # (11)
+end
+
+
+
+

まず、例によって先頭で必要なライブラリをrequireしています (1)。今回はすでに説明したようにftoolsライブラリとdigest/md5ライブラリを使います。

+

続いて定義されているDateDirクラスは日付ごとのディレクトリが持つ情報を格納するクラスです (2)。そのメソッドはinitializeメソッドによる初期化と、重複しているファイルを見つけるfind、新たに作ったファイルの情報をDateDirに追加するpushです。内容によるファイルの重複チェックは先ほど説明したとおりMD5を使って行われます。

+

それからattr_accessorによってファイルに付けられた通し番号の最大値を返す属性max(実体はインスタンス変数)を定義しています。

+

DateDirクラスは同じ日付に対しては同じオブジェクトが得られるべきなので、クラスメソッドDateDir.findで重複チェックを行っています。また、間違ってnewを直接呼んでしまわないようにprivate_class_methodを使ってprivate宣言しています。これで、newメソッドはレシーバを省略した形式でしか呼び出せなくなります。

+

get_md5メソッドはファイル名からその内容のMD5ハッシュ値を求める「関数」です (3)。内容はまあ見たとおりですね。Digest::MD5クラスを使ってMD5ハッシュ値を計算しています。

+

ここからがこのスクリプトの処理の本質になります。この画像管理スクリプトは以下の手順で処理を行います。

+

まず、必要であればデジカメデバイスのマウントを行います (4)。マウントされているドライブの情報は /etc/mtabに含まれています。ここにUSBで接続したデジタルカメラ(デバイスは /dev/sda1)が含まれていなければ、systemメソッドを使ってmountコマンドを呼び出しマウントします。すでに説明したように /etc/fstab/dev/sda1に対してuserオプションが設定されていれば一般ユーザー権限でマウントできるはずです。

+
+

マウントが済んだらデジカメのメモリ中の画像ファイルの情報を取り出します。DCF規格に従ったデジカメの場合、画像はマウントしたファイルシステムに以下のように格納されているはずです。

+
+
dcim/<数字><メーカー>/<接頭子><数字>.<拡張子>
+
+

具体的にはこんな感じです。

+
+
dcim/100casio/cimg0001.jpg
+
+

拡張子はjpg(画像)、avi(動画)、wav(音声)、mpg(動画)くらいでよいのではないでしょうか。他の拡張子に対応させる場合には、リストの (5) の部分のパターンを変更すればよいでしょう。

+

これらのファイルのファイル名のリストを取り出します (4)。ワイルドカードマッチで取り出したファイルのリストをsort_byメソッドで日付順にソートしています。このsort_byメソッドは1.7系にしかないメソッドなので、お使いのRubyが1.6系である場合には、この部分を、

+
+
.sort{|a,b| File.mtime(a) <=> File.mtime(b)}
+
+

に書き換える必要があります。個人的にはsort_byを始めとする便利なメソッドが増えているので日々使うのもすっかり1.7系に移行してしまいました。ただ、1.7系はまだ開発版なので万人にお勧めとはいかないのが残念なところです。

+

ここからはこのファイル名リストに含まれるファイル名1つ1つに対して処理を行います。

+

まず、ファイルの更新時間(mtime)から(2002-09-11)のような形式で日付を取り出しておきます。これがファイルコピー先のディレクトリ名になります。

+

日付が得られたので、その日付を元にDateDirオブジェクトを作ります (6)。これはDateDir.getメソッドを呼ぶだけです。

+

スクリプトの要件で定義したように、ファイルの拡張子が .wavであれば、それは .jpgファイルに対する音声メモです (7)。拡張子以外の部分が等しい .jpgファイルがデジカメ上にあるかをチェックを行い、存在すればそのファイルが日付ディレクトリにコピーされているかを調べます。あるファイルと同一のファイルが日付ディレクトリ内にあるかどうかはDateDirクラスのfindメソッドを使います。もしあれば、コピーされている画像ファイルの拡張子だけを .wavに変えた名前をコピー先ファイル名とします。

+

音声メモでなければ、日付ディレクトリ内にそのファイルと同じ内容を持つファイルがあるかどうかDateDirクラスのfindメソッドでチェックします (8)。

+

チェックの結果同じファイルが存在しなければ、ファイルのコピーを行います。コピーによりファイルが増えるわけですからpushメソッドで情報の更新を行います (9)。ファイル名は日付ディレクトリ中の最大通し番号(maxメソッドで得られる)を使います。

+

実際のコピーにはftoolsライブラリが提供するFile.copyメソッドを使います (10)。

+

できるだけ元のファイルの情報を残しておきたいので、ファイルの更新時間もコピーします (11)。これはFile.utimeメソッドによって行います。utimeメソッドは作成時間と更新時間の両方を設定できますが、ここでは手を抜いて両方を元のファイルの更新時間(mtime)に指定しています。

+
+
+ +

宿題

+
+

今回は画像ファイルの同一性判定にMD5ハッシュを使いましたが、同一性判定の方法は他にいくつもあります。たとえば、まずサイズが同じかどうかを判定し、サイズが同じだった場合に限って内容を直接比較するというアプローチもありえます。ファイルのサイズが重複するケースは比較的少ないうえ、ファイルサイズはFile.statで内容を読み込むことなく得られますから、一度全部のファイルを読み込んでMD5ハッシュを計算するよりも速いでしょう。

+

実際に実装したら確かにずいぶん高速でした。こんなシンプルな方法のほうが速いのになぜわざわざMD5を使ったかというと、実は、

+
    +
  • 最初に思い付いたのがMD5を使う方法だった

  • +
  • すぐにサイズを使ったほうがきっと速いと気が付いたが、せっかくだからMD5の解説がしたかった

  • +
+

というような理由です。

+

MD5を使った比較から、サイズをベースにした比較への変更は読者への宿題にしておきましょう。性能向上することが明らかなほうがやる気が出るというものです。

+

ちなみに元のプログラムではファイルの比較がDateDirクラスの中にまとめられているので、initializeメソッドとfindメソッド、それからpushメソッドを書き換えるだけで、対応できます。行にして3行ですね(get_md5メソッドを削るぶんも合わせると全部で7行の変更)。簡単ですので、どうぞやってみてください。

+

解答は本誌付録CDに収録してもらうようにします。

+
+
+

まとめ

+
+

ということで、今回はシェルスクリプトが得意とするようなファイル処理をRubyでやってみました。また、ftoolsdigest/md5の紹介も行いました。

+

これで本当に簡単な数行のスクリプトの場合を除けば、ファイル処理においてもRubyは優れているということを示すことができたと思います。まあ、もともと、Perl以降のスクリプト言語はその辺を目指して登場してきたものなので、当たり前といえば当たり前なんですけどね。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-042.xhtml b/docs/vol1/xhtml/p-042.xhtml new file mode 100644 index 0000000..998d011 --- /dev/null +++ b/docs/vol1/xhtml/p-042.xhtml @@ -0,0 +1,64 @@ + + + + + +第18章 ファイル処理 + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ 顔じゃないよ、心だよ

+
+

「人間、顔じゃない」とはよくいわれることです。私自身も外見ばかり気にして、中身を大切にしない人を見ると、そんなことでどうするの、などとおせっかいに心配してしまいます。確かに人間にとって一番重要なことは外見ではありません。人間の価値は中身、つまりどのような精神(こころ)を持っているかによって決まるのではないでしょうか。

+

では、ソフトウェアはどうなのでしょう。

+

ソフトウェアの外見といえば、インターフェイス部分でしょうか。これは人間の外見よりももっと重要な位置を占めるような気がしてきます。というのも、インターフェイスは人間とコンピュータが直接やりとりする部分ですから、ソフトウェアひいてはコンピュータがあくまでも人間の司令を受けて動作するものである以上、それはかなり重要な位置を占めると考えられます。

+

ソフトウェアの外見がインターフェイスだとするならば、中身はなんなのでしょう。

+

それは、やっぱり機能、つまりそのソフトウェアが何ができるかではないでしょうか。たとえばワープロならどれだけ機能があるか、どんな修飾ができるか、どんなすばらしいデザインを表現できるか、というのが中身に当たるのではないでしょうか。

+

しかし、最近のソフトウェアには使いもしない機能がてんこ盛りのものも少なくないように感じます。いつか使う「かもしれない」機能、誰かは使っている「かもしれない」機能でも、普段の私には不要な機能というものが多い気がします。

+

ということを考えるとソフトウェアにとっては重要なのは中身よりもむしろ外見であるという結論が引き出せそうな気がします。もちろん、最低限の機能を満たしていなければなりませんが、それさえ満たせば外見こそが最も重要な要素ということです。

+

では、私の得意な分野であるプログラミング言語はどうなのでしょう。プログラミング言語もソフトウェアである以上、やはり外見の方が大事という性質があるのでしょうか。

+

実は、そうなのです。いや、実際のところ言語の場合、他のソフトウェアよりもさらに外見が重要であるといえると思います。

+

いくつかのプログラミング言語をご存じの方ならばお気づきでしょうが、ほとんどのプログラミング言語の「できること」のレベルにはそれほど差がありません。もちろん、論理型言語のPrologや関数型言語のMLやHaskellのように根本となる考え方(パラダイム)から違うものは別ですが、それ以外のものの違いといえば、極端な話、単に文法や記法のような外見の違いであると言い切ることもできるでしょう。

+

実際にGNUのコンパイラであるGCCは、もともとは単なるCコンパイラでしたが、バージョンが進むにつれ文法を解釈するフロントエンドと実際に機械語を生成するバックエンドとの分離が徹底され、今ではC, C++だけでなく、Ada, Pascal, FORTRANなどなどの言語のコンパイルができるようになりました。近日中にCOBOLのフロントエンドも完成するとのことです。ですから、以前gccはGNU C Compilerの略でしたが、もはやC Compilerではないので、正式にはGNU Compiler Collectionの略称だということになったのだそうです。

+
+

なお、余談ですがGCCのCOBOLのフロントエンドを作っているのは、私の会社のメンバーです。実は会社の業務でCOBOLコンパイラが必要になったのですが、オープンソースなCOBOLコンパイラがなかったので自分たちで作ることにしたのです。

+

そういえばマイクロソフトの .NETも言語を超えた共通の実行系CRL(Common Runtime Library)を提供していますね。これもVBやC#を始めとする複数の言語を共通の中間言語(IL)に変換することを実現しています。言語を超えてもできることは本質的に同じというわけです。

+

このように、新しい環境によってどの言語でもできることが大差なくなってくると、どの言語を選ぶかどうかの決め手は以前とは変化してくると思います。いや、変化するというよりも、以前からそれが決め手だったのですが、新しい環境により、もっと明確に意識されるようになったのでしょう。

+

私は長い間プログラミング言語おたくを続けてきて、その中で「生き残る言語」の性質を知りたいと考えていました。技術的に優れた言語が消えていったり、問題のある言語が悪口をいわれながら栄えたりするのはなぜだろうと考えていました。そして、最近になってようやっと得られた結論はこれです。

+
    +
  • 外見のよい言語が生き残る

  • +
  • 資産のある言語が生き続ける

  • +
+

FORTRANの外見が? と思う人もいるでしょうが、その言語の登場したタイミングを考慮しなければなりません。FORTRANの誕生した1950年代には「人間に読めるプログラミング言語」は最高にかっこよかったのです。私の生まれる前だから知らないけど、たぶん。

+

そして、最初の生存競争を生き残った言語のうち十分に資産を蓄積した言語が生き続けるのです。FORTRANやCOBOLは十分資産を蓄えたので、まだ現役です。PL/Iはちょっと資産が足りなかったようです。

+

さて、われらがRubyはどうやら一番最初の生存競争を生き延びつつあるようです。Rubyの開発を始めた1993年にはこんなにたくさんの人が使うようになるとは考えていませんでした。正直なところ、現れては消えていく数多くの言語の仲間入りする可能性はかなり高いと作者でさえ考えていたのです。

+

しかし、「私好みの言語」をよいと思ってくださる方は予想以上に多く、意外にたくさんの人がRubyを受け入れてくださいました。今では日本語で20冊、英語で6冊、ドイツ語で3冊、フランス語で1冊のRuby関連書籍が発行されています。近々ロシア語も書籍も登場するそうです。

+

これもひとえにRubyがかっこよかったからです。外見の勝利です。人間は顔じゃないけど、プログラミング言語は顔だったんですね。

+

ただ、ソフトウェアだって言語だって、かっこばかりで中身が全然なくっちゃ話になりません。中身が伴って外見もよくなくちゃいけないって意味では人間とそう違いはないかもしれませんねえ。

+
+

まとめ

+
    +
  • 人間は中身が大事

  • +
  • でも、ソフトウェアは外見が大事

  • +
  • プログラミング言語は外見がすべて

  • +
  • かっこいい言語を使おう

  • +
+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-043.xhtml b/docs/vol1/xhtml/p-043.xhtml new file mode 100644 index 0000000..8e0253d --- /dev/null +++ b/docs/vol1/xhtml/p-043.xhtml @@ -0,0 +1,417 @@ + + + + + +第19章 ネットワークプログラミング + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay19 +
+

+初等Ruby講座
+ネットワークプログラミング +

+
+

[Linux magazine, 2002年12月号]

+
+

ネットワークプログラミングの基礎であるソケットについて解説しています。この辺は20年経ってもほとんど変化はありませんね。例題としている「アンテナ」というのは若い人はもうご存じないでしょうね。Webページを一定間隔で巡回して、その更新状況を知るためのソフトウェアのことを当時はアンテナと呼んでいました。現代で言うRSSリーダーに近いものですが、しかし、そのRSSリーダーでさえ時代遅れの感があります。最近は情報は勝手にプッシュ通知でやって来ますからねえ。

+

「Ruby開発日記」は、「予想外」というテーマで人も言語も将来のことはわからないという話を書いています。未来はいつでも予想不能ですねえ。

+
+
+

「サーバーならLinux」とはよくいわれることですが、今回はネットワークのクライアントとしても活用できるぞ、というところを見てみます。

+
+
+

クライアント/サーバー

+
+

クライアント/サーバーとはシステムの構成方法の1つで、サービスを提供するサーバーと、そのサービスを利用する(おそらくは複数の)クライアントから構成されます。しかし、一言にクライアント/サーバーといっても技術の進歩に伴い時代によって変化しています。

+

はるかな太古、なんていうとその時代を知っている人には失礼でしょうが、昔は中心に巨大なコンピュータがあり、その周りに「端末」と呼ばれる機械がつながっていました。1960年代後半から80年代にかけてのことです。当時コンピュータは高価なものだったので、利用者全員がコンピュータを使うことは夢にも考えられませんでした。端末には画面とキーボードがありましたが、行うのは入出力だけで、実際の処理はすべて中央コンピュータが行っていました。これがクライアント/サーバーという概念のはしりです。

+
+

しかし、時代が経つにつれ、コンピュータの価格が下がり、端末としてコンピュータを使えるようになりました。そうなるとただの画面とキーボードの代わりに使うばかりではもったいないと、処理の一部を端末側に移すようになってきました。これらはインテリジェント端末と呼ばれることもありました。また、PCのような安価なコンピュータに端末のふりをさせるやり方(端末エミュレータ)もこの頃普及しました。おそらく、この頃が「クライアント/サーバー」という言葉が誕生した頃(80年代後半頃)だと思います。

+

しかし、ムーアの法則により、クライアント側のコンピュータ処理能力はどんどん向上します。下手をするとクライアントのほうが高い処理能力を要求するなんて事態も発生するようになりました。これが90年代頃ではないでしょうか。

+

1つの例はX Window Systemです。今ではLinuxを始めとするUNIX系OSでのGUIを統一してしまった感のあるXですが、登場したばかりの頃はいろんな意味で驚きのあるクライアント/サーバーシステムでした。

+

Xで(当時)驚かされた点は、

+
    +
  • ユーザーが直接使う画面、キーボード、マウスなどを管理するのがサーバーで、処理を行うのがクライアントであるという、旧来のクライアント/サーバーとはまったく逆の構成

  • +
  • GUIプログラムの処理の部分がそれなりに複雑なので、しばしばクライアントのほうが規模が大きくなった

  • +
+

という「逆転現象」でした。Xサーバーだけを積んだ「X端末」なるマシンも登場しました。端末がサーバーを持っているという事態に混乱した人も多数いました。

+

もう1つの例はWWWです。もともとWWWはHTMLで記述されたテキストデータを提供するだけのものでした。ですから、CGIのようなサーバー側で動的にデータを生成するものが少々あっても、Webサーバーに必要な処理というのはたいしたものではありませんでした。むしろ、HTMLを構文解析し、適切に表示するクライアント側プログラム(ブラウザ)のほうが肥大化し、たくさんの計算能力を必要としました。

+

このように、90年代はクライアント側の時代であったと考えることができるでしょう。

+

しかし、2000年以降は話が複雑になってきます。さすが時代は21世紀です(意味不明)。

+

まず、第一に起きたことはWWWの異常なまでの普及です。これによって人気のあるWebサーバーにはアクセスが集中するようになりました。このトラフィックに対処するためサーバー側にもいろいろな工夫が求められるようになりました。たとえば、複数のWebサーバーに負荷を分散する「ロードバランシング」や、単なるCGIよりも効率のよい新しいスタイルのサーバーサイドプログラム(ApacheモジュールやFastCGI, Servlet)です。

+

一方、クライアント側もJavaScriptによるクライアント側処理の複雑化や、XMLやSVGなどの対応するフォーマットの多様化、あるいはFlashなどのプラグインで、より肥大化しています。かつては10年間隔ぐらいで重点が交互に移っていたクライアントとサーバーは、いまや数カ月単位で振動しているという印象さえあるほどです。

+
+
+ +

基本に立ち返る

+
+

表面を眺めると変化の大きいクライアント/サーバーシステムですが、その基本となる部分に注目してみると、意外なほど変化していません。

+

WWWの基礎となるHTTPは現在使われているHTTP 1.1(RFC2068)が制定されたのが1997年、HTTP 1.0(RFC1945)は1996年です。メール転送のSMTP(RFC780)は1981年、FTP(RFC542)は1973年に決まっています。FTPが制定された時点では、本書の読者の多くは生まれていないんじゃないでしょうか。

+

それらプロトコルを伝える通信手段(トランスポート層、ネットワーク層)である、TCP/IPやイーサネットは、それぞれ1970年代の産物です。

+

ということは、これらの基本をマスターしていれば、当面は安泰ということもいえるでしょう。今回は、Rubyでこれらを使ったネットワークプログラミングをどう行うかについて学びましょう。

+
+
+

ソケット

+
+

ネットワーク経由の通信には「ソケット」というものを使います。電球を差し込んだりするアレですね。前もって「待ち合わせた」ところでお互いにソケット同士をつなぎあわせることで通信路ができるわけです。待ち合わせる場所はソケットの種別によって変わってきますが、最もよく用いられるTCP/IPのソケットでは、ホスト(ホスト名またはIPアドレスで指定する)とポート(1から65535までの整数で指定する)の組み合わせで待ち合わせます。サーバー側があるポート番号でリクエストを待ち受け、クライアント側がサーバーのホスト名と接続するポート番号を指定してつなぎにいく、という形になります。

+

ですから、URLの、

+
    +
  • http://www.ruby-lang.org:80/ja/index.html

  • +
+

の「//」から「/」の間は「ホストwww.ruby-lang.orgのポート80番」で待っているWebサーバーのソケットを指定していることになります。

+

他の種類のソケットではまた違う条件で待ち合わせることになります。

+

UNIXは入出力は統一的にストリームとして扱います。ネットワーク経由の入出力も例外ではありません。Rubyではソケットに対する入出力も普通のファイルに対する入出力とまったく同じ形式で行うことができます。違うのは接続する手続きとソケットに対してのみ有効な処理だけです。

+
+
+

Socketクラス群

+
+

Rubyはソケット関係のクラスが目的別に8つ用意されています(図19.1)。ソケットはストリームですから、これらのクラスはIOのサブクラスです。

+
+ +
+ fig1901 +
+

図19.1●ソケット関連クラス群

+
+ +

実はこれらソケットクラスをそのまま使う機会は減ってきています。後述のそれぞれのプロトコルに対応した「net/」クラスを使うほうが簡単だからです。とはいえ、基礎は大切です。net/ クラスもこれらのクラスを使って記述されていますし、新しいプロトコルに対応するクラスを自分で作るためにはソケットの知識は不可欠です。ここでは簡単に各ソケットクラスについて解説しておきましょう。

+

BasicSocketIOの直接のサブクラスで、他のすべてのソケットクラスのスーパークラスです。BasicSocketは抽象クラスで、インスタンスを作りません。BasicSocketクラスのメソッドは表19.1のとおりです。

+
+

表19.1●BasicSocket クラスのメソッド

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
クラスメソッド説明
close_read読み込みのクローズ
close_write書き込みのクローズ
getpeername接続先ソケットの情報
getsockname自ソケットの情報
getsockopt(opt)ソケットオプションの取得
recv(len[,flag])データの受け取り
send(str[,flag[,to]])データの送信
setsockopt(opt,val)ソケットオプションの設定
shutdown([how])ソケット通信の終了
+
+

IPSocketTCPSocketUDPSocketのスーパークラスで抽象クラスです。IPSocket表19.2のメソッドを持っています。

+
+

表19.2●IPSocketクラスのメソッド

+ + + + + + + + + + + + + + + + + +
クラスメソッド説明
addr自ソケットの情報
peeraddr接続先ソケットの情報
recvfrom(len[,flag])データの受け取り
+
+

TCPSocketはコネクション型のソケット、つまり相手先と接続して継続的にデータを流すタイプのソケットです。一方、UDPSocketはパケット型のソケット、つまりデータを1つ1つの塊として相手先を指定して送ることができます。相手先を固定することもできますが、必ずしもそうする必要はありません。

+

UNIXSocketは「UNIXドメインソケット」のクラスです。UNIXドメインソケットとは同じコンピュータのプロセス間で通信する手段で、待ち合わせに「ホスト名とポート」ではなく、「ファイルパス」を使います。その他の点はTCPSocketと同様、コネクション型のストリーム入出力を行います。

+

TCPServerUNIXServerは、それぞれTCPSocetUNIXSocketのサーバー版です。これらのクラスはサーバー側のソケット処理を簡単にしてくれます。

+

最後のSocketクラスは低レベルソケットインターフェイスです。SocketクラスはC言語レベルのすべてのソケットAPIに対応するメソッドを持っています。ですから、Socketクラスを使えば、Cで記述するのと同じくらいきめ細かなプログラミングができます。繁雑なのでめったにやることはありませんが。

+ +

各ソケットクラスのメソッドなどはドキュメントを参照してください。Rubyのドキュメントとしては、

+
    +
  • 『Rubyデスクトップリファレンス』(オライリージャパン)

  • +
  • 『プログラミングRuby』(ピアソンエデュケーション)

  • +
  • http://www.ruby-lang.org/ja/man-1.6/

  • +
+

があります。

+
+
+

初めてのソケットプログラミング

+
+

というわけで、ソケットクラスを用いたプログラムを書いてみましょう。最初は簡単にネットワーク経由で時刻を取得するプログラムです。

+
+
require 'socket'
+print TCPSocket.open("localhost", "daytime").read
+
+

簡単ですね。「openしてreadする」これだけです。それ以外のことはRubyとそのクラスライブラリが取り計らってくれます。openの引数はホスト名とサービス名です。サービス名の代わりにポート番号で指定してもかまいません。

+

このプログラムがうまく動作すれば、

+
+
Thu Oct 10 11:04:55 2002
+
+

のように時刻が表示されます。ここで、

+
+
Connection refused - "connect(2)"
+
+

というようなエラーメッセージが表示された人は、残念でした。お使いのマシンでは時刻を返すdaytimeサービスが起動していません。最近はほとんどのマシンでセキュリティなどの理由でdaytimeサービスは停止してあるようですね。

+

その場合には「クライアント側のソケットプログラムはこのようにするものである」と納得するだけにしておいてください。

+

もし、どうしてもdaytimeサービスの結果が見たければ、/etc/inetd.confというファイルにdaytimeという文字列を含む行が、「#」でコメントアウトされているはずです。

+
+
#daytime  stream  tcp  nowait  root  internal
+#daytime  dgram   udp  wait    root  internal
+
+

今使うのはTCP/IPですから、上のほうの「tcp」を含む行の「#」を外してください。それから実際にdaytimeサービスを提供するinetdを再起動してください。

+
+
+
# /etc/init.d/inetd restart
+
+

でも、わざわざそこまですることはないかなあ。

+

とにかく、このようにRubyならたった2行(実質は1行)でソケットプログラミングができます。Cでのソケットプログラミングの経験がある人にとっては、驚きといえるのではないでしょうか。

+

では、サーバー側も見てみましょう。先ほどのdaytimeプログラムが動かなかった人が大半でしょうから、daytimeサーバーと同じ働きをするサーバーを書いてみましょう。本来のdaytimeサービスのポートはrootでなければ使えませんから(1024番以下のポートはroot権限が必要)、待ち合わせるポートは12345番ということにしましょう。

+
+
require 'socket'
+s = TCPServer.new(12345)
+loop {
+  cl = s.accept
+  cl.print Time.now.strftime("%c")
+  cl.close
+}
+
+

これで終わりです。ネットワークサーバーというと大げさな印象がありますが、拍子抜けするくらい簡単ですね。このプログラムを起動しておいて、別のターミナルから先ほどのクライアントプログラムを "daytime" の部分を12345に置き換えて実行してみましょう。

+
+
Thu Oct 10 12:29:59 2002
+
+

のように時刻が表示されれば成功です。

+

このようにRubyを使えばネットワークプログラミングの基本的な部分は決して難しくありません。しかも、RubyのソケットクラスはIPv6対応ですから、いろいろややこしいIPv6対応を考えなくても、IPv4とIPv6との両方に対応したプログラムが書けます(クライアント側ならです。サーバー側は実は明示的にIPv6対応する必要があります)。

+
+
+

プロトコル

+
+

「Rubyを使えばネットワークも簡単」とはいうものの、上記のソケットプログラムはしょせんつなげただけです。ちゃんとしたネットワークプログラムはつないだ先と取り決めに従ってきちんとデータのやりとりをしなければなりません。この取り決めのことを「プロトコル」といいます。

+

プロトコル(protocol)とは辞書によると、

+
+

①(条約の)議定書・原案。

+

②外交儀礼。

+

③コンピューター – システムで、データ通信を行うために定められた規約。情報フォーマット、交信手順、誤り検出法などを定める。

+

【広辞苑】

+
+
+

なのだそうです。この場合は③の意味ですね。どちらがどのような順序でどのようなメッセージを送るか、データを送信する場合にはどのような形式で送るかなどを、通信の両端で正しく合意できていないと情報交換はできません。たとえば、先ほどのdaytimeサービスでさえ、

+
    +
  • クライアントが接続したらサーバー側から日付文字列を送る

  • +
+

というプロトコルに従っていたわけです。ネットワーク上のサービスはどのようなものでも、何らかのプロトコルに従って通信されています。たとえばWWWで使われるHTTP(HyperText Transfer Protocol)、ファイルのアップロードなどに使われるFTP(File Transfer Protocol)、メールの取得に使うPOP3(Post Office Protocol version 3)、メールの送信に使うSMTP(Simple Mail Transfer Protocol)などです。やたらPで終わる略語が多いと思ったらみんなプロトコルという意味だったんですね。

+

このようなプロトコルをネットワーク層(イーサネットなど)、トランスポート層(TCP/IPなど)の上位にある層なので「アプリケーション層」と呼ぶことがあります。ちゃんとしたネットワークアプリケーションを作るにはこのプロトコルを処理するプログラムを書く必要があります。

+

しかし、アプリケーション層のプロトコルは先ほどのdaytimeのような簡単なものばかりじゃないんですよねえ。

+
+
+

「net/」シリーズ

+
+

しかし、「難しいものを簡単に」というのはRubyのゴールの1つです。Rubyにはこれらのプロトコル(のうち、いくつか)を簡単に扱うためのクラスライブラリが標準添付されています。

+
    +
  • net/ftp

  • +
  • net/http

  • +
  • net/imap

  • +
  • net/pop

  • +
  • net/smtp

  • +
  • net/telnet

  • +
+

これらにはライブラリ名に「net/」が付いているので、個人的に「net/」シリーズと呼んでいます。発音するときには「ねっとすらっしゅ」ライブラリという感じでお願いします。これらのライブラリはプロトコルのクライアント側に相当します。これらのプロトコルのサーバーをいちから作りたい人はそんなにいないでしょうからね。

+

なお、「ねっとすらっしゅ」ライブラリの作者はnet/imapが前田修吾さん、net/telnetが青山和光さん、残り全部が青木峰郎さんです。

+

今回はその中でも主要なものである、net/httpを見てみましょう。

+
+
+ +

簡易アンテナの実装

+
+

「アンテナ」っていうのは、テレビなどの電波を受けるもの、ではなくって、この場合はWWWページの更新状況をチェックするためのプログラムなんだそうです。たとえば、

+
    +
  • http://www.ruby-lang.org/ja/hotlinks.html

  • +
+

なんかは、このアンテナの出力結果になるわけですね(図19.2)。

+
+ +
+ fig1902 +
+

図19.2●Ruby Hotlinks

+
+

今回は、「ねっとすらっしゅ」ライブラリで、アンテナの基本になる部分を実装してみましょう。基本的な手順は、

+
    +
  1. net/httpを使ってページのヘッダーを取得
  2. +
  3. ヘッダーのLast-Modifiedフィールドの情報を得る
  4. +
+

だけです。

+
+
+

アンテナ料理の下準備

+
+

さて、例によって例題プログラムで使うライブラリの解説をしておきましょう。アンテナの例題では以下のライブラリを使います。

+
    +
  • uri

  • +
  • net/http

  • +
+

uriはURI(Uniformed Resource Indicator)のことです。URL(Uniformed Resource Locator)とどこが違うのかといわれると正直なところよくわかりません。ただ、URLはURIのサブクラスなのだそうですから、URIでしか表現できないものがあるんでしょう、きっと(頼りない)。

+ +

uriライブラリの提供するURIクラスには以下のクラスメソッドがあります。

+
    +
  • URI.extract 「URIっぽい」部分の抽出
  • +
  • URI.join    URIの結合
  • +
  • URI.parse   URIオブジェクトの取得
  • +
  • URI.split   URIを個々の要素に分割
  • +
+

個々のURIオブジェクト(実際には種別ごとにクラスが違うのですが)にはたくさんのメソッドがありますが、その主要なものは以下のとおりです。

+
    +
  • scheme   URIの種別(httpとか)
  • +
  • userinfo ユーザー情報
  • +
  • host     ホスト名
  • +
  • port     ポート番号
  • +
  • path     パス
  • +
  • query    補助
  • +
+

一方のnet/httpライブラリはHTTPクライアントのためのライブラリです。Net::HTTPクラスの主要なクラスメソッドは以下のとおりです。

+
    +
  • new(host,port)    新しいHTTP接続
  • +
  • start(host, port) HTTP処理
  • +
  • get(uri)          GETによる取得
  • +
  • get_print(uri)    GETしたページの出力
  • +
+

Net::HTTPクラスの主要なインスタンスメソッドは以下のとおりです。

+
    +
  • get(path[,header])  GETリクエスト
  • +
  • head(path[,header]) HEADリクエスト
  • +
  • post(path[,header]) POSTリクエスト
  • +
  • start               HTTP処理
  • +
  • finish              接続終了
  • +
+

Net::HTTPを使った典型的な典型的な処理の流れは、

+
    +
  • Net::HTTP.startを使って全体を囲む

  • +
  • インスタンスメソッドを使ってページを取得

  • +
+

となります。

+
+
Net::HTTP.start(host, port) {|http|
+  r = http.get  # とかなんとか
+}
+
+
+

HTTP::startを使えば明示的にfinishする必要はありません。Rubyが忘れずにfinishしてくれます。

+
+
+

簡易アンテナの実装

+
+

下準備したことを踏まえて、簡易アンテナを実装してみましょう(リスト19.1)。ページの更新時刻が取得できたときにはそれを表示し、できなかった場合(CGI出力されたページなどは時刻が表示されません)には、「不明」と出力します。

+
+

リスト19.1●簡易アンテナ

+
#!/usr/bin/ruby
+
+require 'uri'
+require 'net/http'
+
+uri = URI.parse(ARGV[0])
+if uri.scheme != "http"
+  STDERR.puts "error: #{$0} only supports http"
+  exit 1
+end
+
+Net::HTTP.start(uri.host, uri.port) {|http|
+  response = http.head(uri.path)
+  printf "URL %s の更新時刻は", uri
+  if response.key?("last-modified")
+    printf "%sです。\n", response["last-modified"]
+  else
+    printf "不明です。\n"
+  end
+}
+
+
+

なんだか、例題そのもののような超簡単なプログラムになってしまいましたね。では、実行してみましょう。ここでは仮にプログラムの名前をpagedate.rbとします。

+
+
URL http://www.ruby-lang.org/ の更新時刻は不明です。
+
+

あら?

+ +

実はwww.ruby-lang.orgのトップページはCGIになっているので、時刻がわからないのです。では、他のページを見てみましょう。

+
+
URL http://www.ruby-lang.org/ja/ の更新時刻はSat, 07 Sep 2002 15:45:02 GMTです。
+
+

今度はCGIではないので、ちゃんと取れました。めでたし、めでたし。このようにわずか数行でアンテナが書けるというのはたいしたものです(例によって自画自賛)。

+

しかし、これではあまりにも簡易すぎますね。ちょっと考えただけでも、「HEADが失敗したらGETでページ全体を取得してみる」とか「ページがリダイレクトされた場合の対応」とかの対応が考えられていませんし、それにそもそもアンテナというものは、指定されたURLの更新時刻を調べるだけではなくて、URLが以前チェックしたときから更新されているかどうかをチェックするものです。

+

とはいうものの、ページの制限もありますし、この辺は読者への宿題ということにしておきましょう。

+

HEADの失敗やリダイレクトへの対応は、例外処理を使えば比較的簡単に実現できます。失敗したときにもリダイレクトされたときにもheadメソッドは例外を発生させますから、beginrescueで例外を捕捉してやれば対応できますよね。

+

また、更新チェックは、たとえば、

+
    +
  • 過去の更新時刻をファイルに覚えておいて、今回取得した時刻と比較する

  • +
  • 時刻が取れなかった場合、ページ内容のMD5値を取っておいて、現在の内容のMD5値と比較する(いつ更新したかはわからなくても更新したかどうかはわかる)

  • +
+

などの方法が考えられます。

+
+
+

実用への遠い道

+
+

このようにRubyのネットワークプロトコル対応クラスライブラリを使えば、簡易なアンテナならすぐに書けてしまうわけですが、ここから実用的なアンテナまではなかなか遠い道のりがあります。

+

実は本格的なアンテナの実装には、本当はいろいろ考慮すべき点があるんだそうです。いわく、

+
+

などなどなど。まあ、ただ単にいつ更新したか知りたいというだけでそれぞれのページに迷惑をかけるわけにはいかないので、妥当な心づかいだと思います。

+

今回はHTTPアクセスの基本の説明しかしませんでしたが、これから本気でアンテナを開発する人はぜひこれらの点にも気を付けてすばらしいものを作ってくださいませ。

+

なお、Rubyで書かれた本格的なアンテナといえば「たまてばこ」があります。「たまてばこ」のホームページは

+
    +
  • http://www.wakaba.toyonaka.osaka.jp/~ikemo/soft/tama/

  • +
+

です。

+

「たまてばこ」は1700行を超える大作です。小さなプログラムでもすぐに1000行を超えてしまうようなCと違ってRubyの1700行はかなり大規模です。ただ、「たまてばこ」では「net/」ライブラリは使わず、URLクラスなどを始めとして、クラスライブラリをかなり自前で用意しています(一部net/httpcgiを使っていますが)。標準ライブラリを使えば、もう少し小さくなるかもしれません。

+
+
+

まとめ

+
+

というわけで、今回はネットワークプログラミングの基礎であるソケットと、その上位にあるアプリケーション層のネットワークプロトコルライブラリについて学びました。しかし、ネットワークプログラミングは奥が深いので、まだまだ説明できません。次回はぜひクライアント/サーバーモデルを超えたネットワークプログラミングである「分散プログラム」について見てみたいと思います。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-044.xhtml b/docs/vol1/xhtml/p-044.xhtml new file mode 100644 index 0000000..321392f --- /dev/null +++ b/docs/vol1/xhtml/p-044.xhtml @@ -0,0 +1,75 @@ + + + + + +第19章 ネットワークプログラミング + + + + +

Matz Essays Volume 1

+ + +
+

◆ Ruby開発日記 ◆ 予想外

+
+

未来は予想できないとは誰もが感じていることでしょう。子供のときになりたいと思っていた職業に本当につく人はごく少数でしょう。子供のなりたい職業といえば、男の子なら野球選手とかサッカー選手やパイロットなどありがちでしょうか。私の同級生にもそんなのが多かったような気がします。女の子は看護師や保育士とかが典型的なのかなあ。

+

私はといえば小学校低学年の頃は精神科の医者になりたいと思っていました。妙に具体的ですね。しかし、医学部には解剖実習があるという事実を恐れた私は、この夢をあきらめたのでした。人の心には興味があったんですけどねえ。

+

次になりたいと思ったのは、科学者です。それも理論物理学の研究者になりたいと思っていました。講談社のブルーバックスや、白揚社の『ガモフ全集』などを読んでその気になったものです。やっぱり変な小学生ですね。

+ +

しかし、中学生になって重大な事実が2つ発覚しました。1つは私自身に数学に関するセンスがまったくないこと、もう1つは理論物理学には数学が要求されることです。大変なショックでした。どうにも数学がわからなくてねえ。

+

というわけで、理論物理学者の夢も早々にあきらめてしまいました。ちょうどその頃、コンピュータに出合い、プログラマーになることを目指すようになりました。幸いにして(やはり数学が障害になりましたが)、なんとかプログラマーの道を進むことができました。しかも、高校生の頃からいつかなりたいと思った言語設計者になれるとは。まったく、人の人生はわからないものです。

+

ところで、うちの5歳の息子は常々「植物博士になりたい」と言っています。やや妙なのは父親譲りでしょうか。

+
+

言語の人生

+

人の人生もわからないものですが、プログラミング言語の人生もわからないものです。

+

だいたいにしてプログラミング言語は他のソフトウェアに比較して寿命が長いものです。

+

たとえば1950年代生まれのFORTRANやCOBOLが今に至るまで現役であるとは、当時の関係者でも誰一人として予想しなかったのではないでしょうか。実際には数々の改訂と進化を経て、これらの言語は(時代遅れといわれつつも)まだまだ第一線で使われています。

+

Lispはもっと変わった出自の言語です。というのも、このLispはもともとプログラミング言語として開発されたわけではないのです。1958年、MITのJohn McCarthy教授はチューリングマシンよりも高度な数学的計算モデルとしてLambda Calculusというものを研究していました。もともとこれは人間がアルゴリズムを表現するための記法であって、プログラミング言語であるとは考えられていませんでした。

+

McCarthy教授たちはLambda Calculusがチューリングマシンよりも扱いやすいモデルであることを示すため、Lambda自身を評価する万能Lambda関数を記述して、それが万能チューリングマシンよりも簡単に書けることを示そうとしたのでした。そこでプログラム自身をデータとして表現する表記法として現在Lispで使われているS式が考え出されたのです。

+

ところが、当時McCarthy教授の大学院生であったSean Russellさんがこの万能Lambda関数evalをコンピュータで実行できればLambdaインタプリタになるのではないかと考えたのでした。

+

正直なところ、これはMcCarthy教授の予想を超えたものでした。教授は後にインタビューでこのように語っています。

+
+

Steve Russelが言った。ほら、evalをプログラムしたらどうだろう……、で、私は答えたものさ。はは、君は理論と現実を混同しているよ。このevalは人が読むためのもので、計算するためのものじゃない。でも彼はそのアイデアを進めて実際にやってしまった。つまり、彼は私の論文のevalを [IBM] 704の機械語に直して、バグを取り、それをLispインタプリタと称したんだ。実際そうだった。それが、Lispが現在の姿を取った瞬間だった。

+

— Paul Graham「Revenge of the Nerds」から引用

+
+

ですから、他の言語と違い、Lispは人間が表記する記法という言語というよりも、数学的記法の1つとして誕生したのです。

+
+

10月号のこの日記でも書きましたが、Lispの進んだ点というのは、この他の言語とはまったく異なる出自によるものなのです。

+

LispやFORTRAN, COBOLのように予想外に長生きする言語もあれば、期待されながらもいつの間にか消えてしまった言語もあります。こちらも予想外でしょう。

+

プログラム言語の人生(?)もわからないものです。

+
+
+

Rubyの場合

+

Rubyも予想外といえば予想外です。

+

昔から言語が作りたいと思っていた私は、大学の卒業研究でも教授の勧めを振り切って独自のプログラミング言語を設計しました。Classicという名前だったこの言語はRubyとは正反対で静的な型を持つオブジェクト指向言語で、一言でいうと「Cの外見を持つEiffel」という感じでした。ま、結局はあんまりうまくいかなかったんですけど、反面教師という形でRubyに影響を与えています。

+

Rubyが静的な型を持たないオブジェクト指向言語なのは、この辺も影響していると思います。

+

もともとRubyは私のおもちゃで、自分のテキスト処理などができればそれでよいと思ってました。ところが1995年に公開して以来、私が予想もしなかった人たちが、どんどんRubyを使い、また改善の提案も寄せてくれました。

+

開発を始めた9年前にはまさか海外にもRubyユーザーがいて、アメリカでRuby Conferenceが開かれるようになるとは予想もしませんでした。今年も11月1日から3日までワシントン州シアトルで開催されます。来月号ではレポートできるのではないかと思います。

+
+
+

まとめ

+
    +
  • 人の人生はわからない

  • +
  • 言語の人生もわからない

  • +
+
+
参考文献
+

「技術野郎の復讐 — Revenge of the Nerds」
+Paul Graham著, Shiro Kawai訳
+http://www.shiro.dreamhost.com/scheme/trans/icad-j.html

+
+
+
+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-045.xhtml b/docs/vol1/xhtml/p-045.xhtml new file mode 100644 index 0000000..7db574a --- /dev/null +++ b/docs/vol1/xhtml/p-045.xhtml @@ -0,0 +1,248 @@ + + + + + +第20章 番外編: Rubyカンファレンスレポート + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay20 +
+

+初等Ruby講座
+番外編:
Rubyカンファレンスレポート +

+
+

[Linux magazine, 2003年1月号]

+
+

第2回RubyConfの訪問記になります。このときは、RubyConf, OOPSLA, LL2の3つのカンファレンスに出席し、それぞれで発表するという強行軍でした。しかも、RubyConfとOOPSLAはワシントン州シアトル、LL2はボストンのMITで開催されるということで、途中にアメリカ大陸横断を含んで足掛け12日間という無茶なスケジュール。ちなみに私にとっては、このときが最長の海外旅行でした。一生、2週間以上日本を離れないで終わりそうな気がします。

+

RubyConfは2回目にしてずいぶん成長しているのがうかがえます。出席も初回よりは倍近くに増えています。それでも60人強ですが。

+
+
+

2002年11月1日から2日まで、アメリカ、ワシントン州シアトルで開催された第2回Rubyカンファレンスに出席してきました。今回は「初等Ruby講座」番外編として、そのレポート(+α)を行いたいと思います。

+
+
+

2002年10月31日(木)

+
+

さて、いよいよ出発の日です。過去最長(12日間)の海外旅行で、しかもその間に3つのカンファレンスで3回「英語」で発表があるということで、そうとう緊張しています。

+

私は島根県という田舎に住んでいるので、海外に出るためにはまず国際線の出ている空港まで移動しなければなりません。そこで出雲空港から羽田空港、それから電車で成田まで移動です。遠い……。

+

成田空港ではゲートで無線LANが使えました。これは便利。出国前に最後のメールチェックです。16時55分のUA便で一路シアトルへ。所要時間は8時間15分です。機中観た映画は「ニューヨークの恋人」と「9デイズ」です。国際線は映画がたくさん観られるのがうれしいですね。

+

シアトルに到着したら同じ日の朝9時でした。もちろん時差のせいなんですが、時間が逆戻りして、なんだか変な気分です。シアトルと日本では17時間の時差があります。その後Ruby関係者+αで中華料理の昼食を。

+
+

夕方からはSeattle Perl Mongersのみなさまのミーティングに参加させていただきました。メインはDan Sugalski(Perl 6のVMであるところのParrotの開発者)によるParrotとPerl 6の話でした。Parrotについては興味深く聞きました。将来のRubyの実装にも参考にできそうな話がたくさんでした。Perl 6のほうは「Perl 6ってなんだか読みにくいRubyみたい」というのが正直な印象です。

+

ホテルに戻ったら、クレジットカードやら免許証やらが入ったカード入れをなくしていることに気が付いて真っ青になりました。がーん。

+
+
+

2002年11月1日(金)

+
+

気を取り直して、今日からRubyカンファレンスです。1日目のプログラムは以下のとおりです。

+
+
Using SWIG to Develop C/C++ Entensions for Ruby
By Lyle Johnson
+

SWIGを使った拡張ライブラリの開発についての紹介です。SWIGはCやC++のヘッダーファイルといくらかの補足的な情報に基づいて、拡張ライブラリのソースコードを出力するツールです。LyleはSWIGを使ってFOX(GUIライブラリ)インターフェイスを開発しています。

+
+
+
"Are We Done Yet?": thoughts on acceptance testing
By Nathaniel Talbott
+

Test::Unitの開発者であるNathanielによってAcceptance Testについて紹介がありました。Acceptance Testとは最終的に顧客が仕様を満足しているかを確認するためのテストで、ある意味ユニットテストの対極にあります。しかし、ユニットテストと同様の自動化は(ある程度)可能だということでした。また、いくつかの顧客に対してAcceptance Testを実施した経験が紹介されました。

+

Test::Unitは彼の身辺が片付いたら本家にマージするとのことでした。彼は2週間前に結婚したばかりですから(奥さんを連れてきてました)、いろいろ忙しいのでしょう。

+
+
+
TaksMaster: Distributing computing with Ruby
By Phil Thomson
+

TaskMasterはタスクを複数のクライアントマシンで分散実行するためのフレームワークです。開発動機はテスト実行だけでも数日かかるようなプログラムの実行を分散することで時間を短縮しようというものです。TaskMasterはマスター・スレーブ構成になっていて、マスターサーバーがスレーブにタスクを分配して結果を集めるようになっています。

+

また、Philは類似の技術(DRb, MPIなど)との比較も行いました。

+
+
+
Adapting and Extending Perl Idioms in Ruby
By Deniel Berger
+

Perlのやり方をRubyで実現するには、というテーマの発表です。主に引数の渡し方、それもキーワード引数を中心にやり方をいろいろと紹介していました。まあ、Perlのやり方に引きずられすぎているという印象もありましたが、なかなか興味深い発表でした。

+
+
+ +
Simplify your life with YAML for Ruby
By why the lucky stiff
+

YAMLはYAML Ain’t Markup Languageの略で、一種のデータ表現言語です。話を聞く前はよくあるXMLの別表現だと思っていたのですが、よく聞くとむしろオブジェクトのシリアライズを目的としたデータフォーマットでした。YAMLにはPerl, Python, RubyなどのAPIが提供されており、Why(名前です)はRuby版の開発者です。YAMLはもともとPerlのInline.pmのシリアライズ用のフォーマットとして開発されたもので、会場には原作者のBrian Ingarsonもいました。YAMLに関する情報はhttp://yaml.org/から入手できます。

+
+
+
ラウンドテーブル
By Matz
+

夕食の後、Ruby作者を囲んで自由に質問をするという時間がありました。将来の予定などを含めていろいろな質問がありました。質問とその答えは、

+
    +
  • http://www.pablotron.org/rubyconf2002/matz_rountable-20021101.txt

  • +
+

にまとめられています。

+

ラウンドテーブルの最後に、高橋征義さんが日本からはるばる持ってきた、日本で出版されたすべてのRuby関連書籍が紹介され、みなの注目を集めていました。22冊も運んでくださった高橋さん、ご苦労様でした。

+

1日目が終わってホテルに帰ったら、Seattle Perl Mongersの皆さんによってカード入れは発見され、フロントに届けられていました。感謝感激です。Seattle.pmの皆さん、ありがとう(聞こえないか)。

+
+
+
+

2002年11月2日(土)

+
+
+
Better Web libraries for Ruby: NARF
By Patrick May and Tom Clarke
+

Ruby標準のcgi.rbはよくできているが、実際にWebアプリケーションを開発する場合に少々不満が残るので、cgi.rbをベースに新しいCGIライブラリ(NARF)を開発した、という話です。

+

注目すべき点は、Actionという形で個別のCGIページ出力よりも高レベルで(複数のページの集合体である)アプリケーションを記述できる点と、ユニットテスト用メソッドが組み込まれていて、見栄え(HTML)の出力をテンプレートとして分離することで、一般に難しいといわれているCGIプログラムのユニットテストをきれいな形で実現していたことです。これは一見の価値があります。NARFの情報は、

+
    +
  • http://narf-lib.sf.net/

  • +
+

からどうぞ。

+

なお、NARFの開発者の二人はcgi.rbの開発者、青山さんに非常に敬意を払っていて、ぜひ自分たちの成果を青山さんに還元したいということでした。和光ってのは英語では発音しにくいみたい(「わくー」みたいになっちゃう)。

+
+
+ +
Rethinking Web Scripting
By Avi Bryant
+

WebアプリケーションフレームワークIOWAの開発者でSmalltalkerでもあるAvi Bryantは今年は、継続(continuation)を使ったデモを行ってくれました。

+

Webアプリケーションは複数のページの集合になるため、ロジックが分散してしまうのが難点です。しかし、Aviは各ページの状態を継続として保存しておくことで、見かけは普通のプログラムのままWebアプリケーションを実現しました。

+

具体的には、

+
+
from = ask("where are you traveling from?")
+to = ask("where are you traveling to?")
+say("you are traveling from %s to %s."%[from,to])
+
+

というような簡単なプログラムを、asksayの定義を置き換えるだけで、Webアプリケーションとして実行されてみせたのです。さらに驚くべきことにはこのWebアプリケーションはブラウザの「戻る」ボタンにまで対応しているのです。

+

この手法は実験的なもので規模の大きなアプリケーションには対応できませんが(大量の継続オブジェクトを生成するため)、小規模なアプリケーションを手軽に開発するためには有効そうです。

+
+
+
What I Learned about Ruby Web Applications or How I Spent My Summer Vacation
+By Dave Thomas
+

夏休みをバカンスで過ごすつもりが、急に仕事が入ってWebアプリケーションを開発するはめになった、というお話です。これだけなら笑い話なのですが、その仕事は非常に複雑なビジネスロジックを持つ業務用システムで、規模は大きく(画面数で400)、開発期間は短く(実質2カ月)、人員は自分一人というかなり厳しい条件でした。

+

しかし、優秀なソフトウェアコンサルタントであるDave Thomasは、Rubyを使い2万6千行のWebアプリケーションを2カ月で開発し、顧客を満足させ、無事支払いを受けたそうです。すばらしい。

+

その成功の秘密として、Rubyを使ったこと、ビジネスロジックとアプリケーションロジックの分離などがあげられていました。

+

昨年は、テスト用プログラムなど間接的に開発に利用し効率を上げるというプレゼンテーションが行われましたが、今年は直接業務アプリケーションをRubyで開発する話が登場し、Rubyの浸透を印象付けました。

+
+
+
OpenPKSD (OpenPGP Public Keyserver)
By Hironobu Suzuki
+

OpenPKSDは、すずきひろのぶさんによるOpenPGP公開鍵サーバーの実装です。pksdにはpgp.ai.mit.eduなどで運用されているC言語で実装された版(Cで1万7千行)があるのですが、メンテナンス性やスケーラビリティに問題があるので、Rubyで実装したのだそうです。Ruby版はPostgreSQLをバックエンドに使い、3千行ほどで実現できたそうです。OpenPKSDは170万鍵を格納し、性能的にはC版と遜色ないということです。ひろのぶさんはデモ用マシンを持ち込んで実演するなど本気でした。OpenPKSDの情報は、

+
    +
  • http://www.openpksd.org/

  • +
+

にあります。

+
+
+ +
Exceptional Ruby
By Paul Brannan
+

Rubyにおける正しい例外の扱いについてと、特にC++で記述された拡張ライブラリにおいて、C++の例外とRubyの例外を共存させる方法について解説されました。

+

まず、「例外セーフ」という考え方が示されました。「例外セーフ」とは、実行中に例外が発生しても異常な事態が発生しないことです。たとえば、

+
    +
  • 例外が発生したので不完全なデータ構造が残った

  • +
  • 例外が発生したのでデータベースにゴミが入った

  • +
  • 例外が発生したのでプログラムが異常終了した

  • +
+

などの事態は「例外セーフ」ではありません。「例外セーフ」であるためには、rescueensureなどを利用して後始末が必要になる場合もあります。

+

次に、Rubyの例外は内部的にsetjmp/longjmpを使っているので、C++の例外と混ざるとクラッシュします。つまり、C++はRubyの例外を知らないので後始末ができませんし、逆も同様です。また、後始末されないC++の例外はクラッシュを引き起こすということです。ですから、C++の拡張ライブラリを開発する場合には、

+
    +
  • スタック上にC++オブジェクトを置かない

  • +
  • どうしても置く場合にはすべてのRuby例外を捕捉する

  • +
  • すべてのC++例外を捕捉する。必要ならRuby例外に変換する

  • +
+

ことが必要になります。Rubyの開発を始めた頃にはC++には例外なんてなかったのですが、いろいろ面倒になったものですね。発表資料は、

+
    +
  • http://rm-f.net/~cout/code/ruby/ruby_exceptions/html/

  • +
+

で参照できます。

+
+
+
FreeRIDE
By Rich Kilmer
+

FreeRIDEはRuby用のIDE(統合開発環境)です。GUIにはFOXを使い、WindowsでもLinux(や他のUNIX系OS)でも動作します。Ruby用IDEといえば、FreeRIDEの他にも古くはRubyWinや、Windows用のRDEなどがあるのですが、FreeRIDEの特徴は以下の2点です。

+
    +
  • 青木さんのRipperライブラリを使った構文解析を利用している。ちなみにRipperは海外で結構人気でした

  • +
  • FreeBUSという一種のアプリケーションフレームワークの上に構築されている。FreeBUSはFreeRIDEに依存しないフレームワークで、作者(Rich Kilmer)は他にもいくつかのアプリケーションを構築したそうです

  • +
+
+

FreeRIDEの詳しい情報は、

+
    +
  • http://www.rubyide.org/

  • +
+

から入手できます。

+
+
+
Ruby and Parrot
By Dan Sugalski
+

Danは次期PerlであるPerl 6の実行エンジンであるParrotの開発者です。しかし、ParrotはPerl 6専用というわけではなく、汎用の動的言語エンジンになる予定です。そして、Perl 6は現存する動的言語のあらゆる機能を持っているので、原理的にはParrotはあらゆるRubyを含むあらゆる動的言語を実行できるとのことでした。

+

また、実際にParrotはSchemeやRubyなどのフロントエンドを提供しているということでした。

+

Rubyの開発者としては、コアの部分を他のプロジェクトに依存するわけにはいかないので、将来とも公式エンジンとしてParrotを採用することはないと思いますが、選択可能なバックエンドとしては常に注目していきたいと感じました。

+

なお、Danとは土曜日のPerl Mongersのミーティング、Ruby Conference、月曜日のOOPSLAワークショップ、最後に次の土曜日にボストンでのLL2と、アメリカ滞在中に頻繁に遭遇しました。

+
+
+
Be Minor, Be Cool
By Yukihiro Matsumoto
+

私によるキーノートスピーチです。「マイナーであることを恐れるな、よいものはいつも最初はマイナーだ」というようなテーマで話しました。しかし、毎年英語でのプレゼンテーションには泣かされます。

+

プレゼンテーション資料は、

+
    +
  • http://www.ruby-lang.org/en/

  • +
+

から参照できるようにしておきます。

+
+
+
+

2002年11月3日(日)

+
+
+
Packaging Ruby apps with setup.exe
By Andy Hunt
+

Pragramic Programmersのもう一人、Andy HuntによるWindowsでRubyプログラムのインストールパッケージを作る手法についての紹介です。AndyはInstallShieldを使ったWindows版Rubyパッケージを配布していますが、Windows向けのフリーパッケージツールを利用することで、Rubyプログラム、ライブラリ、インタプリタを全部まとめてインストーラを作る方法を紹介しました。この場合、アプリケーションごとに別々のRubyインタプリタなどを持つことになりますが、今やディスクは安いし問題にはならない、むしろバージョン問題などを気にしなくてよいので望ましいくらいだ、ということでした。

+
+

Windowsに関心のない私は、残念なことにこのツールの名前を忘れてしまいました。たぶん、近いうちにAndyがリリースすると思います。http://www.pragprog.com/から入手できるようになるでしょう。

+
+
+
A Live Demo System (on CD) for Ruby on Linux
By Steven Gibson
+

歯科医師を対象にしたシステムの紹介も兼ねて、Rubyを中心にしたワンCD Linuxを用意し、CD-ROMドライブからブートできるPCならどこでも簡単にデモができることが紹介されました。Rubyを知ってもらうには実際に動作しているところを見てもらうのが一番で、LinuxやRubyの宣伝に非常に効果的だったと報告されました。

+
+
+
まとめ
By David Alan Black
+

今年のコーディネータだったDavid Alan Blackからまとめが行われました。昨年、今年とOOPSLA直前に開かれていましたが、来年はどうしようかという発言がありました。アナハイム(来年のOOPSLA開催地)の他、ハワイ、バルセロナなど好き勝手なことをみんなが叫んでいましたが、結局来年のコーディネータに一任という結論になりました。来年もDavidがやるのかな。今年はほとんど一人で取り仕切っていたので、彼は大変そうでした。お疲れさま。

+

今年は昨年よりも多い60人以上の参加者がありました。来年はもっと多くなるかもしれません。また、ヨーロッパでも同様のカンファレンスを行おうという動きがあるようです。Rubyの発展が目に見えるようです。

+
+
+
BOF
+

カンファレンス終了後いろいろなテーマでBOFが開かれました。が、私はこの時点で時差ぼけやらですっかり疲れてしまって、ホテルで休んでいました。

+
+
+
+

2002年11月4日(月)

+
+

Rubyカンファレンスはこれで終わったのですが、私はそのまま同じ会場で開かれたOOPSLAというカンファレンスに参加しました。OOPSLAとはObject-Oriented System, Language and Applicationの略で、世界最大のコンピュータ関連学会ACM(Association for Computing Machinery、直訳すると計算機械学会)の開催する由緒あるオブジェクト指向関連カンファレンスです。

+

今年はOOPSLAの一部として、1st International Workshop on Runtime Kernel Support for Dynamic Language and Component Based Architecture(長い)というワークショップが開かれ、そこのスピーカーとして招待されていたのです。Smalltalkや .NETの偉い人にまじって拙い英語で発表しましたが、このワークショップに出席してわかったことは以下のとおりです。

+
    +
  • Rubyの実装レベルはまだまだだ

  • +
  • スクリプト言語全般でもLispやSmalltalkには負けている

  • +
  • .NETは頑張っている(頑張ろうとしている)

  • +
  • でも、個人的にはまだ移行に決心はつかない

  • +
+
+

私の発表資料は他のと同様http://www.ruby-lang.org/から見えるようにしておきます。

+
+
+

2002年11月5日(火)から7日(木)まで

+
+

この間はOOPSLAに参加していました。6日(水)に開かれたDave ThomasとAndy Huntによるチュートリアルでは『C#エッセンシャルズ』の著者のBrad Merrilや『アジャイルソフトウェア開発』の著者のAlistair Cockburnなどの有名人も参加していました。

+
+
+

2002年11月8日(金)

+
+

この日は移動日でした。RubyカンファレンスとOOPSLAが開催されたシアトルから、LL2の会場であるMITがあるボストンまで移動するのです。途中デンバーを経由して7時間半の旅でした。機中で観た映画は「オーシャンズ11」。しかし、アメリカってのは同じ国の中でも時差があるんですね。シアトルとデンバーの間で1時間、デンバーとボストンの間が2時間。おかげで何時間飛行機に乗っていたのかわからなくなりそうでした。

+
+
+

2002年11月9日(土)

+
+

LL2当日。LL2とは「第2回 Lightweigt Languageに関するワークショップ」の略です。Lightweight Languageとは何か、というと、うーん、誰もよくわからないんですよねえ。まあ、LispとかSmalltalkとかPythonとかRubyとか、インタプリタがベースでダイナミックな言語というのは共通理解のようです。

+

私はLightweight Languageの定義とは何か、から始めて、人間に優しい言語、人気の出る言語を作るための原理・原則についてRubyを例にして紹介しました。この発表資料もhttp://www.ruby-lang.org/に置いておきます。

+

ボストンはなかなか素敵な街でしたが、見て回る時間がなかったのが残念です。今度はぜひ観光で訪れたいと思います。とかいって、海外旅行のときはいつもカンファレンスに出席してるんで、観光なんて余裕があったことはないんですよねえ。

+
+
+

2002年11月10日(日)

+
+

さて、長かったアメリカ滞在も終わり、とうとう帰国です。正直なところRubyカンファレンスが終わったあたりでもう帰りたくなっていたので「やっと」という印象があります。帰りの飛行機の中で観た映画は「K19:ウィドウメーカー」「K-PAX」「サイン」「スクービードゥ」でした。映画の見すぎで寝れなかったという(笑)。

+

日本に着いたら、もう11日(月)の夕方でした。1日損した気分ですね。日本はやっぱりいいなあ、と思いつつ、でも、あちこちでタバコの煙に苦しめられました。そういえばアメリカにいる間はタバコを見かけませんでした。これだけはアメリカのようになってほしいなあ。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-046.xhtml b/docs/vol1/xhtml/p-046.xhtml new file mode 100644 index 0000000..4c75668 --- /dev/null +++ b/docs/vol1/xhtml/p-046.xhtml @@ -0,0 +1,341 @@ + + + + + +第21章 プロセスとフォーク + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay21 +
+

+初等Ruby講座
+プロセスとフォーク +

+
+

[Linux magazine, 2003年2月号]

+
+

コンカレントプログラミングの手段のうち、今回はプロセスについて解説しています。プロセスの生成やゾンビ化を避ける手段など、現代においても有効なテクニックを解説しています。もっとも、最近はこの辺りはフレームワークにおまかせで、コアなシステムプログラミングをする人でもなければ触らないのかもしれませんが。

+

また、書籍『Rubyソースコード完全解説』について紹介しています。名著ですが、絶版なんですよね。Web版はまだ読めるのでそちらでどうぞ。

+

「Ruby開発日記」は、「ソフトウェアの一生」について語っています。ソフトウェアの運命は数奇なものです。オープンソースソフトウェアは比較的長寿ですが、それでも消えていくものもたくさんあります。そんな中でRubyは30周年を迎えられて幸せなほうですね。

+
+
+

今回は、OSにおけるプログラムの実行実体であるプロセスとその生成について解説します。

+
+
+

並列実行

+
+

一度に複数の仕事を依頼されたときには、あなたはどう対処しますか? 一度に1つずつ片付けていきますか? でも、仕事が5分や10分では終わりそうもないときには? 仕事をもっと小さな単位に分割して、それらを適当な順番で終わらせていきますか?

+

昔のコンピュータは一度には1つのことしかできませんでした。シングルタスクといって、1つの仕事が終わるまではひたすら待たされたことを覚えていらっしゃるかもしれません。パソコンではMS-DOSの頃はそんな感じでした。

+
+

最近のコンピュータはマルチタスクで同時に複数の作業ができます。Linuxを始めとするOSはこの仕事(タスク)を片付ける単位としてプロセスを使います。簡単にいうと1つのプログラムを起動してからそれが終了するまでがタスクで、その実行中のプログラムがプロセスです。

+

UNIX系OSでは新しいプロセスを作ることをフォーク(fork)と言います。ナイフ、フォーク、スプーンのフォークですね。forkには「枝分かれ」という意味もあるので、そちらのほうから来ているのだと思います。UNIXでは新しいプロセスを作るということは、プロセスがまず自分自身のコピーを作り、それからオリジナルとコピーが違う処理を始めます。だから「枝分かれ」なんですね。

+

一方、1つのプロセスの中で複数の処理を同時に行うこともできます。このいわば「プロセス中のプロセス」のようなものは「スレッド」と呼びます。スレッド(thread)とは「縫い糸」のことです。実行の流れが縫い糸のようにちくちくと現れたり消えたり(実行されたり止まったり)することから連想されたのでしょうか。スレッドについては来月解説する予定です。

+
+
+

プロセス生成

+
+

Rubyでは新しいプロセスを作るやり方はいくつもあります。

+

一番簡単なのはsystemメソッドでしょう。systemメソッドはコマンドを実行し、その終了ステータスだけを返します。

+
+
system "mv foo.txt bar.txt"
+
+

実際にはsystemは、

+
    +
  • シグナルハンドラの設定

  • +
  • 標準入出力のフラッシュ

  • +
  • プロセスの生成

  • +
  • プロセスの終了待ち

  • +
  • プロセスのステータス取得と後始末

  • +
+

などのめんどくさい処理を行っているのですが、ユーザーはそんなことを気にする必要はありません。systemは呼び出したプロセスが正常終了した場合にはtrue、異常終了だった場合にはfalseを返します。

+

次に簡単なのプロセス生成は「コマンド呼び出し」でしょう。これはコマンドを呼び出し、そのコマンドの標準出力を文字列として得るものです。

+
+
`ls -l`
+
+

ls -lの出力を1つの文字列として取り出します。

+

system同様、コマンド呼び出しもプロセス生成などについて考える必要はありません。

+

そして一番プリミティブな方法がフォークです。forkはUNIXのforkシステムコールを呼び出し、直接プロセスを生成します。forkは細かなことまで指定できますが、逆にコマンド呼び出しやsystemに比べると少々気を使う必要があります。

+

また、forkはUNIXのシステムコールですから、UNIXでないプラットフォーム(Windows版Rubyとか)では動作しません。Cygwin版RubyではCygwinが頑張ってthreadを使ってエミュレートしているそうですが。

+
+
+ +

フォーク

+
+

UNIXで新しいプロセスを作るという処理は、基本的にフォークによって自分自身のコピーを作ることから始まります。他のコマンドの実行は、フォークした新しいプロセスの中でexecシステムコールによって、自分自身(が実行しているプログラムを)他のコマンドに置き換えることによって行われます。ほとんどの場合、やりたいのはコマンドを実行することだと思うのに、まず自分のコピーを作るというのはどういうことか、という気になりますが、これはそういうものだと納得するしかありません。UNIX Wayというものです。

+

Rubyでプロセスをフォークするためにはforkメソッドを使います。forkメソッドは大きく分けて2つの使い方があります。最初の使い方はUNIXシステムコール的なやり方です。

+
+
if pid = fork()
+  # オリジナルのプロセスで実行される
+else
+  # フォークされたプロセスで実行される
+end
+
+

このように(ブロックなしで)呼び出されたforkメソッドは、新しいプロセスを作り、オリジナル(親)とコピー(子)の両方でforkの続きを実行します。ただし、親ではforkは子供のプロセスID(数値)を返し、子ではnilを返します。

+

子プロセスは親プロセスのコピーですから、同じプログラムを実行し、同じ状態(変数の値とか)を共有しますが、これらはあくまでもコピーですから、子プロセスで変数を書き換えても親プロセスには反映されません。SF好きならパラレルワールドを思い出すかもしれません。分岐してしまった2つの世界(プロセス)はもう別々の歴史を刻むのです。

+

if文のelse節で子プロセスの実行が終わらなければ、そのまま続きを実行してしまいます。うっかり親子で同じ処理を続けてしまわないように注意してください。

+

もう1つの使い方は少々Ruby的です。

+
+
fork {
+  # フォークされたプロセスで実行される
+}
+
+

forkメソッドにブロックを指定して呼び出すと、ブロックの内容は子プロセスでだけ実行されます。また、ブロックの実行が終わると子プロセスは自動的に終了します。このほうがわかりやすいといえばわかりやすいですね。

+
+
+ +

プロセスの入出力

+
+

新しいプロセスが起動されたとき、親プロセスの標準入出力はそのまま引き渡されます。ですから、新しく実行を始めたコマンドが受け取る入力は、

+
    +
  • 標準入力

  • +
  • argvexecシステムコールに指定したもの)

  • +
+

になります。一方、出力は、

+
    +
  • 標準出力

  • +
  • 標準エラー出力

  • +
  • 終了ステータス

  • +
+

の3種類です。通常の出力は標準出力に、何かエラーが発生したときのメッセージ出力は標準エラー出力に行います。これはエラーメッセージが出力結果を壊してしまわないようにという配慮です。

+

入出力先を切り替える、シェルのリダイレクトやパイプのようなことを行うためには、forkしたあとでexecする前に標準入出力(ファイルディスクリプタの0, 1, 2番)を切り替えることで実現します。切り替えが終わったあとでexecするわけです。forkexecが分離しているのはこの前処理を行うためだったんですねえ。

+

プログラムは標準入出力を使ったデータのやりとりとは別に、その実行が成功したかどうかを示す数値を返すことができます。これが終了ステータスです。終了ステータスはexitの引数で指定します(デフォルトは0)。プロセスの実行が成功したときには0、失敗したときには0以外を返します。シェルスクリプトでは最も最近終了したプロセスの終了ステータスは変数「$?」に格納されています(偶然ですが、Rubyでも同じ名前の変数で終了ステータスが得られます)。

+

たとえば、ファイルの中にrubyという文字列が含まれているかを調べるためにはgrepコマンドを使います。文字列が含まれている場合には終了ステータス0、含まれていない場合には、それ以外を返します。

+
+
% grep ruby file.txt
+% echo $?
+1    # 含まれていなかった場合
+
+
+
+

ゾンビ(zombie)

+
+

ゾンビっていうとアレですね、死人がうろうろと歩き回るっていう奴ですね。気持ち悪い。それがプロセスと何の関係があるかっていうと、あるんです。プロセスはちゃんと供養しないとゾンビになるんです。たとえば、

+
+
+
% ruby -e 'fork{};sleep'
+
+

こんなふうに起動するとRubyは子プロセスをフォークして、子プロセスは何もせずに終了します。親プロセスのほうは子プロセスを放置してそのままsleepします。

+

この状態でpsでプロセスの状態を見ると、

+
+
% ps a
+  PID TTY      STAT   TIME COMMAND
+ 2297 ttyp0    S      0:00 ruby -e fork{};sleep
+ 2298 ttyp0    Z      0:00 [ruby <defunct>]
+ 2303 ttyp0    R      0:00 ps a
+
+

というような出力が得られると思います。このZステータスを持つ、<defunct> と表示されるプロセスがゾンビです。ゾンビっていうのは要するに「終了はしたものの、親に終了ステータスを報告していないプロセス」です。

+

ゾンビを成仏させる方法は、以下のいずれかです。

+
    +
  • 親プロセスがwaitまたはwaitpidシステムコールで子プロセスの終了ステータスを得る

  • +
  • 親プロセスが先に死んでしまう

  • +
+

waitシステムコールは子プロセスの終了ステータスを取り出します。終了ステータスを報告した子プロセスはゾンビを卒業し、リソースを解放して消滅します。waitpidはプロセスIDを指定してwaitするものです。複数の子プロセスが存在するケースで有効です。

+

親プロセスが先に死ぬと、子プロセスはinitプロセスと呼ばれる「ご先祖プロセス」に養子縁組みされます。initはプロセスID1番を持つすべてのプロセスの親に当たるプロセスです。initは養子縁組みされた子プロセスに対してwaitシステムコールを呼び、そのプロセスに属するリソースを解放し、ゾンビを消滅させます。

+

子プロセスがいつ終了したかとか、その終了ステータスとかに関心がない場合には、この養子縁組みを利用して二重forkというテクニックを用いることができます。二重forkでは、まずforkによって子プロセスを作り、その子プロセスをさらにforkして実際の処理を行います。子プロセスは実際の処理を行う孫プロセスをforkした後、すぐに終了してしまいます。子プロセスの終了により、孫プロセスはinitに養子縁組みされていますから、孫プロセスが終了した時点でinitがただちにwaitを呼び、プロセスはゾンビになることなく、リソースの解放が行われます。

+
+
+

Processのメソッド

+
+

プロセスに対する操作はProcessモジュールが行います。Processはクラスでなくモジュールである点に注意してください。主な理由はUNIXのプロセス操作がプロセスIDという数値をベースに行われるからです。

+

Processのメソッドは基本的にはUNIXのプロセス関係システムコールに対応しています(表21.1)。各メソッドの動作はmanを見ることでだいたい知ることができます。たとえば、Cでの、

+
+
n = getpgid(pid, pgid);
+
+

という呼び出しは、

+
+
Process.getpgid(pid, pgid)
+
+

になります。ただし、「uid」「uid=」などはそれぞれgetpid, setpidに読み替えてください(egid/euid/gidも同様)。Processを明示的に指定するぶん長くなりますが、あるクラスでProcess操作を頻繁に行うならば、

+
+
include Process
+getpgid(pid, pgid)
+
+

のように記述することもできます。

+ +
+

表21.1●Processのメソッド

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
メソッド説明メソッド説明
egid実効グループIDppid親プロセスID
egid=実効グループID設定setpgidプロセスグループの設定
euid実効ユーザーIDsetpgrpプロセスグループの設定
euid=実効ユーザーID設定setpriorityプライオリティの設定
exit!強制終了setsidセッション開始
forkforktimesプロセス時間取得
getpgidプロセスグループの取得uidユーザーID
getpgrpプロセスグループの取得uid=ユーザーID設定
getpriorityプライオリティの取得waitwait
gidグループIDwait2プロセスIDとステータスを得るwait
gid=グループID設定waitall全子プロセス対象のwait
killシグナルを送るwaitpidpidを指定したwait
pidカレントプロセスIDwaitpid2プロセスIDとステータスを得るwaitpid
+
+
+

フォークの実例

+
+

では、二重forkテクニックを含めて、forkの使い方を見てみましょう。リスト21.1forkを使ったソケットサーバーの例です。

+ +
+

リスト21.1●forkによるソケットサーバー

+
require "socket"
+
+gs = TCPserver.open(0)
+loop do
+  s = gs.accept
+  fork do
+    fork do     # zombie化を避ける二重fork
+      処理を行う
+      s.close
+    end
+  end
+  s.close       # 親では不要
+  Process.wait  # 直接の子を「供養」
+end
+
+
+

ま、短いプログラムですし、そんなに難しくないですよね。二重forkだけ少々不自然ですが、まあこんなもんでしょう。

+

もう1つ別の例を見てみましょう。RubyのsystemメソッドをProcessの機能を使って自力で定義した例をリスト21.2に示します。

+
+

リスト21.2●mysystem

+
def mysystem(cmd)
+  STDOUT.flush
+  STDERR.flush
+  pid = fork do
+    exec cmd
+  end
+  pid, status = Process.waitpid2(pid)
+  return if status == 0
+  return false
+end
+
+
+

forkして子プロセスを作った後、execメソッドを使って実行ファイルを置き換えています。プログラムの実効ステータスはpidを指定しないでwaitだけでも取得できますし、最後のコマンドの終了ステータスは変数 $? で取得できます。ですから、別の解はリスト21.3のようになります。

+ +
+

リスト21.3●mysystem

+
def mysystem(cmd)
+  STDOUT.flush
+  STDERR.flush
+  fork do
+    exec cmd
+  end
+  Process.wait
+  return if $? == 0
+  return false
+end
+
+
+

どちらが好みかというのは人によって違うと思います。他のスレッドが同時にmysystemを呼ぶことが(そして結果として、予想外のProcesswaitしてしまうことが)ありえるかによってpidを指定するかどうかが決まるでしょう。また、$? というPerl的変数が許容できるかどうかで $? を使うかwaitpid2を使うかが決まるでしょう。

+
+
+

プロセスの制約

+
+

このようにプロセスは処理を他のプログラムに任せたり、同時に複数の処理を待つことなく実行したりする場合に便利ですが、いくつかの制約があります。

+
    +
  • UNIX系OS以外ではうまく動かない

  • +
  • プロセス間通信が大変

  • +
+

Windows版Ruby(VC++版)ではforkが動きませんし、DOSでもダメです。また、プロセスは環境をコピーしてしまい、変更が親に反映されないのでプロセスの間で情報のやりとりをするのが大変です。情報をやりとりする場合には、

+
    +
  • 標準入出力で通信

  • +
  • ソケットで通信

  • +
  • シェアードメモリを使う

  • +
+

などの方法がありますが、どれもそれなりに面倒です。簡単に使えるのはやはりプロセス起動後には情報のやりとりがほとんどないケースでしょう。ただし、上記のいずれも少々面倒なだけで不可能でないことは申し添えておきます。

+
+
+

まとめ

+
+

今回、駆け足でプロセスについて解説しました。OSの重要な概念であるプロセスを少ないページ数で解説するのはちょっと無理がありますね。この説明でわからなかった人(多数いそうですが)は、ぜひ何かの教科書でプロセスについて調べてみてください。来月はプロセスとはまた違った並列実行へのアプローチ、スレッドについて学ぼうと思います。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-047.xhtml b/docs/vol1/xhtml/p-047.xhtml new file mode 100644 index 0000000..5a5b174 --- /dev/null +++ b/docs/vol1/xhtml/p-047.xhtml @@ -0,0 +1,40 @@ + + + + + +第21章 プロセスとフォーク + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ 知られざるRuby ◆ Rubyソースコード完全解説

+
+

この原稿を書いているちょうどそのとき、書籍小包が私の自宅に届けられました。それが青木峰郎著、まつもとゆきひろ監修の『Rubyソースコード完全解説 – Ruby Hacking Guide』(インプレス、ISBN4-8443-1721-0)です。私は監修者ですから、発売前に原稿に目を通していたのですが、やはり実際に書籍になったものを手にするときの気分は格別です。

+

ということで、かなり手前みそですが今回の「知られざるRuby」は、この『Rubyソースコード完全解説』を紹介することにしましょう。

+

この本はRubyのソースコードの解説書というよりもむしろRubyを題材にしたソースコードの読み解き方の解説書です。あまり知られていないかもしれませんが、ソースコードというのは人間が読んでもとても勉強になる読み物です。ソースコードを読むことで、作者の持つ知識・技術・思想・哲学・美学などを知ることができます。そうです。ソースコードはバグまでも完全に記述されている優れたドキュメントでもあるのです。

+

しかし、ソースコードを読むことは決して簡単な作業ではありません。もともとソースコードが人間向けの読み物として書かれていないこともありますが、なによりソースコードの読解には独特の技術が必要になるからです。本書では作者ではない人物が未知のソースコードを解読するありさまがリアルに表現されています。

+

私はRubyの作者ですからRubyのソースコードについて私以上に知っている人はいないはずですが、それでも学ぶことが数多くありました。自分の書いたつたない部分を解説されていたりするのを読むと、とても恥ずかしかったですが。

+

ただ、少々悲観的にとらえると本書は決して優しい本ではありません。いや、実用的なソフトウェアのソースコードはそもそも簡単なものではないので、この本が優しくないのはむしろ当然なのですが。本書の前書きにもこうあります。

+
+

正直に言って、この本は易しくはない。少なくとも、対象の持つ本質的な難しさ以上には易しくはなっていない。しかし、だからこそ本書は面白い(かもしれない)のだ。

+
+

この本は読者を選ぶかもしれません。しかし、優しいだけの軟弱な本(『できる〜』とか『わかる〜』とか)ばかりでなく、こんな硬派の本が出てきたことをうれしく思います。

+

まったく個人的な印象ですが、これほど読んでいてわくわくした本は久しぶりな気がします。いや、コンピュータ関係の本では初めてかもしれません。青木さんならではの率直な語り口にも好感が持てます。ぜひお勧めしたい一冊です。

+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-048.xhtml b/docs/vol1/xhtml/p-048.xhtml new file mode 100644 index 0000000..23b7535 --- /dev/null +++ b/docs/vol1/xhtml/p-048.xhtml @@ -0,0 +1,66 @@ + + + + + +第21章 プロセスとフォーク + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ 大人になる、ということ

+
+

最近、Ruby界隈では「18歳」というのがキーワードのようです。自分が18歳だったときにはどんなだったとかいうようなことが話題になっています。それはともかく、18歳というと私の年齢のおおよそ半分であることに衝撃を受けたのでした。

+

しかし、今回話をするのは「リアル人生」ではなくて、ソフトウェアそれもフリーソフトウェアプロジェクトの一生について考えます。

+
+

フリーソフトウェアの一生

+

つい先日もこのコーナーで「言語の人生」について語ったような気がしますが、懲りずに似たような話を繰り返します。

+

ソフトウェアも人間と同じように、あるとき誕生し、そして次第に成長し、そしてだんだんと衰退していきます。生物と違ってソフトウェアには明確な死はありませんが、使われなくなり忘れ去られたソフトウェアは死んでいるのとあまり変わらないでしょう。それでは私が一番よく知っているフリーソフトウェアであるRubyを題材にフリーソフトウェアの一生を(というか、Rubyの場合はまだ半生ですが)見てみましょう。

+
+
+

ソフトウェアの誕生

+

いつをもってソフトウェアが誕生したとみなすかは人によって定義が違います。Rubyの場合、「名前が重要」というのが私のポリシーですから、プロジェクトの名前が決まった日である1993年2月24日を「Ruby誕生日」と宣言してます。もうすぐ10歳ですね。

+

開発者にとっては作り始めたときにはすでに「存在している」イメージなのですが、実際に使われるようになってこそ存在すると考える方もいらっしゃいます。そのような方は最初のリリース日が誕生日だと考えるようです。その定義では最初のアルファリリースを行った1994年12月に生まれたことになるのかもしれません。

+

ヨタ話はともかく、フリーソフトウェアの初期において最も重要な点は「どの段階でリリースするか」ということです。フリーソフトウェアプロジェクトが成功するかどうかの最初の難関がここにあります。

+

リリース時期は微妙な要素ですが、私の考えは、「まがりなりにも動いたら、できるだけ早い時期にリリースすべき」です。

+

ここで最も重要なことは「動く」ことです。コンセプトだけで動かないソフトウェアは、よっぽど運がよくないと成功しません。動くからこそ誰かの関心を呼ぶわけですし、動くからこそ次の段階である成長を実現できるわけですから。

+
+
+ +

ソフトウェアの成長

+

そして、最初のリリースという難関を突破したプロジェクトは次の段階に進むわけです。sourceforge.netsourceforge.jpに登録されている多くのプロジェクトはここの段階にあると思います。この段階で重要な点は「どのようにコミュニティを構築するか」ということです。

+

コミュニティは、テストやデバッグに協力してくれたり、自分では考えもつかなかった新しいアイデアを提案してくれたりと、フリーソフトウェアプロジェクトの原動力と言ってもよいと思います。コミュニティの形成がうまくいかなかったプロジェクトは衰退します。

+

コミュニティの形成については、そのソフトウェアが本当によいものであれば、メーリングリストとWebページがあれば、使って面白いと思った人たちが集まってくれると、私は信じています。実際にRubyもそうでした。ただ、数年前と違ってフリーソフトウェアもずいぶん数が多くなっているので、現代のフリーソフトウェアプロジェクトが生き残るためには何らかの方法で「目立つこと」が要求されるかも知れません。

+

ああ、重要な点を忘れていました。一番に大切にしなければならないコミュニティは開発コミュニティであってユーザーコミュニティではない、ということです。少々暴言ですが。フリーソフトウェアを開発する動機で最も大きいものは「ソフトウェア開発したいから、楽しいから」というものではないかと思いますが、単なるソフトウェアユーザーはなかなかそれを理解してくれません。フリーソフトウェアの場合、どれだけユーザーがいても「もうけ」があるわけではありませんから、より楽しい開発に集中するのはむしろ当然だと思います。優れたソフトウェアのユーザーコミュニティは、開発コミュニティの周辺に自然発生すると思います。

+

開発コミュニティのサイズですが、Rubyの場合を見てみると、興味深いことに開発コミュニティが100人を超えたあたりで安定したコミュニティが形成され、それは人数が増えても、提案やレポートの数はさほど変化しないようです。コミュニティの数を追求する必要はないということですね。

+
+
+

ソフトウェアの安定

+

コミュニティも形成でき、開発が進めば、フリーソフトウェアプロジェクトは安定し、いわば壮年期に入ります。

+

しかし、ここに重大な落とし穴があります。1つは「モチベーション」です。同じソフトウェアを3年も4年も開発すると飽きてくるので、安定的に開発することが難しくなることがあります。

+

もう1つは「生活の安定」です。フリーソフトウェアはそれ自身がお金を生みませんから、生活を続けることが困難になる場合があります。中心開発者の生活が維持できなければ、プロジェクトは終焉しゅうえんを迎えるでしょう。

+

モチベーションに関しては、管理者の世代交代という手が使えるかもしれません。実際、私が以前に開発していた(今でも使っている)cmailというメールリーダーは開発者が交代し、集団管理体制になっています。

+

生活の安定に関しては、私自身は運よくなんとかなりましたが、一般的な解答は見つかっていません。フリーソフトウェアから恩恵を受けている各社がフリーソフトウェア開発者をもっと雇用してくれるとよいのですが。

+
+
+ +

ソフトウェアの衰退

+

ソフトウェアは死にません。ただ消え行くだけです。しかし、いくつかの点に注意してうまくやることでフリーソフトウェアの衰退を回避できるのではないかと思うのです。あなたのフリーソフトウェア、衰退を回避できますか?

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-049.xhtml b/docs/vol1/xhtml/p-049.xhtml new file mode 100644 index 0000000..39139b7 --- /dev/null +++ b/docs/vol1/xhtml/p-049.xhtml @@ -0,0 +1,397 @@ + + + + + +第22章 スレッド(その1) + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay22 +
+

+初等Ruby講座
+スレッド(その1) +

+
+

[Linux magazine, 2003年3月号]

+
+

コンカレントプログラミングのもう1つの手段、スレッドについて解説しています。コンカレント性についての要求は多種多様で、いろいろな状況に対応するために、この後、FiberやRactorも増えることになるのですが、ここではそれは置いておきましょう。

+

今回はスレッドにまつわるいろいろな問題(デッドロックやレースコンディションなど)についても解説しています。記事中、「スレッドとセキュリティ」についても解説していますが、この機能は最新のRubyでは(セキュリティ上の信頼性が期待したより低かったので)削除されています。

+

「Ruby開発日記」は、パフォーマンス改善の原則についてです。正直、このような原則的なところは時代で変化することはなく、正直、今読んでも「いいこと言ってるな」と感じます。

+
+
+

プロセスについて学んだ先月に引き続いて、今月はもう1つの並列処理手段であるスレッドについて解説します。

+
+
+

スレッド

+
+

複数の仕事を同時に実行したい場合、OSはプロセスという単位を使って並列実行する、と先月学びました。しかし、プロセスによる並列実行には面倒な点がいくつかありました。それは、

+
    +
  • プロセスの操作が直接的でない

  • +
  • プロセス間通信が面倒

  • +
+

の2点です。新しいプロセスを作るにはforkというシステムコールを使いますが、作られたプロセスは実行中のプロセスの「子供」として登録されますが、子供とはいえ独立した存在なので、できるのは終了ステータスの取り出しなど限られた情報のやりとりだけです。

+
+

プロセス同士は独立した空間を持っていますから、子プロセスで変数の値を書き換えても親には影響を与えません。プロセス間で通信するには、パイプやソケットなどを使ってバイトストリームで行うしかありません。UNIXにはいちおう「共有メモリ」なんてものもあるんですが、そんなに使いやすくもありません。共有メモリについては今後機会があれば説明しようと思います。

+

独立した空間はお互いに邪魔をしないように保護するという目的があるので悪いことばかりではないのですが、緊密なやりとりを行いながら並列実行したいというニーズにはあまり向きません。そのような場合に使われるのが「スレッド」です。スレッドは1つのプロセスの中で複数の処理を並列に実行するものです。スレッド(thread)とは「縫い糸」のことです。実行の流れが縫い糸のようにちくちくと現れたり消えたり(実行されたり止まったり)することから連想されたのでしょう。

+

プロセスと違って、スレッドは空間を共有します。ですから、たとえば別々のスレッドから1つのオブジェクトを参照するなんてこともできます。バイトストリームに変換してから通信するなどの面倒なことを行わなくても情報のやりとりを行うことができます。

+

Rubyのスレッドは自前で用意している「ユーザーレベルスレッド」です。これはOSが提供する「カーネルレベルスレッド」と違ってマルチCPUを活用することもできませんし、スレッド切り替えのコストも高いので性能的には不利です。しかし、OSがスレッド機能を提供しているかどうかにかかわらず、どのOS(たとえMS-DOSでも!)でも同じように動くので、気軽にスレッドを体験することができます。

+
+
+

スレッド生成

+
+

Rubyではスレッドは普通のオブジェクトですから、newを使って生成します。

+
+
th = Thread.new{p 42}
+
+

これで新しいスレッドを生成し、そのうえでブロックを実行します。ですから、ブロックの中の「p 42」という処理の部分はスレッド生成に続く部分と並列に実行に実行されます。「並列に」と言っても本当に同時に動くわけではなくて、実際には短い時間で仕事を切り替えつつ交代に細切れに実行しています。厳密にはこのような「擬似並列」のことを「コンカレント」、複数のCPUを使って本当に同時に実行することを「パラレル」と呼びます。

+

スレッドはブロック内の処理が終わると終了します。newには他にforkとかstartとかいう別名もあり、こちらのほうがよく使われるかもしれません。

+
+
+

Threadのメソッド

+
+

Threadクラスには表22.1に示すクラスメソッドと表22.2に示すインスタンスメソッドがあります。

+ +
+

表22.1●Threadクラスメソッド

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
メソッド説明メソッド説明
Thread.
abort_on_exception
例外でプロセスを中断Thread.killカレントスレッド終了
Thread.
abort_on_exception
=val
中断状態の設定Thread.listスレッド一覧
Thread.critical割り込み禁止状態Thread.mainmainスレッド
Thread.critical
=val
割り込み禁止の設定Thread.newスレッドの生成
Thread.currentカレントスレッドThread.pass実行権の委譲
Thread.exitカレントスレッド終了Thread.startnewの別名
Thread.forknewの別名Thread.stop一時停止
+
+
+

表22.2●Threadインスタンスメソッド

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
メソッド説明メソッド説明
th[key]スレッド固有データth.
priority
優先度
th[key]
=val
固有データ設定th.
priority
=val
優先度の設定
th.
abort_on_exception
例外でプロセス中断th.raise(
exception)
例外の発生
th.
abort_on_exception
=val
中断状態の設定th.runスレッドを実行させる
th.alive?生きているかth.
safe_level
安全レベル
th.exitスレッドの終了th.statusスレッドの状態
th.joinスレッド終了を待つth.stop?停止しているか
th.key?(key)keyの対応データがあるかth.valueスレッドの戻り値
th.keyskeyの一覧th.wakeupスレッドを起こす
th.killスレッドの終了
+
+

Rubyのスレッドに特有の注意すべき点がいくつかあります。

+

第一はmainスレッドが終了すると他のスレッドは全部終了することです。mainスレッドとはプログラムが始まった時点で最初に動作しているスレッドです。プロセスと違ってRubyのスレッドに親子関係はありませんが、mainスレッドだけは特別扱いされます。mainスレッドが終了すると他のスレッドは処理中だろうが問答無用で強制終了されます。

+

第二の注意点は、実行中に例外が発生したスレッドは(デフォルトでは)黙って終了することです。プログラム開発中に予想外の例外が原因で、いつの間にかスレッドがいなくなってびっくりすることがときどきあります。これに対応する方法はいくつかあって、一番簡単なものはデバッグ中はインタプリタに -dオプションを渡すことです。-dオプションが与えられるとインタプリタは例外が発生すると、たとえその例外が捕捉されても例外名と発生したプログラム中の場所を標準エラー出力に表示してくれます。

+

もう1つの方法はabort_on_exceptionを指定することです。abort_on_exceptionを指定したスレッドが例外で中断されるとインタプリタ全体が強制終了します。Threadのクラスメソッドで指定するとすべてのスレッドに対して有効になります。

+
+
t = Thread.new{...}
+t.abort_on_exception=true       # tでの例外で終了
+
+Thread.abort_on_exception=true  # すべての例外で終了
+
+

上記の -dオプションもabort_on_exceptionもどちらかというとデバッグ用の対応ですが、実際の運用時向けのチェック手段もあります。例外で終了したスレッドに対してjoinメソッドやvalueメソッドを呼ぶと、それらのメソッドがスレッドを終了させた例外を再発生させます。スレッド中で例外が発生したかどうかチェックしたり、例外に対応させたりする場合に有効です。

+
+
+
t = Thread.new{...}
+t.join      # tの終了を待つ
+            # 例外があれば再発生
+
+

joinメソッドはスレッドの終了の待ち合わせ、valueメソッドはスレッドの終了を待ったうえで、スレッドに与えたブロックが最後に評価した値を返します。

+
+
+

スレッドの実行状態

+
+

スレッドには以下の4つの状態があります。

+
+

run

+

実行中。厳密にいえばRubyのスレッドは処理を少しずつ細切れに切り替えながら行うので、実行中というよりは「実行可能」という感じでしょうか。

+

stop

+

停止中。停止の理由は「IO待ち」「select(2)待ち」「時間待ち」「join待ち」があります。「時間待ち」とは指定した時間まで停止している状態(sleepなど)。「join待ち」とは他のスレッドの終了をjoinメソッドで待っている状態のことです。

+

to_kill

+

終了処理中。スレッドが強制終了されて、完全に終了する前にensureなどの処理を行う状態。

+

killed

+

終了。終了処理が完全に終わってしまった状態。一度終了したスレッドは復活することはありません。Thread.listでも終了してしまったスレッドは一覧に含まれません。

+
+

これらの状態は図22.1のように状態遷移します。

+
+ +
+ fig2201 +
+

図22.1●スレッドの状態遷移

+
+

スレッドの現在の状態を知るメソッドはstatus, alive?, stop? です。「生きている」場合にはstatusはスレッドの現在の状態を文字列で返します(killed状態についてはnilを返します)。alive? はスレッドが「生きている」かどうかを返します。stop? はスレッドが停止しているときに真を返します。「停止している」にはkilled状態も含まれます。

+

あと、状態とは直接は関係ないのですが、Thread.passを実行すると現在のスレッドの状態はrunのまま、他のrun状態のスレッドに実行権を譲ります。Rubyのスレッドは時間が経つと勝手に実行権を取り上げるようになっているので、明示的にこれを指定する必要があるケースは少ないですけど。

+
+
+ +

スレッドとセキュリティ

+
+

スレッドはただ処理を(擬似)並列に実行するための仕掛けですから、「スレッドとセキュリティに何の関係があるの」という疑問はもっともです。しかし、Rubyのセキュリティモデルはスレッドを利用しているのです。昨年の8月号の本連載『CGI』でも解説したようにRubyでは変数 $SAFEの値を操作することで、セキュリティレベルを設定できます。$SAFEに設定する値とその意味を表22.3に示します。

+
+

表22.3●セキュリティレベルとその意味

+ + + + + + + + + + + + + + + + + + + + + + + + + +
セキュリティレベル説明
0外部から入力された値が汚染される(デフォルト)
1汚染された値による危険な操作が禁止される
2プロセスやファイルに対する危険な操作が禁止される
3生成されるすべてのオブジェクトが汚染される
4グローバル情報の変更が禁止される
+
+

このセキュリティレベルはスレッド独立の値です。ということは、あるスレッドで $SAFEを高く設定しても他のスレッドには影響を与えないということです。これを利用して信頼できないプログラムをスレッドに隔離して実行することができます。

+
+
def safe_eval(str)
+  Thread.new{
+    $SAFE = 4           # ここだけレベル4に
+    eval(str)           # 文字列の評価・実行
+  }.value               # 結果を受け取る
+end
+
+safe_eval("1+1")        # => 2 (安全だから)
+safe_eval("`rm -r /`")  # 例外「Insecure operation」
+
+
+
+

スレッドの使用例

+
+

それではスレッドを使ったプログラムの使用例を見てみましょう(リスト22.1)。

+
+

リスト22.1●スレッドによるソケットサーバー

+
require "socket"
+
+gs = TCPserver.open(0)
+loop do
+  # スレッドの起動
+  # startの引数はブロックパラメータに渡る
+  Thread.start(gs.accept) do |s|
+    処理を行う
+    s.close
+  end
+end
+
+
+ +

簡単ですね。先月解説したプロセスを使ったソケットサーバー(リスト22.2)と比較するとより直接的であることがわかると思います。

+
+

リスト22.2●forkによるソケットサーバー

+
require "socket"
+
+gs = TCPserver.open(0)
+loop do
+  s = gs.accept
+  fork do
+    fork do     # zombie化を避ける二重fork
+      処理を行う
+      s.close
+    end
+  end
+  s.close       # 親では不要
+  Process.wait  # 直接の子を「供養」
+end
+
+
+

まだ、Thread.startの引数について説明していませんでしたね。Thread.startに引数を渡すとその引数がブロックパラメータに代入されます。スレッドに対して値を渡す1つの方法です。もちろん、ブロックからは外側の変数が見えるので、変数によって渡せばよいと思うのは自然なことですが、このプログラムのループ部分を、

+
+
s = gs.accept
+Thread.start() do
+  処理を行う
+  s.close
+end
+
+

と書き換えても一応動作しますが、実はタイミングによって誤動作します。つまり、スレッドの処理が終わる前に次のacceptが実行されると変数sの値が書き換わってしまうからです。

+

スレッドプログラミングではそれぞれのスレッドが同時に動くため、通常以上にプログラムの挙動の予想が困難です。より想像力が必要だともいえるでしょう。

+
+
+ +

共有の苦痛

+
+

スレッドを使えば情報のやりとりが簡単というのはうれしいことばかりではありません。実社会でもそうですが、共同生活でお互いが勝手なことをしていては遅かれ早かれ破綻します。スレッド処理においては、以下のような問題が発生する可能性があります。

+
    +
  • データ整合性の喪失

  • +
  • デッドロック

  • +
+

これらはスレッドによる問題というよりは並列処理全般で発生する問題ですが、スレッドでは空間を共有するためより起こりやすいということです。

+

これらの問題を個別に見てみましょう。

+
+

データ整合性の喪失

+

先ほどのソケットサーバーの例でもありましたが、共有しているデータを書き換えると思わぬ悪影響が発生します。スレッド同士は同じ空間を共有しているので、意図せずに同じ変数やオブジェクトを同時にアクセスしている可能性があります。「データ整合性の喪失」の例として最もよく使われるのが銀行口座の例です(図22.2)。

+
+ +
+ fig2202 +
+

図22.2●銀行口座の整合性喪失

+
+

簡単に流れを説明すると、ユーザーAは口座の残高を取得し、引き出したぶんの2千円を引いた額を新しい残高として設定します。一方、ユーザーBも同様に、口座の残高を取得し、引き出したぶんの4千円を引いた新しい残高を設定します。最初は1万円しかなかったのに、最終的には合計が1万2千円になっています。こんな銀行はまずいでしょう。

+

この手順がまずいのは複数の並列手続き(スレッド)から口座を共有していることに無頓着だったせいです。残高を取得してから新しい残高を設定するまでの処理は、途中で割り込まれてはいけません。このような分割してはいけない処理のことを「アトミックな処理」と呼びます。アトミックな処理の間、他のスレッドがこの口座を操作しないように保護してやる必要があります。

+
+
+

デッドロック

+

デッドロックは複数のスレッドが資源を奪いあって身動きが取れなくなる状態です。銀行口座に続きプログラミングでない例で恐縮ですが、古典的な例として「哲学者の晩餐ばんさん」を紹介しようと思います。哲学者の晩餐とは以下のような話です。

+ +
+

5人の哲学者が丸いテーブルに座っています。哲学者の前にはスパゲティの皿が置いてあります。また、哲学者の両側には1本ずつ箸が置いてあります(全部で5本)。哲学者は一日中思索しますが、ときどきお腹が空いたら両側の箸を取り、スパゲティを食べます。

+

さて、ある瞬間、偶然に哲学者全員がスパゲティを食べようとします。同時に全員が食べるだけの箸はありませんので、哲学者はそれぞれ箸を取りますが、箸が2本手に入らないので食べることができず、全員が飢えてしまいます。あるいは全員でなくても隣り合った哲学者が同時に箸を取りあうと面倒なことが発生します。

+
+

「スパゲティを箸で食うのか」とか「手で食べたらいけないのか」とか「スパゲティがダメならケーキを食べたらいいじゃないの」とかツッコミどころ満載の話ですが、まあ古典ですから。ともかく、このように資源(この場合は箸)が足りない場合、上手に考えないとデッドロックになります。プログラムは普通哲学者以上に融通が効かないので、餓死するまで停止することになるでしょう。

+

Rubyのサンプルプログラムにphilos.rbというものがあります。これを実行すると図22.3のような出力を延々と続けます。

+
+
+
-o-o-o|*|o-o-o-o-o
+-o|*|o|*|o-o-o-o-o
+-o|*|o|*|o|*|o-o-o
+-o|*|o-o-o|*|o-o-o
+|o|*|o-o-o|*|o-o|*
+|o-o-o-o-o|*|o-o|*
+|o-o|*|o-o|*|o-o|*
+|o-o|*|o-o-o-o-o|*
+|o-o|*|o-o-o|*|o|*
+...
+
+

図22.3●philos.rb の出力

+
+

これは実はこのphilos.rbは「哲学者の晩餐」シミュレータなのです。各行は丸いテーブルを直線に引き伸ばしたものだと考えてください。行の内容は単位時間ごとの哲学者とテーブルの状態を表しています。「o」は思索している哲学者、「*」は食事している哲学者です。また、「-」は置かれている箸、「|」は使用中の箸です。

+

ただ、philos.rbはデッドロックを避けるようにプログラムされています。具体的には、空腹になった哲学者はそれぞれまず右手側の箸を取り(空いていなければ待つ)、それから左手側の箸を取ります。左の箸が空いていなければ、右手の箸も一度返してしまい、空腹を我慢しつつ思索に戻ります。このようにすればむやみに箸を占有しないのでデッドロックは発生しません。このような「ルール」を用意することで、哲学者たちは飢えてしまうことはありません、空腹を少々我慢することはあっても。

+
+
+
+

まとめ

+
+

今月はスレッドによる並列処理のさわりについて学びました。また、スレッドによって発生しやすいトラブルについても学びました。このトラブルはプロセスでも発生します。

+

並列処理のトラブルはタイミングによって起きたり起きなかったりするのでつらいです。トラブルが起きないようにするためには、そもそもトラブルが起きない構造にする必要があります。それには……あ、今月は誌面が尽きてしまいました。トラブルが起きないスレッドプログラミングについては来月、ということで。ではまた。

+
+
+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-050.xhtml b/docs/vol1/xhtml/p-050.xhtml new file mode 100644 index 0000000..fd664b1 --- /dev/null +++ b/docs/vol1/xhtml/p-050.xhtml @@ -0,0 +1,50 @@ + + + + + +第22章 スレッド(その1) + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ スピード狂

+
+

私が住んでいる田舎では、歩いていける範囲内では買い物も満足にできないし、公共交通機関も発達していないので、結果として自動車は必需品です。自動車は地球環境の観点からはあまり望ましくない乗り物なのですが、実に便利な道具です。自動車のハンドルを握るとついついスピードを出してしまう人がいます。私は無茶な運転はしないように心がけているつもりですが、常に法定速度以内で運転するのはなかなか難しいものです。

+

自動車のスピードの出しすぎは命に関わる場合があるので止めたほうがよいのですが、コンピュータ業界は全体として「スピードは善である」というポリシーが徹底しているように思います。

+

実際にコンピュータのハードウェアはどんどん進化しています。インテルの創立者ゴードン・ムーアの提唱したムーアの法則によればLSIの集積度は18カ月で2倍になるのだそうです。集積度が上がるにつれて性能も飛躍的に向上しています。

+

今から60年近く前、世界最初の汎用デジタルコンピュータENIACが登場したとき、「これが3台あれば世界中の計算がまかなえる」といわれたものでした。調べてみるとENIACは1秒間に5000回の加算が可能であったのだそうです。

+

アーキテクチャがまったく違うため現代のCPUと比較することは難しいですが、単純に加算に必要な時間を(クロック周波数×パイプラインのステージ数)で計算するとなると1GHzのPentium 3(ステージは10段)ではCPUならば同じ時間に200万倍以上の計算ができることになります。世界に3台どころじゃありませんね。今や机の上にENIACの200万倍の計算パワーが乗っているわけです。恐るべし。

+

ハードウエアと比較すると、ソフトウェアのほうはそう簡単には進歩しません。アルゴリズムの教科書などを見てみても主要なアルゴリズムは30年も40年も前に発見されたものばかりで、最近画期的なアルゴリズムが見つかったなんて話はとんと聞きません。昔はハードウェアはなかなか変更できなくて、ソフトウェアは変更しやすいから「ハード」と「ソフト」と呼んだと聞くのですが、3年前に話題になった2000年問題などを思い起こすと今やソフトウェアのほうがよっぽど硬いという気がしてきます。ハードの方はどんどん進歩するので、それに甘えているということもあるかもしれません。なにしろ何もしなくても新しいハードを導入すれば同じソフトウェアが高速に動作するんですから。

+

しかし、いくらハードの進化が速くても私の目の前のコンピュータが自動的に高速になるわけではないので、ソフトウェアのほうでも高速化のための地道な努力が必要です。悲しいことにソフトウェアはハードウェアのように簡単に何倍も高速になったりはなかなかしないので。

+

しかも、ハードウェアの進歩以上に処理速度が要求される分野はいつの時代にも存在するので、ソフトウェア技術者の中にも処理速度向上のためなら手段を選ばないスピード狂はかなりいます。高速化が難しいぶん、あるいはハードウェア技術者より多いかもしれません。

+
+ +

ソフトウェアの高速化

+

ソフトウェアの高速化はむやみに行っても効果が薄いことが多々あります。効率のよい高速化のためには従わなければならない鉄則がいくつかあります。

+

最初の鉄則は「正しく動いてから高速化」です。正しく動かないソフトをいくら高速化しても意味がありません。また、高速化の過程ではバグが混入しますが、もとからちゃんと動いていないプログラムでは、高速化のせいだかなんだか判断できません。いくら高速化マニアでもプログラムが一応の完成を見るまではじっと我慢です。

+

2番目の鉄則は「正しく計測する」です。プログラムが速くなったかどうかは時間でわかりますが、処理時間は動作パターンによって大きく変化します。実際には使わないような人工的なパターンで計測したために、実際の処理はちっとも速くなってないというのもありがちなケースです。また、外部からの影響を受けて計測結果が変動することもたびたびあります。外部からの影響を避けるため、何度も繰り返して計測し、最も速いもの(平均ではない)を選ぶなどの工夫が必要です。あるいは起動してから終了するまでの時間ではなく、消費したCPU時間を計測するのもよい方法です。CPU時間の計測にはtimeコマンドを使います。

+

3番目の鉄則は「改善の対象を正しく選ぶ」です。世の中のほとんどの現象には「80:20則」とか「90:10則」が当てはまります。つまり、「80%の時間が20%のコードで消費される」とか「90%のコストが10%の部分に費やされる」とかいう状態です。ですから、効率の悪い部分の改善してその部分の実行速度を10倍にしたとしても、全体のうちその部分が占めていた割合が1%しかなかったら、全体としては99.1%しか高速化しません。まず時間を最も消費しているボトルネック部分から高速化するべきです。

+

4番目の鉄則は「正しいアルゴリズムを選ぶ」です。高速化の手法は大きく分けると2種類あって、1つはアルゴリズムを変更することで、もう1つはいわゆる「小手先の変更」です。小手先の変更とは、たとえばコードの無駄を省いたり、一度計算した結果を保存しておいて再利用するなどの比較的小規模な変更です。小手先の変更のほうが、簡単ですぐに思い付くのでそちらに走りやすいのですが、まず「この処理に使っているアルゴリズムは最も高速なものか」ということを検討する必要があります。小手先の変更ではかなり運がよくても実行速度は数割しか変化しません。ほとんどの場合は数%程度でしょう。しかし、アルゴリズムの変更の場合にはもっと劇的な変化が期待できます。場合によっては100倍や1000倍の変化も夢ではありません。適切なアルゴリズムを選んだ後、初めて小手先の変更を行うようにしましょう。

+

このような鉄則に従うことによって、ソフトウェアの無駄が取り除かれ、効率よくソフトウェアの高速化を行うことができます。

+

私を含めて多くのソフトウェア開発者にとってソフトウェアのチューニングは楽しい側面も持っています。自分の作業の成果が目に見える数字(実行時間)として現れるため、達成感が大きいためです。あそこで1秒、ここで2秒と実行時間を削減していくうちに寝食を忘れてしまうことも珍しくありません。

+

特に言語処理系のようなどういうふうに使われるかは前もって予想もできないようなプログラムでは、ありとあらゆる部分がボトルネックになりえます。というわけで、先ほどの鉄則も忘れてむやみやたらといじりまわるわけです。

+

確かに楽しいですけど、なんか本来の目的を逸脱しているような……。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-051.xhtml b/docs/vol1/xhtml/p-051.xhtml new file mode 100644 index 0000000..9c22022 --- /dev/null +++ b/docs/vol1/xhtml/p-051.xhtml @@ -0,0 +1,320 @@ + + + + + +第23章 スレッド(その2) + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay23 +
+

+初等Ruby講座
+スレッド(その2) +

+
+

[Linux magazine, 2003年4月号]

+
+

スレッドについての解説の続きです。今回はスレッドでの問題を避けるための仕組みであるロックとキューと条件変数について解説しています。当時はマルチコアコンピュータが一般的でなかったせいもあって、スレッドについてはさらっと解説していますね。記事執筆時からしばらく経つと、パフォーマンス改善のために複数コアを活用することが一般的になり、そのためのスレッドの重要性が増すことになるのですが。

+

が、コンカレントプログラミングの原則は同じです。ここに書いていない大原則として、「フレームワークやライブラリに任せて、一般ユーザーはそもそのコンカレントプログラミングするべきでない」というものがありますが。

+

「Ruby開発日記」は、「プログラマーの幸せ」というテーマでプログラマーという「人種」の気質について解説しています。私自身は、プログラマーが特殊であるとはあまり考えたくないのですが、自分と周囲のズレを考えると否定しきれないものがあります。

+
+
+

先月に引き続いてスレッドについて、特に複数のスレッド間で発生する問題の避け方について学びます。

+
+
+

先月の復習

+
+

まずは先月学んだことを復習してみましょう。

+

Rubyでは1つのプロセスの中で複数の処理を同時に行うことができ、その処理の流れ1つ1つをスレッドと呼びます。Rubyではスレッドもオブジェクトです。スレッドの生成は以下のように行います。

+
+
th = Thread.start{...}
+
+
+

スレッドはブロックの中の処理を開始します。処理はstartに続く処理と並列に行われます。これをコンカレントと呼ぶのでしたね。

+

スレッドの処理が終わったかどうかを知るためにはthに対してalive? メソッドを呼びます。スレッドの処理が終了するまで待つためにはjoinメソッドを呼びます。

+

こうやって生成された各スレッドは空間を共有します。つまり、同じプロセスの中なので同じオブジェクトを参照することが(更新することも)できるのです。これは空間が独立しているために情報共有が難しいプロセスとは異なるところです。

+

しかし、同じオブジェクトに同時にアクセスできるために発生する問題もあります。今月はこの問題を回避する方法を学びます。

+
+
+

リソース問題

+
+

スレッドを使ったプログラムで問題が発生するのは基本的に「同時にアクセスしてはいけないものにアクセスする」ことが原因です。この同時にアクセスしてはいけないものをリソースと呼ぶことがあります。リソースは別に特別なものではなくて、Rubyの場合には基本的にオブジェクトになります。複数のスレッドが利用を求める存在という意味でリソース(資源)と呼ぶわけです。

+

リソースの競合による問題は、大きく分けると、

+
    +
  • 整合性の破綻(先月の銀行口座の例)

  • +
  • デッドロック(先月の哲学者の例)

  • +
+

があります。最も簡単にこの問題を避けるためには、自分が使っている間はリソースを独占してしまえばよいのです。そのためにリソースのロックという手段が提供されています。

+

しかし、何でも独占してしまえばよいというのでは子供と一緒です。すぐに独り占めしたがる子っていましたよね。独占以外にも協調する方法があるので、その手段も紹介しましょう。

+
+
+

ロックによるリソースの独占

+
+

要するに1つしかないものを同時に使おうとするのが間違いの元だ、ということで、誰かが使っている間は他の人は使わないように取り決めれば問題は解決するというものです。現実社会で一番よく目にするのはトイレの個室でしょう。トイレは一度には一人しか使えません。複数の人が入っているとかなり無気味です。そこで、誰かが入っている間は鍵をかけ、外からは赤い印が見えるようにしてあります。

+

スレッド処理の場合も同様に、1つしかないリソースを使う場合にはそれをロックして独占中という印を付けてしまえばよいのです。そのような目的に使われるクラスにMutexがあります。Mutexは相互排他ロック(Mutual Exclusive Lock)の略です。Mutexを使うためにはthreadライブラリをrequireする必要があります。

+
+
require 'thread'
+
+m = Mutex.new
+
+....
+m.synchronize {
+  ...           # リソースを使った処理
+}
+
+
+

synchronizeメソッドを呼び出すと、そのブロック処理を行っている間、Mutexはロックされます。他のスレッドが同じMutexに対してsynchronizeを行おうとすると、現在実行中のsynchronizeが終了してロックが解除されるまで待たされます。このsynchronizeで囲まれたブロックを、他のスレッドが入り込まない領域という意味で「クリティカルセクション」と呼ぶことがあります。ちょうど一度に一人しか入れないトイレと同じですね。

+

synchronizeの代わりにlockメソッドでロックしてunlockメソッドでロックの解除を行うこともできますが、たとえばロック中に例外が発生することなども考えると以下のようにするべきです。

+
+
m.lock
+begin
+  ...     # ロック中の処理
+ensure
+  m.unlock
+end
+
+

ensurebeginの実行を抜けるときに必ず実行されるので、間違いなくunlockされることが保証されます。実はこれはまさにsynchronizeメソッドの定義そのものなので、lockunlockを個別に使うよりはsynchronizeメソッドを使うことをお勧めしておきます。

+

ただ、忘れてはいけないことは、Mutexも、これから説明する他のロックもあくまでも紳士協定でロックのチェックを行わずにリソースを無理やり操作することは禁止されないということです。ロックでリソースを保護する場合には、すべてのアクセスでロックのチェックを行うことを忘れないでください。Ruby的にスマートな方法は、リソースのアクセスをすべてオブジェクト経由にして、そのオブジェクトのメソッドの内部でロックのチェックを行うことです。

+
+
class Resource
+  def initialize
+    @lock = Mutex.new
+  end
+  def use
+    @lock.synchronize {
+       ...
+    }
+  end
+end
+
+r = Resource.new
+r.use
+
+
+

Javaだとメソッド定義にsynchronizeと宣言すれば、メソッド呼び出しによって自動的にロックを行ってくれるのですが、Rubyではそこまでは面倒を見てくれません。

+
+
+

2レベル排他

+
+

しかし、排他制御は単純なMutexでカバーできるほど単純なケースばかりではありません。たとえばデータベースなどは、以下のような条件の制御が必要になります。

+
    +
  • データの参照は同時に行ってもよい

  • +
  • データの更新は同時に行ってはいけない

  • +
  • データの更新中は参照してはいけない

  • +
  • データの参照中は更新してはいけない

  • +
+

このような参照と更新の2レベルがある排他制御は、他にもファイルの読み出しなどしばしば発生します。現実世界ではたとえばテレビを見ること(参照)とチャンネル変更(更新)などがこれに相当するでしょう。誰かがテレビを見ている間、他の人が同じチャンネルを見ることができないというのは行きすぎた独占ですが、だからといって排他制御なしに見ている最中にチャンネルを変えられるのはイヤでしょう。

+

このようなケースのためのロックはsync.rbで定義されているSyncクラスを使います。SyncクラスはMutexと同じように使いますが、synchronizeメソッドに排他モードを指定することができます。排他モードは、更新のように同時に1つしか実行できないEXモードと、EXモードとは同時に実行できないが、その他のモードとは排他しないSHモードがあり、それぞれシンボル「:EX」と「:SH」で指定します。

+

では、Syncクラスの使用例をテレビの例で見てみましょう(リスト23.1)。プログラミングとしてはやや不自然な例ですが我慢してください。Mutexで示したようにリソース(この場合はテレビ)のオブジェクトのメソッドに排他制御を埋め込んでおきましょう。そのほうがスマートですから。

+
+

リスト23.1●Syncクラスの使用例

+
require 'sync'
+
+class TV
+  def initialize
+    @ch = 1
+    @sync = Sync.new
+  end
+  def watch
+    @sync.synchronize(:SH) {
+       ....     # TVを見る
+    }
+  end
+  def set_ch(ch)
+    @sync.synchronize(:EX) {
+      @ch = ch  # チャンネルを変える
+    }
+  end
+end
+
+
+ +

このようにしておけば、誰かがテレビを独り占めしたり、誰かがテレビを見ている最中に勝手にチャンネルを変えたりして、けんかになる事態を避けることができます。家庭に平和が戻ってきますね。

+

さて、syncライブラリにはクラスそのものにSync機能を追加するMix-inも用意されています。これを使って先ほどのテレビの例を修正するとリスト23.2のようになります。

+
+

リスト23.2●Mix-inを使ってリスト23.1を書き換えた場合

+
require 'sync'
+
+class TV
+  include Sync_m # Mix-in
+  def initialize
+    super        # Sync_mの初期化
+    @ch = 1
+  end
+  def watch
+    synchronize(:SH) {
+       ....      # TVを見る
+    }
+  end
+  def set_ch(ch)
+    synchronize(:EX) {
+      @ch = ch   # チャンネルを変える
+    }
+  end
+end
+
+
+

このようにSync_mを使うとわざわざSyncオブジェクトを作らなくても、そのオブジェクト自身に2レベル同期機能を追加することができます。どちらが便利かは一概にはいえませんが、好きなほうを使ってください。Sync_mを使ったほうが無駄なロック用のオブジェクトを使わずに済むわけですが、1オブジェクト使うかどうかでは性能上の差は現れないでしょう。

+
+
+ +

キュー

+
+

リソースの排他制御はスレッド間の協調の典型的な例ですが、協調のスタイルは他にもあります。もう1つの典型例はスレッドの流れ作業的な関係です。つまり、あるスレッドがデータを作り、別のスレッドがそのデータを受け取り、次の処理を行います。このような関係は「生産者・消費者モデル」と呼ばれ、データを作るスレッドを「生産者」、データを受け取るスレッドを「消費者」と呼びます(図23.1)。「消費者」は加工したデータを他の消費者に渡して、生産者の役割を果たすこともあります。

+
+ +
+ fig2301 +
+

図23.1●生産者・消費者モデル

+
+

生産者と消費者とをつなぐ「ベルトコンベア」の役割を果たすクラスが「Queue」クラスです。QueueはFIFO(First-In First-Out)とも呼ばれるデータの入れ物で、その名のとおり、先に入れたものが先に出るという性質があります。では、実際にQueueを使ったプログラムを見てみましょう(リスト23.3)。

+
+

リスト23.3●Queueを使ったプログラムの例

+
require 'thread'
+
+q = Queue.new
+
+producer = Thread.new {
+  10.times {|i|
+    q.push(i)
+    sleep(1)
+  }
+  q.push(nil)
+}
+consumer = Thread.new {
+  loop {
+    i = q.pop
+    break if i == nil
+    puts i
+  }
+}
+consumer.join
+
+
+ +

「生産者(producer)」は1秒ごとに整数をQueueに追加し、「消費者(consumer)」はQueueから取り出した数字を受け取り出力しています。消費者のスピードが速くてQueueが空になってしまうと消費者は生産者がQueueにデータを追加するまでストップします。生産者は一定数の整数を送り出した後、終了のマークとしてnilを送ります。消費者はnilを受け取ったらbreakでループを中断します。メインスレッドはjoinで消費者スレッドの実行を待っています。実際に実行されると以下のようになります。

+
+
% ruby q.rb
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+%
+
+

では、仮に生産者のほうが消費者よりもスピードが速かったとしたらどうでしょう。消費しきれなかったデータはQueueにたまり、Queueの長さはどんどん長くなります。消費しきれないのに全力でデータを生産する必要はないかもしれません。そんな場合に便利なのがSizedQueueです。これは長さに制限のあるQueueで、データが一定以上たまったら、データのpushでストップがかかります。SizedQueueの使い方は簡単で、先ほどのプログラム例だと、

+
+
q = Queue.new
+
+

の部分を、

+
q = SizedQueue.new(size)
+
+
q = SizedQueue.new(size)
+
+

に置き換えるだけです。sizeの部分にはQueueの最大長が入ります。これだけで生産が速すぎても、消費が速すぎても調整を行うことができます。

+

Queueはリソースの競合の解決にも使うことができます。リソースを管理するスレッドを1つ用意して、リクエストはQueueを介してそのスレッドに送るようにします(図23.2)。リソースを直接操作するのは管理スレッドだけですから、競合は発生しません。

+
+ +
+ fig2302 +
+

図23.2●Queueによるリソース制御

+
+

実際の例としてはRuby/Tkがこの手法を使っています。Tkを介したGUI操作は複数のスレッドが同時にアクセスできないので、各スレッドは管理スレッドにGUI命令を転送します。送られたGUI命令は管理スレッドがTkライブラリを呼び出し処理します。

+
+
+ +

条件変数

+
+

今までロックによるリソースの独占とキューによるスレッド間の流れ作業について解説しましたが、スレッド間の協調はもっと複雑な場合があります。

+

たとえば、クリティカルセクションの中で他のスレッドにリソースを準備を要求したいといったケースでは、ただ単にMutexのロックを解除するだけでは、そのリソースの準備ができたかどうか保証することができません。スレッド同士は独立して動作するので、きちんとした同期を行わなければどんなタイミングで動作するかわかりません。一度Mutexを解放して、再度ロックするまでに別のスレッドの処理が終わっているかどうかわからないのです。ですから、確実に処理を行うために必要なのは、

+
+

①クリティカルセクションの中で

+

②ロックを別のスレッドに一時的に手渡し

+

③そのスレッドの処理が完了したときにロックを取り戻す

+
+

手段です。このような待ち合わせには「条件変数(ConditionVariable)」というオブジェクトを使います。慣習的に「変数」と呼びますが、Rubyの変数(ローカル変数など)とは直接は関係がありません。

+

条件変数の使用例がリスト23.4です。ConditionVariableは、Mutexによるクリティカルセクションの中で使います。「th1.join」と「th2.join」は各スレッドの終了を待つためです。メインスレッドの実行が終了するとプログラム全体が終了してしまいますから、これがないと処理が途中で中断させられてしまいます。

+
+

リスト23.4●ConditionVariableを使ってスレッドの同期を行う

+
require 'thread'
+
+m = Mutex.new
+c = ConditionVariable.new
+
+th1 = Thread.start {
+  m.synchronize {
+    puts "1: c.waitによって待ちに入る"
+    c.wait(m)
+    puts "1: 通知を受けc.waitから戻る"
+  }
+}
+
+th2 = Thread.start {
+  m.synchronize {
+    puts "\t2: c.waitを受けて処理開始"
+    puts "\t2: c.signalで処理完了通知"
+    c.signal
+    puts "\t2: まだクリティカルセクション"
+  }
+}
+th1.join
+th2.join
+
+
+ +

このプログラムの実行結果は以下のようになります。

+
+
1: c.waitによって待ちに入る
+        2: c.waitを受けて処理開始
+        2: c.signalで処理完了通知
+        2: まだクリティカルセクション
+1: 通知を受けc.waitから戻る
+
+

条件変数はキューに比べて、スレッドの関係がやや複雑になります。ですから可能であれば、スレッドの関係を生産者・消費者モデルで構築して、構造を単純化するのがよいのではないでしょうか。

+
+
+

スレッドトラブルの回避

+
+

先にも述べましたが、スレッドプログラミングでトラブルが起きるとデバッグが大変です。スレッド関係のバグはタイミングによって発生したりしなかったりすることが多く、同じように実行しても再現しないとか、10回のうち1回だけうまくいかないとかイヤなバグになることが多いのです。

+

それを避けるためには以下のようなルールが役に立ちます。もちろん完全ではありませんが。

+
    +
  • できるだけ各スレッドの処理が独立になるように心がける。複数のスレッドが同じオブジェクトをアクセスしないように。たとえば、HTTPサーバーでリクエスト単位でスレッド処理を行うなどはいい例でしょう

  • +
  • 複数のスレッドが協調する必要がある場合には、できるだけ「生産者・消費者」の関係になるように心がける

  • +
  • どうしても1つのリソースに同時にアクセスする必要がある場合には、必ずMutexなどで保護する

  • +
  • リソースの準備などの関係で待ち合わせが必要な場合には条件変数で同期する

  • +
+

はっきり言ってスレッドプログラミングは難しいです。MutexConditionVariableを使う必要が出てきたら、それはプログラムのやり方が間違ってるかもしれないと考えたほうがよいかもしれません。あなたがクラスライブラリをどんどん作るような上級者であれば話は別ですが。

+
+
+

まとめ

+
+

ということで、今月は先月に引き続きスレッドプログラミングについて、特にトラブルの回避方法について学びました。正直なところ、私自身もスレッドプログラミングの経験値は低いので(Rubyのスレッドシステムを自分で作ったのに)、えらそうな解説はできないのですが、今回紹介したような「ルール」は他の人の経験も踏まえたモノなので、トラブルを避けるために役立つのではないでしょうか。

+

Rubyのスレッドには、どこでも同じように使える、という性質があります。気軽にスレッドを使ってみて慣れてください。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-052.xhtml b/docs/vol1/xhtml/p-052.xhtml new file mode 100644 index 0000000..38e4fa0 --- /dev/null +++ b/docs/vol1/xhtml/p-052.xhtml @@ -0,0 +1,45 @@ + + + + + +第23章 スレッド(その2) + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ プログラマーの幸せ

+
+

「世の中には2種類の人間がいる」というのはよくいわれるセリフですが、私もそう感じるときがあります。つまり、「世の中には2種類の人間がいる。プログラマーとそうでない人々だ」ということです。

+

いや、もちろんプログラマーだって人間なのですが、いろいろな点で「普通の人」とは違っていると思われているように感じます。そのような誤解を解消しようと考察してみたのですが、考えれば考えるほど「やっぱり違うのかもしれないなあ」と思ってしまいます。

+

「普通」の人から見てプログラマーが変わっていると思われる最初の点はコンピュータに触るのが苦痛でないことです。私にはよくわからないのですが、コンピュータというものはよくわからない恐ろしいもので、それを苦もなく扱う人は同じくらい恐ろしいと感じる人は確かにいるようです。ただ、幸いなことに技術の進歩でたいていの家庭にPCが入り込んで、メールやインターネット(つまりWebのこと)を使いこなす「普通の人」も増えてきたので、この先入観は最近減ってきているようです。

+

変わっていると思われるもう1つの点は、プログラマーがプログラムを作り出す点です。「普通の人」にとってはプログラムはすでに存在していて、買うなり、もらうなり、ダウンロードするなりして入手するものであって、自分で作るものではないわけです。それを自分で作ってしまうとはいったいナニモノかという感じでしょうか。たとえると自動車を完全自作する人と同じようにとられているのでしょうか。

+

最後の点としては、生活時間が狂っている人が多いということでしょうか。確かに私の周りを見てもプログラマーには夜型の人が多いのは本当です。私自身も夜中に活動していることが多いですし(時差の関係でメールの読み書きにちょうどいいということもあるし)、私の会社のプログラマーたちも裁量労働制ということもあって、午前中には誰もいないし、夜はいつまでたっても誰も帰らないということがたびたびあるようです。

+

しかし、私はあえて当たり前のことを改めて主張しようと思います。「プログラマーも普通の人間である」と。

+

そもそも多くの人間は夜型になりやすい傾向があります。特に締め切りに追われる状況ではそうです。ですから、プログラマーと同じように締め切りのある職業の方々は似たような生活状況なのではないでしょうか。直接の知り合いはいませんが漫画家や作家の方々も同じように昼夜逆転している人が多いと想像します。

+

寝る時間を削っても仕事をするというのは、その仕事がキツいか、仕事がよっぽど好きかのいずれか、または両方です。プログラマーが昼夜逆転している人が多いということは、プログラマーの身分が低く、労働条件が悪い(ことが多い)ことと、プログラム好きでプログラマーをしている人が多いことを示しているのかもしれません。

+
+

プログラムを作るという行為もかなり誤解されています。もっともエディタ(というか普通の人にとっては「コンピュータの画面」)に向かって、英語のようなものを打ち込んでにやにやしている姿は無気味に映るのかもしれません。モノを作るといっても、そのモノには形があるわけではありませんし。何もないところから創造するという点では、漫画家や作家などと比較されるべきなのかもしれません。現実世界のルールに縛られず、何でもアリというところも似ているでしょう。

+

そうです。プログラマーはクリエーターなのです。しかも出来上がったものは、プログラマーの手を離れて、コンピュータの力を借りて一人で動き出すのです。あるいは1つの世界を創造すると言ってもよいでしょう。プログラムというものはその点で、書かれたものがそのとおりに存在する点において漫画や小説を超えているといえるでしょう。まさにプログラマーは世界を構築する法則を作り出しているのです。そして、その法則(プログラム)に従い、「世界シミュレータ」であるところのコンピュータがその世界を人間の目に見える形に変換しているのです。現代の創造主は電脳に宿るのです。かなり大げさな表現ですが、FF Xとかを見ると「世界の創造」もあながち言い過ぎではないなと感じます。

+

人はモノを作り出すときに喜びを感じます。プログラマーの多くがまっとうな生活を犠牲にしてまでのめり込む理由の1つはこれなのです。それは漫画家が徹夜してでも漫画を書くことと同じような誰にでもある感情です。決して理解できないものではないはずです。しかも、プログラミングは面白い漫画を書いたり、小説を書くよりはずっとずっと簡単に始めることができます。いきなりFF Xのようなものは作れないかもしれませんが、小規模なものならちょっとしたツールをそろえればすぐにでも始められます。そういう目的にはRubyもお役に立てるでしょう。

+

さて、プログラムを作っているとバグはつきものです。コンピュータは人間が意図したとおりではなく、プログラムに書かれているとおりに動作します。そして、プログラマーはしばしば自分の意図をプログラムに表現することに失敗して、思わぬ結果を得ることになります。そうなると、意図した結果を得るために、表現の間違いを探し出し、正しい表現に置き換えねばなりません。これは一種のパズルのようなものです。

+

パズルが好きな人も多いと思います。これは別に新しい何かを産み出すことではありませんが、自分の能力の限界に挑戦し、脳を活性化させます。これにはある種の快感が伴います。プログラムを作り出すことで1つの世界を創造する喜びを味わい、その後はパズルの喜びを感じることができるプログラミングはなんとすばらしい精神活動でしょう。これほど高尚な創造行為があるでしょうか。また、プログラミングとは、コンピュータ相手の非人間的な行動と思われがちですが、実際には人間の心の中にある意図を明確化して表現し、また人間の過ちを見つけ出し、正しく表現し直すという非常に人間的な活動なのです。

+

とはいえ、プログラマーもいつも好きなプログラムばかり作っているわけにはいきません。仕事のために面白くもないプログラミングを我慢する必要もあります。また、パズルというにはあまりに難解で非人間的なバグが襲ってくることもあります。そういうときに限って締め切りが厳しく迫ってくるのです。プログラマーという仕事はそれほど楽なことばかりではありません。

+

いかん、少々疲れてきたようです。こういうときは仕事を早めに切り上げて温泉にでも行きましょうか。お湯につかってのんびりしてると新しいアイデアが浮かんでくるかもしれません。

+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-053.xhtml b/docs/vol1/xhtml/p-053.xhtml new file mode 100644 index 0000000..8f0b16b --- /dev/null +++ b/docs/vol1/xhtml/p-053.xhtml @@ -0,0 +1,274 @@ + + + + + +第24章 データの保存 + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay24 +
+

+初等Ruby講座
+データの保存 +

+
+

[Linux magazine, 2003年5月号]

+
+

データの保存というタイトルで、MarshalPStoreを取り上げています。通常であればSQLiteとか何らかのRDBMSを取り上げるべきなのかもしれませんが、私はRDBMSがあまり得意でないので、こういうアプローチになっています。ただ、PStoreはあくまでもオモチャなので、データ容量的にもアクセス速度的にも、本気のアプリケーションでは使い物になりませんね。

+

「Ruby開発日記」は、「プログラマーと数学」というテーマです。結局は自分が学生時代に数学が得意でなかったという告白になっているような気がします。学生時代は本当に数学で苦労しました。

+
+
+

プログラムが作成したデータをファイルに保存しておきたいことはたびたびあります。今回はRubyでデータ保存を行う方法をいくつか紹介します。

+
+
+

オブジェクトの保存

+
+

Rubyのオブジェクトは現実世界にある「もの」と違ってコンピュータのメモリの中に作られます。ですから、プログラムの実行が終わってしまえば、メモリは初期化されてしまい、オブジェクトは煙のように消えてしまいます。しかし、すべてのデータがプログラムの終了とともに消えてしまっては困るので、必要なデータはファイルに保存し、次回読み込んでくる必要があります。では、問題です。

+

第1問。ここに "hello world\n" という文字列データがあったとして、このデータをカレントディレクトリのdataというファイルに保存し、再び読み込むためにはどうしたらよいでしょうか。

+

ファイルは文字列の読み込み、書き込みに対応しているので、これは簡単です。解答は以下のようになるでしょう。

+
+
data = "hello world\n"
+open("./data", "w") {|f|
+  f.write data
+}
+open("./data", "r") {|f|
+  data = f.gets
+}
+
+
+

それでは、第2問。書き込み・読み込みを行うデータが単なる文字列でなく、配列やオブジェクトだったら? しかも、オブジェクトが参照している先の先まで入出力する必要があったら?

+

これは少々面倒です。文字列のような参照を含まないデータであれば、比較的簡単ですが、Rubyのオブジェクトには参照で複数のオブジェクトが連結していたり、場合によっては1つのオブジェクトを複数の場所から参照していることもあります(図24.1)。

+
+ +
+ fig2401 +
+

図24.1●Ruby のオブジェクト

+
+

単純にオブジェクトの参照をたどって書き出すだけでは、複数回参照されているオブジェクトの情報が何度も書き出されてしまいますし、参照が循環していた場合には最悪無限ループに陥ります。これはまずいでしょう。

+

このような関係にあるかもしれないオブジェクトの情報を一度ファイルに書き出し、そしてもう一度読み込むにはどうしたらよいのでしょうか。

+

今月はこのようなオブジェクトデータの保存と読み込みについて学びます。

+
+
+

Marshal

+
+

ファイルには文字列(厳密にはバイト列)なら格納できるわけですから、オブジェクトを文字列に変換できれば問題は解決です。このようにオブジェクトをバイト列に変換することをシリアライズとかマーシャル(またはマーシャリング)とか呼びます。

+

辞書を調べるとシリアライズ(serialize)は「直列化」とか「順番に並べる」とかいう意味で、オブジェクトをバイトの並びに置き換えることを表現しています。一方、マーシャル(marshal)には「兵隊などを整列させる」という意味がありますので、こちらも同じような意味です。Javaではシリアライズという単語をよく使うようですが、Rubyではマーシャルと呼ぶことのほうが多いようです。いずれの単語で呼ぶにせよ、Rubyにはそのような機能が標準で組み込まれています。それがMarshalです。

+

Marshalモジュールには、ほぼ任意のRubyオブジェクトをバイト列に変換するメソッドdumpと、逆に変換されたバイト列から元のオブジェクト(のコピー)を復元するメソッドloadが提供されます(表24.1)。

+ +
+

表24.1●Marshalモジュールのメソッド

+ + + + + + + + + + + + + +
メソッド説明
dump(object[,io][,limit])objectとそこから参照されるオブジェクトをバイト列に変換します。第2引数としてIOオブジェクトが指定されたときにはそのIOに対して変換結果を書き出します。そうでないときには変換されたバイト列を返します。整数limitが指定されたときには、limit段以上深くリンクしたオブジェクトを変換しようとすると例外が発生します
load(from[, proc])dumpによって変換されたバイト列(文字列)、または変換されたバイト列を保存したIOオブジェクトを引数にとり、元のオブジェクトと同じ状態を持つオブジェクトを返します。第2引数として手続きオブジェクトprocが指定されたときには復元されたそれぞれのオブジェクトに対してprocが呼び出されます
+
+

Marshalを使えば、複雑な構造を持つデータも簡単にファイルに書き出すことができます。もちろん、図24.1に示した同じオブジェクトが複数回参照されるケースにも、参照が循環するケースにもちゃんと対応しています。

+

Marshalは基本的なオブジェクトの構造を知っていますから、普通のオブジェクトからバイト列への変換はMarshalモジュールのdumpメソッドが自動的に行ってくれます。使い方は簡単です。

+
+
data = Marshal.dump(obj)
+
+

これでobjとそこから参照されるすべてのオブジェクトをバイト列に変換したものがdataに代入されます。ファイルに直接出力する場合には、

+
+
f = open("/tmp/data", "w")
+Marshal.dump(obj, f)
+
+

とします。dumpしたデータを読み戻すためには、

+
+
obj2 = Marshal.load(data)
+
+

とします。obj2には元のオブジェクトのコピーが代入されます。ファイルから読み出す場合はどうすればよいかもうわかりますよね。

+
+
f = open("/tmp/data", "r")
+obj2 = Marshal.load(f)
+
+

このようにMarshalを使えばオブジェクトをバイト列に簡単に変換できます。この機能を使えばオブジェクトデータを簡単にファイルにセーブすることができます。

+

Marshalを使えば、オブジェクトのディープコピーも行えます。普通にcloneメソッドなどでオブジェクトのコピーを行っても、そのオブジェクトだけがコピーされ、参照先はコピーされません。こういうコピーをシャローコピー(shallow copy – 浅いコピー)と呼びます。しかし、ときとして参照先も含めて再帰的にコピーしたい場合があります。このようなコピーはディープコピー(deep copy – 深いコピー)と呼ばれます。Marshalを使えばディープコピーは以下のように実現できます。

+
+
obj2 = Marshal.load(Marshal.dump(obj))
+
+

こんなに便利なMarshalですが、以下のような欠点もあります。

+ +
    +
  • (a) 変換結果がバイナリで人間に理解しにくい

  • +
  • (b) 変換フォーマットのバージョンがそろっている必要がある

  • +
  • (c) あらゆるオブジェクトがダンプできるわけではない

  • +
  • (d) スレッドやマルチプロセスに対応していない

  • +
  • (e) 単なるdeep copyの手段としては効率が悪い

  • +
+

また、Marshalは、

+
    +
  • 特異メソッドを定義したオブジェクト

  • +
  • IOやソケットなど基本的にプロセスを超えて保存できないオブジェクト

  • +
  • 拡張ライブラリが定義したようなRubyが保存方法を知らないオブジェクト

  • +
+

は保存できません。

+

これらのオブジェクトはMarshalが適切な保存方法を知らないから保存できないわけです。適切な保存方法を知らないというのは、

+
    +
  • 適切な保存方法が(一般的には)存在しない

  • +
  • そのオブジェクトに対する情報がない

  • +
+

のいずれかです。いずれにせよ、一般的には保存できなくてもある特定のプログラムにおいて適切な方法があれば、それをMarshalに教えることができればよいわけです。このようなMarshalをカスタマイズしたいというニーズを満たすために、ユーザーがクラスごとに独自の変換が行うための_dump, _loadメソッドを定義することができます。

+

Marshal.dumpはダンプしようとするオブジェクトに _dumpメソッドが定義されているときには、そのメソッドを呼び出した結果(文字列である必要があります)を変換結果とします。_dumpの引数としては現在のlimitが渡されます。Marshal.loadは復元しようとするクラスに _loadメソッドが定義されていれば、そのメソッドを呼び出します。_loadの引数には_dumpが返した文字列がそのまま渡されます。_dumpはインスタンスメソッドですが、_loadはクラスメソッドであることに注意してください。_dump_loadの使い方の例をリスト24.1に示します。

+
+

リスト24.1●_dump, _loadの使い方

+
# _dump, _loadの例
+class Foo
+  def initialize(str=nil)
+    @foo = str
+  end
+  def _dump
+    String(@foo || "")  # @fooの値をdump
+  end
+  def Foo._load(str)
+    self.new(str)       # 保存された値で初期化
+  end
+end
+
+
+ +

一度、_dumpを定義したらMarshalは何も助けてくれません。Marshalが自動的にやってくれていたインスタンス変数の書き出しなども、必要であれば自分でやる必要があります。したがって、_dumpの利用は慎重に行うべきでしょう。先に述べたようなMarshalが対応できないオブジェクトをどうしてもダンプしたい場合にだけ用いるべきです。

+

Marshalの応用例としては、分散Rubyを実現するdRubyがあります。dRubyはオブジェクトをMarshalによって変換してネットワークを超えた分散プログラミングを可能にしています。dRubyについては詳しくは『dRubyによる分散オブジェクトプログラミング』(関将俊著、アスキー、ISBN4-7561-3961-2)を参照してください。

+
+
+

AMarshal

+
+

Marshalは変換結果はバイナリ列でひと目では内容がわかりません。効率は少々低くてももっと見やすい形で出力したいというニーズもあるかもしれません。

+

そのようなニーズに応えるのが田中哲(akr)さん作のAMarshalです。AMarshalは標準添付ではないので、まずインストールする必要があります。URLは、

+
    +
  • http://cvs.m17n.org/~akr/amarshal/

  • +
+

です。ここからAMarshalのtarballを取ってきます。原稿執筆時点ではamarshal-0.4.tar.gzでした。これをどこかで展開します。いろいろファイルが入っていますが、必要なのはamarshal.rbだけです。これを適当なRubyのライブラリディレクトリにコピーします。たとえば、私は、

+
    +
  • /usr/local/lib/site_ruby/

  • +
+

にコピーしました。AMarshalの機能はMarshalとほぼ同じです。ただし、limitの指定はありません。

+
+
obj = [1, "2", 3.0]
+dump = AMarshal.dump(obj)
+AMarshal.load(dump)   # => [1, "2", 3.0]
+
+

AMarshalの最大の特徴はその変換出力です。上記の例題でのdumpの結果は以下のようになります。

+
+
v = []
+v[0] = Array.allocate()
+v[0] << 1
+v[1] = "2"
+v[0] << v[1]
+v[2] = 3.0
+v[0] << v[2]
+v[0]
+
+
+

見ればわかるように、これはRubyプログラムです。つまりAMarshalは実行するとdumpに指定したのと同じ構造のオブジェクト(群)を生成するようなプログラムを出力するのです。機械生成なのでやや非人間的ですが、Marshalのバイナリ出力を解析することを思えば、それよりはずっとわかりやすいでしょう。AMarshalの目的は「わかりやすさ」なので効率に関しては目をつむりましょう。データファイルサイズでも読み込み効率でもMarshalには勝てません。

+

あ、AMarshalには1つ重要な制限があります。AMarshalの実行にはRuby 1.7で導入されたClass#allocateメソッドが必要です。というか、AMarshalのためにallocateメソッドが追加されたようなものなんですが。ですから、まだ1.6系をお使いの方は、次の安定板である1.8が登場するまでしばらくはおあずけですね。すでに昨年のクリスマスには1.8のpreviewが出ていますから、1.8の正式リリースは遠くないと思います。

+

オブジェクトのマーシャルを行うライブラリにはAMarshalの他にも、XMLを使うclxmlserialTMarshal, XMarshalなどなどがあります。みなRAAに登録されているので、興味のある人は調べてみてください。

+
+
+

PStore

+
+

PStore(persistent store,(オブジェクトの)永続的保存)はMarshalの応用例の1つです。Marshalは指定したデータをバイナリ列に変換するだけでしたが、PStoreはそれを利用して簡単なオブジェクト指向データベースを実現しています。オブジェクトのマーシャリングという一番面倒な部分をMarshalモジュールが実現してくれるので、PStoreはコメントや空行を含めてもわずか160行程度のプログラムです。

+

PStoreの特徴は以下のとおりです。

+
    +
  • Marshalを使って任意のRubyオブジェクトをそのまま格納できる

  • +
  • 使いやすいインターフェイス

  • +
  • トランザクションがある

  • +
+

トランザクションとはデータベースの処理の単位です。トランザクション中の処理が問題なく終了すればその結果はデータベースに正式に書き込まれますが、もし途中で何らかの原因(例外など)で失敗すれば、データベースの状態はトランザクション開始前に戻ります。これにより、トランザクションによってデータベースが不整合な状態になることを防ぐことができます。

+

PStoreにはデータを一度全部メモリに読み込むので大規模データベースには向かないという欠点もあります。しかし、数百Kバイト程度の小規模のデータベースならあまり問題にはなることはないでしょう。RubyのアプリケーションインデックスであるRAA(http://raa.ruby-lang.org/)も長らくPStoreを使っていました。

+

PStoreオブジェクトの使い方は次のとおりです。

+
    +
  1. オープンする
    +まずはデータベースをオープンして、PStoreデータベースオブジェクトを得ます。 +
    +
    db = PStore.new(path)
    +
    +pathにはデータベースファイルのパスを指定します。
  2. +
  3. トランザクションを開始する
    +先ほど説明したとおり、トランザクションはデータベース処理の単位です。データベースに対する処理はトランザクション単位で書き込まれますので、処理途中で例外が発生したときなどにはデータベースの状態はトランザクション開始前の状態にとどまります。トランザクションはtransactionメソッドにブロックを指定することで開始します。 +
    +
    db.transaction {
    +  ...
    +}
    +
    +ブロックを抜けるとトランザクションが終了します。
  4. +
  5. オブジェクトの登録、取得
    +PStoreオブジェクトは一種のハッシュとして扱えます。オブジェクト登録・取得は[]メソッドを使います。 +
    +
    db["foo"] = object  # オブジェクトの設定
    +db["bar"]           # オブジェクトの取得
    +
    +ただ、ハッシュと違って登録されていない名前に対するオブジェクトの取得はエラーになりますから、データベースに名前が登録されているかどうか定かでない場合には、あらかじめ確認する必要があります。データベースに登録されているすべてのオブジェクト名の一覧を得るのはrootsメソッド、ある名前のrootが登録されているかどうかをチェックするのはroot?メソッドです。 +
    +
    db.roots          # root名一覧
    +db.root?("foo")   # root名fooがあれば真
    +
    +rootという名前はトランザクションが成功したときのオブジェクトの書き出しが、名前を付けられたオブジェクトを根(root)にして行われることからきています。
  6. +
  7. トランザクションの終了
    +transactionメソッドに指定したブロックの実行が終わればトランザクションは自動的に終了し、データベースから参照されている全オブジェクトの現在の状態がファイルに書き込まれます。
    + トランザクションの途中で強制的にトランザクションを終了させるメソッドも用意されています。commitメソッドはトランザクションを正常終了させます。transactionメソッドの実行も終了します。abortメソッドはトランザクションを強制的に中断します。データベースへの変更はファイルに書き込まれず、トランザクション開始前の状態のまま残されます。abortメソッドもtransactionメソッドの実行を中断します。
  8. +
+

CGIでは複数同時に起動された場合に対応するためデータファイルへのロックが必要になるのですが、PStoreは内部で自動的にflockを使って排他処理してくれますので、その点でも心配せずに使えます。ただし、PStoreは同じプロセスの中での複数のスレッドからの同時アクセスには対応していません。先月学んだような方法で排他制御する必要があります。

+

データベース内のオブジェクトへの参照と更新は必ずトランザクション内で行ってください。途中のオブジェクトを取り出してトランザクション外で状態を変更してもエラーにはなりませんが、その変更はデータベースには反映されません。

+
+
+
db_data = nil
+db.transaction {
+   db["foo"] = [1,2,3]
+   db_data = db["foo"]  # 持ち出し
+}
+# トランザクション終了
+
+# データへのアクセスは可能
+p db_data[0]            # => 1
+
+# データの更新も可能だがDBには反映されず
+p db_data[0] = 42
+
+

PStoreのトランザクション処理手順を簡単に説明しておきます。

+
    +
  1. データファイルをflockでロック
  2. +
  3. データファイルからMarshalを使ってデータを読み込む
  4. +
  5. ブロックを実行
  6. +
  7. ブロック実行が成功ならMarshalでデータを書き込む
  8. +
  9. 失敗していたら何もしない
  10. +
+

実際にはここにバックアップファイルの作成やエラー処理を含むのでもうちょっとややこしいですが、基本的にはこんなものです。

+

手前みそながら、PStoreはCGIに限らず非常に使い勝手がよいので、小規模なデータベースでしたら、ぜひ活用してください。

+
+
+

まとめと次回予告

+
+

複雑なデータをファイルに保存したり、再読み込みしたりする処理は面倒なものですが、MarshalAMarshalを使えば、簡単に実現できます。また、これらの機能を使えばPStoreのようなオブジェクト指向データベースも数百行で簡単に実現できます。

+

とはいえ、たとえば設定ファイルや簡単なデータの保存にはマーシャリングはオーバースペックでしょう。来月はそんなデータを簡単に扱えるファイルフォーマットであるXMLやより新しいフォーマットであるYAMLなどについて紹介します。「RubyでXML」についてはすでにそれをテーマにした書籍も出ていますから、新顔のYAMLを中心に解説しようと思います。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-054.xhtml b/docs/vol1/xhtml/p-054.xhtml new file mode 100644 index 0000000..a04a6f5 --- /dev/null +++ b/docs/vol1/xhtml/p-054.xhtml @@ -0,0 +1,50 @@ + + + + + +第24章 データの保存 + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ プログラマーと数学

+
+

プログラミング好きと数学好きの間には何らかの関連があると思われます。プログラミングの基礎を作った人々は、古くはバベッジやブルバキ、やや新しいとチューリングやフォン・ノイマンなど多くは数学者です。数学者の名前を取ったプログラミング言語もたくさんあります(HaskellやTuringなど。PascalやOccamは数学者ではないのかな)。

+

また、私の周りを見渡しても、私の最初の本『オブジェクト指向スクリプト言語Ruby』の共著者である石塚圭樹さんは数学科の出身ですし、『Rubyプログラミング入門』を書いた原信一郎さんは長岡科学技術大学の数学の先生です。

+

また、私の同僚でmod_rubyerubyの開発者、前田修吾さんは数学科から哲学科に転学したというちょっと変わった経歴を持っています。

+

Ruby関連でも数学好きは活躍していまして、数学の話題を主に取り扱う(というか、一般の話題から分離する)ためにruby-mathという専用のメーリングリストが用意されています。

+

これはいったいなぜなんでしょう?

+

プログラミングは論理をベースにしていて、論理学は数学の一部であること、また数値を扱うプログラムが多いこと、また数学者がプログラミングの歴史で果たしてきた役割が大きいことなども考えられますが、数学のきっちり答えが出る厳密さと、あいまい性を排除する(したがる)プログラミングに共通点があるからかもしれません。

+

とはいえ、プログラミングに数学が必須かというとそういうわけでもありません。大学でも機械工学、電子工学などでは研究においても数学は必須ですが、情報工学では(もちろん数学は必須科目ですが)、数学を使わなくてもできる研究はいくらでもあります。

+

私自身も数学は大変苦手です。小学生の頃は算数が苦手という意識はなかったのですが、中学校に入り、数学という科目名になったあたりからもう駄目でした。この数学苦手の傾向は年を経るに従ってだんだん強まって、高校ではすべての教科の中で数学の成績が飛び抜けて悪いという悲惨な状況でした。苦手というよりも、脳の中で数学を扱う部分が壊れているんじゃないだろうかと、自分でも疑うほどでした。

+

大学入試のときも当時の共通一次テストで、他のすべての科目の失点の合計よりも数学の失点のほうが多いという大失態を演じましたし、二次試験でも苦戦して数学のおかげであやうく大学生になれないところでした。大学に入ってからも必須の数学(線形代数とか)が理解できず、留年ぎりぎりで教授に泣きついたことを覚えています。

+

どうして自分はこんなに数学が駄目なんだろうと考えたことがあります。どうやら、私が数学が駄目な理由にはいくつかあるようで、まず第一に、私は数学の試験にはつきものの「一定の時間内に正しい計算結果を出す」ことが大変苦手のようです。学生時代、数学の教科書を読むのはそんなに嫌いじゃありませんでしたし、理解できる部分もありました。しかし、試験のときにはいつもいろいろ試行錯誤して時間切れというのが落ちでした。素早くルールを適用して正しく計算というのがどうも駄目みたいです。定理の詳細が思い出せず、試験中に図を書いて定理を「再発見」しようとするようでは時間がいくらあっても足りません。実際、今でもプログラミングに当たって動かしてみて確かめて初めて理解するというケースが多いように思います。

+
+

2番目に、数学(が提供する考え方、モデル)が何の役に立つのかわからず、興味が持てなかったということがあります。理科とかは単なる知識(光の速度は秒速30万キロで、月まで1秒以下で届くんだよ、とか)としても興味が持てたのですが、算数で習った以上のレベル、微分とか積分とか行列式とかが実際の生活の何の役に立つというのでしょう。いや、実際にはあとになって役立つ分野があることを知るのですが、そのときにはもう手遅れで数学の苦手意識が邪魔をしてそういう分野にもまた興味が持てませんでした。

+

最後に、数学というものは興味がない状態で扱うには複雑すぎるのでしょう。学生時代には古文の授業も役に立ちそうもないという理由で同様に興味は持てませんでしたが、それでも成績は数学よりはずいぶんマシだったように思います。

+

「そんなに数学苦手でプログラマーとしてやっていけるの?」とよく聞かれます。でも、大丈夫です。ご心配なく。

+

もちろんプログラミングの中にも数学がどうしても必要な分野はあります。そういう分野で私がちゃんとやっていけるとは思えません。しかし、幸いなことにコンピュータ業界でそういう分野はそれほど多くありません。それよりもプログラミングではむしろ人間に関わることが多いのです。

+

コンピュータはよく人間的でないと思われています。確かにコンピュータに人間性は期待できないのですが、プログラミングというものは、人間の意図をくんでコンピュータにわかる形で表現するという非常に人間的な活動です。必要なスキルは、開発しているこのプログラマーはいったい何を達成したいと思っているのかというような意図を読み取る能力とか、このプログラムの挙動をあいまいさなく表現するといった、学校の科目でいえば国語が相当するようなものが主流です。

+

プログラミングを学ぶ一番の方法は他の人が書いたプログラムを読むことですが、それには数学の能力というよりは、やはり読解力などの国語的能力が活躍するような気がします。

+

私の専門であるプログラミング言語の設計については、特にそのことがいえます。この機能を使ってプログラムを書くとプログラマーはどう感じるだろうかという想像力や、人間の脳が扱いやすい複雑さとそうでない複雑さを判別するなど、プログラミング言語の設計というものは、基本的にコンピュータよりも人間のほうを向いた活動なのです。

+

ですから、世の中の数学苦手の諸君、プログラミングをあきらめることはありません。あなたたちには明るい未来が待っています。

+

でもね、数学の能力はいらなくても国語力やその基礎になる考える能力はやっぱり必要なんですよね。肉体労働の対極にあるプログラミングという活動では、数学の能力ではないにしても、脳を活動させる能力はいずれにしても要求されそうです。

+

もっとも、プログラミング自身が脳を鍛える体操といえないことはないですね。これからの高齢化社会にはボケ防止にデバッグとかが流行るかもしれません。

+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-055.xhtml b/docs/vol1/xhtml/p-055.xhtml new file mode 100644 index 0000000..dad91f0 --- /dev/null +++ b/docs/vol1/xhtml/p-055.xhtml @@ -0,0 +1,340 @@ + + + + + +第25章 XMLとYAML + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay25 +
+

+初等Ruby講座
+XMLとYAML +

+
+

[Linux magazine, 2003年6月号]

+
+

S式とXMLとYAMLについて解説しています。時代背景を説明すると、この頃はJavaの普及に伴ってXML全盛期で、データ表現形式や設定ファイルなどの目的に(必要以上に)XMLが多用されていました。一方、データ表現に特化したYAMLは登場したばかりで、日本ではまだほとんど誰も知らない状況です。現在では、データ表現形式としては、JSONが一般的になり、XMLもYAMLも「負の遺産」と見なされることが増えたのが時代を感じさせます。特にあの読みにくく解析しにくいXMLに悩まされることがほとんどなくなったのはありがたいですね。

+

「Ruby開発日記」は、スパムフィルターについてです。現代ではほとんどの人はGMailなどのWebメールサービスに移行していて、自前でスパムフィルターを設定する人などほとんどいないのではないでしょうか。私はといえば、今でもBogofilterを愛用しているのです。

+
+
+

先月に引き続きRubyによるデータ保存について、特にデータ保存のフォーマットについて学びます。

+
+
+

データの表現

+
+

データをファイルに格納する場合、それが単純なテキストデータ(文字列)であればそのまま記録するだけで十分です。しかし、保存したいデータがそのようなテキストデータだけで済むわけではありません。実際には保存したいデータは何らかの「構造」を持っています。

+

構造とは、単なる1つの値ではなく、複数の値の集まりやそれらの間の関連を意味します。配列やハッシュは構造です。構造のあるデータをファイルに格納する方法としては先月説明したMarshalがありますが、それにはちょっとだけ問題があります。

+
+
    +
  • フォーマットがバイナリで人間には読めない
    +いや、無理すれば読めますが、楽ではないですよね。

  • +
  • フォーマットバージョンの変化に弱い
    +Marshalのフォーマットは上位互換ですが、双方向に交換するとき(dRubyとか)には双方のバージョンを完全にそろえる必要があります。

  • +
+

そこで、もうちょっと人間に読みやすいデータフォーマットがほしいと思うときがあります。たとえば、設定ファイルやちょっとしたデータファイルにはMarshalよりも人間が読んで意味がわかり、編集もできるようなフォーマットのほうがよいでしょう。

+

これも先月紹介したAMarshalは「構造のあるデータをそれを生成するRubyのプログラムで表現する」というユニークなアプローチでした。これなら確かに人間にも読めますし、編集もできますが、AMarshalの出力したRubyプログラムは正直言ってあまり読みやすくはありませんし、また入力はデータをそのまま実行してしまうので、悪意のある書き換えが行われたらかなり危険です。AMarshalで読み込むファイルに、

+
+
system "rm -rf ."
+
+

なんて行が含まれていたらかなり悲惨でしょう。

+

そこで、構造が表現できて、人間にも簡単に読み書きできるような安全なフォーマットに対するニーズが生まれるわけですが、そのようなフォーマットをプログラムごとに毎回設計するのも大変です。

+

そこで登場する考えがメタ・データフォーマットです。「メタ」というのは「一段高い」というような意味を表す接頭語で、たとえば「メタクラス」は「クラスのクラス」を意味します。ですから、メタ・データフォーマットとは「データフォーマットのフォーマット」のことです。要するに「データフォーマットの枠組み」、あるいは「データフォーマットの文法」というようなもののことです。ここで「メタ」と「データ」の間に「・」を入れているのは「データのデータ」である「メタデータ」という言葉もあるので、「メタデータのフォーマット」と解釈されないためです。あくまでも「メタなデータフォーマット」なのですから。余談ですが、「メタ」という単語を多用する人物には注意したほうが賢明です。いつも、話を抽象化して一段高いレベルから話したがるからです。抽象度が高くなると、とたんに話が難しそうに聞こえるからです。そういえば、私もそういうタイプですねえ。

+

話をメタ・データフォーマットに戻しましょう。設定ファイルに必要な項目はプログラムごとに違うでしょうが、その「フォーマットのフォーマット」つまり、設定ファイルの文法は共通にすることができます。そうすれば、共通のAPIでファイルを読み書きすることもできます。

+

メタ・データフォーマットには以下のような性質が求められます。

+
    +
  1. 構造を表現することができる

  2. +
  3. 人間にも読み書きできる

  4. +
  5. 統一的なAPIで読み書きできる

  6. +
+

このようなメタ・データフォーマットの例としては、LispのS式、XML、そして今月新たに紹介しようとしているYAMLがあります。

+
+
+ +

S式

+
+

S式(S-Expression)というのは、私の好きなプログラミング言語であるLispで使われるメタ・データフォーマットです。LispではあらゆるデータをS式で表現します。Lispではプログラムもデータですから当然のようにS式で記述されます。Lispを少しでもご存じの方は、あのかっこだらけの表記を思い出されることでしょう。あれがS式です。

+
+
(define (fact (n)) (* n (fact (- n 1))))
+
+

上記のS式は階乗を求めるプログラムですが、同時に、階乗の計算法というデータをS式というメタ・データフォーマットで記述したものでもあります。S式では当然普通のデータも表現できます。

+
+
(address-entry
+  (name "まつもとゆきひろ")
+  (addr "島根県八束郡"))
+
+

S式のSはSymbolのSです。つまり、数値ではなく記号で表現できるという意味のようです。LispはFORTRAN, COBOLと並んで最古のプログラミング言語の1つですから、S式は最も古いメタ・データフォーマットです。

+

S式はLispとあまりにも強く結び付いているので、Rubyではあまり用いられません。S式を解釈する独立したライブラリはないのではないかと思います。ただ、Rubyで書いたLisp処理系はrougeとかruchemeとかいくつかありますので、それらにはS式を解釈するルーチンが当然含まれていると思います。また、RAAを探るとnpreader.rbというS式に似た形式を読み出すライブラリがあるようです。S式のかっこに抵抗がなければ、いろいろ面白い応用が考えられると思います。

+
+
+

XML

+
+

XMLはもう流行語ですから聞いたことがない人はいないかもしれません。XMLはeXtensible Markup Languageの略で、SGMLの流れをくんだマークアップ言語です。XMLのすごいところは単なるマークアップ(テキストに対する意味付け)を超えて、メタ・データフォーマットとしての機能を確立した点です。XMLを使えば、構造を持つデータを、人間が読める形式で表現できます。しかも、1つのフォーマットでいろいろな種類データを表現できます。

+

もっとも、これは何十年も前からLispとS式が実現してきたことなのですが、XMLによって大衆もその便利さを再発見したということなのでしょう。Javaによって大衆がガベージコレクションや例外処理のありがたさに初めて気が付いたのと似ているといえるかもしれません。

+

紹介された経緯からか、XMLとJavaがひとまとめに語られることが多いのですが、実はXMLはスクリプト言語に向いています。XMLの処理は本質的にはテキスト処理ですし、XMLの属性や中身(CDATA)には型の情報がありませんから、型を厳密に考えなくてもよい動的言語のほうが都合がよい場合が多いのです。

+

-30zw RubyでXML処理を行おうという人も多く、RAAをXMLというキーワードで検索するとなんと55個のプロジェクトが見つかります(2003年4月5日現在)。そのうち、XMLを読み書きするものに限定すると表25.1のようになります。

+ +
+

表25.1●Ruby用XMLパーサー

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
XMLパーサー説明
libxmllibxmlの拡張ライブラリ
nqxmlpure Ruby XMLパーサー
rexml定番XMLライブラリ(1.8で標準添付予定)
xml-simplePerlのXML::SimpleのRuby版
xmlparserexpatの拡張ライブラリ
xmlscan高速pure Ruby XMLパーサー
libgdome-rubyDOMレベル2 Core API(拡張ライブラリ)
mwdomDOMレベル2パーサー + ライタ
xampl-ppXML pullパーサー
+
+

なんでこんなにあるのって感じですが、これはおそらくは発展の過程の1つで、次第に収束していくのではないかと思います。現在はREXMLがかなり広く使われているようです。また、REXMLをベースにしてより使いやすいAPIを提供するライブラリ(xml-simplexml-configfile)も登場してきています。

+

RubyとXMLについては『Ruby de XML』(オーム社、ISBN4-274-06500-6)というローマ字読みするとそのまんまなタイトル(フランス語だとdeは英語のofですから「XMLのRuby」って意味でちょっとおかしいですけど)の書籍が出ていますので、そちらも参照してみてください。

+
+
+

YAML

+
+

XMLの次はYAMLです。YAMLについて最初に聞いたのがいつだったのかはっきり覚えていませんが、たぶん去年の夏頃だったのではないかと思います。そのとき感じたのは「ああ、またXMLの亜流か」という正直あまり期待しない気持ちでした。

+

XMLというのはSGML流の(HTML流と言ったほうが通じるかも)、

+
+
<TAG attr="attribute">.....</TAG>
+
+

という形式で、とにかく冗長で見にくいので、それを改善するXMLの別記法がたくさん考え出されていたからです。そのような記法には行ベースのPYXやS式ベースのSXMLがあります。ですから、YAMLもそのような別記法だと早とちりしてしまったわけです。

+

というわけで、最初はあまり興味を持っていなかったYAMLだったのですが、去年のRubyConf(2002)でYAMLの話を聞いてそれがまるっきりの誤解だったことが明らかになりました。

+

YAMLはYAML Ain’t Markup Language(YAMLはマークアップ言語じゃない)の略です。ありがちな「Yet Another」ではないんですね。発音は「やむる」、アメリカ人っぽく発音すると「やむぅ」でしょうか。名前が示すようにYAMLはマークアップ言語ではありません。もともと、PerlのInline.pmを開発していたBrian Ingarsonがデータのシリアライズに使えるフォーマットとして考えついたものなのだそうです。YAMLはプラットフォーム非依存のデータシリアライズフォーマットで、表現はテキスト形式を使っているのでプラットフォームのアーキテクチャに依存せず、人間にも読みやすく、編集しやすくなっていますし、また、Perl, Ruby, PythonでAPIが提供されていますから、プログラミング言語を超えてデータの受け渡しが可能です。

+
+

リスト25.1はYAMLのサンプルです。YAMLの文法はあとで紹介しますので、ここでは雰囲気を感じてください。

+
+

リスト25.1●YAMLサンプル

+
title: Escape of the Unicorn
+animations:
+  - title: background sky
+    author: Justyna
+    frames:
+      - file: bg_sky_1.png
+        ms:   500
+      - file: bg_sky_2.png
+        ms:   500
+  - title: background water
+    author: Jacek
+    frames:
+      - file: bg_water.png
+        ms:   300
+      - file: bg_water1.png
+        ms:   200
+      - file: bg_water2.png
+        ms:   200
+      - file: bg_water3.png
+        ms:   300
+      - file: bg_water2.png
+        ms:   200
+      - file: bg_water1.png
+        ms:   200
+
+
+

文法が説明されなくても、データとしてすんなり読めますよね。比較のため、これをXMLで表現するとリスト25.2のようになります。

+
+

リスト25.2●YAMLサンプルと等価なXML

+
<title>Escape of the Unicorn</title>
+<animations>
+  <animation>
+    <title>0 background sky</title>
+    <author>Justyna</author>
+    <frames>
+      <frame><file>bg_sky_1.png</file><ms>500</ms></frame>
+      <frame><file>bg_sky_2.png</file><ms>500</ms></frame>
+    </frames>
+  </animation>
+  <animation>
+    <title>1 background water</title>
+    <author>Jacek</author>
+    <frames>
+      <frame><file>bg_water.png</file><ms>300</ms></frame>
+      <frame><file>bg_water1.png</file><ms>200</ms></frame>
+      <frame><file>bg_water2.png</file><ms>200</ms></frame>
+      <frame><file>bg_water3.png</file><ms>300</ms></frame>
+      <frame><file>bg_water2.png</file><ms>200</ms></frame>
+      <frame><file>bg_water1.png</file><ms>200</ms></frame>
+    </frames>
+  </animation>
+</animations>
+
+
+ +

わからないことはないけど(Marshalの生バイナリデータに比べれば)、ごちゃごちゃしてますよね。YAMLのほうがずっといい。

+

YAMLの利点はすっきりしているだけではありません。YAMLはXMLをメタ・データフォーマットとして使うときの欠点の多くを克服しているのです。

+

すでに述べたようにもともとXMLはテキストのマークアップ言語であるSGMLから派生しているので、データ表現として考えるといくつも欠点があります。

+
+

冗長

+

XMLは開始タグと終了タグの両方を持ち、表現がかなり冗長です。個人的には人間にもあまり読みやすくないと思うのですが、それはともかく、XMLをデータフォーマットに使ってデータ表現の分量が増えるっていうのは、通信量や記憶容量の増加につながるうれしくない性質です。

+

一方、YAMLは「-」「:」「|」などの記号とインデントを使って、すっきり見やすいだけでなく、データ量の削減も実現しています。

+

解析が難しい

+

行単位で解析できない上、任意にネストするXMLはプログラム的にかなり処理しにくいフォーマットです。また、マークアップという性質が残っているので、データ表現として考えるとやや不自然なところがあります。

+

一方、YAMLは基本は行単位ですし、もともとデータ表現に特化していますから、APIも自然です。

+

型情報がない

+

XMLの起源はテキストマークアップですから、データ型の情報は外部から与えてやる必要があります。たとえばある属性の値は整数であるとかは、プログラムのほうで解釈してやる必要があります。

+
+

一方、YAMLでは型情報はデータフォーマットの一部として最初から含まれていますから、外部から型を教えてやったり、明示的に変換したりする必要はありません。

+
+

データ表現にも使えるマークアップ言語のXMLとデータ表現専用のYAMLとを同列に比較してはいけない部分はあるのですが、データ表現という用途に限定した場合にはYAMLの圧勝といえるでしょう。忘れてはいけないのはXMLの現在の用途の多くがYAMLでカバーできるデータ表現だということです。

+
+
+

YAMLの文法

+
+

YAMLはデータ表現のための比較的シンプルな文法のデータフォーマットですが、それでも全部解説すると限られた誌面には収まりません。ここではYAMLの文法の概要を簡単に紹介しておきましょう。

+

まず、データの繰り返し(配列)は先頭に「-」を置いたものの並びです。ですから、

+
+
- foo
+- bar
+- baz
+
+

は配列["foo", "bar", "baz"]になります。ネストはインデントで表現します。

+
+
- foo
+-
+  - bar
+  - baz
+
+

["foo", ["bar", "baz"]]になります。それからマッピング(ハッシュ)は「:」で表現します。

+
+
foo: 1
+bar: 2
+baz: 3
+
+

{"foo"=>1, "bar"=>2, "baz"=>3}というハッシュを表現します。複数行に渡るテキストは「|」か「>」を使います。

+
+
- |
+  これは複数行テキストです。
+  改行もそのままです。
+- >
+  これも複数行テキストです。
+  こちらは改行が空白に置き換わります。
+
+

そうそう、YAMLデータはUnicode(UTF-8)で記述することになっています。日本語を含むテキストを用意するときは注意してください。

+

あと、いくつかの基本的データ型があります。

+
+
    +
  • nil     ~, nil
    +
  • +
  • boolean true, false, +, -
  • +
  • integer 0, 1, 2, 100,000(コンマは整数の一部)
  • +
  • float   1.0, 1.23e23
  • +
  • time    2003-05-08 00:49:38.634752 +09:00
  • +
  • date    1965-04-14
  • +
+

これらのパターンにマッチしないデータは文字列として扱われます。逆にこれらのパターンにマッチするデータを文字列として扱うためにはデータの前に“!str”と書きます。つまり、

+
+
- 2003-04-14
+
+

Dateオブジェクトを1つ含む配列と解釈されますが、

+
+
- !str 2003-04-14
+
+

"2003-04-14" という文字列を要素とする配列になります。

+
+
+

yaml.rb

+
+

このYAMLフォーマットを読み書きするのがyaml.rbです。Debianにはyaml.rbのパッケージ(libyaml-ruby)があるのでそれをインストールするだけです。それ以外では、

+
    +
  • http://yaml4r.sourceforge.net/

  • +
+

からリンクをたどってtar.gzファイルをダウンロードして、適当なディレクトリで展開した後、root権限で、

+
+
# ruby install.rb
+
+

を実行するとインストールできます。

+

yaml.rbにはたくさんの機能があるのですが、基本的なものは以下の2つです。

+
+

Kernel#to_yaml

+

すべてのオブジェクトに追加されるメソッドで、そのオブジェクト(と、そこから参照されるオブジェクト)をYAMLで表現した文字列を返します。

+

YAML::load(yaml)

+

YAML入力(文字列またはIO)をオブジェクトに戻します。

+
+

たとえば、リスト25.1のYAMLデータをオブジェクトに変換するには以下のようにします。

+
+
require "yaml"
+data = YAML::load(open("unicorn.yaml"))
+
+

なんと簡単な。これで取り出せるデータはリスト25.3のようになります。

+ +
+

リスト25.3●yaml.rbで読み込んだデータ

+
{"title"=>"Escape of the Unicorn",
+"animations"=>
+  [{"title"=>"background sky",
+    "author"=>"Justyna",
+    "frames"=>
+     [{"ms"=>500, "file"=>"bg_sky_1.png"},
+      {"ms"=>500, "file"=>"bg_sky_2.png"}]},
+   {"title"=>"background water",
+    "author"=>"Jacek",
+    "frames"=>
+     [{"ms"=>300, "file"=>"bg_water.png"},
+      {"ms"=>200, "file"=>"bg_water1.png"},
+      {"ms"=>200, "file"=>"bg_water2.png"},
+      {"ms"=>300, "file"=>"bg_water3.png"},
+      {"ms"=>200, "file"=>"bg_water2.png"},
+      {"ms"=>200, "file"=>"bg_water1.png"}]}]}
+
+
+

YAMLデータとロードされたオブジェクトを見比べると、配列、ハッシュ、文字列、数値などが「見かけどおり」にロードされているのがわかるでしょう。こうやって配列やハッシュの形で取り込んでしまえば、あとは自由に操作・加工できますよね。

+

また、YAMLはプラットフォーム非依存ですから、YAMLデータをRubyからPerlへ、PerlからPythonへ、というような移動も自由自在です。

+
+
+

まとめ

+
+

ここまでで誌面が尽きてしまいました。Marshalの代わりにYAMLを使ったPStore互換のYAML::Storeとか、yaml.rbより100倍高速とうわさされる新ライブラリSyckとかYAML周りにはまだまだホットな話題がたくさんです。

+

XMLとYAMLについては実際の応用についても触れたいので、できれば来月も続けて解説しようと思います。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-056.xhtml b/docs/vol1/xhtml/p-056.xhtml new file mode 100644 index 0000000..4358d0b --- /dev/null +++ b/docs/vol1/xhtml/p-056.xhtml @@ -0,0 +1,79 @@ + + + + + +第25章 XMLとYAML + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ 迷惑メールをやっつけろ

+
+

私は毎日数百通のメールを受け取ります。そのうちの大部分はRuby関連を始めとするメーリングリストからのものです。要するに一日のかなりの割合をメールの読み書きに費やしているわけです。おそらくはプログラミングしている時間よりも長いでしょう。しかし、ここのところspamとか迷惑メールと呼ばれる不要なメールの割合が次第に増えてきました。

+
    +
  • 出会い系サイトの紹介

  • +
  • アダルトグッズの販売

  • +
  • spam用アドレスの販売

  • +
  • 意図不明でサイトに誘導するもの

  • +
  • ウィルス、ワーム

  • +
  • 「量子論は間違っている」

  • +
  • 詐欺(私はコンゴの富豪の未亡人で……)

  • +
+

きりがありません。こんなもので商売になると思う人がいるのが信じられませんが、いずれにせよこんなものを読まされて人生の貴重な時間を浪費するのは耐えられません。そこで少々対策を施すことにしました。

+

私は会社においてあるサーバーにすべてのメールを集めて、そこからPOPで自分のノートパソコンにメールを取り込んでいます。POPで取り込む際にfetchmailとprocmailを使ってMaildirフォルダに分類し、それをローカルホストのIMAPサーバーを経由してcmailで読んでいます(図25.1)。

+
+ +
+ fig2501 +
+

図25.1●迷惑メール対策

+
+

こんなことをしているのは、

+
    +
  • メールの取得は安定的に(会社のサーバー)

  • +
  • 1メール1ファイルを実現したい(Maildir)

  • +
  • cmailを使いたい

  • +
+

というニーズを実現するためです。

+ +

迷惑メールを撃退するために、まず私が始めたことは、サーバーにspamassassinというソフトを導入したことです。これは迷惑メールの持つ特徴をルールによって検出し「spamらしさ」を判定したうえで、ある一定以上のspamスコアを持つメールを排除するというものです(実際の排除はprocmailが行います)。このソフトの効力は絶大で、しばらくは私に届く迷惑メールが激減しました。特に英語のspamを見ることはなくなりました。

+

ところがspamassassinも万能ではありません。まず、第1にspamassassinは英語圏で開発されたもので、ルールも英語の迷惑ルール対象に用意されていますから、日本語の迷惑メールをほとんど検出できないことです。第2にはspam業者も学習してきて単純なルールはすり抜けるようなメールを用意するようになってきたのでした。

+

そこに登場したのが「人気の言語を作るには」という論文で知られるPaul Grahamの「a Plan for spam」という論文です。これは要するにメールをBayesianフィルタで統計的に処理することで迷惑メールかどうか判定しよう、というものです。自分で作ってもよかったのですが、Bayesian処理の数学的側面に負けてしまったので(笑)、既存のものを使うことにしました。私が選んだのはspamprobeというソフトです。日本語には対応していないようだったので少々不安だったのですが、試しにしばらく運用したところ問題なく日本語の迷惑メールも検出できるようです。

+

Bayesianフィルタは学習によって成長します。単語の間の関連や頻度を学習結果と比較することによってメール全体のスコアを決定するという仕組みです。そこで今までに蓄えた「正しいメール」と「迷惑メール」をspamprobeに食わせてやるとデータベースを作ってくれます。

+

spamprobeはノートパソコン側に設定することにしました。spamassassinをすり抜けてくるメールは微妙なので、誤判定が起きては嫌だったのと、サンプルを学習させるのに手元のほうが便利だったからです。spamprobeが計算したスコアを使ってノートパソコン側のprocmailで迷惑メールを「ゴミ箱フォルダ」にまとめることにしました。

+

さて、これでほとんどの迷惑メールが退治できるようになりました。通販も、詐欺も、ウィルスもきちんと排除します。これで一件落着と思ったのですが、ただ1つだけ問題が残っていました。実は私のところにはアンケートサイトからのメールや、通販サイトからのメールも来るのですが、これらは迷惑メールと非常に似た構造を持っています。これらを迷惑メールと区別しなければなりません。メール分類ソフトにはよく「ホワイトリスト」という「このアドレスは大丈夫だよ」というような機能が付いているのですが、spamprobeにはそのような機能はないようです。

+

そこでどうしたかというと、bogofilterという別のBayesianフィルタソフトを使って自分にとって有用なメールをspamprobeに渡す前に分別することにしました。これで完璧です。

+

しかし、構築したときは段階的に行ったので気にもしませんでしたが、サーバー側のspamassassinと合わせて全部で3段にもフィルタをかけるのは少々異常かもしれません。

+

ところで、Bayesianフィルタはサンプルをたくさん与えて学習させるほど賢くなります。ソフトウェアがだんだん頭が良くなっていく様子を見るとなんだかかわいく感じてしまいます。昔の「たまごっち」のようなイメージなんですが、感情移入しすぎでしょうか。

+

私のフィルタたちは今日もメールを餌にして成長しています。

+
+ +
参考文献
+

smapassassin
+http://www.spamassassin.org/

+

「a Plan for spam」
+http://www.paulgraham.com/spam.html
+http://www.shiro.dreamhost.com/scheme/trans/spam-j.html(和訳)

+

spamprobe
+http://sourceforge.net/projects/spamprobe/

+

bogofilter
+http://sourceforge.net/projects/bogofilter/

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-057.xhtml b/docs/vol1/xhtml/p-057.xhtml new file mode 100644 index 0000000..1ee4885 --- /dev/null +++ b/docs/vol1/xhtml/p-057.xhtml @@ -0,0 +1,318 @@ + + + + + +第26章 XMLとYAML(その2) + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay26 +
+

+初等Ruby講座
+XMLとYAML(その2) +

+
+

[Linux magazine, 2003年7月号]

+
+

先月に引き続き「XMLとYAML」です。というか、YAMLの宣伝ですね、これは。当時はYAMLの知名度が圧倒的に低かったので、このように宣伝する必要があったわけですね。その甲斐あったか、YAMLは各方面に使われるようになりましたが、柔軟性が高すぎたのか、結局はJSONのほうがよく使われるようになりましたね。やはり、インデントに意味をもたせる文法が良くなかったか。

+

「Ruby開発日記」は、「Matzにっき」についてです。2003年5月から書いていたんですねえ。だいぶ長いこと更新してきたのですが、X(旧Twitter)などのより簡単な情報発信に流れてしまい、結局、中断しています(最後のエントリは2020年8月)。資料的価値もあるので、継続したほうが良いと思うんですがねえ。

+
+
+

今月は先月に引き続きXMLとYAMLについて学びます。まず、似て異なるXMLとYAMLについて、その共通点と違いのそれぞれについて考えてみましょう。それからXML, YAML双方の実際の活用方法について紹介します。

+
+
+

XMLとYAMLの違い

+
+

先月号が発売されて「XMLとYAML」の記事を読まれた方の中に、「なぜXMLとYAMLを比較するのか。XMLとYAMLは本質的に違うものだ」という声があったと聞いています。確かにそのとおりです。XMLとYAMLは違うものです。

+

XMLは枠組みを提供し、その応用によって、文章のマークアップもできれば、データの表現もできれば、アルゴリズムの記述もできます。具体的にはDTDやスキーマというものと組み合わせることで、いくらでも意味を与えることができます。そういう意味で、XMLは言語の言語、あえてたとえればアルファベットのようなものです。アルファベットに文法が追加されて英語やフランス語、ドイツ語になるように、XMLに意味が追加されて、XHTML, SVG, Docbookなどになるのです。

+ +

一方、YAMLにはそんな汎用性はありません。YAMLは最初から「データ表現」を目的としてデザインされた言語です。シーケンス、マッピングなどの意味は固定されていて変えられません。ですから、用途的に同じレベルなのはXMLではなく、その応用であるSOAPとかになると思います。

+

XMLの表現できる広い広い世界が一部YAMLがカバーする世界と重複していると表現してもよいでしょう。

+

それなのになぜ、先月といい今月といい「XMLとYAML」などと2つを対等に並べるタイトルを付け、比較するような記事を書いたのでしょうか。もちろん、これにはわけがあります。

+

まず第1の理由はXMLの流行により、XMLが最適でない分野にまで乱用されている事実をなんとかしたいという思いです。XMLはeXtensible Markup Languageの略であり、もともとはSGMLというドキュメントに構造を付加するマークアップ言語をコンピュータで処理しやすいように単純化したものです。しかし、今や世の中のXMLデータのうち、人間が読むことが期待されているものがどれだけあるでしょう。あまりにも複雑で読むことができない(より正確には、たとえ読めても理解できない)データに、人間が読むためにデザインされた冗長なタグ情報を付けることにどれだけ意味があるのか疑問です。

+

諸般の事情があるのでしょうが、最近ますます「適材適所」の原則がないがしろにされているように思います。

+

私が勤務しているネットワーク応用通信研究所でもXMLは広く使われています。そして、私が見る限りその使用例のほとんどはXMLである必然性のないものです。そのほとんどはデータファイルや設定ファイルのフォーマットとしてXMLを使っています。さらに悪いことにはその半分くらいまでにはDTDやスキーマは明確には定義されておらず、DOMやSAXを使ったプログラムによって暗黙に定義されています。

+

XMLを選択すればDOMやSAXなどが構文解析してくれるのでプログラマーは手が抜けてありがたいという気持ちは私にも理解できます。しかし、そろそろ私たちは、そういう単なる設定ファイルやデータファイルにはXMLはあまり向いていないという事実を直視すべきではないでしょうか。

+

とはいうものの、他の選択肢も示さずにそんなことを主張するのは無責任です。そこでYAMLの登場です。YAMLなら、

+
    +
  • XMLよりはコンパクトな表記

  • +
  • (スキーマを含んでいるので)単なるwell formed XMLよりは明確な意味

  • +
  • ドキュメントの読み書きを行う標準的なAPI

  • +
+

など、XMLを使うのはあんまり向いていないんじゃないかという分野で、XMLと同等のうれしさを実現できます。足りないのは知名度ですね。

+

これがもう1つの理由になります。つまり、YAMLはまだまだ知られていないので、この機会に宣伝したいということです。すでに述べてきたようにYAMLはXMLより後から登場していますし、データ表現という目的に合わせて最適化されていますから、そういう分野ではXMLよりもずっと優れています。それなのに知られていないのはもったいないでしょう?

+
+
+ +

XMLとYAMLの共通点

+
+

これだけ違う違うと言いながら、この2つには重要な共通点があります。それは「人間が読む(読める)ことを前提にしている」点です。ひと昔前ならば、計算機のCPUパワーやディスク容量、ネットワークの大域幅は重要な資源なので、これを浪費しないことが重要でした。よって、なるたけこれらを無駄にしないために、データ表現はコンパクトなものが求められていました。

+

ところがどうでしょう。現代においては少々浪費したほうがよいのではないかというくらい潤沢にこれらの資源が手に入ります。こんな時代にあって昔のようなケチケチした態度は無用です。それがXMLやYAMLのようなテキスト指向のデータフォーマットが誕生した背景でしょう。

+

このような思想は実は昔からありました。たとえばメール転送プロトコルSMTPやネットニュースのプロトコルNNTPなどを始めとするインターネットを支えるプロトコルのほとんどは相当古いものからテキスト指向でした。それが時代の背景によって後押しされたと考えることができるでしょう。

+

このようなテキスト指向のフォーマットには非常によいところがあります。つまり、バイナリフォーマットと違い、人間の目で見てわかる(ことが多い)ということは、そのフォーマットを扱う専用のプログラムがなくても処理できるということです。

+

たとえば、私が本棚から発掘したフロッピーディスクに昔々父親が残したMultiplan(マイクロソフト製スプレッドシートプログラム、Excelの先祖と思えばよい)のファイルが入っていたとしましょう。現在、ちゃんと動作するMultiplanを入手することは困難ですから、このファイルの内容を読むためには大変な苦労が必要でしょう。

+

このファイルの内容がプレーンテキストあるいはそれに近い形式(たとえばCSV)で書いてあれば、もっと簡単に救済できたでしょう。

+

このようにバイナリフォーマット、特に商用ソフトウェアのフォーマットは内容がいつまで読めるか保証されないという不安があります。XMLやYAMLのようなテキスト指向のフォーマットならばそのような心配はありません。

+
+
+

YAMLの欠点

+
+

YAMLも完璧ではありません。正直いうとYAMLにもいくつかの欠点があります。それが許容できるかどうかは状況によって異なると思います。欠点と制約を知り、自分の状況でそれがどれだけ影響するかを判断するのが、プログラマーの正しい態度でしょう。

+

日本人にとってYAMLの最大の欠点は文字コードです。YAMLの仕様では文字コードはUnicode(UTF-8)しか許容しません。日本人としてはEUC-JPやらシフトJISやらで記述したいところでしょう。これを「英語圏の横暴だ」と見るか、あるいは「これからはUnicodeでしょ」と見るかで感じ方が変わってくるでしょう。

+

一方、XMLは先頭のXML宣言の中で明示的に文字コード(エンコーディング)を指定することができます(デフォルトはUnicode)。将来的にはYAMLも何らかの方法でエンコーディング指定ができるようになるといいなあ。ならないかなあ。

+
+

もう1つの欠点は、YAMLのインデントベースの文法は構造そのものの変化に弱いことです。たとえばXMLならアトリビュートを加えるだけで済む変化(リスト26.1(a))が、YAMLだと少々大げさになります(リスト26.1(b))。また、終端ノードを非終端ノードに変更するときもXMLよりもYAMLのほうが変化が大きいのがわかるでしょう(リスト26.1(c)および(d))。

+

数行程度の簡単なものならば、どちらも大差ないと考えることができますが、より構造が複雑なデータについてはコストが高くなります。

+
+

リスト26.1●XMLとYAMLの構造変化の対応

+
#シリアライズフォーマットの変更
+(a) XML
+    <foo>bar</foo>
+      ↓
+    <foo targetId="foo_3">bar</foo>
+
+(b) YAML
+    foo: bar
+      ↓
+    foo:
+      targetId: foo_3
+      content: bar
+
+# グラフ上で終端ノード→非終端ノードへの変更
+(c) XML
+    <foo><bar/><baz/></foo>
+      ↓
+    <foo><bar>1</bar><baz/></foo>
+
+(d) YAML
+    foo: [ bar, baz ]
+      ↓
+    foo:
+      - bar: 1
+      - baz
+
+
+

もっとも、私自身はこの問題についてあまり重要視していません。というのは、私はデータ構造の変換というものは、同じ構造の範囲内でちょいちょいといじるのと違って、かなり大げさなものであると考えていて、そのような大げさな変更を不確かな人間に頼るより、ツールで行うべきだと考えています。いったんデータを読み込んで、プログラムで構造を変化させたうえでもう一度吐き出せば、問題なしです。しかも、YAMLはインデントに意味があり、機械出力したものでもさほど読みにくくならないといううれしさがあります。これはプログラムが出力するXMLファイルが、適切にインデントや改行を入れてくれないため実質読めないものが多いのと対照的です。

+ +

なお、このYAMLの欠点の項については、私のWeb日記に対してのmakiさんこと高橋征義さんと、なひさんことなかむらひろしさんのツッコミを参考にさせていただきました。

+
+
+

YAMLの応用

+
+

YAMLの実際の使用例としては以下のようなものがあります。

+
    +
  • 設定ファイル

  • +
  • pstoreの代用(YAML::Store

  • +
+

それに加えて、メタデータとしての使い方を紹介しましょう。これは最近私の同僚によるプロジェクトで使われた例です。そのプロジェクトでは、システムはセンサーの付いたコントローラとそれらを管理するサーバーからなっています。私たちはサーバーのソフトウェアの開発を請け負ったのですが(ご存じない方のために。私の会社はLinuxをベースにしたシステムインテグレーションを行っています)、そのコントローラとサーバー間の通信にはかなり複雑なプロトコルが用いられていました。しかも、そのプロトコルは今後コントローラのバージョンアップによって追加・拡張される可能性がありました。

+

これを全部こつこつプログラムするのは大変です。そこで、同僚はまずプロトコルをYAMLで記述し、そこからプロトコルハンドラのプログラムを自動生成することにしました。プロトコルを表現したデータは以下のような感じです(前田修吾)。

+
+

リスト26.2●プロトコルのメタデータ

+
- name: DateRecord    # 定義するデータ型の名前 (Rubyのクラス名としてvalidなもの)
+  type: record        # データ型のタイプ (record|request|response)
+  entries:            # データ型の項目
+    - name: year      # 項目名 (Rubyのメソッド名としてvalidなもの)
+      type: bcd       # 項目の型 (integer|bcd|binary(string)|ユーザー定義データ型)
+      length: 2       # 数値桁数
+    - name: month
+      type: bcd
+      length: 2
+    - name: day
+      type: bcd
+      length: 2
+- name: TimeRecord8
+  type: record
+  entries:
+    - name: hour
+      type: bcd
+      length: 6
+    - name: min
+      type: bcd
+      length: 2
+- name: DailyReportRequest
+  type: request
+  code: 0x00                  # 種別コード (sub-codeもいるかも)
+  entries:
+    - name: num_records
+      type: integer
+      length: 2
+    - name: start_date
+      type: DateRecord
+- name: DailyReportResponse
+  type: response
+  code: 0x01
+  entries:
+    - name: num_records
+      type: integer
+      length: 2
+    - name: reports
+      type: string
+
+
+
+

YAMLの応用としては、この他にもXML-RPCならぬYAML-PPCなどいろいろ考えられると思います。

+

ちょうどこの原稿を書いている最中に、Ruby 1.8.0の正式リリースにはYAML処理ライブラリが(高速版パーサーSyckも含めて)含まれることが決まりました。これでますますYAMLの取り扱いが簡単になりますね。

+
+
+

XMLによる設定ファイル

+
+

しかし、いろいろYAMLついて紹介してきましたが、今現在でのYAMLの最大の欠点といえば、やはり知名度のなさでしょう。上記のプロトコルのメタデータのような一種の開発ツールとして使うならともかく、ビジネス目的のプロトコルに使う場合には「YAML? なにそれ?」と顧客に一蹴されそうです。

+

また、YAMLをサポートしているのは、現時点ではRuby, Perl, Pythonだけですから、たとえばJavaなどとやりとりしたい場合には使えません。そんな場合には、変な苦労をするよりもXMLを使ったほうが現実的というケースはいくらでもあるでしょう。世の中は技術論だけでは済まないのです。

+

そこで、そんな場合にもXMLを簡単に扱うためのライブラリxml-configfileを紹介します。

+

XMLを扱うライブラリとしてはREXMLが定番なのですが、これはXMLのほぼフル機能をカバーするために少々複雑なライブラリになっています(そういえば、REXMLも1.8.0での標準添付が決まりました)。xml-configfileREXMLを利用して、設定ファイルのような簡単なXMLファイルからデータを取り出すライブラリです。xml-configfileは書き出しはサポートしていません。

+
+

xml-configfileをインストールするためには、まずREXMLをインストールします。原稿執筆時点での最新版は、

+
    +
  • http://www.germane-software.com/archives/rexml_2.5.8.tgz

  • +
+

でした。これを展開したディレクトリで、

+
+
# ruby bin/install.rb
+
+

とすれば(root権限が必要です)、REXMLのインストールは完了です。REXMLについては『Ruby de XML』(オーム社、ISBN4-274-06500-6)に解説されています。

+

次に、本命のxml-configfileをインストールします。最新版は、

+
    +
  • http://www.maik-schmidt.de/projects/xmlconfigfile/dist/xmlconfigfile_0.9.0.zip

  • +
+

をダウンロードします。それから、ダウンロードしてきたファイルを展開します。zipファイルはunzipプログラムで展開できます。

+
+
# zip xmlconfigfile_0.9.0.zip
+# cd xmlconfigfile_0.9.0
+# ruby install.rb config
+# ruby install.rb setup
+# ruby install.rb install
+
+

でインストールは完了です。実際のインストールする部分にはルート権限が必要です。

+

では、実際にXMLファイルを読み込んできましょう。たとえば、リスト26.3のようなXMLデータを読み込むとしましょう。

+
+

リスト26.3●サンプルXMLデータ

+
<!-- Sample XML file. -->
+<?xml version="1.0" encoding="iso-8859-1"?>
+<config>
+ <version>1.7</version>
+ <splash-screen enabled='yes' delay='5000'/>
+ <greeting lang="en">Hello, world!</greeting>
+ <greeting lang="de">Hallo, Welt!</greeting>
+ <db env="test">
+   Standard connection.
+   <pwd>tiger</pwd>
+   <driver>
+     <vendor>MySql</vendor>
+     <version>3.23</version>
+   </driver>
+ </db>
+ <db env="prod">
+   <pwd>secret</pwd>
+   <driver>
+     <vendor>Oracle</vendor>
+     <version>8.1</version>
+   </driver>
+ </db>
+</config>
+
+
+ +

このデータがconfig.xmlというファイルに入っていたとすると、読み込みのためには、まず、

+
+
require 'xmlconfigfile'
+conf = XmlConfigFile.new('config.xml')
+
+

これでconfオブジェクトを通じて、XMLファイルの中身を読むことができます。たとえばバージョン情報は、

+
+
conf.get_string("/config/version")
+
+

で、"1.7" という文字列が得られます。引数に与えるのはXPATHという形式で、XMLの「ノード」を指定する記法です。XPATHにおいて、

+
+
/conf/version
+
+

<config> ノード中の <version> ノードを意味します。get_string[]で代用することができますから、

+
+
conf["/config/version"]
+
+

でも"1.7"を取り出せます。

+

XMLには型情報がないので、読み込み側でほしい型を指定する必要があります。ですから、同じ情報を文字列でなく、実数でほしい場合には、

+
+
conf.get_float("/config/version")
+
+

と指定します。型を指定して取り出すメソッドを表26.1に示します。

+
+

表26.1●XmlConfigFileの型指定メソッド

+ + + + + + + + + + + + + + + + + + + + + +
メソッド説明
get_string(path)文字列
get_int(path)整数
get_float(path)実数(浮動小数点数)
get_boolean(path)真偽値
+
+

整数や実数を指定した場合、実際のXML値が変換できない形式であった場合にはArgumentError例外が発生します。

+

真偽値が要求された場合、"yes""on", "true", "1" などは真に、"no", "off", "false", "0" などは偽と見なされます。

+

XPATHでは属性は "@" を付けて指定します。ですから、/config/splash-screenノードのdelayという属性値を指定するXPATH表現は、

+ +
+
/config/splash-screen/@delay
+
+

になります。

+

複数のレコードを属性値で選択することもできます。

+
+
/config/greeting[@lang='de']
+
+

は「複数あるgreetingノードのうち属性値が 'de' のもの」を意味します。ですから、このXPATHはドイツ語のあいさつ 'Hallo, Welt!' を含むノードを指定していることになります。

+

最後に、XML設定ファイルから複数の値を一気に取り出すためにはget_parametersメソッドを使います。

+
+
conf.get_parameters("/config/db[@env='test']")
+
+

のように指定すると、get_parametersメソッドは属性envの値がtestであるdbノードの内容をハッシュとして返します。

+
+
{
+  "db.driver.vendor"=>"MySql",
+  "db.driver.version"=>"3.23",
+  "db.pwd"=>"tiger"
+}
+
+

xml-configfileにはその他にも省略時のデフォルト値、環境変数の読み込み、相対パス指定など便利な機能があるのですが、ここではすべては解説できません。詳しい使い方は、ホームページ、

+
    +
  • http://www.maik-schmidt.de/projects/xmlconfigfile/index.html

  • +
+

に紹介されています、英文ですが。まあ、作者はドイツの方ですので、英語であるだけありがたいと考えるしかないですね。

+
+
+

まとめ

+
+

今回は先月に引き続きXMLとYAMLについて学びました。XMLもYAMLもRubyのライブラリを用意すれば簡単に操作することができます。試しに使ってみてはいかがでしょう。そのときには、ちょっと考えて適材適所を心がけていただければ幸いです。それでは、また来月。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-058.xhtml b/docs/vol1/xhtml/p-058.xhtml new file mode 100644 index 0000000..0781423 --- /dev/null +++ b/docs/vol1/xhtml/p-058.xhtml @@ -0,0 +1,80 @@ + + + + + +第26章 XMLとYAML(その2) + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ うぇぶ日記はじめました。

+
+

皆もすなる日記といふものを、まつもともしてみむとて、するなり。

+

いや、そのまんまなんですが、とうとう始めました、うぇぶ日記。

+
    +
  • http://www.rubyist.net/~matz/

  • +
+

聞くところによると日本では昔から「Web日記」というものが広く使われてきたようです。Ruby関係者(と呼んでいいんだろうか)の日記には興味深く目を通すものもいくつもありました。

+

また、最近ではツッコミを許す日記も出てきて、日記間でリンクを張ったり、ツッコミあったりで楽しそうだなあ、なんて見てました。とはいうものの、なんか抵抗があって自分では書く気にならなかったんですよ。どうも私はオールドタイプで、新しいテクノロジーに順応するのがすごく遅いみたいなんです。この間買ったHDDレコーダのリモコンも使いこなせないし。

+

え? 年寄りだって?

+

確かにそうかも。Windowsさえちゃんと使えないし、ウィンドウマネージャはいまだにfvwmだし。昔使ってたolwm(オープンルックウィンドウマネージャ)はさすがにやめたけど。

+

そんな私が日記を書き始めたのには、何か特別な理由があったかというと、そうでもなくて、特別じゃない小さな理由の積み重ねによるものだったりします。

+

まず、最初の理由になったものは、

+
    +
  • http://www.ruby-lang.org/

  • +
+

のtDiary化でした。今までtDiaryってのは「Rubyのキラーアプリかも」とは思いながらも、実際に自分で使ってみることはありませんでした。しかし、Rubyホームページが採用したのですから、使わないわけにはいきません。実際に使ってみると結構簡単ですし、使い勝手も見栄えもなかなかすばらしいです。感動しました。たださん、ありがとう。

+

次に理由になったのが、tdiary-modeです。私は根っからのEmacsユーザーで、メールもニュースも原稿書きも、コード書きも、デバッグもみ〜んなEmacs上で行うのです。特に文章を書くという行為はEmacsなしには不可能と言っても言い過ぎでないくらい依存しています。体にEmacsのキー操作が染みついています。どのくらい染みついているかっていうと、昔Netscapeを使いながら無意識にEmacsのカーソル移動を使ってて、1時間くらいしてから初めて自覚して驚いたくらいです。

+

逆にブラウザのテキストフォームに文章を入力するのは大嫌いです。いくらMozillaでEmacsキー操作が使えるといっても、kill-regionやyankまではできませんから。あと、ispellによるスペルチェックとlookupによる辞書検索ができないのも痛いです。

+

ですから、Webの更新も当然Emacsで行いたいわけです。事実、昔のRubyホームページはHTMLファイルをEmacsを使って手動で編集していました。そんな私にtdiary-modeはまさに福音でした。これですべての苦しみから解放されるというものです。大げさすぎですか? tdiary-modeを作ってくださった、kitajさんこと、きたさんに感謝します。

+
+

それから、最後に敷居を下げてくれたのはtDiaryのRD styleでした。もともとtDiaryのエントリは「行を意識したHTML」で記述するようになっていたのですが、tDiary 1.5でスタイル機能が追加され、自分の好みのスタイルで記述できるようになったのです。そして、ありがたいことにそのスタイルの中にRDが含まれていたのです。

+

RDというのはそもそも私が最初に書いた本である『オブジェクト指向スクリプト言語Ruby』の原稿を書くときに、TeXも嫌い、変なマークアップ言語も嫌い、できるだけプレーンテキストで書きたいという欲求から、PerlのPODを参考にしてデザインしたフォーマットですから、私にとってこれ以上のものはないわけです。実際、この原稿もRDのような形式で書いたものを、編集者の皆さんが整形してくださっています。

+

こうして、tDiaryとtdiary-modeとRD styleのおかげで技術的な敷居はほとんどなくなっていたわけですが、実際に書き始めるにはもうふた押し必要でした。

+

1つは妻からの後押しでした。いや、別に彼女はWeb日記を書けと言ったわけじゃないんですが。

+

彼女は小学校4年生のときから欠かさずに日記を書いているという偉い人なので、「あなたも書きなさい」と。確かに日記を付けていれば、

+
    +
  • 忘れていた記憶がよみがえる

  • +
  • 後の世代に役立つ記録になる

  • +
+

というメリットもあります。

+

実は私も10年くらい前までは日記を付けていたのです。Emacsで。それが結婚して子供ができたり、仕事が忙しくなったりで、いつの間にか中断していました。まあ、いい機会だし、そろそろ再開しようかなあ、と思ったわけです。

+

もう1つは、情報発信です。今さら「インターネットで世界に情報発信」とかいうのも恥ずかしいのですが、考えてみれば、私のスタイルってのは、メーリングリストで質問に答えて初めて開示するということが多くて、積極的に自分から何かを伝えるということがあまりなかったように思います。

+
    +
  • 知りたかったらメーリングリストで聞け

  • +
  • あるいは私の講演を聞け

  • +
  • あるいはコードを読んで想像しろ

  • +
+

というのは、ちょっと不親切かなあ、と10年目にして反省したわけです。まあ、「プログラマーはコードで語る」って姿勢がカッコいいんじゃないかとずっと思ってきたってのは内緒ですけどね。

+

そんなこんなで、5月1日からWeb日記を書き始めたのです。この原稿を書いている時点ではまだ2週間ほどしか経っていないのですが、今のところ毎日欠かさず書けています。

+

肝心の内容はといえば、

+
    +
  • 子供に絵本を読んでやった

  • +
  • 教会に行った

  • +
+

というような個人的な内容から、

+
+
    +
  • Ruby 1.8の予定と課題

  • +
  • Riteの構想と課題

  • +
  • XML批判とYAMLの宣伝

  • +
+

のような「固い」内容までごった煮のようにつまった妙なものになっているのです。

+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-059.xhtml b/docs/vol1/xhtml/p-059.xhtml new file mode 100644 index 0000000..bd17097 --- /dev/null +++ b/docs/vol1/xhtml/p-059.xhtml @@ -0,0 +1,314 @@ + + + + + +第27章 エクストリーム・プログラミング + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay27 +
+

+初等Ruby講座
+エクストリーム・プログラミング +

+
+

[Linux magazine, 2003年8月号]

+
+

「エクストリーム・プログラミング」通称XPについて解説してます。20年前だとアジャイルという言葉はあっても、ソフトウェア開発フレームワークとしてのScrumはまだ誕生したばかりみたいですね。会社(ネットワーク応用通信研究所)でのXP実践の経験をまとめています。実際の経験をベースにしているので、アジャイル開発実践の記録として今でも価値のあると思います。記事中では、XPを適用したプロジェクトのリーダーの前田修吾さんにインタビューをしているのですが、彼はRubyのコミッターでもあり、頼もしい後輩です。2023年、彼はネットワーク応用通信研究所の社長に就任しました。すごい。

+

「Ruby開発日記」は、「10年後の技術者」と題して未来予測をしています。予想した10年後はとっくに過ぎていますが、予想された

+
    +
  • 死ぬ技術もある

  • +
  • OS、ネットワーク、言語は大して変化しない

  • +
  • アスペクト指向が言語取り込まれているかも

  • +
+

はほぼ当たっていると言えるでしょう。最後のアスペクト指向はすっかり聞かなくなりましたが、たとえばRubyにModule#prependが取り込まれたように、見えない形でほそぼそと生き残っています。

+

20年経っても、この傾向は変わっていないようです。

+
+
+

今月はRubyそのものではなく、Rubyを使った開発手法について学んでみましょう。今月取り上げるのは、最近話題のソフトウェア開発体制である「エクストリーム・プログラミング」です。

+
+
+ +

XPとは何か?

+
+

XPとはエクストリーム・プログラミングの略で、最近話題の開発手法、というよりも開発体制のことです。エクストリームとは「極限」「極端」という意味ですから「極端流プログラミング」とでも呼べばよいのでしょうか。

+

XPでは過去のプログラミング経験でよいと呼ばれるものを極限まで推し進めたものです。

+
    +
  • コードレビューがよいのであればいつでもコードレビューを行う

  • +
  • テストがよいのであれば全員がいつでもテストを行う

  • +
  • 設計がよいのであれば設計を日常の一部にする

  • +
  • シンプルがよいのであればシステムを最もシンプルな状態に保つ

  • +
  • アーキテクチャが重要であれば全員がいつでもアーキテクチャを定義し洗練する

  • +
  • 結合テストが重要であれば、1日数回結合テストする

  • +
  • 短いイテレーションがよいのであれば短い単位で行う

  • +
+

従来のプログラミングのやり方を経験している人にとっては、あまりに極端で「え? いつでもコードレビューを行う? そんなにムリ」とか、「いつでもテスト? そんなにテストしてどうするの?」とか疑問がわきそうな内容です。

+

しかし、XPを提唱したKent Beckは、一見無茶に聞こえるこれらが非常に有効であることを示したのです。

+
+
+

解決すべき問題

+
+

私は長らく職業プログラマーでお客さまのためのプログラムを書いてきました。フリーソフトウェアの開発にかまけてきたため期間の割には経験豊富とはいえないのですが、それでも以下のような問題をいつも抱えていました。

+
    +
  • 見積もりが当てにならない

  • +
  • ユーザーの要求がころころ変わる

  • +
  • バグが多すぎ、テストに時間がかかりすぎ

  • +
  • テストが不十分

  • +
  • システム完成後、ドキュメントを書くのが大変

  • +
+

こういう問題を抱えているのは私だけではないと思います。XPではこれらの問題は以下のようないくつかの間違った仮定のためではないかと考えます。

+
    +
  • ユーザーは自分のほしいものを最初から知っている

  • +
  • ドキュメントはシステムのあるべき姿を記述できている

  • +
  • 変更はあとになればなるほどコストが高くなる

  • +
  • なんとか動作しているコードは修正すべきではない

  • +
+
+

しかし、これらはある条件の下には真実でないかもしれないのです。私は最初にXPについて読んだとき、今まであきらめてきていた「常識」が不変のものではなかったことに気が付いてびっくりしました。

+
+
+

12のプラクティス

+
+

あまりページ数に余裕がありませんし、XPのすごさについて手っ取り早く解説するために、概念的なことではなく、実際に何をやるとどんなよいことがあるかについてまず解説しましょう。

+

XPには具体的な行動である「12のプラクティス(実践)」があります。最近新しいプラクティスが追加されたような話を聞きますが、ここではXPについて最初に解説した本である『XPエクストリーム・プログラミング入門』(ピアソン・エデュケーション、ISBN4-89471-275-X)にあげられているものを紹介します(表27.1)。

+
+

表27.1●XPの12のプラクティス

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
プラクティス説明
計画ゲーム柔軟に頻繁に計画を見直す
短期リリースシンプルなシステムを素早く構築、たびたびリリースする
メタファ比喩によるわかりやすいアーキテクチャを提示する
シンプルな設計いつもシンプルに。複雑さは積極的に取り除く
テスティング常にテストする。顧客は検収テストを書く
リファクタリングシステムの振る舞いを変えずに常にシステムを改善する
ペアプログラミングいつも二人のプログラマーが1台のマシンコンピュータに向かう
共同所有誰でもいつでもすべてのコードを変更できる
継続した結合タスクが終了するごとに1日に何度も結合テストを行う
40時間労働週40時間以上働かない。残業しない
オンサイトユーザー顧客をチームに加え、いつでも質問に答えてもらう
コーディング規約ルールに従ってコーディングする
+
+

こんなことができるのか、効果があるのか、と思われる方がいらっしゃるかもしれません。しかし、実際には正しく適用されれば、効果が出るのです。

+

ここで断っておきますが、私自身はXPについて経験豊富というほどではありません。しかし、私のチームが最近のプロジェクトで実際にXPを行い見事な成果をあげつつあります。

+

たとえば彼らはプロジェクトの言語としてRubyを選択し、Rubyのtest/unitライブラリを使って、自分たちが作るすべてのコードにテストプログラムを書きました。実際にプログラムを書く前にテストプログラムを書くのです(テスティング)。

+

これによってテストプログラムがその機能に関する「動くドキュメント」になります。テストプログラムが正しく動いているということはプログラムにはバグがないということです、テストプログラムそのものに間違いがない限り。プログラムを書き加えるたび、修正を行うたびにテストプログラムが実行されます。プログラムが完成に近づくにつれて失敗するテスト項目が減っていくことになります(継続した結合)。

+ +

テストによってプログラムが期待どおり動作していることがいつでも確認できますから、プログラムの修正が恐くありません。よりシンプルになるように、より効率よくなるようにプログラムをリファクタリングしてもプログラムの正しさが保証されます(リファクタリング、シンプルな設計)。

+

彼らはいつも二人一組で1台のコンピュータに向かってプログラミングします(ペアプログラミング)。一人がコードを書いている間、もう一人はたえずもっと高い視点からツッコミを行います。一人でコードを書いているときとは違った視点からのレビューが常に行われ、対話しながらプログラミングすることにより、見落としがちな点をより早く発見できるうえ、知識や情報の移転が素早く行われます。

+

彼らの書いたコードはすべてCVSに格納されます。リポジトリが共有されているので、問題がある部分を誰でもいつでも修正できます(コード共有)。

+

チームの中には顧客から派遣されてきた技術者がいます。この技術者は開発すべきシステムに対する知識を教えてくれるだけでなく、一緒に開発することで、今後彼らがシステムを保守するときに必要な知識を学び、また私たちが開発していないシステムの他の部分との調整も行っています(オンサイトユーザー)。

+

このシステムが稼働すべき日は外部的な要因から固定されています。しかし別の要因から開発開始は予定よりかなり遅れました。プロジェクトチームは優先順位を常に検討し、稼働日に要求される機能を重要な順から実装していきました(計画ゲーム)。開発が始まっても私たちが開発すべきシステムの全容が見えず、不安がありましたが、現在の予定では必要な機能はすべて期日までに完成しそうです。

+

正直なところ40時間労働は完全には守られていません。しかし、残業は週数時間程度であまり過酷な状況にはなっていません。日常的に働きすぎているということはどこかに間違いがあるということであり、それはもっと早い時点で計画に反映され、解決される必要があります。徹夜続きで生産的な仕事ができるはずもないのです(40時間労働)。

+
+
+

4つのバリュー

+
+

XPがこのようなプラクティスを導き出せたのは「4つのバリュー(価値)」を大切にしたからです。その4つとは、

+
    +
  • コミュニケーション

  • +
  • シンプル

  • +
  • フィードバック

  • +
  • 勇気

  • +
+

です。これらの裏付けがあるからこそ12のプラクティスが効果を発揮するのです。そして、それらのバリューの背後にはチームのメンバーがお互いにリスペクト(尊敬・尊重)する気持ちが期待されます。私の経験からも成功するチームあるいはコミュニティにはこのリスペクトの気持ちがあるように思われます。

+
+
+ +

RubyとXP

+
+

XPはある特定の言語に依存した手法ではありません。しかし、XPというのはもともとSmalltalkでの開発から生まれてきたやり方ですから、Smalltalkのような動的な言語のほうが向いているようです。

+

そういう意味からはRubyはまさにXP向けの言語と呼べるでしょう。RubyはSmalltalkと同様に動的なオブジェクト指向言語ですし、クラスやメソッドをオブジェクトとして取り扱う機能もあります。また、Ruby 1.8からはユニットテストのフレームワークであるtest/unitライブラリが標準添付されています。

+

test/unitライブラリの使用例をリスト27.1に示します。

+
+

リスト27.1●test/unit使用例

+
require 'test/unit'
+
+class TC_MyTest < Test::Unit::TestCase
+  def setup
+    # テスト前の準備
+  end
+     
+  def teardown
+    # テスト後の後片付け
+  end
+ 
+  def test_foo
+    # テストを行う。
+    # testで始まるメソッドがテストされる
+    assert(cond, 'Assertion was false.')
+    assert_equal(expect, actual, 'unexpected result.')
+  end
+end
+
+
+

setup”や“teardown”メソッドはテスト前に実行されます(必要なければ定義しなくてもかまいません)。たとえばテストに用いるファイルの準備やデータベースの接続はsetupメソッドで行い、それらの後始末をteardownメソッドで行います。必要な値をテストメソッド(メソッド名がtestで始まるもの)に渡すためにはインスタンス変数を使うとよいでしょう。

+

テストメソッドでは実際のテスト項目のためにassert(表明)メソッドを使いますが、assertとして用いることができるメソッドのうち主要なものを表27.2に示します。

+ +

テストメソッドの登録や明示的な呼び出しは必要ありません。このプログラムを実行するとテストメソッドを勝手に実行してくれます。実行結果はリスト27.2のようになります(「.」の数がテストの数になります)。

+
+

リスト27.2●test/unitの実行例

+
Loaded suite /tmp/t
+Started
+..
+Finished in 0.002082 seconds.
+
+
+
+
+

プロジェクトリーダーへのインタビュー

+
+

では、今回のわが社の「XPプロジェクト」のリーダーである前田修吾さんにインタビューしてみましょう。ご存じのとおり、前田さんはmod_rubyerubyの開発者でもあります。ちなみにこのプロジェクトでの私の立場は「オブザーバー」であり、プロジェクト全体を暖い目で見守る立場です。

+

なお、このプロジェクトはまだ完了していない事例であり、守秘義務などの関係で具体的な内容には触れられないことをご容赦ください。

+


+

まつもと(以下「ま」):では、前田さん、このプロジェクトについていろいろお伺いしたいのですが、なぜXPを採用しようと思ったのですか?

+

前田さん(以下「前」):はい、最大の理由は「やってみたかったから」ということです。XPという開発手法には前から関心がありましたから。しかし、もちろん理由はそれだけではありません。

+

まず、今回のシステム開発に対して私たちのところまで出向いて一緒に開発してくれるオンサイトユーザーが得られたこと、それからプロジェクト規模がちょうどよかったことが大きいです。あまり小さなプロジェクトではペアプログラミングなどによって生じる無駄を取り返せませんし、逆にあまり大きなプロジェクトではXPはうまくいかなそうです。

+

もう1つの理由はプロジェクトの「トラックナンバー」を大きくしたかったということです。「トラックナンバー」というのはそのプロジェクトの参加人数のうち何人がトラックにひかれても大丈夫かということを意味する数字ですね。トラックナンバーが1だと一人欠けただけでプロジェクトがストップすることを意味します。今回のプロジェクトではペアプログラミングによる知識共有などでトラックナンバーを2か3にできたと思います。

+ +

:このプロジェクトについてさしつかえない範囲で聞かせてください。

+

:あまり詳しいことは言えないのですが、このプロジェクトはある顧客のWebシステムの一部になります。三層アーキテクチャのデータベース層とビジネスロジック層が私たちの開発している部分です。プレゼンテーション層は別のチームがASP.NETを使って開発しています。この別チームの存在がこのプロジェクトの難しいところです。ビジネスロジック層とプレゼンテーション層はSOAPを使って情報をやりとりするようにしました。

+

現在、プロジェクトを開始して1カ月ですが、今までに13000行のRubyコードを書きました。テストを含めると3万行を超えます。最終的な規模はちょっと予想できないのですが、この倍を超えるかもしれません。

+

:3万行のRubyプログラムですか、私が今まで聞いた中でも最大規模ですね。それを1カ月で開発したというのはすごいですね。では、XPを実践する中で12のプラクティスをどのように使いましたか? すべて実践しましたか?

+

:いえ、全部のプラクティスは実践しませんでした。実践しなかったのは具体的には「短期リリース」「メタファ」「コーディング規約」ですね。あと、「40時間労働」もちょっとオーバーしていますね。だいたい毎日9時から7時までの45時間労働くらいでしょうか。今までで一度だけ土曜日に出勤しました。

+

今はまだプロジェクト開始して1カ月ですから「短期リリース」は無理だとか、チームメンバーのスタイルにあまりばらつきがなく「コーディング規約」があまり必要でなかったとかが実践しなかった理由ですね。「メタファ」については既存のシステム互換でそこに機能拡張をするということで、アーキテクチャ的に迷いがなかったので使いませんでした。

+

他のプラクティスはだいたい実践しました。ストーリーカードやタスクカード、CRCカードを用意して「計画ゲーム」を実践したり、ユニットテストを徹底したり。コードはCVSを使って共有しました。また、今回は顧客から二人の技術者をお借りし、こちらの技術者二人といつもペアを組むようにしました。

+

あと、ユニットテストコードがあると安心してコードの変更ができますね。途中で仕様変更とかデータベースのテーブル構成が変わったりしたのですが、比較的容易に対応できました。

+

:生産性から考えると非常に成功したケースと言えそうですね。今回のプロジェクトでXP以外に工夫した点がありますか。

+

:そうですね、テストケースが非常に多いので、テストケースをYAMLの記述から自動生成したりしました。その他、まつもとさんが先月号で紹介したようなYAMLからの自動生成はあちこちで使いました。SOAP通信用のWSDL(WebService Description Language)もRubyを使ってYAMLから記述したんですよ。

+

:そうですか。で、XPの効果のほどは?

+

:正直なところコードを書くスピードは一人でプログラムしたほうがずっと速いです。今回はRubyで開発していますし、もともと生産性はかなり高いのだと思います。やはりペアプログラミングではコミュニケーションのコストがあるので。しかし、ユニットテストのおかげでコードの信頼性は高まったと思います。

+

また、今回はオンサイトユーザーが得られたおかげで要求仕様と実装のギャップのずれの修正が素早く行えました。また、わからないことがあればユーザーに直接質問できますから、ドキュメントが少なくて済みました。というか、全然書いていません。

+ +

:これまで1カ月間XPな生活を過ごしているわけですが、XP生活の印象は?

+

:なんだか今までに比べると「仕事してるな」って感じがします。結局、朝9時から7時までびっちり仕事しています。メールをチェックする頻度もすっかり減ってしまいました。ペアプログラミングの効果かもしれません。最初は密度が高くてかなり疲れましたが、この1カ月でだいぶ慣れました。

+

:ありがとうございます。では最後に、またXPしたいと思いますか?

+

:そうですね。プロジェクトの規模によっては行ってみたいと思います。3000行くらいのプロジェクトなら一人のほうが絶対速いですし。それからプロジェクトの予算によりますね。あまり小さな予算だと複数の開発者を割り振れませんから。それから、XPを行うためには顧客や会社の理解が前提だと思います。特にオンサイトユーザーについては顧客に理解してもらうのが難しいかもしれません。

+

あと、どの言語を使うかというのもありますね。Cとかじゃやりたくありません。JavaやC++でもやっぱりイヤですね。XPを行うときの言語はメタプログラミング機能を持っててほしいです。

+

:では、もう一人、今度はオンサイトユーザーとしてチームに参加されたYoshinoさん(ペンネーム)にもお伺いしましょう。

+

:Yoshinoさんは以前からRubyについてご存じでしたか?

+

Yoshino(以下「Y」):使ったことはありませんでした。でも、Rubyという名前は知っていました。世界的に有名ですから。

+

:またまた。ほめても何も出ませんよ(笑)。今回、XPについて初めて聞かれたときにはどのように感じましたか?

+

Y:なんだか楽しそうだな、と思いました。今まで自社内で開発してましたが、今までやってきたやり方とは全然違いますし。また、いつも二人でプログラミングすると聞いてわくわくしました。

+

:では、1カ月経った今のXPに対する感想は?

+

Y:今回、仕様がはっきり決まっていない部分があって、そこの調整役が大変でした。最初、私は単なる開発者として参加したつもりだったのですが、実際には調整役の責任が大きくて。また、調整を行うときには、調整役が十分な権限を持たないといけないと思いました。

+

あと、情報を共有するのが大変だと感じることがありました。今回、プレゼンテーション層の開発が別チームなのでそことのやりとりに課題が残りましたね。

+

全体的には非常によい環境で刺激的な仕事ができたと思います。ペアプログラミングというやり方はユニークで面白いと思いました。後はペア間のレベルの違いをどう克服するかについて考えないといけないと思います。今回、私たちはRubyを全然知らなかったので。ペアプログラミングを通じてずいぶん学びましたが。

+

:また、XPしたいと思いますか?

+

Y:うーん、次はもっと若い人に経験してもらいたい、かな(笑)。でも、自分のところでもやってみたいとは思っています。たとえば新人教育用プロジェクトでやってみるとか。そのときにはペアプログラミングにおける役割分担についても考えてみたいです。あと、ユニットテストはすばらしいと思いました。これについてはぜひ今後もやっていきたいと思っています。

+

今回、初めてのことばかりで非常によい経験でした。すばらしい環境で仕事ができて光栄です。ありがたいです。

+
+

:ほめても何も出ませんってば。お話を聞かせてくださって、こちらこそありがとうございます。

+
+
+

XPとオープソース開発

+
+

私自身がXP開発経験が豊富というわけでもないのに、XPに肩入れをするのにはわけがあります。XPのバリューとプラクティスは、私が深く関わっているオープンソース開発と非常によく似ているのです。

+

私たちはCVSなどを用いてコード共有します。ペアプログラミングは行いませんが、変更は多数の開発協力者が確認します。私たちはシンプルな実装を好み、しばしばリファクタリングを行います。私たちはメーリングリストを通じて実際のユーザーの意見に耳を傾けます。

+

また、コミュニケーション、シンプル、フィードバック、勇気の4つのバリューとその背後にあるリスペクトの気持ちはオープンソース開発にも必要な原則です。

+

結局、XPが注目されるのも、オープンソースが注目されるのも、それらが達成しようとしている柔軟性にも原因があるのかもしれません。

+

XPを始めとする柔軟な開発手法を提唱している人々がAgile Manifest(Agileは「機敏な」という意味)という文書で顧客の価値と柔軟性を最重視する姿勢を宣言しましたが、その人々の中にProgramming Rubyを書いたDaveThomasとAndy Huntがいたのは偶然ではないと思います。

+
+
+

まとめ

+
+

ということで、今回はXPについて駆け足で解説しました。

+

Rubyの開発にはまだ十分取り入れていないXPのプラクティスがあります。計画ゲームとか(完全な)ユニットテストとか。これらもRubyの開発に取り入れて、より信頼性の高いRubyを作ろうかな、なんてこの原稿を書きながら考えました。

+

皆さんのXPを試してみませんか、できることから、一部だけでも。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-060.xhtml b/docs/vol1/xhtml/p-060.xhtml new file mode 100644 index 0000000..bfadf92 --- /dev/null +++ b/docs/vol1/xhtml/p-060.xhtml @@ -0,0 +1,61 @@ + + + + + +第27章 エクストリーム・プログラミング + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ 10年後の技術者

+
+

先日、『日経ソフトウェア』という雑誌の創刊5周年企画で「10年後生き残るソフトウェア技術者になるには」というようなテーマのパネリストとして招待されました。

+

パネルそのものはテーマにはあまり沿わずに進んだのですが、改めて10年後のコンピューティングについて考えるとなかなか面白いものがあります。日進月歩どころか秒進分歩のこの業界で10年先ははるかな未来のように感じられます。いったいどうなっていることでしょう。おそらくは「とても予想できない」というのが皆さんの答えではないでしょうか。

+

確かにここ数年のコンピュータの発展はめざましいものがあります。誰も彼もが電子メールを使い、インターネットにアクセスし、町のあちこちでURLを見かける世の中を誰が想像できたでしょう。10年前にはコンピュータなど関係なかったビジネスさえコンピュータなしには成立しなくなっています。コンピュータ好きだと変人扱いされていたのは、そんなに昔のことではないはずなのに。この調子で進歩すればいったいどこまで進んでいくのでしょう。

+

しかし、私はこのことについて少し違った考えを持っています。確かにみんなが電子メールを使い、インターネットにアクセスする世の中に変わったかもしれませんが、電子メールやインターネットが生まれたのは30年も前のことです。それから確かに進歩はしていますが、本質的な部分は変わっていません。

+

コンピュータの性能は昔に比べて信じられないほど向上していますが、速度が速くなったとはいえ、今までになかったアーキテクチャが登場したわけではなく、基本的な進歩は容量やクロックの向上です。

+

つまり、私たちが目にしている劇的な進歩は実は量的なものがほとんどで、質についてはそれほど変化していないような気がするのです。

+

コンピュータが初めて誕生したのは今から約半世紀前です。このときからしばらくの間はコンピュータのハードウェアはとてつもないスピードで進化しました。またソフトウェアについても今までに存在しなかった新しいものが次々と登場しました。いや、コンピュータがなかったので今まで存在しないのは当たり前だったんですけど。

+

そして「プログラミング言語」という概念が生まれ進歩し、それと同時にさまざまなアルゴリズムが「発見」されました。コンピュータ科学の進歩は誕生から2, 30年の間、一般の人があまり知らないところで猛烈に行われていたのです。

+

実際にアルゴリズムの教科書を見て、それぞれのアルゴリズムが最初に発表されたのがいつか調べてみると面白いことがわかります。現代でも重要だと見なされているアルゴリズムの多くは30年以上前に発表されたものばかりです。

+

そうなんです。コンピュータのコストは下がり、これからも今まで使われなかったところにまで進出してくるでしょうが、それはあくまでも普及という量的な変化で、今の形のコンピュータの質的な進歩はすでに何十年も前に落ち着いてしまっているのです。

+

私の得意なプログラミング言語の分野はさらに変化の少ない分野です。今になっても新しい言語は次々と誕生していますが、その背景となる概念に関してはこの20年は劇的な変化は発生していません。今までに存在していたものの焼き直しで、使い勝手を少々よくしたようなものばかりです。

+
+

将来の変化についてはもう1つの要素があります。それはコンピュータと違って人間はそれほど変化しないということです。

+

たとえば車を見てください。コンピュータ同様、車の性能もどんどん向上していますが、その操作系(ハンドル、ペダル、メータなど)は長い間ほとんど変化していません。最大の変化はマニュアルからオートマに変わったことくらいでしょうか。人間が関わる部分は急激に変化しても人間が対応しきれないのです。

+

このことを踏まえるとこの先10年のコンピュータの進歩は今までの10年の進歩とさして変わらない方向性とレベルだろうと推測できます。要するに10年前から今日までの変化を見れば、10年後もなんとか予測できるだろうということです。

+

10年前(1993年)のコンピューティングを振り返ってみると、

+
    +
  • インターネットはあった

  • +
  • 電子メールもチャットもニュース(掲示板)もあった

  • +
  • Windowsも(3.1だけど)あった

  • +
  • MotifとOPENLOOKの競争に結論が出かかっていた

  • +
  • マシンはとても遅く高かった

  • +
  • 誰も携帯電話をもってなかった

  • +
  • Webはあったかもしれないが一般的ではなかった

  • +
  • Rubyの開発が始まった

  • +
+

くらいでしょうか。これを見ると、研究者や一部の人が使っていたものが大衆に普及した10年といえるでしょう。

+

この先10年はこれがますます進むでしょう。10年後は今以上に多くの人が知らないうちにコンピュータを使うようになるでしょう。お年寄りや子供も含めてインターネットを使うようになり、またコンピュータを経由して得られる情報も桁違いに大きくなるでしょう。

+

10年後には今ある特定のソフトウェアや技術は生き残っていないかもしれませんが、基礎を作っている技術(OS、ネットワーク、言語)などはたいして変化していないと思います。

+

プログラミング言語については、今あるものの多くが10年後も生き残っているでしょう。きっとRubyもどこかで使われていると思います。新しい言語が登場しているかもしれませんが、現代のものとそれほど違わないでしょう。もし違いがあるとしたらAOP(アスペクト指向プログラミング)を取り込んだ言語でメジャーなものが登場しているかもしれません。まだはっきりとはわかりませんが、アスペクト指向は言語の次の大きな変化になる可能性があります。

+

というわけで、ソフトウェア技術者はそれほど心配する必要はなさそうです。基礎的な技術であるアルゴリズムや、OSやネットワークの原理について押さえておけばそれはきっと10年後も有効でしょう。新しいものも次々登場するでしょうが、表層的な理解ではなく基礎を知っていれば恐れることはありません。

+

ただ、この予測は前提としてこの先10年コンピュータに質的に劇的な変化が起きないということを仮定しています。でも、もしかしたら、明日にも驚異的な発明が行われて世界が変わるかもしれません。そんなことが起きたら、それはそれで楽しそうな気がします。映画『ターミネーター』のような未来になるのでなければ。

+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-061.xhtml b/docs/vol1/xhtml/p-061.xhtml new file mode 100644 index 0000000..87872ec --- /dev/null +++ b/docs/vol1/xhtml/p-061.xhtml @@ -0,0 +1,376 @@ + + + + + +第28章 独習Ruby + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay28 +
+

+初等Ruby講座
+独習Ruby +

+
+

[Linux magazine, 2003年9月号]

+
+

今回は「Rubyを改めてまとめる」というテーマでの解説です。20年経っていてもRubyの基本的な仕様はさほど変わっていませんから、今でも有効な文章ではないでしょうか。もっとも、後半のリファレンスマニュアルページについては、若干見栄えが変わっていますが、それでもそのまま使えます。もちろんriコマンドも健在です。

+

「Ruby開発日記」は、オープンソースコンベンション2003出席記です。大変楽しいカンファレンスでした。このときまでに私が出席したことのあるカンファレンスの中では最大規模(キーノート出席者が1000人超え)で、オープンソースの勢いに感心しました。

+

このときに「サピア・ウォーフ仮説」という名前について教えてもらったと記録されています。「言語が思考に影響を与える」というこの仮説について名前を知らずに使っていました。独自に思いついたと言いたいところですが、おそらくは高校生のときに読んだSF小説『バベル17』から影響を受けたというのが正解でしょう。

+
+
+

連載開始から2年近くが過ぎ、ここのところ「初等」の名にふさわしくないくらい高度な内容が続きましたが、今月は基本に立ち返って「独習」のためのコツなどについて学びましょう。

+
+
+

Rubyの基礎

+
+

そもそもプログラミング言語というものは本を読んだだけでわかるほど簡単なものではありません。ただ、全体像をつかむことで理解が速まるかもしれません。ここでは、復習を兼ねてRubyの全体構成を文法とライブラリに分けて眺めてみましょう。

+
+
+ +

Rubyの文法

+
+

まずはRubyの文法を駆け足で眺めてみましょう。詳細はリファレンスマニュアルや他の書籍に譲ることにして、概観を捉えてください。Rubyの文法を構成する要素はそんなに多くありません。

+
    +
  • オブジェクト式

  • +
  • 代入

  • +
  • 変数

  • +
  • メソッド呼び出し

  • +
  • 演算子式

  • +
  • 定義文

  • +
  • 制御構造

  • +
+

これだけです。この7つの構文要素を押さえれば文法については完璧です。意外と簡単と思いませんか。

+
+
+

オブジェクト式

+
+

オブジェクト式というのはプログラム中で直接オブジェクトを表現するための文法です。オブジェクト式で表現できるオブジェクトは表28.1のとおりです。

+
+

表28.1●Rubyのオブジェクト式

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
オブジェクト式
整数1 1_000
浮動小数点数1.5 3.145e20
文字列"abc\n" 'abc'
正規表現/def+/
配列[1, 2, [3]]
ハッシュ{"a"=>97, "b"=>98}
シンボル:foo
+
+

文字列と正規表現には表28.1に示したものの他に“%”で始まる「任意の区切り文字による表現」と「ヒアドキュメント」が使えます。

+
+
# 任意の区切り文字による表現
+%!def! %{abc} 
+
+# ヒアドキュメント
+<<END
+「<<」の後ろの表現(この場合はEND)までが文字列
+END
+
+
+
+

変数

+
+

変数とは値に対する名札です。Cをよく知っている人は「変数は名札である」ということに気を付けてください。

+

Rubyの変数は6種類あります。変数の種別ごとにそれぞれ名前の先頭に記号が付いているのでひと目で区別できます。

+ +
    +
  • ローカル変数(小文字の英字で始まる)

  • +
  • グローバル変数($で始まる)

  • +
  • インスタンス変数(@で始まる)

  • +
  • クラス変数(@@で始まる)

  • +
  • 定数(大文字の英字で始まる)

  • +
  • 擬似変数(ローカル変数と外見は同じ)

  • +
+

この中で、定数と擬似変数には説明が必要です。定数はクラスに所属する値で一度代入するとその値を変えることはできません。厳密にはやればできますが、怒られます。擬似変数は値は自動的に設定され変更することができないので「擬似」と呼ばれます。Rubyには、

+
+
self nil true false __FILE__ __LINE__
+
+

の6つの擬似変数があります。

+
+
+

代入

+
+

代入とは変数の値を設定するものです。代入されて変数は初めて存在します。

+

代入は左辺(代入先)と右辺(代入元)を「=」でつなぎます。

+
+
a = 123
+
+

はローカル変数aの値を整数123に設定することになります。

+

Rubyでは代入の左辺も右辺も複数指定することができます。ですから、

+
+
a, b = b, a
+
+

とすれば変数abの値を交換することができます。右辺と左辺の数が合わないときには、余った左辺にはnilが代入され、余った右辺の式は単に無視されます。

+

末尾の左辺の前に「*」を付けると余った右辺が配列として代入されます。

+
+
a, b, *c = 1,2,3,4   # cは[3,4]になる
+
+

末尾の右辺の前に「*」を付けるとその式の値を配列として右辺に展開します。

+
+
a, b, c = *[1,2,3]   # a=1, b=2, c=3になる
+
+
+
+

メソッド呼び出し

+
+

メソッド呼び出しは他の言語の関数呼出しに似た形式を使います。

+
+
obj.method(arg1, arg2)
+
+

objmethodというメソッドを呼び出します。引数の周りのかっこはあいまいでないときには省略できます。引数リストは代入の右辺と同様に末尾の式の前に「*」を置くと配列展開されます。

+
+

上の例におけるobjのような「.」の前の式を「レシーバ」と呼びますが、これは省略できます。

+
+
method(arg1, arg2)
+
+

省略した場合のレシーバはself、すなわち現在実行中のメソッドのレシーバが使われます。ですから、Rubyでは、

+
+
print "hello world\n"
+
+

selfprintメソッドの呼び出しであると解釈されます。

+

メソッド呼び出しにはブロックを付加することができます。

+
+
obj.each {|i| ....}
+obj.each do |i| ... end
+
+

ブロックには「{}」で囲まれたものと「do end」で囲まれたものがあり意味は同じです。

+

ブロックを付加されたメソッドはその中から「ブロックを呼び返す」ことができます。呼び返しは値を付けて行われることがあり、その値は「||」の間の変数に代入されます。

+

メソッドの中からブロックを呼び返すためにはyield文を使います。

+
+
yield         # 値を渡さない場合
+yield n       # 1つの値を渡す場合
+yield n1,n2   # 複数の値を渡す場合
+
+
+
+

演算子式

+
+

Rubyの演算子式のほとんどは実際にはメソッド呼び出しです。つまり、

+
+
1 + 2
+
+

は「1オブジェクトの+メソッドを2を引数として呼び出す」という意味になります。演算子式の中には、

+
+
obj[0]
+
+

のような変則的なものもあります。これは「obj[]メソッドを0を引数として呼び出す」ことを意味します。

+

演算子の中には文法として意味が決まっていて変更することができないものもあります。たとえば「||」演算子などは再定義できません。

+
+
+

定義文

+
+

class文、module文、def文、undef文、alias文は定義文と呼ばれます。それぞれクラス、モジュール、メソッドの定義、メソッド定義の取り消し、別名定義の働きがあります。

+
+
# クラス定義
+class Foo < Object    # Objectを継承するFooを定義
+  ....
+end
+
+# モジュール定義
+module Bar            # モジュールBarを定義
+  include Mod         # モジュールModの機能を取り込む
+  ....
+end
+
+# メソッド定義
+def foo(arg1, *rest)  # fooを定義。*は代入左辺と同じ
+  ....
+end
+
+# メソッドの取り消し
+undef bar             # barメソッドを未定義に
+
+# 別名定義
+alias new old         # newをoldの別名に
+
+
+

定義文のバリエーションとして、特定のオブジェクトだけに作用する特異クラス定義と特異メソッド定義があります。これらの文を使うとある特定のオブジェクトにだけ機能や属性を追加できます。

+
+
# 特異クラス定義
+class << obj          # ボディの定義がobjにだけ作用
+  ....
+end
+
+# 特異メソッド定義
+def obj.bar(arg)      # objだけにメソッドbarを定義
+  ....
+end
+
+
+
+

制御構造

+
+

条件判断のifunless, ループのwhile, until, for, ループ脱出のbreak, redo, next, retry, メソッド終了のreturn, 文のグループ化と例外処理を行うbeginが制御構造です。

+

Rubyでは文法組み込みの制御構造だけでなく、メソッドとブロックを使って実行の流れを制御することもあります。たとえば無限ループを構成するloopメソッドや大域ジャンプを行うcatchメソッドとthrowメソッドなどがあります。

+
+
# 無限ループ
+loop { ... }
+
+# 大域ジャンプ
+catch(:foo) {
+   ...
+   throw(:foo)        # 対応するcatchまでジャンプ
+   ...
+}
+
+
+
+ +

Rubyのライブラリ

+
+

ここまでかなり駆け足で説明してきましたが、Rubyの文法はこれで全部です。ここまでを理解していれば、どんなRubyのプログラムも「読む」ことができます。たとえば、

+
+
n = Integer(ARGV[0])
+
+

を見ると、

+
    +
  • 定数ARGV[]メソッドを0を引数として呼び出し

  • +
  • その返ってきた値を引数にしてIntegerメソッドを呼び出し

  • +
  • Integerメソッドが返した値をローカル変数nに代入する

  • +
+

という意味だとわかります。しかし、このプログラムの意味を「理解」するためには、[]Integerなどのメソッドがどのような動作をして、どんな値を返すかを知らなければなりません。

+

これらを実現しているのがRubyのライブラリです。このライブラリを理解して初めてRubyプログラムを理解することができます。

+

ライブラリはRubyインタプリタに組み込まれているものだけで、クラスを182個、モジュールを13個も含みます。その他のライブラリを考えると数え切れません。肝心なのは全部覚えることではなくて、どうやって調べればよいかを学ぶことです。

+
+
+

リファレンスマニュアル

+
+

ライブラリを調べるためによいリファレンスマニュアルがオンラインで参照できます。

+
    +
  • http://www.ruby-lang.org/ja/man/

  • +
+

ここでは日々進化するRubyに追随して更新されるリファレンスが提供されています(図28.1)。

+
+ +
+ fig2801 +
+

図28.1●リファレンスマニュアル

+
+

たとえば、トップから「組み込みクラス/モジュール/例外クラス」にジャンプし、そこから「String」に飛ぶとStringクラスの詳細な解説を見ることができます。

+ +

しかし、いくら時代は「ブロードバンドで常時接続」になりつつあるとはいえ、いつもインターネットへのアクセスがあるとは限りません。それに上記のオンラインのリファレンスマニュアルは肝心のメソッドを調べるのに、そのクラスのページを開いてメソッドを探して、と少々面倒です。

+

そこで、オフラインで参照できるマニュアル用ツールが用意されています。それはri(英語版)とReFe(日本語版)です。

+

Dave Thomasによるriの出力例は図28.2のようになります。

+
+
+
------------------------------------------------------------- Array#each
+    arr.each {| item | block } -> arr
+------------------------------------------------------------------------
+    Calls block once for each element in arr, passing that element as a
+    parameter.
+       a = [ "a", "b", "c" ]
+       a.each {|x| print x, " -- " }
+    produces:
+       a -- b -- c --
+
+

図28.2●ri

+
+

riの入手先は以下のとおりです。

+
    +
  • http://www.pragmaticprogrammer.com/ruby/downloads/ri.html

  • +
+

Dave Thomasはriの(というか元になったProgramming Rubyの)1.8対応版を執筆中だそうです。

+

一方、ReFeRaccなどの作者としても有名な青木さんによる日本語ドキュメントツールです。ReFeの出力例は図28.3のようになります。

+
+
+
Array#each
+--- each {|item| .... }
+
+    各要素に対してブロックを評価します。self を返します。
+
+    例:
+
+        # 1、2、3 が順番に表示される
+        [1, 2, 3].each do |i|
+          puts i
+        end
+
+

図28.3●refe

+
+

ReFeの入手先は以下のとおりです。

+
    +
  • http://www.loveruby.net/ja/prog/refe.html

  • +
+

riReFeはドキュメントの言語以外にもいくつかの点が違います。

+ +

これらの違いを踏まえて両方を使い分けるのがよいのではないでしょうか。

+
+
+

マニュアルの参照の仕方

+
+

Rubyのリファレンスを参照していて、一番困るのはメソッドの説明が見つけにくいことです。たとえば、Arrayクラスのeach_with_indexメソッドの説明が読みたいと思って、ReFeを使っても、

+
+
not match: Array#each_with_index
+
+

と表示できません。

+

それはそのようなメソッドがスーパークラスかインクルードされているモジュールで定義されていることを意味しています。riReFeは「そのクラスで定義されているメソッド」しか解説してくれないからです。

+

あるクラスのスーパークラスまたはインクルードされているモジュールはancestorsメソッドで得ることができます。たとえばArrayクラスについては、

+
+
[Array, Enumerable, Object, Kernel]
+
+

であることがわかります。これらのクラスやモジュールを順に調べていけば、Enumerableeach_with_indexが定義されていることがわかると思います。

+
+

また、メソッド名だけでriReFeを起動するとそのメソッドを持つクラスの一覧を表示してくれるので役に立ちます。

+
+
+

まとめ

+
+

今月は久しぶりにRubyの基礎を概観してみました。これからRubyを勉強したいという人に役立てば幸いです。

+

Rubyを身につけるには実際にコードを書いたり、読んだりするのが一番です。文法を押さえたらコードを探検しに行くのはどうでしょう。RAAにはRubyのプログラムがたくさんあります。

+
    +
  • http://raa.ruby-lang.org/

  • +
+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-062.xhtml b/docs/vol1/xhtml/p-062.xhtml new file mode 100644 index 0000000..a41d171 --- /dev/null +++ b/docs/vol1/xhtml/p-062.xhtml @@ -0,0 +1,114 @@ + + + + + +第28章 独習Ruby + + + + +

Matz Essays Volume 1

+ + +
+

◆ Ruby開発日記 ◆ オープンソースコンベンション2003

+
+

7月6日から11日まで、オレゴン州ポートランドでオープンソースコンベンション2003が開かれました。

+

オープンソースコンベンションはO’Reillyが毎年開催しているオープンソース関連の最大のイベントです。このイベントは当初Perl Conferenceとして始まりましたが、次第に他のオープンソースソフトウェアも取り込んで、ここ5年ほどはオープンソースコンベンション(OSCON)として定着しています。

+

さて、Ruby関連の書籍はO’Reillyからは1冊しか出版されていないせいか、あるいはその1冊もさして売り上げに貢献していないせいか、それとも他の理由かわかりませんが、とにかく昨年まではRubyはOSCONからはお呼びがかかりませんでした。

+

ところが今年はRubyトラックが用意され複数のRubyの発表がありました。これがO’Reillyの方針転換か、Ruby人口の増加と知名度のアップが原因かはよくわかりませんが。私も今回Rubyの作者として招待されました。久しぶりの海外遠征です。

+

OSCONのプログラムからRuby関連のものを抜き出したものを表28.2に示します。

+
+

表28.2●Ruby Conferenceプログラム

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
7/808:45-17:15Ruby in a Day 1日チュートリアル
7/1010:45-11:30The Power and Philosophy of Ruby
7/1011:30-12:15Ruby for Perl Programmers
7/1013:45-14:30Take a FreeRIDE with Ruby
7/1014:30-15:15Data-Driven Classes in Ruby
7/1016:30-17:15Understanding Ruby’s Object Model
7/1017:15-18:00What I Did on My Summer Vacation
+
+

このリストからもわかるように7月10日(木)は「Rubyの日」で、一日中何かしらRuby関連のプログラムが開かれていました。

+

プログラムが行われた部屋は50名程度でいっぱいになってしまう小さな部屋だったのですが、中には立ち見が出るほど盛況だったものもありました。Rubyがアメリカのオープンソース界でも注目を集めつつあることが実感できました。

+ +

それでは、プログラムの内容を1つずつ見てみましょう。

+
+
Ruby in a Day
+

Dave Thomasがあちこちでやっているチュートリアルです。ユーザーの手元のコンピュータで実際にプログラムを作って学ぶ実践的なチュートリアルでなかなか好評だったようです。私は会場にこの日の夕方に着いたので出席できませんでした。

+
+
+
The Power and Philosophy of Ruby
+

「Rubyの日」の先頭は私による発表です。「言語が人間の思考に影響を与える」という仮説に従って、プログラマーによりよい影響を与えるプログラミング言語とはどのようなものかということについて論じました。発表資料は、

+
    +
  • http://www.rubyist.net/~matz/slides/oscon2003/index.html

  • +
+

にあります。発表後、その仮説は「Sapir-Whorf Hypotheses」と呼ぶのだと教えてもらいました。私が考え出した仮説と同じことを70年以上も前に考えた人がいるとは驚きです。

+
+
+
Ruby for Perl Programmers
+

Phil TomsonによるPerlユーザーのためのRuby入門です。ポートランド在住の彼は地元のPerlユーザーグループで同じ発表を行ったのだそうです。「みんな優しかったよ。殺されないですんだし」とは彼の弁です。

+
+
+
Take a FreeRIDE with Ruby
+

Rich Kilmerが数年前から作っているIDE(統合開発環境)FreeRIDEとそのベースになっているアーキテクチャFreeBASEの解説です。彼はDARPAから仕事を受けているベンチャー企業の経営者で、JavaをRubyに置き換えて生産性を実現していると言っていました。

+
+
+
Data-Driven Classes in Ruby
+

Michael GrangerとDavid McCorkhillによるクラス設計の話。私はこのプログラムには用事で参加できませんでした。彼らはこの2月にrubycrafters.comというRubyをベースにした企業を立ち上げたのだそうです。やるな。

+
+
+
Understanding Ruby’s Object Model
+

Rubyのオブジェクトモデルについて。Rubyのソースコード中、object.cにある図をきちんと説明したという感じでしょうか。

+

発表したChris Pineは最近子供が産まれたそうなのですが、「女の子だったらRubyと名付けたのに……」と言っていました。実際は男の子だったので「C」という名前にしたそうです。本気?

+
+
+ +
What I Did on My Summer Vacation
+

Dave Thomasが昨年の夏休みに奥さんの働くNGOのためにWebシステムを構築したという話で、昨年のRuby Conferenceでの発表とほぼ同内容です。結局夏休みはなかったということなんでしょうか。

+

このWebシステムというのが、教育者と生徒が教材を管理するというもので、長年の歴史的事情により発注も支払いも教材管理も複雑怪奇で、しかもそのビジネスルールをそのままWeb化する必要があったという厳しい条件があったのにもかかわらず、Rubyを使ったので6週間で完成したという話でした。

+

会場から「Pythonでもできるんじゃないか」という質問がありましたが、「できるかもしれないが、私には無理だった」と答えていました。確かにPythonが得意な人とRubyが得意な人と割とはっきり分かれる傾向があるようです。Dave Thomasは「猫好きと犬好きみたいなもの」と形容していました。

+

OSCONはもちろんRuby関連以外にもPerlやPython, Apache, PHPなど盛りだくさんでした。私としてはPerlの作者Larry Wallと再会できたことや、Pythonの作者Guido van Rossumと会えたことなどが貴重な経験でした。もちろん良好な関係でしたとも。一緒に夕食を食べたりもしましたしね。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-063.xhtml b/docs/vol1/xhtml/p-063.xhtml new file mode 100644 index 0000000..7a29ed2 --- /dev/null +++ b/docs/vol1/xhtml/p-063.xhtml @@ -0,0 +1,292 @@ + + + + + +第29章 再入門オブジェクト指向 + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay29 +
+

+初等Ruby講座
+再入門オブジェクト指向 +

+
+

[Linux magazine, 2003年10月号]

+
+

「オブジェクト指向」を改めて解説しています。背景としては、この時代はオブジェクト指向が大変注目されていた反面、あまり品質のよろしくない記事があふれていて、やや混乱が発生していたことがあります。この記事ではRubyをベースにしたオブジェクト指向解説を行っています。あくまでもRubyの話なので、たとえば「仕様の継承と実装の継承の区別」とか「継承と型の関係」などについて触れられていないのが、現代の視点からは不満がありますね。

+

また、後半ではirbを紹介しています。irbは現代でも活用されていますし、当時と比較すると補完機能などについても大変進歩していますが、基本的な部分は20年前から変わらず存在しています。REPLをベースにした体験の良さを一般に示したのはirbの功績ではないでしょうか。

+

「Ruby開発日記」では、LL Saturdayの参加記録です。当日まさかの台風直撃で、開催が危ぶまれるほどでしたが、楽しいイベントでした。帰りの飛行機が飛んでほんとに良かったです。

+
+
+

先月は『独習Ruby』と題して、Ruby全体の概観と独習のコツを復習してみましたが、いかがでしたでしょうか。今月はRubyの基礎の基礎であり、意外と理解が難しいと思われている「オブジェクト指向」について学びます。

+
+
+

オブジェクト指向は難しい?

+
+

オブジェクト指向が難しいと感じている人に、私が解説するというのは至難の業です。なんといっても最初にオブジェクト指向について触れたはるか昔の高校時代から今に至るまで、私はオブジェクト指向が難しいなどと一度も思ったことがないからです。自分が難しいと思っていないのに、難しいと感じている人にわかる説明をするのは困難ですよね。

+

どうやらオブジェクト指向には、わかる人にはすんなりわかっても、わからない人にはさっぱりわからないという傾向があるようです。そこで、いろいろ調査してみました。Web日記へのツッコミや周辺の人々の経験から、オブジェクト指向の学習についていくつかのことがわかってきました。

+
+

まず、第1に「オブジェクト指向は難しい」というイメージが先行しているということです。確かにオブジェクト指向の解説には、オブジェクト、クラス、メソッドなどどこかで聞いたようなカタカナ言葉が大量に発生します。しかも、よそで聞くのとでは意味が違うようです。いまさら別の訳語を用意するわけにもいきませんので慣れるしかないのですが、改めてまとめてみたいと思います。

+

第2に、実用的でない解説が横行していることがあります。たとえば継承というとすぐに「哺乳類」は「動物」のサブクラスだとかいうような例題が登場します。しかし、実際のプログラミングで継承を使うときに、哺乳類だの脊椎動物だのと知識表現的な使い方をすることはまずありません。使われない例題で解説するようでは理解できない人が出ても当然でしょう。私自身もそういう本を書いているので同罪ではあるのですが。

+

第3に、オブジェクト指向言語にはいろいろあって、微妙に流儀や定義が違ううえ、説明するほうも自分の流儀に固執するので、複数の書籍がそれぞれ違うことが書いてある(ように読める)ので、混乱を招いていることがあるようです。よく考えると結局は似たようなことが書いてあるわけですが、初心者のうちは表面的な違いが重要になりますから。

+

第4に、オブジェクト指向という考え方は、データの構造のようなものを表現する考えであるともいえます。コンピュータの処理を手続きだけ考える人は、このデータ構造という部分で引っかかるようです。

+

しかし、私はオブジェクト指向は難しい考え方ではなく、先入観を取り払って適切に学習すればすぐに身につく考え方だと思います。今回はその辺に挑戦してみましょう。

+
+
+

わかるオブジェクト指向

+
+

まずはいくつかの基本的な単語を学びましょう。オブジェクト指向ってのは、どういうわけだかよく使われる単語を再利用するクセがありますが、オブジェクト指向の文脈ではこうなんだと思って覚えてください。

+
+

オブジェクト

+

プログラムに登場するさまざまな「概念」のうち、データとして直接取り扱えるものがオブジェクトです。たとえば、数や文字列はオブジェクトです。この「データをオブジェクトとして捉える」というのがオブジェクト指向の最大公約数だと思います。その他のものはみな「あると便利なもの」でしょう。

+

メソッド

+

オブジェクト指向の重要な要素として「オブジェクトは自分自身にふさわしい処理を知っている」というものがあります。この自分自身にふさわしい処理のことを「メソッド」と呼びます。

+

オブジェクトのユーザーは処理の詳細については何も知らなくても、メソッドを通じてオブジェクトを操作できます。むしろ、オブジェクトの内部構造や処理手順などについては知らないほうがよいとされています。

+
+

クラス

+

人間は分類するのが好きです。あの生き物とこの生き物は同じ特徴を持つから「犬」という同じ種類の生き物として分類しようとかいうような活動は、はるかな昔から行われてきました。

+

オブジェクト指向においても、同じ特徴を持つオブジェクトをまとめた「クラス」というものがあります。クラスはオブジェクトの特徴を決めるひな形になります。たとえば「文字列クラス」は個々の文字列が持ついろいろな特徴をまとめたものです。そしてすべての文字列オブジェクトは、文字列クラスに所属します。

+

あるオブジェクトがどんな構造を持つか、どんなメソッドを持つかはクラスによって決まります。

+

実はクラスはオブジェクト指向に必須の概念ではないのですが、非常に便利なのでほとんどのオブジェクト指向言語で採用されています。

+

オブジェクトのことを「インスタンス」と呼ぶ場合がありますが、これはオブジェクトがクラスに属していることを強調する表現です。

+

継承

+

「継承」というのは、あるクラスの機能を引き継いで新しいクラスを作ることです。既存のクラスの機能をちょっとだけ変えたものを作るときに便利です。継承はオブジェクト指向の解説では必ず強調される機能ですが、実際にはそんなに多用されるわけではありません。次第に慣れていけばよいでしょう。

+
+
+
+

できるオブジェクト指向

+
+

さて、「オブジェクト」「メソッド」「クラス」「継承」などの基本単語について定義しましたが、これだけで、オブジェクト指向の考え方が身につくとは思えません。実際にオブジェクト指向を身につけるためにはやはり自分でやってみることが重要のようです。

+

自分で小さなプログラムを書き、実際に動かしてみることで理解が進むでしょう。その点Rubyは、

+
    +
  • 純粋オブジェクト指向言語であらゆるものがオブジェクト

  • +
  • インタプリタですぐに実行できる

  • +
+

などの特徴がありますから、この目的にぴったりです。また、irbというプログラムを使えば対話形式でプログラムできます。

+

Rubyがインストールされていれば、irbも同時にインストールされているはずです。

+
+
% irb
+irb(main):001:0>
+
+

irbのプロンプトの意味は以下のとおりです。

+
+ +
+ fig2901 +
+

図29.1●irbのプロンプトの意味

+
+
+

インデントレベルはifwhileなどのネストのレベルを示します。行番号は今までirbに入力した行数を示します。そしてselfは現在のselfの値を表示します。

+

readlineライブラリがインストールされていれば、bashなどと同様に行編集が使えるはずです。

+

では、irbを使って実行しながら、オブジェクト指向を実感してみましょう。

+
+
irb(main):001:0> a = "foo"
+=> "foo"
+
+

文字列 "foo" を変数aに代入しました。変数aが指しているのは文字列というオブジェクトですから、メソッドを呼び出すことができます。

+
+
irb(main):002:0> a.size
+=> 3
+
+

この文は「aという変数が指すオブジェクトのsizeメソッドを呼び出す」という意味です。「.」の左側がメソッド呼び出しの対象になります。

+

文字列オブジェクトはStringクラスに属するオブジェクトで、Stringクラスのsizeメソッドは文字列の長さを返します。変数aが指す文字列は "foo" ですから、その長さは3ですよね。

+

オブジェクトは文字列だけじゃないので、今度は別の種類のオブジェクトを試してみましょう。同じ変数aに配列を代入してみます。

+
+
irb(main):003:0> a = [1,2]
+=> [1, 2]
+
+

変数aは今度は配列を指すようになりました。今までaから指されていた文字列は誰からも参照されなくなります。誰からも参照されなくなったオブジェクトはシステムが自動的に後片付けをしてくれますので、忘れて結構です。

+

さて、配列に対してもメソッドを呼び出しましょう。

+
+
irb(main):004:0> a.size
+=> 2
+
+
+

文字列のときと同じく、sizeメソッドを呼び出しました。この配列は2要素の配列ですからサイズは2です。「a.size」というプログラムが文字列の長さを求めたときとまったく同じであることに注目してください。文字列の長さを求める処理と配列の長さを求める処理は内部的には異なっているかもしれませんが、正しい処理方法が自動的に選択されています。

+

このように同じ名前のメソッドを呼び出しても「.」の左側の式のオブジェクト種別に応じて、適切な処理が選択されることを「動的結合」と呼びます。また、同じ式の振る舞いが実行時に異なることを「ポリモルフィズム」とか「多態」と呼びます。

+

あるオブジェクトのクラスを知るためにはclassメソッドを使います。

+
+
irb(main):005:0> a.class
+=> Array
+irb(main):006:0> "a".class
+=> String
+irb(main):007:0> 1.class
+=> Fixnum
+
+

FixnumというのはFixed-size Numberの略で、コンピュータが高速に取り扱うことができる範囲(32ビットCPUでは231−1〜−231)の整数のことです。この範囲を超える整数はBignumというクラスに所属します。

+

FixnumクラスはIntegerクラスを継承しています。Integerクラスは整数に共通の性質を定義しているクラスで、FixnumBignumがこれを継承しています。このようなクラスをFixnumの「スーパークラス」と呼びます。Integerクラスはさらに数値に共通の性質を定義しているNumericクラスを継承し、NumericクラスはRubyのあらゆるオブジェクトに共通の性質を定義しているObjectクラスを定義しています。

+

クラスのスーパークラスはsuperclassメソッドで得られます。

+
+
irb(main):005:0> Fixnum.superclass
+=> Integer
+irb(main):005:0> Integer.superclass
+=> Numeric
+irb(main):005:0> Numeric.superclass
+=> Object
+irb(main):005:0> Object.superclass
+=> nil
+
+

Objectはすべてのクラスの頂点ですから、スーパークラスを持ちません。ですから、superclassメソッドはnilを返します。nilとは「何もない」という意味です。

+
+
+ +

irb補完計画

+
+

あ、そうそう。irbを使うに当たって便利なおまじないをご紹介しましょう。ホームディレクトリにリスト29.1に示す内容の .irbrcというファイルを用意しておきます。

+
+

リスト29.1●.irbrc

+
require "irb/completion"
+
+
+

この設定を行うと、TABキーを押すことで変数名やクラス名、メソッド名などの補完が使えるようになります。一度使ってみればわかりますが、とてつもなく便利です。

+
+
irb
+irb(main):001:0> foo = "foo"
+=> "foo"
+irb(main):002:0> fo<ここで2度TAB>
+foo     for     fork    format
+
+

今まで入力した部分で候補が1つだけに絞れる場合にはTAB一度だけで補完してくれます。そうでない場合には2度TABを押すとリストを表示します。ここでは "fo" で始まる変数とメソッドをリストしています。

+
+
irb(main):002:0> foo.g<ここで2度TAB>
+foo.grep   foo.gsub   foo.gsub!
+
+

irbの偉いところは、TABを押した地点の左側を解析して、fooという変数が指しているオブジェクトが文字列であれば、文字列のメソッドを一覧してくれることです。上の例では、文字列メソッドのうち "g" で始まるメソッドの一覧を表示しています。もちろん変数fooの中身が他のオブジェクトに変わればそれにも追随してくれます。

+
+
+

クラスの世界

+
+

さて、再びオブジェクト指向のおさらいに戻りましょう。あまり大規模でないプログラムならRubyが提供しているクラスとオブジェクトを使うだけで十分です。

+

Rubyの提供しているクラスは主要なものだけでも、

+ +

があります。これらを単に組み合わせるだけでも強力なプログラムが実現できます。

+

しかし、プログラムが少々複雑になってくると、データ中心の構成にしたくなります。このようなデータ型を定義するためには、自分自身でクラスを作ればよいのです。Rubyでは組み込みのクラスも、自分の作ったクラスも区別がありません。

+

では、実際にクラスを作ってみましょう。

+

例題として簡単なカウンタクラスを作ります。カウンタクラス(名前はCounterにしましょう)は、以下の機能を持ちます。

+
    +
  • 初期化(初期値はゼロ)

  • +
  • カウント値を増加

  • +
  • 現在のカウント値を報告

  • +
+

実際のカウンタプログラム(counter.rb)をリスト29.2に示します。

+
+

リスト29.2●counter.rb

+
 1  class Counter
+ 2    def initialize
+ 3      @count = 0
+ 4    end
+ 5    def count_up
+ 6      @count += 1
+ 7    end
+ 8    def count
+ 9      return @count
+10    end
+11  end
+
+
+

簡単なプログラムではありますが、最初ですからていねいに見てみましょう。

+

1行目でクラスの定義を始めています。「class」の後ろに続くのがクラス名になります。Rubyではクラス名の最初の文字を大文字にする必要があります。

+

def」で始まるのがメソッド定義です。「class」に対応する「end」の範囲内の「def」で、そのクラスのメソッドが定義されます。ここではinitialize, count_up, countの3つのメソッドを定義しています。

+

initializeは初期化用のメソッドです。Rubyではオブジェクトが新しく作られたときに、initializeという名前のメソッドが自動的に呼ばれます。initializeメソッドはカウント値(@count)をゼロに初期化しています。「@」で始まる変数は「インスタンス変数」といってオブジェクトごとの状態を保持します。

+ +

count_upメソッドはカウント値を増加させるメソッドです。メソッドはカウント値に指定された数を足すという単純なものです。

+

countメソッドは現在のカウント値を返すメソッドです。@countの値をreturnで指定しています。

+

では、このプログラムをirbを使って試してみましょう。まず、loadでカウンタプログラムを読み込みます。

+
+
irb(main):001:0> load "counter.rb"
+=> true
+
+

次に実験用にカウンタオブジェクトを1つ作ってみましょう。オブジェクトを作るにはクラスのnewメソッドを呼び出します。newメソッドの内部でinitializeメソッドが呼ばれて初期化が行われます。

+
+
irb(main):002:0> c = Counter.new
+=> #<Counter:0x40290cb8 @count=0>
+
+

では、カウンタを増やしてみましょう。

+
+
irb(main):003:0> c.count_up
+=> 1
+
+

ちゃんと増えたかどうか確認します。

+
+
irb(main):004:0> c.count
+=> 1
+
+

できているようですね。

+

このようにirbを使えば、オブジェクト指向機能を始めとしたRubyのいろいろな機能を手軽に試してみることができます。「習うより慣れろ」とも言いますし、実際にプログラムを動かしてみることで多くのことが学べるのではないでしょうか。

+
+
+

継承の実際

+
+

さて、上記のプログラムでは特にスーパークラスを指定しませんでした。あのプログラムのCounterクラスのスーパークラスはいったいなんになるのでしょう。これも試してみましょう。

+
+
irb(main):007:0> Counter.superclass
+=> Object
+
+

何も指定しなかった場合のスーパークラスはObjectクラスなんですね。

+
+

では今度はスーパークラスを指定してクラスを作ってみましょう。先ほどのCounterクラスは増加のみでしたが、減少もできるカウンタBiCounterを作ります(リスト29.3)。

+
+

リスト29.3●bicount.rb

+
require 'counter.rb'
+class BiCounter < Counter
+  def count_down
+    @count -= 1
+  end
+end
+
+
+

class文でクラス名の後ろに「<」に続けてクラスを指定すると、そのクラスがスーパークラスになります。省略した場合は先ほど見たようにスーパークラスはObjectになります。BiCounterクラスはCounterクラスのすべてのメソッドを受け継いだうえにcount_downメソッドが追加されます。

+

これも試してみます。

+
+
irb(main):008:0> load "bicount.rb"
+=> true
+irb(main):009:0> b = BiCounter.new
+=> #<BiCounter:0x40278bf4 @count=0>
+irb(main):010:0> b.count_up
+=> 1
+irb(main):011:0> b.count
+=> 1
+irb(main):012:0> b.count_down
+=> 0
+irb(main):013:0> b.count
+=> 0
+
+

よしよし。

+
+
+

まとめ

+
+

今月はオブジェクト指向の紹介を行いました。かなり駆け足でしたが、オブジェクト指向プログラミングのきっかけになれば幸いです。また、対話型Rubyインタプリタirbも紹介しました。irbも大変便利なのでぜひ使ってみてください。

+
+
+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-064.xhtml b/docs/vol1/xhtml/p-064.xhtml new file mode 100644 index 0000000..09b326c --- /dev/null +++ b/docs/vol1/xhtml/p-064.xhtml @@ -0,0 +1,81 @@ + + + + + +第29章 再入門オブジェクト指向 + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ LL Saturday

+
+

8月9日、台風10号が接近しつつある中、東京渋谷にある法学館5号館で、LL Saturdayと銘打ったイベントが開催されました。

+

LLとはLightweight Languageの略で、軽量級プログラミング言語として最近注目されているPerl, PHP, Python, Ruby(アルファベット順)について、それぞれの第一人者が解説するというような感じのイベントです。

+

軽量級といっても、言語仕様が小さいとか、プログラムのサイズが小さいとかいう意味ではありません。また、実行時の負荷が軽いという意味でもありません。

+

Lightweight Languageというのはもともとは一昨年からMITで開催されているイベントの名前で、スクリプト言語やLisp, MLなどの言語をまとめるために考え出された名称です。

+

主催者としては当初はDynamic Languageと呼びたかったらしいのですが、それではMLのような型推論を行う静的型の言語が含まれなくなるなど、いろいろ悩んだ末に付けた名前のようです。

+

昨年、私は2回目になるLL2に出席してきました。LL1ではLightweight Languageの定義については議論されなかったようでしたので、そこで「脳力の消費量が少ない言語」という「俺定義」を披露してきました。もちろんこれがLLの公式な定義になるわけがないのですが、LL2の出席者にはそれなりに前向きに受け取ってもらえたようです。

+

さて、そんなLightweight Languageについて日本でも語ろうと企画が始まったLL Saturdayですが、定員200名で出席者の募集を行ったところわずか3日でいっぱいになるという「事件」が発生しました。それなりに注目を浴びていたようです。

+

当日は悪天候の中、150名を超す人たちが会場に集まりました。スタッフによるとこの条件での150名はいいほうだそうです。会場ではアスキーやオライリージャパンを始めとする出版社がLL関連書籍を陳列・販売していました。会場だけの特別割引もあり、結構売れていたようです。

+

肝心のプログラムですが、

+
    +
  • Language Update

  • +
  • オブジェクト指向とLL

  • +
  • 君ならどう書く

  • +
+

という3本立てでした。

+
+
Language Update
+

各言語の最近の話題を解説していました。Perlは5.8.0のリリース間近、PHPはPHP 5のリリースが年内に予定されていて、Pythonは2.3がリリースされたばかり、Rubyも1.8.0のリリース直後ということで、それぞれの最新バージョンの動向などが紹介されました。

+

Perl 6の話題も期待されていたと思うのですが、このことに関してはPonie(Perl 6のインタプリタエンジンであるParrot上に再実装されるPerl 5)くらいしか紹介されませんでした。

+
+

それぞれの新しい機能が紹介される中、言語機能としてはお互いに参考にしあって次第に近づいてきているような印象を受けました。特にPerl 6やPythonの新バージョンはRubyが持っていた機能の同等品を取り込んできて(Rubyを参考にしたとは限りませんが)、ますます差が近づいているようでした。

+

将来はParrotのような「共通エンジン」が登場して、言語の違いは文法の違いだけになってしまうのかもしれません。

+
+
+
オブジェクト指向とLL
+

それぞれの言語のオブジェクト指向機能について紹介したのちに、「Lightweight Languageにおけるオブジェクト指向」などについて語るパネルが行われました。

+

パネルのほうは少々発散気味でしたが、Python担当の磯さんが「手順としてプログラムをとらえる人はオブジェクト指向とか構造体とかが理解しにくい」という発言が印象的でした。

+
+
+
君ならどう書く
+

同じテーマについて各言語で独立にプログラムを書くことで、それぞれの言語の比較をしようという試みです。

+

テーマは3つあって

+
    +
  • RSSビューア

  • +
  • Apacheログ解析

  • +
  • ネットワークじゃんけん

  • +
+

でした。今回取り上げた言語はそれぞれライブラリが充実してきていますので、この程度のタスクは割と簡単に実現できてしまいます。

+

しかし、Lightweight Languageというだけあって、皆さん、

+
    +
  • 会場でデバッグ

  • +
  • プレゼン中にコード変更

  • +
+

など、驚くようなフットワークの軽さを見せつけてくださいました。会場のディスプレイの見にくさとあいまって、聴衆を混乱に陥れてくださいました。いや、冗談です。

+

このセッションで印象的だったのはきたさんのプレゼンです。彼が書いたRubyのコードからviのエディタコマンドでend行を消すと、あら不思議、なんとなくPythonのコードに見えてくるじゃありませんか。行末にコロンを追加したら本当に動くかもしれません。

+

言語設計者としてはPythonとRubyは相当違うと日頃から思っているのですが、日常的な使用では実は大した差なんてないのかもしれません。

+

LL Saturday全体を通して振り替えると、ディスプレイの解像度の低さと、相性の悪いPCによるトラブルが続出したことを除けば、全体に質の高いイベントだったのではないでしょうか。

+

なお、じゃんけん対決による最強Lightweight Language決定戦ではRubyが圧勝であったことをご報告しておきます。単なる乱数の結果ではあるんですが。

+
+

さて、このようにして終了したLL Saturdayだったのですが、私はイベント終了後、懇親会の誘いも断って、松江に帰るためにそそくさと羽田空港に向かって移動したのですが、なんと私が乗るはずだった出雲空港行きは天候事情のため欠航になってしまいました。

+

こんなことなら懇親会に出ればよかったです。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-065.xhtml b/docs/vol1/xhtml/p-065.xhtml new file mode 100644 index 0000000..ab98b30 --- /dev/null +++ b/docs/vol1/xhtml/p-065.xhtml @@ -0,0 +1,394 @@ + + + + + +第30章 ここからのRuby + + + + +

Matz Essays Volume 1

+ + +
+ +Matz Essay30 +
+

+初等Ruby講座
+最終回: ここからのRuby +

+
+

[Linux magazine, 2003年11月号]

+
+

「初等Ruby講座」連載の最終回です。「ここからのRuby」というタイトルで、入門が終わった人がRubyを使って今後何にチャレンジするかということについて解説しています。ここでWebが強調されないのが2003年という感じですね。「Rubyと言えばWeb」というイメージがありますが、そのイメージを生み出したRuby on Railsが誕生したのは2004年です。私自身がWebが得意でないことも反映していると思います。

+

あと、プログラミング言語の解説でつまづきがちな変数の認識の仕方としての「箱モデル」と「名札モデル」についても解説しています。言語によっても向いたモデルが異なるのが難しいところですね。

+

「Ruby開発日記」では、ユーザビリティ原則からプログラミング言語の設計について考えています。「もうRubyも10歳だし、そろそろ超えるような言語が(日本から)出てもよい頃だ」と書いていますが、20年経ってもRubyを超える言語が日本から出てくる気配がありません。さみしい。日本人はいろいろ恵まれていると思うのですが、言語をデザインしたいという「人種」は私が思う以上に希少なようです。

+
+
+

さて、長らく連載してきた『初等Ruby講座』もとうとう今回で最終回です。「私に入門は無理じゃないか」という不安を振り切っての連載でしたが、案の定少々難しすぎたように思います。今回は最終回に当たって過去を振り返り、それからRubyとともに歩む未来を見つめます。

+
+
+

対象となる読者の気持ち

+
+

入門記事を書くに当たって一番難しいのは、読者の気持ちを理解することです。以前別のところでも書きましたが、解説記事を書けるほど知識を持った人は、何もわからなかった初心者のときの気持ちを忘れているものです。説明するのに必要な知識を得る過程で失っていくものもあるんですね。

+
+

ご多分に漏れず、私も初心者のときの気持ちを忘れてしまっています。私がプログラミングを始めたのはもう20年以上昔ですから、記憶があいまいなのもしかたないかもしれません。

+

最近、Rubyのメーリングリストで「変数のたとえ話」について議論が行われました。これはつまり、初心者向けの本の中で変数について説明するときにどのようなたとえを使うのが望ましいか、というものです。

+

議論と発端となったのは、ある本で「変数はオブジェクトが入る箱のようなもの」という説明がしてあったことにあります(図30.1)。

+
+ +
+ fig3001 +
+

図30.1●変数は箱

+
+

箱にはそれぞれ名前が書いてあって、それが変数名になるわけですね。しかし、Rubyでは複数の変数が1つのオブジェクトを「格納する」ことがあります。

+
+
a = "abc"
+b = a       # a とb は同じオブジェクト
+a[0] = "A"  # a を"Abc"に変えると
+puts b      # b も変わる
+
+

現実世界ではある「モノ」が2つの箱に同時に入ることはありえませんから、このたとえは割と早い時点で破綻しそうです。

+

このたとえは変数を箱だと考えるものですから「変数=箱モデル」、略して「箱モデル」と呼びましょう。

+

一方、私が出した本、(『オブジェクト指向スクリプト言語Ruby』)を含めてRubyの解説書の多くで使われているのが「変数は名札のようなもの」というたとえです(図30.2)。

+
+ +
+ fig3002 +
+

図30.2●変数は名札

+
+

これを「変数=名札モデル」略して「名札モデル」と呼びましょう。1つの「モノ」に複数の名札を付けるのはめったにないことですが、箱と違って超現実的というわけではありません。

+

このように、名札モデルはRubyの変数の振る舞いをより自然に説明しますから、Rubyだけを考えるなら名札モデルのほうが優れていると考えることができます。

+

ところが、ことはそれほど単純ではないのです。Rubyにだけ関していえば確かに「名札モデル」のほうが優れているかもしれませんが、プログラミング言語一般として考えると箱モデルのほうがふさわしい場合もありえます。

+

たとえばC言語を考えると、Cでは、

+ +
    +
  • 変数の実体は値を入れる入れ物である

  • +
  • 代入は入れ物の中身をコピーすることである

  • +
  • その入れ物はオブジェクトそのものである

  • +
+

という性質があります。

+

ということは「箱モデル」の説明のほうがしっくりくるわけです。仮に初心者がCのような言語に関してはひととおりの知識があるのであれば、むしろ箱モデルのほうがわかりやすい場合も考えられます。

+

Cと箱モデルは非常に相性がよく、変数は値の入る箱であると考えてまず間違いありません。C言語では代入によって箱の中身(値)がコピーされます。また箱には名前の他に番地が付いていて、番地を経由してアクセスできます。変数名の前に「&」演算子を付けるとその変数の番地を得ることができます。

+

C言語の変数は「別の箱を指す番地が書いた紙の入った箱」と表現できます。代入では箱の中身である番地(を書いた紙)がコピーされて指されている箱には変化がありません(図30.3)。

+
+ +
+ fig3003 +
+

図30.3●Cのポインタ変数を箱モデル

+
+

このように「わかりやすいたとえ」を考えただけでも、前提や対象者によってさまざまな条件が絡み合い難しいものです。入門あなどるなかれ。

+
+
+

初等講座を振り返る

+
+

この『Ruby初等講座』は今回が25回目、つまりもう2年以上も連載してきたことになります。「初等」なんてタイトルを付けちゃったばかりに苦労しました。必ずしもうまくいったという自信はないのですが、ここではこの2年の軌跡をたどってみましょう。

+

過去の連載の内容を表30.1に示します。

+ +
+

表30.1●『Ruby初等講座』連載目次

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
回数題名知られざるRubyRuby開発日記
第1回
2001年11月号
はじめの一歩openデンマーク訪問記
第2回
2001年12月号
条件判断とループendLinux Conference 2001
第3回
2002年1月号
オブジェクトと変数変数Ruby Conference 2001 + OOPSLA
第4回
2002年2月号
配列ソートRuby 1.8
第5回
2002年3月号
ハッシュハッシュアルゴリズムオープンソースな生活
第6回
2002年4月号
文字列の操作packとunpack国際感覚
第7回
2002年5月号
パターンマッチ文字コードRiteと鬼車
第8回
2002年6月号
入出力stdioの憂鬱
第9回
2002年7月号
数と電卓coerceインプットメソッド
第10回
2002年8月号
CGI(その1)Web セキュリティ若い世代への憂慮あるいは杞憂
第11回
2002年9月号
CGI(その2)フリーソフトウェアな生活
第12回
2002年10月号
CGIの道具箱時代はLispに追いついたか
第13回
2002年11月号
ファイル処理顔じゃないよ、心だよ
第14回
2002年12月号
ネットワークプログラミング予想外
第15回
2003年1月号
番外編 Rubyカンファレンス2002リポート
第16回
2003年2月号
プロセスとフォークRubyソースコード完全解説大人になる、ということ
第17回
2003年3月号
スレッド(その1)スピード狂
第18回
2003年4月号
スレッド(その2)プログラマーの幸せ
第19回
2003年5月号
データの保存プログラマーと数学
第20回
2003年6月号
XMLとYAML迷惑メールをやっつけろ
第21回
2003年7月号
XMLとYAML(その2)うぇぶ日記はじめました。
第22回
2003年8月号
エクストリーム・プログラミング10年後の技術者
第23回
2003年9月号
独習Rubyオープンソースコンベンション2003
第24回
2003年10月号
再入門オブジェクト指向LL Saturday
第25回
2003年11月号
これからのRuby言語のユーザビリティ
+
+

こうして振り返ってみると、いろいろ苦労したことが思い出されます。

+

この連載の前半では「知られざるRuby」というちょっと高度なコーナーが存在していました。ドキュメントにも書いていないような細かいところを解説するコーナーで書いてる本人も結構面白かったのですが、ソースコードを読みながらRubyの隅っこの部分の挙動を解説する作業は結構手間がかかりますし、なにより原稿を書いてる最中にRubyのデバッグや修正に熱中してしまったりと思わぬ展開になることもしばしばでした。

+

ただ単に解説を書くよりも大変手間がかかってしまうことと、入門のほうが回数を進めるうちにだんだんと高度になってきた関係もあってこれはいつの間にかなくなってしまいました。

+

「知られざるRuby」コーナーがなくなったのとほぼ同時期に、解説するテーマもRuby言語そのものから応用分野の方向へと話題が移ってきました。たとえばCGIとかXMLとかです。

+

Ruby言語そのものについてはたくさん本も出ていますから、実際にRubyを使いたい人には、こちらの応用のほうが役に立ったかもしれません。

+

このあたりは他にはない解説だと自画自賛しているのですが、今改めて原稿を読み返してみるとちょっと実例が少ないかもしれませんね。あまり「初等」ではなかったかなあ。

+

この連載では毎月最後の1ページを使って「Ruby開発日記」というコラムを掲載してきました。これはRuby開発者である私の開発生活の一部を切り取ったような「無駄話」を毎月書いてきました。出席したカンファレンスのことや、Rubyの開発状況、オープンソースデベロッパーを取り巻く状況などについてが主な話題でしたね。

+
+

また、プログラマーという人種の生態について書いたこともありました(2002年4月号2002年8月号など)。「プログラマーも人間だ、ちょっと変わってるかもしれないけど」ということが伝わりましたでしょうか。

+

本編よりもこちらのほうが面白いという意見もあったようです。せっかくなのでこういう試みはこれからも続けていきたいと思います。

+
+
+

これからのRuby

+
+

「これからRubyがどうなるか」ではなく、読者の皆さんが『初等Ruby講座』で学んだあと、「これからRubyで何をするか」という話です。

+

Rubyというプログラミング言語がありました。ひととおりいろんなことができることはわかりました。では、それを使って何をしましょう?

+

以前、地方紙のインタビューを受けたときにコンピュータのことにあまり詳しくない記者の方に「で、そのRubyというソフトはどんなことができるんですか」と質問されて、大変困ったことがあります。

+

プログラミング言語ってのは、何をプログラミングするかで、できることが決まるわけですから。「プログラムできます」と答えてもわかってもらえないでしょうし。悩んだ挙げ句「何でもできます」などとわかったようなわからないような答えを出したんですが、記者の方はなんだか不思議そうな顔をしていました。

+

Rubyで何ができるかを決めるのは、Rubyを使う人一人一人です。Rubyはプログラムされた命令に従って、何でも実行します。あなたは何を命令しますか?

+

Rubyの使い方として、典型的なものの1つとして考えられるのは簡単なテキスト処理でしょう。

+

先日、スパムフィルタによって分類されたメールフォルダ(mbox形式)のメールのサブジェクトから、フィルタによって付けられた「***** SPAM *****」という文字列を削りたいと思いました。sedとかでも簡単な処理でしょうが、あいにく私はsedを使いこなせるほど知りません。そこでRubyの出番です。

+
+
% ruby -i~ -pe 'sub(/\*{5} SPAM \*{5}/, "")' mbox
+
+

これだけでmboxというメールフォルダの置き換えが行われます。修正前のフォルダはmboxという名前で残されます。このプログラムでは「***** SPAM *****」という文字列すべてを置換してしまいます。気になる場合には、

+
+
/^Subject: \*{5} SPAM \*{5} /
+
+

という正規表現にしたほうがよいのかもしれません。まあ、簡単な処理ですから、そこまで厳密にする必要はないでしょうが。

+
+
+ +

日常生活に役立てる

+
+

「Rubyをいかに使うか」という問いへの答えの参考にするため、私が日常的に使っているRubyスクリプトの中からいくつかを紹介しましょう。

+

まず最初にご紹介するのは「文字重複チェックプログラム」です(リスト30.1)。

+
+

リスト30.1●文字重複チェックプログラム

+
ARGF.each do |line|
+  print ARGF.file.path, " ", ARGF.file.lineno, ":", line if line.gsub!(/([あ-ん])\1/e, '[[&]]')
+end
+
+
+

私はここのところ原稿を書いたりドキュメントを書いたりする仕事が多いのですが、その中で一番よくある間違いが文字(特に助詞)の二重打ちです。

+

原稿を書いていく過程で、たとえばこんな文章を書いたとします。

+
+
よくあるエラーが、文字の二重打ちです
+
+

この中にある「エラー」の部分を「間違い」に訂正しようとして、「エラー」を消し、勢いで「間違いが」と打ってしまったとします。結果は、

+
+
よくある間違いがが、文字の二重打ちです
+
+

となります。

+

こういうエラーをチェックするのが文字重複チェックスクリプトです。このスクリプトを使うと同じひらがなが連続している部分を表示してくれます(図30.4)。

+
+
+
genko.txt 257:Ruby というプログラミング言語がありました。ひ[[とと]]おり
+genko.txt 341:私は[[ここ]]のところ原稿を書いたりドキュメントを書いた
+genko.txt 352: よくある間違い[[がが]]文字の二重打ちです
+
+

図30.4●文字二重打ちチェック

+
+

「ひととおり」とか「ここのところ」とか問題ないものも検出していますが、恥ずかしい間違いを見逃すよりはよいでしょう。私は原稿を提出する前にこのスクリプトにかけることにしています。

+

さて、原稿を書くときのための「文字重複チェックプログラム」ですが、本業のプログラム書きでも小さなプログラムが活躍します。プログラムの規模はよくステップ数と呼ばれる単位で数えます。これは基本的には行数なのですが、コメントや空行の数を除きます。リスト30.2はこの処理を行ってくれるプログラムです。

+ +
+

リスト30.2●ステップカウントプログラム

+
#! /usr/bin/ruby
+
+n = 0
+open = false
+while line = gets()
+  line.gsub! %r|//.*|, ''
+  line.gsub! %r|/\*.*\*/|, ''
+  if %r|/\*|                  # open comment
+    line.gsub! %r|/\*.*|, ''
+    open = true
+  end
+  unless /^\s*$/ =~ line
+    n += 1
+  end
+  if open
+    until %r|\*/| =~ line
+      line = gets()
+    end
+    line.sub! %r|.*\*/|, ''
+    open = false
+    redo
+  end
+end
+p n
+
+
+

このプログラムは、空白のみの行、コメント行を除いた行数をカウントします。Cのコメントは複数行にわたることがあるので、少々工夫してあります。

+

たとえば原稿執筆時点で最新のRubyのソースコードは全部で72830行あります。しかし、このプログラムを使って空白行とコメントを取り除くと、実質は62283行であることがわかります。実に1万行以上空白とコメントがあるのですね。

+

これらはほんの一例にすぎません。原理的にはRubyは「何でもできる」のですから、限界はプログラマーの想像力です。どんなことができる便利になるでしょうか。あなたならRubyに何をさせますか?

+
+
+

プログラムできる喜び

+
+

パーソナルコンピュータがまだ「マイコン」と呼ばれていた頃、コンピュータを使うことはプログラムすることとほぼ同じ意味でした。まだまだ未熟なマイコンは、何かしたいと思えばBASICなどを使ってプログラムしてやらなければ何もできなかったものです。私がコンピュータに初めて触れたのはそういう時代でした。それはPC-1210というシャープのポケットコンピュータで、電卓サイズにBASICを積んだ当時としては画期的なものでした。

+
+

そしていわゆるパソコン、それからワークステーションと、時代につれて使うコンピュータは進歩してきました。今では以前ワークステーションと呼ばれていたコンピュータよりもはるかに性能の高いパソコンが皆さんの机の上に乗っています。

+

ところがコンピュータが進歩した現在、コンピュータユーザーでプログラムする人はすっかり減ってしまいました。今や、コンピュータを使うということは、たいていはどこかの誰かが作ったプログラムを使うだけという意味になってしまいました。

+

現在、ほとんどの人にとっては、コンピュータというものは、単にインターネットブラウザやメールリーダー、ワープロなどの出来合いのアプリケーションを動かすための機械になってしまいました。

+

そもそもコンピュータを買ってきても、プログラミングできるツールは付いてきませんし。

+

私たち古い世代はコンピュータに興味を持つと同時に、自然にプログラミングを始めたわけですが、今の若い人たちはいったいどういうふうに感じているのでしょうか。最初に使うアプリケーションがいきなりインターネットエクスプローラのような大規模で完成度が高いソフトウェアだったりした日には、ちょっと自分でやってみようという気持ちにならないんじゃないでしょうか。

+

それはコンピュータが成熟したという意味でもありますが、以前よりずっと性能が高くなって、いろいろなことができるようになったはずのコンピュータなのに、アプリケーションとしてあらかじめ用意された機能を、用意されたとおりに使うしかない現状は融通が利かないと感じることもあります。

+

Rubyやその仲間のプログラミング言語は、そのような融通の利かなさを打破して、再びプログラムできる喜びを取り返すのに役立ちます。自分でコンピュータの動きを自由にプログラムできるのはなんとすばらしいことでしょう。失われていた自由を再び得たような気分です。

+

たとえば、

+
    +
  • メールサーバーにたまったSobig.Fのウィルスメールを一気に削除したい?
    +Rubyならできます。

    +
      +
    • http://amrita.s14.xrea.com/d/?date=20030905

    • +
  • +
  • Web日記を始めて、自分向けにカスタマイズしたい?
    +Rubyならできます。

    +
      +
    • http://www.tdiary.org/

    • +
  • +
+

Ruby, Rython, Perl, PHPなどのスクリプト言語処理系はたいていフリーソフトウェアですから、無料で入手できます。また、移植性も高いのでLinuxでもWindowsでも、その他多くのOSでもほぼ同じように利用できます。これらを使えばBASICが主流だった昔のように自由にプログラミングができるようになります。

+

コンピュータをプログラムする喜びを再び私たちの手に。そしてコンピュータを万能機械として活用しようではありませんか。

+
+
+

予告

+
+

「プログラムできる喜び」について宣伝したところで、この連載はとうとう終わりです。今まで長い間本当にありがとうございました。この連載が皆さんがRubyを理解する助けに少しでもなったのであれば幸いです。

+

『Ruby初等講座』はこれで終わりますが、もうちょっと目先を変えた形での連載を始めたいなと思っています。今までは入門でしたが、今度はもうちょっと応用寄りになると思います。お楽しみに。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-066.xhtml b/docs/vol1/xhtml/p-066.xhtml new file mode 100644 index 0000000..72b0ead --- /dev/null +++ b/docs/vol1/xhtml/p-066.xhtml @@ -0,0 +1,78 @@ + + + + + +第30章 ここからのRuby + + + + +

Matz Essays Volume 1

+ + +
+ +

◆ Ruby開発日記 ◆ 言語のユーザビリティ対象となる読者の気持ち

+
+

Jakob Nielsen博士によれば、使いやすさ、「ユーザビリティ」には以下の5つの質的な構成要素があるのだそうです(http://www.usability.gr.jp/alertbox/20030825.html)。

+
    +
  • 学習容易性
    +初めてそのデザインに触れたユーザーが、どれくらい容易に基本的なタスクを達成できるようになるか?

  • +
  • 効率性
    +いったんそのデザインを学習したユーザーが、どれくらい迅速にタスクを達成できるようになるか?

  • +
  • 記憶性
    +しばらく使用しない期間をはさんだあと、再びそのデザインに戻ってきたユーザーが、どれくらい容易に習熟度を取り戻すことができるか?

  • +
  • エラー
    +ユーザーが犯すエラーの数、そのエラーの深刻さ、そして、そのエラーからの回復の容易さはどうか?

  • +
  • 満足度
    +そのデザインを使うと、どれくらい楽しいか?

  • +
+

実に興味深いです。世間に存在するツールすべてがこのユーザビリティを念頭において設計されていればどんなによいことでしょう。たとえば、昨年購入したテレビはハードディスク内蔵で機能的には大変満足なのですが、残念ながら使い勝手は今ひとつです。カタログには使い勝手については載ってないんですよねえ。プログラミング言語もツールですから、当然使い勝手があります。上記の要素をプログラミング言語にあてはめるとどうなるでしょう。

+
+ +
学習容易性
+

学習が容易という観点からいうと、プログラミング言語はあまり独創的でないほうが望ましいのかもしれません。プログラマーたるもの、たいてい1つや2つの言語は知っているわけですから、既存の言語の知識が応用できるかどうかは、どれだけ素早く学ぶことができるかに大きく影響すると思います。もっとも、あまりに独自性がないと、その言語の存在価値もなくなってしまうわけですから、バランスが重要です。

+
+
+
効率性
+

プログラミング言語の効率は大きく分けると2種類あります。1つはプログラムがどれだけ高速に実行されるかという実行効率、もう1つはその言語で開発するときにどれだけ手早く開発できるかという開発効率です。もちろん両方優れているのに越したことはないのですが、最近のコンピュータの演算速度の進歩を考えると、より注目すべきは開発効率でしょう。

+

プログラミング言語の開発効率を向上させる方法はいろいろありますが、最も重要な要素は「簡潔性」だと思います。基本的には「簡潔に書けることは善」です。もっとも簡潔にしようとするあまり、病的に読めないプログラムになっては困ります。要するに、プログラマーがコンピュータにやらせたいと思っていることを、余計な指定なしに最少のステップで伝達する、というのが究極の目標でしょう。

+
+
+
記憶性
+

これは「学習容易性」と同様に他の言語からの類推ができることが有効だと思います。また、言語そのものが一貫性を持っていると、やはり類推が効いて思い出しやすいと思います。

+
+
+
エラー
+

プログラミング上で繰り返してしまう間違いというのはあるものです。私が経験があるのは、たとえばPascalでセミコロンの扱いを間違えるとか、Perlでセミコロンを忘れるとかです。あと、Cで文をブロックで囲むのもよく忘れます。このように頻繁に繰り返されるエラーに注意を払って取り除くことでよい言語を設計することができます。Rubyの場合、改行に意味を持たせてセミコロンの間違いをなくし、文はいつもendで終わるようにして、単文と複文の違いによる文法エラーもなくしています。

+
+
+
満足度
+

プログラミング言語の満足度はいろいろな要素で決まるのですが、きびきびプログラミングできる言語は使っていて楽しいものです。楽しいプログラミング、満足できるプログラミングは言語の究極の目標の1つです。プログラミングは本来非常に知的で、創造的で、楽しい活動のはずです。その本来の楽しさを取り返すのに「使いやすい言語」は役立つはずです。

+
+
+

使いやすい言語を求めて

+

普通の人はプログラミング言語を自分で設計したりはしないもののようなので、世の中に存在する言語の中から「使いやすい」ものを選ぶ必要があります。どんな言語が使いやすいか、あるいはどういう言語に満足するかは人によって違うので、これまでで述べた、5要素を基準にいろいろ探してみるのはどうでしょう。

+
+
+

え? どんな言語がよいか教えてくれって? 私に聞けば答えはRubyに決まってるでしょう?

+
+

私の知っている範囲でよさそうな言語にはRuby, Python, Lisp, Schemeなどがあります。その他にもあなたにぴったりの言語が見つかるかもしれません。

+
+
+

使いやすい言語を作ろう

+

世の中にプログラミング言語があふれているとはいえ、自分にぴったりの言語がなければ作るしかありません。私はもっともっと自分のプログラミング言語を設計する人が現れてもよいと思っています。もちろん日本でも言語を設計したことのある人はたくさんいると思います。しかし、それらの多くは純粋に研究のためであったり、ツールの組み込み用であったりで、使い勝手についてはあまり考慮されていないように思います。Rubyももう10歳になります。そろそろRubyを超えるような言語が新たに登場してもよい頃かもしれません。

+
+
+ +

+
+
+ + diff --git a/docs/vol1/xhtml/p-bmatter-001.xhtml b/docs/vol1/xhtml/p-bmatter-001.xhtml new file mode 100755 index 0000000..844ecf3 --- /dev/null +++ b/docs/vol1/xhtml/p-bmatter-001.xhtml @@ -0,0 +1,59 @@ + + + + + +初出一覧 + + + + +

Matz Essays Volume 1

+ + +
+

初出一覧

+
    +
  • 言語にとって美とは何か?: Rubyにみるスクリプト言語の実装技法, TransTECH, 1999年8月号, 翔泳社.

  • +
  • Free Language Report: オブジェクト指向スクリプト言語Ruby, C Magazine, 1999年8月号, SBクリエイティブ.

  • +
  • UNIX系OS間の移植性について, TransTECH, 1999年9月号, 翔泳社.

  • +
  • 連載 スクリプト言語: スクリプト言語の歴史, bit, 2001年2月号, 共立出版.

  • +
  • 特集 開発ツールをもっと便利に使おう: 最強の開発環境を求めて: Ruby最古のユーザーとしての開発環境, C Magazine, 2001年11月号, SBクリエイティブ.

  • +
  • 初等Ruby講座: はじめの一歩, Linux magazine, 2001年11月号, アスキー.

  • +
  • 初等Ruby講座: 条件判断とループ, Linux magazine, 2001年12月号, アスキー.

  • +
  • 初等Ruby講座: オブジェクトと変数, Linux magazine, 2002年1月号, アスキー.

  • +
  • 初等Ruby講座: 配列, Linux magazine, 2002年2月号, アスキー.

  • +
  • 初等Ruby講座: ハッシュ(または連想配列), Linux magazine, 2002年3月号, アスキー.

  • +
  • 初等Ruby講座: 文字列の操作, Linux magazine, 2002年4月号, アスキー.

  • +
  • 初等Ruby講座: パターンマッチ, Linux magazine, 2002年5月号, アスキー.

  • +
  • 初等Ruby講座: 入出力, Linux magazine, 2002年6月号, アスキー.

  • +
  • 初等Ruby講座: 数と電卓, Linux magazine, 2002年7月号, アスキー.

  • +
  • 初等Ruby講座: CGI, Linux magazine, 2002年8月号, アスキー.

  • +
  • 初等Ruby講座: Rubyで作るCGI, Linux magazine, 2002年9月号, アスキー.

  • +
  • 初等Ruby講座: CGIの道具箱, Linux magazine, 2002年10月号, アスキー.

  • +
  • 初等Ruby講座: ファイル処理, Linux magazine, 2002年11月号, アスキー.

  • +
  • 初等Ruby講座: ネットワークプログラミング, Linux magazine, 2002年12月号, アスキー.

  • +
  • 初等Ruby講座: 番外編: Rubyカンファレンスレポート, Linux magazine, 2003年1月号, アスキー.

  • +
  • 初等Ruby講座: プロセスとフォーク, Linux magazine, 2003年2月号, アスキー.

  • +
  • 初等Ruby講座: スレッド(その1), Linux magazine, 2003年3月号, アスキー.

  • +
  • 初等Ruby講座: スレッド(その2), Linux magazine, 2003年4月号, アスキー.

  • +
  • 初等Ruby講座 データの保存, Linux magazine, 2003年5月号, アスキー.

  • +
  • 初等Ruby講座: XMLとYAML, Linux magazine, 2003年6月号, アスキー.

  • +
  • 初等Ruby講座: XMLとYAML(その2), Linux magazine, 2003年7月号, アスキー.

  • +
  • 初等Ruby講座: エクストリーム・プログラミング, Linux magazine, 2003年8月号, アスキー.

  • +
  • 初等Ruby講座: 独習Ruby, Linux magazine, 2003年9月号, アスキー.

  • +
  • 初等Ruby講座: 再入門オブジェクト指向, Linux magazine, 2003年10月号, アスキー.

  • +
  • 初等Ruby講座: 最終回: ここからのRuby, Linux magazine, 2003年11月号, アスキー.

  • +
+
+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-bmatter-002.xhtml b/docs/vol1/xhtml/p-bmatter-002.xhtml new file mode 100755 index 0000000..7a62704 --- /dev/null +++ b/docs/vol1/xhtml/p-bmatter-002.xhtml @@ -0,0 +1,1369 @@ + + + + + +索引 + + + + +

Matz Essays Volume 2

+ + +
+

索引

+

数字・記号

+

!str, 350

+

!, 116

+

"", 146

+

'', 146, 147

+

*, 115, 151

+

+, 115, 151

+

-, 76, 115

+

..., 113

+

.., 113

+

., 166, 382, 395

+

;, 81, 85

+

<=>, 115, 155

+

<% %>, 256

+

<%= %>, 256

+

<, 399

+

===, 169

+

==, 155

+

=>, 129

+

=~, 169, 170, 172

+

=, 381

+

=~, 151

+

?, 82

+

@@, 100, 381

+

@, 100, 381

+

[]=, 115, 130, 150, 151, 172

+

[], 112, 115, 130, 150, 151, 165, 172, 339, 382

+

$_, 99, 105

+

$~, 99

+

$, 99, 381

+

$1, 99, 105

+

$KCODE, 178

+

$SAFE, 105, 227, 315

+

%, 151, 199, 380

+

%形式, 146, 148

+

&, 115, 200

+

_, 99

+

_dump, 336

+

_load, 336

+

^, 200

+

~, 200

+

<<, 115, 148, 151, 184, 188, 200

+

>>, 200

+

\<ch>, 147

+

\a, 147

+

\b, 147

+

\C-x, 147

+

\cx, 147

+

\e, 147

+

\f, 147

+

\M-x, 147

+

\M-\C-x, 147

+

\n, 147

+

\nnn, 147

+

\r, 147

+

\s, 147

+

\t, 147

+

\xnn, 147

+

|-, 76

+

||, 382

+

|, 75, 115, 167, 200

+

10年後の技術者, 376

+

12のプラクティス, 369

+

2の補数, 201

+

4つのバリュー, 370

+

A

+

abort, 237, 339

+

acos, 206

+

acosh, 206

+

action, 222

+

alias, 382

+

all?, 118

+

alloca, 11

+

AMarshal, 337

+

ancestors, 386

+

ANSI C, 46

+

any?, 118

+

Apache, 219

+

ARGF, 191

+

ARGV, 69

+

Array, 112

+

asin, 206

+

asinh, 206

+

assert, 372

+

assert_equal, 372

+

assert_instance_of, 372

+

assert_kind_of, 372

+

assert_nil, 372

+

assert_raises, 372

+

assert_respond_to, 372

+

assoc, 115, 139

+

at, 115

+

atan, 206

+

atan2, 205

+

atanh, 206

+

atime, 184

+

autoconf, 47, 195

+

AutoFAQ, 233, 239

+

インストール, 243

+

カスタマイズ, 239

+

初期化, 239

+

メイン処理, 241

+

automake, 51

+

autoscan, 50

+

awk, 55

+

B

+

Babbage, Charles, 341

+

Bangメソッド, 116

+

base64, 159

+

BASIC, 8, 60, 409

+

BasicSocket, 280

+

close_read, 280

+

close_write, 280

+

getpeername, 280

+

getsockname, 280

+

getsockopt, 280

+

recv, 280

+

send, 280

+

setsockopt, 280

+

shutdown, 280

+

Bayesianフィルタ, 353

+

Beck, Kent, 368

+

begin, 85, 171, 383

+

BiCounter, 399

+

Bignum, 18, 198, 395

+

binmode, 184

+

Black, David Alan, 109, 297

+

BNF, 8

+

BOF(Bird Of a Feather), 93

+

bogofilter, 353

+

Bourbaki, Nicolas, 341

+

Brannan, Paul, 295

+

break, 86, 383

+

Bryant, Avi, 109, 294

+

Bシェル, 54

+

C

+

C++, 12

+

capitalize, 151, 154

+

capitalize!, 151

+

casecmp, 155

+

casefold?, 169

+

cat, 191

+

catch, 383

+

ceil, 203

+

center, 151

+

CGI, 234

+

FileStore, 249

+

header, 234

+

print, 234

+

Session, 249

+

cgi, 222, 234

+

CGI(Common Gateway Interface), 25, 216, 231, 247

+

cgi.rb, 255

+

cgi/session.rb, 248

+

char, 44

+

chmod, 184, 193

+

chomp, 151

+

chomp!, 151

+

chop, 151

+

chop!, 151

+

chown, 184

+

Clarke, Tom, 293

+

class, 97, 103, 382, 395, 399

+

clear, 115, 130, 134

+

close, 184, 188

+

closed?, 184, 189

+

close_read, 184

+

close_write, 184

+

clxmlserial, 338

+

cmail, 143, 352

+

Cockburn, Alistair, 77, 298

+

coerce, 210

+

collect, 115, 117, 130

+

collect!, 115

+

commit, 237, 339

+

compact, 115

+

compact!, 115

+

compile, 169

+

complex, 207

+

concat, 115, 151

+

ConditionVariable, 328

+

configure, 47

+

constant, 82

+

Content-typeヘッダー, 219

+

cos, 205

+

cosh, 206

+

count, 151

+

Counter, 397

+

CPAN(Comprehensive Perl Archivers Network), 232

+

crypt, 151

+

csh, 54

+

ctime, 184

+

Cunningham, Ward, 77

+

C言語, 3, 46

+

Cシェル, 54

+

D

+

daytime, 281

+

def, 98, 103, 382

+

default, 130, 136

+

default=, 130

+

defined?, 82

+

delete, 115, 130, 151

+

delete!, 151

+

delete_at, 115

+

delete_if, 115, 130, 133

+

Deniel, Berger, 292

+

detect, 115, 117, 130, 135

+

Deutsch, L. Peter, 268

+

Digest::MD5, 268

+

<<, 268

+

digest, 268

+

hexdigest, 268

+

update, 268

+

digest/md5, 268

+

Dijkstra, Edsger Wybe, 81

+

Dir.glob, 114

+

divmod, 199

+

do, 85

+

downcase, 151, 154

+

downcase!, 151

+

downto, 204

+

dump, 151

+

E

+

E, 205

+

each, 115, 117, 130, 151, 155, 184, 187

+

each_byte, 151, 156, 184, 187

+

each_index, 115

+

each_key, 130, 132

+

each_line, 151, 156, 184, 187

+

each_pair, 130, 132

+

each_value, 130

+

each_with_index, 115

+

eban, 195, 269

+

else, 83, 84

+

ぶらさがり〜, 89

+

elsif, 83

+

Emacs, 63, 364

+

empty?, 115, 130, 151

+

end, 28, 88, 171

+

ENV, 138

+

eof?, 184

+

eql?, 140

+

ERb, 258

+

eruby, 256

+

eRuby(埋め込みRuby), 255, 256

+

escape, 169

+

EUC-JP, 177

+

eval, 289

+

exec, 301

+

EXILIM, 264

+

exp, 205

+

expat, 26

+

F

+

false, 82

+

FAQマネージャ, 232

+

fcntl, 184

+

fetch, 118, 129, 130, 137

+

File, 75, 183, 269

+

File::foreach, 69

+

File::new, 76

+

File::open, 76

+

fileno, 184

+

FileStore, 249

+

fill, 115

+

find, 115, 117, 130

+

find_all, 115, 117, 130

+

first, 115

+

Fixnum, 17, 198, 395

+

flatten, 115

+

flatten!, 115

+

Float, 154, 198, 203

+

flock, 184, 191

+

floor, 203

+

flush, 184, 189

+

fnmatch, 195

+

for, 69, 87, 116, 383

+

fork, 300, 301, 304, 311

+

二重〜, 303, 304

+

form, 220

+

Fowler, Martine, 77

+

frexp, 205

+

FSF(Free Software Foundation), 245

+

ftools, 268, 269

+

chmod, 269

+

cmp, 269

+

compare, 269

+

copy, 269

+

cp, 269

+

install, 269

+

makedirs, 269

+

makepath, 270

+

move, 269

+

mv, 269

+

rm_f, 269

+

safe_unlink, 269

+

syscopy, 269

+

verbose, 269

+

FTP(File Transfer Protocol), 279, 283

+

G

+

GC, 13, 124

+

getc, 184, 186

+

getchar, 186

+

get_parameters, 363

+

gets, 184, 186

+

Gibson, Steven, 297

+

GOTO, 81

+

gprof, 6

+

Graham, Paul, 353

+

Granger, Michael, 388

+

grep, 27, 115

+

gsub, 151, 172, 173

+

gsub!, 151, 172, 173

+

Gtk, 26

+

GUI(Graphical User Interface), 26

+

GUIライブラリ, 26

+

H

+

Hash[], 129

+

hash, 140

+

Hash.new, 136

+

hello world, 27

+

Hendrickson, Chet, 109

+

hex, 151

+

hiddenフィールド, 248

+

host, 285

+

HotSpot, 12

+

HTML(Hyper Text Markup Language), 216

+

フォーム, 220

+

HTTP(HyperText Transfer Protocol), 248, 279, 283

+

クライアント, 216, 285

+

サーバー, 216

+

リクエスト, 251

+

HTTP

+

finish, 285

+

get, 285

+

get_print, 285

+

head, 285

+

new, 285

+

post, 285

+

start, 285

+

Hunt, Andy, 77, 109, 296

+

hypot, 206

+

I

+

IEEE, 46

+

if, 81, 383

+

後置〜, 83

+

修飾子, 83

+

ifnone, 118

+

include?, 115, 130, 151

+

index, 115, 151, 172

+

indexes, 115, 130

+

indices, 115, 130

+

initプロセス, 303

+

inject, 118

+

input, 222

+

insert, 118

+

inspect, 134

+

int, 44

+

Integer, 154, 198, 202, 203, 395

+

intern, 151, 156

+

invert, 130, 135

+

IO, 75, 183

+

IO.readlines, 114

+

IO::popen, 76

+

ioctl, 184

+

IOWA, 255

+

IPSocket, 280

+

addr, 280

+

peeraddr, 280

+

recvfrom, 280

+

irb(対話型Ruby), 72, 208, 393

+

J

+

JAOO(Java and OO), 77

+

Java, 3

+

Jeffries, Ron, 109

+

JIS(ISO-2022-JP), 177

+

JIT(Just In Time compile), 12

+

Johnson, Lyle, 109, 292

+

join, 115

+

K

+

K&R, 46

+

kcode, 169

+

Kconv, 238

+

kconv, 238

+

Kernel\#to_yaml, 350

+

key?, 130

+

keys, 130, 132

+

Kilmer, Rich, 295, 388

+

kitaj, 365

+

L

+

Lambda Calculus, 289

+

Lambdaインタプリタ, 289

+

The Language List, 4

+

last, 115

+

last_match, 169

+

ldexp, 205

+

Lea, Doug, 77

+

Leavengood, Ryan, 109

+

Lee, Tim-Burners, 215

+

length, 96, 115, 130, 151, 152, 171

+

libtool, 51

+

lineno, 184

+

lineno=, 184

+

Linux Conference 2001, 92

+

Lisp, 60, 260, 289, 345

+

ljust, 151

+

LL Saturday, 400

+

LL(Lightweight Language), 400

+

LL2, 298

+

local_variables, 114

+

log, 205

+

log10, 205

+

long, 44

+

long\,long, 44

+

loop, 383

+

lstat, 184

+

M

+

map, 115, 117, 130

+

map!, 115, 130

+

Marshal, 334

+

dump, 334

+

load, 334

+

match, 169, 170

+

MatchData, 170

+

Math, 205

+

Matz, 109, 293, 296, 388

+

max, 115

+

May, Patrick, 293

+

McBreen, Pete, 109

+

McCarthy, John, 289

+

McCorkhill, David, 388

+

MD5アルゴリズム, 267

+

Meeting2000, 38

+

member?, 115, 130

+

Merril, Brad, 298

+

min, 115

+

Mix-in, 325

+

mode, 193

+

mod_ruby, 251

+

module, 103, 382

+

modulo, 199

+

mtime, 184

+

MUSHA, Akinori, 268

+

Mutex, 322

+

lock, 323

+

synchronize, 323

+

unlock, 323

+

N

+

name, 222

+

net/ftp, 283

+

net/http, page{283--285}

+

net/imap, 283

+

net/pop, 283

+

net/smtp, 283

+

net/telnet, 283

+

NetNet

+

HTTP, 285

+

new, 169, 312

+

next, 87, 151, 156, 383

+

next!, 151

+

Nielsen, Jakob, 411

+

nil, 82, 395

+

nitems, 115

+

Numeric, 395

+

Nygaard, Kristien, 78

+

O

+

Object, 395

+

oct, 151

+

offset, 171

+

Ong, Emil, 109

+

OOPSLA(Object-oriented Programming, System, Language and Applications), 108, 297

+

open, 73, 185

+

入出力モード, 74

+

パーミッション, 74

+

引数, 74

+

owned?, 193

+

P

+

p, 134

+

pack, 115

+

Parrot, 292

+

parsedate, 175

+

Pascal, 3

+

path, 184, 285

+

PC-1210, 409

+

Perl, 4, 23, 55

+

Perl 6, 292

+

PI, 205

+

pid, 184

+

Pine, Chris, 388

+

pop, 115

+

POP3(Post Office Protocol version 3), 283

+

port, 285

+

pos, 184, 189

+

pos=, 184, 190

+

POSIX, 46

+

post_match, 171

+

pre_match, 171

+

print, 184, 187

+

printf, 69, 184, 187, 202

+

Process, 303

+

egid, 304

+

egid=, 304

+

euid, 304

+

euid=, 304

+

exit!, 304

+

fork, 304

+

getpgid, 304

+

getpgrp, 304

+

getpriority, 304

+

gid, 304

+

gid=, 304

+

kill, 304

+

pid, 304

+

ppid, 304

+

setpgid, 304

+

setpgrp, 304

+

setpriority, 304

+

setsid, 304

+

times, 304

+

uid, 304

+

uid=, 304

+

wait, 304

+

wait2, 304

+

waitall, 304

+

waitpid, 304

+

waitpid2, 304

+

prof, 6

+

PStore, 338

+

pstore, 235

+

push, 115

+

putc, 184, 188

+

puts, 68, 97, 184

+

Python, 4, 23, 57

+

Q

+

query, 285

+

Queue, 326

+

SizedQueue, 327

+

quote, 169

+

R

+

RAA(Ruby Application Archive), 231

+

rand, 206

+

rassoc, 115

+

Rational, 211

+

rational, 207

+

rational.rb, 211

+

rb_eval, 10

+

read, 184, 186

+

readable?, 193

+

readchar, 184

+

readline, 184, 186

+

readlines, 184, 186

+

redo, 87, 383

+

ReFe, 385

+

Regexp, 163

+

rehash, 130, 141

+

reject, 115, 130, 133

+

reject!, 115, 130, 133

+

remainder, 199

+

reopen, 184

+

replace, 115, 130, 151

+

retry, 383

+

return, 383

+

reverse, 115, 151, 152

+

reverse!, 115, 151, 152

+

reverse_each, 115, 117

+

rewind, 184, 190

+

REXML, 360

+

ri, 385

+

rindex, 115, 151, 172

+

Rite, 178

+

rjust, 151

+

root, 236

+

root?, 237, 339

+

roots, 237, 339

+

Rubotzky, Ken, 109

+

Ruby, 4, 23, 58, 66

+

1.2.6, 25

+

1.3.4, 25

+

1.6, 232

+

1.6.0, 124

+

1.7, 118, 122, 154, 155, 202, 203, 205, 206, 338

+

1.8, 124, 371

+

1.8.0, 400

+

2.0, 178

+

GC, 13, 124

+

GUI操作, 30

+

M17N, 178

+

インストール, 25, 70

+

インタプリタ, 8, 10

+

エラーチェック, 34

+

オブジェクト指向, 31, 35

+

拡張ライブラリ, 21, 36

+

ガベージコレククション, 13

+

基礎, 379

+

気持ち良さ, 32

+

クラスライブラリ, 31

+

サンプルプログラム, 27, 68

+

実行, 69

+

実装, 8

+

スレッド, 19

+

設計思想, 27

+

多言語対応, 125

+

楽しいプログラミング, 27

+

抽象化, 35

+

データ型, 16

+

特徴, 4, 24, 58, 67

+

表現, 33

+

文法, 33, 380

+

メモリ管理, 12, 34

+

役割, 25

+

ライブラリ, 384

+

リファレンスマニュアル, 384

+

Ruby Conference, 108, 291

+

Rubyインタプリタ, 69

+

コマンドラインオプション, 72

+

使い方, 71

+

『Rubyソースコード完全解説』, 307

+

Russell, Sean, 289

+

S

+

scan, 151, 172, 173

+

scheme, 285

+

Schwarz, Randal, 122

+

Schwern, Michel G., 77

+

seek, 184, 190

+

select, 115, 117, 130, 135, 222

+

self, 97, 382

+

Session, 249

+

close, 250

+

delete, 250

+

FileStore, 249

+

new, 249

+

SGmail, 26, 36

+

sh, 54

+

shift, 115, 130, 134

+

short, 44

+

Simula, 78

+

sin, 205

+

sinh, 206

+

size, 115, 130, 151, 152, 171, 394, 395

+

SJIS, 177

+

slice, 115, 150, 151

+

slice!, 115, 151

+

Smalltalk, 8, 60

+

SMTP(Simple Mail Transfer Protocol), 279, 283

+

Socket, 280

+

sort, 115, 117, 120, 130

+

降順, 120

+

昇順, 120

+

比較回数, 121

+

比較条件, 120

+

sort!, 115, 117

+

sort_by, 118, 122

+

source, 169

+

spam, 352

+

spamassassin, 353

+

spamprobe, 353

+

split, 151, 153, 172

+

sprintf, 146, 149, 202

+

sqrt, 205

+

squeeze, 151

+

squeeze!, 151

+

srand, 206

+

stat, 184

+

STDERR, 183

+

stderr, 183

+

STDIN, 183

+

stdin, 183

+

stdio, 194

+

STDOUT, 183

+

stdout, 183

+

step, 204

+

store, 130

+

String, 145, 238, 394

+

string, 171

+

strip, 151

+

strip!, 151

+

sub, 151, 172, 173

+

sub!, 151, 172, 173

+

submit, 222

+

succ, 151

+

succ!, 151

+

Sugalski, Dan, 292, 296

+

sum, 151

+

superclass, 395

+

swapcase, 151, 154

+

swapcase!, 151

+

Sync, 324

+

sync, 184, 325

+

sync=, 184

+

Sync_m, 325

+

sysread, 184, 187

+

system, 300

+

syswrite, 184, 188

+

S式, 289, 345

+

T

+

Talbott, Nathaniel, 109, 292

+

tan, 205

+

tanh, 206

+

Tcl, 8, 57

+

TCP/IP, 279, 283

+

TCPServer, 280

+

TCPSocket, 280

+

tDiary, 364

+

tdiary-mode, 364

+

tell, 184, 189

+

test/unit, 371

+

textarea, 222

+

then, 81

+

Thomas, Dave, 77, 109, 294, 385, 388

+

Thomson, Phil, 292

+

Thread, 312

+

[]=val, 313

+

[], 313

+

abort_on_exception, 313

+

abort_on_exception=val, 313

+

alive?, 313

+

critical, 313

+

critical=val, 313

+

current, 313

+

exit, 313

+

fork, 313

+

join, 313

+

key?, 313

+

keys, 313

+

kill, 313

+

list, 313

+

main, 313

+

new, 313

+

pass, 313

+

priority, 313

+

priority=val, 313

+

raise, 313

+

run, 313

+

safe_level, 313

+

start, 313, 316

+

status, 313

+

stop, 313

+

stop?, 313

+

value, 313

+

wakeup, 313

+

threaded code, 12

+

throw, 383

+

times, 204

+

Tk, 26, 57

+

TMarshal, 338

+

TMTOWTDI(There's More Than One Way To Do It), 56

+

to_f, 151, 203

+

to_i, 151, 202, 203

+

Tomson, Phil, 388

+

to_s, 134, 156, 202

+

tr, 151

+

tr!, 151

+

trace_var, 105

+

transaction, 236, 339

+

tr_s, 151

+

tr_s!, 151

+

true, 82

+

truncate, 184

+

tty?, 184

+

Turing, Alan Mathison, 80, 341

+

type, 222

+

U

+

UDPSocket, 280

+

undef, 382

+

uniq, 115

+

uniq!, 115

+

UNIX, 43

+

UNIXServer, 280

+

UNIXSocket, 280

+

unless, 84, 383

+

修飾子, 85

+

unpack, 151, 153, 157

+

unshift, 115

+

until, 86, 383

+

修飾子, 86

+

upcase, 151, 154

+

upcase!, 151

+

update, 130

+

upto, 151, 156, 204

+

URI(Uniformed Resource Indicator), page{284}

+

URI

+

extract, 285

+

join, 285

+

parse, 285

+

split, 285

+

uri, page{284}

+

URL(Unified Resource Locator), 216, 279

+

userinfo, 285

+

UTF-8, 177

+

uuencode, 159

+

V

+

value?, 130

+

values, 130, 132

+

van Rossum, Guido, 389

+

VM(Virtual Machine), 93

+

von Neumann, John, 341

+

W

+

wait, 303

+

waitpid, 303

+

Wall, Larry, 55, 161, 389

+

Warlus, 255

+

Webアプリケーション, 248

+

Webアプリケーションフレームワーク, 255

+

Webセキュリティ, 225

+

Web日記, 364

+

Welch, Brent, 77

+

while, 85, 383

+

修飾子, 85

+

why the lucky stiff, 293

+

write, 184, 188

+

WWW(World Wide Web), 215, 278

+

X

+

X Window System, 278

+

XMarshal, 338

+

XML(eXtensible Markup Language), 26, 345, 355

+

YAMLとの共通点, 357

+

YAMLとの違い, 355

+

xml-configfile, 360

+

XmlConfigFile

+

get_boolean, 362

+

get_float, 362

+

get_int, 362

+

get_string, 362

+

XMLパーサー, 26

+

XMLプログラミング, 26

+

xor, 201

+

XP, 368

+

12のプラクティス, 369

+

4つのバリュー, 370

+

XPATH, 362

+

XSS(Cross Site Scripting), 227

+

Y

+

yacc(yet another compiler compiler), 8

+

YAML, 346, 355

+

XMLとの共通点, 357

+

XMLとの違い, 355

+

応用, 359

+

欠点, 357

+

文法, 349

+

YAML

+

loadload, 350

+

yaml.rb, 350

+

YARPC19101(Yet Another Ruby/Perl Conference 19101), 124

+

yield, 382

+

Yoshino, 374

+

+

青木峰郎, 92, 283, 307, 385

+

青山和光, 283

+

アクセス権, 192

+

値, 96, 101

+

型, 7

+

アプリケーション層, 283

+

余り, 199

+

アラインメント, 45

+

アラン・チューリング, 80

+

アンカー, 167

+

アンテナ, page{284}

+

+

イーサネット, 279, 283

+

石塚圭樹, 212, 341

+

移植性, 43

+

磯蘭水, 401

+

イテレータ, 87, 132, 204

+

井上浩, 213

+

インスタンス, 97, 393

+

インスタンス変数, 100, 105, 381

+

インタプリタ, 4

+

インデックス, 113

+

インプットメソッド, 212

+

インラインメソッドキャッシュ, 11

+

+

うぇぶ日記, 364

+

+

エクストリーム・プログラミング, 368

+

エドガー・ダイクストラ, 81

+

演算子式, 382

+

円周率, 205

+

エンディアン, 44, 160

+

ビッグ〜, 44

+

リトル〜, 44

+

+

オープンソース, 142

+

オープンソースコンベンション2003, 387

+

生越昌己, 213

+

お手軽プログラミング, 25

+

鬼車, 180

+

オブジェクト, 95, 98, 392

+

型, 7

+

指定, 98

+

保存, 333

+

文字列, 394

+

オブジェクト式, 380

+

オブジェクト指向, 4, 31, 66, 391

+

+

カーネルレベルスレッド, 19, 312

+

開発環境, 63

+

開発効率, 6

+

カウンタクラス, 397

+

拡張ライブラリ, 21, 30

+

数, 198

+

画像管理, 265

+

仮想機械, 8, 93

+

型, 7

+

値の〜, 7

+

オブジェクトの〜, 7

+

静的な〜, 7

+

動的な〜, 7

+

型仮定, 12

+

狩野宏樹, 213

+

ガベージコレククション(GC), 13, 260

+

簡易言語, 3

+

環境変数, 138

+

かんな, 213

+

カンプフ・フォークトテスト, 80

+

+

偽, 82

+

キー, 128

+

規格, 46

+

擬似変数, 97, 100, 381

+

擬似乱数, 206

+

基数, 202

+

寄付モデル, 245

+

逆ハッシュ, 135

+

木山まさと, 92, 124

+

キュー, 326

+

きゅうり, 213

+

きゅうり改, 213

+

+

クイックソート, 120

+

空間効率, 6

+

クッキー, 235, 248

+

組み込みクラスライブラリ, 16

+

クライアント, 216

+

クライアント/サーバー, 277

+

クラス, 97, 98, 382, 393, 396

+

スーパー〜, 97

+

クラス定義, 32

+

クラス変数, 100, 105, 381

+

クラスライブラリ, 31

+

グルーピング, 167

+

グローバル変数, 99, 105, 381

+

+

継承, 393, 398

+

言語処理系

+

実装, 5

+

+

公開鍵, 267

+

公開鍵暗号, 267

+

構造, 343

+

後置if, 83

+

構文解析, 8

+

構文木, 8

+

コードポイント, 177

+

国際感覚, 161

+

小迫清美, 180

+

コピー

+

シャロー〜, 335

+

ディープ〜, 335

+

コピー・オン・ライト, 16

+

コマンド呼び出し, 300

+

コマンドライン引数, 68

+

コレクション, 112

+

コンカレント, 312

+

コンサバティブGC, 15

+

コンテナ, 112

+

コンパイラ, 8

+

コンパイラ・インタプリタ方式, 8

+

+

サーバー, 215

+

サーバー/クライアントモデル, 216

+

再帰, 260

+

最左最長一致, 166

+

最適化, 9

+

佐原大輔, 92

+

算術演算, 199

+

参照, 101

+

+

ジェネレーション・スキャベンジング, 15

+

シェル, 54

+

時間の計算, 31

+

式, 81, 98

+

鴫原厚博, 36

+

四捨五入, 203

+

辞書, 128

+

自然対数の底, 205

+

実行効率, 6

+

シフトJIS, 177

+

シャローコピー, 335

+

ジャンプ, 86

+

シュウォーツ変換, 122

+

出版モデル, 245

+

出力, 181

+

条件判断, 81, 383

+

条件変数, 328

+

証明, 79

+

シリアライズ, 334

+

真, 82

+

シングルタスク, 299

+

シンボル, 10, 156

+

信頼できない実行, 225

+

+

数学, 341

+

数学関数, 205

+

数値

+

変換, 203

+

文字列化, 202

+

数値計算, 17

+

数値表現, 198

+

スーパークラス, 97, 395, 399

+

スクリプト言語, 4, 53

+

性質, 59

+

スコープ, 103

+

トップレベル, 103

+

ブロック, 103

+

すずきひろのぶ, 294

+

スタティックスコープ, 260

+

ステートレス性, 248

+

ストップ・アンド・コピー, 14

+

ストリーム, 182

+

スピード狂, 319

+

スレッド, 19, 300, 311, 321

+

EXモード, 324

+

killed, 314

+

run, 314

+

SHモード, 324

+

stop, 314

+

to_kill, 314

+

カーネルレベル〜, 19

+

実行状態, 314

+

状態遷移, 314

+

消費者, 326

+

生産者, 326

+

生成, 312

+

セキュリティレベル, 315

+

相互排他ロック, 322

+

データ整合性の喪失, 317

+

デッドロック, 317

+

ユーザーレベル〜, 19

+

リソースの競合, 322

+

ロック, 322

+

+

正規表現, 18, 163

+

〜マッチ, 170

+

〜リテラル, 170

+

アンカー, 167

+

オプション, 168

+

グルーピング, 167

+

最左最長一致, 166

+

選択, 167

+

なまけものマッチ, 166

+

任意の1文字, 166

+

メタ文字, 164

+

文字クラス, 165

+

よくばりマッチ, 166

+

制御構造, 81, 383

+

生産者・消費者モデル, 326

+

整数, 17, 198

+

サイズ, 44

+

静的な型, 7

+

性能向上の原則, 6

+

性能の改善, 6

+

咳, 258

+

関将俊, 258

+

セッション, 248

+

セミコロン, 81, 85

+

線形検索, 139

+

宣言, 103

+

+

相互排他ロック, 322

+

操作性, 6

+

ソート, 117, 119

+

安定, 122

+

クイック〜, 120

+

バブル〜, 119

+

ソケット, 279

+

ソケット操作, 29

+

ソケットプログラミング, 281

+

ソフトウェア

+

移植性, 195

+

高速化, 320

+

ゾンビ, 302

+

+

大域ジャンプ, 383

+

ダイジェスト値, 267

+

代入, 101, 381

+

高橋征義, 293, 359

+

多重継承, 260

+

多態, 395

+

ただただし, 364

+

田中哲, 337

+

種, 206

+

楽しいプログラミング, 27

+

たまごっち, 353

+

たまてばこ, 288

+

+

置換, 173

+

逐次実行, 81

+

中間言語, 8

+

抽象化, 35

+

チューリングテスト, 80

+

チューリングマシン, 80

+

+

ディープコピー, 335

+

定義文, 382

+

定数, 100, 106, 381

+

データ構造, 343

+

データ整合性の喪失, 317

+

テーブル, 128

+

テキスト処理, 25

+

哲学者の晩餐, 317

+

デッドロック, 317

+

テンプレート文字, 157

+

+

動的結合, 395

+

動的な型, 7, 260

+

動的ページ, 217

+

特異クラス定義, 383

+

特異メソッド定義, 383

+

特殊変数, 171

+

トランザクション, 236, 339

+

トランスポート層, 279, 283

+

+

なかむらひろし, 359

+

名札モデル, 404

+

名前, 156

+

なまけものマッチ, 166

+

+

二重fork, 303

+

入出力, 181

+

入出力モード, 74

+

入力, 181

+

任意の1文字, 166

+

任意の区切り文字, 380

+

+

縫い糸コード, 12

+

+

ネットワーク層, 279, 283

+

ネットワークバイトオーダー, 45

+

ネットワークプログラミング, 25, 279

+

+

パーサー, 8

+

パーミッション, 74

+

パーミッションマスク, 75

+

排他制御, 324

+

排他的論理和, 201

+

バイトコード, 8

+

パイプ, 183

+

パイプライン, 75, 183

+

配列, 16, 111, 112, 394

+

インデックス, 113

+

繰り返し, 116

+

範囲, 113

+

メソッド, 115

+

要素の参照, 112

+

要素の変更, 113

+

要素番号, 113

+

配列式, 112

+

箱モデル, 404

+

パターン, 163

+

文法, 164

+

バックスラッシュ表現, 146

+

ハッシュ, 16, 128

+

アクセス, 129

+

値, 128

+

キー, 128

+

逆〜, 135

+

検査, 131

+

更新, 131

+

削除, 131, 133

+

出力, 134

+

生成, 129

+

ソート, 136

+

追加, 131

+

デフォルト値, 129, 136

+

反転, 134

+

ハッシュ値, 267

+

ハッシュ式, 129

+

ハッシュテーブル, 128

+

バッファオーバーラン, 225

+

バッファリング, 189

+

パトロンモデル, 246

+

バブルソート, 119

+

原信一郎, 341

+

パラレル, 312

+

+

非UNIX環境, 51

+

ヒアドキュメント, 146, 148, 255, 380

+

引数, 96

+

左ビットシフト, 201

+

ビッグエンディアン, 44

+

日付, 175

+

ビットand, 200

+

ビットor, 200

+

ビット演算, 200

+

秘密鍵, 267

+

標準出力, 183

+

標準入出力, 75, 183

+

標準入力, 183

+

+

ファイル

+

ロック, 191

+

ファイル処理, 263

+

ファイルポインタ, 189

+

フィルタ, 183, 190

+

フォーク, 300, 301

+

副業モデル, 244

+

複素数, 207

+

複文, 89

+

複利計算, 207

+

符号化方式, 177

+

浮動小数点数, 198

+

部分文字列, 150

+

ぶらさがりelse, 89

+

フラッシュ, 189

+

フリーソフトウェア, 244

+

一生, 308

+

プログラマー, 330

+

数学, 341

+

プログラミング

+

基礎, 81

+

プログラミング教育, 26

+

プログラミング言語, 3, 66

+

型, 7

+

実装, 4

+

制御構造, 81

+

設計, 4

+

プロセス, 300

+

生成, 300

+

制約, 306

+

ゾンビ, 302

+

入出力, 302

+

ブロック, 89, 382

+

プロトコル, 279, 282

+

プロトタイピング, 26

+

プロファイラ, 6

+

プロファイル, 6

+

+

グループ化, 383

+

文のまとまり, 88

+

+

並列実行, 299

+

べき乗, 199

+

ヘッダー, 219

+

別名定義, 382

+

変数, 98, 103, 380

+

〜は参照である, 101

+

〜は容器である, 101

+

インスタンス〜, 100, 105, 381

+

擬似, 381

+

擬似〜, 100

+

クラス〜, 100, 105, 381

+

グローバル〜, 99, 105, 381

+

スコープ, 103

+

宣言, 103

+

たとえ話, 404

+

定数, 100, 106, 381

+

有効範囲, 103

+

ローカル〜, 99, 103, 381

+

+

ポインタのサイズ, 44

+

ポート, 279

+

ホスト, 279

+

ポリモルフィズム, 395

+

+

マーク・アンド・スイープ, 14

+

マークアップ言語, 216

+

マーシャル, 334

+

マイコン, 409

+

前田修吾, 213, 252, 256, 283, 341, 359, 372

+

マクロ言語, 3

+

松尾尚典, 38

+

マッピング, 128

+

マルチタスク, 300

+

+

右ビットシフト, 201

+

+

無限多倍長整数, 18

+

無限ループ, 383

+

+

迷惑メール, 352

+

メソッド, 96, 98, 382, 392

+

〜キャッシュ, 11

+

〜呼び出し, 381

+

終了, 383

+

メタ・データフォーマット, 344

+

メタ文字, 164

+

メッセージ, 96, 98

+

+

文字クラス, 165

+

文字コード, 150, 168, 177, 238

+

文字集合, 177

+

モジュール, 382

+

文字列, 16, 145

+

大文字小文字の変換, 154

+

逆順, 152

+

数値化, 202

+

整数化, 153

+

長さ, 152

+

比較, 155

+

浮動小数点数化, 154

+

部分〜, 150

+

分割, 153

+

文字単位, 152

+

文字列オブジェクト, 394

+

文字列リテラル, 146

+

+

有効範囲, 103

+

ユーザーレベルスレッド, 19, 312

+

ユーザビリティ, 411

+

有理数, 207

+

ユニットテスト, 371

+

+

要素番号, 113

+

よくばりマッチ, 166

+

+

ライブラリ, 384

+

ラベル, 98

+

乱数, 206

+

擬似〜, 206

+

ランダムアクセス, 189

+

+

リソース, 322

+

リテラル, 98, 146

+

リトルエンディアン, 44

+

リファレンスカウント, 13

+

リファレンスマニュアル, 384

+

+

ループ, 81, 85, 383

+

脱出, 383

+

中断, 86

+

無限〜, 383

+

+

例外, 260

+

例外処理, 383

+

レシーバ, 96, 382

+

連想配列, 128

+

+

ローカル変数, 99, 103, 381

+

ロック, 322

+

論理積, 200

+

論理和, 200

+

+

わたなべひろふみ, 195, 269

+
+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-colophon.xhtml b/docs/vol1/xhtml/p-colophon.xhtml new file mode 100755 index 0000000..9bd7daf --- /dev/null +++ b/docs/vol1/xhtml/p-colophon.xhtml @@ -0,0 +1,84 @@ + + + + + +Matz Essays + + + + +

Matz Essays Volume 1

+ +
+ + +
+
+

+
+
+

Matz Essays Volume 1

+
+
+

1999〜2003

+
+
+ + +
+

まつもと ゆきひろ

+
+ + + +
+

2024年11月26日 発行

+
+ +
+

本書(電子版)は下記にもとづいて制作しました

+

『Matz Essays Volume 1』

+

2024年11月26日初版発行

+ +
+ +
+

発行者 夏野 剛

+

発行所 株式会社ドワンゴ

+

〒 104-0061 東京都中央区銀座4-12-15歌舞伎座タワー

+

03-3549-6153(編集)

+ +

https://asciidwango.jp/

+
+ +
+

+本書(電子版)に掲載されているコンテンツ(ソフトウェア/プログラム/データ/情報を含む)の著作権およびその他の権利は、すべて株式会社ドワンゴおよび正当な権利を有する第三者に帰属しています。法律の定めがある場合または権利者の明示的な承諾がある場合を除き、これらのコンテンツを複製・転載、改変・編集、翻案・翻訳、放送・出版、公衆送信(送信可能化を含む)・再配信、販売・頒布、貸与等に使用することはできません。 +

+アスキードワンゴ編集部
+編 集  鈴木嘉平、星野浩章

+
+ +
+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-cover.xhtml b/docs/vol1/xhtml/p-cover.xhtml new file mode 100755 index 0000000..7c1ea46 --- /dev/null +++ b/docs/vol1/xhtml/p-cover.xhtml @@ -0,0 +1,27 @@ + + + + + +Matz Essays + + + + +
+ + + + + +
+ + + diff --git a/docs/vol1/xhtml/p-fmatter-001.xhtml b/docs/vol1/xhtml/p-fmatter-001.xhtml new file mode 100755 index 0000000..48bd270 --- /dev/null +++ b/docs/vol1/xhtml/p-fmatter-001.xhtml @@ -0,0 +1,36 @@ + + + + + +Matz Essays + + + + + +

Matz Essays Volume 1

+ + + +
+
+ + + +
+
+ +

+
+ + + + diff --git a/docs/vol1/xhtml/p-fmatter-002.xhtml b/docs/vol1/xhtml/p-fmatter-002.xhtml new file mode 100755 index 0000000..01fb29d --- /dev/null +++ b/docs/vol1/xhtml/p-fmatter-002.xhtml @@ -0,0 +1,29 @@ + + + + + +Matz Essay + + + + +

Matz Essays Volume 1

+ + +
+

商標

+

本文中に記載されている社名および商品名は,一般に開発メーカーの登録商標です.
+なお,本文中では TM・©・® 表示を明記しておりません.

+
+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-fmatter-003.xhtml b/docs/vol1/xhtml/p-fmatter-003.xhtml new file mode 100755 index 0000000..c10ecf9 --- /dev/null +++ b/docs/vol1/xhtml/p-fmatter-003.xhtml @@ -0,0 +1,35 @@ + + + + + +はじめに + + + + +

Matz Essays Volume 1

+ + +
+

はじめに

+

本書は、1999年から2003年までいろいろな雑誌掲載された私が執筆した記事をまとめたものです。これらの記事を書いていた頃には10年後には創造もつかない変化が起きているに違いないと思っていましたが、実際に10年後を超えて、20年経っても割と「当たり前」な変化しか起きなかったように思います。

+

コンピューターの性能は順当に向上し、当時はまだ一部の人だけがアクセスできていたインターネットも一般化しました。普通の人が普通に超高性能コンピューター(スマートフォンのことです)を持ち歩き、いつでもどこでもインターネットにアクセスする未来はすばらしいものですが、「予想を超えた」とまでは言い難い気がします。

+

見方を変えれば、本書にまとめた記事の有効性もあまり減っていないということでもあります。20年前の記事を読み返してみれば、時代の中に忘れ去ってしまった未来へのヒントが埋まっているかもしれません。

+

そういえば、この「はじめに」を書いている2024年は、ChatGPTなどの登場により、いわゆるAIが夢物語ではなくなったタイミングではあります。また、量子コンピューターの領域でも進歩があったと報道されています。もしかすると、さらに10年先には、今度こそ驚くような未来がやってきているのかもしれません。

+

楽しみです。

+

2024年9月
+島根県の温泉地にて
+まつもとゆきひろ

+
+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-fmatter-004.xhtml b/docs/vol1/xhtml/p-fmatter-004.xhtml new file mode 100755 index 0000000..5f1ec9e --- /dev/null +++ b/docs/vol1/xhtml/p-fmatter-004.xhtml @@ -0,0 +1,29 @@ + + + + + +著者紹介 + + + + +

Matz Essays Volume 1

+ + +
+

著者紹介

+

まつもと ゆきひろ

+

プログラミング言語Rubyの創始者。Rubyアソシエーション理事長。ほかネットワーク応用通信研究所フェロー、OSS Vision社CTOなど肩書多数。鳥取県出身、島根県在住。2009年から島根県松江市名誉市民。妻1人、子4人、犬猫1匹ずつ。東京嫌い、温泉好き。

+
+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-fmatter-005.xhtml b/docs/vol1/xhtml/p-fmatter-005.xhtml new file mode 100644 index 0000000..01e1427 --- /dev/null +++ b/docs/vol1/xhtml/p-fmatter-005.xhtml @@ -0,0 +1,32 @@ + + + + + +本書の刊行にあたって + + + + +

Matz Essays Volume 1

+ + +
+

本書の刊行にあたって

+
    +
  • 元原稿を参照しているため、表現や表記などが雑誌掲載時と異なっていることがあります。

  • +
  • URLは雑誌掲載時のままになっています。リンク切れなどがあることがあります。

  • +
  • 付録CD-ROMは本書には添付していません。

  • +
+
+ +

+
+ + + diff --git a/docs/vol1/xhtml/p-fmatter-006.xhtml b/docs/vol1/xhtml/p-fmatter-006.xhtml new file mode 100644 index 0000000..f41c3ac --- /dev/null +++ b/docs/vol1/xhtml/p-fmatter-006.xhtml @@ -0,0 +1,66 @@ + + + + + +Volume 2 目次 + + + + +

Matz Essays Volume 1

+ + +
+

Volume 2 / 2003〜2007

+

第31章 探訪Ruby: Rubyの国へようこそ

+

第32章 探訪Ruby: テスト第一主義

+

第33章 探訪Ruby: Wiki Wiki

+

第34章 探訪Ruby: Blogの世界

+

第35章 探訪Ruby: アスペクト指向

+

第36章 探訪Ruby: RubyとEmacs

+

第37章 探訪Ruby: Instiki

+

第38章 探訪Ruby: テンプレート

+

第39章 探訪Ruby: DBM

+

第40章 探訪Ruby: tDiary

+

第41章 探訪Ruby: Webアプリケーションの基礎

+

第42章 探訪Ruby: Webアプリケーションの基礎(その2)

+

第43章 探訪Ruby: Webアプリケーションフレームワーク

+

第44章 探訪Ruby: マークアップ・マークダウン

+

第45章 探訪Ruby: ダイコン

+

第46章 探訪Ruby: 最終回・ネタのタネ

+

第47章 まつもと ゆきひろのハッカーズライフ: ハッカーとの遭遇

+

第48章 まつもと ゆきひろのハッカーズライフ: キーボードへのこだわり

+

第49章 まつもと ゆきひろのハッカーズライフ: ハッカーと仕事

+

第50章 まつもと ゆきひろのハッカーズライフ: Emacs 対 vi

+

第51章 オープンソース開発って何だろう

+

第52章 まつもと ゆきひろのハッカーズライフ: ハッカー環境問題

+

第53章 まつもと ゆきひろのハッカーズライフ: 言語の重要性

+

第54章 まつもと ゆきひろのハッカーズライフ: 言語の重要性 その2

+

第55章 まつもと ゆきひろのハッカーズライフ: ハッカーとオープンソース

+

第56章 まつもと ゆきひろのハッカーズライフ: 測定狂時代

+

第57章 まつもと ゆきひろのハッカーズライフ: ソースを読もう

+

第58章 まつもと ゆきひろのハッカーズライフ: Let's Talk Lisp

+

第59章 まつもと ゆきひろのハッカーズライフ: スケーラビリティ

+

第60章 まつもと ゆきひろのハッカーズライフ: オープンソースライセンス

+

第61章 まつもと ゆきひろのハッカーズライフ: Get Thing Done

+

第62章 まつもと ゆきひろのハッカーズライフ: 若人への手紙

+

第63章 まつもと ゆきひろのハッカーズライフ: オープンソースのマーケティング

+

第64章 まつもと ゆきひろのハッカーズライフ: キャズム

+

第65章 まつもと ゆきひろのハッカーズライフ: 言語の壁

+

第66章 まつもと ゆきひろのハッカーズライフ: ハッカーの生産性

+

第67章 まつもと ゆきひろのハッカーズライフ: 理系・文系

+

第68章 まつもと ゆきひろのハッカーズライフ: 美しいコード

+

第69章 まつもと ゆきひろのハッカーズライフ: オープンソースよ、永遠に

+
+ +

+
+ + + diff --git a/docs/vol2/image/ch31/31-1.jpg b/docs/vol2/image/ch31/31-1.jpg new file mode 100644 index 0000000..d96dc91 Binary files /dev/null and b/docs/vol2/image/ch31/31-1.jpg differ diff --git a/docs/vol2/image/ch31/31-2.jpg b/docs/vol2/image/ch31/31-2.jpg new file mode 100644 index 0000000..5f6b8d9 Binary files /dev/null and b/docs/vol2/image/ch31/31-2.jpg differ diff --git a/docs/vol2/image/ch32/32-1.jpg b/docs/vol2/image/ch32/32-1.jpg new file mode 100644 index 0000000..73f16bf Binary files /dev/null and b/docs/vol2/image/ch32/32-1.jpg differ diff --git a/docs/vol2/image/ch32/32-2.jpg b/docs/vol2/image/ch32/32-2.jpg new file mode 100644 index 0000000..3caf9df Binary files /dev/null and b/docs/vol2/image/ch32/32-2.jpg differ diff --git a/docs/vol2/image/ch36/36-1.jpg b/docs/vol2/image/ch36/36-1.jpg new file mode 100644 index 0000000..3d305b2 Binary files /dev/null and b/docs/vol2/image/ch36/36-1.jpg differ diff --git a/docs/vol2/image/ch38/38-1.jpg b/docs/vol2/image/ch38/38-1.jpg new file mode 100644 index 0000000..5309444 Binary files /dev/null and b/docs/vol2/image/ch38/38-1.jpg differ diff --git a/docs/vol2/image/ch41/41-1.jpg b/docs/vol2/image/ch41/41-1.jpg new file mode 100644 index 0000000..34a22b1 Binary files /dev/null and b/docs/vol2/image/ch41/41-1.jpg differ diff --git a/docs/vol2/image/ch41/41-3.jpg b/docs/vol2/image/ch41/41-3.jpg new file mode 100644 index 0000000..07837a0 Binary files /dev/null and b/docs/vol2/image/ch41/41-3.jpg differ diff --git a/docs/vol2/image/ch41/41-4.jpg b/docs/vol2/image/ch41/41-4.jpg new file mode 100644 index 0000000..c198222 Binary files /dev/null and b/docs/vol2/image/ch41/41-4.jpg differ diff --git a/docs/vol2/image/ch43/43-1.jpg b/docs/vol2/image/ch43/43-1.jpg new file mode 100644 index 0000000..af2652e Binary files /dev/null and b/docs/vol2/image/ch43/43-1.jpg differ diff --git a/docs/vol2/image/ch43/43-2.jpg b/docs/vol2/image/ch43/43-2.jpg new file mode 100644 index 0000000..26853b9 Binary files /dev/null and b/docs/vol2/image/ch43/43-2.jpg differ diff --git a/docs/vol2/image/ch43/43-3.jpg b/docs/vol2/image/ch43/43-3.jpg new file mode 100644 index 0000000..e84b9a7 Binary files /dev/null and b/docs/vol2/image/ch43/43-3.jpg differ diff --git a/docs/vol2/image/ch43/43-4.jpg b/docs/vol2/image/ch43/43-4.jpg new file mode 100644 index 0000000..a896f97 Binary files /dev/null and b/docs/vol2/image/ch43/43-4.jpg differ diff --git a/docs/vol2/image/ch45/45-1.jpg b/docs/vol2/image/ch45/45-1.jpg new file mode 100644 index 0000000..701d5e7 Binary files /dev/null and b/docs/vol2/image/ch45/45-1.jpg differ diff --git a/docs/vol2/image/ch48/48-1.jpg b/docs/vol2/image/ch48/48-1.jpg new file mode 100644 index 0000000..5126b55 Binary files /dev/null and b/docs/vol2/image/ch48/48-1.jpg differ diff --git a/docs/vol2/image/ch48/48-2.jpg b/docs/vol2/image/ch48/48-2.jpg new file mode 100644 index 0000000..394f06b Binary files /dev/null and b/docs/vol2/image/ch48/48-2.jpg differ diff --git a/docs/vol2/image/cover.jpg b/docs/vol2/image/cover.jpg new file mode 100644 index 0000000..408afce Binary files /dev/null and b/docs/vol2/image/cover.jpg differ diff --git a/docs/vol2/image/tobira.jpg b/docs/vol2/image/tobira.jpg new file mode 100644 index 0000000..a708d63 Binary files /dev/null and b/docs/vol2/image/tobira.jpg differ diff --git a/docs/vol2/index.xhtml b/docs/vol2/index.xhtml new file mode 100755 index 0000000..fadda0e --- /dev/null +++ b/docs/vol2/index.xhtml @@ -0,0 +1,68 @@ + + + + + +Matz Essays Volume 2 + + + + +

Matz Essays Volume 2

+ + +
+

はじめに

+

2003〜2007

+

第31章 探訪Ruby: Rubyの国へようこそ

+

第32章 探訪Ruby: テスト第一主義

+

第33章 探訪Ruby: Wiki Wiki

+

第34章 探訪Ruby: Blogの世界

+

第35章 探訪Ruby: アスペクト指向

+

第36章 探訪Ruby: RubyとEmacs

+

第37章 探訪Ruby: Instiki

+

第38章 探訪Ruby: テンプレート

+

第39章 探訪Ruby: DBM

+

第40章 探訪Ruby: tDiary

+

第41章 探訪Ruby: Webアプリケーションの基礎

+

第42章 探訪Ruby: Webアプリケーションの基礎(その2)

+

第43章 探訪Ruby: Webアプリケーションフレームワーク

+

第44章 探訪Ruby: マークアップ・マークダウン

+

第45章 探訪Ruby: ダイコン

+

第46章 探訪Ruby: 最終回・ネタのタネ

+

第47章 まつもと ゆきひろのハッカーズライフ: ハッカーとの遭遇

+

第48章 まつもと ゆきひろのハッカーズライフ: キーボードへのこだわり

+

第49章 まつもと ゆきひろのハッカーズライフ: ハッカーと仕事

+

第50章 まつもと ゆきひろのハッカーズライフ: Emacs 対 vi

+

第51章 オープンソース開発って何だろう

+

第52章 まつもと ゆきひろのハッカーズライフ: ハッカー環境問題

+

第53章 まつもと ゆきひろのハッカーズライフ: 言語の重要性

+

第54章 まつもと ゆきひろのハッカーズライフ: 言語の重要性 その2

+

第55章 まつもと ゆきひろのハッカーズライフ: ハッカーとオープンソース

+

第56章 まつもと ゆきひろのハッカーズライフ: 測定狂時代

+

第57章 まつもと ゆきひろのハッカーズライフ: ソースを読もう

+

第58章 まつもと ゆきひろのハッカーズライフ: Let’s Talk Lisp

+

第59章 まつもと ゆきひろのハッカーズライフ: スケーラビリティ

+

第60章 まつもと ゆきひろのハッカーズライフ: オープンソースライセンス

+

第61章 まつもと ゆきひろのハッカーズライフ: Get Thing Done

+

第62章 まつもと ゆきひろのハッカーズライフ: 若人への手紙

+

第63章 まつもと ゆきひろのハッカーズライフ: オープンソースのマーケティング

+

第64章 まつもと ゆきひろのハッカーズライフ: キャズム

+

第65章 まつもと ゆきひろのハッカーズライフ: 言語の壁

+

第66章 まつもと ゆきひろのハッカーズライフ: スケーラビリティ

+

第67章 まつもと ゆきひろのハッカーズライフ: 理系・文系

+

第68章 まつもと ゆきひろのハッカーズライフ: 美しいコード

+

第69章 まつもと ゆきひろのハッカーズライフ: オープンソースよ、永遠に

+

初出一覧

+

索引

+

奥付

+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-001.xhtml b/docs/vol2/xhtml/p-001.xhtml new file mode 100644 index 0000000..e493bbe --- /dev/null +++ b/docs/vol2/xhtml/p-001.xhtml @@ -0,0 +1,28 @@ + + + + + +Volume 2 + + + + +

Matz Essays Volume 2

+ + +
+ +

2003〜2007

+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-002.xhtml b/docs/vol2/xhtml/p-002.xhtml new file mode 100644 index 0000000..c394df3 --- /dev/null +++ b/docs/vol2/xhtml/p-002.xhtml @@ -0,0 +1,449 @@ + + + + + +第31章 Rubyの国へようこそ + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay31 +
+

+探訪Ruby
+Rubyの国へようこそ +

+
+

[Linux magazine, 2003年12月号]

+
+

今はなき『Linux magazine』(アスキー・メディアワークス)2003年12月号からの連載記事です。ちょうど前の連載が11月号で終わっているので、間を空けずに連載開始ですね。前回の連載である「初等Ruby講座」が初心者向けだったのと比べて、中級者程度を仮定して原稿を書いています。とはいえ、初回ですから今回はRubyの概要を説明している程度ですね。背景となる哲学まで解説しているのは、Ruby作者が執筆する連載ならではと言えるかもしれません。

+

前回の連載に引き続き、コラム「Ruby開発日記」は継続しています。今回はオープンソースとビジネスがテーマになっています。

+
+
+

今年でRubyが誕生してから10年になります。その間にRuby言語とそれを取り巻く世界はどんどん広がってきました。この連載では、Rubyの作者という立場からRubyの世界のあちこちを探検して、新しいもの、珍しいものなどを紹介しようと思います。

+
+
+

Rubyの紹介

+
+

数年前と違い、今では書店に行けば「Ruby」とタイトルに入った本が数冊は並んでいますから、本誌の読者であれば全然聞いたことはないという人のほうが少なくなっているかもしれません。10年前に私が作った「おもちゃ」がここまで広まっているのを見ると驚きを通り越して不思議な感じがします。

+

でも、実際にRubyを使うところまではいかない人も大勢いらっしゃるようです。実際、私に会って、「Ruby使おうと思ってはいるんですけど」とか、「興味はあるんですけど」とかおっしゃる方はたくさんいます。「けど」がつきまとううちははまだRubyの本当のうれしさが伝わっていないのかもしれません。

+

さて、話を元に戻して、今回は初回ですから、まずRubyとはいったい何かという点から始めたいと思います。

+
+

一言で言ってしまうとRubyは、

+
+

オブジェクト指向プログラミング言語

+
+

です。つまり、プログラムを作るときに、コンピュータの仕事の手順を記述するための「言語」です。人間の使う言語と違って、融通の利かないコンピュータにも理解してもらう必要がありますからあいまいな部分があってはいけません。同じような理由から語彙ご|いも非常に限られています。プログラミング言語にはRubyの他にもBASIC, FORTRAN, COBOL, C, C++, Pascal, Lisp, Perl, Java, Smalltalkなどがあります。

+

Rubyの特徴は手軽さです。Rubyのプログラムは簡単に書いて簡単に実行できます。たとえば定番の"hello world"プログラムをRubyで書くと、

+
+
print "hello world\n"
+
+

と1行で書けます。意味も見ただけで明らかです。これを実行するのも、このプログラムがhello.rbというファイルに格納されているとして、

+
+
ruby hello.rb
+
+

だけです。Rubyはインタプリタ型言語ですからプログラムを読み込んですぐに実行するからです。

+

ところがこれをCで書くと

+
+
#include <stdio.h>
+int
+main(int argc, char **argv)
+{
+  printf("hello world\n");
+}
+
+

と行数換算で6倍、バイト数でも3倍以上にもなります。本質の部分(printfの行)の内容はほぼ同じですから、残りは「おまじない」ということになります。これがJavaならもっと増えます。

+
+
public class HelloWorld {
+  public static void main(String[] args) {
+    System.out.println("Hello World");
+  }
+}
+
+

Javaプログラムはバイト換算ではRubyの5倍近くなります。「なんでこんなにおまじないが……」と思わせます。

+

また、これらの言語ではプログラムを実行する前に、コンパイラによって実行形式に変換する必要があります。コンパイラによる変換には数分から場合によっては数時間も必要になりますから、すぐ実行というわけにはいきません。そのぶん、実行は高速だったりするんですけど。

+

Rubyのありがたい点はそれだけではありません。プログラムを書いていて、何もかもをゼロから書くのは現実的ではありません。他の人が作ってくれた機能をライブラリとして利用して、それらを組み合わせてプログラムを作るのが普通でしょう。ですから、言語の優劣は単なる文法の違いだけではなく、どれくらい使いやすいライブラリがそろっているかという点によって決まります。ただ単にいろいろなライブラリがあるだけでは十分ではなく、それらがきちんと整理されていて、使いやすい必要があります。

+
+

Rubyの場合、文字列や配列などの基本的なデータ構造とそれに関連する手続きはクラスという単位で整理されていて非常に強力です。

+

例としてRubyでの時刻の取り扱いを見てみましょう。Rubyでは時刻はTimeクラスで取り扱います。現在時刻を表示させるにはこのようにします。

+
+
puts Time.now
+# => Wed Oct 15 12:23:19 JST 2003
+
+

では、Rubyが誕生してから(正確にはその日の午前0時から)、今まで何秒過ぎたかということは、以下のように計算します。

+
+
puts Time.now - Time.local(1993,2,24)
+# => 335708687.362405
+
+

えーと10月15日現在で3億3570万8687秒ですか。Timeは内部的にはマイクロ秒単位で時刻を管理していますので小数点以下の端数が表示されています。

+

基本的なデータ構造以外のライブラリも、データベース、ネットワーク、CGI、XMLなどなど充実しています。先輩であるPerlやPythonに比べると数という点ではまだ少々負けていますが、主要な機能は網羅していると思います。

+

Rubyの最大の特徴はそれがフリーソフトウェアであることかもしれません。Rubyは「自由なソフトウェア」として、すべてのソースコードが完全に無償で公開されています。利用についてまったく費用がかかりません。書籍などを買えば別ですが。開発者である私に対しても、謝礼や代金を払う必要はありません。

+
+
+

昔々のお話

+
+

昔々、1990年代初頭、私はPerlを使っていませんでした。しかし、同僚は「Perlっていいぞ」と勧めてきます。当時私が使っていたのは、シェル(bsh)、AWK、そしてEmacsです。コマンドによる処理を自動化させるためにはシェルを使い、もうちょっと複雑な処理が必要なときにはシェルスクリプトの中でAWKを使いましたが、実は当時の私はほとんどの処理はEmacsで行っていました。10年前、すでにEmacsは私の一部でした。テキスト編集はもちろん、メールを読むのも、書くのも、ネットニュースの読み書きも、Emacsで行っていました。それだけではありません。少々複雑な処理が必要な場合にはEmacsのマクロを使い、もっと複雑な処理が必要ならばEmacs Lispでプログラムを組んでいました。メールリーダーさえ、Emacs Lisp製の自作のものでした(cmail)。これは今でも使っています。

+

そんな私が同僚に勧められてPerlについて調べてみたときに感じたのは、お互いに矛盾する2つの印象でした。

+

最初の1つは「これは面白い」というものです。シェルというのはいろんなことができますが、しょせんはコマンドの組み合わせに若干の制御構造を追加したようなものです。言語として考えたときには、あまりほめられたものではありません。ところが、このPerlという言語は外部コマンドを利用しなくてもたいていのことはできるのです。シェルのようで、しかも「より普通のプログラミング言語」という点は非常に魅力的でした。また、ほんのちょっと普通のプログラミング言語への一歩を踏み出すだけで、どれほどいろんなことができるようになるか、というのは感動的でさえありました。

+
+

ところが同時に大きな不満も感じました。私は高校時代からどういうわけかプログラミング言語に非常に興味を持ち、優れた言語を追い求めてきました。興味がつのってとうとう大学ではプログラミング言語の研究室に所属したほどです。ここで私はいろいろな経験をしましたが、それはまた別の機会に。

+

とにかく、そういう「言語おたく」の視点からみると、Perlという言語はどうにも手抜きというか、半端というか、正直いうと出来が悪い言語という印象でした。この分野において「普通のプログラミング言語」がこんなにも強力なのであれば、「よいプログラミング言語」があれば、それはすごくすばらしいものになるのではないだろうか。そう思ったのがすべての始まりだったのです。

+

私は、もともと「いつかは自分の言語を作ってやろう」と考えていました。大学の卒業論文も独自のオブジェクト指向言語の設計と実装がテーマでした。これは完成の域には至りませんでしたが。就職後しばらくなりをひそめていた創作意欲がわき上がります。「言語を作ろう」。1993年の始め頃です。

+

それから後は、名前が決まり、自分の作業ディレクトリに広がっていた過去に書いていた言語処理系のソースコードを切ったり張ったりして、曲がりなりに動くまで数カ月、普通のプログラミングに使えるようになるまではさらに数カ月、1993年の終わり頃にはとりあえず使えるようなものができていました。

+

とはいえ、私自身はこれを「自分のおもちゃ」と感じていましたので、他人に使ってもらう決心が付いてアルファテスターを募集したのは1994年の12月、インターネットで一般公開したのはさらにその1年後の1995年12月でした。

+
+
+

Rubyを取り巻く世界

+
+

こんなふうに始まったRubyの物語ですが、Rubyを使ってみたいという人が増えるにつれて、それを取り巻く世界はどんどんどんどん広がっていきました。

+

Rubyの世界を図に書くとこんな感じでしょうか(図31.1)。かなり醜いですが、扇形の中心に「コア」、その外側に「添付ライブラリ」と「添付拡張ライブラリ」、その外側に「ライブラリ」、一番外に「アプリケーション」がくる図になっています。

+
+ +
+ fig3101 +
+

図31.1●Rubyの世界

+
+

中心にあるのが「コア」です。これはRubyの本体で、文法とそれを解釈実行するインタプリタからなっています。これは「Rubyそのもの」と呼んでもよいですが、実際のところRubyの世界全体からすれば小さな領域です。私にとっては一番面白いところで、Rubyの世界の中で私の「居住地」はやはりこの周辺になるでしょう。

+ +

コアはいくつかの部品から構成されています。Rubyプログラムを解釈し構文木(こうぶんぎ)と呼ばれる内部構造に変換するパーサー(parser)、その構文木をたどりながら実行するエバリュエータ(evaluator)、実行中にオブジェクトを作り出したりいらなくなったオブジェクトをリサイクルしたりするガベージコレクタ(garbage collector)などです。これらの部品はみなCで書かれたプログラムです。

+

コアのすぐ下にあるのが「組み込みライブラリ」です。組み込みライブラリは、Rubyのライブラリのうちインタプリタに初めから組み込まれているもので、これもCで記述されています。これらはコアの機能を利用して実行されます。

+

Rubyの組み込みライブラリに含まれるクラスのうち代表的なものを表31.1に示します。

+
+

表31.1●組み込みクラス(抜粋)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
クラス説明クラス説明
Array配列Kernel共通関数モジュール
Bignum多倍長文字列Marshalオブジェクトシリアライズ
ClassクラスのクラスMath数学関数モジュール
Comparable比較機能モジュールModuleモジュールのクラス
DirディレクトリNumeric数のクラス
Enumerable数え上げ機能モジュールObjectオブジェクト
Exception例外Range範囲クラス
FileファイルRegexp正規表現
Fixnum小整数String文字列
Float浮動小数点数Struct構造体
GCガベージコレクタSymbolシンボル
HashハッシュThreadスレッド
IO入出力(Fileの親クラス)Time時刻
Integer整数
+
+

コアと組み込みライブラリとでインタプリタを構成します。

+

組み込みライブラリの外側には「添付ライブラリ」があります。添付ライブラリとはインタプリタに組み込まれてはいないが、Rubyインタプリタとともに配布されているライブラリです。

+

組み込み以外のライブラリには、Cで実装されたものと、Rubyで実装されたものがあります。使う側からはどちらもまったく同じように使えるのですが、あえて区別する場合にはCで実装されたものを拡張ライブラリと呼びます。拡張ライブラリは、既存のライブラリとRubyの橋渡しをするもの(ラッパーとも呼ぶ)や、インタプリタでは間に合わない高速な処理を実現するためのものなどがあります。

+

Rubyの添付ライブラリのうち、代表的な拡張ライブラリを表31.2に、Rubyで記述されたものを表31.3に示します。

+ +
+

表31.2●添付拡張ライブラリ(抜粋)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ライブラリ説明ライブラリ説明
Win32APIWin32システムコールの呼び出しiconviconvによる文字コード変換
bigdecimal多倍長浮動小数点数nkfnkfによる文字コード変換
cursescursesライブラリラッパーreadline行入力編集
dbmdbmライブラリラッパーsocketネットワークソケット
digest/md5MD5ハッシュ値(SHA1などもあり)stringioStringIO
dlC関数の呼び出しtkTkインターフェイス
etcユーザー情報の取得win32oleWin32OLE
fcntlfcntl用定数zlibzlib圧縮ルーチン
+
+
+

表31.3●添付ライブラリ(抜粋)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ライブラリ説明ライブラリ説明
benchmarkベンチマークnet/smtpSMTP処理
cgiCGIプログラミングnet/telnetTelnet処理
complex複素数observerオブザーバーデザインパターン
csvCSVファイルの読み書きopen-uriURI
date日付optparseコマンドライン引数解析
debugデバッガparsedate日付解釈
delegateデレゲーターデザインパターンppデータ整形
drb分散Ruby(distributed ruby)profileプロファイラ
erb埋込Ruby(embedded ruby)pstore簡易オブジェクトデータベース
fileutilsファイル操作rexmlXML処理ライブラリ
findディレクトリスキャンscanf文字列解析
ftoolsファイル操作tempfile一時ファイル
irb対話Ruby(interactive ruby)test/unitユニットテスト
matrix行列threadスレッド支援
net/ftpFTP処理uriURI処理
net/httpHTTP処理webrick汎用インターネットサーバー
net/imapIMAP処理xmlrpcXML RPC
net/popPOP処理yamlYAMLライブラリ
+
+

インタプリタの外側には、世界中のRubyユーザーが作ったライブラリとアプリケーションが存在しています。Rubyを使ったプログラムはあらゆる領域に及んでいます。その多くはRAA(Ruby Application Archive)からアクセスできます(図31.2)。

+
+ +
+ fig3102 +
+

図31.2●RAA(http://raa.ruby-lang.org/)

+
+

RAAは開発者が自主的に登録したアプリケーションやライブラリなどのインデックスサイトです。RAAは情報を提供するだけで配布や検証などは行いません。原稿執筆時点で、340アプリケーション、629ライブラリが登録されています。

+ +

RAAのカバーする領域はさまざまです。FoxやwxWindowのようなGUIツールキットもあれば、PostgreSQL, MySQLなどのデータベースアクセスライブラリ、WeblogやWeb日記のようなWebベースのアプリケーションとそれを支援するライブラリ、テキスト処理、数値処理、人工知能、バイオインフォマティックスなどなどなど。実に192のカテゴリにおよぶ1011のプロジェクトがそろっています。

+

この連載では今後RAAから興味深いプロジェクトを選び出して紹介することにも挑戦したいと考えています。

+

とはいえ、RAAには欠点もあります。RAAはあくまでも自主的に登録されたものですから、分類や説明などに不備があるものや、古くてメンテされていないものも残っています。ですから、ほしいものを探すときにはカテゴリだけではなく、検索を利用する必要もあります。たぶん、管理者によるカテゴリの再分類やレーティング(優れたプログラムへの投票)などの機能が付け加わればもっとよくなると思うのですが。

+
+
+

思想と哲学

+
+

ここまでRubyの世界を概観してきましたが、実はRubyの世界にはもう1つ隠された領域があります。地下世界とでも呼ぶのでしょうか。

+

それは「思想と哲学」です。

+

私がRubyの作者として知られるようになったここ数年、あちこちのイベントやカンファレンスで講演を依頼されるようになりました。それらの講演で使った発表資料の一部は、

+
    +
  • http://www.rubyist.net/~matz/slides/

  • +
+

で見ることができます。これをご覧になった人は奇妙に思われるかもしれない点があります。それは「Rubyそのものについてほとんど説明していない」点です。資料の中にはRubyのプログラムが1行も登場しないものもあります。

+

実は、私は単なるプログラミング言語としてのRubyよりも、その背後にある思想やら哲学やらのほうによっぽど興味があり、機会があればそのことについて話したいと考えているのです。ですから、講演では原則とか設計原理とかそんな話ばかりしているのです。言語開発者からプログラミング言語Rubyの具体的な紹介や機能について聞くことを期待していた聴衆には迷惑な話かもしれませんが。

+
+

そんなRubyの「思想と哲学」というのはどのようなものでしょうか。一言でいうと「人に優しいプログラミング言語」です。そして目標は「楽しいプログラミング」です。

+

もちろんプログラミングにおいて最も重要なことは、「なんのためにプログラムするか?」です。プログラミングの目的が何らかの仕事を達成することである以上、これは当然です。「どのような言語でプログラムするか」はそれに比べたら些細ささいなことでしかありません。まともなプログラミング言語であれば、どんなアルゴリズムでも記述可能です。生産性に最も重要なのは言語の文法ではなく、目的に合ったライブラリが存在するかどうかです。

+

しかし、実際にはプログラミングを行う人の「気分」を最も左右するのは、どのような言語で、あるいはどのような環境でプログラムするかです。プログラマーの気分を重視するとき、言語は重要な要素になるのです。

+

Rubyはもともと私の「おもちゃ」として生まれましたが、Rubyをデザインするときに一番気にしていたのは、どのような言語でプログラムしたら自分が一番気分が良いか、ということです。気分というのはなかなか表現しにくいのですが、結局は「思ったことをすぐに実現できる」かどうかということのようです。

+

ですから、Rubyはプログラムをすぐに実行できるインタプリタの手軽さと、やりたいことをすぐに片付けられる強力なクラスライブラリを兼ね備えているのです。

+

強力な機能を持っていても、それを覚えきれなければ仕方がありません。そこでRubyはオブジェクト指向という統一的なルールを導入し、豊富な機能をクラスライブラリという形で分類しました。同じ数の機能を提供するのでも、数百個の関数がフラットに並ぶ構造よりも、機能別にクラスに分けられ、各機能がメソッドとして提供されるほうがずっと理解しやすく覚えやすいだろうという考えからです。

+

また、変数や式に型がないが、オブジェクト自身が自分の型を知っている型システムや、実行時にプログラムの挙動を制御できるリフレクションなどのダイナミックプログラミング機能もそのためです。

+

Rubyの特徴と言ってもよい、変数のスコープがひと目でわかる変数名や、ループやコールバックの抽象化をエレガントに行うことができるブロックも、読みやすく、書きやすい言語の実現を目的としています。

+

その他、ここには書ききれないような細かな点が積み重なってRubyは、私にとって、気分の良い言語になりました。幸いなことに、世界中の数多くのプログラマーも私と同じようにRubyを使うと気分が良いと感じてくれているようです。

+

そして、世界中の人が喜んでRubyを使ってくれていて、今日もRubyの世界は広がり続けているのです。

+
+
+

まとめ

+
+

今回はRubyの世界を概観してみました。来月からは、Rubyの世界のそれぞれの領域から目新しいことを探し出して皆さんに紹介していこうと思います。どうぞお楽しみに。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-003.xhtml b/docs/vol2/xhtml/p-003.xhtml new file mode 100644 index 0000000..f3b9be0 --- /dev/null +++ b/docs/vol2/xhtml/p-003.xhtml @@ -0,0 +1,57 @@ + + + + + +第31章 Rubyの国へようこそ + + + + +

Matz Essays Volume 2

+ + +
+ +

◆ Ruby開発日記 ◆ オープンソースのビジネスストーリー

+
+

連載のタイトルは変わりましたが、このコラムは続きます。

+

世の中にはなぜ成立しているのか素人にはよくわからないが、とにかく成り立っているというようなものがいくつも存在します。私にとってそのようなものの代表格は株式市場です。いや、資金調達のシステムとしての原始的な株式については理解できるのですが、それだけでは現在の株式市場は理解できません。現在の株式市場はもっと複雑な要素が絡み合って成立しているので、日常生活のお金の動きからの連想だけでは全体像をつかめないからです。

+

同じようなことがオープンソースにもいえるのかもしれません。特にソフトウェアでビジネスを行ってきた人々にとっては、飯の種であるソースコードを無償で完全に公開するなど考えられないことに思えるでしょう。

+

しかし、現実にオープンソースソフトウェアが大量に存在し、中には機能的にもソースコード非公開で自由なライセンスに基づかない商用ソフトウェアに匹敵するものも数多くあります。この現実はいったいどうしたことでしょう。

+

その秘密はソフトウェアの特殊性にあります。

+

まずオープンソースを可能にしたソフトウェアの第1の性質は、ソフトウェアの開発費用が他の工業製品に比べると非常に安いことです。もちろんソフトウェアの中には非常に複雑で高価なものもあります。しかし、たとえば自動車を組み立てるための生産ラインのコストを考えると、開発用コンピュータとプログラマーの人件費など安いものです。また、プログラミングの習得が難しいと言っても、他の分野の職人に比べるとまだ容易に身につけられる技術と言ってよいでしょう。よってソフトウェア開発のコストは比較的回収しやすいということになります。

+

第2の性質は昔ながらの工業製品と違って、ソフトウェアは情報にすぎないという点です。そして情報は簡単に複製でき、しかも複製によって劣化しません。パッケージとしてCD-ROMをきれいな箱に入れて店舗販売するならともかく、インターネットで配布してしまえば、新たに配布する場合のコストはゼロです。「ユーザーが一人増えたから売り上げがいくら」というパッケージモデルで考えるから損をするような気になりますが、実際にはユーザーが一人だろうが、千人だろうがコストは同じです。

+

第3の性質はソフトウェア業界の産業構造です。一説によるとソフトウェア産業の内訳はパッケージが1割、自社向けが3割、受注ソフトが6割なのだそうです。つまり、極端な言い方をすればソフトウェア産業の9割は顧客の個別のニーズを満たすカスタムメイドのソフトウェアを開発しているのです。であれば、そのカスタムメイドのソフトウェアの一部を汎用コンポーネントとして公開することによるリスクはそれほど高くありません。

+

第4の性質はソフトウェアは共同開発が可能な点です。ソフトウェアはモジュール分割や分業によるチーム開発が可能ですし、規模の大きなソフトウェアでは必然でさえあります。CVSのようなツールを利用すれば共同開発はますます容易になります。

+

これらの性質を前提条件とすると、以下のようなストーリーを導くことができるのではないでしょうか。

+
+

ソフトウェア産業の大部分のプレイヤーは、顧客のためのカスタムソフトウェアを安価に、しかも信頼性高く実現したいと考えています。しかし、ソフトウェアに対する要求は次第に高度化しており、その結果ソフトウェアはどんどん複雑になってきています。このようなソフトウェアを開発するには大きなコストがかかります。よっぽど規模の大きな企業でなければ、そのようなソフトウェアを自社だけで開発するのは不可能です。そこで既存のソフトウェアコンポーネントを利用せざるをえません。

+

ソフトウェアコンポーネントを入手する方法は2通り考えられます。開発費を捻出できるような大企業にコンポーネントの開発を任せ、その企業からコンポーネントを購入することにより開発コストの一部を金銭で負担する(従来モデル)か、コンポーネントを共同開発し、無償で自由に利用してもらうことにより、開発にかかる人件費を広く薄く分担する(オープンソースモデル)か、です。

+

オープンソースモデルはコンポーネント利用者(受益者)全員が開発コストを負担するわけではないため、一見不平等に思えるかもしれませんが、上記の第2の性質によって、配布側から見るとユーザーが増えてもコストは変化しないことを思い出してください。であれば、開発に参加してくれる人を招くため、自由にソフトウェアを利用してもらったほうが結局は得ということになります。

+

まとめると、オープンソースモデルの場合、単なる利用者は優れたソフトウェアが無償で、かつソースコード付きで利用できてお得。開発参加者は、利用者としての利点だけでなく、開発に自分の意見をフィードバックさせる機会が得られてお得。メインの開発者は自分の開発したコンポーネントのテスト、デバッグ、機能拡張などに外部の開発者の力を借りられてお得。しかも、優れたオープンソース開発者には、名誉、ブランド、技術力の喧伝なども得られる。自分の技術をデファクトスタンダードにするチャンスも生まれる、と全員にお得なチャンスが与えられるのです。

+

このようにしてオープンソースが最終的に利益をもたらし得るストーリーを構築できます。

+

もちろん、オープンソースだから成功するなんて単純な話ではありません。成功するオープンソースもあれば、失敗するものもあります。また、成功するまでに予想以上に時間がかかるものもあります。しかし、どんなビジネスにもそのようなリスクは付き物ではないでしょうか。

+

今回はビジネス上の損得だけからもオープンソースは意味があることを示してみました。しかし、そもそもオープンソース運動とその原点となったフリーソフトウェア運動は、ソフトウェアの自由を保証するためであったことも忘れてはいけません。みんなの手にソフトウェアの自由を。そして、みんなが得をするような道を。

+
+

謝辞

+

今回のコラムはessaさんの「圏外から一言」、およびsheepmanさんの「羊堂本舗」を参考にさせていただきました。どうもありがとうございます。

+
    +
  • 圏外から一言
    +http://amrita.s14.xrea.com/d/

  • +
  • 羊堂本舗
    +http://sheepman.parfait.ne.jp/

  • +
+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-004.xhtml b/docs/vol2/xhtml/p-004.xhtml new file mode 100644 index 0000000..ac81c0b --- /dev/null +++ b/docs/vol2/xhtml/p-004.xhtml @@ -0,0 +1,323 @@ + + + + + +第32章 テスト第一主義 + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay32 +
+

+探訪Ruby
+テスト第一主義 +

+
+

[Linux magazine, 2004年1月号]

+
+

もう今ではあまり聞かなくなったXP(eXtreme Programming)の中でもテスティングについて解説しています。ここでは網羅的なテスト(特にユニットテスト)の重要性について、熱心に解説しています。が、正直なところ、当時からテスト第一主義に関しては違和感を感じていました。何かというと、私たちは問題を解決するためにソフトウェアを開発したいが、テストが書きたいわけではないということですね。ただ、人類はソフトウェアの正しさを確認するテストよりもよい方法を知らないので渋々テストを書いているわけです。もうちょっと未来であればテストや正しさの確認は人工知能に任せられるようになるかもしれません。

+

「Ruby開発日記」はソフトウェア特許についてです。現代(2024年)ではソフトウェア特許についてあまり話題に上らなくなっていますが、当時はホットなトピックでした。現代でも画像や動画フォーマットではソフトウェア特許のせいで自由な開発が難しくなったので問題解決したわけではないのですが。

+
+
+

最近流行のソフトウェア開発技法であるエクストリーム・プログラミング(通称XP)に含まれるプラクティス(実践内容)の中でもテスティングは最も即効性のあるものです。今月はRubyにおけるテスティングを探検してみましょう。

+
+
+

ソフトウェアテスト

+
+

読者の皆さんの中に職業プログラマーの方がどのくらいいらっしゃるかはわかりませんが、ソフトウェア開発は以下のような手順に従うことが多いものです。

+
+

「ヒアリング」で顧客のニーズを聞き出し、それをコンピュータで実行可能なやり方を見つけ出す「分析」、処理手順を決定する「設計」が続きます。そして「実装」でその設計に従い実際のプログラムを作り、作ったプログラムの各部分が正しく動くことを確認するのが「単体テスト」、プログラム全体が正しく動くことを確認するのが「結合テスト」です。結合テストは顧客がプログラムを受け入れるかどうかを確認するのでアクセプタンスとも呼ばれます。

+

上から順に作業を進めて最後まで行けばめでたく完成の運びになるわけです。このような「上から下へ」流れる開発工程を「ウォーターフォール」型開発と呼びます。しかし、世の中はそんなに甘くはないのです。

+

最初の問題は顧客は自分のほしいものが最初からわかっているわけではないということです。頭の中にあるのは、こんなものがほしいという漠然としたイメージだけです。それを聞き出し、分析し、設計して、「これでいいですね」と念押ししても、実際にモノを見るとイマジネーションが刺激されて、「ここはこうじゃなくて」とか「こんなこともできたらいいな」などの要望が発生します。それでなくても、分析のミス、設計のミスは珍しくありませんから、そのたびに手戻りが発生して、やり直す破目になります。

+

変化はソフトウェア開発の敵であり、そして日常でもあるのです。

+

エクストリーム・プログラミングはそうした状況の中、「変化を受け入れよう」という思想から生まれました。XPには具体的な行動である「12のプラクティス(実践)」があります(表32.1)。

+
+

表32.1●XPの12のプラクティス

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
プラクティス説明
計画ゲーム柔軟に頻繁に計画を見直す
短期リリースシンプルなシステムを素早く構築、たびたびリリース
メタファ比喩によるわかりやすいアーキテクチャを提示
シンプルな設計いつもシンプルに。複雑さは積極的に除去
テスティング常にテストする。顧客は検収テストを書く
リファクタリングシステムの振る舞いを変えずに常にシステムを改善
ペアプログラミングいつも二人のプログラマーが1台のコンピュータに向かう
共同所有誰でもいつでもすべてのコードを変更できる
継続した結合タスクが終了するごとに1日に何度も結合テストを行う
40時間労働週40時間以上働かない。残業しない
オンサイトユーザー顧客をチームに加え、いつでも質問に答えてもらう
コーディング規約ルールに従ってコーディングする
+
+

今回はこれら12のプラクティスのうち、テスティングについて取り上げます。

+
+
+ +

仕様としてのテスト

+
+

ソフトウェアを開発するときにテストするのは当たり前のことです。私はうっかりものなのでよく忘れることがありますが。だから、ただ単にテストするというだけでしたら別に珍しいことではありません。

+

XPのテスティングの特徴は、

+
    +
  • テストを自動化する。テスティングフレームワークを使う

  • +
  • プログラム本体よりも先にテストを書く

  • +
  • プログラム開発中頻繁にテストを行う。たとえばコンパイルを行うたびにテストする

  • +
+

という点にあります。2番目や3番目の特徴はちょっと常識の範囲を超えているような気さえします。この辺がXPの「極端流」の由縁です。

+

プログラム本体よりも先にテストを書くということは、テストする本体がまだないわけですから、やってもしょうがないような気がします。それなのになぜ先にテストを書くかというと、そのテストそのものがプログラムのあるべき姿を示している、いわば仕様であると考えることができるからです。

+

普通、設計段階には仕様書というものがあります。これは日本語で書くことが多いのですが、人間の言語はとかくあいまいなものです。しかし、テストプログラムはプログラムですからあいまい性はありません。テストを書くということはコンピュータが実行可能な形で仕様を決めるということなのです。

+

一度テストプログラムを書いてしまえば、きちんとした仕様があるわけですから、自分がどこまでできたのか、直すべき点がどこなのかいつもはっきりわかりますし、また、仕様変更があっても、プログラム全体が正しく動作することを確認することができます。仕様変更する場合には、もちろん先にテストプログラムを新しい仕様に合わせて書き直してから開発を始めるわけです。

+
+
+

test/unit

+
+

XPのテスティングの特徴の1つはテスト用のライブラリ、テスティングフレームワークを用いることです。Java用にはJUnit、C++にはCppUnitがあり、その他いろいろな言語用にそれぞれのテスティングフレームワークがあります。

+

そしてRuby用にはテスティングフレームワークとしてtest/unitがあります。実は最初のRuby版のテスティングフレームワークはRubyUnitといって、日本人の助田雅紀さんが開発したものがありました。RubyUnitとテスティングをテーマにした書籍『Rubyを256倍使うための本 極道編』(ISBN4-7561-3687-7)も出版されています。

+

一方、test/unitRubyUnitの影響も受けながらNathaniel Talbottが開発したものです。test/unitはRuby 1.8.0以降には標準添付されています。test/unitは先達のRubyUnitに敬意を払っていますから、RubyUnit互換機能を持っています。ですから、test/unitがあればRubyUnitのために書かれたテストプログラムもほぼ完全に動作します。

+

test/unitライブラリの使用例をリスト32.1に示します。

+ +
+

リスト32.1●test/unit使用例

+
require 'test/unit'
+
+class TC_MyTest < Test::Unit::TestCase
+  def setup
+    # テスト前の準備
+  end
+     
+  def teardown
+    # テスト後の後片付け
+  end
+ 
+  def test_foo
+    # テストを行う。
+    # testで始まるメソッドがテストされる
+    assert(cond, 'Assertion was false.')
+    assert_equal(expect, actual, 'unexpected result.')
+  end
+end
+
+
+

"setup""teardown" メソッドはテスト前に実行されます(必要なければ定義しなくてもかまいません)。たとえばテストに用いるファイルの準備やデータベースの接続はsetupメソッドで行い、それらの後始末をteardownメソッドで行います。必要な値をテストメソッド(メソッド名がtestで始まるもの)に渡すためにはインスタンス変数を使うとよいでしょう。

+

テストメソッドでは実際のテスト項目のためにassert(表明)メソッドを使いますが、assertとして用いることができるメソッドのうち主要なものを表32.2に示します。

+
+

表32.2●assertメソッド

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
メソッド説明
assert(値[,メッセージ])値が真か
assert_equal(値1,値2[,メッセージ])値が一致するか
assert_raises(例外[,メッセージ])...ブロックが例外を出すか
assert_instance_of(クラス,値[,メッセージ])クラスのインスタンスか
assert_kind_of(クラス[,メッセージ])クラス(か子孫)のインスタンスか
assert_respond_to(obj,id[,メッセージ])idというメソッドを持つか
assert_nil(値[,メッセージ])nil
+
+
+
+ +

テストのある生活

+
+

テスト第一主義によるプログラム開発は以下のような手順で進みます。

+

まず、実際にプログラムを書く前にテストを書きます。このテストがプログラムの仕様になります。先にも書いたようにテストを書くことでこれから作るプログラムがどんな動きをするべきかが明確になります。

+

テストは、正しく書く必要があります。プログラムがどのように動くべきか確認するテストが正しいテストです。テストはメソッド1つ1つを確認するのがよいでしょう。実際に使われるケースに近いテストを行うのが望ましいとされます。ただし、実際の運用で間違った入力が行われることは珍しくありませんから、正しくない値がきたらきちんとエラーを検出するかとか、普段期待していないケースについてもテストしましょう。いろいろなケースについてテストする必要がありますから、テストプログラムは大きくなる傾向があります。私の知っているほとんどのケースではテストのほうがプログラム本体よりも大きくなっています。

+

テストを書いたら、プログラムの開発を始めます。メソッドを1つ完成させたらテストを実行してそのメソッドが正しく動くことを確認します。正しく動かなければ何か問題があるわけです。よく考えてメソッドを直します。あるいはテストを作るときに何か勘違いをしていて、テストのほうを直す必要があるかもしれません。修正が終わればまたテストを実行します。テストを全部パスするまでこれを繰り返します。

+

このようにプログラム開発とテストを繰り返し、ついに全部のテストにパスするようになれば、単体テストは完了です。最終的には完成したプログラムがきちんと動作して、顧客(自分自身かもしれませんが)の目にかなうかどうかを結合テストで確認して、晴れてプログラム完成ということになります。

+

このようにテスト第一主義で開発した場合には、プログラムがテストしたとおりに動くことはいつも確認されているわけですから、安心ですし、いくつテストがあってそのうちどのくらいパスしているかがはっきりわかりますから達成感があります。パスするテストが次第に増えていくのは快感で、やる気が増します。あまりに頻繁にテストを行うのでテスト中毒と呼ばれちゃったりすることもあります。

+

ソフトウェア開発の現場では、「動いているソフトウェアはいじらない」という教訓が言い伝えられています。ちょっとした変更が思わぬ副作用を引き起こすことがあるからです。変更してしまったことでバグを組み込んでしまったり、機能が損なわれたりすることを「デグレード」と呼ぶことがあります。ここはあまりきれいなコードでないので直したほうがあとでわかりやすいだろうな、と思っても、副作用が恐くて手出しできないこともあります。このようにまずいコードを直す行為を「リファクタリング」と呼びます。デグレードの危険性があるのでリファクタリングは大変勇気のいる行為です。ところが、きちんとテストが用意されていれば、変更による副作用がないことを確認できます。テストは変更に対する「勇気」を与えてくれます。

+

テスト第一主義が役に立つのは自分でプログラムを書くときばかりではありません。他の人が開発したプログラムを保守するときにも有効です。そのプログラムにテストがある場合にはテストコードを読むことでそのプログラムの仕様を理解できます。

+

では、他人が書いた、テストのないプログラムを保守するときにはどうしましょう。テスト第一主義の立場からいえば、(時間が許すならば)まずそのプログラムのためのテストを書くことをお勧めします。それによってプログラムに残っているかもしれないバグを見つけ出すことができますし、なによりテストがあればそれ以降の変更がない場合とは比べ物にならないくらい楽になります。

+
+
+ +

システム構成の見直し

+
+

テスト第一主義に移行するためにはプログラムの構成そのものを見直す必要があるかもしれません。テスト中毒者はテストのためならなんだってするものです。以下に示すのは、最近私が手伝ったプロジェクトでの話です。

+

このプロジェクトは一種のWebグループウェアの開発でした。しかし、段階的な開発を行ううちにプログラムの構成が複雑になり、保守が大変になってきました。古いシステムの構成を図32.1に示します。

+
+ +
+ fig3201 +
+

図32.1●旧システム構成図

+
+

このシステム構成にはいくつか問題がありました。

+
    +
  • モジュール化されていないので、修正の影響範囲が予想しにくい

  • +
  • ロジックと出力が分離されていないので、外観の変更が難しい

  • +
  • 同じ理由でテストが書きにくい

  • +
  • 既存のデータベーススキーマに依存したコードがあちこちに分散していて将来の変更が難しい

  • +
+

そこでシステム構成を変更して、図32.2のようにしてみました。よくいわれるWebアプリケーションの三層構造である、プレゼンテーション層、ビジネスロジック層、データベース層に似ていますが、もうちょっと細かく分割しています。

+
+ +
+ fig3202 +
+

図32.2●新システム構成図

+
+
+
プレゼンテーション
+

eRubyで書かれたこの部分はグループウェア全体の外観を決定します。外観やグループウェアを構成する各部品(カレンダーとかToDoとか)の構成を変更したい場合には、このファイルを変更するだけで済みます。

+

実物は巨大なので、ここではすごく簡略化した例を載せておきます(リスト32.2)。

+ +
+

リスト32.2●プレゼンテーション例

+
<meta http-equiv="Content-Type" content="text/html; charset=euc-jp">
+<% require "example.rb" %>
+<html><head>
+<title>sample presentaion</title>
+</head>
+<body>
+<table border=1 width="100%">
+<tr><td><%= calendar_html() %>
+<td><%= todo_html() %>
+</tr>
+</table>
+</body>
+
+
+

これはカレンダーとToDoを表示するWebアプリケーションです。ここではWebアプリケーションの実体はexample.rbにあることとします。calendar_html()todo_html()がそれぞれ部品のHTMLを出力します。

+
+
+
HTML生成
+

この部分は各部品のHTMLによる外観を決定します。下層にあるビジネスロジックから与えられたデータ(配列や文字列など)をHTMLに変換します(リスト32.3)。

+
+

リスト32.3●HTML生成

+
def todo_html()
+  str = "<ol>"
+  for todo in todo_logic()
+    str << "<li>"
+    str << todo
+  end
+  str << "</ol>"
+  return str
+end
+
+
+

リスト32.3の例ではビジネスロジックに属するtodo_logic()メソッドが返したtodoを表現する文字列の配列を元にしてHTMLを生成しています。

+
+
+
ビジネスロジック
+

ビジネスロジックはアプリケーションの「what」の部分を担当します。上記の例であれば、カレンダーの日付を計算する、とかToDoをリストするとかがロジックになります。

+ +

ビジネスロジックはHTMLを直接生成するのではなく、返したいデータそのものを返すようにします。このようにすることで、外観と論理を分離することができますし、なによりロジック部分だけをテストすることができるようになります。テスト第一主義者としてはロジックのテストを行うことができるということは非常に重要です。

+
+
+
DBアクセス
+

今回のアプリケーションはPostgreSQLを使っているので、データベースからのデータの取り出しや書き込みにはSQLを使うのですが、ビジネスロジックの中で直接SQLを使ってデータベースを呼び出すことは今回は避けました。データを取り出したい処理、あるいは更新したい処理ごとにメソッドを定義します。

+

その理由は大きく分けて2つあります。1つは将来データベースに関する大きな変更を加えたときに修正が1箇所にまとまっていたほうがわかりやすいことです。極端な話、将来リレーショナルデータベースではなく、オブジェクト指向データベースに移行するかもしれません。直接SQLを書き込んでいれば、そのような部分を探し回って修正する必要があります。

+

もう1つの理由はテストです。更新を伴うデータベースのテストは副作用があるのでなかなか難しいところがあります。しかし、この部分を独立させておけば、とりあえずそれらしいテスト用の結果を返すダミー(Mockと呼びます)に置き換えてテストすることができます。Mockはテストによく使われる手段です。

+
+
+
データベース
+

一番下の層がデータベースです。先にも述べたように今回はPostgreSQLを用いました。データベースは以前のバージョンでも使われていたのでスキーマその他はそのままです。

+
+
+
新構成のメリット
+

このような新構成にすることで、プログラムが役割ごとにモジュール化され保守や更新がより簡単になります。また、以前は難しかったWebアプリケーションのユニットテストを行うことができるようになります。アプリケーションの機能そのものを実現するビジネスロジックの部分は、単なるメソッド呼び出しですから、通常のテストプログラムの手法でテストできます。機能がきちんと実現されていれば、プレゼンテーションおよびHTML生成については、結合テストにおいて目視で確認できます。

+

こういう構成にする場合、各モジュールへの機能の振り分けを適切に行うことが重要です。それぞれのモジュールが持つべき機能をよく考えて設計すべきです。実際、今回のケースでも機能の分割を間違えていて数回構成の見直しが必要になりました。

+

原稿執筆時点では、このプロジェクトはまだ完了していません。コンサルタントとして参加した私としては、これで成功するといいなあと心から願っています。

+
+
+
+

生活を改める

+
+

「わかっちゃいるけどやめられない」というようなことはいろいろあるものです。頭では理解しているけれども、心では理解していないってことなんでしょうか。

+

私にとってはテスティングはそのような「頭では理解しているが心では理解していない」ことの1つです。ソフトウェアを開発する際に今回自分で説明したように、まずテストを書いてから開発にかかり、コンパイルするたびにテストを実行するという手順を踏めばよいことはわかっているのに、面倒だったり、つい昔ながらのやり方で押し通してしまうことがたびたびです。私が開発している最も大きなプログラムは実はRubyそのものなんですが。

+
+

しかし、最近になってようやっとそんなことではいけないと思い始めました。皆さんの協力もあってRubyそのもののテストも整ってきました。変更や改善を行うたびにテストを行い見直す習慣が次第についてきたようです。

+

それでもまだときどき忘れちゃうんですけど。

+
+
+

まとめ

+
+

今回はXPのプラクティスの1つ、テスティングを中心に説明しました。あなたもテスト第一主義でソフトウェア開発してみませんか? 今までとは違う感覚が味わえるかもしれませんよ。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-005.xhtml b/docs/vol2/xhtml/p-005.xhtml new file mode 100644 index 0000000..b95dbd2 --- /dev/null +++ b/docs/vol2/xhtml/p-005.xhtml @@ -0,0 +1,61 @@ + + + + + +第32章 テスト第一主義 + + + + +

Matz Essays Volume 2

+ + +
+ +

◆ Ruby開発日記 ◆ オープンソースと特許

+
+

発明を行うためにはそれなりの苦労が必要です。その苦労した発明が簡単に真似されてしまっては発明しようという意欲がなくなってしまうでしょう。生活も維持できないかもしれません。

+

ですから、その苦労に報いて発明を奨励し、人類の進歩発展に貢献するために、発明を保護する制度が生まれました。これが特許です。特許の歴史は古く、特許制度の原形は紀元前600年頃の古代ギリシャにさかのぼるのだそうです。この頃のシバリスという都市では、特別な料理を創作した料理人にはその料理の1年間の独占が認められていたのだそうです。また、イギリスには17世紀にはすでに現在の形に近い特許制度があったということです。

+

人類の進歩発展に貢献しようという特許の理念はすばらしいものです。特許は情報の公開の見返りに、発明者に一定期間の独占権を与えることでインセンティブを与えようというものです。

+

しかし、特許は発明者(出願者)に出願から20年もの間独占的な権利を与えます。独占的な権利とは、その発明の実施に当たり対価を取ることも、実施を禁止することも自由という権利です。それが問題に感じられるときもあるのです。特にソフトウェアが対象になるときには。

+

日本では長らく発明全体の一部にソフトウェアが用いられている場合を除いて、ソフトウェアは特許の対象にはなっていませんでした。ソフトウェアは「モノ」ではなく、日本の特許法の前提である「自然法則を利用した発明」と見なされなかったからです。

+

しかし、最近の法改正により実体でないソフトウェアそのものに特許を認めるように制度が変更されました。ソフトウェアビジネスにとって特許という強大な権利はありがたいもので、知的所有権の強化の当然の結果といえるかもしれません。

+
+
文化の衝突
+

オープンソースも情報を公開することによって、人類の進歩発展に寄与しようという試みです。情報を公開しようという点では特許に似た側面もあります。

+

ところが先ほども述べたように特許には20年にも及ぶ強大な独占が伴いますから、オープンソースにとって都合が悪い点がいくつかあります。

+
+

これらの点がただちに問題になるわけではありません。ただ、使い方によってはオープンソースにとって非常に都合が悪い事態が発生する可能性があります。知的所有権がますます強化される傾向がある昨今、このままでは悪意のないオープンソース開発者が特許紛争に巻き込まれ、裁判の被告になってしまう事態もありえないこととは言い切れません。オープンソース開発者の一人として私も漠然とした不安を感じずにはいられません。

+
+
+
対抗処置
+

それでは私たちはいったいどうしたらよいのでしょうか。

+

私自身もまだまだ不勉強です。最近になってやっと工業所有権の教科書を少し読んでみたりした程度です。ですから、はっきりとした結論を得ているわけではないのですが、現時点でも以下のような対策が考えられると思います。

+

1つは自分たちのアイデアをどんどん公開して公知(誰もが知っている状態)にすることことです。公知のアイデアには特許が成立しませんから。

+

ただ、公知化の手順はいろいろ面倒で、有効な手段についていろいろ考えたのですが、オープンソース開発者にとって、やはり最も効果的なのはソースコードによってだと思います。つまり、どんどん開発して、どんどん公開していくことで、そのソースコードに含まれるアイデアを公知化していくのが一番よいのではないかと思いました。ソースコードは日付の情報とともに世界中に配布されますから、おそらくは公知の条件を満たすと思います。裁判や審判を経ないと断言はできませんが。

+

もう1つ、私たちが自発的にできることは、オープンソース開発に当たって危なそうなソフトウェア特許をあらかじめリストアップしておくことです。個別の開発者が特許調査を行うのは現実的ではありませんが、特許というのは建前ではその内容が公示されていることになっていますから、危ない特許をリストする行為そのものは、コストを別にすればそんなに難しい行動ではありません。あとはそのコストをどう分配して負担するかですね。海外ではFFII(Foundation for a Free Information Infrastructure, http://www.ffii.org/)がそのようなプロジェクトをすでに始めているようです。

+

これらの対策は比較的容易に始められるはずなので、近い将来実行に移していこうと思います。

+

より長期的には、ソフトウェア特許をオープンソースに対して敵対的に使うよりも、協調したほうが有利であるという社会的な雰囲気を作り出していくことが有効でしょう。

+

それだけでなく、最近ヨーロッパで行われたように、どんなものに特許に適用すると望ましくないのか、どんなものが特許にふさわしいのかについて、法律や経済の専門家を交えた徹底した議論も必要だと思います。

+

さて、そんなことをどうやって始めたらよいのかしら。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-006.xhtml b/docs/vol2/xhtml/p-006.xhtml new file mode 100644 index 0000000..5b03979 --- /dev/null +++ b/docs/vol2/xhtml/p-006.xhtml @@ -0,0 +1,356 @@ + + + + + +第33章 Wiki Wiki + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay33 +
+

+探訪Ruby
+Wiki Wiki +

+
+

[Linux magazine, 2004年2月号]

+
+

そういえば純粋なWikiページを見かけることはなくなりましたね。むしろWikipediaのことを省略してWikiと呼ぶのを聞くことが増えたかもしれません。誰でも気付いたときに情報を更新できるというWikiの性質は、インターネットユーザーが増大した現代ではノイズが多すぎてしまうということなのかもしれません。紹介しているWikiシステムもほとんどがリンク切れです。牧歌的な時代は終わってしまったということでしょうかね。

+

「Ruby開発日記」は「日本Rubyの会」についてです。開発コミュニティとは別に、イベントの主催などを支援する組織としての日本Rubyの会の発足について語っています。現在でも日本Rubyの会はユーザーコミュニティとして活発に活動しています。

+
+
+

この連載でも前の連載でも今までずっと直接プログラム開発に関係するテーマばかり見てきましたが、今月からしばらくユーザーの視点からRubyで記述されたプログラムをどう使うかという観点でRubyの世界を探検します。今月のテーマはWikiです。

+
+
+

Wikiとは何か

+
+

Wikiとは一言でいうと「誰でも自由に編集可能なWebページ群」です。もともとはWard Cunninghamがデザインパターンに関する情報を集めようとして始めたものです。cs.com(http://c2.com/cgi/wiki)にあるオリジナルのWikiには数え切れないほどのいろいろな情報があります。

+

聞くところによると最初のWikiは350行程度のPerlスクリプトだったのだそうです。c2.comは今でも300ドルくらいの自作機で自宅のDSLを使って運用しているそうです。

+

Wikiとはハワイの言葉で「迅速」という意味があるそうです。Ward Cunninghamは最初このシステムを「QuickWeb」と名付けようとしていたそうですが、個人的には「Wiki」という名前にしたのが正解だった思います。短くて印象に残りやすくいい名前です。やっぱり名前は重要ですよね。

+
+

Wikiの歴史や詳細についてはWiki唯一の解説書、Bo Leuf, Ward Cunningham著『Wiki Way — コラボレーションツールWiki』(ソフトバンクパブリッシング、ISBN4-7973-1832-5)を参照してください。

+
+
+

Wikiの特徴

+
+

Wikiには以下にあげるような特徴があります。

+
+

ページの追加・編集が自由

+

Wikiのページを見るとどこかに「Edit this page」とか「編集」いうようなリンクがあります。そこをリンクするとページの内容を含むテキストフィールドが現れます。適当に編集した後、「Save」ボタンを押すとあら不思議、その編集結果が反映されてしまいます。

+

誰でも情報を追加できるという点でWikiはWeb掲示板に似ています。ただ、Wikiではただ単に時系列に沿ってコメントを追加していくだけでなく、新しいページを作ったり、既存のページを編集することが自由にできます。ですから、掲示板などに比べて極端に自由度が高いということができます。逆に自分で意識しないと時間の情報が保存されませんし、誰が書いたのかさえわからないので、注意が必要です。

+

書式は簡単なマークアップ

+

オリジナルWikiは編集時にHTMLを直接書くのを許すのではなく、簡単なマークアップを使っています(表33.1)。HTMLを直接許すと何でもできてしまいますから、妥当な制約だと思います。Wikiクローンと呼ばれるWiki互換システムでは書式が追加されていたり、あるいはまったく別の書式を採用していたりしますが、これについては後述します。

+

WikiNameによるリンク

+

他のWikiページへのリンクはWikiNameと呼ばれる形式で指定します。WikiNameは英単語の先頭を大文字にして結合した文字列です。プログラミングではCamelCaseと呼ばれる形式ですね。Wikiページではこの形式の文字列は、その名前を持つ別のページへのリンクであると見なされます。もし、その名前のページが存在しなければ、WikiNameの右側に「?」というリンクが表示され、そこをクリックすると、WikiNameのページを編集することができます。

+ +

WikiNameは英語では便利な形式ですが、単語を区切る習慣も大文字小文字の区別もない日本語には向きません。そこで日本発のWikiでは「[[」と「]]」で囲まれたものをリンクと解釈するように拡張されているものが多いようです。

+
+
+

表33.1●Wikiマークアップ

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
マークアップ意味マークアップ意味
WikiNameWikiページへのリンク<tab>項目: 説明定義リスト項目
<空行>段落の区切り <p><行頭空白><pre>
—-水平線 <hr><tab><空白>:<tab>引用
<tab>*リスト項目レベル1”文字列”イタリック指定
<tab><tab>*リスト項目レベル2”’文字列”’ボールド指定
<tab>1番号付きリストURLリンク
+
+
+
+

Wikiの魅力

+
+

Wikiは「誰でも編集可能」という点を除けば、ただ単に簡易なマークアップを用いたWebページにすぎませんから、ほとんどあらゆることに使うことができます。しかし、一番向いているのは協同作業による情報の集積でしょう。もともとのc2.comでの目的もデザインパターンの事例や情報を集めることでした。

+

Rubyホームページ(http://www.ruby-lang.org/ja/)では、リファレンスマニュアルやインストールガイドなどにWikiクローンの一種“RWiki”を用いて情報を最新に保っています。Wikiを使ってみんなで寄ってたかって本の原稿の校正を行った例もあるそうです。

+
+
+

そんなことで大丈夫?

+
+

ところで気になるのが、Wikiの安全性です。

+

単なる掲示板でさえ「荒らし」と呼ばれる行為により、悪口などが書き込まれたり、大量の書き込みによってサーバーが麻痺まひしたり、そこまで行かなくても劣悪な状況に追い込まれる事態が問題になっています。Wikiではメッセージの追加だけではなく、自由な変更を許していますから、より深刻な事態が発生しそうです。ページにゴミが書き込まれたり、内容が消されたりしないのでしょうか? とても不安です。

+

ところが、実際にWikiを運用している人の話を聞くとそれほど問題にはなっていないようです。Wikiという仕組みがあまり知られていないせいではないかと思っていましたが、先日デンマークで開かれたJAOOカンファレンスでWard Cunninghamと会ったときにこのことについて話をしたら、c2.comのような超有名な場所でも「荒らし」のような行為による問題はほとんどないそうです。ただ、大量書き込みでロードアベレージが100を超えた(!)ケースが数度あったので、そういう悪質な書き込みはIPアドレスではねているということでした。「私の非力でかわいそうなマシンをいじめないでくれ」とは彼の弁です。

+

Wikiで意外と問題が発生しない理由の1つは悪意を持った人よりも善意を持った人のほうが割合として多いことではないかと思います。ですから、ちょっとしたいたずら書きが発見されると発見した人が即座に修正してしまって問題が顕在化しないことが原因ではないかと考えられます。目玉がたくさんあると問題が早期に検出され重大化しない、というのはオープンソースにもつながる現象ですね。

+

Wikiに対する荒らしとしては、

+
    +
  • よく参照されるページにゴミを書き込む

  • +
  • 役に立たないページを作る

  • +
  • ページの内容を消してしまう

  • +
+

が考えられます。最初のものは発見した人が即座に消してしまいますし、2番目のものはWebサーバーのデータベースに少々無駄なデータが格納されるだけです。一番問題なのは“最後のページを消してしまう”ですが、これも適当なタイミングでバックアップを取っておけば被害は最小限で済むでしょう。

+
+

Wikiに問題が発生しないもう1つの理由は、Wikiという仕組みで「はいどうぞ、何でもしていいですよ」とあからさまに許された場合、人間はかえって悪いことがしにくい傾向があるのではないか? と私は推測しています。

+

Wikiで運用しているRubyホームページのリファレンスマニュアルの場合も問題はほとんど発生していません。ごくたまに数行ゴミを書き込む人がいますが、すぐに対応されています。Rubyリファレンスマニュアルの場合、すべての修正内容が管理者にメールで報告されるので、より発見が早いのです。

+
+
+

Wikiのもう1つの魅力

+
+

Wikiはプログラマーに対しても魅力的です。Wikiというのは非常に単純な原理で動いていますから、Webシステムとしてはかなり簡単な部類に入ります。このことは最初のWikiがわずか350行のPerlスクリプトであったことからもわかります。簡単な実装で面白いことができるというのはプログラミングのテーマとして優れています。

+

ですから、こんな面白いものをプログラマーが放っておくわけがありません。世の中には自分の好みの機能を追加したWikiクローンと呼ばれるWiki互換システムがそれこそ星の数ほど実装されています。その実装もPerl, Python, Ruby, PHP, Javaなどさまざまです。ただ違う言語で実装しただけでなく、Zopeのようなコンテンツマネジメントシステムの上に構築されたZWikiのようなWikiクローンもあります。

+
+
+

さまざまなWikiシステム

+
+

このようにたくさんあるWikiクローンですが、もちろんRubyで実装されたものもたくさんあります。RAAなどを調べてみると、実に11ものWikiクローンがありました。眺めてみるとそれぞれの思想の違いなどもあって面白いです。

+

では、これら11のWikiクローンをひととおり紹介してみましょう。

+
+

AsWiki(http://aswiki.sourceforge.net/

+

TANIGUCHI TakakiさんによるWikiクローンです。RubyによるテンプレートエンジンAmritaを使っているのが特徴です。RCSによる履歴管理もできます。

+

clWiki(http://clabs.org/ruby.htm

+

Chris Morrisさんによるclwikiはデータをプレーンテキストで格納するのが特徴です。BlogkiというWikiによってBlogを書くプログラムが同梱されています。

+

KakiWiki(http://kakiwiki.homeunix.org/KakiWiki/kaki.rbx

+

sayさんによるkakiwikiはtDiaryテーマが使えることと、国際化が特徴です。同じ名前のページを複数の言語で記述し、ブラウザのAccept-Languageで切り替えることができるそうです。また、ドキュメントツリーをキャッシュするため高速だそうです。

+

MiniRubyWiki(http://www.xpsd.com/MiniRubyWiki

+

PhlipさんによるMiniRubyWikiは、Miniという名に反してWikiとしてひととおりの機能を備えています。また、実験中ですがWikiStyleSheetというWikiそのもの(マークアップ形式とか)をカスタマイズする機能を実装しています。

+
+

RWiki(http://rwiki.jin.gr.jp/cgi-bin/rw-cgi.rb

+

咳さんによるRWikiは、マークアップとしてRDを使うというWikiの中では変わり種です。これについてはあとで説明します。

+

RDocWiki(http://www.sourceforge.net/projects/rdoc-wiki

+

Michael NeumannさんによるRDocWikiは、RWikiがRDを使うようにRDoc形式を使うWikiクローンのようです。また、データをセーブするバックエンドなどを交換できるそうです。ただし、まだソースが公開されていません。RDocはRubyプログラムにドキュメントを追加する方法として最近広く使われている形式です。

+

Ruwiki(http://rubyforge.org/projects/ruwiki/

+

Alan ChenさんのRuwikiはネームスペースをサポートしていて、同じ名前の複数のページを持つことができるそうです。海外では結構使われていると聞いたことがあります。

+

TaoWiki(http://f16.aaacafe.ne.jp/~yand/taowiki/taowiki.cgi

+

a.yさんによるTaoWikiは、他のページをインクルードして部品として組み合わせる機能や、スタイルシートや動的なページも編集する機能があります。面白い。

+

Miki(http://www.mikihoshi.com/miki/

+

kanさんのMikiは比較的小規模なWikiクローンです。標準配布のパッケージだけで動作するのでインストールが簡単です。

+

Tiki(http://todo.is.os-omicron.org/tiki.cgi

+

todo.orgによるTikiはRubyによるWikiクローンの老舗といえると思います。Wikiとしてひととおりの機能をすべてそろえているだけでなく、テーマ、多言語対応、プラグイン機能など充実しています。SourceforgeにはTikiWikiという似たような名前のプロジェクトがありますが、こちらはPHPで実装されています。

+

Hiki(http://www.namaraii.com/hiki/

+

最近登場した竹内仁さんによるHikiは、tDiaryテーマとtDiaryのプラグイン(の多く)がそのまま使える点がウケたのか人気上昇中です。tDiaryの資産をそのまま生かすという戦略が成功していますね。なぜかHikiだけはRAAに登録されていませんでしたが、有名なのでここに含めました。

+
+
+
+

RWiki

+
+

(日本の)Ruby界で最も有名ではないかと思われるWikiがRWikiです。

+

RWikiはいわゆるWiki記法ではなく、RD形式を採用しています。RDはRuby Documentの略で、もともとRuby関係のドキュメントやマニュアルなどを記述するためにPerlのPOD(Plain Old Document)形式を参考に作られた(というか、私が作った)形式です。

+

RD形式はインデントを利用した形式が特徴です(表33.2)。RD形式の文章の例をリスト33.1に示します。

+ +
+

表33.2●RD形式

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ブロック
形式
=見出しレベル1インライン
形式
((<...>))リンク
==見出しレベル2((*...*))強調
===見出しレベル3((...))コード <code>
<空行>段落の区切り((%...%))キーボード入力
<インデント>引用((-...-))脚注
*リスト(箇条書き)
(1)番号付きリスト
:用語定義
+
+
+

リスト33.1●RD形式の文章例

+
= これは見出しです
+
+RD形式はRuby Documentの略です。その特徴は
+
+* プレーンテキストに近い見栄え
+* インデントを解釈する
+* ほどよいマークアップ
+
+です。文章中に((*強調*))や、
+((<リンク|URL:http://www.rubyist.net/~matz/>))を
+入れることができます。
+
+  インデントされた文章は引用です
+  プログラムをそのまま取り込むのに便利です
+
+  void
+  main(int argc, char **argv)
+  {
+      printf("hello world\n");
+  }
+
+箇条書きなどはネストできます。つまり
+
+* このような箇条書きの中に
+  * 次のレベルを埋め込むことができます
+* ネストの状態はインデントで決定されます
+
+これは
+
+(1) 番号付きリストでも
+    (1) 当然有効です
+    (2) あまり使わないかもしれませんが
+
+
+
+

実際、私の最初の本である『オブジェクト指向スクリプト言語Ruby』などの原稿や、本連載の原稿などはRDで記述されています。もっともこれらの原稿での変換は編集の人に手動でお願いしていますが。

+

RWikiのその他の特徴としては、以下のようなものがあり、Wikiとしてはかなり変わり種です。

+
    +
  • WikiName不採用
    +リンクはRDのリンクだけを使います

  • +
  • リンク/逆リンクの表示
    +各ページ末尾に、このページからリンクしているページおよびこのページをリンクしているページの一覧がリストされます

  • +
  • 更新時間の表示
    +上記一覧にはそれぞれ更新時間が表示されます

  • +
  • 「?」の不在
    +普通のWikiでは存在しないページへのリンクには「?」マークが付くのですが、RWikiにはその機能はありません。そのリンクの更新時刻が「-」となるので、存在しないことがわかります。

  • +
+
+
+

RWikiのインストール

+
+

個人的にはWikiとの正しい関わり方は、既存のWikiに書き込むか、自分でWikiシステムを作るかどちらかだと思っているのですが、一応インストールについても説明しておきます。

+

開発者の咳さんによると、RWikiの開発当初の目的として「Rubyライブラリの紹介」というものがあったそうなので、RWikiのインストールには他のたくさんのライブラリをインストールする必要があり、かつてはなかなか面倒な作業でした。しかし、Ruby 1.8になってRWikiが使っていたほとんどのライブラリは標準添付になったのでインストールはずいぶん楽になりました。後必要なものはRDtoolだけです。

+
    +
  • tarファイルの入手先
    +http://www2.pos.to/~tosh/ruby/rdtool/archive/rdtool-0.6.14.tar.gz
    +http://www2a.biglobe.ne.jp/~seki/ruby/rwiki-2.0.1.tar.gz

  • +
+

入手したtarファイルを展開し、READMEの内容に従いインストールします。

+
+
+

RWiki活用事例

+
+

RWikiはRuby日本語公式サイト(www.ruby-lang.org/ja)でいろいろな目的に活用されています。

+
+

RDPはWikiを利用していろいろなライブラリなどのドキュメントを集積しようというプロジェクトです。

+
+
+

Wikiの活用

+
+

このようなWikiをどのように利用するのがよいでしょうか。

+

やはり一番向いているのはオリジナルWikiの対象であったドキュメントの集積でしょう。c2.comには1万5000を超えるページが集積されていて、毎月、数百ページずつ増加しているそうです。

+

ドキュメントの集積といえば、Wikipediaがあります。WikipediaはWikiを利用したWeb百科事典です。誰でも自由に項目を追加できますし、間違いを見つけたら訂正することができます。WikipediaのURLは、

+
    +
  • http://en.wikipedia.org/(英語)

  • +
  • http://ja.wikipedia.org/(日本語)

  • +
+

です。

+

個人のホームページとしてWikiを利用するのもよいでしょう。RWiki公式サイトには個人名のページがいくつもあります。Wikiなので個人ページに他人が「ツッコミ」を書き込むこともしばしば発生し、双方向コミュニケーションが成立しています。

+

『リファクタリング』で有名なMartin Fowlerは彼のBlogをWikiで書いています。名付けてBlikiだそうです。もっとも彼のBlikiは一般ユーザーは更新できませんので、厳密にはWiki的ではありません。注目すべきはこのBlikiの翻訳Bliki_jaで、Hikiで運用されています。こちらは普通のWikiですから自由に書き換えられます。

+
    +
  • Martin Fowler’s Bliki
    +http://martinfowler.com/bliki/

  • +
  • bliki翻訳
    +http://capsctrl.que.jp/kdmsnr/wiki/bliki/

  • +
+

その他にもメモやToDoリストなどにWikiを活用している例はたくさんあります。また関連リンク集と称して、ある特定のトピックに関するWebページへのリンクをWikiを使って集めたものもあり、結構便利に使われています。例として、私も関わった「日本発のオープンソース」の件の反応リンク集をあげておきます。

+
    +
  • http://sheepman.parfait.ne.jp/wiki/cmd=list/

  • +
+

から「日本発のオープンソース」をクリックしてください。

+
+
+ +

まとめ

+
+

今回はWikiについて紹介しました。

+

誰にでも自由に書き換えることができ、今までにないコラボレーションを実現できるWikiには新しいWebのあり方として、応用次第で大きな可能性が広がっています。あなたもWikiを活用してみませんか?

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-007.xhtml b/docs/vol2/xhtml/p-007.xhtml new file mode 100644 index 0000000..0907ac1 --- /dev/null +++ b/docs/vol2/xhtml/p-007.xhtml @@ -0,0 +1,117 @@ + + + + + +第33章 Wiki Wiki + + + + +

Matz Essays Volume 2

+ + +
+ +

◆ Ruby開発日記 ◆ 日本Rubyの会

+
+

先日アメリカのテキサス州オースチンで開催されたRuby Conference 2003に出席してきました。前日にはC++の開発者Bjarne Stroustrup博士に招待されてTexas A&M大学でセミナーを開いたりと忙しい旅行であったのですが、実に有意義でした。

+

Ruby Conferenceそのものはわずか50名弱の出席であったのですが、それでも出席者の質も、またプレゼンテーションの質も良好で、これほど優れたカンファレンスはもっと大規模でお金のかかったものを含めてもなかなかありません。プログラムを表33.3に示します。

+
+

表33.3●Ruby Conference 2003プログラム

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1日目
Working in the Garden: Web Apps with Borges – Eric Hodel
The State of XML Processing in Ruby – James Britt
Object-relational mapping with Lafcadio – Francis Hwang
Ruby World: (not) Implemented – Steve Tuckner
Generating Code in Ruby – Jack Herrington
Roundtable with Matz
2日目
Programming LEGO MINDSTORMS with Ruby – Shashank Date
(...aside) – David Alan Black
Building Environment for MUES – Michael Granger
A Cluless Ruby Hacker Explores Security – Nathaniel Talbott
Teaching Ruby to Testers – Bret Pettichord
Introducing Ruby to the Corporate World – Jim Freeze
Keynote 「Visions for the Future」 – Matz
3日目
Building with Rake – Jim Weirich
Toward a Refactoring Framework for FreeRIDE – Hal Fulton
Ruby Java Debug Wire Protocol – Rich Kilmer
+
+

今年で3度目となるRuby Conferenceですが、この質の高さは驚嘆に値します。出席者の中からも「すばらしいカンファレンスだった」「来年もぜひまた来たい」という感想が聞かれました。私もそう思います。

+

今年からはDavid Alan Blackを中心にRuby Central LLCという非営利団体が設立され、今後はその団体の主催という形でRuby Conferenceが開催されることになりました。昨年までは単なる有志の集まりだったRuby ConferenceもRuby Centralの登場で今後はよりきちんとした組織化が行われるのではないかと期待されます。

+

さて、Ruby Conference会場で何度か聞かれた質問に「日本ではRubyカンファレンスって開かれているの?」というものです。

+

話を聞いてみると、海外のRubyユーザーから見ると、日本は開発者は住んでいるし、Ruby関連の本は40冊近くも出版されているし、ruby-listを始めとするメーリングリストで議論は活発に行われているし、日本語でしか書かれていない(ので彼らは興味があっても読めない)ページがたくさんあるし、などなどでまるで日本に対して「黄金の国ジパング」に似たようなイメージを持っているようです。

+

そんな「夢の国」ではきっとRubyに関するイベントはたくさん開かれていて、Ruby Conference以上にすばらしいものに違いない、そういう期待をこめて上記の質問につながっているようです。しかし、意に反して、私の答えは「いや、きちんとしたイベントは開かれていないんだよ」でした。彼らは一様に(ほんの少し)落胆したようでした。

+ +

そうなんです。たぶん日本は世界で一番Rubyユーザーの多い国なんですけど、Rubyカンファレンスのようなイベントがほとんど開催されていません。過去を振り返ると2000年に京都でLinux Conferenceと併催されたカンファレンスはPerl/Ruby Conferenceという名前でPerlコミュニティとの共同開催でした。しかも主催はほとんどPerlコミュニティに任せっきりでした。その次の年に開かれたYAPRC(Yet Another Perl/Ruby Conference)19101でもそうです。2003年はLL Saturdayというイベントが開かれましたが、これもPerl, PHP, Python, Rubyの集まりでした。

+

実はRuby単体のイベントはほとんど開かれていないんです。セミナーのようなものといくつかの単発的な宴会を除けば、ここ数年では2003年10月に島根県の玉造温泉(私の自宅のすぐそばです)で開かれたRuby温泉ミーティングくらいでしょうか。

+

これはもしかするとユーザーコミュニティが存在しないせいかもしれません。PerlでもLinuxでもPostgreSQLでもPHPでも、「日本なんとかユーザー会」のような会が存在していて日本語のWebページを用意したり、ドキュメントを翻訳したり、イベントを主催したりしているようです。

+

ところがRubyの場合、Webページもドキュメントも最初から日本語で用意されていますし、コミュニティといえばメーリングリストを主体としたどちらかというと開発コミュニティそのものしか存在しません。

+

しかし、ユーザーコミュニティの不在はイベントやユーザーとしての交流の少なさなどの弊害を生んでいるといえないこともありません。そこで先日の温泉ミーティングではこの事態を打開すべく「日本Rubyの会」の発足が提唱されました。「日本Rubyユーザー会」ではないのは、日本全国のような広い範囲で「ユーザー会」というのは実効的でないだろうという意見を反映したものです。

+

まだ詳細は決定していないのですが、会長に日本人唯一のRuby Conference皆勤賞(私を除く)のたかはしさんにお願いしようと考えています。私は実際には全然働かない名前だけの「名誉総裁」くらいのポジションを狙っています(笑)。

+

日本Rubyの会では、日本を代表するユーザーコミュニティとして、

+
    +
  • イベントの主催者

  • +
  • 宴会/食事会の名義

  • +
  • 寄付の送金先

  • +
  • 物心両面での開発支援

  • +
  • サーバーなどのホスティング

  • +
  • Webを中心とした情報発信

  • +
  • ユーザー交流支援

  • +
+

などができたらいいなあと考えています。

+

日本における唯一の弱点であるユーザーコミュニティが改善されるとき、日本が名実ともに「Rubyの夢の国、ジパング」になる日がやってくることでしょう(大げさ)。楽しみだなあ。

+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-008.xhtml b/docs/vol2/xhtml/p-008.xhtml new file mode 100644 index 0000000..a9362a6 --- /dev/null +++ b/docs/vol2/xhtml/p-008.xhtml @@ -0,0 +1,228 @@ + + + + + +第34章 Blogの世界 + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay34 +
+

+探訪Ruby
+Blogの世界 +

+
+

[Linux magazine, 2004年3月号]

+
+

Blogもだいぶ廃れましたね。はてなブログは健在ですが。個人が情報発信したいという欲求は変わらないようですが、日々のつぶやきのようなものはX(旧Twitter)やBlueskyに、ちょっと長めの記事はQiitaやZenn、海外だとMediumに、読み物はNoteなどに分散してしまったようです。結果的に記事で紹介されているブログソフトウェアもほとんどが廃れていますね。時代の流れを感じます。

+

「Ruby開発日記」は「Rubyの未来」について。これはちょっと背景を説明しておく必要があります。2000年代初頭、さまざまな言語で過去からのお荷物を捨てて、「次世代言語」に生まれ変わろうという動きが流行したことがありました。PerlではPerl 6, PythonではPython 3000, RubyではRuby 2(コードネームRite)です。これは後に登場するRuby 2.0とは別物です。開発日記ではこの新世代のRuby 2設計の2004年時点での取り組みを解説しています。この時点ではRubyを完全に作り変えようと考えていたことがわかります。

+

各言語の「次世代」はそれぞれ違う運命をたどりました。Perl 6は結局一般化せず、Perlの本流はPerl 5のまま継続しました。今ではPerl 6はRakuという名前に改名され、別の言語となっています。Python 3000はPython 3.xになりました。しかし、その非互換性のためPython 2.xを使い続ける人も多く、Pythonコミュニティは15年以上分断されました。Rubyでは結局作り直すという試みは(主に互換性の維持を理由に)断念されました。当時としては冒険ができず残念に思っていましたが、言語の発展のためにはむしろよい判断でした。その後、RubyはRuby 1.9, Ruby 2.0, Ruby 3.0と漸近的な進化を続けることになったのです。また、このとき実験していたRiteの残骸は後にmrubyにつながりました。

+
+
+

先月のWikiに続いて、今月は何かと話題の個人Webサイト、Blogについて探検してみましょう。

+
+
+ +

Blogとは何か?

+
+

Blog とはWeblog の略です。Webはいうまでもありませんが、Logのほうは、辞書でいう「航海日誌、旅行日記」などの意味で、日々の記録といったニュアンスでしょう。

+

この原稿の執筆に当たってBlogの定義についていろいろ調べてみましたが、これが決定的という定義は見当たりませんでした。基本的にこういう雰囲気だけで使われている言葉に勝手に定義を与えるのは危険です。私はかつて「オブジェクト指向」や「オープンソース」などの定義についていろいろ議論してきた経歴を持つ「前科者」ではありますが、これらについては確固たる自分の意見を持っているからこそできたことで、Blogについて同じような議論を貫く自信もやる気もありません。

+

そこで、多くの人々がBlogであると呼んでいるものに共通すると思われる特徴だけを列記しておきます。

+
    +
  • 毎日あるいはそれに近いペースで更新されている
    +私はこれが最も重要な特徴だと思います。きちんと運営されているBlogは毎日のように更新されます。もっとも、アメリカでのBlogホスティングサービスを対象にしたある調査によると実はBlogサイトの7割は過去2カ月更新されず、平均更新頻度は2週間に一度なのだそうですが。ほとんどのホスティングサービスは利用は無料なので、単にお試しという人が多いのが影響していると思われます。この調査によると1度だけしか更新していない三日坊主以下のBlogが4分の1を超えるそうです。日本でのデータも見てみたいものです。

  • +
  • 新しいものが先頭にくる
    +頻繁に更新されるといえば、先月紹介したWikiのようなものがあります。活発なWikiでは毎日どころか毎時間、毎分の単位で更新が行われます。ですが、Wikiと比較するとBlogには「時系列順」という特徴があります。更新された項目は時間順に並び、トップページでは新しいものほど前に並びます。一方、Wikiそのものには時系列という概念がなく、それが特徴でもあり短所でもあります。中には、ページの構成を工夫してWikiでBlogを運営している人もいます。こういうものはBlikiとかWikilogとか呼ばれることがあるようです。

  • +
  • 個人が運営している
    +Blogは基本的に個人サイトです。Blogは基本的に個人サイトです。たとえば「スラッシュドット」のような一種のニュースサイトも、毎日更新され、新しいものが先に来ますし、実際Blogにも使えるソフトウェアを使って運用されていますから、形式としては上記のBlogの特徴を持ちますが、よっぽど広い定義を持った人以外はこれをBlogと呼ぶ人はいません。やはり、個人が運営しているというのが多くの人が持つBlogに対するイメージだと思います。企業や組織で運営するBlogが存在できないとまでは言いませんが。

  • +
+

以上が、私の考えるBlogの特徴です。中には、BloggerやMovableTypeなどの定番Blogソフトウェアを使ったもの(だけ)がBlogだと断定する人もいますが、これはいくら何でも狭すぎると思います。

+
+

あと、すべてのBlogに共通な特徴とまではいえませんが、Blogにはインターネット上のほかのページにリンクして、そのページについて自分の意見を付け加えるスタイルのものが多く見られます。人によってはこれこそがBlogの本質だと呼ぶ人もいます。確かに歴史的に見ればBlog(あるいはWeblog)と呼ばれたものは、もともとはそのようなフィルタ型のサイトが発祥であったようですが。

+

このような「日々更新」「時系列順」「個人サイト」という特徴を持つサイトとして、日本では古くから「Web日記」とか「テキストサイト」とか呼ばれるものが存在していました。これらはBlogなのでしょうか? そうではないのでしょうか?

+

最初に言ったように、なにがBlogでなにがBlogでないかについて深入りしたくはないのですが、「Web日記」も上記の特徴を備えているので、この記事ではこれらもBlogであるとすることにします。

+

日本で昔からあった日記がアメリカで最近になってBlogとして注目を浴びた背景には、アメリカと日本のインターネット文化の違いがあるのではないかと思います。インターネット発祥の地であるアメリカですが、意外と個人サイトの存在感は薄く、目立っているのは企業や有名人によるサイトばかりでした。そういう状況で決して有名人ではない、個人による「情報発信」のムーブメントとしてBlogという言葉が注目されたのだと思います。

+

一方の日本では、企業サイトと同じくらい個人サイトも存在感を持っていて、その進化の過程で欧米のBlogと同じようなフォーマットを持つ「日記」がより早く誕生したのだと考えています。この違いの理由はいろいろあるでしょうが、一番怪しいのは、日本で一時流布した「インターネットで世界に情報発信」というキャッチフレーズでしょう。このキャッチフレーズに乗せられて開設してしまった個人サイトは数限りなくあったと思います。その中で生き残ってきたものが日本の個人サイトの源だったのではないでしょうか。

+

当時は「日本語で書いててなにが世界に情報発信だ」と思ったものでしたが、こんな効用があるとは予想もできませんでした。

+
+
+

Blogコミュニケーション

+
+

Blogの面白さの本質はコミュニケーションです。たとえば、Blogのスタイルで、毎日自分がどんな食事を食べたかメニューを記録したとして、それを読んでも面白くもなんともありません。100年経ったら歴史的資料として価値が出るかもしれませんが。Blogを通じて外の世界とコミュニケーションをとることで世界がどんどん広がっていきます。

+

ですから、Blogでは外の世界との情報のやりとりが重視されます。多くのBlogツールでは以下のような手段でやりとりを行います。

+
    +
  • リンク

  • +
  • コメント

  • +
  • トラックバック

  • +
  • RSS

  • +
+
+
+ +

リンク

+
+

多くのBlog記事は自分の興味のある情報を示し、それをリストしたり、コメントを付けて自分の考えを示しています。先ほども述べたようにこれがBlogの最も初期の形態です。BlogはWebページですから情報元のURLがあればリンクするのは当然のことです。

+

そのような背景があるので、Blogでは情報を提供するとき、その元ネタのURLを(あれば)明示することが推奨されます。また、どこかのページでその情報を入手した場合には、その経由先のURLを示すのが礼儀正しいやり方でしょう。それに経由先を示すことで、自分と興味の方向が近いサイトとの間にリンクを張ることができます。

+

このようにBlog同士でリンクを張り合うことにより、似た興味を持つ人によるBlogがつながり、次第にコミュニティが形成されていく、これがBlogの楽しさの1つです。

+
+
+

コメント

+
+

しかし、Blogの読者が全員自分のBlogを持っているわけではありません。また、たとえ自分のBlogを持っていたとしても、ほんの一言二言コメントしたいためだけに自分のBlogにエントリを用意するのも大げさに感じられる場合もあります。

+

そういう場合に対応するために、多くのBlogツールにはコメント機能が装備されています。コメントにより、元の記事の見落としが指摘されたり、欠けていた情報が補足されたりします。あるいはコメントをきっかけに有益な議論に発展することもあります。

+

ただ、コメントというのは基本的に匿名の書き込みですから、無責任なもの、攻撃的なもの、悪意のあるものが書き込まれる可能性があります。また、かつて宣伝目的で利用されたこともあります。読者からの自由なフィードバックを重視するか、コメントによる「荒らし」を避けることを重視するか、難しい判断ですが、一般的にはコメント機能を停止しなければならないほど問題が発生することは少ないようです。

+
+
+

トラックバック

+
+

リンクやコメントはBlogが存在する前からあった古くからの手法ですが、トラックバックはBlogによって誕生した新しいコミュニケーション手段です。

+

あるBlog(A)が別のBlog(B)の記事を話題にした場合、AからBへはリンクを張ることによってつながりを示すことができます。しかし、読者にとって知りたい情報は、このページがどのページを話題にしているかということはだけでなく、このページについて語っているページがどこにあるのかということも知りたいでしょう。

+

そこで、Bに対してAで話題になっていることを伝えるための方法ですが、リンクとコメントだけしかなければ、Bのコメント欄にAのURLを書き込むくらいしか方法がありません。正直なところちょっと面倒です。人間は面倒なことはやらないで済ませてしまうことが多いので、これでは面倒さが壁になって知りたい情報が集まらないことになってしまいます。

+
+

サーバーの管理者であれば、ログを見ることでReferer(参照元)の情報を得ることができますが、単なるBlogツールのユーザーには難しいことです(tDiaryには「リンク元」を表示する機能があります)。

+

そこで、登場したのがトラックバックです。トラックバックは相手のエントリを話題にしたことを通知するためのHTTPを使ってBlogツール同士がやりとりするプロトコル(取り決め)です。トラックバックに対応しているBlogツールには各記事ごとにtrackback URLと呼ばれるURLが用意され、記事の一部として表示されます。一方、参照する記事を書いた人は、記事を自分のBlogにアップロードするときに、参照した記事のtrackback URLを指定します。そうすると、アップロードを受けたBlogツールがtrackback URLに対してHTTPで参照情報(URLや要約など)を送ります。これで参照元のBlogは自分がそのページから参照されていることがわかりますから、コメントと同じように参照されているBlogの一覧を表示することができます。

+

トラックバックはMovableTypeというBlogツールで初めて導入されましたが、今ではMovableType以外の多くのBlogツールがトラックバックに対応し、ツール間を超えてやりとりができるようになりました。

+
+
+

RSS

+
+

RSSはRDF Site Summary(RDFによるサイト要約)の略で、機械にもわかる形式でWebサイトの概要を示す手段です。ここまで紹介してきたリンク、コメント、トラックバックはいずれも人間が読み、コミュニケーションするための機能でしたが、RSSはコンピュータが読み込んで処理するための機能です。

+

RSSはXML形式でWebサイトの見出し、概要、更新日付などを記録しています。このファイルを読み込むことで、人間向けに出力されたHTMLファイルを解析するようなことをしなくても、そのBlogに対する情報を収集することができます。各種Blogツールが用意するRSSファイルを読み込んで、Blogの更新と見出しをチェックするRSSリーダーと呼ばれるツールも登場してきています。

+
+
+

さまざまなBlogツール

+
+

MovableTypeなどの初期のBlogツールはPerlで記述されていましたが、Blogツールは単なるWebプログラムですから、実装するのにどの言語でなければならないという理由はありません。実際、Python, PHPなど数多くの言語で実装されたBlogツールがあり、もちろんRubyで実装されたものもたくさんあります。RAAなどを調べてみると、実に9つものBlogツールが登録されていました。眺めてみるとそれぞれの思想の違いなどもあって面白いです。

+

では、これら9つのBlogツール をひととおり紹介してみましょう。

+
+

Blogtari!(http://www.jamesbritt.com/articles/blogatari.html

+

Blogtari! はrubyxml.orgなどXML関係の業績が多いJames BrittさんによるBlogツールです。PerlによるBlogツールBlosxomに触発されて開発されたとのことです。

+

clWiki(http://clabs.org/ruby.htm

+

先月も紹介したChris MorrisさんによるclwikiにはBlogkiというBlogツールが同梱されています。

+
+

Diaria(http://rubyforge.org/projects/diaria/

+

oztenさんによるBlogツールDiariaはBlog用の静的なHTMLを生成するコマンドラインツールです。生成されたHTMLファイルをサーバーにアップロードすることで簡単にBlogを運用できます(が、コメントやトラックバックは無理ですね)。

+

Gurgle(http://ca.compsec.net/arfer/gurgle

+

GurgleはarferさんによるミニマルなBlogツールです。特別なライブラリをインストールすることなく動作します。原稿執筆時点ではWebページにアクセスできませんでした。

+

rb.log(http://www.zweknu.org/src/rb.log/

+

TRrevor Schroederさんによるrb.logはフル機能を備えたBlogツールです。ファイルアップロード後静的ページを生成する機能も備えています。

+

RubyJournal(http://www.thedailychannel.com/rubyjournal/

+

George GirtonさんによるRubyJournalは、Diaria同様クライアント側のBlogツールです。ホストへのFTPアクセスがあればいつでもBlogが始められます。開発はMac OS Xで行われているようです。

+

sakura(http://sakura.sourceforge.jp/cgi-bin/sakura.cgi

+

MoleskinさんによるBlosxom likeなBlogツールです。「私もblosxomを使用する予定でしたが、いまからPerlを覚えるのが面倒なので同じようなものを作ることにしました」だそうです。非常にナイスな開発理由です。

+

tDiary

+

「Rubyのキラーアプリ」として名高い、ただただしさんによる日記ツールです。もともとtDiaryはコメントやトラックバックなどBlogツールの諸機能を備えているのですが、BlogkitというよりBlog的に運用するためのツールも用意されています。Rubyホームページ(www.ruby-lang.org)もこのtDiary+Blogkitで運用されています。

+

xlog(http://sourceforge.jp/projects/xlog/

+

立石孝彰さんによるxlogはXTemplateのサンプルとしてのBlogツールだそうです。ソースコードはCVS経由でのみ入手可能です。

+
+
+
+

tDiary

+
+

2003年5月以来、「Matzにっき」というBlogのようなWeb日記のようなものを書き続けています。URLは、

+
    +
  • http://www.rubyist.net/~matz/

  • +
+

です。現時点で一日も休んでいない(遅れても毎日分ちゃんと書いている)ことを内心自慢に思っています。この「Matzにっき」を運用しているのがtDiaryです。なかなか便利なツールでこれがなければとても毎日の運用はできなかったことでしょう。

+

tDiaryの特徴は、

+
+
    +
  • ツッコミ

  • +
  • トラックバック

  • +
  • テーマ

  • +
  • スタイル

  • +
  • プラグイン

  • +
+

などがあります。

+

「ツッコミ」とは要するにコメントでtDiaryの「t」はツッコミのtというくらいtDiaryの基本的な機能です。tDiaryでのツッコミは短く愛を込めたものが推奨されています。

+

「トラックバック」は上記で説明したものです。これによりtDiaryは他のBlogとコミュニケートできます。

+

「テーマ」はCSS(Cascading Style Sheet)を使ってサイトの外観を変更できる機能です。tDiaryには数多くのテーマが同梱されているので、自分のBlogの外見をいろいろ選ぶことができます。季節や気分に合わせて変更するのも簡単です。また、テーマギャラリーがありますので、その中から「次はどんなテーマにしようか」と考えるのも楽しいです。

+
    +
  • テーマギャラリー
    +http://www.tdiary.org/20021001.html

  • +
+

「スタイル」はBlog記事の入力スタイルです。tDiaryのデフォルトは簡易化されたHTMLですが、その他にもWikiの記法を使うWikiスタイル、Ruby界では人気の高いRDを使うRDスタイルなどが用意されています。RDスタイルがなければ私が「Matzにっき」を始めることもなかったでしょう。

+

「プラグイン」はtDiaryにさまざまな機能を追加するものです。上記のトラックバックもプラグインで実現されています。他にも以下のようないろいろな機能がプラグインで提供されています。

+
    +
  • Amazonアフィリエートプラグイン
    +ISBNに対応するAmazon.co.jpの購入ページへのリンクを生成します。

  • +
  • 脚注プラグイン
    +脚注を付けます。脚注はその日のエントリの下にまとめて表示されます。

  • +
  • リンク生成プラグイン
    +自分の日記のエントリへのリンクを簡単に生成します。

  • +
  • カレンダープラグイン
    +カレンダーを表示します。表示形式・機能によっていくつかのカレンダープラグインが存在します。

  • +
  • ToDoプラグイン
    +ToDoリストを管理します。

  • +
  • RDF出力プラグイン
    +最新のエントリのRDFを出力します。

  • +
+

その他数多くのプラグインが開発されています。

+
+
    +
  • プラグインリスト
    +http://tdiary-users.sourceforge.jp/cgi-bin/wiki.cgi?PluginList

  • +
+

このリストもWikiで管理されていますね。

+

さて、この後tDiaryのインストールについて説明しようと思ったのですが、どうやらそれを始めると、誌面が足りなくなりそうですから、tDiary.orgのサイトを参考にしてください。原稿執筆時点での最新のバージョンは1.5.6です。

+
    +
  • tDiaryのインストール
    +http://www.tdiary.org/20021112.html

  • +
  • tDiaryのインストール
    +http://tdiary-users.sourceforge.jp/cgi-bin/wiki.cgi?FAQ#i1

  • +
+

Webアプリケーションのインストールの経験があれば、インストール手順に従えば難しいことはまったくありません。気を付けるのは .htaccessファイルの記述だけです。私のサイトの場合はリスト34.1のようにしています。

+
+

リスト34.1●.htaccessの例

+
Options +ExecCGI
+
+AddHandler cgi-script .rb
+DirectoryIndex index.rb
+RewriteEngine on
+RewriteBase /~matz
+RewriteRule ^([0-9]+)\.html$ /home/matz/public_html/index.rb?date=$1
+
+<Files "*.rhtml">
+        deny from all
+</Files>
+
+<Files "tdiary.*">
+        deny from all
+</Files>
+
+<Files update.rb>
+        AuthName      tDiary
+        AuthType      Basic
+        AuthUserFile  /home/matz/.htpasswd
+        Require user  matz
+</Files>
+
+
+ +

Rewrite」から始まる3行はApacheのmod_rewriteの設定です。これにより、各日のエントリを20040218.htmlのような静的HTML風のURLに見せることができます。

+
+
+

まとめ

+
+

今回はBlogについて探検してみました。今回紹介したtDiaryはとても奥が深いので、また別の機会を探して探検してみたいです。Emacs上での入力支援ツールtdiary-mode.elや、簡易検索ツールtdiarygrepなど紹介したいことはたくさん残っています。では、またの機会に。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-009.xhtml b/docs/vol2/xhtml/p-009.xhtml new file mode 100644 index 0000000..c52bc55 --- /dev/null +++ b/docs/vol2/xhtml/p-009.xhtml @@ -0,0 +1,53 @@ + + + + + +第34章 Blogの世界 + + + + +

Matz Essays Volume 2

+ + +
+ +

◆ Ruby開発日記 ◆ Rubyの未来

+
+

昨年11月、テキサス州オースチンでRuby Conferenceが開かれました。このカンファレンスについては先月もこのコラムで簡単に紹介しました。

+

このカンファレンスのキーノートで、Rubyの今後に関する発表を行いました。今までも今後の構想については小出しにしてきていたのですが、ここ数年、現在のRubyのメンテナンスにばかりに労力を取られて、新しい開発がさっぱり進んでいませんでしたから、ここで自分を発憤させるためにも今後のロードマップを改めて考えてみたのです。あいにく具体的な期日についてはまとまらなかったのですが、今後の計画については整理できたと思います。

+

まず、今まであいまいにRiteと呼んできたものを、言語仕様と処理系実装に明確に区別することにしました。今後はRubyという言語の新しいバージョンの仕様をRuby2、そしてそのRuby2のための新しい実装をRiteと呼ぶことにしました。

+

Rubyは誕生以来、少しずつあちこちを修正することで進歩してきました。私の今までのさまざまなデザイン上の判断の集合が今のRubyだと言ってもよいでしょう。しかし、Rubyの開発を始めて10年の間には残念ながら正しい判断ばかりではなく、いくつものデザイン上の間違いも犯してきました。Ruby開発の初期ではユーザーも少なく、仕様も割と流動的でしたが、Rubyのユーザーがこれほど多くなってしまうと、互換性の観点からあまり大きな変更を行うのは難しくなっています。小さいように見える変更でも、世の中に存在する数千、数万のRubyプログラムの中にはその変更によって大きく影響を受けるものがないとは限りません。

+

しかし、そのような「間違い」を放置しておくことは長い目で見るとRubyのためになりません。「Broken Window Theory(割れ窓理論)」というのがあるそうです。これは、割れた窓を放置しておくことにより、周辺の環境が次第に悪化する、逆にいえば割れた窓をすぐに修理するなど不具合をまめに管理することで良好な環境を維持できる、という理論です。この理論に従えば、デザイン上の間違いを放置しておくことで、Rubyの優れた特質と魅力が次第に失われてしまう(可能性がある)ということになります。互換性と優れた言語の追求、大きな矛盾ではあります。

+

そこで、このような戦略を立てることにしました。非互換性を生む言語仕様の大きな変更は、たとえ間違いがあっても安易には行わない。これはユーザーが増えてきた言語の設計者の責任でもあると考えます。しかし、言語設計者も人間である以上、間違いは避けられませんから、数年に一度の比較的長いスパンで(基本的な方向性を変えない程度で)非互換性を含む大きな仕様変更を行い、今までの間違いを訂正する機会にすることにしました。互換性が問題になる場合には古いバージョンを使い、時間をかけて移行すればよいのです。

+

そこでこの機会にRubyの言語仕様を一度総ざらえして、問題を探し、直すべき点があれば一気に直してしまおうという試みがRuby2になります。この仕様の不備やデザイン上の間違いを見落としなく行い、後悔しないRuby2の仕様をデザインするため、多くの人の意見を広く聞く機会を用意しました。将来のRubyに対する仕様変更を提案したい人は、RCR(Ruby Change Request)と呼ばれる形式で提案を行います。そして、その提案についていろいろな観点から議論を行い、生き残ったものがRuby2に取り込まれます。RCRの議論は現在、RCRchiveと呼ばれるサイトで行われています(英語)。

+
+
    +
  • RCRchive
    +http://rcrchive.net/

  • +
+

現時点もすでにいくつかの提案が行われています。実は言語のデザインというのは絡み合う要素が多く、なかなかに難しいことで、おそらくはそのうちの大半は採用することはできないでしょうが、言語のデザインに積極的に関わることのできる機会はそれほど多くありませんから、皆さんの中で興味を持たれた方は提案してみてはいかがでしょう。日本人にとっては残念ながら、RCRchiveには英語という壁がありますが、ruby-devメーリングリストでまず日本語で議論してから、みんなで翻訳してRCRchiveに持ち込むという手も考えられます。

+

そのようにして決まっていったRuby2の仕様の中には、実際に動かしてみなければはたして良いのか悪いのかわからないものがあることでしょう。そこで、現在の実装の延長線上でRuby2の仕様を実験するバージョンとして1.9を用意します。1.9の開発は1.8.1のリリース後からすでに始まっています。まだ大規模な改変は行われていませんが、近いうちに活発な実験が行われる予定です。

+

現場のRuby言語処理系についてもいくつか問題があります。

+
    +
  • 組み込みに向かない

  • +
  • スレッドセーフでない

  • +
  • メンテナンス性が低い

  • +
  • 遅い

  • +
+

Ruby処理系も長らくつぎはぎで開発してきたため、性能的にもメンテナンス性的にも限界が近づいています。また、他のプログラムに組み込むことやネーティブスレッドに対する対応は、当初想定していなかったこともあってどうしても弱い分野です。このような理由から、現在の実装を捨ててまったく新しい実装を行いたいというのがRiteです。また、せっかく再実装するのですから今よりもずっと高速に実行できるようにしたいと考えています。現在の処理系にはボトルネックになりそうな箇所がいくつも見つかっていますから、高速化については希望があるはずです。

+

具体的な期日は定まっていませんが、個人的な予想としては今年いっぱいはRuby2の設計と1.9による実験が主な活動になるのではないかと考えています。Riteについては私よりも処理系実装に詳しい人の助けを借りることになりそうです。こんな調子ですから、皆さんの元に安定版としてのRuby2をお届けできるのはどんなに早くても来年以降になりそうです。

+

今まで何年もRiteのことが遅れてきたのは、現在のRubyのメンテナンスに忙しかったこともありますが、言語仕様の改変と言語処理系の実装の2つの大きく複雑な仕事を一緒にやろうとして手に余っていたこと(というか途方に暮れていた)も大きな理由です。今回、言語仕様と言語実装を分離してロードマップを決定したことにより、「新しいRuby」の実現が近づいていくことを希望しています。お楽しみに。

+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-010.xhtml b/docs/vol2/xhtml/p-010.xhtml new file mode 100644 index 0000000..80e85e7 --- /dev/null +++ b/docs/vol2/xhtml/p-010.xhtml @@ -0,0 +1,336 @@ + + + + + +第35章 アスペクト指向 + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay35 +
+

+探訪Ruby
+アスペクト指向 +

+
+

[Linux magazine, 2004年4月号]

+
+

アスペクト指向も定着しませんでしたね。本文には「20年くらい前のオブジェクト指向のようなレベル」と書いていましたが、実際に20年後にはオブジェクト指向のように当たり前にはならずに終わりました。未来はわからないものですね。ただ、クラスなどを超えて横断的な関心事に対処するコンサーン(Concern)などのアスペクト指向の思想を受け継いだような機能をときどき見かけるので、まったく打ち捨てられたというわけでもないようです。

+

「Ruby開発日記」は「ゴミ集め」です。ガベージコレクション(GC)とも呼びますね。当時はJavaによってゴミ集めが一般化していた時代です。C/C++のように手動でメモリ管理するのは間違いが多いので、GCに頼りましょうというのが最新の考えでした。現在では、ゴミ集めのないRustのような言語がもてはやされているのとは対象的ですね。Rustは所有権というゴミ集めとは違うアプローチで自動的にメモリ管理しているのが特徴です。

+
+
+

Wiki, Blogと、ここのところアプリケーションの紹介が続きましたが、久しぶりにプログラミングの話題に戻ろうと思います。今月紹介するのは、「オブジェクト指向の次」とうわさされている「アスペクト指向」です。

+
+
+

関心の分離

+
+

プログラミングにおける重要な原則は「関心の分離(separation of concern)」です。関心の分離は分割統治戦略の一種です。人間は一度にたくさんのことを考えられませんから、関心のあることだけをひとまとめに扱い、関心のないことは見ないで済ませることで、プログラマーの精神的負担を軽減することは必要です。関心の分離によってプログラムの保守性や再利用性を高めることができます。

+

プログラミング言語はこの関心の分離を支援するために進歩してきました。たとえば関数(サブルーチン)は繰り返し使われる手続きの一部を切り出して、そこだけを考えるために発生しましたし、構造化プログラミングもアルゴリズムの制御の流れをgotoによるスパゲッティから、逐次実行、条件分岐、繰り返しという単純な制御構造の組み合わせに変えることで、アルゴリズムそのものに注目できるように支援しています。

+
+

オブジェクト指向言語も、プログラムにおいて最も関心の対象となるデータ構造とそれに対する手続きをクラスという形でひとまとめに記述することで、この関心の分離を支援しています。

+
+
+

アスペクト指向とは

+
+

オブジェクト指向言語はプログラムをオブジェクト単位に分割しますが、世の中のすべての関心がオブジェクト単位に分割できるとは限りません。複数のオブジェクトを横断するような処理は、関心事としては1つなのに複数のクラスにばらばらに分散してしまうことがありえます。

+

例をあげてみましょう。メソッド呼び出しの最初と最後にログを出力したいとすると、従来の方法で単純に実現すると35.1のようになります。

+
+

リスト35.1●Rubyロギング(従来の方法)

+
def log(m)
+  STDERR.print Time.now.to_s, ": ", m, "\n"
+end
+
+class Foo
+  def m1
+     log("m1 begin") # m1処理開始ログ
+     puts "this is m1"
+     log("m1 end")   # m1処理終了ログ
+  end
+  def m2
+     log("m2 begin") # m2処理開始ログ
+     puts "this is m2"
+     log("m2 end")   # m2処理終了ログ
+  end
+end
+
+foo = Foo.new
+foo.m1
+foo.m2
+
+
+

このプログラムを見るとわかるように、「ログを取りたい」という関心事に対する処理が、m1m2のそれぞれのメソッドにばらばらに分散しています。これは関心の分離の原則からみるとかなりまずい事態です。この例は非常に単純な例で、追加された処理はわずか4行ですが、ログを取るメソッドが数十、数百に上り、クラスも1つではなく数十もある場合を考えると気が遠くなりそうです。

+ +

「ログを取る」のは1つの例ですが、他にも認証、帯域制限、プロファイルなどなど分散しがちな関心事はたくさんあります。このような関心事をまとめて記述することができたら、生産性の向上ができそうじゃありませんか。

+

この関心事をまとめたものが「アスペクト(aspect)」です。辞書を引くとaspectは「外観、様相、局面、見方」だそうです。このような関心事を分離して記述し、組み合わせていくプログラミングをアスペクト指向プログラミングと呼びます。また、そのような記述を支援する言語はアスペクト指向言語です。

+

アスペクト指向はSmalltalkも生んだXerox PARC(パロアルト研究所)で生まれました。最も有名なアスペクト指向言語はJavaにアスペクト指向を追加したAspectJです。AspectJについての情報は、

+
    +
  • http://www.aspectj.org/

  • +
+

から入手できます(英語)。AspectJではアスペクトをその名もaspectという単位で定義し、処理をjoin pointと呼ばれる場所に挿入するという形を取ります。AspectJでのjoin pointとは、メソッド呼び出し、インスタンス変数の参照、例外の補足などがあります。詳しくは上記のAspectJのページを見ていただくか、以下のDeveloperWorksの記事を参照してください。

+
    +
  • http://www-6.ibm.com/jp/developerworks/java/020405/j_j-aspectj.html

  • +
+
+
+

AspectR

+
+

さて、われらがRubyの話に戻りましょう。

+

アスペクトを直接記述できる言語という意味ではRubyはアスペクト指向言語ではありません。しかし、Rubyは十分柔軟なので、Rubyの持つリフレクション機能(プログラムそのものを操作する機能)を使って、アスペクト指向プログラミングを実現するライブラリを実現できます。それがAspectRです。AspectRはAvi Bryantの作品です。

+

AspectRを使うと最初の例はリスト35.2のようになります。

+
+

リスト35.2●ロギング(AspectR)

+
require 'aspectr'
+
+class Logger < AspectR::Aspect
+  def log(m)
+    STDERR.print Time.now.to_s, ": ", m, "\n"
+  end
+  def log_enter(method, object, exitstatus, *args) 
+    log("#{method} begin")
+  end 
+
+  def log_exit(method, object, exitstatus, *args) 
+    log("#{method} end")
+  end
+end
+
+class Foo
+  def m1
+     puts "this is m1"
+  end
+  def m2
+     puts "this is m2"
+  end
+end
+Logger.new.wrap(Foo, :log_enter, :log_exit, :m1, :m2) 
+
+foo = Foo.new
+foo.m1
+foo.m2
+
+
+ +

リスト35.1のプログラムとリスト35.2のプログラムはまったく同じように動作します。リスト35.1(20行)よりもリスト35.2(29行)のほうが少し長くなってしまっていますが、これは単純な例なのでしかたありません。注目すべきことは、Fooクラスの定義部分はFooの処理そのものしか記述されておらず、「ログを取る」という関心事がLoggerクラスに完全に分離されているということです。ある日、ログを取る必要がなくなった場合、どんなにたくさんのメソッドでログを取っていてもFooクラスにロギングを関連づけている行だけを削除すれば、ロギング機能を外すことができます。リスト35.1のようにロギング処理がすべてのメソッドに分散していたとしたらかなり面倒なことになるでしょう。

+
+
+

AspectRのインストール

+
+

AspectRのホームページは、

+
    +
  • http://aspectr.sourceforge.net/

  • +
+

です。原稿執筆時点での最新バージョンは0.3.5です。最近はあまり更新されていないようですが、私が試してみた範囲内ではRuby 1.8.1でも動作するようです。ただし、添付のテストはうまく動きませんでした。test/unitRubyUnit互換機能の不具合かもしれません。

+

インストールは、

+
    +
  • http://prdownloads.sf.net/aspectr/aspectr-0-3-5.tar.gz

  • +
+

からtarボールをダウンロードし、適当な場所で展開してから、root権限で、

+ +
+
# ruby install.rb
+
+

を実行するだけです。なんて簡単。これでAspectRの全機能を使うことができます。実際にはAspectRはaspectr.rbという1つのファイルだけで実装されていますから、インストールはsite_rubyディレクトリにaspectr.rbをコピーしているだけです。

+

なお、aspectr.rbの147行目に、1.8では警告が出るtypeメソッドの呼び出しが残っていますから、ここをclassメソッドに置き換えておくとよいでしょう。また、229行目のidメソッドの呼び出しもobject_idに置き換えておくのもお勧めです。

+
+
+

AspectRの使い方

+
+

AspectRではjoin pointはメソッドの呼び出しだけです。AspectJではメソッド呼び出し以外にも、インスタンス変数の参照などもjoin pointにできることを考えると貧弱な気がしますが、インスタンス変数の参照はアクセサメソッドで代用できますし、実用上はこれで十分です。join pointに処理を挿入できるということはAspectRを使えば、任意のメソッド呼び出しの前後にあとからコードを追加できるということです。リスト35.2の例ではメソッド呼び出す前と後にログに出力するコードを追加していました。

+

では、再びリスト35.2のプログラムを例に、AspectRを使ったコード挿入の方法を解説しましょう。リスト35.2のプログラムを少しずつ見ていきましょう。

+
+
require 'aspectr'
+
+

AspectRを使うときには先頭でascpctrライブラリをrequireする必要があります。よく見るとaspectr.rbはたった308行しかないんですね(コメント、空行を含む)。Rubyの記述力のすごさを見る思いです。自画自賛ですが。

+
+
class Logger < AspectR::Aspect
+
+

アスペクトはAspectR::Aspectクラスのサブクラスとして定義します。このクラスにjoin pointに挿入する手続き(アスペクト指向の用語ではアドバイス(advice)と呼びます)をメソッドとして定義します。

+
+
def log(m)
+  STDERR.print Time.now.to_s, ": ", m, "\n"
+end
+
+

このlogメソッドはadviceではありません。adviceメソッドから呼び出すためのユーティリティメソッドです。ここでは標準エラー出力に時刻とともにメッセージを出力しています。

+
+
def log_enter(method, object, exitstatus, *args) 
+  log("#{method} begin")
+end
+
+

log_enteradviceとして使うメソッドです。AspectRのadviceにはメソッドの実行前に呼び出されるbefore adviceと実行後に呼び出されるafter adviceがあります。AspectJにはbefore adviceafter adviceの他にメソッドを包み込む形で置き換えるaround adviceがありますが、AspectRでは提供されません。しかし、なくても実用上は十分です。AspectJのadviceメソッドは以下の引数で呼び出されます。

+ +
    +
  • 第1引数   メソッド名(文字列)
  • +
  • 第2引数   レシーバ
  • +
  • 第3引数   終了情報(戻り値、例外)
  • +
  • 第4引数以下 メソッドに与えられた引数
  • +
+

第3引数の終了情報には、正常終了した場合にはその値が配列に、例外終了した場合にはtrueが渡されます。正直なところtrueよりは例外オブジェクトが渡ってくれたほうがうれしいなあ。before adviceはまだメソッドの実行が終了していませんから、終了情報に意味はありません。

+
+
def log_exit(method, object, exitstatus, *args) 
+
+

log_exitafter adviceです。引数の意味などはbefore adviceと同じです。

+
+
class Foo
+
+

Fooクラスはアスペクトの追加対象となるクラスです。「ログを取る」というアスペクトが分離された結果、本来の処理に集中できています。

+
+
Logger.new.wrap(Foo, :log_enter, :log_exit, :m1, :m2) 
+
+

この行が重要です。この行がアスペクト(Loggerクラス)と対象となるクラス(Fooクラス)を結び付けています。この行は、Fooクラスのメソッドm1m2の実行前にlog_enter、実行後にlog_exitを呼び出す、と解釈します。wrapメソッドの引数の意味は以下のとおりです。

+
+
wrap(target, before, after, *methods)
+
+

targetはクラスまたはオブジェクトで、クラスを指定した場合にはそのクラスのインスタンスメソッドにadviceを追加します。クラス以外のオブジェクトを指定した場合には、そのオブジェクトだけにadviceを追加します。

+

beforeafteradviceとなるアスペクトのメソッド名を指定します。どちらかのadviceを指定しない場合にはnilを渡します。

+

methodsにはadviceを付加するメソッドを指定します。指定方法としては今回使ったように文字列またはシンボルで1つずつ指定してもよいですし、正規表現を使ってもあるパターンにマッチするメソッドに一気に追加することもできます。たとえば /^m/ を指定すればmで始まる名前を持つメソッド全部にadviceを追加することができます。

+
+
foo = Foo.new
+foo.m1
+foo.m2
+
+

後は通常のFooの処理を行うだけです。メソッドm1m2の実行の前後にはadviceが追加されていますから、標準エラー出力に時刻とメッセージが出力されます。

+
+

もし、adviceを取り除きたくなったら、unwrapメソッドを使うことができます。アスペクトのインスタンスをどこかに保持しておいて(ここでは変数loggerに格納されているとします)、

+
+
logger.unwrap(Foo, :log_enter, :log_exit, :m1, :m2) 
+
+

と呼べばadviceが削除されます。引数の数と意味はwrapと同じです。

+
+
+

AspectRの応用

+
+

任意のメソッドの前後に後から処理を追加できるAspectRの機能はいろいろと応用できると思います。ここではAspectRのサンプルとして添付されているプロファイラ(rbprof)を見てみたいと思います。

+

プロファイラは各メソッドの実行時間と呼び出し回数を集計するものです。考えてみると、プロファイラの仕事とは、各メソッドの呼び出し前に回数カウンタを増加させ、呼び出し開始時刻を保存し、また呼び出し後に現在時刻と呼び出し開始時間との差分を取ってメソッドの実行時間を計測して記録することですから、アスペクトによって実現できます。

+

rbprofをインストールするためには、AspectRのtarを展開したディレクトリにあるaspect/profilerディレクトリに移動し、root権限で

+
+
# ruby ../../install.rb
+
+

を実行します。そうすると、

+
+
% ruby -r rbprof <プロファイルを取るプログラム>
+
+

でプロファイルが取れます。Ruby標準のプロファイラの起動は、

+
+
% ruby -r profile <プロファイルを取るプログラム>
+
+

ですから、ほとんど同じインターフェイスですね。

+

profile(標準プロファイラ)とrbprof(アスペクト利用のプロファイラ)との違いは以下のとおりです。

+
+

実行時間10倍の差はかなり大きなものです。大まかな情報をrbprofでつかんでからprofileを使うなどの使い分けもできると思います。

+

また、同じ手を使って特定のメソッドだけプロファイルを取ることも可能でしょう。本当はrbprofの標準機能として装備しておいてほしいものですが。

+

ここは宿題としたいところですが、少々難しい宿題になりそうなので、解答を用意しておきます(リスト35.3)。

+
+

リスト35.3●プロファイラアスペクト(aprof.rb)

+
require 'aspectr'
+
+class AProf < AspectR::Aspect
+  @@map = {}
+  @@stack = [[0, 0, 0, "dummy"]]
+  def profile(target, *methods)
+    self.wrap(target, :prof_enter, :prof_leave, *methods)
+  end
+  def prof_enter(method, object, exitstatus, *args) 
+    m = method_line(method, object)
+    unless map = @@map[m]
+      map = @@map[m] = [0, 0, 0, m]
+    end
+    map[0] += 1
+    @@stack.push([Time.now, 0.0, m])
+  end
+  def prof_leave(method, object, exitstatus, *args) 
+    tick = @@stack.pop
+    m = tick[2]
+    unless map = @@map[m]
+      map = @@map[m] = [0, 0, 0, m]
+    end
+    cost = Time.now - tick[0]
+    map[1] += cost
+    map[2] += cost - tick[1]
+    @@stack[-1][1] += cost
+  end
+
+  def self.print_profile(f)
+    data = @@map.values
+    data = data.sort_by{|x| x[3]}
+    f.printf "    self       #      self     total\n"
+    f.printf "  seconds    calls  ms/call  ms/call  name\n"
+    for d in data
+      f.printf "%8.2f %8d %8.2f %8.2f %s\n", d[1], d[0], d[2]*1000/d[0], d[1]*1000/d[0], d[3]
+    end
+  end
+
+  private
+  def method_line(method, object)
+    object.class.to_s + "#" + method
+  end
+end
+
+END {
+  AProf.print_profile(STDERR)
+}
+
+
+ +

aprof.rbの使い方は以下のとおりです。

+
+
require 'aprof'
+
+# プロファイルを取りたいプログラムを定義
+def fib(n)
+  if n<2
+    n
+  else
+    fib(n-2)+fib(n-1)
+  end
+end
+
+# プロファイルを取りたいメソッドの指定
+AProf.new.profile(Object, :fib, :print)
+# 実行
+print(fib(20), "\n");
+
+

こうするとObjectクラスのfibメソッドとprintメソッドのプロファイルだけを取ることができます。

+

出力は図35.1のようになります。

+
+
+
6765
+    self       #      self     total
+  seconds    calls  ms/call  ms/call  name
+   49.03    21891     0.17     2.24 Object#fib
+    0.00        1     0.64     0.64 Object#print
+
+

図35.1●aprof使用例

+
+
+
+ +

まとめ

+
+

今月はRubyによるアスペクト指向プログラミングについて探検してみました。アスペクト指向はまだまだ新しい概念です。ちょうど20年くらい前のオブジェクト指向のようなレベルでしょうか。AspectRを使ってあなたもアスペクトの世界を先取りしてみませんか。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-011.xhtml b/docs/vol2/xhtml/p-011.xhtml new file mode 100644 index 0000000..b605365 --- /dev/null +++ b/docs/vol2/xhtml/p-011.xhtml @@ -0,0 +1,46 @@ + + + + + +第35章 アスペクト指向 + + + + +

Matz Essays Volume 2

+ + +
+ +

◆ Ruby開発日記 ◆ ごみ集め

+
+

私は子供のときから片付けるのが苦手で、両親と住んでいた頃は自分の机の上にはいつも山のようにものが積み上がっていました。こんな机では勉強できないので、教科書、参考書、ノートなど必要なものを山の中からピックアップして、兄弟の机を借りて勉強したこともあります。

+

学生時代、一人暮らしのときもひどいものでした。自分の部屋は散らかし放題で、とても客は呼べない状態でした。きちんと片付けることができる人のことはいつも尊敬しています。

+

さて、プログラミングの世界でも片付けは発生します。オブジェクト指向プログラミングではどんどんオブジェクトを作り出すのですが、用事が終わって不要になったオブジェクトは「ごみ」になってしまいます。これをそのままにしておくと、メモリ不足で性能が落ちたり、他のプロセスに迷惑をかけたりします。

+

CやC++であれば使い終わった「オブジェクト」は自分で片付ける必要があります。Cではfree関数を使ってメモリ領域を解放しますし、C++ならdeleteを使います。自分で散らかしたものは自分で片付けよう、というわけですね。大変行儀が良いやり方だと思います。しかし、私のように生来片付けるのが苦手な人は、オブジェクトの後片付けを忘れたり、うっかりまだ使っているものを捨ててしまったりして、面倒なことを引き起こしてしまいます。このようなメモリ関係の間違いは、問題が発生する場所と原因が一致しないことが多く、大変見つけにいバグになります。

+

だいたいそんな細々としたことは人間がわざわざするべきではないのです。そこで、不要になったと判断されたオブジェクトを自動的に片付けてくれる機能のことを「ごみ集め」あるいは「ガベージコレクション(garbage collection)」といいます。「GC」と省略して呼ぶこともあります。

+

Javaに採用されて広く知られるようになったGCですが、最初にGCを採用したのはLispというプログラミング言語で、それは実に今から40年以上前のことです。それ以来、実に長い間研究が続けられてきた分野なのです。GCを使えばCやC++でしばしば悩まされるメモリ関係の問題から完全に解放されて、プログラムの信頼性ははるかに向上します。加えて最近ではGC技術も進歩しているので、手動でメモリ管理した場合に比べて性能の面でも遜色ないケースが多いという報告もあります。

+

そのGCのやり方ですが、コンピュータには人間の意図はわからないので、あるオブジェクトをもうこの先二度と使わないかどうかは自動的には判定できません。ですから、ごみかどうか判定するのには違ったやり方を使います。

+

机の上にいろいろなものが乗っている状態を想像してみてください。それらのものは互いに複雑に関連しあっていて、ひと目では捨ててよいものだか悪いものだかわかりません。あなたの仕事はこの中からごみを見つけ出し、捨ててよいということになります。

+

まず最初にやることは明らかに必要なものを選び出すことです。GCではこれらの「もの」のことを「ルート(root)」と呼びます。ルートのオブジェクトと直接あるいは間接的に関連している「もの」はいつか参照される可能性があるので捨ててはいけません。逆にいうとルートと関連を持たないものは将来参照される可能性がゼロですから、捨ててはいけません。

+

GCの手法の1つmark and sweep法では、まずルートから始めて関連を持つオブジェクト全部に印(mark)を付けていきます。この印が付いているものは捨ててはいけないものと判断されます。関連のあるオブジェクト全部に印を付けた後は、机の上のもの全部を順番に眺めて印が付いていないものを捨ててしまいます。印が付いているものの中には将来二度と使われないものもあるかもしれませんが、それでもかまいません。

+
+

mark and sweep法では、まず「使用中」のオブジェクト全部に印を付けてから、すべてのオブジェクトのうち印の付いてないものを選び出しますから、ごみ集めにかかる時間は現在使用中のオブジェクトの数と、現在存在するすべてのオブジェクトの数に比例します。

+

別の手法copy法では、もう1つ別の机を用意して、ルートオブジェクトを移してしまいます。またルートと関連のあるものも次々と移していきます。関連のあるものをすべて移動させた後、元の机の上にはごみしか残っていないというわけです。元の机に残ったものは一気に捨てることができます。空いた机は次回のごみ集めのときにまた使います。2つの机を使うとはぜいたくな方法ですが、ごみ集めにかかる時間が「生きている」オブジェクトの数だけに比例することがうれしいケースもあります。

+

mark and sweep法にしても、copy法にしても生きているオブジェクトの数が増えればごみ集めの時間が長くなってしまうことには変わりありません。ごみ集めというのはいわば無駄な時間ですから短ければ短いほどよいわけで、そのために作業が中断したりしては本末転倒です。そこでいろいろな方法が考えられています。たとえば、あるオブジェクトがいくつのオブジェクトから参照されているかいつも管理しておくreference count法、机をいくつかの領域に区切って、頻繁に使う領域は頻繁に片付けるgenerational法などがあります。これらにはそれぞれに長所と短所がありますので、必要に応じて組み合わせたり、切り替えたりして使われています。

+

最近の言語にはほとんど何らかの形のごみ集め機能があります。Javaもそうですし、もちろんRubyにもあります。Perl, Python, PHP, Lispなどなど。ないのはCやC++, Fortran, Pascalなど古い言語ばかりです。

+

あなたのプログラムが実行中にたくさんのオブジェクトを作り出すときに、その背後ではごみ集め機能ががんばっていて、使われなくなったオブジェクトを人知れず回収して、メモリ領域をリサイクルしているのです。あなたのプログラムがうまく動いたとき、ガベージコレクタのことをちょっとだけ思い出してくださいね。そうすれば私も苦労して実装したことが報われるというものです。

+

現実世界の話に戻ると、結婚して私の部屋の状態は以前よりもずいぶんマシにはなったのですが、私と私の遺伝子を受け継いだ子供たちがどんどん散らかしてくれるので、妻がどんなに整頓しても、いつまでたっても部屋は片付きません。どうか私のうちを訪問するときには、あらかじめ片付けてからお迎えできるように前もって連絡してくださいね(苦笑)。

+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-012.xhtml b/docs/vol2/xhtml/p-012.xhtml new file mode 100644 index 0000000..71b00ab --- /dev/null +++ b/docs/vol2/xhtml/p-012.xhtml @@ -0,0 +1,386 @@ + + + + + +第36章 RubyとEmacs + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay36 +
+

+探訪Ruby
+RubyとEmacs +

+
+

[Linux magazine, 2004年5月号]

+
+

EmacsとRuby開発について解説しています。20年後にどうなっているかというと、世の中のエディタがVSCodeが主流になり、Emacsはだいぶ下火になっているのが悲しい現実です。Viの方が(Vimという形で)利用者が多いのが意外というかなんというか。現代では開発はツール支援が重要になっています。もっとも、あまり知られていないだけでEmacsやVimでもコード補完など統合開発環境と同等のことはできるのですが。

+

「Ruby開発日記」は「Emacsによる開発」というタイトルで、私の開発環境を紹介しています。Emacs、独自日本語入力配列、自作メールリーダーと並べると変態的な感じですね。20年経っても、私は変わらずEmacs、独自日本語入力配列、自作メールリーダーです。ただし、独自日本語入力の変換エンジンは当時利用していたCannaではなく、Mozcになっていますし、自作メールリーダーはcmailではなくmorqという全文検索ベースのもの(非公開)になっています。

+
+
+

Emacsをご存じですか? Emacsはviと並ぶUNIX上での定番エディタです。筆者は自他共に認めるEmacsファンで、Emacsに触らない日は1日もないほどです。今回は、RubyとEmacsの関係について語りましょう。

+
+
+

Emacs入門

+
+

Emacsは古くからあるテキストエディタです。Emacsの特徴はなんといってもその拡張性でしょう。EmacsにはEmacs LispというLisp処理系が内蔵されていて、カスタマイズや機能拡張を自由に行うことができます。

+

一番最初のEmacsはTECOというエディタマクロで記述されていたのだそうです。最初のEmacsをTECOで記述したのはフリーソフトウェア財団(FSF)のRMSことRichard Stallmanです。TECOによるEmacsが実装されたのは1976年だといわれています。それから、後にJavaの設計者となるJames Goslingが1981年にUNIXへの移植を行います。GoslingによるEmacs(通称Gosling EmacsまたはGosmacs)は、MockLispと呼ばれるLispもどきでの拡張機能を持っていました。

+
+

しかし、GoslingはGosmacsの権利をUnipressという企業に売却してしまい、StallmanはEmacsのソースコードを使った開発に不自由を感じるようになりました。StallmanがFree Software運動を始めたのはこのときの怒りが原因ではないかと聞いたことがあります。

+

いずれにしても、Unipress Emacsをベースに作業することができなかったStallmanは今度はCを使って再びゼロからEmacsを開発しました。これが現在広く使われているGNU Emacsです。GNU EmacsはGosmacsの拡張性を参考にしていますが、MockLispのようなカスタマイズ用のまがいものの言語ではなく、ちゃんとしたLispであるEmacs Lispを内蔵していました。

+
+
+

環境としてのEmacs

+
+

Emacsは起動時にユーザーのホームディレクトリにある.emacsという名前のファイルを読み込みます。.emacsの内容はEmacs Lispのプログラムとして解釈されます。Emacs LispからはEmacsの持つあらゆる機能にアクセスできますから、理論上はEmacsのすべてをカスタマイズすることが可能ということです。

+

しかも、Emacs Lispはフル機能を持つLisp言語ですから、ただ単にEmacsのカスタマイズのために使うだけでなく、それを使って任意のプログラムを書くこともできます。たとえば、各種プログラミング言語用のエディットモードもEmacs Lispで記述されています。

+

また、編集機能だけでなく、独立のアプリケーションも記述できます。例としてはネットニュースリーダーであるGnus, メールリーダーであるWanderlust, Mew, そしてcmail, それからIRCクライアントのLieceなどがあります。

+
+
+

Rubyモード

+
+

おっと、Rubyの話でしたね。もちろん、Emacs好きの開発した言語であるRubyはEmacsと関係があります。とはいえ、言語仕様上の関連は、文字を表現する「?a」のような記法がEmacs Lispと共通であるというだけですが。

+

私はRubyの開発を始める前には、cmailというメールリーダーを開発していました。cmailは最終的には数千行を超える規模のEmacs Lispプログラムで、LispとEmacsのよい勉強になりました。

+

このような私が、Rubyの開発を始めて言語文法をどうするか悩んでいた際に、一番気にしたのはオートインデントができるかどうかでした。当時、endでブロック構造を表現するプログラミング言語の編集モードはオートインデントをサポートしていないものがほとんどでした。ですから、オートインデントが実現できないようなら、CやJavaのようなブレース({})を使った仕様に変えようかと思っていたのです。しかし、数日Emacs Lispで戯れていると、endを使っていてもオートインデントできる編集モードのめどがつきました。それが現在Rubyに標準添付されているruby-mode.elの起源です。もし、あの時点でオートインデントに成功していなければ、Rubyは現在のようなendで終わる文法を持っていなかったことでしょう。

+
+

ruby-mode.elをインストールするためには、Emacsのロードパスが通ったディレクトリにruby-mode.elを置きます。性能上の問題がある場合にはバイトコンパイルしてruby-mode.elcを作ったほうが有利かもしれません。以前はライブラリのバイトコンパイルは必須でしたが、最近はマシンの性能も向上しているのでバイトコンパイルなしでも十分な速度で動くことも多いようです。

+

バイトコンパイルしてruby-mode.elからruby-mode.elcを作るためには、ruby-mode.elのあるディレクトリで、

+
+
% emacs -batch -f batch-byte-compile ruby-mode.el
+
+

と実行します。出来上がったruby-mode.elc.elファイルの代わりにロードパス上のディレクトリに置きます。

+

インストールしたruby-modeを使うために、自分の .emacsファイルにリスト36.1の内容を追加します。

+
+

リスト36.1●ruby-modeのための設定

+
(setq auto-mode-alist
+    (append '(("\\.rb$" . ruby-mode))
+           auto-mode-alist))
+
+(setq interpreter-mode-alist
+    (append '(("ruby" . ruby-mode))
+           interpreter-mode-alist))
+
+
+

これで拡張子 .rbを持つファイルか、ファイルの先頭に、

+
+
#! /usr/bin/ruby
+
+

のような記述のあるファイルは自動的にRubyプログラムだと見なされます。ruby-modeでは以下の機能が有効です。

+
    +
  • オートインデント

  • +
  • 予約語などの色付け

  • +
  • プログラム構成要素単位のカーソル移動

  • +
+
+
+

ハイライトとオートインデント

+
+

ruby-mode.elの設定を行ってから、EmacsにRubyプログラムをロードするとプログラムに色が付きます。これをハイライトと呼びます。デフォルトでは表36.1のように色が指定されています。

+
+

表36.1●ruby-modeの色

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
要素
コメント
予約語
メソッド定義
変数オレンジ
定数
文字列ピンク
正規表現ピンク
+
+ +

ruby-modeを使ってひと目でわかるのはハイライト機能ですが、作った本人はruby-modeの目玉はオートインデント機能のほうだと思っています。

+

ruby-modeでプログラムを編集中に、TABキーを押すとコンテキストに応じてその行をインデントします。改行時にはC-jCtrlキーを押しながらj)を押すと現在の行をインデントし、改行してからインデントレベルにカーソルを移動してくれます。

+

Emacsを使ってRubyプログラムを入力する場合、プログラムの本文だけを入力すればオートインデント機能によって自動的に行ってくれます。

+
+
+

Rubyプログラムの編集

+
+

ruby-modeではハイライトやオートインデントほどは目立ちませんが、他にもRubyプログラムの編集に役立つ機能が提供されています。表36.2ruby-modeの編集機能を一覧しておきます。

+
+

表36.2●ruby-modeの編集機能

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
編集コマンド編集コマンド
C-jruby-reindent-then-newline-and-indentESC C-nruby-end-of-block
TABruby-indent-commandESC C-pruby-beginning-of-block
{ruby-electric-braceESC C-fruby-forward-sexp
}ruby-electric-braceESC C-bruby-backward-sexp
C-c C-eruby-insert-endESC C-eruby-end-of-defun
ESC C-qruby-indent-expESC C-aruby-beginning-of-defun
ESC C-hruby-mark-defun
+
+

各編集コマンドを解説します。

+
+

ruby-indent-command

+

カレント行をインデントします。

+

ruby-reindent-then-newline-and-indent

+

カレント行をインデントし、改行した後、次の行のインデントレベルにカーソルを移動させます。

+

ruby-electric-brace

+

ブレースを挿入してからカレント行をインデントします。

+

ruby-insert-end

+

end」を挿入してからカレント行をインデントします。

+

ruby-indent-exp

+

カレント行がifなどの制御文やかっこなど複数行に渡る式を含むとき、式の末尾までの範囲内をインデントします。

+ +

ruby-beginning-of-block

+

現在のブロックの先頭に移動します。ブロックとはif文など複数の行に渡る文(または式)の先頭のことです。

+

ruby-end-of-block

+

現在のブロックの末尾に移動します。

+

ruby-beginning-of-defun

+

現在の「defun」の先頭に移動します。「defun」とはEmacsの用語では行の先頭で始まるブロック構造です。Rubyの場合、行の先頭から始まるclass文、module文、def文などが対象になります。

+

ruby-end-of-defun

+

現在の「defun」の末尾に移動します。

+

ruby-mark-defun

+

現在の「defun」の末尾にマークを置き、カーソルは「defun」の先頭に移動させます。

+

ruby-forward-sexp

+

式1つだけ前方にカーソルを移動させます。式が複数の行に及ぶ場合も正しく解釈します。

+

ruby-backward-sexp

+

現在の式の先頭までカーソルを移動させます。

+
+
+
+

ruby-modeのカスタマイズ

+
+

Emacsで提供されている編集モードをカスタマイズするためにはフック(hook)を使います。各モードは開始時にhookを呼び出しますから、そのモードに対するhookを設定しておいて、その中でカスタマイズを行います。

+

ruby-modeをカスタマイズするにはruby-mode-hookを使います。たとえば私はruby-modeで、

+
    +
  • abbrev(予約語補完)を行う

  • +
  • 改行キー(C-m)にreindent-then-newline-and-indentを、C-jnewlineを割り当て

  • +
+

しています。そのための設定をリスト36.2に示します。

+
+

リスト36.2●ruby-modeのカスタマイズ例

+
(add-hook 'ruby-mode-hook
+         '(lambda ()
+           (abbrev-mode 1)
+           (define-key ruby-mode-map "\C-m"
+                      'ruby-reindent-then-newline-and-indent)
+           (define-key ruby-mode-map "\C-j" 'newline)))
+
+
+ +

add-hookruby-mode-hookに手続きを追加する関数です。複数の手続きをまとめるためにはlambdaでくくる必要があります。

+
+
+

RRB

+
+

ruby-mode.elはRubyプログラムを編集することを支援する編集モードでしたが、Rubyプログラムの開発をより積極的に支援するEmacsのパッケージがあります。それはRRB(Ruby Refectoring Browser)です。

+

リファクタリングとは「ソースコードの動作を変えずに、内部構造をよりよいものに修正すること」です。

+

RRBは各種リファクタリング手法のうち、以下のものを支援します。

+
    +
  • 変数名の変更

  • +
  • メソッド名の変更

  • +
  • 定数名(クラス/モジュール名を含む)の変更

  • +
  • メソッドを親クラス/子クラスに移動する

  • +
  • コードの一部分をメソッドとして切り出す

  • +
  • 共通のスーパークラスを持つクラス群に対し新しいスーパークラスを定義する

  • +
+

原稿執筆時点でRRBのバージョンは0.0.2ですから、将来新たなリファクタリング機能が追加されるかもしれません。また、現時点ではEmacs上でのみ動作していますが、Emacs以外にもFreeRIDEやEclipseなどのIDE上でも動作するようになるかもしれません。

+

RRBについての情報は、

+
    +
  • http://www.kmc.gr.jp/proj/rrb/index.html

  • +
+

からどうぞ。

+
+
+

RRBのインストール

+
+

RRBはまだバージョンが若いせいか、私が試したときにはインストールが少々面倒でした。ここではちょっと詳しくインストール手順を説明しておきましょう。RRBは基本的にRuby 1.8対応です。1.6をお使いの方はshimライブラリを入手しておきましょう。

+

まず、ダウンロードします。0.0.2のダウンロードファイルは、

+
    +
  • http://www.kmc.gr.jp/proj/rrb/archive/rrb-0.0.2.tar.gz

  • +
+

です。これを展開するとカレントディレクトリにrrb-0.0.2というディレクトリができます。普段ならこのディレクトリのsetup.rbを実行することでインストール完了となることが多いのですが、現在のRRBは対応していません。ぜひsetup.rb(またはinstall.rb)対応をお願いしたいものです。

+

RRBには2つ拡張ライブラリが付属していますので、それぞれコンパイルします。

+
+
+
% cd reflection
+% ruby extconf.rb
+% make
+% sudo make install
+% cd ../ripper
+% ruby extconf.rb
+% make
+% sudo make install
+% cd ..
+
+

Rubyで書かれたライブラリもインストールします。

+
+
% cd lib
+% sudo cp -r rrb <Rubyライブラリのパス>
+
+

インストールするディレクトリは

+
+
ruby -e 'p $:'
+
+

の結果表示されるディレクトリから選んでください。私は /usr/lib/ruby/site_ruby/1.8にインストールしました。

+

忘れてはいけないことはripperディレクトリに置かれているripper.rbrrb/ripper.rbとしてコピーする必要があることです。

+
+
% sudo cp -r ripper/ripper.rb <ライブラリのパス>/rrb
+
+

次にrrbの本体であるコマンドライン版rrbを実行パスの設定されているディレクトリにコピーします。私は $HOME/binを選びました。実行モードの設定も必要です。

+
+
% chmod a+x bin/*
+% cp bin/* $HOME/bin
+
+

最後にEmacsからRRBを操作するためのEmacs Lispプログラム(rrb.el)をインストールします。これもload-pathが設定されているディレクトリならどこでもかまわないのですが、私は $HOME/lib/emacsを普段から使っているのでそこにコピーします。

+
+
% cp elisp/rrb.el $HOME/lib/emacs
+
+

これでやっとインストール完了です。

+
+
+ +

RRBを使ってみる

+
+

苦労してインストールしたのですから、活用しないと損です。さっそく使ってみたいのですが、技術的な理由からRRBには以下の制限があることを覚えておいてください。

+
    +
  1. 複数のスクリプトからなるプログラムの場合、すべてのスクリプトをEmacs上に読み込んでおかなければならない

  2. +
  3. また、Emacs上に読み込まれているすべての*.rbがリファクタリングの対象となる

  4. +
  5. スクリプトは定義部と実行部に分かれていなければならない

  6. +
+

最後の点だけは特に注意が必要ですので、詳しく説明しておきます。RRBはプログラムを解析するため、プログラムを構成するRubyファイルをrequireを使って読み込みます。そのときに、定義だけでなく実際の実行も行われてしまうといろいろとまずいわけです。基本的には「requireしても副作用が起きない」ことが必要です。

+

具体的には、プログラムの実行部分を、

+
+
if $0==__FILE__
+  ...
+end
+
+

のように囲んでしまうのがよいでしょう。これでrequireされたときには副作用がなく、コマンドとして実行されたときには(__FILE__$0が一致するので)実行部にも制御が渡ります。このテクニックはライブラリではテストコードを囲むために用いられます。

+

では、使ってみましょう。誌面の関係で簡単なコードしか使えませんが、リスト36.3のようなサンプルを使います。

+
+

リスト36.3●RRB例題コード

+
class Foo
+  def power(n,p)
+    n * p
+  end
+  def print_powertwo(n)
+    print "n = ", n
+    print "; ", n, "*2 = ", power(n, 2), "\n"
+  end
+end
+
+class Bar<Foo
+end
+
+
+ +

このサンプルに対して以下のような変更を行います。

+
    +
  • powerメソッドをtimesメソッドに名称変更
    +M-x rrb-rename-methodを使います。対象となるクラス(今回はFoo、複数指定可)を指定し、新しい名前を入力します。古い名前でエラーを出すメソッドが自動的に用意されますので、必要に応じて削ります。

  • +
  • timesの引数(n, p)(n1, n2)
    +M-x rrb-rename-local-variableを使います。対象となるメソッドと新旧の変数名を入力します。

  • +
  • print_powertwoprint_twotimesに名称変更
    +M-x rrb-rename-methodを使います。

  • +
  • print_twotimesBarクラスに移動
    +M-x rrb-pushdown-methodを使います。メソッドと移動先のクラスを指定します。

  • +
  • FooクラスをTwoTimesクラスに名称変更
    +M-x rrb-rename-constantを使います。新旧の定数名(クラス名)を指定します。

  • +
+

最終的な結果はリスト36.4のようになります。この例では自動的に挿入された例外発生用のメソッドは取り除いてあります。

+
+

リスト36.4●リファクタリング結果

+
class TwoTimes
+  def times(n1,n2)
+    n1 * n2
+  end
+end
+
+class Bar<TwoTimes
+  def print_twotimes(n)
+    print "n = ", n
+    print "; ", n, "*2 = ", times(n, 2), "\n"
+  end
+end
+
+
+

RRBで行ったような変更は、もちろんエディタの置換機能を使っても実現することはできます。しかし、RRBはRubyプログラムを解析して、意味的に正しい変更を行ってくれますからずっと安心して変更を行うことができます。

+

なお、今回紹介した、ruby-mode.el(最新版)、rrb-0.0.2, yacc.elは付録CD-ROMに掲載しています。

+
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-013.xhtml b/docs/vol2/xhtml/p-013.xhtml new file mode 100644 index 0000000..2c85e41 --- /dev/null +++ b/docs/vol2/xhtml/p-013.xhtml @@ -0,0 +1,62 @@ + + + + + +第36章 RubyとEmacs + + + + +

Matz Essays Volume 2

+ + +
+ +

◆ Ruby開発日記 ◆Emacsによる開発

+
+

本文でも書いたように私はEmacsの中で「生活」しているわけですが、実際どのような生活をしているか、を紹介しましょう。

+

Emacsはエディタですからやはり編集が基本です。Emacsはカーソル移動にCtrlキーを使った組み合わせを用います。たとえば1文字進めるためにはC-f、1文字戻るためにはC-b、1行進むためにはC-n、1行戻るためにはC-pを用います。これらのキーバインドはすでに私の体の一部になっています。ずいぶん以前になりますが、Mozilla上でテキスト入力中に無意識にEmacsキーバインドを用いていて、しばらくたってからMozilla上でも有効であることに改めて気が付いてびっくりしたことがあります。

+

次は、日本語入力です。私は自分自身をプログラマーだと自認していますが、この原稿を始めとして文章を入力する機会も少なくありません。日本語入力には「かんな」を使っています。「かんな」には日本語入力の配列を再定義する機能があるのですが、それを用いて独自の日本語配列を定義しています。「きゅうり改」と名付けられたこの配列(図36.1)は、左手で子音、右手で母音を入力するようになっており、効率的に日本語を入力できるようになっています。

+
+ +
+ fig3601 +
+

図36.1●きゅうり改

+
+

「小」続く文字が小さい文字(ゎとか)であることを示します。また、「ょ」などのキーを2度打ちすると「ょう」のような連母音になります。

+

以下が「きゅうり改」による入力例です。

+
+
エディタ入力について紹介する
+;vbkgheiirodjekgjkg;foodhkfjrj
+edhitanyuuryokunituiteshoukaisuru
+
+

アルファベットで見ると何を入力しているのかひと目ではわかりませんね。正直、私にもわかりません。配列は手が覚えているので、目で見てもよくわからないのです。しかし、よく見ると、入力に使うキーのうちほとんどが打ちやすいホームポジションと上段のキーで占められていて、指の移動が少なく効率がよいことがわかるかもしれません。

+

私はもう10数年この配列を愛用しています。配列を決めてから使いこなせるようになるまでに3日くらいかかりましたが、それ以降は快適です。

+

英文の入力にはflyspell-modeが便利です。flyspell-modeは実行時にスペルチェックを行ってくれるマイナーモードです。スペルが単語に色を付けてくれます。マイナーモードですから、英文を入力するときにはいつも設定しておくとよいでしょう。

+

私の生活の多くは電子メールの読み書きに費やされています。電子メールを読むためにはcmailを使います。cmailは私がもう10年以上使っているメールリーダーです。1998年頃までは私が自分でメンテナンスしていました。それ以降はRubyのほうが忙しくて面倒が見れなくなったので、メーリングリストによる集団管理体制に移行しています。cmailは設計は古いのですが、MIME対応やIMAP対応など現在でも十分な機能を備えています。もう手になじんでしまっているので、なかなか他のメールリーダーに移行できません。

+ +

私がプログラムを開発するときに、よく使う言語はC, Ruby, shの順です。RubyよりもCを使う時間のほうが長いというのがなんだか皮肉な気もしますが、実際私はRubyを使っている時間よりもRubyを作っている時間のほうが長いということでしょう。

+

これらの言語にはそれぞれのための編集モードが用意されており、予約語の色付けを行ったりオートインデントを行ったりできます。ただし、shモードにはオートインデント機能はありません。

+

Rubyでのプログラミングを支援してくれるruby-modeとRRBについては本文で紹介しました。その他にもrubydb.elというものがあってEmacsを使ってRubyデバッガを実行できるのですが、私自身はRubyでデバッガを使わないのでほとんど使ったことがありません。

+

Cの編集モードはcc-modeといいます。単なるc-modeでなくcc-modeである理由はどうやらC++などC類似の言語にも対応しているからのようです。実際、私がEmacsを使い始めた頃にはCの編集モードはc-modeでしたし。

+

Cでプログラムしているときには実行する前にコンパイルしなければなりません。プログラムを書いたらいきなり実行できるRubyよりも不自由なものですが、それがCですからしかたがありません。コンパイルにはM-x compileを使います。これを実行するとEmacsに新しいウィンドウを開いてコンパイルを実行してくれます。また、コンパイル中にエラーが発生すると、キー操作1つでエラー発生箇所にジャンプできます。

+

プログラミング中に関数の定義場所や使われている場所を探したい場合もあります。このときにはM-x grepを使います。これはM-x compileと同様の機能で、コンパイル結果の代わりにgrepの検索結果を表示して、やはりキー操作でジャンプできます。

+

デバッグにはM-x gdbが便利です。現在実行中のソースコードを見ながらデバッグできます。

+

Rubyのソースコードには1つだけCではなく、yaccで記述されたものが含まれています。Rubyの構文解析を担当しているparse.yがそのファイルです。yaccは文法ルールの中にCのコードが埋め込まれた記述です。yacc自身が一種の言語であると考えてもよいでしょう。

+

大概の言語にはその言語用の編集モードがあるのですが、なぜかyaccモードはEmacsに標準添付されていません。いくら多くの言語処理系がyaccを使っているとはいえ、yaccはあまりにもマイナーなのでしょうか。

+

私はずっと以前ネットで入手したyacc.elというファイルを使っています。このファイルには作者の名前も何も書いてないのですが、なかなか便利です。このyaccモードはCコードを編集するときにはCモードに、外側のyaccルールを編集するときにはyaccモードに切り替わります。

+

唯一の欠点はCコードの編集中に外側のyacc記述が見えないことです。どこかにもっとよいyaccモードはないものでしょうか。

+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-014.xhtml b/docs/vol2/xhtml/p-014.xhtml new file mode 100644 index 0000000..56f6647 --- /dev/null +++ b/docs/vol2/xhtml/p-014.xhtml @@ -0,0 +1,419 @@ + + + + + +第37章 Instiki + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay37 +
+

+探訪Ruby
+Instiki +

+
+

[Linux magazine, 2004年6月号]

+
+

今回はWikiアプリケーションであるInstikiとその内部の解説です。もうInstikiはメンテナンスされていませんし、使われている技術の多くも(発想はともかく)現代ではなくなってしまっています。そういう意味では時代遅れの記事なのですが、注目すべきはInstikiの作者です。作者はDavid Heinemeier Hansson, つまりRuby on Railsの作者DHHその人です。彼はこの記事が公開された年にRuby on Railsを公開し、Ruby成功の立役者になりました。Instikiを見ると彼の技術へのこだわりがちょっとうかがえるような気がします。

+

「Ruby開発日記」は「歴史は繰り返す」です。繰り返し登場しては失敗する技術というものがあるものです。ここであげた3つの技術は、20年経ってもやっぱり何度も登場しては成功せずに終わっています。わずかな例外としては子供向けビジュアル言語であるScratchでしょうか。同じような技術は他にもあると思います。そういえばタブレットコンピュータも繰り返し失敗した技術でしたが、iPadによってとうとう成功してしまって、貴重な例外になりましたね。

+
+
+

今月は「とんがったWiki」であるInstikiの内部を眺めることで、Instikiが使っている「新しい道具」について学ぶことにします。

+
+
+

Instiki

+
+

InstikiはWikiを実現するWebアプリケーションで、David Heinemeier Hanssonによって作られました。誰でも自由に書き込むことができるWebシステムであるWikiについては、第3回でも取り上げましたが、今月はWikiそのものについては解説しません。Wikiの詳細は2004年2月号の第3回を参照してください。Instikiの原稿執筆時点での最新バージョンは0.7.0です。Instikiのホームページは、

+
+
    +
  • http://instiki.nextangle.com:3000/wiki/show/HomePage

  • +
+

です。このサイトもInstikiで運用されています。

+

ユーザーから見たInstikiの最大の特徴は、インストールが簡単なことです。tar.gzファイルをダウンロードして、任意の場所に展開したら、その中にあるinstiki.rbを実行すれば終わりです。あとは何も必要ありません。HTTPサーバーを用意する必要さえないのです。必要なライブラリもすべて添付されています。

+

しかし、Instikiの本当の面白さはその実装にあります。今回はInstikiを実現している中身を探ることで、明日の開発に役立つ技術を学ぶことにしましょう。

+

Instikiを構成している技術には以下のようなものがあります。どれも面白いものばかりです。

+
    +
  • WEBrick

  • +
  • ERB

  • +
  • RedCloth

  • +
  • Madeleine

  • +
+

Instikiはこれらの技術をMVCで構成しています。MVC(Model-View-Controller)についてはまた別の機会に解説したいと思います。

+
+
+

WEBrick

+
+

WEBrickは、CQ出版社の今はなき『Open Design』誌における連載「Rubyではじめるインターネットプログラミング」の中から生まれたインターネットサーバー構築用ツールキットです。Ruby 1.8からは標準添付になっています。作者は高橋征義さんと後藤裕蔵さんです。

+

InstikiはWEBrickを使って、自前でHTTPサービスを提供します。Apacheなどのインストールが必要ない反面、HTTP標準の80番ポートを使用するためにはInstikiの実行そのものにroot権限が必要になります。

+
+
+

ERB

+
+

ERBはdRubyの作者でもある咳さん(関将俊さん)によるRubyで記述されたeRuby(embedded Ruby)の実装です。eRubyがフォーマットの名前で、ERBがそれを解釈するツールの名前です。ERBはRuby 1.8に添付されているのですが、なぜかInstikiにも含まれています。

+

eRubyはテキストにRubyのプログラムを埋め込んだものです。これがembeddedと呼ばれるゆえんです。Rubyプログラムを埋め込んだeRubyファイルの例をリスト37.1に示します。

+
+

リスト37.1●eRubyファイルの例

+
<HTML>
+<BODY>
+<H1>現在時刻</H1>
+<%= Time.now %>
+<UL>
+<%# loop in eRuby %>
+<% for i in %w(foo bar baz) %>
+  <LI><%= i %></LI>
+<% end %>
+</UL>
+<H1>eRuby書式</H1>
+<P>eRubyは <%% と %%> の間にRubyを埋め込みます。</P>
+</BODY>
+</HTML>
+
+
+ +

これをERBで処理すると、eRubyは「<%」と「%>」で囲まれた部分を処理します。その外側はそのまま残します。「<%= %>」で囲まれた部分はRubyのプログラムとして解釈され、その結果がファイルに埋め込まれます。

+

<% %>」で囲まれた部分はプログラムとしての解釈だけを行い、結果の埋め込みは行いません。

+

<%# %>」で囲まれた部分はコメントとして解釈し、式の評価も結果の埋め込みも行いません。最後に「<%%」と「%%>」はエスケープとして解釈され、それぞれ「<%」と「%>」に置換されます。

+

eRubyファイルを解釈するには以下のようにします。

+
+
require 'erb'
+
+s = ERB.new(str).result(binding)
+
+

ここでstrはeRubyフォーマットの文字列、bindingはRubyプログラムを解釈する場合の名前空間です。ある場所での名前空間はbindingメソッドで得ることができます。bindingの指定は省略可能で、その場合にはトップレベルで評価することになります。

+

InstikiではそれぞれのページのテンプレートをeRubyファイルとして用意しておいて、ERBを通すことでHTMLファイルを出力しています。テンプレートを利用することで、画面表示を比較的自由にカスタマイズすることができるようになっています。

+
+
+

RedCloth

+
+

Wikiは普通「Wiki記法」と呼ばれる簡単なマークアップを使って入力するのですが、InstikiはWiki記法を使いません。その代わりに使っているのがTextileと呼ばれる記法です。もともとはPHPあたりから登場した記法のようですが、表37.1のような記法でマークアップできるものです。

+
    +
  • http://www.textism.com/tools/textile/

  • +
+ +
+

表37.1●Textile記法

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
記法表記意味表記例
ブロックh[n].ヘッダー(H1, H2h1.
記法bq.ブロック引用bq. this is quoted
#番号リスト# numeric list
*リスト* bullet list
インライン_foo_強調
記法__it__斜体
*strong*強調(拡大字体)
**bold**ボールド
??cite??引用
-del-削除
+ins+挿入
^super^上付き
~sub~下付き
@code@コード
"link":URLリンク
!URL!イメージ
|a|table|row|テーブル
%(class)span%範囲指定%{color:red}hello%
+
+

RedClothはTextileを解釈してHTMLに変換するためのライブラリです。作者はRuby用YAML処理系の作者でもあるwhy the lucky stiffです。変な名前ですが、「Why」は彼の本当の名前だそうです(発音は「わい」)。

+

Textileは思想的にはRDなどに近いのですが、RDには、

+
    +
  • 汎用のドキュメントフォーマットを目指しているが、TextileはHTML専用、他のフォーマットには変換できない

  • +
  • ブロック記法は貧弱だが、インラインが豊富

  • +
+

という特徴があります。個人的にはTextile表現は文章を書くためには貧弱すぎる印象を持っているのですが、Wikiくらいの軽い文章を書くのには向いているのかもしれません。

+

TextileはHTMLに特化しているので、RDでは難しいイメージやテーブル、色指定や、それらのアラインメント(右寄せ、左寄せ、センタリングなど)も指定できます。また、<pre><code>などはHTMLタグを直接書くことで対応しています。

+

イメージはURLを「!」で囲むことで表現します。また、かっこで説明文(alt)を指定することもできますし、冒頭にアラインメント指定子(「<」「>」「=」のいずれかで、それぞれ左寄せ、右寄せ、センタリング)を指定します。

+
+
!>http://www.rubyist.net/image/ruby.gif(Ruby Gem)!
+
+
+

かっこによる指定はリンクにも使えます。たとえば、

+
+
"a link (textism site)":http://www.textism.com/
+
+

と指定すれば、「textism site」の部分がHTMLのaタグのタイトル属性になります。

+

RedClothの使い方は非常に簡単で、redclothライブラリをrequireした後、RedCloth.new(str)RedClothオブジェクトを作り、to_htmlメソッドでHTMLに変換するだけです。

+
+
require 'redcloth'
+
+html = RedCloth(str).to_html
+
+

InstikiはURLのリンク化とWikiワードにも対応していますが、これはRedClothの機能ではなく、Instikiの内部で置換を行っています。

+
+
+

Madeleine

+
+

MadeleineはObject PrevalenceのRuby実装です。作者はAnders Bengtssonです。

+
    +
  • http://madeleine.sourceforge.net/

  • +
+

Object Prevalenceとはアプリケーションの永続化のためのデザインパターンの一種です。Object Prevalenceでは永続化するデータをデータベースサーバーのような異なるプロセスに置くのではなく、オブジェクトをそのままアプリケーションプロセスのメモリに格納します。リレーショナルデータベースを使う場合のように、データベースに格納するためにオブジェクトをテーブルに変換する必要もなく、検索のためにSQL文を組み立ててサーバーとソケット通信する必要もないので、構造が簡単で、高性能を実現しやすい特徴があります。

+

Java用のObject PrevalenceライブラリのPrevaylerのホームページには、JDBCを経由してOracleを利用する場合と比較すると、Prevaylerのほうが9000倍(!)高速なケースがあると自慢しています。

+
    +
  • http://www.prevayler.org/wiki.jsp

  • +
+

9000倍とは穏やかではありません。その秘密はどこにあるのでしょうか。

+

Object Prevalenceの仕組みは、スナップショットとジャーナリングにあります。実際に処理するデータは実行中のアプリケーションのメモリ中に保持し、検索などはSQLなどを介さず直接行います。データの更新はメモリに対して行うと同時に、ジャーナルログにも書き込みます。

+

また、定期的にメモリ中のデータをスナップショットと呼ばれるファイルに書き出し、そこまでのジャーナルログは消去します。

+

プログラムの起動時には、

+
    +
  • スナップショットが存在しなければアプリケーションデータを初期化

  • +
  • スナップショットがあれば最新のものを読み込む

  • +
  • ジャーナルログもあればそれも読み込み、アプリケーションデータを最新のものにする

  • +
+

という手順でメモリ中にデータを用意します。プログラムが異常終了した場合でも、スナップショットとジャーナルログは残されていますので、最新のデータを復旧することが可能です。

+
+

基本的な原理はこれだけです。しかし、Java版(Prevayler)では、別の仮想マシンにアプリケーションデータのReplica(レプリカ、複製)を用意して、ジャーナリングとスナップショットの生成はそちらに任せています。これによりスナップショットのために実行を停止させる必要がなく、高性能が実現できているわけです。Ruby版のMadeleineではReplicaは(まだ)使われていませんから、スナップショットを取るために全体が停止してしまいます。ですから、Java版ほど劇的な性能は実現できないような気がします。

+

Object Prevalenceにも欠点がないわけではありません。

+

まず、すべてのデータをメモリ中に保持するため、データ量が増大するとそれにつれてメモリを消費します。リレーショナルデータベースでは参照しないデータはファイルだけに格納されるので必要メモリ量は少なくて済みます。もっとも最近はメモリの最大値は増加しており、サーバーではギガバイト単位のメモリ容量も少しも珍しくなくなりました。価格も低下の一方ですから、メモリ容量は重大な問題にならなくなっているかもしれません。

+

次の問題は、データがアプリケーションプロセスのメモリ中に展開されているため、複数プロセスが同一データにアクセスすることには向かないということです。この場合には、データを保持するプロセスがサーバーとなって、複数のクライアントからのアクセスを受け取るという形になりますが、そうなると、せっかく削減したソケット通信を再び導入することになってしまいます。ここは上手に実装しないとせっかく実現した高性能を台無しにしてしまう可能性があります。

+

最後に、Object Prevalenceそのものの欠点ではないのですが、実は現在のRuby処理系には大量オブジェクトの扱いにGCの問題がありますから、それでなくてもあまり大量の永続データの扱いには向きません。とはいっても、Instikiが取り扱うようなWikiデータ程度の規模なら何の問題もないでしょう。

+

RubyにおけるMadeleineの使い方は以下のとおりです。

+

まず、システムの初期化を行います。

+
+
require 'madeleine'
+
+m = SnapshotMadeleine.new("/path/to/storage") {
+  Data.new
+}
+
+

SnapshotMadeleine.newの引数にはスナップショットやログを置くディレクトリへのパスを指定します。省略可能な第2引数にはスナップショットへオブジェクトを書き込む際に文字列化を行うモジュールを指定します。デフォルトでは組み込みのMarshalモジュールを用いますが、たとえばYAMLモジュールを指定することで、YAMLフォーマットでセーブすることができるようになります。

+

既存のスナップショットが存在しない場合には、ブロックを評価してその結果をアプリケーションデータにします。

+

戻り値がMadeleineへのハンドルになります。データへのアクセス(特に更新)は必ずこのハンドルを経由する必要があります。アクセスの方法は以下のとおりです。

+
+
m.execute_command(command)
+
+
+

commandはコマンドオブジェクトと呼ばれて、データへのアクセスを行うオブジェクトです。execute_commandが呼ばれると、以下の手順で処理を行います。

+
    +
  • ロックを行う

  • +
  • ログに書き込む

  • +
  • command.executeを呼ぶ

  • +
+

commandはどんなクラスのオブジェクトでもかまわないのですが、以下のメソッドを持つ必要があります。

+
+
command.execute(data)
+
+

dataはアプリケーションデータになります。

+

Madeleineには使用上の注意がいくつかあります。

+

まず1つは、commandexecuteコマンドは副作用を持っていてはいけない、ということです。ここでいう副作用を持たないとはアプリケーションデータ以外に影響を及ぼさないということです。これはたとえばアプリケーションデータ以外のオブジェクトを変更することとか、何かを出力することを含みます。executeコマンドはログからの復旧などユーザーが予想しない時点で呼び出されますので、副作用があるとアプリケーションに悪影響が発生します。

+

それから、dataはスナップショットとしてファイルに保存されますから、ファイルに書き込むための文字列化を行うことができるデータだけを含む必要があります。たとえば、SocketなどIOオブジェクトのサブクラスは文字列化できないので、データに含むことはできません。間接的に参照するのでもだめです。

+

また、commanddataへの参照を持たないという点にも注意する必要があります。アプリケーションデータへの参照はexecuteの引数として渡されるものだけを使ってください。commandはやはり文字列化されてジャーナルログに記録されますから、アプリケーションデータへの参照を持っているとログがふくれあがって性能上の問題が発生します。

+

では、ここでMadeleineを使った簡単なプログラムを見てみましょう(リスト37.2)。

+
+

リスト37.2●Madeleineサンプル

+
require 'madeleine'
+
+class CountData
+  attr_accessor :count
+  def initialize
+    @count = 0
+  end
+end
+
+class CountInc
+  def execute(data)
+    data.count += 1
+  end
+end
+
+class CountShow
+  def execute(data)
+    data.count
+  end
+end
+
+# Madeleineハンドルを初期化
+m = SnapshotMadeleine.new("/tmp/data") {
+  CountData.new
+}
+
+Thread.start {
+  loop {
+    sleep 120      # 120秒ごとにスナップショットを取る
+    m.take_snapshot
+  }
+}
+while line = gets
+  case line
+  when /^inc/
+    printf "count -> %d\n", m.execute_command(CountInc.new)
+  when /^show/
+    printf "count: %d\n", m.execute_query(CountShow.new)
+  end
+end
+
+
+ +

このプログラムは標準入力に“inc”と打つとカウンタの値を1増やし、“show”と打つとカウンタの現在値を表示します。execute_queryメソッドはまだ説明していませんが、これはログに記録しないでコマンドを実行するメソッドです。ログに記録が行われませんから、実行されるコマンドはアプリケーションデータを更新しないようにしてください。データの更新が行われると、メモリ中のデータとジャーナルの間に不整合が発生して深刻な問題を引き起こします。

+
+
+

Madeleine::Automatic

+
+

Instikiでは実際にはMadeleineのMadeleine::Automaticという機能が使われています。Madeleine::Automaticを使ったサンプルをリスト37.3に示します。

+ +
+

リスト37.3●Madeleine::Automaticサンプル

+
require 'madeleine'
+require 'madeleine/automatic'
+
+class CountData
+  include Madeleine::Automatic::Interceptor
+  attr_accessor :count
+  def initialize
+    @count = 0
+  end
+  def inc
+    @count += 1
+  end
+end
+
+m = Madeleine::Automatic::
+      AutomaticSnapshotMadeleine.new("/tmp/data") {
+  CountData.new
+}
+Thread.start {
+  loop {
+    sleep 10
+    m.take_snapshot
+  }
+}
+while line = gets
+  case line
+  when /^inc/
+    printf "count -> %d\n", m.system.inc
+  when /^show/
+    printf "count: %d\n", m.system.count
+  end
+end
+
+
+

Madeleine::Automaticを使ったものとの違いは、

+
    +
  • ハンドルのクラス名が違う(えらい長い)

  • +
  • コマンドが登場しない

  • +
  • データオブジェクトのメソッドを(m.system経由で)直接呼べる

  • +
+

という点です。コマンドを使わないで済むのでプログラムがずいぶんすっきりしているのがわかります。

+
+
+ +

まとめ

+
+

今月は実際のアプリケーションであるInstikiの中身を見ることで、新しい技術をいくつか紹介しました。Instikiは進歩が速く、今回の原稿を用意している間に3回もバージョンアップが行われました。実際にInstikiを使うかどうかはともかく、他人のソースコードを読むことは大変に勉強になります。あなたもソースコードを探検してみませんか?

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-015.xhtml b/docs/vol2/xhtml/p-015.xhtml new file mode 100644 index 0000000..7461ce0 --- /dev/null +++ b/docs/vol2/xhtml/p-015.xhtml @@ -0,0 +1,59 @@ + + + + + +第37章 Instiki + + + + +

Matz Essays Volume 2

+ + + +
+

◆ Ruby開発日記 ◆歴史は繰り返す

+
+

この「Ruby開発日記」の中でもかつて何度も言及したことがあるのですが、プログラミング言語は半世紀を超える非常に長い歴史を持ち、しかも非常にゆっくりと進歩してきました。1つの概念が誕生して世の中に広まるまでに10年、20年かかるのはざらです。

+

その中で同じようなアイデアが繰り返し繰り返し発明されては消えていきました。なかにはごくまれに何度目かの再発明で初めて成功するものもありますが、たいていは以前に失敗したアイデアはたとえ再登場してもまた消えていくのがオチです。そのようなことはプログラミング言語に限ったことではないのかもしれませんが、今回はプログラミング言語の周辺からそのようなアイデアをいくつか眺めてみて、今後同じような失敗をしないための肥やしにしてみたいと思います。

+
+

ビジュアル言語

+

現在のプログラミング言語のほとんどは単語が一列に並んでいるという観点からは、いわば1次元の言語です。これを2次元に拡張しようというのがビジュアルプログラミング言語です。

+

このアイデアは研究者にとって魅力的に感じられるようで、昔から繰り返し繰り返し登場しています。古くはフローチャートや構造化記法(PAD)などからプログラムを生成しようとする試みがありましたし、中にはPrographのように実際に商品化されたものもあります。しかし、どれも大きな成功を収めたとはいえないように思います。

+

理由はいろいろあるのでしょうが、私はプログラミング言語の2つの側面を無視したせいではないかと考えています。

+

1つは、入力の繁雑さです。「旧式」のプログラミング言語は、文章を記述するようにキーボードはぱたぱたと打つだけで入力できますが、ビジュアル言語の多くは、パレットから部品を選んでキャンバスに置き、他の部品と線を連結する、などなどの操作をマウスを使って行う必要があります。入力には特殊なツールが必要ですし、正直なところ、プログラミングのロジックを記述するにはやってられない作業です。初心者にプログラミングの概念をイメージさせる教材としてはいくらか効果があるかもしれませんが、仕事の道具としてはいかにも物足りません。

+

もう1つは(こちらのほうが本質的なのですが)、人間の思考が基本的に1次元だということです。おそらくは脳の内部では非常に粒度の高いパラレルな処理を行っているのだと思いますが、人間の意識は同時に1つのことを順に行うことに慣れ切っています。この意識に適合するのは多次元的なビジュアル言語よりも、1次元的な旧来の言語のほうが向いているように思います。旧来のプログラミング言語は自然言語の体裁を流用しており、コンピュータは進歩しても人間はそれに適用しきれないというわけです。何千年も使われてきたやり方をなめてはいけません。

+

今後もロジック記述の方法としてビジュアル言語が主流になることはないと思います。ただし、画面レイアウトのような特殊な領域ではビジュアル言語と呼んでもよいようなものが登場することはあるかもしれません。

+
+
+ +

構造エディタ

+

私が学生時代にはもう下火にはなっていましたが、70年代から80年代にかけて「構造エディタ」というテーマが流行していました。これはプログラミング言語の文法を理解したエディタで、たとえばブロック構造などを(PADのような)図で表現したり、入力中に構文エラーを報告したりすることができるようなエディタです。

+

構造エディタのプログラムを図式で表現する部分は、何度繰り返されてもまったく定着しませんでした。これは構造エディタのために新しい言語を用意する、というような研究テーマとしてはともかく実用上はどうかというような戦略の問題だけではなく、ビジュアル言語と同じような理由も含まれていたのではないかと思います。プログラムの見かけは思った以上に重要なのです。

+

構造エディタの機能のうち、もっと控えめなほうはさまざまに形を変えて最近のエディタやIDE(統合開発環境)に取り込まれています。たとえば、前号のこの連載で紹介したように、Emacsを含む多くのエディタは、オートインデントやプログラムを適切に色付けによってプログラミングを支援してくれますし、RRB(Ruby Referctoring Brower)のように、プログラムの構造を理解した編集支援を行ってくれるツールもあります。

+
+
+

文法独立の言語

+

プログラミング言語は、実際は外見を決定する文法(シンタックス)と、意味を定義するモデル(セマンティック)とに分けることができます。あえてどちらがより重要かと問われれば、当然セマンティックのほうが重要と答えるわけです。

+

この考えを推し進めると、セマンティックの方だけを定義しておいて、シンタックスは自由に選べるのはどうだろうか、というアイデアが登場します。このアイデアは古くから何度も何度も登場します。

+

古くはS式とM式を選択できるLisp、もうちょっと近くでは文法切り替えができる言語を目指したGuile(GNUのSchemeインタプリタ)、最近だとPerl 6チームが開発しているParrotがあります。しかし、どれも成功したとはいえません。Parrotだけはまだ可能性があると思っていますが。

+

これらが成功するのが難しい理由は、1つは実現の難易度の問題でしょう。複数の言語に対応するセマンティック部分は、それらの言語の公倍数の機能を持つ必要があり、複雑さが格段に上昇してしまうからです。

+

もう1つの問題は、前にも述べましたが、文法(見かけ)は思ったよりも重要ということでしょう。意味の点から考えると外見は枝葉末節にすぎないのですが、プログラマーの気持ちという点では実に本質に近いところにあるのではないでしょうか。言語ごとに微妙に異なるセマンティックのために意識を切り替えるスイッチとして文法の違いが役立っている、そんな気がします。

+
+
+

まとめ

+

今回は、プログラミング言語界で日の目を見なかったいくつかのアイデアを観察してみましたが、結論としては「文法が重要」ということでしょうか。言語にとって「見かけ」とか「気分」とかがいかに重要であるかを再確認した思いです。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-016.xhtml b/docs/vol2/xhtml/p-016.xhtml new file mode 100644 index 0000000..b76d3a6 --- /dev/null +++ b/docs/vol2/xhtml/p-016.xhtml @@ -0,0 +1,329 @@ + + + + + +第38章 テンプレート + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay38 +
+

+探訪Ruby
+テンプレート +

+
+

[Linux magazine, 2004年7月号]

+
+

テンプレートツールについて説明しています。今回紹介したものの中でERBは現役でRuby on Railsの標準テンプレートエンジンとして活躍していますね。他のものはあまり見かけなくなりましたね。

+

「Ruby開発日記」は「プロトタイプな世界」と題してプロトタイプベースのオブジェクト指向について解説しています。プロトタイプベースオブジェクト指向は今回の記事でも紹介しているように、いくつかの言語で採用されましたが、最近はあまり人気がありません。JavaScriptが広まったときには、これでプロトタイプベースのオブジェクト指向が広まるかと期待されましたが、そうはならなかったようです。人間にとってクラスベースのほうが理解しやすいのかもしれませんね。生き残ったJavaScriptも、そもそも独自のオブジェクトをユーザーが定義するようなスタイルは一般的ではありませんし、そもそも言語にclass文が導入されたりしてクラスベースに近づいています。

+
+
+

もともと「テンプレート」とは図形を書いたりするときに使う穴を抜いた下敷きのようなものです。コンピュータ業界ではそこから転じて、大枠を決めておいて穴埋めしてファイルを生成するような場合の「雛形」のような意味で使われます。

+
+
+

ファイル生成

+
+

プログラムの実行結果を含めたHTMLファイルを生成するなど、プログラムによってファイルを生成する処理は珍しいことではありません。

+

何も考えないでファイルを生成するプログラムを書くとリストリスト38.1のようになります。

+ +
+

リスト38.1●ファイル生成プログラム

+
puts "<HTML><HEAD><TITLE>こんにちは</TITLE></HEAD>"
+puts "<BODY><P>"
+printf "こんにちは。現在の時刻は%sです\n", Time.now
+puts "</P></BODY></HTML>"
+
+
+

生成結果はリスト38.2です。

+
+

リスト38.2●実行結果

+
<HTML><HEAD><TITLE>こんにちは</TITLE></HEAD>
+<BODY><P>
+こんにちは。現在の時刻はThu May 13 13:28:15 JST 2004です
+</P></BODY></HTML>
+
+
+

これはプログラムが主でファイルの内容が従という構成です。この構成ではファイルの静的部分がプログラムのあちこちに分散して、見通しが悪くなります。

+

また、Webサービスなどではデザイナーとプログラマーが分業することがしばしば行われます。そういう場合にデザイナーとプログラマーが同じファイルを同時に変更するといろいろと面倒なことが起こります。

+

そういう諸問題を避けるため、ファイルの静的部分と動的な内容を埋め込む場所を記述したテンプレートを用意し、動的な内容の展開はそのテンプレートを解釈するライブラリ(テンプレートエンジンと呼ぶことがあります)によってによって展開させることが有効となります。6月号でInstikiを紹介した際にもテンプレートについて触れましたが、今回はそれらにどのような種類があるのかを解説したいと思います。

+
+
+

eRuby

+
+

Ruby界隈で最も古いテンプレートエンジンはeRubyです。これはPerlのテンプレートエンジンeperlに触発されて開発されたものです。eRubyの開発者は前田修吾さんです。eRubyは、

+
    +
  • http://modruby.net/

  • +
+

から入手可能です。原稿執筆時点での最新バージョンは1.0.5となっています。

+

eRubyの最大の特徴はこれが独立したインタプリタであるということです。他のテンプレートエンジンがRubyで記述されたライブラリであるのと比べると、この点が大きな違いになっています。eRubyはCで記述されたインタプリタなので、より高速に動作し、フィルタとして実行するのに向いています。一方、ライブラリではないので他のRubyプログラムから呼び出しにくい(プロセス起動しなければならない)点は欠点になるかもしれません

+

eRubyインタプリタが処理するファイルフォーマットを一種の言語であると考えてeRuby(embedded Ruby)と呼ぶことがあります。eRubyのブロックは「<%」で始まり、「%>」で終わります。ブロックの中身はRubyのプログラム(の断片)でなければなりません。eRubyはプログラムが出力を行った場合、そのブロックを出力結果に置き換えます。

+ +
+
$ cat foo.erb
+こんにちは、<% print "みなさん!" %>
+$ eruby foo.erb
+こんにちは、みなさん!
+
+

<%」の次に「=」がくる場合、そのブロックの内容をRubyプログラムとして評価した値に置き換えます。

+
+
$ cat bar.erb
+こんにちは、<%= "みなさん!" %>
+$ eruby bar.erb
+こんにちは、みなさん!
+
+

<%」の次に「#」がくる場合、ブロックはコメントとして無視されます。

+
+
$ cat baz.erb
+こんにちは、<%# これは
+コメント %>みなさん!
+$ eruby baz.erb
+こんにちは、みなさん!
+
+

<%」の次に「%」がくる場合、これは「<%」のエスケープと解釈されます。静的部分に「<%」という文字列を含むために用います。

+
+
$ cat quux.erb
+erubyは<%%と%>の間を実行します
+$ eruby quux.erb
+erubyは<%と%>の間を実行します
+
+

%」で始まる行は、Rubyプログラムとして評価します。eRubyはその行を出力結果で置き換えます。

+
+
$ cat calc.erb
+% x = 1 + 1
+1 + 1 = <%= x %>
+$ eruby calc.erb
+1 + 1 = 2
+
+
+
+ +

ERB

+
+

ERBは純Rubyで記述されたeRubyを解釈するライブラリです。ですからeRubyとは逆の特徴を持つことになります。プログラムからはこちらのほうが使いやすいのではないかと思います。ERBの作者はdRubyの作者でもある関将俊さんです。

+

実装上の違いの他にERBはeRubyとはいくつかの非互換性があります。

+
    +
  • <% %>」の間での出力はERBの処理に反映されない。ERBは「<% %>」の間のRubyプログラムを実行するだけで、出力の切り替えは行わない

  • +
  • %」で始まる行の解釈は行わない

  • +
+

eRubyプログラムの実例をリスト38.3に示します。この例題を見ればわかるように「<% %>」を使ってループ処理を行うことも簡単にできます。

+
+

リスト38.3●eRubyプログラム例

+
<%
+users = File.readlines("/etc/passwd").collect{|l|
+  data = l.split(/:/)
+  [data[0], data[4]]
+}
+%>
+このマシンのユーザー名と実名:
+
+<% for a,n in users %>
+アカウント: <%= a %>、名前: <%= n %>
+<% end %>
+
+
+

ERBは1.8以降には標準添付されています。1.6系でERBを使う場合には、

+
    +
  • http://www2a.biglobe.ne.jp/~seki/ruby/erb-2.0.4.tar.gz

  • +
+

からダウンロードしてください。

+
+
+

amrita

+
+

eRubyやERBなどeRuby系のテンプレートエンジンは、元になるファイルにRubyプログラムの断片を埋め込むものです。ですから、量はともかくデザインの中にロジックの一部が紛れ込んでいることになります。また、eRubyファイルは厳密にはHTMLファイルではありませんから、HTMLエディタなどでは処理できません。

+

このような問題に対処できるのがamritaです。amritaは純粋なHTML(またはXHTML)ファイルとRubyのデータを結び付けてテンプレート展開を行います。amritaの作者は中島拓さんです。amritaREADMEから特色を引用します。

+ +
    +
  • amritaのテンプレートは <? ... ?><% ... %> のような特殊なタグを含まない純粋なHTML/XHTML文書である

  • +
  • テンプレートは、ほとんどのHTMLエディタで作成が可能

  • +
  • 出力の動的な部分を修正しても、Rubyコードには修正が不要

  • +
  • モデルデータは、Hash Array文字列のような標準のRubyのデータである。また、自作のクラスのオブジェクトをそのままモデルデータにすることも可能

  • +
  • 出力はロジックでなくデータでコントロールされる。そのため、テストやデバッグが楽(eXtream Programmingにも向いている)

  • +
  • 簡単にHTMLテンプレートをRubyのコードにコンパイルすることもできる

  • +
+

amritaはテンプレートとモデルデータをid属性によってマッチさせて、HTML文書を出力します(図38.1)。テンプレートファイルのタグの中身がモデルデータの値に完全に置き換わっていることに注目してください。

+
+ +
+ fig3801 +
+

図38.1●amritaのテンプレート展開

+
+

実際の展開は以下のように行います。

+

まず、amrita/templateライブラリをロードします。

+
+
require "amrita/template"
+
+

それからテンプレートエンジンオブジェクトを生成します。生成にはテンプレートファイル名を指定します。

+
+
tmpl = Amrita::TemplateFile.new("template.html")
+
+

テンプレートと対応するモデルデータを用意します。モデルデータはテンプレートのid属性(この例ではtitlebody)に対応した値を持つ必要があります。

+ +
+
data = {
+   :title => "hello world",
+   :body => "Amrita is a html template library"
+}
+
+

そしてテンプレートエンジンによって展開を行います。

+
+
tmpl.expand(STDOUT, data)
+
+

expandメソッドの最初の引数は出力先のストリームです。「<<」メソッドを持つ任意のオブジェクトを出力先に指定できます。第2引数はモデルデータです

+
+
+

amritaの繰り返し

+
+

先ほどの例はただ単純な置換でしたが、amritaは繰り返しを行うこともできます。idに対応する値が配列(厳密にはEnumerable)であった場合には、対応するタグを配列の要素の数だけ繰り返します。

+

たとえば先ほどの例でモデルデータの一部を以下のように配列にすると、

+
+
data = {
+   :title => "hello world",
+   :body => ["foo", "bar", "baz"]
+}
+
+

出力はこうなります。

+
+
<html>
+    <body>
+        <h1>hello world</h1>
+        <p>foo</p><p>bar</p><p>baz</p>
+    </body>
+</html>
+
+

テンプレート展開は再帰的に行われます。たとえば、次のようなテンプレートとモデルデータに対する出力を考えてみてください

+
+
テンプレート:
+<table border="1">
+  <tr><th>lang</th><th>author</th></tr>
+  <tr id="table1">
+    <td id="lang"><td id="author">
+  </tr>
+</table>
+
+モデルデータ:
+data = {
+  :table1=>[
+    { :lang=>"Ruby", :author=>"Matz" },
+    { :lang=>"Perl", :author=>"Larry" },
+    { :lang=>"Python", :author=>"Guido" },
+  ]
+}
+
+
+

id=table1に対応する値は3要素の配列ですから、<tr> の部分は3回繰り返されることになります。<tr> の中身には“lang”というidを持つタグと“author”というidを持つタグがありますから、配列の各要素がそれぞれモデルデータとして適用され、テンプレート展開されます。その結果、全体の展開結果は以下のようになります。

+
+
<table border="1">
+  <tr><th>lang</th><th>author</th></tr>
+  <tr>
+    <td>Ruby</td><td>Matz</td></tr><tr>
+    <td>Perl</td><td>Larry</td></tr><tr>
+    <td>Python</td><td>Guido</td></tr>
+</table>
+
+

キーに対応する値は多くの場合、文字列が使われますが、nilまたはfalseだった場合にはタグそのものが削除されます。また、trueだった場合にはテンプレートのタグの中身がそのまま使われます。

+

あと、タグの属性を指定する方法を紹介しておきます。HTMLファイルを生成する場合にはタグの中身だけでなく、属性を指定したいケースも多かろうと思います。そういう場合にはAmrita::aメソッドを使います。

+
+
Amrita::a(:href="http://www.rubyist.net/"){"Matz"}
+
+

Amrita::aメソッドの戻り値はAmrita::AttrArrayクラスのオブジェクトで、これが値として与えられた場合には、引数を属性、ブロックの値を内容としてタグが展開されます。

+
+
テンプレート:
+<html>
+  <body>
+    <a id="web">my page</a>
+  </body>
+</html>
+
+モデルデータ:
+data = {
+  :web=>Amrita::a(:href=>"http://www.rubyist.net/"){
+    "Matz"
+  },
+}
+
+出力
+<html>
+  <body>
+    <a href="http://www.rubyist.net/">Matz</a>
+  </body>
+</html>
+
+
+

amritaにはここでは紹介した他にもいろいろな機能があります。たとえば手続きオブジェクトなどを値に使う方法や、テンプレート展開をコンパイルして高速化する方法などは非常に興味深いです。詳しくはamritaのドキュメントを参照してください。

+
+
+

amritaの入手とインストール

+
+

原稿執筆時点でのamritaの最新版は、安定版が1.0.2、開発版が1.8.2です。それぞれ、

+
    +
  • http://www.brain-tokyo.jp/research/amrita/amrita-1.0.2.tar.gz

  • +
  • http://www.brain-tokyo.jp/research/amrita/amrita-1.8.2.tar.gz

  • +
+

から入手できます。

+

インストールは簡単で、ダウンロードしたtarファイルを展開し、作成されたディレクトリ(たとえばamrita-1.0.2など)に移動してから、root権限でruby install.rbを実行するだけです。

+
+
+

amritaのライバルたち

+
+

RAAにはamritaの影響を受けたと思われるテンプレートエンジンライブラリが複数登録されています。

+
+

xtemplate

+

xtemplateは立石孝彰さんによるXML用テンプレートエンジンです。属性の扱い(「@」を前置したキーは属性)の扱いやXPathが使える点などはamritaよりも優れているように見えます。xtemplateはモデルデータとしてXMLやYAMLを直接使うこともできます。xtemplateについては、

+
    +
  •   http://xtemplate.sourceforge.net/

  • +
+

を参照してください。原稿執筆時点でのxtemplateの最新版は0.7.0です。

+

misen

+

misenは白井薫さんが開発されたテンプレートエンジンです。misenの特徴はHTMLだけではなく、任意のフォーマットの展開ができることです。特にスタイルを定義することで、タグの形式も独自に定義できる点が面白いです。サンプルとしてLaTeXスタイルが用意されていて、

+
+
\documentclass{article}
+\title{\misen:title}
+\begin{document}
+  \maketitle
+  \begin{misen:body}
+    body will be inserted here
+  \end{misen:body}
+\end{document}
+
+
+

のような指定ができます。XML(SGML)ふうのタグの代わりにTeXの \begin{}, \end{} で範囲が指定されています。

+

misenについては、

+
    +
  •   http://devel.korinkan.co.jp/misen/

  • +
+

を参照してください。原稿執筆時点でのmisenの最新バージョンは0.11.1です。

+

kwartz

+

厳密にいうとkwartzは「Rubyで実装されたテンプレートエンジン」で「Rubyのためのテンプレートエンジン」ではありません。kwartzはテンプレートに埋め込む言語を独自言語とすることで、Rubyだけでなく、PHPやJavaにも対応しています。kwartzはRuby, PHP, Java用のテンプレート展開コードを生成します。利用者は生成されたコードを(コンパイルして)利用します。kwartzについての情報は、

+
    +
  •   http://www.kuwata-lab.com/kwartz/

  • +
+

を参照してください。kwartzはIPAの平成15年度未踏ソフトウェア創造事業の対象でした。kwartzの開発者は桑田誠さんです。

+
+
+
+

まとめ

+
+

今月はテンプレートエンジンについて調べてみました。現在はWebアプリケーションの開発が盛んです。RubyでWebアプリケーションを、と考えている人も多いのではないでしょうか。Webページのデザインとロジックを分離できるテンプレート技術は優れたWebアプリケーションを開発するのに非常に役に立つ技術です。amritaを始めとしたテンプレートエンジンはきっとそのために役立つことでしょう。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-017.xhtml b/docs/vol2/xhtml/p-017.xhtml new file mode 100644 index 0000000..2637538 --- /dev/null +++ b/docs/vol2/xhtml/p-017.xhtml @@ -0,0 +1,66 @@ + + + + + +第38章 テンプレート + + + + +

Matz Essays Volume 2

+ + +
+ +

◆ Ruby開発日記 ◆プロトタイプな世界

+
+

オブジェクト指向の三要素といえば、昔から、

+
    +
  • 継承

  • +
  • 情報隠蔽いんぺい

  • +
  • 動的結合

  • +
+

の3つと相場が決まっていたものですが、本当にこれらは必須なものなのでしょうか。

+

たとえば、Common Lispのオブジェクト指向機能であるCLOSには情報隠蔽を支援する機能はなく、オブジェクトのインスタンス変数(スロットと呼ぶ)は自由にアクセスできます。では、CLOSはオブジェクト指向ではないかというと、そんなことはないでしょう。

+

C++ではvirtualと明示的に指定しなければ動的結合が行われません。では、動的結合がなければオブジェクト指向とは呼べないでしょうか。個人的にはそんな環境でオブジェクト指向プログラミングをしたくはありませんが、しかし、そんな私でも動的結合がなければオブジェクト指向ではないというのは少々言い過ぎのように思います。

+

では、継承はどうでしょうか。

+

広く知られているオブジェクト指向言語には、たいてい継承機能が付いています。オブジェクトの種類をまとめるクラスが存在して、あるクラスの機能を受け継いで、一部変更・追加などを行い別のクラスを作り出すことが継承です。

+

クラスというのは、たとえば四本足で「わんわん」とえる生き物を「犬」という総称的な名前を付けて分類する行為の延長線上ですから、人間にとって非常に自然な発想であるのは確かです。また、多くの教科書で継承の例題として生物の分類(脊椎動物→哺乳類→犬など)が頻繁に用いられるように、継承も日常的な発想の延長です。

+

しかし、自然ではあっても必須かというとそうでもないかもしれません。別のやり方があるからです。クラスを使うクラス主義に対して、プロトタイプ主義と呼ばれるこのやり方では、クラスという単位を使わずにオブジェクトだけを単位とします。

+

クラス主義が「このクラスに属するオブジェクトを1つ作る」と指定するところを、プロトタイプ主義では「あのオブジェクトと同じのをもう1つ」と指定します。クラスの代わりに具体的なオブジェクトが「雛形(プロトタイプ)」になるわけです。

+

クラス主義が継承を使って「あのクラスと同じだけど、ここが違うクラスを作る」となるところで、プロトタイプ主義では「あのオブジェクトと同じだけど、ここが違うオブジェクト」を新しく作り、それを新しい雛形にします。

+

プロトタイプ主義ではクラスという抽象的な存在は登場せず、代表的なオブジェクトという具体的な存在によってプログラミングを行います。

+

このようなプロトタイプ主義の言語のはしりはSelfという言語であるといわれています。Selfはプロトタイプ型Smalltalkというべき言語で、ずっとSunで研究が行われてきました。Self自体はまだまだ知る人ぞ知るという感じですが、Selfの研究成果はJavaのHotSpotに応用されたり、morphicやtraitsなどの一部の機能はSmalltalkに「逆輸入」されたりしています。

+
+

しかし、もしかするとプロトタイプ主義の起源はオブジェクト指向の他の多くの機能と同じようにLisp界にあるのかもしれません。Common Lispのオブジェクト指向CLOSの仕様について議論されているとき、参考にされたものの1つにHP(Hewlett Packard)で開発されていたCommon Objectというものがあったそうです。このCommon Objectではクラスとオブジェクトの区別がなく、クラスの役割はその種別の代表となるオブジェクトが果たしていたそうです。正確な仕様や開発時についての情報が入手できませんでしたが、あるいはSelfよりも早い時期にプロトタイプ主義を導入していたのかもしれません。

+

他にもプロトタイプ主義の言語はいくつかあるのですが、一番有名なものはJavaScriptかもしれません。歴史的な事情からJavaの名前を使っているもののたいして似ていないJavaScriptはいっぷう変わったオブジェクト指向機能を備えています。JavaScriptを通じてプロトタイプ型オブジェクト指向プログラミングを学んだ人も多いのではないでしょうか。JavaScriptはいまだに進歩を続けていて、バージョン2.0では今度はクラスも導入されたり、セレクタネームスペースというまだ珍しい機能を提供したりしています。JavaScriptは今後注目すべきオブジェクト指向言語かもしれません。

+

時代がプロトタイプ主義に追いついたのか、最近になってプロトタイプ型言語が次々登場しています。皆さんもプロトタイプ型言語で未来の(?)プログラミングを体験してみませんか?

+
+

Io(http://www.iolanguage.com/

+

Ioはプロトタイプ型オブジェクト指向スクリプト言語です。非常に小さな文法と性能がよい(自称)スクリプト言語です。

+

Prothon(http://www.prothon.org/

+

ProthonはPythonをベースにプロトタイプ主義で再設計したものです。今年の7月までは仕様を模索している段階だそうです。

+

SeRuby(http://kumiki.c.u-tokyo.ac.jp/~ichiyama/projects/reports/seruby/

+

SeRubyは言語ではなく、Rubyの上でプロトタイプ型オブジェクト指向プログラミングを実現するものです。わずか200行ほどのRubyプログラムで実現されています。

+

Ruby

+

さて、われらがRubyはもちろんクラス型オブジェクト指向言語なのですが、特異メソッドを使ってプロトタイププログラミング(もどき)を行うことができます。オブジェクトの生成は雛形となるオブジェクトをcloneすることで行い、オブジェクトの定義には特異クラス定義、

+
+
class <<obj
+  ...
+end
+
+

を使います。ただ、Rubyではいくつかのオブジェクト(true, false, nil, fixnum, symbol)に特異メソッドが定義できませんから実用には難しいかも。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-018.xhtml b/docs/vol2/xhtml/p-018.xhtml new file mode 100644 index 0000000..cc445d3 --- /dev/null +++ b/docs/vol2/xhtml/p-018.xhtml @@ -0,0 +1,509 @@ + + + + + +第39章 DBM + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay39 +
+

+探訪Ruby
+DBM +

+
+

[Linux magazine, 2004年8月号]

+
+

DBMについて解説しています。DBMは完全に廃れた技術というわけではありませんが、最近は直接見かけることは少なくなりました。一部の検索エンジンのバックエンドや、アプリケーション組み込みのデータベース(今風の表現だとKey Value Store(KVS)ですね)として今でも使われていますが、そういう局面でも今はSQLiteのほうが人気があるかもしれませんね。SQLiteも当時から存在していましたが、現代のようにあらゆる局面で使われるほどではなかったですね。

+

「Ruby開発日記」は「メールリーダーの新アプローチ」です。当時使っていたcmailに見切りをつけて新しいものを自作しようという宣言なのですが、これがどうなったかというと、1年以上の構想期間を経て、3週間で基本部分を実装し、実は今でもそれを使っているのです。当時はmorgという名前にしようと思いましたが、名前の重複があったのでmorq(mail organizer using qdbm)という名前になりました。ちょうど本文で解説したQDBMを利用しているので、この記事に触発された完成されたと言っても過言ではありません。morqはオープンソースにしておらず、ユーザーは私一人です。

+
+
+

私はデータベースに関するプログラミングの経験があまりないので、リレーショナルデータベースなど使いこなせないのですが、今月はそんな私でも使える簡易データベースのDBMとその仲間を紹介します。

+
+
+

DBM

+
+

データベースといえば主流はリレーショナルデータベースです。オープンソースのものではPostgreSQLやMySQLが有名ですね。これらはテーブルに格納されたデータをSQL(Structured Query Language – 構造化問い合わせ言語)を使って操作するものです。RubyにもPostgreSQL, MySQL, それにInterbaseやOracleなどなど各種のリレーショナルデータベースを操作するライブラリが提供されています。

+
+

しかし、Rubyが得意とする日常的プログラミングでは、わざわざPostgreSQLのようなサーバー/クライアント方式のリレーショナルデータベースを用意するのは大げさと思われることもしばしばあります。そういう場合はテキストデータを使ってデータの処理を行うことが多いのですが、今度はデータ量が増えたときに毎回ファイルを開いて先頭から読み込んでいくのが耐えられなくなることもあります。

+

そういうケースでは、プレーンテキストよりも高速で、サーバー/クライアント方式のリレーショナルデータベースよりも手軽なデータの保存手段が求められているのです。

+

そのような手段はいろいろ考えられますが、1つの解としては、SQLiteのようなサーバー/クライアント方式でないリレーショナルデータベースを導入することがあります。SQLiteはライブラリ形式のリレーショナルデータベースで、直接ファイルに読み書きを行います。名前にもあるようにSQLも使えますし、性能もそれなりに出るようです。これはこれで面白い方法なので、また別の機会に解説したいと思っています。

+

もう1つの解が今月紹介するDBMです。DBMは昔からUNIXで提供されているデータベースで、その名前はDataBase Managerの略のようです。そのまんまですね。DBMはリレーショナルなんて高度な演算は行いませんし、SQLもありません。データはハッシュの形で管理します。キーに対応する値を取り出す、それだけです。

+

RubyのDBMインターフェイスでは、DBMクラスのオブジェクトはHashとほぼ同じメソッドを持ち、ほとんど同じように使えます。違いは? というと、DBMはファイルに関連付けられていて、格納したデータはプログラムが終了してもファイルに保存される(だからこそ「データベース」である)ことと、DBMではキーも対応する値も文字列でなければならないという点です。

+

リレーショナルデータベースのようにSQLによる問い合わせもできず、ハッシュのような任意のオブジェクトの格納もできない制限の強いDBMですが、使いにならないかというとそうでもありません。DBMの構造はリレーショナルデータベースのテーブルよりも単純ですが、簡単なインデクシング(1対1の関連)の扱いはむしろ簡単です。複数の値を対応させたい場合にも、値を文字列にパックすることで実現できます。Rubyにはpackメソッドやunpackメソッドがありますから、このような目的には便利です。

+
+
+

RubyのDBMインターフェイス

+
+

RubyのDBMインターフェイスが提供するクラスはHashクラスとほぼ同じです。簡単な使用例をリスト39.1に示します。

+
+

リスト39.1●DBM使用例

+
require 'dbm'
+
+d = DBM.open("/tmp/bark")
+d["dog"] = "bow"
+d["cat"] = "mew"
+puts d["cow"]
+d.close
+
+
+

前後にopencloseがある以外は、ハッシュと同じように使えることがわかるでしょう。

+

Rubyには3つのDBMインターフェイスが標準添付されています。

+ +
+

dbm

+

そのプラットフォームで使えるDBMライブラリ(ndbmまたはgdbm)を使った標準的なdbmライブラリです。ほとんどのプラットフォームで使えますが、利用しているライブラリが異なる可能性があるので、プラットフォーム間のデータ互換性はありません。

+

gdbm

+

GNU dbmライブラリのインターフェイスです。gdbmがインストールされているプラットフォームで有効です。gdbmは多くのndbmライブラリよりも高性能ですし、ndbmライブラリに対していくつかの機能が追加されています。

+

sdbm

+

dbmgdbmはプラットフォームに何らかのdbmライブラリがインストールされていなければ利用できませんが、sdbmはパブリックドメインのDBMライブラリが添付されていますので、事実上すべてのプラットフォームで利用できます。しかし、欠点もあって、sdbmはあまり性能に恵まれず、そのうえデータベース中の各要素のサイズ(キーと値のサイズ合計)がわずか1Kバイトに制限されます。

+
+

日常的な使用にはgdbmを使うのがよいと思われます。DBMクラスの仕様を表39.1に示します。

+ +
+

表39.1●DBMクラス

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
クラスメソッド
DBM::open(path[,mode])DBをオープン(modeは権限値)
DBM類に共通のインスタンスメソッド
dbm[key]keyに対応する値
dbm[key]=valuekeyに対応する値をセット
dbm.clearDBを空にする
dbm.closeDBをクローズ
dbm.delete(key)keyに対応する要素を削除
dbm.each_key{|key,val|}keyに対する繰り返し
dbm.each_pair{|key,val|}DB要素に対する繰り返し
dbm.each_value{|key,val|}valueに対する繰り返し
dbm.each{|key,val|}DB要素に対する繰り返し
dbm.empty?DBが空のとき、真
dbm.fetch(key)keyに対応する値(不存在で例外)
dbm.has_key?(key)DBがkeyを含むか
dbm.has_value?(val)DBがvalを含むか
dbm.include?(key)DBがkeyを含むか
dbm.index(val)valに対応するキー
dbm.invert値からkeyへのハッシュ
dbm.key?(key)DBがkeyを含むか
dbm.keysDB中のキーの配列
dbm.lengthDB中の要素数
dbm.reject!{|key,val|}条件が真の要素を削除
dbm.replace(hash)DBをhashの内容で置換
dbm.shiftDBから任意のキーと値を取る
dbm.sizeDB中の要素数
dbm.store(key,val)keyに対応する値をセット
dbm.to_aDBを配列化
dbm.to_hashDBをハッシュ化
dbm.update(hash)DBをhashの内容で更新
dbm.value?(val)DBがvalを含むか
dbm.valuesDB中の値の配列
dbm.values_at(key, ...)key, ...に対応する値の配列
gdbm固有のインスタンスメソッド
dbm.cachesize=nキャッシュサイズの設定
dbm.fastmode=boolfastmodeの設定
dbm.reorganizeDBをハッシュ化
dbm.sync同期
dbm.syncmode=bool同期モードの設定
+
+

DBM」の部分を「GDBM」「SDBM」に変えることで、それぞれgdbmsdbmに対応できます。

+
+
+

DBMの制限の回避

+
+

すでに述べたように、DBMの最大の制限は「キーも値も文字列しか取れない」ことです。キーのほうは文字列限定でもさほど困ることはありませんが、1つのキーにレコードを対応させたいケースはたびたびあります。

+

このような場合の回避方法はいくつかあります。1つはデータを文字列にpackする方法です。Arrayメソッドにはpackというメソッドがあって、配列の要素をテンプレートに従い文字列化することができます。同じテンプレートをStringクラスのunpackメソッドに渡すことで逆に配列に戻すことができます。たとえば、8ビット整数4つをpackするにはこのようにします。

+
+
packed = [192,168,0,1].pack("CCCC")
+
+

逆にunpackはこうです。

+
+
p packed.unpack("CCCC")  # => [192,168,0,1]
+
+

C”はchar(文字)の意味で、符号付き8ビット整数を意味します。テンプレート文字の後ろに数字を置くと繰り返し、または長さ指定になります。ですから、“C4”は“CCCC”と同じ意味です。pack, unpackのテンプレート文字を表39.2に示します。

+
+

表39.2●packテンプレート

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
文字説明文字説明
@絶対位置への移動llong
AASCII文字列(スペースを詰める, 長さ指定)MQ-encodingされた文字列(RFC2045)
aASCII文字列(nulを詰める、長さ指定)mbase64された文字列
Bビットストリング(上位ビットから下位ビット)Nビッグエンディアンのlong
bビットストリング(下位ビットから上位ビット)nビッグエンディアンのshort
Cunsigned charP構造体(固定長文字列)へのポインタ
ccharpナル終端の文字列へのポインタ
D, ddouble(機種依存)Q, q64-bit number
Eリトルエンディアンのdouble(機種依存)Sunsigned short
eリトルエンディアンのfloat(機種依存)sshort
F, ffloat(機種依存)UUTF-8文字列
Gビッグエンディアンのdouble(機種依存)uuuencodedされた文字列
gビッグエンディアンのfloat(機種依存)Vリトルエンディアンのlong
H16進文字列(下位ニブルが先)vリトルエンディアンのshort
h16進文字列(上位ニブルが先)wBER圧縮された整数
Iunsigned intX1バイト後退
iintxnullバイト
Lunsigned longZASCII文字列(nullを詰める)
+
+ +

パックテンプレートを見るとわかるようにpackで扱えるデータタイプは整数や文字列のような基本的なデータタイプだけです。より複雑なデータを文字列化するためにはMarshalを用います。Marshalはオブジェクトを文字列化します。

+
+
pack = Marshal.dump(obj)
+
+

たったこれだけで、オブジェクトを文字列化することができます。文字列からオブジェクトを取り出すのも同じくらい簡単です。

+
+
unpack = Marshal.load(pack)
+
+

とても簡単なMarshalですが、注意すべき点がいくつかあります。

+
+

最後にYAMLを紹介しておきます。これはMarshalが用いているバイナリフォーマットの代わりにYAMLというデータ記述言語を用いるものです。使い方は以下のとおりです。

+
+
require 'yaml'
+pack = obj.to_yaml   # => YAML化した文字列
+
+unpack = YAML::load(pack)
+
+

たとえば配列[1,2,3]をYAML化すると以下のようになります。

+
+
--- 
+- 1
+- 2
+- 3
+
+

先頭の“”がYAMLの始まりを、“- ”が配列の各要素を意味します。

+

YAMLMarshalとほぼ同じ制限がありますが、Marshalと違ってプレーンテキストを用いたフォーマットですから、(少々効率は落ちるものの)バージョン依存性が低く、また出力されたデータを人間が見て理解することができます。

+
+
+

Berkley DB

+
+

標準添付のDBMよりも高機能のDBMライブラリとして、Sleepycat(http://www.sleepycat.com/)からBerkeley DBというライブラリが提供されています。BDBはDBMの後継に当たるライブラリで、DBMに対して以下の機能が追加されています。

+
+

また、BDBのほうが各種DBMよりも性能が上回ることが多いようです。BDBはスパムフィルタのBogofilterやバージョン管理システムのSubversionなど多くのアプリケーションで利用されています。

+

RubyのDBDインターフェイス(bdb)は、フランス人RubyハッカーであるGuy Decouxによって開発されています。原稿執筆時点での最新バージョンは0.5.0です。bdbについての情報は、

+
    +
  • http://moulon.inra.fr/ruby/bdb.html

  • +
+

から入手できます。Guy Decouxはruby-talkメーリングリストに古くから参加しているのですが、

+
    +
  • すごく革新的なプログラムを次々開発する

  • +
  • Rubyの内部にすごく詳しい。下手すると私より詳しい

  • +
  • あんまり英語が得意でない

  • +
  • 誰も彼に直接会ったことがない

  • +
+

と謎の多い人物です。彼によって開発されたプログラムには、RAAに登録されているものだけでも、

+
    +
  • bdb

  • +
  • bdb1(Berkeley DB version 1.8用)

  • +
  • bz2(ファイル圧縮ライブラリlibbzインターフェイス)

  • +
  • mmapmmapファイルをアクセスする)

  • +
  • pl-ruby(PostqreSQLでサーバーサイドRuby)

  • +
+

があります。それ以外にも、

+
    +
  • ii(Rubyの構文木を出力)

  • +
  • 未公開のマルチメソッド実装(同じ名前で引数の型が異なる複数メソッドを実現する改造版Ruby)

  • +
+

があり、最後のものは何度お願いしてもコードを見せてもらえませんでした。やっぱり謎だ。

+
+
+

QDBM

+
+

BDBとは別の方向で強化されたデータベースライブラリがQDBMです。QDBMの作者は平林幹雄さんで、原稿執筆時点での最新バージョンは1.8.12です。

+

QDBMの特徴は高性能と高機能です。QDBMのホームページ(http://qdbm.sourceforge.net/)によると、QDBMはgdbmなどよりも数倍高速なのだそうです。またQDBMは移植性も高く、LinuxやWindowsを始めとして多くのプラットフォームで動作します。

+
+

QDBMは機能としては、

+
    +
  • 基本API(Depot)

  • +
  • 大容量向けAPI(Curia)

  • +
  • ndbm互換API(Relic)

  • +
  • gdbm互換API(Hovel)

  • +
  • ユーティリティAPI(Cabin)

  • +
  • B+木API(Villa, Vista)

  • +
  • 転置インデックスAPI(Odeum)

  • +
+

が提供されます。これらのライブラリの名前は5文字の英単語から適当に選んだので意味はないそうです。

+

基本APIのDepotは最も高速に動作します。Depotはハッシュのインデックス部分をmmapシステムコールを用いてRAM上に置くため、高速に動作します。Curiaは大量データに対応するため、データベースをディレクトリ中の複数のファイルで構成します。32ビットOSでは2Gバイト以上のサイズのファイルを取り扱うことができませんが、Curiaを用いることで64ビットOSに移行することなく2Gバイトを超えるデータを扱うことができます。

+

互換APIのRelicやHovelを利用すると既存のDBMを用いたアプリケーションを簡単にQDBM対応に変更することはできます(データベースファイルの互換性はありません)。

+

Cabinはデータベースとは直接関係ないユーティリティを集めたAPIです。QDBMの上位API(VillaやOdeumなど)はこのユーティリティを活用して実装されています。

+

VillaはB+木を提供するAPIです。B+木は順序のあるデータベースで、範囲を指定した検索や部分一致検索を行うことができます。また、Villaはトランザクション機能を持ちます。

+

Odeumが提供する転置インデックス(Inverted Index)は、全文検索で用いられる技術です。Odeumは全文検索エンジンの基本的機能を提供しますから、アプリケーション側ではテキスト解析やユーザーインターフェイスなどを実装するだけで、比較的簡単に全文検索エンジンを作ることができます。Odeumは平林さん自身が作られたパーソナル検索エンジンEstraierで用いられています。

+
+
+

QDBMのRubyインターフェイス

+
+

QDBMには作者自身の手によるC, C++, Java, Perl, RubyのAPIが用意されています。残念なことにC以外の言語では提供されるのは、Depot, Curia, VillaだけでOdeumなどの興味深いAPIは直接使うことができません。

+

しかし、単に高速でスケーラブルなDBMとして使っても十分に有効だと思います。QDBMインターフェイスを使ったサンプルをリスト39.2に示します。

+ +
+

リスト39.2●QDBMサンプル

+
require 'depot'  # depotを使う
+
+name = ARGV[0]
+begin
+  # optionは「書き込み可能、データベースがなければ作成」
+  Depot.open("addr", Depot::OWRITER|Depot::OCREAT){|depot|
+    puts name
+    puts depot[name]
+  }
+# Depotの例外はDepot::EANYのサブクラス
+rescue Depot::EANY => e
+  puts e.message
+end
+
+
+

QDBMのRubyインターフェイスはスレッドを考慮していますから、複数のスレッドから同時にアクセスしても平気です。

+
+
+

まとめ

+
+

さて、今月はSQLなどを使わない簡易データベースDBMとその仲間を紹介しました。DBMは複雑な条件検索はできませんが、アクセスパターンが1種類しかないようなデータベースに最適です。そしてそのようなデータベースはそれなりに多いものです。

+

実際、今月DBMを紹介しようと思ったのは、私が今作ろうとしているアプリケーションでQDBMを使うことにしたのがきっかけです。そのアプリケーションについては「Ruby開発日記」のほうで。

+

簡単、便利なデータベース、DBMを使ってみませんか。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-019.xhtml b/docs/vol2/xhtml/p-019.xhtml new file mode 100644 index 0000000..7958608 --- /dev/null +++ b/docs/vol2/xhtml/p-019.xhtml @@ -0,0 +1,49 @@ + + + + + +第39章 DBM + + + + +

Matz Essays Volume 2

+ + +
+ +

◆ Ruby開発日記 ◆メールリーダーの新アプローチ

+
+

私が電子メールを日常的に使うようになってから、もう15年以上経ちます。最初は日に数通、それも日本語のものだけでした。あの頃は海外への接続は制限されており、高額な国際回線料の負担分配を明確化するために、海外にメールを出すためにはいろいろ面倒なことが多かったものです。当時は「いまだに鎖国しているようだ」と感じたものです。

+

それから10数年の間に、インターネットは一般化し、パソコン通信と呼ばれていたものはすべてインターネットに流れ込み、コンピュータだけでなく携帯電話からも電子メールのやりとりができるようになりました。それに伴い電子メールの数も飛躍的に増大しました。海外からもどんどんメールが入ってきます。

+

さらに悪いことに電子メールの世界にはスパムが蔓延まんえんしています。どこからアドレスを集めるのか知りませんが、毎日毎日数百通のスパムが送りつけられています。バイアグラだの、出会い系だの、もうけ話だの、もう勘弁してほしいです。

+

Rubyが世界中で使われ、海外のいろいろなところからメールをもらうことが増えたこと、また、「ruby-talkメーリングリスト」の流量が半端でなく増えたこともあって、私の1日に受け取る電子メールの量は10年前の100倍くらいになっているような気がします。スパムも含めれば1日1000通を超えることは珍しくありません。

+

以前(2004年6月号)でも紹介したように、私に届くスパムのほとんどはフィルタを使って除去していますが、それでも数百通は残ります。それらのほとんどは目を通すだけですが、返事を書く必要があるものも多く、中には今はいらないけど後から参照したいというようなものもあります。これだけメールが多くなると従来の方法での管理が難しくなってきました。

+

私が現在使っているメールリーダーはcmailです。Emacs上で動作するこのメールリーダーは、私自身がずっと手を入れてきたこともあって、もう10年以上愛用しているものです。しかし、ここ最近のメール量の増大に対応できなくなってきました。

+

最初の問題が動作速度です。cmailはすべてEmacs Lispで記述されたメールリーダーです。インタプリタであるEmacs Lispの実行速度はお世辞にも速いとはいえません。メールを分類しているフォルダに数千通もメールがたまるとただリストを表示するだけでも数分かかってしまうこともあります。これではメールを読むだけでもストレスです。

+

もう1つは、検索機能の弱さです。たまったメールの数が数万通を超えると通常の方法では探し出すことは困難です。「えっと、この件について以前誰かからメールをもらったよな」と思っても、ため込んでいる膨大な過去メールの中から必要な情報を引き出すことはなかなか困難です。結局、メール検索はあきらめて、Googleのようなインターネット検索に頼ることになってしまいます。手元に情報があるのに実にもったいない。

+

そんな不満を抱えつつも、私の望みをすべてかなえるメールリーダーにも巡り合えず、慣れたインターフェイスから移行する気にもならないまま日々を過ごしていたのですが、ある日、Gmailというサービスについて耳にしました。

+ +

GmailはGoogleが提供する新しいメールサービスです。その特徴はなんといっても一人当たり最大1Gバイトものディスク容量が割り当てられるということです。そして、1Gバイトもの容量から目当てのメールを見つけ出すために、Google得意の検索技術が活用されるということです。

+

なかなか画期的なサービスのように聞こえます。しかし、Webを使ったメールサービスはインターネットアクセスが使えなくなったときに困ります。出張中などインターネットにつなげない局面でもメールからデータを引き出したいのは自然な欲求です。やはり、大事なデータは手元に起きたいというのが心情です。その後、Gmailには広告を表示するためメールの内容を解析するのはプライバシーの侵害になるのではないかという懸念が広まるのですが、それはまた別の話です。

+

そういうニュースを読んでいたときに、ふと思い付きました。私の旧式のノートパソコンでも20Gバイトのディスクを載せ、まだ2Gバイトくらいは容量が残っています。ならばわざわざGmailに頼らなくても自前で検索ベースのメールリーダーを作ってしまえばよいのではないかと。

+

調べてみると過去にも検索をベースにしたメールリーダーはいろいろとあったようです。有名なものではOperaに付属するM2というメールリーダーが検索をベースにしていてるそうです。そのようなメールリーダーでは「フォルダ」の代わりに検索条件を指定してメールを絞り込んだりするようです。

+

しかし、私の環境で使えるオープンソースのものは見当たりません。特に私は文章をEmacs以外で書く気にはなりませんので、Emacsフロントエンドは必須です。なければ作るしかありません。なんか久々に大きなものを作る気になってきました。

+

そんなわけで、先日来新しいメールリーダーを設計しているのです。メールを読むというよりは管理するツールなので、「メールオーガナイザー」と読んでいます。愛称は「morg」にしようと思っていたら、そういう名前のオープンソースソフトウェアはすでにあるそうです。名前を決めるのも難しい時代になってきましたね。どんな名前にしようかなあ。

+

設計しているとはいっても、こればかりしているわけにもいかないので、作業は遅々として進まないのですが、それでも基本的な設計はできて、データベース部分についてはなんとか形になりそうです。

+

基本的にはQDBMのOdeumを利用して、パーソナル検索エンジンEstraierを参考にしながら実装しようと考えています。データベース部が完成したら、今度はEmacsフロントエンドです。Web日記でメールオーガナイザーの開発について紹介したら、「Webインターフェイスがほしい」などリクエストもいただいています。

+

というわけで、このところメールオーガナイザーのことをいろいろと考えています。予想以上に手間がかかって完成するのはまだまだ先になりそうですが、個人的にもぜひほしいツールなので口先だけにならないようにしたいと思います。

+

とかいって、あんまりこっちに熱中していると、本業やらRuby2の件やらおろそかになってしまうという難しい状況にあるのですが。

+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-020.xhtml b/docs/vol2/xhtml/p-020.xhtml new file mode 100644 index 0000000..003f730 --- /dev/null +++ b/docs/vol2/xhtml/p-020.xhtml @@ -0,0 +1,261 @@ + + + + + +第40章 tDiary + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay40 +
+

+探訪Ruby
+tDiary +

+
+

[Linux magazine, 2004年9月号]

+
+

今回はtDiaryの解説です。が、BlogだのWeb日記やらが低調となった現代ではあまりピンとこないですね。tDiaryの作者のただただしさんは「20年続くソフトウェア」という標語を掲げて開発していたと聞きますが、2024年でtDiaryは25周年だそうですから、目的は達成したと言っても過言ではないですね。わたしの日記はいまだにtDiaryですが、もうかなり長い間更新もしていません。Rubyは開発開始から30年以上経ちましたが、まだまだ健在です。もともと言語はソフトウェアの中でも寿命が長いジャンルではありますが。

+

「Ruby開発日記」は「挑戦! 言語塾」というタイトルで当時開設したばかりのlangsmithメーリングリストの話題です。このメーリングリストは、しばらくの間は続きましたがそのうち立ち消えてしまいました。その後も何度かこのようなコミュニティが立ち上がりましたが、なかなか続きません。協力して同じものを作るコミュニティは継続しやすいですが、それぞれが別々のものを作るコミュニティは続かないものなのかもしれません。

+
+
+

以前、Blogについて紹介したときに、さらりと触れたtDiaryですが、今月はこの「Rubyのキラーアプリ」とうわさされるtDiaryとその周辺について探検してみましょう。

+
+
+

tDiary

+
+

tDiaryは、ただただしさんによって開発されているWeb日記ツールです。tDiaryの「t」は「ただのt」であるとも、「ツッコミのt」であるともいわれていますが、確かなことはわかりません。余談ですが「ただただし」というのは本名だそうです。

+

日記ツールとしてのtDiaryの特徴は、

+
+
    +
  • ツッコミ

  • +
  • トラックバック

  • +
  • テーマ

  • +
  • スタイル

  • +
  • プラグイン

  • +
  • 多言語対応

  • +
+

などです。

+

「ツッコミ」とは要するにコメントです。「コメント」でなく「ツッコミ」と表現されているのは、「ちょっとした気の利いた短文を入れてほしい」という意味が込められているのだそうです。ですから、tDiaryでのツッコミは短く愛を込めたものが推奨されています。別にツッコミの長さの制限はないので、短いものしか許されないというわけではありません。

+

「トラックバック」は2004年3月号の本連載でも説明しましたが、トラックバック対応のサイト同士が、「あなたのことを話題にしましたよ」という連絡を行うための仕掛けです。新しい日記文を登録するときにトラックバック先のアドレス(Trackback Ping URL)を指定すると、そのURLを使ってtDiaryが自動的に先方のサイトに通知してくれます。もちろん、tDiaryもトラックバックを受け付けることができます。

+

「テーマ」はCSS(Cascading Style Sheet)を使ってサイトの外観を変更できる機能です。tDiaryはテンプレートを変更することなく、「テーマ」と呼ばれるCSSを取り換えるだけで外観を変更できます。テーマによって自分の日記の個性を表現することができますし、気分に合わせて日記の外観を変化させることも簡単です。tDiaryとともに配布されているテーマであれば、設定画面から簡単に選択できますし、そうでなくてもインストールは簡単です。tDiaryテーマはすでに200種類以上公開されています。また、tDiaryのテーマを理解するツールも数多くあります。たとえば、日記サイトの「はてな」、Wikiの「Hiki」などです。

+
    +
  • テーマギャラリー
    +http://www.tdiary.org/20021001.html

  • +
+

あまりにたくさんあるので選択するのが大変なくらいです。zphotoを使ったFlashによる選択ツールも便利です。

+
    +
  • http://tdiary2.tdiary.net/theme/zphoto.swf

  • +
+

サムネイル画像にカーソルを合わせるとイメージが拡大され、クリックするとサンプルページにジャンプします。

+

「スタイル」は日記文の入力フォーマットです。tDiaryのデフォルトでは、日記のフォーマットは簡易化されたHTMLです(表40.1)。

+
+

表40.1●tDiary HTMLフォーマット

+ + + + + + + + + + + + + + + + + + + + + + + + + +
入力フォーマット説明
空行セクションの区切り
セクション先頭サブタイトル
段落
<」で始まるセクション整形の対象外
<%= plugin %>プラグイン
+
+

その他にもWikiの記法を使うWikiスタイル、Ruby界ではよく使われているRDを使うRDスタイルなどが用意されています。もちろん私は自分が考案したRDを使うのが好みなので、RDスタイルを愛用しています。スタイル情報はエントリごとに記録されていますから、途中でスタイルを変更することも可能です。

+ +

「プラグイン」はtDiaryにさまざまな機能を追加するものです。上記のトラックバックなどもプラグインで実現されています。他にも以下のようないろいろな機能がプラグインで提供されています。

+
+

絵日記プラグイン

+

画像のアップロードと本文での表示を実現します。

+

Amazonアフィリエイトプラグイン

+

ISBNに対応するAmazon.co.jpの購入ページへのリンクを生成します。表紙の画像(書影)を取り込むこともできます。

+

脚注プラグイン

+

脚注を付けます。脚注はその日のエントリの下にまとめて表示されます。

+

リンク生成プラグイン

+

自分の日記のエントリへのリンクを簡単に生成します。

+

カレンダープラグイン

+

カレンダーを表示します。表示形式・機能によっていくつかのカレンダープラグインが存在します。

+

ToDoプラグイン

+

ToDoリストを管理します。

+

RSS出力プラグイン

+

最新のエントリのRSS(RDF Site Summary)を出力します。

+

コメントメールプラグイン

+

ツッコミが付いたときにメールで通知するプラグイン。qmail, sendmail, SMTPそれぞれに対応版があります。

+

最近のエントリ一覧プラグイン

+

最近のエントリのリストを表示するプラグイン。サイドバーに置くのが効果的です。

+

更新時刻表示プラグイン

+

日記を更新した時刻をグラフで表示してくれるプラグインです。人によっては生活パターンが明らかになってしまうかもしれません。

+
+

その他数多くのプラグインが開発されています。

+
    +
  • プラグインリスト
    +http://tdiary-users.sourceforge.jp/cgi-bin/wiki.cgi?PluginList

  • +
+

安定版ではtDiary 2.0から「多言語対応」が含まれるようになりました。1.4まではボタンやメニューなどの言語はデフォルトでは日本語に決め打ちでした。カスタマイズで英語などに変更することはできましたが、統一的な枠組みはなくそれなりに大変な作業でした。2.0からは設定1つで言語を切り替えられます。tDiaryの標準配布には、日本語(ja)、英語(en)、中国語(zh)のリソースが含まれています。韓国語などの新しい言語への対応も(その言語がわかれば)そんなに大変ではないでしょう。

+
+
+ +

tDiaryのインストール

+
+

原稿執筆時点でのtDiaryの最新版は、6月27日(たださんの誕生日)にリリースされた2年ぶりの安定バージョンである2.0です。入手先は、

+
    +
  • http://www.tdiary.org/20021112.html

  • +
+

です。tDiaryのインストールは、過去に何らかのWebアプリケーション(またはCGI)のインストールの経験があれば、難しいことはまったくありません。

+

Debianなどではパッケージになっていますが、ここでは自分のサイトにゼロからインストールする手順を説明します。まず、ソースを入手します。2.0のソースは、

+
    +
  • http://www.tdiary.org/download/tdiary-full-2.0.0.tar.gz

  • +
+

です。サイズは約1.7Mバイトあります。デフォルト以外のプラグインとテーマを除いた最少パッケージ(134Kバイト)もありますが、プラグインやテーマが使えなくてはtDiaryの魅力半減ですから、ここではすべてのプラグインとテーマを含んだフルパッケージをダウンロードします。

+

パッケージを展開したものをサーバーのホームディレクトリの「public_html」ディレクトリに転送した後は、

+
    +
  • ホームディレクトリに .htpasswdを用意

  • +
  • public_html.htaccessを用意

  • +
  • (CGIが書き込み可能な)データディレクトリの用意

  • +
  • tdiary.confにデータディレクトリの設定

  • +
+

をするだけで、基本的なインストールは完了です。後はブラウザで日記を表示させ、設定メニューから、プラグインやテーマなどを設定することができます。

+

トラックバックを使うためにはmisc/plugin/trackbackディレクトリにあるtb.rbpublic_html直下にコピーし、実行可能(chmod a+x tb.rb)にしておきます。

+

詳しいインストール方法は、

+
    +
  • tDiary FAQ
    +http://tdiary-users.sourceforge.jp/cgi-bin/wiki.cgi?FAQ#i1

  • +
+

を参照してください。

+
+
+

BlogKit

+
+

「Web日記」と「Blog」がどこが違うのか、という論争には加わりたくないのですが、MovableTypeを始めとするBlogツールとWeb日記ツールであるtDiaryとは、トラックバックなど共通の機能を持ちますが、記事の扱いで少しだけ差があります。

+
+

tDiaryは日記ツールですから、日が重要な単位になります。同じ日に複数の話題を書くとそれぞれはその日のエントリの中の1セクションになります。コメントやトラックバックも日単位です。一方、MovableTypeなどのいわゆるBlogツールではセクションに当たる1つの記事が1つの単位です。コメントやトラックバックなども記事単位で付きます。同じ日に複数のエントリを書いたとしても、それは「たまたま日付が同じである連続したエントリ」であるとしか扱われません。この辺が日記ツールとBlogツールの違いといるかもしれません。

+

tDiaryを使っていても、記事単位で扱いたいという要望はあると思います。特に同じ日に全然違った話題で複数のセクションを書いたときに、コメントが混ざってしまわないように、セクションごとにコメントやトラックバックを受け付けたいという希望を持つ人は多いような気がします。また、いわゆるBlogツールがほしいが、同時にtDiaryのテーマ機能やプラグインも利用したいという要望もあるでしょう。それらを解決するのがBlogKitです。

+

基本的な発想は、tDiaryの日付を他のBlogツールにおける記事番号として扱おう、というものです。記事の日付そのものは別に記録する「最終更新時刻」で表示します。

+

BlogKitを導入するとtDiaryの挙動が以下のように変化します。

+
    +
  • 日付が意味を持たなり、記事番号扱いになる

  • +
  • 記事に「最終更新時刻(Last-Modified)」が付くようになる

  • +
  • リスト表示時には、記事の最初のセクションだけサマリーとして表示される

  • +
  • 「更新」を選ぶと、自動的に空いている記事番号を割り当てる

  • +
  • Recent EntriesやWhat’s Newで更新情報が公開される

  • +
  • なにやら英語っぽい表記になる

  • +
+

これらの挙動の変化によって、tDiaryを「日記」に特化したツールを超えて、Blogツールとして扱うことができるようになります。

+

1日1つ以上のエントリを連続して入力すると「日付のように見える番号」が恐ろしい勢いで進んでいくことになりますが、まあ、気にしない、ということで。

+

BlogKitは、

+
    +
  • http://www.tdiary.org/download/tdiary-blogkit-2.0.0.tar.gz

  • +
+

から入手できます。「Rubyホームページ(http://www.ruby-lang.org/)」もtDiary+BlogKitで運用されています。

+
+
+

ツッコミメール

+
+

さて、インストールも完了し、tDiaryを運用できるようになったということで、tDiaryの利用をより便利にする小技をいくつか紹介しましょう。

+

まずはツッコミメール機能です。comment_mailプラグインを有効にすると、ツッコミが行われたときにメールが届きます。これは大変便利な機能ですが、このままでは、ツッコミした人の情報がログを見ないとわからないという不満があります。

+
+

困ったことがあれば自分で解決できるのがオープンソースのよいところです。そこでこの点も改善してみましょう。この改善はツッコミメールのテンプレート(skel/mail.rtxt)を書き換えることで行います。mail.rtxtはeRubyファイルでプラグインなどを使うことができます。私はリスト40.1のようなmail.rtxtを使っています

+
+

リスト40.1●mail.rtxt

+
From: <%= name %> <<%= mail %>>
+To: <%= receivers.join(', ') %>
+Date: <%= date %>
+Message-Id: <%= message_id %>
+Subject: <%= mail_header %>-<%= serial %> 
+<%= name %>
+MIME-Version: 1.0
+Content-Type: text/plain; charset="iso-2022-jp"
+Content-Transfer-Encoding: 7bit
+Errors-To: <%= receivers[0] %>
+X-Mailer: tDiary <%= TDIARY_VERSION %>
+X-URL: http://www.tdiary.org/
+X-REMOTE-ADDR: <%=TCPSocket.gethostbyname(ENV['REMOTE_ADDR'])[0] rescue ENV['REMOTE_ADDR']%>
+
+<%= body %>
+--
+<%= @conf.index =~ %r|^https?://|i ? '': @conf.base_url %><%= @conf.index.sub(%r|^\./|, '') %><%= anchor(@date.strftime('%Y%m%d') + ('#c%02d' % serial)) %>
+
+
+

追加されたのは、「X-REMOTE-ADDR」で始まるヘッダー部の最後の行です。内容はツッコミを書いた人のホスト名が表記されます。逆引きができなかった場合には、IPアドレスが表示されます。

+
+
+

tdiary-mode.el

+
+

私がtDiaryを選んだ理由の1つがtdiary-mode.elにより、Emacsで文章を書き、Emacsの中から文章をアップロードできることです。

+

そしてもう1つの理由が、文書フォーマットを選択することができ、RDを使うことができる点です。

+

ところが、tdiary-mode.elはtDiaryのデフォルトの文書フォーマットである「簡易HTML」に対応して、HTML文書の入力支援しか持っていません。RDの入力には不要なものばかりです。RD文書入力用にはrd-mode.elがありますから、tDiaryで日記を書くためにもぜひ利用したいものです。

+

なければ自分でできるのがオープンソースのよいところです。元Emacs Lispハッカーの私は、tdiary-mode.elを改造して、RD版tdiary-mode.elを作りました。これはtdiary-mode.elhtml-modeをベースにしているところを、rd-modeをベースにするように変更しただけのものです。

+ +

改造版tdiary-mode.elは573行もありますから、ここにリストは載せられませんが、付録CD-ROMに収録してもらうことにします。RDでtDiaryを利用していらっしゃる人には利用価値があるのではないでしょうか。

+

このtdiary-mode.elはRDとHTMLが切り替えられないのが欠点です。私はRDしか使わないので困ってないのですが、誰か有志(勇士)がよりよいものを作ってくださらないかと期待しています。

+
+
+

http.el

+
+

唐突に話は変わりますが、先日、www.ruby-lang.orgとして利用していたマシンがクラックされてしまいました。実質的な被害はなさそうだったのですが、改ざんがないことを証明できなかったので大変な思いをしました。ホスト管理者は大変な思いをして復旧していました。

+

そんなこともあるので、www.ruby-lang.orgとマシンを共有しているので、その影響を受けることになりました。tDiaryのアップデートもSSL経由のHTTPSでなければ行えないようになりました。Plain HTTP経由のBASIC認証ではパスワードが漏洩ろうえいする危険があるからです。

+

まあ、妥当な制限だと思いましたが、1つ困ったことが起きました。http.elがHTTPSに対応していないのです。http.elは、tdiary-mode.elがtDiaryの更新のために利用している小さな(127行)Emacs Lispプログラムです。http.elは簡易ブラウザとしてEmacsの中からWWWを呼び出すことができます。

+

困ったことがあれば、まず調査、そして自分で解決です。ちょっと調べた範囲内では、http.elをSSL対応にするコードは存在しないようです。しかし、ssl.elというプログラムがあって、SSLによるネットワーク接続を(opensslを使って)実現しているようです。

+

そこでまたハックです。

+

ssl.elを利用したちょっとした改造により、「https:」で始まるURLへのアクセスはSSL経由でアクセスするようになりました。改造したhttp.el(128行)もCD-ROMに収録しておきます。今回収録する各種プログラムのライセンスはGPLとします。

+
+
+

トラックバック

+
+

トラックバックはBlogや日記の間をつなぐ面白い仕掛けです。「あなたのエントリに言及しました」という連絡により、文章や発想が広がっていきます。

+

しかし、いかんせんトラックバックは面倒です。Permalink(エントリに対応する恒常的URL)を開き、Trackback Ping URLをコピーし、自分のページの更新ページにそれをペーストするのは、結構な手間で、思わず「そんなことするくらいならトラックバックは止めよう。リンク元解析で見つけてくれるよ」と感じてしまいます。

+

しかし、私は間違っていました。トラックバックには「Trackback Auto-Discovery」という仕様があって、エントリにRDFでPing URLを含めることができるのでした。後は適切なツールでそれを抽出するだけです。無知は罪ですよね。

+
+

抽出するにはBookmarkletを使います。Bookmarkletとは、ブックマークのURL欄に登録しておく小さなJavascriptプログラムです。Javascript対応のブラウザでは、ブックマークから選択することで、現在表示中のページに対してJavascriptプログラムを起動することができます。

+

Trackback Ping URLを抽出するBookmarkletをリスト40.2に示します(すべて1行です)。

+
+

リスト40.2●trackback bookmarklet

+
javascript:document.getElementsByTagName( "html" )[0].innerHTML.match(/trackback:ping=[\'\"](http.+)[\'\"]/i);var pingUrl = RegExp.$1;location.href = 'http://YOURSITE/update.rb?plugin_tb_url='+escape(pingUrl);
+
+
+

JavaScript対応のブラウザで新規ブックマークの登録を行い、そのURL欄にリスト40.2の内容を登録します。リスト40.2YOURSITEの部分は自分の日記のURLに変更してください。ブックマークのタイトルは適当に「Trackback」とでもしておくとよいでしょう。Trackback Auto-Discoveryに対応したページを表示中にこのBookmarkletを起動すると、Trackback Ping URLが入力された状態で日記の更新ページにジャンプします。

+

tDiaryのトラックバック対応はTrackback Auto-Discoveryに対応しています。その他、MovableType他多くのBlogツールが対応していますので、このBookmarkletは使いでがあると思います。私も以前よりもトラックバックを打つことが多くなったような気がします。

+

このtrackback bookmarkletもtrackback.jsとしてCD-ROMに収録しておきます。

+
+
+

まとめ

+
+

今月はWeb日記ツールtDiaryを改めて紹介してみました。こうして「困ったこと」を解決していくたびに思うのは、「オープンソースってありがたい」ってことです。商用ツールではなかなか自分で解決というわけにはいきません。問題を解決してそれを公表することで、他の人を助けることもできるようになります。

+

開発者から見たtDiaryの醍醐味は、やはりプラグインでどんどん機能を拡張できる点でしょう。機能を追加することでtDiaryがますます使いやすくなります。日記を書くことよりも機能を追加することのほうが楽しくなるという本末転倒な結果になってしまうケースもありますが、趣味なんだからよしとしましょう。プラグインプログラミングはRubyの入門にも向いていそうです。近いうちにtDiaryプラグインプログラミングについても探検してみたいと思います。

+
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-021.xhtml b/docs/vol2/xhtml/p-021.xhtml new file mode 100644 index 0000000..5c40d79 --- /dev/null +++ b/docs/vol2/xhtml/p-021.xhtml @@ -0,0 +1,54 @@ + + + + + +第40章 tDiary + + + + +

Matz Essays Volume 2

+ + +
+ +

◆ Ruby開発日記 ◆挑戦! 言語塾

+
+

以前にも書いたような気がしますが、プログラミング言語というものはコンピュータサイエンスの中でも「総合芸術」とでも呼ぶような存在です。

+

プログラミング言語の文法はユーザーインターフェイスにつながるものがありますから人間工学や心理学と関連のある領域です。また、コンパイラやインタプリタなどの処理系の実装は、数学的バックグラウンドを必要とする理論的な背景があります。それからライブラリの実装には各種アルゴリズムやデータ構造に関する知識が要求されます。

+

ものづくりが好きでコンピュータとプログラミングを愛する人にとって、プログラミング言語というのは非常に挑戦的なエキサイティングな分野なのです。しかし、「普通」のプログラマーにとって言語は「ありき」として存在するもので、自分で設計したりするものではないようです。まあ、そう思うのは自然だと思いますけどね。

+

しかし、私はコンピュータに触れ始めた少年時代にこのプログラミング言語の魅力に取りつかれ、それ以来ずっと「いつかは自分の言語を作りたい」と思い続けてきました。また、機会あるごとにいろんな言語の文法や仕様を調べたり、処理系のソースを読んだりするようなことを続けてきました。Pascal, C, Lisp, Smalltalkなどなど。今から約10年前にRubyを作り始めたときには、それまでに得てきたそのような知識が役に立ちました。

+

少年時代から、自分自身のことをコンピュータに関しては変わり者であると自覚していましたから(その他の分野でも変わり者だったかもしれません)、そんなふうに言語を作りたがるのは私くらいなものだろうと考えていたのですが、ここ数年、「Rubyの作者」として名前が知られるにつれて、あいさつとして「実は私も言語を作りたいと思ってるんです」という言葉をたびたび聞くようになりました。どうやら単なる社交辞令というだけでもなさそうです。

+

そうなんです。自分のプログラミング言語が作りたいと子供のときから思い続けてきた私は確かに変わり者かもしれませんが、似たような「自分言語を作ろうと考える変わり者」はまだまだたくさんいるのです。We are not alone.

+

とはいえ、言語を作りたいと思っている人の数ほどには、言語は存在していないようです。これは「作りたいなあ」と思っていながらも、実際には構想だけで終わる例がとても多いということではないでしょうか。また、数少ない「自分言語」も、広く使われるようなものはごく少数です。

+

このような構想倒れについて考察すると、いくつかの原因が思い当たります。

+

まず、第一には熱意が持続しないという点です。私自身もRubyの前に作ろうと思った言語は1つや2つではありません。しかし、考えているうちにアイデアに穴があったり、それほど優れているとは思えなくなったり、ただ単に飽きてしまったりなどいろいろな理由で実現には至りませんでした。1つの言語を作るのには長い時間が必要です。その時間をいかに「耐える」のか、というのが鍵になりそうです。

+

もう1つの理由は、プログラミング言語とその処理系の実装に必要な知識の領域が幅広いことのように思います。先に「総合芸術」と言いましたが、これは見方を変えれば「敷居が高い」ということでもあります。言語設計に必要なさまざまな領域の知識を身につけるのは、趣味の個人としては少々荷が重いかもしれません。

+
+

他にも、プログラミング言語のデザインは通常のソフトウェアとは少々趣(おもむき)が異なることなどもあるように思います。

+

しかし、このような障害を乗り越えてしまえば、プログラミング言語のデザインはとても楽しいものです。少なくとも私にとってはそうです。プログラミング言語を作るということは、プログラミングの1つの世界を作り出すような創造性にあふれた作業です。

+

このような言語を作ることの楽しさを感じてもらうため、同じような志を持つ仲間と励まし合ったり、先達やさまざまな領域の専門家と意見交換したり、言語のデザインそのものについて話し合える場があれば、未来の言語設計者に役立つのではないかと考えました。

+

そこで、そんな場としてとりあえずメーリングリストを作ってみることにしました。メーリングリスト名は「言語鍛冶」「言語職人」というニュアンスを込めて「langsmith」という名前にしました。

+

アドレスは、<langsmith@quickml.atdot.net>です。アドレスを見ただけでもわかるようにQuickMLで運営されていますので、メールを出すだけで参加できます。参加希望者は、私のアドレス<matz@ruby-lang.org>にCCして、上記のアドレスにメールを送ってください。最初のメールには簡単な自己紹介を含めてくださるとよいでしょう。

+

原稿執筆時点でのlangsmithメーリングリストの参加者は55名です。

+

まだ、langsmithメーリングリストは始まったばかりですが、すでに「私の言語にこれこれの機能を導入しようと思うが、意見が聞きたい」とか、「こういう目的の言語に望ましい性質は何か」などの非常に興味深い意見の交換が行われています。

+

langsmithメーリングリストのログは以下で参照できます。

+
    +
  • http://www.atdot.net/~ko1/w3ml/w3ml.cgi/langsmith/

  • +
+

langsmithメーリングリストに参加したから、すぐに言語が作れるようになるとは私も考えていません。しかし、まず「プログラミング言語ってのは自分好みのものを自由に作ってもよいんだ」という意識を持つことが大事なんじゃないかなあって思います。また、一人じゃできないことも何人かが助け合えば可能になるってこともありえるんじゃないかと希望しています。

+

最初はおもちゃのようなものでも、「自分の言語」を作り出し、改良していけるようになっていけば、世界が広がるんじゃないかと思います。そしてそんな言語がたくさん生まれてくれば、プログラミング言語の裾野が広がり、影響を与えあって、今までにないすばらしい言語が登場するかもしれません。中にはRubyのあとに続いたり、広く世間で使われるメジャーな言語が登場してくるかもしれません。

+

そんなふうになって、プログラミング言語を取り巻く世界が広がって、よりよいものになればよいなあと考えているのです。

+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-022.xhtml b/docs/vol2/xhtml/p-022.xhtml new file mode 100644 index 0000000..b33cc6a --- /dev/null +++ b/docs/vol2/xhtml/p-022.xhtml @@ -0,0 +1,362 @@ + + + + + +第41章 Webアプリケーションの基礎 + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay41 +
+

+探訪Ruby
+Webアプリケーションの基礎 +

+
+

[Linux magazine, 2004年10月号]

+
+

Webアプリケーションの基礎、特にHTTP, CGI, FastCGI, mod_rubyについて紹介しています。FastCGIやmod_rubyは現代では使われていませんが、CGIの仕組み(渡される情報)は現代のWebアプリケーションにもほとんどそのまま引き継がれています。

+

「Ruby開発日記」は2004年に開催された「Lightweight Language Weekend」についてですね。このイベントは2003に第1回(Lightweight Language Saturday)に始まり(第1回のレポートは本書第29章にちょっとだけあります)、この「Weekend」は2回目になります。LLイベントは途中で「Learn Language」と名前を変えて(途中中断をはさみつつ)継続し、2023年には20周年を記念するイベントも開催されました。20周年イベントでは私も含めた第1回のスピーカーが登壇し、懐かしい思いをしました。

+
+
+

今月から数回Webアプリケーションプログラミングについて学ぼうと思います。今月はその基礎編です。

+
+
+

Webアプリケーションプログラミング

+
+

正直、私自身がWebプログラミングについての経験が足りないので、本連載で解説するのを避けてきたのですが、現実問題、WebプログラミングはRubyの重要な適用分野です。いつまでも避けているわけにもいきません。

+

そこで今月から数回は自分でも学びつつ、Rubyで楽しいWebプログラミングを目指すことにします。

+

ところで、同様の理由で避けてきた分野として、GUIプログラミングとデータベースプログラミングがあります。これらも大変重要なテーマですから、Webプログラミングについての解説が片付いたら、これらも皆さんと一緒に学んでいこうと思います。

+
+
+ +

WWW

+
+

WWW(World Wide Web)はもともとCERNで開発された相互リンクした技術文書を参照する手段でした。文書間のリンクが世界中をクモの巣(Web)のようにつなぐありさまをもってWWWと名付けられました。

+

WWWが最初に登場した時点では、その基本となる技術は、

+
    +
  • 相互にリンクした構造を持つ文章を記述する手段としてのHTML(Hyper Text Markup Language)

  • +
  • 各所のサーバーに分散した、HTMLで記述された(ものばかりとは限らないけど)文書を指定する手段であるURL(Unified Resource Locator)

  • +
  • URLで指定した文書をネットワーク経由でサーバーから取り出すためのプロトコルであるHTTP(Hyper Text Transport Protocol)

  • +
+

でした。ですから、基本的には静的な文章を表現する手段だったわけです。しかし、ページの内容を動的に生成する技術が登場したときに、WWWは新しい局面を迎えることになります。

+
+
+

HTML

+
+

HTMLについては今さら説明する必要はそれほどないかもしれません。HTMLはSGMLと同じ構造を持つ文書フォーマットです。特徴は誰でも知っている <HTML> ... </HTML> という対になったタグです。SGMLから受け継いだこのタグフォーマットは、さらにXMLに受け継がれて増殖中です。

+

HTMLは基本的に文書の構造(「見出し」とか「箇条書き」とか)を表現します。しかし、構造だけでなく、フォントの大きさや色などに代表される「見栄え」も指定できてしまう柔軟性(悪くいえばいいかげんさ)を備えています。

+
+
+

URL

+
+

WWWの基礎技術のうち、URLほど広く目にするものはないかもしれません。今や、テレビのCMや広告のポスターにURLが含まれることは珍しくありませんし、お店で買ってきた商品のラベルにもURLが印刷されています。ラジオをつければ頻繁に「エイチティーティーピーコロン……」というくだりを耳にします。URLはときどき「インターネットアドレス」と呼ばれるようです。文書やサイトを示すアドレスであるという意味からは間違っているわけではありませんが、なんか違和感があります。

+
+
+

HTTP

+
+

URLやHTMLほど人目に触れないのがHTTPです。HTTPはWWWクライアント(WWWブラウザ、Internet ExplorerやMozillaなど)とWebサーバーの間の通信のフォーマットです。

+

HTTPはインターネットの他のプロトコルと同様にテキストベースのプロトコルです。また、その通信内容の構造は電子メールとほぼ同じになっています。HTTPの通信内容はステータス行、ヘッダーとボディからなります。ステータス行は通信の先頭行であり、通信種別を示す情報を含んでいます。ヘッダーは、

+
+
+
フィールド名: 内容
+
+

という行が並んだものです。たとえば、ボディの内容を表す部分は、

+
+
Content-Type: text/html
+
+

のようになります。ヘッダーとボディとの間は空行で区切られています。ボディ部は任意のデータで、そのデータのフォーマットなどの情報はヘッダーに含まれています。サーバーからクライアントへのHTTP通信内容の例をリスト41.1に示します。

+
+

リスト41.1●HTTP通信

+
HTTP/1.1 200 OK
+Date: Fri, 13 Aug 2004 06:50:56 GMT
+Server: Apache/1.3.31 (Debian GNU/Linux)
+Last-Modified: Fri, 13 Aug 2004 06:50:40 GMT
+Content-Length: 76
+Content-Type: text/html; charset=iso-8859-1
+
+<html>
+<head><title>hello world</title>
+<body>
+Hello World!
+</body>
+</html>
+
+
+
+
+

CGI

+
+

さて、リクエストに応じて動的に情報を提供するための技術がCGI(Common Gateway Interface)です。CGIはHTTPによってリクエストを受けたサーバーが、サブプロセスとしてプログラムを起動し、そのプログラムの出力をそのままクライアントに返す仕組みです。この仕組みによって、クライアントからのリクエストに応じた動的な対応が可能になりました。

+

CGIではクライアントからの情報は、環境変数と標準入力を通じてサーバーからCGIプログラムに渡されます。プログラムの実行結果は標準出力を通じてサーバーに渡され、最終的にはその内容がクライアントに転送されます。環境変数も標準入出力も、UNIXでは一般的な情報の受け渡し方法ですから、これらを扱うことができればどんなプログラミング言語でもCGIプログラムを書くことができます。

+

CGIプログラムの実行は具体的には以下の手順で行われます。

+
    +
  1. まず、HTTPクライアント(WWWブラウザ)がHTTPサーバー(Apacheなど)にネットワーク経由でHTTPリクエストを送ります。クライアントはリクエストとして、要求するURLなどの情報を送ります。

  2. +
  3. クライアントから指定されたURLがCGIに対応するものであった場合、サーバーはいくつかの環境変数を用意し、CGIプログラムをサブプロセスとして起動します。HTTPリクエストがPOSTであった場合、リクエストに含まれる情報は標準出力を通じてCGIプログラムに渡されます。CGIプログラムに渡される環境変数のうち代表的なものを表41.1に示します。

  4. +
  5. CGIプログラムは、与えられた情報を元に実行を行い、標準出力にHTMLを出力します。何らかの理由で実行がうまくいかなかったときには終了ステータスとしてゼロ以外の数値を返します。

  6. +
  7. サーバーはCGIプログラムの出力を受け取り、必要に応じてHTTPプロトコルのヘッダーを追加して、その内容をクライアントに転送します。クライアントに渡される情報はURLで指定されるものが静的なファイルであっても、CGIの実行結果であっても違いはありません。

  8. +
+

CGIプログラムの実行の仕組みを図41.1に示します。

+ +
+

表41.1●CGIで渡される環境変数(代表的なもの)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
環境変数説明
AUTH_TYPE認証方式
CONTENT_LENGTHボディの長さ
CONTENT_TYPEボディのMIMEタイプ
GATEWAY_INTERFACECGIのバージョン
HTTP_ACCEPTブラウザが受信可能なMIMEタイプ
HTTP_ACCEPT_ENCODINGブラウザが受信可能な文字コード
HTTP_ACCEPT_LANGUAGEブラウザが受信可能な言語
HTTP_REFERER参照元ページのURL
HTTP_USER_AGENTクライアント種別
PATH_INFOURLの末尾のパス情報
PATH_TRANSLATEDPATH_INFOの実パス
QUERY_STRINGGETメソッドのデータ
REMOTE_ADDRクライアントのIPアドレス
REMOTE_HOSTクライアントのホスト名
REQUEST_METHODリクエストの種別(GET, POST, HEAD
SCRIPT_NAMECGIプログラム名
SERVER_NAMEサーバーのホスト名
SERVER_PORTサーバーのポート番号
SERVER_PROTOCOLHTTPのバージョン
SERVER_SOFTWAREサーバー種別
+
+
+ +
+ fig4101 +
+

図41.1●CGIプログラムの実行

+
+
+
+ +

CGIプログラム

+
+

では、簡単なCGIプログラムをRubyで書いてみましょう。CGIプログラムと言っても何も複雑なことはありません。ただ、環境変数などから情報を得て、標準出力に向けて出力する、それだけです。他のプログラムと何の違いはありません。環境変数や標準入出力の扱い、HTMLを始めとするテキスト操作などはRubyの得意分野です。

+

例題プログラムをリスト41.2に示します

+
+

リスト41.2●CGIサンプルプログラム

+
#! /usr/bin/ruby
+addr = ENV["REMOTE_ADDR"]
+client = ENV["HTTP_USER_AGENT"]
+
+print "content-type: text/html\n\n"
+print <<END
+<html>
+<head><title>hello world</title>
+<body>
+hello, it's #{Time.now.to_s}.<br>
+you are using #{client} from #{addr}.
+</body>
+</html>
+END
+
+
+

このプログラムの実行結果は図41.2のようになります。

+
+
+
hello, it's Fri Aug 13 23:40:25 JST 2004.
+you are using w3m/0.3.2+mee-p24-19+moe-1.5.0 from 127.0.0.1.
+
+

図41.2●CGIサンプル実行結果

+
+ +

これは単純なCGIプログラムをテキストブラウザで表示させた例なので貧弱な外見ですが、多くのWebアプリケーションの実装は、複雑に見えても同じ原理の延長です。

+
+
+

CGIの欠点

+
+

このようにCGIという仕組みを導入することで、WWWは単なる相互リンクされたファイルを参照する仕組みから、ネットワーク経由のアプリケーションを実現する仕組みへと進歩しました。これはすばらしい進歩だったと思います。

+

しかし、そんなCGIにも欠点があります。最大の欠点は性能です。CGIはクライアントからのリクエストを受け取るたびにCGIプログラムをサブプロセスとして起動します。サブプロセスの起動はOSにとってあまり軽いタスクではありません。アクセス頻度が数秒に一度程度なら何の問題もないのですが、アクセス頻度の高いサイトではCGIによるプロセス生成の負荷は馬鹿になりません。

+

もう1つの欠点は、レベルの低さです。CGIというのは動的なページを実現するための最低限の仕組みです。現代的なWebアプリケーションが備える機能(たとえば、セッション管理やユーザー管理、コンテンツ管理などなど)は提供されていませんから、各自が自分で実装するしかありません。

+

これらの欠点は解決不能ではありません。性能の問題はmod_rubyを始めとする「Apacheモジュール」や、FastCGIによって解決できます。また、レベルの低さはそれを補うライブラリやフレームワークを用意することで対応できます。

+

高レベルのWebアプリケーション用ライブラリやフレームワークについては来月以降紹介するとして、今月はCGIの性能問題をmod_rubyやFastCGIがどう解決するかを見てみましょう。

+
+
+

mod_ruby

+
+

HTTPサーバーの代表格であるApacheには、動的にリンクされるモジュールを使って機能を拡張することができます。実はApacheではCGIの呼び出しもモジュールによって実現されています。HTTPサーバーに機能が追加できるなら、インタプリタそのものをモジュールとしてApacheに取り込めばCGIプログラムをサブプロセスとして実行する必要はなくなるわけです。これでプロセス起動のコストを削減できます(図41.3)。

+
+ +
+ fig4103 +
+

図41.3●Apacheモジュールの実行

+
+ +

Rubyに対するApacheモジュールも提供されていて、mod_rubyと呼ばれています。mod_rubyに関する情報は、

+
    +
  • http://modruby.net/

  • +
+

から入手できます。入手方法やインストール方法については上記のサイトを参考にしてください。

+

mod_rubyを使った場合、CGIプログラムはApacheプロセス内部で直接実行されます。その結果として以下の点に注意する必要があります。

+
    +
  • グローバル変数が初期化されない。よって、前回のグローバル変数の値がそのまま残っている。ただし、前回実行されたスクリプトを特定することは困難なので残っている値を利用することは現実的でない。

  • +
  • また、通常、Apacheは同時に複数プロセスが動作してリクエストを待つので、情報共有のためにもグローバル変数は役に立たない。

  • +
  • requireでライブラリをロードするのは一度だけ。なので以前の実行でライブラリがすでに読み込まれていれば、ロードされないかもしれない。特にデバッグ時にライブラリを変更してもロードされない点に注意。

  • +
  • mod_rubyがCGIプログラムに提供するAPIは通常のCGI実行とはいくつか差がある。ただし、Rubyが標準で提供しているcgiライブラリはその差を隠蔽いんぺいしてくれる。

  • +
  • mod_rubyを利用中に妙なライブラリを利用するなどして、インタプリタそのものが誤動作するなどの問題が発生すると、Apacheを道連れに落ちることがある。mod_rubyへ移行はプログラムそのものが安定して動作するようになってからをお勧めする。

  • +
+
+
+

FastCGI

+
+

FastCGIはApacheモジュールとしてインタプリタを組み込んでしまうのとは別のやり方でプロセス起動を削減し、性能を向上させる仕組みです。

+

FastCGIの基本的な発想は、CGIプログラム1つ1つを小さなデーモン(あるいは常駐プロセス)にしたてることです。Apacheでは、この機能はやはりモジュール(mod_fastcgi)で実現されています。

+

mod_fastcgiはリクエストを受け付けると、

+
    +
  1. リクエストに対応する子プロセスがすでに起動しているかどうかを調べます

  2. +
  3. 起動していなければ、起動します

  4. +
  5. 子プロセスにリクエストを転送します。子プロセスは、(1) リクエストを受け付け、(2) 実際の処理を行う、というループになっている必要があります

  6. +
+

という手順で実行を行います。FastCGIの実行を図41.4に示します。

+
+ +
+ fig4104 +
+

図41.4●FastCGIの実行

+
+ +

FastCGIには、直接Apacheにインタプリタがリンクされるわけではないので異常事態に強い、というメリットがあります。また、mod_fastcgiとFastCGIプログラムの通信に用いられるFastCGIプロトコルを解釈できれば、任意のプログラミング言語でCGIプログラムを書くことができるということも重要なメリットです。

+

一方、実装されるプログラムごとにプロセスが常駐するので、プログラム数が多い場合には、モジュールを使ってインタプリタをリンクする場合と比較してプロセス数が増加するというデメリットがあり、またリクエストを受け付けては処理を行うループ構成となるようにCGIプログラムの書き換えが必要となることもデメリットです。

+

FastCGIについての詳細情報は、

+
    +
  • http://www.fastcgi.com/

  • +
+

から入手できます。Apacheモジュールmod_fastcgiについては、以下のページを参照してください。

+
    +
  • http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html

  • +
+

RubyからFastCGIを利用する場合には、MoonWolfさんがメンテナンスしているfastcgiライブラリを入手する必要があります。fastcgiライブラリはpure Ruby版とfastcgi.comから入手できるCライブラリをリンクしたC版が同梱されており、pure Ruby版の原作者はEli Greenで、C版の原作者は私(まつもと)です。

+
    +
  • http://www.moonwolf.com/ruby/archive/ruby-fcgi-0.8.4.tar.gz

  • +
+

コンパイルとインストールには添付のinstall.rbを使います。

+
+
+
% ruby install.rb config
+% ruby install.rb setup
+% sudo ruby install.rb install
+
+

fastcgiライブラリを使ったRubyプログラムはリスト41.3のような感じになります(fastcgiライブラリのREADMEから引用)。

+
+

リスト41.3●FastCGIサンプル

+
#!/usr/bin/ruby
+require "fcgi"
+
+FCGI.each_cgi {|cgi|
+  name = cgi['name'][0]
+  puts cgi.header
+  puts "You are #{name} " if name
+  puts "Connecting from #{cgi.remote_addr}"
+}
+
+
+

each_cgi”でFastCGIのループを構成しています。実際のプログラムでは、処理がすべてeach_cgiのループの中に入るようにします。

+

実はfastcgiライブラリはCGI実行にも対応していますので、リスト41.3のサンプルプログラムはCGIプログラムとしても動作します。将来、FastCGIに移行することが明らかな場合には最初からそのような書き方をするのも1つの方法です。

+

プログラムをFastCGIに対応させる場合にもう1つ注意すべき点は、FastCGI実行中のプログラムからはENVによる環境変数の参照が行えないことです。each_cgiから渡されるCGIオブジェクトにはCGIが環境変数として渡す情報がメソッドを経由して取得できますから、最初からそれらのメソッドを使って情報を得るようにしてください。

+
+
+

まとめ

+
+

今月はWebプログラミングの基礎と、CGIの性能上の欠点を解消する手段としてmod_rubyfastcgiを紹介しました。来月はもう少し「レベルの高い」Webプログラミングについて皆さんと一緒に学ぼうと思っています。

+
+
+ +

おまけ — RubyConf 2004

+
+

ところで、今年のRubyは10月1日からバージニア州のワシントンDCエリアで開催されます。残念ながら今年は私は参加できません。さすがに臨月の妻を置いて海外旅行はできませんでした。

+

私の代わりにはキーノートを発表してくれるのは、なんとObjective-CのデザイナーであるBrad Coxだそうです。私は彼の著書『オブジェクト指向のプログラミング』に大きな影響を受けた過去があるので、ぜひお会いしたかったので、ますます残念です。

+

皆さんの中で時間的・経済的に余裕のある方がもしいらっしゃれば、ぜひ参加されてはいかがでしょう。「達人プログラマー」たちを始めとする世界のRuby界を代表するような人物と直接コンタクトできるチャンスです。また、参加者には先着50名様まで、この秋出版されるPickaxe本ことProgramming Ruby 第二版を世界で最初にプレゼントされることが決まっています。例年、出席者は50名前後ですから、今からでも間に合うのではないでしょうか。参加を検討してみてはいかがでしょう。Ruby Conferenceについての情報は、

+
    +
  • http://www.rubyconf.org/

  • +
+

からどうぞ。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-023.xhtml b/docs/vol2/xhtml/p-023.xhtml new file mode 100644 index 0000000..f0145ec --- /dev/null +++ b/docs/vol2/xhtml/p-023.xhtml @@ -0,0 +1,109 @@ + + + + + +第41章 Webアプリケーションの基礎 + + + + +

Matz Essays Volume 2

+ + +
+

◆ Ruby開発日記 ◆ Lightweight Language Weekend

+
+

昨年「Lightweight Lightweight Saturday」と題して開催され、日本唯一の軽量言語専門イベントとしてすっかり定着した「Lightweight Language」が、今年はいっそうパワーアップして帰ってきました。今年は「Lightweight Language Weekend」というタイトルで8月7日(土)、8日(日) の2日にわたって、東京新宿区の日本電子専門学校を会場として開催されました。

+

昨年は土曜日のみの1日イベントでしたが、今年は「Weekend」の名のとおり、2日間にわたるイベントに成長していました。開催期間が延びただけでなく、出席者も順調に増加していました。出世魚のようです。この調子で成長すれば、来年は「Lightweight Language Week」、さらに将来には「Lightweight Language Month」とかにまで行くのではないでしょうか。

+ +

冗談はともかく、日本ではこの種のイベントを休日以外に行うのはまだまだ難しいようです。「LL Weekend」のようなマイナーな言語を含むイベントの場合、仕事として認知されることはまだまだ難しいのからでしょうか。

+

アメリカではRubyのようなマイナーな言語のカンファレンスでも、金曜午後から日曜午前までの間で開催しています。また、アメリカは日本のように一極集中していませんから、どこで開いても大変な距離の移動が必要な参加者がいて、平日開催以上に障害は大きいように思います。そんな状況下でもそれなりに参加者が集まるというのは、アメリカのIT業界の層の厚さを意味しているのでしょうか。

+

今年の「LL Weekend」のプログラムは表41.2のとおりです。自称「LLの専門家」として参加してきた私ですが、個人的な事情で1日目しか参加できませんでしたので、1日目を中心にレポートします。2日目については参加された方からうかがった内容を中心に報告します。

+
+

表41.2●LL Weekend プログラム

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1日目: 8月7日 (土)
10:00〜12:00Language Update
13:00〜14:30LLを仕事に
15:00〜16:40君ならどう書く
2日目: 8月8日 (日)
10:00-10:10「その場でどう書く」課題発表
10:10-12:00LLとblog
13:00-14:00Lightning Talk
14:30-16:30その場でどう書く
+
+
+

Language Update

+

Common Lisp, Curl, Gauche, Groovy, Haskell, Perl, PHP, Pnuts, Python, Ruby, Squeakの各言語の近況を紹介するセッションです。「これは本当にLLなのか、と疑問の声があがる」とか、「Update以前に誰も知らないので紹介に終始してしまう」などという事態も発生しましたが、なかなか興味深いセッションでした。Curl, Gauche, Groovy, Haskell, Pnutsなどは知名度を高めるために一生懸命言語を紹介していました。宣伝に一番成功したのはGroovyだったような気がします。意外なのは海外では不動の地位を確立しているPythonがほとんどの時間を言語紹介に費やしたことです。Language Updateに限らずPythonは奮闘していました。世界的なPythonの広まりを考えると違和感ありまくりなのですが。

+

RubyのUpdateは私が紹介したのですが、コンピュータの調子が悪く、プロジェクターの画面が出ずに苦労しました。ここ数年、プレゼンのたびに苦労しているので「これはもうプレゼンはするな」という啓示かと愚痴っていたら「新しいコンピュータを買えってことじゃないですか」とツッコまれてしまいました。

+
+
+

LLを仕事に

+

アンケートの結果を元にBOF形式で語るセッションでした。

+

世間では仕事に使う言語としてはLLはほとんど認知されていなんだなあ、ということが実感できたセッションでした。というか、職業プログラマーの愚痴大会化していたような。皆さん、厳しい環境で我慢しておられるのですね。個人的にはもう少し住みよい環境を求めて流動化してもよいような気がするのですが。

+

あと、アンケートの引用から、Rubyユーザーの忠誠度の高さがうかがえました。Rubyってば愛されてるなあ。

+
+
+

君ならどう書く

+

デザインパターンを重視した「ls-lRシェル」と「nQueenゲーム」の2つのお題に各言語の代表者が挑戦するセッション。Ruby陣営は私の会社から前田修吾くんと、転職したばかりのかずひこくんを送り込みました。前者は言語の差よりも挑戦者の性格が出たような気がします。後者はSqueakが優勝でRubyは2位に終わりました。ちょっとテストが甘かったのが敗因のようです。ゲームとしては面白かったのですが、コードの中身まで踏み込めなかったのは残念でした。

+
+
+ +

LLとblog

+

BlogツールのほとんどはLLで記述されているので、各種Blogツール(日記ツール)の関係者による発表。技術的な話よりもBlogビジネスとかの話が中心になったようで、ちょっと残念かも。

+
+
+

Lightning Talk

+

制限時間5分でとにかく発表してしまうという「ライトニングトーク」です。今回は10人の人々が交代で話してくださいました。詳細はプログラムのページ、

+
    +
  • http://ll.jus.or.jp/llw2004/program.html

  • +
+

を参照してください。Rubyからは「日本Rubyの会」設立の発表と、次期Ruby VMと期待されているYARV(Yet Another Ruby VM)の発表がありました。

+
+
+

その場でどう書く

+

朝、発表になったお題に対して、午後までにプログラムを完成させて、それについて発表するというセッションです。今回のテーマはスケジュール管理のWebプログラムということでした。時間が短いこともあって苦労された人も多かったようです。たださんがtDiaryのプラグインを2行ほど修正して飛び入りしたりと、会場からの反響も大きかったようです。

+
+
+

まとめ

+

2日目の様子は伝聞なのですが、全体に荒削りながら楽しめるイベントであったようです。ただ、来年に向けて少々改善の余地はあるようです。個人的にはもっと技術面にフォーカスした内容を期待したいです。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-024.xhtml b/docs/vol2/xhtml/p-024.xhtml new file mode 100644 index 0000000..473bf31 --- /dev/null +++ b/docs/vol2/xhtml/p-024.xhtml @@ -0,0 +1,459 @@ + + + + + +第42章 Webアプリケーションの基礎 + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay42 +
+

+探訪Ruby
+Webアプリケーションの基礎
(その2) +

+
+

[Linux magazine, 2004年11月号]

+
+

Webアプリケーションの基礎の解説、2回目です。今回はHTMLのフォームとCGIが中心です。現代のWebアプリケーション開発でも基本的な構成は継承していますが、さすがにフォームやCGIを直接使うことはなくなりましたね。あと、Webアプリケーションフレームワークも紹介していますが、個別の紹介は次章に任せます。

+

「Ruby開発日記」は「汝は人狼なりや?」ということで、人狼ゲームの紹介です。本文中でも紹介した人狼BBSですが、2020年1月31日いっぱいでサービス終了してしまいました。ログは今でも読めるので(2024年3月現在)、読み返すのも楽しいですね。

+
+
+

先月に引き続きWebアプリケーションの基礎になる部分を学びましょう。それから、Webアプリケーションの作成を簡単にしてくれるWebアプリケーションフレームワークの簡単な紹介を行います。

+
+
+

FormとGETとPUT

+
+

先月解説したように、CGIとはURLにプログラムを対応させて、動的にページを作り出す仕組みのことです。先月は「Hello World」という文字列と現在時刻を表示させました(リスト42.1)。

+
+

リスト42.1●CGIサンプルプログラム

+
#! /usr/bin/ruby
+addr = ENV["REMOTE_ADDR"]
+client = ENV["HTTP_USER_AGENT"]
+
+print "content-type: text/html\n\n"
+print <<END
+<html>
+<head><title>hello world</title>
+<body>
+hello, it's #{Time.now.to_s}.<br>
+you are using #{client} from #{addr}.
+</body>
+</html>
+END
+
+
+ +

しかし、「Webアプリケーション」と呼ばれるものはただ単にURLに対応するプログラムを起動すればよいというものではありません。実際にはユーザーからのさまざまな入力を受け付けます。CGIプログラムがクライアントからの入力を得るためにはHTMLのFORMを使います。

+

Linux magazineの読者の皆さんならば、Webブラウザ上でラジオボタンやリストボックス、テキスト領域などへの入力を行ったことがあると思います。HTMLではそれらを総称してFORM(フォーム)と呼びます。HTMLが提供するFORMには表42.1のようなコントロールがあります。

+
+

表42.1●HTML FORMのコントロール

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
INPUTタグのtype属性説明
checkboxチェックボックス
fileファイルのアップロード
hidden非表示
imageイメージボタン
passwordパスワード(入力内容を「*」でマスク)
radioラジオボタン
reset入力のクリア
selectリストボックス
submit入力送信ボタン
text1行テキスト入力
textarea複数行テキスト入力
+
+

HTMLの入力要素は <FORM> タグの中で使います。<FORM> タグには以下のような属性を指定します。

+
    +
  • action

  • +
  • method

  • +
  • enctype

  • +
+

action」はそのフォームの中でsubmitボタンまたはイメージボタンが押されたときにリクエストが送られるURLです。

+

method」はGETまたはPOSTです。GETの場合にはパラメータはURLと一緒に渡されます。具体的には、

+
+
http://www.example.com/index.rb?foo=5&bar=10
+
+

という形式です。「index.rb」までがURL、「?」以降がパラメータになります。複数のパラメータの区切りには「&」が用いられます。また、特殊な意味を持つ文字(空白文字や「&」そのもの、全角文字など)は、「%」に続く16進二桁で表現します。たとえば空白は「%20」、チルダは「%7E」と表現されます。

+ +

一方、POSTではパラメータはHTTPリクエストのボディ部に格納されます(GETの場合は通常ボディ部は空)。パラメータは1行に1つずつ、「名前=値」という形式で渡されます。それと、ファイルアップロードの場合には「マルチパート形式」という方法で渡されるのですが、ここでは説明を省略します。

+

enctype」はリクエストのエンコーディング方法を示します。デフォルトは「application/x-www- form-urlencoded」です。通常は明示的に指定する必要はないでしょう。

+

ですから、リスト42.2のようなHTMLファイルがあれば、パラメータfoobarを与えて、CGIを起動することができます。

+
+

リスト42.2●FORMを使ったHTMLファイル

+
<html>
+<head><title>HTML FORM</title>
+<body>
+<form method="POST" action="http://www.example.com/update.rb">
+<!-- <input type="">で指定する -->
+<input type="checkbox" name="foo" value="1">1<br>
+<input type="checkbox" name="foo" value="2">2<br>
+<input type="checkbox" name="foo" value="3">3<br>
+<!-- selectはメニューリスト -->
+<select name="bar">
+<option value="東京">東京</option>
+<option value="東京">松江</option>
+<option value="東京">那覇</option>
+</select>
+</body>
+</html>
+
+
+
+
+

ステートレスなHTTP

+
+

CGIとHTMLフォームによって、ユーザーの入力を受け付け動的なページを生成できるようになりました。これにより、たとえば検索パターンを受け付けて、データベースをアクセスし、検索結果を返すようなWebアプリケーションを作ることができます。しかし、これだけでは、もっと複雑なWebアプリケーションを作るためにどうしても欠けているものがあります。それは同一クライアントからの一連のアクセスを1つのものとしてまとめる機能です。このような一連のアクセスのことを「セッション」と呼びます。

+

単純なデータベース検索のようなものを除けば、多くのWebアプリケーションは「状態」を必要とします。たとえば、ショッピングカートのあるインターネットショップを実現するWebアプリケーションでは、顧客が今までどのような商品をカートに入れたのかという「状態」を記憶しておく必要があるでしょう。あるいはアンケートを取るサイトでは、ユーザーが今までにどのような質問に答えたのかを記録する必要があるでしょう。これらを実現するためにはセッションが必要です。

+ +

HTTPは基本的にHTTPリクエストに含まれている情報だけでレスポンスの内容が決まり、アクセスの文脈などを考慮しないプロトコルです。このようなプロトコルのことを「状態がない」あるいは「ステートレス」と呼びます。ステートレスなプログラムは文脈を管理する必要がありませんから、実装も運用も楽だという利点がありますが、そのままではセッションを実現することができません。

+

そこでHTTPの上に何らかの「仕組み」を用意することでセッションを実現することになります。

+
+
+

セッションの作り方

+
+

では、どうやってセッションを実現すればよいでしょう。結局、一連のHTTPのやりとりに、これらを連続のものとして識別するIDを埋め込むことができればよいわけです。

+

サーバー側でIDを生成してクライアントに渡し、次のリクエストでそのIDを含むリクエストを渡してもらうためにはいくつか方法があります。

+

最初の方法はHTML FORMのhiddenフィールドを使う方法です。hiddenフィールドはHTML FORMの一部としてパラメータ渡しされますが、ブラウザ上では表示されないものです。表示されないということは、編集もされないのでサーバー側が渡した値をそのまま返すことが期待されます。サーバーがhiddenフィールドにセッション識別用のIDを渡せば、クライアントはその識別IDを戻してくれることが期待できます。これでHTTPリクエストが、HTTPレスポンスに対応することがはっきりわかります。HTTPレスポンスとHTTPリクエストの間の関連付けができれば、連続するやりとりすべてをセッションとして管理することが可能です。

+

hiddenフィールドを使ってセッションを実現するときには、HTML FORMのactionにはGETを選択するべきではありません。GETではパラメータがすべてURLに埋め込まれるため、他人が簡単にセッションIDを入手することができます。POSTにすればすべて解決とまではいえませんが、GETよりははるかに安全です。

+

セッションを実現するもう1つの方法はCookieを使うことです。CookieはHTTPのやりとりにくっつけることができる小さなデータで、HTTPレスポンスにくっつけてクライアントに渡すと、クライアントはCookieに指定された範囲のURLにアクセスするときに、リクエストに同じCookieをくっつけてサーバーに渡します。「くっつく」とはHTTPリクエストやHTTPレスポンスのヘッダー部分に格納されるという意味です。

+

それぞれのCookieは以下の属性を持っています。

+
+

このような仕組みを使ってセッションを実現します。

+
+
+

CGIプログラミング

+
+

さて、仕組みについては十分に見てきました。ここからは実際にプログラムを作ってみましょう。残念ながら実用的なプログラムを作るには誌面が足りそうにないので、非常に簡単なセッションを使ったCGIプログラムを作ります(リスト42.3)。

+
+

リスト42.3●セッションを使ったCGI

+
#! /usr/bin/ruby
+require 'cgi'
+require 'cgi/session'
+
+cgi = CGI.new("html4")
+session = CGI::Session.new(cgi)
+name = cgi["name"].to_s
+
+cgi.out("charset" => "EUC-JP") {
+  cgi.html {
+    cgi.head { cgi.title {"hello session"}}
+    cgi.body {
+      if name != "" || session["name"]
+        if name != session["name"]
+          session["name"] = name
+        end
+        "こんにちわ #{session["name"]}さん。<br><br>" +
+        "あなたが #{session["name"]}さんでなければ、"
+      else
+        "はじめまして"
+      end +
+      "<br><br>名前入力してください<p>" +
+      cgi.form("post", "example.cgi") {
+        cgi.text_field("name") +
+        cgi.submit("login")
+      }
+    }
+  }
+}
+
+
+ +

まず、このプログラムが使っている2つのライブラリ、“cgi”と“cgi/session”について紹介します。これらのライブラリはいずれも標準添付ですから、Rubyが使える場所では特にインストールする必要はありません。

+

cgiはHTTPサーバーから受け取ったCGI情報の解釈と、HTTPサーバーへの応答の作成を支援するライブラリです。GETPOSTで受け取ったパラメータはcgiオブジェクトを使って、

+
+
cgi[name]
+
+

という形式で受け取ることができます。パラメータが存在しないときには空文字列を返します。cgiオブジェクトはHTMLのタグに対応したメソッドを持っています。基本的にタグは、

+
+
cgi.tag("attr"=>"value"){"body"}
+
+

という形式で呼び出します。引数には属性を、ブロックにはタグの内側にくる文字列を指定します。リスト42.3>の例題を見ればわかるように、タグメソッドはネストして呼び出すこともできます。cgiオブジェクトの生成には、生成するHTMLのレベルを指定します。有効なレベルは、

+
    +
  • html3   – HTML3.2
  • +
  • html4   – HTML4.01
  • +
  • html4Tr – HTML4.01 Transitional
  • +
  • html4Fr – HTML4.01 Frameset
  • +
+

です。「とりあえず」の場合には例題にあるように“html4”を指定しておけばよいのではないでしょうか。

+

cgi/sessionhiddenフィールドやCookieを使ったセッション管理を自動的に行ってくれるライブラリです。セッションはハッシュのように使える一種のデータベースで、

+
    +
  • クライアントからのリクエストにセッション情報が含まれていなければ自動的にセッションIDを生成

  • +
  • セッション間で共有される情報は、

    +
    +
    session[name]
    +
    +

    で参照・格納が可能

  • +
+ +

というものです。例題では“name”という名前で、ユーザーから与えられた名前情報を格納していました。cgi/sessionを使うには、まずセッションオブジェクトを生成する必要があります。セッションオブジェクトの生成は、

+
+
CGI::Session(cgi)
+
+

で行います。cgiCGIオブジェクトです。CGI::Sessionオブジェクトはオプションを受け取ることもできます。

+
+
session = CGI::Session.new(cgi,
+    'database_manager' => CGI::Session::PStore,
+    'session_key' => '_rb_sess_id',
+    'session_expires' => Time.now + 30 * 60,
+    'prefix' => 'pstore_sid_')
+
+

有効なオプションを表42.2に示します。

+
+

表42.2●HTMLフォームのコントロール

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
オプション説明
session_keyHTMLに埋め込むセッションキーの名前(_session_id
session_idセッションID(自動生成)
new_session真のとき、強制的に新しいセッションを開始する(false
database_managerデータベースクラス(CGI::Session::FileStore
session_domainセッション用Cookieのdomain
session_expiresセッション用Cookieのexpires
session_pathセッション用Cookieのpath
session_secureセッション用Cookieのsecure
+
+

database_managerにはcgi/sessionの実際のデータを格納するクラスを指定します。標準で用意されているのはCGI::Session::FileStoreCGI::Session::MemoryStoreです。cgi/session/pstorerequireすればデータをPStoreデータベースに格納するCGI::Session::PStoreも利用可能です。バックエンドを用意すれば、セッションデータをPostgreSQLなどDBMSに格納することもできます。

+
+
+

Webアプリケーションフレームワーク

+
+

というわけで、cgiライブラリとcgi/sessionライブラリを利用すれば、セッションを持つCGIプログラムを比較的簡単に開発できるのですが、正直なところある程度以上複雑なWebアプリケーションを開発するためには、少々低レベルすぎる印象があります。HTTPが渡すCGIデータの構造とか、生成するHTMLの属性とか細かなところは気にせず、もっとハイレベルな開発が行いたいものです。また、数多くのページから構成されるWebアプリケーションでは、共有部分を効率よく開発したり、デザインの共通性を最大限に生かすことも求められます。

+ +

このようなニーズに答えるためのものが「Webアプリケーションフレームワーク」です。Webアプリケーションフレームワークと呼ばれるものはたくさんあります。RAAを調べてみたところ、何らかのWebアプリケーションの枠組みを提供するものは実に19個も登録されています(表42.3)。どうもRAAに登録されていないものもいくつかあるようです。

+
+

表42.3●RubyのWebアプリケーションフレームワーク

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
プロジェクト作者説明
ArrowMichael GrangerApacheを対象にしたフレームワーク
BorgesEric Hodel継続でセッションを実現する
CGIKit鈴木鉄也WebObjects類似のフレームワーク
CerisewglozerJ2EE類似のフレームワーク(amritaを利用)
IowaKirk Hainesコンポーネントベースフレームワーク
MortarevanMason類似のフレームワーク
NarfP. May & T. ClarkeUnitTest重視のcgiライブラリ
NoraMoonwolfcgiを置き換えるライブラリ
RadicalIdan SoferHTTPサーバーを含むpure Rubyフレームワーク
RailsDavid H. HanssonMVC(ActionPack)とDB(ActiveRecord)で実現
RoachAaron Barnettjspならぬrspを利用するAPサーバー
Ruby.APPStephan SchmidtJ2EEやZopeを意識したフレームワーク
SWSMarek JanukowiczWebObjects類似のフレームワーク
WAFBryan ZarnettJakarta Struts類似のフレームワーク
WakabaUENO KatsuhiroHTTPリクエストをメソッド呼び出しとみなす
cgi-applicationMoonwolfcgiとcgi/sessionを使ったフレームワーク
div関将俊dRubyを使ったフレームワーク
rwebiGELcgiを置き換えるライブラリ
webapp田中哲CGI/FastCGI/mod_ruby/WEBrickを利用可能
+
+

表42.3のプロジェクトを大きく分類すると、以下のようになるようです。

+
+

標準添付のcgiライブラリを置き換えるもの

+

標準添付のcgiライブラリに不満を持つ人は多いようです。確かにPerlのCGIをベースに設計されたcgiライブラリはちょっと古い印象があります。もっと使いやすいライブラリをというニーズは多いのでしょうか。Narf, Nora, Rwebなどがこれに当たります。

+

J2EEやWebObjectsなどJava系技術の移転

+

Web系技術の開発が一番盛んなのはやはりJava周辺かもしれません。Java周辺で開発された技術をRubyに移転するプロジェクトもたくさんあります。CGIKit(WebObjects)、Cerise(J2EE)、SWS(WebObjects)、WAF(Struts)、Roach(JSP)などがこれに当たります。

+

Java系ではありませんが、PerlのWebアプリケーションフレームワークであるMasonを移植したMortarも方針は類似しているでしょう。

+ +

比較的小規模なアイデア勝

+

アプリケーションを1つのオブジェクトで実現するWakaba, dRubyを使ったdiv、CGI/FastCGI/mod_ruby/WEBrickをほとんどコード変更なしで移行できるwebapp, 標準添付のcgiを利用することでわずか200行強で実装されているcgi-applicationなどはこのあたりに分類されるでしょう。

+

プロダクションレベルを狙うもの

+

Iowa, Railsなどは他からの影響を受けつつもある程度独自路線をとっているようです。これらのフレームワークはショッピングサイトの構築など実アプリケーションに使われています。

+

その他

+

mod_rubyをターゲットにしたArrow, 継続(Continuation)を使ったBorges, pure Rubyを売り物にしているRadical, J2EEなども意識したRuby.APPなどは、まだ十分な情報を入手していません。これから流行するかもしれないので、注目していきたいです。

+

特にBorgesは、Iowa(表42.3で紹介されたものの初期のバージョン)が、SmalltalkのSeasideというフレームワークに移植され、それ(の一部)を再びRubyで実装したというなかなか奇妙な経緯を持つフレームワークです。継続ベースのWebアプリケーションフレームワークは性能を向上させるのが難しいのですが、面白いアイデアではあります。Ruby以外だと、Schemeの継続ベースWebアプリケーションフレームワークKahuaは経済産業省の未踏プロジェクトの対象になっていましたね。

+
+

「こんなになくてもいいじゃないか」という気分にもなりますが、やはり思想の違いを反映しているのでしょう。「Webアプリケーションのあるべき姿」についての考えがそれぞれ違う以上、簡単には結論は出ないようです。ただ、最近の流行を見ていると、

+
    +
  • MVC(モデル/ビュー/コントローラー)の分離

  • +
  • ビューにはテンプレートを使う

  • +
+

ことが有望だと考えられているようです。あとは性能、データベースアクセス周り、ロジック部の書きやすさなどが判断基準になるようです。

+
+
+

まとめ

+
+

今月はHTTPのセッションについて学んだ後、RubyのWebアプリケーションフレームワークについて眺めてみました。

+

しかし、RAAに登録されたものだけで19個となると、「どれを使ったらいいのか」という気分になりますね。もちろん、個人個人のニーズに合ったものを選ぶのが一番なので、来月以降これらのフレームワークのいくつかを紹介しようと思っています。

+

先月、今月とほとんどRubyのプログラムが登場しませんでしたので、来月はRubyプログラムを作れるといいなと思っています。

+

なお、Webアプリケーションフレームワークの調査には、同僚であるかずひこさんのご協力をいただきました。どうもありがとうございます。

+
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-025.xhtml b/docs/vol2/xhtml/p-025.xhtml new file mode 100644 index 0000000..0c094ac --- /dev/null +++ b/docs/vol2/xhtml/p-025.xhtml @@ -0,0 +1,86 @@ + + + + + +第42章 Webアプリケーションの基礎 + + + + +

Matz Essays Volume 2

+ + +
+ +

◆ Ruby開発日記 ◆ 汝は人狼なりや?

+
+

私は普段ゲームと呼ばれるものはほとんどしません。うちにはゲーム機はありませんし、アクションゲームははっきり言ってかなり苦手ですし(反射神経が鈍い)、パズルもあんまり難しいのは投げ出してしまいます。また、コンピュータのロールプレイングゲームは、あの経験値稼ぎの非人間的な繰り返しに耐えられません。しかし、そんな私でもカードゲームやパーティゲームは結構好きで、「ウォーターワークス(水道管ゲーム)」や「UNO」などはずいぶん遊んだものです。

+

最近、うちの家族はアメリカ土産の「Pandamonium」というゲームで遊んでいます。これはめくったカード(楽器を演奏するパンダ柄)に対応するポーズを素早くとり、間違えた人は場に出ているカードを総取りする、というゲームです。マリンバとドラム、チューバとホルンなど似たような楽器があって混乱します。

+

さて、そのようなカードゲームにあって、面白そうだけど遊べなかったゲームというのが「汝は人狼なりや?」または「ミラーズホロウの人狼」と呼ばれるカードゲームです。「汝は人狼なりや?」は村人の中に紛れ込んだ人狼を退治するため、占いなどの特殊能力と推理力を駆使して、狼を見つけ出し処刑する、というカードゲームです。無実の人を処刑してしまうことがあるところとかがちょっとブラックですが、推理ゲームとしてよくできていると思います。何でも2003年ドイツ年間ゲーム大賞ノミネートなのだそうです。

+

遊ぶのが難しいというのは、入手が難しいこともあるのですが、それ以上に難点となるのが、参加人数です。このゲームには最低8人のプレイヤーが必要なのですが、なかなか8人集めるのは大変です。また、推理力を必要とする知的なゲームなので、うちの子供たちのような「小さいお子様」は参加できません。私の周りで大人ばかり8人、子供にじゃまされない状態で集めるのはちょっと困難そうです。それはそれでしょうがないなあと思っていたのですが、ある日面白いものを見つけました。

+

その日はいつものようにWebを眺めていたのですが、「人狼BBS」というページに遭遇しました。

+
    +
  • 人狼BBS
    +http://ninjinix.x0.com/wolf/index.rb

  • +
+

これこそ「汝は人狼なりや?」をオンラインで実現するものでした。インターネットを経由して、見知らぬ同士が参加するわけです。本物の「汝は人狼なりや?」では夜のフェーズにはみんなが目を閉じる必要があるのですが、Webで行うならば必要な人に情報を必要なだけ見せることができます。人狼同士の相談も誰にも気付かれずに行うことができます。考えてみれば、これこそインターネット向けのゲームではないでしょうか。すばらしい。

+

などと一人で感動していたのですが、冷静になってURLをよく見ると「index.rb」なんて文字列が含まれているではないですか。なんと、人狼BBSはRuby製だったのです。喜ばしいことです。

+

さて、「汝は人狼なりや?」も「人狼BBS」もご存じない方のためにゲームのルールを簡単に紹介しておきます。

+

基本は、掲示板形式で会話を進めながら、人狼を探し出し処刑することです。すべての人狼を処刑できれば村人たちの勝ち、村人の数が人狼と同数以下にまで減らされてしまったら人狼の勝ちとなります。誰を処刑するかは、生き残っている村人たちの投票によって決定します。

+
+

村人の中には特殊能力を持つ者がいます(表42.4)。これらの能力と推理力を駆使して人狼を特定します。しかし、人狼たちや狂人(人狼に味方する人間)が特殊能力者になりすますかもしれません。

+
+

表42.4●「汝は人狼なりや?」の特殊能力

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
名称能力
村人ありません
人狼毎夜、1人だけ村人を殺害できます。人狼同士にしか聞こえない会話が可能です。14人までは2匹、15人以上では3匹です
占い師毎夜、村人1人を占うことができます。その村人が人狼か人間かがわかります
霊能者投票による処刑や突然死で死んだ者が、人狼であったか人間であったかがわかります
狂人人狼側の人間です。人狼の勝利が狂人の勝利となります。狂人と人狼は、お互いに正体を知りません
狩人毎夜、1人を人狼の襲撃から守ることができます
共有者もう1人の共有者を知ることができます
+
+

もちろん人間は誰も死なず、人狼だけが退治されればそれに越したことはないのですが、人狼側も知恵を振り絞るので、一方的な勝利はほぼ不可能です。何人か人間を犠牲にしても確実に人狼を退治する非情さが求められます。人狼側も味方を犠牲にしても最終的な勝利を目指す点では同じです。限られた情報を最大限に生かし、全力を尽くしてそれぞれの目的の達成を目指すのが、このゲームの面白さの本質です。

+

人狼BBSでは過去のゲームのログも公開されています。このログも、人間が語った言葉だけ(プレイヤーと同等)レベル、人狼の秘密の会話やすでに死んでしまった人のツッコミの声が聞こえるレベルなど、いろいろなレベルで読むことができます。

+

すべての会話が読めるレベルは、それぞれのプレイヤーがそれぞれの勝利を目指して必死に思考する過程がわかる1つの読み物として読むことができます。推理や戦略、陰謀と裏切りを含む優れた読み物です。また、ときには善意が周囲から誤解され、人間側が自滅していく悲劇のドラマが展開されることもあります。

+

人間の声だけが聞こえるレベルでは、プレイヤーと一緒になって推理を楽しむことができます。プレイヤーの発言を読みながら、誰が人狼か、誰が狂人か、どの発言がどういう意味があるのか、などと考えながら読むのは、もしかすると下手な推理小説よりも面白いかもしれません。一粒で二度おいしいとはこのことでしょうか。私はここ数週間、人狼BBSのログを読むのにはまってしまって、仕事が滞って大変でした。あやうく今月の原稿にもさしつかえるところでした。危なかった。

+

とまあ、ここまで、えらそうに人狼BBSについて書いてきましたが、実はログを読むばかりで、まだプレイヤーとして参加したことはないのです。この原稿を仕上げて、仕事も一段落したら、こっそり参加してみようかな。ログを読む以上にはまってしまって、大変なことになったらどうしよう。

+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-026.xhtml b/docs/vol2/xhtml/p-026.xhtml new file mode 100644 index 0000000..edd130d --- /dev/null +++ b/docs/vol2/xhtml/p-026.xhtml @@ -0,0 +1,509 @@ + + + + + +第43章 Webアプリケーションフレームワーク + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay43 +
+

+探訪Ruby
+Webアプリケーション
フレームワーク +

+
+

[Linux magazine, 2004年12月号]

+
+

先月までのWebアプリケーションの基礎に引き続き、Webアプリケーションフレームワークについて解説しています。ちょうどこの原稿が書かれた2004年後半はRubyに限らず、各種言語でWebアプリケーションフレームワークが数多く登場した時期です。そのせいもあって、リストには17個ものフレームワークが紹介されています。今回の原稿で題材として取り上げたのは、CGIKitというフレームワークで、当時の評価は大変高かったのですが、いつの間にか消えてしまいましたね。ちなみに原稿で取り上げたCGIKit 1.2.1は現在でもダウンロードは可能でした。2024年現在ではRails一強で、たまにSinatraやHanamiも話題に登るといった状況でしょうか。

+

「Ruby開発日記」は「Ruby Conference 2004レポート」です。ちょうどこのとき、末娘が誕生するタイミングで、コロナ禍になるまでRubyConfを欠席したのはこの年だけでした。ちょうどDHHのRuby on Railsの紹介をしていますね。笹田くんがYARVの発表したのもこの年でした。

+
+
+

先月はWebアプリケーションの基礎になる「セッション」について学び、Webアプリケーションフレームワークを簡単に紹介しました。今月は、フレームワークの実際を見てみようと思います。

+
+
+

Webアプリケーションフレームワーク

+
+

先月も紹介したRubyによるWebアプリケーションフレームワークのリストを表43.1に示します。先月のリストから厳密には「フレームワーク」とは呼べないものを取り除き、新しくRosieを追加しました。

+ +
+

表43.1●RubyのWebアプリケーションフレームワーク

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
プロジェクト作者説明
ArrowMichael GrangerApacheを対象にしたフレームワーク
BorgesEric Hodel継続でセッションを実現する
CGIKit鈴木鉄也WebObjects類似のフレームワーク
CerisewglozerJ2EE類似のフレームワーク(amritaを利用)
IowaKirk Hainesコンポーネントベースフレームワーク
MortarevanMason類似のフレームワーク
RadicalIdan SoferHTTPサーバーを含むpure Rubyフレームワーク
RailsDavid H. HanssonMVC(ActionPack)とDB(ActiveRecord)で実現
RoachAaron Barnettjspならぬrspを利用するAPサーバー
Rosietwelvesoft.com独自テンプレートのシンプルフレームワーク
Ruby.APPStephan SchmidtJ2EEやZopeを意識したフレームワーク
SWSMarek JanukowiczWebObjects類似のフレームワーク
WAFBryan ZarnettJakarta Struts類似のフレームワーク
WakabaUENO KatsuhiroHTTPリクエストをメソッド呼び出しとみなす
cgi-applicationMoonwolfcgiとcgi/sessionを使ったフレームワーク
div関将俊dRubyを使ったフレームワーク
webapp田中哲CGI/FastCGI/mod_ruby/WEBrickを利用可能
+
+

WebアプリケーションフレームワークはWebアプリケーションの「枠組み」を提供し、最小限の記述でWebアプリケーションを記述できます。

+
+
+

フレームワークの機能

+
+

Webを使ったアプリケーションを実現するための基本的な技術は本連載の10月号で解説したCGIです。しかし、CGIだけを使ってある程度以上のWebアプリケーションを作るのは大変です。そこでCGIのような低レベルなインターフェイスの上にもっと使いやすいライブラリを用意し、さらにWebアプリケーションの枠組みまで提供するのがWebアプリケーションフレームワークになります。

+

Webアプリケーションフレームワークでは以下のような機能が提供されます(すべてのフレームワークが以下のすべてを提供しているとは限りません)。

+
+

セッション管理機能

+

1つのページでおしまいというようなCGIプログラムならともかく、Webアプリケーションは複数のページが連携され、一連の流れとして取り扱われる必要があります。Web上でそのような「セッション」を実現する方法については前回で紹介しましたが、はっきりいって面倒です。

+

ユーザー管理機能

+

たとえば会員制のサイトの場合、ログインした場合には会員個人のページを表示するなど、ユーザーごとに個別化した対応を求められることがあります。いくつかのWebアプリケーションフレームワークでは、このユーザーごとの処理も支援しています。

+ +

ビューとロジックの分離

+

Webアプリケーションのビュー(外見)とロジック(処理)を分離することにはメリットがあります。プログラムの中にHTMLの断片がちりばめられていると、デザインを変更したいときにはプログラムのあちこちを修正する必要があります。

+

それでなくても、HTMLエディタのようなツールを使ってきれいにデザインしたい、あるいはHTMLは専門のデザイナーに任せたい、というニーズがあるので、HTMLは1ファイルにまとまっていたほうがうれしいものです。ですから、ほとんどのWebアプリケーションフレームワークは、何らかの手段でビューとロジックの分離を支援しています。

+

コンポーネント

+

Webアプリケーションの複数のページで「部品」を共有したいというニーズがあります。たとえば、画面の片隅にカレンダーを出したいとか、ヘッダーには共通メニューを置きたいとか。そのようなニーズに応えるための仕掛けがコンポーネントです。コンポーネントはページ全体ではなく、その一部となるような「部品」です。

+

ページ遷移

+

Webアプリケーションはその実行に伴って、ページからページに移動します。その基本的な処理は、

+
    +
  • ページを表示する

  • +
  • ユーザーからの入力を受け取る

  • +
  • 入力に応じて処理を行う

  • +
  • 新しいページを表示する

  • +
+

という流れです。CGIでプログラムを書く場合には、各ページごとにこれら1つ1つを別々のCGIプログラムで実現する必要があります。Webアプリケーションフレームワークは、これを各ページを定義することで行います。多くのフレームワークはページはオブジェクトに対応し、処理はメソッドに対応します。

+
+

今月は数あるWebアプリケーションフレームワークの中からCGIKitを紹介します。

+
+
+

CGIKit

+
+

CGIKitは鈴木鉄也さん(スパイスオブライフ)によって開発されているWebアプリケーションフレームワークです。

+
    +
  • CGIKitホームページ
    +http://www.spice-of-life.net/cgikit/index.html

  • +
+

最近、私の会社では社内で開発するWebアプリケーションのためのRubyによる各種フレームワークの評価を行ったのですが、その中で最も評価が高かったのが、このCGIKitでした。

+

CGIKitの特徴には以下のようなものがあります(ホームページから引用)。

+
+
+
+

CGIKitのインストール

+
+

CGIKitはWebページからダウンロードできます。ライセンスはRubyのものと同じです。原稿執筆時点での最新版は1.2.1でした

+
    +
  • http://www.spice-of-life.net/archive/cgikit-1.2.1.tar.gz

  • +
+

ダウンロードしたtar.gzファイルを展開します。展開したディレクトリ(例: cgikit-1.2.1)に移動し、下記の手順でインストールスクリプトを実行します。

+
+
% ruby install.rb config
+% su
+# ruby install.rb install
+
+

これでインストールは完了です。

+
+
+ +

CGIKitによるWebアプリケーション

+
+

CGIKitを使ったWebアプリケーションを実現するCGIプログラムは簡単です(リスト43.1)。なんと5行しかありません。CGIKitによるWebアプリケーションの本質はコンポーネントにあるので、起動する部分はこれだけで済むのです。どんなに複雑なWebアプリケーションでもおおむね同じCGIプログラムで対応できます。

+
+

リスト43.1●CGIKitを使ったCGIプログラム

+
#!/usr/bin/ruby
+require 'cgikit'
+
+app = CKApplication.new
+app.run
+
+
+

このCGIプログラムをWebサーバーから見える位置に置きます。CGIプログラムとして起動できるように、パーミッションの設定や、Webサーバーの設定を行うのを忘れてはいけません。

+
+
+

CGIKitのコンポーネント

+
+

Webアプリケーションの本質、コンポーネントはこのCGIプログラムが置かれているディレクトリに、コンポーネントごとに別々のディレクトリに置きます。

+

CGIKitではWebアプリケーションの各ページもコンポーネントです。CGIKitは起動されるとデフォルトではMainPageコンポーネントを表示します。コンポーネントを構成するディレクトリには3つのファイルが必要です。MainPageコンポーネントの場合、

+
    +
  • MainPage/        ディレクトリ
  • +
  •    MainPage.html ビュー(HTMLファイル)
  • +
  •    MainPage.ckd  バインディングファイル
  • +
  •    MainPage.rb   ロジック(Rubyプログラム)
  • +
+

という構成になります。ディレクトリ名と同じ名前で拡張子が異なるファイルが3つ、これが基本です。

+

ビューは通常のHTMLファイルです。ただし、ロジックの内容が反映される箇所が <cgikit> タグで示されます。MainPage.htmlの例をリスト43.2に示します。

+
+

リスト43.2●MainPage.html

+
<html>
+  <head>
+    <title>CGIKit Sample - input</title>
+  </head>
+  <body>
+    <cgikit name="ErrorCondition">
+      <p><cgikit name="Error" /></p>
+    </cgikit>
+    <cgikit name="Form">
+      <p>名前: <cgikit name="Name" /></p>
+      <p>性別: <cgikit name="Female" />女性 / <cgikit name="Male" />男性</p>
+      <cgikit name="Submit" />
+    </cgikit>
+  </body>
+</html>
+
+
+ +

HTMLファイル中の <cgikit> タグは「エレメント」と呼ばれて置換の対象になります。エレメントの名前はname属性で決定されます。リスト43.2にはErrorCondition, Error, Form, Name, Female, Male, Submitの各エレメントが使われています。実際に出力されるときには各エレメントがHTMLに置換されるわけです。

+

各エレメントがどのようなものであるかは「バインディングファイル」で決定されます。バインディングファイルの例をリスト43.3に示します。

+
+

リスト43.3●MainPage.ckd

+
ErrorCondition : CKConditional {
+  condition = error;
+}
+
+Error : CKString {
+  value = error;
+}
+
+Form : CKForm {
+}
+
+Name : CKTextField {
+  value = name;
+}
+
+Female : CKRadioButton {
+  name = "sex";
+  value = "女性";
+  selection = sex;
+}
+
+Male : CKRadioButton {
+  name = "sex";
+  value = "男性";
+  selection = sex;
+}
+
+Submit : CKSubmitButton {
+  action = check;
+}
+
+
+ +

バインディングファイルでは各エレメントの種別と属性について記述します。バインディングファイルの記述は以下のようになっています。

+
+
エレメント名 : エレメント種別 {
+  属性名 = 属性値;
+  ...
+}
+
+
+

エレメントの種別を表43.2に示します。表を見ればわかるように、コンポーネントはエレメントの一種なので、コンポーネントをコンポーネントに埋め込むことができます。

+
+

表43.2●エレメント種別

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
エレメント概要
CKStringバインディングしたアクションの結果を表示する
CKHyperlink他コンポーネントやメソッドにリンクを張る
CKImageリソースディレクトリ内の画像を表示する
CKConditional設定した条件の結果によってHTMLを表示する
CKRepetition指定した範囲の内容を繰り返す
CKFormフォームを用意する。送信データはそれぞれエレメントにバインディングした変数に代入される
CKTextFieldテキストフィールドを表示する
CKRadioButtonラジオボタンを表示する
CKCheckboxチェックボックスを表示する
CKPopUpButtonポップアップボタンを表示する
CKTextテキストエリアを表示する
CKBrowser複数選択可能なリストを表示する
CKFileUploadファイルアップロードフィールドを表示する
CKSubmitButton送信ボタンを表示する
CKResetButtonリセットボタンを表示する
CKFrameフレームにコンポーネントを設定する
CKComponentコンポーネント内に別のコンポーネントを設定する
CKContentネスティングしたコンポーネントにて、親コンポーネントを表示する
CKGenericElement一般的なHTMLタグを生成する
+
+

value属性に指定するのはメソッドまたはインスタンス変数です。コンポーネントのオブジェクトに、指定した名前のメソッドが存在しなければ、名前の先頭に「@」を付けたインスタンス変数の値をvalueにします。

+

action属性に指定するのはメソッドです。そのメソッドを実行した結果として次のページが決定されます。

+

さて、ロジック部を記述するMainPage.rbリスト43.4)は、ごく普通のRubyプログラムで、以下の点に注意します。

+
    +
  • コンポーネント名と同名のクラスを定義する

  • +
  • CKComponentを継承する

  • +
  • valueactionに指定したメソッドを定義する

  • +
  • actionに指定したメソッドは「次のページ」を返す

  • +
+
+

リスト43.4●MainPage.rb

+
class MainPage < CKComponent
+  def check
+    if @name.empty?
+      @error = '名前を入力してください'
+      return
+    elsif @sex.nil?
+      @error = '性別を選択してください'
+      return
+    end
+    nextpage = page('DisplayPage')
+    nextpage.name = @name
+    nextpage.sex = @sex
+    return nextpage
+  end
+end
+
+
+

「次のページ」を意味するオブジェクトは、

+
+
page(コンポーネント名)
+
+

で生成します。必要なら「次のページ」オブジェクトに対してメソッドを呼び出し、値の設定などを行います。単に「return」すると同じページをもう一度表示することになります。「処理を行って、次のページに遷移する」というWebアプリケーションで最も重要な点が非常に簡単に実現できていることに注目してください。

+ +

「次のページ」に当たる「DisplayPage」コンポーネントをリスト43.5に示します。これはMainPageで入力された名前と性別を表示するだけのコンポーネントです。MainPageと同様に、DisplayPageディレクトリを作成し、その中にリスト43.5の3つのファイルを格納します

+
+

リスト43.5●DisplayPageコンポーネント

+
ビュー DisplayPage.html
+<html>
+  <head>
+    <title>CGIKit Sample - display</title>
+  </head>
+  <body>
+    <p>名前: <cgikit name="Name" /></p>
+    <p>性別: <cgikit name="Sex" /></p>
+  </body>
+</html>
+
+バインディング DisplayPage.ckd
+Name : CKString {
+  value = name;
+}
+
+Sex : CKString {
+  value = sex;
+}
+
+ロジック DisplayPage.rb
+class DisplayPage < CKComponent
+  attr_accessor :name, :sex
+end
+
+
+

この例題は非常に簡単なWebアプリケーションで、最初のページで入力された名前と性別を次のページに表示します。これを実行した結果を図43.1図43.2に示します。

+
+ +
+ fig4301 +
+

図43.1●MainPage

+
+
+ +
+ fig4302 +
+

図43.2●DisplayPage

+
+

入力にミスがあった場合には、最初のページに戻りますが、エラーメッセージを表示します。図43.3が名前を入力しなかった場合、図43.4が性別を入力しなかった場合のメッセージです。

+
+ +
+ fig4303 +
+

図43.3●エラーメッセージ1

+
+
+ +
+ fig4304 +
+

図43.4●エラーメッセージ2

+
+

MainPage.rbでインスタンス変数 @errorに設定したメッセージが、ビューのErrorエレメントによって表示されていいます。エラーメッセージを追加するだけなら、わざわざ別のページを用意する必要はありません。

+
+
+ +

CGIKitの評価

+
+

Webアプリケーションフレームワークの代表として紹介したCGIKitですが、実際に使ってみるとコンポーネントベースのアプリケーション設計は、思った以上に使いやすいし、再利用性も高そうです。

+

ただ、不満もないわけではありません。ビューとして用いるHTMLで用いられる <cgikit> タグはHTMLエディタとの相性があまりよくありません。amritaのようなHTMLとして正当なテンプレートか、あるいはいっそRosieで用いられている「#[]#」のようなHTMLのタグとは関係のない記号を用いたほうがデザイナーとプログラマーの分業には有効だったかもしれません。

+

あと、これは不満ということではありませんが、私にはバインディングファイルの有効性がよくわかりませんでした。エレメントの種別と属性くらいならテンプレートに直接書いてもよいような気がしましたし、validate(値の正当性チェック)のようなものはむしろロジックの一部として記述したほうがよいように思いました。

+

もっとも、私はまだほんの小さなサンプルプログラムしかいじっていませんから、もっと大きなWebアプリケーションでたくさんのコンポーネントを利用する場合にはバインディングファイルが有効になるのかもしれません。

+
+
+ +

まとめ

+
+

今月はWebアプリケーションフレームワークの代表としてCGIKitを紹介しました。フレームワークを使うとWebアプリケーションの開発が飛躍的に簡単になります。

+

今回紹介したCGIKitにはロジック部のデータベースマッピングを支援するTapKitと呼ばれる姉妹品があります。こちらはSQLを書くことなくRDBを操作できるライブラリで、WebObjectのEnterprise Objects Frameworkを参考に作られているそうです。こちらも近いうちに紹介できたらと考えています。

+

なお、CGIKitのサンプル作成など、今月も同僚であるかずひこさんのご協力をいただきました。どうもありがとうございます。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-027.xhtml b/docs/vol2/xhtml/p-027.xhtml new file mode 100644 index 0000000..fdb23c5 --- /dev/null +++ b/docs/vol2/xhtml/p-027.xhtml @@ -0,0 +1,95 @@ + + + + + +第43章 Webアプリケーションフレームワーク + + + + +

Matz Essays Volume 2

+ + +
+ +

◆ Ruby開発日記 ◆ (行ってないのに)Ruby Conference 2004レポート

+
+

10月1日から3日まで、バージニア州ワシントンDCエリアにてRuby Confernce 2004が開催されました。今年は4番目の子供の出産と重なったので残念ながら出席できませんでしたが、今年の会場は無線LANが使えたので、会場からのレポートが数多く提供されました。

+

今回は、それらのレポートを元にして「行ってないのに勝手にカンファレンスレポート」をお送りします。

+
+

10月1日(金)

+

Teaching Ruby in a Corporate Environment(Jim Freeze)

+

ISO9000企業でRubyのような「新参ソフト」を活用するというのがテーマ。Jimは毎年似たようなテーマで努力と成果を報告してくれています。

+


+

Using and Extending Ruwiki(Austin Ziegler)

+

JavaプログラムとそれをコントロールするRubyプログラムの例。RubyをDSLとしても利用しているとそうです。

+

Ruwikiについて。Ruwikiは海外では結構人気のあるWikiクローンです。カスタマイズ(マークアップのカスタマイズ、マークアップエンジンの変更、ストレージバックエンドの変更など)が簡単だそうです。

+


+

Tycho: A Proposed Ruby-based PIM(Hal Fulton)

+

RubyによるPIM(Personal Information Manager)の実装。

+


+

Hacking Ruby(Paul Brannon)

+

Rubyをハックする。いろいろやってくれてるようですが、オブジェクトをすり替えるbecomeだけは邪悪すぎるので勘弁してください。

+


+

Alph(Rich Kilmer)

+

AlphはMacromedia Flashをフロントエンドとするユーザーインターフェイス。Flashならではの美しい画面をRubyからコントロールできます。

+
+
+

10月2日(土)

+

Narf: revisiting a 2 year old(Patrick May)

+

NarfはRuby標準添付のcgi.rbの置き換えを狙うもの。興味深いのは彼が紹介したWikiへのいたずらを防止する仕組みで、悪質なIPアドレスからのアクセスは「コピー」にリダイレクトされ、いたずらやスパムなどの変更は他には見えない、というものです。これでもうスパムもいたずらも恐くない?

+


+
+

ruby-doc.org: Now and the Future(James Britt)

+

Rubyのドキュメントを集めたruby-doc.orgについて。特に集められたドキュメントのカテゴリ分けなどについて。Jamesはこの後、ヨーロッパのRubyカンファレンス(Euroko)にも参加するそうです。

+


+

Ruby on Rails(David Heinemeier Hansson)

+

最近売り出し中のWebアプリケーションフレームワークRuby on Railsについて。Rubyのすばらしさを発見したDavid(元PHPプログラマー)は、それを大衆に広めるためのツールとしてRailsを開発したそうです。

+


+

The Many Facets of RubyGems(Jim Weirich)

+

Ruby向けパッケージングシステムRubyGemsについて紹介。RubyGemsはそのうち標準になる、かもしれません。

+


+

YARV: Yet Another Ruby VM(SASADA Koichi)

+

Rubyの高速化の試み。「言葉の壁」にもかかわらずかなり高い評価を得たようです。よく頑張りました。ベンチマークも紹介され、それなりに速くなってる(というか、今のRubyが遅いんだけど)ことが示されました。

+


+

"Test::Unit".downcase.sub(/::/,"/")(Nathaniel Talbott)

+

Test::Unitの作者であるNathaniel Talbottが「よりRubyっぽい」テストスイートについて語る。現在「test/unit2」として開発中だとか。

+


+

RubyZine(Shashank Date)

+

飛び入り。Rubyに関するWebzineの発行が決定した、というお知らせ。

+


+

Objective-C: A Retrospective(Brad Cox)

+

Objective-Cの設計者であるBrad Coxによるプレゼンテーション。聞きたかった。ところで、彼の話の後半は、彼の最近の活動であるDRM(Digital Rights Management)関係の話だったようです。ちょっと意外。

+
+
+

10月3日(日)

+

RubyX(John Knight)

+

RubyXはあらゆるスクリプトがRubyで書かれたLinuxディストリビューション。興味深い試みです。そこまで徹底するのがすごい。

+


+

Ruby on Windows(Dan Berger)

+

RubyはWindowsにおいて弱い(私のせいだ)。その点を改善するためのwin32utilプロジェクトについて。

+


+
+

How Dynamic Can You Get?(Jamis Buck)

+

Dependency Injection(またはInvesion of Control)の紹介。Ruby用CコンテナCoplandが題材になっていたそうです。

+


+

Code generation with Ruby in a heterogenous network application(Gorden James Miller)

+

「Rubyはコード生成ツールとして非常に有効だ」という話。目には見えなくてもRubyはあちこちで活躍しています。

+
+
+

まとめ

+

レポートからの印象からは、今年のカンファレンスも成功だったようです。来年は行きたいなあ。10月の前半は外してくれるといいんだがなあ。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-028.xhtml b/docs/vol2/xhtml/p-028.xhtml new file mode 100644 index 0000000..1ec33d2 --- /dev/null +++ b/docs/vol2/xhtml/p-028.xhtml @@ -0,0 +1,468 @@ + + + + + +第44章 マークアップ・マークダウン + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay44 +
+

+探訪Ruby
+マークアップ・マークダウン +

+
+

[Linux magazine, 2005年1月号]

+
+

シンプルなマークアップ言語の良さを力説した後、実例として、RD, Textile, Markdownの3つを紹介しています。Markdownは現在非常に広く使われていますが、残りの2つはあまり見なくなりましたね。ただし、私が書く原稿は今でもRD(のサブセット)で書いていますし、プレゼン用スライドもRDで書いてRabbitで表示させていますから、私にとってはRDはまだまだ現役ですね。

+

「Ruby開発日記」は「名前重要」です。内容は私があちこちで述べていることで、たとえばオライリーの『プログラマが知るべき97のこと』(2010年)という本に書いたエッセイとだいぶ重複していますが、時期的にこちらがオリジナルですね。後半、末娘の名付けに悩む心情はなかなかリアルです。20年前にそんなに悩んでいたとはすっかり忘れていました。

+
+
+

今月はWebからちょっと離れて、ドキュメント記述などに使われるマークアップ言語について探検します。

+
+
+

私の嫌いなもの

+
+

ただ単に文章を書くだけならプレーンテキストで十分ですが、プレーンテキストではカバーしきれない要求、たとえば図を入れたい、きれいにフォーマットしたい、などに応えるためには、何らかのマークアップが必要になります。マークアップのフォーマットにはいろいろあって、代表的なものにはTeX, SGML, HTML, XMLなどがあります。これらは有名ですからこれ以上の説明は不要ですよね。

+

マークアップ言語の目的と意義については理解しているつもりなんですが、これらはそろいもそろって、読むにはマークアップが目ざわりで、書くのはおっくうという欠点があります。TeXの、

+
+
\begin{document}
+  ...
+\end{document}
+
+
+

というのも、たかだか文書の始まりと終わりを指定するのになんでそんなに書かねばならないのか、と不満ですし、HTMLやXMLの、

+
+
<HTML>
+<HEAD>...</HEAD>
+<BODY>...</BODY>
+</HTML>
+
+

のようなのも冗長で好きではありません。「マークアップ(markup)」という単語はもともと「組版指示」というような意味ですから、文章の内容から考えるとさほど重要な要素ではありません。飾りみたいなもんです。それがこんなに目立つ記法を持っているというのは何か間違っているとしか言いようがありません。そんなわけで私は「マークアップ言語」が嫌いです。

+

そんなマークアップ言語嫌いがデザインした文書フォーマットがRD(Ruby Document)です。

+
+
+

RD

+
+

RDは、私たちが『オブジェクト指向スクリプト言語Ruby』(アスキー、ISBN4-7561-3254-5)を執筆していたときにデザインしたマークアップ言語です。RDはPerlのドキュメントに用いられていたPOD(Plain Old Document)フォーマットと、プレーンテキストからTeX文書を生成してくれるPlain2というツールの記法とを組み合わせて生まれました。

+

その特徴は、

+
    +
  • プレーンテキストふうの記法(と若干のインライン記法)だけで文書のマークアップができる

  • +
  • インデントを利用することにより、自然な文章の構造化を行うことができる

  • +
  • rd2プログラムにより、さまざまなフォーマットに変換可能

  • +
+

という点です。

+

RDフォーマットは上記の『オブジェクト指向スクリプト言語Ruby』の執筆に用いられた後、Ruby関連のさまざまなドキュメントに用いられ、今では、

+
    +
  • RWiki  – RDを全面的に採用したWiki
  • +
  • tDiary – スタイルの1つとしてRDを利用可能
  • +
  • Rabbit – RDで記述するプレゼンテーションソフト
  • +
+

などさまざまな局面で用いられています。また、『Ruby 256倍本』の1つ『Rubyを256倍使うための本 魔道編』(アスキー、ISBN4-7561-3747-4)では、RDラブを自称する「るびきち」氏がRDを主要なテーマにして1冊まるまる解説しています。

+
+
+ +

RDの文法

+
+

RDはプレーンテキストに毛が生えたようなフォーマットですから、あまり複雑な構造を表現することはできません。ですが私たちが日常的に書く文章のほとんどはカバーできると思います。

+

RDの基本的な文法は以下のとおりです。

+
    +
  • 空行は段落の区切り

  • +
  • = 見出し」で見出し(<H1>相当)になる。以下、「== 小見出し1」(<H2>)、「== 小見出し2」と続く

  • +
  • *」で箇条書き

  • +
  • (1)」で数字付き箇条書き

  • +
  • :」で定義リスト

  • +
  • インデントした領域は引用

  • +
  • 二重かっこによるインライン。((- 脚注 -))((<リンク>))など

  • +
+

リスト44.1にそれらを使ったRDの例を示します。

+
+

リスト44.1●RDの例

+
= 大見出し - RDの文法
+
+この章ではRDの文法を解説します。
+
+== 段落区切り
+
+RDの文章はプレーンテキストと同様です。
+文章中の改行は特別扱いされず、バックエンド(TeXや
+HTML)にそのまま渡されます。
+
+ただし、空行は段落の区切りとしての意味を持ちます。
+
+== 箇条書き
+
+RDの箇条書きは2種類あります
+
+  * 単なる箇条書き
+    * 箇条書きはネストできます
+  * 数字付き箇条書き
+
+数字付き箇条書きは
+
+  (1) 最初の要素
+  (2) 次の要素
+  (4) かっこ内の数字は無視されます
+
+のように書きます。
+
+用語の定義もできます。
+
+  : 用語
+
+    同じレベルで始まる段落がその定義になります。
+
+また、インデントによって引用できます。
+
+  #! /usr/bin/ruby
+  print "hello world"
+
+以上で簡単な説明は終わりです。
+
+
+
+
+ +

RDtool

+
+

RDを実際に処理するプログラムがRDtoolです。RDtoolはToshさんによって開発されました。現在のメンテナンスはMoonWolfさんによって行われています。RDに関する情報は、

+
    +
  • http://www2.pos.to/~tosh/ruby/rdtool/ja/index.html

  • +
+

から入手できますが、メンテナー交代を反映していないので少々情報が古いようです。原稿執筆時点でのRDtoolの最新版は0.6.16です。

+

Debianではパッケージ化されているので、

+
+
# apt-get install rdtool
+
+

だけでインストールできます。その他のプラットフォームでは、rdtool-0.6.16.tar.gzを展開したディレクトリで、

+
+
# ruby setup.rb
+
+

をroot権限で実行することでインストールできます。Emacsのrd-mode.elを利用したい人はutilsディレクトリにあるrd-mode.elというファイルをEmacs Lispディレクトリにコピーしてください。インストールが完了するとrd2というコマンドが使えるようになります。

+
+
+
# rd2 example.rd > example.html
+
+

のようにするとRDを変換してくれます。デフォルトではHTMLに変換します。

+
+

rd2のバックエンド

+

rd2コマンドの主要な部分はrdライブラリとして分離されていますので、Rubyで書かれた他のプログラムからRDを処理することが簡単にできます。

+

rd2にはHTMLとman形式への変換を行うバックエンドが用意されていますが、自分でバックエンドを用意することで新しい形式へ比較的簡単に対応することができます。RDツールはVisitorパターンを使っているので、バックエンドを記述するためには、

+
    +
  • RD::RDVisitorクラスのサブクラスを用意する

  • +
  • それぞれのマークアップに対応するメソッド(例: apply_to_Headline)を再定義し、目的の形式に変換した文字列を返す

  • +
+

だけです。実際、HTMLバックエンドは(コメントや空行も含めて)487行、man形式対応はわずか247行で実現されています。

+

ここではバックエンドの1つの例として、同僚の前田修吾さんが作ったrd2sxiを紹介しましょう。これはRDから、OpenOffice.orgのプレゼンテーションスライドファイルを生成するプログラムです。

+

rd2sxiは全部で1700行を超えるプログラムなので(出力するXMLのテンプレートが大きいのです)、誌面に収まりませんが、作者の前田さんの許可を得たので、編集部のWebサイト(http://www.ascii.co.jp/linuxmag/update/)に掲載してあります。ダウンロードして、rd2sxi-0.1.tar.gzを展開したら、そのディレクトリのsetup.rbを実行してください

+

なお、rd2sxiはオープンソースGISソフトウェア、GRASS日本語版と一緒に配布されているIPAフォントがインストールされていることを仮定していますから、ご使用になる場合はIPAフォントをインストールする(あるいはrd2sxiを修正する)必要があります。GRASS日本語版については、

+
    +
  • http://www.grass-japan.org/FOSS4G/GRASS/grass-int.html

  • +
+

を参照してください。

+
+
+
+

Rabbit

+
+

RabbitはkouさんによるRDでスライドが記述できるプレゼンテーションソフトウェアです。原稿執筆時点でのRabbitの最新版は0.0.5です。

+
    +
  • http://www.cozmixng.org/~rwiki/?cmd=view;name=Rabbit

  • +
+

rd2sxiがRDテキストからOpenOffice.orgのスライドを作って、画像やテーマなどの編集はOpenOffice.orgに任せているのに対して、RabbitはRDだけでスライドを完結させることができます。画像を入れることもできますし、テーマ機能で見栄えを別に定義することもできます。

+
+

なかなか楽しいソフトウェアですし、実用性も高そうです。

+
+
+

Textile

+
+

目立ちすぎるマークアップは嫌い、という考えは私だけのものではないらしく、RDと似たコンセプトのマークアップは他にもあります。その1つがTextileです。TextileのホームページのURLを以下に示します。

+
    +
  • http://www.textism.com/tools/textile/

  • +
+

Textileの文法については、以下のほうがわかりやすいかもしれません。

+
    +
  • http://hobix.com/textile/

  • +
+

Textileの特徴は以下のとおりです。

+
+

シンプルなマークアップ

+

行をベースにした非常にシンプルなマークアップです。

+

シンプルなインライン

+

RDのインラインは二重かっこ(())を使うので少々うっとうしいのですが、Textileではインライン修飾記法はもっとシンプルです。

+

HTML専用

+

複数のフォーマットに変換できるRDに対して、TextileはHTML専用です。そのぶん、HTML的なスタイルや属性の指定が可能です。右揃え、センタリング、左揃えなどのアラインメントの指定も可能も可能です。また、Textileで表現しきれない複雑なことはHTMLを直接書くことで対応します。

+
+

Textileも空行が段落の区切りを意味します。見出しは行の先頭に「h1.」を置くことで表現します。レベルに合わせて「h1.」から「h6.」までがあります。引用は「bq.」、脚注は「fn1.」、箇条書きはRD同様「*」を用い、数字付き箇条書きは「#」です。

+

その他Textileの文法を表44.1に示します。

+ +
+

表44.1●Textileのスタイル指定

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ブロック修飾子
hn.見出し <Hn>
bq.引用 <BLOCKQUOTE>
fnn.脚注
p.段落 <P>
*箇条書き
#数字付き箇条書き
インライン修飾子
_強調_<EM>
*strong*<STRONG>
??引用??<CITE>
@コード@<CODE>
-削除-<DEL>
+挿入+<INS>
^上付き^<SUP>
~下付き~<SUB>
%span%<SPAN>
テーブル
|a|table|row||で囲む
リンク
"linktext":urlリンク
!imageurl!イメージ
[n]脚注への参照
+
+

先にも述べたように、TextileはHTML専用なのでHTML属性などを指定することができます。ブロック修飾子の場合ピリオドの直前に、クラスやIDを指定するかっこ「()」や、スタイルを指定するブレース「{}」、言語を指定するブラケット「[]」を置くことができます。インライン修飾子の場合には、これらの指定を先頭の文字の直後に置きます。スタイル指定できる場所にはアラインメントも記述できます(表44.2)。

+
+

表44.2●Textileのスタイル指定

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
(class)クラス指定
(#id)ID
{style}スタイル
[lang]言語
<右寄せ
>左寄せ
=センタリング
<>ジャスティファイ
+
+

リスト44.2がTextileの使用例です。

+
+

リスト44.2●Textileの例

+
p(example). A          → <p class="example">A</p>
+p(#big-red). Red       → <p id="big-red">Red</p>
+p{color:blue}. Blue    → <p style="color:blue">Blue</p>
+p[fr]. rouge           → <p lang="fr">rouge</p>
+
+It's %{color:red}red%  → <p>It's <span style="color:red;">red</span>
+p>. Right              → <p style="text-align:right;">Right</p>
+
+
+
+
+ +

Markdown

+
+

もう1つのプレーンテキストふうマークアップがMarkdownです。

+
    +
  • http://daringfireball.net/projects/markdown/syntax

  • +
+

Markdownの特徴はTextileとほぼ同じです。

+
    +
  • シンプルなマークアップ

  • +
  • シンプルなインライン

  • +
  • HTML専用

  • +
+

ただし、TextileのようなCSSクラスやスタイルの指定などはできません。逆に引用などの表現力はMarkdownのほうが高いように感じます。Markdownの文法をリスト44.3に示します。

+
+

リスト44.3●Markdownの文法

+
見出しは下線で
+==============
+
+小見出しも同様
+--------------
+
+# 「#」も見出しに使える
+## 「#」の数でH1からH6まで
+
+> 引用は「>」を行頭に
+> > ネストした引用も可能
+
+* 箇条書き
+
+1. 数字付き箇条書き
+
+コードの引用はRD同様インデントで行う
+
+  #! /usr/bin/ruby
+  print "hello world"
+
+インラインはTextileに似ている。
+
+  * _強調_
+  * *strong*
+  * `コード`
+
+リンクはブラケットを使う
+
+  [リンク](http://www.rubyist.net)
+
+自動的にリンクにすることもできる
+
+  <http://www.rubyist.net/~matz>
+
+
+
+
+ +

RedClothとBlueCloth

+
+

TextileにしてもMarkdownにしても記法だけではあまり意味がなく、実際にHTMLに変換するツールがあって初めて役に立ちます。TextileをHTMLに変換するRuby用のツールはRedClothといいます。Markdown用のツールはBlueClothといいます。なんだかややこしいですが、RedClothが先にあって、BlueClothがその名前を真似たということのようです。

+

RedClothの作者はSyck(YAML for Ruby)の作者でもある「why the lucky stiff」です。RedClothは、

+
    +
  • http://www.whytheluckystiff.net/ruby/redcloth/

  • +
+

から入手することができます。原稿執筆時点でのRedClothの最新版は3.0.0で、このバージョンからTextileとMarkdownの両方をサポートします。インストールするとredclothコマンドによってTextileからHTMLへの変換ができるようになります。

+

また、

+
+
require 'redcloth'
+
+

とすることで、Rubyプログラムから変換を行うこともできます。

+

RedClothオブジェクトはStringクラスのサブクラスです。オブジェクトの生成は以下のように行います。

+
+
r = RedCloth.new("_textile_ text")
+
+

RedClothオブジェクトからHTMLを取り出すにはto_htmlメソッドを使います。

+
+
r.to_html # => "<p><em>textile</em>text</p>"
+
+

to_htmlに引数を指定することで、変換エンジンを変更できます。

+
+
# Markdownとして変換
+r.to_html(:markdown)
+
+# Textileとして変換
+r.to_html(:textile)
+
+# Markdown優先で変換
+r.to_html(:markdown, :textile)
+
+
+

RDtoolは「1つの文法からいろいろな形式に変換する」というアプローチでしたが、RedClothは「複数の文法からHTMLに変換する」というアプローチです。どちらも興味深いですね。

+

BlueClothのほうもRedClothとほとんど同じです。ホームページは、

+
    +
  • http://bluecloth.rubyforge.org/

  • +
+

です。原稿執筆時点での最新版は1.0.0です。BlueClothとRedClothの違いは、

+
    +
  • BlueClothはMarkdown専用

  • +
  • blueclothコマンドは完全なHTMLファイルを出力できる

  • +
+

ことでしょうか。

+
+
+

まとめ

+
+

今月はプレーンテキストふうマークアップ言語を3種類紹介してみました。皆さんもシンプルマークアップの魅力を体験してみませんか。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-029.xhtml b/docs/vol2/xhtml/p-029.xhtml new file mode 100644 index 0000000..ac709a1 --- /dev/null +++ b/docs/vol2/xhtml/p-029.xhtml @@ -0,0 +1,43 @@ + + + + + +第44章 マークアップ・マークダウン + + + + +

Matz Essays Volume 2

+ + +
+ +

◆ Ruby開発日記 ◆ 名前重要

+
+

「名前重要」は私の座右の銘です。

+

プログラミングにおいては名前は重要です。プログラムあるいはその一部に名前を付けることはデザインの重要な一部です。関数名、変数名、クラス名、モジュール名などなどなどプログラムには名前を付ける必要がある存在がいっぱいです。

+

経験的にはよい名前を付けることができれば適切な設計はできたも同然という明らかな傾向があります。プログラムは形のない概念上の存在であることがプログラミングにおける名前の重要性を高めているのかもしれません。適切な名前を付けることができるということは、その存在が果たすべき役割が明確に把握できているということで、それだからよい設計ができるのだと考えられます。実際、Rubyに対する提案のうち、「適切な名前が思い付かない」という理由で保留になったものがかなりたくさんあります。

+

しかし、名前のパワーはそれだけにとどまらないようです。古代インディアンの伝説では、すべてのものには「真実の名前」があり、その名前を知るものはそのものをコントロールすることができるのだそうです。彼らにとっては名前を探求することと適切に名前を付けることは魔術の一部であり、世界を思いどおりにするための手段の1つなのです。この世界観を取り入れたファンタジー小説にアーシュラ・K・ルグィンの『ゲド戦記』がありますね。

+

名前に力があるという考えはインディアンに固有のものではありません。日本でも姓名判断が広く行われていて、名前(の漢字の画数)によって運命が決まると信じている人は多いようです。子供の名前を決めるときに画数を考えて決める人はまだまだ多いようです。

+

人間の名前がその人の運命を決める(かもしれない)ように、プログラムの名前もそのプログラムの運命を決める「何か」があるのかもしれません。世の中にはオープンソースプログラムがそれこそ星の数ほどあります。その中には成功して多くのユーザーから広く使われるものもありますが、どういうわけだかあまり人気を集めることができずに消え去っていくものもあります。技術的な理由だけでは説明できない「何か」がソフトウェアの運命を左右しているような気がしてなりません。最近、私はそれはもしかしたら「名前」なのではないだろうかと怪しんでいるのです。

+

もちろん、人間の運命同様、ソフトウェアの運命が名前だけで決まるというのはナンセンスです。それはもっと複雑で、技術的な理由や、コミュニティの性格、ユーザーの気質、開発者の特質などがからまって決まるものだと思います。しかし、その中で名前も重要な要素のような感じがします。ソフトウェアというものが形のないものであるだけに、名前の重要性はより強まっているように思います。ちょっと考えてみると、メジャーなオープンソースソフトウェアにはみんないい名前が付いています。長すぎず、覚えやすく、発音しやすく、よいイメージがある名前が付いているものがほとんどではないでしょうか。ちょっと考えてみても、「Perl」— 短くて発音しやすい、「Linux」— インパクトがあり、覚えやすい、「Ruby」— 短くて、発音しやすく、美しいイメージがある、ちょっとありふれすぎてるかな、などが考えられます。

+

他のオープンソースプロジェクトのことについては断言できませんが、私がここ10年以上関わってきたRubyに関していえば、もしRubyがRubyという名前でなかったら、これほどまでに広まらなかっただろうと考えています。Rubyという名前が人々をひきつけ、Rubyという名前が開発者である私のモチベーションを維持するのに役立っていたのではないかと考えています。

+
+

これまで私は、Rubyの誕生日は1993年2月23日であるとあちこちで語ってきました。これは、最初のバージョンをリリースした日でも、Rubyのプログラムが初めて動作した日でもなく、実は「Rubyという名前が決まった日」なのでした。この日の時点ではRuby処理系は1行も書かれていませんし、それどころか言語の文法さえ漠然としたもので、はっきりとは定まっていませんでした。にもかかわらず、私がこの日を誕生日とするのは、ソフトウェアにとってそれだけ名前が重要であるということを反映しているからです。名前が付いて、私の頭の中に「Rubyと名付けられた何か」が存在を始めた日こそが、Rubyの誕生日ではないかと思っているのです。この「何か」は誕生後11年以上が過ぎた今日でも少しずつ変化しながら成長しているのです。

+

このように、ソフトウェアプロジェクトにとって名前は重要です。もし皆さんが、何か新しいソフトウェアの開発を開始することがありましたら、プロジェクトの一番初めにきちんと時間をとって名前について考えてみてはどうでしょう。そのわずかの手間が、将来そのプロジェクトの命運を左右することになるかもしれません。

+

さて、普段から「名前重要」と繰り返している私なのですが、先日、大変困ったことが起きました。実は、10月28日に三女が産まれまして。この少子化のご時世に4人目の子供に恵まれるというのは、大変めでたいことなのですが、産まれた子供には名前を付けなければならないのです。しかも、産まれて14日以内に届け出ないと罰金モノなのだそうです。名前が重要で運命を左右するかもしれないと常々感じている私としては大変なプレッシャーです。この子の将来未来永劫にわたって影響を与えかねない名前をわずかな時間の間に決めなければならないというのは大変な責任です。

+

もちろん、妊娠がわかってから実際の出産までには何カ月もあったのですが、締め切りが近くならないとなかなか本気にならないのはいつものことです。いくつかの候補を絞ったものの、実際に産まれてきた子供の顔を見るとイメージが違う気がして絞り込めません。おまけに夫婦それぞれに思い入れがある名前があったりして、なかなか合意に至りません。上の子供たちまで「あんな名前がいい」「こんなのは嫌だ」と口をはさむので大混乱です。今までで一番名付けに苦労したような気がします。

+

産まれて1週間が過ぎ、10日が過ぎ、周りからの「まだ決まらないの」というプレッシャーを跳ね返しつつ、夫婦の間で真剣な話し合いが行われ、やっと決定したのは締め切りの数日前でした。ここでは公表しませんが、シンプルでかわいらしいよい名前が付けられたと自画自賛しています。願わくば本人もこの名前を気に入ってくれるとよいのですが。

+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-030.xhtml b/docs/vol2/xhtml/p-030.xhtml new file mode 100644 index 0000000..37aa70a --- /dev/null +++ b/docs/vol2/xhtml/p-030.xhtml @@ -0,0 +1,364 @@ + + + + + +第45章 ダイコン + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay45 +
+

+探訪Ruby
+ダイコン +

+
+

[Linux magazine, 2005年2月号]

+
+

DI(Dependency Injection)について解説しています。私自身はDIについて否定的で、Rubyのような動的な言語では煩雑になるだけという意見を持っているのですが、この文章を書いた時点では意外と好印象で驚きます。20年の間に意見が変化したのでしょうか。タイトルは「DIコンテナ」から取ったものであると書いてありますが、他では聞いたことない略称なのでたんなるジョークとして理解すべきなんでしょうね。

+

「Ruby開発日記」は「文字コードの憂鬱ゆううつ」です。文字コードについて歴史的事情を含めて解説しています。この辺の悩みがRuby 1.9のM17N(多言語化、Multilingualization)につながるんですね。とはいえ、現在では文字コードはほぼUnicodeの勝利に終わったので、もう悩む必要はないのかもしれません。もっともUnicode自身が絵文字を含めて大変実装が難しいので、そういう意味での悩みは尽きないのですが。

+
+
+

今月は、最近、(Java方面で)人気のDependency Injectionパターンと、それを実現するためのツールであるDIコンテナ、略してダイコンについて紹介します。

+
+
+

Dependency Injection

+
+

「Dependency Injection(依存関係の挿入)」(通称DI)はソフトウェアを構成する単位(コンポーネント: モジュール、クラスなど)の間の依存性をコードから分離して、コンポーネント間の結合を弱め、再構成を可能にするデザインパターンです。DIはもともと「Inversion of Control(制御の反転)」(通称IoC)と呼ばれていました。しかし、これでは何の制御を反転させるのかわからないので、『リファクタリング』のMartin Fowlerらによって付けられた新しい名前が「Dependency Injection」です。

+

DIを言葉だけで説明するのは少々骨が折れるので、ちょっとした例を元に考えてみたいと思います。仮に私がBlogツールを開発しようとしているとしましょう。そのツールの構成はたとえば図45.1のようになるでしょう。

+ +
+ +
+ fig4501 +
+

図45.1●Blogツール構成図

+
+
+

表45.1●各コンポーネントの機能

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Blog全体の管理
InputHTTPリクエストを解析する
DBMSデータを格納、検索する
MarkupテキストデータをHTML化する
Pluginカレンダーなど各種部品
DisplayテンプレートからHTMLを構成する
+
+

旧来のやり方で開発するならば、それぞれのコンポーネント(クラス)は全体を管理するBlogクラスが初期化します。たとえばリスト45.1のような感じでしょうか。

+
+

リスト45.1●従来型初期化

+
class Blog
+  def initialize
+    @input   = Input.new(...)
+    @dbms    = DBMS.new(...)
+    @markup  = Markup.new(...)
+    @plugin  = Plugin.new(...)
+    @display = Display.new(...)
+  end
+end
+...
+
+
+

この場合、このBlogクラスはInput, DBMS, Markup, Plugin, Displayの各クラスと強く結び付いてしまいます。Blogクラスを使うということは自動的にこれらのクラスを使うということになり、プログラムの内部構造に手を入れない限り、これらの組み合わせを変えられなくなってしまっています。たとえば、データを格納するDBMSを交換したくなった場合、あるいはMarkupの文法をTextileからRDに変更したくなった場合、プログラムを(この場合はBlogクラス)を書き換えるか、コンポーネントを置き換えできる仕組みを自分で作り込んでおく必要があります。

+

リスト45.1ではBlogクラスと他のクラスとの関係しか記述していませんが、実際のプログラムでは他のクラス相互も関係を持っていますから、依存関係はより複雑になり、クラスの置き換えの手間は増大するでしょう。また、各コンポーネントが強く連結されていると、コンポーネントごとのユニットテストを行うことが難しくなります。

+ +

結局、問題を引き起こしているのは依存性なのです。Dependency Injectionはそのような依存性を制御するテクニックです。

+
+
+

ハリウッド原則

+
+

このようなDIのことを「ハリウッド原則」に従っているといわれることがあります。「ハリウッド原則」とは「電話をするな。こっちが電話するから(Don’t call us. We’ll call you.)」ということなのだそうです。

+

つまり、映画を作るためにどのようなスタッフを選ぶかはプロデューサーが決定して通知するので、個々のスタッフは個別に他のスタッフやプロデューサーを呼び出してはいけないということです。DIコンテナに応用すると、個別のコンポーネントは他のコンポーネントやコンテナのことを関知する必要はない、というところでしょうか。

+

ハリウッド原則はDIに限らず、一般的にフレームワークと呼ばれるもの全般で有効なテクニックです。ということは、DIコンテナは各種フレームワークの基盤として応用できるツールであるということになります。Javaの世界でDIコンテナが注目されているのも、このあたりが理由のようです。

+
+
+

Constructor Injection

+
+

Martin Fowlerらによれば、依存関係の指定方法にはConstructor Injection, Setter Injection, Interface Injectionの3種類があるのだそうです。

+

Constructor Injectionはコンポーネントのオブジェクトを生成するときのコンストラクタ(Rubyの場合ならnew)の引数として依存するコンポーネントを指定する方法です。Setter Injectionはコンポーネントオブジェクトの生成後にメソッドを使って依存するオブジェクトを指定する方法です。Interface Injectionは(Javaの)interfaceを用いてSetter Injection類似のことを行う手法ですが、interfaceのない(必要ない)Rubyでは用いられません。

+

では、実際にDIコンテナを使った例を見てみましょう(リスト45.2)。このサンプルプログラムで利用しているDI::Containerというクラスは自作のものです。実装は非常に簡単ですから、あとで紹介します。

+
+

リスト45.2●DI的初期化(Constructor Injection風味)

+
class Blog
+  def initialize(input,disp)
+    @input   = input
+    @display = disp
+  end
+end
+...
+
+def start_app
+  c = DI::Container.new
+  c.register(:blog, Blog, :input, :display)
+  c.register(:input, Input)
+  c.register(:dbms, DBMS)
+  c.register(:markup, Markup)
+  c.register(:plugin, Plugin, :dbms)
+  c.register(:display, Display, :dbms, :markup, :plugin)
+  blog = c.instance(:blog)
+  ...
+end
+
+
+ +

registerメソッドの引数の意味は

+
+
サービス名, クラス, 依存するサービス名, ...
+
+

です。

+

Constructor Injectionではコンテナに対してインスタンスが要求されると、コンテナはそのインスタンスがまだ生成されていなければ、そのクラスのnewメソッドを使って生成します。そのとき、newメソッドには依存するサービスのインスタンスが引数として渡されます。

+

リスト45.2Blogクラスの定義を見てください。他のコンポーネントは外から与えられ、Blogクラスの定義中に他のクラス名やそれらのインスタンスの生成方法は登場しません。これによりBlogクラスから他のコンポーネントへの依存性をなくすことができました。これならば、コンポーネントの構成を変更することも簡単ですし、依存するコンポーネントとして他のコンポーネントの「ふり」をするオブジェクトを与えれば、コンポーネントごとにユニットテストを行うこともできるようになりました。

+

ここで用いたDI::Containerの定義をリスト45.3に示します。20行以下ということで、これはおそらく世界最少のDIコンテナ(の1つ)と言ってもよいでしょう。Rubyの記述力には驚くばかりです(自画自賛)。

+
+

リスト45.3●DI::Container(Constructor Injection)

+
module DI
+  class Container
+    def initialize
+      @services = {}
+      @instances = {}
+    end
+    def register(name, component, *dependency)
+      @services[name] = [component, dependency]
+    end
+    def instance(name)
+      return @instances[name] if @instances[name]
+      component, dependency = @services[name]
+      args = dependency.map {|service|
+        self.instance(service)
+      }
+      @instances[name] = component.new(*args)
+    end
+  end
+end
+
+
+ +

DI::Containerが行っていることは簡単で、Rubyが読める人ならコードを見ればすぐわかるでしょう。初期化して(initialize)、サービスを登録して(register)、インスタンスの要求に対してまだ生成していないオブジェクトを生成する(instance)、これがDIのコアのすべてです。

+
+
+

Setter Injection

+
+

Setter Injectionはnewメソッドで依存関係を指定するのではなく、属性指定で依存するコンポーネントを与えます。こちらはコンポーネントインスタンスを生成した後、属性設定メソッド(obj.foo=valのようなもの)を使って依存関係を構築します。Setter Injectionを使ったコンポーネントの定義をリスト45.4に示します。

+
+

リスト45.4●DIコンポーネント(Setter Injection風味)

+
class Blog
+  attr_accessor :input, :display
+  ...
+end
+...
+
+
+

Constructor Injectionのコンポーネントと比べて、initializeでの依存関係の構築がなくなり、attr_accessorだけになっているぶん、定義がずいぶん簡単になっているのがわかります。Setter Injectionを使うDIコンテナの定義をリスト45.5に示します。リスト45.5のDIコンテナはAPIはConstructor Injectionのものと同じにしてありますから、start_appの定義はリスト45.2のものから変更の必要はありません。

+
+

リスト45.5●DI::Container(Setter Injection)

+
module DI
+  class Container
+    def initialize
+      @services = {}
+      @instances = {}
+    end
+    def register(name, component, *dependency)
+      @services[name] = [component, dependency]
+    end
+    def instance(name)
+      return @instances[name] if @instances[name]
+      component, dependency = @services[name]
+      @instances[name] = c = component.new
+      args = dependency.map {|service|
+        c.__send__(service.to_s+"=", self.instance(service))
+      }
+      c
+    end
+  end
+end
+
+
+ +

このバージョンのDI::Containerには依存関係が循環していても動作するという副作用があります。が、Setter Injectionが必ずそうだという保証があるわけではありません。

+
+
+

Block Injection

+
+

Jim Weirichが自らのBlogで紹介したより「Rubyらしい」やり方がBlock Injectionです。あ、Block Injectionは私が名付けたもので、Jim本人はそうは呼んでいません。

+
    +
  • Jim WeirichのBlogの記事
    +http://onestepback.org/index.cgi/Tech/Ruby/DependencyInjectionInRuby.rdoc

  • +
+

Constructor InjectionやSetter Injectionはインスタンスの生成方法が厳密に定められていますが、Block Injectionはブロックを使ってインスタンス生成方法を指定するので、Constructor InjectionにもSetter Injectionにも対応できます。Block Injectionを使った例をリスト45.6に示します。

+
+

リスト45.6●Block Injection

+
def start_app
+  c = DI::Container.new
+  c.define(:blog) {|c| Blog.new(c.input, c.display)}
+  c.define(:input) {Input.new}
+  c.define(:dbms){DBMS.new}
+  c.define(:markup){Markup.new}
+  c.define(:plugin){|c|
+    p = Plugin.new
+    p.dbms = c.dbms
+  }
+  c.define(:display) {|c|
+    Display.new(c.dbms, c.markup, c.plugin)
+  }
+  blog = c.instance(:blog)
+  ...
+end
+
+
+ +

ここでは仮にPluginコンポーネントだけSetter Injectionで定義され、残りはConstructor Injectionであるとしてみました。Block Injectionではインスタンス生成方法をブロックを指定するので、インスタンスを生成するメソッドはnewでなくてもかまいませんし、Constructor InjectionもSetter Injectionも同時に利用できます。ということは、より柔軟性が高いということができます。

+

Block Injectionを行うDIコンテナをリスト45.7に示します。instance_evalなどリフレクションを使ったdefineメソッドの定義がちょっとトリッキーですが、それ以外は非常にシンプルだと思います。

+
+

リスト45.7●DI::Container(Block Injection)

+
module DI
+  class Container
+    def initialize
+      @services = {}
+      @instances = {}
+    end
+    def define(name, &block)
+      @services[name] = block
+      class << self
+        self
+      end.instance_eval {
+        define_method(name) { self.instance(name) }
+      }
+    end
+    def instance(name)
+      return @instances[name] if @instances[name]
+      @instances[name] = @services[name].call(self)
+    end
+  end
+end
+
+
+
+ +

DIコンテナライブラリ

+
+

RAA(http://raa.ruby-lang.org/)には、以下のDIコンテナライブラリが登録されています。

+
    +
  • Rico

  • +
  • Copland

  • +
  • Needle

  • +
+
+

Rico

+

Thoughtworks(Martin Fowlerらがいる会社)に所属しているDan Northが、自作のJava用のDIコンテナPicoを自らRubyに移植したものがRicoです。私の知る限りではRuby用では最も古いDIコンテナです。古いといっても2003年ですが。

+

RicoはConstructor Injectionを採用しています。Ricoは古い実装ということもあり、機能が少なく実際にアプリケーションの一部として使うには物足りないかもしれませんが、DIパターンの実装を理解するためにちょっと試してみるのには役立つかもしれません。少ないと言っても上記の20行のDIコンテナよりははるかに高機能ですし。

+
+
+

Copland

+

Coplandはユタ州Brigham Young UniversityのJamis Buckが、Java用のDIコンテナHiveMindを参考に開発したものです。JamisはHiveMindとDIを理解するための習作として作ったと述べています。

+

CoplandはSetter Injectionを採用しています。Coplandのもう1つの特徴は、コンポーネントの構成はコードではなくYAMLで記述することです。Java系のDIコンテナでは構成をXMLで記述することが多いのですが、その影響を受けつつRubyらしさを出したということでしょうか。

+

しかし、作者のJamisはCoplandを開発しているうちに、「これはRubyらしくない」と感じたようです。その彼がよりRubyらしいDIコンテナとして、新たに開発したのが次に紹介するNeedleです。

+
+
+

Needle

+

Needleは「Rubyらしさ」を追求したLight-weightなDIコンテナライブラリです。ここでの「Rubyらしさ」はどうやらブロックとmethod_missingを多用したデザインのことのようです。

+

NeedleはRuby Conference 2004において、Jamis BuckとJim Weirichとの間で交わされた会話から生まれました。その後、JimはBlock Injectionに関するBlog記事を書き、JamisはNeedleを開発しました。

+

Needleを使ったDIプログラミングの例をリスト45.8に示します。

+ +
+

リスト45.8●Needleプログラミング例

+
require 'needle'
+
+def start_app
+  r = Needle::Registry.new
+  r.define do |b|
+    b.blog{Blog.new(r.input, r.display)}
+    b.input{Input.new}
+    b.dbms{DBMS.new}
+    b.makrup{Markup.new}
+    b.plugin{Plugin.new(r.dbms)}
+    b.display{Display.new(r.dbms, r.markup, r.plugin)}
+  end
+  blog = r.blog
+  ...
+end
+
+
+

defineメソッドがブロックに渡すものは「Builder」と呼ばれるオブジェクトで、このオブジェクトは自分が知らないメソッドが呼び出されると(method_missingを使って)、自動的にサービスを登録してくれます。defineメソッドとBuilderを使う代わりに

+
+
r.register(:blog) {Blog.new(...)}
+
+

を呼び出すこともできます。

+

Needleはオブジェクトの生成と依存関係の挿入以外にも、サービスの呼び出しにフックをかけるInterceptorと呼ばれる機能があります。この機能によってサービスの呼び出しをログに記録するなどの処理が可能になります(リスト45.9)。このInterceptor機能は、NeedleだけでなくRicoやCoplandでも実現されています。

+
+

リスト45.9●NeedleのInterceptor

+
reg.register(:foo) {...}
+reg.intercept(:foo).with {Needle::LoggingInterceptor}
+
+
+

これにより、サービスfooの呼び出しはNeedle::LoggingInterceptorを使ってフックされることになります。自分でクラスを作ることによりフックをカスタマイズすることができますし、withの代わりにdoingを使うことで、ブロックを使ってInterceptorを定義することもできます(リスト45.10)。

+ +
+

リスト45.10●NeedleのInterceptor

+
reg.register(:foo) {...}
+reg.intercept(:foo).doing {|chain,ctx|
+  ...
+  chain.process_next(ctx)
+}
+
+
+

InterceptorはなんとなくAOP(アスペクト指向プログラミング)を思い出させます。AOPという概念はこうして定着していくのかもしれません。

+
+
+
+

まとめ

+
+

Ruby界隈ではDIはまだまだ新しい概念です。私は昨年のJAOOカンファレンスでRicoについて紹介してもらいましたが、本当に理解しようとしたのはつい最近です。しかし、DIコンテナの基本部分がわずか20行以下で実装できるのは私にとっても驚きでした。簡単な割にはなかなか有効そうなテクニックなので、次の仕事ではDIを使ってみようかなあって考えています。

+

本連載のために参考にした記事をあげておきます。まだまだDIに対する理解が浅い私にとって、これらには大変お世話になりました。この場を借りて各記事の作者の方にお礼します。ありがとうございました。

+
    +
  • Martin FowlerによるDIの解説
    +http://www.martinfowler.com/articles/injection.html

  • +
  • 角谷さんによるその邦訳
    +http://www.kakutani.com/trans/fowler/injection.html

    +

    Rubyist MagazineのDIコンテナ紹介の記事。本連載と思いっきりかぶっている。

  • +
  • http://jp.rubyist.net/magazine/?0002-RLR

  • +
  • Jim WeirichによるDI紹介の記事

    +

    この記事の内容がきっかけになってNeedleが生まれた。

  • +
  • http://onestepback.org/index.cgi/Tech/Ruby/DependencyInjectionInRuby.rdoc

  • +
  • Dave ThomasによるDI紹介の記事
    +http://www.pragprog.com/pragdave/Tech/TransparentIOC.rdoc

  • +
+
+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-031.xhtml b/docs/vol2/xhtml/p-031.xhtml new file mode 100644 index 0000000..573ff02 --- /dev/null +++ b/docs/vol2/xhtml/p-031.xhtml @@ -0,0 +1,59 @@ + + + + + +第45章 ダイコン + + + + +

Matz Essays Volume 2

+ + +
+ +

◆ Ruby開発日記 ◆ 文字コードの憂鬱ゆううつ

+
+

テキストが記号の羅列のようになってしまう文字化けは困りものです。海外でも「Moji-Bake」と呼ぶことがあると聞いたことがあります。これは全部「文字コード」が悪いのです。

+

コンピュータは基本的にオンとオフで構成される2進数しか扱えませんから、数値以外のデータを直接取り扱うことはできません。テキストやそれを構成する「文字」は一度数に直してから処理します。コンピュータに表現させようとする文字の集まりを「文字集合」、文字集合に属する個々の文字を数に置き換える処理を「文字符号化」といいます。この「文字符号化」の方法を俗称として「文字コード」と呼びます。アルファベットのための符号化方法としてはASCII(ISO646)が有名です。古くはEBCDICという符号化系も存在していたそうですが、今では一部の汎用機を除いては使われなくなっています。

+
+

西洋の歴史的事情

+

ASCIIが事実上唯一の標準となった英語のアルファベットについてはほとんど問題は生じません。問題はそれだけでは表現できない文字があることです。ヨーロッパには英語で用いられる「通常」のアルファベットの他に、アクセント記号などの付いた文字を使う言語があります。これらはASCIIでは表現しきれません。幸い、ASCIIはアルファベットと記号だけで128文字以下しか使わないので1バイトのうち7ビットしか消費していませんでした。そこで、(もともとはパリティとして使われていた)残りの1ビットを使って導入した128文字にそれらの文字を割り振った「文字コード」が設計されました。これがISO-8859です。ISO-8859には国ごとにいくつかのバリエーションがあり、ISO-8859-1からISO-8859-16まであります。一番広く使われているのがISO-8859-1、通称Latin1です。

+
+
+

わが国の歴史的事情

+

アルファベットのような表音文字は文字の種類が少ないため、1バイトあれば全部の文字がなんとか表現できます。しかし、日本語の数千の表意文字はとても1バイトでは表現しきれません。

+

汎用機全盛の昔にはEBCDICコードの空き領域にカタカナを割り当てたEBCDIKという文字コードが使われていたそうです。その後、ASCIIが広まるとISO-8859と同様の方法でカタカナを取り込んだJIS-X-0201が登場しました。これがいわゆる半角カナです。

+

しかし、カタカナだけではどうにも読みにくいので、通常使う全部の文字を表現したいという要求は当然でした。そこで登場したのが、世界初の多バイト文字コードであるJIS-X-0208でした。これはISO-2022という規格で定められた枠組みに従い、複数バイトで1文字を表現することで、ひらがな、カタカナ、漢字、記号、ギリシア文字など数千の文字を含む文字集合を表現できる文字コードです。今でも電子メールは基本的にこの文字コードを使っています。

+

しかし、ISO-2022の定める枠組みは「ここからはアルファベット」「ここからは漢字」というように文字種を切り替える仕組みになっていますから、コンピュータによってテキスト処理をするのにはどうにも使いにくい方式でした。場所によって同じバイト列の持つ意味が変化するので、いつも先頭から1バイトずつスキャンしなければなりません。

+
+

それを解決するために登場したのがシフトJISです。シフトJISは、以下のような条件を満たす文字コードとして登場しました。

+
    +
  • マルチバイト文字の先頭バイトは最上位ビットがオンである

  • +
  • 先頭からスキャンしなくても処理可能

  • +
  • 当時すでに広く使われていた半角カナと共存可能

  • +
  • JIS-X-0208の文字からテーブルを使わなくても変換可能

  • +
+

シフトJISは文字コード、つまり文字に対応する番号がJISの文字番号をずらす(シフトする)ことによって得られるから、このように名付けられたそうです。シフトJISは本誌(Linux Magazine)を出版しているアスキーの技術者が発明したのだと聞いています。シフトJISはマイクロソフトによって採用され、PC業界で広く用いられました。

+

一方、UNIX業界ではEUC(Extended UNIX Code)と呼ばれる文字コードが使われていました。シフトJISでは半角カナの領域を避けるため、漢字などのマルチバイト文字の一部は通常のASCIIの範囲と重なっていましたが、EUCでは半角カナの使用をきっぱりあきらめたため、マルチバイト文字は最上位ビットがオンのバイトだけから構成されます。これにより、マルチバイト文字とシングルバイト(ASCIIの範囲)文字とが明確に区別できます。

+

このような歴史的事情により、日本ではJIS-X-0208(通称JIS)、シフトJIS、EUCの3種類の文字コードが広く使われることになりました。JISは比較的簡単に区別が付きますが、シフトJISとEUCはテキストから自動的に区別することは困難です。文字コードの判定を間違えたときは当然正しい文字を表示できません。これが文字化けの原因です。1つの国で3種類もの文字コードが並列して使われた例は珍しいと思います。日本は文字化け先進国だったのです。ちっともうれしくありませんが。

+

おかげで日本では複数の文字コードを切り替えて処理する技術が発展しました。

+
+
+

海外の事情

+

表意文字を使うアジアの各国では状況は似たようなものです。韓国ではEUCとほぼ同じ符号化方法であるEUC-KRが主に用いられています(EUC-KRと対比して日本のものをEUC-JPと呼びます)。中国ではGB2312、台湾ではBig5と呼ばれる文字コードがあります。どれも似たような方法でマルチバイト文字を表現しています。幸い、日本のように1つの国の中で複数の文字コードが対立するような事態はあまり起きなかったようですが、国際的に情報交換ができないのは問題です。

+

その問題に対する答えとして登場したのがUnicodeです。Unicodeは古今東西の文字をすべて表現できる文字集合を目指して開発されました。過去の反省を生かしていろいろとうれしい点も多いUnicodeですが、「Unicodeに含まれない文字は救済不可能」などの課題も残っています。結局、プログラムが対応しなければならない文字コードがまた1つ増えたというのが現実のようです。Unicode以後の文字処理の話はまた今度。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-032.xhtml b/docs/vol2/xhtml/p-032.xhtml new file mode 100644 index 0000000..23d99bc --- /dev/null +++ b/docs/vol2/xhtml/p-032.xhtml @@ -0,0 +1,377 @@ + + + + + +第46章 最終回・ネタのタネ + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay46 +
+

+探訪Ruby
+最終回・ネタのタネ +

+
+

[Linux magazine, 2005年3月号]

+
+

『Linux Magazine』連載「探訪Ruby」の最終回で、(当時の)私の情報源を紹介しています。とはいえ、紹介されているRAA, RubyForgeはすでになく、メーリングリストは現存するものの低調です。ブログなども紹介されているものは(私自身の「Matzにっき」を含めて)ほとんど止まっています。IRCもSlackやDiscordに置き換えられましたね。「アンテナ」が紹介されていますが、この言葉も久々に耳にしましたね。現代ではRSSにすっかり置き換えられていて、そのRSSも利用者が減っていますね。Ruby関連のカンファレンスの紹介もありますが、RubyConfの参加者が数十人と今とはまったく違った印象ですね。たとえばRubyKaigi 2024の参加者が1400人であることを考えると隔世の感があります。

+

では、現在の情報源は何かと考えると、私はいまだにRSSリーダーを使っています。あとは、X(旧Twitter)かなあ。どちらも当時はなかったものですね。YouTubeやTikTokで情報発信というのもあるようですね。

+

「Ruby開発日記」は「型についての一考察」です。この頃は動的型全盛期だったはずですが、すでに静的型についての検討が行われているのは興味深いですね。しかも、静的型に対する私の態度がこれだけ時間が経ってもまったく変化していない。

+
+
+

最終回となる今回は、今までの連載を振り返るとともに、今まで紹介してきたようなRuby関連の情報をどのように入手できるかについて紹介します。

+
+
+

連載のまとめ

+
+

本連載は2003年12月号から、「入門Ruby」に続く連載として始まりました。前回の連載は初心者を卒業してから幾星霜いくせいそうの私にとって入門記事を書くのは結構厳しく、音を上げてしまったというのが、正直なところです。それに続くこの「探訪Ruby」はRuby関連の最新情報を紹介するような連載として始まりました。表46.1に第1回から先月までの本連載の過去の内容を示します。

+ +
+

表46.1●「探訪Ruby」連載リスト

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
03年12月号Rubyの国へようこそRubyの簡単な紹介と連載の方向性について
第1回オープンソースのビジネスストーリービジネス視点からのオープンソース論
04年1月号テスト第一主義テストファーストプログラミング
第2回オープンソースと特許ソフトウェア特許の問題点と課題
04年2月号Wiki Wiki誰にでも書き換え自由なWikiについて
第3回日本Rubyの会RubyConf 2003と日本Rubyの会
04年3月号Blogの世界Rubyによる各種Blogソフトウェアの紹介
第4回Rubyの未来Riteについて
04年4月号アスペクト指向アスペクト思考の紹介とAspectR
第5回ごみ集めガベージコレクション入門
04年5月号RubyとEmacsruby-modeとRRBの紹介
第6回Emacsによる開発Emacsのある生活
04年6月号InstikiInstiki, Redcloth, Madeleineの紹介
第7回歴史は繰り返す過去の失敗から学ぶ
04年7月号テンプレート各種テンプレートライブラリの紹介
第8回プロトタイプな世界プロトタイプベース言語
04年8月号DBMdbm, gdbm, bdb, qdbmの紹介
第9回メールリーダーの新アプローチ検索ベースのMUAについて
04年9月号tDiarytDiaryの紹介など
第10回挑戦! 言語塾Langsmithメーリングリストについて
04年10月号Webアプリケーションの基礎HTTPとCGIについて解説
第11回Lightweight Language WeekendLLWイベントレポート
04年11月号Webアプリケーションの基礎(その2)CGIとセッション
第12回汝は人狼なりや?人狼BBSの紹介
04年12月号WebアプリケーションフレームワークCGIKit解説
第13回Ruby Conference 2004レポート行ってないのにレポート
05年1月号マークアップ・マークダウンRD, Redcloth, Blueclothの紹介
第14回名前重要私の「座右の銘」について
05年2月号ダイコンDependency Injectionについて
第15回文字コードの憂鬱文字コードに由来するトラブルなど
05年3月号ネタのタネ最終回、情報収集の方法
最終回型に関する一考察プログラミング言語と型について
+
+

本編のほうはそのときどきの最新ネタを紹介したつもりですが、ちょっと難しすぎたかもしれません。また誌面と私自身が下調べに使える時間の両方の制約から内容の突っ込み具合が足りないと感じられた方もいらっしゃるかもしれません。一方、私の日ごろ考えていることの一端を書きつづった「Ruby開発日記」は、いろいろな人に楽しんでいただけたようです。どうやら「役に立たないが面白い」という評価を受けていたようですが、そうだとしたらまったく私の狙いどおりということになります。

+
+
+ +

Ruby情報の集め方

+
+

さて、今まで本連載で紹介してきたようなRuby界での情報を得るためにはどうしたらよいでしょうか。私が利用しているものは、主に以下のような方法です。

+
    +
  • RAA

  • +
  • RubyForge

  • +
  • メーリングリスト

  • +
  • Blog/日記

  • +
  • Web Magazine

  • +
  • IRC

  • +
  • 各種イベント

  • +
+

それら1つ1つについてもうちょっと詳しく紹介してみましょう。

+
+
+

RAA(Ruby Application Archive)

+
+

RAA(http://raa.ruby-lang.org/)は今回の連載の最大の情報源です。これはRuby関連のアプリケーション、ライブラリ、その他についての情報を集めたインデックスサイトです。RAAはインデックス情報を提供するだけで、ソフトウェアの配布などは行いません。オープンソースコミュニティでたとえるならば、freshmeat.netであって、sourceforge.netではないというところでしょうか。

+

RAAのトップページには、新たに登録された5プロジェクトと、最近更新された10プロジェクトがリストされています。これをウォッチしているだけでかなり最新状況を把握できます。最近ではRSSフィードにも対応しています。

+
    +
  • http://raa.ruby-lang.org/index.rdf

  • +
+

をお使いのRSSリーダーに登録すれば、いつでも最新の更新をチェックできます。RAAは単にライブラリだけではなく、ドキュメントやRubyで書かれたアプリケーションもインデックスしていますから、RAAの更新をチェックするだけでRuby界隈の動向のかなりの割合をカバーできそうです。

+

原稿執筆時点で、RAAには220カテゴリーにわたる1308のプロジェクトが登録されています。またプロジェクトを登録している開発者は646人に及びます。

+
+
+

RubyForge

+
+

RAAがfreshmeat.netのRuby版であるならば、sourceforge.netのRuby版がRubyForge(http://rubyforge.org)です。RubyForgeはRuby関連のプロジェクトをホスティングするサイトです。RubyForgeはCVSリポジトリ、バグトラッキングなどオープンソースプロジェクトに必要なツールをさまざまに取りそろえています。ホスティングにはSourceForgeで使われていたソフトウェアから派生したgForgeを使っています。残念ながらgForgeはPHPで記述されています。まあ、使えるツールは使えばいい、何でもRubyで再発明する必要はない、という現実的な判断なのでしょう。アメリカ人らしい合理性です。

+
+

RubyForgeもRSSフィード

+
    +
  • http://rubyforge.org/export/rss_sfnews.php

  • +
+

を提供しています。原稿執筆時点で、RubyForgeには469プロジェクト、1419ユーザーが登録されています。RubyForgeはInfoEther社の提供で運営されています。

+
+
+

メーリングリスト

+
+

昔からRubyの開発はメーリングリストを主体として行われてきました。Web全盛のこの時代にあっても、いまだにコミュニティの中心と開発の母体はメーリングリストにあるといえます。Ruby関連のメーリングリストはたくさんありますが、ここでは主要な4つのメーリングリストを紹介します。

+

ruby-listはRubyユーザーのためのメーリングリストです。ruby-listは最も古いメーリングリストです。その始まりは1995年にさかのぼります。最近は書籍やWebから情報を得られるせいか流量がすっかり少なくなってしまい、メールはだいたい日に数通程度です。雰囲気は良好だと思っていますが、聞くところによると初心者には近寄りがたいところがある、と感じられるところもあるそうです。そうなのか。まあ、「なんか動きがおかしいのですが」という質問に対して、「このパッチで直ります」などと、開発者メーリングリスト顔負けの回答が来ることがあるところなどが原因なのでしょうか。とはいえ、別に恐いところでないので気軽に参加していただければよいと思います。

+

原稿執筆時点でのruby-listの累計メール数は40537、登録メンバー数は2470名です。

+

ruby-devは開発者のためのメーリングリストです。バグレポートや修正パッチ、あるいはRubyの仕様を改善する提案などが流れています。C言語の知識や各種OSに関する移植性などの情報、プログラミングテクニック、また言語やライブラリの設計のあり方についても論じられることがあります。Rubyに関わっていて一番面白いのは、もしかしたらここではないかと思います。言語そのものに関心がある人、Rubyの実装に興味がある人だけでなく、プログラミング全般に興味のある人にもお勧めのメーリングリストです。原稿執筆時点でのruby-devの累計メール数は25473、登録メンバー数は661名です。

+

ruby-listとruby-devは日本語のメーリングリストですが、海外でも広く使われているRubyには英語のメーリングリストも存在します。それがユーザー向けのruby-talkと開発者向けのruby-coreです。

+

ruby-talkの登録メンバー数は1306名しかいませんが、NetNewsのcomp.lang.rubyニュースグループと相互乗り入れを行っています。ruby-talkに投稿したメールは自動的にcomp.lang.rubyに流れますし、逆にcomp.lang.rubyに投稿された記事はruby-talkの転送されます。そのため、ruby-talkの読者の数がどれだけいるのかは調べることができません。おそらくは万単位の人が読んでいるのだろうと想像します。読者数を反映するのか、メールの流量も多く、今や日本語のRuby関連のメーリングリストの流量すべてを合わせたものの数倍の量のメールが流れます。現在までの累計メール数は126464で、実にruby-listの4倍を超えます。同様の形態を取っているcomp.lang.pythonやcomp.lang.perlよりは流量が少ないとはいえ、下手をすると1日に100通をゆうに超えるメールが流れます。これが全部英語ですから、読むのもなかなか大変です。

+
+

ruby-talkではいくつか面白い企画が行われています。その内のいくつかを紹介します。

+
+

ruby-dev summary

+

最近では、海外ユーザーのほうが多くなってしまったと思われるRubyですが、開発の中心はいまだに日本人が担っています。そのことから重要な議論はruby-devメーリングリストで日本語で行われることが多いです。そのような「最新動向」をぜひとも知りたいという非日本人のためにruby-devでの議論のサマリーをruby-talkに紹介するという企画です。数名の日本人ボランティアが毎週翻訳を行っています。新規ボランティアは常時募集中です。

+

Ruby Quiz

+

毎週出されるプログラミング課題にRubyで答えるという企画です。ルールとして、問題提出後48時間は解答をポストしないことになっています。最近の問題としては「コマンドライン引数で受け取った数字をASCIIアートでLCD表示しなさい」というものがありました。

+

Ruby Weekly News

+

Ruby関連の情報を毎週まとめています。当然ですが海外のニュースが中心です。

+

comp.lang.ruby FAQ

+

comp.lang.rubyニュースグループ(およびruby-talkメーリングリスト)に関する「よくある質問」をまとめたものです。1カ月に1回程度ポストされています。このFAQリストをメンテナンスしているのはRuby Wayの著者であるHal Fultonです。

+
+

英語による開発者メーリングリストruby-coreには351名の登録メンバーがおり、累計メール数は4230通です。こちらはNetNewsと相互接続していないので流量はおとなしいものです。ruby-devに負けないくらい濃い内容のものが多いですが、メールの量はruby-devよりもはるかに少ないです。

+

これらのメーリングリストは開発者と直接やりとりできる機会でもあります。あまり気負わずに参加してみてはどうでしょうか。

+

これらのメーリングリストに参加するには、「ruby-*-ctl@ruby-lang.org」にメールを送ります。メールのサブジェクトは空でよく、本文に「subscribe 名字名前」という行を含めます。アドレスの「*」の部分にはメーリングリストの名前「list」「dev」「talk」「core」が入ります。Fromアドレス宛に登録確認メールが来ますので、そのメールに返答することで登録が完了します。

+
+
+

Blog/日記

+
+

Blogや日記から得られる情報も馬鹿になりません。Ruby関連の日記やBlogを監視するためには、アンテナと呼ばれるものが便利です。アンテナは各種Webページをチェックして最近更新されたものから順に並べてくれます。

+

Ruby関連のアンテナで最も充実しているものは、おそらく、Ruby hotlinks(通称「るるりん」、http://www.rubyist.net/~kazu/samidare/)でしょう。とりあえずRubyに関係ありそうな話題が頻繁に登場する日記などはほとんどカバーしているのではないでしょうか。

+
+

その他に私が愛用しているアンテナは、\ayアンテナ(http://kazuhiko.tdiary.net/a/)です。これらには直接Rubyに関連ないページもたくさん含まれていますが、面白いものが多いです。

+

さて、個別のBlogからもいくつか紹介しておきましょう。まず、最初に紹介するのは私自身の「にっき」(あるいはBlog)です。

+

Matzにっき(http://www.rubyist.net/~matz/)は、Rubyのこと、プログラミング言語論から私の家族や日常生活まで、雑多な内容の日記です。更新は毎日とはいかないのですが、365日毎日ぶんのエントリを書いています。最近はRubyの記事に混じって、「XML批判」とか「PHPについての考察」なんかも書いています。

+

もう1つはYAML for Rubyの開発者、why the luckystiffが音頭をとって始まったRedHanded(http://redhanded.hobix.com/)です。日本語がわからないなりに、日本の状況を一生懸命ウォッチしている姿が印象的です。最近はライターに日本人も加わり、より的確に強力に日本の情報を「世界に発信」してくれるのではないでしょうか。

+
+
+

Web Magazine

+
+

Ruby界でも「雑誌のスタイルをとったWeb」であるWeb Magazineが発行されました。その名もRubyist Magazine(http://jp.rubyist.net/magazine/)、略して「るびま」です。現時点では、第4号まで、ほぼ月一(ただし、1月はお休み)で発行されています。例として第4号の目次を表46.2に示します。

+
+

表46.2●Rubyist Magazine 第4号 目次

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
0004号(2004-12)
巻頭言
Rubyist Hotlinks 第4回
Rubyではじめるプログラミング
RubyOnRailsを使ってみる
Ruby Library Report 第3回
lilyでブログカスタマイズ 第2回
Win32OLE活用法 第2回
build Ruby on Windows 第1回
Ruby/Tk講習会レポート
RubyNews
RubyEventCheck
編集後記
+
+

入門から高度な内容までカバーした充実した内容です。これだけのクオリティのWeb Magazineを毎月、しかも、無料で、発行していることは驚きに値します。

+

「るびま」が発行されたときには海外の人たちから、続々と「いいなぁ」という声が上がりました。そこで、現在はartima.comのBill Vennersを中心に英語版のRuby Web Magazineの発行に向けて準備中なのだそうです。無事発行の暁には、「るびま」との翻訳記事のやりとりも行いたい、とのことです。

+
+
+

IRC

+
+

Intenet Relay ChatでもRubyについて語り合っている人々がいます。Ruby関連のチャンネルはたくさんあるようですが、主要なものは日本語では「%Ruby」、英語では「#ruby-lang」です。

+ +
+

%Ruby

+

irc.tokyo.wide.ad.jpなどでコネクトできます。*.jpドメインからの接続に限定されています。最近はあんまり盛り上がっていないようです。「%rrr」というチャンネルもあると聞いたことがありますが、私は参加したことがありません。

+

#ruby-lang

+

irc.freenode.netでコネクトできます。こちらは180名以上の参加者がおり、活発な議論が行われています。WebアプリケーションフレームワークであるRuby on Railsについて語り合う「#rubyonrails」やRubyForgeを話題とする「#rubyforge」などもあるそうですが、こちらも参加したことはありません。

+
+

しかし、IRCはリアルタイムな会話なので大変時間を消費します。IRCに没頭すると開発の時間や原稿を書く時間がごっそり削られてしまいますから、私はこれらのチャンネルにログ記録用のbotを常駐させて、1日一度のペースでログをメールで読んでいます。これらのチャンネルでmatzを見つけて、話しかけてもお返事できないかもしれませんから、あらかじめごめんなさい。だいたい、matzをキーワードにして斜め読みしていますから、IRCで私に何か伝えたいときには、ログの中にmatzという単語を含めると確実です。

+
+
+

各種イベント

+
+

ネット上のやりとりもよいですが、ときには直接顔を合わせるのも大変有効です。Rubyもあちこちで認知されていて、Rubyに関連する話題が取り上げられるイベントも増えてきています。現時点で予定がはっきり決まっているのは『オープンソースカンファレンス2005』です。

+
    +
  • 『オープンソースカンファレンス2005』

  • +
  • 2005年3月25日(金)・26日(土) 10:00-17:00

  • +
  • 開催場所: 日本電子専門学校 7号館(東京・大久保)

  • +
  • 主催: オープンソースカンファレンス実行委員会

  • +
+

それからLightweight Languageというイベント(一昨年はLL Saturday、昨年はLL Weekend)が毎年開かれています。これはMITで開かれているLightweight LanguageというイベントにインスパイアされたRuby, Python, Perl, PHPなどの軽量言語を取り扱うカンファレンスです。実は、このLightweight Languageが今年も開催されるという情報を入手しました。まだ、日程や場所などは未確定です。昨年は、Ruby, Python, Perl, PHPにとどまらず、Curl, Gauche,Groovy, Pnuts, Squeakなどについても紹介されました。今年はどのようなプログラムになるでしょうか。

+

また、大阪では毎年『関西オープンソース』というイベントが大阪産業創造館で行われており、きっと今年も開催されるでしょう。

+

海外でもイベントは行われています。今年、確定しているものは、8月のOSCON 2005(O’Reilly Open Source Convention. August 1-5, 2005. Portland, OR)および、秋頃のRuby Conferenceです。Ruby Conferenceはアメリカ東海岸と西海岸で交代で開催されています。昨年は東海岸(バージニア州)で開かれたので、今年はどこか西海岸で開催されるでしょう。

+
+

Ruby Conferenceは毎年の参加者が50名から60名程度のこぢんまりとしたカンファレンスです。しかし、Dave ThomasやDavid Alan Black, Hal FultonなどRuby界の有名人と直接会うことができますし、なによりプレゼンテーションの内容が、この人数にはもったいないくらいに高度かつ良質なものばかりです。毎年、日本人は3~4名くらいしか参加していません(昨年は私も出席できませんでした、残念)が、今年はもうちょっと多くの日本人が参加できればよいと思っています。言語が英語なのがちょっと難点ですが。夏頃までには開催地も決まり、情報がhttp://www.rubycentral.org/conference/で告知されると思います。

+

一方のO’Reilly OSCONのほうは、オープンソース全般をカバーする大規模なカンファレンスです。私は2003年に出席する機会がありましたが、キーノートスピーチでは聴衆が数千人を超える本当に大きなカンファレンスでした。OSCONではPerl, Python, MySQLなどオープンソースソフトウェアごとにトラックが割り当てられ、同じ部屋で連続してそのソフトウェアに関するセッションが開かれます。

+

昨年は人材不足などからRubyトラックを開催できませんでしたが、今年はすでにRubyトラックを実現するための画策が始められているそうです。日程(と予算)の都合がつけば今年は私もぜひ出席したいと考えています。こちらも詳細はまだ発表になっていませんが、いずれ、http://conferences.oreillynet.com/で告知されることでしょう。こちらもLarry WallやGuido van Rossumや、そのほかオープンソース業界の有名人と直接コンタクトできる貴重な機会です。日本からOSCONに参加するツアーとか組んだらどうかなあ。「まつもとゆきひろと行くOpen Source Convention」とか。いかがでしょう。

+

アメリカのイベントばかり紹介しましたが、ヨーロッパでは、EuRuKo(European Ruby Conference)が開催されています(昨年の会場はドイツ、カールスルーエ)。今年も開催されそうですが、まだ情報が入ってきていません。

+
+
+

おわりに

+
+

長らく続いてきた連載もこれが最終回です。なごり惜しい気はしますが、お別れです。ただ、Rubyそのものはこれからも発展を続けます。これからますます広がっていく「Rubyの世界」の中で、皆さんとまたどこかで再会できることと思います。そのときまで、さようなら。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-033.xhtml b/docs/vol2/xhtml/p-033.xhtml new file mode 100644 index 0000000..ea5e559 --- /dev/null +++ b/docs/vol2/xhtml/p-033.xhtml @@ -0,0 +1,69 @@ + + + + + +第46章 最終回・ネタのタネ + + + + +

Matz Essays Volume 2

+ + +
+ +

◆ Ruby開発日記 ◆ 型についての一考察

+
+

プログラミング言語の重要な要素として「データの型」があげられます。文字列データはString型とか、整数データはint型とかいうときのアレのことです。CやJavaのような言語は変数や式の型がプログラム中に明示されます。このような型を「静的な型」と呼びます。「静的」というのは「プログラムを見ただけで実行しなくてもわかる」という意味です。ところがプログラミング言語によっては変数や式の型をプログラムの中では指定しないので、実行してみるまでは型がわからないものもあります。Rubyはその典型的な例ですね。このような型を「動的な型」と呼びます。「動的」は読んで字のごとく「静的」の反対で、「実行して初めて決まる」という意味です。

+

静的な型はプログラムの字面を解析しただけで型の整合性が漏れなくチェックできるので、バグを早く見つけることができるというメリットがあります。

+

一方、動的な型には、変数や式の型を明示的に指定しなくて済むのでプログラムが簡潔になる、また、状況に応じていろいろな型のデータを統一的に扱うなど柔軟性が高いというメリットがあります。特に一連のメソッドを持っていれば、そのオブジェクトの型がなんであっても動作するのは大きな利点で、Duck Typingと呼ばれています。たとえば、ファイル入出力を行うIOの代わりに文字列に対して読み書きを行うStringIOを用いるようなことが簡単にできます。

+
+
log = Logger.new(STDERR)
+....
+
+s = StringIO.new
+log = Logger.new(s)
+....
+puts s.string   # 文字列を取り出す
+
+

Javaなどであれば、IOStringIOの双方が共通にimplementするinterfaceが必要になるところです。Duck Typingの由来は英語のことわざである「アヒルのように鳴き、アヒルのように歩くものはアヒルに違いない」から来ているようです。

+

Javaなどの静的型を持つ言語の経験が多い人にとっては、Rubyなどのような「実行してみないとわからない」型は不安に感じるもののようです。メーリングリストなどでも、「Rubyにも静的な型を」というリクエストがたびたび登場します。これはRubyに対してだけではなく、Pythonなどでも同様のようです。もちろん全面的に静的な型を採用してしまえば、もう違う言語になってしまうのですが、オプショナルであればかまわないだろうという主張のようです。

+

そのような要望を受けて、Pythonの開発者として名高いGuido van Rossumが、Pythonに将来(省略可能な)静的型システムの導入について自らのブログに書き込みました。

+
+

これを読んでみると、最初の2つは、Javaでも導入されたばかりのGeneric Typeや、型のオーバーロード、interfaceの導入など、静的型言語の型システムとしても十分に通用する本格的なものをアイデアとして紹介しています。しかも、型の同一性の判定に、デフォルトではDuck Typing(必要とされるメソッドを持つかどうかで判定)を採用するなど、動的型の言語として長い歴史を持つPythonの伝統にも配慮したすばらしいものです。

+

しかし、これを読んでいろいろと考えたあとで、私が思ったことは「Rubyではこのような静的型システムの導入はやめよう」ということでした。主な理由は以下のようなものです。

+
    +
  • これだけ完備した型システムを用意しても、オプショナルでは効果は半減以下である。言語が複雑になる割には得られるものが少ない

  • +
  • 効率のよい実装が思い付かない。私の知識や能力の限界かもしれない

  • +
+

Guidoも同じように思ったのかどうかはわかりませんが、数日後に3番目の記事がポストされました。こちらでは新文法の導入は、

+
    +
  • def文でのメソッド引数と戻り値の型の指定

  • +
  • interface文の導入

  • +
+

に限定し、また、型のチェックは実行時に行うことにしたようです。いきなり現実的ですね。これならば実装もずいぶん簡単になりますし、それなりの効果もありそうです。もっともコンパイル時のチェックを希望する人々にとっては大きな後退かもしれませんね。

+

Rubyについては現時点では、将来的にも静的な型を導入するつもりはありません。型によるチェックは便利な点もありますが、すべてのバグを見つけてくれるわけではありませんし、むしろ網羅的なユニットテストを推奨するほうがバグの検出率も高く、実用的だと考えるからです。Lispなどの動的型言語の長い歴史も静的な型なしでもうまくやれるということを示しているように思います。とはいうものの、今後面白いアイデアが見つかれば、喜んで検討を行うつもりです。

+

型に関する話題はまだまだホットです。特にプログラムには型を記述せずに、その使われ方から変数や式の型を決定する型推論という手法にはさまざまな可能性が秘められているようです。Pythonに対しては、よく似た文法を採用し、静的な型と型推論を導入したBoo(http://boo.codehaus.org/Home)という言語が開発中です。

+

Rubyに対しては、Rubyプログラムを解析して型推論を行い、可能な限り静的な型チェックを試みるTyping Ruby(http://truby.sourceforge.jp/index.j.html)が登場しています。

+

Typing Rubyはまだまだ開発が始まったばかりですが、非常に興味深い可能性を示していると思います。

+

プログラミング言語と型、切っても切れないこの関係には今後も注目していこうと思っています。

+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-034.xhtml b/docs/vol2/xhtml/p-034.xhtml new file mode 100644 index 0000000..7006392 --- /dev/null +++ b/docs/vol2/xhtml/p-034.xhtml @@ -0,0 +1,106 @@ + + + + + +第47章 ハッカーとの遭遇 + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay47 +
+

+まつもと ゆきひろのハッカーズライフ
+ハッカーとの遭遇 +

+
+

[UNIX USER, 2005年4月号]

+
+

連載タイトルにも含まれている「ハッカー」という言葉の説明です。言葉というのは生き物でなかなか思うようには使われてくれないものですが、ハッカーについては、当時広まりつつあった「ハッカーとは悪い人」というイメージはこの20年でだいぶ払拭できたのではないでしょうか。うれしいことです。一方、WikipediaのことをWikiと省略するのは残念ながら定着してしまいそうです。

+

今回の記事ではさらに「ハッカー」と呼ばれる人の気質を『ハッカーズ大辞典』(JARGON FILEの和訳)からの定義とLarry Wallの「ハッカー三大美徳」を引いて紹介しています。この気質は時間が経っても変化しないようです。やはり人間の気質や性格は技術などに比べてだいぶゆっくりとしか変化しないようです。

+
+

こんにちは、はじめまして。まつもと ゆきひろと申します。世間ではRubyというプログラミング言語を作った人として知られています。職業はプログラマーで自称ハッカーでもあります。この連載では私たち「ハッカー」の生態や心理について紹介できればと考えています。

+
+

ハッカーとは

+
+

「ハッカー」といっても必ずしもネットワーク経由でシステム侵入を行ったり、パスワードを破ったりするような悪者ではありません。そういえば最近そういう誤用をあまり耳にしなくなりましたね。

+

ハッカー(Hacker)とは文字どおり「Hackする人」という意味です。「Hack」というのはもともとは「(おのなどで)叩き切る」というような意味だそうですが、転じて「(プログラムなどを)でっちあげる」というような意味で使われています。「でっちあげる」というとずさんな仕事のように思えるかもしれませんが、実際には「素早い仕事」というニュアンスで、すばらしく出来のよいものも「ハック」ですし、その場限りに間に合わせのような仕事もやっぱり「ハック」です。そんな仕事を好むハッカーは、要するにコンピュータ(あるいはプログラムできるものすべて)にのめり込むようなタイプです。

+
+

ハッカーたち自身がまとめた用語集である「jargon file」ではハッカーをリスト47.1のように定義しています。長いでしょう? それだけHackerという単語にこだわりがあることがうかがえます。

+
+

リスト47.1●ハッカーの定義(『ハッカーズ大辞典』、アスキー、ISBN4-7561-0374-X の掲載要旨)

+
+

①プログラム可能なシステムの細かい部分を探ったり、その機能を拡張する方法を探求したりするのに喜びを感じる人。必要最小限のことしか勉強したがらない大半のユーザーとは対照的。

+

②熱中して(さらには取りつかれたように)プログラミングする人、またはプログラミングを単に理論化するのではなく、プログラミングを楽しむ人。

+

③ハック価値(hack value)を評価できる人。

+

④手早くプログラミングするのが得意な人。

+

⑤ある特定のプログラムのエキスパート、または頻繁にそれを使って仕事する人。たとえば「UNIXハッカー」。

+

⑥あらゆる種類のエキスパートまたは熱狂的なファン。たとえば「天文ハッカー」。

+

⑦創意工夫を発揮して制約を打破したり回避したりするという知的な難問を楽しむ人。

+

⑧(誤用)あちこち調べ回って機密情報を探りだそうという悪意の詮索好き。このことから「パスワードハッカー」「ネットワークハッカー」などといわれる。正しい用語は「クラッカー」。

+
+
+

ハッカーは他の人から見て、一見不可能だと思えることを現実にしてしまうことがあります。プログラミングの知識がない人、経験が浅い人からは魔法使いのようにみえることもあります。プログラミングの生産性も大変高いことが多いようです。「好きなことにのめり込んでいる」という意味では幸せそうですが、富とか資産とかいう単語とは無縁の人が多いようです。しかし、世の中には例外的に自分の才能を上手にお金に変える手段を見いだしているハッカーもいます。

+
+
+

良いハッカー・悪いハッカー

+
+

システム侵入をするような悪い人がハッカーと呼ばれたのは、それがかつて「創造的な知的チャレンジ」だったので、そのようなことを行うハッカーが存在したからです。

+

たいていのハッカーは世間一般と倫理観に少々ずれがあります。ときには法律を順守することや「良い社会人」であることよりも、知的好奇心を満たすことのほうが大切だと考える気持ちはわからないでもないです。

+

ですが、ハッカーであることと、悪人であることはまったく独立です。いや、むしろ、仮に本物のハッカーがシステム侵入を行ったとしても、それの動機はおそらく「やってみたかったから」「できることを証明したかった」であって、「利益を得たい」というような犯罪的な動機ではないだろうと思います。ハッカーにはお金のような物質的欲求の低い人が多いのです。

+
+
+ +

あなたはハッカーか

+
+

さて、「あなた」はハッカーでしょうか。こんな雑誌(と言ったら失礼ですね)を読むくらいですから、あなたはきっとコンピュータのことが好きで、プログラミングにも関心があるでしょう。また、単なるコンピュータユーザーにとどまらず(そうだったら、Windows関係の雑誌を読んでるはずです)、ハッカー的特質を備えたUNIX系OSのユーザーであるということは、あなたがハッカーである可能性は十分に高いと言ってもよいと思います。

+

私の仮説によると、ハッカー的素質を持つかどうかは、以下の2つの条件を満たしているかどうかで決まります。

+

1つは「クリエイターであるかどうか」です。マニア的要素を持つ人材は「コレクター」タイプと「クリエイター」タイプに別れる傾向があります。もちろん、両方の特質を備えている人もありますが、この場合問題になるのは「プログラミングに対してどちらがより強く表れるか」です。コレクタータイプはマシンスペックや最新の機器に関心を持ち、コンピュータやソフトウェアの使い方には関心を寄せますが、クリエイタータイプは「ないなら作ってしまおう」「ここが気に入らないから直そう」などと自分でばりばり「世界」を変えてしまいます。ですから、自分の好みにいろいろ設定できるようなソフトウェア、特にプログラミングできるようなソフトウェアを好みます。「もの作り」への情熱、それがハッカー的素質の第1要素ではないでしょうか。

+

もう1つは「ブレーキが壊れていること」です。こういう言い方は変かもしれませんが、私が知っているハッカーのほとんどは、何らかの形でブレーキが壊れています。普通の人が「あ、これ無理そう、やめよう」と思うところで、ハッカーは「やればできるかも、やってみよう」と思うようです。その理由は、単なる無知だったり、無謀、根拠の薄い自信、あるいは人並み外れた能力だったりするのですが、とにかく人よりはあきらめが悪い、遅い傾向が見受けられます。だからこそ人のできないことができたりするわけです。もっとも、いつも成功するというわけではありません。しかし、ハッカー予備軍は人知れぬ失敗を繰り返しつつ、いつかハッカーへと成長するのです。

+
+
+

ハッカー三大美徳

+
+

今や知らぬ人などいないスクリプト言語であるPerlの作者、Larry Wallは当代一流のハッカーです。彼によれば、プログラマーの三大美徳は、

+
    +
  • 無精

  • +
  • 短気

  • +
  • 傲慢ごうまん

  • +
+

なのだそうです。もちろん、ここでの「プログラマー」はハッカー的特質を備えたプログラマーのことです。

+

しかし、「無精」だの「短気」だの「傲慢」など、どう考えても短所にしか思えないような特質が「美徳」とはいったいどういうことでしょう。Larry自信の言葉を引用すればこれらの定義はリスト47.2のようになっています。

+ +
+

リスト47.2●ハッカー三大美徳(『プログラミングPerl』、オライリー・ジャパン、ISBN4-8731-1096-3 の掲載要旨)

+
+
    +
  • 無精(laziness)

    +

    トータルで見たエネルギーの支出を減らすために、多大な努力をするようにあなたを駆り立てる性質。こうして労力を省くために書いたプログラムは他人も使うようになり、そのプログラムに関する質問にいちいち答えずに済ますためにドキュメントを書くようになる。そのために、プログラマーにとって最も重要な素質である。またそれゆえ、この本が存在するのである。

  • +
  • 短気(impatience)

    +

    コンピュータがサボっているときに感じる怒り。あなたの司令に反応するだけでなく、実際に指令を予測する — あるいは、少なくともそのようなふりをする — プログラムを書く原動力になる。それゆえに、プログラマーにとって2番目に重要な素質である。

  • +
  • 傲慢(hubris)

    +

    ゼウスの怒りに触れるほど、プライドが高いこと。また、他人にケチを付けられないようなプログラムを書く(そして維持する)ための原動力になるもの。それゆえ、プログラマーにとって3番目に重要な素質である。

  • +
+
+
+

「楽をするためには苦労をいとわない」というような変な話なのですが、ハッカーというものは自分の知的欲求を満たさないことには指一本動かすのも嫌がる一方で、自分のやりたいことにはどんな苦労も苦労と思わない傾向があります。また、短気と傲慢のせいで、自分がコンピュータのために働かされていることを嫌います。コンピュータに与えるデータを作るために機械的作業を繰り返すくらいなら、機械的作業をコンピュータにやらせるスクリプトを作ろうとします。あるいは、今後そのような作業をいっさいしなくてもよいようなツールの開発を開始するかもしれません。普通の人には本末転倒に見えるかもしれません。でも、それがハッカーなのです。

+

この連載では、そんなハッカーたちの生態や心理を観察していこうと思います。ハッカーの生き方は、もしかしたらあなたがより良いプログラマーになるのに役立つかもしれません。保証はできませんけど。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-035.xhtml b/docs/vol2/xhtml/p-035.xhtml new file mode 100644 index 0000000..87d8d2d --- /dev/null +++ b/docs/vol2/xhtml/p-035.xhtml @@ -0,0 +1,158 @@ + + + + + +第48章 キーボードへのこだわり + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay48 +
+

+まつもと ゆきひろのハッカーズライフ
+キーボードへのこだわり +

+
+

[UNIX USER, 2005年5月号]

+
+

和田英一先生(東京大学名誉教授、現IIJ技術研究所研究顧問。2024年時点で御年93歳だそう)と増井俊之さんと私自身のキーボードについてのこだわりを紹介しています。先のお二人と私を並べるのはおこがましい感じがしますが、自らの経験を語ることも大事なので。

+

和田先生は自分の好みのキーボードを商品化しておられます。増井さんはここではキーボードの「;」キーの置き換えしか紹介していませんが、後にAppleに移ってiPhoneのフリック入力を開発されました。世のほとんどの人が日本語を入力するときには増井さんの業績にお世話になっているわけです。私自身は自分の使っている日本語入力用配列「きゅうり改」を紹介しています。20年経った現在でもまだ「きゅうり改」を使い続けているのです。日本語入力システムは「かんな」から「Mozc」に変化しましたが。最近はオープンソースの日本語入力システム開発は低調で、他に入力配列レベルでカスタマイズできるシステムもなく、もしMozcになにかあったらどこに移行すればよいのか悩ましいところです。

+
+
+

キーボードとくら

+
+

生産性にこだわる職人は、自分の道具を選びます。現代のコンピュータ職人が最も接する道具といえば、キーボードではないでしょうか。今月はハッカーのキーボードへのこだわりについて考察します。

+

ハッカーのキーボードとして最も有名なものはHappy Hacking Keyboard でしょう。これは国内ハッカーの「はしり」とでも呼ぶべき、和田英一東京大学名誉教授1 の監修で企画された商品であり、「UNIXハッカーのためのキーボード」と高く評価されています。特徴としてはこだわりのキースイッチ、Sun Type3キーボード2 互換のキー配列、最小限のキー数3 とそれによるサイズの削減などがあります。和田教授のキーボードへのこだわりは、以下のような談話からもうかがえます。

+ +
+

アメリカ西部のカウボーイたちは、馬が死ぬと馬はそこに残していくが、どんなに砂漠を歩こうとも、鞍は自分で担いでいく。馬は消耗品であり、鞍は自分の体に馴染んだインターフェイスだからだ。いまやパソコンは消耗品であり、キーボードは大切な、生涯使えるインターフェイスであることを忘れてはいけない。

+
+

最近ではパソコンは数年で性能が陳腐化してしまいます。CPUが遅い(遅くなるのはCPUではなくソフトなのですが)、HDDの容量が足りないなどの不満から、どうしても買い替えてしまいます。しかし、手になじむ高品質のキーボードは、PC本体を買い替えても、ずっと使える大事なインターフェイスであるというのはわかる気がします。日常接する時間の長いインターフェイスであればこそのこだわりであるといえるでしょう。

+
+
+

十人十色

+
+

ハッカーのキーボードに対するこだわりは、いろいろな形で現れます。知人のPerlハッカーは、アルファベット入力にDvorak配列4 を使い、日本語入力にはT-Code5 を使っています。キーボードやキー配列にこだわる人は多いですが、DvorakとT-Codeの両方を使っているのは彼だけです。

+
+ +
+ fig4801 +
+

図48.1●Dvorak配列

+
+

これまたハッカーとして知られる産業総合研究所の増井俊之さん は、キーボードの配列を変更しているようです。Rubyist Magazine6 第5号のインタビューによれば、最近あまり使わなくなったセミコロン(;)を置き換えてしまったのだそうです。

+ +
+

増井:それから、最近気がついたことは、セミコロン打たなくていいですよね、あんまり。キーボード入力してる時、セミコロンって小指にあるじゃないですか。これは非常にいいポジションなんだけど、CとかPerlでは打つけど、Rubyでは打たなくていいですよね。で、使わないキーがこんないいところにあるのはもったいないから、これリターンにしちゃったんですよ。

+

一同:(爆笑)

+

増井:だから、私の機械は今全部、右手の小指がリターンなんです。すると手を全然動かさなくていけるんですよ。普通の人は、バックスペースで右手を動かすし、リターンでも動かすから、かなり無駄なんですよ。日本語を入力しててもかなり手が動いてるはずです。でも、ここをリターンにしてから、ほとんど手を動かさずに舐めるように入力できるようになりましたよ。

+
+

Rubyに合わせてキー配列を変えるというのも、「ブレーキが壊れている」ハッカーらしい態度ですね。このインタビューには、他にも増井さんのハッカー的人格の原点がうかがえるようなエピソード7 がたくさん載っていますから必読です。

+
+
+

日本語入力の配列は「きゅうり改」

+
+

さて、最後に私自身のことも話しておきましょう。ノートPC派の私は和田教授のような「一生もののキーボード」というわけにはいきません。

+

それでもキー配列に対するこだわりはあって、まずアルファベット入力には、日本語キーボード(いわゆるJIS配列)を英語配列(いわゆるASCII配列)で使っています。これは「A」の段で「Enter」の隣のキーの数が気になるからです。英語キーボードではここにキーがないため「'」キーが変なところにさまよっている8 のが気に入りません。また、JIS配列では「[」と「]」が縦に並ぶのも不満です。「(」と「)」はちゃんと横に並んでいるのに。私がASCII配列を好むのは、最初に就職した会社で使っていたソニーのワークステーション(NEWS)のキーボードが、この「ASCII配列だがEnterの横にキーが多い」タイプだったことが原因の1つかもしれません。

+

キー配列のこだわりはもう少しあって、実は日本語入力用のキー配列に独自のものを定義しています(図48.2)。私はこの配列を「きゅうり改」と呼んでいます。

+
+ +
+ fig4802 +
+

図48.2●日本語入力配列「きゅうり改」

+
+

「きゅうり改」は左手が子音、右手が母音となるキー配列です。たとえば「G」のキー(アルファベットのQキー)を押してから、「U」のキー(アルファベットのJキー)を押すと「ぐ」が入力されます。基本的に左手と右手が交互に動きますからリズムよく入力できます。

+ +

「きゃ」のように拗音ようおんを含む文字は子音キーのあとに「ゃ」キーのような拗音キーを打ちます。それ以外の小文字は「小」キーを使い、

+
    +
  • H」「小」「A」→「ふぁ」

  • +
+

というように入力します。ローマ字入力であいまいになりやすい「ん」と「っ」は独立したキーが割り当ててありまから、「な行」と「ん」が混ざってしまったとか、「っ」で終わる文が打ちにくいとかいう問題とも無縁です。

+

日本語変換システムとしてCannaを使用している方は、私の使っているkpdefファイルを利用することにより、「きゅうり改」を試してみることができます。kpdefファイルは次のURLからダウンロードしてください。

+
    +
  • http://www.rubyist.net/~matz/kyuri.kpdef

  • +
+

Canna付属のmkromdicを使って、kyuri.kpdefファイルからローマ字かな変換テーブル(ドットで始まる名前のファイル)を作ってください。あとは、その変換テーブルをホームディレクトリに置き、.cannaファイルに、

+
+
(setq romkana-table "<変換テーブル名>")
+
+

という行を追加すると「きゅうり改」が使えるようになります。<変換テーブル名> の部分は、作成したローマ字かな変換テーブル名にしてくださいね。

+

「きゅうり改」は完全に私のオリジナルのアイデアというわけではなく、もともとは狩野宏樹さんが1991年に作成された「きゅうり9」という配列に触発されて誕生しました。確か1992年頃だったと思うのですが、キー配列のカスタマイズに凝っていた私は、日本語入力に「きゅうり」を使おうというアイデアに取りつかれました。しかし、実際に使ってみると私にはちょっと使いにくいところがあったので、私の指の癖に合わせて打ちにくいキーを移動したり、拗音の連続で「ゃあ」「ょう」「ゅう」などを入力できるような改善を行い、「きゅうり改」が誕生しました。

+

日本語入力の配列をまったく新しいものにするのには少々勇気が必要でしたが、やってみたら3日で慣れました。プログラマーと言ってもプログラムばかり書いているわけではなく、ドキュメント書きやメール書きなど、日本語を入力する割合のほうがはるかに多いので、日本語入力の効率は非常に重要です。「きゅうり改」は私の生産性を高めてくれています。

+

このように手になじむ道具の追求はハッカーの特質の1つです。皆さんも身の回りのツールの使い勝手を極めてみませんか。ハッカーの気持ちがわかるかもしれません。

+
+
+
+
+
    +
  1. +

    和田英一名誉教授

    +

    和田教授はパラメトロン計算機の開発やAlgol Nの設計、また和田研フォントの開発などでも知られている。
    +http://www.ipsj.or.jp/katsudou/museum/pioneer/e-wada.html +

    +
  2. +
  3. +

    Sun Type3キーボード

    +

    筆者も学生時代に愛用していた名作。その後、SPARC Stationに付属していたType4キーボードはがくんと品質が下がっており、がっかりしたことが記憶に残っている。 +

    +
  4. +
  5. +

    最小限のキー数

    +

    初期のHappy Hacking Keyboardにはカーソルキーすら存在しなかった。どうしても必要なときには、ファンクションキーと併用することで代用。さすがに「使いにくい」との声が上がったのか、Happy Hacking Keyboard Lite以降は小さなカーソルキーが追加されている。 +

    +
  6. +
  7. +

    Dvorak配列

    +

    August Dvorak博士 が開発したキーボード配列。Dvorak Simplified Keyboard(DSK)と呼ばれることもある。一般のキーボード配列は上段の並びからQwerty配列 と呼ばれる。Dvorak配列は図48.1のような配列で、母音が左手に集中しているため、学びやすく打ちやすいといわれている。 +

    +
  8. +
  9. +

    T-Code

    +

    無連想2ストロークによる日本語入力方式。2つのキーの組み合わせに漢字、ひらがな、カタカナが割り当てられていて、それらを直接入力する。変換作業がないため高速に入力できるが、「覚えてない文字は打てない」のが弱点。ただし、ひらがなさえ覚えていれば変換による入力は可能で、変換した文字のストロークを教えてくれる。
    +http://openlab.jp/tcode/ +

    +
  10. +
  11. +

    Rubyist Magazine

    +

    「日本Rubyの会」の有志が発行しているWebマガジン。Rubyist Hotlinksという連載ではRubyに関連する「有名人」のインタビューを掲載している。
    +http://jp.rubyist.net/magazine/ +

    +
  12. +
  13. +

    原点がうかがえるようなエピソード

    +

    筆者が一番印象に残ったのは、ICチップをつないでマイコンを作った話。1970年代後半にはマイコンキットが(小さな)ブームになったが、それを見て「そんなキットを買ってるやつは『なんと生ぬるいんだ』とか『回路なんかできてるじゃん、面白くないな』とか思ってた。自分で回路を考えて、チップを全部そろえて、配線して、それを制御するプログラムを全部書いて、というのがコンピュータの自作だと思ってた」というのは尋常ではない。
    +http://jp.rubyist.net/magazine/?0005-Hotlinks +

    +
  14. +
  15. +

    '」キーが変なところにさまよっている

    +

    英語キーボードでは、「'」キーの位置が「1」の横とか右手側一番下の段など、製品ごとに異なる。悪い冗談としか思えない。 +

    +
  16. +
  17. +

    きゅうり

    +

    オリジナルの「きゅうり」の情報は、狩野さんのページから入手可能。
    +http://khdd.net/kanou/im/kyuuri.html +

    +
  18. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-036.xhtml b/docs/vol2/xhtml/p-036.xhtml new file mode 100644 index 0000000..08632f6 --- /dev/null +++ b/docs/vol2/xhtml/p-036.xhtml @@ -0,0 +1,110 @@ + + + + + +第49章 ハッカーと仕事 + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay49 +
+

+まつもと ゆきひろのハッカーズライフ
+ハッカーと仕事 +

+
+

[UNIX USER, 2005年6月号]

+
+

ハッカーっぽい活動はどうしても自分の趣味に陥りがちで、どうしても生活の安定とは遠ざかる傾向があります。それは社会の仕組みの一部で20年経ってもあまり変化していません。そんな中で、ハッカーらしく生きながら、生活を成り立たせる方法について語っています。ここで紹介したいくつかの手段、たとえば趣味のハックと仕事のハックを分ける、ハッカー活動を上司に認めてもらう、起業するなどは現代でも有効です。この時点ではあまり具体化していませんでしたが、ハッカー活動を背景にして、企業の技術顧問に収まるという手段を取る人も増えました(私もいくつかの企業の技術顧問をしています)。あと、ここでは書いてなかったタイプとして、起業や投資などで財を成し、アーリーリタイアしてプログラミング三昧という人も何人かは知っています。こういうのも憧れますが、冷静に考えると、現在の私の状態も、好きなプログラミングをして、カンファレンスなどに出席し、ときどき技術顧問として技術者と交流し、コミュニティ活動もするなど、かなり時間に余裕を持って生活しているので、アーリーリタイアと大差ないのかもしれません。

+
+

ハッカー傾向のある人々は、正直あまりビジネス向けではないかもしれません。なにしろ彼らの美徳は「無精」「短気」「傲慢」ですし、好きなことにはのめりこむタイプですが、逆に嫌いなことはあまり我慢しないかもしれません。しかし、ビジネスとはそんなに甘いものではないのかもしれません。

+

しかし、ハッカーも人間です。眠たくもなれば、お腹も空きます。いくらハッカーでも、かすみを食べて生きていくわけにはいきません。そこで今月は、ハッカーの仕事生活を紹介しましょう。もっとも私の周辺のごく限られたサンプルからの情報なので、独断と偏見があることはあらかじめご承知ください。

+
+

論文や卒業がネック

+
+

ハッカーが多く見受けられるのは、やはり大学や研究機関のたぐいです。大学や研究機関から生まれた「ハッカー技術」は、UNIXやC(AT&Tベル研究所)、BSD(カリフォルニア大学バークレー校)、TeX(スタンフォード大学)などを始めとして数多く存在します。この半世紀、研究職ハッカーがコンピュータサイエンスを発展させてきたと言っても過言ではありません。

+
+

これらはハッカーの知的探求心を満足させつつ、職業として成立する貴重な分野です。ハッカーにとって理想的、と思えるでしょうが、世の中はそれほど甘くありません。研究社会では、主に論文によって業績が評価されます。プログラムを作ることが大好きで、それだけをやっていたいソフトウェア系ハッカーにとって、論文書きはそれなりに苦痛を伴います。人類全体への貢献を考えると、プログラミングが得意なハッカーにはプログラミングだけをさせておいたほうがためになるような気もしないでもないのですが、社会の仕組みとはそうなっていないようです。

+

大学の職員ではなく、学生ハッカーも見逃せません。学生は若さとそれに伴う行動力に満ちあふれていますし(そうでない人もいますが)、また、時間に余裕があるケースが多いので、なかなか面白い作品を作り上げることもあります。学生の作品として最も有名なものは、ヘルシンキ大学時代のLinus Torvalds氏によるLinuxがあります。学生ハッカーには試験やアルバイトといった障害もさることながら、「いつかは卒業してしまう」という厳しい(?)現実があります。Ruby界でもそのような例があり、一番印象的なのは、Intelのx86系を対象にしたJITコンパイラrubyjitと、同じ作者によるRubyをCに変換するrb2cです。いずれも非常に面白いプロジェクトだったのですが、作者の卒業に伴い開発が中止してしまい、後を継ぐ勇者が現れませんでした。残念なことです。

+
+
+

本業と副業が逆転

+
+

学生が卒業すると、就職することになります。大学に残り研究員になることについてはすでに述べたので、企業に就職した場合について考えてみましょう。なお少数ながら、就職して業務でソフトウェアを開発するうちにプログラム開発の面白さに気付きハッカー魂に目覚める「社会人デビュー組」もいます。

+

社会人ハッカーの多くは「副業タイプ」です。本業の仕事は業務命令としてこなしつつ、本当に面白いプログラミングは自分の趣味として行います。仕事の合間とか、帰ってから自宅でとか、寝る時間を削ってとか。まあ、プログラミング以外の趣味を持つ人はたくさんいるわけですから、それと同じだと考えられます。食べるためにそれなりの仕事をして、自分の趣味のためにプログラミングをする。なかなか安定したライフスタイルかもしれません。

+

問題は、ハッカー的人格にあります。ハッカーはブレーキが壊れている傾向があるため、ついつい好きな方にのめりこんでしまいます。また、成果をオープンソースソフトウェアとして公開したりすると、そのソフトウェアのユーザーが増えるにつれ、サポートやメンテナンスなどにかかる時間が半端でなくなってきます。私も最初は、社会人プログラマーの余暇としてRubyの開発を始めました。数年は仕事の合間を縫って開発してきましたが、Rubyのユーザーが増え、メーリングリストのメールが一日数十通を超えるようになると、そのメールを読んで返事を書き、また報告されたバグを修正するだけでも、一日の大部分を消費するようになります。そうなると破綻が見えてきます。ここで、副業タイプのハッカーは次のアクションへの選択を迫られます。

+
+
+ +

次の一手

+
+

考えられる選択肢はいくつかあります。すぐに思い付くのは以下のようなものでしょうか。いずれも実際に行われている選択肢です。

+
    +
  • フリープログラマーになる。仕事をする期間と「ハックする」期間を明確に分ける

  • +
  • 上司を説得してハッカーというもの理解してもらう

  • +
  • ハッカーに対して理解のある職場に転職する

  • +
  • 起業する

  • +
+

フリープログラマーというのは安定性に欠けるのは難点ですが、自分の思いどおりの仕事ができます。ただし、自分で仕事を見つけるためのコネクションが不可欠ですし、自己管理能力も求められます。ハッカーはこの自己管理能力の弱い人が多いようにも思われますが。

+

上司を説得した例としては、WideStudioの平林さんがいらっしゃいます。彼はIPA1 の「未踏プロジェクト2」に応募し、スーパークリエーター3 に認定されることで上司に理解してもらい、自分のハックの成果(=WideStudio)を仕事にすることを認めてもらうことに成功しました。このように、未踏プロジェクトを利用して自分のハックを世に認めてもらった人は多いようです。

+

私自身は、転職によって居場所を見つけました。1997年に現在の職場であるネットワーク応用通信研究所に転職したわけですが、転職の際には「Rubyの開発者」として採用していただきました。この会社はハッカーの扱い方を心得ていて、居心地のよい職場環境を提供してくれています。おかげで転職以来8年間、快適に仕事をさせてもらっています。ハッカーの多くは経済的成功への野心が少なく、食うに困らない収入があれば、適当に面白い仕事と技術的チャレンジ、および他のハッカーとの良好な交流があるだけで満足します。ハッカーの生産性は「普通の技術者」の数倍から数十倍に相当しますから、会社にとっても十分にお得なわけです。また、有名なハッカーには企業の看板、あるいは広告塔としての働きもありますから、そこでも有効に活用することができます。

+

ハッカーと言ってもいろんな種類の人間がおり、中には野心のあるタイプの人もいます。プログラムをハックするというよりも、技術を生かして社会をハックするといった感じでしょうか。ベンチャー文化が発達したアメリカでは、ハッカーによる技術を前面に押し出したベンチャービジネスがいくつも見受けられます。成功して大金持になるハッカーもいるわけです。たとえば、Lispを「秘密兵器」としたViaWebというインターネットショッピングASP事業で成功した(『ハッカーと画家』4 の、といったほうが有名でしょうか)Paul Graham氏、Netscape Navigatorのリードプログラマーとして有名なMark Andreessen氏などがいます。あまり起業とかベンチャーが盛んではない日本ではあまり目立った「起業ハッカー」はいない5 のですが、日本の将来のためには社会が変化して、このようなハッカーがどんどん出てくるようになったほうがよいのではないかと思います。

+
+
+ +

Win-Winの関係

+
+

ハッカーは悪い側面だけが強調されてしまうと、社会適合性の欠けたただのぐうたら社員ということになってしまいます。けれども、ハッカーたちは、実は優れた生産性を持つプログラマーであるだけでなく、新しい技術への鋭敏なアンテナの持ち主でもあります。このような人材は活用の仕方によっては、企業の「秘密兵器」になり得るのではないでしょうか。ハッカーは知的好奇心を満足させるプロジェクトや住み心地のよい環境を手に入れてハッピー、企業は優れた生産性を活用して業績を上げてハッピー、というような構図が一般的になるとよいのですが。

+
+
+
+
+
    +
  1. +

    IPA

    +

    情報処理推進機構。経済産業省所管の独立行政法人。情報処理技術者試験や各種支援事業を行う他、セキュリティ情報の提供を行うJP-CERTの運営も行っている。
    +http://www.ipa.go.jp/ +

    +
  2. +
  3. +

    未踏プロジェクト

    +

    正式名称は「未踏ソフトウェア創造事業」。今までにないソフトウェアの開発を行う個人を発掘することを主眼とした事業。まつもとも2000年度(第1回)で採用されている。
    +http://www.ipa.go.jp/jinzai/esp/ +

    +
  4. +
  5. +

    スーパークリエーター

    +

    未踏プロジェクトでめざましい成果をあげた人に対してIPAが授ける「称号」。未踏プロジェクト採用者全員に与えられるわけではない。ちなみにまつもとはもらっていない。 +

    +
  6. +
  7. +

    『ハッカーと画家』

    +

    オーム社から翻訳書が発行されている。
    +http://ssl.ohmsha.co.jp/cgibin/menu.cgi?ISBN=4-274-06597-9 +

    +
  8. +
  9. +

    目立った「起業ハッカー」はいない

    +

    元オン・ザ・エッヂ(現ライブドア)CTOの小飼弾さんは日本における例外的な起業ハッカーかもしれない。彼の日本人的でないセンスのおかげだろうか。 +

    +
  10. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-037.xhtml b/docs/vol2/xhtml/p-037.xhtml new file mode 100644 index 0000000..ed6fb65 --- /dev/null +++ b/docs/vol2/xhtml/p-037.xhtml @@ -0,0 +1,102 @@ + + + + + +第50章 Emacs 対 vi + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay50 +
+

+まつもと ゆきひろのハッカーズライフ
+Emacs 対 vi +

+
+

[UNIX USER, 2005年7月号]

+
+

有名なエディタ対立であるEmacs対Viを、MITのLisp文化とAT&Tベル研のUNIX文化の違いであるとまとめたのは、個人的にはすばらしい考察だと自画自賛しているのですが、ほとんどの人にとってはどうでもいいことのようで、当時でもあまり話題になることはありませんでした。そうこうしているうちに、IDE(統合開発環境)が台頭し、特にVSCodeの登場以降は、EmacsもViも「ロートルが使うエディタ」という印象が強くなってしまいました。

+

まあ、実際、若い人でEmacsを使う人はもうほとんどいませんし、Vi(この場合はVimかNeoVim)を使う人もそれほどは多くありません。ただ、EmacsもVimも進歩を続けていて、たとえばLSP(Language Server Protocol)を話して、VSCodeっぽいリッチなコード補完ができたりするんですけどねえ。

+
+
+

論争のタネ

+
+

ハッカーといっても一枚板ではありません。いろんな種類のハッカーがいます。悪ぶっているだけの「自称ハッカー」は論外としても、いろんな趣味、いろんな文化のハッカーが存在します。そして彼らは、しばしば自分の意見や文化について論争を起こします。そのような論争のテーマはたくさんありますが、典型的なものは「どのプログラミング言語が一番優れているか」「どのOSが最もよいか?」、または「サイコーのエディタはEmacsかviか?」などです。今回は最後にあげた「Emacsかviか?」を例に、ハッカー文化圏について眺めてみましょう。

+
+
+

TECOから進化したEmacs

+
+

オリジナルのEmacsは、Richard StallmanがTECOエディタ用に開発したマクロです。Richard StallmanといえばGNU活動で有名になりましたが、もとは超一流のハッカーで、ばりばりプログラムを書く人物であることを忘れてはいけません。TECOはマクロ機能を備えたラインエディタで、Stallmanはそのマクロ機能を駆使して、スクリーンエディタである最初のEmacsを書き上げたわけです。

+
+

TECOによるEmacsが実装されたのは1976年だといわれています。その後、Javaの設計者となるJames Goslingが1981年にUNIX版Emacsの開発を行います。GoslingによるEmacs(通称Gosling EmacsまたはGosmacs)は、MockLispと呼ばれるLispもどきの言語を使った拡張機能を持っていました。しかし、GoslingはGosmacsの権利をUnipressという企業に売却してしまい、StallmanはGosmacsをベースにした新しいEmacsの開発を行えなくなってしまうのです。

+

いずれにしても、Unipress Emacsをベースに作業することができなかったStallmanは、再びゼロからEmacsを開発しました。これが現在広く使われているGNU Emacsです。GNU EmacsはGosmacsの拡張性を参考にしていますが、MockLispのようなカスタマイズ用のまがいものの言語ではなく、より「ちゃんとした」LispであるEmacs Lispを内蔵しています。Emacsの本体は基本的な編集機能とEmacs Lispを内蔵しているだけで、Emacsの便利な機能のほとんどはEmacs Lispを使って後付けで実装されています。ということは、一般のユーザーもEmacsの基本機能をベースにしてさまざまな機能を実装することができるということです。事実、Emacsではプログラミング言語を支援する各言語モードなど、さまざまな編集支援機能がユーザーからの寄贈によって追加されています。また、エディタの機能を超えて、メールリーダー、ニュースリーダー、Webブラウザ、ゲームなどなど、あらゆる領域での拡張機能が提供されています。Emacsはもはや単なるエディタではなく、1つの環境、あるいは一種のOSと呼べるくらいにまで発展しているわけです。

+
+
+

edから進化したvi

+
+

viはVisual Editorの略だといわれています。BSDの立役者で、長らくサン・マイクロシステムズの副社長だったBill Joyによって1976年頃に開発されたviは、UNIXの標準ラインエディタであったedおよびexをベースにスクリーンエディット機能を追加したものです。edマクロを使った非対話モードの作業が簡単に行える点がメリットであり、いつでもラインエディットに戻れる安心感があります。最近では、どちらも必要になることは珍しくなりましたが、私が学生のころは端末がおかしくなってviをラインモードで使う1 こともときどきありました。

+

そういえば、学生時代の友人で普段からラインエディタexを愛用していたEくんは、ある日「lessのように画面を直接見ながらedコマンドで編集できるエディタがあれば完璧だ。lessedと名付けよう2」と言いながら、lessのソースコードをハックしていました。見かねて「それはviというものなんだよ」と教えてあげると彼は感動していました。1988年のことです。毎日UNIXを使いながらviを知らなかった彼は大物なんだか、変人なんだか。

+
+
+ +

ニュージャージー対マサチューセッツ

+
+

viはUNIX哲学の体現だといえます。ニュージャージー州にあるAT&Tベル研究所で始まったUNIXの哲学は、「単機能のツールを組み合わせた柔軟性」です。UNIXツールのことをよく知る人なら、各種フィルタを組み合わせた「パイプライン処理」のことを思い起こすはずです。cat, grep, awk, sed, nroff, pic, tblなどの単機能のツールをパイプラインでつなげて加工するのは職人芸と言ってもよいでしょう。UNIX哲学では、もちろんエディタもそのようなツールの一種として見なされます。2つのファイルの差分を取るツールdiffは、「-e」オプションを指定するとed形式で出力します。この出力結果の最後に「w」を付加してedに渡すと、自動的に書き換えて保存してくれます。もっともこのやり方では、ファイルが少しでも変更されていると悲惨な結果になるので、最近はedよりも少し賢いpatchコマンドを使うことがほとんどでしょうけど。

+

このようにviは、「小さいことはいいことだ」あるいは「よけいなことはしない」という思想を反映したツールなのです。

+

一方のEmacsはまた違う思想を反映しています。今ではUNIXで使われることが多く、UNIX系エディタとみなされやすいEmacsですが、オリジナルが開発されたのはUNIX上ではありません。Emacsが反映している(と思われる)のは、MIT(米マサチューセッツ工科大学)のLisp文化です。Lispは1958年頃にMITで誕生3 しました。もうすぐ50年になるんですね。ほとんどのLisp処理系は対話的に処理を行い、必要となる関数を次々定義していくことで環境を整備するという開発スタイルを取ります。このような環境で育ったLispハッカーにとって、Lisp処理系に編集機能を追加し、エディタにまで育て上げるというアプローチはごく自然なものだったのだろうと想像します。1970年代、Stallmanが名うてのLispハッカーであったことを考えると、「彼をして、このエディタあり」ということだったのでしょう。

+

Emacsの魅力はその拡張性、さらにいえばその「プログラム可能性」です。Emacsを開発のベースに使えば、画面操作などの標準で備わっている基本的な機能を利用して効率よくプログラムを開発できます。私自身、日常的な編集を支援するEmacs用の小さなツールをたくさん書いていますし、さらにRubyの変数とインデントを支援するruby-mode、Emacs上のメールリーダーを2つ(cmailとmorq4)開発しています。

+

しかし、一方ではEmacsはもともとはエディタでありながら、あらゆる機能を飲み込んでいってしまい、どんどん複雑化、肥大化しています。これを称して「キッチンシンク」と呼ぶ人もいます。洗い物を何でも雑多に突っ込んだ台所の流しのようだと揶揄やゆしているのです。

+

小さいツールを組み合わせることによって柔軟性を提供するUNIX思想、プログラム可能なツールによる柔軟性を提供するLisp思想。2つのエディタの対立は2つの思想の対立でもあったのです。

+
+
+ +

NASAの乱入

+
+

しかし、後にEmacsがUNIX上の代表的なエディタとみなされるようになって、戦況はやや混乱します。どちらもUNIXの仲間と思われてしまって、UNIX対Lispというわかりやすい対立軸が見えにくくなったからです。そして、別の「キッチンシンク」であるPerlの参戦によって、この対立軸は完全に見えなくなります。

+

Perlは、UNIX系文化がもともと個別のツールで提供していた機能をすべて1つの言語で提供しようというアプローチのものです。開発者のLarry Wall(当時NASAのジェット推進研究所所属)がLisp系文化の影響を受けていたかどうかは定かではありませんが、「キッチンシンク」アプローチの有効性を広く示したことには変わりありません。結果的にPerlはUNIX文化とLisp文化の掛け橋になったといえるでしょう。

+

皆さんも今度「Emacs対vi」の論争を見たら、それは遠い昔のUNIX対Lispの思想の対立の名残なのだと思ってください。思想は形を変えながら今もハッカー文化の中に息づいているのです。

+
+
+
+
+
    +
  1. +

    viをラインモードで使う

    +

    スクリーンエディットに必要な端末の制御コードがわからないとき、viはラインモードで起動する。これは要するにedとして動作するということ。 +

    +
  2. +
  3. +

    lessedと名付けよう

    +

    ここでviのことを黙っていたら、もしかしたらEmacs, viに並ぶ第三のハッカー用エディタができていたかもしれない。惜しいことをした。 +

    +
  4. +
  5. +

    Lispは1958年頃にMITで誕生

    +

    Lispの父はJohn McCarthy。実際にプログラミング言語として開発したのはSteve Russellだといわれている。 +

    +
  6. +
  7. +

    morq

    +

    morqは全文検索ベースのメールリーダー。現在のユーザーは私一人。2005年夏一般公開予定である。 +

    +
  8. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-038.xhtml b/docs/vol2/xhtml/p-038.xhtml new file mode 100644 index 0000000..cbf98f6 --- /dev/null +++ b/docs/vol2/xhtml/p-038.xhtml @@ -0,0 +1,128 @@ + + + + + +第51章 Emacs 対 vi + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay51 +
+

+オープンソース開発って何だろう +

+
+

[Linuxソフトウェアアンテナ, 2005年7月号]

+
+

技術評論社のムック『Linuxソフトアンテナ』オープンソース特集の一部となる記事です。オープンソースの定義からその精神、メリットなどについて記述しています。

+

注目すべきは、オープンソース開発者のさまざまな具体例をあげているところです。これらの開発者は当時の個人的な知人の経験に基づいて記述されています。いずれも具体的なモデルがいるか、あるいは複数の実在人物の経験を組み合わせたものです。最後例6「マツモトユキヒロさん」は誰だかはっきりわかりますね。他にも例1は学生時代の私、例2はRubyを作る前の私、例3は同僚複数の経験の組み合わせ、例4はネットワーク応用通信研究所社長(当時)の井上浩氏、例5はRubyコミッターの中田さんがモデルです。

+
+

「オープンソース開発」とは、簡単にいうと「オープンソースのやり方にしたがったソフトウェア開発」です。しかし、その一言で終わってしまうにはあまりにも深い意味を持つ言葉でもあります。

+
+

「オープンソース」の定義

+
+

まず、「オープンソース」という言葉の定義から見てみましょう。オープンソースと聞くと、「ソースがオープン」だからソースコードが一般に公開されていればそれでいいんでしょ、と考えてしまいがちですが、そもそも「オープンソース」という言葉は世に出たその瞬間から明確な定義をもって誕生した言葉なのです。OSI(Open Source Initiative)によればオープンソースの定義は次に示すとおりです。この原文はhttp://www.opensource.jp/osd/osd-japanese.htmlで入手可能です。

+
+
+

オープンソースの定義

+

オープンソースの定義

+

八田 真行訳、2004年2月21日

+


+

「オープンソース」とは、単にソースコードが入手できるということだけを意味するのではありません。「オープンソース」であるプログラムの頒布条件は、以下の基準を満たしていなければなりません。

+

1. 再頒布の自由

+

「オープンソース」であるライセンス(以下「ライセンス」と略)は、出自の様々なプログラムを集めたソフトウェア頒布物(ディストリビューション)の一部として、ソフトウェアを販売あるいは無料で頒布することを制限してはなりません。ライセンスは、このような販売に関して印税その他の報酬を要求してはなりません。

+

2. ソースコード

+

「オープンソース」であるプログラムはソースコードを含んでいなければならず、コンパイル済形式と同様にソースコードでの頒布も許可されていなければなりません。何らかの事情でソースコードと共に頒布しない場合には、ソースコードを複製に要するコストとして妥当な額程度の費用で入手できる方法を用意し、それをはっきりと公表しなければなりません。方法として好ましいのはインターネットを通じての無料ダウンロードです。ソースコードは、プログラマーがプログラムを変更しやすい形態でなければなりません。意図的にソースコードをわかりにくくすることは許されませんし、プリプロセッサや変換プログラムの出力のような中間形式は認められません。

+

3. 派生ソフトウェア

+

ライセンスは、ソフトウェアの変更と派生ソフトウェアの作成、並びに派生ソフトウェアを元のソフトウェアと同じライセンスの下で頒布することを許可しなければなりません。

+

4. 作者のソースコードの完全性(integrity)

+

バイナリ構築の際にプログラムを変更するため、ソースコードと一緒に「パッチファイル」を頒布することを認める場合に限り、ライセンスによって変更されたソースコードの頒布を制限することができます。ライセンスは、変更されたソースコードから構築されたソフトウェアの頒布を明確に許可していなければなりませんが、派生ソフトウェアに元のソフトウェアとは異なる名前やバージョン番号をつけるよう義務付けるのはかまいません。

+

5. 個人やグループに対する差別の禁止

+

ライセンスは特定の個人やグループを差別してはなりません。

+

6. 利用する分野(fields of endeavor)に対する差別の禁止

+

ライセンスはある特定の分野でプログラムを使うことを制限してはなりません。例えば、プログラムの企業での使用や、遺伝子研究の分野での使用を制限してはなりません。

+

7. ライセンスの分配(distribution)

+

プログラムに付随する権利はそのプログラムが再頒布された者すべてに等しく認められなければならず、彼らが何らかの追加的ライセンスに同意することを必要としてはなりません。

+
+

8. 特定製品でのみ有効なライセンスの禁止

+

プログラムに付与された権利は、それがある特定のソフトウェア頒布物の一部であるということに依存するものであってはなりません。プログラムをその頒布物から取り出したとしても、そのプログラム自身のライセンスの範囲内で使用あるいは頒布される限り、プログラムが再頒布されるすべての人々が、元のソフトウェア頒布物において与えられていた権利と同等の権利を有することを保証しなければなりません。

+

9. 他のソフトウェアを制限するライセンスの禁止

+

ライセンスはそのソフトウェアと共に頒布される他のソフトウェアに制限を設けてはなりません。例えば、ライセンスは同じ媒体で頒布される他のプログラムがすべてオープンソースソフトウェアであることを要求してはなりません。

+

10. ライセンスは技術中立的でなければならない

+

ライセンス中に、特定の技術やインターフェースの様式に強く依存するような規定があってはなりません。

+
+

1998年初頭、マイクロソフトとの競争に敗れつつあったネットスケープ社は起死回生のウラ技として、1つの大胆な戦略を立てました。それは自社の主力製品であるNetscape Navigatorの新バージョンをフリーソフトウェアとして公開し、コミュニティの力で成長させようというものです。しかし、フリーソフトウェアの「フリー」という言葉が「自由」よりも「無料」を想起させるのが悩みの種でした。ライバルのInternet Explorerも無料ですから、マーケティング戦略的にはもっと差別化できる用語が必要です。そこでネットスケープ社は論文『伽藍とバザール』で知られるEric Raymondに相談しました。1998年2月3日、Raymondは何人かの「フリーソフトウェアビジネスの有名人」と会議を開き、その会議の最中に「オープンソース」という言葉が誕生したのでした。同じ月のうちにBruce PerensがまとめていたDebian Freesoftware Guidelineをベースにして上記のオープンソースの定義(の最初のバージョン)が策定されました。1998年2月23日にネットスケープ社は自社のブラウザのソース公開を行うというアナウンスを行い、これが「オープンソース」という言葉が広く使われるようになった最初です。

+

オープンソースの定義と用語の歴史的経緯を考えると、以下のことがいえると思います。

+
+

オープンソースはライセンスのあり方の定義である

+

オープンソースの定義には個別のソフトウェアの性質について何も語っていません。定義されているのはそのは頒布条件とライセンスだけです。オープンソースという考え方が注目しているのは、「どんなソフトウェアか」ではなく「ソフトウェアをどう扱えるか」であるということです。

+

オープンソースはマーケティング用語である

+

オープンソースという用語はビジネスを強く意識しています。1980年代以降長く使われてきたフリーソフトウェアという用語を使わずあえて新しい用語を作ったのは、「無料」との混同を嫌ったこともありますが、効果的なマーケティングに新語が効果的であることも理由でしょう。

+
+

自由を守るとことを目的とするフリーソフトウェアと比較すると、オープンソースはずいぶん実利主義に見えます。実際、オープンソースという言葉がなければ、現在のようにソースが公開されていて自由に利用できるソフトウェアは広まらなかったと思います。

+
+
+ +

オープンソースでできること

+
+

先ほど、オープンソースはソース公開ではない、と述べました。オープンソースの定義によればオープンソースであることとは、ただ単にソースコードが入手できることではなく、入手したソースコードをどのように取り扱うことが許されるべきかについて明確な規定があります。これはいったいなぜなのでしょうか。オープンソースの定義の10ヶ条のような細かな「制約」がありながらなお「自由」というのはどういうことなのでしょうか。

+

それは、一見制約に見える定義をゆるめると結局そのソフトウェアに対して「できること」が減ってしまうからです。オープンソースの定義を満たしている限り、ソフトウェアの利用者はそのソフトウェアに対して著作権者の許可をいちいち求めることなくどこまでのことができるのか明確です。たとえば、あるソフトウェアのライセンスに「利用は平和目的に限定される」という条項があったとしましょう。平和目的は立派な心がけでそれ自体に避難すべきところはありません。しかし、そのソフトウェアを含むディストリビューションは、受け取った人がそのソフトウェアを平和目的以外に用いないように確認する必要性が発生するかもしれません。そんなことが起きれば、ソフトウェアの利用に大きな制限がかかってしまうでしょう。そういうことを避けるため、そのようなライセンスはオープンソースの定義の第6項で禁止されています。面倒に見える決まりはみなソフトウェアを安心して利用するためにあるのです。

+

あるソフトウェアのライセンスがオープンソースの定義を満たしているとわかれば、少なくとも以下のことを自由に行ってよいことがわかります。

+
    +
  • そのソフトウェアを再配布する

  • +
  • そのソフトウェアを改造する

  • +
  • 改造したソフトウェアを配布する

  • +
  • そのソフトウェアをビジネスの一部に組み込む

  • +
+

これらのことは、開発者にとっては安心して開発に参加できることを意味します。いちどオープンソースソフトウェアとして公開されたものは、将来、主開発者に何かあって開発が中断されても、有志が開発を継続することができますから、消え去ってしまうことを心配する必要がありません。自分の労力が無駄にならないという安心感があります。

+

継続の安心感は、ビジネスマンにとっても有効です。自分のシステムのコンポーネントとして組み込んだソフトウェアがベンダーの都合でサポートが中止される事態は、商用ソフトウェアにおいて珍しいことではありません。しかし、オープンソースソフトウェアならば、ソースコードが入手できているので、必要ならば必要なだけ自分でサポートを行うこともできます。

+

ビジネスにとってのオープンソースのメリットは継続性だけではありません。最近はソフトウェア産業も成熟してきているので、新規参入者が既存のビッグプレイヤーを打ち負かすのは容易なことではありません。打ち負かすどころか生き残ることさえ難しいでしょう。生き残りに有効な方法は、既存のプレイヤーが採用していない新しいルールの下で勝負することです。この新しいルールとしてオープンソースが有望なのです。たとえばデータベースを考えてみましょう。商用データベースはOracleにしてもIBMのDB2にしても長い時間とたくさんのお金をかけて開発されてきたものです。これに勝つのは並たいていのことではありません。しかし、スウェーデンのMySQL ABという小さな会社は自社開発のデータベースエンジンをオープンソースソフトウェアとして公開することで、これらのビッグプレイヤーと対等に渡り合っています。

+
+

ソフトウェアビジネスにおいてソースコードを他人にあげてしまうことは許容できないと考える人は多くいます。しかし、考えてみればソースコードを隠蔽いんぺいすることによって稼げるお金はどんどん小さくなっています。現在のソフトウェア産業構造は、ソフトウェアパッケージが1割、自社向けシステムの開発が3割、受注システムが6割なんだそうです。今やソフトウェア産業のお金が動くところは、ソリューション構築やサービスの提供、そしてサポートなのです。ですから、コアコンポーネントのソースコードを無償で提供しても、それによってもうける手段はそれこそ山のようにあるわけです。いや、マイクロソフトほどはお金を生み出せないかもしれませんが。

+
+
+

オープンソース関係者の気持ち

+
+

一言にオープンソースといっても、それに関わる人々の気持ちはそれぞれです。ここではオープンソースに関わる人々のさまざまな立場と気持ちを紹介します。なお、ここで紹介する人々は現実の人間をモデルにしていますが、あくまでもフィクションです。

+
+

例1: 学生

+

タナカ・カナタさん(22)は国立大学工学部の4年生です。最初は研究に利用するツールとしてオープンソースソフトウェアに出合いました。カナタさんはStallmanの唱えるソフトウェアの自由を信奉しており、ソフトウェアはできる限り自由であるべきであると信じています。ですから、彼の使うソフトウェアはすべてソースコードが入手できるオープンソースソフトウェアです。カナタさん自身はオープンソースソフトウェアと呼ぶよりもフリーソフトウェアと呼ぶ方を好みます。しかし、この世から「不自由なソフトウェア」を駆逐するべきだというほど過激な思想を持っているわけではありません。現在、カナタさんはオープンソースソフトウェアの主開発者として活躍しています。もともとはインターネットで他の人が作ったツールを改造していたのですが、変更点を還元しようと開発者にコンタクトしたら「もう使わなくなってしまったのでご自由にどうぞ」といわれてしまいました。そのままにするのももったいないので自分でメンテナンスすることにしました。メーリングリストには数十人のユーザーが集まっています。コミュニティの規模としては小さいですが、なごやかな雰囲気で活発に開発が行われています。カナタさんの開発はあくまでも趣味の領域で、本業の研究の合間を縫って行われています。

+

例2: 職業プログラマー

+

シミズ・ヒロシさん(31)は職業プログラマーです。ヒロシさんはプログラマーとしての自分に箔を付けるために名刺の代わりになるソフトウェアがあればよいと感じてオープンソフトウェアの開発を行いました。自分の能力を見てもらうためにはソースコードも含めてすべて公開するオープンソースが最適だと考えたのです。ヒロシさんの試みは成功し、優秀なプログラマーとして見なされる機会が増えてきました。ヒロシさんは自分のソフトウェアをひっさげて最近転職を果たしました。勤務条件も向上しました。転職先の会社は一定の範囲内でオープンソースソフトウェアの開発を認めてくれています。最近では雑誌の記事を書くなどの副業収入も少し入ってくるようになりました。

+
+

例3: 若手SE

+

コダマ・マドカさん(25)は某ソフトハウスに勤める若手SEです。SEという肩書を持ってはいるものの、実際には顧客との交渉から設計、コーディングに至るまで何でもこなします。最近、マドカさんは上司から、業務で開発したプロダクトをオープンソース化するように命じられました。これまでマドカさんはPHPやMySQLなどオープンソースソフトウェアのユーザーだったわけですが、急に開発者として参加することになって戸惑っています。業務開発の世界とオープンソースの世界とではやり方がずいぶん違うようです。でも、業務としてオープンソースに関われることはもしかしたら幸せなことかもしれないと考え始めています。

+

例4: オープンソース企業CEO

+

イノウエ・コウジさん(45)はソフトウェア系ベンチャー企業の社長です。コウジさんは8年前、仲間と一緒に起業しました。Linux系サーバーの構築とソフトウェア開発が主たる業務です。以前SEの経験もあるコウジさんはRubyなどを使ったソフトウェアの開発も行いますが、あくまでも趣味のレベルです。しかし、会社のコアとなる技術としてオープンソースは欠かせないと感じています。コウジさんの会社のように小さなソフトウェア企業が大企業と渡り合うためには、オープンソースが重要な武器になると考えているからです。コウジさんの会社のソフトウェア開発はほぼ例外なくオープンソースソフトウェアを利用して行われます。これにより、コストや開発期間で競争力を獲得しています。また、ブラックボックスがないので問題が発生したときにどこまでも対処可能であるという安心感も見逃せません。コウジさんはただ利用するだけでは差別化が十分ではないとして、積極的に自社開発ソフトウェアをオープンソース化したり、オープンソース開発者を雇用したりしています。おかげでオープンソース業界では知る人ぞ知る企業としてもポジションを獲得しつつあるようです。

+

例5: フリープログラマー

+

ゴトウ・ユウゴさん(38)はフリープログラマーです。普段は派遣のプログラマーとして働いたり、知人から紹介してもらったソフトウェア開発を行っています。ユウゴさんのもう1つの顔はオープンソース開発者です。ユウゴさんはあるオープンソースソフトウェアの開発にも関わっています。ユウゴさんは主開発者ではありませんが、協力者として長らく開発に参加してきました。メーリングリストに報告されたバグをユウゴさんがいち早く修正したりすることは珍しくありません。主開発者からの信頼も厚く、ソースコードへのコミット権もいただいています。ユウゴさんが提案して採り入れられた機能も数多くあります。プログラミングが大好きなユウゴさんにとって、オープンソース開発への参加は最大の趣味と言ってもよいでしょう。ソフトウェアがオープンソースでなければユウゴさんは参加することはできなかったわけですから、ユウゴさんはオープンソースに感謝しています。

+

例6: 職業オープンソース開発者

+

マツモト・ユキヒロさん(40)は某ソフトハウスに勤務するソフトウェア技術者です。ユキヒロさんはここ10年ほどあるオープンソースソフトウェアを開発しています。ユキヒロさんの開発したソフトウェアは日本のみならず世界中で使われています。ユキヒロさんはもともと副業としてオープンソースソフトウェアを開発していましたが、サポートなどに割かれる時間が本業に影響を与えているのが悩みでした。そこで、7年前オープンソースソフトウェアの開発者として現在の勤務先に転職しました。ユキヒロさんは技術コンサルタントや技術マネージャとしても働きつつ、多くの時間をオープンソースソフトウェアの開発に割いています。勤務先はユキヒロさんがオープンソース開発者として露出することで間接的に利益を上げているので、そのような時間の使い方を認めています。ユキヒロさんは開発だけでなく、各種イベントでのプレゼンテーションや雑誌記事の執筆なども積極的にこなしています。それらに割かれる時間が多すぎて開発時間が少なくなっているのが最近の悩みです。

+
+ +

ここであげたのは多種多様なオープンソース関係者のうちごく一部ですが、それでも参加する動機、時間の使い方、経済的事情などさまざまであることがうかがえると思います。あくまでも趣味で参加する人もいれば、逆に単に仕事として参加する人もいます。

+
+
+

まとめ

+
+

オープンソースはソフトウェアを有効に利用する新しい手法です。これは単にソフトウェアのソースコードを公開するだけでなく、すべての人に一定の自由を保証するライセンスを含むよう明確に定義されています。オープンソースへの関わり方は多種多様ですが、いずれにしてもオープンソースが保証する自由が大きく影響を与えています。オープンソースに関わる人のほとんどは何らかの形でこの自由を利用しようとしています。利用の仕方は自分が自由を満喫するためだったり、ビジネス上で優位に立つためだったりしますが。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-039.xhtml b/docs/vol2/xhtml/p-039.xhtml new file mode 100644 index 0000000..05e8965 --- /dev/null +++ b/docs/vol2/xhtml/p-039.xhtml @@ -0,0 +1,110 @@ + + + + + +第52章 ハッカー環境問題 + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay52 +
+

+まつもと ゆきひろのハッカーズライフ
+ハッカー環境問題 +

+
+

[UNIX USER, 2005年8月号]

+
+

ハッカーが自分の環境に対して手を加えたい欲求を「環境問題」と呼ぶという一発ネタ。ハッカー気質のある人は暇があると自分の環境に手を加えてしまいたい欲があり、たまにうっかり環境を破壊してしまい、むしろウキウキしながら環境復旧するマッチポンプのような状況を発生させることがあります。これは問題解決を楽しみとするハッカー気質の持ち主にとって大変楽しい活動ですが、環境復旧にかかりきりになっている間は生産性が下がってしまうので、周囲に迷惑をかけてしまう可能性もあります。これもまた「ハッカー環境問題」の一種かもしれません。

+
+
+

環境を改変していく能力

+
+

「環境問題」といっても、地球温暖化などの話ではありません。ハッカーが生活する空間、つまりコンピューティング環境のお話です。

+

地球上には数えきれないほどの種類の生物が棲息していますが、人間ほど広い範囲に住んでいる生物はいないと思います。人間は灼熱の赤道直下から、極寒の極地にまではびこっています。これは生物としての耐久力が高い1 からではありません。人類の繁栄は、衣服や住居によって自分の周囲の環境をコントロールすることで実現されているのです。どんなに外が寒くても快適な住居を作り上げる能力、暖房や冷房などを発明する能力、そしてなによりも自分の周囲の環境を積極的に改変していこうという意欲、これこそ人類が地球を実質的に支配している理由でしょう。もっとも、積極的に地球環境を改変しすぎて、環境バランスを崩しそうになっていることは大問題ですが、それはまた別の話です。

+
+
+ +

世の中にあふれかえるコンピュータ

+
+

現代では、20年以上前とは比べものにならないくらい大量の、しかも高性能のコンピュータが人々の周りに満ちあふれています。最近のコンピュータは昔のスーパーコンピュータをはるかにしのぐ性能を持っています。また、数の点での発展も目覚しく、テレビやカメラ、車にも炊飯器などにもコンピュータが内蔵されています。日本などの先進国では、すべての人が何らかの形でコンピュータを所有しているのではないでしょうか? また、ほとんどの人が所有している携帯電話は、通話機能とネットワークアクセス機能を持った携帯型コンピュータそのものです。電車の中や道端でケータイに向かってピコピコやっている姿を見ながら、すごいことが現実なったものだと感心します。携帯型コンピューティングデバイスを誰もが持っていて、日常生活で頻繁に利用するなどとは、SFの中でしか描かれなかった未来像です。

+

また、組み込みでない汎用型コンピュータ、いわゆるPC2 の普及率も高く、コンピュータ人口は昔に比べて格段に増加しています。しかし、ほとんどの人は、コンピュータを特定の仕事を果たす道具として利用するだけで、Webページをブラウズしたり、ワープロソフトで文書を作成したり、表計算ソフトで集計したりはするものの、プログラミングすることはほとんどありません。私の家族もご多分に漏れず、多少はPCを使うようになりました。子供たちの学校には「パソコン室」なるものがあり、ある種の情報リテラシーのような授業が存在します。そのせいか、私がいないうちにPCの電源を入れて、インターネット経由でアニメを見たり、ゲームを楽しんだりしているようです。

+

道具としてコンピュータを使う、与えられた機能を与えられたままに使いこなす。それはそれで悪いことではないのですが、与えられた環境に自分を適合させるというのは、人類としては退化しているように感じます。実際に情報系の大学ではコンピュータを使ったことはあるが、プログラミングについては何も知らない新入生が大量に入学して当惑する事態が頻発しているそうです。

+
+
+

プログラムによるカスタマイズ

+
+

私がコンピュータに初めて接した頃、コンピュータはプログラミングのための道具でした。BASICしか載っていないコンピュータでは、できることなどたかが知れていましたが、それでもプログラミングすることは楽しい経験でした。当時のコンピュータ3 のユーザーは、ほぼ全員が何らかのプログラミングを行っていました。あの頃のコンピュータは非力で、機能も少ないものでしたが、プログラミングによって「自分のやりたいことをやらせている」という感覚が味わえたものです。してみると、この20数年の間にコンピュータというもののありかたはすっかり変化してしまったようです。

+

しかし、今も昔も変わらず、ハッカーという人種は「無精」で「短気」で「傲慢」なので、与えられた環境にそのまま満足するとか、環境に自分を合わせるなどということには我慢ができないものです。ハッカーは「電子計算機」であるコンピュータを前にして、電卓を叩くなどということには耐えられません。彼らは「傲慢」なので、そのようなことは自分がコンピュータの奴隷になったように感じますし、「無精」なので単純作業の繰り返しに我慢ができません。また、「短気」なのでそのような苦痛から逃げ出すためにあらゆる努力を行い、おそらくはその作業を自動化するプログラムを作り上げてしまうでしょう。

+ +

このことこそが、ハッカーの多くがLinuxなどUNIX系OSを愛好する理由だと思います。Windowsなどとは違い、UNIX系OSではカスタマイズの余地が非常に大きいのが特徴です。気に入らない部分があれば自分の手に馴染むようにカスタマイズする、それがハッカーのやり方です。

+

私が長らくEmacsを愛用しているのも同じ理由です。前回も紹介しましたが、Emacsはコア部分以外のすべてがEmacs Lispによる拡張機能として実現されています。ほしい機能があれば、Emacs Lispでプログラムすることですぐに機能が追加できます。15年以上Emacsを使ううちに、.emacsファイル4 に蓄えられた自分のために作ったEmacs Lisp関数は相当の分量になっています。あまりにカスタマイズしている5 ため、他の設定ではEmacsを使いこなせません。また、私の .emacsでは、他の人は当惑することでしょう。

+
+
+

ハッカーはオールドタイプ?

+
+

環境を改善して省力化を実現するためには、スクリプト言語も有効です。データから数値を抽出して集計するために、紙に印刷してから電卓を叩くなどという苦行を行う代わりに、小さなスクリプトを書いて計算させたほうが、よっぽど生産的です。たとえ電卓を叩くよりプログラミングに時間がかかったとしても、それはそれでいいんです。単純作業を繰り返すよりは、プログラミングという生産的な作業を行ったほうが100倍幸福です。Ruby, Python, Perlなどのスクリプト言語は、まさにそのような「ハッカーの道具」としてハッカーの電脳環境の改善を目的として誕生したのです。

+

先日、電気工事のミスによってオフィスのサーバー室が停電になってしまい、UPSを付けていなかったすべてのマシンが止まってしまいました。私のメールボックスが置いてあるマシンもこの事件に巻き込まれて、ネットワーク経由でメールが取り出せなくなったのです。メールボックスの一部にゴミが混じってしまったため、POPサーバーが誤動作するようになったようで、ユーザー認証に失敗してしまいます。なぜ、メールボックスが壊れたくらいでそんな事態になるのか理解不能ですが、このままではメールの読み書きができず、私にとっては死活問題です。しかし、ハッカーたるもの、このようなときも慌てず騒がず6、壊れたメールボックスを手元にコピーして、簡単なRubyスクリプトで切り出し、メールソフトに読み込みました。与えられた環境でしか生きられなければ、このような事態には手も足もでなかったことでしょう。

+ +

ハッカーはただ単にコンピュータを使うだけでなく、その背後にあるソフトウェアの仕組みを理解し、必要であればその仕組みそのものを変えてしまう力を持っています。その力の源は「プログラミング」なのです。電脳環境をプログラムすることで、与えられたソフトウェアを使うだけでは実現できない魔法のような力を発揮できる、それがハッカーのパワーなのです。

+

このように見てみるとハッカーは電脳世界の進化の最前線にいるように思えてきます。しかし、よく考えてみると現実世界でも文明が進歩するにつれて専門化が進み、ほとんどの人は自分で家を建てたりはしません。自動車を作ったりもしません。やるのはせいぜい趣味の日曜大工程度でしょうか。それと対比するならば、あらゆることを自分の手で改造しないと気が済まないハッカーは、進化の最前線どころか時代に取り残されたオールドタイプなのかも知れませんねえ。まあ、自分で何にもできないユーザー7 よりは幸せですが。

+
+
+
+
+
    +
  1. +

    生物としての耐久力が高い

    +

    生物としての耐久力が高いといえばクマムシだろう。体長1mmほどのこの生き物は、ほぼ絶対零度の−272°Cから151°Cまで耐え、真空にさらされても平気なのだそうだ。 +

    +
  2. +
  3. +

    いわゆるPC

    +

    日本では「パソコン」と呼んだほうが通りがいいかも。「マイコン」を経験した世代には、パソコンという略語にはなんとなく抵抗がある。「パーソナルコンピュータ」なら「パーコン」ではないか。いや、それもいやだけど。「スーパーコンピュータ」の略である「スパコン」にも同様に抵抗がある。 +

    +
  4. +
  5. +

    当時のコンピュータ

    +

    70年代後半から80年代前半にかけて、個人向けのコンピュータは「マイコン」と呼ばれていた。パーソナルコンピュータの略である「パソコン」という単語が登場するのはもう少しあとになる。 +

    +
  6. +
  7. +

    .emacsファイル

    +

    Emacsのカスタマイズファイル。Emacs起動時にホームディレクトリの .emacsというファイルが読み込まれる。ここに関数やキー設定などを書いておくことで自分専用のEmacsを用意できる。 +

    +
  8. +
  9. +

    あまりにカスタマイズしている

    +

    今回、.emacsファイルの行数を数えたら812行あり、思ったより少なかった。私の場合、第2回(2005年5月号)で紹介したように日本語入力用のキー配列まで変えている。 +

    +
  10. +
  11. +

    慌てず騒がず

    +

    うそである。実際にはかなり慌てたし、騒ぎもした。しかし、この対応は出張中のホテルで行われたので、慌てたのも騒いだのも誰にも見られなかったのは幸いだ。しかし、なんで出張中のタイミングで停電が起きるかなあ。 +

    +
  12. +
  13. +

    自分で何にもできないユーザー

    +

    MITハッカーコミュニティではこのようなユーザーのことを「負け犬(looser)」と引っかけて「luser」と呼んでいたそうだ。ハッカーの傲慢さがうかがえる。 +

    +
  14. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-040.xhtml b/docs/vol2/xhtml/p-040.xhtml new file mode 100644 index 0000000..70ccb84 --- /dev/null +++ b/docs/vol2/xhtml/p-040.xhtml @@ -0,0 +1,116 @@ + + + + + +第53章 言語の重要性 + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay53 +
+

+まつもと ゆきひろのハッカーズライフ
+言語の重要性 +

+
+

[UNIX USER, 2005年9月号]

+
+

言語オタクとしてプログラミング言語の重要性について語っています。言語には、思考の手段、記述の手段、読む手段、そして実行する手段としての側面があり、それぞれに重要であると述べています。プログラムを開発する人は例外なく何らかのプログラミング言語を学ぶわけですが、その言語は記述の手段と実行の手段としてしか捉えていない人が多いでしょう。しかし、あまり注目されない思考の手段としての言語、また読む手段としての言語も重要です。思考の手段としての言語は、人間がプログラミング的に思考することを支援してくれますし、ある種のパラダイムを強調する言語では、「問題の捉え方」そのものが変化してしまうかもしれません。また、プログラミングを学ぶ最良の方法は他人の書いたプログラムを読んで考えることであり、そのためにも読む手段としての言語は重要です。これは時代が変わっても変化しない真理だと思います。

+
+
+

言語へのこだわり

+
+

ハッカーは、プログラミング言語にこだわる人がとても多いことが知られています。たとえば『ハッカーと画家』1 などの著書で知られるPaul Graham2 はLispに大変こだわっており、著書やエッセイの中でLispのパワーについてたびたび熱く語っています。私自身もプログラミング言語に深いこだわりを持つハッカーであり、自ら「言語おたく」を自称しています。私以外にもプログラミング言語好きのハッカーは数多く、中にはそれを本職にしてしまっている「言語屋」と呼ばれる人たちも存在するほどです。

+ +

では、なぜハッカーはそんなにも言語にこだわるのでしょうか。それはおそらく、プログラミング言語がハッカーの力と密接な関係があることだと思います。前回、プログラミングがハッカーの力の源であると述べました。そして、そのプログラミングが言語を通じて行われる以上、言語はハッカー最強の道具です。ですから、どのような言語をどのように使うかは、ハッカーの力の大きさに直接関係します。ハッカーは自分の能力や道具の良し悪しにとても敏感ですから、どうしても言語にこだわってしまうのでしょう。

+

いわゆる「普通の人」には、この気持ちを理解することは難しいのかもしれません。普段あまりプログラムを書かない人にとって、どのような言語でプログラムを書こうとも、結局はアルゴリズムを記述しているのであり、その本質は大差ないと感じることでしょう。確かにチューリング完全3 な言語であれば、任意のアルゴリズムを記述可能だそうですから、数学的には(ある一定の条件を満たす)すべての言語は等価なのかもしれません。しかし、現実にはすべての言語は等価ではないのです。

+
+
+

思考表現の手段としての言語

+
+

たとえ理論的には同じアルゴリズムを記述したとしても、言語が違えばその表現は大きく異なります。そして、その表現の違いが言語の違いを生むのです。プログラミング言語というものは、プログラムというコンピュータに対する仕事の手順を記述するものですから、その対象はコンピュータであると考えがちです。しかし、実際には言語によって影響を受けるのは人間のほうです。言語は人間の思考をコンピュータにも理解できる形で表現する手段なわけですが、思考の道具でもあるのです。

+

自然言語学には「人が話す言葉と、人の物事の理解の仕方や振る舞い方には密接な関係があるのではないか」という「Sapir-Whorf仮説」というものがあります。私自身も日本語で話しているときと英語で話しているときで性格が違うような気がする4 ので、個人的な経験からはこの仮説は成立していそうなのですが、実際にこれが正しいことを示す学術的な証拠は見つかっておらず、どっちかっていうと否定されているっぽい仮説です。しかし、自然言語についてこの仮説が正しいかどうかにかかわらず、使用するプログラミング言語によってプログラマーの発想が影響を受けるのは事実です。

+

私がBASICユーザーだった中学生の頃、関数が自分自身を呼び出す再帰という考え方が理解できず、3日間Pascalの教科書とにらめっこした覚えがあります。まあ、実際に手元にPascalの処理系があって実行することができればもっと早く理解できたのかもしれませんが、当時は自分で自由に使えるコンピュータを(BASICポケコン以外は)所有していませんでしたから。もし私が最初に使った言語がLispだったりしたら、おそらく再帰という考え方に違和感を覚えることはなかったでしょう。

+

このことから、「より強力な言語を使うことはプログラマーがよりよい発想を持つ助けになる」ということがわかります。言語の選択はプログラマーの能力に影響を与えるのです。また、一度学んだ発想は他の言語を使うときにも応用しやすいので、新しい言語を学ぶということはより優秀なプログラマーへの近道でもあります。名著として知られる『達人プログラマー』5 の中で著者たちは「1年に1つ新しい言語を覚える」というチャレンジを提案しています。これも同じ理由からです。実は彼らはこの本を書いた直後にこのチャレンジを自ら実践してRubyを発見し、あまりに気に入ったので英語圏における初のRuby解説書である『プログラミングRuby』6 を書いたのでした。

+
+
+ +

プログラムを書く手段としての言語

+
+

このように言語は考えることも助けてくれますが、考えるだけではプログラムは完成しません。やはり言語の一番重要な側面はプログラムを書くことです。

+

さて、名著『人月の神話』7 によれば「基本的な1ステートメントを製造するに要する工数は言語によらずほぼ一定」なのだそうです。とすれば、同じ処理を記述するときに言語Aで1000行、言語Bで10行必要であったとすれば、言語Bを採用するだけで生産性がおよそ100倍になるということになります。「そんなバカな」と思うかもしれませんが、たとえばJavaとRubyで同じ処理を記述する場合、ステートメント数で2倍以上の差がつくことは珍しくありません。アセンブラとRubyだったら100倍どころか1000倍の差がつくケースもありそうです。プログラミング言語進化の歴史は「いかにより簡潔な記述を可能にするか」を探し求めてきた歴史でもあります。

+

また、言語の差よりも重大なのはライブラリがそろっているかどうかです。たとえばネットワーク経由でHTTPアクセスをしたい場合、ソケットを用いてネットワークコネクションを確立する処理から書き始めれば、どんな言語を使っても500行以下で実現できるとは思えません。しかし、HTTPを直接扱うことのできるライブラリがあればHTTPアクセスそのものがたった1行で実現できるかもしれません。この差は大変重要です。

+
+
+

プログラムを読む手段としての言語

+
+

ほとんどの場合、プログラミングとは一度プログラムを書いてそれで終わりというわけにはいきません。バグがあればプログラムを読み返して、本当に正しい記述が行われているか確認しなければなりませんし、他の人が書いたプログラムを読んで保守しなければならないこともたびたびです。また、半年も経てば自分が書いたプログラムでも他人が書いたものと同じです。読んでみないと、何をしようとしていたのかわからないものです。とすると、もしかするとプログラムを書く時間よりもプログラムを読む時間のほうが長いかもしれません。

+ +

一般に、書きやすい言語によって書かれた簡潔なプログラムは、無駄な「お約束」が少なく、処理の本質に集中できるので読みやすいことが多いのですが、プログラムの簡潔さと読みやすさがいつも比例するとは限りません。簡潔すぎて情報量が少ないプログラムは読解のために推測すべきことが多くなり、かえって読みにくいこともあります。たとえば、書くときには面倒なだけの型情報は、読むときにはずいぶん役に立ちます。また、記号などを使って「圧縮」されたプログラムは短くても超難解です。「write-only language」とか「executable line noise」などと悪口をいわれるプログラム言語もあります。特に名前を出しませんが(笑)。

+
+
+

プログラム実行系としての言語

+
+

異なる言語で、同じアルゴリズムを使って同じようにプログラムを書いたとしても、同じような速度で実行できるとは限りません。プログラミング言語は、その実行形式(コンパイラ型かインタプリタ型か)や処理系の優劣によって実行性能が大きく変動するからです。ハッカーの中にはスピード命というタイプもいますし、最終的なプログラムの実行性能を重要視する人も多いです。一般に柔軟で生産性の高い言語は、実行時に行うことが多くて、実行性能が低い傾向があります。開発時の生産性か実行時の性能か。なかなか難しいトレードオフです。

+

ああっ、もう誌面が尽きてしまいました。言語おたくが言語について語り出すと止まりません。来月もこの続きを語ることにしましょう。

+
+
+
+
+
    +
  1. +

    『ハッカーと画家』

    +

    Paul Graham著、川合史郎訳『ハッカーと画家 コンピュータ時代の創造者たち』オーム社(ISBN4-27406-597-9)。ハッカーの生態をわかりやすく描いた書籍として知られている。全16章中、実に4章が言語を主題にしており、さらに多くの章がハッカーと言語の関係について語っている。ハッカーについて理解したい人にとって必読書。ハッカーにとっては前半は当り前すぎて退屈かも。 +

    +
  2. +
  3. +

    Paul Graham

    +

    『ハッカーと画家』の著者。ベンチャー企業Viaweb(現Yahoo!Store)を成功させたリッチなハッカー。Viawebの成功の秘密は、Lispを使った生産性にあったそうだ。ハッカーでもリッチになれるという希望の星。しかし、彼の名を本当に高めているのはViawebの成功ではなく、彼の書くハッカーの生態を描き出したエッセイである。『ハッカーと画家』に未収録のものも多いが、そのいくつかは川合史朗さんによって翻訳されている。
    +http://www.shiro.dreamhost.com/scheme/index-j.html +

    +
  4. +
  5. +

    チューリング完全

    +

    Alan Turingがアルゴリズムを記述するために考案した仮想的な機械チューリングマシンを表現できる言語のクラス。チューリング完全な言語は停止可能な任意のアルゴリズムを記述できるらしい。 +

    +
  6. +
  7. +

    性格が違うような気がする

    +

    私自身は英語を使っているときのほうが論理的な思考をするような気がする。先日、英会話番組でソニンも同じようなことを言っていたから、そう感じるのは私だけではないらしい。 +

    +
  8. +
  9. +

    『達人プログラマー』

    +

    Andrew Hunt, David Thomas著、村上雅章訳『達人プログラマー』ピアソン・エデュケーション(ISBN4-89471-274-1)。プログラマーとしての能力を向上させる基本的なルールについて解説した本。プログラマー必読の書だと思う。 +

    +
  10. +
  11. +

    『プログラミングRuby』

    +

    Andrew Hunt, David Thomas著、田和勝訳、まつもとゆきひろ監訳『プログラミングRuby』ピアソン・エデュケーション(ISBN4-89471-453-1)。英語ではRuby 1.8対応の第2版が出版されている。第2版も邦訳が予定されている。 +

    +
  12. +
  13. +

    『人月の神話』

    +

    Frederick Phillips Brooks Jr.著、滝沢徹/牧野祐子/富澤昇訳『人月の神話【新装版】』ピアソン・エデュケーション(4-89471-665-8)。なんか今月はピアソンの本ばっかりだな。原著は20年以上前に書かれたものだが、その本質は変わらない。 +

    +
  14. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-041.xhtml b/docs/vol2/xhtml/p-041.xhtml new file mode 100644 index 0000000..482b03f --- /dev/null +++ b/docs/vol2/xhtml/p-041.xhtml @@ -0,0 +1,110 @@ + + + + + +第54章 言語の重要性 その2 + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay54 +
+

+まつもと ゆきひろのハッカーズライフ
+言語の重要性 その2 +

+
+

[UNIX USER, 2005年10月号]

+
+

前回「言語の重要性」の続編です。今回は、自作言語について語っています。多くの人にとって、プログラミング言語はプログラムを開発するために「学ぶもの」であって、「作るもの」ではありません。人間というのは無意識のうちに線を引いて「ここからは自分の領域ではない」と思い込んでしまうもののようで、「言語を作る」ことというのは典型的な領域外に属する活動のようです。この「無意識の線引き」からは誰も自由ではなく、最近、Rubyのために「コンパイラ・コンパイラ」(構文解析機のソースコードを自動生成するツール)を開発する話になったときに、自分がコンパイラ・コンパイラは既存のものを利用するだけで新規に開発することを意識の外に置いていたことを自覚して愕然がくぜんとした経験があります。

+

今回は「おまけ」としてOSCON参加レポートもついています。そういえばこの頃は直接Rubyをテーマにしない海外カンファレンスにもたまには出席していましたね。カンファレンスへの招待が増えるにつれて、Rubyを直接テーマにしないカンファレンスへの出席は避けるようになってしまいました。2024年にはRuby on Rails20周年ということでRails World Conferenceに出席しますが、それは本当に稀有な例外です。

+
+
+

本当のハッカーの定義

+
+

しばらく前の話になりますが、VA Linux Business Forum 2005においてOSS Roundupという討論会に参加しました。これは、私を含むオープンソースに深く関わっている5名のパネリストがオープンソースに関して自由に語る形式のものです。この中で「ハッカーとは何か?」という話題が登場しました。一番定義が緩いのは「オープンソースプログラマー」の肩書でテレビにも出演していらっしゃる小飼弾氏で、「(民主主義社会では)あらゆる人はハッカーである」と定義しておられました。それはまた極端な。

+

でも、これをきっかけにイベントが終わったあと、「本当のハッカーの定義は何だろうか?」と改めて考えました。私は、小飼さんほど範囲を広げるつもりはないのですが、自分の中で「ハック」という用語をプログラミングに限定しているわけでもなさそうです。あえて、言語化すると「普通の人は変えられないと思い込んでいるものも変えることができる力を持つ人」くらいでしょうか。そして、コンピュータに関連した領域では、その力の源はプログラミング能力にありそうです。コンピュータに関係ない領域でも、政治力を駆使して社会を変革する「ソーシャルハック」とかありそうな気がします。経済力と常識にとらわれない発想力で社会に大きな影響を与えるライブドアの堀江さんなどは、「ソーシャルハッカー」なのかもしれません。まあ、そんな私でも、やっぱり何も修飾なしに「ハッカー」という単語を使えばやっぱりそれはプログラミングを行うハッカーのことを指しますけどね。

+
+
+ +

キーワードはINSANE

+
+

もっと最近の話になりますが、8月1日から5日まで米オレゴン州ポートランドで開催されたO’Reilly Opensource Convention(通称OSCON)1 に参加してきました。前述の弾さんとは、ここでもまたご一緒しました。また、David Thomas, Rich Kilmer, Jim Weirichなど、Rubyを通じて知り合った海外の友人と再会できたのもうれしいことです。OSCONのリポートは227ページを参照していただくとして、世界中からオープンソースに関心がある人々とハッカーが集う様子は壮観でした。

+

さて、OSCONで強く感じたキーワードはINSANEです。辞書を引くと「正気じゃない」「精神異常の」とか「ばかげた」「非常識な」とかひどい意味が並んでいますが、実際にはもっとずっとポジティブに使われています。口語で「正気の沙汰さたじゃない」や「普通じゃない」などというときのニュアンスに近いでしょうか。この連載で何度も繰り返しているハッカーの「ブレーキが壊れている」様子をよく表現していると思います。

+

OSCONの4日目に、Larry Wall(Perl)、Rasmus Lerdorf(PHP)、Guido van Rossum(Python)と一緒にランチをとる機会がありました。言語デザイナーとして、こんな豪華なメンバーのランチに参加できたのは光栄の極みです。Unicode対応のやり方とか、最近のトピックについていろいろと話をした2 のですが、ここでもINSANEは話題になっていました。GuidoがLarryに向かって「あなたたち(Perlピープル)のINSANEさはけた違いだ」と言っていましたが、これは「あなたたちは気が違っている」という意味ではなく、「われわれも確かにINSANEだが、Perlの人たちにはかなわない」というニュアンスでしょう。Guidoは、「われわれはこれだけ後方互換性に気を使って言語を変化させているのに、まだ変化が速いといわれる」とちょっと残念そうでした。確かにPythonピープルは保守的な人が多いかも。先入観でしょうか。それに比べると、Rubyはずいぶんいいかげんだなあ。

+
+
+ +

自分言語を作るのは難しくない!?

+
+

正直なところ、世の中にこれだけたくさんの言語があって、それぞれに特徴を持っているのに、さらにまた新しい言語を作ろうと思い付くその時点ですでに相当INISANEでしょう。常識的な人間であれば「これだけいろんな言語があるんだから、この中から自分の目的に合うものを探そう」という発想をするはずです。そこを「自分の言語を作ろう」と思っちゃうんですから、INSANEといわれても仕方がありません。しかも、言語を作ってそれを成功させようと思ったら、ただ単に言語をデザインするだけでなく、

+
    +
  • 処理系を実装(誰も代わりに「自分言語」の処理系を書いてくれない)

  • +
  • ドキュメントを書き(誰も代わりに「自分言語」のドキュメントを書いてくれない)

  • +
  • Webサイトを用意(誰も代わりに……以下省略)

  • +
+

などする必要があります。なかなか厳しいいばらの道ですよね。

+

じゃあ、「自分の言語」を作るのは本当に非現実的なことなのでしょうか。まつもとがRubyを作ったのは、本当に彼が「正気じゃない」からでしょうか。そうでないとは言い切れないのが悲しいところですが、私も最初から他の人と違ったことをしたいからRubyを作ったわけではないのです。「成功しよう」とか「完璧なものを作ろう」とか最初から気負うから大変なのですが、実際には「作りたい」という思いがあったから作った、そして言語の設計や実装はそれほど難しいことではないから実現できたわけです。

+

冷静に考えてみると、システムをデザインするということは、ある意味、言語に語彙ごいを加えていくことに他なりません。「どのような語彙をそろえているか」は言語の性質を決定しますから、自分の言語をデザインしていることでもあります。その辺をもうちょっと突き詰めて、語彙を超えて文法の領域にまで手を入れたくなると、アラ不思議、自分言語の出来上がり、ということになります。

+

ここ数十年のコンピュータサイエンスの進歩により、言語処理系の開発は本当に簡単になりました。特別に変な文法でなければ、BNF3 に似たような記法で文法を定義するだけで、コンパイラと呼ばれるツールを使ってあっという間に構文解析器を自動生成することができます。今や性能のことを第一に考えなければ、簡単な言語を作ることなんて数日あればできることです。

+

しかし、自分言語を作ることで何かメリットがあるのでしょうか? ええ、あるんです。

+

1つ目のメリットは、言語処理系というものがプログラミングの中でしばしば登場するテクニックの集合体であることです。たとえば、設定ファイルを読み込むルーチンなどは、言語処理系の字句解析、構文解析そのものです。また、言語処理系はコンピュータサイエンスの総合芸術ですから、処理系を実装するために用いたテクニックはあらゆるプログラミングに応用できます。

+

また、よい言語をデザインしようということは、人間の気持ちをより深く考えることでもあります。つまり、インターフェースのあるべき姿について、より深い考察を行うことでもあるわけです。自分言語を作る2つ目のメリととしては、言語レベルでの使い勝手を考えることで、プログラムの使い勝手に関するより深い経験と知識を身につけられることがありそうです。

+
+
+ +

一段高いプログラマーへの道

+
+

最後に、言語デザインはとにかく面白いです。プログラミングの世界は、結局すべてのものが言語によって表現されるので、言語を変化させることはあらゆるもののあり方を変化させることでもあります。つまり、言語のデザインは、究極の自由だといえるでしょう。このような自由を享受する機会は、それほど多くないはずです。

+

自分言語をデザインするということは、一段高いプログラマーへの道に通じているのかもしれません。たとえ結果的に、その言語が他の人にまったく使われなかったとしても……。そう思って、昨年からlangsmithメーリングリスト4 を運営しています。言語好きな人たちが集まって、自分の新言語を発表したり、議論したりしています。この中から未来を担う言語が登場したり、時代をリードするプログラマーが誕生したりするといいなと思っているのです。

+
+
+
+
+
    +
  1. +

    OSCON

    +

    今年のOSCONの情報ページやプレゼンテーション資料は以下のURLで公開されている。

    +

    http://conferences.oreillynet.com/os2005/
    +http://conferences.oreillynet.com/pub/w/38/presentations.html +

    +
  2. +
  3. +

    いろいろと話をした

    +

    LL(Lightweight Language)言語のデザイナーが顔を合わせてざっくばらんな話ができる機会はそうそうあるものではなく(前回は2年前)、せっかくの超貴重な機会なのだが、私は英語が拙くてあまり突っ込んだ話はできなかった。痛恨。 +

    +
  4. +
  5. +

    BNF

    +

    Backus Naur Formの略。BNF記法は、ALGOL 60の文法定義を形式的に行うために考案されたものである。 +

    +
      +
    • 右辺 ::= 左辺

    • +
    +

    という形式で文法要素を決定するルールを並べていくことで全体の文法を定義する。yacc(yet another compiler compiler)のようなツールはBNF(に似た)記法を受け入れて構文解析を行うソースコードを出力する。 +

    +
  6. +
  7. +

    langsmithメーリングリスト

    +

    アドレスは<langsmith@quickml.atdot.net>。参加するためには、まつもとのアドレス<matz@ruby-lang.org>にCCしながら上のアドレスに参加希望のメールを出すこと。 +

    +
  8. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-042.xhtml b/docs/vol2/xhtml/p-042.xhtml new file mode 100644 index 0000000..001d722 --- /dev/null +++ b/docs/vol2/xhtml/p-042.xhtml @@ -0,0 +1,129 @@ + + + + + +第54章 言語の重要性 その2 + + + + +

Matz Essays Volume 2

+ + +
+ +

まつもと ゆきひろのOSCONレポート

+
+
+

2005年8月1日から8月6日にかけてアメリカ、オレゴン州ポートランド開催されたO’Reilly Open Source Convention 2005に参加してきました。アメリカに限らず世界のあちこちからハッカーが集まる特別な機会について、やや偏った視点からレポートします。

+
+
+

OPEN SOURCE CONVENTION

+

オープンソースコンベンションはもともとはO’Reillyが主催していたPerl Conferenceが母体になっています。Perl Conferenceの規模が段々大きくなり、他のオープンソースに関するイベントも取り込むようになってオープンソースコンベンションという形になりました。3年前まではアメリカ西海岸のあちこちで開催されていましたが、一昨年にオレゴン州ポートランド市に定着し、昨年からは毎年Oregon Convention Centerで開催することにしたようです。Linus Torvaldsもオレゴン在住のようですし、今やオレゴン州はオープンソースの聖地と呼んでもよいのかもしれません。しかし、その割にはポートランド空港の入国管理官との間に、

+
+

管理官 : 入国目的は?

+

ハッカー: OSCONに出席するため

+

管理官 : OSCON?

+

ハッカー: オープンソースのイベントである

+

管理官 : オープンソースとは何か?

+

ハッカー: ソースコードを自由に……

+

管理官 : ?

+

ハッカー: あーっ、めんどくさい

+

ハッカー: そのパソコンはネットに繋がってないのか

+

管理官 : 繋がってない

+

ハッカー: インターネットで見れば一瞬なのに

+

管理官 : まあ、いいでしょう。次回は書類を用意して

+

ハッカー: @#%@!#$%

+
+

のようなやりとりが実際に行われたようで、聖地といえども一般への周知はまだまだのようです。

+
+
+

トラック

+

OSCONは2000人以上が参加する巨大なイベントです。セッションは複数のトラックに別れて同時並行に開催されます。だいたい興味深い話が同時に行われることになっているようで、泣く泣く選ばなければならないなどということはしょっちゅうです。今年開設されたトラックは以下のとおりです。

+
+
+

Apache

+

Apache 2.2の話題から各種モジュール、Webスケーラビリティまでさまざまな話題が取り扱われました。

+

Databases

+

海外ではMySQLが人気なのですが、MySQLに限らずPostgreSQL, Firebird, IngresなどさまざまなDBMSが取り上げられました。それぞれの開発者が直接「対決」するセッションもありました。

+

Emerging Topics

+

新しい話題全般を詰め込んだセッションです。Macromedia Flashを利用したリッチクライアント技術から、オープンソース開発のケーススタディまで、雑多な内容が詰め込まれています。

+

Java

+

あまりオープンソース的でないといわれることの多いJavaですが、Spring, Hibernate, Tapestry, Eclipseのような具体的なオープンソースソフトウェアにフォーカスしたセッションが多数開かれていました。

+

Perl Confernece 9

+

前述のようにOSCONの母体となったPerl Conferenceです。今年で9年目になります。もっとも独立したイベントにはなっておらず、「OSCONにはPerl関連のセッションが多いな」程度の印象です。Perl 6やPerl 5それぞれの最新情報が披露されました。

+

PHP Conference 5

+

Perl同様PHP Conferenceも併設されています。

+

Products & Serveces

+

オープンソースビジネス企業が自社の経験や商品を披露するトラックです。今年のOSCONはビジネスとしてのオープンソースも注目されており、たくさんの人を集めていました。

+

Python

+

Pythonについてのトラックもあります。Pythonは毎年2月にPython Conference(通称PyCon)を別に開いているので、Conferenceという名前は付いていません。

+

Ruby

+

昨年のOSCONにはRubyトラックがなかったのですが(一昨年にはあった)、今年は開設されました。今年は平均的なJavaによる開発よりも10倍生産性が高いといううわさのWebアプリケーションフレームワークRuby on Railsが大変話題で、注目を集めていました。

+

Security

+

Securityセッションではセキュリティ、特にネット上でのセキュリティについてさまざまな角度から扱っていました。

+

XML

+

「なぜXMLか」とかいうような段階はすでに過ぎ去り、具体的にどのようにXMLを扱うかというようなレベルに到達していました。「なぜSOAPは複雑でなければならないか」というセッションはなかなか興味をひかれました。時間の都合で聞けませんでしたが。

+
+
+
+ +

Perl 6

+

やはり、Perl Conferenceから始まったOSCONではPerlが幅を効かせています。割り当てられる部屋もPerl関係は数百人が収容できる大部屋がほとんどで、ときには異様に密度が低いこともありました。それでもDamian ConwayとLarry WallがPerl 6のあるべき姿を紹介するセッションは広い会場でも立ち見が出るほどの盛況ぶりでした。Perl 6は話を聞けば聞くほど「ほんとに実装できるんかいな」というような「何でもあり」の仕様なのですが、Haskellで実装されたPugsと呼ばれるPerl 6コンパイラ(フロントエンド)も、Parrotと呼ばれる仮想マシン(バックエンド)も順調に開発が進行しているということで、このままであれば、あと数年で普通の人が実際に使えるPerl 6が手に入るかもしれません。数年前にも同じことを聞いたようにも思いますが。

+
+
+

Ruby

+

今年はRubyは注目されていました。Ruby関連のセッション(表54.1)はどれも超満員でした。もっとも部屋が狭かっただけという説もありますが。

+
+

表54.1●Ruby関連のセッション

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Extracting Rails from BasecampDavid Heinemeier Hansson
Yield to the Block:
The Power of Blocks in Ruby
Yukihiro Matsumoto, netlab.jp
10 Things Every Java Programmer
Should Know About Ruby
Jim Weirich, Consultant, Compuware
A Starry Afternoon, a Sinking Symphony,
and the Polo Champ Who Gave It All Up
for No Reason Whatsoever
why the lucky stiff
Metaprogramming RubyGlenn Vanderburg, Principal, Delphi Consultants, LLC
Dependency Injection:
Vitally Important or Completely Irrelevant?
Jim Weirich, Consultant, Compuware
+
+

今年の注目は、やはりRuby on Railsのおかげでしょう。実際に10倍の生産性の違いがあるかどうかはともかく、チュートリアルでは目の前ですいすいとWebアプリケーションが実装されていく姿はやはり感動を覚えます。おかげでRails 開発者のDavid Heinemeier Hansssonは、このOSCONで発表されたO’Reilly+Google the Best Hacker of the Year Awardを受賞しました。めでたいことです。

+
+
+

オープンソース全般

+

OSCONは技術主体のイベントではあるのですが、やはりビジネスとしてのオープンソースに関心は高く、それ向けのセッションはかなり人が多かったようです。また、「オープンソースと女性」というテーマでの議論も行われていました。しかし、確かにOSCONでも女性は少数派ですが、少なくとも、ほぼ皆無の日本よりはずっとたくさんの女性を見掛けたことだけは確かです。

+
+
+ +

まとめ

+

技術主体のオープンソース関連のイベントとしては世界最大といってもよいOSCONからのレポートをお届けしました。私は時間(と体調)の都合からあまりたくさんのセッションに参加することができませんでした。来年はもっと体調を整えて、積極的に参加したいと思っています。やはりここが最新情報が発信される場であり、人と人とのコネクションが構築される場所ですから。それにはもっと英語を勉強しなくちゃな。

+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-043.xhtml b/docs/vol2/xhtml/p-043.xhtml new file mode 100644 index 0000000..dd803b4 --- /dev/null +++ b/docs/vol2/xhtml/p-043.xhtml @@ -0,0 +1,117 @@ + + + + + +第55章 ハッカーとオープンソース + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay55 +
+

+まつもと ゆきひろのハッカーズライフ
+ハッカーとオープンソース +

+
+

[UNIX USER, 2005年11月号]

+
+

フリーソフトウェアとオープンソースについて簡単に解説しています。フリーソフトウェアやオープンソースについては本書でも何度も解説していますから、あまり新しい内容はありませんが、これらが目指す「ソフトウェアの自由」はハッカー気質に取って欠かせないもので、だからこそ一流のハッカーであったStallmanが生涯をかけて啓蒙してきたのだと思います。人々がもう少しフリーソフトウェアについて柔軟に受けれ入れていれば、Stallmanがプログラミングに集中できたのではないかと残念に思う気持ちと、彼の努力がなければそもそも人類はフリーソフトウェアもオープンソースソフトウェアも手に入れることができなかったであろうから喜ばしい気持ちと両方があるのです。

+
+
+

オープンソース貢献者賞

+
+

先日、IPAとオープンソース推進フォーラムから「オープンソース貢献者賞」なる賞をいただきました。今回受賞したのはDebianプロジェクトの貢献者として知られる鵜飼さん(日本ヒューレット・パッカード)、Linuxカーネルハッカー高橋さん(VA Linux Systems Japan)、NamazuやGonzuiなどで知られる高林さん(グーグル)、そして、まつもと(ネットワーク応用通信研究所)の4人でした。第1回目ということで、オープンソース界隈でそれなりに名前が知られていて、かつ実際にコードを書く人を中心に選定したというところでしょうか。いずれにしても大変ありがたいことです。

+

本来、私は表に出たり、有名になったりするのはあまり好きではありません。しかし、最近ちょっと考えを変えることにしました。私自身が好むと好まざるとにかかわらず、「Rubyのまつもと」はこの業界では知れ渡ってしまったのですから、今さらこれをどうにかできるものではありません。それならば、むしろ精いっぱい「成功」して、あとに続くオープンソース開発者の模範あるいはロールモデルになろうと思うようになりました。「オープンソースをやっていても食べていけるんだ」とか、「オープンソースを仕事にして幸せそうだ」とかいうありさまを見せることも私の使命の1つなのでしょう。きっと。

+
+

さて、今回、オープンソースの貢献者の一人として認定された私ですが、私がいわゆるオープンソースと関わり出したのは、オープンソースという単語が生まれた1998年をはるかにさかのぼります。当時はフリーソフトウェア1 と呼ばれていました。最初の出合いは1989年頃で、EmacsとGCCに触れたのが始まりだったと思います。それ以来、職業プログラマーが会社のために仕事として開発したものはともかく、そのようなしがらみのないものをフリーソフトウェアとして公開するのは当然だと考えていました。フリーソフトウェアにはお世話になりっぱなしなのですし、それくらいは当り前ではないでしょうか。

+
+
+

フリーソフトウェア好きのハッカーたち

+
+

私に限らず多くのハッカーたちはフリーソフトウェア(オープンソースソフトウェア)が大好きです。それはなぜでしょうか。

+

タダだから? それもあるでしょう。数多くの優秀なソフトウェアが無償で利用できるのは大変ありがたいことです。私のノートPCには、OSとしてDebian GNU/Linux、そして数えきれないほどのソフトウェアがインストールされていますが、そのほとんどはフリーソフトウェアです。

+

しかし、ハッカーがフリーソフトウェアを愛する最も大きな理由は経済的なものではなく、自由です。ハッカーは自分が理不尽と感じる理由で行動が制限されることを大変嫌います。あるソフトウェアがどのように動いているか知りたくなったときには、ソースコードを読んでそれを確かめたい。ソフトウェアにバグがあったときには、自分でそれを直したい。自らの行動を制限するものがあれば、ソースコードを読み、プログラミングテクニックを駆使して、それを排除したい。それはもうハッカーの本能のようなものです。

+

世間的に「ハッカー」という言葉に悪い意味を与えてしまったクラッカー(システム侵入者)たちも、もともとは自分たちに対する制限への過剰反応が起源です。70年代にMITなどの大学に生息していたハッカーたちは、自分たちの問題を自由に解決するため、ときどきかなり過激なこと2 を行ったと聞いています。それだけ彼らは自由を切望し、自由を獲得するために闘争していたのです。いつもそれが正しかったとは言いませんが。

+
+
+

フリーソフトウェアの起源

+
+

そもそも、フリーソフトウェア自体も、自由の獲得が起源になっています。昔々、ソフトウェアはハードウェアの付属物であり、コンピュータを買うとソースコードごと付いてくることは決して珍しくなかったそうです。メーカーから買ってきたコンピュータはOSすら付いておらず、「あらゆるソフトウェアはユーザーが開発する」というケースもあったようで、同じコンピュータを購入したユーザー同士は自分たちが開発したソフトウェアを交換して、お互い助け合っていました。洗練されてはいませんが、今のオープンソースソフトウェアと少し似ていますね。

+ +

その後、ソフトウェアは商品となり、ソースコードは簡単には外に出せない「企業秘密」に変貌します。昔を知るものにとっては、だんだん自由が奪われていったわけです。それでも大学に所属している人々は相変わらずソフトウェア交換を行っていたわけですが、その自由も次第に商用ソフトウェアに侵食されていきます。

+

そして、とうとう大学で細々と行われていた自由なソフトウェア交換をゆるがすような「事件」が発生します。それは本連載の第4回「Emacs対vi」(2005年7月号)で説明したEmacsにまつわる事件です。

+

最初のEmacsは、Richard Stallmanという天才ハッカーがITS3 上のマクロエディタTecoを使って記述したものです。使いやすいエディタと評判になったEmacsは数多くの派生版を生み、後にJavaの設計者となるJames Goslingによって1981年にはUNIX版のEmacsが開発されます。GoslingによるEmacs(通称Gosmacs)は、MockLispと呼ばれるLispもどきの言語を使った拡張機能を持っていました。Stallmanも、UNIX版Emacsがほしいと思い、Gosmacsをベースに変更作業を行おうとしました。MockLispではなく、本物のLispを組み込んだEmacsがほしくなったからです。しかし、GoslingはGosmacsの権利をUnipress4 という企業に売却してしまい、StallmanはGosmacsをベースにして新しいEmacsの開発できなくなりました。結局、Stallmanはゼロから開発した5 のですが、まさにこのとき「ソフトウェアの自由は自分で守らなければいけない」ことが明らかになったのです。この時点で「ソースコードが公開されているだけでは十分ではない」という事実に気付いたStallmanは、世間を15年は先んじていたといえるでしょう。

+

この後、Stallmanはソフトウェアの自由を守るための団体であるFSF6 を組織し、ソフトウェアの自由を保証するライセンスであるGPL(GNU General Public License)を定義し、また上から下まで完全に自由なOS環境であるGNU(GNU’s Not Unix)を作るべく積極的に活動し始めました。

+

いや、ソフトウェアの自由は確かに大変重要なものですが、だからといって「そこまでやるか?」と言いたくなります。この辺がStallmanの「ブレーキが壊れた」部分であり、それこそ彼が真のハッカーである証しなのでしょう。フリーソフトウェア運動を推し進める彼の情熱とパワーには、敬服するしかありません。驚くべきエネルギーです。でも、おかげでStallmanは、プログラミングに割く時間があまり取れないそうです。彼の膨大なエネルギーを純粋にプログラミングに向けることができたらどんな偉大なことが実現できていただろうかと考えると、すごくもったいないような気がします。

+
+
+ +

フリーソフトウェアからオープンソースへ

+
+

ある意味、現在は幸せな時代です。オープンソースは世間の注目を浴び、膨大なフリーソフトウェアの蓄積があって、ほとんどあらゆる種類のソフトウェアのソースコードを自由に閲覧したり、必要に応じて改造や再配布も自由に行うことができます。また、プログラミングに必要な情報のほとんどはインターネットを経由で瞬時に入手できます。UUCP7 のバケツリレーでメールやニュースを受け渡し、ソフトウェアの配布は磁気テープの回覧で行っていたことが神話時代のことのように感じられます。

+

しかし、そのような幸せと自由は過去のハッカーたち(特にStallman)が熾烈しれつな闘争によって勝ち取ったことを忘れてはいけません。将来、再びソフトウェアの自由を奪い去ろうとする動きが発生しないとも限りません。実際にソフトウェア特許やDRM8 の領域で、その傾向がうかがえます。私たちハッカーとその仲間たちは、いざというときに自由のために立ち上がる備えをしておきべきかもしれません。

+
+
+
+
+
    +
  1. +

    当時はフリーソフトウェア

    +

    もちろん現在でもフリーソフトウェアという呼び名は存在している。個人的な意見をいえば、フリーは自由を意味するので、用語としてはオープンソースよりも優れていると思う。しかし、マーケティング用語としてはオープンソースのほうが成功したのは間違いなく、また、私がご飯が食べられているのもそのマーケティングのおかげである。複雑な気持ちだ。 +

    +
  2. +
  3. +

    かなり過激なこと

    +

    たとえば、ハッカーという言葉そのものを生んだMIT鉄道クラブ(TMRC: Tech Model Railroad Club)のメンバーが夜な夜なMITの建物に忍び込んでコンピュータを無断使用していたことは有名である。当時、その行為はあまり問題視されなかったようだ。大らかな時代である。 +

    +
  4. +
  5. +

    ITS

    +

    ITS(Incompatible Timesharing System)はMIT人工知能研究所(のハッカーたち)が独自に開発したOSである。当初はDEC PDP-6、その後PDP-10で動作した。 +

    +
  6. +
  7. +

    Unipress

    +

    UnipressはGosmacsをUnipress Emacsという商品として販売を始め、自分たちが権利を持つUnipress Emacsのソースコードを再配布しないようにStallmanに通告した。しかし、Goslingがこの事態を望んでいたとか、予想していたと考えるべきではないと思う。 +

    +
  8. +
  9. +

    ゼロから開発した

    +

    これが現在も使われている(私も使っている)GNU Emacsの起源である。Unipress Emacsがもはや影も形もないことを考えると皮肉なことである。 +

    +
  10. +
  11. +

    FSF

    +

    Free Software Foundationの略。 +

    +
  12. +
  13. +

    UUCP

    +

    Unix to Unix CoPyの略。一定間隔、あるいは要求に応じて電話回線などを使ってリモートホストに接続し、メールやファイルを交換する接続形態。 +

    +
  14. +
  15. +

    DRM

    +

    Digital Rights Managementの略で、音楽や動画などのデジタルコンテンツに対して、暗号化などを施して不正コピーや流出を防ぎ、正規流通を促進させる枠組み、およびそれに利用されるテクノロジー。 +

    +
  16. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-044.xhtml b/docs/vol2/xhtml/p-044.xhtml new file mode 100644 index 0000000..7317107 --- /dev/null +++ b/docs/vol2/xhtml/p-044.xhtml @@ -0,0 +1,126 @@ + + + + + +第56章 測定狂時代 + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay56 +
+

+まつもと ゆきひろのハッカーズライフ
+測定狂時代 +

+
+

[オープンソースマガジン, 2005年12月号]

+
+

今回はパフォーマンスのプロファイリングとチューニングの話について触れています。この「ハッカーズライフ」連載は分量が少ないのでどれも表面的な解説しかできないのが残念なところですね。ツールについてもgprofの解説しかありません。実際にはプロファイリングツールには各種あり、使いこなせばずいぶん便利なものがたくさんあるのですが、これはまた別の機会に解説するとしましょう。

+

「おまけ」は「ハッカー養成塾」というタイトルで、わたしの過去作品や教訓などについて記述しています。うろおぼえですが、掲載誌が変わったタイミングで執筆者の多くがこのような記事を書いたのだったと思います。今(2024年)から見れば30年以上前の話がほとんどで、懐かしいですね。ここであげられている作品のうち、morqだけは今でも使っているのです。

+
+
+

測定狂

+
+

「バカは風邪をひかない」と言いますが、幸い私のバカさ加減は許容範囲内のようで、年に数回風邪をひきます。もっとも例年夏風邪をひくことが多いので「やっぱりバカなんだ」と思うことも多いのですが、今年はなんとか大丈夫だったようです。

+

風邪をひくと体温計で体温を測ります。昔は水銀の入った体温計ですが、最近はデジタル体温計が主流のようです。測定が終わるとビープ音が鳴ったりして、なかなか賢いです。さらに、わずか1秒で体温がわかる、耳で測定する体温計もあります。ガジェット好きとしてはぜひほしいアイテムですが、家族の理解が得られず、まだ入手していません。

+

風邪をひいたときの行動は人によっていろいろでしょうが、私はとにかく頻繁に体温を測ります。ひどいときなど数分おきに測定して、家族をあきれさせることもあります。でも、体温を測るとなんだか自分の体のベンチマークを取っているようで楽しくなりませんか? 病気が直っていく様子がうかがえるようで、私にとっては病気でしんどいときの数少ない楽しみです。

+
+

日常生活における測定といえば、体重測定があります。体温ほど熱心に測定する気にならないのは、なかなか自分の望む方向に(私の場合は減る方向に)変化しないせいに違いありません。健康のためにはもうちょっと痩せたほうがいいんだけどなあ。運動もしないでコンピュータの前に座ってばかりなので、増加してないだけでも喜ぶべきなのかもしれませんが。気乗りしないといっても、数日に1回は入浴前に体重計に乗り、その結果をPDAに記録してグラフを書いてたりするんで、やっぱり測定好きなんですね。

+
+
+

スピード狂

+
+

さて、私の知人のハッカーの中には何人か「スピード狂」がいます。スピード狂といっても峠で自動車レースをするわけではなくて、プログラムの実行速度を速くするために異常に熱意を燃やす人たちのことです。彼らはRubyのようなスクリプト言語は使いません。「だって遅いじゃん」。使うのはもっぱらCです。まれにC++を使う人もいますが、あまり多くはありません。「C++は(コンストラクタとか暗黙の呼び出しがあるから)実行コストが直接的に見えないので好きでない」という人が多いようです。Javaは以前に比べてずいぶん高速化されましたが、それでもまだ不満そうです。また、個人的な知人たちの中にはいませんが、高速化のためにはアセンブラを駆使する人も存在すると聞いたことがあります。もっともRISC以降、単にアセンブラで書くよりCなどで書いたほうが高速化されることも多いようで、アセンブラ派はめっきり数が少なくなったようです。

+

もちろん、そんなに極端な人はたくさんはいないでしょうが、ハッカーの多くは何らかのスピード狂的側面を持っているようで、同じ動作となるプログラムの実行時間を短縮するという課題は燃えるものがあります。プログラムを繰り返し実行しながら「ここをいじるとコンマ何秒短縮された」などとハックを繰り返すのは、ハッカーにとってある意味大変幸福な時間です。パズルを解くときの知的チャレンジに似ているからでしょう。

+

CでコンパイルしたプログラムをCPUで直接実行するのに比べると、Rubyのようなインタプリタ型言語の実行はだいたい100〜1000倍くらい遅い1 ことが知られています。では、スピード狂はまったくインタプリタ型言語に寄り付かないかというと、そうでもないようです。もともとの実行時間が長いほど、改善による時間短縮幅が大きく、より達成感を感じられるからです。インタプリタ型言語の場合、処理をどれだけライブラリルーチンで消費できるかが鍵になります。Rubyの各ライブラリルーチンはそれぞれそれなりに工夫して作られていますから、上手に使いこなせば、素朴にCで実装したプログラムと同等近い性能が出る場合もあるそうです。

+
+
+ +

無駄な努力

+
+

プログラムの高速化のように、プログラムの意味を変えずに性質(実行速度とかメモリ消費量など)を改善することを最適化2 と呼びます。ハッカーは最適化が大好きですが、そのような最適化の努力がいつも報われるとは限りません。最近のruby-talkメーリングリストに以下のようなポスト(ruby-talk:158426)がありました。

+
+

私の会社ではC++による3次元レンダリングソフトを利用しています。そのソフトはJavaScriptとLua3 のバインディングが提供していました。LuaはRubyほどには使い勝手がよくなかったうえ、私たちは速度とメモリの効率のため、より面倒なプログラミングテクニックを使う必要がありました。そのテクニックは確かに効果があり、FPS4 は1〜2%向上していました。私は大変苦労してLuaのプログラムを書き、単純なシーンのレンダリングで890〜910FPSを達成したことに誇りを覚えていました。

+

しかし、昨日、C++プログラマーの一人がレンダリングアルゴリズムにある変更を行ったところ、FPSが劇的に向上しました。今まで15FPSだった複雑なシーンのレンダリングが170FPSでできるようになったのです。私たちが非常に苦労して実現した数%の向上など、10分ほどかけて適切なアルゴリズムに変更するだけで吹き飛んでしまったのです。

+
+

ソフトウェア業界には昔から「premature optimization is source of all evil(早すぎる最適化はすべての悪の源)」ということわざがあります。プログラムの高速化においては努力がいつも報われるとは限らないのです。無闇な高速化の試みは、かえってプログラムの見通しが悪くなったりする弊害のほうが大きいのです。

+
+
+

無駄でない努力

+
+

ハッカーたるもの無駄な努力をするべきではありません。天より与えられたハッキングの才能を浪費することは許されないのです5。では、努力を無駄にしないためにはどうしたらよいでしょうか。

+

そのためには「パレートの法則」を理解する必要があります。パレートの法則とは「80 : 20則」とも呼ばれる法則で、全体の8割の数値は全体を構成する2割の要素が生み出しているという法則です。19世紀後半のイタリアの経済学者ヴィルフレド・パレート(Vilfredo Federico Damaso Pareto)が発見したことから名前が付いています。

+ +

パレートの法則からわかるのは、要するに努力が報われる8割の領域と報われない2割の領域6 があり、報われない領域でいくら努力しても無駄にしかならないということです。だから最適化を始める前にその作業は無駄になるかならないかを見極める必要があります。

+
+
+

プロファイラ

+
+

作業が無駄になるかどうか見極めるツールをプロファイラと呼びます。Linuxで最も有名なプロファイラはgprofでしょう。

+

gprofを使うためにはコンパイル時にccのコマンドラインオプションに「-pg」を追加します。このオプション付きでコンパイルされたプログラムには、関数の実行状態を測定するルーチンがリンクされます。プログラムを実行するとカレントディレクトリにgmon.outファイルが生成されます。プロファイル結果を見るには、そのディレクトリで、

+
+
gprof <プログラム名>
+
+

を実行します。これによって、

+
    +
  • どの関数がどのくらいの時間を消費しているか?

  • +
  • どの関数がどこから何回呼ばれているか?

  • +
+

などの情報を含む長いリストが出力されます。それを調べることで、「どの関数で最も時間がかかっているか」とか「どの関数が無駄に関数を呼び出しているか」などを確かめられます。この情報に基づくことで、より効果的な最適化ができることでしょう。

+

実行速度を見るgprof以外にも、メモリ消費量を調べるメモリプロファイラなど、測定するプログラムはたくさんあります。

+

正しく測定して効果的な最適化を行うことは、ハッカーの測定狂的性質とスピード狂的性質の両方を一度に満足させることができます。一粒で二度おいしい最適化、あなたも試してみませんか?

+
+
+
+
+
    +
  1. +

    100〜1000倍くらい遅い

    +

    とはいっても、実際には実行時間のほとんどがCで書かれたライブラリルーチンの中で消費されるので、直接1000倍の差がつくことはめったにない。 +

    +
  2. +
  3. +

    最適化

    +

    最適化とはいうものの、最適(最もよい状態)になることはめったにない。より正確には「ちょっとマシ化」とでも呼んだほうがよさそうだ。英語では「optimization」と呼ぶ。「optim-」は「optimistic(楽観的)」から来ているから、英語のほうが実態を的確に表現しているようだ。 +

    +
  4. +
  5. +

    Lua

    +

    ブラジルで開発されたスクリプト言語。アプリケーションの組み込みの容易さと実行速度の高速さが特徴だといわれている。ブロックが「end」で終わるところだけはRubyに似ているかも。 +

    +
  6. +
  7. +

    FPS

    +

    Frame Per Second。1秒間に処理できる画面数。 +

    +
  8. +
  9. +

    浪費することは許されないのです

    +

    念のため注釈を付けておくが、当然冗談である。だが、ハッカーが無駄な努力を嫌う傾向があるのは事実である。 +

    +
  10. +
  11. +

    8割の領域と2割の領域

    +

    パレートの法則の80 : 20の関係は経験則であり、実際にはもっと極端な場合もしばしばある。少なくともプログラミングに関しては90 : 10とか99 : 1とかは決して珍しくない。 +

    +
  12. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-045.xhtml b/docs/vol2/xhtml/p-045.xhtml new file mode 100644 index 0000000..02a8e88 --- /dev/null +++ b/docs/vol2/xhtml/p-045.xhtml @@ -0,0 +1,95 @@ + + + + + +第56章 測定狂時代 + + + + +

Matz Essays Volume 2

+ + +
+ +

ハッカー養成塾!

+
+
+

著者プロフィール

+

1965年大阪生まれ、鳥取県育ち。大学卒業以来ずっとぐうたら職業プログラマーをやってます。今の職場に来るまでは、職業プログラマーとして、社内OAシステムや造船設計システムを開発してきたことになっています。が、その実体はシステム内部で使われるツールやライブラリを好き勝手に開発してきました。

+
+
+

作品リスト

+

ハッカーはその結果によって評価されるのだと思います。最近では「Rubyのまつもと」として知られるようになってしまいましたが、その他にも以下のようなものを作ってきました(その他パッチなどは数知れず)。今まで作ってきたものは「ハッカーとしての私」を表現しているような気がします。

+
+
EmacsLispによるメールリーダーcmail
+

cmailはもともと山梨大学(当時)の林さんが開発されたメールリーダーですが、学生時代の私は、自分が使いやすいようにcmailをいろいろと改造して使っていました。それより以前はVMという名前のメールリーダーを使っていました。が、VMはだんだん規模が大きくなって、拡張の方向性が私の好みと合わず、また操作性も低下したように感じました。そういうときに見つけたcmailは、私にぴったりだったというわけではありませんが、規模が小さく見通しがよかったので、私好みに改造するのは難しくなさそうでした。

+

このように改造を施したcmailですが、フリーソフトウェアなんだしせっかくなのでこの改造を本家に還元しようとして林さんにメールを書いたら、「私はもう使っていませんからご自由にどうぞ」ということで私が引き取りました。私にとって最初のフリーソフトウェアということになります。cmailはコミュニティの協力を得てさまざまな新しい機能が追加され(mime対応、gpg対応、3ペイン表示、imap対応などなど)、発展を続けました。私は数年前にメンテナを引退しましたが、昨年新しいメールリーダーmorq(後述)に移行するまでは、実に14年もの間cmailを使い続けていました。

+
+
+
日本語が使えるXView風GUIライブラリ
+

以前勤めていた会社(独立系ソフトハウス)で社内OAシステムを作るのに日本語が通るGUIツールキットのソースコードが手に入らなかったというのを言い訳にして開発した自作GUIライブラリです。日本語が通らないというのはGUIライブラリの国際化の進んだ今では信じられないことですが、もう13, 4年も前のことですから。当時はまだMotifとOpenLookの対立が盛んな頃で、OpenLookのほうが優れていると感じていた私はOpenLookツールキットであるXViewを参考にしたAPIをデザインしました。ただし、単なるGUIライブラリだけではなく、まずObjective-C風のメッセージ呼び出しの仕組みとクラスライブラリ(OrderedCollectionとか)を作って、その上にGUIツールキットを構築しました。ですから、WindowButtonのような部品もそのオブジェクト指向ライブラリのクラスとして実装しました。あの頃、私は若かったなあ。このライブラリは社内ツールにとどまり、フリーソフトウェアにできなかったのは残念なことです。もうどこにもソースコード残ってないだろうなあ。実は、このライブラリのごく一部はRubyに流用されています(st.cとメソッドキャッシュの部分)。

+
+
+ +
bash 1.12でEUC-JPを通すパッチ
+

これも13年くらい前の仕事です。当時のbashはクォートによるエスケープに文字の8ビット目を使うという凶悪な仕様だったのでEUC-JPを使えるようにするためには苦労しました。確か基本的な部分は一晩徹夜して実装したような気がします。独身の頃は会社で一晩中ハックするのも珍しくありませんでした。昔は日本語を通すという単純なことを実現するだけでもえらい苦労したもので、当時の日本人技術者の労力はこうして浪費されていたのです。

+
+
+
オブジェクト指向データベースクラスタの分散ガベージコレクション
+

これは9年くらい前の仕事です。ObjectStoreというC++のオブジェクト指向データベースを使ったシステムを開発していたのですが、メモリ管理を自動化したいという要求に応えるため、同一データベース内ではマーク&スイープ方式のガベージコレクション、データベースを超えた参照(external reference)はリファレンスカウントを使う方式の分散ガベージコレクタを実装しました。ネットワーク上に分散したデータベースの位置を管理するため、小さな中央サーバーを用意したのですが、この部分はRubyを利用しました。ソケットプログラミングが非常に楽で重宝した覚えがあります。

+
+
+
検索ベースメールリーダーmorq
+

長らくRubyの開発ばかり行って、単体で動作するアプリケーションはあまり開発していなかったのですが、昨年から今年にかけて久しぶりに開発したアプリケーションがmorqです。本当はMail ORGanizerでmorgにしたかったのですが、Music ORGanizerでmorgという名前のソフトがすでに存在していましたので、Mail ORganizer using Qdbm(QDBMを使ったメールオーガナイザー)という意味でmorqという名前にしました。「もーく」と発音してくださいね。

+

morqはgmailの影響を受けて開発されました。大量のメールが届く昨今ではメールを分類するという行為は遠からず破綻します。これからはgmailのような「必要なメールは検索する」「必要に応じてメールにラベルを付けることもできる」というメール管理法が有効だと考えました。しかし、gmailにはネットワーク接続がないと使えない、1Gバイトの容量制限(当時)は将来的には不安、という欠点があります。しかし、ふと見ると手元のパソコンにはまだ数Gバイトの空きがあるではありませんか。ハッカーたるものなければ自作すればよいのです。そういうわけで作ったのがmorqです。現在はバックエンドはRuby+全文検索エンジンRast。フロントエンドはEmacs Lispでできています。現時点では本気でmorqを使っているのは世界中で私だけのようです。cmail以上にマイナーなメールリーダーですね。

+

私はもうかなり長い間morqをメインのメールリーダーとして使っていますが、なかなか快調です。「確か先月頃、こんなフレーズを含むメールがあったよな」というおぼろげな記憶からメールを見つけ出し、そのメールを含むスレッドを一度に読むことができます。人間がいくらこまめに分類しても、数万から数十万通のメールからこの速度で目的のメールを見つけ出すことはできません。morqはRastのパッケージに同梱されています。

+
+
    +
  • http://projects.netlab.jp/rast/?download.ja

  • +
+

からRastの最新版を入手してください。原稿執筆時点での最新版はRast 0.3.1です。Subversionを使えば、より新しいmorqを入手できます。

+
+
svn checkout http://projects.netlab.jp/svn/morq/trunk/morq
+
+
+
+

ハッカー人生訓

+

ハッカーの定義はあいまいなものです。ハッカーとは「ハックする人」以上の意味を持たず、むしろある種の性格分類だからです。その性格がプログラミングに向かうとき、人はその人をハッカーと呼びますし、分野が違えば「異端者」「改革者」「革新派」などと呼ばれることでしょう。

+

さて、そのようなハッカーが幸せに生きるための人生訓のようなものをいくつか紹介しましょう。いくつかは、私の「ハッカー人生」から学んだものですし、いくつかは実体験の裏打ちのないものです。

+
+
「できない」といわない。「やらない」という
+

プログラミングの世界で不可能なことはめったにありません。制限は時間と手間と知識の限界だけです。ですから、いつでも「やればできる」のです。こう考えれば「やりたいことをやらない」理由が減ります。

+
+
+
ほしいものがなければ自分で作ろう
+

世の中に同じものがすでにあるのに新しいものを作るのは無駄なだけです。そのような無駄は「車輪の再発明」として嫌われます。しかし、車輪が存在しないとき、あるいは自分ならもっとよい車輪が作れると感じたときには躊躇ちゅうちょせずに作り始めるのがハッカーというものです。ちょっと偉くなると自分で作る代わりに部下に作らせたりします。

+
+
+
制約を打ち壊せ
+

あらゆる制約はハッカーの敵です。自分の能力、意志、行動、言論への制約により不愉快な思いをするときには、それを取り除くべく行動するのがハッカー的です。フリーソフトウェア運動も、オープンソースもそのようにして発生しました。ただ、あまり過激になって非合法活動(クラッキングとか)に身を落とさないように忠告します。

+
+
+
コミュニケーションを忘れない
+

ハッキング能力とコミュニケーション能力は比例しません。しかし、ハッカーとして「作品」を一人で完成させることができる人はまれです。ですから、(たとえ苦手でも)コミュニケーションに時間をとることがハッカーとして「成功」するのに役立ちそうです。

+
+
+
+ +

まとめ

+

大学卒業以来、「ハッカーでも社会人として(なんとか)生きていける」ことを目標に生きてきました。今のところなんとか実現できているようです。皆さんもハッキングと幸せな生活を両立させてくださいませ。Happy Hacking!

+
+
+
+ +

+
+
+ + diff --git a/docs/vol2/xhtml/p-046.xhtml b/docs/vol2/xhtml/p-046.xhtml new file mode 100644 index 0000000..c97c55b --- /dev/null +++ b/docs/vol2/xhtml/p-046.xhtml @@ -0,0 +1,132 @@ + + + + + +第57章 ソースを読もう + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay57 +
+

+まつもと ゆきひろのハッカーズライフ
+ソースを読もう +

+
+

[オープンソースマガジン, 2006年1月号]

+
+

ソースの読み方について解説しています。多くの人はプログラミングはソースコードを記述することだと認識しているようですが、実際にはコードを読む時間のほうがずっと長いものです。特にデバッグのときにはコードに間違いがないか一生懸命読むものです。そういう観点から、コードの読み方と、それから(将来の自分を含めた)誰かがコードを読むときに読みやすいコードを書くコツについて簡単ながら説明しています。「プログラミングはむしろ人間のため」という原則を思い出させる記事ですね。

+
+
+

ハッカー能力を向上させる方法

+
+

『Code Reading』1 という書籍があります。私が監訳をしているからいうわけではないのですが、結構よい本です。今回の「ハッカーズライフ」はこの本を読んだ人には当り前のことかもしれません。

+

ハッカーとしての能力を身につけるのに最も優れた方法は、実際にコードを書くことと、他の人の書いた優れたソースコードを読むことだと思います。特にコードを読むことは普段あまり強調されませんが、他人のソースコードはいろんな意味で知恵と知識の源です。考えてみれば、私自身もたくさん他人のソースコードを読んで学んだように思います。

+

「コードを読むことは勉強になる」ということで、世の中には「Linuxカーネル読書会」と称して、Linuxカーネルのソースをmain関数から読み進めていく会合もあるのだそうです。一度出席してみたい気もしますが、私自身はそのような読み方でモチベーションが維持できるような気がしません。なんていうのかな、相手が巨大すぎるというか。ソースコードには小説のような読み物ではないので、漠然とソースコードを読むことはなかなかできないものです。

+
+
+ +

ソースコード読解の秘訣

+
+

私が過去にどのようなソースコードの読み方をしてきたのか振り返ってみると、プログラミング能力の向上を目指したコードの読み方のヒントがあるかもしれません。

+

まず1つは、「全体を読もうとしない」ことです。ソースコードには「物語」はないので、全体を通して読む必要はありません。面白そうなところをつまみ食いして、先人の知恵を学べばそれで十分です。

+

もう1つは、「目的を持って読む」ことです。何かを学ぼうと思ってソースコードを読めば、効果的に読解して知識を得ることができます。たとえば2、「再起下降構文解析の実装法を学ぶ」とか、「この処理系がどのようにガベージコレクションを実装しているか」とか、「なぜこの処理系はこんなに高速に動作するのか」とかです。プログラミングの教科書にあるコードは非常に小規模なものが多く、枠組を理解することはできますが、実用的なプログラムでどのような問題が発生するか、あるいはそれら問題にどう対処するかについては、あまり教えてくれません。実際に動いているプログラムはそのような「教科書が教えてくれない話」の宝庫です。

+

目的を持って読む、といえば、自分のプログラムに取り込むためにソースコードを読むというのも1つの重要な目的です。幸いなことに現代ではオープンソース、言い換えればライセンス的に自由なソフトウェアのソースコードがたくさん手に入ります。自分が十分に理解していない領域のコードでも、他の人が苦労して作ってくれたソースコードを参考にして問題解決できることはたびたびあります。私が体験した最近の例ではpom(phase of the moon)というプログラムがあります。pomは月の満ち欠け(月齢)を表示するプログラムです。昨年、末の娘が生まれたとき、空に見事な満月がかかっているのに感動して、他の人の生まれた日の月齢が知りたくなりました。で、pomを使おうと思ったのですが、あいにくエポック(1970年1月1日)以前には対応していません。そこで、BSDライセンスのpomのソースコードを入手し、Cのソースコードをほぼ逐語的にRubyに書き換えました。日付ライブラリがエポック以前に対応しているRubyでは、そのままエポック以前の日付にも対応したpom3 が完成したわけです。

+

ただし、ライセンス的に自由なソフトウェアといっても、何をするのも完全に自由なもの4 はそれほどありません。他の人が作ったソースコードを取り込むときには、それぞれのライセンスを尊重するようにしましょう。特にGPLは「ソフトウェアの自由」を保証するため、いくつかの制約5 が加えられています。理由があってそのような制約があるのですから、無視しないようにしましょう。

+
+
+ +

ソースコードの読み方テクニック

+
+

ソースコードを読み解くときには、まずプログラム全体の枠組みを理解することが有効でしょう。プログラム全体を読む必要はないのですが、自分がほしい情報がどこにあるかを見つけ出すためには、プログラム全体の構造のイメージをつかんでおくと便利なのです。ここで最も役に立つのはソースファイル名です。たいていのソフトウェアのソースコードはいくつかのファイルに分割されていますし、各ファイルにはプログラムの機能に関連した名前が付いています。たとえば、メモリ管理について知りたいときには「memory.c」とか「gc.c」などという名前の付いたファイルが怪しいといえます。

+

それで見つからない場合には、プログラムのスタートであるmain関数(Cの場合)からたどっていくことになります。ここでもプログラム全体を読む必要はないので、個別の関数の働きは関数名から推測して、必要そうなところを探すのに集中します。

+

ソースコード探索における最大の武器はgrepです。grepは「正規表現にマッチする行を見つける」という単純なツールですが、適当なキーワードを検索するとか、関数やメソッド、変数を探すなど、いろいろな目的に活用できます。Emacsなどgrep機能を支援するエディタでは、見つけた行に直接ジャンプすることも可能です。その他、関数定義に直接ジャンプするctags6 や、プログラムをハイパーテキストとして参照するGLOBAL7 なども役に立ちます。

+

プログラム読解向けツールのニューフェイスは、ソースコード検索エンジンGonzui8 でしょう。プログラム中の識別子をインデックス化するctagsやGLOBALとは違い、Gonzuiはさまざまな検索を支援しています。関数定義だけでなく、関数の呼び出しを見つけることもできますから、「関数呼び出しの実例から使い方を学ぶ」というような使い方にも向いています。

+
+
+

良いソースコード/悪いソースコード

+
+

このように、ソースコードを読む経験を積むと、読みやすいソースコードと読みにくいソースコードがあることに気が付くと思います。私が最悪と思ったのは(失礼ながら)Perl 5のソースコードです。

+
    +
  • ライブラリ手続きがpp.c, pp_ctl.c, pp_hot.c, pp_sys.cなどに分散しており、ファイル名が手続きを探すヒントにならない

  • +
  • 関数定義にマクロが使われているためctagsの類が使えない

  • +
  • 内部データのアクセスにもマクロが多用されており、また極端な省略形が使われているため、名前から機能が想像できない。SvUVX()SvPOK_only_UTF8()などなどの数百の名前が何を意味するのか理解するまではソースコードを把握できないような気がする。

  • +
+ +

もちろん、このソースコードを書いたLarry Wallは一流のハッカーで、上記の点にはそれなりの理由があるのです。たとえば、ソースコードファイルが機能別になっていない点は「Perl開発当時の古いマシンではオブジェクトファイルの配置によって関数呼び出し速度が異なっていたため、少しでも速度を稼ぐ意味で、頻繁に呼び出される関数を目的によらず1つのファイルに集めた」という理由がありますし、マクロの多用も「コードの繰り返しを避け、ソースコードをコンパクトにまとめる」という理由があります。わからないでもないのですが、ソースコードを読み解くという観点からは、やはりつらいものがあります。

+

このことから、良いソースコード、読みやすいコードの書き方を学ぶことができます。Perlのソースコードのちょうど反対をすればよいのです。Rubyを開発する際、Perlのソースコードも参考にしましたが、この悪い点は真似しないようにと決めていました。これによって、RubyのソースコードはPerlのものよりはずいぶんマシになっていると思います。例によって自画自賛ですが。

+
+
+

誰かのために、自分のために

+
+

ソースコードを読むことがハッカーの能力を高める。このことは最近になって次第に知られてきました。ここではもう一歩踏み込んで、ソースを読むこととその読み方について考えてみました。

+

ソースコードを書く際は、読まれることを意識するのも重要です。あなたの書いたソースコードを読む必要があるのは、見知らぬ誰かだけではなく、半年後の自分自身なのかもしれませんから。

+
+
+
+
+
    +
  1. +

    『Code Reading』

    +

    Diomidis Spinellis著、まつもとゆきひろ/平林俊一/鵜飼文敏監訳、トップスタジオ訳、『Code Reading — オープンソースから学ぶソフトウェア開発技法』、毎日コミュニケーションズ発行、ISBN4-8399-1265-3
    +http://book.mycom.co.jp/book/4-8399-1265-3/4-8399-1265-3.shtml +

    +
  2. +
  3. +

    たとえば

    +

    「たとえば」と書いているが、どれも私が実際に目的としたものばかりである。すぐに思い付く範囲内で読んだソースコードが言語処理系ばかりというのも、さすが「言語おたく」というか何というか。 +

    +
  4. +
  5. +

    エポック以前の日付にも対応したpom

    +

    ソースコードは以下のURL。
    +http://www.rubyist.net/~matz/20041028.html +

    +
  6. +
  7. +

    何をするのも完全に自由なもの

    +

    著作権が存在しない、いわゆるパブリックドメインに該当するものはまさに「何をするのも完全に自由」。メジャーなソフトウェアで明示的にパブリックドメインなものは、SQLiteくらいではないだろうか。 +

    +
  8. +
  9. +

    いくつかの制約

    +

    自由のために制約が導入されるというのも皮肉なものであるが、そのような制約がなければソフトウェアの自由が奪われてしまうというのがFSFの考えであり、それには一理あると私も思う。 +

    +
  10. +
  11. +

    ctags

    +

    ソースコードに対する識別子をインデックス化するツール。基本的にctagsはvi用だが、Emacs用のetagsもある。もっともこの2つのプログラムが作るインデックスは互換なので、どちらでも使える。現在では、両方ともRubyをサポートしている。 +

    +
  12. +
  13. +

    GLOBAL

    +

    エディタから独立したソースコード索引システム。C, C++, Yacc, Java, PHP4に対応。
    +http://tamacom.com/global-j.html +

    +
  14. +
  15. +

    Gonzui

    +

    検索エンジンNamazuの開発者と知られる高林哲さんが未踏プロジェクトで開発したソースコード検索エンジン。
    +http://gonzui.sourceforge.net/ +

    +
  16. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-047.xhtml b/docs/vol2/xhtml/p-047.xhtml new file mode 100644 index 0000000..a620aab --- /dev/null +++ b/docs/vol2/xhtml/p-047.xhtml @@ -0,0 +1,128 @@ + + + + + +第58章 Let’s Talk Lisp + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay58 +
+

+まつもと ゆきひろのハッカーズライフ
+Let’s Talk Lisp +

+
+

[オープンソースマガジン, 2006年2月号]

+
+

最古にして最強のプログラミング言語(誇張した表現)であるLispについて語っています。こんなに強力なのにLispはいつになっても人気がないんですよねえ。ガベージコレクションにしても例外処理にしても、多重継承にしてもLispが起源だったり、そうでなくてもLispでの実験によって発展した技術は多くて、JavaやPython, Rubyなんかで知られるようになったテクニックや実装技術でLisp由来のものは驚くほど多いのですが。もっとも私もLispから多大な影響を受けていながら、全然Lispでない言語を作っているので人のことは言えないのですが。

+
+
+

Lispは学ぶ道具? 使う道具?

+
+

Eric Raymondのエッセイ「ハッカーになろう」1 では、Lispのことが以下のように紹介されています。

+
+

LISPは、それをモノにしたときのすばらしい悟り体験のために勉強しましょう。この体験は、その後の人生でよりよいプログラマーとなる手助けとなるはずです。たとえ、実際にはLISPそのものをあまり使わなくても。

+
+

それに対してLispハッカーであるPaul Grahamは以下のように反応2 しています。

+ +
+

彼がLispについて言っていることはよくある意見だ。つまり、Lispを学べばよいプログラマーになれる、でもそれを実際に使うことはない、と。

+

何故だい? プログラミング言語なんてただの道具じゃないか。Lispでよいプログラムが書けるなら、使うべきなんだ。

+
+

そのとおり。あまり使われていませんが、Lispには優れたところがたくさんあります。私は、Paul GrahamほどのLispハッカーではありませんが、Lispプログラマーの端くれ3 として、今回はそのすばらしいLispについて簡単に紹介してみようと思います。

+
+
+

Lispの歴史

+
+

Lispの歴史は古く、その誕生は1958年だといわれています。1958年といえば、まだほとんどのプログラミング言語が登場していない時期です。この頃にすでに存在していたプログラミング言語で今でも生き残っているのはFORTRAN(1954年)とCOBOL(1959年)くらいのものでしょう。

+

Lispがプログラミング言語として特異なのは、もともとはプログラミング言語として設計されたのではなく、数学的な計算モデルとして設計されたことに原因があると考えます。Lispの設計者として知られるJohn McCarthyは、それが計算機言語として使えるとは考えもしませんでした。単なる計算モデルとして記述した万能関数evalを、彼の研究室の大学院生であるSteve RussellがIBM 704の機械語で実現したことによって、初めてプログラミング言語Lispが誕生したのです。

+
+
+

Lispのすごさ

+
+

しばらく前になりますが、あるオブジェクト指向関連のイベントで「私がオブジェクト指向を理解したのはMartin Fowlerの『リファクタリング』を読んでからです」という講演を聞いて驚愕きょうがくしました。オブジェクト指向に最初に触れたのが1980年代で、処理系といえばSmalltalkという私のような人間は「もう、じじいの領域だな」と強く感じた一瞬でした。

+

しかし、実際問題として、Javaで初めてオブジェクト指向を学んだ人はすでにかなりの割合になっているようです。そういう人たちにとっては、オブジェクト指向が真新しい概念のように感じられるかもしれません。また、Javaで強調されている例外処理やガベージコレクション、バーチャルマシンなどの概念も、最近登場したもののように感じられることでしょう。

+

しかし、実際にはそれらはみんなJavaよりも何十年も(文字どおり何十年も)前にLispで実現されていたのです。オブジェクト指向の登場が1968年のSimulaであったことをご存じの方も多いと思います。実に40年近くも前のことです。1980年代にはLisp処理系上にオブジェクト指向システムを構築する研究が多数行われており、それらの研究を踏まえて、1988年にはCommon Lispの標準としてCLOS(Common Lisp Object System)が取り込まれています。

+ +

このCLOSは多重継承やマルチプルメソッドなど現在でもまだ斬新と考えられる機能がたくさん盛り込まれています。また、最近話題のアスペクト指向に類似の機能4 であるメソッドコンビネーションも含まれています。Javaなどがようやく取り込もうとしている技術は、20年前のLispの技術だったわけです。

+

アスペクト指向だけではありません。Javaで初めてガベージコレクションを知ったという人も多いでしょうが、Lispはごく初期の処理系からガベージコレクションを備えていました。データをオブジェクトとして取り扱い、メモリの割り当てを明示的に行わないLispでは、ガベージコレクションは必須だったのです。これまた40年も前の技術ということになります。

+

バーチャルマシン、バイトコードインタプリタなどの単語もJavaとともに広く知られるようになりましたが、もともとはSmalltalkで用いられていた技術です。Smalltalkの実装は1970年代後半から1980年代初頭にかけて行われており、その技術もまたLispの影響を受けています。見る人が見ればSmalltalkの処理系はLispの処理系にそっくりであることがわかります。

+

同じ時期に生まれたFORTRANやCOBOLが今まで蓄積した膨大な資産を管理するためにほそぼそと生き残っているのに対して、Lispはいつも時代の最先端に位置しているのは興味深いことです。

+
+
+

Lispの強さ

+
+

最先端の機能を提供してきたLispですが、その強さは特定の機能に見出せるわけではありません。むしろ、Lispはいろいろな機能を実験してみるのに非常に便利なため、その中でよいものが生き残ったと考えるべきでしょう。Lispの先進性は、Lispの強さの副作用とでも呼ぶべきものなのです。

+

その強さを表現するキーワードは「動的」です。インタプリタそのものであるevalを持っているLispは非常に動的で、単なるデータだけでなくプログラム自体を取り扱うことができます。Javaなどにもリフレクションという名前でプログラム自体を扱う機能が提供されていますが、データとプログラムが同一のフォーマットで表現されるLispの動的性にはかないません。プログラム自体を扱うプログラミング、メタプログラミングによって、Lispの上にいわば別の新しい言語を作り出すことが簡単にできます。新しく言語処理系を書かなくても目的別に特別設計された言語、ドメイン特化言語5 を作り出すこともできますし、言語自体に手を加えなくてもオブジェクト指向機能のようなものも実現できます。

+
+
+

Lispの不幸

+
+

そんなに優れた言語なのに広まらなかったLispは、不幸としかいいようがありません。もちろん、ただ単に運が悪かっただけではなく、いろいろと理由はあるのでしすが。

+

その1つはさまざまな誤解です。Lispは他のプログラミング言語に比べて「高級」なので、実装が大変に難しく、なかなか性能のよい処理系が登場しませんでした。性能第一のFORTRANなどの言語と比較すると、その点で長らく不利でした。その後、実装技術が向上して他の言語と遜色ない性能が出せるようになってもその誤解は解けず、いつまでも「Lispは遅い、使えない」と思われてきたのです。また、学者しか使わない言語というイメージが形成されたのもつらいところです。

+ +

もう1つの理由はあのかっこでしょう。Lispのプログラムには大量のかっこが登場します。慣れれば優先度などが明確に表現されるよい文法なのですが、少なくとも初心者は引いてしまいそうです。また、プログラミングスタイルが「普通」の言語とは相当異なることも問題です。この点では関数型言語も似たような障壁に当たっているようです。

+
+
+

MatzLisp

+
+

JavaはLispで培われてきた技術を広く知らしめるのに貢献しました。今まで知る人ぞ知る技術であったものが、Javaのおかげでたくさんの人が知る「常識」に格上げされたといってもよいでしょう。しかし、Javaには、Lispの強さのごく一部しか取り込んでいません。「時代は動的言語」といわれていますが、それはJavaが提供しなかったLispの強さを取り込んでいく、世間がますますLispの強さに気が付いていく過程なのかもしれません。私のデザインしたRubyがその一翼を担っているのは誇らしい限りです。

+

先日、某イベントの二次会で「実はRubyは『MatzLisp』6 っていうLispの方言だったんだよ!」と語られたようです。なんとも傑作なネタですが、Lispの強さを痛感した私が「自分が満足するために」作り出したRubyは、文法こそ違うものの、その本質としてLisp文化を継承しているのかもしれません。

+
+
+
+
+
    +
  1. +

    ハッカーになろう

    +

    原題は「How To Become A Hacker」。日本語訳は山形浩生さんの訳で以下のURLで読める。
    +http://cruel.org/freeware/hacker.html +

    +
  2. +
  3. +

    以下のように反応

    +

    エッセイ「普通のやつらの上を行け」での反応。このエッセイの川合史郎さんによる日本語訳は以下のURLで読める。
    +http://www.shiro.dreamhost.com/scheme/trans/beating-the-averages-j.html +

    +
  4. +
  5. +

    Lispプログラマーの端くれ

    +

    もっとも私が普段プログラムしているのはCやらRubyやらで、Lispを使うのはもっぱらEmacsのためだし、私の書いたプログラムはいつもLispらしくないのだが、今回はそれは棚に上げることにする。 +

    +
  6. +
  7. +

    アスペクト指向に類似の機能

    +

    それもそのはず、CLOSの設計者にはAspectJの開発者Gregor Kiczaleがいる。「アスペクト指向に類似」というよりアスペクト指向の元になったというべきか。 +

    +
  8. +
  9. +

    ドメイン特化言語

    +

    目的別に特別設計された言語(DSL: Domain Specific Language)。 +

    +
  10. +
  11. +

    MatzLisp

    +

    MITで開発された有名なLispにMacLispというものがあり、それとかけているという点でもこのネタは秀逸である。 +

    +
  12. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-048.xhtml b/docs/vol2/xhtml/p-048.xhtml new file mode 100644 index 0000000..b49a776 --- /dev/null +++ b/docs/vol2/xhtml/p-048.xhtml @@ -0,0 +1,110 @@ + + + + + +第59章 スケーラビリティ + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay59 +
+

+まつもと ゆきひろのハッカーズライフ
+スケーラビリティ +

+
+

[オープンソースマガジン, 2006年3月号]

+
+

原子やそれ以下の素粒子の振る舞いが、われわれの日常生活の常識では考えられない挙動をとる量子力学的構造が、プログラミングの世界でも成立して、データ量やアクセス数などが増加すると、思いもかけない挙動が発生することを予見した記事。文中で「プログラミング言語では過去20年間大きな変化が起きていない」とありますが、それから約20年経ってもあまり大きな変化は起きていないようです。もちろん、動的言語全盛期の当時と、静的言語が流行している現在とではいろいろ異なることもありますが、当時まったく存在していなかった概念ではないという意味で。現在でもスケーラビリティは未来予測のための重要なキーワードだと思います。大きな構造は時間が経ってもそれほど変化しないようです。

+
+
+

20年間大きな変化のないプログラミング言語

+
+

先日、某雑誌のインタビューに答えているときに「プログラミング言語の領域では、ここ20年革新的な進歩は起こっていない」と発言して、記者の人を驚かせてしまいました。「これだけ進歩の激しいIT業界にあって、20年間にわたって革新的な進歩のないとはいったいどういうことか」という顔をしていました。

+

前回でも紹介したように、プログラミング言語において革新的な「発明」はLispの周辺でみんなずっと昔に登場してしまっています。進歩しているように見えるのは、最近になってようやっと世間が追いついてきて、昔から存在していたものを「再発見」しているからです。オブジェクト指向に例外処理、ガベージコレクションやバーチャルマシンも何もかも、もう何十年も前から存在していたのです。知られてなかっただけで。

+

もっともプログラミング言語というものは、もともと人間が自分の考えをどのように表現するかという「記法」としての性質が強いですから、人間の本質がなかなか変化しない以上、あまり急激には進歩できない領域ではあります。

+
+

プログラミング以外の領域においても、決して進歩が速いとはいえそうにありません。急激に普及したネットワークの分野でも、改めて考えてみれば、今でも日常的に使われているメールが最初に発信されたのはもう30年以上前のことですし、インターネットの誕生からも同じくらいが経過しています。Webは確かに目新しいアイデアですが、それを構成している技術的要素は古くから存在していたもので、決して革新的なものではありません。

+

結局、普通の人が感じている「ITの目覚ましい発展」とは、実際には「ITの目覚しい進歩」ではなく、「ITの目覚しい普及」にすぎなかったのでしょう。今まで知らなかったからといって、存在していなかったとは限りませんからね。

+
+
+

ハードウェア領域の目覚しい進歩

+
+

ソフトウェアの領域と比べて、ハードウェアの方の進歩には目覚しいものがあります。

+

1946年、世界最初の電子計算機と呼ばれたENIACは、1秒間に5000回の演算を行うことができたそうです。ということは、0.005MIPS1 ということになりますね。さて、私の愛用のコンピュータでbogoMIPS2 を計算するとだいたい3162bogoMIPSになるので、大ざっぱに比較すると63万2400倍になります。63万倍ですか……。技術に進歩は恐ろしいものですね。

+

ところが、これらの変化が本当に「革新的」と呼べるものかというとよくわかりません。実際、世界で最も多く使われているCPUはいまだに30年以上前の命令アーキテクチャを引きずっているわけですし、逆に真に革新的だと思われたiAPX4323 などのCPUアーキテクチャはほとんど失敗に終わっています。これらは連続的な進歩であって、別に革新的なことではないのかもしれません。小さな工夫を積み重ねてハードウェアの性能を向上させ、大量生産によって価格を低減し、高い計算能力を備えたデバイスを広く普及させてきた、不断の努力の結果だといえるでしょう。このような変化は連続的なものですが、偉大な変化でもあります。

+

実際、63万倍の変化は馬鹿にできません。同じx86アーキテクチャで比較しても、1978年リリースの8086/5MHz(0.33MIPS程度)と2004年リリースのPentium M/1.6GHz(3162MIPS程度)では、26年間にMIPS値が9500倍以上になっています。

+
+
+ +

スケールによる劇的な変化

+
+

以上のことから、ITの変化というのは、ソフトウェアの進歩よりもハードウェアの性能向上と、価格低下による普及によって実現されてきたといっても過言ではないでしょう。これまでの歴史では考えられなかったような高性能のコンピュータが、広く、多く、大量に使われるようになっているのです。連続的な変化とはいっても、これだけスケールが伴うと大きなインパクトが発生します。

+

物理学の領域では、極端にスケールが違うとまったく違う法則に従うことが知られています。たとえば、極端に小さな素粒子の世界では、粒子はある場所に確率的にしか存在しないとか、どんなに観測精度を向上させても決して確定させることができない事象が存在するとか、日常的な常識では考えられないこと4 が起こります。また、逆に何億光年という極端に大きなスケールにおいても、異なる法則が登場してきます。

+

今、この瞬間にITの世界でも似たようなことが発生しているような気がしてなりません。20年前、私たちがやりとりするメールの数は一日数通とかそんなものでした。しかし、今や一日数百通のメールを受け取るのは普通ことで、中には数千通を受け取る人もいるでしょう。世界中に数千ページしかWebページが存在しなかった頃と、数億ページの情報があふれている現在とでは、情報の探し方も異なってくるはずです。

+

このような変化を発生させている元凶は、人間にあります。数百通のメールを管理を想定して設計されたアプリケーションで数百万通のメールの管理をすることは不可能ではないでしょうが、たぶんいろいろな局面で不都合が発生することでしょう。メールの処理に耐えがたい時間がかかったり、必要なメールを見つけ出すことができなかったり。あるいは、数億ページのWebからある単語を含むページを探し出すことは、通常のテキストファイルを検索するgrepのようなツールが用いているのと同じアルゴリズムを用いても不可能ではないでしょうが、ベタに検索したのでは結果が出るまで何日もかかるでしょうし、検索結果として得られたその単語を含んでいる数万ページのうち、どれが重要か判別することはほとんど不可能でしょう。人間には忍耐力にも取り扱うことのできる複雑さにも限界があるのです。

+

このことをいち早く認識していたのがGoogleです。彼らは、ある情報を含むページをただ単にリスト化するだけでは不十分であり、どのページが重要であるかを高速かつ自動的に行う必要があることを認識していました。PageRankという手法を導入した彼らの検索エンジンは既存のライバルたちよりも「よい結果」を返し、Googleは後発ながらもあっという間に首位の座を獲得したのです。

+
+
+

キーワードはスケーラビリティ

+
+

ということで、これからのキーワードは「スケーラビリティ」になると予想します。今までと同じペースかどうかはともかく、コンピュータの性能はこれからも向上していくでしょう。また、ネットワークにつながれたコンピュータの台数や、それらの持つ記憶容量、また流通する情報量は、想像を超えるレベルで増加するはずです。「そのような事態にどう対処するか?」が今後最も大きな課題になっていくわけです。

+

考えてみれば、増大する複雑さに対処するというのは、プログラミング言語を始めとしたソフトウェアが長年直面してきた課題ですが、これからはその増加速度がどんどん加速するのではないでしょうか。

+ +

エクストリーム・プログラミング(XP)は、「テストがよいものであれば、限りなく頻繁に行えばよいだろう」「コードレビューがよいのであれば、いつもレビューしながらプログラムすればよいだろう」というように、ボリュームを極端(エクストリーム)にひねってみることから誕生したのだそうです。IT業界の未来を予想する際も、ボリュームを極限までひねってみるのがよいかもしれません。たとえば、「インターネットユーザーが数十億人を超えたらどうなるだろう」とか「一日に受け取るメールが数万通を超えるようになったらどうしたらよいだろう」など。そのときは、できるだけ機械化・自動化する(Google PageRankの例)、みんなの力を結集する(Wiki、ソーシャルタギング5 などの例)、非同期化してクライアントに処理を分散する(Ajaxなどの例)といった技法をいろいろと考案していく必要があります。ふと考えてみると、これらはみな最近話題になっているWeb 2.06 で注目されているものだったりします。Web 2.0を説明するには、スケーラビリティの観点から行うとよいかもしれません。

+

私の得意なプログラミング言語の領域でも、「ある言語から利用可能なライブラリが数万を超えたらどうなるだろう」とか「名前の衝突の回避や組織化をどう行えばよいのか」など、いろいろ考えることはありそうです。

+
+
+
+
+
    +
  1. +

    MIPS

    +

    Million Instructions per Secondの略。CPUが1秒当たりに処理できる命令数を100万命令単位で表現した数値。CPUの性能を比較するおおざっぱな指標として、ふた昔前に用いられた。もっともコンピュータの性能はI/Oなどトータルなシステムで決定されるので、処理命令数だけでは決まらない上、命令によって処理時間が異なったりするCPUもあるので、結局、指標としては廃れてしまった。 +

    +
  2. +
  3. +

    bogoMIPS

    +

    Linuxが内部で計算する「非科学的な」MIPS値。CPUの大まかな性能を示すが、厳密なベンチマークに使えるほど正確な値ではない。ただ、Linuxマシンであればすぐに得られるので便利な値でもある。/proc/cpuinfoを表示させることで確認できる。 +

    +
  4. +
  5. +

    iAPX432

    +

    1981年、i80386に先立って発表されたインテル初の32ビットプロセッサ。 +

    +
  6. +
  7. +

    日常的な常識では考えられないこと

    +

    日常的なスケールでは無視しても実質的に変わらないため、これらを考慮しない法則が使われている。 +

    +
  8. +
  9. +

    ソーシャルタギング

    +

    ソーシャルブックマーク(Web上に登録していくブックマークシステム)などに集積された情報にタグを付加してカテゴライズする方法。 +

    +
  10. +
  11. +

    Web 2.0

    +

    次世代Webの主流となるべきスペックやポリシーのこと。現段階では、はっきりした定義が決められているわけではない。 +

    +
  12. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-049.xhtml b/docs/vol2/xhtml/p-049.xhtml new file mode 100644 index 0000000..76bf318 --- /dev/null +++ b/docs/vol2/xhtml/p-049.xhtml @@ -0,0 +1,127 @@ + + + + + +第60章 オープンソースライセンス + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay60 +
+

+まつもと ゆきひろのハッカーズライフ
+オープンソースライセンス +

+
+

[オープンソースマガジン, 2006年4月号]

+
+

オープンソースライセンスの選び方についてです。この点については現代でもほとんど状況に変化はありませんね。この原稿が書かれた頃と比べると、MITライセンスの使用例が劇的に増えたことと、Apacheライセンスを採用するものもそれなりに増えた点でしょうか。この当時はRubyは独自ライセンス(Rubyライセンス)とGPLのデュアルライセンスでしたが、このあと、大変な苦労をしてRubyライセンスとBSDライセンスのデュアルライセンスに変更しました。これはRubyライセンスにリライセンスを許可する条項が入っていたから可能になったもので、そうでない場合はFSFが行っていたようにソースコード貢献者すべてから事前に同意を取っていなければ事実上不可能だったことでしょう。

+

ライセンスの取り扱いは確かに面倒ですが、より大きな面倒を避けるために最初にキチンとしておいたほうがよいでしょう。これは時代を超えた教訓です。

+
+
+

プロジェクトの障害

+
+

先日、15年ぶりにGPL1 の新版(v3)のドラフトが公開2 されました。具体的な内容はともかく、フリーソフトウェア/オープンソースのライセンスが再び注目されています。今回はライセンスというものについて考えてみましょう。

+

世の中にハッカー気質を持つ人はたくさんいますが、その数だけオープンソースプロジェクトがあるかというと、なかなかそうはいきません。「自分のオープンソースプロジェクトを始める」のは敷居が高く、さまざまな障害があるわけですが、オープンソースプロジェクトにおける非技術的な障害の筆頭はライセンスでしょう。他の問題の多くは、そもそも根源的な問題である「何を作るのかを決める」とか、「実装手段を考える」あるいは「デバッグする」に至るまで、ある程度「ハッカー魂」を揺さぶるものがあります。だからこそ、ハッカーたちは喜んでプロジェクトに参加するわけです。

+ +

ところが、ライセンスときたら、プログラムほど明確ではないし、技術的にも面白くもないし、その割には厳密な話をし始めると面倒で、煮ても焼いてもおいしくありません。いっそ「こんなものなければよいのに」と思うほとです。では、なぜわざわざライセンスを用意するのかというと、主な理由は意思表明のためです。適切なライセンスには、「開発者がこのソフトウェアをどう扱ってほしいか」という意思をはっきり示すことになります。それによって、ユーザーは安心してソフトウェアを使うことができるわけです。これが不明瞭な場合、ユーザーが確実に安心してソフトウェアを利用するには、開発者に直接問い合わせるしかありません。世界中から「こんなケースに使ってもよいか?」と問い合わせが殺到するのは、決してうれしいことではありません。また、ライセンスには、訴訟など法的なトラブルから守られる(かもしれない)という期待も込められています。

+
+
+

ライセンスの選び方

+
+

オープンソースプロジェクトを開始するとして、そのソフトウェアに設定するライセンスとして何を選んだらよいでしょうか。

+

私からできる最初のアドバイスは、「決して新しいライセンスを作らない」です。13年前、Rubyの開発を開始した時点でそのことがわかっていれば、私の人生はもうちょっとだけ楽だったでしょう。当時の私は、ソースコードの流用を明示的に許可したライセンスを提供したいと考えていました。そこで、Perlのライセンス(Artisticライセンス3)をベースに流用を許可し、入出力データ(Rubyの場合はRubyプログラム)には制限が及ばないことを明記したライセンスを用意しました。ライセンスを作るのは、プログラミングに少し似ています。何を許可したいとか、何を禁止したいとか考えるのはアルゴリズムのときと同じです。思い返せば、確かに作っていたときは楽しかったんです。

+

しかし、この「コード」はプログラムよりもずっとデバッグが大変です。プログラムほど簡単に「実行」できないので、問題はすぐには発見されませんし、たとえ見つかっても簡単には修正できません。

+

たとえば、自分で作ったライセンスに含んだ条項に何らかの問題があったとしましょう。すぐに思い付くのは、GPLと互換性がなく、GPLソフトウェアとリンクができなくなってしまうケースです。「似たような思想に基づいて作られたライセンス同士が非互換とは、いったいどういうことか」と悲しくなりますが、これがライセンスの現実です。その場合には、(困るユーザーもいることでしょうが)GPLソフトウェアとのリンクをあきらめるか、あるいはライセンスの変更を行うことになります。

+

このとき、ライセンスを適用したソフトウェアの全コードがあなたによって書かれたものであれば、問題はほとんどありません。新しいバージョンを新しいライセンスでリリースすればそれでおしまいです。しかし、たとえば別の人のパッチが含まれていれば、ソフトウェアはあなただけのものではなく、法的にはパッチを書いた人との共同著作物という扱いを受けます。厳密な話をすると、他人の権利を勝手に侵害するわけにはいきませんから、理論上ライセンスの変更にはこれら共同著作者すべての合意が必要です。何年もの間、開発を行ってきたRubyのようなソフトウェアでは、権利関係者が何人いるのかすでに確認しようがありません。FSF4 のソフトウェアのようにコードを貢献する場合には権利譲渡契約を結ぶようにしているなら楽なんでしょうが、パッチをもらうたびにそのような手続きを行うのも面倒です。たいていの場合は、ライセンスの変更をあきらめてしまうか、さんざん努力しても結局は全員の確認を取れず、誰かから苦情が出ないことを祈りつつ、変更することになります。

+ +

自分でライセンスを作るということは、そのような苦労を何年にもわたって一手に引き受けるということも意味するのです。心からアドバイスします。止めたほうが無難です。

+
+
+

オープンソースライセンスの選び方

+
+

あなたがオープンソースプロジェクトを開始するとして、将来問題が起きない(起きにくい)ライセンスを選ぶにはどうしたらよいでしょうか。

+

世の中にはたくさんのオープンソースライセンス5 が存在します。すでに多すぎるほどです。この中から選び出すだけで十分でしょう。それもメジャーなものを選択することをお勧めします。だいたいライセンス変更のようなトラブルが起きるのは、マイナーなライセンスを選んだプロジェクトと相場が決まっています。メジャーなものは広く使われているので、大きな問題が残っていないと考えられますし、いざというときに仲間が多いのも安心です。マイナーなライセンスでは、他のライセンスとの組み合わせなど十分な検討が行われていないことが多く、不安が残ります。

+

ソフトウェアのライセンスを選ぶに当たってまず考えなければならないのは「コピーレフトを望むか」どうかでしょう。コピーレフトとは、簡単にいうと「あなたの書いたフリーなソフトウェアに対して、自由を擁護しないものがタダ乗りすることを許さない」というものです。FSFはソフトウェア自由の擁護者として、コピーレフトを強く推進しています。一方、コピーレフトについてさほど気にしていない開発者も多いようです。あなたがコピーレフトを望むのであれば、選択肢はほぼGPLしかありませんし、ライブラリとして使われるものであればLGPL6 も候補にあがります。ライブラリにGPLを適用すると、事実上GPLソフトウェアからしか使えないので、より「制限」の緩いLGPLのほうが広く使われる可能性があります。しかし、LGPLはあまり使い勝手がよくないうえに、「わかりにくい」「十分な考察が進んでいない」などの欠点がありますので、コピーレフトを強く望まない場合にはお勧めできません。

+

コピーレフトにそれほどを強いこだわりのない人には、GPL以外の選択肢もあります。ここで重要なのは、「そのソフトウェアは、別のソフトウェアとのリンクが必要かどうか」です。将来、何らかの形でGPLソフトウェアとのリンクされることが予想される場合には、GPLとの互換性7 が重要になります。プラグイン機能を持つソフトウェアやライブラリはこの点に注意しなくてはなりません。GPLと矛盾しないライセンスとしては、修正BSDライセンスやX11ライセンス8 などがあります。

+ +

もう1つの検討すべき要素は、関連ソフトウェアとライセンスをそろえることでしょう。たとえばEclipseプラグインなどはCPL9 を選択すべきでしょう。また、PHP関連のソフトウェアはPHPとライセンスをそろえたほうが無難です。Rubyで書かれたソフトウェアは、Ruby自身とはライセンス的に独立なのですが、それでもRubyライセンスを選ぶ人が多いようです。

+
+
+

避けたいライセンストラブル

+
+

すでに述べたように、ライセンスのトラブルは「面倒くさい」「面白くない」などイヤなものです。しかも、悪いことに法的な問題は「問題があるのかないのかはっきりしない」というオマケ付きです。「はっきりしたことは裁判してみないとね」とかいわれても、被告になんかなりたくないし……。

+

訴訟リスクをゼロにすることは不可能みたいですが、それでも面倒な問題を回避するためにも、ライセンスについては賢く付き合いたいものです。そして、われわれの本来の「仕事」であるプログラミングに集中しましょう。Happy Hacking。

+
+
+
+
+
    +
  1. +

    GPL

    +

    http://www.gnu.org/licenses/gpl.txt +

    +
  2. +
  3. +

    ドラフトが公開

    +

    http://gplv3.fsf.org/draft/ +

    +
  4. +
  5. +

    Artisticライセンス

    +

    Perlのライセンスは、このArtisticとGPLの二本立て。Larryが自分の意見を表明したもので、FSFからは単体では「フリーソフトウェアライセンスではない」とまでいわれている。これをベースにしたのがRubyライセンスの不幸の始まりというか何というか。 +

    +
  6. +
  7. +

    FSF

    +

    Free Software Foundationの略。
    +http://www.fsf.org/ +

    +
  8. +
  9. +

    たくさんのオープンソースライセンス

    +

    「オープンソースの定義」を満たすとしてOpen Source Initiativeが認定したライセンスは60種類近くに上る。認定されていないが定義を満たすであろうライセンス(Rubyライセンスもその1つ)を含めるといったいいくつになることやら。 +

    +
  10. +
  11. +

    LGPL

    +

    http://www.gnu.org/licenses/lgpl.txt +

    +
  12. +
  13. +

    GPLとの互換性

    +

    http://www.gnu.org/licenses/license-list.ja.htmlにはGPLと他のライセンスの組み合わせという観点から種々のフリーソフトウェアライセンスについて述べられている。ライセンスのことを知ろうと思えばまずここを読んでほしい。 +

    +
  14. +
  15. +

    修正BSDライセンスやX11ライセンス

    +

    http://www.opensource.org/licenses/bsd-license.php
    +http://ftp.x.org/pub/X11R7.0/doc/html/LICENSE.html +

    +
  16. +
  17. +

    CPL

    +

    Common Public Licenseの略。
    +http://www.opensource.org/licenses/cpl1.0.txt +

    +
  18. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-050.xhtml b/docs/vol2/xhtml/p-050.xhtml new file mode 100644 index 0000000..27192b8 --- /dev/null +++ b/docs/vol2/xhtml/p-050.xhtml @@ -0,0 +1,138 @@ + + + + + +第61章 Get Thing Done + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay61 +
+

+まつもと ゆきひろのハッカーズライフ
+Get Thing Done +

+
+

[オープンソースマガジン, 2006年5月号]

+
+

タイトルこそ「Get Things Done」ですが、David Allen氏のライフハック術とは無関係にToDo管理について紹介しています。さすがに利用しているツールについては時代の影響が大きく、現在でも使っているツールはほぼありませんでした。現在はPDAの代わりにAndroidスマートフォン、スケジュール管理はGoogle Calendar、メモにはGoogle Keep(とhowmもまだ使っている)、Rubyのイシュー管理はRedmineに移行しています。そういえばメール管理にmorqを使っている点は同じでしたね。

+
+
+

老化現象

+
+

時間とともに衰えていくのは世の常ですが、万年青年を目指す私も最近老化を感じるときがあります。最も端的なのは忘れっぽくなったことです。日常会話でも「アレ」とか「ソレ」とかを連発して、家族に笑われます。どうでもいいようなトリビアはいつまでも覚えているのに、日常生活に関わる肝心なことを忘れてしまうのです。私の記憶はいったいどうなっているんでしょう。

+

つい最近のことですが、海外出張の際、自宅を出てからパスポートを忘れているのに気が付き、慌てて取りに戻りました。さらに翌日、出張先のレストランでパスポートの入ったカバンを忘れるという失態を演じてしまいました。幸いすぐに気が付いて、次の日には無事に回収できましたが、あまりの失敗にこれ以上ないほど落ち込みました。しかし、こんなうっかり者でも致命的な事態を回避できたのは、不幸中の幸いというか、悪運が強いというか。

+

ハッカーたるもの、能力の不足はツールで補うべきでしょう。かくいう私も、いくつかのツールを使って役に立たない記憶力を補っています。

+
+
+ +

スケジュール

+
+

まずはスケジュール管理。世のビジネスマンと比べるとアポイントメントの少なそうな私ですが、それでもスケジュール管理は必要です。月に1度程度はどこかのイベントでプレゼンテーションを行っていますし、年に数回は海外出張もあります。また、仕事以外にも細々とした予定が入るものです。

+

それらのスケジュール管理にはPDAを使っています。機種はもう骨董品レベルのVisor Edgeです。モノクロ低解像度で、最新機種に比べるとオモチャのようなものですが、意外と役に立って1 くれています。

+

一時は普段から持ち歩いているノートPCでスケジュール管理しようと思ったこともあったのですが、ノートPCではサスペンド状態から復帰するのにそれなりの時間(10秒程度)がかかります。通常の利用であればそれほど気にならない時間ですが、ちょっとスケジュールを確認するだけのときには耐えがたいほど長く感じます。フル充電のバッテリでも3時間弱しか持たないので、残量をいつも気にしなければならない点も問題です。

+

それに比べたら、PDAは起動が速いのでいざというときにすぐ見られますし、充電しなくても何日も持つのでバッテリ残量をほとんど気にする必要がありません。また、PDAではスケジュール管理しかしないので、CPUパワーも気にならないのです。ちなみに私は、日本語入力にPOBox2 を、スケジュール一覧にDate@Glance3 というソフトウェアを使っています。

+
+
+

ToDo管理

+
+

PDAには、スケジュール管理以外にToDo管理やメモなどの機能がありますが、私はそれらをほとんど利用していません。ToDoなどを知りたいと思うときにはPCを起動していることが多く、わざわざPDAの打ちにくい入力方法4 を使う必然性がないのです。

+

ToDo管理をするソフトウェアはたくさんありますが、私は主にメールリーダーの機能を使っています。仕事の依頼の多くはメール経由でやってきますから、必要なメールに特定のラベルを付けておくことで、ToDoリストの代わりになります。自作のメールリーダー(morq)には、1キーでメールに「action」というラベルを付けたり、外したりする機能を用意しています。私の場合、スケジュールなどと独立したToDoはあまり存在しないので、この程度で十分のようです。PDAによるToDo管理は、入力が面倒なためにあまり更新しなくなり、更新しないのでチェックもせず、結局活用しなくなりました。また、Webアプリケーションを含む、その他のツールも長続きしませんでした。もっと忙しい人ならば、違った結論になるのかもしれません。

+ +

また、現在のToDo管理方法に完全に満足しているわけでもないので、これからもToDo管理ツールについては考えていきたいと思っています。

+
+
+

メモ

+
+

ちょっとした思い付きをその場限りで書き留めるためとか、考えをまとめるために書くメモは、その辺の紙切れに書き付けます。何でもPCに頼ってしまうのは悪い癖です。図やグラフを含めて考えを記述する方法としては、PCよりも古典的な手法である紙とペンのほうが勝っています。

+

しかし、紙が万能というわけではなく、いくつかの欠点もあります。1つは検索できない点です。紙に書かれたデータはあとで検索できません。片付け上手でない私は、書いたメモをすぐにどこかになくしてしまうので、あとで参照する必要のあるデータは検索できる形式でPCに格納しておきます。

+

もう1つの欠点は、書いた字を読めないことがある点です。私の字はお世辞にもほめられたものではありません。文章を書くのにPCを日常的に使うようになってからは、ペンで字を書く機会がすっかり減ってしまい、ますます汚い字になってしまいました。単に汚いだけなら恥ずかしいだけで済むのですが、あとで自分でも読めないとなると笑えません。そのような欠点を考えると、文字だけで表現できるメモはやっぱりPCに入力するのがよさそうです。

+

メモ入力に使っているツールは、Emacsでの「一人お手軽Wikiもどき」であるhowm5 です。実はhowmにはスケジュール管理機能とかToDo管理機能も付いているのですが、私はそれらをあまり活用していません。もっぱら「思い付いたことを適当に書きとどめる」という使い方をしています。ふと浮かんだアイデアは、そのままだとすぐに忘れてしまうので、とりあえずhowmにメモとして放り込み(PCを起動していないときは紙に書いてあとで入力)、必要に応じてあとで検索しています。

+

今回、自分のhowmメモの中身を振り返ってみると、「原稿のネタ」「Ruby 2.0新仕様のアイデア(ボツになるもの多し)」「日記6 の下書き」「議事録」などが多いようです。

+

Webを眺めていて、日記のネタとしてあとでコメントしようと思った場合には、いしなおさんのMM/Memo7 を活用させてもらっています。Firefoxのbookmarkletで簡単に登録できるので重宝しています。興味を持ったページはとりあえずMM/Memoに登録しておき、あとでその中から選んで日記に書いています。何でもWebブラウザで行う最近の風潮は(たとえAjaxを駆使していても)あまり賛成できませんが、いま見ているものに対する処理をWebブラウザ内で行うことについては非常に合理的だと思います。

+
+
+ +

協調作業

+
+

私自身の情報管理は上記の方法でほぼ満足しているのですが、これだけではカバーできないこともあります。それは複数の人が関わる協調作業についてです。

+

私の関わる協調作業で最も重要なのは、やはりRubyの開発でしょう。特に、バグフィックスや機能拡張などにおけるToDo管理は必須です。このために利用しているツールは2つあり、1つは「Rubyのバグ(勝手に)トラッカー8」です。これは、メーリングリストに報告されたバグがどんどん登録され、ChangeLogに対応するメール番号を入れてコミットすると、自動的にfixedマークが付けられます。

+

バグの管理はRubyのバグ(勝手に)トラッカーでよいとしても、リリーススケジュールの管理には不十分です。Rubyの開発者は30人近くいますから、彼らをまとめるには別のツールが必要となります。この目的にはbasecamp9 を使っています。これはRuby on Railsの開発者David Heinemeier Hanssonが所属する37signalsが提供しているプロジェクト管理ツールです。というか、basecampの基本部分を抜き出したものがRailsなのです。1.8.4リリースのときからbasecampを使い始めましたが、非常に便利です。

+
+
+

いつでも最適化を

+
+

情報管理は基本中の基本です。ハッカーであれば、自分の情報管理を最適化するための工夫を怠ってはなりません。日々精進です。ところでこの原稿を書いていて、やり忘れていたToDoを発見してしまいました。私にもまだまだ改善の余地はありそうです。

+
+
+
+
+
    +
  1. +

    意外と役に立って

    +

    すっかり気に入っているので、将来壊れてしまったときに備えてもう1台譲り受けて保管している。 +

    +
  2. +
  3. +

    POBox

    +

    予測と曖昧検索に基づく高速テキスト入力システム。
    +http://pitecan.com/OpenPOBox/PalmInline/ +

    +
  4. +
  5. +

    Date@Glance

    +

    Palm OS用の予定表補完ソフトウェア。
    +http://simple-palm.com/dateatglance/ +

    +
  6. +
  7. +

    PDAの打ちにくい入力方法

    +

    予測変換入力法であるPOBoxを使えばずいぶんマシになるが、それでも長文の入力は現実的ではない。 +

    +
  8. +
  9. +

    howm

    +

    http://howm.sourceforge.jp/ +

    +
  10. +
  11. +

    日記

    +

    「Matzにっき」
    +http://www.rubyist.net/~matz/ +

    +
  12. +
  13. +

    MM/Memo

    +

    http://www.1470.net/mm/ +

    +
  14. +
  15. +

    Rubyのバグ(勝手に)トラッカー

    +

    http://mput.dip.jp/rubybugs/ +

    +
  16. +
  17. +

    basecamp

    +

    http://basecamphq.com/ +

    +
  18. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-051.xhtml b/docs/vol2/xhtml/p-051.xhtml new file mode 100644 index 0000000..178bcf2 --- /dev/null +++ b/docs/vol2/xhtml/p-051.xhtml @@ -0,0 +1,104 @@ + + + + + +第62章 若人への手紙 + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay62 +
+

+まつもと ゆきひろのハッカーズライフ
+若人への手紙 +

+
+

[オープンソースマガジン, 2006年6月号]

+
+

当時の中学生からいただいたお手紙への返事。もちろん、雑誌の記事にするだけでなく、返答部分は本人にも直接送っています。しかし、2006年に15歳ということは、(2024年)現在すでに32歳ということになりますね。改めて時間のすぎることの早さを実感します。

+

さて、返事の中では静的型言語、動的型言語、関数型言語(Lispを関数型言語と呼ぶのは若干の抵抗がありますが)の3つを学ぶことを勧めています。現在であれば第1グループはC, C++, Rust, Go, 第2グループはJavaScript, Python, Ruby, 第3グループはHaskell, OCamlくらいでしょうか。言語のレパートリーは若干変化しても、本質的な部分はあまり変わっていないようです。

+
+
+

後輩からの手紙

+
+

先日、見ず知らずの方からメールをいただきました。プログラマーの後輩として、どのようにプログラミングを学べばよいか尋ねる内容です。以下、引用します。

+
+

はじめまして。

+

僕は、今15歳です。

+

僕は、コンピュータにとても興味があり、近い将来、ITに関わる仕事がしたく、まつもとさんのような超優秀なプログラマーになりたいと思っています。そこで、いまからITについて猛勉強しようと、いろんなウェブサイトを見て回りましたが、何しろ情報量が非常に多く、かえって混乱してしまいました。そこで、ご質問なんですが、具体的にどのようなことから勉強を始めればいいですか。それから、入門に適切な書籍やウェブサイトなども紹介していただければ幸いです。

+

お忙しいでしょうが、お返事をお待ちしております。

+
+
+

私ごときが「超優秀なプログラマー」と呼ばれるのは気恥ずかしいのですが、若い人がプログラミングに対して真剣に向き合おうという姿勢はとても頼もしく感じました。

+
+
+

昔の記憶

+
+

考えてみると、私の若い頃と現在の若者とでは周囲の状況がまったく違います。私は、ちょうど彼と同じ頃にプログラミングを始めましたが、そのときに使っていたのはシャープのポケコン1 で、400ステップのBASICプログラムを実行するのがやっとでした。あの頃はインターネットというものはありませんから、情報の入手先も主に雑誌と書籍だけです。雑誌に載っているプログラムリストを必死に入力して、動かしては悦に入っていたことを覚えています。主にゲームでしたが、文字がピコピコ動く程度のかわいいものでした。

+

それに比べると、今の若い人たちは大変恵まれた環境にいます。世間にはコンピュータがあふれていますし、情報はインターネットからいくらでも入手できます。また、ゲームは昔に比べると超高速のマシン上ですばらしい品質のものが遊べるようになりました。しかし、プログラミングに興味がある人は以前よりも減ってしまったようです。現代ではIT技術が進歩しすぎて、コンピュータを使うことと、プログラミングが分離してしまっているのかもしれません。

+

傑作SF作品である『宇宙船ビーグル号の冒険』2 の1エピソードでは、非常に優れた文明が、衛星も近くの惑星もないため、段階的に発展できず、恒星間航行の技術を発明することなく滅んでしまいます。コンピュータの発達期に少年であった私たちは、自分自身の成長に応じてコンピュータが発展してきました。しかし、今の若者が「ファイナルファンタジー」を見て自分で作ろうと思っても、実現させるのはほとんど不可能3 ではないでしょうか。

+
+
+

先輩からの返事

+
+

現代において、コンピュータ好きのうち、プログラミングに興味を持つ人の割合は確かに下がっています。ただ、決していなくなったわけではありません。そして、その中にはインターネットからの情報とオープンソースの力を借りて、まさに超優秀なプログラマーが育っています。

+

たとえば、Haskell4 を使ってPerl 6コンパイラの基礎を2週間で作ってしまったAudrey Tangは、そのとき20歳になったばかりでしたし、その他にも10代や20代前半で大きな成果を上げる若者もそれなりにいます。15歳でコンピュータとプログラミングに興味を持ち、優秀なプログラマーになりたいと考える彼も、将来大変有望なのではないでしょうか。

+ +

そう思って、以下のような返事を書きました。

+
+

お若いのにご自分の将来について真剣に考えていらっしゃることを大変うれしく思っています。

+

さて、どのようなことから勉強を始めればよいかという質問ですが、これは大変答えにくいものです。というのは、何が効果的かは人によって違うからです。同じ本を読んで感動する人もいれば、全然何も感じない人もいます。コンピュータの勉強も似たようなものだと思います。

+

しかし、私がプログラミングを始めたのも15歳くらいでしたし、自分の若いころを振り返って役に立ちそうなことをいくつかあげておきましょう。

+

まず、コンピュータを使うこととプログラミングすることはまったく違うということをしっかり認識してください。昔、そう20年くらい昔にはこの2つはあまり区別されていませんでしたが、いまでは全然違います。ソフトウェアを使うのもまあ重要なことですが、プログラミングによってコンピュータに新しい仕事を教えこむことはそれ以上にエキサイティングで楽しいことです。

+

次に、プログラミング言語について学んでください。それもできれば複数。プログラミングは言語を駆使して行います。言語を知らなければプログラミングはできません。そして、プログラムを作るのにどのプログラミング言語を使うかで、プログラマーの思考は影響を受けます。ですから、1つだけの言語では考え方が偏ってしまいます。C, C++, Javaのような言語と、RubyやPythonのような言語、それからLisp, ML , Haskellのような言語の3種類からそれぞれ自分に向いていそうなものを学ぶといいと思います。プログラミングは手段であって目的ではないことに気を付けてください。つまり、プログラミングは(わかる人にとっては)とても楽しい行為ですが、それでも「完成したプログラムで何を実現するか」ということが最も重要なことで、それがなければプログラミングそのものにはたいして意味はなくなってしまいます。自分がどんな分野に興味があるのか、プログラムを作ることによってどんなことがしたいのか、そのことがわかれば能力を伸ばすことができると思います。私の知っている優秀なプログラマーは、みんな「自分が何を好きで、何がやりたいのか」をはっきり知っています。私自身はプログラミング言語にとても興味があり、いつか自分の言語を作ろうと心に思ったのは高校生のころでした。そのような気持ち(熱意)の継続により、大学に入り、就職し、Rubyを作り、世間から「優秀なプログラマー」と見なされるようになったわけです。自分から見れば、実際に優秀かどうかはやや疑問符が付きますが。いずれにしても、プログラミング言語を作りたいと思ってから実際にRubyを作るまでには10年以上かかっていますから、その間「作りたい」という気持ちを維持できたことが成功の1つの原因ではないかと思います。

+

自分が何に興味があるのかある程度認識できたら、その分野を中心にほかの人の書いたプログラムを読むのがよいのではないかと思います。書籍に載っているサンプルプログラムや、オープンソースソフトウェアのソースコードなど読むことのできるプログラムはたくさんあります。ただ、漠然と読むのは難しいですし、興味を維持することができませんから、知りたいことを調べるために読むという目的意識が必要でしょう。

+

私の若いころには思いどおりに使えるコンピュータなど個人レベルで買えるものではありませんから、中学生、高校生のころは本ばかり読んでいました。Pascalの本、Lispの本、人工知能の本など。ちゃんとプログラムを書き出したのは大学に入ってからです。それでも基本的な知識はあったので別に困りはしませんでした。

+
+

さて、年寄りの話はここまでです。パーソナルコンピュータが普及し、インターネットが当たり前になった時代に生きるいまの若い人は、また違った形でプログラミングを身につけるのではないかと思います。しかし、時代が変わっても、熱意とか継続とかは変わらない原則ではないかと思います。ご自分がなぜコンピュータに興味を持つようになって、本当はどんなことをしたいのか、ちょっと考えてみてはいかがでしょう。

+
+

このような若者がいる限り、「未来は明るい」と信じたい今日このごろです。

+
+
+
+
+
    +
  1. +

    シャープのポケコン

    +

    シャープ製ポケットコンピュータ「PC-1210」。1980年に発売された、シャープ製BASIC内蔵ポケットコンピュータの第1号。これでプログラミングにはまった業界人は多いと聞く。 +

    +
  2. +
  3. +

    『宇宙船ビーグル号の冒険』

    +

    A・E・ヴァン・ヴォークト著/沼沢洽治訳、「宇宙船ビーグル号の冒険」、東京創元社、ISBN4-488-60901-5。 +

    +
  4. +
  5. +

    実現させるのはほとんど不可能

    +

    しかし、技術の進歩によってフルCGアニメをほぼ一人で作成した「ほしのこえ」(制作: 新海誠)や「惑星大怪獣ネガドン」(監督: 粟津順)のような例もあるので油断はできない。 +

    +
  6. +
  7. +

    Haskell

    +

    プログラミング言語の研究を行うための共通の基盤を築くことを目的とし、近年定説となりつつあるプログラミング言語理論を集大成して作られた非正格純粋関数型言語。 +

    +
  8. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-052.xhtml b/docs/vol2/xhtml/p-052.xhtml new file mode 100644 index 0000000..d46f2f0 --- /dev/null +++ b/docs/vol2/xhtml/p-052.xhtml @@ -0,0 +1,142 @@ + + + + + +第63章 オープンソースのマーケティング + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay63 +
+

+まつもと ゆきひろのハッカーズライフ
+オープンソースのマーケティング +

+
+

[オープンソースマガジン, 2006年7月号]

+
+

技術者として「良いものを作れば売れる(or 広まる)」という考えは自然なものです。私もそのように感じてきましたし、実際Rubyについても開発開始(1993年)からこの記事を書く少し前(2005年頃)まで、私自身がそれほど広報活動をしたわけではありません。しかし、Ruby on Railsの登場は私の考えを変えました。きちんとした情報発信をしなければ、たとえ良いものでも広まらない、あるいは広まるのが遅すぎるという事実は私にとって驚きでした。まあ、気がついたからと言ってすぐに行動を改められるわけではなく、Rubyの広報活動はコミュニティメンバーに任せきりではありますが、それでも各種情報発信やカンファレンスの開催などでRubyに対する認知は大きく向上したのは確かです。

+
+
+

マーケティング表裏

+
+

マーケティングはお金に魂を売った人たちのもの、それが言い過ぎなら、ビジネス指向の人たちだけのもの。そんなふうに考えていた時期が私にもありました。技術者は技術で勝負。技術的に優れたものを作ればそれでよい、と思っていたからです。しかし最近、少々考えを改めつつあります。

+

たしかにマーケティングと呼ばれる活動の中には、些細ささいなことを大げさに表現したり、技術的にたいしたことのないものをさもすばらしいことのように表現して、大衆の耳目を集めるのが目的となっているものもあります。エンジニアやハッカーのような中身がわかっている人々にとって、そのような活動はあまり尊敬できるものではありません。むしろ軽蔑や揶揄やゆの対象となることが多いでしょう。

+

しかし、すべてのマーケティング活動がそのような「邪悪なもの」というわけではありません。マーケティングには人間の心理的特性に着目し、好意的な反応を引き出すというような興味深い側面もあります。今回はそのようなマーケティングの側面と、オープンソースにおけるマーケティングについて考えてみましょう。

+
+
+ +

オープンソースとマーケティング

+
+

オープンソースソフトウェア(以下OSS)は、その定義から無償で入手1 できる必要があるので、市場の拡大・活性化を目的とするマーケティングとは無縁のようにも感じられます。OSSのユーザーが増えても、その増大に応じた「売り上げ」が上がるわけでもありませんし、ユーザーが増えたからといって開発者が増えるとも限りません。

+

しかし、OSSの成功のためには、ある種のマーケティングが役に立つのではないかと最近感じ始めています。効果的なマーケティング手法を使うことによって、OSSプロジェクトの成功確率が高まるのではないかと思うようになったからです。

+
+
+

オープンソースの成功

+
+

「OSSの定義」を満たすライセンスを付けてソースコードを公開したからといって、必ずしも成功するとは限りません。一昔前にしばしば見受けられた「OSSにすればコミュニティができて、バザールモデル2 が成立し、開発サイクルがうまく回る」などという幻想は、現実が知られるにつれて目にしなくなってきました。

+

個人的な意見ですが、成功するためにまず必要なのは、何をもって成功とするかを定義することではないでしょうか。もちろん、偶然「成功」することもあるでしょうが、意図的に成功するには、どのような状態を達成できれば成功と見なすかを定めないと難しいはずです。

+

では、ここで「OSSの成功」を定義しておきましょう。人によって定義は違うでしょうが、パッと考えつく範囲内では、

+
    +
  • コミュニティの成立

  • +
  • バザールモデルによる持続的な開発

  • +
  • 好きなこと(OSS開発)をやりつつ生活の安定

  • +
+

などがあります。他にも、ビジネス的成功など、いろいろ考えられますが、とりあえず今回は上記のような成功に絞って考えましょう。

+

これらの成功をまとめると、「持続」というキーワードが登場するような気がします。コミュニティが成立しないようなプロジェクトは、オリジナルの開発者が延々と孤独に開発を続けることになりがちですし、開発者が飽きてしまえばそれでおしまいです。私自身の経験から考えても、外部からのインプットはモチベーションの維持に大変重要です。また、生活の安定も持続のために必要な要素で、学生主体のプロジェクトが主開発者の卒業や就職のために停止してしまった例3 もたくさんあります。逆に、持続性が期待できないプロジェクトには参加を躊躇ちゅうちょされるでしょうし、バザールモデルによる開発も成立しにくいでしょう。つまり、上記の定義に従えば、「OSSの成功とその持続性は、表裏一体である」ということができます。

+
+
+ +

持続することの難しさ

+
+

「継続は力なり」という言葉もありますが、逆に「それだけ継続することは難しい」ということを意味しています。

+

先日調査した結果4 によると、Freshmeat.Net5 に登録されている25841プロジェクトのうち、実に61%に当たる15779プロジェクトが1年以上更新されていませんでした。「とりあえずアイデアを思い付いて、プロジェクトを登録してみた」というPlanningレベルでは、実に8割を超えるプロジェクトが停滞しています。このデータは、持続的開発が実に困難であることを示しています。プロジェクトをOSSとして公開しても、必ずしもコミュニティが成立するわけではなく、またそれだけで持続的に開発できるわけではないのです。

+

では、持続的開発には何が必要なのでしょうか。重要なのは、開発者の生活の安定と外部からの継続的なインプットだと思います。しかし、これらは鶏と卵のようなものです。つまり、開発者の生活が安定するために仕事を頑張りすぎればOSSを開発する時間はなくなってしまいますし、逆にOSSを開発するために仕事を抑制すれば今度は生活が安定しません。一番よいのはOSS開発者としてどこかの企業に雇用され、OSS開発自体が仕事になることですが、そのためにはOSSそのものの価値が高く評価されることも必要でしょうし、OSS開発が安定している、つまりすでに持続的な開発が行われているかどうかが重視されそうです。持続的な開発のために生活の安定を求めているのに、そのためにはすでに持続的な開発が行われていることを求められるようでは、簡単な解決策はなさそうです。

+

では、いったいどうすればよいのでしょう。

+
+
+

持続への解決策

+
+

1つには開発者の生活の安定でしょう。OSS開発にかまけていても生活に不安がなければ、心配することなく開発に没頭できます。特に経済的な側面で心配のないことが重要です。しかし、親の遺産を受け継ぐような運のよい話はめったにありませんし、ビジネス的に成功してお金持ちになった人は、今度はビジネスに対する責任からOSS開発どころではない人のほうが多そうです。結局、これはあまり一般的に有効な方法ではなさそうですね。

+ +

もう1つの方法がマーケティングの利用です。商品ではないので「広報」と呼んだほうがよいのかもしれません。オープンソースは商品ではありませんから、売れなければならないというプレッシャーはありませんし、単純に考えればユーザーがどれほど増えても関係ないと思えます。しかし、ある程度以上のユーザーを獲得できれば、開発モチベーションの観点からも、コミュニティ形成の観点からも、大変有利です。また、LinuxやRubyのようにスポンサーによる支援6 を受けるためには、そのプロジェクトが技術的に優れていることは当然として、「どれだけユーザーがいて、どれだけの影響力があるか」が重要になります。

+
+
+

マーケティング手法

+
+

近年、OSS分野のマーケティングにおける成功例としては、Ruby on Rails7(以下Rails)があげられるでしょう。RailsはWebアプリケーションフレームワークとして優れている点はもちろんですが、「Javaの10倍の生産性」というわかりやすいキャッチフレーズや「わずか10分でブログソフトを実装」という人目を引くビデオによって注目されました。「わかる人だけわかればよい」というようなテキストオンリーの無愛想なドキュメントが横行するOSS業界にあって、このアプローチは特異だったように思えます。

+

具体的には、

+
    +
  • 何が達成できるのか、わかりやすい表現

  • +
  • 特にビジュアルな表現

  • +
+

が重要のようです。Rubyのような言語はこれが苦手なので、ずっと困っていました。言語はプログラムを書けば原理的には何でもできるからです。差別化も難しいですし。しかし、Railsの成功のおかげで「RubyではRailsが使えます」といえるようになりました。最近、Rubyが注目されている原因8 の1つはRailsでしょう。ありがたいことです。

+

これからはオープンソース分野においても、見栄えや広報などマーケティング的側面がますます重要視されるのかもしれません。

+
+
+
+
+
    +
  1. +

    無償で入手

    +

    正確には「無償で入手することを妨げてはならない」。有償の頒布が禁じられているわけではないし、配布手数料・複製コストなどを徴収することも認められている。 +

    +
  2. +
  3. +

    バザールモデル

    +

    1997年5月にEric S. Raymond氏が発表した論文「伽藍とバザール(The Cathedral and the Bazaar)」に登場するキーワードで、有志の開発者がゆるく組織化されたソフトウェア開発モデル。Linuxが代表格。
    +http://www.catb.org/~esr/writings/cathedral-bazaar/ +

    +
  4. +
  5. +

    主開発者の卒業や就職のために停止してしまった例

    +

    筆者にとって印象に残っているのは、カリフォルニア大学バークレー校で開発されていたSatherという言語。Eiffelの影響を受けつつも、より優れた言語であったSatherだが、主開発者の卒業による開発のバトンタッチに手間取り、とうとう消えてしまった。 +

    +
  6. +
  7. +

    調査した結果

    +

    2006年3月調べ。 +

    +
  8. +
  9. +

    Freshmeat.Net

    +

    http://freshmeat.net/ +

    +
  10. +
  11. +

    スポンサーによる支援

    +

    CMっぽくなるが、「ネットワーク応用通信研究所はRubyの開発を支援しています」。 +

    +
  12. +
  13. +

    Ruby on Rails

    +

    Webアプリケーションフレームワーク。Rubyによる動的プログラムを活用した生産性に定評がある。
    +http://www.rubyonrails.org/ +

    +
  14. +
  15. +

    Rubyが注目されている原因

    +

    もう1つの原因は「開発者のヒゲ」ではないかと邪推している。一説によると、開発者がヒゲを生やしている言語は成功するらしい。
    +http://www.rubyist.net/~matz/20041206.html#p02 +

    +
  16. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-053.xhtml b/docs/vol2/xhtml/p-053.xhtml new file mode 100644 index 0000000..9279ba2 --- /dev/null +++ b/docs/vol2/xhtml/p-053.xhtml @@ -0,0 +1,117 @@ + + + + + +第64章 キャズム + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay64 +
+

+まつもと ゆきひろのハッカーズライフ
+キャズム +

+
+

[オープンソースマガジン, 2006年8月号]

+
+

前回に引き続き、マーケティングよりの話題です。キャズム理論をベースにオープンソースが広く受け入れられるための方策について考察しています。この点については昔から変化していないのにも関わらず、現在になってもあまり取り組まれていない印象があります。やはり、根っからの技術者である(ことが多いと思われる)オープンソース開発者にとっては、マーケティングよりも「良いものを作れば売れる」という方向性のほうが馴染みやすいのでしょうか。しかし、それで「作品」が広く使われないのであれば、それは大変もったいない話です。

+
+
+

市場における5セクション

+
+

マーケティングに関する面白い話題としては、「キャズム」と呼ばれるものがあります。これは米国のマーケティングコンサルタントであるジェフリー・ムーア氏が提唱した概念です。

+

彼によれば「市場は大きく分けて5つのセクションに分類できる」そうです。

+
+

①イノベータ(新しモノ好き)

+

②アーリーアダプタ(差別化志向)

+

③アーリーマジョリティ(差別化したいが危険は避ける)

+

④レイトマジョリティ(定番志向)

+

⑤ラガード(頑固者)

+
+

新商品が出ると「人柱1」としてすぐに試してみるイノベータや、とりあえず成功の見込みがありそうならやってみる2 アーリーアダプタなどは、新しいものを受け入れるのに柔軟です。しかし、大衆はもっと保守的です。「失敗したら損」ですし、定着しなければ投資が無駄になることを恐れる気持ちと、新しい技術で差別化したい気持ちの両方を持つアーリーマジョリティは、成功例が複数登場するまで手を出しません。もっと保守的なレイトマジョリティは、周り全部がその技術に移行してしまって、重い腰を上げざるを得なくなるまで動きません。超守旧派であるラガードは、周りに取り残されても構わず古い技術に固執します。

+ +

ある商品が次のセクションに受け入れられるのは大変です。特に、イノベータとアーリーアダプタで構成される初期市場と、アーリーマジョリティやレイトマジョリティによって構成されるメジャー市場の間には、容易には越えられない「chasm(キャズム: 深いミゾ)」があるとされています。新しい技術のマーケティングに関しては、「このセクション間のミゾをどう飛び越すか?」が課題になります。

+

同じ人が、同時期に異なるセクションへ所属することもあります。たとえば、筆者はプログラミング言語について、とりあえず新しいものを調べてみるという点ではアーリーアダプタレベルでしょうが、実際に使っているのはCとRubyばかりで、むしろレイトマジョリティレベルでしょう。エディタについては、いつまでたってもEmacsに固執するという点でラガードかもしれません。オープンソース関係者だからといって、いつもアーリーアダプタとは限らないわけです。

+

キャズムはテクノロジーマーケティングの文脈で誕生した概念ですが、実際には人間が新しいものを受け入れる過程全般を表現していると思います。新しい文化、たとえば日本のアニメが世界市場で受け入れられる過程を観察すると、似たような分類が見られることでしょう。

+
+
+

キャズムの乗り越え方

+
+

では、キャズムを越えて、マジョリティ(大衆)にアピールするためにはどうしたらよいでしょう。大衆は周りの人が使っていないと使い始めないわけですし、周りの人が使っているということは、すでに大衆が受け入れているということになります。これでは「鶏と卵3」です。

+

ジェフリー・ムーア氏は、「マーケットを小さなセグメントに分割して狭いマーケットでの成功事例を蓄積し、それを背景に近傍のセグメントへの波及効果を狙い、最終的に対象をマジョリティに拡大する」という方法を提案しています。分割統治のテクニックですね。

+
+
+

オープンソースとキャズム

+
+

オープンソースソフトウェアの多くはボランティアによって開発されており、採算を気にする必要はありません。好きだから、楽しいから、という理由で開発に参加している有志には、採算やマーケットへのアピールなどあまり関係なさそうです。というか、本質的に無償であるオープンソースソフトウェアにマーケットって存在するんでしょうか。別にユーザーが少ないからといって、ただちに「不採算だから開発をやめる」ということにはならないわけですし。

+ +

となると、マーケティングから誕生した概念であるキャズムは、オープンソースソフトウェアとは関係ないという結論になるのでしょうか? そんなことはありません。前回も触れましたが、オープンソースソフトウェアにもある種のマーケティングが必要です。それは以下の理由によります。

+
    +
  • 知名度の価値

  • +
  • コミュニティの持続性

  • +
+

誰も知らないオープンソースソフトウェアは、開発者が1人、あるいは少数で細々と開発することになります。このようなソフトウェアは持続性という観点から脆弱ぜいじゃくです。「孤独な開発」は開発者のモチベーションを下げがちですし、不採算による開発の中止こそ存在しなくても、開発者が何らかの事情(卒業・就職、転職、家庭の事情、やる気がなくなった、など)で開発を続けられなくなると、そのプロジェクトは停止してしまいます。そして、停止したプロジェクトはほとんどの場合、再び顧みられることはなく、死に絶えてしまいます。

+

逆に知名度があると、そのソフトウェアの周辺にはユーザーや開発者による「コミュニティ」が発生しやすくなります。実際にはコミュニティができたからといって、すぐに開発協力者がどんどん出てくるわけではないのですが、たとえ開発に参加する人がそれほどいなくても、知名度とそれによるコミュニティは大きな助けになります。ビジネスほどの大規模なマーケティングは不要としても、オープンソースにも知名度を高める必要があり、それは一種のマーケティングと呼んでも差し支えない活動です。そして、マーケティング活動には必ずキャズムがつきまとうのです。

+

たとえば、新しくオープンソースのDBMSが登場したとして、それが実際に広く使われるようになるまでに、高い壁があることは容易に想像できます。実績がないから使われず、使われないからなかなか実績が蓄積しない、というのは典型的なキャズムの構図です。

+
+
+

オープンソースキャズムの深さ

+
+

さて、ここまでの観察をまとめると、

+
    +
  • オープンソースにもマーケティングが必要

  • +
  • オープンソースにもキャズムがある

  • +
+

ことがわかります。人心に影響を与えるマーケティングに疎い筆者には残念な結論です。しかし、オープンソースのキャズム論には、通常のマーケットと違う要素が付け加わります。

+

まず、アーリーアダプタ層の大きさです。現時点では、オープンソースという概念そのものが、アーリーアダプタか、せいぜいアーリーマジョリティ層の手を出すレベルです。ようやくレイトマジョリティ層もオープンソースに気が付いてきたくらいでしょうか。その結果、オープンソースに深く関わっている人たちは、新しモノ好きの傾向が強いだろうといえるでしょう。

+

次は、オープンソースの導入コストの低さです。オープンソースソフトウェアが無償といっても、新しいものを試すにはそれなりにコストが必要です。特に学習コストはバカになりません。しかし、商用ソフトウェアと比較すればコストは低いので、冒険しやすいことはいえるでしょう。

+
+

最後に、開発を持続させるために必要な「顧客」が少なくて済む点があります。商品を販売することでコストを回収することを目指す「商品」とは異なり、オープンソースソフトウェアは開発持続に必要なモチベーションなどを維持することだけが必要です。これは、ビジネスを維持することに比べると圧倒的に少ない顧客数で達成できそうです。

+

結果として、オープンソースキャズムは思ったよりも深くないということです。

+
+
+

オープンソースキャズムの克服

+
+

では、どうすればよいのかといえば、やはりムーア氏の提唱した戦略を実践することでしょう。セグメントを限定して、そこでのシェアを高めることです。たとえば、汎用言語であるRubyが、Ruby on RailsによってWebアプリケーションという分野で注目されたことは(筆者の意図したとおりというわけではありませんが)よい例です。また、「お試しコスト」を下げるため、既存のライバルとの互換性を高めて、移行しやすくするのも有効でしょう。

+

キャズムを意識した戦略により、より多くのオープンソースソフトウェアが「生き残る」ようになればと祈っています。

+
+
+
+
+
    +
  1. +

    人柱

    +

    剣呑な呼び名だが、必ずうまくいくと保証されているわけでもないのに新商品を試し、場合によってはドライバを書くなどして対応させてしまうような人たちのこと。われわれ(特にLinuxのようなマイナーOSのユーザー)の使う多くのデバイスは、このような人柱の数知れぬ「犠牲」のうえに使えるようになっている。合掌して感謝しよう。 +

    +
  2. +
  3. +

    成功の見込みがありそうならやってみる

    +

    その心は「成功したら差別化できてラッキー」。 +

    +
  4. +
  5. +

    鶏と卵

    +

    「鶏が先か卵が先か」というのは、古来さまざまな人を悩ましてきた問題ですが、先日、英国で哲学者や生化学者のグループが討論した結果、鶏が先という結論が出たそうだ。「生物の遺伝子は途中で変化しないことから、最初の鶏になった卵が先」ということらしい。こんなことに真剣になる哲学者たちってステキ。 +

    +
  6. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-054.xhtml b/docs/vol2/xhtml/p-054.xhtml new file mode 100644 index 0000000..61afc63 --- /dev/null +++ b/docs/vol2/xhtml/p-054.xhtml @@ -0,0 +1,124 @@ + + + + + +第65章 言語の壁 + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay65 +
+

+まつもと ゆきひろのハッカーズライフ
+言語の壁 +

+
+

[オープンソースマガジン, 2006年9月号]

+
+

言語と言っても人間の言語、特に英語学習について語っています。この20年の間、機械翻訳はものすごい進歩していて、多くの場合で使い物になるようになりました。私は学生時代に英語のマニュアルを機械翻訳で日本語にする作業のオペレーターのバイトをしたことがあるのですが、もとの原稿をOCRで読み込む作業もエラーだらけで修正に追われ、読み込んだ英文が長すぎると機械翻訳システムがエラーを出すので英文を短く分割し、出てきた日本語の品質が悪いので校正したりと、「これだったら自分で翻訳したほうがずっと早くて楽だ」と思ったものでした。それを考えると現代の機械翻訳のレベルは驚異的です。私も英作文のときにはたまに機械翻訳を使うことがあります。しかし、それでもなお、直接英語を使ってコミュニケーションする機会がなくなったりはしないので、結局勉強しないといけないんですよね。

+
+
+

外国語教育

+
+

うちの子も中学生になって、英語を勉強するようになりました。日本語とはまったく違う新しい言語を学習するのに苦労しているようです。私自身も言葉で苦労することは多く、「日本語と英語は何でこんなに違っているのだろうか」と悩むことしきりです。

+

先日、テレビでフィンランドの小学生がインタビューされているのを見ましたが、10歳にして流暢りゅうちょうな英語を話す姿はとても印象的でした。北欧は英語の上手な人が多いのですが、それにしても小学生で外国語を使いこなす姿は普通の日本人には真似できません。やはり、幼いときからどれだけ外国語に接することができるかが鍵なのかもしれませんね。

+

フィンランドへ行ったことはありませんが、その隣国であるデンマークには2度ほど訪問しました。ホテルでテレビを見ると、ほとんどのチャンネルで英語の番組を放送していました。字幕さえ付いていません。毎日見るテレビ番組が英語なら、たしかに上手になるわけです。

+
+
+ +

独自文化の維持

+
+

テレビ番組が英語ばかりというのには、もちろん理由があります。デンマークは人口がわずか500万人強の小国なので、独自の番組をなかなか維持できません。必然的にテレビ番組は輸入に頼ることになります。おかげで国民は英語が上手になりますが、逆に独自の文化を維持しづらくもなります。これはデンマークばかりではなく、小さな国がどこでも直面している問題です。

+

先日訪問したマレーシアでも、IT関係の情報は英語でしか入手できず、マレーシア人講師がマレーシア人に教えるITセミナーでも英語が用いられていました。それに比べると、日本ではほとんどあらゆる局面において日本語だけで用が足りてしまいます。日本人が英語下手な最大の原因でもありますが、見方を変えると幸せなことかもしれません。世界的に見ればどうしても英語中心のコンピュータ業界でさえ、ほとんど英語を使わずに済むのですから。

+
+
+

ハッカーにとっての英語の重要性

+
+

しかし、このような幸運な日本の中にあっても、ハッカーに限っては英語を使う十分な理由があります。Eric Raymondは『ハッカーになろう』1 の中で次のように語っています。

+
+

4. まともに英語ができないならば、身につけなさい。

+


+

……英語以外を母語とする数名から、英語はハッカー文化やインターネットでの作業用言語なんだということ、ハッカーコミュニティーで役に立つには英語を知らないとダメだということを指摘するようにいわれたのです。

+

これはまったくそのとおりです。1991年頃、母語が一緒でも英語を第一外国語として身につけている多くのハッカーたちは、技術的な議論をするときに英語を使うというのを知りました。そのとき教わったのは、英語は技術用語がほかの言語よりも豊富で、だから英語のほうがとにかくこの仕事に向いているのだということです。

+

フィンランド人であるリーヌス・トーヴァルズは自分の書くコードのコメントを英語で書きます(どうやら英語以外で書こうなんて考えたこともないようです)。彼の流暢な英語は、Linuxの開発者の世界的なコミュニティをリクルートしてくるにあたっての、とっても大事な要因でした。

+
+

日本語に限っていえば、先達の労苦によって「英語のほうが技術的な議論に向いている」ということはありません。とはいえ、ハッカー文化が英語圏の文化に大きく影響を受けていることは否定できませんし、それに逆らうのもあまりスマートな態度ではないでしょう。

+ +

さらにいえば、コンピュータ関係の多くの情報が英語で発信されている以上、それを誰かが日本語に翻訳してくれるまで口を開けてボーッと待っているのは、どう考えても「ハッカー的」な態度ではなさそうです。

+
+
+

ハッカー流英語学習法

+
+

それでは、どのようにして英語を学習すればよいのでしょうか。ハッカーらしいスマートな学習方法はないものでしょうか。

+

知人の中で最も効果的に外国語を学習した人たちは、いずれも「その国に飛びこんで1日中その言語を使わざるを得ない状況に自分を追い込む」ことを実践していました。この方法は非常に効果的で、早ければ数か月で言語をマスターできます。ただ、デンマークのようにテレビから英語漬けという環境でない限り、肉体的、精神的、そして経済的にとてもチャレンジングな方法なので、どなたにでも手放しでお勧めできるわけではありません。

+

もっと「普通の人」にお勧めの方法2 はないものでしょうか。すぐに思い付くのは、世の中によくある英会話学校です。しかし、英会話学校はネイティブスピーカーの優秀な講師をそろえているかもしれませんが、だからといって「EmacsとViのどちらが優れているか」とか、「動的言語の優位性」などについてのディスカッションに付き合ってくれるかというと、どう考えても望み薄です。ハッカーの英語という観点からは、英会話学校は費用対効果が低いかもしれません。ハッカーとしての英語を身につけたい場合には、もうちょっと違う方法を考えてみる必要がありそうです。

+

英語力は、以下の4つに分類されます。

+
    +
  • 読む(reading)

  • +
  • 書く(writing)

  • +
  • 聞く(hearing)

  • +
  • 話す(speaking)

  • +
+

理想をいえば、この4つがまんべんなく上手であるのに越したことはないのですが、とりあえずハッカー的に英語を学ぶのであれば、これらには明確な優先順位があります。つまり、

+
    +
  • reading >>> writing >> hearing > speaking

  • +
+

です。

+

現在、ほぼあらゆる情報はインターネットを経由した文字情報3 でやり取りされます。ですから、まず優先すべきは文字情報の取り扱い、それも情報収集に効果的なreadingの強化でしょう。

+

言語の学習は、いかに大量にその言語に触れるかで決まります。英語を学ぶ、特にreading能力の強化は、どれだけたくさんの英語を読むかで決まるわけです。ここでお勧めなのは、ソフトウェア関係のメーリングリストを読むことです。自分が興味を持っており、最新情報の日本語化が少し遅いようなジャンルのメーリングリストがよいでしょう。私はruby-talkなどのRuby関連メーリングリストを活用していますが、これは流量が少々多すぎる4 かもしれません。まあ、読めなかったり、意味がわからなくても、実害はないので、どんどん読み飛ばせばよいのですが。このとき、翻訳ソフトを使ってはいけません。あれは学習の害にしかなりません。文法理解の力を怠けさせるからです。その代わりに辞書を活用してください。

+ +

readingができるようになれば、次第に英語表現の幅が広がり、writingもできるようになります。writingには好きなだけ時間を使うことができるので、readingによって英語に親しむようになれば、次第に上手に表現できるようになってきます。少しずつメーリングリストに書き込みもできるようになるでしょう。

+

hearingとspeakingはもう一段難しくなります。readingとwritingで基礎体力を付けたら、海外のイベントに参加するとか、Skypeで話すなどの手段も考えられますが、これらについてはまた別の機会にまとめましょう。

+
+
+

逆手に取る

+
+

日本において英語が読めるというのは、いろいろな意味で有利になります。日本語のおかげで「情報格差」が存在するわけですが、「格差のあるところにチャンスあり」です。最新の情報を入手している、知られざる情報を教えてあげられる、日本の情報を発信できる、どれをとっても立派なチャンスです。ビジネスとして成功するか、ハッカーとして大成するか、いずれにしても英語力はあなたの助けになることでしょう。

+
+
+
+
+
    +
  1. +

    『ハッカーになろう』

    +

    原題『How To Become A Hacker』。
    +http://www.catb.org/~esr/faqs/hacker-howto.html
    +山形浩生による日本語訳は以下のURLで読める。

    +

    http://cruel.org/freeware/hacker.html +

    +
  2. +
  3. +

    「普通の人」にお勧めの方法

    +

    もっとも、「ハッカー」であって「普通の人」というのは存在しないような気もするが、気にしないことにしよう。 +

    +
  4. +
  5. +

    インターネットを経由した文字情報

    +

    最近はPodcastingのような音声情報も増えているが、情報入手という観点からは無視しても実害は少ないだろう。ちなみに、Podcastingはヒアリングのトレーニングに役立つ。 +

    +
  6. +
  7. +

    流量が少々多すぎる

    +

    原稿執筆時点でruby-talkの1日当たりのメール数は200通弱である。 +

    +
  8. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-055.xhtml b/docs/vol2/xhtml/p-055.xhtml new file mode 100644 index 0000000..173f652 --- /dev/null +++ b/docs/vol2/xhtml/p-055.xhtml @@ -0,0 +1,107 @@ + + + + + +第66章 ハッカーの生産性 + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay66 +
+

+まつもと ゆきひろのハッカーズライフ
+ハッカーの生産性 +

+
+

[オープンソースマガジン, 2006年10月号]

+
+

プログラマーの生産性を高めるライフハックについて。集中して作業できる「魔法の時間」こと「ゾーン」の入り方などについて解説しています。中国の故事に「三上」というのがあって、良い詩作ができる場所(時間)が3つあって、「馬上、厠上、枕上」というのだそうです。つまり、移動中の馬の上、トイレに座っているとき、寝る直前なのだそうです。現在の状況に合わせると「散歩(通勤)しているとき、お風呂(シャワー)に入っているとき、寝る直前」でしょうか。確かにこのタイミングでいろいろ思いつくことが多いように思います。私は犬を飼っていて、自宅にいるときには朝と夕に散歩しているのですが、これが気分転換と発想に貢献しています。

+
+
+

生産性の高さ

+
+

古典的な意味での「ハッカー」は、ときとして信じられないほどの生産性を実現します。一説によると、並のプログラマーと一流のプログラマーの間には、数十倍から100倍くらいの生産性の差があるそうです。考えてみれば、ある種のプログラマーは普通の人が逆立ちしても思い付かないようなプログラムを実装してしまいますから、その場合には「不可能を可能にした」という意味で、生産性の差は100倍どころではありませんね。ハッカーが魔法使い(ウィザード)にたとえられるのもわかる気がします。

+

私自身は「不可能を可能にする」系のハッカーではありませんが、それでも「普通の人がやらないことをやり遂げた人」ではあります。また、全体の平均はともかく、瞬間的な「最大風速値」はかなり高いのではないかと思っています。

+

このような生産性は、どのようにしたら実現できるのでしょうか? 普通の人が、ハッカーたちの生産性をある程度まねする方法はないものでしょうか。

+
+
+ +

ゾーン

+
+

プログラミングの生産性を向上させる方法はいくつか考えられますが、その最大のものは「ゾーンを大切にする」ことではないかと思います。

+

人間が集中力を最大限に発揮できる時間は限られています。人によって異なりますが、15分から1時間程度ではないでしょうか。注意散漫になることなく、最大限の集中力を発揮して高い生産性を維持できる時間のことを、一部の人たちは「ゾーン」と呼んでいます。ゾーンの最中の生産性は目を見張るものがありますが、逆にゾーンに入っていない時間の生産性はあまり褒められたものではありません。これは、程度の差こそあれ万人に共通ではないでしょうか。となると、いかにゾーンを長時間維持できるか、あるいはゾーンに突入できる回数が、その人の生産性を決めるといっても過言ではないでしょう。

+

過去の経験からいっても、ゾーンは大変壊れやすいものです。「本気で仕事を片付けるぞ」と集中しようとしたとき、電話がかかってきたり、後ろから声をかけられたりすると、それだけであっという間に去ってしまいます。そして一瞬で失ったゾーンを取り返すには、また何十分も、あるいは何時間もかかるのです。プログラマーの高い生産性を実現するには、外界の雑音をシャットアウトできる環境1、いちいち電話を取り次がなくても済むような環境が必要です。

+

集中しているときは、オフィスにいても些細ささいな音がじゃまになりますし、ノイズを遮断しようと音楽でも聞こうものなら、音楽自体が思考を疎外してしまいます。また、どうしてもメールやWebのチェックが誘惑となって、仕事に集中できないこともあります。メールやRSSリーダーで浪費した時間を合計すると、恐ろしいことになりそうです。インターネットは便利なものですが、同時に生産性を下げるわな>でもあります。深夜であれば2、そのような「誘惑」が少ないので、比較的集中力が維持しやすくなります。

+

ゾーンは大変気難しいものです。プログラマーのほんのちょっとしたイライラがゾーンを疎外します。ですから、プログラマーには優れた、そして手になじんだツールが絶対に必要です。私がEmacsに固執するのは、そのような理由からです。エディタ以外のツールはときどき新しいものに置き換えてきましたが、Emacsだけは手放せません。すでに私の一部になっており、(特に文章を書くときには)これがないと自分の思考を文章として表現できないのです。人によって違うものの、ハッカーにはそれぞれ「こだわりのツール」があるようです。考えてみれば、私が開発してきたRubyも、ゾーンの大敵となるストレスを減らすことが最初の目的だったような気がします。その他、コンパイル・リンク時間、テスト時間など、イライラの原因はいくらでもあります。予算との兼ね合いの範囲内で最良のツールを選ぶことは生産性に直結します。この点、オープンソースソフトウェアの無償性はありがたいですね。優れたツールがタダで入手できる3 わけですから。

+
+
+ +

ゾーンの入り方

+
+

広い世の中には「さあ、今から仕事をするぞ」といって、スイッチを入れるようにゾーンに入れる人もいるのかもしれませんが、私はなかなかそのようにはいきません。プログラムをどうやって実装しようかあれこれ考えたり、メールを読んだり、Webを見たり、ぼーっとしているように見える時間をだいぶ過ごさないと、ゾーンに入れないのです。端から見ると怠けているように感じられるかもしれませんが、実際は……ただ怠けているだけのときもかなりありそうです。でも、多くの時間を情報収集に使っているのは本当で、他の人の考えや苦労話はアイデアの宝庫となっています。

+

実際の私をご存じの人は、まるで動物園の熊のようにあちこち歩き回っているのを目撃したことがあるかもしれません。これは癖のようなもので、難しい問題に遭遇したときや、原稿のテーマが思い付かないとき4 には、ブツブツと小声で独り言を口にしながらあちこち歩き回ります。少々体を動かすことでリラックスする効果があり、比較的自由に考えを巡らせることで、アイデアを練ってゾーンに入る準備ができるようです。そういえば、Rubyの言語仕様の一部5 は、10年以上前、当時まだ赤ん坊だった長女を抱っこして寝かしつけながら考えたものです。

+

私自身の生活を観察してみると、メールなどでバグリポートが来て、それを直す場合などは比較的ゾーンに入りやすく、逆に大規模なバージョンアップのような改善はなかなかゾーンに入りにくい傾向があります。どうやら「何をするか明確にわかっているときは、短時間でゾーンに入れる」ようです。バグリポートの場合、「原因を同定して、それを直す」という手順が明確に定まっていて、しかも多くのケースでは期待される結果も明白です。あらかじめ詳細な設計を行う必要まではないのですが、頭の中で何らかの「見通し」があるということなのでしょう。一方、仕様改善などは、「そもそもどのような仕様が望ましいのか」という点から考える必要がありますから、楽しいことではあるのですが、取りかかる前にはどのようなことをどのような手順で行うべきか明確でないことがほとんどです。したがって、タスクを細かく分割し、1つ1つの手順の「見通し」が明確になればゾーンに入りやすくなるのかもしれません。これで本当にゾーンに入りやすくなるのか、さっそく試してみようと思います。

+
+
+

夢のお告げ

+
+

ゾーンは生産性を高める大事な時間ですが、私にとってアイデアを得る瞬間がもう1つあります。それは眠りに入る直前です。いろいろ悩んだ挙げ句、問題が解決できなくて失意の下に寝床に入るとき、眠る直前のぼんやりとした思考の中で突然稲光のように何かがひらめくことがあります。「あ、あそこにバグがある」とか。

+

たぶん、それまでに詰め込んだいろいろな知識や思考が眠りに落ちる直前に再構成されて、思わぬところが結合するのではないかと思います。脳裏にソースコードが浮かんで、ある特定の行にバグがあることが目に見えるような経験をすると、「俺ってば、すげー」と思うと同時に「普通じゃないよな」とも思います。

+ +

もっとも、そのような「思考のひらめき」は自分で制御できないので、いつも成功するとは限りません。ある晩、「あそこにバグを見つけた」とひらめいて、あとでソースコードを眺めてみたら、そのバグはずいぶん昔に修正済みだったという経験もあります。

+
+
+

まとめ

+
+

プログラミングという生産活動は、精神の状態に非常に大きく作用されるという点で、通常のモノの生産というよりは、デザインや芸術に近いものがあります。そのような精神活動を伴うからこそ、プログラミングは面白いのだと思います。ハッカーの特質の1つは、(意識的か無意識的かはわかりませんが)そのような精神活動をある程度制御できる点にあるのかもしれません。

+

皆さんも、自分の精神を制御してハッカーの生産性を身につける工夫をしてみませんか? とはいえ、人間の心は本当に複雑なので、完全な制御など誰にもできないのですが……。

+
+
+
+
+
    +
  1. +

    外界の雑音をシャットアウトできる環境

    +

    筆者は自宅で仕事をすることも多いのだが、自宅では幼い子供が「おとーさーん」とすり寄ってくるので、親バカモード全開で「かわいいなあ」と思うものの、正直全然仕事にはならない。 +

    +
  2. +
  3. +

    深夜であれば

    +

    もっとも、Webはいつでも更新されているし、海外からのメールは時差の関係で深夜のほうが多かったりする。 +

    +
  4. +
  5. +

    優れたツールがタダで入手できる

    +

    もちろんオープンソースソフトウェアの利点が単に無償であることにとどまらないのは、本誌の読者であれば当然ご存じのことだろう。 +

    +
  6. +
  7. +

    原稿のテーマが思い付かないとき

    +

    今月もだいぶ歩いてしまった。 +

    +
  8. +
  9. +

    Rubyの言語仕様の一部

    +

    ちなみに、そうやって赤ん坊をあやしながら思い付いたものとして、たとえば「変数名でスコープが決まる」という言語仕様があげられる。 +

    +
  10. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-056.xhtml b/docs/vol2/xhtml/p-056.xhtml new file mode 100644 index 0000000..0f2c3d6 --- /dev/null +++ b/docs/vol2/xhtml/p-056.xhtml @@ -0,0 +1,124 @@ + + + + + +第67章 理系・文系 + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay67 +
+

+まつもと ゆきひろのハッカーズライフ
+理系・文系 +

+
+

[オープンソースマガジン, 2006年11月号]

+
+

理系と文系の区別について。意味がないなとずっと思っているのですが、20年経っても衰退する気配はないですね。人間はわかりやすい二分論が大好きで、固執するということなんでしょうか。ただ、プログラミングの重要な一部としてユーザーの気持ちを考えること、要求されているものを明確化し言語化するなどの工程があり、それに求められる特質は世間一般に言う「文系的能力」なのは間違いないと思うのですが、あまり理解が進まないですね。やはり「プログラミングは理系のもの」とイメージはいくら時間が経っても強固なものなんですかね。

+
+
+

2種類の人間

+
+

「世の中には2種類の人間がいる」というのは、よく使われるフレーズです。私の一番のお気に入りは「世の中には2種類の人間がいる。人間を2種類に分類したがる人間と、そうでない人間だ」です。実際、人類の分類癖は相当なもので、科学の発展の歴史は分類の歴史といってもよいでしょう。私の愛するオブジェクト指向も、その根源にはクラスによる分類が関係していそうです。

+

学生の頃の典型的な分類は、理系・文系というものです。私が高校生の頃は、「工学部や理学部、医学部などに進学したい人は理系、それ以外は文系」というような分け方でした。また、理系では数学や物理、化学などの「理系科目」が重視され、文系では国語、英語、社会などの「文系科目」が重視されていたように思います。高校を卒業してからずいぶん経つのですが、今でも似たような分類をしているのではないでしょうか。

+

しかし、高校時代の私は、この分類があまり好きではありませんでした。高校生になる前からコンピュータ好きで、将来はプログラミングの分野に進もうと考えていた私は、当然のように理系に進みました。ソフトウェア関係の大学の学科はたいてい工学部とか理学部などに所属していましたし、その目的のためには理系に進むのが当然の選択のはずでした。

+
+

ただ1つ、想定外だったのは、興味の方向と裏腹に私の得意科目が(得意な順に)国語、英語、社会であり、苦手なのは(マシな順に)化学、物理、数学だったことです。理科や数学が嫌いというわけではないのですが、どうにも計算が苦手で、よい成績を取れませんでした。特に数学は、母校(いちおう進学校でした)の理系百数十人中、ダントツで最下位1 ということもあったほどです。自分の望む進路のためには一番苦手な科目を克服しなければならず、相当つらい思いをしたこともたびたびでした。

+

そんな私ですが、共通一次試験2 の数学以外の科目と、二次試験の英語に頼って何とか大学に潜り込みました。しかし、大学に入ってからも数学には苦労させられます。理系学科の学生は、(私のような一部の例外を除くと)かなり数学のデキがよいのです。周りの学生が当然のように理解していることがさっぱりわからず、単位取得に苦労3 しました。

+

進路指導上の便宜的な分類のはずの「理系・文系」ですが、その影響は卒業して社会に出てからも続きます。たとえば、理系学科を卒業した人よりも文系学科を卒業した人のほうが、生涯収入が高いそうです。これも学歴社会の影響でしょうか。また、「理系的性格の人はこういう行動をする」という性格分析も盛んです。

+
+
+

理系的人格

+
+

ところで、理系的人格って何でしょう。典型的には「工学部や理学部の出身4」「数学や理科などが得意」「ものづくりが好き」などと形容されるようです。他にも「ブランドなど形のないものを受け入れない」「何でも修理・分解したがる」「人間関係が上手でない」「理屈っぽい」「知識が深い」「のめり込みやすい」などがあげられます。

+

たしかに、この業界には、そういう条件に当てはまる人が多そうです。とはいえ、そういう人ばかりかというとそうでもないですし、「そういう人が、本当にプログラミングに向いているのか?」と問われると、実は少々疑問があるのです。

+
+
+

プログラミングと理系・文系

+
+

そもそもコンピュータは、理系的分野から誕生した技術です。初期の計算機は歯車で構成されていましたから機械工学の産物ですし、電子化された最近のコンピュータは電気工学、電子工学などで作り上げられています。また、計算機の基礎となる理論は、数学によって構成されています。ブール代数やラムダ計算5 など、名前を聞いたことがあるのではないでしょうか。

+ +

そのような「理系の塊」と言ってもよいコンピュータですが、本当に理系的人格だけが必要なのでしょうか? 考えてみると、コンピュータとその背景の原理は理系的ですが、ソフトウェアの開発は必ずしも理系的ではありません。ソフトウェア開発の手順を見てみると、多くの場合、ソフトウェアが解決すべき問題を把握し、その問題を解決するような仕組みとしてソフトウェアがデザインされます。そこで最も重要なのは、「人間が関わる組織の作業をコンピュータによっていかに自動化するか」という視点です。扱うのは人間と人間の関わりであり、あえて分類するなら文系的な活動です。さらにソフトウェア開発には、仕様書や設計書といった文書がつきものです。文書作成も本質的には文系的活動ですよね。

+

オープンソースソフトウェアでも事情はさほど変わりません。オープンソースソフトウェア開発で最も重要なのは仕様の策定やコミュニティ運営だと思いますが、仕様の策定は先ほど述べたように文系的活動ですし、コミュニティ運営に求められる人々のコミュニケーションを円滑に行うことも文系的活動以外のなにものでもないでしょう。

+

私は言語設計者として、人々が使いやすいプログラミング言語はどうあるべきかについて日々考えていますが、「人間がプログラミング言語を使っていてどう感じるかについて考えること」も、これまた文系的です。「人間工学に近い分野であり、理系ではないか」と言ってくださる方もいますが、Rubyのデザインは決して工学的アプローチにのっとっておらず、お世辞にも理系的ではありません。

+

要するに、一般の人には理系バリバリと思われているプログラマーの活動は、実はその大半を文系的活動が占めているわけです。私のような理系的頭脳に欠陥がある人間でもやっていける理由は、この辺にあるのです。

+

ただ、プログラミングが理系的人格と無縁かというと、そうでもありません。あまり頻繁ではありませんが、何らかの数学的知識が必要な領域のプログラムは存在しますし、また理系的発想が問題を解決してくれる局面もあります。つまり、プログラミングにはいろいろな才能が発揮されるさまざまな領域が含まれるということなのでしょう。

+
+
+

プログラマーへの向き・不向き

+
+

プログラマーが文系的活動を多く含むのであれば、いわゆる文系人間を捕まえてきてプログラマーに仕立てれば良いのでしょうか? そのようなことは、実際に行われています。たとえば、私が最初に就職したソフトハウス6 では、同期入社が数百人もいて、その多くがプログラミング経験のない文系学生でした。「やっぱり向いていない」と短期間で辞めていく人もそれなりにいましたが、どちらかといえばプログラマーとして適応していく人のほうが多いようでした。プログラマーとしての適性は、世間でいう文系・理系とは違う軸に分布しているようです。そういえば、大学時代の同期にも「やっと卒業した。もう二度とコンピュータは触らない」と高らかに宣言した人がいましたね。

+

このようなことを考慮すると、実際にプログラマーに向いているのは、

+ +

という人ではないかと思います。世の中にはコンピュータが動作するロジックを頭の中で描けない人が一定数いますが、そういう人はやっぱりプログラマーには向いていない気がします。

+
+
+

文系的理系プログラマーの勧め

+
+

このように、プログラマーの活動には思った以上に文系的要素が含まれています。文系・理系と区別するばかりでなく、調整能力に優れた文系的特質と、技術力に優れた理系的特質の両方を組み合わせてこそ、最高のプログラミングを達成できるのではないでしょうか。

+

理系的なプログラマーの皆さん、文系的能力を伸ばすことにも関心を持ちましょう。文系的人間の皆さん7、実はプログラマー適性の持ち主かもしれませんよ。そういうことを気付かせてくれたという点でも、私の理系的学力のなさも捨てたものではなかったかもしれません。ありがたいことです。

+
+
+
+
+
    +
  1. +

    ダントツで最下位

    +

    正直、ここまで低いと自分の脳を信頼できなくなりますね。 +

    +
  2. +
  3. +

    共通一次試験

    +

    正式名称「共通第1次学力試験」。現在の大学入試センター試験に相当するテスト。年が知れる。 +

    +
  4. +
  5. +

    単位取得に苦労

    +

    しかも、情報系って数学が必須科目なのである。 +

    +
  6. +
  7. +

    工学部や理学部の出身

    +

    「それ人格じゃないじゃん」という批判は甘んじて受けよう。 +

    +
  8. +
  9. +

    ブール代数やラムダ計算

    +

    ブール代数はジョージ・ブールが考案した0と1からなる2値論理を扱う代数系、ラムダ計算はアロンゾ・チャーチによって考案された関数の定義と実行を抽象化した計算体系。 +

    +
  10. +
  11. +

    私が最初に就職したソフトハウス

    +

    当時の経営者がそこまで考えて学生を採用していたかどうかはわかりませんが……。 +

    +
  12. +
  13. +

    文系的人間の皆さん

    +

    本当の文系的人間は『オープンソースマガジン』は読まないような気もするが。 +

    +
  14. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-057.xhtml b/docs/vol2/xhtml/p-057.xhtml new file mode 100644 index 0000000..9769cef --- /dev/null +++ b/docs/vol2/xhtml/p-057.xhtml @@ -0,0 +1,120 @@ + + + + + +第68章 美しいコード + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay68 +
+

+まつもと ゆきひろのハッカーズライフ
+美しいコード +

+
+

[オープンソースマガジン, 2006年12月号]

+
+

コードの美しさについて語った記事です。コードの美しさといえば、そのテーマにまるまる一冊を捧げた『ビューティフルコード』(オライリージャパン)があります。この本は著名なプログラマーによるエッセイ集で、実は私も寄稿しています。英語版のBeautiful Codeが出版されたのが2007年6月(日本語版は2008年4月)であることを考えると、この時点ではエッセイに取り掛かっていた可能性があります。おそらくは出版前でまだ口外できないものの、作業中のエッセイに触発されてこの記事を書いたものと思われます。とはいえ、今読み返したら、この記事の内容と、実際のエッセイの内容はあんまり重複してませんねえ。

+
+
+

ハッカーの好む美しさ

+
+

新しい日本の総理大臣のキャッチフレーズは「美しい日本」だそうです。日本にはよいところ、美しいところがたくさんあることは承知しているつもりですが、それでも政治の分野で「美しい」という主観的な基準を持ち込むことには少々抵抗があります。「美しい」とは、大変魅力的であると同時に、人によって基準の異なる大変難しい単語でもあるわけです。

+

政治家と同様に、ハッカーも美しさにこだわります。ハッカーが扱うのは主にソフトウェアですから、こだわりの対象はソフトウェアの美しさです。しかも、たとえばGUIにおけるアイコンのデザインといったアートの美しさよりも、コードの美しさやシステム構成の美しさに対するこだわりのほうが大きいように感じます。この辺は「数式の美しさ」にこだわる数学者の心情1 と似ているのかもしれません。

+
+
+ +

どのようなコードが美しいのか

+
+

では、ハッカーはどのようなコードを美しいと感じるのでしょう。私もコードについて美しいとか醜いとか日々感じているにもかかわらず、改めて「どのようなコードが美しいのか?」と問われると、即答できない自分に気が付きます。

+

とはいえ、何かあるはずなので考えてみたところ、以下のような基準を思い付きました。

+
    +
  • 明快なコード

  • +
  • 単純なコード

  • +
  • すばらしいコード

  • +
+

どうやら、私はこのようなコードを美しいと感じているようです。

+
+
+

明快なコード

+
+

以前、社会人になった直後のことですが、Cで書かれているにもかかわらず、どう見てもCOBOLにしか見えないプログラムを読む機会がありました。Cなのに、

+
    +
  • すべての変数はグローバル

  • +
  • 変数名、関数名は連番数字付き

  • +
  • ファイル先頭には「IDENTIFICATION DIVISION」というコメント

  • +
+

というプログラムには、正直、頭がクラクラ2 しました。COBOLがダメだとは言いませんが、TPOがあるだろうと思ったものです。

+

これは極端な例かもしれませんが、「プログラムとは、文法エラーにならないだけでは十分でなく、その表現に優劣がある」ということを強く感じさせる経験でした。簡単にいうと、「同じ言語で書かれていても、読みやすいプログラムと、読みにくいプログラムがある」のです。

+

もちろん、コンピュータ側はプログラムの読みやすさなんて気にしません。どんなに超絶技巧を尽くした暗号のようなプログラム3 でも、文法を満足している限り、何の文句もいわずに実行します。しかし人間の場合、そうはいきません。人間は視覚に影響されやすく、またプログラムに埋め込まれている「意図」に敏感に反応するからです。仮にプログラムに登場する名前をまったくランダムな単語で置換したとすると、プログラムの挙動を読み取れなくなる人が続出するでしょう。

+

プログラムを人間が読む行為は、一般に考えられているよりもずっと重要なことなのです。ほとんどのプログラムは「書いて実行したらそれで終わり」ではなく、バグがあれば、人間がプログラムを読んで、本当にやりたいことと実行内容の違いを見つけ出さなければいけません。他人が書いたプログラムを保守するためにも、プログラムを読まなければいけませんし、自分自身が書いたものでも半年以上経っていれば他人が書いたものと同じような状態になります。

+ +

となると、プログラムの見栄えや読みやすさも、それだけ重要ということになります。コードは適度にグループ化されているか、関数は適切に分割されているか、変数名や関数名に適切な名前を選んでいるか、あるいはインデントやフォーマットが一貫しているか、そういう見かけが「美しいコード」への第一歩です。

+

ある程度経験を積んだプログラマーは、それなりに自分のコーディングスタイルを持っています。インデントや制御構造の使い方、名前の付け方などに独自の癖が出やすいからです。以前、私ともう1人のベテランが一緒にプログラムを開発していたときなど、一目見ただけで誰がどの部分を担当していたのかわかったものでした。ある関数の担当が変わると、最初に自分のコーディングスタイルに合わせてリファクタリングしてから作業にかかったからです。途中から意地の張り合いみたいになってしまいましたが。

+
+
+

単純なコード

+
+

単純なコードには美しさを感じます。とはいえ、単純なだけのコードなら誰にでも書けます。しかし、一見とても複雑そうに思える問題の本質をとらえて、的確に分割し、単純な部品の組み合わせに切り分けられたコードを見るのは喜ばしいものです。単純なコードに美しさを感じるのは、複雑さの中に隠された秘密を解き明かし、鋭利な切り口で切り出してくる職人芸に対して感銘を受けるからかもしれません。

+

単純なコードは、理解しやすく、変更に強いものです。しかし、この単純さは適切な分析によって取り出されたからこそ得られるもので、ただ漫然と問題に当たっていただけでは得られません。最初の単純さを生み出す過程4 にこそ、美しさの秘密が眠っています。

+

たとえば、デザインパターンは「単純な美」を実装したものでしょう。ソフトウェア開発にしばしば登場するパターンに名前を付けたカタログであるデザインパターンは、典型的な問題を解決するための分析を優秀なプログラマーが一生懸命考えてくれたおかげで、生み出されたものです。これらのパターンはよく考えられていて、問題が必要以上に複雑にならないように、各オブジェクトに役割が適切に分担されていますし、将来の変化への対応も局所的に解決できるようになっています。このような理解しやすさや変更への強さなどが、ハッカーにソフトウェアを美しいと感じさせる原因なのでしょう。

+
+
+

すばらしいコード

+
+

外見が美しいコード、構成が美しいコードは、確かに美しいのですが、これだけでは真にハッカーの心を打つコードとはいえません。真に必要なのは「驚き」です。つまり、普通に考えてはできそうもないことをさらりとやってのけるコードに美しさを感じます。その本質はアルゴリズムにあります。

+

最近、感動した例としては、圧縮されたインデックス検索を行うコードです。通常、全文検索などで用いるインデックスは、文書量が大きくなるとそれに比例する形でサイズが増加します。ある程度以上大きなインデックスは扱いにくいし、ディスク容量の問題も発生するので圧縮したいのですが、インデックス操作の効率は検索速度に直結します。空間効率を取るか、実行効率を取るかは難しい問題です。

+ +

ところが、両方の効率を一度に実現するすばらしいアイデアがあったのです。通常、データ圧縮に用いるハフマンコーディングという技法は、頻出する「単語」に短いコードを、あまり登場しない「単語」に長いコードを割り振ることで圧縮を行います。このため、圧縮データは単語辞書と辞書によるコード化を行った文書の組み合わせになります。一方、全文検索に用いる転置インデックスは、単語辞書と出現位置の組み合わせですから、本質的に類似した構造を持っています。圧縮辞書検索はこの類似を利用して、圧縮されたままインデックスを直接検索できるテクニックです。これを最初に見たときには、やられたと思いました。発想による美しいコード、私にとってこれが最も美しいコードに感じられます。

+
+
+

まとめ

+
+

ハッカーがコードに感じる美しさについて、書きつづってみました。実利主義的なハッカーは、読みやすい、変更しやすいなど実用的なコードに美しさを感じます。しかし、頭の良い人の書いたコードに対する羨望せんぼう(や嫉妬)のような感情からも美しさを感じるようです。自分の書いたコードを読み返すと、美しいものもそうでないものもあります。皆さんも、美しさという観点からコードを眺めてみてはいかがでしょう。

+
+
+
+
+
    +
  1. +

    「数式の美しさ」にこだわる数学者の心情

    +

    映画『博士の愛した数式』では、オイラーの等式が美しい数式の典型として登場している。数学オンチの私にはよくわからないけれども。 +

    +
  2. +
  3. +

    正直、頭がクラクラ

    +

    そのプログラムを一緒に眺めていた先輩は「どんな言語でもCOBOLは書けるんだよ」としみじみ語っていた。 +

    +
  4. +
  5. +

    超絶技巧を尽くした暗号のようなプログラム

    +

    実例としては、毎年開催されるIOCCC(International Obfuscated C Code Contest)に参加しているコードがあげられる。びっくりすること、請け合い。
    +http://www.ioccc.org/ +

    +
  6. +
  7. +

    単純さを生み出す過程

    +

    これは優れた工芸品に対して受ける感動と同類のものだと思われる。あるいは、雪の結晶のような自然の作り出す、シンプルであると同時に神秘的な美しさにもつながる。 +

    +
  8. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-058.xhtml b/docs/vol2/xhtml/p-058.xhtml new file mode 100644 index 0000000..9c0d3b4 --- /dev/null +++ b/docs/vol2/xhtml/p-058.xhtml @@ -0,0 +1,138 @@ + + + + + +第69章 オープンソースよ、永遠に + + + + +

Matz Essays Volume 2

+ + +
+ +Matz Essay69 +
+

+まつもと ゆきひろのハッカーズライフ
+オープンソースよ、永遠に +

+
+

[オープンソースマガジン, 2007年1月号]

+
+

ハッカーズライフ連載の最終回。フリーソフトウェアとオープンソースについての解説。特にあまり触れられない背景と動機について解説しています。オープンソースのありかたもあまり変化していませんね。この20年の変化について考えると、

+
    +
  • ソフトウェア全般

    +
      +
    • オープンソースがより一般化した

    • +
    • ソフトウェアがますますWeb中心になった

    • +
    • コンピュータの性能は上がった

    • +
  • +
  • プログラミング言語

    +
      +
    • 静的型言語の流行

    • +
    • 関数型アプローチの流行

    • +
    • 新しい言語は企業がバックについているものが多い

    • +
    • JavaScript/TypeScript技術の盛り上がり

    • +
  • +
+

などがありますね。しかし、総合的に見るとこれだけ時間が経っているのに、ユーザー体験は意外と変化していないというのが正直なところです。これから20年後はいったいどうなっているのでしょうか。新しい技術により、まったく違う世界になっているのか、それともやはり「意外と変化していないね」と笑うのか。未来はどっちでしょう。長生きして明るい未来を見たいものです。

+
+
+ +

オープンソースって?

+
+

オープンソース/フリーソフトウェアとの付き合いも、ずいぶん長くなりました。初めてフリーソフトウェアに触れたのは大学時代の1989年で、EmacsとGCCがフリーソフトウェアとの最初の接近遭遇です。自分でリリースした最初のフリーソフトウェアは、Emacs Lispで書かれたメールリーダーcmailでした(1990年)。その後、Rubyをリリースしたり(1995年)、今の会社に転職してフルタイムのフリーソフトウェア開発者になったり(1997年)、私の生活に占めるオープンソースの割合はどんどん高まってきています。

+

このように「オープンソースな生活」をしている私ですが、困ったこともあります。IT業界人でない人に、「オープンソースとは何か」をなかなか理解してもらえないのです。こちらにもこだわりがあるので「無償のソフトウェア」などという簡単かつ誤解を生みそうな表現はできませんし、苦労して説明しても結局は煙に包まれたような顔をされてしまいます。

+

この「理解されにくさ」は、そもそもオープンソースという単語が誤解されやすいうえに、いろいろな側面を持つ多面的な概念であることと無関係ではないでしょう。というわけで、今回はオープンソースという概念の多面性について考えてみます。

+
+
+

定義

+
+

まずは、言葉の定義から見ていきましょう。フリーソフトウェアの定義は「(FSF1 の考える)ソフトウェアの自由を保証するソフトウェア」であり、ここでいうソフトウェアの自由とは、

+
    +
  1. 実行する自由

  2. +
  3. ソースコードを読む自由

  4. +
  5. ソースコードを改変する自由

  6. +
  7. ソースコードを再配布する自由

  8. +
+

のことです。実行の自由を保証するため、ソフトウェアは無償で入手できる必要がありますし、学習・研究用にソースコードも入手可能でなければいけません。また、不具合の修正や自分の目的に合わせるための改変を許可したり、他の人に勧めるために再配布も自由である必要があります。

+

一方、オープンソースソフトウェアとは、「オープンソースの定義」2 を満たすライセンスで配布されているソフトウェアのことです。オープンソースの定義が、作者の意向でもソフトウェア自体の性質でもなく、ライセンスによって定められることに注目してください。つまり、どのようなソフトウェアであるかよりも、そのソフトウェアが「どのように扱えるか」という点を問題としているわけです。

+
+
+ +

オープンソースの歴史

+
+

オープンソースは1998年に誕生した新しい単語ですが、それよりもはるか以前から成立していたフリーソフトウェア運動に端を発しています。これは、当時MITに所属していたRichard M. Stallmanが始めた「ソフトウェアの商用化によって、それ以前にプログラマたちが享受していたソフトウェアの自由が奪われていくのを憂えて誕生させた運動」です。1983年、Stallmanが自身で開発していたEmacsエディタなどをGPL3 の下で公開し、他の人々にもこのライセンスの採用を勧めました。それ以来、StallmanとFSFは数多くのソフトウェアをGPLの下で公開しており、優れたコンパイラであるGCCや、UNIXコマンドの多くをカバーするGNUツールなど、オープンソースにとって欠かせない存在になっています。

+

しかしこの考えは、企業にあまり理解されませんでした。一説には「フリー」という単語が「無料」を連想させるからとも、商用ソフトウェアを毛嫌いするStallmanの態度が商用ソフトウェアも扱うIT企業に敬遠されたともいわれていますが、はっきりしたことはわかりません。そのような環境の中で、フリーソフトウェアをビジネス領域に定着させるための方策として誕生した単語が「オープンソース」です。これは「ソースコードを公開している」という部分しか表現していない単語ですが、

+
    +
  • 真新しい言葉が持つよいイメージ

  • +
  • 無料というビジネス的に望ましくない側面を強調しすぎていない

  • +
  • なんとなく新しいビジネストレンドという印象を与えられる

  • +
+

などの利点から、ビジネスに近い人々から支持されました。Stallmanたちは「オープンソースという言葉には最も重要な『自由』が含まれていない」と反対しましたが、現実派は「オープンソースの定義によって自由は保証されている」として、この点をあまり重視しませんでした。

+
+
+

オープンソースの背景

+
+

「なぜオープンソースなのか?」、また「なぜオープンソースが成立できるのか?」という点が気になる人もいるでしょう。

+

フリーソフトウェアの場合は簡単です。フリーソフトウェアには「ソフトウェアの自由を維持する」という崇高な目的があります。過激な行動こそありませんが、基本的には人類の歴史で繰り返されてきた自由闘争の一種です。ところが、自由にそれほど固執しないオープンソースの場合には、ちょっと話がややこしくなります。無理やり一言で表現するなら「近年の社会の変化に適合したソフトウェア開発のありかた」とでもいえばよいでしょうか。

+

もともと知識や情報を共有するという行為は、科学の領域では普通のことでした。知識を論文として(無償で)公開し、先人の業績を利用して新たな研究を行うことは当たり前のことです。ソフトウェア、特に商用ソフトウェアは「商品」として扱われるので忘れがちですが、論文の内容と同様のやり方が適用できます。とはいえ、ソフトウェアの開発にはそれなりに費用がかかるわけですが、それを「無料のソフトウェア」として公開してしまうことに抵抗はないのでしょうか? これには2つの理由があります。

+ +

最初の理由は、コンピュータの普及とインターネットの発達です。昔はきちんとしたソフトウェアを開発をしようと思えば、高価な大型コンピュータを導入し、たくさんの技術者を集める必要がありました。それが今では家庭にあるコンピュータでも十分可能で、ネットでのやり取りだけで開発されることも珍しくありません。趣味のプログラミングのレベルでも、相当な開発ができるようになったのです。

+

もう1つの理由は、ソフトウェアの複雑化とコモディティ化4 が同時に起こったことです。ソフトウェアがカバーする領域はどんどん広くなり、ソフトウェアはどんどん複雑になっています。昔は1万行程度で実現できていたOSも、今では600万行を超える5 ような一大ソフトウェアになっています。ところが、人間の財布の中身はさほど変化しませんから、1つ1つのソフトウェアの規模がどんなに大きくなってもソフトウェア全体にかけられる費用はそんなに増やせません。結果として、よほどの大手でなければ、自分たちだけで必要なソフトウェアを開発・提供できなくなってしまいました。企業がオープンソースに注目する理由はここにあります。「すべてのソフトウェアを自分たちで開発できない以上、競争力の源になる一部を除いて、みんなで共有して開発したほうが得になる」という企業側の冷徹な計算があるのです。

+

オープンソースに関わる人々の思惑は1つではありません。経営上の損得から参加する企業、ソフトウェアの自由のために参加するプログラマー、上司の命令だから参加する会社員、それこそ千差万別です。しかし、オープンソースによってすべての人が幸せになる可能性があり、それが継続していくような発展的な循環が起きているのは、本当によいことだと思います。そして潤沢なオープンソースソフトウェアに囲まれることで、ハッカーは自らがハッカーたりえる「場」を見いだせるのではないでしょうか。

+
+
+

終わりに

+
+

これまで皆さんにご好評いただいた本連載「まつもとゆきひろのハッカーズライフ」は、残念ながら今回で最終回になります。UNIX USER時代から数えると24回にもわたってお付き合いいただき、本当にありがとうございます。連載が終わってもハッカーとしての人生はまだまだ終わりません。これからも皆さんがプログラミングライフを楽しむことができるようお祈りしています。Happy Hacking!

+
+
+
+
+
    +
  1. +

    FSF

    +

    Free Software Foundationの略。
    +http://www.fsf.org/ +

    +
  2. +
  3. +

    「オープンソースの定義」

    +

    オープンソースの定義は、もともとDebianディストリビューションにおいてフリーソフトウェアと認定する基準を定めた「DFSG(Debian Free Software Guideline)」がベースになっている。第1特集Part 4のリスト1参照。
    +http://opensource.org/docs/definition.php
    +http://www.opensource.jp/osd/osd-japanese_plain.html +

    +
  4. +
  5. +

    GPL

    +

    GNU General Public Licenseの略で、ソフトウェアの自由を保証するために定義されたライセンス。
    +http://www.gnu.org/copyleft/gpl.html +

    +
  6. +
  7. +

    コモディティ化

    +

    コモディティ(Commodity)は日用品。日用品のようにありふれていて、安く手に入り、生産にコストがかけられなくなること。 +

    +
  8. +
  9. +

    600万行を超える

    +

    最近のLinuxカーネルだけの話。Windows 2000が約2000万行、Windows Vistaもおそらく数千万行クラスだと思われる。 +

    +
  10. +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-bmatter-001.xhtml b/docs/vol2/xhtml/p-bmatter-001.xhtml new file mode 100755 index 0000000..206e348 --- /dev/null +++ b/docs/vol2/xhtml/p-bmatter-001.xhtml @@ -0,0 +1,67 @@ + + + + + +初出一覧 + + + + +

Matz Essays Volume 2

+ + +
+

初出一覧

+
    +
  • 探訪Ruby: Rubyの国へようこそ, Linux magazine, 2003年12月号, アスキー.

  • +
  • 探訪Ruby: テスト第一主義, Linux magazine, 2004年1月号, アスキー.

  • +
  • 探訪Ruby: Wiki Wiki, Linux magazine, 2004年2月号, アスキー.

  • +
  • 探訪Ruby: Blogの世界, Linux magazine, 2004年3月号, アスキー.

  • +
  • 探訪Ruby: アスペクト指向, Linux magazine, 2004年4月号, アスキー.

  • +
  • 探訪Ruby: RubyとEmacs, Linux magazine, 2004年5月号, アスキー.

  • +
  • 探訪Ruby: Instiki, Linux magazine, 2004年6月号, アスキー.

  • +
  • 探訪Ruby: テンプレート, Linux magazine, 2004年7月号, アスキー.

  • +
  • 探訪Ruby: DBM, Linux magazine, 2004年8月号, アスキー.

  • +
  • 探訪Ruby: tDiary, Linux magazine, 2004年9月号, アスキー.

  • +
  • 探訪Ruby: Webアプリケーションの基礎, Linux magazine, 2004年10月号, アスキー.

  • +
  • 探訪Ruby: Webアプリケーションの基礎(その2), Linux magazine, 2004年11月号, アスキー.

  • +
  • 探訪Ruby: Webアプリケーションフレームワーク, Linux magazine, 2004年12月号, アスキー.

  • +
  • 探訪Ruby: マークアップ・マークダウン, Linux magazine, 2005年1月号, アスキー.

  • +
  • 探訪Ruby: ダイコン, Linux magazine, 2005年2月号, アスキー.

  • +
  • 探訪Ruby: 最終回・ネタのタネ, Linux magazine, 2005年3月号, アスキー.

  • +
  • まつもと ゆきひろのハッカーズライフ: ハッカーとの遭遇, UNIX USER, 2005年4月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: キーボードへのこだわり, UNIX USER, 2005年5月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: ハッカーと仕事, UNIX USER, 2005年6月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: Emacs 対 vi, UNIX USER, 2005年7月号, SBクリエイティブ.

  • +
  • オープンソース開発って何だろう, Linuxソフトウェアアンテナ, 2005年7月号, 技術評論社.

  • +
  • まつもと ゆきひろのハッカーズライフ: ハッカー環境問題, UNIX USER, 2005年8月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: 言語の重要性, UNIX USER,2005年9月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: 言語の重要性 その2, UNIX USER, 2005年10月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: ハッカーとオープンソース, UNIX USER, 2005年11月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: 測定狂時代, オープンソースマガジン, 2005年12月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: ソースを読もう, オープンソースマガジン, 2006年1月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: Let's Talk Lisp, オープンソースマガジン, 2006年2月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: オープンソースライセンス, オープンソースマガジン, 2006年4月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: Get Thing Done, オープンソースマガジン, 2006年5月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: 若人への手紙, オープンソースマガジン, 2006年6月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: オープンソースのマーケティング, オープンソースマガジン, 2006年7月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: キャズム, オープンソースマガジン, 2006年8月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: 言語の壁, オープンソースマガジン, 2006年9月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: ハッカーの生産性, オープンソースマガジン, 2006年10月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: 理系・文系, オープンソースマガジン, 2006年11月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: 美しいコード, オープンソースマガジン, 2006年12月号, SBクリエイティブ.

  • +
  • まつもと ゆきひろのハッカーズライフ: オープンソースよ、永遠に, オープンソースマガジン, 2007年1月号, SBクリエイティブ.

  • +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-bmatter-002.xhtml b/docs/vol2/xhtml/p-bmatter-002.xhtml new file mode 100644 index 0000000..f14a655 --- /dev/null +++ b/docs/vol2/xhtml/p-bmatter-002.xhtml @@ -0,0 +1,796 @@ + + + + + +索引 + + + + +

Matz Essays Volume 2

+ + +
+

索引

+

数字・記号

+

.emacs, 62

+

<% %>, 75, 86

+

<%= %>, 75, 87

+

<%# %>, 75, 87

+

ruby-mode, 205

+

\ayアンテナ, 186

+

A

+

a.y, 29

+

action, 132

+

add-hook, 66

+

advice, 53

+

after, 54

+

Alan Chen, 29

+

amrita, 88

+

amrita/template, 89

+

Andreessen, Mark, 202

+

Apacheモジュール, 124

+

arfer, 42

+

Array, 7

+

Arrow, 138, 144

+

Artisticライセンス, 256

+

ASCII, 179

+

ascpctr, 53

+

AspectJ, 51

+

AspectR, 51

+

使い方, 53

+

AspectR::Aspect, 53

+

assert, 16

+

assert_equal, 16

+

assert_instance_of, 16

+

assert_kind_of, 16

+

assertassert_nil, 16

+

assert_raises, 16

+

assert_respond_to, 16

+

AsWiki, 28

+

AWK, 5

+

B

+

Barnett, Aaron, 138, 144

+

basecamp, 262

+

bdb, 103

+

before, 54

+

benchmark, 8

+

Bengtsson, Anders, 77

+

Berger, Dan, 155

+

Berkeley DB, 102

+

bigdecimal, 8

+

Bignum, 7

+

binding, 75

+

Black, David Alan, 34, 188

+

Bliki, 32

+

Block Injection, 174

+

Blog, 38

+

BlogKit, 112

+

Blogtari!, 41

+

Blogツール, 41

+

BlueCloth, 165

+

BNF(Backus Naur Form), 225

+

bogoMIPS, 252

+

Bookmarklet, 116

+

Borges, 138, 144

+

Brannon, Paul, 154

+

Britt, James, 34, 41, 155

+

Broken Window Theory, 46

+

Bryant, Avi, 51

+

bsh, 5

+

Btree, 102

+

Buck, Jamis, 156, 176

+

Builder, 177

+

C

+

cc-mode, 71

+

Cerise, 138, 144

+

cgi, 8, 136

+

CGI(Common Gateway Interface), 121

+

欠点, 124

+

cgi-application, 138, 144

+

cgi/session, 136

+

CGIKit, 138, 144, 145

+

cgikit, 147

+

checkbox, 132

+

CKBrowser, 149

+

CKCheckbox, 149

+

CKComponent, 149

+

CKConditional, 149

+

CKContent, 149

+

CKFileUpload, 149

+

CKForm, 149

+

CKFrame, 149

+

CKGenericElement, 149

+

CKHyperlink, 149

+

CKImage, 149

+

CKPopUpButton, 149

+

CKRadioButton, 149

+

CKRepetition, 149

+

CKResetButton, 149

+

CKString, 149

+

CKSubmitButton, 149

+

CKText, 149

+

CKTextField, 149

+

Clarke, T., 138

+

Class, 7

+

CLOS, 94, 248

+

clWiki, 28, 41

+

cmail, 5, 106, 205

+

command, 79

+

Common Lisp, 94, 248

+

Comparable, 7

+

complex, 8

+

Constructor Injection, 171

+

Cookie, 134

+

Copland, 176

+

copy, 60

+

Cox, Brad, 155

+

CPL(Common Public License), 258

+

cs.com, 25

+

csv, 8

+

ctags, 245

+

Cunningham, Ward, 25

+

curses, 8

+

D

+

date, 8

+

Date, Shashank, 34

+

Date@Glance, 260

+

DBM, 97, 98

+

制限, 99

+

dbm, 8, 99

+

debug, 8

+

Decoux, Guy, 103

+

delegate, 8

+

DI(Dependency Injection), 169

+

DI::Container, 171

+

Diaria, 42

+

digest/md5, 8

+

Dir, 7

+

div, 138, 144

+

DIコンテナライブラリ, 176

+

dl, 8

+

drb, 8

+

DRM(Digital Rights Management), 234

+

Dvorak, August, 196

+

Dvorak配列, 196

+

E

+

EBCDIC, 179

+

EBCDIK, 179

+

ed, 204

+

Emacs, 5, 61, 203, 233

+

〜による開発, 70

+

GNU, 62

+

Rubyモード, 62

+

オートインデント, 64

+

ハイライト, 63

+

Emacs Lisp, 5, 62, 204

+

enctype, 133

+

Enumerable, 7

+

ERB, 74, 88

+

erb, 8

+

eRuby, 74, 86

+

etc, 8

+

EUC(Extended UNIX Code), 180

+

EuRuKo(European Ruby Conference), 188

+

eval, 248

+

evan, 138, 144

+

ex, 204

+

Exception, 7

+

execute, 79

+

execute_command, 79

+

expand, 90

+

F

+

FastCGI, 124, 125

+

fastcgi, 126

+

fcntl, 8

+

Feldt, Robert, 51

+

FFII(Foundation for a Free Information Infrastructure), 23

+

File, 7

+

file, 132

+

fileutils, 8

+

find, 8

+

Fixnum, 7

+

Float, 7

+

flyspell-mode, 70

+

form, 132

+

Fowler, Martin, 32, 169

+

Freeze, Jim, 34, 154

+

FSF(Free Software Foundation), 233, 257, 292

+

ftools, 8

+

Fulton, Hal, 34, 154, 188

+

G

+

GC, 59

+

GC, 7

+

gdbm, 99

+

George Girton, 42

+

GET, 132

+

GLOBAL, 245

+

Gmail, 107

+

GNU Emacs, 62

+

GNU(GNU's Not Unix), 233

+

Gonzui, 245

+

Gosling Emacs, 62, 204

+

Gosling, James, 62, 204, 233

+

Gosmacs, 62, 204, 233

+

GPL(GNU General Public License), 233, 255, 293

+

gprof, 238

+

Graham, Paul, 202, 219, 247

+

Granger, Michael, 34, 138, 144

+

GRASS, 161

+

Green, Eli, 126

+

grep, 245

+

Guido van Rossum, 188

+

Guile, 84

+

Gurgle, 42

+

H

+

Hacker, 191

+

Haines, Kirk, 138, 144

+

Hansson, David Heinemeier, 73, 138, 144, 155, 229, 262

+

Happy Hacking Keyboard, 195

+

Hash, 7, 98

+

Herrington, Jack, 34

+

hidden, 132, 134

+

Hiki, 29

+

Hodel, Eric, 34, 138, 144

+

hook, 65

+

howm, 261

+

HTML(Hyper Text Markup Language), 120

+

フォーム, 132

+

HTTP(HyperText Transfer Protocol), 120

+

リクエスト, 123, 133, 134

+

レスポンス, 134

+

http.el{http.el}, 115

+

Hwang, Francis, 34

+

I

+

iAPX432, 252

+

iconv, 8

+

iGEL, 138

+

image, 132

+

input, 132

+

INSANE, 224

+

Instiki, 73

+

Integer, 7

+

Interface Injection, 171

+

Io, 95

+

IO, 7

+

Iowa, 138, 144

+

IPA, 201

+

IPAフォント, 161

+

irb, 8

+

IRC, 186

+

ISO-8859, 179

+

J

+

Janukowicz, Marek, 138, 144

+

jargon file, 192

+

JavaScript, 95, 237

+

JIS-X-0201, 179

+

JIS-X-0208, 179

+

join, 102

+

join point, 51

+

Joy, Bill, 204

+

K

+

KakiWiki, 28

+

kan, 29

+

Kernel, 7

+

Kiczale, Gregor, 249

+

Kilmer, Rich, 34, 154, 224

+

Knight, John, 155

+

kou, 161

+

kwartz, 93

+

L

+

lambda, 66

+

langsmithメーリングリスト, 118, 226

+

Latin1, 179

+

Lerdorf, Rasmus, 224

+

LGPL, 257

+

Lightweight Language, 187

+

Lightweight Language Weekend, 128

+

Lisp, 84, 205, 219, 247

+

動的性, 249

+

Lua, 237

+

luser, 218

+

M

+

Madeleine, 77

+

Madeleine::Automatic, 80

+

mark and sweep, 59

+

Markdown, 164

+

文法, 164

+

Marshal, 7, 101

+

Math, 7

+

matrix, 8

+

Matz, 34, 126, 229

+

Matzにっき, 42, 186

+

May, Patrick, 138, 154

+

McCarthy, John, 205, 248

+

method, 132

+

methods, 54

+

Miki, 29

+

Miller, Gorden James, 156

+

MiniRubyWiki, 28

+

MIPS, 252

+

misen, 92

+

MM/Memo, 261

+

MockLisp, 62, 204

+

mod_fastcgi, 125

+

mod_ruby, 124

+

Module, 7

+

Moleskin, 42

+

MoonWolf, 126, 138, 144, 160

+

Moore, Geoffrey A., 271

+

morq, 205

+

Morris, Chris, 28, 41

+

Mortar, 138, 144

+

N

+

Narf, 138

+

Needle, 176

+

net/ftp, 8

+

net/http, 8

+

net/imap, 8

+

net/pop, 8

+

net/smtp, 8

+

net/telnet, 8

+

Neumann, Michael, 29

+

nkf, 8

+

Nora, 138

+

North, Dan, 176

+

Numeric, 7

+

O

+

O'Reilly OSCON, 188, 224, 227

+

Object, 7

+

Object Prevalence, 77

+

observer, 8

+

open-uri, 8

+

optparse, 8

+

OSI(Open Source Initiative), 207

+

ozten, 42

+

P

+

pack, 99

+

PageRank, 253

+

Parrot, 84

+

parsedate, 8

+

password, 132

+

PC-1210, 264

+

PDA, 260

+

Perens, Bruce, 209

+

Perl, 5, 206

+

Pettichord, Bret, 34

+

Phlip, 28

+

POBox, 260

+

POD(Plain Old Document)形式, 29

+

pom(phase of the moon), 244

+

POST, 133

+

pp, 8

+

profile, 8

+

Prothon, 95

+

pstore, 8

+

Q

+

QDBM, 103

+

Queue, 102

+

Qwerty配列, 196

+

R

+

RAA(Ruby Application Archive), 8, 183

+

Rabbit, 158, 161

+

Radical, 138, 144

+

radio, 132

+

Rails, 138, 144

+

Range, 7

+

Raymond, Eric, 209, 247, 268, 276

+

rb.log, 42

+

rbprof, 55

+

RCR(Ruby Change Request), 46

+

RCRchive, 47

+

RD(Ruby Document), 29, 114, 158

+

文法, 159

+

rd-mode.el, 114

+

rd2, 161

+

rd2sxi, 161

+

RDocWiki, 29

+

RDtool, 160

+

readline, 8

+

Recno, 102

+

RedCloth, 76, 165

+

RedHanded, 186

+

Regexp, 7

+

Replica, page{78}

+

reset, 132

+

rexml, 8

+

Rico, 176

+

Rite, 46

+

Roach, 138, 144

+

Rosie, 144

+

RRB(Ruby Refectoring Browser), 66

+

RSS(RDF Site Summary), 41

+

Ruby, 95

+

1.8, 74

+

1.8.0, 15

+

1.9, 47

+

組み込みライブラリ, 7

+

コア, 6

+

思想と哲学, 9

+

世界, 6

+

誕生日, 168

+

特徴, 4

+

未来, 46

+

メーリングリスト, 184

+

リフレクション機能, 51

+

Ruby Conference, 34, 46, 128, 154, 188

+

Ruby hotlinks, 185

+

Ruby on Rails, 262, 270

+

ruby-mode, 64

+

ruby-mode-hook, 65

+

ruby-mode.el, 63

+

ruby-mode.elc, 63

+

Ruby.APP, 138, 144

+

Ruby2, 46

+

rubydb.el, 71

+

RubyForge, 183

+

Rubyist Magazine, 186

+

RubyJournal, 42

+

RubyUnit, 15

+

Rubyのバグ(勝手に)トラッカー, 262

+

Rubyライセンス, 256

+

Russell, Steve, 205, 248

+

Ruwiki, 29

+

rweb, 138

+

RWiki, 29, 158

+

S

+

sakura, 42

+

SASADA Koichi, 155

+

Sather, 269

+

say, 28

+

scanf, 8

+

Schmidt, Stephan, 138, 144

+

Schroeder, TRrevor, 42

+

sdbm, 99

+

select, 132

+

Self, 94

+

SeRuby, 95

+

Setter Injection, 171, 173

+

socket, 8

+

Sofer, Idan, 138, 144

+

SQL, 97

+

SQLite, 98

+

ssl.el, 115

+

Stallman, Richard, 61, 203, 233, 293

+

str, 75

+

String, 7

+

stringio, 8

+

Stroustrup, Bjarne, 34

+

Struct, 7

+

submit, 132

+

SWS, 138, 144

+

Symbol, 7

+

T

+

T-Code, 196

+

Talbott, Nathaniel, 15, 34, 155

+

TANIGUCHI, Takaki, 28

+

TaoWiki, 29

+

target, 54

+

tDiary, 42, 109, 158

+

スタイル, 110

+

ツッコミ, 110

+

ツッコミメール, 113

+

テーマ, 110

+

トラックバック, 110

+

プラグイン, 111

+

tdiary-mode.el, 114

+

TECO, 61, 203

+

tempfile, 8

+

test/unit, 8, 15

+

text, 132

+

textarea, 132

+

Textile, 75, 162

+

文法, 162

+

Thomas, Dave, 188

+

Thomas, David, 224

+

Thread, 7

+

thread, 8

+

Tiki, 29

+

Time, 5, 7

+

tk, 8

+

todo.org, 29

+

Tosh, 160

+

Tuckner, Steve, 34

+

Turing, Alan Mathison, 220

+

twelvesoft.com, 144

+

U

+

UENO Katsuhiro, 138, 144

+

Unicode, 180

+

unpack, 99

+

unwrap, 55

+

uri, 8

+

URL(Unified Resource Locator), 120

+

V

+

VA Linux Business Forum 2005, 223

+

van Rossum, Guido, 224

+

Vanderburg, Glenn, 229

+

vi, 204

+

Visor Edge, 260

+

W

+

WAF, 138, 144

+

Wakaba, 138, 144

+

Wall, Larry, 188, 193, 206, 224, 246, 256

+

Web 2.0, 254

+

Web Magazine, 186

+

webapp, 138, 144

+

Weblog, 38

+

WEBrick, 74

+

webrick, 8

+

Webアプリケーション, 119, 131

+

Webアプリケーションフレームワーク, 137, 143

+

Weirich, Jim, 34, 155, 174, 224, 229

+

wglozer, 138, 144

+

why the lucky stiff, 76, 165, 229

+

WideStudio, 201

+

Wiki, 25

+

荒らし, 27

+

安全性, 27

+

活用, 32

+

クローン, 28

+

特徴, 26

+

マークアップ, 26

+

WikiName, 26

+

Wikipedia, 32

+

Win32API, 8

+

win32ole, 8

+

WWW(World Wide Web), 120

+

X

+

X11ライセンス, 258

+

xlog, 42

+

xmlrpc, 8

+

XP, 13, 254

+

xtemplate, 92

+

Y

+

yacc(yet another compiler compiler), 225

+

YAML, 102

+

yaml, 8

+

Z

+

Zarnett, Bryan, 138, 144

+

Ziegler, Austin, 154

+

zlib, 8

+

+

アーリーアダプタ, 271

+

アーリーマジョリティ, 271

+

アクセプタンステスト, 14

+

アスペクト, 51

+

アスペクト指向, 49, 50, 249

+

アドバイス, 53

+

アンテナ, 185

+

+

いしなお, 261

+

依存関係, 169

+

イノベータ, 271

+

インタプリタ, 6

+

+

ウォーターフォール型開発, 14

+

鵜飼文敏, 231

+

美しいコード, 288

+

+

エクストリーム・プログラミング, 13, 254

+

エバリュエータ, 7

+

エレメント, 148

+

+

オートインデント, 62, 64

+

オープンソース, 292

+

キャズム, 272

+

持続性, 269

+

成功, 268

+

定義, 207, 292

+

特許, 22

+

背景, 293

+

ビジネスストーリー, 11

+

マーケティング, 268

+

歴史, 293

+

オープンソースカンファレンス2005, 187

+

オープンソース貢献者賞, 231

+

オープンソースライセンス, 257

+

オブジェクト指向, 94

+

+

カーソル, 102

+

学生ハッカー, 200

+

拡張ライブラリ, 7

+

かずひこアンテナ, 186

+

ガベージコレクション, 59

+

ガベージコレクタ, 7

+

関西オープンソース, 187

+

関心事, 50

+

関心の分離, 49

+

かんな, 70

+

+

キーボード, 195

+

起業ハッカー, 202

+

キッチンシンク, 205

+

キャズム, 271

+

きゅうり, 198

+

きゅうり改, 70, 197

+

+

組み込みライブラリ, 7

+

クラス, 5

+

クラス主義, 94

+

桑田誠, 93

+

+

継承, 94

+

結合テスト, 14

+

研究職ハッカー, 200

+

言語おたく, 6

+

言語塾, 117

+

+

構造エディタ, 84

+

構文木, 7

+

傲慢, 194

+

コード

+

美しい〜, 288

+

単純な〜, 289

+

明快な〜, 288

+

小飼弾, 202, 223

+

後藤裕蔵, 74

+

コピーレフト, 257

+

コマンドオブジェクト, 79

+

ごみ集め, 59

+

コメント, 40

+

コンピューティング環境, 215

+

+

最適化, 237

+

+

シェル, 5

+

時刻, 5

+

実装, 14

+

シフトJIS, 180

+

自分言語, 225

+

ジャーナリング, 77

+

ジャーナルログ, 77

+

社会人ハッカー, 200

+

修正BSDライセンス, 258

+

状態, 133

+

情報隠蔽, 94

+

白井薫, 92

+

+

スーパークリエーター, 201

+

スケーラビリティ, 253

+

助田雅紀, 15

+

鈴木鉄也, 138, 144, 145

+

ステートレス, 134

+

スナップショット, 77

+

スピード狂, 236

+

スロット, 94

+

+

咳, 29, 74

+

関将俊, 29, 74, 88, 138, 144

+

設計, 14

+

セッション, 133

+

+

ソーシャルタギング, 254

+

ソースコード

+

読解, 244

+

ゾーン, 280

+

測定狂, 235

+

ソフトウェアテスト, 13

+

ソフトウェア特許, 234

+

+

高橋浩和, 231

+

高橋征義, 35, 74

+

高林哲, 231, 245

+

竹内仁, 29

+

ただただし, 42, 109

+

立石孝彰, 42, 92

+

田中哲, 138, 144

+

短気, 194

+

単純なコード, 289

+

単体テスト, 14

+

+

知的所有権, 22

+

チューリング完全, 220

+

+

ツッコミ, 43

+

+

データベース, 97

+

手紙, 263

+

テスティング, 13

+

フレームワーク, 15

+

テスト第一主義, 17

+

添付ライブラリ, 7

+

テンプレート, 85

+

テンプレートエンジン, 86

+

+

動的結合, 94

+

特許, 22

+

ドメイン特化言語, 249

+

トラックバック, 40, 115

+

トランザクション, 102

+

+

中島拓, 89

+

名前重要, 167

+

汝は人狼なりや?, 140

+

+

日本Rubyの会, 34

+

+

パーサー, 7

+

ハイライト, 63

+

バインディングファイル, 148

+

バザールモデル, 268

+

ハッカー, 191

+

英語, 276

+

英語学習法, 277

+

学生〜, 200

+

研究職〜, 200

+

三大美徳, 193

+

社会人〜, 200

+

生産性, 279

+

素質, 193

+

定義, 192, 223

+

ハリウッド原則, 171

+

パレートの法則, 237

+

半角カナ, 179

+

+

ヒアリング, 13

+

ビジュアル言語, 83

+

雛形, 94

+

平林俊一, 201

+

平林幹雄, 103

+

+

無精, 194

+

フック, 65

+

フリーソフトウェア, 232, 292

+

起源, 232

+

定義, 292

+

フリープログラマー, 201

+

プログラミング言語, 219

+

プロトタイプ, 94

+

プロトタイプ主義, 94

+

プロファイラ, 55, 238

+

文系的理系プログラマー, 286

+

分析, 13

+

文法独立の言語, 84

+

+

マーク・アンド・スイープ, 59

+

マークアップ, 26

+

マークアップ言語, 157

+

マーケティング, 267

+

前田修吾, 86, 161

+

増井俊之, 196

+

+

未踏プロジェクト, 201

+

+

明快なコード, 288

+

メールリーダー, 106

+

+

文字, 179

+

文字コード, 179

+

文字集合, 179

+

文字符号化, 179

+

+

ユニットテスト, 14

+

+

ライセンス, 209, 255

+

ラガード, 271

+

+

理系・文系, 283

+

理系的人格, 284

+

リファクタリング, 66

+

リフレクション機能, 51

+

リレーショナルデータベース, 97

+

リンク, 40

+

+

るびきち, 158

+

るびま, 186

+

+

レイトマジョリティ, 271

+

+

ロギング, 103

+

ロック, 103

+

+

和田英一, 195

+

割れ窓理論, 46

+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-colophon.xhtml b/docs/vol2/xhtml/p-colophon.xhtml new file mode 100755 index 0000000..3033062 --- /dev/null +++ b/docs/vol2/xhtml/p-colophon.xhtml @@ -0,0 +1,84 @@ + + + + + +Matz Essays + + + + +

Matz Essays Volume 2

+ +
+ + +
+
+

+
+
+

Matz Essays Volume 2

+
+
+

2003〜2007

+
+
+ + +
+

まつもと ゆきひろ

+
+ + + +
+

2024年11月26日 発行

+
+ +
+

本書(電子版)は下記にもとづいて制作しました

+

『Matz Essays Volume 2』

+

2024年11月26日初版発行

+ +
+ +
+

発行者 夏野 剛

+

発行所 株式会社ドワンゴ

+

〒 104-0061 東京都中央区銀座4-12-15歌舞伎座タワー

+

03-3549-6153(編集)

+ +

https://asciidwango.jp/

+
+ +
+

+本書(電子版)に掲載されているコンテンツ(ソフトウェア/プログラム/データ/情報を含む)の著作権およびその他の権利は、すべて株式会社ドワンゴおよび正当な権利を有する第三者に帰属しています。法律の定めがある場合または権利者の明示的な承諾がある場合を除き、これらのコンテンツを複製・転載、改変・編集、翻案・翻訳、放送・出版、公衆送信(送信可能化を含む)・再配信、販売・頒布、貸与等に使用することはできません。 +

+アスキードワンゴ編集部
+編 集  鈴木嘉平、星野浩章

+
+ +
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-cover.xhtml b/docs/vol2/xhtml/p-cover.xhtml new file mode 100755 index 0000000..7c1ea46 --- /dev/null +++ b/docs/vol2/xhtml/p-cover.xhtml @@ -0,0 +1,27 @@ + + + + + +Matz Essays + + + + +
+ + + + + +
+ + + diff --git a/docs/vol2/xhtml/p-fmatter-001.xhtml b/docs/vol2/xhtml/p-fmatter-001.xhtml new file mode 100755 index 0000000..e751a60 --- /dev/null +++ b/docs/vol2/xhtml/p-fmatter-001.xhtml @@ -0,0 +1,37 @@ + + + + + +Matz Essays + + + + + + +

Matz Essays Volume 2

+ + + +
+
+ + + +
+
+ +

+
+ + + + diff --git a/docs/vol2/xhtml/p-fmatter-002.xhtml b/docs/vol2/xhtml/p-fmatter-002.xhtml new file mode 100755 index 0000000..f1992e6 --- /dev/null +++ b/docs/vol2/xhtml/p-fmatter-002.xhtml @@ -0,0 +1,29 @@ + + + + + +Matz Essay + + + + +

Matz Essays Volume 2

+ + +
+

商標

+

本文中に記載されている社名および商品名は,一般に開発メーカーの登録商標です.
+なお,本文中では TM・©・® 表示を明記しておりません.

+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-fmatter-003.xhtml b/docs/vol2/xhtml/p-fmatter-003.xhtml new file mode 100755 index 0000000..7d80b9c --- /dev/null +++ b/docs/vol2/xhtml/p-fmatter-003.xhtml @@ -0,0 +1,34 @@ + + + + + +はじめに + + + + +

Matz Essays Volume 2

+ + +
+

はじめに

+

本書は、過去(2003年から2007年まで)、いろいろな雑誌に掲載された執筆した私の記事をまとめたものです。もう20年くらい前のことになってしまいますね。若かったからなのか、この頃の執筆量は現在よりもはるかに多いですね。この時期には、執筆だけでなく、ばりばりとプログラミングもしていたはずなので、すごい生産性です。若さってすごい。

+

Vol. 2の記事は基本的にRubyのことをまとめたものです。言語としてのRubyは驚くほど変化が少なく、これらの記事に書かれていることは現在でも有効です。ただ、前半「探訪Ruby」をまとめた部分は、ちょうどRuby on Railsがまだ有名になっていない頃の記事ですから、特にWWWを扱った部分は古さが目立ち、直接役立つ知識ではなくなっていますね。たとえば第37章で紹介しているInstikiはRuby on Rails開発前のDHHが手掛けたWebアプリケーション(Wikiシステム)です。直接役に立たなくても、過去のDHHの思想の一端が垣間見えるかもしれません。

+

この本をまとめるにあたって、すべての記事を読み返してみました。もちろん、古い内容もたくさんありますが、今でも変わらないものもたくさんあります。私たちは普段、次々と現れる新技術に追い回されていて、常に新しいことを勉強しなければ取り残されてしまうような不安にさいなまれがちです。しかし、古い記事を読み返してみると、長期的な視点では変化しないものがあり、本書を読みながら、どの部分は古びて、どの部分は生き延びているか考えることで、この業界で個別の流行に過度に振り回されずに生きることについての示唆を得られるような気がしました。

+

本書を読む皆さんには、私と同じ時代を生きた人もいるかもしれません。それらの方々は懐かしく感じながら読んでいただければ幸いです。あるいは、これらの記事が書かれたは自分が生まれる前だった読者もいらっしゃるでしょう。そういう方々には、本書から時代を超えた学びがあることを心から希望します。

+

2024年9月
+島根県の温泉地にて
+まつもとゆきひろ

+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-fmatter-004.xhtml b/docs/vol2/xhtml/p-fmatter-004.xhtml new file mode 100755 index 0000000..0624526 --- /dev/null +++ b/docs/vol2/xhtml/p-fmatter-004.xhtml @@ -0,0 +1,29 @@ + + + + + +本書の刊行にあたって + + + + +

Matz Essays Volume 2

+ + +
+

著者紹介

+

まつもと ゆきひろ

+

プログラミング言語Rubyの創始者。Rubyアソシエーション理事長。ほかネットワーク応用通信研究所フェロー、OSS Vision社CTOなど肩書多数。鳥取県出身、島根県在住。2009年から島根県松江市名誉市民。妻1人、子4人、犬猫1匹ずつ。東京嫌い、温泉好き。

+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-fmatter-005.xhtml b/docs/vol2/xhtml/p-fmatter-005.xhtml new file mode 100644 index 0000000..3df1da5 --- /dev/null +++ b/docs/vol2/xhtml/p-fmatter-005.xhtml @@ -0,0 +1,32 @@ + + + + + +本書の刊行にあたって + + + + +

Matz Essays Volume 2

+ + +
+

本書の刊行にあたって

+
    +
  • 元原稿を参照しているため、表現や表記などが雑誌掲載時と異なっていることがあります。

  • +
  • URLは雑誌掲載時のままになっています。リンク切れなどがあることがあります。

  • +
  • 付録CD-ROMは本書には添付していません。

  • +
+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-fmatter-006.xhtml b/docs/vol2/xhtml/p-fmatter-006.xhtml new file mode 100644 index 0000000..47ad487 --- /dev/null +++ b/docs/vol2/xhtml/p-fmatter-006.xhtml @@ -0,0 +1,57 @@ + + + + + +Volume 2 目次 + + + + +

Matz Essays Volume 2

+ + +
+

Volume 1 / 1999〜2003

+

第1章 言語にとって美とはなにか?: Rubyにみるスクリプト言語の実装技法

+

第2章 Free Language Report: オブジェクト指向スクリプト言語Ruby

+

第3章 UNIX系OS間の移植性について

+

第4章 連載 スクリプト言語: スクリプト言語の歴史

+

第5章 特集 開発ツールをもっと便利に使おう: 最強の開発環境を求めて: Ruby最古のユーザーとしての開発環境

+

第6章 初等Ruby講座: はじめの一歩

+

第7章 初等Ruby講座: 条件判断とループ

+

第8章 初等Ruby講座: オブジェクトと変数

+

第9章 初等Ruby講座: 配列

+

第10章 初等Ruby講座: ハッシュ(または連想配列)

+

第11章 初等Ruby講座: 文字列の操作

+

第12章 初等Ruby講座: パターンマッチ

+

第13章 初等Ruby講座: 入出力

+

第14章 初等Ruby講座: 数と電卓

+

第15章 初等Ruby講座: CGI

+

第16章 初等Ruby講座: Rubyで作るCGI

+

第17章 初等Ruby講座: CGIの道具箱

+

第18章 初等Ruby講座: ファイル処理

+

第19章 初等Ruby講座: ネットワークプログラミング

+

第20章 初等Ruby講座: 番外編: Rubyカンファレンスレポート

+

第21章 初等Ruby講座: プロセスとフォーク

+

第22章 初等Ruby講座: スレッド(その1)

+

第23章 初等Ruby講座: スレッド(その2)

+

第24章 初等Ruby講座: データの保存

+

第25章 初等Ruby講座: XMLとYAML

+

第26章 初等Ruby講座: XMLとYAML(その2)

+

第27章 初等Ruby講座: エクストリーム・プログラミング

+

第28章 初等Ruby講座: 独習Ruby

+

第29章 初等Ruby講座: 再入門オブジェクト指向

+

第30章 初等Ruby講座: 最終回: ここからのRuby

+
+ +

+
+ + + diff --git a/docs/vol2/xhtml/p-toc-001.xhtml b/docs/vol2/xhtml/p-toc-001.xhtml new file mode 100755 index 0000000..c338063 --- /dev/null +++ b/docs/vol2/xhtml/p-toc-001.xhtml @@ -0,0 +1,318 @@ + + + + + +目次 + + + +

目次

+

はじめに

+

著者紹介

+

本書の刊行にあたって

+

2003〜2007

+

第31章 探訪Ruby: Rubyの国へようこそ

+

Rubyの紹介

+

昔々のお話

+

Rubyを取り巻く世界

+

思想と哲学

+

まとめ

+

Ruby開発日記 — オープンソースのビジネスストーリー

+

第32章 探訪Ruby: テスト第一主義

+

ソフトウェアテスト

+

仕様としてのテスト

+

test/unit

+

テストのある生活

+

システム構成の見直し

+

生活を改める

+

まとめ

+

オープンソースと特許

+

第33章 探訪Ruby: Wiki Wiki

+

Wikiとは何か

+

Wikiの特徴

+

Wikiの魅力

+

そんなことで大丈夫?

+

Wikiのもう1つの魅力

+

さまざまなWikiシステム

+

RWiki

+

RWikiのインストール

+

RWiki活用事例

+

Wikiの活用

+

まとめ

+

Ruby開発日記 — 日本Rubyの会

+

第34章 探訪Ruby: Blogの世界

+

Blogとは何か?

+

Blogコミュニケーション

+

リンク

+

コメント

+

トラックバック

+

RSS

+

さまざまなBlogツール

+

tDiary

+

まとめ

+

Ruby開発日記 — Rubyの未来

+

第35章 探訪Ruby: アスペクト指向

+

関心の分離

+

アスペクト指向とは

+

AspectR

+

AspectRのインストール

+

AspectRの使い方

+

AspectRの応用

+

まとめ

+

Ruby開発日記 — ごみ集め

+

第36章 探訪Ruby: RubyとEmacs

+

Emacs入門

+

環境としてのEmacs

+

Rubyモード

+

ハイライトとオートインデント

+

Rubyプログラムの編集

+

ruby-modeのカスタマイズ

+

RRB

+

RRBのインストール

+

RRBを使ってみる

+

Ruby開発日記 — ◆Emacsによる開発

+

第37章 探訪Ruby: Instiki

+

Instiki

+

WEBrick

+

ERB

+

RedCloth

+

Madeleine

+

Madeleine::Automatic

+

まとめ

+

Ruby開発日記 — ◆歴史は繰り返す

+

第38章 探訪Ruby: テンプレート

+

ファイル生成

+

eRuby

+

ERB

+

amrita

+

amritaの繰り返し

+

amritaの入手とインストール

+

amritaのライバルたち

+

まとめ

+

Ruby開発日記 — ◆プロトタイプな世界

+

第39章 探訪Ruby: DBM

+

DBM

+

RubyのDBMインターフェイス

+

DBMの制限の回避

+

Berkley DB

+

QDBM

+

QDBMのRubyインターフェイス

+

まとめ

+

Ruby開発日記 — ◆メールリーダーの新アプローチ

+

第40章 探訪Ruby: tDiary

+

tDiary

+

tDiaryのインストール

+

BlogKit

+

ツッコミメール

+

tdiary-mode.el

+

http.el

+

トラックバック

+

まとめ

+

Ruby開発日記 — ◆挑戦! 言語塾

+

第41章 探訪Ruby: Webアプリケーションの基礎

+

Webアプリケーションプログラミング

+

WWW

+

HTML

+

URL

+

HTTP

+

CGI

+

CGIプログラム

+

CGIの欠点

+

mod_ruby

+

FastCGI

+

まとめ

+

おまけ — RubyConf 2004

+

Ruby開発日記 — Lightweight Language Weekend

+

第42章 探訪Ruby: Webアプリケーションの基礎(その2)

+

FormとGETとPUT

+

ステートレスなHTTP

+

セッションの作り方

+

CGIプログラミング

+

Webアプリケーションフレームワーク

+

まとめ

+

Ruby開発日記 — 汝は人狼なりや?

+

第43章 探訪Ruby: Webアプリケーションフレームワーク

+

Webアプリケーションフレームワーク

+

フレームワークの機能

+

CGIKit

+

CGIKitのインストール

+

CGIKitによるWebアプリケーション

+

CGIKitのコンポーネント

+

CGIKitの評価

+

まとめ

+

Ruby開発日記 — (行ってないのに)Ruby Conference 2004レポート

+

第44章 探訪Ruby: マークアップ・マークダウン

+

私の嫌いなもの

+

RD

+

RDの文法

+

RDtool

+

Rabbit

+

Textile

+

Markdown

+

RedClothとBlueCloth

+

まとめ

+

Ruby開発日記 — 名前重要

+

第45章 探訪Ruby: ダイコン

+

Dependency Injection

+

ハリウッド原則

+

Constructor Injection

+

Setter Injection

+

Block Injection

+

DIコンテナライブラリ

+

まとめ

+

Ruby開発日記 — 文字コードの憂鬱ゆううつ

+

第46章 探訪Ruby: 最終回・ネタのタネ

+

連載のまとめ

+

Ruby情報の集め方

+

RAA(Ruby Application Archive)

+

RubyForge

+

メーリングリスト

+

Blog/日記

+

Web Magazine

+

IRC

+

各種イベント

+

おわりに

+

Ruby開発日記 — 型についての一考察

+

第47章 まつもと ゆきひろのハッカーズライフ: ハッカーとの遭遇

+

ハッカーとは

+

良いハッカー・悪いハッカー

+

あなたはハッカーか

+

ハッカー三大美徳

+

第48章 まつもと ゆきひろのハッカーズライフ: キーボードへのこだわり

+

キーボードとくら

+

十人十色

+

日本語入力の配列は「きゅうり改」

+

第49章 まつもと ゆきひろのハッカーズライフ: ハッカーと仕事

+

論文や卒業がネック

+

本業と副業が逆転

+

次の一手

+

Win-Winの関係

+

第50章 まつもと ゆきひろのハッカーズライフ: Emacs 対 vi

+

論争のタネ

+

TECOから進化したEmacs

+

edから進化したvi

+

ニュージャージー対マサチューセッツ

+

NASAの乱入

+

第51章 オープンソース開発って何だろう

+

「オープンソース」の定義

+

オープンソースでできること

+

オープンソース関係者の気持ち

+

まとめ

+

第52章 まつもと ゆきひろのハッカーズライフ: ハッカー環境問題

+

環境を改変していく能力

+

世の中にあふれかえるコンピュータ

+

プログラムによるカスタマイズ

+

ハッカーはオールドタイプ?

+

第53章 まつもと ゆきひろのハッカーズライフ: 言語の重要性

+

言語へのこだわり

+

思考表現の手段としての言語

+

プログラムを書く手段としての言語

+

プログラムを読む手段としての言語

+

プログラム実行系としての言語

+

第54章 まつもと ゆきひろのハッカーズライフ: 言語の重要性 その2

+

本当のハッカーの定義

+

キーワードはINSANE

+

自分言語を作るのは難しくない!?

+

一段高いプログラマーへの道

+

まつもと ゆきひろのOSCONレポート

+

第55章 まつもと ゆきひろのハッカーズライフ: ハッカーとオープンソース

+

オープンソース貢献者賞

+

フリーソフトウェア好きのハッカーたち

+

フリーソフトウェアの起源

+

フリーソフトウェアからオープンソースへ

+

第56章 まつもと ゆきひろのハッカーズライフ: 測定狂時代

+

測定狂

+

スピード狂

+

無駄な努力

+

無駄でない努力

+

プロファイラ

+

ハッカー養成塾!

+

第57章 まつもと ゆきひろのハッカーズライフ: ソースを読もう

+

ハッカー能力を向上させる方法

+

ソースコード読解の秘訣

+

ソースコードの読み方テクニック

+

良いソースコード/悪いソースコード

+

誰かのために、自分のために

+

第58章 まつもと ゆきひろのハッカーズライフ: Let’s Talk Lisp

+

Lispは学ぶ道具? 使う道具?

+

Lispの歴史

+

Lispのすごさ

+

Lispの強さ

+

Lispの不幸

+

MatzLisp

+

第59章 まつもと ゆきひろのハッカーズライフ: スケーラビリティ

+

20年間大きな変化のないプログラミング言語

+

ハードウェア領域の目覚しい進歩

+

スケールによる劇的な変化

+

キーワードはスケーラビリティ

+

第60章 まつもと ゆきひろのハッカーズライフ: オープンソースライセンス

+

プロジェクトの障害

+

ライセンスの選び方

+

オープンソースライセンスの選び方

+

避けたいライセンストラブル

+

第61章 まつもと ゆきひろのハッカーズライフ: Get Thing Done

+

老化現象

+

スケジュール

+

ToDo管理

+

メモ

+

協調作業

+

いつでも最適化を

+

第62章 まつもと ゆきひろのハッカーズライフ: 若人への手紙

+

後輩からの手紙

+

昔の記憶

+

先輩からの返事

+

第63章 まつもと ゆきひろのハッカーズライフ: オープンソースのマーケティング

+

マーケティング表裏

+

オープンソースとマーケティング

+

オープンソースの成功

+

持続することの難しさ

+

持続への解決策

+

マーケティング手法

+

第64章 まつもと ゆきひろのハッカーズライフ: キャズム

+

市場における5セクション

+

キャズムの乗り越え方

+

オープンソースとキャズム

+

オープンソースキャズムの深さ

+

オープンソースキャズムの克服

+

第65章 まつもと ゆきひろのハッカーズライフ: 言語の壁

+

外国語教育

+

独自文化の維持

+

ハッカーにとっての英語の重要性

+

ハッカー流英語学習法

+

逆手に取る

+

第66章 まつもと ゆきひろのハッカーズライフ: スケーラビリティ

+

生産性の高さ

+

ゾーン

+

ゾーンの入り方

+

夢のお告げ

+

まとめ

+

第67章 まつもと ゆきひろのハッカーズライフ: 理系・文系

+

2種類の人間

+

理系的人格

+

プログラミングと理系・文系

+

プログラマーへの向き・不向き

+

文系的理系プログラマーの勧め

+

第68章 まつもと ゆきひろのハッカーズライフ: 美しいコード

+

ハッカーの好む美しさ

+

どのようなコードが美しいのか

+

明快なコード

+

単純なコード

+

すばらしいコード

+

まとめ

+

第69章 まつもと ゆきひろのハッカーズライフ: オープンソースよ、永遠に

+

オープンソースって?

+

定義

+

オープンソースの歴史

+

オープンソースの背景

+

終わりに

+

初出一覧

+

索引

+

奥付

+ +