見習い魔法使いの日常

果たして魔法が使える日は訪れるのか!?

突貫工事で英単語学習ツールを作る

はじめに...

ちょー適当な記事です。そして、筆者はWeb開発の経験がないです!!

背景

最近英語とのエンカウント率がぐっと上昇。もはや彼を避けるすべなし。

僕は彼との正面からの戦いを決意した…。

と、いうことで、英語学習の中で英単語学習のための単語保存ツールを作ることにしました。ただ、今回の趣旨としては「英単語学習ツールを作るぞ!!」ではなくて、「もうなんでもいいから英単語を保存できて見返せるやつはよ」状態なので動けばいいやの精神でいろんなことをすっ飛ばしてやります。

ほんとに現在進行形で困ってる。

概要

英語論文とかを読むとき知らない単語それはもういっぱい出てくる。その時々で調べるけれど、頭に残らず次にであった時は「あ、この単語前もみた!」で意味を覚えていない。そこで、わからなかった単語を調べて、例文とともに登録すれば覚えるための材料になるのではと考えた。

僕の思い浮かべるフローは以下の通り。

単語の保存

  1. フォームに単語と例文を入れて登録ボタンを押す。
  2. DBに書き込まれる。

登録した単語の表示

  1. URLを叩く。
  2. DBからデータを取ってくる。
  3. 登録した単語たちを表示。

これで、僕がどんな単語にであって、どんな文で出会ったのがわかる(はず)。

作成開始

技術力がないので、パクリ実装をします。

ディレクトリの構成はこんな感じ。

 .
├── db.go
├── main.go
└── templates
    ├── index.html
    └── register.gtpl

main.go

package main

import (
	"fmt"
	"log"
	"net/http"
	"path/filepath"
	"sync"
	"text/template"

	_ "github.com/lib/pq"
)

type templateHandler struct {
	once     sync.Once
	filename string
	templ    *template.Template
}

func (t *templateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	t.once.Do(func() {
		t.templ = template.Must(template.ParseFiles(filepath.Join("templates", t.filename)))
	})
	t.templ.Execute(w, r)
}

func register(w http.ResponseWriter, r *http.Request) {
	fmt.Println("method:", r.Method) //リクエストを取得するメソッド
	if r.Method == "POST" {
		r.ParseForm()
		InsertData(r.Form["word"][0], r.Form["example"][0])
	}
	http.Redirect(w, r, "/", 301)
}

type Data struct {
	Words    []string
	Examples []string
}

func show(w http.ResponseWriter, r *http.Request) {
	words, examples := SelectData()
	p := Data{
		Words:    words,
		Examples: examples,
	}
	tmpl := template.Must(template.ParseFiles(filepath.Join("templates", "index.html")))
	tmpl.Execute(w, p)
}

func main() {
	http.Handle("/", &templateHandler{filename: "register.gtpl"})
	http.HandleFunc("/register", register)
	http.HandleFunc("/show", show)
	log.Println("Webサーバを開始します")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}

まず、大元は前回のブログ記事で扱った本のコードをパクりました。
nishisi.hatenablog.com

付加要素として、フォームから入力を取ってくる必要があったのでその部分の処理は以下のドキュメントを参考にさせていただきました。
https://astaxie.gitbooks.io/build-web-application-with-golang/ja/04.1.html
今回は突貫なのでこのページしか読んでいませんが、時間を作ってしっかり読んでおきたいところ。コードをみてちょこちょこいじっただけだからほとんどわかってない...。

register.html

<html>
<head>
<title></title>
</head>
<body>
<form action="/register" method="post">
    英単語:<input type="text" name="word">
    例文:<input type="text" name="example">
    <input type="submit" value="登録">
</form>
</body>
</html>

index.html

