Velocity

方向を定めて

UoPeopleの入学を見送ってCourseraで勉強することにした

3行

  • 手段が目的化していたため、UoPeopleに入学するのは見送った
  • 入学を目指すプロセスにおいても学びは多かった
  • Courseraで数学とCS勉強中

UoPeopleの入学見送り

下記のエントリで記した通り、アメリカのオンライン大学のUoPeopleに入学しコンピュータサイエンスを学ぶために、3月頃まで英語を集中的に勉強していた。

tmkk.hatenablog.com

結論としては、入学を見送ることにした。

Twitterはてブのコメントではポジティブな反応も多く、中には応援してくれていた方もいたため、申し訳なく思う。

入学を見送るきっかけになったのは、目的を再考する機会が訪れたからだった。

目的の再考

今働いている会社では一週間に一度、チームのマネージャーと30分間話す1on1を実施している。 マネージャーにはUoPeopleに興味があることや、英語を勉強している旨は1月頃には伝えていた。

とある日の1on1でUoPeopleについての話になり、そこでキャリアについての考え方に対してツッコミをもらった。

ツッコミは「手段が目的化してない?」というものだった。 ここでいう "手段" は "コンピュータサイエンスを学ぶ" ということを指している。

その時はまぁ確かになーと思いつつ、1on1の後にゆっくり考えてみた。

...

しかし、色々キャッチアップしていくうちに、よりコンピュータサイエンスチックなことをやってるエンジニアへの憧れ・尊敬の意が強くなってきた。何より、僕が少し勉強しただけでは理解できない熟成された知識が、単純にかっこいいように見えた。

この文章は自分がコンピュータサイエンスを学ぶ意欲、つまりUoPeopleに入学しようと思った動機を言語化したものだ。(冒頭のエントリから)

改めて読み直すと、恥ずかしながら非常に抽象的なことに気づく...。

本来であれば何かやりたいことが目標としてあり、それを達成するために必要であればコンピュータサイエンスを学ぶという順序関係が自然だ。

対して、自分は明確な目標がなく「学んでいればやりたいことが何か見つかるだろう」という思考でいた。目標設定を行うために、UoPeopleで勉強しようとしていた。

ここでいくつかの疑問が出てきた。

  1. 目標はいつ見つかるのか?確実に見つかるのか?
  2. 目標が見つかると仮定しても、そのプロセスの中で他の一般教養を学ぶ必要はあるか?
  3. 一般教養の学習が目標設定に大きな意味を持つと仮定しても、投資時間が多すぎないか?

これらを踏まえて客観的に自分のアプローチを見直した時に、必ずしも今このタイミングでUoPeopleに入学する必要はないという結論に至った。

目標設定を行うためのアプローチは、他にもいくらでもあるのだから。

UoPeopleを目指して良かったこと

入学自体は見送ったわけだが、それまでの過程は決して無駄ではなく良いことばかりだった。

英語力の上達

入学の英語スキルの目安の一つとしてTOEFL iBT61点というラインがあったので、年末〜入学を見送った3月初旬にかけて英語を勉強していた。

文法は基礎から鍛え直すため「English Grammar In Use」という英語で解説されている英語の教科書を終わらせたり、「TOEFLテスト英単語3800」という単語帳を使って単語を覚えたりしていた。他にもReading対策など。

実際、英語を数ヶ月間集中的に勉強したことで英文の読解力は確実に上がった。

個人的にはEnglish Grammar In Useが一番やって良かった。英語で英語を勉強することは想像以上に学習効果が高いので文法からやり直したい人には自信を持ってオススメできる。

UoPeopleの日本人コミュニティの開設

嬉しいことに、自分のUoPeopleのエントリを読んでUoPeopleに興味を持ったという方が何人もコンタクトをくれた。

全員で交流できれば楽しいだろうなーと思いつつ、僕の観測上では日本人のUoPeopleのコミュニティは存在しなかった。せっかくなので自分で日本人向けのUoPeopleのslackワークスペースを作った。興味がある人なら自由参加のやつ。

嬉しいことに60人強の人にJOINしていただき、今でもアクティブメンバーが15人くらいいる。内半数以上はUoPeopleに入学した方々(!)で日々交流が行われている。

みんな大変そうだけど卒業者が出るといいなぁ...。

キャリアについて再考する機会

「目標が明確になっていないと、良い意思決定が出来ない」ということは一見当たり前だが、自分にとっては良い気づきだった。

今後も、自身が進むべき道が分からなくなってしまったら「何のためにやるのか?」ということを繰り返し自問しよう。

これから

