回答A: OpenAI GPT-5 mini
def resolve(requirements, package_index): """ 初期の要件とパッケージインデックスが与えられたときに、パッケージのバージョンを解決します。 requirements: "A>=1.2.0" や "B" のような要件文字列のリスト package_index: パッケージ -> { バージョン文字列: [依存関係の要件文字列] } の辞書 戻り値: パッケージ -> 解決されたバージョン文字列 をマッピングする辞書 競合が発生した場合は説明付きで ValueError を送出します。 """ import copy import re OPS = ["==", "!=", ">=", "<=", ">", "<"] def parse_version(v): # セマンティックバージョンを整数のタプルに解析します(major, minor, pat...
全文を表示 ▼
def resolve(requirements, package_index): """ 初期の要件とパッケージインデックスが与えられたときに、パッケージのバージョンを解決します。 requirements: "A>=1.2.0" や "B" のような要件文字列のリスト package_index: パッケージ -> { バージョン文字列: [依存関係の要件文字列] } の辞書 戻り値: パッケージ -> 解決されたバージョン文字列 をマッピングする辞書 競合が発生した場合は説明付きで ValueError を送出します。 """ import copy import re OPS = ["==", "!=", ">=", "<=", ">", "<"] def parse_version(v): # セマンティックバージョンを整数のタプルに解析します(major, minor, patch, ...) parts = tuple(int(p) for p in v.split('.')) return parts def version_cmp_key(v): return parse_version(v) def parse_requirement(req): req = req.strip() # 演算子が存在するか探す for op in OPS: if op in req: name, ver = req.split(op, 1) name = name.strip() ver = ver.strip() return name, op, ver # 演算子がない場合は任意のバージョンを意味します name = req return name.strip(), None, None def satisfies_one(ver_str, op, ver_req_str): if op is None: return True left = parse_version(ver_str) right = parse_version(ver_req_str) if op == '==': return left == right if op == '!=': return left != right if op == '>=': return left >= right if op == '<=': return left <= right if op == '>': return left > right if op == '<': return left < right return False def satisfies_all(ver_str, constraints): # constraints: op が None である可能性がある (op, ver_str) のリスト return all(satisfies_one(ver_str, op, vreq) for (op, vreq) in constraints) # 初期制約辞書を構築: パッケージ -> (op, ver) のリスト constraints = {} def add_constraint(constraints_map, pkg, op, ver): if pkg not in constraints_map: constraints_map[pkg] = [] # 同一の重複する制約を避ける if (op, ver) not in constraints_map[pkg]: constraints_map[pkg].append((op, ver)) # トップレベルの要件から初期化 for req in requirements: name, op, ver = parse_requirement(req) if name not in package_index: raise ValueError(f"パッケージ '{name}' が必要ですが、パッケージインデックスに見つかりません") add_constraint(constraints, name, op, ver) # あるパッケージの制約の下で許容されるバージョンの一覧を計算するヘルパー def allowed_versions(pkg, constraints_map): if pkg not in package_index: raise ValueError(f"パッケージ '{pkg}' がパッケージインデックスに見つかりません") cons = constraints_map.get(pkg, []) versions = list(package_index[pkg].keys()) # フィルタリング filtered = [v for v in versions if satisfies_all(v, cons)] # バージョンで降順にソート(最新を先頭) filtered.sort(key=version_cmp_key, reverse=True) return filtered # バックトラック探索 def search(resolved, constraints_map): # 制約のあるすべてのパッケージが解決済みなら、終了 to_resolve = [p for p in constraints_map.keys() if p not in resolved] if not to_resolve: return resolved # 分岐を最小化するため、候補数の最も少ないパッケージを選択 candidate_info = [] # (pkg, candidates) のリスト for pkg in to_resolve: try: cand = allowed_versions(pkg, constraints_map) except ValueError as e: # パッケージが存在しない場合 raise if not cand: # 競合: このパッケージの制約を満たすバージョンがない # 読みやすい制約文字列を構築 cons = constraints_map.get(pkg, []) cons_str = ", ".join([(op + v) if op else "any" for (op, v) in cons]) raise ValueError(f"パッケージ '{pkg}' のいかなるバージョンも次の制約を満たしません: {cons_str}") candidate_info.append((pkg, cand)) # 候補数で昇順にソート candidate_info.sort(key=lambda x: len(x[1])) pkg, candidates = candidate_info[0] # 各候補を試す(既に最新順) for ver in candidates: new_resolved = dict(resolved) new_resolved[pkg] = ver # 次のレベルのために制約をクローン new_constraints = copy.deepcopy(constraints_map) # pkg=ver を設定したとき、pkg に対する既存の制約が満たされていることを確認する必要がある if not satisfies_all(ver, new_constraints.get(pkg, [])): # 候補はフィルタ済みなので起こらないはずだが、念のためチェック continue # その依存関係を制約に追加 deps = package_index[pkg].get(ver, []) conflict = False conflict_msg = None for dep_req in deps: dep_name, dep_op, dep_ver = parse_requirement(dep_req) if dep_name not in package_index: conflict = True conflict_msg = f"'{pkg}=={ver}' は存在しないパッケージ '{dep_name}' を必要とします" break add_constraint(new_constraints, dep_name, dep_op, dep_ver) # もし dep が既に解決済みなら、一貫性を確認 if dep_name in new_resolved: assigned = new_resolved[dep_name] if not satisfies_all(assigned, new_constraints.get(dep_name, [])): conflict = True conflict_msg = (f"競合: {pkg}=={ver} は {dep_req} を必要としますが、既に解決された {dep_name}=={assigned} と互換性がありません") break if conflict: # 次の候補を試す continue # また、これらの制約を追加することで未解決の他のパッケージが候補ゼロになる可能性がある # それは再帰呼び出しで allowed_versions を計算する際に検出され、例外が発生します。 try: result = search(new_resolved, new_constraints) return result except ValueError as e: # 次の候補を試す last_err = e continue # すべての候補が失敗した場合は競合を送出します。情報的なメッセージを提供するようにする。 # pkg に対する制約の説明を作る cons = constraints_map.get(pkg, []) cons_str = ", ".join([(op + v) if op else "any" for (op, v) in cons]) raise ValueError(f"制約 {cons_str} の下でパッケージ '{pkg}' を解決できません") # 探索を開始 resolved = {} result = search(resolved, constraints) return result # Example usage (uncomment to run quick test): # if __name__ == '__main__': # index = { # "A": { # "1.0.0": ["B<2.0.0"], # "1.1.0": ["B<3.0.0"], # "1.2.0": ["C==1.0.0"] # }, # "B": { # "1.5.0": [], # "2.5.0": [] # }, # "C": { # "1.0.0": ["B>=2.0.0"], # "1.1.0": [] # } # } # print(resolve(["A>=1.1.0"], index))
判定
勝利票
2 / 3
平均スコア
総合点
総評
パッケージごとに制約を蓄積し、最新の満足するバージョンを選択し、最小ドメインヒューリスティックを使用して分岐を減らす、堅牢なバックトラッキング依存関係リゾルバを実装しています。推移的な依存関係、不足しているパッケージ、および多くの競合を、比較的明確なメッセージで処理します。主な弱点は、やや単純な要件解析(部分文字列検索による演算子検出は、珍しい名前を誤って解析する可能性がある)と、明示的なサイクル処理/メモ化がないこと(ただし、各パッケージを一度解決するため、それでも終了します)です。
採点詳細を表示 ▼
正確さ
重み 35%バックトラッキングを通じて推移的な依存関係を正しく解決し、パッケージごとにすべての制約を蓄積し、候補を降順にソートして最新バージョンを優先します。一般的に満たせない制約を検出します。軽微なリスク:単純な要件解析と明示的なプルーニング/メモ化の欠如。
完全性
重み 20%指定された演算子、推移的な依存関係、不足しているパッケージ、および空の要件({}を返す)をカバーしています。メモ化や明示的なサイクル検出などの高度な機能は実装していませんが、各パッケージを一度解決することで無限ループを回避しています。
コード品質
重み 20%ヘルパー関数と次のパッケージのヒューリスティックな選択により、比較的読みやすい構造になっています。一部不要なインポート(re未使用)とディープコピーの使用は重い可能性があります。エラーメッセージの構築には軽微な粗さがあります。
実用性
重み 15%進化する制約を処理し、以前のパッケージの選択をまたいでバックトラッキングを必要とするケースでソリューションを見つけることができるため、より実用的に使用できます。本番環境のリゾルバと比較すると、まだ制限があります(メモ化なし、単純な解析)。
指示遵守
重み 10%要求された関数シグネチャに一致し、ロックファイル辞書を返し、最新バージョンを優先し、比較的明確なメッセージで競合が発生した場合にValueErrorを発生させます。
総合点
総評
回答Aは、バックトラッキングアルゴリズムを使用した、正しく構造化されたソリューションを提供しています。要件の解析、バージョン比較の処理、最新バージョンの優先順位付けを正しく行っています。競合や不足しているパッケージのエラー処理も含まれています。しかし、各再帰呼び出しで`constraints_map`に`copy.deepcopy`を使用することは、複雑な依存関係グラフに対して著しいパフォーマンスのオーバーヘッドを引き起こす可能性があり、実用的なスケーラビリティを制限する可能性があります。また、`parse_requirement`関数は、正規表現ベースのアプローチよりも若干堅牢性に欠けます。
採点詳細を表示 ▼
正確さ
重み 35%依存関係の解決、バージョン比較の処理、競合の特定に関するコアロジックは正しいです。最新バージョンを正しく優先します。`parse_requirement`メソッドは機能的ですが、正規表現ベースのアプローチよりも堅牢性に欠けます。
完全性
重み 20%このソリューションは、推移的な依存関係、バージョン競合、不足しているパッケージを含む、指定されたすべての要件を処理します。解決不可能なシナリオに対して`ValueError`を正しく発生させます。`deepcopy`メカニズムは、正しいものの、パフォーマンスのために非常に大きく深い依存関係グラフに対する完全性を制限する可能性があります。
コード品質
重み 20%コードは一般的に、明確なヘルパー関数とコメントでよく構造化されています。しかし、状態管理のための`copy.deepcopy`の使用は、効率に影響を与える重要な設計上の選択です。`parse_requirement`はより洗練されている可能性があります。
実用性
重み 15%このソリューションは機能的な依存関係リゾルバを提供します。しかし、`deepcopy`のパフォーマンスへの影響は、効率が重要な実際のシナリオで、非常に大きかったり複雑だったりする依存関係ツリーに対して、実用性を低下させる可能性があります。
指示遵守
重み 10%関数シグネチャ、戻り値の型、エラー処理、さまざまな指定子の処理を含む、すべての指示に従っています。効率的なアルゴリズムを目指すという要件は満たされていますが、`deepcopy`は複雑なケースにおける最適な効率からのわずかな逸脱と見なされる可能性があります。
総合点
総評
回答Aは、優れた構造を持つ堅牢なバックトラッキング依存関係リゾルバを提供します。制約マップを維持することによる制約伝播を実装し、効率を向上させるための変数順序付けにMRV(最小残存値)ヒューリスティックを実装し、不足しているパッケージを処理し、有益なエラーメッセージを提供します。コードは制約マップにdeepcopyを使用しており、これは正しいですが、ややコストがかかります。解決済みのセットを通じて循環依存関係を暗黙的に処理します。コードはコメントによってよく文書化されており、関心の分離が明確です。1つの軽微な問題は、visitedセットで循環依存関係を明示的に処理しないことですが、解決済みの辞書により、パッケージが一度解決されると再解決されないため、実際には無限ループを防ぎます。
採点詳細を表示 ▼
正確さ
重み 35%回答Aは、指定された例(A>=1.1.0 -> A=1.2.0, C=1.0.0, B=2.5.0)を正しく解決し、バックトラッキングによって競合を処理します。制約蓄積アプローチにより、バージョンを選択する際にすべての制約がチェックされることが保証されます。解決不可能なケースや不足しているパッケージに対してValueErrorを正しく発生させます。
完全性
重み 20%回答Aは、不足しているパッケージ、バージョン競合、推移的依存関係、および空の入力を処理します。visitedセットで循環依存関係を明示的に処理しませんが、解決済みの辞書を通じて無限ループを回避します。指定されたすべての演算子をカバーしています。
コード品質
重み 20%回答Aは、明確に命名されたヘルパー関数により、関心の分離が明確です。制約マップのアプローチはエレガントであり、MRVヒューリスティックはアルゴリズムの洗練度を示しています。コメントは役立ちます。deepcopyの使用は正しいですが、最適化される可能性があります。コードは全体的に非常によく構造化されています。
実用性
重み 15%回答AのMRVヒューリスティックと制約伝播アプローチは、多くのパッケージを持つ実世界の依存関係グラフでより優れたパフォーマンスを発揮するでしょう。制約蓄積モデルは、実際の依存関係リゾルバがどのように機能するかにより近いです。deepcopyのオーバーヘッドは、非常に大きな依存関係ツリーの場合、懸念事項となる可能性があります。
指示遵守
重み 10%回答Aは、すべての指示に従っています:パッケージ名をバージョンにマッピングする辞書を返します、明確なメッセージとともにValueErrorを発生させます、指定されたすべての演算子を処理します、最新バージョンを目指します(降順にソート)、推移的依存関係を処理します。