<!DOCTYPE html>
<html lang="en">
    <head>
	<meta charset="UTF-8">
	<title>登録リスト</title>
    </head>
    <body>
	<h3>登録英単語リスト</h3>
	<ul>
	    {{range $i, $v := .Words}}
	    <li>{{$v}} {{index $.Examples $i}} </li>
	    {{end}}
	</ul>
    </body>
</html>

Goのテンプレートもわからず、どうやってスライスを渡して表示すればいいのか...という悩みに下の記事が解決してくれました。ありがとうございます。
golang : template の range で index を使う - i++

最後はデータベース。初めてPostgreSQLを使って見ました。セットアップは以下の通り。

$ go get github.com/lib/pq              # goのパッケージをダウンロード
$ brew install postgresql                  # brewを使ってインストール
$ brew services start postgresql    # postgersqlのサービス開始
$ createdb eng-words                     # データベースを作成
$ psql eng-words                             # データベースにアクセス
# データベースの作成
eng-words=# CREATE TABLE wordlist (uid integer, words character varying(20) ,example varying(200)) WITH (OIDS=FALSE);

データベースも2年ほど前授業で触ったきりだから、よく覚えていない。あとはGoでデータを出し入れできればOK。以下参考にしたドキュメント。
https://astaxie.gitbooks.io/build-web-application-with-golang/ja/05.4.html

db.go

package main

import (
	"database/sql"

	_ "github.com/lib/pq"
)

func InsertData(word string, example string) {
	db, err := sql.Open("postgres", "dbname=eng-words sslmode=disable")
	checkErr(err)

	stmt, err := db.Prepare("INSERT INTO wordlist(word,example) VALUES($1,$2) RETURNING uid")
	checkErr(err)
	_, err = stmt.Exec(word, example)
	checkErr(err)
}

func SelectData() ([]string, []string) {
	var words []string
	var examples []string
	db, err := sql.Open("postgres", "dbname=eng-words sslmode=disable")
	rows, err := db.Query("SELECT word,example FROM wordlist;")
	checkErr(err)
	for rows.Next() {
		var word string
		var example string
		err = rows.Scan(&word, &example)
		checkErr(err)
		words = append(words, word)
		examples = append(examples, example)
	}
	return words, examples
}

func checkErr(err error) {
	if err != nil {
		panic(err)
	}
}

あとは、コンパイルして実行するだけ。

突貫結果

http://localhost:8080 にアクセスして単語と例文を入力し登録ボタンを押す。
f:id:Nishisi:20180418002315p:plain

http://localhost:8080/showにアクセスするとDBに登録された内容が表示される。
f:id:Nishisi:20180418002236p:plain

まとめ

急ピッチで動くことだけを考えて作ってつもりが、ここまでくるのにエラーなどにも捕まって5時間かかりました。そして!作ったのはいいけど使う気しないーー。まあ自分でも色々ツッコミどころはあります。見た目がダサいとかログが全く出力されないとか入力を検査してないとか etc。まだまだGo自体もよくわかっていないので、今後の学習とともにアップグレードをしていけばいいかなと思います。登録すればDBにとりあえず書き込まれるので、我慢して使ってみます...。

Go言語によるWebアプリケーション開発を読んでみたけど、Goの文法が主にわからない件

背景

長らく僕のPCのデータとして君臨していた書籍をついに開くときがきました。この書籍発売したのが2016年。当時Goにはまっていた友人に借りるも、結局開かぬまま返却。その後、Goの勉強をして改めて読みたいと思い図書館で借りるも中途半端に1章くらい読んでまた返却。ついに電子版を購入するも、長らくただの容量として僕のPCを占領していたのですが、ついに解禁されました。

いやー長かった。そして、よしやるぞ!と意気込んだの良かったけれどGoの文法がわからないという初歩的なところでつまずきました。かれこれ1年触ってなかったら、そりゃもう覚えてないよ。

と、いうことで、今回は文法的 わからなかったことを中心にまとめていきたいと思います。

1章 WebSocketを使ったチャットアプリケーション

sync.Once型について