このエントリを書いている今でも、未だに確固なる目標は見つかっていない。
特定の技術に対して造詣が深いエンジニアになりたいということをぼんやり思っているくらいだ。

何をするべきか考えた結果、ひとまずアルゴリズム・データ構造・コンピュータアーキテクチャあたりをピンポイントで勉強することにした。これらは領域に左右されず、特定の技術を深く理解する上で役に立つ知識だと思ったので、最低限勉強しておこうと思ったからだ。

ここら辺をピンポイントで勉強するにはCourseraが良いとお勧めされたので、Courseraを使って勉強を進めている。

とりあえずデータ構造とアルゴリズムのコースを登録してみた。

coursera.org

しかし、一番最初のイントロダクションで「離散数学の基礎知識があると理解度が上がるぞ」みたいなことを言われたので、「コンピュータサイエンスのための離散数学入門」みたいなコースからやることにした。きちんと理解したかったので...。

coursera.org

Introduction to Discrete Mathematics for Computer Science

この離散数学のコースは5つのコースから成り立っている。(ややこしいので、コース以外の呼び名が欲しい)

  • Mathematical Thinking in Computer Science
  • Combinatorics and Probability
  • Introduction to Graph Theory
  • Number Theory and Cryptography
  • Delivery Problem

5つ全部やるとして、ボリューム的には週5時間×6ヶ月くらいが目安だと公称されている。
現状2/5は終わっていて、今は3つ目のグラフ理論のコースに取り組んでいる。

