write ahead log

ロールフォワード用

goでbrainfuckを書いた

こんな面白そうな記事があったので作ってみた.

github.com

全ての最適化を実装するのはちょっとしんどかったので(というか途中で飽き始めた)2段階目まで作った.

実行速度を見ると元記事通り, 明らかに差が出るのが面白い.

試行回数1だけど.

$ go test -bench .
BenchmarkFactorSimpleRun-4                     1        56483572100 ns/op
BenchmarkMandelbrotSimpleRun-4                 1        202497152900 ns/op
BenchmarkFactorOptimizedRun-4                  1        24686566000 ns/op
BenchmarkMandelbrotOptimizedRun-4              1        79534122700 ns/op
BenchmarkFactorOptimized2Run-4                 1        13370674300 ns/op
BenchmarkMandelbrotOptimized2Run-4             1        41272916900 ns/op
PASS
ok      _/C_/msys64/home/IMAI/dropbox/lab/gobrainfuck   418.021s

Optimize2段階目時点で5倍くらい早い.

いやー, 面白い.

参考

JITコンパイルでの冒険

Goでベンチマーク - Block Rockin’ Codes

CentOS6にOracle 11g XEを入れる

vagrantでCentOS6.7を入れて試している. (CentOS7はちょっと今回スルーで....)

1. ダウンロード

ここからライセンス同意すればダウンロードできる.

RPMで配布されている.

何気にアカウント登録を求められるのがめんどい...

64bit版しかないけど, 今時32bitもなかろう.

2. インストール

2.1. zipを解凍して中身を確認

とりあえずzipを解凍

[vagrant@localhost ~] unzip oracle-xe-11.2.0-1.0.x86_64.rpm.zip

Disk1ディレクトリが出来る.

中身はこんな感じ.

[vagrant@localhost ~]$ cd Disk1/
[vagrant@localhost Disk1]$ ls
oracle-xe-11.2.0-1.0.x86_64.rpm  response  upgrade

2.2. 依存パッケージのインストール

一度試すとbcが要るといわれたので入れる.

[vagrant@localhost Disk1]$ sudo yum install bc

2.3. [番外編]vagrantのswapが足りないといわれたので増やす

[vagrant@localhost Disk1]sudo swapon -s
[vagrant@localhost Disk1]df
[vagrant@localhost Disk1]sudo dd if=/dev/zero of=/swapfile bs=1M count=2500
[vagrant@localhost Disk1]sudo mkswap /swapfile
[vagrant@localhost Disk1]sudo swapon /swapfile

2.4. rpmでインストール

[vagrant@localhost Disk1]$ sudo rpm -ivh oracle-xe-11.2.0-1.0.x86_64.rpm
Preparing...                ########################################### [100%]
   1:oracle-xe              ########################################### [100%]
   Executing post-install steps...
   You must run '/etc/init.d/oracle-xe configure' as the root user to configure the
    database.

2.5. configureを実行

[vagrant@localhost ~]$ sudo /etc/init.d/oracle-xe configure

Oracle Database 11g Express Edition Configuration
-------------------------------------------------
This will configure on-boot properties of Oracle Database 11g Express
Edition.  The following questions will determine whether the database should
be starting upon system boot, the ports it will use, and the passwords that
will be used for database accounts.  Press <Enter> to accept the defaults.
Ctrl-C will abort.

Specify the HTTP port that will be used for Oracle Application Express [8080]:  #[デフォルトで良いのでEnter]

Specify a port that will be used for the database listener [1521]: #[デフォルトで良いのでEnter]

Specify a password to be used for database accounts.  Note that the same
password will be used for SYS and SYSTEM.  Oracle recommends the use of
different passwords for each database account.  This can be done after
initial configuration: #[passwordとか適当に]
Confirm the password:  #[passwordとか適当に]

Do you want Oracle Database 11g Express Edition to be started on boot (y/n) [y]:y

Starting Oracle Net Listener...Done
Configuring database...Done
Starting Oracle Database 11g Express Edition instance...Done
Installation completed successfully.

