junkerstock
 quiz-perl中級2 

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Perl中級 100本ノック</title>
<style>
:root {
/* 知識の森をイメージしたディープグリーン */
--primary-color: #1b5e20;
--secondary-color: #4e342e;
--correct-color: #2e7d32;
--incorrect-color: #c62828;
--light-bg: #e8f5e9;
--white-bg: #ffffff;
--text-color: #263238;
--highlight-bg: #c8e6c9;
}
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 4px 15px rgba(0,0,0,0.1);
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.5rem; }
#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: 6px;
background-color: #ddd;
border-radius: 3px;
margin-bottom: 15px;
overflow: hidden;
}
#progress-fill {
height: 100%;
background-color: var(--primary-color);
width: 0%;
transition: width 0.3s;
}
#question {
font-size: 18px;
font-weight: 600;
min-height: 50px;
margin: 10px 0 20px;
line-height: 1.6;
text-align: left;
}
code {
background-color: #f1f8e9;
padding: 2px 5px;
border-radius: 3px;
font-family: Consolas, Monaco, monospace;
color: #d84315;
font-size: 0.9em;
border: 1px solid #c5e1a5;
}
#options {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
margin: 15px 0;
}
@media (min-width: 600px) {
#options { grid-template-columns: 1fr 1fr; }
}
.option {
background: var(--white-bg);
color: var(--primary-color);
border: 1px solid var(--primary-color);
border-radius: 5px;
padding: 12px;
font-size: 15px;
cursor: pointer;
transition: all 0.2s;
text-align: left;
}
.option:hover:not(:disabled) {
background-color: var(--primary-color);
color: white;
}
.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: 2px;
font-weight: bold;
}

#feedback-area {
background-color: var(--highlight-bg);
border-radius: 6px;
padding: 15px;
margin-top: 15px;
text-align: left;
border-left: 5px solid var(--primary-color);
animation: fadeIn 0.3s;
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
#feedback-title { font-weight: bold; font-size: 1.1em; margin-bottom: 5px; }
.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: 5px;
padding: 10px 25px;
font-size: 16px;
cursor: pointer;
margin-top: 10px;
display: inline-block;
}
#next-button { display: block; margin: 10px auto 0; width: 200px; }

#timer-area { margin-top: 15px; font-weight: bold; color: #777; }

/* 結果画面 */
#result-area h2 { font-size: 2rem; margin: 0; }
#score-display { font-size: 3rem; font-weight: bold; color: var(--primary-color); margin: 10px 0; }
#explanation-list { text-align: left; max-height: 60vh; overflow-y: auto; margin-top: 20px; padding: 10px; border: 1px solid #ddd; }
.review-item { border-bottom: 1px solid #eee; padding: 10px 0; font-size: 0.9em; }
.review-q { font-weight: bold; }
.review-status.ok { color: var(--correct-color); }
.review-status.ng { color: var(--incorrect-color); }
</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>s</div>
</div>

<div id="result-area" class="panel hidden">
<h2>Result</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中級 100本ノック",
timerSeconds: 30, // 1問あたりの時間
questionCount: 100 // 出題数(データがあれば最大数まで)
};

// 状態管理
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')
};

