# Lineのコーディングスキルテスト2021をやってみた. 噂に聞いていた実装編をやってみました. https://engineering.linecorp.com/ja/blog/commentary-of-coding-test-2021/ 制限時間3時間で, A4 4枚程度の使用を満たすコンソールアプリを作れという話でタクシーの料金計算を実装しました. せっかくなので実装を貼っちゃいましょう with Golang. :::spoiler 実装 using go ```go package main import ( "bufio" "errors" "fmt" "os" "regexp" "strconv" ) type Time struct { Hh int Mm int Ss int Fff int } func (t1 *Time) GetMs() int { return (t1.Hh*3600+t1.Mm*60+t1.Ss)*1000 + t1.Fff } type Meter struct { Integer int Decimal int } func (m Meter) Get10xMeter() int { return m.Decimal*10 + m.Decimal } type Record struct { Time Meter } func (r1 *Record) IsLaterThan(r2 *Record) bool { return r1.Time.GetMs() > r2.Time.GetMs() } func (r1 *Record) IsIn45sAfter(r2 *Record) bool { return r1.Time.GetMs()-r2.Time.GetMs() <= 45*1000 } func (now *Record) IsSlow(prev *Record) bool { KmPerH := (now.Get10xMeter()) / 10 / (now.Time.GetMs() - prev.Time.GetMs()) * 3600 return KmPerH <= 10 } func (now *Record) IsNight() bool { t := now.Time.GetMs() % 24 * 60 * 60 * 1000 midnight := (&Time{}).GetMs() sixAM := (&Time{Hh: 6}).GetMs() return (midnight <= t && t < sixAM) } func (now *Record) IsPeakTime() bool { t := now.Time.GetMs() % 24 * 60 * 60 * 1000 sixAM := (&Time{Hh: 6}).GetMs() nine30AM := (&Time{Hh: 9, Mm: 30}).GetMs() six30PM := (&Time{Hh: 18, Mm: 30}).GetMs() midnight := (&Time{Hh: 23, Mm: 59, Ss: 59, Fff: 999}).GetMs() return (sixAM <= t && t < nine30AM) || (six30PM < t && t < midnight) } // 走行レコード func ParseRunRecord(s string) (*Record, error) { regex := regexp.MustCompile(`^(\d\d):(\d\d):(\d\d)\.(\d\d\d)\s(\d\d?)\.(\d)$`) if !regex.MatchString(s) { return nil, fmt.Errorf("%v does not match with %v", s, regex) } matches := regex.FindStringSubmatch(s) hh, _ := strconv.ParseInt(matches[0], 10, 64) mm, _ := strconv.ParseInt(matches[1], 10, 64) ss, _ := strconv.ParseInt(matches[2], 10, 64) fff, _ := strconv.ParseInt(matches[3], 10, 64) meterInt, _ := strconv.ParseInt(matches[4], 10, 64) meterDecimal, _ := strconv.ParseInt(matches[5], 10, 64) if hh > 99 { return nil, errors.New("hh > 99") } r := Record{ Time{ Hh: int(hh), Mm: int(mm), Ss: int(ss), Fff: int(fff), }, Meter{ Integer: int(meterInt), Decimal: int(meterDecimal), }, } return &r, nil } type Records []Record func (rs Records) Validate() error { if len(rs) < 2 { return errors.New("records must contain at least 2 lines") } for i := 0; i < len(rs)-1; i++ { if ok := rs[i+1].IsLaterThan(&rs[i]) && rs[i+1].IsIn45sAfter(&rs[i]); !ok { return errors.New("time of records must ascending order") } } totalMeterX10 := 0 for i := range rs { totalMeterX10 += 10*(rs[i].Meter.Integer) + rs[i].Meter.Decimal } if totalMeterX10 < 1 { return errors.New("total meter must be at least 0.1") } return nil } func (rs Records) GetFirstFare() int { if rs[0].IsNight() { return 600 } if rs[0].IsPeakTime() { return 520 } return 400 } func (r Record) GetKukanFare() int { switch { case r.IsNight(): return 60 case r.IsPeakTime(): return 52 default: return 40 } } func Min(x int, y int) int { if x < y { return x } return y } func (rs Records) CalcFare() int { fare := 0 short := 0 long := 0 m10x := 0 for _, r := range rs { m10x += r.Meter.Get10xMeter() if m10x >= (1000+400*short)*10 && m10x < 10200 { short += 1 fare += r.GetKukanFare() } if m10x >= (10200+350*long)*10 { long += 1 fare += r.GetKukanFare() } } slowTaxiMeter10x := 0 for i := 0; i < len(rs)-1; i++ { if rs[i+1].IsSlow(&rs[i]) { slowTaxiMeter10x += rs[i+1].Get10xMeter() - rs[i].Get10xMeter() } } slowTaxiMeter10x /= 10 fare /= 10 return rs.GetFirstFare() + fare } func main() { filename := "input.txt" fp, err := os.Open(filename) if err != nil { panic(err) } defer fp.Close() scanner := bufio.NewScanner(fp) var records Records for scanner.Scan() { record, err := ParseRunRecord(scanner.Text()) if err != nil { panic(err) } records = append(records, *record) } if err := records.Validate(); err != nil { panic(err) } fmt.Print(records.CalcFare()) } ``` ::: ## 良かった点 気をつけられた点としては, できるだけ少数ではなくintでそれぞれの値を扱って来たこと(SecondではなくMicroSecondをintで持っとくとか). ## ダメだった点 深夜に思い立ってゆっくりやっていた, 仕様の一部は満たせていません(泣). (低速のやつ) コーディングと言うより, 仕様とのにらめっこゲームになっていました... 今回はテストケースが手もとにない, 探り探りの状況でひとまず仕様を満たすものができて, ネットから簡単なテストケースが動いただけです. 正直テストのテの字もできていないので, 全然点数をもらえない気はします. ## どうすればよかったか 1. 先に問題全体に目を通して 2. 大体の仕様を把握した上で, テストが1つ通るようなコードを書く 3. その上で, 細かい実装を処理していく. (できればテストコードを書きながら...) ## これからやること 正直, インターンの定員30人というのはヤバすぎるので, 相当コーディング試験で頑張らないといけないなぁという気持ちです. ですが, 僕は競技プログラミング的な強さはあまりないので, こういうところを練習していけたらと思います(アレ?)