雑草SEの備忘録

東大を卒業し、SEとして働くことになった。備忘録的に作業を綴る。

Goならわかるシステムプログラミング 6.6.1のKeep-Alive対応のHTTPサーバーのコードがわかりにくい問題

少し前にはてな界隈でも話題になった、Goならわかるシステムプログラミングという本を写経しています。



Goならわかるシステムプログラミング

  • 作者: 渋川よしき
  • 出版社/メーカー: Lambda Note
  • 発売日:2017/10/19
この本は、Go言語の勉強のために購入したのですが、Go言語というより、HTTPプロトコルなど、比較的程レイヤーの解説および実装となっていて、Go言語の勉強というよりも、TCPとは何か、UDPとは何か、OSにおけるファイルシステムとはどういうものなのかということを学べる書籍となっています。そういう意味で、今まで何気なく使っているものより少し下のレイヤーの技術を勉強するにはもってこいの本だと思います。

その中で、「6.6.1 速度改善(1)」にあるp107ページのコードがあるのですが、「6.5.1 TCPソケットを使ったHTTPサーバー」の差分になっており、差分といってもどこをどう編集すればいいか少し戸惑ったので、diffで表現して見ました。
また、差分表記ではないものは、こちらにあります。

 package main

 import (
     "bufio"
     "fmt"
+    "io"
     "io/ioutil"
     "net"
     "net/http"
     "net/http/httputil"
     "strings"
+    "time"
 )

 func main() {
     listener, err := net.Listen("tcp", "localhost:8888")
     if err != nil {
         panic(err)
     }
     fmt.Println("Server is running at localhost:8888")
     for {
         conn, err := listener.Accept()
         if err != nil {
            panic(err)
         }
-        fmt.Printf("Accept %v\n", conn.RemoteAddr())
-        request, err := http.ReadRequest(
-            bufio.NewReader(conn))
-        if err != nil {
-              panic(err)
-        }
-        dump, err := httputil.DumpRequest(request, true)
-        if err != nil {
-              panic(err)
-        }
-        fmt.Println(string(dump))
-        response := http.Response{
-              StatusCode: 200,
-              ProtoMajor: 1,
-              ProtoMinor: 0,
-              Body:    ioutil.NopCloser(strings.NewReader("Hellow World\n")),
-        }
-        response.Write(conn)
-        conn.Close()
+
+        go func() {
+            defer conn.Close()
+            fmt.Printf("Accept %v\n", conn.RemoteAddr())
+
+            for {
+                conn.SetReadDeadline(time.Now().Add(5 * time.Second))
+                request, err := http.ReadRequest(bufio.NewReader(conn))
+                if err != nil {
+                     neterr, ok := err.(net.Error)
+                     if ok && neterr.Timeout() {
+                          fmt.Println("Timeout")
+                          break
+                     } else if err == io.EOF {
+                          break
+                     }
+                     panic(err)
+                }
+                dump, err := httputil.DumpRequest(request, true)
+                if err != nil {
+                     panic(err)
+                }
+                fmt.Println(string(dump))
+                content := "Hello World\n"
+
+                response := http.Response{
+                     StatusCode:    200,
+                     ProtoMajor:    1,
+                     ProtoMinor:    1,
+                     ContentLength: int64(len(content)),
+                     Body: ioutil.NopCloser(
+                          strings.NewReader(content)),
+                }
+                response.Write(conn)
+            }
+        }()
     }
 }

Androidのアプリ開発を自走できるようになるまで

このブログを書くのもかなり久しぶりになってしまいました。
最近は技術系の話はQiitaに書くようになって、個人ブログは放置している状態なのですが、今回はコラム的なものなのでこちらで書きたいと思います。

 

かれこれRailsの仕事を初めてもうすぐ3年になります。ベンチャー企業で働くと時が経つのが早いです。

と同時に3年もRailsの仕事を続けているのかという驚きもあります。

と言っても最近はGoogle spreadsheetとにらめっこしたり、Google presentationとにらめっこする日々も多く、気づいたらcommitしていないという日も何度かあり、プライベートで作業しているリポジトリも諸般の事情によりbitbucketを利用していたりするのでGitHubに草がつかないということが出て来るようになりました。

 