// クイズデータ(全100問)
const ALL_QUESTIONS = [
// --- リファレンスとデリファレンス (1-10) ---
{ q: "無名配列コンストラクタはどれ?", o: ["[]", "{}", "()", "<>"], a: 0, exp: "<code>[]</code> は無名配列へのリファレンスを返します。<code>{}</code> は無名ハッシュです。" },
{ q: "無名ハッシュコンストラクタはどれ?", o: ["{}", "[]", "()", "%()"], a: 0, exp: "<code>{}</code> は無名ハッシュへのリファレンスを生成します。" },
{ q: "配列 <code>@a</code> のリファレンスを取得する演算子は?", o: ["\\", "&", "*", "$"], a: 0, exp: "<code>\\@a</code> で配列のリファレンスを取得できます。" },
{ q: "配列リファレンス <code>$ar</code> の2番目の要素にアクセスする記法は?", o: ["$ar->[1]", "$ar[1]", "@ar->[1]", "${$ar}[1]"], a: 0, exp: "<code>$ar->[1]</code> が標準的です。<code>$$ar[1]</code> も可ですが、<code>$ar[1]</code> は間違いです。" },
{ q: "ハッシュリファレンス <code>$hr</code> のキー 'k' の値にアクセスする記法は?", o: ["$hr->{k}", "$hr{k}", "%hr->{k}", "&hr{k}"], a: 0, exp: "矢印演算子を使って <code>$hr->{k}</code> と書きます。" },
{ q: "<code>ref($var)</code> が 'CODE' を返した場合、<code>$var</code> は何?", o: ["サブルーチンリファレンス", "ファイルハンドル", "オブジェクト", "暗号化文字列"], a: 0, exp: "CODE はサブルーチン(コード)へのリファレンスを示します。" },
{ q: "配列リファレンス <code>$ar</code> をデリファレンスして配列全体に戻す記法は?", o: ["@$ar", "%$ar", "$$ar", "&$ar"], a: 0, exp: "<code>@{$ar}</code> または省略して <code>@$ar</code> と書きます。" },
{ q: "<code>$sub_ref->()</code> と等価な古い記法は?", o: ["&$sub_ref", "*$sub_ref", "%$sub_ref", "@$sub_ref"], a: 0, exp: "<code>&$sub_ref</code> はデリファレンスしてサブルーチンを実行します。" },
{ q: "次のうち、シンボリックリファレンス(推奨されない)はどれ?", o: ["$name='x'; $$name", "$r=\\$x; $$r", "$r=[]; $r->[0]", "$r={}; $r->{k}"], a: 0, exp: "文字列を変数名として扱う <code>$$name</code> は <code>use strict 'refs'</code> でエラーになります。" },
{ q: "<code>Scalar::Util::weaken($ref)</code> の効果は?", o: ["参照カウントを増やさない", "変数を未定義にする", "メモリを強制解放する", "読み取り専用にする"], a: 0, exp: "循環参照によるメモリリークを防ぐため、参照カウントを加算しない弱い参照を作ります。" },

// --- 複雑なデータ構造 (11-20) ---
{ q: "2次元配列(配列の配列)の要素 <code>$AoA[0][0]</code> にアクセスする際の省略記法は?", o: ["$AoA[0]->[0]と同じ", "$AoA{0}{0}", "@AoA[0][0]", "$AoA->[0][0]"], a: 0, exp: "隣接する添字の間の矢印は省略可能です。<code>$AoA[0][0]</code> は <code>$AoA[0]->[0]</code> と等価です。" },
{ q: "ハッシュの配列 (AoH) を作成する正しい構文は?", o: ["my @a = ({k=>1}, {k=>2});", "my @a = ([k=>1], [k=>2]);", "my %a = ({k=>1}, {k=>2});", "my @a = {k=>1, k=>2};"], a: 0, exp: "リストの中に無名ハッシュ <code>{}</code> を並べます。" },
{ q: "<code>push @$ar, $val;</code> は何をする?", o: ["リファレンス $ar が指す配列に要素を追加", "$ar という配列に追加", "エラーになる", "$ar の参照先を書き換える"], a: 0, exp: "<code>push</code> の第1引数には配列そのものが必要なので、<code>@$ar</code> とデリファレンスする必要があります(最近のPerlでは実験的にリファレンスも渡せますが、基本はデリファレンス)。" },
{ q: "<code>keys %$hr</code> は何をする?", o: ["リファレンス $hr が指すハッシュのキーを取得", "$hr というハッシュのキーを取得", "エラー", "ハッシュの値をリセット"], a: 0, exp: "<code>keys</code> にはハッシュが必要なので、<code>%$hr</code> でデリファレンスします。" },
{ q: "ディープコピー(深いコピー)を行う標準的な方法は?", o: ["Storable::dclone", "copy", "clone", "duplicate"], a: 0, exp: "<code>Storable</code> モジュールの <code>dclone</code> がよく使われます。" },
{ q: "<code>$hash{key} = [1, 2, 3];</code> この構造は?", o: ["ハッシュの値が配列リファレンス", "ハッシュの値が配列", "多重ハッシュ", "構文エラー"], a: 0, exp: "値として無名配列リファレンスを代入しています(HoA)。" },
{ q: "無名配列の中に無名ハッシュを入れる構文は?", o: ["[{a=>1}, {b=>2}]", "({a=>1}, {b=>2})", "{{a=>1}, {b=>2}}", "[a=>1, b=>2]"], a: 0, exp: "<code>[]</code> の中に <code>{}</code> を列挙します。" },
{ q: "データ構造をダンプ(表示)するのによく使われるモジュールは?", o: ["Data::Dumper", "Data::Print", "Dump::Value", "Show::Data"], a: 0, exp: "<code>Data::Dumper</code> はコアモジュールで、デバッグの定番です。" },
{ q: "<code>my $copy = $ref;</code> これはどういうコピー?", o: ["シャローコピー(参照のコピー)", "ディープコピー", "データの移動", "エイリアス作成"], a: 0, exp: "リファレンスのアドレスをコピーするだけなので、指す先は同じ実体です。" },
{ q: "JSONデータをPerlのデータ構造に変換するモジュールと関数は?", o: ["JSON::decode_json", "JSON::parse", "JSON::to_perl", "JSON::load"], a: 0, exp: "<code>JSON</code> モジュールの <code>decode_json</code> が一般的です。" },

// --- スコープと変数 (21-30) ---
{ q: "<code>my</code> 宣言された変数のスコープは?", o: ["レキシカル(ブロック内)", "ダイナミック(呼び出し元含む)", "グローバル", "パッケージ"], a: 0, exp: "<code>my</code> はレキシカルスコープを作ります。宣言されたブロック内でのみ有効です。" },
{ q: "<code>local</code> 宣言できる変数は?", o: ["パッケージ変数(グローバル)", "レキシカル変数", "定数", "リテラル"], a: 0, exp: "<code>local</code> は既存のパッケージ変数の値を一時的に退避・変更するもので、レキシカル変数には使えません。" },
{ q: "<code>our</code> 宣言された変数の性質は?", o: ["パッケージ変数のレキシカルなエイリアス", "完全なプライベート変数", "定数", "スレッドローカル変数"], a: 0, exp: "<code>our</code> はパッケージ変数を、そのスコープ内で完全修飾名なしで使えるように宣言します。" },
{ q: "Perl 5.10以降で、状態を保持する(初期化を一度だけ行う)変数は?", o: ["state", "static", "persist", "keep"], a: 0, exp: "<code>state $x = 0;</code> はC言語のstatic変数のように振る舞います。" },
{ q: "<code>use strict;</code> が強制しないチェックは?", o: ["ファイルハンドルの変数化", "変数の宣言", "シンボリックリファレンスの禁止", "ベアワードの禁止"], a: 0, exp: "<code>use strict</code> は vars, refs, subs をチェックしますが、ファイルハンドルの扱い自体は強制しません(ただしベアワードFHはsubs/varsで引っかかる場合あり)。選択肢の中では「ファイルハンドルの変数化(レキシカルFH)」は推奨されますが、strictの直接の対象というよりはモダンPerlの作法です。正しくは「strictはこれらをチェックする」の裏返しですが、strictは「未宣言変数の使用」等をエラーにします。" },
{ q: "<code>local</code> の値が元に戻るタイミングは?", o: ["スコープ(ブロック)を抜けた時", "明示的にrestoreした時", "プログラム終了時", "次のlocal宣言時"], a: 0, exp: "ブロックを抜けると自動的に元の値に戻ります。" },
{ q: "クロージャとは?", o: ["環境(レキシカル変数)を閉じ込めたサブルーチン", "クラスのメソッド", "モジュールの終わり", "プライベート変数"], a: 0, exp: "定義時のレキシカル変数を保持し続ける無名サブルーチンなどを指します。" },
{ q: "<code>do { ... }</code> ブロックの戻り値は?", o: ["最後に評価された式の値", "常に1", "常にundef", "ブロック内の変数の数"], a: 0, exp: "<code>do</code> ブロックは最後に評価した値を返します。" },
{ q: "動的スコープ(Dynamic Scope)を持つ宣言は?", o: ["local", "my", "our", "state"], a: 0, exp: "<code>local</code> は動的スコープを提供します(呼び出されたサブルーチン内でも変更が見える)。" },
{ q: "パッケージ変数の完全修飾名の例は?", o: ["$Package::var", "$Package.var", "$Package->var", "$var@Package"], a: 0, exp: "<code>$PackageName::VariableName</code> の形式です。" },

// --- 関数とコンテキスト (31-40) ---
{ q: "<code>map</code> 関数の基本的な動作は?", o: ["リストの各要素を変換して新しいリストを返す", "条件に合う要素を抽出する", "リストを並べ替える", "要素数を出力する"], a: 0, exp: "<code>map { 処理 } @list</code> は変換処理を行います。" },
{ q: "<code>grep</code> 関数の基本的な動作は?", o: ["条件に合う要素を抽出してリストを返す", "リストを変換する", "正規表現で置換する", "ファイルを読み込む"], a: 0, exp: "<code>grep { 条件 } @list</code> はフィルタリングを行います。" },
{ q: "<code>sort</code> ブロック内で比較に使われる特殊変数は?", o: ["$a と $b", "$1 と $2", "$x と $y", "$left と $right"], a: 0, exp: "<code>$a</code> と <code>$b</code> がパッケージ変数として自動的に使われます。" },
{ q: "数値として昇順ソートするブロックは?", o: ["{ $a <=> $b }", "{ $a cmp $b }", "{ $a - $b }", "{ $a > $b }"], a: 0, exp: "<code><=></code> は数値比較、<code>cmp</code> は文字列比較です。" },
{ q: "降順(大きい順)にソートするには?", o: ["$b <=> $a", "$a <=> $b", "reverse sort", "$b cmp $a"], a: 0, exp: "<code>$b</code> と <code>$a</code> の位置を逆にすると降順になります。" },
{ q: "<code>wantarray</code> 関数が <code>undef</code> を返すコンテキストは?", o: ["無効コンテキスト(Void context)", "スカラコンテキスト", "リストコンテキスト", "文字列コンテキスト"], a: 0, exp: "戻り値が使われない Void context では <code>undef</code> を返します。" },
{ q: "<code>scalar(@array)</code> は何を返す?", o: ["要素数", "最後の要素", "最初の要素", "配列のリファレンス"], a: 0, exp: "配列をスカラコンテキストで評価すると要素数を返します。" },
{ q: "リスト <code>(1, 2, 3)</code> をスカラ変数に代入すると? <code>$x = (1, 2, 3);</code>", o: ["3 (最後の要素)", "1 (最初の要素)", "3 (要素数)", "エラー"], a: 0, exp: "カンマ演算子の性質により、最後の要素が返されます。" },
{ q: "サブルーチンから <code>return;</code> (引数なし) した場合の戻り値は?", o: ["コンテキストによる(undef または 空リスト)", "常にundef", "常に0", "常に1"], a: 0, exp: "スカラコンテキストでは <code>undef</code>、リストコンテキストでは <code>()</code>(空リスト)になります。" },
{ q: "シュワルツ変換(Schwartzian Transform)の目的は?", o: ["ソートの高速化(計算コスト削減)", "メモリ節約", "コードの短縮", "ハッシュの反転"], a: 0, exp: "高コストなキー計算を一度だけ行い、ソート効率を上げるイディオムです。" },

// --- 正規表現 (41-55) ---
{ q: "正規表現の修飾子 <code>/x</code> の効果は?", o: ["空白とコメントを許可(可読性向上)", "大文字小文字無視", "複数行マッチ", "拡張正規表現を無効化"], a: 0, exp: "パターン内の空白を無視し、コメントを書けるようにします。" },
{ q: "<code>/m</code> 修飾子の効果は?", o: ["^ と $ が行頭・行末にマッチ", ". が改行にマッチ", "大文字小文字無視", "最小マッチ"], a: 0, exp: "文字列全体ではなく、内部の改行ごとの行頭・行末にマッチさせます。" },
{ q: "<code>/s</code> 修飾子の効果は?", o: [". が改行を含む全文字にマッチ", "空白を無視", "置換を行う", "サブルーチンを実行"], a: 0, exp: "Single line モード。ドット <code>.</code> が改行にもマッチするようになります。" },
{ q: "最短マッチ(Non-greedy)を表す量指定子は?", o: ["*?, +?", "*!, +!", "*-, +-", "*, +"], a: 0, exp: "量指定子の後に <code>?</code> を付けると最短マッチになります。" },
{ q: "肯定先読み (Lookahead) の構文は?", o: ["(?=...)", "(?!...)", "(?<=...)", "(?<!...)"], a: 0, exp: "<code>(?=...)</code> は直後にパターンがあることを確認しますが、消費しません。" },
{ q: "否定後読み (Negative Lookbehind) の構文は?", o: ["(?<!...)", "(?<=...)", "(?!...)", "(?=...)"], a: 0, exp: "<code>(?<!...)</code> は直前にパターンがないことを確認します。" },
{ q: "名前付きキャプチャ (Perl 5.10+) の構文は?", o: ["(?<name>...)", "(?:name...)", "(?P<name>...)", "(:name...)"], a: 0, exp: "<code>(?<name>...)</code> でキャプチャし、<code>%+</code> ハッシュでアクセスします。" },
{ q: "置換演算子 <code>s///e</code> の <code>e</code> は?", o: ["置換部分をコードとして評価(eval)", "エラー無視", "正規表現のエスケープ", "行末マッチ"], a: 0, exp: "置換文字列側をPerlコードとして実行し、その結果で置換します。" },
{ q: "<code>qr//</code> 演算子の役割は?", o: ["正規表現オブジェクト(コンパイル済み)の生成", "クォート置換", "逆順リスト", "ランダム正規表現"], a: 0, exp: "正規表現をコンパイルして変数に格納し、再利用できるようにします。" },
{ q: "<code>\b</code> メタ文字の意味は?(文字クラス外)", o: ["単語の境界 (Word boundary)", "バックスペース", "空白", "行頭"], a: 0, exp: "単語構成文字(<code>\w</code>)とそれ以外(<code>\W</code>)の境界にマッチします。" },
{ q: "<code>(?:...)</code> の意味は?", o: ["キャプチャしないグループ化", "コメント", "コード実行", "先読み"], a: 0, exp: "グルーピングはしたいが、<code>$1</code> などに保存したくない場合に使います。" },
{ q: "<code>\d</code> がマッチする文字集合は?", o: ["数字 [0-9]", "数字以外", "空白", "単語構成文字"], a: 0, exp: "Digit。数字にマッチします。" },
{ q: "<code>\s</code> がマッチする文字集合は?", o: ["空白文字(スペース、タブ、改行等)", "文字列", "記号", "小文字"], a: 0, exp: "Space。ホワイトスペースにマッチします。" },
{ q: "正規表現内で直前の成功位置から再開するアンカーは?", o: ["\G", "\A", "\Z", "\b"], a: 0, exp: "<code>\G</code> は <code>/g</code> 修飾子と併用し、前回マッチした場所の直後からマッチさせます。" },
{ q: "<code>quotemeta</code> 関数(または <code>\Q...\E</code>)の役割は?", o: ["メタ文字をエスケープする", "クォートを削除する", "メタデータを取得する", "文字コードを変換する"], a: 0, exp: "正規表現の特殊文字を無効化(エスケープ)して、文字通りにマッチさせます。" },

// --- ファイル操作とIO (56-65) ---
{ q: "ファイル全体を一度に読み込む(スラープする)慣用句は?", o: ["local $/; <$fh>", "undef $/", "read $fh, all", "slurp $fh"], a: 0, exp: "入力レコード区切り文字 <code>$/</code> を未定義にすると、ファイル末尾まで一気に読み込みます。" },
{ q: "3引数の <code>open</code> の推奨される書き方は?", o: ["open(my $fh, '<', $file)", "open($fh, \"< $file\")", "open($file)", "open('<', $file)"], a: 0, exp: "モードとファイル名を分ける3引数形式が安全です。" },
{ q: "ファイルテスト <code>-f</code> の意味は?", o: ["通常ファイルである", "ファイルが存在する(-e)", "ディレクトリである(-d)", "読み取り可能(-r)"], a: 0, exp: "ディレクトリやデバイスではなく、通常のファイルであれば真を返します。" },
{ q: "ファイルテスト <code>-s</code> の意味は?", o: ["ファイルサイズ(空でないなら真)", "ソケットである", "SGIDビット", "シンボリックリンク(-l)"], a: 0, exp: "ファイルのサイズ(バイト数)を返します。0なら偽として扱えます。" },
{ q: "ファイルハンドルを自動的に閉じる条件は?", o: ["レキシカル変数のスコープを抜けた時", "プログラムの最後だけ", "明示的にcloseしないと閉じない", "エラー時のみ"], a: 0, exp: "レキシカルファイルハンドル(<code>my $fh</code>)は、スコープを抜けると自動的にcloseされます。" },
{ q: "<code>__DATA__</code> トークンの役割は?", o: ["以降をデータとして扱い、<DATA>で読める", "プログラムの終了", "デバッグ用", "コメント開始"], a: 0, exp: "ソースコード内にデータを埋め込むのに使われます。" },
{ q: "ディレクトリ内のファイル一覧を取得する関数は?", o: ["glob または readdir", "dir", "ls", "filelist"], a: 0, exp: "<code>glob('*')</code> や <code>opendir</code>/<code>readdir</code> を使います。" },
{ q: "ファイルパスをOSに依存せず操作するためのコアモジュールは?", o: ["File::Spec", "Path::Class", "File::Path", "Dir::Handle"], a: 0, exp: "<code>File::Spec</code> が標準的です(最近は <code>Path::Tiny</code> なども人気ですがコアならこれ)。" },
{ q: "<code>binmode $fh</code> の役割は?", o: ["バイナリモードにする(改行変換等を無効化)", "テキストモードにする", "実行モードにする", "バッファをフラッシュする"], a: 0, exp: "特にWindowsで画像などを扱う際に重要です。" },
{ q: "<code>select</code> 関数の役割(1引数)は?", o: ["デフォルトの出力ファイルハンドルを変更する", "入力を待つ", "リストから選ぶ", "ソケットを監視する"], a: 0, exp: "<code>print</code> の出力先(デフォルトはSTDOUT)を切り替えます。" },

// --- 特殊変数 (66-75) ---
{ q: "エラーメッセージ(dieなど)が格納される変数は?", o: ["$@", "$!", "$?", "$_"], a: 0, exp: "<code>eval</code> での例外内容は <code>$@</code> に入ります。" },
{ q: "システムエラー(OSのエラーメッセージ)が格納される変数は?", o: ["$!", "$@", "$E", "$ERR"], a: 0, exp: "<code>$!</code> (errno) に入ります。" },
{ q: "外部コマンドの終了ステータスが格納される変数は?", o: ["$?", "$!", "$@", "$RC"], a: 0, exp: "<code>system</code> やバッククォートの戻り値情報は <code>$?</code> に入ります。" },
{ q: "正規表現のマッチした全体が入る変数は?", o: ["$&", "$0", "$MATCH", "$+"], a: 0, exp: "<code>$&</code> にマッチした部分全体が入ります(ただし速度低下の要因になる場合あり)。" },
{ q: "現在の行番号が入っている変数は?", o: ["$.", "$LINE", "$#", "$/"], a: 0, exp: "<code>$.</code> です。" },
{ q: "入力レコード区切り文字(デフォルトは改行)は?", o: ["$/", "$\\", "$|", "$,"], a: 0, exp: "<code>$/</code> です。" },
{ q: "出力の自動フラッシュ(バッファリング無効)を制御する変数は?", o: ["$|", "$!", "$^F", "$%"], a: 0, exp: "<code>$|</code> を1にするとオートフラッシュ有効になります。" },
{ q: "環境変数が格納されているハッシュは?", o: ["%ENV", "%SHELL", "%PATH", "%CONFIG"], a: 0, exp: "<code>%ENV</code> です。" },
{ q: "コマンドライン引数が格納されている配列は?", o: ["@ARGV", "@ARGS", "@_", "@INC"], a: 0, exp: "<code>@ARGV</code> です。" },
{ q: "シグナルハンドラを設定するハッシュは?", o: ["%SIG", "%HND", "%INT", "%KILL"], a: 0, exp: "<code>$SIG{INT} = sub { ... };</code> のように使います。" },

// --- パッケージとモジュール (76-85) ---
{ q: "現在のパッケージ名を宣言するキーワードは?", o: ["package", "namespace", "module", "class"], a: 0, exp: "<code>package MyPkg;</code> で名前空間を切り替えます。" },
{ q: "モジュールをロードし、importメソッドを呼ぶキーワードは?", o: ["use", "require", "load", "include"], a: 0, exp: "<code>use Module;</code> はコンパイル時にロードとインポートを行います。" },
{ q: "実行時にモジュールをロードする(インポートはしない)キーワードは?", o: ["require", "use", "do", "import"], a: 0, exp: "<code>require Module;</code> は実行時にファイルを読み込みます。" },
{ q: "モジュールが正常に読み込まれたことを示すために、ファイルの最後には何が必要?", o: ["真の値(通常は 1;)", "return;", "end;", "__END__"], a: 0, exp: "初期化成功を示すため、最後に真の値が必要です。" },
{ q: "関数をエクスポートするために継承する標準モジュールは?", o: ["Exporter", "Import", "Public", "Module::Base"], a: 0, exp: "<code>use parent 'Exporter';</code> して <code>@EXPORT</code> 等を設定します。" },
{ q: "デフォルトでエクスポートする関数リストを入れる配列は?", o: ["@EXPORT", "@EXPORT_OK", "@ISA", "@PUBLIC"], a: 0, exp: "<code>@EXPORT</code> は自動的にエクスポートされます。" },
{ q: "リクエストされた時だけエクスポート可能な関数リストは?", o: ["@EXPORT_OK", "@EXPORT", "@OPTIONAL", "@ALLOW"], a: 0, exp: "<code>use Module qw(func);</code> と指定された時だけインポートされます。" },
{ q: "<code>BEGIN</code> ブロックが実行されるタイミングは?", o: ["コンパイル時(可能な限り早く)", "実行時", "プログラム終了時", "モジュールロード後"], a: 0, exp: "パースされた瞬間に実行されます。" },
{ q: "<code>CHECK</code> ブロックが実行されるタイミングは?", o: ["コンパイル終了後、実行直前", "コンパイル時", "実行時", "終了時"], a: 0, exp: "コンパイルフェーズがすべて終わった後に実行されます。" },
{ q: "モジュールの検索パスが含まれる配列は?", o: ["@INC", "%INC", "@PATH", "%ENV"], a: 0, exp: "<code>@INC</code> にディレクトリパスが入っています。" },

// --- オブジェクト指向 (86-95) ---
{ q: "リファレンスをオブジェクト(クラスに関連付け)にする関数は?", o: ["bless", "new", "class", "obj"], a: 0, exp: "<code>bless $ref, 'Class';</code> でオブジェクト化します。" },
{ q: "オブジェクトの実体として最もよく使われるデータ構造は?", o: ["ハッシュリファレンス", "配列リファレンス", "スカラリファレンス", "型グロブ"], a: 0, exp: "プロパティを名前で管理しやすいハッシュリファレンスが一般的です。" },
{ q: "親クラス(継承元)を定義する特殊配列は?", o: ["@ISA", "@PARENT", "@SUPER", "@INHERIT"], a: 0, exp: "<code>@ISA</code> 配列に含まれるクラスからメソッドを探索します。" },
{ q: "メソッド探索で、どのクラスにもない場合に呼ばれる特殊メソッドは?", o: ["AUTOLOAD", "MISSING", "DEFAULT", "FALLBACK"], a: 0, exp: "<code>AUTOLOAD</code> サブルーチンで未定義メソッド呼び出しをフックできます。" },
{ q: "オブジェクトが破棄される時に呼ばれるデストラクタは?", o: ["DESTROY", "END", "DELETE", "CLEANUP"], a: 0, exp: "参照カウントが0になった時などに呼ばれます。" },
{ q: "オブジェクトが特定のクラス(または派生クラス)に属するか確認するメソッドは?", o: ["isa", "can", "is", "check"], a: 0, exp: "<code>$obj->isa('Class')</code> で確認します。" },
{ q: "オブジェクトが特定のメソッドを持っているか確認するメソッドは?", o: ["can", "has", "exists", "isa"], a: 0, exp: "<code>$obj->can('method')</code> で確認します。" },
{ q: "親クラスのメソッドを呼び出すための擬似クラスは?", o: ["SUPER", "PARENT", "BASE", "ROOT"], a: 0, exp: "<code>$self->SUPER::method()</code> で親クラスのメソッドを呼びます。" },
{ q: "<code>UNIVERSAL</code> クラスとは?", o: ["全てのクラスの基底クラス", "グローバル変数", "モジュール管理クラス", "宇宙クラス"], a: 0, exp: "<code>isa</code> や <code>can</code> はここに定義されており、全オブジェクトが継承します。" },
{ q: "最近のPerlで導入されつつある、コアのオブジェクトシステム構文は?", o: ["class / method", "obj / sub", "struct / func", "define / action"], a: 0, exp: "Corinnaプロジェクト由来の <code>class</code> 構文が実験的に導入されています。" },

// --- その他・イディオム (96-100) ---
{ q: "<code>qw(a b c)</code> は何と等価?", o: ["('a', 'b', 'c')", "\"a b c\"", "['a', 'b', 'c']", "{a=>1, b=>1, c=>1}"], a: 0, exp: "Quote Words。空白区切りの文字列リストを作ります。" },
{ q: "<code>perl -c script.pl</code> のオプションの意味は?", o: ["構文チェックのみ行う", "コンパイルして実行", "デバッグモード", "警告を有効化"], a: 0, exp: "実行せずにシンタックスチェックを行います。" },
{ q: "<code>perl -e 'code'</code> のオプションの意味は?", o: ["ワンライナーを実行する", "編集モード", "環境変数設定", "エラーチェック"], a: 0, exp: "コマンドライン引数の文字列をPerlコードとして実行します。" },
{ q: "<code>or</code> と <code>||</code> の違いは?", o: ["優先順位が違う(orは低い)", "orはビット演算", "||は文字列連結", "違いはない"], a: 0, exp: "<code>or</code> は優先順位が非常に低く、<code>open ... or die</code> のように制御フローによく使われます。" },
{ q: "<code>unless ($x)</code> はどういう意味?", o: ["if (!$x)", "if ($x)", "while (!$x)", "until ($x)"], a: 0, exp: "「もし〜でなければ」。ifの否定形です。" }
];

