Wen
[Go言語] Sliceを完璧に複製するには? (How to perfectly clone a slice?)

結論

b := append(a[:0:0], a...)

解説

a[:0:0]a[0:0:0] の省略で、 Full slice expressions(フルスライス式?)と言います。

  • その構文

    a[low : high : max]
    

    最初のインデックス(low) が 0の場合、a[: high : max] に省略することができます。

注意:普通のスライス式 a[0:0] ですと、 ba と同じメモリを使うことになります。

おまけ

b := make([]T, len(a))
copy(b, a)

OR

b := append([]T(nil), a...)

でも複製できますが、

前者は、nil slicenon-nil blank slice に、

後者は、non-nil blank slicenil slice に複製してしまう問題あります。

nil判定を入れれば問題解消できますが、コードが長くなります。

if a == nil {
    b = nil
} else {
    b = make([]T, len(a))
    copy(b, a)
}

ベンチマーク

テストコード

package main

import (
    "testing"
)

const N = 1024 * 1024
type T = int64
var xForMakeCopy = make([]T, N)
var xForAppend = make([]T, N)
var yForMakeCopy []T
var yForAppend []T

func Benchmark_MakeAndCopy(b *testing.B) {
    for i := 0; i < b.N; i++ {
        yForMakeCopy = make([]T, N)
        copy(yForMakeCopy, xForMakeCopy)
    }
}

func Benchmark_Append(b *testing.B) {
    for i := 0; i < b.N; i++ {
        yForAppend = append(xForAppend[:0:0], xForAppend...)
    }
}

テスト結果

❯ go version
go version go1.14.2 darwin/amd64
❯ go test -bench=.
goos: darwin
goarch: amd64
Benchmark_MakeAndCopy-8             1142            990016 ns/op
Benchmark_Append-8                  2289            526206 ns/op

appendcopy より2倍ぐらい早いようでした。

参考

How to efficiently clone a slice?

[Gin] 文字列をHTMLとして返す (Return string as HTML)

問題

Gin にはtemplateを使ったHTMLを返すメソッドは用意されていますが、

c.HTML(http.StatusOK, "index.tmpl", gin.H{
        "title": "My site",
})

文字列をHTMLとして返すメソッドは用意されてないようです。

解決策

//Do what you need to get the cached html
yourHtmlString := "<html><body>I am cached HTML!</body></html>"

//Write your 200 header status (or other status codes, but only WriteHeader once)
c.Writer.WriteHeader(http.StatusOK)
//Convert your cached html string to byte array
c.Writer.Write([]byte(yourHtmlString))

参考

https://github.com/gin-gonic/gin/issues/628#issuecomment-222012174

[Go言語] 初心者必見シリーズ: 構造体(Structs)

構造体

構造体は、フィールド(field)の集まりです。

定義方法

構造体は下記のように typestruct を使用して定義します。

  • 構文

    type 構造体の名前 struct {
        フィールド名 型名
        フィールド名 型名
        フィールド名 型名
        ...
    }
    

    同じ型のフィールド名は、カンマ区切りで書くことができます。

    type 構造体の名前 struct {
        フィールド名, フィールド名, フィールド名 型名
        フィールド名 型名
    }
    
  • 人のデータをまとめた構造体です。

      type People struct {
              Name string
              Sex string
              Age int
              Height int
              Weight int
      }
    

初期化

方法 1:Key:Value で初期化します。

people := People{Name: "太郎"}

設定しない要素は ゼロ値 で初期化される。

方法 2:要素の定義順に初期化します。

people := People{"太郎", "男", ...}

この方法では、全部の field を設定する必要があります。

方法 3:定義後にドット(.)を用いて field を初期化します。

people := People{}
people.Sex = "男"
people3.Age = 8

構造体へのポインター(Pointer to structs)

方法 1:& (アドレス演算子)で構造体への Pointer を取得します。

people := People{Name: "太郎"}
p := &people
(*p).Name = "花子"

(*p)p に省略できます。

p.Name = "花子"

方法 2: new(構造体名) で構造体へのポインターを生成します。

people := new(People) // Peopleのポインタ型
(*people).Name = "花子"
people.Name = "花子"  // (*people) は peopleに省略できます。

new(T) は T 型の無名変数(unnamed variable)を作成し、それを T のゼロ値へ初期化し、 *T 型の値であるそのアドレスを返します。

new(People)&People{} は等価です。

タグ(Tag)

Go 構造体タグは、Go 構造体宣言のフィールドの後に表示される注釈です。

実行時にreflectパッケージでその情報を取得し利用したりします。

良くある構造体を JSON に変換する例として、

