junkerstock
 quiz-perl中級 

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Perl中級力確認クイズ(学習モード)</title>
<style>
:root {
/* 中級編は少し落ち着いたインテリジェントな色味(濃い藍色)に変更 */
--primary-color: #2c3e50;
--secondary-color: #5d6d7e;
--correct-color: #27ae60;
--incorrect-color: #c0392b;
--light-bg: #ecf0f1;
--white-bg: #ffffff;
--text-color: #2c3e50;
--highlight-bg: #d6eaf8;
}
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: 90%;
max-width: 800px;
}
.panel {
background-color: var(--white-bg);
border-radius: 8px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
padding: 30px;
text-align: center;
margin-bottom: 20px;
border-top: 5px solid var(--primary-color);
}
.hidden {
display: none !important;
}
h1, h2 {
color: var(--primary-color);
margin-bottom: 1rem;
}
#question-number {
font-size: 16px;
font-weight: bold;
color: var(--secondary-color);
margin-bottom: 10px;
}
#question {
font-size: 20px;
font-weight: 600;
min-height: 60px;
margin: 15px 0 25px;
line-height: 1.6;
}
code {
background-color: #f4f6f7;
padding: 2px 6px;
border-radius: 4px;
font-family: Consolas, Monaco, 'Andale Mono', monospace;
color: #e74c3c;
font-size: 0.95em;
border: 1px solid #bdc3c7;
}
#options {
display: flex;
flex-direction: column;
gap: 15px;
margin: 20px 0;
}
.option {
background: var(--white-bg);
color: var(--primary-color);
border: 2px solid var(--primary-color);
border-radius: 6px;
padding: 15px;
font-size: 16px;
cursor: pointer;
transition: all 0.2s;
font-family: inherit;
position: relative;
}
.option:hover:not(:disabled) {
background-color: var(--primary-color);
color: white;
}
.option:disabled {
cursor: default;
opacity: 0.6;
}
.option.correct-choice {
background-color: var(--correct-color) !important;
color: white !important;
border-color: var(--correct-color) !important;
opacity: 1 !important;
}
.option.incorrect-choice {
background-color: var(--incorrect-color) !important;
color: white !important;
border-color: var(--incorrect-color) !important;
opacity: 1 !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;
opacity: 1 !important;
}
.option.correct-answer::after {
content: " ← 正解";
font-size: 0.8em;
color: var(--correct-color);
}

#timer-area {
margin-top: 25px;
font-size: 18px;
font-weight: bold;
color: var(--secondary-color);
}

#feedback-area {
background-color: var(--highlight-bg);
border-radius: 8px;
padding: 20px;
margin-top: 25px;
text-align: left;
border: 1px solid #a9cce3;
animation: fadeIn 0.5s;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
#feedback-title {
font-size: 20px;
font-weight: bold;
margin-bottom: 10px;
}
.feedback-correct { color: var(--correct-color); }
.feedback-incorrect { color: var(--incorrect-color); }

#feedback-text {
font-size: 16px;
line-height: 1.6;
margin-bottom: 20px;
}

#next-button {
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 6px;
padding: 12px 30px;
font-size: 18px;
cursor: pointer;
display: block;
margin: 0 auto;
}
#next-button:hover {
background-color: #1a252f;
}