HTMLなどをハードコードしないで、テンプレートにまとめちゃいましょう!ってときに、テンプレートのコンパイルは一回だけにしましょうという処理に使われたやつです。読んでるだけだとへぇーこうやるんだ程度で進んでたけど、まあよくわかってない。

ということで、syncパッケージについて調べてみよう。
godocを叩くと...。

$ go doc sync
package sync // import "sync"

Package sync provides basic synchronization primitives such as mutual
exclusion locks. Other than the Once and WaitGroup types, most are intended
for use by low-level library routines. Higher-level synchronization is
better done via channels and communication.

Values containing the types defined in this package should not be copied.

type Cond struct{ ... }
    func NewCond(l Locker) *Cond
type Locker interface{ ... }
type Map struct{ ... }
type Mutex struct{ ... }
type Once struct{ ... }
type Pool struct{ ... }
type RWMutex struct{ ... }
type WaitGroup struct{ ... }

同期処理のパッケージですね。Once型あったので、詳細をみます。

$ go doc sync.Once
type Once struct {
	// Has unexported fields.
}
    Once is an object that will perform exactly one action.


func (o *Once) Do(f func())

書籍の通り、Doメソッドを持ってるみたい。

$ go doc sync.Once.Do
func (o *Once) Do(f func())
    Do calls the function f if and only if Do is being called for the first time
    for this instance of Once. In other words, given

    var once Once

    if once.Do(f) is called multiple times, only the first call will invoke f,
    even if f has a different value in each invocation. A new instance of Once
    is required for each function to execute.

    Do is intended for initialization that must be run exactly once. Since f is
    niladic, it may be necessary to use a function literal to capture the
    arguments to a function to be invoked by Do:

    config.once.Do(func() { config.init(filename) })

    Because no call to Do returns until the one call to f returns, if f causes
    Do to be called, it will deadlock.

    If f panics, Do considers it to have returned; future calls of Do return
    without calling f.

sync.Once.DoはOnceインスタンスに対して、最初にDoが呼び出された場合だけ関数fを呼び出す。
なるほど!!加えて、書籍には遅延初期化うんぬんかいてるけど、呼び出しがない限り実関数fも呼び出されないわけね。

独自のハンドラについて

初めて読んだとき意味がわからなくて調べたから覚えてる。まず、ハンドラって何?独自ってどういうこと?って苦しみました。ここでいうハンドラは普通にhttp.Handleなんだよね。で、独自っていうのが http.Handleはstring型とHandler型を受け取るんだけど Handlerってのはインターフェースだからこの要件を満たして独自のものを作ろうってこと。最初は意味がわからなかった。

$ go doc http.Handle
package http // import "net/http"

func Handle(pattern string, handler Handler)
    Handle registers the handler for the given pattern in the DefaultServeMux.
    The documentation for ServeMux explains how patterns are matched.

$ go doc http.Handler
package http // import "net/http"

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}
    A Handler responds to an HTTP request.

    ServeHTTP should write reply headers and data to the ResponseWriter and then
    return. Returning signals that the request is finished; it is not valid to
    use the ResponseWriter or read from the Request.Body after or concurrently
    with the completion of the ServeHTTP call.

    Depending on the HTTP client software, HTTP protocol version, and any
    intermediaries between the client and the Go server, it may not be possible
    to read from the Request.Body after writing to the ResponseWriter. Cautious
    handlers should read the Request.Body first, and then reply.

    Except for reading the body, handlers should not modify the provided
    Request.

    If ServeHTTP panics, the server (the caller of ServeHTTP) assumes that the
    effect of the panic was isolated to the active request. It recovers the
    panic, logs a stack trace to the server error log, and either closes the
    network connection or sends an HTTP/2 RST_STREAM, depending on the HTTP
    protocol. To abort a handler so the client sees an interrupted response but
    the server doesn't log an error, panic with the value ErrAbortHandler.