下記のようにフィールドの後ろに json タグをつけると、

  type People struct {
          Name string        `json:"name"`
          Sex string        `json:"sex"`
          Age int                `json:"age"`
          Height int        `json:"height"`
          Weight int        `json:"weight"`
  }

Go 言語の json 標準パッケージで構造体を JSON に変換すると、フィールド名が Tag で定義された名前に置換されます。

people := People{Name: "太郎", Sex: "男"}
jsonBytes, _ := json.Marshal(people)
fmt.Println(string(jsonBytes))
// JSON: {"name": "太郎", "sex": "男"}

その仕組み

jsonパッケージの内部で下記のようにタグ情報を取得して JSON データに変換しています。

 t := reflect.TypeOf(people)
 for i := 0; i < t.NumField(); i++ {
     field := t.Field(i)
     tag := field.Tag.Get("json") //  `json`タグ情報を取得
 }

下記のように、複数のタグはスペースで区切って記述できます。

  type People struct {
          Name string        `json:"name" validate:"required"`
          Sex string        `json:"sex" validate:"required"`
          Age int                `json:"age" validate:"required"`
          Height int        `json:"height" validate:"required"`
          Weight int        `json:"weight" validate:"required"`
  }

埋め込みフィールド(Embedded fields)

型だけで宣言されていて、明示的なフィールド名がないフィールドを埋め込みフィールドと呼びます。

Go 言語は埋め込みで他言語のクラスの継承ぽいことができます。

埋め込み子構造体(Address)のフィールドが昇格し、構造体の外(Person)からダイレクトにアクセスすることが可能です。

person.Adress.city → person.city
person.Adress.state → person.state
// PersonがAdressを継承しているように、Adressのフィールドをダイレクトにアクセスすることが可能となります。

Person に同名のフィールドやメソッドがあるときは、Address にあるそのフィールドは昇格されないです。

構造体の比較

reflect.DeepEqual はポインターか否かにかかわらず、

フィールドの値が同じであれば true を返します。

import (
    "reflect"
)

...
people1 := People{Name: "太郎"}
people2 := People{Name: "太郎"}

println(reflect.DeepEqual(people1, people2))     // true
println(reflect.DeepEqual(&people1, &people2))   // true
println(people1 == people2)    // true
println(&people1 == &people2)  // false

動画(YouTube)

[Go言語] 初心者必見シリーズ: マップ(Map)

マップ(Map)

Golang ではハッシュテーブル(hash table)のことを map と呼びます。

キーと値の組を複数個格納し、キーに対応する値をすばやく参照するためのデータ構造です。

Mapのイメージ

定義方法

方法 1

  • 構文

    変数名 := map[型]型{初期値1, ..., 初期値n}
    
  • languages := map[string]string{
        "go":"golang",
        "rb":"ruby",
        "js":"javascript",
    }
    

方法 2

  • 構文

    変数名 := make(map[型]型, 容量)
    
  • languages := make(map[string]string)
    languages["go"] = "golang"
    languages["rb"] = "ruby"
    languages["js"] = "javascript"
    

要素の挿入と更新

  • 要素の挿入や更新:

    languages := map[string]string{}
    languages["go"] = "golang" // "go"キーが存在する場合は、valueが "golang" に更新され、
                                 //  存在しない場合は、("go", "golang") という Key-ValueのペアがMapに挿入される。
    
  • 要素の取得:

     elem, ok := languages["go"] // "go"キーが存在する場合は、 ok == true
                                 //          存在しない場合は、ok == false
    

    要素取得のイメージ

要素の走査 (iteration)

  • range で Map を走査できる。

    for key, value := range languages{
      ...
    }
    

    注意: Go の Map の iteration は、

    要素がランダムに取得されるため、実行するたびに異なる結果になります。

要素の衝突

複数の異なるキーが同じバケットに入ることを衝突(collision)と呼びます。