#score {
font-size: 32px;
font-weight: bold;
color: var(--primary-color);
margin: 15px 0 25px;
}
.action-button {
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 6px;
padding: 12px 24px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
margin: 5px;
}
.action-button:hover {
background-color: #1a252f;
}
#show-explanation-button {
background-color: var(--secondary-color);
}
#explanation-list {
text-align: left;
margin-top: 20px;
}
.explanation-item {
background: #fafafa;
border: 1px solid #e0e0e0;
border-radius: 6px;
padding: 20px;
margin-bottom: 20px;
}
.explanation-item h3 {
font-size: 16px;
margin: 0 0 10px 0;
color: var(--primary-color);
border-bottom: 1px solid #ddd;
padding-bottom: 8px;
}
.explanation-item p {
margin: 8px 0;
line-height: 1.6;
font-size: 15px;
}
.user-answer.correct { color: var(--correct-color); font-weight: bold; }
.user-answer.incorrect { color: var(--incorrect-color); font-weight: bold; text-decoration: line-through; }
.comment {
margin-top: 15px;
padding: 12px;
border-radius: 4px;
font-size: 14px;
border-left: 4px solid;
}
.comment.correct { background-color: #e9f7ef; border-color: var(--correct-color); color: #1e8449; }
.comment.incorrect { background-color: #f9ebea; border-color: var(--incorrect-color); color: #922b21; }
</style>
</head>
<body>

<div id="container">

<div id="quiz-area" class="panel">
<h1 id="main-title"></h1>
<div id="question-area">
<p id="question-number"></p>
<p id="question"></p>
</div>
<div id="options">
<button class="option"></button>
<button class="option"></button>
<button class="option"></button>
</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">
残り時間: <span id="timer">30</span>秒
</div>
</div>

<div id="result-area" class="panel hidden">
<h2 id="result-title"></h2>
<p id="result-message"></p>
<p id="score"></p>
<button id="show-explanation-button" class="action-button"></button>
<button id="restart-button" class="action-button"></button>
</div>

<div id="explanation-area" class="panel hidden">
<h2 id="explanation-title"></h2>
<div id="explanation-list"></div>
<button id="restart-button-2" class="action-button"></button>
</div>
</div>

<script>
'use strict';

// ===================================
// ① プログラムのロジック
// ===================================

let quizData = [];
let userAnswers = [];
let currentShuffledOptions = [];
let currentQuestionIndex = 0;
let score = 0;
let timerInterval;
let timeLeft;

const quizAreaEl = document.getElementById('quiz-area');
const resultAreaEl = document.getElementById('result-area');
const explanationAreaEl = document.getElementById('explanation-area');
const questionNumberEl = document.getElementById('question-number');
const questionEl = document.getElementById('question');
const optionButtons = document.querySelectorAll('.option');
const timerEl = document.getElementById('timer');
const timerAreaEl = document.getElementById('timer-area');
const feedbackAreaEl = document.getElementById('feedback-area');
const feedbackTitleEl = document.getElementById('feedback-title');
const feedbackTextEl = document.getElementById('feedback-text');
const nextButtonEl = document.getElementById('next-button');
const scoreEl = document.getElementById('score');
const explanationListEl = document.getElementById('explanation-list');
const showExplanationButton = document.getElementById('show-explanation-button');
const restartButtons = [document.getElementById('restart-button'), document.getElementById('restart-button-2')];
const mainTitleEl = document.getElementById('main-title');
const resultTitleEl = document.getElementById('result-title');
const resultMessageEl = document.getElementById('result-message');
const explanationTitleEl = document.getElementById('explanation-title');

function shuffleArray(array) {
const newArray = [...array];
for (let i = newArray.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[newArray[i], newArray[j]] = [newArray[j], newArray[i]];
}
return newArray;
}

function startQuiz() {
quizData = shuffleArray(quizConfig.allQuizData).slice(0, 10);
userAnswers = [];
currentQuestionIndex = 0;
score = 0;
quizAreaEl.classList.remove('hidden');
resultAreaEl.classList.add('hidden');
explanationAreaEl.classList.add('hidden');
showQuestion();
}

function showQuestion() {
feedbackAreaEl.classList.add('hidden');
timerAreaEl.classList.remove('hidden');

optionButtons.forEach(btn => {
btn.disabled = false;
btn.className = 'option';
btn.innerHTML = '';
delete btn.dataset.isCorrect;
});

if (currentQuestionIndex >= quizData.length) {
showResult();
return;
}

const currentQuestion = quizData[currentQuestionIndex];
questionNumberEl.textContent = `Question ${currentQuestionIndex + 1} / ${quizData.length}`;
questionEl.innerHTML = currentQuestion.question;

const optionsWithCorrectness = currentQuestion.options.map((optionText, index) => ({
text: optionText,
isCorrect: (index === currentQuestion.answer)
}));

currentShuffledOptions = shuffleArray(optionsWithCorrectness);

currentShuffledOptions.forEach((optionData, index) => {
optionButtons[index].textContent = optionData.text;
optionButtons[index].dataset.isCorrect = optionData.isCorrect;
});

startTimer();
}

function startTimer() {
timeLeft = quizConfig.timerSeconds;
timerEl.textContent = timeLeft;
clearInterval(timerInterval);
timerInterval = setInterval(() => {
timeLeft--;
timerEl.textContent = timeLeft;
if (timeLeft <= 0) {
clearInterval(timerInterval);
handleAnswer(-1);
}
}, 1000);
}

function handleAnswer(selectedIndex) {
clearInterval(timerInterval);

const currentQuestion = quizData[currentQuestionIndex];
let isCorrect = false;
let selectedOptionText = "時間切れ";

optionButtons.forEach((button, index) => {
button.disabled = true;
if (button.dataset.isCorrect === 'true') {
button.classList.add('correct-answer');
}
});

if (selectedIndex !== -1) {
const selectedOption = currentShuffledOptions[selectedIndex];
selectedOptionText = selectedOption.text;
isCorrect = selectedOption.isCorrect;

if (isCorrect) {
optionButtons[selectedIndex].classList.add('correct-choice');
score++;
} else {
optionButtons[selectedIndex].classList.add('incorrect-choice');
}
}

userAnswers.push({ text: selectedOptionText, isCorrect: isCorrect });

timerAreaEl.classList.add('hidden');
feedbackAreaEl.classList.remove('hidden');

if (isCorrect) {
feedbackTitleEl.textContent = "正解";
feedbackTitleEl.className = "feedback-correct";
} else {
feedbackTitleEl.textContent = selectedIndex === -1 ? "時間切れ" : "不正解";
feedbackTitleEl.className = "feedback-incorrect";
}

feedbackTextEl.innerHTML = `<strong>解説:</strong><br>${currentQuestion.explanation}`;
}

function nextQuestion() {
currentQuestionIndex++;
showQuestion();
}

function showResult() {
quizAreaEl.classList.add('hidden');
resultAreaEl.classList.remove('hidden');
scoreEl.textContent = `正答数: ${score} / ${quizData.length}`;
}

function showExplanations() {
resultAreaEl.classList.add('hidden');
explanationAreaEl.classList.remove('hidden');
explanationListEl.innerHTML = '';

quizData.forEach((q, index) => {
const userAnswer = userAnswers[index];
const isCorrect = userAnswer.isCorrect;
const commentArray = isCorrect ? quizConfig.correctComments : quizConfig.incorrectComments;
const randomComment = commentArray[Math.floor(Math.random() * commentArray.length)];

const explanationHTML = `
<div class="explanation-item">
<h3>Q${index + 1}: ${q.question}</h3>
<p>あなたの回答: <span class="user-answer ${isCorrect ? 'correct' : 'incorrect'}">${userAnswer.text}</span></p>
<p>正解: <strong>${q.options[q.answer]}</strong></p>
<p><strong>【解説】</strong>: ${q.explanation}</p>
<div class="comment ${isCorrect ? 'correct' : 'incorrect'}">講評: ${randomComment}</div>
</div>
`;
explanationListEl.innerHTML += explanationHTML;
});
}

function applyTheme(config) {
document.title = config.title;
mainTitleEl.textContent = config.title;
resultTitleEl.textContent = config.resultTitle;
resultMessageEl.textContent = config.resultMessage;
explanationTitleEl.textContent = config.explanationTitle;
showExplanationButton.textContent = config.showExplanationButtonText;
restartButtons.forEach(button => button.textContent = config.restartButtonText);
}

function initializeQuiz(config) {
applyTheme(config);
optionButtons.forEach((button, index) => button.addEventListener('click', () => handleAnswer(index)));
nextButtonEl.addEventListener('click', nextQuestion);
restartButtons.forEach(button => button.addEventListener('click', startQuiz));
showExplanationButton.addEventListener('click', showExplanations);
startQuiz();
}

// ===================================
// ② テーマ別データベース (Perl中級編)
// ===================================
const quizConfig = {
title: "Perl Programming Quiz (Intermediate)",
resultTitle: "試験終了",
resultMessage: "学習成果を確認してください。",
explanationTitle: "問題の振り返り",
showExplanationButtonText: "全問の解説を見る",
restartButtonText: "再挑戦",
timerSeconds: 30,
correctComments: [
"正解です。正確な理解に基づいた回答です。",
"素晴らしい。中級レベルの概念を習得しています。",
"その通りです。リファレンスの扱いも問題ありません。",
"完璧です。Perlの仕様を深く理解されています。",
"正解。文脈(コンテキスト)の違いも見抜けています。",
"見事です。実務でも通用する知識レベルです。",
"正解です。効率的なコード記述への第一歩です。",
"その通り。モジュールの仕組みを理解されています。",
"正解です。この調子で学習を進めてください。",
"素晴らしい。複雑なデータ構造も恐れるに足りません。"
],
incorrectComments: [
"不正解です。リファレンスの構文を再確認しましょう。",
"惜しいですが、誤りです。デリファレンスの優先順位に注意が必要です。",
"正しくありません。スカラコンテキストとリストコンテキストの違いを復習してください。",
"誤りです。正規表現の修飾子の意味を混同している可能性があります。",
"不正解です。変数のスコープ(my/local)の違いは重要なポイントです。",
"違います。ハッシュの構造とアクセス方法を見直しましょう。",
"不正解。匿名配列と匿名ハッシュのコンストラクタを区別してください。",
"誤りです。モジュールのインポート機構について確認をお勧めします。",
"正解ではありません。特殊変数の挙動をドキュメントで確認しましょう。",
"不正解です。しかし、この誤りはより深い理解へのきっかけとなります。"
],
allQuizData: [
// --- リファレンスとデリファレンス ---
{ question: "配列リファレンス <code>$aref</code> の2番目の要素にアクセスする正しい記述はどれですか?", options: ["$aref[1]", "$aref->[1]", "@$aref[1]"], answer: 1, explanation: "リファレンス経由でのアクセスには矢印演算子 <code>-></code> を使用します。<code>$aref[1]</code> は文法エラーではありませんが、<code>$aref</code> という名前の配列の要素を指すため意図とは異なります(strict環境下ではエラー)。" },
{ question: "次のコード <code>$ref = { a => 1, b => 2 };</code> で作成されるデータ構造は何ですか?", options: ["無名配列へのリファレンス", "無名ハッシュへのリファレンス", "通常のハッシュ"], answer: 1, explanation: "波括弧 <code>{}</code> は無名ハッシュコンストラクタであり、作成されたハッシュへのリファレンス(参照)を返します。無名配列は角括弧 <code>[]</code> です。" },
{ question: "サブルーチンリファレンス <code>$subref</code> を実行(呼び出し)する正しい記述はどれですか?", options: ["&$subref", "$subref->()", "両方とも正しい"], answer: 2, explanation: "<code>$subref->()</code> が現代的な推奨される記法ですが、<code>&$subref</code> もデリファレンスとして機能し、正しく実行されます。" },
{ question: "配列 <code>@array</code> へのリファレンスを作成する演算子はどれですか?", options: ["&", "*", "\\"], answer: 2, explanation: "バックスラッシュ演算子 <code>\\</code> を変数の前に置くことで、その変数へのリファレンスを作成できます。例: <code>$ref = \\@array;</code>" },

// --- 複雑なデータ構造 ---
{ question: "「ハッシュの配列 (LoH)」において、2番目のハッシュのキー 'key' の値にアクセスする記述は?<br><code>$LoH[1]->{'key'}</code>", options: ["正しい", "誤り(矢印は不要)", "誤り(角括弧と波括弧が逆)"], answer: 0, explanation: "正しい記述です。また、隣接する添字の間の矢印は省略可能なので <code>$LoH[1]{'key'}</code> と書くこともできます。" },
{ question: "<code>ref($var)</code> 関数が <code>'HASH'</code> を返した場合、<code>$var</code> は何ですか?", options: ["ハッシュそのもの", "ハッシュへのリファレンス", "ハッシュのキー"], answer: 1, explanation: "<code>ref</code> 関数は、変数がリファレンスである場合にその参照先の型(SCALAR, ARRAY, HASH, CODE等)を返します。通常の値なら空文字列を返します。" },

// --- スコープとパッケージ ---
{ question: "<code>local</code> 修飾子で宣言された変数の性質として正しいものは?", options: ["レキシカルスコープを持つ(ブロック内のみ有効)", "グローバル変数の値を一時的に退避・変更する(動的スコープ)", "プログラム全体で永続化される"], answer: 1, explanation: "<code>local</code> は新しい変数を作るのではなく、既存のグローバル変数(パッケージ変数)の値を一時的に変更し、ブロックを抜けると元の値に戻します。レキシカル変数は <code>my</code> です。" },
{ question: "<code>package MyClass;</code> という宣言の後、宣言なしで使われる変数 <code>$var</code> の完全修飾名は?", options: ["$main::var", "$MyClass::var", "$var"], answer: 1, explanation: "<code>package</code> 宣言によりデフォルトの名前空間が切り替わるため、パッケージ変数はそのパッケージに属することになります(<code>strict vars</code> 下では <code>our</code> 等での宣言が必要)。" },

// --- 正規表現(中級) ---
{ question: "正規表現の修飾子 <code>/g</code> の意味は?", options: ["大文字小文字を無視する", "グローバルマッチ(すべての一致を対象にする)", "正規表現内で変数を展開する"], answer: 1, explanation: "<code>/g</code> (global) 修飾子をつけると、最初の一致だけでなく、文字列内のすべての一致箇所を対象に処理を行います。" },
{ question: "正規表現 <code>/(\d+)-(\d+)/</code> において、2番目の括弧でキャプチャされた内容が格納される変数は?", options: ["$1", "$2", "$&"], answer: 1, explanation: "括弧で囲まれた部分(キャプチャグループ)は、左から順に <code>$1</code>, <code>$2</code>, ... という特殊変数に格納されます。" },

// --- 関数とコンテキスト ---
{ question: "<code>map</code> 関数のブロック内で、現在処理中の要素を表す変数は?", options: ["$item", "$_", "$self"], answer: 1, explanation: "<code>map</code> や <code>grep</code> のブロック内では、リストの各要素がデフォルト変数 <code>$_</code> にエイリアスされます。" },
{ question: "<code>grep { ... } @list</code> をスカラコンテキストで評価すると何が返されますか?", options: ["条件に一致した要素のリスト", "条件に一致した要素の数", "最後の要素"], answer: 1, explanation: "<code>grep</code> はリストコンテキストでは一致した要素を返しますが、スカラコンテキストでは一致した「数」を返します。" },
{ question: "リスト <code>(1, 5, 10)</code> を数値として昇順にソートする正しい記述は?", options: ["sort { $a cmp $b } (1, 5, 10)", "sort { $a <=> $b } (1, 5, 10)", "sort (1, 5, 10)"], answer: 1, explanation: "<code>sort</code> のデフォルトは文字列比較です。数値比較を行うには比較ブロック <code>{ $a <=> $b }</code> (宇宙船演算子) を指定する必要があります。" },

// --- 特殊変数とその他 ---
{ question: "特殊変数 <code>$/</code> を変更することで何が制御できますか?", options: ["出力フィールド区切り文字", "入力レコード区切り文字(行の定義)", "配列のインデックスの開始番号"], answer: 1, explanation: "<code>$/</code> (input record separator) のデフォルトは改行ですが、これを変更することで、例えば一度にファイル全体を読み込む(undefにする)などの操作が可能になります。" },
{ question: "Perlがライブラリ(モジュール)を探すディレクトリのリストが格納されている配列は?", options: ["@INC", "%ENV", "@PATH"], answer: 0, explanation: "<code>@INC</code> 配列には、<code>use</code> や <code>require</code> でモジュールをロードする際に検索されるディレクトリパスが含まれています。" },
{ question: "<code>use strict;</code> 環境下で、変数名を文字列で指定してアクセスしようとすると発生するエラーは?", options: ["Symbolic references are disallowed", "Global symbol requires explicit package name", "Undefined subroutine"], answer: 0, explanation: "<code>strict 'refs'</code> は、文字列を変数名として使う「シンボリックリファレンス」を禁止します。推奨されるのはハードリファレンスです。" },
{ question: "ファイルテスト演算子 <code>-e</code> は何を判定しますか?", options: ["ファイルが実行可能か", "ファイルが存在するか", "ファイルが空でないか"], answer: 1, explanation: "<code>-e</code> (exists) はファイルやディレクトリが存在する場合に真を返します。実行可能かは <code>-x</code>、空でないかは <code>-s</code> です。" },
{ question: "<code>qw( foo bar baz )</code> は何と等価ですか?", options: ["('foo', 'bar', 'baz')", "\"foo bar baz\"", "{ foo => 'bar', baz => undef }"], answer: 0, explanation: "<code>qw</code> (quote word) 演算子は、空白で区切られた文字列をリストとして返します。カンマやクォートを書く手間が省けます。" },
{ question: "ハッシュ <code>%hash</code> の要素数(キーの数)を効率的に知る方法は?", options: ["length(%hash)", "scalar(keys %hash)", "count(%hash)"], answer: 1, explanation: "<code>keys %hash</code> でキーのリストを取得し、それを <code>scalar()</code> で評価する(あるいは単にスカラコンテキストに置く)ことで個数を取得できます。" },
{ question: "モジュール内で <code>1;</code> を最後に記述する主な理由は?", options: ["モジュールのバージョンを示すため", "初期化に成功したことを真(true)として返すため", "ファイルの終わりを示すため"], answer: 1, explanation: "<code>require</code> や <code>use</code> でモジュールを読み込む際、そのファイルが最後に真の値を返す必要があるため、慣例として <code>1;</code> を置きます。" }
]
};

// ===================================
// ③ 実行命令
// ===================================
document.addEventListener('DOMContentLoaded', () => {
initializeQuiz(quizConfig);
});

</script>

</body>
</html>


使用変数

$ref
a
applyTheme -------( Function )
b
baz
btn
button
charset
class
className
commentArray
content
currentQuestion
disabled
entShuffledOptions
explanationAreaEl
explanationHTML
explanationListEl
explanationTitleEl
feedbackAreaEl
feedbackTextEl
feedbackTitleEl
foo
handleAnswer -------( Function )
i
id
index
initializeQuiz -------( Function )
innerHTML
isCorrect
j
lang
mainTitleEl
name
newArray
nextButtonEl
nextQuestion -------( Function )
onsWithCorrectness
optionButtons
questionEl
questionNumberEl
quizAreaEl
quizConfig
quizData
randomComment
restartButtons
resultAreaEl
resultMessageEl
resultTitleEl
rrentQuestionIndex
scale
score
scoreEl
selectedIndex
selectedOption
selectedOptionText
showExplanations -------( Function )
showQuestion -------( Function )
showResult -------( Function )
shuffleArray -------( Function )
startQuiz -------( Function )
startTimer -------( Function )
textContent
timeLeft
timerAreaEl
timerEl
timerInterval
title
userAnswer
userAnswers
wExplanationButton
width