技術ネタじゃないけど.
Apple Musicを使ってるんだけど, 欲しい曲が無い時にリクエストの出し先がわからなくて困ってた.
やっと見つけたので.
どんどん曲数増えればいいなぁ.
技術ネタじゃないけど.
Apple Musicを使ってるんだけど, 欲しい曲が無い時にリクエストの出し先がわからなくて困ってた.
やっと見つけたので.
どんどん曲数増えればいいなぁ.
こんな面白そうな記事があったので作ってみた.
全ての最適化を実装するのはちょっとしんどかったので(というか途中で飽き始めた)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倍くらい早い.
いやー, 面白い.
vagrantでCentOS6.7を入れて試している. (CentOS7はちょっと今回スルーで....)
ここからライセンス同意すればダウンロードできる.
RPMで配布されている.
何気にアカウント登録を求められるのがめんどい...
64bit版しかないけど, 今時32bitもなかろう.
とりあえず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
一度試すとbcが要るといわれたので入れる.
[vagrant@localhost Disk1]$ sudo yum install bc
[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
[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.
[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.
下記を実行すれば環境変数はいい感じにしてくれるらしい.
[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
いい感じ.
[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>
Windows10で.
テンプレートが定義済みの場合と未定義の場合で処理を分けられる.
割と便利そう.
とりあえずコードを置いておく.
{{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>
golangのテンプレートエンジン(html/template)ではデフォルトで組み込み関数が用意されていますが, 物足りない場合は自作する事になります.
基本は以下です.
FuncMapに登録する関数は以下の規約があるようです.
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> <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
以前, templateパッケージを利用する方法を調べたけど, 構成的な意味でもうちょっと実用的な例が欲しいので書き残しました.
コードはgithubに置いておきます.
https://github.com/twinbird/go-template-samplegithub.com
ブログシステムを考えてみます.
一般に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}}
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を呼び出す仕組みに変えるだけで高速化もできそうです.
(どの程度早くなるかはわかりませんが)