func FileServer(root FileSystem) Handler
func NotFoundHandler() Handler
func RedirectHandler(url string, code int) Handler
func StripPrefix(prefix string, h Handler) Handler
func TimeoutHandler(h Handler, dt time.Duration, msg string) Handler

書籍では、独自のハンドラとして上述のsync.Onceを用いたサブディレクトリのHTMLを返すハンドラを作りました。この内容しょっぱなですが、結構難しいのではなかろうか。

バッファ付きのチャネルです。

はは、チャネルってなんだっけ?まとめると長くなりそうなので、別の記事で書きます。

ヘルパー関数

ヘルパー関数っ なんぞやあぁ!と最初は思いましたがまあ他言語でいう コンストラクタ みたいなやつでしょう。書籍にも記述がありますが、Goはクラスがなく構造体で代用しているのでヘルパー関数を使ってクラスっぽくしてるって僕は解釈してます。

まとめ

ふぅ。ほんとは1章丸々書きたかったけど、長くなりすぎるのもいやなのでこの辺で切りました。書籍の続きはログの出力からなので、1章のアプリケーションのコードに関する感じた疑問は全部かけたかな。個人的にはHTTPハンドラがよくわからなくて苦戦し、わかってしまえばもうちょろいって印象です。この辺りはWe開発経験のある人だとつまらない内容なのでしょう。僕はその経験がなく概念がわかっていなかったので苦労しました。ただ、調べる過程で何度も文章を読み直したりしたので、さらっと読んだ時よりすごく理解が深まりました。

このプログを書いてる段階で3章まで読んでいるのですが、結構小さな疑問とかはほったらかしにしてるので、書籍を読み進めつつ、次は2章をじっくりと振り返っていきたいと思います。

クリーンインストールしました!!

背景

 また今年も春の季節がやってまいりました。毎年この季節になるとPCのクリーンインストールをするかしないか考えます。ただ時間もかかりますし、特に困っていることもないので今回は見送る予定でした。

しかし、それは突然訪れた。

 デスクトップの移動をしても強制的に前のものに戻されたり、チカチカと点滅を繰り返したり。僕の相棒が調子を崩しているではないか!?こういう時の再起動をしても改善されなかったのでクリーンインストールをすることにします。

 僕はMacを使い続けて4年になるので、まあ結構設定をいじってます。毎回クリーンインストールするたびに、再設定に余計な時間がかかっていたのでもうまとめちゃいます。

あ、ただし内容はちょーオレオレですよ。

1.クリーンインストール

 Macをまっさらな状態にしてあげます。クリーンインストールの方法はググればすぐに出てくるので省きます。

2.キーボード周り

 私のMacがUSキーボードということもあり、いくつか設定する必要があります。

システム環境設定>キーボードからまずキーボードの項目。

f:id:Nishisi:20180405185021p:plain

キーのリピートとリピート入力認識までの時間を速いに振ります。基本的にエディタにはVimプラグインを入れるのでリピートとか認識が遅いとイライラします。

また就職キーからCaps LockをCtrlに変更します。

f:id:Nishisi:20180405185302p:plain

これは僕の癖です。この話をしたら友人と宗教的な戦争になりかけたので大きな声で言えませんが、僕は重宝してます。

USキーボードだとデフォルトでは日本語入力ができないので、入力ソースから日本語を追加します。

f:id:Nishisi:20180405185724p:plain

これで日本語入力と英字入力の切り替えは Ctrl + Space でできるようになります。

あとはショートカットをちょこちょこいじるのですが、まあ困った時でいいかなって感じ。

3.トラックパッド

トラックパッドも結構オレオレ設定です。そのため他人のトラックパッドを触った時はカーソルのスピードとか選択の仕方とかで戸惑うこともしばしば。

システム環境設定>トラックパッドからポイントとクリックを選択。

f:id:Nishisi:20180405190201p:plain

