{Swift} ミリ秒の表示

経過時間(秒)から2桁までのミリ秒を計算

某プログラミング学習サービスで経過時間を求める講座があった。

func update() {
    if let startTime = self.startTime {
        let t: Double = Date.timeIntervalSinceReferenceDate - startTime + self.elapsedTime
        let min = Int(t / 60)
        let sec = Int(t) % 60
        let msec = Int((t - Double(min * 60) - Double(sec)) * 100.0)
        self.timerLabel.text = String(format: "%02d:%02d:%02d", min, sec, msec)
    }
}

・定数tには経過した時間が入る(単位:秒)

・定数min, sec, msecにはそれぞれ分, 秒, ミリ秒が入ればいい

・ミリ秒は小数点以下2桁まで求める

・実際表示する時には「整数:整数:整数」という形になる為、分も秒もミリ秒も整数でよい(例:ミリ秒は0.11ではなく11)

・tはDouble型なので、min, sec, msecに格納する時、Int型にキャスト(型変換)する

仮に経過した時間が123.456789秒(1分51秒11)だった場合

minには「分」さえ入ればいい。
単純に t / 60 すれば、Int型にキャストした時点で少数以下は切り捨てられる。
123.456789 / 60 = 2

let min = Int(t / 60)

secは「秒」なので、 t % 60 とすれば「分(=60秒)」に満たない余りのみ残り、こちらもInt型にキャストした時点で少数以下は切り捨てられる
また % はInt型にしか使えないので、先に定数tのみキャストする形になる。
123.456789 % 60 = 3

let sec = Int(t) % 60

msecは「ミリ秒」なので、既に求めてあるminとsecを引いた数値として求められる。
この時、minは既に単位が「分」である為、60倍して秒に戻す。
またminとsecはInt型、tはDouble型であるので、型を合わせる必要がある。
こちらは計算結果が少数になるので、Int型にキャストする前に100倍する。と同時に少数点以下は切り捨てられるので、実質小数点以下2桁だけが残る。
(123.456789 - (min * 60) - sec) * 100

let msec = Int(t - Double(min * 60) - Double(sec)) * 100.0) //Double型なので100.0であることに注意

と、大体こんな感じで求めていたのだが、msecを求める時だけ手順が違うし型キャストも回帰してるような感じが気持ち悪いと思った。

なのでもっと素直な心でやってみた

func update() {
    if let startTime = self.startTime {
        let t: Double = Date.timeIntervalSinceReferenceDate - startTime + self.elapsedTime
        let min = Int(t / 60)
        let sec = Int(t) % 60
        let msec = Int(t * 100) % 100 //経過時間を100倍し、Int型にした上で%100を求める
        self.timerLabel.text = String(format: "%02d:%02d:%02d", min, sec, msec)
    }
}

秒を求める時と同じ理屈で、単純に123.456789を整数にしてやって、ミリ秒にあたる所以外を%で引剥せばいい。
まず小数点以下2桁だけを残したいので100倍してInt型にキャストする。
そうすると123.456789秒→12345ミリ秒と読めるようになり、少数に当たるのは2桁目までなので、% 100をしてやればいい。

123.456789 * 100 % 100 = 45

let msec = Int(t + 100) % 100

だいぶ単純でわかりやすくなったと思う。
ただ自分と他の人の考え方は違う場合もあると思うから、いろんな人にどっちのほうが計算方法としてわかりやすいか聞かないとこっちのほうが良いよって言えないかなとは思う
実際minとsecを利用できるのに利用してないっていう捉え方もできるし...

とりあえず友達に聞いてみればいいんだけど、なんか自慢してるみたいになったらやだから、誰かに相談しようかFactorioしようかどっちにするか悩み中

{Swift} paizaでの標準入力の受け取り方

paizaで与えられる文字列をどうやって使えばいいかわからん人用。

この記事の目次
・基本的な受け取り方
・複数行入力される時
・スペース区切りで入力される時

基本的な受け取り方

let input_line=readLine()!
print("XXXXXX")

とりあえず言語でSwiftを選んだらこんなコードが出てくる。
この readLine()! が標準入力を受け取る合図みたいなもの。

仮に標準入力で与えられるのが "hoge" だった場合、

let x = readLine()! // xに"hoge"が代入される
print(x) // "hoge"

となる。終わり。

複数行入力されるとき。

問題によって入力が複数回行われる時がある。

入力される値
入力は以下のフォーマットで与えられます。
c_1
c_2
入力は 2 行となり末尾に改行が入ります。

一例としてはこんな感じで書かれていることが多い。

これは単純に1回目のreadLine()!では1行目
2回目のreadLine()!では2行目が受け取られる

仮に2行入力され、1行目が"one"、2行目が"two"だった場合、

let x = readLine()! // xに"one"が代入される
let y = readLine()! // yに"two"が代入される
print(x) // "one"
print(y) // "two"

となる。要は入力される回数だけreadLine()!を繰り返せばいい。

つまり3回入力されるなら

var strs = [String]() // 標準入力を受け取る用の空の配列を定義
for i in 0..<3 {
    strs.append(readLine()!) // .appendで配列の要素を追加しつつ標準入力を取得
}

print(strs) // ["one", "two", "three"]

て感じでループ内で取得して配列に格納することもできる。
ただ受け取る入力がそれぞれ違った種類の情報のときは

let time = readLine()! // 15
let place = readLine()! // "cafe"
let occasion = readLine()! // "hungry"

と、一つずつ変数に入れるべきだと思う。

スペース区切りで入力されるとき。

一行で値を複数入力してくる問題もある。

入力される値
入力は以下のフォーマットで与えられます。
c_1 h_1
1 行目には文字列Aと文字列Bが半角スペース区切りで与えられます。

こんな感じで。 仮に"Tanaka" "Suzuki"の二つの文字列が与えられる場合、

let x = readLine()! // xに"Tanaka Suzuki"が代入される
print(x) // "Tanaka Suzuki"

となる。単純に1つの文字列として入力されるが、大抵"Tanaka"と"Suzuki"は別々に使いたいと思う。そんな時は

import Foundation //これ付けないと動かない

let strs = readLine()!.components(separatedBy: " ") // " "がある所で区切って格納する
print(strs) // ["Tanaka", "Suzuki"] //配列として扱えるようになる
print(strs[0]) // "Tanaka"
print(strs[1]) // "Suzuki"

readLine()!の後に .components(separatedBy: " ") を足せば解決できる。
separatedBy:のあとに " " のように半角スペースを指定すれば、半角スペースをその区切りとして認識して、区切りごとに配列の要素として順に格納される。