dev blog

プログラミングめも

Go言語|Basics

Go言語の基礎メモ。

関数

func add(x int, y int) int {
    return x + y
}

引数の型が同じ場合は、最後の引数の型だけ書いてもOK。

func add(x, y int) int {
    return x + y
}

関数の中では、 var 宣言の代わりに := の代入文を使って暗黙的な型宣言ができる。

func main() {
    var i, j int = 1, 2
    k := 3
    c, python, java := true, false, "no!"

    fmt.Println(i, j, k, c, python, java)
}

// 1 2 3 true false no!

For文

func main() {
    sum := 0
    for i := 0; i < 10; i++ {
        sum += i
    }
    fmt.Println(sum)
}

// 45

Defer

defer に渡した関数の実行を、呼び出し元の関数の終わり(returnする)まで遅延させることができる。

func main() {
    defer fmt.Println("world")

    fmt.Println("hello")
}

// hello
// world

ポインタ

& オペレータは、そのオペランド(operand)へのポインタを引き出す。
* オペレータは、ポインタの指す先の変数を示す。

i := 42
p = &i 
fmt.Println(*p) // ポインタpを通してiから値を読みだす -> 42

*p = 21 // ポインタpを通してiへ値を代入する
fmt.Println(i) // 21

Goでは関数呼び出しの時に、引数や戻り値のコピーを行う。
このとき変数をポインタにしていた場合は、そのポインタが指すアドレス値をコピーするため、コピーコストを抑えられる。

構造体

構造体内で宣言された変数をフィールドと呼ぶ。 頭文字が大文字だとpublicに、小文字だとprivateになる。

// public
type Vertex struct {
    X int
    Y int
}

// private
type vertex struct {
    X int
    Y int
}

配列

intの10個の配列。
配列の長さは型の一部分なので、配列のサイズを変えることはできない。

var a [10]int

スライス

スライスは配列への参照のようなもの。
スライスはどんなデータも格納しておらず、単に元の配列の部分列を指し示す。

primes := [6]int{2, 3, 5, 7, 11, 13}
var s []int = primes[1:4] // primesの1から4を含むスライスを作成
fmt.Println(s)

// [3 5 7]

配列の作成とそれを参照するスライスを同時に作成できる。

array := [3]bool{true, true, false} // これは配列
slice := []bool{true, true, false} // 上記と同じ配列を作成し、それを参照するスライスを作成
fmt.Println(array)
fmt.Println(slice)

// [true true false]
// [true true false]

これは上記と同様の配列を作成し、それを参照するスライスを作成

Map

Key と Value

m := make(map[string]int)
m["Answer"] = 42
fmt.Println(m["Answer"])

// 42

メソッド

Goにはクラスの仕組みが無い。
構造体(Vertex)にメソッド(Abs)を定義できる。
データと処理を強く結びつけたいときに多用される。

type Vertex struct {
    X, Y float64
}

func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    v := Vertex{3, 4}
    fmt.Println(v.Abs())
}

Absメソッドの引数 v を、レシーバと呼ぶ。

レシーバにポインタを使うこともできる。
ポインタレシーバを使う理由は二つ。

  • ポインタレシーバが指す先の変数を、メソッド内で変更するため。
  • メソッドの呼び出し毎に変数のコピーを避けるため。レシーバが大きな構造体である場合に効率的。
type Vertex struct {
    X, Y float64
}

func (v *Vertex) Scale(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    v := &Vertex{3, 4}
    fmt.Printf(v.Abs())
    v.Scale(5)
    fmt.Printf(v.Abs())
}

// 5
// 25

Goroutine

ゴルーチン。Goのランタイムに管理される軽量なスレッド。

go f(x, y, z)

と書くと、新しいgoroutineが実行される。

func say(s string) {
    for i := 0; i < 5; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
}

func main() {
    go say("world") // 別のゴルーチンで実行される
    say("hello") // go say の完了を待たずに実行される
}

// hello
// world
// hello
// world
// world
// hello
// hello
// world
// hello

チャネル

チャネルを使うと、複数のゴルーチン間で値の送受信ができる。

あるゴルーチンから別のゴルーチンへ値を渡したいときは、チャネルへその値を送信する。

