3
2

【Jupyter Notebook】Atcoderの標準入力を楽に受け取る環境作り

Last updated at Posted at 2023-07-30

Jupyter Notebookで競プロの入力例を受け取る、快適な環境作りを紹介します。

記事でやること

  • 競プロしやすいファイル構成を作る
  • 標準入力セットをスニペットに登録
  • Atcoderから入力例を自動で読み込む

環境

  • OS : macOS
  • エディター : Visual Studio Code
  • 言語 : Python

1. 競プロしやすいファイル構成を作る

まずは、以下のようにフォルダ内に2つのファイルを作成します。

atcoder
	|- main.ipynb
	|- input.txt

input.txtファイルには、入力例を「2つの改行で区切る」形で書き込みます。また、「1つの改行」がある場合、それはある入力例の続きとみなします。自分で作成した入力例も、ここに書き込むことができます。なんかWAになるとき、簡単にコードテストできるので良き。

input.txt
入力例1(1行目)
入力例1(2行目)

入力例2(1行目)
入力例2(2行目)

入力例3(1行目)
入力例3(2行目)

:

2. 標準入力セットをスニペットに登録

次に、標準入力を受け取るためのスニペットを登録します。スニペットを使うと、入力例が受けとる呪文を毎回書く必要がなくなります。登録の仕方は以下ような感じです。

  1. vscodeでcmd+shift+p。コマンドパレットを開く。
  2. コマンドパレットにUser snippetを入力
  3. 言語はpython.jsonを選択
  4. 開いたpython.jsonファイルに以下を追加
python.json
{
    "cp load input": {
        "prefix": "cp load input",
        "body": [
            "input_case = 1",
            "with open('./input.txt') as file:",
            "    input_lines = file.read().split('\\n\\n')[input_case - 1].split('\\n')",
            "def input(): return input_lines.pop(0)"
        ]
    },
    "cp int": {
        "prefix": "cp int",
        "body": ["int(input())"]
    },
    "cp map": {
        "prefix": "cp map",
        "body": ["map(int, input().split())"]
    },
    "cp s list": {
        "prefix": "cp s list",
        "body": ["input().split()"]
    },
    "cp n list": {
        "prefix": "cp n list",
        "body": ["list(map(int, input().split()))"]
    },
    "cp 2d n list": {
        "prefix": "cp 2d n list",
        "body": ["[list(map(int, input().split())) for i in range(n)]"]
    },
    "cp 2d s list": {
        "prefix": "cp 2d s list",
        "body": ["[list(input()) for i in range(n)]"]
    },
}

prefixは、入力サジェストされるワードの指定です。
より正確なルールや言葉に従って命名したい人は、設定を変更してみて下さい。

登録したスニペットを使ってみる

例として、ABCコンテスト312のC問題の入力例を受け取ってみましょう。
まずはinput.txtに、入力例を貼り付けます。(後にこれも自動化する方法を紹介します)

input.txt
3 4
110 90 120
100 80 120 10000

5 2
100000 100000 100000 100000 100000
100 200

3 2
100 100 100
80 120

次にmain.ipynbで、先ほど登録したスニペットを利用して、入力例を受け取りましょう。

入力例を受け取るときは、最初にcp load inputスニペットを使用します。これで、input.txtの内容を読み込むコードが記述されます。提出時は、この記述を削除してください。

main.ipynb
# cp load inputを使用
input_case = 1
with open('./input.txt') as file:
    input_lines = file.read().split('\n\n')[input_case - 1].split('\n')
def input(): return input_lines.pop(0)
# cp mapを使用
n, m = map(int, input().split())
# cp listを使用
a = list(map(int, input().split()))
b = list(map(int, input().split()))

input_caseの数字を変更することで、何番目の入力例を受け取るかを指定できます。

3. Python+Automatorで入力例を自動取得

Atcoderから入力例をポチポチコピーするの面倒ですよね。
それをAutomatorを使って自動化してみましょう。
自動化すると、Atocderの問題URLから自動でinput.txtに入力例を書き込めます。

