junkerstock
 quiz-perl上級70本ノック 

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Perl上級 70本ノック</title>
<style>
:root {
/* 深遠なる知識の海:ディープパープルとゴールド */
--primary-color: #311b92;
--secondary-color: #4527a0;
--accent-color: #ffd700;
--correct-color: #00695c;
--incorrect-color: #b71c1c;
--light-bg: #f3e5f5;
--white-bg: #ffffff;
--text-color: #212121;
--highlight-bg: #e1bee7;
}
body {
font-family: 'Hiragino Kaku Gothic ProN', 'Hiragino Sans', Meiryo, sans-serif;
background-color: var(--light-bg);
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
padding: 20px 0;
}
#container {
width: 95%;
max-width: 900px;
}
.panel {
background-color: var(--white-bg);
border-radius: 8px;
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
padding: 25px;
text-align: center;
margin-bottom: 20px;
border-top: 5px solid var(--primary-color);
}
.hidden { display: none !important; }

h1 {
color: var(--primary-color);
margin-bottom: 0.5rem;
font-size: 1.6rem;
text-shadow: 1px 1px 2px rgba(0,0,0,0.1);
}

#question-number {
font-size: 14px;
font-weight: bold;
color: var(--secondary-color);
margin-bottom: 10px;
text-transform: uppercase;
letter-spacing: 1px;
}

/* プログレスバー */
#progress-bar {
width: 100%;
height: 8px;
background-color: #d1c4e9;
border-radius: 4px;
margin-bottom: 20px;
overflow: hidden;
}
#progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--secondary-color), var(--primary-color));
width: 0%;
transition: width 0.3s ease;
}

#question {
font-size: 19px;
font-weight: 600;
min-height: 60px;
margin: 10px 0 25px;
line-height: 1.6;
text-align: left;
border-bottom: 1px solid #eee;
padding-bottom: 15px;
}

code {
background-color: #f5f5f5;
padding: 2px 6px;
border-radius: 4px;
font-family: Consolas, Monaco, monospace;
color: #d81b60;
font-size: 0.9em;
border: 1px solid #e0e0e0;
}

#options {
display: grid;
grid-template-columns: 1fr;
gap: 12px;
margin: 15px 0;
}
@media (min-width: 600px) {
#options { grid-template-columns: 1fr 1fr; }
}

.option {
background: var(--white-bg);
color: var(--primary-color);
border: 2px solid var(--secondary-color);
border-radius: 6px;
padding: 15px;
font-size: 15px;
cursor: pointer;
transition: all 0.2s;
text-align: left;
position: relative;
}
.option:hover:not(:disabled) {
background-color: var(--primary-color);
color: white;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.option:disabled { cursor: default; opacity: 0.7; }

/* 正誤判定スタイル */
.option.correct-choice {
background-color: var(--correct-color) !important;
color: white !important;
border-color: var(--correct-color) !important;
}
.option.incorrect-choice {
background-color: var(--incorrect-color) !important;
color: white !important;
border-color: var(--incorrect-color) !important;
}
.option.correct-answer {
background-color: white !important;
color: var(--correct-color) !important;
border-color: var(--correct-color) !important;
border-width: 3px;
font-weight: bold;
}
.option.correct-answer::after {
content: "★";
position: absolute;
right: 10px;
color: var(--accent-color);
}

#feedback-area {
background-color: var(--highlight-bg);
border-radius: 6px;
padding: 20px;
margin-top: 20px;
text-align: left;
border-left: 6px solid var(--primary-color);
animation: fadeIn 0.4s;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
}
@keyframes fadeIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } }

#feedback-title { font-weight: bold; font-size: 1.2em; margin-bottom: 8px; }
.fb-correct { color: var(--correct-color); }
.fb-incorrect { color: var(--incorrect-color); }

#next-button, .action-button {
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 6px;
padding: 12px 30px;
font-size: 16px;
cursor: pointer;
margin-top: 15px;
display: inline-block;
transition: background 0.3s;
}
#next-button:hover, .action-button:hover { background-color: #512da8; }
#next-button { display: block; margin: 15px auto 0; width: 220px; }

#timer-area { margin-top: 20px; font-weight: bold; color: #666; font-size: 0.9em; }