さて、そろそろ新しいこともしたいなと思い、業務の合間にRails以外の勉強もしていて、6月にはcouseraのdeeplearning コースを3〜4ヶ月かけて修了するなどを行っていたのですが、まずは広く浅く学んでみようということで、Androidアプリ開発の勉強も本格的に着手しました。

 

今日は、Androidのアプリ開発を学び始め、ある程度自分で調べて実装できるようになる(自走できる)までどう勉強したかを述べたいと思います。

 

Androidアプリ自体は、昨年お6月から今年の3月ぐらいまでよちよち.kotlinという勉強会を開催しており、そこで経験者をお招きし、kotlinおよびAndroidの勉強をしていました。

とはいうものの、簡単なゲームアプリを作って公開した程度で、画面も一つしかなく、Androidの基本的なことを知らないまま開発してしまったので、基礎からきちんと学びたいなと思っていました。

まず、Androidアプリ開発をインターネットの情報で勉強するのは初心者にはなかなか至難の技だと思います。というのもAndroid自体は結構前から普及しており、かつ進化も早いため、「Androidアプリ開発勉強法まとめ!」といった記事でも紹介されているサイトが数年前のものだったり、レガシーな情報だったりするものが多いです。

初心者にとってこれをやればOKみたいなものはAndroidに関しては見つけにくいのが現状だと思います。iOSはAppleがSwift Playgroundsなどを提供してくれているのでその点勉強しやすいかと思いますが、Androidはなかなか手強い。Googleの公式ドキュメントも初心者からしてみれば全く何が書いてあるかわからないと思います。少なくとも私はわからなかった。

また、最近はweb開発ややアプリ開発の教室もあるのですが、なかなか高額でかつ時間も取れないということで休日や平日の夜に勉強できるのならばやはり書籍を買って最初から最後までやり遂げるということが大切だと思っていました。受験生の時も多くの参考書を買うのではなく、一つの参考書を何回もやるというのは鉄則です。(私は他の参考書にも浮気していたから成績が悪かった)

 

そこで、丸善やジュンク堂といった本屋に行き、Androidアプリの開発入門的な本を探して見ました。

探すポイントとしては、

出版時期が比較的新しい

前述の通り、進化がとても早いので1年前に出版された情報ですら、最新の環境では状況が違ったりします。

Android Studioの最新版に対応している(2018/8/6現在ではAndroid Studio 3です)

Android StudioはAndroidの統合開発環境なのですが、年1ぐらいでバージョンアップされるので、バージョンが違うとやはり勝手が違ったりします。

レイアウトに関する説明がある

 Androidのデザインの概念を簡単でもいいので記述してくれてあり、概要がわかるものがいいです。また、Android Studioでは、部品をドラッグ&ドロップなどしてGUIで直感的に配置できるのですが、書籍によってはレイアウトファイル(xmlファイル)を載せているだけでGUIの操作方法などを説明してくれていないものもあります。Googleもxmlファイルの記述は不要というプレゼンをしているので、できるかぎりGUIで説明しているものを選ぶといいと思います。ただし、GUIでの説明は画像をふんだんに使う必要があって説明や編集が大変だったりするので丁寧に説明してくれているものが少ない印象です。

サンプルアプリがあって写経できる

プログラミングの学習方法の一つは写経だと思うのですが、きちんと動くコードがあって写経できるものがいいと思います。また、完成品をバンと載せるのではなく、適宜実行できて一つ一つ何をやっているのかがわかる書籍がおすすめです。

kotlinで説明をしてくれている

GoogleがAndroidの開発言語として指定してはや1年以上がたちました。まだまだJavaで開発している現場も多いと思いますが、今後はkotlinがメインになってくるでしょう。であれば、kotlinで説明している書籍が良いと思います。ただし、Javaの知識が不要ということは決してありません。私は新卒で入社した会社でJavaを学んだので基本的なところが把握していましたが、kotlinと並行してJavaも学んでおいたほうが良いでしょう。

 