まずは「タップでクリック」にチェックをつけます。そして「軌跡の速さ」を少しあげます。

また、ドラッグを3本指に設定したので、アクセスビリティーから3本指のドラックを有効にします。

システム環境設定>アクセシビリティ>マウスとトラックパッドトラックパッドオプション

f:id:Nishisi:20180405190443p:plain

トラックパッド周りは以上。

4.Dockなど

普段からDockは非表示にする人なので、そいつを設定します。

システム環境設定>DockからDockを自動的に表示/非表示にチェックを入れます。

また、システム環境設定>一般からメニューバーとDock を暗くするにチェックを入れます。理由?Darkでかっこいいから。

5.Homebrew

brewを入れてやります。
macOS 用パッケージマネージャー — macOS 用パッケージマネージャー

一応前の環境でのパッケージリストを保存してはいるのですが、まあ必要になったら入れるって方針です。

6.ターミナル周り

一応dotfileをgithubに置いているので、それを反映させます。

詳しい方法は僕のリポジトリに載せる(予定)なので、そちらを見てください。dotfilesを作るのに結構時間がかかり、かつ内容はもうほとんど覚えていませんが、すぐに環境を作れるので過去の自分をなでなでしてやりたい。よくやった!!

github.com

7.アプリ

最後は普段使っているアプリを入れていきます。

Apple Store

Slack

Slack

  • Slack Technologies, Inc.
  • ビジネス
  • 無料

Magnet マグネット

Magnet マグネット

  • CrowdCafé
  • 仕事効率化
  • ¥120


その他

www.alfredapp.com

ランチャーアプリです。Sportlightを使っていましたが、こちらに乗り換えました。

www.jetbrains.com

JetBrainsのToolBoxです。学生は無償なので入れないなんて考えられない。社会人になったら買いたいですよ。


しゅうりょーーーー。

まとめ

こんな感じで完了です。
なんやかんやでダンロードやインストールに時間がかかるのでめんどくさいです。

本当は今季は見送る予定でしたが、調子悪くなったから致し方なし。
ヤバかったところもクリーンインストールして改善されたので、そこにホッとしてます(ハード的なものでなくてほんとよかった)。

多分、クリーンインストールする機会があればこのページを見ながらやるので、追加設定などがあればこちらのページを更新していきます。

YAPC:Okinawa 2018に参加しました!!

YAPC:Okinawaに参加してきました!!
yapcjapan.org

前回はOsakaに学生サポートで参加させていただいた経験もあります。

Perlをごりごり触っていたのは2年前で、かなりのスパンが空いてしまったためPerl中心の話は結構難しかったです。
Perlのイベントなので当たり前ですが。
それでも、面白くまたモチベーションが上がるような話もたくさん聞くことができました。
エンジニアに対する尊敬がグーーーンと上昇しましたね。

今回ボランティアという立場であるために、完全に集中して聞けるトークは少なかったのですが、新しいモチベーションが生まれました。

トークしたーい。

もちろんトークできるような技術も背景も何もありません。ネタも。
でも登壇者さんがすごく楽しそうにはなして、参加者も楽しく聞いてって空間がもう素晴らしい。
前回は登壇者の話を頑張って理解しようという意気込みで聞いていたため、今回のような会場の雰囲気に対して何かを感じることがありませんでしたね。
今後のモチベーションとしては、トークできるくらい今やっていることに打ち込んで、機会を得るべくとりあえず応募してみるってことですね。

ただ、スケジュールの都合で懇親会に参加できなかったことだけが心残りです。
いろんな方とお話しして繋がりを作りたかった...。

ともあれ僕もLTのイベントとかあれば、どんどん申し込んでいこうと思いました。
そのためにも、話せる内容が必要なので日頃のコーディングでネタを探していこうと思いました。

最後になりましたが、イベントの運営の皆様、ありがとうございました。

論文読みの準備

前回の更新からもう1ヶ月以上空いていることに驚きです。