3. 接続確認

3.1. SQLPlusの設定

下記を実行すれば環境変数はいい感じにしてくれるらしい.

[vagrant@localhost ~]$ . /u01/app/oracle/product/11.2.0/xe/bin/oracle_env.sh

~/.bashrcに環境変数設定用シェルスクリプトの呼び出しを追加.

[vagrant@localhost ~]$ vi ~/.bashrc
# User specific aliases and functions
. /u01/app/oracle/product/11.2.0/xe/bin/oracle_env.sh

3.2. SQLPlusで確認

いい感じ.

[vagrant@localhost ~]$ sqlplus system

SQL*Plus: Release 11.2.0.2.0 Production on Thu Oct 12 03:07:33 2017

Copyright (c) 1982, 2011, Oracle.  All rights reserved.

Enter password:

Connected to:
Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production

SQL>

参考

Linux(CentOS) 64bit版へOracle XE(Express Edition)をインストール

Oracle XEをVagrantで入れようとしたらswapが足りなかった

goのtemplateパッケージのblockアクションを使う

テンプレートが定義済みの場合と未定義の場合で処理を分けられる.

割と便利そう.

とりあえずコードを置いておく.

{{define "layout"}}

<html>
    <head></head>
    <div>
        {{block "contents" .}}
        not found
        {{end}}
    </div>
</html>

{{end}}
{{define "contents"}}
<h1>contents</h1>
{{end}}
package main

import (
    "html/template"
    "os"
)

func main() {
    if len(os.Args) > 1 && os.Args[1] == "no" {
        t, _ := template.ParseFiles("layout.html")
        t.ExecuteTemplate(os.Stdout, "layout", nil)
    } else {
        t, _ := template.ParseFiles("layout.html", "contents.html")
        t.ExecuteTemplate(os.Stdout, "layout", nil)
    }
}

実行結果

外部テンプレートを使う場合.

$ ./sample.exe


<html>
        <head></head>
        <div>

<h1>contents</h1>

        </div>
</html>

blockアクションで表示.

<html>
    <head></head>
    <div>
        
        not found
        
    </div>
</html>

goのhtml/templateでカスタム関数を追加する

golangのテンプレートエンジン(html/template)ではデフォルトで組み込み関数が用意されていますが, 物足りない場合は自作する事になります.

考え方

基本は以下です.

  • template.FuncMapを作る(関数名と関数のmap)
  • このmapをテンプレートのFuncs関数に渡して登録する

FuncMapに登録する関数は以下の規約があるようです.

  • 引数はいくつでもOK
  • 戻り値は1つだけ
  • ただし2つ目がerrorなら戻り値2つもOK

2つ目の戻り値errorがnilでなければ処理は中断し, Execute関数はtemplate.ExecErrorを返します.

サンプルコード

実用的とは言えない例ですが.

package main

import (
    "fmt"
    "html/template"
    "net/http"
    "time"
)

// 年-月-日にフォーマット
func formatDate(t time.Time) string {
    layout := "2006-01-02"
    return t.Format(layout)
}

// 引数の文字列を頭文字を大文字にして連結して返す.
// 合計文字数が15バイトを超えたらエラーにする
func CatAndUpper(args ...string) (string, error) {
    ret := ""
    for _, s := range args {
        ret += s
    }
    if len(ret) >= 15 {
        return "", fmt.Errorf("too long string")
    }
    return ret, nil
}

// 頭に$を付けるだけ
func formatDollar(c int) string {
    return fmt.Sprintf("$%d", c)
}

func testHandler(w http.ResponseWriter, r *http.Request) {
    // カスタム関数を登録
    fm := template.FuncMap{
        "fdate":   formatDate,
        "fdollar": formatDollar,
        "cat":     CatAndUpper,
    }
    t := template.New("tmpl.html").Funcs(fm)
    t, _ = t.ParseFiles("tmpl.html")

    // 流し込む適当なデータ
    data := &struct {
        Date   time.Time
        Dollar int
    }{}
    data.Date = time.Now()
    data.Dollar = 100

    // テンプレート実行
    fmt.Println(t.Execute(w, data))
}

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