そんな観点から本屋で漁っていると、比較的最近出版された本で以下の本が見つかりました。 

基本からしっかり身につくAndroidアプリ開発入門 Android Studio 3対応

2018年5月22日発売で比較的新しく、kotlinで解説、Anroid Studioも3系に対応しています。

amazonのレビューを見るとなかなか辛辣な意見もあるのですが、立ち読みして雰囲気悪くなかったので、購入しました。

そして、3週間ほどかけて、すべて写経し終えることができました。改めて、この本のいいところは、

・レイアウトをGUIで説明してくれている

・フラグメント、API通信などアプリで使う基本的なことを解説してくれている

・加えて、JobSchedulerなどバックグラウンドでの処理の仕方も説明がある

などだと思います。今まで断片的に知っていて、Androidについてもやもやしていたところがある程度クリアになったという印象です。

一方で人によっては物足りないかもなと思うのは、

・Retrofit2やormaなどのよく使われるライブラリに関する説明はない

・kotlinで記述されているが、kotlinの良さを最大限に発揮した書き方ではない

・テストの書き方がない

というところです。ただ、安易に外部ライブラリに頼らず最初は初めから組み込まれているもので実装するというのもいい経験だと思うので、入門書としてはいいのではないかと思います。

またkotlinに関しても、あまりkotlin固有のテクニックを使ってしまうと、巷に溢れている情報はまだまだJavaのものも多いので、Javaとの行き来が難しくなってしまうので、これはこれでわかりやすいと思いました。

 

個人的には、この本に加えて、日本語での初のkotlin解説本も読むといいともいます。実は、私は昨年の5月にKotlinスタートブックを購入し、Androidアプリ開発勉強しようと思って読み始め、写経もしたのですが、ちんぷんかんぷんでした。この本は、一通りJavaでAndroid開発できる人向けで、一からAndroidアプリ開発をしようという人向けではないと思います。kotlinの言語仕様を学ぶにはいいかと思いますが、Androidアプリ開発の最初の入門書には向いていません。

ただし、前述した「基本からしっかり身につくAndroidアプリ開発入門 Android Studio 3対応」の物足りないなと思う部分をいくつかカバーしてくれており、私は「基礎から身につく〜」をやってから改めてKotlinスタートブックのAndroidアプリの部分を読んで、ListViewつかっていたところをRecyclerViewに書き直すなど自分風にアレンジしつつ、4時間ほどで読み直せたので、やはり1年前に私にはAndroidの基本的なところが身についていなかったようです。

Kotlinスタートブック -新しいAndroidプログラミング

 今は、オリジナルのアプリを作っている最中で、必ずしも本に出てきたことだけで実装が完了するわけではないものの、google先生に聞けばなんとか先に進めるという状況までなりました。自走できるようになったわけです。

ただし、今の状態ではまだまだ仕事で使えるレベルではなく、アーキテクチャはどうすればいいとか、動くものはできたけどより良い設計はどうあるべきかなどはまだまだこれからといった形です。

 

もし学生の方でAndroidアプリ開発を仕事にしてみたいというのであれば、本を1冊やりきり、オリジナルのアプリでも一つぐらい作ってどこかのベンチャー企業にインターンやアルバイトといったかたちで応募すればお金をもらいながら経験を積めると思います。やはりお金を払って学ぶよりお金をもらいながら学ぶ方が良いですからね。。

 

最後に、Androidを学ぶ上で大事なのは、Androidのオキテに沿って実装する必要があるということ。一からプログラミングをするときは、自分が書くコードがすべてですが、Androidはある種のフレームワークのようなもので、すでに多くのコードがそこにあり、使いたいものを適宜持ってきて使うという感じになります。なので、例えばRecyclerViewを使いたいならば、適切にメソッドをオーバーライドして、自分のアプリに合うようにカスタマイズするという形です。そこに難解なアルゴリズムは存在しません。あるのは、いかにGoogleが指示するように実装していくかです。最初のうちはそれに対してあまり深いことを考えてはいけません。

