import re import html def markdown_to_html(markdown_text: str) -> str: """MarkdownのサブセットをHTMLに変換します。 サポートされている機能: - ヘッダー(# .. ######) - 2スペースネストの順序なしリスト - フェンスされたコードブロック(```) - 段落 - インライン:***太字+イタリック***、**太字**、*イタリック* """ def escape(text: str) -> str: # HTMLに敏感な文字をエスケープします。 return html.escape(text, quote=False) def process_inline(text: str) -> str: """インラインマークダウンを処理します。エスケープ済みのテキストでも機能します。...
全文を表示 ▼
import re import html def markdown_to_html(markdown_text: str) -> str: """MarkdownのサブセットをHTMLに変換します。 サポートされている機能: - ヘッダー(# .. ######) - 2スペースネストの順序なしリスト - フェンスされたコードブロック(```) - 段落 - インライン:***太字+イタリック***、**太字**、*イタリック* """ def escape(text: str) -> str: # HTMLに敏感な文字をエスケープします。 return html.escape(text, quote=False) def process_inline(text: str) -> str: """インラインマークダウンを処理します。エスケープ済みのテキストでも機能します。 マーカー*, **, *** はエスケープされた文字を含まないためです。 """ escaped = escape(text) # 優先順位:***、次に**、次に*。 # 非欲張りマッチを使用し、非空白境界を緩やかに要求します。 # ***text*** escaped = re.sub( r'\*\*\*(.+?)\*\*\*', r'<strong><em>\1</em></strong>', escaped, ) # **text** escaped = re.sub( r'\*\*(.+?)\*\*', r'<strong>\1</strong>', escaped, ) # *text* escaped = re.sub( r'\*(.+?)\*', r'<em>\1</em>', escaped, ) return escaped lines = markdown_text.split('\n') output = [] # 状態トラッカー in_code_block = False code_buffer = [] paragraph_buffer = [] # リストスタック:現在開いているインデントレベル(0、1、2、...)のリスト list_stack = [] def flush_paragraph(): if paragraph_buffer: content = ' '.join(paragraph_buffer) output.append(f'<p>{process_inline(content)}</p>') paragraph_buffer.clear() def close_lists_to(level): """`level`個のリストのみが開いた状態になるまでリストを閉じます(level = 開いている<ul>の数)。""" while len(list_stack) > level: output.append('</li>') output.append('</ul>') list_stack.pop() if list_stack: # ネストされたリストを閉じた後、親<li>は開いたままです。 # 次の兄弟が開始するか、完全に閉じる際に閉じます。 pass def close_all_lists(): while list_stack: output.append('</li>') output.append('</ul>') list_stack.pop() i = 0 while i < len(lines): line = lines[i] # コードブロックの処理 if in_code_block: if line.strip().startswith('```'): # コードブロックの終了 code_content = '\n'.join(code_buffer) output.append(f'<pre><code>{escape(code_content)}</code></pre>') code_buffer = [] in_code_block = False else: code_buffer.append(line) i += 1 continue # コードブロックの開始をチェック stripped = line.strip() if stripped.startswith('```'): flush_paragraph() close_all_lists() in_code_block = True i += 1 continue # 空行 if stripped == '': flush_paragraph() close_all_lists() i += 1 continue # ヘッダー? header_match = re.match(r'^(#{1,6}) +(.*)$', line) if header_match: flush_paragraph() close_all_lists() level = len(header_match.group(1)) content = header_match.group(2).rstrip() output.append(f'<h{level}>{process_inline(content)}</h{level}>') i += 1 continue # リスト項目? # 先頭のスペース(ネストには偶数である必要がある)と「- 」に一致 list_match = re.match(r'^( *)- +(.*)$', line) if list_match: flush_paragraph() indent_spaces = len(list_match.group(1)) # インデントが2の倍数である場合にのみ有効なネストレベルとして扱う indent_level = indent_spaces // 2 content = list_match.group(2) # リストスタックを調整 if not list_stack: # 新規開始:最初のリストはレベル0である必要がある indent_level = 0 output.append('<ul>') list_stack.append(0) output.append(f'<li>{process_inline(content)}') else: current_depth = len(list_stack) - 1 # トップインデックス if indent_level > current_depth: # 新しいネストされたリストを開く(一度に1レベル深くするだけ) indent_level = current_depth + 1 output.append('<ul>') list_stack.append(indent_level) output.append(f'<li>{process_inline(content)}') elif indent_level == current_depth: # 同じレベルの兄弟:前の<li>を閉じる output.append('</li>') output.append(f'<li>{process_inline(content)}') else: # デデント:深いリストを閉じる while len(list_stack) - 1 > indent_level: output.append('</li>') output.append('</ul>') list_stack.pop() # このネストされたリストが含まれていた親<li>も閉じる output.append('</li>') # 現在同じレベル:兄弟を追加 output.append(f'<li>{process_inline(content)}') i += 1 continue # それ以外の場合は、段落行です if list_stack: close_all_lists() paragraph_buffer.append(line.strip()) i += 1 # 入力の終了:すべてフラッシュ if in_code_block: # 未クローズのコードブロック:持っているものをすべて発行 code_content = '\n'.join(code_buffer) output.append(f'<pre><code>{escape(code_content)}</code></pre>') flush_paragraph() close_all_lists() return '\n'.join(output) if __name__ == '__main__': sample = '''# Header 1 This is a paragraph with **bold** and *italic* text. This is the same paragraph. - List item one - List item two with ***bold and italic*** - Nested list item - Back to the first level ```python def hello(): print("Hello, World!") ``` ''' print(markdown_to_html(sample))
判定
勝利票
0 / 3
平均スコア
総合点
総評
回答Aは、必要な機能をすべて実装しており、構造化され、コメントも明確です。インラインパーサーは、優先順位に従って正規表現の置換を使用しており、ほとんどのケースで機能しますが、微妙な欠点があります。HTML文字をインラインパターンに適用する前にエスケープしないため、エスケープが最初に適用されるとパターンに干渉する可能性があります。より重要なのは、リストの終了ロジックにデデント時のバグがあることです。同じレベルで再オープンする前に、親アイテムに対して余分な`</li>`を出力するため、不正なHTMLが生成される可能性があります。コードブロックの検出は、必要以上に緩い`line.strip().startswith('```')`を使用しています。全体として、リスト処理におけるエッジケースの問題はあるものの、堅実な試みです。
採点詳細を表示 ▼
正確さ
重み 35%回答Aは、ヘッダー、段落、コードブロック、および基本的なインラインフォーマットを正しく処理します。しかし、リストのデデントロジックにはバグがあります。ネストされたリストを閉じるときに、親アイテムに対して余分な`</li>`を出力するため、不正なHTMLが生成される可能性があります。インライン正規表現アプローチは、`*italic`のような閉じられていないマーカーに対して、テキスト内の後続の`*`と意図しない境界で一致する可能性もあります。
完全性
重み 20%必要な機能はすべて実装されています:ヘッダー(h1-h6)、ネストされた順序なしリスト、言語指定子を無視したコードブロック、段落、および3つのインラインスタイル(太字+斜体、太字、斜体)すべて。タスクのメインサンプルが処理されます。
コード品質
重み 20%コードは、明確なヘルパー関数と良好なコメントで整理されています。ドキュメンテーション文字列は役立ちます。しかし、リスト管理ロジックはやや複雑で、微妙なバグがあります。インライン解析のための正規表現アプローチはよりシンプルですが、堅牢性に欠けます。`close_lists_to`関数は定義されていますが、メインループでは使用されていません(`close_all_lists`のみが使用されています)。
実用性
重み 15%このソリューションは一般的なケースで機能し、サンプル入力を含む実行可能な`__main__`ブロックが含まれており、テストに役立ちます。しかし、リストのネストのバグと潜在的なインライン正規表現の問題により、エッジケースでの実用的な信頼性が低下します。
指示遵守
重み 10%指定されたすべての指示に従っています:正しい関数シグネチャ、必要なすべての機能のサポート、指定されたインライン優先順位の処理、サポートされていない機能の実装なし、および閉じられていないタグの処理(ただし、正規表現では不完全)。HTMLエスケープが適用されています。
総合点
総評
単純なステートマシンと正規表現ベースのインライン置換により、必須のブロックタイプとインラインフォーマットを実装し、コードブロック内のHTMLを含むHTMLを正しくエスケープしています。ただし、リスト生成には複数の構造的な問題があります。無効なネストにつながる可能性のある</li>および</ul>シーケンスを出力し、デデント(余分な</li>)を誤って処理し、親<li>内にないネストされた<ul>を開く可能性があります。正規表現によるインライン処理は、トリッキー/不正なケース(例:**太字 *イタリック***のような重複マーカー)に対して堅牢ではなく、インライン要素の再帰的なネストも要求/期待されるようにはサポートしていません。
採点詳細を表示 ▼
正確さ
重み 35%基本的なヘッダー/段落/コードブロックと単純な強調表示については正しいですが、リストのネスト/終了は頻繁に無効です(余分な</li>、ネストされた<ul>の配置の問題)。また、正規表現による強調表示は、重複/ネストされたケース(例:**太字 *イタリック***)を誤って処理する可能性があります。
完全性
重み 20%要求されたすべての機能カテゴリをカバーしていますが、リストのネストサポートは信頼性が低く、インラインのネストは単純なパターンを超えて実際にはサポートされていないため、実質的な完全性が低下しています。
コード品質
重み 20%可読性とモジュール性は高いですが、リストスタックのロジックは未使用/効果のないヘルパー(close_lists_to)と壊れやすい状態遷移により複雑化しており、正規表現の多用は明確さと堅牢性を低下させています。
実用性
重み 15%単純な入力には役立ちますが、無効なリストHTMLと強調表示のエッジケースは、指定されたサブセットの実際のユースケースを制限します。
指示遵守
重み 10%要求されたサブセットを実装し、コードブロックの言語指定子を無視していますが、トリッキーなインライン入力に対する堅牢性の要件と、正しいリストのネスト/終了は十分に満たされていません。
総合点
総評
回答Aは、ヘッダー、段落、コードブロックなどのほとんどのブロックレベル要素を正しく処理する、よく構造化されたステートマシンベースのソリューションを提供します。しかし、シーケンシャルな正規表現置換を使用したインライン要素の解析アプローチは根本的に欠陥があります。この方法は、ネストされたインラインスタイル(例:イタリックを含む太字)を正しく処理できず、これは重大な正しさの問題です。リスト処理ロジックも、代替案と比較して複雑で堅牢性に欠けます。
採点詳細を表示 ▼
正確さ
重み 35%ソリューションは、ネストされたインラインスタイルを正しく解析できません。たとえば、`**太字 *イタリック* 太字**` は、シーケンシャルな `re.sub` 呼び出しが新しいHTMLタグが含まれる中間文字列に対して操作されるため、誤ってレンダリングされます。これは、解析ロジックにおける重大な正しさの欠陥です。
完全性
重み 20%ソリューションは、要求されたすべての機能を実装しようとしています。しかし、ネストされたインラインスタイルの実装が不正確であるため、機能的に完全に完了しているとは見なせません。
コード品質
重み 20%コードは一般的にヘルパー関数や状態変数でよく構造化されています。しかし、シーケンシャルな正規表現置換を解析に選択したのは、この問題に対するアルゴリズム的な選択として不適切であり、その落とし穴に対する理解不足を示しています。リスト処理ロジックもかなり複雑です。
実用性
重み 15%パーサーが一般的なユースケース(ネストされたインラインスタイル)を正しく処理できないことは、その実用的な価値を著しく制限します。実際のMarkdownドキュメントの変換には信頼性がありません。
指示遵守
重み 10%ソリューションは、関数のシグネチャ、サポートされるMarkdownの特定のサブセット、および出力形式を含む、プロンプトのすべての指示に従っています。