/* 結果画面 */
#score-display { font-size: 3.5rem; font-weight: bold; color: var(--primary-color); margin: 20px 0; }
#result-message { font-size: 1.2rem; margin-bottom: 20px; }
#explanation-list {
text-align: left;
max-height: 60vh;
overflow-y: auto;
margin-top: 25px;
padding: 15px;
border: 1px solid #ddd;
background: #fff;
border-radius: 4px;
}
.review-item { border-bottom: 1px solid #eee; padding: 12px 0; font-size: 0.95em; }
.review-q { font-weight: bold; margin-bottom: 5px; }
.review-status.ok { color: var(--correct-color); font-weight: bold; }
.review-status.ng { color: var(--incorrect-color); font-weight: bold; }
.review-ans { color: #555; font-size: 0.9em; }
</style>
</head>
<body>

<div id="container">
<div id="quiz-area" class="panel">
<h1 id="main-title"></h1>
<div id="progress-bar"><div id="progress-fill"></div></div>
<div id="question-number"></div>
<div id="question"></div>
<div id="options"></div>

<div id="feedback-area" class="hidden">
<div id="feedback-title"></div>
<div id="feedback-text"></div>
<button id="next-button">次へ ❯</button>
</div>

<div id="timer-area">TIME: <span id="timer"></span></div>
</div>

<div id="result-area" class="panel hidden">
<h2>判定結果</h2>
<div id="score-display"></div>
<div id="result-message"></div>
<button id="restart-button" class="action-button">再挑戦する</button>
<div id="explanation-list"></div>
</div>
</div>

<script>
'use strict';

// 設定
const CONFIG = {
title: "Perl上級 70本ノック",
timerSeconds: 45, // 上級なので思考時間を少し長めに
};

// 状態管理
let state = {
questions: [],
currentIndex: 0,
score: 0,
timer: null,
timeLeft: 0,
userAnswers: []
};

// DOM要素
const els = {
quizArea: document.getElementById('quiz-area'),
resultArea: document.getElementById('result-area'),
mainTitle: document.getElementById('main-title'),
progressFill: document.getElementById('progress-fill'),
qNum: document.getElementById('question-number'),
qText: document.getElementById('question'),
options: document.getElementById('options'),
feedbackArea: document.getElementById('feedback-area'),
fbTitle: document.getElementById('feedback-title'),
fbText: document.getElementById('feedback-text'),
nextBtn: document.getElementById('next-button'),
timerArea: document.getElementById('timer-area'),
timer: document.getElementById('timer'),
scoreDisplay: document.getElementById('score-display'),
resultMsg: document.getElementById('result-message'),
reviewList: document.getElementById('explanation-list'),
restartBtn: document.getElementById('restart-button')
};

// クイズデータ (全70問)
// 注意: エスケープ文字(\)はJS文字列内では \\ と表記する必要があります
const ALL_QUESTIONS = [
// --- 1. Symbol Tables & Typeglobs (1-10) ---
{ q: "現在のパッケージのシンボルテーブルにアクセスするためのハッシュは?(パッケージ名がPkgの場合)", o: ["%Pkg::", "%Pkg::SYMBOLS", "%Pkg::EXPORT", "%{Pkg}"], a: 0, exp: "シンボルテーブルは <code>%PackageName::</code> という名前のハッシュに格納されています。" },
{ q: "<code>local *main::foo = sub { ... };</code> は何をしている?", o: ["グローバル関数 foo を一時的に上書き(モンキーパッチ)", "レキシカル関数を定義", "エイリアスを作成", "コンパイルエラー"], a: 0, exp: "型グロブへの代入を <code>local</code> 化することで、スコープ内でのみグローバル関数を書き換えるテクニックです。" },
{ q: "型グロブ <code>*foo</code> が保持していないデータ型は?", o: ["レキシカル変数 (my)", "スカラ ($foo)", "配列 (@foo)", "ファイルハンドル"], a: 0, exp: "型グロブはシンボルテーブル(パッケージ変数)のエントリであり、レキシカル変数(my)はパット(Pad)で管理されるため含まれません。" },
{ q: "<code>*foo{ARRAY}</code> の戻り値は?", o: ["@foo へのリファレンス", "@foo の要素数", "@foo のコピー", "未定義"], a: 0, exp: "その型グロブに関連付けられた配列へのリファレンスを返します。" },
{ q: "<code>strict 'refs'</code> が禁止するのは?", o: ["シンボリックリファレンス", "ハードリファレンス", "循環参照", "無名サブルーチン"], a: 0, exp: "文字列を変数名として扱う行為(例: <code>${'var'}</code>)を禁止します。" },
{ q: "エイリアスを作成するために <code>*dst = *src;</code> を実行した際の影響は?", o: ["dst は src の全てのデータ型(スカラ, 配列等)を共有する", "スカラ値だけがコピーされる", "src が削除される", "エラーになる"], a: 0, exp: "型グロブ全体の代入は、名前に対する全てのスロットのエイリアスを作成します。" },
{ q: "定数を定義する <code>use constant NAME => 'val';</code> の内部実装に近いのは?", o: ["プロトタイプ () を持つインライン化可能なサブルーチン", "読み取り専用スカラ変数", "C言語のマクロ", "tie された変数"], a: 0, exp: "空のプロトタイプ <code>sub NAME () { 'val' }</code> として実装され、コンパイル時にインライン展開されます。" },
{ q: "<code>__PACKAGE__</code> トークンは何を返す?", o: ["現在のパッケージ名の文字列", "パッケージオブジェクト", "ファイル名", "行番号"], a: 0, exp: "コンパイルされている時点でのカレントパッケージ名を文字列で返します。" },
{ q: "<code>CHECK</code> ブロックが実行される正確なタイミングは?", o: ["コンパイルフェーズ終了直後、実行フェーズ開始前(LIFO順)", "モジュールロード時", "プログラム終了時", "最初の関数呼び出し時"], a: 0, exp: "コンパイル終了後に後定義順(LIFO)で実行されます。O'Reilly本などで「コンパイル直後」と記述されます。" },
{ q: "<code>require</code> がファイルを検索する際に使用しない配列は?", o: ["%INC", "@INC", "特になし(両方使う)", "%ENV"], a: 0, exp: "<code>@INC</code> は検索パス。<code>%INC</code> は読み込み済みファイル(パス)の記録用です。検索には <code>@INC</code> を使います。" },

// --- 2. Advanced OOP & Internals (11-20) ---
{ q: "<code>bless $ref;</code> (第2引数なし) を実行すると、どのパッケージにblessされる?", o: ["現在のパッケージ (current package)", "main", "UNIVERSAL", "エラーになる"], a: 0, exp: "第2引数を省略すると、その時点の <code>package</code>(カレントパッケージ)にblessされます。" },
{ q: "メソッド解決順序(MRO)における「C3」とは?", o: ["多重継承時のメソッド探索アルゴリズムの一種", "3つのクラスを継承する制限", "クラス定義の構文", "C言語レベルの最適化"], a: 0, exp: "ダイヤモンド継承問題などを解決するためのアルゴリズム。<code>use mro 'c3';</code> で有効化できます。" },
{ q: "<code>can</code> メソッドが返すものは?", o: ["サブルーチンリファレンス(またはundef)", "真偽値 (1/0)", "メソッド名", "クラス名"], a: 0, exp: "メソッドが存在すればそのコードリファレンスを返し、存在しなければundefを返します。" },
{ q: "オブジェクトのデストラクタ <code>DESTROY</code> が呼ばれるタイミングは?", o: ["参照カウントが0になった時、またはグローバル破壊フェーズ", "明示的に delete した時", "スクリプト終了時のみ", "メモリ不足時"], a: 0, exp: "Perlは参照カウント方式のGCを採用しており、カウントが0になると即座に呼ばれます。" },
{ q: "<code>Scalar::Util::refaddr($obj)</code> の用途は?", o: ["オブジェクトのメモリアドレス(ID)を取得", "リファレンスの型を取得", "再帰的な参照を確認", "blessを解除"], a: 0, exp: "オブジェクトがオーバーロードされていても、純粋なメモリアドレス(数値)を取得して同一性判定に使います。" },
{ q: "<code>Class::MOP</code> (Meta Object Protocol) が提供する機能は?", o: ["クラス、メソッド、属性などを動的に操作するAPI", "データベース接続", "Webフレームワーク", "非同期処理"], a: 0, exp: "Mooseの基盤となっている、Perlのクラス構造自体を操作するためのプロトコルです。" },
{ q: "<code>inside-out</code> オブジェクトとは?", o: ["オブジェクト自体は空のリファレンスで、データは外部のレキシカルハッシュで管理する手法", "クラスの外からアクセスできるオブジェクト", "継承を使わないオブジェクト", "C言語で書かれたオブジェクト"], a: 0, exp: "カプセル化を厳密にするための古典的な手法(<code>Class::Std</code>など)です。" },
{ q: "<code>DOES</code> メソッド(Perl 5.10+)と <code>isa</code> の違いは?", o: ["DOESはロール(Role)の消費もチェックする", "DOESは継承を見ない", "isaは非推奨", "違いはない"], a: 0, exp: "<code>DOES</code> は「その役割(インターフェース/Role)を果たせるか」を確認するために導入されました。" },
{ q: "<code>Tie::Scalar</code> などを使って変数の挙動を変える仕組みは?", o: ["tie", "bind", "connect", "link"], a: 0, exp: "<code>tie $var, 'Class';</code> で、変数へのアクセスをクラスメソッド(FETCH/STORE)にフックさせます。" },
{ q: "Perl 5.38以降で実験的に導入された、コアのクラス構文キーワードは?", o: ["class / field / method", "obj / prop / sub", "struct / member / fn", "package / has / sub"], a: 0, exp: "Corinnaプロジェクト由来の <code>class</code> 構文です。" },

// --- 3. Subroutines & Context (21-30) ---
{ q: "プロトタイプ <code>sub foo (&@)</code> の意味は?", o: ["第1引数にブロック(無名関数)、第2引数以降にリストを取る", "リファレンスと配列を取る", "コードリファレンスと配列リファレンスを取る", "ビット演算"], a: 0, exp: "<code>grep</code> や <code>map</code> のような構文(<code>foo { ... } @list</code>)を定義できます。" },
{ q: "<code>lvalue</code> サブルーチンとは?", o: ["代入の左辺になれるサブルーチン", "長い値を返すサブルーチン", "リストを返すサブルーチン", "遅延評価サブルーチン"], a: 0, exp: "<code>sub foo : lvalue { ... }</code> と定義し、<code>foo() = 1;</code> のように代入可能です。" },
{ q: "<code>goto &sub</code> の特徴は?", o: ["現在のスタックフレームを置き換えてジャンプする(呼び出し元に戻らない)", "通常のサブルーチン呼び出し", "ラベルへのジャンプ", "再帰呼び出し"], a: 0, exp: "<code>AUTOLOAD</code> などで、自分自身をスタックから消して別の関数に委譲する場合に使われます。" },
{ q: "<code>caller(0)</code> が返さない情報は?", o: ["メモリ使用量", "パッケージ名", "ファイル名", "行番号"], a: 0, exp: "呼び出し元のコンテキスト情報は返しますが、メモリ使用量は返しません。" },

// ★修正済み箇所: exp内のダブルクォートをシングルクォートに変更
{ q: "<code>wantarray</code> が返す3つの状態は?", o: ["真(list), 偽(scalar), undef(void)", "1, 0, -1", "配列, スカラ, ハッシュ", "真, 偽, エラー"], a: 0, exp: "リストコンテキスト(1)、スカラコンテキスト('')、無効コンテキスト(undef)です。" },

{ q: "<code>state</code> 変数の制約として正しいのは?", o: ["配列やハッシュの初期化はリストコンテキストではできない(Perlのバージョンによる)", "サブルーチン外でしか使えない", "リファレンスを入れられない", "数値しか扱えない"], a: 0, exp: "古いPerlでは <code>state @a = (1, 2)</code> のようなリスト初期化ができませんでした(5.28で改善)。" },
{ q: "<code>Memoize</code> モジュールの機能は?", o: ["関数の戻り値をキャッシュして高速化する", "メモリリークを防ぐ", "関数の実行履歴を保存", "デバッグ出力を抑制"], a: 0, exp: "純粋関数(同じ引数なら同じ結果)の計算結果をキャッシュします。" },
{ q: "クロージャで変数が共有されることによる有名なバグパターンは?", o: ["ループ内の無名関数でループ変数を参照してしまう", "グローバル変数の競合", "メモリ不足", "スタックオーバーフロー"], a: 0, exp: "ループ変数は各イテレーションでエイリアスされるため、<code>my</code> でコピーしないと全て最後の値を参照するなどの問題が起きがちです。" },
{ q: "<code>sub () { 42 }</code> のように空プロトタイプを持つ関数は?", o: ["定数畳み込み(インライン化)の対象になる", "引数を受け取れないだけで通常通り実行", "エラーになる", "メソッドとして定義される"], a: 0, exp: "コンパイル時に値が確定する定数として扱われます。" },
{ q: "<code>Devel::Peek::Dump($var)</code> の用途は?", o: ["変数の内部構造(PVMGなど)やフラグ、参照カウントを表示", "変数の値をダンプ", "メモリ解放", "型変換"], a: 0, exp: "XSレベルデバッグや、UTF8フラグの状態確認などに使われる強力なツールです。" },

// --- 4. Regex & Text Manipulation (31-40) ---
{ q: "正規表現中の <code>\\K</code> の意味は?", o: ["これ以前のマッチ部分を置換対象から除外(Keep)", "キーワードマッチ", "後読みアサーション", "強制終了"], a: 0, exp: "<code>s/pattern\\K/replacement/</code> のように使い、可変長の後読みのような動作を高速に実現します。" },
{ q: "正規表現中の <code>\\G</code> アンカーの意味は?", o: ["前回のマッチ終了位置(pos)にマッチ", "行頭", "行末", "単語境界"], a: 0, exp: "<code>/g</code> 修飾子と併用し、連続してパースを行う際などに使います。" },
{ q: "<code>(?>...)</code> または <code>(?>...)</code> (Atomic Grouping) の効果は?", o: ["一度マッチしたらバックトラックしない", "再帰マッチ", "条件付きマッチ", "コメント"], a: 0, exp: "バックトラックを抑制し、パフォーマンスを向上させるために使います。" },
{ q: "正規表現内でのコード実行 <code>(?{ code })</code> を許可するプラグマは?", o: ["use re 'eval';", "use strict;", "use regex;", "use code;"], a: 0, exp: "セキュリティリスクがあるため、明示的な許可が必要です。" },
{ q: "再帰的な正規表現(括弧の対応など)を書くための構文は?", o: ["(?R) または (?0)", "(?RECURSE)", "\\R", "(?SELF)"], a: 0, exp: "<code>(?R)</code> は正規表現全体を再帰的に適用します。" },
{ q: "<code>Unicode Property</code> にマッチさせるメタ文字は?", o: ["\\p{Prop}", "\\u{Prop}", "\\U{Prop}", "\\P{Prop}"], a: 0, exp: "<code>\\p{Han}</code> (漢字) のように使います。否定は <code>\\P{...}</code> です。" },
{ q: "<code>split</code> の第3引数(LIMIT)に負の数を指定すると?", o: ["末尾の空フィールドを削除しない", "最大分割数が無制限になる", "エラー", "逆順に分割"], a: 0, exp: "通常 <code>split</code> は末尾の空要素を捨てますが、負数を指定すると全て残します。" },
{ q: "<code>pos($scalar)</code> 関数の役割は?", o: ["m//g マッチの現在の位置を取得・設定", "文字列の長さ", "部分文字列の位置", "カーソル位置"], a: 0, exp: "<code>\\G</code> アンカーが参照する位置情報を操作します。" },
{ q: "<code>qr//</code> でコンパイルされた正規表現オブジェクトが文字化けして見えるのはなぜ?", o: ["内部表現としてシリアライズされているから仕様", "バグ", "エンコーディングミス", "暗号化されている"], a: 0, exp: "<code>(?-xism:...)</code> のような内部修飾子付きの文字列表現になりますが正常です。" },
{ q: "<code>study($scalar)</code> 関数の現状は?", o: ["現代のPerlではほぼ効果がない(No-opに近い)", "必須の最適化", "バグの原因", "削除された"], a: 0, exp: "かつては検索を最適化しましたが、現在の正規表現エンジンでは効果が薄いか、逆に遅くなることもあります。" },

// --- 5. Signals, Process & System (41-50) ---
{ q: "シグナルハンドラ内で安全に実行できない操作は?", o: ["malloc/freeを伴う操作(printやdieなど)", "変数の代入", "goto", "exit"], a: 0, exp: "シグナルはいつ割り込むか不明なため、「遅延シグナル」を使わない古いPerlではメモリ操作がクラッシュ要因でした(現在はDeferされるが注意は必要)。" },
{ q: "<code>%SIG</code> ハッシュで <code>'__WARN__'</code> フックを設定する目的は?", o: ["warn の出力を捕捉・加工する", "警告を無効化する", "エラーを致命的にする", "シグナルを無視する"], a: 0, exp: "<code>$SIG{__WARN__}</code> はシグナルではなく、<code>warn</code> 関数へのフックです。" },
{ q: "<code>eval</code> ブロック内でも捕捉できないシグナルは?", o: ["KILL (9)", "INT (2)", "TERM (15)", "HUP (1)"], a: 0, exp: "SIGKILL はプロセスを強制終了させるため、アプリケーション側では捕捉できません。" },
{ q: "子プロセスの終了を待たずに親プロセスが先に終了した場合、子プロセスはどうなる?", o: ["孤児プロセス (Orphan) となり init/systemd に回収される", "ゾンビプロセスになる", "強制終了される", "停止する"], a: 0, exp: "親が死ぬと孤児になります。親が生きていて <code>wait</code> しないとゾンビになります。" },
{ q: "<code>system(1, @args)</code> のように第1引数に1を渡す(Windows等)とどうなる?", o: ["非同期に実行(プロセス待ちをしない)", "エラーチェックを厳密にする", "シェルを経由せずに実行", "1秒待つ"], a: 0, exp: "Win32環境などで <code>P_NOWAIT</code> 相当の動作(非同期実行)をさせるハックです。" },
{ q: "<code>exec</code> 関数の特徴は?", o: ["現在のプロセスを新しいコマンドで置き換える(戻ってこない)", "別プロセスで実行して待つ", "出力を取得する", "シェルを起動する"], a: 0, exp: "成功すると、現在のプロセスイメージが書き換わるため、以後のPerlコードは実行されません。" },
{ q: "<code>autodie</code> プラグマの役割は?", o: ["open/close 等が失敗した時に自動的に die する", "プログラム終了時に自動的に die する", "die を無効化する", "自動的にデバッグする"], a: 0, exp: "<code>open ... or die</code> を書く手間を省けます。" },
{ q: "<code>$^O</code> (または $OSNAME) 変数の中身は?", o: ["オペレーティングシステム名 (linux, MSWin32など)", "Perlのバージョン", "実行ユーザー名", "メモリ使用量"], a: 0, exp: "OS判定に使用します。" },
{ q: "<code>fork</code> が失敗する可能性(undefを返す)を考慮すべき理由は?", o: ["メモリ不足やプロセス数制限(ulimit)", "OSが対応していない", "バグ", "常に成功する"], a: 0, exp: "リソース不足でforkできないことはあり得ます。" },
{ q: "<code>read</code> 関数と <code>sysread</code> 関数の違いは?", o: ["sysreadはバッファリングされない(stdioを経由しない)", "sysreadは速い", "readはテキスト用", "違いはない"], a: 0, exp: "バッファリングされたIO(<>, print)と <code>sysread/syswrite</code> を混在させるとデータ不整合が起きるため注意が必要です。" },

// --- 6. Security & Taint Mode (51-60) ---
{ q: "Perlを <code>-T</code> オプションで起動すると有効になるモードは?", o: ["Taint(汚染)モード", "Traceモード", "Testモード", "Threadモード"], a: 0, exp: "外部からの入力(環境変数、引数、ファイル入力等)を「汚染された」データとして扱い、危険な操作(system等)を禁止します。" },
{ q: "汚染されたデータ(Tainted data)を浄化(Untaint)する唯一の方法は?", o: ["正規表現のキャプチャ($1など)を通す", "変数を上書きする", "lengthをチェックする", "暗号化する"], a: 0, exp: "正規表現でパターンマッチさせ、安全を確認して抽出した <code>$1</code> などは汚染が解除されます。" },
{ q: "3引数の <code>open</code> が2引数より安全な理由は?", o: ["ファイル名に含まれるメタ文字(パイプ等)による意図しないコマンド実行を防げる", "速いから", "Unicode対応だから", "文字コード指定できるから"], a: 0, exp: "2引数だと <code>open($fh, \"$input\")</code> で入力が <code>| rm -rf /</code> だった場合に実行されてしまいます。" },
{ q: "ハッシュのキー順序がランダム化されているセキュリティ上の理由は?", o: ["アルゴリズミック・コンプレキシティ・攻撃(Hash DoS)の防止", "高速化のため", "メモリ節約", "仕様バグ"], a: 0, exp: "意図的に衝突を狙った入力を送りつけ、ハッシュ操作を最悪計算量に落とす攻撃を防ぐためです。" },
{ q: "<code>List::Util::shuffle</code> が暗号学的に安全でない理由は?", o: ["乱数生成器が予測可能(mt19937等ではない場合がある)", "バグがある", "遅いから", "偏りがある"], a: 0, exp: "標準の <code>rand</code> に依存しており、これは暗号用ではありません。<code>Math::Random::Secure</code> 等が必要です。" },
{ q: "<code>Safe</code> モジュールの役割は?", o: ["制限されたコンパートメント(Opcode制限)でコードを実行する", "変数を暗号化する", "スレッドセーフにする", "DB接続を安全にする"], a: 0, exp: "<code>eval</code> する際に、system などの危険なオペコードを禁止した環境を作ります。" },
{ q: "<code>perl -Ilib</code> オプションの役割は?", o: ["@INC(ライブラリパス)の先頭にディレクトリを追加", "インストール", "情報表示", "無視する"], a: 0, exp: "モジュール読み込みパスを追加します。" },
{ q: "CGIなどで <code>param()</code> をリストコンテキストで受ける脆弱性は?", o: ["パラメータ汚染(意図しない多重パラメータ)によるロジック破壊", "SQLインジェクション", "XSS", "バッファオーバーフロー"], a: 0, exp: "スカラを期待している箇所にリストを渡されると、ハッシュのキーと値がずれるなどの問題が起きます。" },
{ q: "<code>YAML::Load</code> や <code>Storable::thaw</code> を信頼できないデータに行うリスクは?", o: ["オブジェクトのデストラクタ等を利用したコード実行(RCE)", "文字化け", "メモリリーク", "なし"], a: 0, exp: "デシリアライズ時に特定のオブジェクトを生成させ、<code>DESTROY</code> などをトリガーに悪意あるコードを実行される可能性があります。" },
{ q: "<code>SetUID</code> されたPerlスクリプトの挙動は?", o: ["自動的にTaintモードが有効になる", "rootになる", "実行できない", "警告が出る"], a: 0, exp: "カーネルがsuidperlをサポートしていない場合など複雑ですが、基本はセキュリティのためTaintモードが強制されます。" },

// --- 7. Modern Perl & Tools (61-70) ---
{ q: "<code>use feature 'signatures';</code> (Perl 5.20+) で可能になる構文は?", o: ["sub foo ($x, $y) { ... }", "sub foo : int { ... }", "class Foo { ... }", "async sub"], a: 0, exp: "サブルーチンシグネチャ。引数のアンパック(<code>my ($x) = @_;</code>)を簡潔に書けます。" },
{ q: "<code>Carton</code> とは何か?", o: ["Perlのモジュール依存関係管理ツール(RubyのBundler相当)", "Webフレームワーク", "テストツール", "インストーラー"], a: 0, exp: "<code>cpanfile</code> を元にモジュールを <code>local/</code> にインストールし、バージョンを固定します。" },
{ q: "<code>Plack / PSGI</code> の役割は?", o: ["WebサーバとWebアプリの間の標準インターフェース", "データベースドライバ", "テンプレートエンジン", "CSSフレームワーク"], a: 0, exp: "PythonのWSGI、RubyのRackに相当し、PerlのWeb開発をモダナイズしました。" },
{ q: "<code>cpanm</code> (App::cpanminus) の特徴は?", o: ["設定不要、軽量、メモリ省消費のCPANインストーラー", "GUIツール", "全機能入りIDE", "コンパイラ"], a: 0, exp: "従来のCPANシェルよりも圧倒的に手軽で高速なインストーラーです。" },
{ q: "Perl 7 プロジェクトの現状(2023-2024時点)は?", o: ["Perl 5.3x系として新機能(class等)を取り込む方針にシフトした", "リリース済み", "Perl 6になった", "中止された"], a: 0, exp: "「Perl 7」というメジャーバージョンアップ案はいったん落ち着き、Perl 5.x系列で着実に機能追加(Corinna等)が進んでいます。" },
{ q: "<code>Dualvar</code> (Scalar::Util) とは?", o: ["数値コンテキストと文字列コンテキストで異なる値を持つスカラ", "2つの値を持つ配列", "リファレンスと実体", "キーと値"], a: 0, exp: "<code>$!</code>(数値だとerrno、文字列だとエラー文)などが代表例です。" },
{ q: "<code>perl -E</code> オプションは <code>-e</code> と何が違う?", o: ["全ての新しいfeature(say等)が有効になる", "エラーチェックが厳しい", "例外を投げる", "エンコーディング指定"], a: 0, exp: "ワンライナーで <code>say</code> などを使いたい時に便利です。" },
{ q: "<code>Smartmatch</code> (~~) 演算子の現状は?", o: ["非推奨(Deprecated)または削除予定", "推奨される", "高速化された", "基本機能になった"], a: 0, exp: "仕様が複雑すぎてバグが多く、実験的機能から正式採用されずに非推奨化が進んでいます。" },
{ q: "<code>List::Util</code>, <code>Scalar::Util</code> がコアモジュールである意味は?", o: ["別途インストール不要で使える", "Perl自体に組み込まれている", "最速", "削除できない"], a: 0, exp: "標準ライブラリとして同梱されているため、依存関係を気にせず使えます。" },
{ q: "<code>perl -v</code> で表示される <code>v5.xx.y</code> の偶数バージョン(5.36, 5.38)は?", o: ["安定版(Stable)", "開発版", "レガシー版", "長期サポート版"], a: 0, exp: "奇数(5.37等)は開発版、偶数は安定版です。" }
];

// 初期化
function init() {
els.mainTitle.textContent = CONFIG.title;
state.questions = ALL_QUESTIONS;

// 70問もあるのでシャッフルして毎回違う順で楽しみたい場合は、
// 以下のコメントアウトを外してください。
// shuffleArray(state.questions);

state.currentIndex = 0;
state.score = 0;
state.userAnswers = [];

els.quizArea.classList.remove('hidden');
els.resultArea.classList.add('hidden');

showQuestion();
}

// 問題表示
function showQuestion() {
if (state.currentIndex >= state.questions.length) {
endQuiz();
return;
}

// UIリセット
els.feedbackArea.classList.add('hidden');
els.timerArea.style.display = 'block';
els.options.innerHTML = '';
els.nextBtn.style.display = 'none';

// プログレスバー更新
const progress = ((state.currentIndex) / state.questions.length) * 100;
els.progressFill.style.width = `${progress}%`;

const qData = state.questions[state.currentIndex];
els.qNum.textContent = `QUESTION ${state.currentIndex + 1} / ${state.questions.length}`;
els.qText.innerHTML = qData.q;

// 選択肢生成(正解インデックスを保持したオブジェクトを作成してシャッフル)
const opts = qData.o.map((text, i) => ({ text, isCorrect: i === qData.a }));
shuffleArray(opts);

opts.forEach(opt => {
const btn = document.createElement('button');
btn.className = 'option';
btn.innerHTML = opt.text; // HTMLタグ許可
btn.onclick = () => handleAnswer(opt.isCorrect, btn);
btn.dataset.correct = opt.isCorrect;
els.options.appendChild(btn);
});

startTimer();
}

// タイマー開始
function startTimer() {
clearInterval(state.timer);
state.timeLeft = CONFIG.timerSeconds;
els.timer.textContent = state.timeLeft;

state.timer = setInterval(() => {
state.timeLeft--;
els.timer.textContent = state.timeLeft;
if (state.timeLeft <= 0) {
clearInterval(state.timer);
handleAnswer(false, null, true); // 時間切れ
}
}, 1000);
}

// 回答処理
function handleAnswer(isCorrect, btnElement, isTimeout = false) {
clearInterval(state.timer);

// 全ボタン無効化 & 正解表示
const buttons = els.options.querySelectorAll('.option');
buttons.forEach(b => {
b.disabled = true;
if (b.dataset.correct === "true") {
b.classList.add('correct-answer');
}
});

if (isTimeout) {
els.fbTitle.textContent = "Time Out...";
els.fbTitle.className = "fb-incorrect";
} else {
if (isCorrect) {
btnElement.classList.add('correct-choice');
state.score++;
els.fbTitle.textContent = "Excellent!";
els.fbTitle.className = "fb-correct";
} else {
btnElement.classList.add('incorrect-choice');
els.fbTitle.textContent = "Incorrect...";
els.fbTitle.className = "fb-incorrect";
}
}

// 履歴保存
const qData = state.questions[state.currentIndex];
state.userAnswers.push({
q: qData.q,
a: qData.o[qData.a],
isCorrect: isCorrect,
userChoice: isTimeout ? "Time Out" : btnElement.textContent
});

// 解説表示
els.fbText.innerHTML = qData.exp;
els.feedbackArea.classList.remove('hidden');
els.timerArea.style.display = 'none';

// 次へボタン
els.nextBtn.style.display = 'block';
els.nextBtn.onclick = () => {
state.currentIndex++;
showQuestion();
};
}

// 終了処理
function endQuiz() {
els.quizArea.classList.add('hidden');
els.resultArea.classList.remove('hidden');

const percentage = Math.round((state.score / state.questions.length) * 100);
els.scoreDisplay.textContent = `${state.score} / ${state.questions.length} (${percentage}%)`;

let msg = "";
if (percentage >= 90) msg = "恐れ入りました。あなたは真のPerl Hackerです。";
else if (percentage >= 70) msg = "素晴らしい!上級者を名乗るに相応しい知識です。";
else if (percentage >= 50) msg = "難所をよく乗り越えました。さらに深淵を目指しましょう。";
else msg = "Perlの道は険しいですが、学びがいのある言語です。復習しましょう!";

els.resultMsg.textContent = msg;

// 詳細レビュー生成
els.reviewList.innerHTML = "";
state.userAnswers.forEach((ans, i) => {
const div = document.createElement('div');
div.className = "review-item";
const statusClass = ans.isCorrect ? "ok" : "ng";
const statusIcon = ans.isCorrect ? "✔" : "✘";
div.innerHTML = `
<div class="review-q"><span class="review-status ${statusClass}">${statusIcon} Q${i+1}</span>: ${ans.q}</div>
<div class="review-ans">正解: <b>${ans.a}</b></div>
`;
els.reviewList.appendChild(div);
});
}

// ユーティリティ
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}

// イベントリスナー
els.restartBtn.addEventListener('click', init);

// 開始
init();

</script>
</body>
</html>


使用変数

*dst
@a
ALL_QUESTIONS
b
btn
buttons
charset
class
className
CONFIG
content
correct
currentIndex
disabled
display
div
els
endQuiz -------( Function )
foo
handleAnswer -------( Function )
i
id
init -------( Function )
innerHTML
isTimeout
j
lang
msg
name
NAME
onclick
opt
opts
percentage
progress
qData
questions
scale
score
showQuestion -------( Function )
shuffleArray -------( Function )
startTimer -------( Function )
state
statusClass
statusIcon
textContent
timeLeft
timer
userAnswers
width

Content-type: text/html error-smemo8

ERROR !

ファイルの差し替えに失敗しました: ./smemo8.log