数学のコースとはいえ、for Computer Science というsuffixがついている通り、練習問題としてコードを書いたりもする(言語はpython

コースが終わると証明書がもらえるのが嬉しい。

f:id:tmk_0613:20200425175136p:plain
Course Certificate (Mathematical Thinking in Computer Science)

f:id:tmk_0613:20200425175355p:plain
Course Certificate (Combinatorics and Probability)

一旦はこの離散数学のコースを終わらせることを目標として頑張っていきたい。

今度こそやりきりたい!!!

働きながら米国のコンピュータサイエンスの学士号を取得する、UoPeopleという選択肢

2019年もついに終わりを迎え、2020年になろうとしている。

6月末に転職してから半年が経った。 SESから自社開発になり、自分の動き方・考え方も少しずつ変化したように感じられる。 技術的な部分だけではない、前職とはまた違った観点でエンジニアリングそのものの難しさを実感している。

しかし、自社開発ならではのサービスとチームの距離の近さは素晴らしく、一つ一つサービスを良くしている実感を得られるのはやはり楽しい。 同僚も優秀な方ばかりで、転職して良かったと心から思う。

一方で、この半年で心の奥底からふつふつと湧いてきたものもあった。

コンピュータサイエンスへの興味だ。

コンピュータサイエンスへの憧れ

僕は工業高校を卒業してからすぐ、石油天然ガスのプラントに就職。 その後、1年半でベンチャー企業(SES)に転職。 そして再び1年半後、現職(Webサービス開発)に転職、という経歴を持っている、22歳のWebエンジニアだ。

つまりエンジニアとしては最近3年目を迎えたことになるが、2年目を迎えた頃と今では、意外にも根本的な技術的理解はほとんど同じように感じる。

これはつまり、業務に必要になった技術をキャッチアップするだけでは、既に技術面の成長曲線の伸びが鈍化し始めているように感じている。

もちろん、エンジニアに求められる本質的な要素は、課題を技術力でどう解決するか、という How の部分だと思う。今のまま流行りのフレームワークやコンテナ技術、IaaSをキャッチアップしていけば、幅広い武器を持っている価値の高いエンジニアになることができると思う。

その方面で勝負するために、今までは必要に駆られつつ、スポンジのように様々なことを吸収してきた

しかし、色々キャッチアップしていくうちに、よりコンピュータサイエンスチックなことをやってるエンジニアへの憧れ・尊敬の意が強くなってきた。何より、僕が少し勉強しただけでは理解できない熟成された知識が、単純にかっこいいように見えた。

そして、エンジニアとして同じ土俵に上がりたいと思った。

まぁ正直な所、彼らが単に超絶優秀なだけで自分が多少かじったところで太刀打ちできないという可能性が大いにあるが、人間、自分の可能性を諦めたくないものだ。

コンピュータサイエンスをどのように学ぶか

こうして、コンピュータサイエンスを学び多少なりとも低レイヤーを理解したいという欲望が生まれた。

さて、どうやって勉強しよう。アルゴリズムを勉強する?コンパイラを作ってみる?OS自作入門の本とか買ってみる?

正直なところ、どれもピンとこない。これらを勉強することで僕の曖昧な目標に近づくようなイメージがあまり沸かない。何より僕は飽き性だ、過去の傾向からこのような個別的な方法の成功率が低いことは理解していた。

そこで出会ったのが、OSSU (Open Source Society University) というリポジトリだ。

ここではコンピュータサイエンス関連のMOOC(オンラインで受講できる無料の講義)がカリキュラムとして体系的にまとめられていた。2-3年程かかるものの、これをやりぬくことで自分が欲している基礎力を身に付けることができるような気がした。

善は急げ、とにかくCore CSの最初のコースをやってみた。
www.edx.org

やってみた感想としては、若干複雑な気持ちになった。コースの内容自体は素晴らしく非常に勉強になるし、これを2-3年続ければ確かに大きく成長できるとは思うが、数年続けたことを証明するコンピュータサイエンスの学位が欲しいと感じてしまった。

UoPeopleとの出会い

どうしたものかと考えつつ、コンピュータサイエンスの学士号が取得できる通信制大学に目を通すが、校風が古かったりカリキュラムが整備されていなかったり、あまり惹かれる大学は見つからない。

社会人学生の色々なブログを物色していると、唯一惹かれる通信制大学を見つけた。しかも、アメリカの大学。

それがタイトルのUoPeople (University of the People) だ。
www.uopeople.edu

ユニバーシティ・オブ・ザ・ピープル(University of the People、略称 UoPeople、本部: アメリカ合衆国カリフォルニア州 パサデナ)は、地球上のあらゆる貧困地域、遠隔地域と高等教育機会をつなぎ、高等教育の普及による世界のよりよいコミュニティ生成を目指すことを理念とした、米国の501(c)(3)非営利の高等教育機関である。[2] [3]学長は、ユダヤイスラエル人の起業家であるシャイ・レシェフ(Shai Reshef)。アメリカ合衆国教育省(U.S.DoED), 大学単位認定審議会(CHEA), 遠距離教育単位認定委員会(DEAC)及びカリフォルニア私立高等教育局(BPPE)に認定されている。

ユニバーシティ・オブ・ザ・ピープル - Wikipedia

2009年から運営されている大学で、以下の特徴がある。

...完璧だ、完璧すぎる。これこそ自分が求めていた大学だ。

しかし、UoPeopleでCSの学士号を取得するにはいくつかのハードルがある。

  • 最低限の英語能力
    • UoPeopleは全世界の人のための大学なのでネイティブ並の英語は求められないが、テキスト・課題の提出・コミュニケーションなどは全て英語で行われる。
    • 英語がネイティブではない人は、入学するためには下記のどちらかを満たす必要がある。
      • English Composition 1 というコースを入学前に受講する。
      • TOEFL・IELTS・英検など、認定された試験である程度の実力があることを保証する。
    • The Non-Native English Speaker's Easy Guide to Proving English Proficiency | University of the People
  • 勉強時間の確保
    • UoPeopleは1年間で5学期に分かれていて、1学期に1つか2つのコースを取ることができる。
    • 1つのコースは週に15時間ほどの勉強時間を確保する必要があるので、学士号を4年で取得したい場合は2つのコースを取得し週に30時間の勉強時間が必要。
  • モチベーションの維持
    • 確実にこれが大きな大きな壁となる。
    • 働きながら学位を取得するということは今自分が想像しているよりも大変に違いない。

これらのハードルを乗り越えて学士号を取得する過程で、大きな成長を得られるはずだ。

まとめ

思い返すと今まで、長期的な目標を立ててコツコツと達成した成功経験はあまり無い。大学受験もしていないし、強いて言えば資格の勉強くらいだ。目の前の課題をクリアすることで経験値を積み上げていた。

だからこそ長期的な目標を立て、チャレンジしてみたいという気持ちがある。

まずは最低限の英語を身に付けるため、TOEFLを受験しようと勉強を進めている。来年の5月に受ける予定で目標は61点以上だ。

そしてUoPeopleに入学し、2019-2020 TERM5 (来年6月~) に1コース (週15時間) 受講するつもりだ。

同じようにコンピュータサイエンスの学士号を取得したい人は少なからずいると思うので、コメントやらTwitterやらで質問をもらえれば僕が知っている範囲で答えます。一緒に受講できたら最高ですね。

参考としてUoPeopleのTED talkを貼っておく。 www.ted.com



今年読んだ本の中に「そして、ぼくは旅に出た。」という本がある。

honto.jp

その中で出てきた世界的探検家、ウィルスティーガーの言葉が最高にかっこいい。

Put your boots on and start walking! (ブーツを履いて歩き出せ!)
- Will Steger

とにかく歩き出さないと始まらない。

さぁ、5年後の自分がどうなっているか楽しみだ。頑張れ自分。

*1:2019.12.31 8:55 追記: 学士号だけではなく修士号も取得できる旨を追記しました。

令和元年、22歳になった

f:id:tmk_0613:20190613000237j:plain
伊豆大島に着船するフェリー

6月13日で22歳になった。

ゆるっと21歳を振り返っていく。

エンジニアとして

フロントからサーバーまで一連の開発を経験し、少しずつ一人前のエンジニアに近づいている実感を得られている。

  • フロントエンド
    • React, Vueを用いたSPAの設計方法・ベストプラクティスがなんとなく掴めた
    • RxJSと仲良くなった
    • 簡易なポートフォリオを作った
  • サーバーサイド
    • Goを用いたシンプルなAPIの実装が出来るようになった
    • DBの基本的な操作方法・設計方法を理解した
    • Dockerを使った簡単なコンテナ管理が出来るようになった
    • AWS, Firebaseチョット出来るマンになった
  • その他
    • プロジェクトのディレクション、テストの進め方や外部サービスとの連携など、ソフトウェア開発を前に進めるための要素を間近で観ることで理解することができた
    • 分からないことがあったり問題が発生した時に雰囲気で理解しようとするのではなく、原因を正しく理解するよう努めるようになった
    • 説明したり議論する際に、以前より論理立てて話す事が出来るようになった
    • 平易な英語で書かれているドキュメントなら8割方読めるようになった
    • 副業を経験した

趣味として

  • 一眼レフを買った
    • 晦日に初売り価格でD5600を勢いで買った
    • 写真を撮る楽しさを知った
    • 良い写真を撮る事は難しい事だと気づいた
  • ロードバイクに乗った
    • 伊豆大島の御神火ライドに参加した
    • 7時間耐久のもてぎエンデューロに参加した
    • 桃と桜のサイクリングに参加した
    • GWに4泊5日で茨城、福島、宮城を巡る仙台キャンプツーリングをした
  • ハーフマラソンに出場した
    • 二日酔いで出場した結果、2:02:47だった
    • 次こそは2時間切りたい
  • 旅行サークルに入った
    • 日帰りメインだけど結構行った
    • 最高の友人と出会った
    • 色々行った結果、自然、特にスケールが大きい景色が好きな事に気づいた
      f:id:tmk_0613:20190611235524p:plain
      一際目立つMEGAドンキ

心境の変化

フロントからサーバーまで一連の開発を経験し、少しずつ一人前のエンジニアに近づいている実感を得られている。少なくとも会社に依存して生きていかないで良くなった。

ただ、同い年の学生は大学4年生と考えると少し焦る気持ちがある。。。

来年には「CS専攻でした」とか「某社でインターンやってました」という強いエンジニアが入ってくるかもしれないと考えると、負けるわけにはいかねえ!!!という気持ちになってくる。

また、去年の今頃に書いたブログにはこんなことが書いてある。

そもそも自分がどういったエンジニアになりたいか明確に定まっていない部分が問題なんだと思う。

一年経った今の心境としては、特定の技術やプラットフォームに専門するというよりも、事業を上手くグロースさせるエンジニアになりたいと思っている。 特定の技術 いわゆる How に囚われるのではなく、あくまで大切なのは What その技術は何を実現するためにあるのか、だということを理解できたのはこの1年での大きな成長だと感じる。

7月からまたステージが変わる。 置かれている環境が急速に充実していく事を感じるが、同時によりバリューを求められる。

22歳、仕事も趣味も全力で突っ走って行きたい💪

「選択」の重要性と難しさ

私たちは日常の中で無意識の内に数え切れない程の選択をしている。

朝起きて食べるもの、上司との会話、勉強をするかゲームをするかドラマを見るか...
全ては己の意思だ、誰かに強制されているわけではない。

朝、習慣的にご飯・味噌汁を食べる人とハンバーガー・ポテトを食べる人ではおそらく前者の方が健康的な日々を送ることができるだろう。

選択肢の数だけ人生は分岐していて、その数は数え切れないほど多い。

幾千万という選択のフィードバック・感触を理解し、分析することで人間は成長していく。 これこそが一般的に振り返りが大切だと云われる理由だ、過去の選択の結果を振り返ることは選択の質自体を上げること自体に繋がる。

そして、年月が経つ程、日常的な選択の数・影響は小さくなっていく。

よく「学生の時にもっと勉強しておけば...」という言葉を聞くし、自分自身でもそう思う。 これは過去の選択の積み重ねの結果、理想と離れた地点にいることを悔やんでいるのだ。

この事象を受け止め、理想地点に向かって近づく選択を選び続ければ、いつか辿り着くだろう。 逆に事象から目を逸らし、仕方ないねと飲み込むことで、さらに理想地点からは遠ざかっていく。 これこそが「選択」の重要性と難しさだ。選択次第で未来は変わり得るが、ソシャゲのイベントのようにいつから始まるわけでもなく日常の中に溶け込んでいるため重要さが認識し難い。 日常の中で、場合によっては無意識に行なっていることを、自分の意志の力で制御しなければいけない。

この意志の力は消費性で、一日の中で使える回数は限られている。

「よし、勉強しよう!」という一念発起的な選択は疲れるし、毎日継続して行うには不安定すぎる。 だからこそ目標に向かって近づく行動を習慣化し、無意識に行えるようにすることで良い選択を増やすことができる。 習慣の特性・習慣化の方法を知りたい人は「小さな習慣」という本を読むと良い。

www.diamond.co.jp

私たちは選択の上に生きている。選択を積み重ねた結果で私たちはできている。

APIキーを使ったGoogle APIの認証をGoで行う

f:id:tmk_0613:20190212221810p:plain

概要

まず、前提としてGoogle APIを使用するためには、リクエストを発行するクライアントが正当である事を主張するために認証が必要である。

Google APIの認証方法は3種類に大別される。(2019/02/11時点)

  1. APIキー
  2. OAuth 2.0
  3. サービスアカウント

当記事では、Google APIsが提供している各APIを利用するためのGoライブラリ google-api-go-client を利用した、APIキーを用いた認証方法を簡潔にまとめる。

github.com

準備

APIキーの用意

Google API console にアクセスし、ヘッダーから「プロジェクトの選択」をクリックする。(無い場合は新規に作成する)

Google APIs プロジェクトの選択

次に 「メニュー」 → 「認証情報」を開き、「認証情報を作成」→「APIキー」と進むと、APIキーが作成される。このAPIキーをリクエストに埋め込む事で認証が行われ、Google APIを利用することができる。

Google APIキーの発行

実装

パッケージのimport

まず、利用するAPIのパッケージをimportする。

今回は YouTube Data API を利用するので、対応するパッケージの youtube パッケージをimportしている。APIキーで認証を行う場合は、 transport パッケージも併せて必要となる。

import (
    "net/http"
    "google.golang.org/api/googleapi/transport"
    "google.golang.org/api/youtube/v3"
)

クライアントの定義

次に、HTTPクライアントに値する構造体を定義する。

developerKey := "[作成したAPIキー]"

client := &http.Client{
    Transport: &transport.APIKey{Key: developerKey},
}

transportパッケージ内で APIKey型は以下のように定義付けられている。

type APIKey struct {
    // Key is the API Key to set on requests.
    Key string

    // Transport is the underlying HTTP transport.
    // If nil, http.DefaultTransport is used.
    Transport http.RoundTripper
}

上で定義した clientTransport フィールドにAPIキーを指定した、http.Client型のポインタだ。 この事から google-api-go-client は単に httpパッケージを用いた通信処理をラップして、扱いやすくしているライブラリだという事が伺える。

APIサービスの定義

APIを利用するためにサービスを定義する。定義の際には、各API用のパッケージに定義されている New メソッドを用いる。

New メソッドの引数に先ほど定義したクライアントを指定することで、APIキーをサービス自体に埋め込んでいる。

service, err := youtube.New(client)
if err != nil {
    log.Fatalf("Error creating new YouTube client: %v", err)
}

メソッドの利用

上記で定義したサービスに定義されているメソッドを呼ぶ事で、対象のAPIに実際のリクエストを飛ばすことができる。

文章だと理解しづらいと思うので Search メソッドを利用してみる。 流れとしては、リクエストの詳細を call として定義し、Do でリクエストを飛ばしている。

query := "The Chemical Brothers - Go"
maxResults := 1

call := service.Search.List("id,snippet"). // Listメソッドを指定
    Q(query). // 検索クエリを指定
    MaxResults(maxResults) // 最大取得件数を指定
response, _ := call.Do() // 定義したリクエスト(`call`)を実行

Searchメソッドの場合、このような形式で response が返ってくる。

[]*youtube.SearchResult{
  &youtube.SearchResult{
    Etag: "\"XpPGQXPnxQJhLgs6enD_n8JR4Qk/ZIRkyzKLO8yyOOhAO8Io-oAZJ9o\"",
    Id:   &youtube.ResourceId{
      ChannelId:       "",
      Kind:            "youtube#video",
      PlaylistId:      "",
      VideoId:         "LO2RPDZkY88",
      ForceSendFields: []string{},
      NullFields:      []string{},
    },
    Kind:    "youtube#searchResult",
    Snippet: &youtube.SearchResultSnippet{
      ChannelId:            "UC11RIQQg3bSkGtnpx9dom5g",
      ChannelTitle:         "ChemicalBrothersVEVO",
      Description:          "Explore more music from The Chemical Brothers https://ChemicalBrothers.lnk.to/essentials The new album 'Born In The Echoes' is out now. Get it on iTunes ...",
      LiveBroadcastContent: "none",
      PublishedAt:          "2015-05-04T18:30:01.000Z",
      Thumbnails:           &youtube.ThumbnailDetails{
        Default: &youtube.Thumbnail{
          Height:          90,
          Url:             "https://i.ytimg.com/vi/LO2RPDZkY88/default.jpg",
          Width:           120,
          ForceSendFields: []string{},
          NullFields:      []string{},
        },
        High: &youtube.Thumbnail{
          Height:          360,
          Url:             "https://i.ytimg.com/vi/LO2RPDZkY88/hqdefault.jpg",
          Width:           480,
          ForceSendFields: []string{},
          NullFields:      []string{},
        },
        Maxres: (*youtube.Thumbnail)(nil),
        Medium: &youtube.Thumbnail{
          Height:          180,
          Url:             "https://i.ytimg.com/vi/LO2RPDZkY88/mqdefault.jpg",
          Width:           320,
          ForceSendFields: []string{},
          NullFields:      []string{},
        },
        Standard:        (*youtube.Thumbnail)(nil),
        ForceSendFields: []string{},
        NullFields:      []string{},
      },
      Title:           "The Chemical Brothers - Go",
      ForceSendFields: []string{},
      NullFields:      []string{},
    },
    ForceSendFields: []string{},
    NullFields:      []string{},
  },
}   

YouTube Data APIに対してリクエストを発行し、レスポンスを受け取る事ができた。

まとめ

google-api-go-clientを用いて、Google APIにアクセスできるようになった。

youtubeAPIサンプルが分かりやすいので最後に置いておく。 github.com

AOJで、出力結果は合っているように見えるWAでハマった

TL;DR

  • ios::sync_with_stdio(false) を削除するとACだった
  • ios::sync_with_stdio(false) を削除せず、endlを"\n"に置き換えてもACだった
  • 同期を切っていることが問題ではなく、バッファのフラッシュ回数が原因?

発端

Stable Sortを解いていた、コードを書き自信満々でSubmit

"WA"

悲しい2文字が出力された、4つ目のテストケースで落ちている、どこがおかしいんだ。
まぁシュっと修正して早く次の問題やろう。


コード・出力とにらめっこしつつ、ハマり始めて30分...

やっぱり分からない。ロジックも問題なさそうに見える。
仕方ないので、他の回答者の方のコードを拝借し、ローカルで出力を比較する。

# 自分の出力
H3 D3 S3 S4 C4 H4 S5 H5 D5
Stable
H3 D3 S3 H4 C4 S4 D5 S5 H5
Not stable
# 正しい出力
H3 D3 S3 S4 C4 H4 S5 H5 D5
Stable
H3 D3 S3 H4 C4 S4 D5 S5 H5
Not stable

...同じじゃねえかーーーーーーーーーーーーーー!!!
なぜだ、なぜなんだ。出力が正しければ良いのが竸プロという世界ではないのか(偏見)

いよいよ分からなくなってきた、竸プロは難しい。

メンタルを試されているのか...?

段々と「出力は間違っていない、処理中に問題が発生しているに違いない」という謎思考になってくる。 もはや自分の偏見さえも覆している。

原因は入力方法

ここで参考にしていたコードの人の入力方法を確認すると、自分のコードと違いがあることに気づく。 自分と同じくcinでの入力方法を用いているが、cinとcout、iostreamとstdioの同期を切っていないことに気づく。

入力なんて関係ないだろう、とは思いつつなんとなく同期を切るコードを削除してみる。

// cin.tie(0);
// ios::sync_with_stdio(false);

Submit


"AC"

...同期〜〜〜〜〜〜〜〜〜〜〜!!! さらに ios::sync_with_stdio(false); だけ削除してSubmitしてもACになることが判明。

念願のACとはいえ、今後もハマる可能性が大いにあり得るので、入力についてちょっと調べてみる。

ここで、International Olympiad In Informatics 2009, Technical Info Sheet(日本訳) を見つける。 https://www.ioi-jp.org/ioi/2009/problems/day2/Technical_jp.pdf

どうやら ios::sync_with_stdio(false) で同期を切る場合は、endl ではなく "\n" での改行が推奨されているらしい。 endlでの改行と"\n"での改行を違いを、後者の方がちょっと早いという認識でしかなかったが、とりあえずこれも試してみることにした。

ios::sync_with_stdio(false)を再度記述し、endlを全て"\n"に置き換えてみて、Submit。

"AC"

なんとACだ。正直びびった。 不思議に思いつつ、endlと"\n"の違いについて調べてみる。

  • endl

    • 改行を出力し、バッファをフラッシュする
  • "\n"

    • 改行を出力する

なるほど、バッファをフラッシュするか否かの差異があるらしい。

この記事などが参考になった。 C++ の endl とか flush の存在意義 - ハトネコエ Web がくしゅうちょう

まとめ

TL;DRに書いてあることが自分の中での結論。 結局、今回の問題がコードの問題かAOJ特有の問題かは分からず、根本原因を解明した訳ではないが同じようにハマっている誰かの助けになれば幸いだ。

今までは視認性からendlを用いていたが、これからは"\n"を使用することにしよう。

2018年の振り返りと2019年の抱負

2018年の振り返り、そして2019年の抱負を綴るには少し出遅れてしまった感が否めないが、better late than never ということで書いていく。

2018年の振り返り

1月 ~ 2月

GASで社内業務の効率化してた。
いわゆる6分の壁を乗り越えるための time-driven-trigger に苦戦していた記憶がある。 kido0617.github.io

また、Node.js/Express/GCP/OAuth2.0 辺りの勉強も兼ねて、GASで行なっていた一連の処理をWebアプリにリプレイスする業務も行なっていた。 その頃は、それぞれの要素がシステム全体でどのような役割を担っているかも正確に把握出来ていなく、雲を掴むような感覚でコードを書いていた気がする。

あとは、TypeScriptとかAWSの勉強させてもらったり。

3月 ~ 5月

別の現場に異動して、kintoneで地図プラグインアプリ開発してた。
gitの使い方とかREST APIの基本的な考え方とかJSのPromiseとか、最初の1ヶ月は非常に学びが多かった。 US圏向けのジオコーディングAPIの調査をやらせてもらったおかげか、ここで英語ドキュメントへの耐性がついた気がする。

そのチームはテストコードを書く文化が無く、E2Eテストで品質を担保するような運用だった。 後半の方は毎週本番環境にリリースを行っていたため、その度にdev環境と本番環境に対して手動でE2Eテストを行なっていたが、それが辛かった。

試験項目が多いため、どうしてもテスト工数が多くなってしまい、仕様変更やバグ修正が追いつかなくなってきてしまう。 よって必然的に残業時間が増えてしまっていた。

ここでテスト自動化の重要性を理解した。

6月 ~ 7月

何でも屋さんの時期。
Go/Revelでミニマムな出欠管理アプリ開発を開発したり、Javaシステムの動作検証・デバッグの手伝いをしたりしていた。

この時期は1つのことに集中できないために悶々としていたが、この時期のおかげでGo/Javaの文法やオブジェクト指向プログラミングの考え方について理解することが出来たため、意外と良い経験になっているかもしれない。
OOPを学ぶ過程でデザインパターンも勉強したのだが、既に頭の中で風化しつつあるのでよろしくない。


オブジェクト指向の勉強にはこの書籍を用いて勉強した。少し古い本だが、オブジェクト思考の概念を理解するには良い書籍だったように感じる。
なぜ、あなたはJavaでオブジェクト指向開発ができないのか―Javaの壁を克服する実践トレーニング


また、6月に21歳の誕生日を迎えたため、こんな記事も書いていた。

tmkk.hatenablog.com

業務ではプレーンのJSとjQueryしか触っていなくて、 モダンな環境で開発がしたいという気持ちが非常に強い。切実に。


現時点では アーキテクチャAPIの設計 とか パフォーマンスの改善 を将来的にはやってみたい。 しかし元々フロントエンドに憧れていたので、SPAの実装とかにも興味あるし...といった感じで定まらない。

モダンなフロント開発をしたい欲に満ち溢れている様子が見て取れる。

8月

8月はファイル共有システムのデモを開発していた。念願のSPA開発だ。

Vue.js/Bulma でのフロントエンドをメインに担当していたが、API Gateway/LambdaでのAPI・サーバーサイドの開発や、Serverless frameworkを用いたデプロイ環境構築など、AWS周りにも横断的に携わっていた。

この時期はかなり能動的に動ける環境だった、ありがたいことにフロントエンドの技術選定から任せていただいた。
JS/CSSフレームワークは、波が来てそうなVue.jsと相性が良さそうなBulmaをチョイスした。

Vue.jsは本当に使いやすく日本語ドキュメントも充実しているため、人気が出るのも納得した。
Vue CLIによるDeveloper Experienceも素晴らしく、スピード感が求められるデモ開発では非常に助かった。

丁度Vue CLI 3.0が出たタイミングだったので記事も書いた。Vue CLI UIの体験が良すぎて、びびった。

tmkk.hatenablog.com

qiita.com

9月上旬

社内でミニマムな採用試験アプリの開発していた。

ここでは、俗に言うMERNスタック(MongoDB/Express/React/Node.js) で開発を行なった。
Reactに触れるのはほぼ初めてだったが、Vue.jsでの開発経験があったため、スムーズに開発を行うことができた。

少人数だったが、自分以外フロントエンドの開発経験が無い人だったため、設計も担当することになった。 Atomic Designを推進したが、開発者毎のイメージの違いやレベル感の差異、用意されていた開発期間の短さから、結局コンポーネント毎の粒度にばらつきが出てしまった。

設計、チームでの意思統一の難しさを実感することができ、良い経験になった。

9月下旬 ~ 11月

仮想通貨取引所のフロントエンド開発をしていた。

この現場は TypeScript/React/Redux/redux-thunk/ImmutableJS/LESS/gRPC/WebSocket とモダンな開発環境で楽しかった。 また、問題提起に対して積極的に議論し共有する風潮があったため、様々な課題がみるみる内に改善されていく様は初めての体験で驚いた。

ただ、以下の事柄が暗黙知となってしまっており、書く人によってコードの書き方にバラツキが出たり新規参画者のキャッチアップが大変になってしまっていた。

  • Atomic Designを参考にしたコンポーネント設計
  • コーディングスタイル
  • propsの渡し方やPureComponentの使用などについて

これを解決するために、フロント周りの開発ルールをWikiに書き起こし、明文化する活動も行なっていた。

また、フロントエンドの設計にも携わらせていただいた。Reduxあるあるだと思うが、特にローディング周りの設計に苦労した。
各非同期処理の前後に HOGE_REQUEST/HOGE_SUCCESS/HOGE_FAILURE を書き、その中で isLoading を切り替える実装は負債になり得る可能性が非常に高いと感じたため、以下の記事を参考に実装を行なった。

medium.com

結果として HOGE_REQUEST/HOGE_DONE というアクション名をキーに、isLoading のstateを切り替える実装にした。
ここら辺については別途、記事として書きたい。

悲しいかな、再び短期間で異動となってしまったが、ここでの開発から得た学びは非常に大きかった。

12月

現在進行形で採用管理アプリの開発をフロントエンドからサーバーサイドまで担当している。

  • フロント: RxJS
  • BFF: Node.js/Express
  • API: Go/Echo

DIやTable Driven Testなど概念は理解できているが実装はしたことがない、といった事柄がたくさんあるので全て吸収していければと思う。

そしてRxJSは難しい...。


2018年は幅広い技術に触れることができ、広く浅くの年だった。

2019年の抱負

2019年は「基礎力」をテーマに勉強を進めていきたいと考えている。

特に下記の事柄の「基礎力」だ。

  1. コンピュータサイエンス
  2. 英語力
  3. 文章力

これらはエンジニアとして生きていく上で、風化しにくく価値が変わりにくいものだと自分は考えている。

コンピュータサイエンス(CS)

自分はCSのバックグラウンドが無い為、必ずどこかで勉強しなくてはいけない運命なのだ。

とりあえず年末年始の時間を使って下記の本を読んでいる。計算・アルゴリズム・データ構造・P=NP問題 などの概念を、有名な物語に紐づけて解説している書籍でとっつきやすくオススメ。
ワンス・アポン・アン・アルゴリズム: 物語で読み解く計算 Martin Erwig

また、競技プログラミングにも興味が出てきた。AtCoderのBeginner向けの問題をやってみたら結構楽しかったので、コンテストにも参加してみたい。

英語力

よく大事って言われるけど、学習が続かないランキング第一位。(俺調べ)

これに関しては、半年前から通勤中に iKnow というアプリで学習を進めている。

iknow.jp

継続的に iKnow での学習を進めつつ、3月頃にTOEICも受ける予定なのでそれに向けての対策もやっていきたい。

文章力

これについては最近まであまり意識していなかったが、ドキュメントを書いたりSlackでのコミュニケーションなど文章力が必要になる機会は非常に多いことに気づいた。

TOEICが終わった頃に一冊本などを読んで基礎から学び直したい。


2019年は基礎力を高め、変化に強いエンジニアを目指そう。