Go の Map は衝突を起こしたキー同士をポインターでつなぎます。(連鎖法

要素衝突時

動画 (YouTube)

[vuepress] Hot reload not working

Environment

  System:
    OS: macOS 10.15.4
  Binaries:
    Node: 14.3.0 - /usr/local/bin/node
    Yarn: 1.22.4 - /usr/local/bin/yarn
    npm: 6.14.4 - /usr/local/bin/npm
  Browsers:
    Chrome: 83.0.4103.61
  npmPackages:
    @vuepress/core:  1.5.0
    @vuepress/theme-default:  1.5.0
    vuepress: ^1.5.0 => 1.5.0

Solution

I got it working by downgrading watchpack to 1.6.1.

Just add resolutions section to package.json

and run yarn install

  "resolutions": {
    "watchpack": "1.6.1"
  }
[python] 最大再帰処理回数(Maximum Recursion Depth)を変更する

2018年07月27日の記事の再掲&Update(WordpressのBackupを無くしてしまったため。)

背景

再帰処理の回数 の多い関数を実行すると
プログラムが下記のような RecursionError エラーが出て停止するようになります。

RecursionError: maximum recursion depth exceeded

結論

そのような時は、コードを先に見なすべきですが、(ループに直すとか)
一時的にその再帰回数をあげる方法もあります。

import sys

sys.setrecursionlimit(2000)

この上限値はプラットフォームによって異なりますが、(platform-dependent)
指定した上限値が大きすぎたりするとエラーになります。

因みに、現在の設定値は、 sys.getrecursionlimit() で取得可能です。

補足

sys.setrecursionlimit() で設定する値は、
厳密には最大再帰回数ではなくPyhtonインタープリターのスタックのサイズであるため、
この値より若干少ない再帰回数であってもエラー(RecursionError)となる。

Set the maximum depth of the Python interpreter stack to limit.

下記の例にあるように、 上限値を 2000 に設定していても、
1998 でエラーとなりました。

import sys

def test(n):
    if n == 1:
        print('Finish')
        return
    test(n - 1)

sys.setrecursionlimit(2000)
test(1998)
# RecursionError: maximum recursion depth exceeded while calling a Python object

参考

https://docs.python.org/3/library/sys.html#sys.setrecursionlimit
Pythonの再帰回数の上限を確認・変更

[Go言語] 初心者必見シリーズ その1:配列とスライス

配列(Arrays)

配列は 連続したメモリ領域 を確保するデータ構造です。

配列の構成イメージ

定義方法

方法 1

構文:

  var 配列名 [サイズ]型

例:

  var a [2]string

方法 2

構文:

    var  配列名  [サイズ]型
        = [サイズ]型{初期値1, ..., 初期値n}

例:

    var a [2]string
        = [2]string{"Hello", "Go"}

NOTE: 宣言と同時に代入する。

方法 3

構文:

    配列名 := [サイズ]型{初期値1, ..., 初期値n}

例:

    a := [2]string{"Hello", "Go"}

NOTE: 宣言と同時に代入するなら、方法 2 よりもこちらが簡潔でおすすめ。

方法 4

構文:

    配列名 := [...]型{初期値1, ..., 初期値n}

例:

    a := [...]string{"Hello", "Go"}

NOTE: 配列のサイズを “…” にすると、コンパイラーが自動的に要素数を数えてくれる。

多次元配列 (Multiple Dimension)

多次元配列 (Multiple Dimension) は、
[]の数 を増やせば 1 次元配列と同じように定義することができる。

例:

    a := [][]string{
        {"Hello", "Go"},
        {"Hello", "Python"},
    }

要素の走査 (iteration)

方法 1: len 関数を利用することで配列のサイズを取得。

for i := 0; i < len(array); i++ {
  fmt.Println(array[i])
}

方法 2 : range を使って配列を走査。

for index, value := range array{
    fmt.Println(index, value)
}

まとめ

  • 配列は、宣言後にサイズを変更できません。(固定長

    あらかじめデータサイズの確定しないデータには向いてません。

  • 配列は、基本同じ型をもつ要素を格納しますが、

    いろんな型を混在させたい場合は interface 型で初期化します。

    array := []interface{}{“hello", 10, 2.5}
    

スライス(Slices)

スライスは可変長の配列の様な動きをするデータ構造です。

定義方法

定義方法は配列と似ていて、サイズを指定せずに宣言します。

方法 1

構文:

var 名前 []型

例:

var a []string

方法 2

構文:

var 名前 []型 = []型{初期値1, ..., 初期値n}

例:

var a []string = []string{"Hello", "Go"}

方法 3

構文:

名前 := []型{初期値1, ..., 初期値n}

例:

a := []string{"Hello", "Go"}

方法 4

構文:

名前 := make([]型, 初期長さ ,初期容量)

例:

a := make([]string, 2, 10)

方法 4 は、配列にはないような宣言方法。

初期容量はオプション引数。

スライスの構成

スライスの構成イメージ

スライスは配列への参照のようなもの。

要素の走査 (iteration)

走査方法は、配列と同様です。

要素の追加 (append)

スライスへ新しい要素を追加するには、Go の組み込みの append 関数 を使います。

要素追加時の動きは、こちらの動画が分かりやすくご覧いただければと思います。

動画(YouTube)

この記事を動画で見たければ、こちらでお願いします。