例えばActivityを作るとonCreateというメソッドをオーバライドしなければいけないのですが、onCreateが実際にどういう実装で使われているのかを追っていては先に進めません。Activityにはライフサイクルがあって、onCreateはこのタイミングで呼ばれるメソッドであるということを盲目的に知識としてインプットします。

実は、2014年頃、新卒の研修でJavaを勉強し、Androidアプリ開発できるようになったぞ!と思って本を一冊買って挫折したことがあります。このときは、ここで内部実装まで考えてしまい、なんでonCreateなの?どういうカラクリで動いてるの?などと考えてしまっておりました。そしてもうわからーん!となったわけです。

この辺りはそういうものだと思って先にするむのが吉です。おそらく理解はあとからついてきますし、場合によっては理解しなくてもAndroidのオキテを把握して置くだけで十分なときもあるでしょう。

 

では、快適なAndroidアプリ開発ライフを〜!

 

2018.8.19 追記)

久しぶりに技術書を頭からきちんとやって、間違いもいくつか見つかったので、出版社にお伝えしたところ、公式サイトにも正誤表を載せていただけました。大きな間違いではないので、あまり困ることはないかもしれませんが、参考にしていただければ幸いです。

SBクリエイティブ:【正誤情報】『基本からしっかり身につくAndroidアプリ開発入門 Android Studio 3.x対応』

unless A && Bを使っても読める場合

以前

unless A && B

は辞めたいという記事を書きました。
normalse.hatenablog.jp
しかし、その後、場合によっては使用しても良いという意見になりましたので、追記します。
「場合」というのは、guard節を使うとき。

def hoge
  return unless A && B
  something_hoge
end

のような場合です。このようなソースコードの読み方は、A && Bのときのみsomething_hogeを実行すると呼びます。すなわち、

def hoge
  if A && B
    something_hoge
  end
end

と同じということですね。この二つは等価で、十分に読めるので、guard節でunlessを使う場合は、unless A && Bも許容できるのではないかと思いました。

git submoduleのちょっとしたこと

git submoduleでsubmoduleが更新されたから自分のブランチでも反映させたいという場合、

git submodule foreach git pull origin master

こう叩くと、すべてのsubmoduleが最新化されます。 あとは普通にcommitすればOKです。

自分で更新する場合はいいのですが、git submoduleを使っていると、誰かがsubmoduleを更新してcommitしたあと差分が見えてしまうことがあります。具体的には次の画像のような感じです。git stはgit statusのエイリアスです。
f:id:smartenergy:20170102135240p:plain:w360
これ、気持ち悪いですよね。submoduleの方は触ってないから差分を解消したい!そういうときには

git submodule update

をします。
f:id:smartenergy:20170102135644p:plain
すると、差分は解消されます。
f:id:smartenergy:20170102135716p:plain:w300
デザイナーさんとかで特にsubmoduleを触らないけど、メインのリポジトリは触って困るという場合の備忘録でした。

RailsリポジトリをTravisCI, CodeCov, CodeClimateと連携

Ruby on Railsエンジニア2年目で初めて本格的にRails newをして諸々の初期設定をしたので備忘録的に作業を綴ります。

CIツールはCircleCIなどいくつかありますが、今回はTravisCIを利用させていただきました。

TravisCIの設定

language: ruby

rvm:
 - 2.3.1
services:
 - postgresql
bundler_args: '--without development --deployment'
cache: bundler
before_script:
  - cp config/database.travis.yml config/database.yml
  - bundle exec rake db:create
  - bundle exec rake db:migrate
script:
  - bundle exec rspec spec
  • config/database.travis.ymlを追加します。↑のbefore_scriptのところでcpしているファイルですね。
test:
  adapter: postgresql
  database: travis_ci_test
  username: postgres
  • Travis CIのバッチをREADME.mdに追加したい場合は追加してください。