サンプルテンプレート

<html>
    <head>
   </head>
    <body>
        <div>日付: {{fdate .Date}}</div>
        <div>ドル: {{fdollar .Dollar}}</div>
        <div>つなげる: {{cat "string" "string2"}}</div>
        <div>長いのつなげる: {{cat "string" "string2" "string3"}}</div>
    </body>
</html>

生成されるHTML

途中でエラーを返しているので途中まで生成して止まっています.

<html>
    <head>
   </head>
    <body>
        <div>日付: 2017-09-19</div>
        <div>ドル: $100</div>
        <div>つなげる: stringstring2</div>
        <div>長いのつなげる: 

コンソール

テンプレート内でエラーを起こしたのでExecuteメソッドのエラーが帰ってきています.

今回はfmt.Printlnで標準出力へ出してみました.

$ ./sample.exe
template: tmpl.html:8:32: executing "tmpl.html" at <cat "string" "string...>: error calling cat: too long string

golangでテンプレートエンジンを使う(その2)

以前, templateパッケージを利用する方法を調べたけど, 構成的な意味でもうちょっと実用的な例が欲しいので書き残しました.

コードはgithubに置いておきます.

https://github.com/twinbird/go-template-samplegithub.com

やりたいこと

  • どのページでも使うlayoutをまとめたい
  • 部品的なHTMLを使いまわしたい

作るもののイメージ

ブログシステムを考えてみます.

一般に2種類のページ枠があるでしょう.

  • 一般公開ページ
  • 管理者ページ

もう少し具体的に以下のページを考えます.

  • 一般公開ページ
    • 個別記事の編集
    • 個別記事の参照
  • 管理者ページ
    • 個別記事の参照

一般公開ページと管理者ページではメニューが異なるという事にします.

挙げるとキリがないのでこの辺で.

コード

長いですが, メモなのでそれぞれ全部載せます.

テンプレートは全体的に適当です.

ディレクトリ構成
$ tree .
.
├── main.go
└── templates
    ├── admin_menu.html    # 管理者用メニュー(define "menu")
    ├── entry_editor.html  # 記事エディタ(define "contents")
    ├── entry_view.html    # 記事ビュワー(define "contents")
    ├── layout.html        # 共通レイアウト(define "layout")
    └── public_menu.html   # 公開用メニュー(define "menu")

1 directory, 6 files
テンプレートコード
共通レイアウト

layout.html

ここでdefine "layout"としています.

{{define "layout"}}
<!DOCTYPE html>
<html>
    <head>
       <title>{{.Title}}</title>
       <meta charset="utf-8">
   </head>
    <body>
        <div class="menu">
            {{template "menu" .}}
        </div>

        <div class="container">
            {{template "contents" .}}
        </div>
    </body>
</html>
{{end}}
管理者用メニュー

admin_menu.html

ここと公開用メニューでdefine "menu"としています.

{{define "menu"}}
<!-- Admin menu -->
<ul>
    <li><a href="">New Entry</a></li>
    <li><a href="">Edit Entry</a></li>
</ul>
{{end}}
公開用メニュー

public_menu.html

{{define "menu"}}
<!-- Public menu -->
<ul>
    <li><a href="">About me</a></li>
    <li><a href="">Latest Entry</a></li>
</ul>
{{end}}
記事エディタ

以下2つはdefine "contents"です.

entry_editor.html

{{define "contents"}}
<textarea>{{.Entry.Text}}</textarea>
{{end}}
記事ビュワー

entry_view.html

{{define "contents"}}
<pre>
{{.Entry.Text}}
</pre>
{{end}}
goのコード
package main

import (
    "fmt"
    "html/template"
    "net/http"
)

// RenderData はテンプレートへ引き渡す表示用のデータです
type RenderData struct {
    Title string
    Entry *Entry
}

// Entry は1記事に相当します
type Entry struct {
    Text string
}

