プロを目指す人のためのRuby入門を読んだ

10月から転職してRubyも書く人になったので、プロを目指す人のためのRuby入門を読んでみた。 この流れで勉強すればよかったな、っていう振り返りも最後に合わせて記載。

この本をやる前の状態

読んでどうだったか

  • ちょっとはプログラミング経験あって本気でRuby習得目指す人向けとあるように、初歩の初歩的な内容はほぼない。
  • 本全体に渡ってサンプルコードが多く、説明もわかりやすかった。
  • とはいえ、「第10章 yieldとProcを理解する」はわからなかった部分もあった。
    • よく分からなかった部分もなんとなくそういうものがあるのだな、とは分かったので業務の中で直面した時に戻ってくればよいかな。
  • Railsチュートリアルを読む前にやるべきだったな、と強く思う
    • 特に、シンボルやクラス、モジュールの辺りはざっとでも読んでから入ったら習得具合が違ったのではないかと思う。
    • Railsチュートリアル自体は非常によいチュートリアルだった。プログラミング初心者があの内容をしっかりできたら非常に力がつくのではないかと思う。というか、心がとても強いと思う。
  • 他の本を読んでいないので比較はできないが、自分には優しすぎず難しすぎずなちょうどいいレベル感だと感じた。
    • Railsやっていてよく分からないなぁ、というところも分かりやすく書いてあった印象。実務に戻ってみないと効果のほどは分からんが、、、

Rails学習のオススメな道筋

プログラミング経験1年以上を想定。 初心者目な人はProgateを先にガッツリやってみてから下記ステップへ。

  1. この本(プロを目指す人のためのRuby入門)をざっとやる。そんな時間かけなくてもいい。
  2. Railsチュートリアルをやる。ちゃんとやる。
    • Rubyに関して分からないところが出てきたら、この本に立ち戻る
    • Railsに関して分からないところが出てきたら、railsガイドを見よう。特にActiveRecordとかは重点的に見るといいと思う。

道筋と書いたけどこの2ステップになるかな〜

python3でプロコンのためのデータ構造とアルゴリズム

プログラミングコンテスト攻略のためのアルゴリズムとデータ構造の基礎編までを終えた。
アリ本が有名だが、こちらの方が評判がよくまとまっていると感じた。
新しい言語でやりたかったというのもあって、python3で書いている。
githubにコードと簡単なコメントあり。

なぜデータ構造とアルゴリズムの勉強を始めたのか

  • 自分は文系出身だが情報工学を専攻していたエンジニアがベースとして持っている知識は身につけようと考えたから。
  • インタプリタの実装をしてみたときや、広告配信サーバ周りの会話でちょっとしたアルゴリズムの話が出ることがあり、エンジニアのベースだと感じたから。

やってみて何かいいことあった?

  • コードやライブラリの計算量を少し意識できるようになった
  • 木の巡回やヒープ、動的計画法など、今までなんとなく見たようなものが体系だって学べた
  • この問題はこんな綺麗に解決できるのか!という感動があるw

コンピュータサイエンスおもしろい!

ハノイの塔を実装する

ハノイの塔についてこちら

前提

  • 3つの杭を3つの配列(orig, tmp, to)で表現する
  • 各配列をスタックとみなし行える操作はpushとpopのみ
  • 円盤1つを配列内のintとして表現。大きな円盤は大きな数字で
    • 例えば、3段のハノイの塔は[3,2,1]といった形で表現する

目標

  • n段のorigをtoに移し替えること

まずは

  • 1段のとき、2段のとき、3段のときを考えてその法則性を探る

1段

  1. orig (1)-> to
orig = []
tmp  = []
to   = [1]

2段

  1. orig (1)-> tmp
# 1段と同じ操作をorig -> tmp
orig = [2]
tmp  = [1]
to   = []
  1. orig (2)-> to
# 1段と同じ操作
orig = []
tmp  = [1]
to   = [2]
  1. tmp (1)-> to
