Qiita Engineer Festa 2024(キータ・エンジニア・フェスタ 2024) - Qiita
において、約1ヶ月で38記事という大量の記事の投稿を要求されることがわかった。
そこで、あまりコストをかけずに記事数を稼ぐ方法を考えた結果、「Welcome to AtCoder を様々な言語で解く」ことを思いついた。
単に解くだけでなく、使用する言語仕様の解説を入れれば、記事として一応成立するだろう。
Welcome to AtCoder
PracticeA - Welcome to AtCoder
Welcome to AtCoder では、以下の形式で整数 $a$, $b$, $c$ および文字列 $s$ が入力として与えられる。
a
b c
s
この入力をもとに、与えられた整数の和 $sum = a + b + c$ および文字列 $s$ を、以下の形式で出力することが求められる。
sum s
今回用いた OCaml の機能
定数と関数の定義
Expressions and Definitions
Functions
Recursive Functions
let 名前 = 値;;
のように書くことで、定数を定義できる。
それぞれの定義の後に ;;
をつける。
let
が連続する場合など、;;
をつけなくてもいい場合もあるようである。
しかし、後述の出力を行う場合など、;;
をつけないとエラーになる場合もあるようである。
;;
はつけておくのが安全だろう。
let 名前 = 値 in 式
のように書くことで、「式」の中でしか使わない定数を定義できる。
この let … in …
全体も式である。
let 関数名 引数名1 引数名2 … = 式;;
のように書くことで、(再帰しない) 関数を定義できる。
let rec 関数名 引数名1 引数名2 … = 式;;
のように書くことで、再帰関数を定義できる。
すなわち、再帰関数を定義する場合は、通常の let
に続いて空白と rec
を書く。
パターンマッチ (リストの要素の取り出し)
match リスト with
| [] -> リストが空の場合の式
| x :: xs -> リストが空でない場合の式
と書くと、リストが空のときと空でないときで処理を分け、さらにリストの最初の要素を取り出すことができる。
「リストが空でない場合の式」において、x
が「リスト」の最初の要素、xs
が「リスト」から最初の要素を取り除いたリストである。
整数の処理
a + b
により、a
と b
の和を求めることができる。
a - b
により、a
から b
を引いた差を求めることができる。
a * b
により、a
と b
の積を求めることができる。
Int.to_string 値
により、「値」を十進数で表した文字列を得ることができる。
文字列の処理
a ^ b
により、文字列 a
と b
を連結した文字列を得ることができる。
String.make 個数 文字
により、「文字」を「個数」回繰り返した文字列を得ることができる。
String.get 文字列 位置
により、「文字列」の「位置」番目 (0-origin) の文字を得ることができる。
String.get_uint8 文字列 位置
により、「文字列」の「位置」番目のバイトを整数 (int
) で得ることができる。
String.split_on_char 文字 文字列
により、「文字列」を「文字」で区切った文字列のリストを得ることができる。
String.fold_left 関数 初期値 文字列
により、「初期値」から「文字列」のそれぞれの文字について左から順に「関数」で処理した結果を得ることができる。
「関数」は、「今の値」と「処理する文字」を引数にとる。
すなわち、以下のような処理を行う。
function fold_left(func, initial_value, str) {
let value = initial_value;
for (let i = 0; i < str.length; i++) {
value = func(value, str.charAt(i));
}
return value;
}
入出力
Side-Effects and the unit Type
read_line ()
により、標準入力から1行読み込み、結果を文字列として得ることができる。
得られる文字列には、末尾に改行文字は含まれない。
これは、read_line
関数に引数 ()
(unit) を渡している。
print_endline 文字列;;
により、標準出力に「文字列」 (String
) を出力し、さらに改行を出力できる。
提出コード
let str_to_int_step value ch =
let ch_str = String.make 1 ch
in value * 10 + (String.get_uint8 ch_str 0) - 48
;;
let str_to_int s =
String.fold_left str_to_int_step 0 s
;;
let rec sum_list l s =
match l with
| [] -> s
| x :: xs -> sum_list xs (s + (str_to_int x))
;;
let line1 = read_line ();;
let line2 = read_line ();;
let line3 = read_line ();;
let bc_str = String.split_on_char (String.get " " 0) line2;;
let a = str_to_int line1;;
let sum = sum_list bc_str a;;
let result_str = (Int.to_string sum) ^ " " ^ line3;;
print_endline result_str;;
提出 #54993981 - AtCoder Beginners Selection
提出コードの解説
let str_to_int_step value ch =
let ch_str = String.make 1 ch
in value * 10 + (String.get_uint8 ch_str 0) - 48
;;
文字列の数値への変換を一桁進める。
文字を String.make
で文字列に変換し、さらに String.get_uint8
で文字コードに変換する。
この桁の数値は、変換結果から 0
の文字コードを引いた値である。
1個上の桁までの変換結果に (十進数なので) 10 を掛け、さらにこの桁の数値を足すことで変換を進める。
let str_to_int s =
String.fold_left str_to_int_step 0 s
;;
String.fold_left
を用い、各桁の変換を行うことで、文字列をそれが表す数値に変換する。
let rec sum_list l s =
match l with
| [] -> s
| x :: xs -> sum_list xs (s + (str_to_int x))
;;
数値を表す文字列のリストについて、それらを数値に変換しながら、和を求める。
let line1 = read_line ();;
let line2 = read_line ();;
let line3 = read_line ();;
入力の各行を読み込む。
let bc_str = String.split_on_char (String.get " " 0) line2;;
2行目を空白で分割する。
String.get
を用いて文字を用意している。
let a = str_to_int line1;;
let sum = sum_list bc_str a;;
1行目の文字列を数値に変換し、それに2行目の文字列の各部分が表す数値を加算して和を求める。
let result_str = (Int.to_string sum) ^ " " ^ line3;;
print_endline result_str;;
求めた和・空白・3行目を連結し、出力する。