ch := make(chan int)
ch <- 100

あるゴルーチンから渡された値を受け取るときは、チャネルを利用して受信する。

n := <- ch

C#|bit全探索

bit全探索の備忘録です。

bit全探索とは

N個のものからいくつか選び取るパターンを全列挙するための手法です。
N個のものからいくつか選び取るパターンは2N通りあるので、3個のものからいくつか選びとるパターンは、23通り=8通りとなります。
※1個も選ばないパターンも含みます。

bit列で考える

例えば、りんご(R)・みかん(M)・ばなな(B) の3個の果物があるとします。
この3個の果物をいくつか選び取るパターンを見ていきます。
それぞれ選ばない場合は0、選ぶ場合は1のしるしをつけます。

R | M | B
---------
0   0   0
0   0   1
0   1   0
0   1   1
1   0   0
1   0   1
1   1   0
1   1   1

このように、8通りのパターンを作ることができます。 よく見ると、2進数表記のbit列になっています。
これに10進数表記を追記します。

RMB
---
000  -> 0
001  -> 1
010  -> 2
011  -> 3
100  -> 4
101  -> 5
110  -> 6
111  -> 7

この8通りのbit列それぞれのx桁目が0なのか1なのかが分かれば、8通りのパターンの内訳が分かります。

10進数の6を見ると、1 1 0となっています。この2桁目が 1 であることを知るためには、右に2bitシフトして、0桁目のbitが0なのか1なのかを確認します。

110
011 // まず右に1bitシフト
001 // 続いて右にもう1bitシフト

10進数の6を右に2bitシフトしたbit列を求める場合、C#ではこのように書くことができます。

6 >> 2

// => 001

あとは 001 の0桁目のbitが 1 であることが分かればOKです。
0桁目のbitが0なのか1なのかを確認するためには、AND演算を使います。
AND演算をすると、2つのbit列の各桁ごとのbitを見て、どちらも1の場合のみ1を返し、どちらかが0またはどちらも0の場合は0を返します。
これをうまく使って s & 1 とすれば、sの0桁目のbitが0の場合は0を、1の場合は1を返すので、0桁目のbitを確認することができます。

(6 >> 2) & 1

// => 1

C#でbit全探索

では、先ほどの8通りのbit列それぞれのx桁目が0なのか1なのかを調べて、8通りのパターンの内訳を明らかにしていきます。

int N = 3; // N個のもの
int P = (int)Math.Pow(2, N); // P通りのパターン
var R = new int[P, N]; // 結果を格納する配列

// P通りのbit列を一つずつ確認する
for (int i = 0; i < P; i++)
{
    // 右からx桁目が0なのか1なのかを調べる
    for (int x = 0; x < N; x++)
    {
        var a = (i >> x) & 1;
        R[i, x] = a;
    }
}

結果を格納したRを出力してみます。

for (int i = 0; i < P; i++)
{
    string s = "";

    for (int j = 0; j < N; j++)
    {
        s += R[i, j].ToString();
    }

    Console.WriteLine(s);
}
000
100
010
110
001
101
011
111

ちゃんと8通りのパターンが作成されました。

Nuxt.js|SSRのライフサイクル

Nuxt.jsのSSRCSRのライフサイクルがよく分からず、検証してみたので備忘録として残しておきます。
今回使用しているバージョンはNuxt 2.12です。

SSRCSR

サーバーサイドレンダリング(SSR)とは、サーバー側でHTMLを描画してブラウザに直接表示する手法です。
詳しくは公式ガイドに載っています。
Vue.js サーバサイドレンダリングガイド | Vue SSR ガイド

Nuxt.jsでSSRを使う場合は、

  • 初回の描画時はSSRCSR
  • それ以降の再描画時はCSRのみ

となっています。

ライフサイクル

ライフサイクルを確認するためにこのようなコードを用意しました。

<template>
  <div>
    <h1>SAMPLE</h1>
    <nuxt-link to="/blue">リンク</nuxt-link>
  </div>
</template>