var (
    // サンプルデータ
    sampleEntry *Entry      = &Entry{Text: "This is a sample entry"}
    sampleData  *RenderData = &RenderData{Title: "sample", Entry: sampleEntry}
    // テンプレートディレクトリ
    templatesDir string = "templates"
)

func main() {
    // admin:編集ページ
    http.HandleFunc("/admin/edit/someEntry", editAdminSomeEntry)
    // admin:参照ページ
    http.HandleFunc("/admin/refer/someEntry", referAdminSomeEntry)
    // public:参照ページ
    http.HandleFunc("/refer/someEntry", referSomeEntry)

    http.ListenAndServe(":8080", nil)
}

// admin用:編集ページ
func editAdminSomeEntry(w http.ResponseWriter, r *http.Request) {
    execTemplate(w, sampleData, "layout", "admin_menu", "entry_editor")
}

// admin用:参照ページ
func referAdminSomeEntry(w http.ResponseWriter, r *http.Request) {
    execTemplate(w, sampleData, "layout", "admin_menu", "entry_view")
}

// public用:参照ページ
func referSomeEntry(w http.ResponseWriter, r *http.Request) {
    execTemplate(w, sampleData, "layout", "public_menu", "entry_view")
}

// execTemplate はfilesのテンプレートからHTMLを構築して,
// wに対して書き込みます.
// HTML構築の際にはdataを利用します.
// filesで指定するテンプレートには必ず{{define "layout"}}された
// ものを1つだけ含む必要があります.
func execTemplate(w http.ResponseWriter, data interface{}, files ...string) {
    // 渡された引数からテンプレートパスの集合を作る
    var pathes []string
    for _, f := range files {
        p := fmt.Sprintf("%s/%s.html", templatesDir, f)
        pathes = append(pathes, p)
    }
    //上記で作ったパスの一覧を使ってテンプレートを作る
    template := template.Must(template.ParseFiles(pathes...))
    // layoutが必ず基点になるという事にする
    template.ExecuteTemplate(w, "layout", data)
}

テンプレートをキャッシュするか

init時に全てのテンプレートをParseしてmapなどに保存しておくことが出来るはずです.

今回のexecTemplate関数をmapからテンプレートを取得し, パッケージ標準のExecuteTemplateを呼び出す仕組みに変えるだけで高速化もできそうです.
(どの程度早くなるかはわかりませんが)

vimのnetrwを使う

vimには標準プラグインとしてnetrwというファイラが付属している.

あんまり触ってなかったんだけど,ふと思い立って使い方を調べてみた.

起動

コマンド 機能
:Ex カレントバッファでnetrwを開く
:Tex タブを開いてnetrwを開く

ディレクトリ移動

操作 機能
Enter 選択中のディレクトリを開く
o バッファを水平分割して開く
v バッファを垂直分割して開く
t 新しいタブで開く
- 1つ上の階層へ戻る
u 以前にいたディレクトリへ戻る
U uで戻る前にいたディレクトリへ戻る
c 開いているバッファのカレントをvimのカレントに変更

CRUD操作

操作 機能
Enter 選択中のファイルを開く
% 新規ファイルを作成
d ディレクトリを作成
D カーソル下orマークしたファイル/ディレクトリを削除
R ファイル/ディレクトリをリネーム

もうちょい複雑な操作

操作 機能
mf ファイルのマーク/アンマーク
mu 全てのアンマーク
mr ファイル名指定でのマーク(*でのワイルドカード可)
mt コピー/移動先にカーソル下のディレクトリを設定
mc マークしたファイルをコピー
mm マークしたファイルを移動
md マークしたファイルを差分を見る
mz マークしたファイルを圧縮/展開

複雑な操作の手順

複雑な操作は手順がややこしいのでメモっておく.

  1. mtで移動/コピー先を指定(カーソル下でmtを入力)
  2. mfなどで移動/コピーするファイルをマーク
  3. mc/mmなどでコピーや移動を実行

その他

操作 機能
i 表示を切り替え
s 表示をソート