少し気持ちが入れ替わったというか、最近やっていることを軽くまとめて更新していこうと思います。
今まではでかい記事を書こうとして、少しずつまとめていたものの結局最後までたどり着かなくてお蔵入りしてたので。

最近ツイッターで見かけた、こちらのスライドを参考に論文を読むための準備を整えました。

www.slideshare.net

僕もいくつか英語論文を読みましたが、英語力のなさも手伝ってそれはもう多大な時間をかけて最初から最後まで読んでましたね。
それも一回では理解できないので、なんども。

何本か読んで行くうちに、論文中でも重要そうな箇所とそうでもない箇所があることに気づいていましたが、それでも一応全部目は通すから時間はかかるかかる。
上記のスライドを見て、読み方をパクることにしました。

せんせもいってました「世の中、パクリ学習ですよ!!」(<-いい意味です)

今のやり方は無駄が多し!!

ということで、最後のスライドの文書をLatexで作りました。

早速スタックされた論文5本くらいを読んでいきます。

Ubuntu16_04にDropboxを入れた

サーバ側とファイルをあれこれするのがめんどくさくなったので、クラウドストレージサービスを用いてイライラを解消することにしました。

手順としてはこちらのページのまんまです。
www.dropbox.com

$ cd ~ && wget -O - "https://www.dropbox.com/download?plat=lnx.x86_64" | tar xzf -
$ ~/.dropbox-dist/dropboxd

あとはスクリプトを落としてきて、パスを通してあげます。
僕はホームディレクトリに .bin を作ってそこにあれこれ入れています。

# 個人の好み
$ mkdir .bin && cd .bin
$ echo '~/.bin:PATH' > ~/.zshrc
$ exec $SHELL -l
$ wget -O dropbox "https://www.dropbox.com/download?dl=packages/dropbox.py"
$ chmod 750 dropbox
$ dropbox
Dropbox command-line interface

commands:

Note: use dropbox help <command> to view usage for a specific command.

 status       get current status of the dropboxd
 throttle     set bandwidth limits for Dropbox
 help         provide help
 puburl       get public url of a file in your dropbox\'s public folder
 stop         stop dropboxd
 running      return whether dropbox is running
 start        start dropboxd
 filestatus   get current sync status of one or more files
 ls           list directory contents with current sync status
 autostart    automatically start dropbox at login
 exclude      ignores/excludes a directory from syncing
 lansync      enables or disables LAN sync
 sharelink    get a shared link for a file in your dropbox
 proxy        set proxy settings for Dropbox

$ dropbox start

Dropboxは他のサービスと比べると容量は少ないですが、今回の用途ではそれほど必要としないので問題ありません。

線形代数の勉強してます

本を読んだら感想をブログに書こうと思ってはや、4ヶ月。
これまでたった一冊の本も完読することができてませんでした。

平行して同時に読み進めているのはいくつかありますが...。
教授に「参考書に1週間もかけるな!!」って言われたのを思い出します。

早速本題へ。

今日はとある事情により図書館で時間を潰そうと立ち寄ったところ、「マンガでわかる線形代数」という本に目が止まりました。
中を見ると登場人物の女の子がたいそう可愛いではないか。
それに、中も絵(マンガ)がメインで、線形代数の全体像を掴むのに適してるかも。

ということで全部読みました。

全体像を掴むことが目的だったので、本の全てを理解するほど深くは読み込まず、2時間かからないくらいで読めてしまいました。
うむ、満足。

別の教材を使って線形代数を勉強していますが、字・数式ばかりでなんちゃって理系の僕には辛かったんですよ。
マンガ本は敬遠しちゃう傾向があったのですが、副教材としては素晴らしいかもしれないですね。

とりあえず、完読一冊目!!

線形代数をやるに至った経緯とかは、本命の別教材を終えた時にでも書こう。

図書館に来て、適当な本を読むのも案外楽しいかも。

www.amazon.co.jp