<script>
export default {
  layout: 'top',
  data() {
    console.log('data / server: ' + process.server)
    return {
      value: ''
    }
  },
  asyncData() {
    console.log('asyncData / server: ' + process.server)
  },
  fetch() {
    console.log('fetch / server: ' + process.server)
  },
  beforeCreate() {
    console.log('beforeCreate / server: ' + process.server)
  },
  created() {
    console.log('created / server: ' + process.server)
  },
  beforeMount() {
    console.log('beforeMount / server: ' + process.server)
  },
  mounted() {
    console.log('mounted / server: ' + process.server)
  }
}
</script>

process.serverは、SSRの場合にtrueを返します。
このページを読み込んでconsoleタブを確認してみると、このような結果になりました。

f:id:koz2020:20200510062745p:plain

下記の順番でライフサイクルフックが実行されています。

(SSR) asyncData 
(SSR) beforeCreate *
(SSR) data *
(SSR) created *
(SSR) fetch

(CSR) beforeCreate *
(CSR) data *
(CSR) created *
(CSR) beforeMount
(CSR) mounted

初回描画時は、SSR -> CSRという順番でライフサイクルフックが実行され、beforeCreate data created(*)はどちらでも実行されていることが分かります。

次に、再描画時の動作を確認するために、Nuxt.jsアプリケーション内の別のページから<nuxt-link>をクリックしてこのページに遷移してみます。
すると、このような結果になりました。

f:id:koz2020:20200510064109p:plain

下記の順番でライフサイクルフックが実行されています。

(CSR) asyncData 
(CSR) beforeCreate
(CSR) data
(CSR) created
(CSR) beforeMount
(CSR) fetch
(CSR) mounted

再描画時はCSRのみで、SSRのライフサイクルフックは実行されていません。

いつからwindowオブジェクトやDOMにアクセスできるのか

Nuxt.jsで開発しているときにwindowオブジェクトにアクセスするとwindow is not definedというエラーが出ることがあります。 これはSSRでまだwindowオブジェクトが存在していないからです。 CSRのみでwindowオブジェクトにアクセスできます。

まとめ

  • 初回描画時はSSR -> CSR、再描画時はCSRのみ
  • 初回描画時のライフサイクルは、asyncData -> beforeCreate * -> data * -> created * -> fetch -> beforeMount -> mounted
    (*SSRでもCSRでも実行される)
  • windowオブジェクトとDOMにアクセスできるのは、CSRのみ

JavaScript|getterとsetter

getterとsetterの備忘録です。

オブジェクトについておさらい

オブジェクトは、プロパティの集まりです。
プロパティとは、name: 'John'といった名前(キー)と値のセットのことです。
プロパティの値には、データだけではなく関数も定義できます。この関数をメソッドと呼びます。

userオブジェクトに、name ageのデータとgreetメソッドを定義してみます。

const user = {
  name: 'John',
  age: 25,
  greet() {
    console.log('Hello!')
  }
}

このようにオブジェクトにアクセスすることができます。

console.log(user.name)
// => John

user.greet()
// => Hello!

getterとsetterとは

オブジェクトのプロパティは、

  • データプロパティ
  • アクセサプロパティ

の2種類に分類されます。

