はじめに
JavaScript で AtCoder の問題を解く方法を教える機会があり Notion にまとめたので、どうせなら公開しようと思って記事を書いています。
※ JavaScript の関数やメソッドを学んだくらいの方を想定しています。
目次
1.AtCoderとは
2.標準入出力とは
3.基本の型
4.受け取った値
5.とりあえずこれだけは覚える
6.値の扱い方
7.全体のコード
8.さいごに
1. AtCoderとは
AtCoder は、競技プログラミングコンテストを開催している国内サイトです。
詳しくは↓のサイトで詳しく説明されているので、こちらでは割愛します。
2. 標準入出力とは
AtCoder では、何らかの入力値を受け取って、それを問題の指定通りに計算等して、答えとして出力します。
その答えが正しければ正解です。
C問題以降になると、出力した値が正しくても、プログラム的に計算処理等の速度が遅いとタイムアウトの判定で不合格となるため、処理の効率も意識する必要が出てきます。
標準入出力の操作が必須となるため、そもそも「標準入出力とは?」が分からない方は以下サイトをご参照ください。
3. 基本の型
function Main(input) {
// input(入力値)を使って何らかの処理をする
// 答えの出力
console.log(〇〇);
}
// 標準入力を引数として、 ↑ の関数を実行
Main(require("fs").readFileSync("/dev/stdin", "utf8"));
まず、全体の流れとして、標準入力を受け取って、答えを出力をする必要がある。
JavaScript で標準入力を受け取るためには、fsモジュール を利用する。
fs モジュールとは
Node.js に組み込まれているモジュール。
※ Node.js とは
本来ブラウザ上で動作する JavaScript の、PCでの実行環境。
※ モジュールとは
プログラムの一部として独立した機能を持つコードの集まり。
コンピューター上のファイルやディレクトリを操作するための機能を提供する。
主な機能:
- ファイルの読み込み
- ファイルへの書き込み
- ファイルの作成や削除
- ディレクトリの作成や削除
- ファイル名の変更
この中の、require("fs")
が該当部分。
Main(require("fs").readFileSync("/dev/stdin", "utf8"));
require
で、fs モジュールを読み込むための命令をしています。
続けて、読み込んだ fs モジュールの中の readFileSync
メソッドを実行しています。
これは、引数で指定したファイルを読み込むメソッドです。
readFileSync
メソッドには引数を2つ渡している。
-
"/dev/stdin"
:
ファイルとして標準入力を指定している -
"utf8”
:
UTF-8という文字コードを指定している
(UTF-8:一般的な文字コードで、ほとんどのテキストデータで使用されている)
→つまり、この部分では
Main(require("fs").readFileSync("/dev/stdin", "utf8"));
Main 関数の引数として、「(一般的な文字コードを指定した)標準入力」を受け取るよということになります。
では、その Main 関数って何なんだ?ということですが、それをこれから定義します。
Main 関数が受け取るパラメーターを仮に input
としましょう。
function Main(input) {
}
つまり、この Main 関数を
Main(require("fs").readFileSync("/dev/stdin", "utf8"));
として、require("fs").readFileSync("/dev/stdin", "utf8")
を引数に渡して実行すると、input
には(一般的な文字コードを指定した)標準入力が渡されるということです。
4. 受け取った値
実際の問題では、例えば以下の問題なら
入力は以下の形式で与えられる。
a b c s
とあるので、
require("fs").readFileSync("/dev/stdin", "utf8")
(= 標準入力)には、この入力値が入ってくることになります。
ただし、この場合は改行が入ってるので、↑の改行部分は改行コード /nに置き換えられるため、実際は以下のような値にます。
"a\nb c\ns"
また、標準入力として受け取った値は、文字列になるので注意が必要です。
改行コードとは
参考:JavaScriptでの改行の使い方。改行ができないときは?
つまり、
a(改行)
b c(改行)
s(改行)
↓
"a\nb c\ns"
として受け取ります。
受け取った値をそのまま console.log すると
function Main(input) {
console.log(input);
}
改行コード \n は改行としての役割をするので
a
b c
s
として出力されます。
5. とりあえずこれだけは覚える
ここまでが、Main 関数の基本でした。
「問題を解く」というのは、この input
に入ってくる値( string
型であることに注意!!)を使って、指定の通りに計算したり配列の分割をしたりして、最後に答えとなる値を console.log
で出力するということです。
理解するまで少し難しいと思うので、最初はこれ↓をコピペして、
function Main(input) {
// ここに処理を書く
console.log();
}
Main(require("fs").readFileSync("/dev/stdin", "utf8"));
// ここに処理を書く
の部分に処理を、console.log()
の引数に答えを書けばいいと思えば OK です!
6. 値の扱い方
↑ の問題文
整数 𝑎,𝑏,𝑐 と、文字列 s が与えられます。
a + b + c の計算結果と、文字列 s を並べて表示しなさい。
入力
入力は以下の形式で与えられる。a b c s
出力
a+b+c と s を空白区切りで 1 行に出力せよ。
入力例 1(実際に渡される値)
1 2 3 test
出力例 1
6 test
1+2+3 は 6 なので、上記のように出力します。
標準入力として受け取ると、
1
2 3
test
は前述のとおり "1\n2 3\ntest"
という一つの文字列になりますが、一つの文字列ではなく、それぞれ 1, 2, 3, test という別の値として使いたいですよね。
そのために、まずは split
メソッドを使って、 \n
の部分で本来の入力の1行毎に文字列を分割して、配列に格納しましょう。
↓ では、split
した値を input
変数に再代入しています。
input = input.split("\n");
そうすると、input
はどうなっているかというと、
function Main(input) {
console.log(input); // "1\n2 3\ntest"
input = input.split("\n");
console.log(input); // 出力:["1", "2 3", "test"]
}
というように、元々の入力の一行ずつ、配列の要素に格納されているのが分かります。
さらに "b c"
の部分も分割したいので、今度は空白で分割しましょう。
let line2 = input[1].split(" "); // input[1]は "2 3"
console.log(line2) // 出力:["2", "3"]
※ 2行目なので line2 という変数名にしてみましたが、変数名はなんでもOKです。
これで、a, b, c, s をそれぞれ別の値として使える!と思いきや、もうひと工夫必要です。
問題文では、s は文字列だけど、a, b, c は整数だと言っています。
前述のとおり、標準入力で受け取ると勝手に文字列に変わってしまうので、整数に変換してあげないといけません。
function Main(input) {
console.log(input); // 出力:"1\n2 3\ntest"
input = input.split("\n");
console.log(input); // 出力:["1", "2 3", "test"]
let line2 = input[1].split(" ");
console.log(line2) // 出力:["2", "3"]
// 文字列から 10 進数に変換
let a = parseInt(input[0], 10);
console.log(a) // 出力:input[0]は "1" → 1(整数)
let b = parseInt(line2[0], 10);
console.log(b) // 出力:line2[0] は "2" → 2(整数)
let c = parseInt(line2[1], 10);
console.log(c) // 出力:line2[1] は "3" → 3(整数)
let s = input[2];
console.log(s) // 出力:"test"(文字列)
}
そして、求められている答えは
a + b + c の計算結果と、文字列 s を並べて表示しなさい。
なので、
console.log(a+b+c, s); // 出力: 6 test
これで答えが出せました!!
7. 全体のコード
提出する時は、途中の値を確認するために書いた console.log
は消さないといけません。
(コメントアウトでも大丈夫です)
function Main(input) {
input = input.split("\n");
let line2 = input[1].split(" ");
let a = parseInt(input[0], 10);
let b = parseInt(line2[0], 10);
let c = parseInt(line2[1], 10);
let s = input[2];
console.log(a+b+c, s);
}
Main(require("fs").readFileSync("/dev/stdin", "utf8"));
8. さいごに
いかがだったでしょうか?
Main 関数の引数の標準入力の部分が少し難しかったかもしれませんが、そこはいわゆる"おまじない"としてコピペして使っていいと思います。
とはいえ、全く意味を知らないで使うのではなく、何をやっているのか知ろうとすることは大切だと思ったので、調べながらまとめてみました。
JavaScript で AtCoder デビューをする助けになれば嬉しいです。