0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

CrystalでAtCoder(Log #1)

Last updated at Posted at 2021-08-02

序文

Crystalについては資料が少ないので、学習記録を残してみます。
リファレンス的なものについては、公式サイトを参照して下さい。
バージョンは0.33.0です。
最初の題材は、AtCoder ProblemsBoot camp for Beginnersです。
Apple Siliconにインストールした際の記録は別記事にしました。immatureなので限定公開記事にしています。

Easy_1

求める答えは、$⌈(B-1) / (A-1)⌉$。変な括弧は切り上げ。

メソッドの定義

$⌈x/y⌉$を返すdiv_ceil(x, y)関数を定義する。
x, y ↦ ⌈x/y⌉であるので関数で間違いはないのだが、Crystalでは値を返す返さないに関わらず、全てメソッドと呼ぶ。
div_ceil(x, y)はプログラムそれ自体というグローバルオブジェクト1に関連付けられたメソッド。
Crystalではすべてがオブジェクトとメソッドで出来ている。

div_ceil.cr
def div_ceil(x, y)
  (x+y-1) // y
end

メソッドの定義はdefendで行う。
ここではx, yという引数を受け取って計算し、返り値(オブジェクト)を返している。
引数や返り値の型を明示する必要はない。
整数を与えれば、デフォルトでは符号付き32ビット整数として扱われる。
pythonと違って、インデントに入れ子構造を示す役割はない。
値を返すのにreturn文は不要。
+, -, *, /, %, **はそれぞれは加減乗除、剰余、累乗の計算を行う中置演算子。
ただしCrystalでは演算子/は商を実数(小数型)で返すので、ここでは都合が悪い。
**整数で返す演算子は//**で、商を切り下げて整数型で返す。小数や負数を含む除算でも同じこと(-∞に向かって丸める)。
一文で言うと、x//y = ⌊x/y⌋

標準入力を受けとる

input.cr
a, b = read_line.split.map(&.to_i)

標準入力

read_lineは標準入力から1行を読みこむメソッドで、String型で返す。2

文字列の加工

Object#methodはオブジェクトObjectに対応したmethodを示す表記で、実際のコードではinstance.methodの形で書く。
String#splitString型に対応したsplitメソッド。1つのStringをホワイトスペースで区切って複数のStringに分解し、Array型で返す。

StringArray

Stringは文字列をUTF-8で保持する。UTF-8は1文字に費やすメモリが不定長(1〜4バイト)なので、ランダムアクセスできない(s[i]は用意されているがiに比例したコストがかかり、時間計算量は$O(|S|)$ )になるので注意が必要。

ただし、要素が全てASCII文字であれば、全て1バイトなので原理上ランダムアクセス可能となり、実際$O(1)$となるように実装されている。

競プロでは$O(1)$と見做してよい。

Arrayは配列を表すデータ構造で、不定長が許される。全ての要素の型が同じ必要があるが、Union型も許される。

マッピングと型変換

to_iはオブジェクトを整数型に変換するメソッドで、map(&.to_i)は配列等の要素一つ一つを整数型に変換する。
構文を調べていくと奥が深い。このまま覚えるのがよい。

奥が深いとは `Array#map(...)`メソッドは元の配列の要素一つ一つを`()`内のブロックに渡して、そのブロックからの返り値からなる新しい配列を返す。 ブロック`&.to_i`はブロック`{|x| x.to_i}`と等価の表現。 `{|x| x.to_i}`というブロックは一変数を`x`に受け取り、`x.to_i`を返す表現。

ここまでのまとめ

標準入力が"4 10"の場合を例にとると、
read_line.split.map(&.to_i)
"4 10".split.map(&.to_i)
["4", "10"].map(&.to_i) ([“4”, “10”]で長さ2の配列)
["4".to_i, "10".to_i] (“4”, “10”は文字列)
[4, 10] (4, 10は数値)

多重代入

ここまででa, b = [4, 10]
CrystalはPythonのように多重代入が可能。
左辺がコンマ区切り、右辺がIndexable(添字でアクセス可能)の場合にこの構文が可能となり、先頭から順にマッチさせられて代入される。

a, b = [4, 10]
a = [4, 10][0]; b = [4, 10][1]
a = 4; b = 10

となる。;を使用すると複数の文を1行で記述できる。
右辺の要素が余った時は、残った要素は使用されない。足りない場合はランタイムエラーとなる。

標準出力

output.cr
puts div_ceil(b-1, a-1)

求める答えはdiv_ceil(b-1, a-1)の値であるから、これを標準出力する。
putsは引数をStringに変換して標準出力するメソッド。
Crystalではf(x)f xは同じ。f(x, y)f x, yは同じ。
つまりputs(div_ceil(b-1, a-1))でもよい。
puts(div_ceil b-1, a-1)でもputs div_ceil b-1, a-1でもよい。深さ優先なのか。
(puts (div_ceil b-1, a-1))と書くとちょっとLispっぽい。
可読性を考えると、返り値が重要なものはf(x)、副作用が重要なものはf xが良いのではなかろうか。

正答例

main関数のような形で書く必要はなく、実行したい処理は地の文に書く。

abc139_b.cr
def div_ceil(x, y)
  (x+y-1) // y
end

a, b = read_line.split.map(&.to_i)
puts div_ceil(b-1, a-1)

出来上がり。
メソッド定義はC++の様に必ずしも前置である必要はなく、後置でもいける。

abc139_b.cr
a, b = read_line.split.map(&.to_i)
puts div_ceil(b-1, a-1)

def div_ceil(x, y)
  (x+y-1) // y
end
  1. https://ja.crystal-lang.org/reference/syntax_and_semantics/the_program.html

  2. https://crystal-lang.org/api/1.0.0/IO.html#read_line(*args,**options):String-instance-method

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?