Atcoderから入力例を取得するPythonコードを作成

まずは、input_reader.pyという名前のPythonファイルを作成します。保存場所はどこでも大丈夫ですが、私の場合は/Users/username/python/app/automator/input_reader.pyに保存しました。

次に、このPythonファイルに以下のコードを書き込みます。

コード内のfile_path変数にinput.txtのフルパスを設定して下さい。これは、スクレイピングした文字列をinput.txtに書き込むのに必要です。

input_reader.py
import sys
import requests
from bs4 import BeautifulSoup


def get_problem_examples(url):
    try:
        r = requests.get(url)
        r.raise_for_status()  # HTTPエラーがあればここで例外を引き起こすよ
    except requests.exceptions.RequestException as err:
        print(f"リクエストに問題があったみたいだよ💁‍♀️: {err}")
        sys.exit(1)

    soup = BeautifulSoup(r.text, "html.parser")
    data = soup.find_all("div", class_="part")
    input_data = []
    output_data = []

    for d in data:
        h3 = d.find("h3")
        if h3:
            pre = d.find("pre")
            if h3.string.startswith("入力例"):
                input_data.append(pre.text)
            if h3.string.startswith("出力例"):
                output_data.append(pre.text)

    return input_data, output_data


def write_to_file(input_data, output_data, file_path):
    if input_data and output_data:
        input_data = "\n".join(input_data)
        output_data = "\n".join(output_data)
        res = input_data + "\n--------Answer--------\n" + output_data
        with open(file_path, mode="w") as f:
            f.write(res)


def main():
    url = sys.argv[1]
    # ---- ここ大事! input.txtのフルパスを設定 ----
    file_path = "~/input.txt"
    # ----------------------------------------
    if url.startswith("https://atcoder.jp/contests/"):
        input_data, output_data = get_problem_examples(url)
        write_to_file(input_data, output_data, file_path)


if __name__ == "__main__":
    main()

BeautifulSoupも使うので、pipでインストールしておきましょう。

pip install beautifulsoup4

ショートカットキーで、Automator->Python実行の設定をする

次に、Automatorを使ってPythonスクリプトを実行する設定を行います。
これにより、ショートカットキーを押すだけで先ほど作成したinput_reader.pyが実行されます。

まず、Automatorを起動して、cmd + nで新規から「クイックアクション」を選択し作成します。それから、ワークフローから「シェルスクリプトを実行」を追加し、以下のスクリプトを入力します。

# クリップボードの内容を取得
clip_content=$(pbpaste)

# input_reader.pyを実行
/opt/homebrew/Caskroom/miniforge/base/envs/python3_8/bin/python /Users/username/python/app/automator/input_reader.py "$clip_content"

ただし、# input_reader.pyを実行と書かれているところは、ユーザー環境に合わせて記述します。

{pythonコマンドのフルパス} {input_reader.pyのフルパス} "$clip_content"

Pythonコマンドのフルパスはターミナルでwhich pythonなどと実行し、取得してください。

そしたら 「ワークフローを受け取る項目」を「入力なし」に設定し、保存して完了です。
s3.png

最後にショートカットキーの設定をします。Macの「システム設定」から「キーボード->キーボードショートカット」と進み、さらに「サービス->一般」からinput_readerをダブルクリックし、ショートカットを設定します。私はcmd + shift + ctrl + rと設定しました。
s5.png

これでおしまいです。

実際に使ってみる

  1. Atcoderの問題ページに行き、URLをcmd + cなどでコピーする
  2. 先ほど作成したショートカットcmd + shift + ctrl + rを実行する

こうすることで、クリップボードにコピーされたURLを入力として、input_reader.pyが実行されます。結果、input.txtに問題の入力例が出力されます。

最後に

最初はちょっと面倒かもしれませんが、一度設定すれば結構快適です。
もっと良い方法や、ここがわからない、これがうまくいかないということがあれば、ぜひコメントください。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2