[![Build Status](https://travis-ci.org/[your_account]/[your_repository].svg?branch=master)](https://travis-ci.org/[your_account]/[your_repository])

CodeCovの設定

gem install travis

travisのgemをインストールします。Gemfileに書いてbundle管理下にしても良いかと思います。

group :development do
   gem 'travis'
end
  • spec/spec_helper.rbを修正。(rspecを使っている場合)
require 'simplecov'

SimpleCov.start 'rails'

if ENV['CODECOV_TOKEN']
  require 'codecov'
  SimpleCov.formatter =  SimpleCov::Formatter::Codecov
end

RSpec.configure do |config|
  (省略)
  • coverageディレクトリができるので、git管理から外しておくと良いでしょう。
/coverage
  • CodeCovに行ってGitHubでSignUpし、自分のリポジトリを追加します。
    • Repository Upload Tokenが出てくるのでコピーしておきます。
  • travisコマンドでCODECOV_TOKENを暗号化します。
    • Gemfileに追加した場合、bundle execが必要な場合あります。
travis encrypt CODECOV_TOKEN=[Repository Upload Token]
  • そうすると次のような意味不明な文字の羅列が出てくるのでそれをコピーし、
secure: "zaZ/DaFV・・・"
  • .travis.ymlに追加します。(本当はもっと長い。ターミナルの横幅にもよるが、100文字ぐらいで6行ほど)
env:
  global:
    - secure: "zaZ/DaFV・・・"
  • これでGitHubにpushしてテストが終わると自動的にCodeCovに情報が通知され、カバレッジが見れるようになります。
  • バッジをREADEME.mdにつけたい方は、以下のようなコードを埋め込んでください。CodeCovの画面上からも取得できます。
[![codecov.io](https://codecov.io/github/[your_account]/[your_repository]/coverage.svg?branch=master)](https://codecov.io/github/[your_account]/[your_repository]?branch=master)

CodeClimateとの連携

  • カバレッジの取得だけでよければCodeCovでいいかと思います。逆に、カバレッジ以外のメトリクスも欲しければCodeClimateを利用して、CodeCovは必要ないでしょう。今回は欲張り的に2つ入れてみたいと思います。
  • カバレッジを取得しないのであれば、CodeClimateのサイトに行きGitHubでSignUpし、自分のリポジトリを追加すれば良いです。
  • バッチをREADME.mdに追加したい場合は、以下のような感じで。
[![Code Climate](https://codeclimate.com/github/[your_account]/[your_repository]/badges/gpa.svg)](https://codeclimate.com/github/[your_account]/[your_repository])
  • カバレッジを取得したい場合は、CodeClimateの右下にあるTest Coverageのところからトークンを取得し、CodeCovと同じように暗号化します。
travis encrypt CODECLIMATE_REPO_TOKEN=[token]
  • .travis.ymlに追加する。
env:
  global:
    - secure: '・・・' # CodeCov用
    - secure: '・・・' # CodeClimate用
  • .travis.ymlにCodeClimateに送信するコマンドを追加します。
after_success:
  - bundle exec codeclimate-test-reporter

以上、一通り作業を終えてから振り返り的にメモをしているので抜け漏れがあるかもしれませんがその際はご指摘お願いします。

rubyで再帰

先日、関数型の初心者の勉強会に参加してきました。
私は、もともと情報系の学科を卒業したわけではないので、アルゴリズムや関数型の考え方は全くの初心者です。

なので、再帰の考え方に慣れるのにとても大変でした。まだ慣れているとは言えないレベルですが。。
備忘録もかねて、rubyで実装してみたいと思います。

今回はmapを実装してみたいと思います。rubyのmap関数はよく使うと思いますが、
配列の各要素に対してある処理を施し同じく配列を返す関数です。

こんな感じですね。

["1", "2", "3"].map { |num| num.to_i }
["1", "2", "3"].map(&:to_i)

各要素をFixnum型にしています。

今回のmapは引数は二つ。
一つ目は処理の対象となる配列。二つ目は処理を施したい関数です。
rubyの場合、関数をオブジェクトにするのはprocとかlambdaですので、これを利用します。

※proc、lambdaについてはこのあたりの記事がわかりやすいです。
http://qiita.com/kidach1/items/15cfee9ec66804c3afd2
Rubyの ブロック、Proc.new、lambdaの違い - Qiita

ではさっそく、こんな感じで実装してみました。

def map(array, f)
  return [] if array.empty?
  [f.call(array[0])] + map(array[1..-1], f) 
end
# もちろんarray.firstやarray.drop(0)を利用してもOK。

1個目の要素に処理をして残りの配列をもう一度自分自身に引き渡す。
実際に手で処理を追っていくと理解が深まりそうです。
だんだんやっていくと空配列になるので、そのときは処理は終わるようにしています。

実際に使ってみた例はこんな感じです。

[1] pry(main)> def map(array, f)
[1] pry(main)*   return [] if array.empty?
[1] pry(main)*   [f.call(array[0])] + map(array[1..-1], f)
[1] pry(main)* end
=> :map
[2] pry(main)> twice = ->(x) { x * 2 }
=> #<Proc:0x007fc3c2c82700@(pry):5 (lambda)>
[3] pry(main)> map([1,2,3,4,5], twice)
=> [2, 4, 6, 8, 10]

ただし、このような再帰関数でうと、関数呼び出しの階数が深くなるとスタックオーバーフローが発生してしまいます。
そこで使うのが末尾再帰というものだそうです。

実装してみた結果がこんな感じです。

def map(array, f, result = [])
  return result if array.empty?
  map(array[1..-1], f, result + [f.call(array[0])])
end

今度はどんどんresultに新しい配列が入っていくので空配列になったときはresultを返すようにします。

配列の掛け算や足し算をする処理なんかも再帰を使って書くとすっきりします。

def sum(array)
  return 0 if array.empty?
  array[0] + sum(array[1..-1])
end

def product(array)
  return 1 if array.empty?
  array[0] * product(array[1..-1])
end

左畳み込みと右畳み込みがあり、今回は、配列の左側から処理をしていっているので、左畳み込みで処理をしてみました。

「オブジェクト指向設計実践ガイド」を読んだ感想

9月2日に発売になったSandi Metz著の「Practical Object-Oriented Design in Ruby: An Agile Primer (Addison-Wesley Professional Ruby) 」の邦訳版です。

私はプログラムの世界には新卒で入社したSIerからJavaから学んだので、新卒の研修ではオブジェクト指向については基本から学びましたが、実戦経験ではRubyがほとんどです。

ただ、これまでプログラムを書いてきてもオブジェクト指向についてなんとなくこんなもんかなーと思いながら先輩エンジニア方のアドバイスもいただきながら書いてきました。

ここらで今一度整理してオブジェクト指向で実装するにはどうしたら良いかを学んでみたいと思い、本書を購入しました。(正確には会社で買ってもらったので、読み終わったら会社の本棚に行きます)

 

この本の英語版自体は、ドイツ人が持っていたので、ちらっと読んだことがあります。それは以前の記事でも少し紹介しました。本書を読む目的としてはドイツ人とのコミュニケーションを円滑に進めるためというのも大きいです。

この本の感想としては発売前に書いてくれている方がいたので、概ね同意します。

d.hatena.ne.jp

とくに注意書きの*3については、私も以前の記事で書いた通り違和感を覚えており、このブログの方に共感をいたしました。

 

さて、私独自の感想については以下のような感じです。

よかった点
  • ダッグタイピングや継承のまわりは勉強になりました。特に継承する際、initializeをオーバーライドしてsuperを使ったりせずにpost_initializeメソッドなどを作ってそれをオーバーライドするというのは考えたことのなかった発想でした。
  • コンポジションのところでOpenStruct使えば良いんじゃない?と思って読み進めたらイメージ通りに実装していて、自分の考え方はあながち間違っていなかったと自信が少しつきました。
悪かった点
  • 邦訳が悪いのか、それともそもそもの英語が悪いのかわからないのですが、文章が少し冗長に感じました。コードで説明してくれているので、勘の良い人はコードのbefore、afterを見るだけで理解出来ると思います。
  • リファクタリング:Rubyエディション を読んだときは心揺さぶられたのですが、そこまでのものではありませんでした。一般的なオブジェクト志向の知識を持っていて、Effective Rubyリファクタリング:Rubyエディション を読んだことのある人はさらっと読む程度で良いのではないかと思います。

全体的にはAmazon的レビューで星3つ ぐらいでしょうか。