http://d.hatena.ne.jp/thata/20100207/1265554365 about Curl command
---------------------------------------------------------------------
https://rei19.hatenablog.com/entry/2015/09/08/093606 elixir lang mix-otpの詳述
大変参考になる
エリクサーは1.6以上では無料電子ブックが公開されている
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
https://qiita.com/mochizukikotaro/items/b09116e0ad2d30e37098 :: gorm
https://medium.com/@cgrant/developing-a-simple-crud-api-with-go-gin-and-gorm-df87d98e6ed1 :: RESTful-Api Server by gin
package main
import (
"fmt"
"sync"
// "time"
)
func main() {
type value struct {
mu sync.Mutex
value int
}
var wg sync.WaitGroup
printSum := func(v1, v2 *value) {
defer wg.Done()
v1.mu.Lock()
defer v1.mu.Unlock()
// time.Sleep(2*time.Second)
v2.mu.Lock()
defer v2.mu.Unlock()
fmt.Printf("sum=%v\n", v1.value + v2.value)
}
var a, b value
wg.Add(2)
go printSum(&a, &b)
go printSum(&b, &a) // if (&a, &b), no problem
wg.Wait()
} // のところをはずすとデッドロック発生
2018年12月16日日曜日
2018年8月23日木曜日
関数電卓、二分探索(広井を改訂)
//
// calc2.go : 電卓プログラム (変数、組み込み関数の追加)
//
// Copyright (C) 2014 Makoto Hiroi
//
package main
import (
"fmt"
"os"
"math"
"text/scanner"
)
// 値
type Value float64
// 構文木の型
type Expr interface {
Eval() Value
}
// 評価
func (e Value) Eval() Value {
return e
}
// 単項演算子
type Op1 struct {
code rune
expr Expr
}
func newOp1(code rune, e Expr) Expr {
return &Op1{code, e}
}
func (e *Op1) Eval() Value {
v := e.expr.Eval()
if e.code == '-' {
v = -v
}
return v
}
// 二項演算子
type Op2 struct {
code rune
left, right Expr
}
func newOp2(code rune, left, right Expr) Expr {
return &Op2{code, left, right}
}
func (e *Op2) Eval() Value {
x := e.left.Eval()
y := e.right.Eval()
switch e.code {
case '+': return x + y
case '-': return x - y
case '*': return x * y
case '/': return x / y
default:
panic(fmt.Errorf("invalid op code"))
}
}
// 変数
type Variable string
// 大域的な環境
var globalEnv = make(map[Variable]Value)
// 変数の評価
func (v Variable) Eval() Value {
val, ok := globalEnv[v]
if !ok {
panic(fmt.Errorf("unbound variable: %v", v))
}
return val
}
// 代入演算子
type Agn struct {
name Variable
expr Expr
}
func newAgn(v Variable, e Expr) *Agn {
return &Agn{v, e}
}
// 代入演算子の評価
func (a *Agn) Eval() Value {
val := a.expr.Eval()
globalEnv[a.name] = val
return val
}
// 組み込み関数
type Func interface {
Argc() int
}
type Func1 func(float64) float64
func (f Func1) Argc() int {
return 1
}
type Func2 func(float64, float64) float64
func (f Func2) Argc() int {
return 2
}
// 組み込み関数の構文
type App struct {
fn Func
xs []Expr
}
func newApp(fn Func, xs []Expr) *App {
return &App{fn, xs}
}
// 組み込み関数の評価
func (a *App) Eval() Value {
switch f := a.fn.(type) {
case Func1:
x := float64(a.xs[0].Eval())
return Value(f(x))
case Func2:
x := float64(a.xs[0].Eval())
y := float64(a.xs[1].Eval())
return Value(f(x, y))
default:
panic(fmt.Errorf("function Eval error"))
}
}
// 組み込み関数の初期化
var funcTable = make(map[string]Func)
func initFunc() {
funcTable["sqrt"] = Func1(math.Sqrt)
funcTable["sin"] = Func1(math.Sin)
funcTable["cos"] = Func1(math.Cos)
funcTable["tan"] = Func1(math.Tan)
funcTable["sinh"] = Func1(math.Sinh)
funcTable["cosh"] = Func1(math.Cosh)
funcTable["tanh"] = Func1(math.Tanh)
funcTable["asin"] = Func1(math.Asin)
funcTable["acos"] = Func1(math.Acos)
funcTable["atan"] = Func1(math.Atan)
funcTable["atan2"] = Func2(math.Atan2)
funcTable["exp"] = Func1(math.Exp)
funcTable["pow"] = Func2(math.Pow)
funcTable["log"] = Func1(math.Log)
funcTable["log10"] = Func1(math.Log10)
funcTable["log2"] = Func1(math.Log2)
}
// 字句解析
type Lex struct {
scanner.Scanner
Token rune
}
func (lex *Lex) getToken() {
lex.Token = lex.Scan()
}
// 引数の取得
func getArgs(lex *Lex) []Expr {
e := make([]Expr, 0)
if lex.Token != '(' {
panic(fmt.Errorf("'(' expected"))
}
lex.getToken()
if lex.Token == ')' {
lex.getToken()
return e
}
for {
e = append(e, expression(lex))
switch lex.Token {
case ')':
lex.getToken()
return e
case ',':
lex.getToken()
default:
panic(fmt.Errorf("unexpected token in argument list"))
}
}
}
// 因子
func factor(lex *Lex) Expr {
switch lex.Token {
case '(':
lex.getToken()
e := expression(lex)
if lex.Token != ')' {
panic(fmt.Errorf("')' expected"))
}
lex.getToken()
return e
case '+':
lex.getToken()
return newOp1('+', factor(lex))
case '-':
lex.getToken()
return newOp1('-', factor(lex))
case scanner.Int, scanner.Float:
var n float64
fmt.Sscan(lex.TokenText(), &n)
lex.getToken()
return Value(n)
case scanner.Ident:
name := lex.TokenText()
lex.getToken()
if name == "quit" {
panic(name)
}
v, ok := funcTable[name]
if ok {
xs := getArgs(lex)
if len(xs) != v.Argc() {
panic(fmt.Errorf("wrong number of arguments: %v", name))
}
return newApp(v, xs)
} else {
return Variable(name)
}
default:
panic(fmt.Errorf("unexpected token: %v", lex.TokenText()))
}
}
// 項
func term(lex *Lex) Expr {
e := factor(lex)
for {
switch lex.Token {
case '*':
lex.getToken()
e = newOp2('*', e, factor(lex))
case '/':
lex.getToken()
e = newOp2('/', e, factor(lex))
default:
return e
}
}
}
// 式
func expr1(lex *Lex) Expr {
e := term(lex)
for {
switch lex.Token {
case '+':
lex.getToken()
e = newOp2('+', e, term(lex))
case '-':
lex.getToken()
e = newOp2('-', e, term(lex))
default:
return e
}
}
}
func expression(lex *Lex) Expr {
e := expr1(lex)
if lex.Token == '=' {
v, ok := e.(Variable)
if ok {
lex.getToken()
return newAgn(v, expression(lex))
} else {
panic(fmt.Errorf("invalid assign form"))
}
}
return e
}
// 式の入力と評価
func toplevel(lex *Lex) (r bool) {
r = false
defer func(){
err := recover()
if err != nil {
mes, ok := err.(string)
if ok && mes == "quit" {
r = true
} else {
fmt.Fprintln(os.Stderr, err)
for lex.Token != ';' {
lex.getToken()
}
}
}
}()
for {
fmt.Print("Calc> ")
lex.getToken()
e := expression(lex)
if lex.Token != ';' {
panic(fmt.Errorf("invalid expression"))
} else {
fmt.Println(e.Eval())
}
}
return r
}
func main() {
var lex Lex
lex.Init(os.Stdin)
initFunc()
for {
if toplevel(&lex) { break }
}
}
// 二分探索の改正
// 二分探索
def bsearch(x, v, low, high)
let
result = 0
in
while low <= high and result == 0 do
let
mid = (low + high) / 2
in
if v[mid] == x then
begin result = 1,high = -1 end
else
if v[mid] < x then
low = mid + 1
else
high = mid - 1
end
end
end
end,
result
end
end
def binarySearch(x, v)
bsearch(x, v, 0, len(v) - 1)
end
// calc2.go : 電卓プログラム (変数、組み込み関数の追加)
//
// Copyright (C) 2014 Makoto Hiroi
//
package main
import (
"fmt"
"os"
"math"
"text/scanner"
)
// 値
type Value float64
// 構文木の型
type Expr interface {
Eval() Value
}
// 評価
func (e Value) Eval() Value {
return e
}
// 単項演算子
type Op1 struct {
code rune
expr Expr
}
func newOp1(code rune, e Expr) Expr {
return &Op1{code, e}
}
func (e *Op1) Eval() Value {
v := e.expr.Eval()
if e.code == '-' {
v = -v
}
return v
}
// 二項演算子
type Op2 struct {
code rune
left, right Expr
}
func newOp2(code rune, left, right Expr) Expr {
return &Op2{code, left, right}
}
func (e *Op2) Eval() Value {
x := e.left.Eval()
y := e.right.Eval()
switch e.code {
case '+': return x + y
case '-': return x - y
case '*': return x * y
case '/': return x / y
default:
panic(fmt.Errorf("invalid op code"))
}
}
// 変数
type Variable string
// 大域的な環境
var globalEnv = make(map[Variable]Value)
// 変数の評価
func (v Variable) Eval() Value {
val, ok := globalEnv[v]
if !ok {
panic(fmt.Errorf("unbound variable: %v", v))
}
return val
}
// 代入演算子
type Agn struct {
name Variable
expr Expr
}
func newAgn(v Variable, e Expr) *Agn {
return &Agn{v, e}
}
// 代入演算子の評価
func (a *Agn) Eval() Value {
val := a.expr.Eval()
globalEnv[a.name] = val
return val
}
// 組み込み関数
type Func interface {
Argc() int
}
type Func1 func(float64) float64
func (f Func1) Argc() int {
return 1
}
type Func2 func(float64, float64) float64
func (f Func2) Argc() int {
return 2
}
// 組み込み関数の構文
type App struct {
fn Func
xs []Expr
}
func newApp(fn Func, xs []Expr) *App {
return &App{fn, xs}
}
// 組み込み関数の評価
func (a *App) Eval() Value {
switch f := a.fn.(type) {
case Func1:
x := float64(a.xs[0].Eval())
return Value(f(x))
case Func2:
x := float64(a.xs[0].Eval())
y := float64(a.xs[1].Eval())
return Value(f(x, y))
default:
panic(fmt.Errorf("function Eval error"))
}
}
// 組み込み関数の初期化
var funcTable = make(map[string]Func)
func initFunc() {
funcTable["sqrt"] = Func1(math.Sqrt)
funcTable["sin"] = Func1(math.Sin)
funcTable["cos"] = Func1(math.Cos)
funcTable["tan"] = Func1(math.Tan)
funcTable["sinh"] = Func1(math.Sinh)
funcTable["cosh"] = Func1(math.Cosh)
funcTable["tanh"] = Func1(math.Tanh)
funcTable["asin"] = Func1(math.Asin)
funcTable["acos"] = Func1(math.Acos)
funcTable["atan"] = Func1(math.Atan)
funcTable["atan2"] = Func2(math.Atan2)
funcTable["exp"] = Func1(math.Exp)
funcTable["pow"] = Func2(math.Pow)
funcTable["log"] = Func1(math.Log)
funcTable["log10"] = Func1(math.Log10)
funcTable["log2"] = Func1(math.Log2)
}
// 字句解析
type Lex struct {
scanner.Scanner
Token rune
}
func (lex *Lex) getToken() {
lex.Token = lex.Scan()
}
// 引数の取得
func getArgs(lex *Lex) []Expr {
e := make([]Expr, 0)
if lex.Token != '(' {
panic(fmt.Errorf("'(' expected"))
}
lex.getToken()
if lex.Token == ')' {
lex.getToken()
return e
}
for {
e = append(e, expression(lex))
switch lex.Token {
case ')':
lex.getToken()
return e
case ',':
lex.getToken()
default:
panic(fmt.Errorf("unexpected token in argument list"))
}
}
}
// 因子
func factor(lex *Lex) Expr {
switch lex.Token {
case '(':
lex.getToken()
e := expression(lex)
if lex.Token != ')' {
panic(fmt.Errorf("')' expected"))
}
lex.getToken()
return e
case '+':
lex.getToken()
return newOp1('+', factor(lex))
case '-':
lex.getToken()
return newOp1('-', factor(lex))
case scanner.Int, scanner.Float:
var n float64
fmt.Sscan(lex.TokenText(), &n)
lex.getToken()
return Value(n)
case scanner.Ident:
name := lex.TokenText()
lex.getToken()
if name == "quit" {
panic(name)
}
v, ok := funcTable[name]
if ok {
xs := getArgs(lex)
if len(xs) != v.Argc() {
panic(fmt.Errorf("wrong number of arguments: %v", name))
}
return newApp(v, xs)
} else {
return Variable(name)
}
default:
panic(fmt.Errorf("unexpected token: %v", lex.TokenText()))
}
}
// 項
func term(lex *Lex) Expr {
e := factor(lex)
for {
switch lex.Token {
case '*':
lex.getToken()
e = newOp2('*', e, factor(lex))
case '/':
lex.getToken()
e = newOp2('/', e, factor(lex))
default:
return e
}
}
}
// 式
func expr1(lex *Lex) Expr {
e := term(lex)
for {
switch lex.Token {
case '+':
lex.getToken()
e = newOp2('+', e, term(lex))
case '-':
lex.getToken()
e = newOp2('-', e, term(lex))
default:
return e
}
}
}
func expression(lex *Lex) Expr {
e := expr1(lex)
if lex.Token == '=' {
v, ok := e.(Variable)
if ok {
lex.getToken()
return newAgn(v, expression(lex))
} else {
panic(fmt.Errorf("invalid assign form"))
}
}
return e
}
// 式の入力と評価
func toplevel(lex *Lex) (r bool) {
r = false
defer func(){
err := recover()
if err != nil {
mes, ok := err.(string)
if ok && mes == "quit" {
r = true
} else {
fmt.Fprintln(os.Stderr, err)
for lex.Token != ';' {
lex.getToken()
}
}
}
}()
for {
fmt.Print("Calc> ")
lex.getToken()
e := expression(lex)
if lex.Token != ';' {
panic(fmt.Errorf("invalid expression"))
} else {
fmt.Println(e.Eval())
}
}
return r
}
func main() {
var lex Lex
lex.Init(os.Stdin)
initFunc()
for {
if toplevel(&lex) { break }
}
}
// 二分探索の改正
// 二分探索
def bsearch(x, v, low, high)
let
result = 0
in
while low <= high and result == 0 do
let
mid = (low + high) / 2
in
if v[mid] == x then
begin result = 1,high = -1 end
else
if v[mid] < x then
low = mid + 1
else
high = mid - 1
end
end
end
end,
result
end
end
def binarySearch(x, v)
bsearch(x, v, 0, len(v) - 1)
end
2018年7月7日土曜日
goroutine,nodejs-sqlite3,nodejs-functional,Revel vs ROR
http://nasust.hatenablog.com/entry/2016/11/17/002020 for goroutine
---------------------------------------------------------
http://neos21.hatenablog.com/entry/2018/04/22/080000
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
https://qiita.com/To_BB/items/aa68027a9f319bce7a33
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーhttps://paiza.hatenablog.com/entry/2018/03/23/paizacloud_golang_revel
cf http://railsapps.github.io/installrubyonrails-ubuntu.html RORウブンツ
https://qiita.com/laineek/items/121a9a20d5eb26fb56f4 ROR超入門
------------------------------------------------------------
go/src/myapp/conf/app.conf:
[dev]
db.info = root:@/mydb?charset=utf8&parseTime=True
--------------------------------------
go/src/myapp/app/controllers/gorm.go:
package controllers
import (
_ "github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm"
"github.com/revel/revel"
"myapp/app/models"
"log"
)
var DB *gorm.DB
func InitDB() {
dbInfo, _ := revel.Config.String("db.info")
db, err := gorm.Open("mysql", dbInfo)
if err != nil {
log.Panicf("Failed gorm.Open: %v\n", err)
}
db.DB()
db.AutoMigrate(&models.Post{})
DB = db
}
---------------------------------------------
go/src/myapp/app/init.go:
package app
import (
"github.com/revel/revel"
"myapp/app/controllers"
)
...
func init() {
...
revel.OnAppStart(controllers.InitDB)
}
-------------------------------------------
go/src/myapp/app/models/post.go:
package models
type Post struct {
Id uint64 `gorm:"primary_key" json:"id"`
Body string `sql:"size:255" json:"body"`
}
----------------------------------------------
go/src/myapp/app/models/post.go:
package models
type Post struct {
Id uint64 `gorm:"primary_key" json:"id"`
Body string `sql:"size:255" json:"body"`
}
------------------------------------------
go/src/myapp/conf/routes:
GET / Post.RedirectToPosts
GET /posts Post.Index
POST /posts Post.Create
POST /posts/:id/delete Post.Delete
-----------------------------------------------
go/src/myapp/app/controllers/post.go:
package controllers
import (
"github.com/revel/revel"
"myapp/app/models"
"errors"
)
type Post struct {
*revel.Controller
}
func (c Post) Index() revel.Result {
posts := []models.Post{}
result := DB.Order("id desc").Find(&posts);
err := result.Error
if err != nil {
return c.RenderError(errors.New("Record Not Found"))
}
return c.Render(posts)
}
func (c Post) Create() revel.Result {
post := models.Post{
Body: c.Params.Form.Get("body"),
}
ret := DB.Create(&post)
if ret.Error != nil {
return c.RenderError(errors.New("Record Create failure." + ret.Error.Error()))
}
return c.Redirect("/posts")
}
func (c Post) Delete() revel.Result {
id := c.Params.Route.Get("id")
posts := []models.Post{}
ret := DB.Delete(&posts, id)
if ret.Error != nil {
return c.RenderError(errors.New("Record Delete failure." + ret.Error.Error()))
}
return c.Redirect("/posts")
}
func (c Post) RedirectToPosts() revel.Result {
return c.Redirect("/posts")
}
-------------------------------------------
go/myapp/app/views/Post/index.html:
{{set . "title" "Todo list"}}
{{template "header.html" .}}
<header class="jumbotron" style="background-color:#A9F16C">
<div class="container">
<div class="row">
<h1>Todo list</h1>
<p></p>
</div>
</div>
</header>
<div class="container">
<div class="row">
<div class="span6">
{{template "flash.html" .}}
</div>
</div>
</div>
<div class="container">
<form action="/posts" method="post">
<div class="form-group">
<div class="row">
<label for="todo" class="col-xs-2">Todo</label>
<input type="text" name="body" class="col-xs-8">
<div class="col-xs-2">
<button type="submit" class="btn btn-success">
<i class="fa fa-plus"></i> Add Todo
</button>
</div>
</div>
</div>
</form>
<h2>Current Todos</h2>
<table class="table table-striped todo-table">
<thead>
<th>Todos</th><th> </th>
</thead>
<tbody>
{{ range .posts }}
<tr>
<td>
<div>{{ .Body }}</div>
</td>
<td>
<form action="/posts/{{.Id}}/delete" method="post">
<button class="btn btn-danger">Delete</button>
</form>
</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
{{template "footer.html" .}}
---------------------------------------------------------
http://neos21.hatenablog.com/entry/2018/04/22/080000
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
https://qiita.com/To_BB/items/aa68027a9f319bce7a33
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーhttps://paiza.hatenablog.com/entry/2018/03/23/paizacloud_golang_revel
cf http://railsapps.github.io/installrubyonrails-ubuntu.html RORウブンツ
https://qiita.com/laineek/items/121a9a20d5eb26fb56f4 ROR超入門
------------------------------------------------------------
go/src/myapp/conf/app.conf:
[dev]
db.info = root:@/mydb?charset=utf8&parseTime=True
--------------------------------------
go/src/myapp/app/controllers/gorm.go:
package controllers
import (
_ "github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm"
"github.com/revel/revel"
"myapp/app/models"
"log"
)
var DB *gorm.DB
func InitDB() {
dbInfo, _ := revel.Config.String("db.info")
db, err := gorm.Open("mysql", dbInfo)
if err != nil {
log.Panicf("Failed gorm.Open: %v\n", err)
}
db.DB()
db.AutoMigrate(&models.Post{})
DB = db
}
---------------------------------------------
go/src/myapp/app/init.go:
package app
import (
"github.com/revel/revel"
"myapp/app/controllers"
)
...
func init() {
...
revel.OnAppStart(controllers.InitDB)
}
-------------------------------------------
go/src/myapp/app/models/post.go:
package models
type Post struct {
Id uint64 `gorm:"primary_key" json:"id"`
Body string `sql:"size:255" json:"body"`
}
----------------------------------------------
go/src/myapp/app/models/post.go:
package models
type Post struct {
Id uint64 `gorm:"primary_key" json:"id"`
Body string `sql:"size:255" json:"body"`
}
------------------------------------------
go/src/myapp/conf/routes:
GET / Post.RedirectToPosts
GET /posts Post.Index
POST /posts Post.Create
POST /posts/:id/delete Post.Delete
-----------------------------------------------
go/src/myapp/app/controllers/post.go:
package controllers
import (
"github.com/revel/revel"
"myapp/app/models"
"errors"
)
type Post struct {
*revel.Controller
}
func (c Post) Index() revel.Result {
posts := []models.Post{}
result := DB.Order("id desc").Find(&posts);
err := result.Error
if err != nil {
return c.RenderError(errors.New("Record Not Found"))
}
return c.Render(posts)
}
func (c Post) Create() revel.Result {
post := models.Post{
Body: c.Params.Form.Get("body"),
}
ret := DB.Create(&post)
if ret.Error != nil {
return c.RenderError(errors.New("Record Create failure." + ret.Error.Error()))
}
return c.Redirect("/posts")
}
func (c Post) Delete() revel.Result {
id := c.Params.Route.Get("id")
posts := []models.Post{}
ret := DB.Delete(&posts, id)
if ret.Error != nil {
return c.RenderError(errors.New("Record Delete failure." + ret.Error.Error()))
}
return c.Redirect("/posts")
}
func (c Post) RedirectToPosts() revel.Result {
return c.Redirect("/posts")
}
-------------------------------------------
go/myapp/app/views/Post/index.html:
{{set . "title" "Todo list"}}
{{template "header.html" .}}
<header class="jumbotron" style="background-color:#A9F16C">
<div class="container">
<div class="row">
<h1>Todo list</h1>
<p></p>
</div>
</div>
</header>
<div class="container">
<div class="row">
<div class="span6">
{{template "flash.html" .}}
</div>
</div>
</div>
<div class="container">
<form action="/posts" method="post">
<div class="form-group">
<div class="row">
<label for="todo" class="col-xs-2">Todo</label>
<input type="text" name="body" class="col-xs-8">
<div class="col-xs-2">
<button type="submit" class="btn btn-success">
<i class="fa fa-plus"></i> Add Todo
</button>
</div>
</div>
</div>
</form>
<h2>Current Todos</h2>
<table class="table table-striped todo-table">
<thead>
<th>Todos</th><th> </th>
</thead>
<tbody>
{{ range .posts }}
<tr>
<td>
<div>{{ .Body }}</div>
</td>
<td>
<form action="/posts/{{.Id}}/delete" method="post">
<button class="btn btn-danger">Delete</button>
</form>
</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
{{template "footer.html" .}}
2018年5月16日水曜日
paizacloud,GolangAnswers, ElixirAnswers,HaskellAnswers
for stack :: curl -sSL https://get.haskellstack.org/ | sh パイザクラウドではディスク不足!
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
for elixir ::
---------------------------------------------------------------------------------------
gobook exercises answers :: https://github.com/torbiak/gopl
go by example japanese :: https://oohira.github.io/gobyexample-jp/hello-world.html
GOの最深部2か所(リフレクションとC言語境界)
リフレクション: https://github.com/torbiak/gopl
C言語境界: https://qiita.com/toshikitsubouchi/items/251f9e69390e1a4260b9
-----------------------------------------------------------------
https://tnomura9.exblog.jp/17526233/ なんだっけ?
elixir book exercises answers ::
https://github.com/laurocaetano/programming-elixir-exercises
elixir and postgresql ::
elixir-postgres:: https://symfoware.blog.fc2.com/blog-entry-1776.html
mix newでつくったmix.exsにあるdepは重要 消してはいけない
かならずmix deps.getなどしておく!
フェニックス:
Best pages :: https://hexdocs.pm/phoenix/up_and_running.html
For referrable japanese pages :: https://dev.classmethod.jp/server-side/web-application-made-with-elixir-and-phoenix/
--------------------------------------------------------------------------
in ubuntu only (haskell books)
$ sudo apt-get install sqlite3 これだけではダメ
$ sudo apt-get install libsqlite3-dev これしてないと10章は動かん
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
for elixir ::
まずapt-getで入れてたelixirを消します
$ apt-get remove elixir
つづいて、適当な場所にelixirのソースコードをcloneしてmake
$ git clone https://github.com/elixir-lang/elixir.git
$ cd elixir
$ git checkout v1.2
$ make clean test
$ sudo make install
gobook exercises answers :: https://github.com/torbiak/gopl
go by example japanese :: https://oohira.github.io/gobyexample-jp/hello-world.html
GOの最深部2か所(リフレクションとC言語境界)
リフレクション: https://github.com/torbiak/gopl
C言語境界: https://qiita.com/toshikitsubouchi/items/251f9e69390e1a4260b9
-----------------------------------------------------------------
https://tnomura9.exblog.jp/17526233/ なんだっけ?
elixir book exercises answers ::
https://github.com/laurocaetano/programming-elixir-exercises
elixir and postgresql ::
elixir-postgres:: https://symfoware.blog.fc2.com/blog-entry-1776.html
mix newでつくったmix.exsにあるdepは重要 消してはいけない
かならずmix deps.getなどしておく!
フェニックス:
Best pages :: https://hexdocs.pm/phoenix/up_and_running.html
For referrable japanese pages :: https://dev.classmethod.jp/server-side/web-application-made-with-elixir-and-phoenix/
--------------------------------------------------------------------------
in ubuntu only (haskell books)
$ sudo apt-get install sqlite3 これだけではダメ
$ sudo apt-get install libsqlite3-dev これしてないと10章は動かん
2018年5月12日土曜日
phoenix primer, git してGHC並列並行
http://yuji-ueda.hatenadiary.jp/entry/2015/12/21/010620 dockerに構築
https://qiita.com/isaoshimizu/items/9e47a82da9c465fc144a
OSXでのフエニクスだが参考になる
ちなみにhttp://blog1.erp2py.com/2011/05/web2py_17.html うえぶ2ぱい
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
https://qiita.com/sand/items/71d0b35d74a4781f3564 ecto―レジメ
https://qiita.com/sand/items/e98c9e98351b816c7e69 phoenixーレジメ
https://www.casleyconsulting.co.jp/blog/engineer/241/ phoenix intro
https://qiita.com/cohki0305/items/9641cfbe77e3e05e7e8d CRUD by Phoenix
http://ruby-rails.hatenadiary.com/entry/20151011/1444560106
フェニックスのためのセットアップが詳しい 三部作で認証やチャットも勉強できそう
-----------------------------------------
https://qiita.com/satosystems/items/e7fb4295598dc61e4c67
- 並列処理はかなり有効
- 並行処理もそこそこ有効(
forkIO
) - 並列処理に迫る並行処理もあった(
async
)
はじめに
Haskell で処理をマルチコアに分散したい場合どのようにすればよいかをまとめておきます。
負荷のかかる処理としてフィボナッチ数の導出を以下のように行うことにします。
fib :: Int -> Int
fib 0 = 0
fib 1 = 1
fib n = fib (n - 2) + fib (n - 1)
最適化に関して
まずは
fib
関数を普通に一回だけ実行します。once :: IO ()
once = print $ fib 42
この関数をシングルスレッドで動作させるとおよそ
2.877s
かかります。引数の 42 は私の環境で早すぎず遅すぎない適当な値です。
今度は以下の関数をシングルスレッドで動作させてみます。
fourTimes1 :: IO ()
fourTimes1 = do
print $ fib 42
print $ fib 42
print $ fib 42
print $ fib 42
この実行には
2.879s
かかりました。時間がかかるはずの関数を 1 回実行するのも 4 回実行するのもほとんど同じ時間です。この時 fib
関数は愚直に 4 回実行されずに何らかの最適化が働いているようです。
では 4 回
fib
関数を実行するのに以下のように行ってみます。fourTimes2 :: IO ()
fourTimes2 = mapM_ (print . fib) [42, 42, 42, 42]
この実行には
11.503s
かかりました(ほぼ 4 倍の時間です)。fib 42
という形になっていないため最適化が行われないのでしょうか? では同様に mapM_
を使用した以下の関数ならどうでしょうか?fourTimes3 :: IO ()
fourTimes3 = mapM_ (\_ -> print $ fib 42) [(), (), (), ()]
この関数の実行は
2.862s
で終わりました。想定どおりです。どうやら部分適用された関数は最適化されないようです1。
以下関数は
fib
の引数がそれぞれ異なるので(おそらく)最適化が行われず、シングルスレッドで実行に 4.965s
かかります。normal :: IO ()
normal = do
print $ fib 39
print $ fib 40
print $ fib 41
print $ fib 42
マルチスレッドではどうでしょうか? スレッドを増やした結果は以下のとおりです。
スレッド数 | 時間 |
---|---|
-N1 | 4.965s |
-N2 | 4.727s |
-N3 | 4.702s |
-N4 | 4.760s |
-N5 | 4.890s |
-N6 | 5.074s |
-N7 | 5.211s |
-N8 | 5.350s |
この関数を逐次処理とし、並行処理と並列処理の比較対象とします。
並行処理(forkIO)
逐次処理でシングルスレッドなら
4.965s
かかる処理を forkIO
で並行処理にすると、どれぐらいの時間で終わるのでしょうか? 実装は以下のようにしました。fork :: IO ()
fork = do
tv <- newTVarIO 0
_ <- fork' tv $ print $ fib 39
_ <- fork' tv $ print $ fib 40
_ <- fork' tv $ print $ fib 41
_ <- fork' tv $ print $ fib 42
atomically $ readTVar tv >>= flip unless retry . (== 4)
where
fork' :: TVar Int -> IO () -> IO ThreadId
fork' tv p = forkIO $ p >> atomically (readTVar tv >>= writeTVar tv . succ)
Haskell は
forkIO
でスレッドを作成できるのですが、残念ながら他の言語の join のようなスレッドの終了を待ち合わせる機能は提供されていません。なので、面倒なのですが、スレッド化したい処理内でスレッドが終わったことをマークする必要があります。今回は TVar Int
の値をインクリメントすることですべてのスレッドの終了を待ち合わせています。待ち合わせには retry
という関数を使用していますが、TVar
の値が変わるまでスレッドがブロックされるので待ち合わせで CPU リソースを消費するということはありません。
さて、この関数の実行時間は以下のとおりです。
スレッド数 | 時間 |
---|---|
-N1 | 4.919s |
-N2 | 4.638s |
-N3 | 3.620s |
-N4 | 3.049s |
-N5 | 3.068s |
-N6 | 3.246s |
-N7 | 3.347s |
-N8 | 3.349s |
4 スレッドまでは時間短縮し、それ以上になると若干時間延長しています。逐次処理と同じ傾向があります。スレッド起動等のオーバーヘッドでしょうか。今回は
forkIO
でスレッド化した処理が 4 つだったので、4 あるいは 5 スレッドで並行化するのが最適であり、スレッドが多ければ良い、ということではなさそうです2。並行処理(async)
コメントで
async
というパッケージを教えてもらいました。今回は以下のように使用しました。async :: IO ()
async = mapConcurrently_ (print . fib) [39, 40, 41, 42]
なんと並行処理が
mapM_
のように一行で記述できてしまいます。実行時間はどうなるでしょうか?スレッド数 | 時間 |
---|---|
-N1 | 4.858s |
-N2 | 2.848s |
-N3 | 2.833s |
-N4 | 2.123s |
-N5 | 2.207s |
-N6 | 2.440s |
-N7 | 2.481s |
-N8 | 2.574s |
効率に関しても
forkIO
よりもかなり良いです。パッケージの実装を見ると、単に forkIO
をラップしているわけではなさそうです。今回のケースで言えば、forkIO
を使う理由が見当たりません。
並行処理(lifted-async
)
async
は並行処理が IO に依存しているため、他のモナド内で使用するには使い勝手が悪い場合がありますが、lifted-async
を使用すれば様々なモナド内で並行処理が行えます。lasync :: IO ()
lasync = mapConcurrently (\a -> return $ fib a) [39, 40, 41, 42] >>= mapM_ print
上記コードは IO モナド内で実行しているためわかりづらいですが、
mapConcurrently
に渡しているラムダから print
が消えており、任意のモナドが使用できることがわかります。スレッド数 | 時間 |
---|---|
-N1 | 4.912 |
-N2 | 4.697 |
-N3 | 4.716 |
-N4 | 4.695 |
-N5 | 4.881 |
-N6 | 5.129 |
-N7 | 5.359 |
-N8 | 5.46 |
時間を計測してみたところ、冒頭のグラフでも見て取れるように、逐次処理と同じパフォーマンスしか出ていません3。
並列処理
並列処理ならどうなるでしょうか?
eval :: IO ()
eval = do
let as = parMap rpar fib [39, 40, 41, 42]
mapM_ print as
並行処理と比べ並列処理はかなり簡潔に記述できます。特に純粋な関数を純粋なまま並列化できるのが美しいです。この関数の実行時間は以下のとおりです。
スレッド数 | 時間 |
---|---|
-N1 | 4.978s |
-N2 | 2.857s |
-N3 | 2.559s |
-N4 | 2.103s |
-N5 | 2.186s |
-N6 | 2.281s |
-N7 | 2.383s |
-N8 | 2.386s |
かなり効率よく処理が行えているようです。-N4 を指定した処理時間が記事冒頭の
once
という関数一回の処理時間(2.877s
)より短いのは、冒頭の処理は -N1 を指定しているからであり、-N4 を指定すると 2.105s
になります。つまりこの並行処理は最も計算時間のかかる fib 42
を行っている間に fib 39
、fib 40
、fib 41
の計算は他の CPU コアを使用して終わっている、ということを意味し、また並列化のオーバーヘッドがほとんどない、ということも表しています。まとめ
シングルスレッドで処理した場合、逐次処理、並行処理、並列処理はどれもほぼ同じ時間がかかります。スレッド数を増やすと並行処理、並列処理はその恩恵が得られ、特に並列処理のそれは顕著です。
Haskell(GHC)では、複数スレッドを用いると自動的に複数のコアが使用されます4。
forkIO
を用いた並行処理でもスレッド数に応じたマルチコアが利用されているようではありますが、その効率は rpar
を用いた並行処理には及びません。ただし async
パッケージを使用した並行処理であれば、rpar
とほぼ同等の効率を得られることもわかりました。
関数型言語と並行・並列処理は相性がよく、今回も実際目的の関数だけをかんたんに並行化・並列化することができました。うまく活用してマシンリソースを効率よく使用していきましょう。
なお、今回使用したソースコードは以下に置いてあります。
-----------------------------------------------------------------------------------mkdir mc
cd mc
git clone https://github.com/satosystems/mc
cd mc
stack build and thereafter....
$ stack exec mc -- normal +RTS -s -N4
$ stack exec mc -- fork +RTS -s -N4
$ stack exec mc -- async +RTS -s -N4
$ stack exec mc -- lasync +RTS -s -N4
$ stack exec mc -- eval +RTS -s -N4
登録:
投稿 (Atom)