# 1段と同じ操作をtmp -> to
orig = []
tmp  = []
to   = [2,1]

3段

  1. orig (1)-> to
  2. orig (2)-> tmp
  3. to (1)-> tmp
# 2段のときと同じ操作を orig -> tmp で実行
orig = [3]
tmp  = [2,1]
to   = []
  1. orig (3)-> to
# 1段のときと同じ操作
orig = []
tmp  = [2,1]
to   = [3]
  1. tmp (1)-> orig
  2. tmp (2)-> to
  3. orig (1)-> to
# 2段目のときと同じ操作を tmp -> to で実行
orig = []
tmp  = []
to   = [3,2,1]

ちょっと多いけど4段目

  1. orig (1)-> tmp
  2. orig (2)-> to
  3. tmp (1)-> to
  4. orig (3)-> tmp
  5. to (1)-> orig
  6. to (2)-> tmp
  7. orig (1)-> tmp
# 3段のときと同じ操作を orig -> tmp で実行
orig = [4]
tmp  = [3,2,1]
to   = []
  1. orig (4)-> to
# 1段のときの操作
orig = []
tmp  = [3,2,1]
to   = [4]
  1. tmp (1)-> to
  2. tmp (2)-> orig
  3. to (1)-> tmp
  4. tmp (3)-> to
  5. orig (1)-> tmp
  6. orig (2)-> to
  7. tmp (1)-> to
# 3段のときと同じ操作を tmp -> to で実行
orig = []
tmp  = []
to   = [4,3,2,1]

上記の試行から3つの手順に分けられる

N段のハノイの場合

  1. N-1段をorig->tmpに移動
  2. 1番下(N段目)をorig->toに移動
  3. N-1段をtmp->toに移動

手順1と3を再帰的に行うことで

コードにすると(python)

動かした回数を出したい場合は、実際に動かす操作を行なっている箇所、つまりto.appendの前後どちらかでカウントをインクリメントすればよい

def move(n, orig, to, tmp):
    if n == 0:
        return
    # n-1段をorigからtmpに移動する
    move(n-1, orig, tmp, to, cnt)
    # n段目をtoに移動する
    to.append(orig.pop())
    # n-1段をtmpからtoに移動する
    move(n-1, tmp, to, orig, cnt)


import sys

# 引数でハノイの塔の高さを指定する
if len(sys.argv) != 2:
    exit
N = int(sys.argv[1])

# N段の塔、空の塔を配列で擬似的に表す
orig = [i for i in range(N, 0, -1)]
tmp = []
to = []

# origの塔をtoに動かす
move(N, orig, to, tmp)

カウントありのpythonコード

goのチャネルの簡単なまとめ

Goならわかるシステムプログラミングを読んで。

概要

  • キュー構造である
    • ただしランダムアクセスはできず、投入と取り出しのみ行うことができる
  • 並列処理されても正しくデータを受け渡す同期機構である
    • goroutine間での情報共有としての利用が推奨されている
  • 読み込み・書き込みで準備ができるまでブロックする機能である
    • データがない状態で取り出そうとすると、他のgoroutineがデータを投入して読み込みの準備ができるまでブロックして待つ
    • バッファに空きがない状態で書き込もうとすると、他のgoroutineがデータを取り出して空きができるまでブロックして待つ

sample

下記スクリプトを実行する。

package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println("script start")
    done := make(chan bool)

    go func() {
        fmt.Println("start sub()")
        time.Sleep(2 * time.Second)
        fmt.Println("sub() is finished")
        done <- true
    }()

    fmt.Println("I'm waiting for tasks to be done")
    <-done
    fmt.Println("all tasks are finished")
}

main は <-done の箇所で、チャネルであるdoneに何らかのデータが入るのを待っている。
goroutineがdoneにtrueを入れると、all tasks are finishedとなる。

参考

mattnさんのこの記事とても分かりやすいです

ブラウザアクセスでファイルをダウンロードさせる方法

やり方

