はじめに
Clojure Advent Calendar 2021の24日目の記事となります。
初めまして!Xcooという会社でWebエンジニアをしております@Hyuga_Yamaguchiと申します!
まだ入って3,4ヶ月の超超新人エンジニア(Clojureも入社して始めました)で、Qiitaに記事を投稿するのも初めてなんですが、会社の先輩の勧めでClojure Advent Calendar 2021に参加させていただくことになりました。
つれづれなるままに会社のこととかClojureとかを書いていきたいと思います
今回は「Clojureの街、Webエンジニアの朝」という題名の3本立てでお送りしたいと思います〜
- 非エンジニアからITベンチャーに転職してスゲー!と思ったこと
- 入社してから意識していたこと
- 競プロの問題をClojureで解いてみた!
とりあえず自己紹介
僕は以前、100年以上の歴史があるバチバチの「THE 日本の大企業」で工場の技術職として働いていました(プログラマーでもシステムエンジニアでもないです)。なんだかんだあって4ヶ月前に、XcooというITベンチャーに転職したのですが、エンジニアという仕事自体が未経験でしたし、Clojureはともかく、関数型プログラミング言語の存在すら知りませんでした。
当然、ITの経験や知識も乏しく、入社前の僕のスペックはこんな感じでした。
- プログラミング歴は1年とちょっとくらい(本気で取り組んだのは社会人になってから)
- 競技プログラミングサービスAtCoderでレートがもうすぐ水色に行きそうな緑(Python, C++)
- HTML, CSSとかでWebサイトをちょっと作ったことがある
Web開発をしたことないですし、インフラの知識も全くなく、そもそも誰かと共同開発をしたこと自体ありませんでした。
Clojureの街、Xcooでおどろいたこと
Webエンジニアに転向して、Xcooスゲー!、ITベンチャースゲー!と思ったことがたくさんあり、前職とのカルチャーショックを多大に受けたのですが、それをいくつか書いていこうと思います。
Markdownめっちゃみやすい!
前職では基本資料作成はWord or Excelで行い、印刷して紙ベースで管理していました。
正直使いづらいなと思っていて、特にExcelとかは元々表計算ツールなのでレイアウトを整えたりするのが非常に面倒でしたし、紙ベースで管理するので、ハイパーテキストの利点を活かせず、参考程度の内容でもいちいち引用しないといけませんでした。
しかし、HackMDというMarkdown記法のドキュメントツールをXcooに入社して初めて知り、こんな快適なルーツがあるのか!と衝撃を受けました。
- レイアウトを勝手に整えてくれる
- 画像もすぐにアップロードされて、簡単に書いてある記事に載せられる
- 表も埋め込みやすい
- TeXも使えて数式も書きやすい
- ハイパーテキストの利点をいかせて、作業量が最小で済む
- グラフも書ける!
コミュニケーションとタスク管理の手段がすごい!
前職では、基本的にコミュニケーションは全てメールでした。その中で仕事の依頼や進捗管理などを行うのですが、1日100件以上のメール来てタスクの抜けが非常に発生しやすい状況となっていました。タスク管理は放っておくと誰もやらないので、プロジェクトを進行する際は、Excelで課題管理表を作り、進捗があるたびに更新するなどしてました。
Xcooではメールを使わず、コミュニケーションツールは基本slackとzoomで行い、タスク管理はGitHubとZenHubで行います。slackは履歴がとてもみやすいので、見落としなどは発生しませんし、GitHubのIssueを有効活用しているのでタスク管理の手間暇がかからずに済みます。とても効率的でとても楽です。
オフィスが綺麗で人が優しい!
かつては築50年の建物で働いていて、和式トイレしかありませんみたいな状況だったのですが、Xcooのオフィスはめちゃくちゃ綺麗で、美味しいコーヒーも飲めて、めっちゃエモい感じの音楽が流れていてとても作業がしやすいです!
また、役員の方たちと気軽に話したりゲームもしたりできますし、質問とかもとてもしやすい環境で、僕が変な質問を投げても非常に丁寧に返してくださるのは自分の成長にもつながりますし、とても嬉しいです。
Webエンジニアの朝を迎えた初心者が意識したこと
ここから下に入社してから数ヶ月間どういうふうに意識して行動してきたかを書いていきます
業務を進めるのに必須な知識を吸収した
前述の通り、ITの経験は競技プログラミングと簡単なWebサイト作るぐらいでしたので、共同開発などしたことがありませんでした。
gitとか触ったことありませんでしたし、コンピューターサイエンスの知識も全くなかったです。また、Clojureも書いたことがありませんでした。
ここらへんの知識はさっさと身につけないといけないなと思ったので、優先度高めで勉強をしていました。
gitはこのサイトをつかったり、Clojureはこの本やこのサイトで演習を積んだり、基本情報技術者試験の勉強をやっていました。
できる限り質問しまくる
入社の最初からテレワークで社員の方達がどのような人なのかわからず、すこしやりづらかったのですが、MTGのたびに困っていることとかわからないことをひたすら質問しました。
答えづらい質問をしてしまうことも多く、ご迷惑をかけることもあったのですが、皆さん優しくて丁寧に答えてくださって非常に助かりました。
僕のような新人エンジニアは十中八九仕事に詰まるので、もっとたくさん質問したいところなのですが、先輩エンジニアの時間をとりすぎるのもよろしくないので、以下のことを意識しています。
- 質問はできる限りまとめてして、時間をかけないようにする
- 「これがわかりません」だと答える側も困るので、限界まで調べて自分がわかっていないことをできる限り具体化する
- 自分が調べて得た知識があっているかどうかをクローズドクエスチョンで確認する
- テキストベースだと伝わらないことが多いので、画面共有をしながら質問する
- 質問することに抵抗があっても、頑張って飲み込んで勇気を持って質問する(大事)
- できる限り下手に出る(一番大事)
技術に貪欲になる
仕事をしていると未知の概念に出会ったり、知らない用語を聞いたり、よくわからないツールを使う必要が出てきます。その度に少し心が折れていたのですが、以下のことを意識して克服していました。
- 未知の概念や用語が出てきたら、速攻で調べてメモ帳とかにストックして忘れないようにする
- Markdown記法のエディタにまとめるのが便利(みやすいし、リンク貼れたり画像貼れたりするので)
- よくわからないツールが出てきたらその技術背景をしっかり調べる
- Documentに「このコマンド打ったらいいですよ〜」と書いてあるケースが多いと思いますが、それだと応用が効かないので、このツールはどういう仕組みで動いていて、コマンドはこういう意味を持っているというのを逐一調べています
- 結果業務にとられる時間は増えますが、長期的な視点で考えてこのスタイルを採用しています
- 今後自分が使いそうな技術を予測して、事前に先輩に「これってどうやって勉強するのがいいですか?」と聞く
- これやっとけば初めて触る時にだいぶ楽になると思ってます
Clojureで競プロの問題を解いてみた
(defn create-data [n]
(loop [counter 0 arr ()]
(if (>= counter n)
arr
(recur (inc counter) (cons (read) arr)))))
(defn bitter-alchemy [n x arr]
(let [material-rest (- x (reduce + arr))]
(+ n (quot material-rest (apply min arr)))))
(def n (read))
(def x (read))
(def data (create-data n))
(println (bitter-alchemy n x data))
(use 'clojure.set)
(defn not-found [s]
(let [n (count s)]
(loop [counter 0 arr #{} s (map-indexed vector s)]
(if (>= counter n)
(let [ans (difference (set (range 26)) arr)]
(if-not (empty? ans)
(char (+ 97 (apply min ans)))
"None"))
(recur (inc counter) (conj arr (- (int (nth (first s) 1)) 97)) (rest s))))))
(println (not-found (read-line)))
(defn input-int-map [s]
(map #(Long/parseLong %) (.split s " ")))
(defn read-n [n]
(for [i (range 1 (inc n))] (read-line)))
(defn get-idx [vec i j]
(get (get vec i) j))
(defn passable-pos [[i j] vec]
(if (= (get-idx vec (dec i) (dec j)) \.)
[i j]
nil))
(defn todo-pos [position grid]
(filter
(comp not nil?)
(conj
[]
(passable-pos [(dec (first position)) (second position)] grid)
(passable-pos [(inc (first position)) (second position)] grid)
(passable-pos [(first position) (dec (second position))] grid)
(passable-pos [(first position) (inc (second position))] grid))))
(defn move [q m] m)
(defn solved? [q a] (= q a))
(defn success [[hist now] grid]
(->>
(todo-pos now grid)
(map (fn [m] (conj [(conj hist m)] (move now m))))))
(defn bfs [seen todo goal grid]
(let [history (first todo)
now (last (first todo))]
(println history)
(cond (empty? todo) -1
(solved? now goal) (count (first history))
(contains? seen now) (bfs seen (next todo) goal grid)
:else (bfs (conj seen now) (concat todo (success history grid)) goal grid))))
(defn distance [rc start goal grid]
(bfs #{} [[[] start]] goal grid))
(defn wall-count [grid hw]
(reduce
+
(for [i (range (first hw))]
(count (filter #(= \# %) (get grid i))))))
(println
(let [hw (input-int-map (read-line))
start [1 1]
goal hw
grid (vec (read-n (first hw)))
dist (distance hw start goal grid)
wall (wall-count grid hw)]
(if (= dist -1)
-1
(->
(* (first hw) (last hw))
(- wall)
(- (+ dist 1))))))
今後の目標はこんな感じ
- 一人前の技術者になって、来年のAdvent Calenderではもっと技術的なかっこいい記事を書きたい!
- Xcooで身につけたスキルで自分なりのアプリケーションを作ってみたい!