データプロパティとは、最初に書いたコードのように値や関数を格納しているプロパティのことです。対してアクセサプロパティは、値を格納しません。getterメソッドやsetterメソッドを定義します。

  • getterメソッド(オブジェクト内のプロパティの値を読み出す
  • setterメソッド(オブジェクト内のプロパティに値を書き込む

getterを使ってみる

では実際にgetterメソッドを書いてみます。

const user = {
  firstName: 'John',
  lastName: 'Smith',
  get fullName() {
    return this.firstName + ' ' + this.lastName
  }
}

console.log(user.fullName)
// => John Smith

getterしか定義されていない場合は、読み込み専用プロパティとなるので、値を書き込んでも無視されます。

user.fullName = 'John'
console.log(user.fullName)
// => John Smith
// 値が変わっていない

ちなみに、getterとデータプロパティの両方に同名のfullNameが存在していると、データプロパティのfullNameは無視されます。

const user = {
  firstName: 'John',
  lastName: 'Smith',
  fullName: 'JOHN SMITH',  // 同名のデータプロパティを追加
  get fullName() {
    return this.firstName + ' ' + this.lastName
  }
}

console.log(user.fullName)
// => John Smith
// getterのfullNameが読み出されている

user.fullName = 'John'
console.log(user.fullName)
// => John Smith
// データプロパティのfullNameへ書き込んでも無視される

setterを使ってみる

続いてsetterメソッドを書いてみます。
setterメソッドは引数(セットする新しい値)を1つだけ持ちます。

const user = {
  firstName: 'John',
  lastName: 'Smith',
  _team: '',
  set team(value) {
    if (value <= 10) {
      this._team = 'team A'
    } else if (value > 10) {
      this._team = 'team B'
    }
  }
}

user.team = 7
console.log(user._team)
// => team A

setterしか定義していない場合は、書き込み専用プロパティとなるので、値を読み出すことはできません。

console.log(user.team)
// => undefined

また、上記のコードでデータプロパティは_team、setterはteamとしていますが、両方ともteamとするとどうなるでしょう。

const user = {
  firstName: 'John',
  lastName: 'Smith',
  team: '', // こちらもteamに変更
  set team(value) {
    if (value <= 10) {
      this.team = 'team A'
    } else if (value > 10) {
      this.team = 'team B'
    }
  }
}

user.team = 7
console.log(user.team)
// => undefined

setterを使ったuser.teamへの書き込みも、データプロパティuser.teamの読み出しもうまく動作しません。

getterとsetterの使いどころ

プロパティを値の読み書きにしか使用しない場合は、データプロパティをそのまま使用すればよいので、あえてgetterやsetterを使用する必要はありません。 user.age = 20user._age = 20も同じことをしています。

const user = {
  firstName: 'John',
  lastName: 'Smith',
  _age: 0,
  get age() {
    return this._age
  },
  set age(value) {
    this._age = value
  }
}

user.age = 20
console.log(user.age)
// => 20

user._age = 20
console.log(user._age)
// => 20

getterやsetterは、プロパティの値を読み出す際に適切な値を計算して返す必要がある場合や、プロパティの値を加工してから書き込む場合などに便利な機能です。

アルゴリズム|計算量

最近AtCoderを始めたので、計算量と実行時間についての備忘録です。
サンプルのコードはC#で書いています。

オーダー記法

このような式の計算量をオーダー記法で表現するとO(N²)となります。

3N²+3N+3  

下記の手順で計算量を求めます。

  1. まず係数を省略する。 -> N²+N+3
  2. 定数を1に変換する。 -> N²+N+1
  3. Nを大きくしたときに一番影響が大きい項を取り出して、O(項)とする。 -> O(N²)

一番影響が大きい項に注目するという点がポイントです。

O(N)

for文でN回ループをまわすと計算量はO(N)となります。

for (int i = 0; i < N; i++) {
  // 何らかの処理
}

O(N²)

for文でN回の二重ループをまわすと計算量はO(N²)となります。

for (int i = 0; i < N; i++) {
  for (int j = 0; j < N; j++) {
    // 何らかの処理
  }
}

O(logN)

logxN = Mは「xをM乗したらNになる」という意味です。
二分探索のように、半分にしていく処理をN回繰り返すと計算量はO(logN)となります。
下記のコードは、ソートされた数字の配列の中から、numより大きい最初の値を二分探索で見つけて、その値のインデックスを返す関数です。

public static int BinarySearch(int[] array, int num)
{
  int start = 0;
  int last = array.Length;
  int mid;

  while (start < last)
  {
    mid = start + (last - start - 1) / 2;
    if (array[mid] > num)
    {
      last = mid;
    }
    else
    {
      start = mid + 1;
    }
  }

  return last;
}

実行時間

おおよその実行時間は下表の通りです。
引用元: W - 2.06.計算量

N O(logN) O(N) O(N²)
1 一瞬 一瞬 一瞬
10 一瞬 一瞬 一瞬
1000 一瞬 一瞬 0.01秒くらい
10⁶ 一瞬 0.01秒くらい 3時間くらい
10⁸ 一瞬 1秒くらい 3年くらい

AtCoderの問題の実行時間制限は2秒であることが多いので、そういった問題を解く時は「1秒で処理できるfor文のループ回数は10⁸回くらい」と覚えておくと良さそうです。

Vue.js|スロット

Vue.jsのスロットの使い方の備忘録です。

スロットとは

複数のページで同じデザイン部品を使いたいとき、一つの子コンポーネントを使い回すことがよくありますが、スロットを使うとこの子コンポーネントの中に親コンポーネント側のコードを埋め込むことができます。
文字で説明しても分かりづらいので実際にコードを書いていきます。

スロットの使いかた

まず、ページのベースとなる親コンポーネントblue.vueを用意します。
コンポーネントCardContent.vueをインポートしています。
注目ポイントは、<card-content>タグの配下にもコードが書かれている点です。

// 親コンポーネント blue.vue
<template>
  <div>
    <h1>BLUE</h1>
    <card-content class="card">
      <p class="text">
        text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text
      </p>
      <img class="photo" src="~/assets/blue.jpg">
    </card-content>
  </div>
</template>

<script>
import CardContent from '~/components/CardContent.vue'
...
</script>

コンポーネントCardContent.vueのコードはこちらです。
注目ポイントは、<slot />タグです。

// 子コンポーネント CardContent.vue
<template>
  <div class="content">
    <p class="title">Card Content</p>
    <div class="wrapper">
      <slot />
    </div>
  </div>
</template>

blue.vueをブラウザで見ると、このように表示されます。

f:id:koz2020:20200503154327p:plain:w300

図で構造を確認します。
コンポーネントの中に子コンポーネントが描画され、子コンポーネントの中の<slot />タグの部分に、親コンポーネント<card-content>タグ配下のコードが描画されます。

f:id:koz2020:20200503161406j:plain

同じ子コンポーネントを使い回す

複数のページで子コンポーネントCardContent.vueを使いつつ、子コンポーネント<slot />タグの部分を変えてみます。

コンポーネントyellow.vueを新しく作成します。
<card-content>タグ配下を、最初に作ったblue.vueとは違うコードにします。

// 親コンポーネント yellow.vue
<template>
  <div>
    <h1>YELLOW</h1>
    <card-content class="card">
      <p class="lead">
        lead lead lead
      </p>
      <img class="photo" src="~/assets/yellow.jpg">
      <p class="text">
        text text text text text text text text text text text text text text text text text text text
      </p>
    </card-content>
  </div>
</template>

<script>
import CardContent from '~/components/CardContent.vue'
...
</script>

最初に作ったblue.vueと今作ったyellow.vueを見比べてみます。
同じ子コンポーネントを使いつつも、<slot />タグのグレー背景の部分は別の内容になっています。

f:id:koz2020:20200503163306j:plain

スロットを使うと、こういった微妙に内容の違うコンポーネントを一つにまとめることができます。

C#|再帰関数

再帰関数の備忘録です。
階乗を求める再帰関数を書いてみます。

そもそも階乗とは

nの階乗は、1からnまでのすべての整数の積です。
n!のようにあらわします。
例えば、5の階乗はこんな感じです。

5! = 1 * 2 * 3 * 4 * 5

// 120

まずはfor文で

比較のためにまずはfor文で書きます。

static void Main()
{
    Console.WriteLine(factorial(5));  // => 120
}

static int factorial(int n)
{
    int result = 1;

    for (int i = 1; i <= n; i++)
    {
        result *= i;
    }

    return result;
}

再帰関数で

続いて再帰関数で書くと、こうなります。

static void Main()
{
    Console.WriteLine(factorial(5));  // => 120
}

static int factorial(int n)
{
    // nが1になったら終了
    if (n == 1) return 1;

    // factorialを再帰呼び出し
    return n * factorial(n - 1);
}

実行される順番はこんな感じです。

factorial(5) = 5 * factorial(4)
factorial(4) = 4 * factorial(3)
factorial(3) = 3 * factorial(2)
factorial(2) = 2 * factorial(1)
factorial(1) = 1 

これの重複部分を省略していくと・・・

factorial(5) = 5 * 4 * 3 * 2 * 1

階乗の計算になります。 終了条件を書き忘れるとループになってしまうので要注意です。