HTTPレスポンスヘッダーにContent-Disposition: attachmentをセットする。
デフォルトでは、Content-Disposition: inlineとなっており、Webページとして表示される。 ちなみにdispositionの意味は、何かの置き方や配置の仕方、という意味。

 The way in which something is placed or arranged, especially in relation to other things

サンプル

例えばgoで書いてみるとこんな感じ。
localhost:8080 にアクセスするとファイルが素敵なお手紙がダウンロードされる。

package main

import (
    "bytes"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/plain")
    w.Header().Set("Content-Disposition", "attachment; filename=nekootoko3.txt")

    var buf bytes.Buffer
    buf.WriteString("Hello!\n")
    buf.WriteString("I'm nekootoko3\n")
    buf.WriteString("I'm happay everyday!\n")
    buf.WriteString("\n")
    buf.WriteString("Sincerely Yours\n")
    buf.WriteString("nekootoko3")
    txt := buf.Bytes()

    w.Write(txt)
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

参考

depを触ってみたよ

the "official experiment" dependency management tool for the Go languageというdepを触ってみた。
これがgoのライブラリ管理ツールでは公式になっていく模様。
公式ドキュメントを見ながらやってみる。

installation

macの場合、crewで入る。

$ brew install dep
$ brew upgrade dep

その他の場合は下記コマンドで良さそう。
アーキテクチャ、OS、GOPATHなんかに応じてインストールしてくれる模様。

$ curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh

depのコマンドを確認

こんな感じ

bash-3.2$ dep
Dep is a tool for managing dependencies for Go projects

Usage: "dep [command]"

Commands:

  init     Set up a new Go project, or migrate an existing one
  status   Report the status of the project's dependencies
  ensure   Ensure a dependency is safely vendored in the project
  version  Show the dep version information
  check    Check if imports, Gopkg.toml, and Gopkg.lock are in sync

Examples:
  dep init                               set up a new project
  dep ensure                             install the project's dependencies
  dep ensure -update                     update the locked versions of all dependencies
  dep ensure -add github.com/pkg/errors  add a dependency to the project

Use "dep help [command]" for more information about a command.

使ってみる

今回のサンプルに使うプロジェクトを作ってそこに移動する。
このプロジェクトが他の人やプロジェクトに利用されることを想定している場合には下記の場所に作る。

$ mkdir -p $GOPATH/src/github.com/<あなたの名前>/<プロジェクト名>
$ cd $GOPATH/src/github.com/<あなたの名前>/<プロジェクト名>

今回のプロジェクトが他で利用されることはないので、今回はここに作る。

$ mkdir -p $GOPATH/src/<プロジェクト名>
$ cd $GOPATH/src/<プロジェクト名>

dep initコマンドを実行し、現在のプロジェクトを解析して初期化する。

$ dep init
$ ls
Gopkg.lock      Gopkg.toml      vendor

vendorは空のディレクトリで、今後depで入るパッケージはここに。
Gopkg.toml は、現在のプロジェクトの依存関係を記述してあるファイル。

$ cat Gopkg.toml
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
#   name = "github.com/user/project"
#   version = "1.0.0"
#
# [[constraint]]
#   name = "github.com/user/project2"
#   branch = "dev"
#   source = "github.com/myfork/project2"
#
# [[override]]
#   name = "github.com/x/y"
#   version = "2.4.0"
#
# [prune]
#   non-go = false
#   go-tests = true
#   unused-packages = true


[prune]
  go-tests = true
  unused-packages = true

Gopkg.lock は、プロジェクトが依存しているパッケージの依存関係がバージョンなんかも合わせて記載されている?

$ cat Gopkg.lock
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.


[solve-meta]
  analyzer-name = "dep"
  analyzer-version = 1
  input-imports = []
  solver-name = "gps-cdcl"
  solver-version = 1

ここからどんな作業でどんな変化が生まれるをかを知るためgit管理して進める。
試しに、ginでwebサーバーを構築して進める。
プロジェクトのルートディレクトリにapp.goを作る。(ドキュメント丸写し)

package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080
}