// 初期化
function init() {
els.mainTitle.textContent = CONFIG.title;
state.questions = ALL_QUESTIONS; // 100問すべて使用
// シャッフルしたい場合は以下を有効化
// 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, opts);
// 正解かどうかをデータ属性に(カンニング防止のため難読化してもいいが今回は簡易に)
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, null, true); // 時間切れ
}
}, 1000);
}

// 回答処理
function handleAnswer(isCorrect, btnElement, currentOpts, 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) {
// 時間切れの場合のUI
els.fbTitle.textContent = "時間切れ...";
els.fbTitle.className = "fb-incorrect";
} else {
// 選択した場合
if (isCorrect) {
btnElement.classList.add('correct-choice');
state.score++;
els.fbTitle.textContent = "正解!";
els.fbTitle.className = "fb-correct";
} else {
btnElement.classList.add('incorrect-choice');
els.fbTitle.textContent = "不正解...";
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: btnElement ? btnElement.textContent : "Time Out"
});

// 解説表示
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マスターです。";
else if (percentage >= 70) msg = "かなり理解されています。実務レベルです。";
else if (percentage >= 40) msg = "基礎はできています。苦手分野を復習しましょう。";
else msg = "ここからがスタートです。解説を読んで復習しましょう!";

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 style="color:#666; margin-top:4px;">正解: <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>


使用変数

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

Content-type: text/html error-smemo8

ERROR !

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