ここでプロジェクトの依存関係をインストールしてくれるというdep ensureを叩くと、、、

$ dep ensure -v
# Gopkg.lock is out of sync with Gopkg.toml and project imports:
github.com/gin-gonic/gin: imported or required, but missing from Gopkg.lock's input-imports

Root project is "dep_sample"
 1 transitively valid internal packages
 1 external packages imported from 1 projects
(0)   ✓ select (root)
(1)     ? attempt github.com/gin-gonic/gin with 1 pkgs; 22 versions to try
(1)         try github.com/gin-gonic/gin@v1.2
(1)     ✓ select github.com/gin-gonic/gin@v1.2 w/3 pkgs
(2)     ? attempt github.com/gin-contrib/sse with 1 pkgs; 1 versions to try
(2)         try github.com/gin-contrib/sse@master
...

# Bringing vendor into sync
(1/8) Wrote gopkg.in/go-playground/validator.v8@v8.18.2: new project
(2/8) Wrote gopkg.in/yaml.v2@v2.2.1: new project
(3/8) Wrote github.com/gin-contrib/sse@master: new project
(4/8) Wrote github.com/gin-gonic/gin@v1.2: new project
(5/8) Wrote github.com/golang/protobuf@v1.1.0: new project
(6/8) Wrote github.com/mattn/go-isatty@v0.0.3: new project
(7/8) Wrote github.com/ugorji/go@v1.1.1: new project
(8/8) Wrote golang.org/x/sys@master: new project

依存関係のあるパッケージをベンダー下にインストールしてきてくれる。
Gopkg.lock にも依存関係がたくさん記述されている。
vendorは重いので、.gitignoreにしておくといいかもしれない。

おわりに

プロジェクトをdep initして、パッケージを追加したらdep ensureする。という方針でなんとなくいいのかな。
Gopkg.tomlについてもちゃんと調べよう。

その他

goまだまだこれからという人はこの記事なんかも読んでみるとよいのかも。

CentOS6にscreen-4.6.2をインストールする

ソースを取ってくる

このサイトから、screen-4.6.2.tar.gzのリンク先をコピーして/usr/local/src/下にダウンロードする。

$ cd /usr/local/src/
$ sudo wget https://ftp.gnu.org/gnu/screen/screen-4.6.2.tar.gz

インストール

まずは解凍

$ sudo tar -zxvf screen-4.6.2.tar.gz

インストール手順書を見てみる

$ cd screen-4.6.2
$ less INSTALL

コピペすると長いので割愛。
手順がいくつかに別れていて、手順0(./autogen.sh の実行。中身は /bin/autoreconf を実行するだけ)は普通やる必要ないみたい。
なので、手順1からやる。./configureの実行。
ただし、そのままやると下記のようなエラーが出る。

$ ./configure
...
- select can't count
configure: checking for tgetent...
configure: checking libcurses... 
configure: checking libtermcap...
configure: checking libtermlib...
configure: checking libncursesw...
configure: checking libtinfow...
configure: checking libncurses...
configure: checking libtinfo...
configure: error: !!! no tgetent - no screen

どうやらtgetentなる端末制御関連のライブラリがないらしいので入れよう。

$ sudo yum install ncurses-devel

もう一度 ./configureを実行すると、最後に下のようなメッセージが出て Makefileconfig.hファイルが作られる。

 Now please check the pathnames in the Makefile and in the user
configuration section in config.h.
Then type 'make' to make screen. Good luck.

sudo makeを実行すると、screenファイルが作られるので任意の場所に置くかシンボリックリンクを貼る。
自分は下のようなやり方で、既存のscreenをoldに変えて、新しくできたscreenにシンボリックリンクを貼った。

$ sudo make
$ sudo mv /usr/bin/screen /usr/bin/screen.old
$ sudo ln -s /usr/local/src/screen-4.6.2/screen /usr/bin/

以上。
あとは必要に応じて、~/.screenrcを変えてもらうと良いかと。
多分CentOS7でもやり方は同じだと思う。