OmniAICreator/Qiita-1.07M huggingface dataset:
【自己位置推定の基礎】確率の基礎のお話

# この記事の要約

- 自己位置推定に必要な確率の基礎的な話（条件付き確率，同時確率，加法定理，乗法定理，全確率の定理，ベイズの定理）を説明しています
- ものすごく基本的なことしか説明していません（教科書を読めばわかることです）



# 確率の基礎的な話

## 確率の表現

ある確率変数$A$が存在するとします．例えば$A$はサイコロの出目を表すような変数だとすると，$A$は1から6の整数の値を取ります．このとき，例えば$A = 1$となる確率は以下の様に表現されます．

``` math
p(A = 1) = \frac{1}{6}
```

このとき，$p(A)$は$A$に従う関数となり，これを確率分布と呼びます．



## 条件付き確率

別の確率変数$B$があったとします．例えば$B$はコイントスの結果の裏表を表すものであり，$p(B = 表) = 1/2$であるとします．

ここで，$B$の値によって投げるサイコロを変える問題を考えます．例えば，$B = 表$のときは普通のサイコロを投げ，$B = 裏$のときは奇数目しかないサイコロ（1, 1, 3, 3, 5, 5）を投げるとします．このとき，$B$は**条件**となり，この様な条件の付いた確率を**条件付き確率**と呼びます．この例では，サイコロの出目$A$が1となる確率はそれぞれ以下の様になります．

```math
p(A = 1 | B = 表) = \frac{1}{6} \\
p(A = 1 | B = 裏) = \frac{2}{6} = \frac{1}{3}
```

なお，$p(A | B)$は$A$の関数（確率分布）であり，$B$は定数として考えられることに注意してください（$p(A | B)$は$B$の関数にはなりません）．



## 同時確率

確率変数$A$と$B$が同時に起こる事象を考えるとき，この様な確率を**同時確率**と呼びます．サイコロを投げて出目が1になる事象$A = 1$と，コインを投げて表になる事象$B = 表$が同時に発生する確率は以下の様になります．

```math
p(A = 1, B = 表) = p(A = 1)p(B = 表) = \frac{1}{6} \cdot \frac{1}{2} = \frac{1}{12}
```

ここで$p(A, B)$は，$A$と$B$の2変数関数（確率分布）となります．

なお，上の同時確率は暗黙的に2つの確率の積の形に分解されていますが，これはお互いの変数が**独立**しているという前提があります．独立とは，互いの事象が影響を与えないことを意味します．



## 加法定理・乗法定理

確率論において最も重要となる2つの定理があります．

```math
p(A) = \sum_{B} p(A, B)
```

```math
p(A, B) = p(A | B) p(B)( = p(B | A) p(A))
```

上が**加法定理**，下が**乗法定理**と呼ばれます．加法定理は，$A$と$B$の同時確率に対して$B$の値を変化させていった確率を足した値が，$A$に対する確率となるということを意味しています．これは**周辺確率**とも呼ばれたりします．

乗法定理は，$A$と$B$が同時に起きる確率は，$B$が起きる確率（もしくは$A$が起こる確率）と，$A$の条件付き確率（もしくは$B$の条件付き確率）の積になることを意味しています．なお，乗法定理をどのように分解するかは，考えているモデルによって変わるので注意してください．



## 全確率の定理

加法定理の右辺の同時確率に対して，乗法定理を適用すると，**全確率の定理**を得ることができます．

```math
p(A) = \sum_{B} p(A, B) = \sum_{B} p(A | B) p(B)
```

これは，$A$の確率を得るために，$B$に関する知識を導入できること，またその際に，$A$と$B$の同時確率を考えなくても良いということを意味しています．



## ベイズの定理

上述の定理を用いると，以下に示す**ベイズの定理**を導出することができます．

```math
p(A | B) = \frac{p(B | A) p(A)}{p(B)} = \frac{p(B | A) p(A)}{\sum_{A} p(B | A) p(A)}
```

これは，事後確率&p(A | B)&が，事前確率&p(B)&と尤度&p(B | A)&の積の形になることを意味しています．なお分母は確率としてのルール（総和が1になる）ことを満たすための値と解釈できます．

「事前」，「事後」という言葉が出ていますが，ベイズの定理では尤度を掛けることで確率が更新されていると考えます．また，重要なことは，左辺と右辺の分子に存在する条件付き確率の変数が反転していることです．反転することで，確率分布のモデル化が容易になるなどの利点を得られたりします．



# まとめ

この記事では，自己位置推定に必要になる確率の基礎的な話をしました．この記事を基に，確率的な自己位置推定の定式化に関する話を進めていこうと思います．

次の記事[【自己位置推定の基礎】ベイズの定理と全確率の定理のグラフィカルモデル](https://qiita.com/NaokiAkai/items/88a37b47ee74a568a813)
【Unity】画像の外部読込時に、端に謎の線が出る際の対処法

画像の外部読込時に、画像の端に謎の線が残る現象が起きました。
どうやら画像の逆端の部分が見えてしまっているようです。
インスペクターで設定を変更すればいいのですが、外部読込の場合スクリプトで設定を変更する必要があります。

#####TextureWrapModeをClampに設定する
```ruby:filename.TextureWrapMode
image.sprite.texture.wrapMode = TextureWrapMode.Clamp;
```

下記記事を参考にしました。

参考記事：
[Unityで画像の端に謎の線が出るときの対処法](https://doruby.jp/users/whale/entries/Unity%E3%81%AE%E7%94%BB%E5%83%8F%E3%81%AE%E7%AB%AF%E3%81%AB%E8%AC%8E%E3%81%AE%E7%B7%9A%E3%81%8C%E5%87%BA%E3%82%8B%E3%81%A8%E3%81%8D%E3%81%AE%E5%AF%BE%E5%87%A6%E6%B3%95)





下記も試したので、一応載せておきます。
（→僕の場合は線が残りました）

```ruby:filename.UseSpriteMesh
image.useSpriteMesh = true;
```

参考記事：
[【Unity】SpriteAtlasでパックしたスプライトをUIに使用したときに出るゴミの対処](http://tsubakit1.hateblo.jp/entry/2018/11/05/224721)
【HTML,CSS,JQuery】スクロール時にヘッダーの背景色を変える

割とよく使うので忘備録として・・。

##スクロール時にヘッダーの背景色を変える

1. タブレット画面幅以下の時のみ
2. ヘッダーが固定
3. ヘッダー透明でトップビジュアルの上にあるが、スクロールしたら透明は見難いから色をつけたい。


```html
<header>
  <p><a href="/"><img src="pathToLogo" alt="ロゴ"></a></p>
  <button><img src="pathToImg" alt="メニューボタンなど"></button>
</header>

```

```javascript
$(function(){
   var breakPoint = 768;
   if ($(window).innerWidth() <= breakPoint) {
     var mainVisualHeight = 420;
     var header = $('header');
     $(window).scroll(function () {
       $(this).scrollTop() > mainVisualHeight ?  header.css('background', '#fff'): header.css('background', 'inherit');
      });
     }
});
```
Azure IoT Hubのアップグレード・ダウングレードの考え方を理解する

# Azure IoT Hubとは？
IoTデバイスとMicrosoft Azureを橋渡しする，Microsoft Azureが提供するマネージドサービスのひとつです。詳細は，[Azure IoT Hubの概要](https://docs.microsoft.com/ja-jp/azure/iot-hub/about-iot-hub)を確認ください。

# IoT Hubの価格帯が知りたい
[Azure IoT Hub の価格](https://azure.microsoft.com/ja-jp/pricing/details/iot-hub/)を参照ください。価格ももちろん大切なのですが，このサイトのFAQにIoT Hubのアップグレード・ダウングレードの説明が簡潔に記載されています。以下でも画像を交えて補足します。

# BasicとStandardの違いは？
[Basic レベルと Standard レベル](https://docs.microsoft.com/ja-jp/azure/iot-hub/iot-hub-scaling?branch=release-iotbasic#basic-and-standard-tiers)をご参照ください。BasicとStandardの大きな違いのひとつとして，Basicではデバイスからクラウドのみの通信が可能ですが，Standardは双方向の通信が可能です。

# IoT Hubのアップグレード方法が知りたい
可能です。[IoT Hub のアップグレード方法](https://docs.microsoft.com/ja-jp/azure/iot-hub/iot-hub-upgrade)をご参照ください。

注意点としましては，F1(Free)の扱いです。Azure Portalで確認したところ，下記の通り**F1(Free)からS1，S2，S3への変更はできません。また，Basicへの変更もできません。**FreeはFreeのままです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492296/f995809e-fdfc-9979-9f64-b88763fe1d57.png)

よって，Freeである程度検証し，Standardへアップグレードして本番へ...という事ができません。Freeを検証用途で使用し，本番適用にはSLAやサポートが受けられるStandardのIoT Hubを別に準備する必要があります。

# IoT Hubのダウングレード方法が知りたい
Azure Portalでも確認したところ，[Azure IoT Hub の価格](https://azure.microsoft.com/ja-jp/pricing/details/iot-hub/)のFAQに記載があるとおり，StandardからBasicへの変更はできないようです。他方，ユニット数・サイズのダウングレードは可能です。

(1) IoT Hubのユニット数削除
可能です。
IoT Hubのユニット数2だったものを

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492296/398e249e-5c0f-8805-66b6-5764c39c05d1.png)

ユニット数1に変更してもチェックは通り，

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492296/2db1de6d-8747-2ca0-8c5a-b4b5bb2bc47a.png)

以下の通り処理が完了します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492296/bdf53af3-fd30-d6e7-c514-9b8d7aec35cd.png)

(2) IoT Hubのサイズを小さいサイズに変更する
可能です。
エディション・レベルがS2のIoT Hubを準備します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492296/63e64b1f-651d-212e-951b-3308e48b5873.png)

現状S2ですが，S1を選択し，[選択]ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492296/a5c5e6ed-f03a-715f-f915-0ecc2dadc158.png)

画面上部にある[保存]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492296/ac2136ab-e9e2-0a19-7dd8-64fe1e148b14.png)

S1に変更できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492296/47e35e35-293d-b6d9-2e4f-53ba95a10130.png)

(3) StandardからBasicに変更する
**できません。**

StandardのIoT Hubを準備します。今回は，S1を準備しています。[価格とスケールティアを選択]をクリックしてBasicに変更しようとしたところ...

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492296/ffb38195-0a7d-65ad-47e9-e1316973b1f3.png)

「下位レベルにダウングレードすることはできません。」というメッセージが表示され，Basicと**F1(Free)**も選択できません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492296/64f36e04-af82-f706-1296-41d99350ba86.png)

PoCの段階では試行錯誤しながらこれらのエディションやレベルを変更するかと思いますが，「試しにStandardモデルのIoT Hubをはじめにデプロイして，Standardの機能がいらないことが分かったら，Basicに変更すればいいんじゃない？」という考え方は，同じIoT Hubでは適用できません。単純なBasic，Standardの違いを検証されるのであれば，Basicから検証しStandardにアップグレードするか，それぞれ別のIoT Hubを準備して検証することがよいでしょう。
git push　エラー出た

#git pushしたらエラーが発生した時のメモ

##初心者です、ある日いつものようにgit pushしたらエラーが出たのでメモ。

・結論原因は前日にgithubのREADMEを直接リモートリポジトリで編集保存してしまったから。。。

その為解決方法はマージすればOKでした。

エラーコード
`error: failed to push some refs to 'git@github.com:username/sample.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.`

マージコード
`git fetch && git merge origin/master`


初心者が一人でやってるとgitでブランチ切ったりマージしたりってあんまりやらないので初歩的過ぎるミスでした。。
あると便利な PHP の配列関数

## 作った理由

とあるプロジェクトで自動化システムを作っています。
大変複雑で、配列を使いまわしているうちに、何度も使う処理を必要にかられて関数にしました。

なんでこういう関数がないんだろう？ と同じように思われている方の参考になれば幸いです。

* array_same ... 単純ですが本当にこれがないと大変です…。
* array_remove ... 同名の関数が PostgreSQL にはあります。PHP にはないのが不思議です。

2020.03.19 ブログの方でコメントを頂き、array_same を短縮化しました。pickles さん、ありがとうございます。

---


## 配列の中の値がすべて同じか

```php
/**
 * 配列の中の値がすべて同じか
 * @param array $array 配列
 * @return bool
 */
function array_same($array)
{
    return count(array_unique($array)) === 1;
}
```

## 配列から指定の値を削除

```php
/**
 * 配列から指定の値を削除
 * @param array $array 削除対象の配列
 * @param array $val 削除する値を配列で指定
 * @return array
 */
function array_remove($array, $val)
{
    // 取り除いた後、インデックスは元のままなので詰める
    return array_values(array_diff($array, $val));
}
```

※ <a href="https://web-atelier-midori.com/blog/%e3%82%b5%e3%83%bc%e3%83%90%e3%82%b5%e3%82%a4%e3%83%89/php/3165/" target="_blank">ブログ</a> に同じ記事を掲載しています。
Nuxt.jsとFirestoreでリアルタイム反映（vuexを使わずお手軽実装）　

# Firestoreの設定
1. FIrestoreのプロジェクト作成
2. コレクション作成（RDBでいうテーブル）
3. ドキュメント作成（RDBでいうレコード）

# Nuxt.js側の設定
## firebaseをインストール
npm install firebase --save

## firebaseのapi情報をfirebaseのコンソールで確認
1. 左上の歯車から[プロジェクトを設定]を選択
2. マイアプリから[アプリを追加]
3. プラットフォーム[</>]を選択
4. API情報が表示される

## firebaseのAPI情報の値を参考にpluginに記述
plugins/firebase.js

```js

import firebase from 'firebase/app'
import 'firebase/firestore'

firebase.initializeApp({
  apiKey: '### FIREBASE API KEY ###',
  authDomain: '### FIREBASE AUTH DOMAIN ###',
  projectId: '### CLOUD FIRESTORE PROJECT ID ###'
})
const db = firebase.firestore()
export default db
```

## nuxt.config.jsへ追記
```js

plugins: [
  { src: '~/plugins/firebase' }
]
```

# データ操作方法
```js

<script>
import db from '~/plugins/firebase'

export default {
  data() {
    return {
      users: []
    }
  },
  methods: {
    /*
     * 一覧取得
     * onSnapshot()メソッド：リアルタイム反映する
     * get()メソッド：リアルタイム反映しない
     */
    getUsers() {
      db.collection(コレクション名).onSnapshot((docs) => {
        this.users = [] // リアルタイム反映のため一度リセット
        docs.forEach((doc) => {
          let user = doc.data()
          user.id = doc.id // 一意のドキュメントIDをプロパティに追加（後のupdateやdeleteでドキュメントIDが必要なため）
          this.users.push(user)              
        })
      })
    },
    /*
     * 検索
     * 複数クエリはwhereをつなげる
     */
    searchUsers() {
      db.collection(コレクション名)
        .where('name', '==', 'boby')
        .where('age', '>', 20)
        .get((docs) => {
          this.users = []
          docs.forEach((doc) => {
            let user = doc.data()
            user.id = doc.id
            this.users.push(user)              
          })
      })
    },
    /*
     * ドキュメント新規登録
     */
    addUser() {
      db.collection(コレクション名).add({
        name: 'Boby',
        age: 23
      })
    },
    /*
     * ドキュメント削除
     */
    deleteUser(docId) {
      db.collection(コレクション名).doc(docId).delete().then(() => {
        //success
      })
      .catch((error) => {
        //error
      })
    },
    /*
     * ドキュメント更新
     */
    updateUser(docId) {
      db.collection(コレクション名).doc(docId).update({
        name: 'Mike',
        age: 18
      }).then(() => {
        //success
      })
      .catch((error) => {
        //error
      })
    }
  }
}
</script>
```


参考：
https://firebase.google.com/docs/firestore/quickstart?hl=ja#initialize
Reactで外部ドメイン向けにリンクを生成する（ReactRouterだとできない？ので。。）

## 概要

Reactで外部ドメイン向けにリンクを生成する（ReactRouterだとできない？ので。。）

## 実装

↓で定義した`LinkToOtherDomain`コンポーネントを使う。

```jsx
import React from "react";

export const LinkToOtherDomain = props => {

    const { to, children, ...other } = props;
    return (
        <a href={to} {...other} rel="noopener noreferrer">
            {children}
        </a>
    );
};
```
Python上でShellファイルを呼び出した後、CSVをParquetに変換する。

#全体的にやりたいこと
大量のCSVファイルをParquetに直す必要があるが、
そもそもCSVファイルのヘッダーにカラム名行が存在しないため、
・CSVファイルにヘッダー追加
・CSVをParquetに変換
この２ステップを行うツールを作成する必要がありました。

＜＜前提＞＞
CSVのヘッダーに追加するカラム名がParquetファイルの値に対するタイトルとなります。
もし、ヘッダー行が存在せず、いきなりデータが存在する場合、
出力したParquetファイルの各タイトルは、１行目のデータとなってしまいます。

##PythonからShellを呼び出す
CSVのヘッダー行を追加する処理はPythonでも書くことができましたが、
Shellで比較的簡単に追加可能だったので、
Shellで作成し、そのファイルをPythonから呼び出すようにしました。

```python:qiita.py
import subprocess

# comment
cmd = './add_header.sh'
subprocess.call(cmd, shell=True)
```

subprocessでShellを指定することで、
外部のShellファイルを呼び出すことができます。

```shell:add_header.sh
##!/usr/bin/env bash
for file in `\find from_dir -maxdepth 1 -type f`; do
    gsed -i '1iheader1,header2' $file
done
```

gsed呼び出し時の「1i」が必要になります。

gsed・・・gnu-sedのインストールをお願いします。

■実行結果CSVファイルのヘッダー
header1,header2

##CSVをParquetに変換
S3上に存在する大量のCSVファイルをParquetに変換する必要がありました。
ファイルは全てローカルにダウンロードしている状態です。

```python:qiita2.py
import pyarrow as pa
import pyarrow.parquet as pq
import pandas as pd
import glob

from_dir = './from_dir/'
to_dir = './to_dir/'

#from_dir内のCSVを全て読み込む
files = glob.glob(from_dir + "*", recursive=True)

#1ファイルずつ変換し、to_dirに格納する
for file in files:
    path_name = file.split('/')
    df = pd.read_csv(file)
    table = pa.Table.from_pandas(df)
    pq.write_table(table, to_dir + path_name[2] + '.pq')
```
csvファイルの読み込み、出力はpandas
Parquetへの変換はpyarrowで簡単に可能です
CloudFrontは同じURL+QueryStringでもキャッシュ応答しないケースがある

## はじめに

あるAPIサービスが、CloudFront + API Gateway + Lambda という構成で稼働しており
CloudFrontは、`URL+QueryString`ごとにキャッシュし、応答速度をあげるために使用しています。

## 事象

このAPIサービスには、Go/PHPそれぞれで実装されたプログラムがHTTPリクエストを送ります。
CloudFrontの設定は以下の通り。（関連設定のみ)

| 設定項目 | 設定値 |
|--------|-------|
| Cache Based on Selected Request Headers | None |
| Object Caching | Customize<br>MinTTL:0<br>MaxTTL: 86400<br>DefTTL: 86400 |
| Forward Cookies | None |
| Query String Forwarding and Caching | Forward all, cache based on all |
| Compress Objects Automatically | No |

Go/PHPそれぞれから同じ URL+QueryString でリクエストを送信した場合は、２回目のリクエストは
キャッシュHitする想定でしたが、なぜかMissになります。(X-Cacheヘッダで確認)

## 原因

Go実装のプログラムでは 標準のnet/httpパッケージを使ってリクエストを送信しています。
net/httpのhttpクライアントはレスポンスがgzip圧縮されていても自動的に展開してくれるため
デフォルトで`Accept-Encoding: gzip`ヘッダが付きます。
CloudFrontは、このAccept-Encodingヘッダの有無でキャッシュが別管理になるため、
同じURL+QueryStringであっても、ヘッダ有無が異なれば再度オリジンアクセスする、という動作になります。
これは、レスポンスが圧縮されているかに関わらず上記の動作になりました。

コメントで指摘あったので追記）
`Vary: Accept-Encoding`がレスポンスヘッダにあればAccept-Encodingヘッダ有無で
キャッシュが別になるのは理解できますが、今回使用しているオリジンは、レスポンスを圧縮しない、かつ
Varyヘッダを返さない仕様です。

#### DefaultClientでリクエスト送信 実装例

```go:main.go
package main

import (
	"fmt"
	"net/http"
)

func main() {
	url := "http://example.com/"
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		fmt.Errorf("%v", err)
		return
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		fmt.Errorf("%v", err)
		return
	}
	defer resp.Body.Close()
}
```

今回CloudFront設定を素直に？以下のように解釈してたので発見が遅れました
* リクエストヘッダでキャッシュ判定しない
* URL+QueryStringごとにキャッシュする
* 自動圧縮は無効化してるのでAccept-Encodingを見ない

↑このAccept-Encodingを見ないというのが勘違いで、オリジン側では通常Accept-Encodingヘッダによって
gzip圧縮するか判定しているはずで、CloudFrontも正しくキャッシュ（非圧縮を期待しているクライアントに圧縮
データを応答しないこと)するためにAccept-Encodingヘッダをキャッシュ判定に使用していると思われます。

Go側でリクエスト送信時にAccept-Encodingヘッダを付けないようにすればキャッシュがうまく使われるように
なります。以下Go実装の修正イメージ

#### Accept-Encodingヘッダを付加しないリクエスト送信 実装例

DisableCompressionをtrueにしたTransportを生成して、それを渡してclientを生成してclient.Doする形

```go:main.go
package main

import (
	"fmt"
	"net/http"
)

func main() {
	url := "http://example.com/"
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		fmt.Errorf("%v", err)
		return
	}
	client := &http.Client{
		Transport: &http.Transport{DisableCompression: true},
	}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Errorf("%v", err)
		return
	}
	defer resp.Body.Close()
}
```

## おわりに

CloudFrontの公式ドキュメントに明確な記述が見つけられなかったので、今回記事にしました。
誰かの助けになれば幸いです。
同じ拡張子なのに動画が再生されない

### やりたいこと
web上で動画を再生したい


### 問題
.mp4や.movの拡張子がついているファイルを再生しようとした時に再生できるものとできないものが存在する。


### 結論
ファイル形式(拡張子)で判断するのではなく、コーデックをブラウザが対応するものに変換する。

---
### 動画の構造
動画は大きく**映像と音声という2つの構造**でできている。
動画は容量が大きいファイルなので圧縮してやる必要がある。
(基本的に動画を録画した時点で勝手に圧縮されたファイルが作成される)

### コーデック
圧縮する行為を**エンコード**
圧縮したままでは再生できないので、圧縮をもとに戻すのが**デコード**と呼ぶ。
エンコードとデコードの2つを組み合わせて**コーデック**と呼ぶ。
動画の場合は映像と音声の2つのコーデックが必要になる。

#### コーデックの種類

代表的な**映像コーデック**
 MPEG-4・ MPEG-1・MPEG-2・ Xvid・Divx・H.263・H.264・HEVC(H.265) など
 
代表的な**音声コーデック**
 MP3・AAC・AC-3・FLAC・LPCM・WMA　など
 
### 動画が再生されない原因
映像が再生されないということは映像コーデックが対応していないブラウザである可能性がある。
映像コーデックをChromeなどブラウザの規格に合わせる必要がある。
逆に音声が流れないという場合は音声コーデックを見直す必要がある。

### コンテナ
.mp4や.movはコーデックの組み合わせるための**コンテナ**と呼ばれるもの。
コンテナごとに扱えるコーデックが決まっている。

**.mp4**
映像: MPEG-4、H.263、H.264 など
音声: MP3、AC3、AACなど

**.mov**
映像: H.263、H.264、HEVCなど
音声: MP3、AAC 、FLAC など

### 具体的なケース
自分のケースで言うと、.movのコンテナで H.264とAACのコーデックだと正常に再生されるけど、.movコンテナ HEVCとAACのコーデックだと映像が再生されないということが起きた。

この場合はHEVCのコーデックを変換して、H.264などにする必要がある。

### まとめ
1つのコンテナでも複数のコーデックを扱うことができるので、.mp4だからと言って全て再生できるとは限らない。コーデックがなんなのか？ブラウザはどのコーデックに対応しているのか？を確認する。


#### 参考
>[コーデックとは？ 動画を扱うなら知っておきたい基礎知識｜ネットワークビデオレコーダー（NVR）](https://nvr.bz/topics/knowledge/what-codec.php)
Embarrassingly parallelとは？ 

日本語での意味が載っているところがすぐに見つからなかったのでメモ。

## 英語のwikipediaページ
https://en.wikipedia.org/wiki/Embarrassingly_parallel

## 意味
「**(タスクの間に)依存性がなく、独立して並行で処理できる**」というような意味です。

Embarrassingly parallelを単体で使うことはあまりないようですね。使うときにはEmbarrassingly parallelなタスク群、という風に使うようです。

Embarrassingly parallelなタスク群といったときには「**それぞれのタスクの間には依存性がなく、それぞれのタスクを独立して並行で処理できるタスク群**」というような意味になります。



## 例
[Apache Beamの概要ページ](https://beam.apache.org/get-started/beam-overview/)から拝借。
> Beam is particularly useful for Embarrassingly Parallel data processing tasks, in which the problem can be decomposed into many smaller bundles of data that can be processed independently and in parallel.

訳：Beamが特に得意なのは、各データが独立しており並行で処理が行えるようなタスク(Embarrassingly Parallelなデータ処理)です。

## 関連
他にもperfectly parallel、delightfully parallel、pleasingly parallelと呼ぶこともあるようです。
Hashの先頭からｎ個をとりだす

データを抽出してchartkickに渡すHashに対し、先頭n件を抽出したかった。
helperに以下のメソッドを作成。

```ruby
def chart_limit(hash, limit=5)
  # Hash[*hash.to_a.shift(limit).flatten!]
  # コメントを受けて以下に変更。
  hash.take(limit).to_h
end
```

```ruby:利用例
hash = {"男性40代"=>13, "女性40代"=>6, "女性30代"=>5, "男性60代"=>4, "男性30代"=>4}
p chart_limit(hash, 3)
#=> {"男性40代"=>13, "女性40代"=>6, "女性30代"=>5}
```
ruby 2.6.5で確認済み。

### 参考
- [Ruby \- Ruby Hashの先頭ｎ個を別のHashとして抽出する｜teratail](https://teratail.com/questions/59631)
- [Hash\.\[\] \(Ruby 2\.7\.0 リファレンスマニュアル\)](https://docs.ruby-lang.org/ja/latest/method/Hash/s/=5b=5d.html)
- [Enumerable\#take \(Ruby 2\.7\.0 リファレンスマニュアル\)](https://docs.ruby-lang.org/ja/2.7.0/method/Enumerable/i/take.html)
- [Enumerable\#to\_h \(Ruby 2\.7\.0 リファレンスマニュアル\)](https://docs.ruby-lang.org/ja/2.7.0/method/Enumerable/i/to_h.html)
【ReactNatve】Platform.OSが必ずiosになった話

# TL;TR
- `Platform.OS`の値が常に`ios`になってしまった
- 原因は初歩的なタイプミスだった
- コミットの粒度はなるべく小さくしよう

# 何が起こったか
ざっくり説明すると`ReactNative`製アプリをAndroidで動かした時に、iOSでしか表示されないUIが表示されていました。
下記のように`Platform.OS`を表示させてみると、出力されるのは`ios`。
なぜかOSの種類に関わらずiosとして扱われてしまっているようでした。

```javascript
import { Platform } from 'react-native';

console.log('os : ', Platform.OS); // --> "os : ios" と出力される・・・
```

`react-native`の不具合ならもっとIssueで大々的に騒がれてそうだけど、そんな気配もない。
となると何らかのバグなのだが、Platform.OSの値が必ずiosになってしまうってどんな状況やねん・・・

と思いながら直近のコミットを細かく見ていくと、

```javascript
import { Platform } from 'react-native';

if(Platform.OS = "ios") {
    // iOS限定の処理をつらつらと
}
```

**ああああああああ!!!!**
**`Platform.OS = "ios"`って書いてるうぅぅぅ!!!!**

この処理以降、`Platform.OS`を参照しても代入された`ios`が取れてしまうわけです。

## 補足
結果的に比較演算子のタイプミスだったわけですが、そもそもこの書き方でif分岐がどうなるかを解説します。

[if...else - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/if...else)によると下記のような記述があります。

> ## 条件式の中での代入
> 代入はコードを眺めたときに等式と混同される可能性があるの?で、条件式の中で単純な代入を使わないほうが望ましいです。たとえば、次のコードを使わないでください。

```javascript
if (x = y) {
   /* do the right thing */
}
```

なるほど、使わない方がいいけど文法的にはアリみたいです。

ちなみにこの場合、ifの評価は代入された後の変数で行うらしく、下記のような結果になります。

```javascript
var a = 0;

if(a){
  // 通らない
}
else {
  // 通る
}

if(a = 10){
  // 通る
}
else {
  // 通らない
}

if(a = undefined){
  // 通らない
}
else {
  // 通る
}
```

つまり先のタイプミスをした

```javascript
if(Platform.OS = "ios") {
    // iOS限定の処理をつらつらと
}
```
は評価としてはtrueになります。

# まとめ
結果的に原因は初歩的なタイプミスだったわけです。
気付いてみればなんてことないんですが、大規模なコミットに埋もれていたため見落としてしまいました。
テストで発見できればよかったのですが、Android端末で問題となった処理を行った後でないと`Platform.OS`が書き換わらないのですり抜けてしまったようです。

教訓としては、コミットの粒度を小さくしてタイプミスを発見できるようにする。です。
adbコマンドを使って実機で楽々モンキーテスト

##モンキーテストとは
>モンキーテストとは、機器やソフトウェア、システムのテスト手法の一つで、対象箇所や操作手順などを事前に定めず、実施者がその場の思いつきで操作してみる方式。開発者の意図などを一切考慮しないことを「猿に使わせてみたらどうなるか」に例えた名称である。
>[http://e-words.jp/w/モンキーテスト.html より引用](http://e-words.jp/w/%E3%83%A2%E3%83%B3%E3%82%AD%E3%83%BC%E3%83%86%E3%82%B9%E3%83%88.html)

すなわち「猿のように画面も見ずに連打」すればいいわけです。
が、人力はちょっとツライですね。もちろんアプリ自体にそういうモードを用意してあげることもできますが、結構骨が折れます。

そんな貴方に朗報です、Androidには**これを自動でやってくれる仕組みが既にあるんです！**

##コマンド

```
adb shell monkey -v -p パッケージ名 --throttle イベント間隔(ms) --pct-touch 100 回数
```

これで、指定したパッケージアプリが自動で起動し（先に起動しておいても良い）、指定したイベント間隔で指定回数ランダムで画面を連打してくれます。
（パッケージ名はUnityのProjectSetting→Player→OtherSettings→PackageNameで指定したものです）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37184/0ab36e30-2075-cc35-8d52-7c4b085ce6a2.png)


このイベント間隔と回数をかけ合わせれば、終わる時間が分かるので、寝る前から次の日の朝まで、仮に10時間モンキーテストさせたい。

という場合は

10時間=600分=36000秒

なので、イベント間隔が100ms(=0.1秒)であれば、360000 を回数に指定すれば良いことになります。

＜例＞

```
adb shell monkey -v -p jp.com.MyApp --throttle 100 --pct-touch 100 3600000
```

## 注意
なお途中で辞めたくなってCtrl+Cで止めても、adbのshell上ではプロセスが動いてしまっているので、モンキーテストは止まりません。

止めるにはプロセスを指定してkillする必要があるため

```
adb shell kill $(pgrep monkey)
```

とやる必要があります。

また、例えばアプリに終了ボタンなんかがある場合はモンキーテストにより押されてしまい、終了してしまう可能性があります。
なので、そのあたりは先に無効化するなどの考慮が必要です。
（※間違っても課金が発生するような処理をそのままにしないように！）

## 準備
adbとは `Android Debug Bridge` からくる略称のツールで、Android実機とPCを接続しデバッグの補助をしてくれるものです。
これは `AndroidSDK` に含まれています。

コマンドプロンプトから adb とコマンドを打って、何かズラズラとadbのヘルプテキストが表示される場合は問題ありませんが、

```
>adb
'adb' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
```
こんな感じになってしまう方はadb.exeへのパスが通っていませんので、先にパスを通す必要があります。

自分でAndroidSDKを用意した方は `AndroidSDKをインストールしたフォルダ\android-sdk\platform-tools` なんですが、
**Unityをインストールする際にAndroidSDKのインストールにチェック入れ**てついでにセットアップした場合は
`[UnityHubで設定したUnityのインストール先]\Hub\Editor\[Unityのバージョン]\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platform-tools`
と、少々奥深くに置いてあるはずです。

![UnityHubで設定したUnityのインストール先](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37184/ae9ac5c3-9e85-acbb-f176-ea8f5b817f0e.png)

これをパスに設定、もしくはフルパスでadbコマンドを実行するようにしてください。

## まとめ
このadbコマンドによるモンキーテストにより

- 上記の意図しない操作による不具合を発見する

に加え、人間ではなかなか現実的でない

- 24時間ゲームを起動し（遊び）続けても大丈夫か

というような、耐久テストも兼ねることができます。　（エイジングテストともいうかな？）
手元のアプリも公開する前にちょっと24時間ぐらい走らせてみたらどうでしょうか。
思わぬ進行不能バグ。　思わぬメモリリークが見つかるかもしれませんよ。

ではでは。
AthenaでCSVファイルからParquet形式へ変換

## Parquetにしたい場合
AthenaでCSVファイルを読み込んでいるとクエリによってスキャンデータ量が大きい場合があります。
例えばカラム数がかなり多いが、実際に使うカラムは少ない場合などが該当すると思います。
またCSV自体を圧縮できていなかった場合は単純にファイルサイズも減少します。

## Parquetに変換
変換したい際にPythonなどを使ってCSV形式からParquetへ変換を行っていたのですが、Athena上でも一応変換（指定ディレクトリに新しいParquetファイル）ができます。

``` sql:convert.sql
CREATE TABLE new_table_name
WITH (
    format='PARQUET',
    external_location='path/to/new/files/directory'
) AS
SELECT * FROM original_table_name
```

このようにして、元々CSVを読み込んでいたテーブルから新しいテーブルを作成します。そのときに`format='PARQUET'`を指定することでファイルがParquetに変換されます。
注意点としては、元ファイルのファイル数と異なるのでどのファイルに何が入っているかわからなくなる点です。
その場合は月ごとなどにテーブル・ディレクトリを作って、それをまとめて見られるテーブルを作成すると対応できます。
全上場企業の過去５年間の決算情報をCSVファイルに変換

## はじめに
前回の記事では金融庁の[EDINET](http://disclosure.edinet-fsa.go.jp/)というサイトから得たXBRL形式のデータから決算書の情報を得る方法について書きました。

[XBRLから上場企業の決算書の情報を得る。](https://qiita.com/teatime77/items/3ed6d4cd27f6440e163a)

その後EDINETでは **EDINET API** というWebAPIが公開され自動でXBRLファイルをダウンロードできるようになりました。

今回は全上場企業の過去５年間のXBRLファイルをダウンロードして、CSVファイルに変換するプログラムを作ってみました。

処理結果のCSVファイルは以下にあります。
[http://lkzf.info/xbrl/data/summary-join.csv](http://lkzf.info/xbrl/data/summary-join.csv)
以下のように１行ごとに決算情報が書かれています。
決算の数値は連結決算の値です。
![集計.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118740/f6cc4f1f-a3ec-5856-e0d9-cbb68c92f9c5.png)

## 簡単な応用例
表計算ソフト(LibreOffice Calc)を使ってCSVファイルから売上総利益率(粗利率)の度数分布表を作ってみました。
ほかにもいろんな使い方があると思います。
![hist.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118740/84b70e08-3cc2-0de7-bda5-e09fd6c902b1.png)

## CSVファイルの作成手順
アプリのソースは前回の記事と同じGitHubに入っています。
[https://github.com/teatime77/xbrl-reader](https://github.com/teatime77/xbrl-reader)

以下に処理内容の説明があります。
[http://lkzf.info/xbrl/doc](http://lkzf.info/xbrl/doc)


## 財務分析のサンプルアプリ
CSVファイルのデータを使って財務分析をするウェブアプリも作ってみました。
以下のURLからアプリにアクセスできます。
[http://lkzf.info/xbrl/chart/](http://lkzf.info/xbrl/chart/)
![app.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118740/550f5f84-1fdc-66c6-cc2c-a0584b72abf5.png)
ChromeとFireFoxで動作を確認しています。
グラフの描画に[Plotly.js](https://plot.ly/javascript/)を使いました。
ソースはGitHubにあります。
[https://github.com/teatime77/xbrl-chart](https://github.com/teatime77/xbrl-chart)

## インラインXBRLファイルで決算情報を確認

EDINETからダウンロードしたZIPファイルの中には、HTMLファイルにXBRLタグを埋め込んだ **インラインXBRLファイル** があります。
このファイルは下図のようにブラウザで開いて内容が見れるので、数値の確認ができます。
![財務諸表.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118740/d190a78a-6d33-3ba8-1af8-84b1d07d266d.png)

ZIPファイルは提出年月日ごとにフォルダに入っています。
例えば提出年月日が **2022/06/24** なら、下図のように **xbrl-reader\zip\download\2022\06\24** にZIPファイルがあります。

ZIPのファイル名の先頭6桁がEDINETコードです。この例では **E00012** がEDINETコードです。

ZIPファイルの中の **XBRL\PublicDoc** の中で末尾が **ixbrl.htm** のファイルが **インラインXBRLファイル** です。
![エクスプローラー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118740/0fe148a7-4178-b752-ae5d-aa2894330b47.png)

貸借対照表や損益計算書は **0105010** や **0105020** で始まる HTMファイルにあることが多いです。 (例外もあります。)

## おわりに

まちがいなどがあれば、コメント欄に書かれるかTwitterに連絡をお願いします。
Twitterでアプリの更新のお知らせもしていきます。
[![Twitter_Logo_White_On_Blue20.png](https://qiita-image-store.s3.amazonaws.com/0/118740/af910f48-8c6b-27cb-7951-eaa6e97afa87.png)](https://twitter.com/teatime77)  [teatime77](https://twitter.com/teatime77)

**ここまで読んでいただき、ありがとうございました。**
メディアからリポジトリを作成する方法

パッケージをインストールする際、インストールメディアからリポジトリを作成する手順です。

メディアをマウントする。

```
# mount /dev/cdrom /media
```
リポジトリファイルの作成・編集

```
# vi /etc/yum.repos.d/dvd.repo
[dvd]
name=dvd
baseurl=file:///media
/gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
```

リポジトリとして認識されていることを確認する。

```
# yum repolist
読み込んだプラグイン:product-id, search-disabled-repos, subscription-manager
This system is not registered with an entitlement server. You can use subscription-manager to register.
リポジトリー ID                                                                リポジトリー名                                                                                          状態
!dvd                                                                           dvd                                                                                                      4,986
Heroku URLの確認　一覧表示

## 背景
[Raisl チュートリアル](railstutorial.jp)を行っていたらherokuの5appの制限一杯になってしまいました。

削除すればいいだんだが、どれがどれかわからない。
（heroku openがクラウドのIDEで入ってなかったり・・）

テストで作ったやつを消せばいいんだがわからないのでherokuアプリの一覧取得と概要取得の方法です。概要を取得することで、herokuのurlの確認できます。

## 一覧を取得(heroku apps)
```bash:アプリの一覧取得
$ heroku apps

=== My Apps
XXXling-savannah-XXXXXX
XXXXXXring-ridge-XXXXXX
XXXXXXous-wildwood-XXXXXX
XXXXXXXXXXXX
pure-XXXXXX-4XXXXXX
```
※適用に出力結果は置換してあります。

## 概要取得(heroku info --app [アプリ名])
```bash:アプリの概要取得

$ heroku info --app　XXXling-savannah-XXXXXX

Addons: XXXXXXXXXXXXXXXXXXX
Dynos: web: 1
Git URL: XXXXXXXXXXXXXXXXXXXXXXXXgit
Owner: XXXXXXXXXXXXXXXXXXX@gmail.com
Region: us
Repo Size: 43 KB
Slug Size: 31 MB
Stack: cedar-14
Web URL: https://XXXXXXXXXXXXXXXXXX.herokuapp.com/
```
※適用に出力結果は置換してあります。

## すべてのアプリの概要を表示 
```bash:一覧取得からの概要表示
$ heroku apps | sed -e '1d' | sed -e '$d' | xargs -n 1 heroku info --app
```
### 解説
```bash:読み解いていくと
$ heroku apps \  # 一覧出力

| sed -e '1d' | sed -e '$d' \ # 一覧で出力されるいらない部分を削除（一行目と最後の行）

| xargs -n 1 heroku info --app # 1つ1つ出力結果を詳細情報を表示するコマンドの引数として渡す。
```
 
## 確認環境 herokuバージョン（7.39.0）

```bash:heroku_version
$ heroku -v
heroku/7.39.0 linux-x64 node-v12.13.0
```

## 参考（リンク）

[Heroku アプリケーションの管理 - 君の瞳はまるでルビー - Ruby 関連まとめサイト](http://www.ownway.info/Ruby/heroku/how/management/application)

[逆引きUNIXコマンド/sedで指定した行を削除する方法 - Linuxと過ごす](https://linux.just4fun.biz/?%e9%80%86%e5%bc%95%e3%81%8dUNIX%e3%82%b3%e3%83%9e%e3%83%b3%e3%83%89/sed%e3%81%a7%e6%8c%87%e5%ae%9a%e3%81%97%e3%81%9f%e8%a1%8c%e3%82%92%e5%89%8a%e9%99%a4%e3%81%99%e3%82%8b%e6%96%b9%e6%b3%95#h9456ca3)

[xargs コマンド | コマンドの使い方(Linux) | hydroculのメモ](https://hydrocul.github.io/wiki/commands/xargs.html)

[Herokuで今までアップしたアプリを整理する](https://qiita.com/YJ2222/items/aded686307a0e346e623)

※この内容は4年前の[はてなの記事](http://onthe6ks.hatenablog.com/entry/2016/02/16/115142)の移植し、校正しました。
Storyboardの名前を変えようとしたら思った以上に変えるべき場所が多かった話

## 環境

Xcode 11.3.1でSceneDelegateも含めた新規プロジェクトを作成した。
対応デバイスはiPadのみにしていた。

## 何が起きたか？

アプリを製作中、ストーリーボード分割のためにMain.storyboardからViewController.storyboardに名前を変えて以下の部分を設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10943/f8a4fcee-e313-11dc-1eb3-5d33f47bc433.png)

これで大丈夫だと実行したところこのエラーが出た

```
Could not find a storyboard named 'Main' in bundle NSBundle
```

## 解決法

iOS13にはiPad対応にするとStoryboardの名前を指定するところがInfo.plistに三ヶ所ありそのすべてを新しい名前にしないといけなかった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10943/4aafb7b5-2f48-8801-bb90-077a772987ef.png)

この画像のViewControllerと書かれているところに新しいStoryboardの名前を入れておかなければいけない。
特にこのネストの深いところにある部分が本当に気づけない。

ちなみにGUI上のMain Interfaceと同期したのはMain storyboard file name (iPad)のみだった。

## 感想

いや、知らんがな笑
forkしたレポジトリの更新とupstreamについて

Gitでforkしたブランチを最新の状態に更新する方法と、それを調べたとき出てきたupstreamという概念についてのメモ。

# 1. forkしたレポジトリを最新の状態に更新する

Github上でレポジトリをforkしたあとの話。

```
$ git clone git@github.com:アカウント名/forkしたレポジトリ.git    #とりあえずforkしたレポジトリをclone
$ cd forkしたレポジトリ
$ git branch    #ブランチを確認
  * master
$ git remote -v    #リモートの状態を確認
  origin https://github.com/アカウント名/forkしたレポジトリ.git (fetch)    #自分のアカウントにあるレポジトリが指定されている
  origin https://github.com/アカウント名/forkしたレポジトリ.git (push)
$ git remote add upstream git://github.com/fork元のレポジトリ.git    #本家をupstreamとして追加
$ git remote -v    #リモートの状態を確認
  origin https://github.com/アカウント名/forkしたレポジトリ.git (fetch)    #自分のアカウントにあるレポジトリが指定されている
  origin https://github.com/アカウント名/forkしたレポジトリ.git (push)
  upstream https://github.com/fork元のレポジトリ.git (fetch)    #本家のレポジトリが指定されている
  upstream https://github.com/fork元のレポジトリ.git (push)
$ git checkout master
$ git fetch upstream    #upstreamに対してfetch
$ git git merge upstream/master    #upstreamのmasterからマージ
```

# 2. upstreamって何よ？

- Gitでの慣例として、自分のリポジトリは「origin」、フォーク元は「upstream」とする。
- origin に対しては read / write ができるが、upstream に対しては read だけができる。

参考：[Flight rules for Git](https://github.com/k88hudson/git-flight-rules)
チーム開発 3/18 

メモで
最終課題１日目
ER図法
テーブル、カラム、タイプ、オプション
ターミナル
Bundle install
Rails s

問題:Git hubでmasterに記入するが共有されない
解決? :Masterに記述する場合は親に当たる人（masterを作った人）が書かないと共有されないかも？
セッティングで招待？しないと共有されない

問題:親じゃない人がプルリクエストした場合にその人がマスターのブランチに戻した時に
記述したところが消えてしまう

前のところのまま更新されていない？
変なエラー？プルリクエストとした後にセッティングした時におかしくなった？
一回招待を取り消してみて再招待してみる
ファイルが消えてしまったためもう一度git cloneしていく
やり直し
解決：やり直し

☑️環境設定

ER図法を提出
問題点、修正点を探す
宅配のアドレスにカラムは１つで十分
サイズのテーブルを消してユーザーに書いてもらう形にする、ユーザーのテーブルにカラムとして追加

まとめ　次どうするか
トップページを二人でやる
トップページは金曜日
ユーザーのログインページ1人だと厳しいかも
新規登録が大変
テストもある

誰が何をするかを決める






＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿________________
調べた事
●Desktopのブランチ消す時は上のバー（アップルのマークある方）にブランチとあるのでそこのデリートで消せる
●has_one
従属する側のモデルに belongs_to を記述し、
従属させる側のモデルに has_one を記述する。
１体１の関係の時使うかも？
かってにidを取って来てくれる
belongs_toも一緒

* 対象を1つ持っているなら、has_one
* 対象を複数持っているなら、has_many
* 自分が対象に所属しているなら、belongs_to
●clear fix
https://qiita.com/mariofujisaki/items/2ad1de8432d7249afadc
参考ページ
Flootを使った時に浮いて回り込んでしまったのを防ぐ
●mixing
@mixin に続いてお好みの名前を定義し、@include で呼び出します。何度も使うようなスタイルがあるなら、あらかじめ定義しておくと便利です。
https://www.webcreatorbox.com/tech/sass-mixin
参考ページ
Android StudioにおけるBuild Outputの文字化けを解消

#状況
Windows10上のAndroid Studio 3.6.1でアプリを開発している。文字列やコメントに日本語を含むコードも問題なくビルド・実行できる。
しかしBuild Output Windowのエラーメッセージが文字化けして読めない。

![fig1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284841/159908c1-1c06-e20b-2703-5dc01722bd43.jpeg)

#解決方法
「メニュー＞Help＞Edit Custom VM Options..」を選択する。

![fig2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284841/433de0f6-c47c-35f6-eeb0-7ccd9e778c5d.jpeg)


表示されたファイルに「-Dfile.encoding=UTF-8」を追加する。
![fig3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284841/acd9a3b9-88bb-6699-ca5d-568233cfc6f5.jpeg)


Android Studioを再起動する。
これで、文字化けが解消した。

![fig4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284841/c706e840-4776-6817-bd3d-0831c508f602.jpeg)
軽量DDDでもやったほうがいい理由、けどやらない理由

# 記事のターゲット

* DDD（ドメイン駆動設計）に興味がある人
* DDD系の本読んだけど掴み所がないなと思った人
* 戦術的DDD（軽量DDD）を取り入れようとしている人

# 戦術的DDD（軽量DDD）とは

ドメイン駆動設計の文脈で語られる**技術的要素のみを取り入れる設計手法**です
Google検索などで引っかかる情報の多くは技術的要素の内容です
（過去記事でも部分的にまとめていますので興味があれば是非）

[レイヤードアーキテクチャの視点](https://qiita.com/kichion/items/aca19765cb16e7e65946)
[ValueObjectという考え方](https://qiita.com/kichion/items/151c6747f2f1a14305cc)
[Serviceクラスの意義と勘所](https://qiita.com/kichion/items/983e8330cd2651540d9c)

「技術的要素のみ？DDDってそういうものじゃないの？」と思ったかも知れません
DDDの本質は**問題解決のための手法**なので、技術的要素はその一つでしかないのです

# 完璧じゃなくても軽量だけでもいい

DDDに関わらず、プログラミング含めたテクノロジーも問題解決のツールの一つです
そのツールが万能になるとき、問題は即座に解決されて様々なメリットをもたらします

* 新しい事業を生み出すためのお金と時間を作る
* 単純作業をなくし、QOLの高い生活が送れる
* 人生の悩みを解決してくれる
* etc...

ただし、**ツールが万能に…**という点が難しいのはこの記事を読んでいる人なら理解していると思います

* レガシーシステムが意味不明でバグの採掘所になっている
* 技術レベルの乏しいメンバーが正しく動くコードを書けない
* etc...

せめて、「問題解決領域である**プログラミング部分を本質的な問題解決に集中できる**ように！！」と
考えた人たちがドメイン駆動設計の技術的要素のみを取り入れる**軽量DDD**を実践し始めています

## うまくワークすると新しい知見が生まれる

戦術的DDDだけでもコードの信頼性・可読性があがり、テスタブルなコードを量産できるようになります
更に、業務をコードで表現できることでビジネス的な改善点が見えるなどのブレイクスルーが…！！！

# だけどやらない

「じゃぁ、なんでみんなやらないの？」「あんまり世の中でやってる話聞かないよね？」のような意見はあるでしょう

## 銀の弾丸ではない

当たり前ですがソフトウェア開発に**それだけで**すべてを解決に導くようなツールや手法はありません
戦術的DDDに集中してしまうと本質的な問題にフォーカスできないと言った副作用が必ずあります

## 学習コストは高い

昨今、わかりやすい書籍が増えてきてかなりハードルは下がっていますがそれでもイメージのしにくい分野です
一人で開発しているならまだしもチームで開発する以上、全員のレベル感に差が出るのが必然です
その足並みを揃えないとどんなフレームも瓦解する未来が待っているでしょう

## コードの制約が表現できない

今回、**一番伝えたかったこと**でもありますが「戦術的DDDはコード的な制約を表現してこそ堅牢」になります
C#/Javaと言った静的型付けを前提とした言語でないとただの口約束・コードレビューでその担保をしなければならず
導入以降の運用にもコストがかかってきます
DDDを謳っている書籍もほとんどがC#/Javaと言った制約の強度を表現力高く書ける言語を例にあげられています

# まとめ

* 戦術的DDDだけでも問題解決のフレームワークとして機能する
* 戦術的DDDが全うされると本質的な問題解決に集中できる
* DDDは銀の弾丸ではない
* 戦術的DDDだけでも学習コストは高い
* **コードの制約が表現できない場合は運用が大変**

なにか始める際の技術選定と同じように、ルールやチームフレームワークも導入・運用の難しさが伴うことが伝わればと思います
teratermでコマンドをマクロから送りたい

#とりあえず
最初のtelnetやらSSL接続の設定(コマンドの組み立てやら何とか)などはあちこちに情報が転がりまくってるのでキャンセルだ
#コード

```macro.ttl
wait '文字' ;;設定した文字が表示されるまで待つ
sendln コマンド ;;コマンドを送信する
```
###`wait '文字'`
設定した文字が表示されるまで待つ
マクロの実行速度は超スピード！？なので、何かしらの処理中に次のコマンドを送ろうとする場合もあるので、安定した動作のためには必須
なお、`'文字'`部分は`"文字"`というようにダブルクォーテーションでくくっても可
$や#にも対応してるから、コイツらを入れることが多いんじゃないかな
###`sendln コマンド`
コマンドを送信する
送信までやってくれるので逐一エンターキーを押す必要はない

基本は上2つのコマンドで事足りるハズ
#ついでに
ttlファイルは通常のtera termでは動かせない
<b>tera term macro interpreter</b>と関連付けること(多分tera termオトしたときは一緒に入ってるでしょ)
#サンプルコード
パスワードを入力する部分を例にやってみる

```macro.ttl
PASSWORD = "password"
wait 'Password' ;;パスワード要求されるまで待つ
sendln PASSWORD ;;コマンド(今回はPASSWORDという変数)を送信する
wait '$'
sendln 次のコマンド
```
Siemens simotion#script使い方 

Scriptを使うことによってドライブの設定、SimotionのTechnology objectsなどを自動設定することができます。そしてScriptのBaseはVBScriptになります。

#Scriptの使い方 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/3fc2096e-fc3b-ea1a-1f65-4b538949ad80.png)

##Script実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/fcf2c3e6-5421-d36b-941b-b333f2b0667a.png)


各Objectをどう引用するかF1で“Object Model”を検索してください。
BICOパラメータの読み込むと接続は“Reading and Interconnecting BICO Parameters”を検索してください。Scriptの割付対象は各Techonology Objectsの中にこうになります：
p1511[0]<-r2050[10]　
p1522[0]<-r2050[11]
p1523[0]<-r2050[12]

r80[0]->r2051[10]　
r2131[0]->r2051[11]
r2132[0]->r2051[12]
 
##パラメータの取り方
Parameters(Int paramIdx,Int index)
例えParametrs738を取りたいなら、Parameters(738,0)になります。
そしてReading and Interconnecting BICO Parameters”の中に書いてるルールによりますと、
<Parameters>=”<Parameters>:<Index/Bit>:<DriveId>”
たとえばParametrs738をParametrs722の1番目のBitに割付する。
Parameters(738,0)=“722:1.”
 
##書き込み
F1で“Writing Symbol values”の文章によりますと以下のルールがあります：
sys: System variable
cfg:Configuration data
Symbols(‘<Name of the TO Variable>“)=<Variable_value>
例えば、Modulo.lengthを100書き込みます。
Symbols(“CFG:Modulo.length”)=100
でも、System variableとConfiguration dataはどこにあるでしょうか？
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/cd634411-42f7-f625-7a54-a30b137eea7c.png)
 
例えば、ここでこのようなプログラムがあります：

```
PROJ.Devices("D445-2").TOs("Ax_1").Symbols("CFG:TypeOfAxis.MaxJerk.maximum") = 1000.00
``` 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/91e2f565-d89f-5975-9eee-cb27f8e0c4b3.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154661/112cc371-49a8-f951-c1ec-a322ef0f39e3.png)

```VBS 
Dim Device
Dim TO
For Each Device In PROJ.Devices
    For Each TO In Device.TOs   
        ' 
        TO.Parameters(1511,0) = "2050:10:."   'p1511[0] Supplementary torque1 
        TO.Parameters(1522,0) = "2050:11:."   'p1522[0] Torque limit upper
        TO.Parameters(1523,0) = "2050:12:."   'p1523[0] Torque limit lower
        ' 
        TO.Parameters(2051,10) = "80:0:."    'r80 Torque actual value 
        TO.Parameters(2051,11) = "2131:0:."  'r2131 Actual fault code
        TO.Parameters(2051,12) = "2132:0:."  'r2132 Actual fault code
    Next
Next

```
Node.js: 文字列をUint8Arrayに変換する方法

Node.jsで文字列をUnit8Arrayに変換する方法です。

```javascript
const str = 'Hello こんにちは'
const encoder = new TextEncoder()
const encoded = encoder.encode(str)
console.log(encoded)
```

実行結果:

```
Uint8Array(21) [
   72, 101, 108, 108, 111,  32,
  227, 129, 147, 227, 130, 147,
  227, 129, 171, 227, 129, 161,
  227, 129, 175
]
```

## 簡単な説明

* `TextEncoder`の`encode`メソッドに文字列を渡すと、`Unit8Array`が返ってくる。
* UTF-8のみサポートしている。
* `TextEncoder`のインターフェイスは[WHATWG Encoding Standard TextEncoder API](https://triple-underscore.github.io/Encoding-ja.html)に準拠している。


## 公式ドキュメント

* [Util | Node.js v13.11.0 Documentation](https://nodejs.org/api/util.html#util_class_util_textencoder)
GitHub Actionsでビルドするコンテナ内でGitHubのprivate repositoryをセキュアに参照する

# はじめに
GitHub Actions、便利ですよね
ソースコードの変更後、いちいち手元でdocker buildしてpushなんてやってられないですし、
外部CIツールを新たに導入するよりは全部GitHub内で完結してたほうがシンプルです。

しかしながら別のprivate repositoryにある社内用ライブラリを持ってくるのが地味に面倒だったのでやり方を書きます

# １行で
リポジトリsecretsにトークンを入れてBuild-time secretsでコンテナ内に注入

# 説明
## 認証方法
GitHub→GitHub内とは言え認証は必要です
httpsかsshで認証することになります
権限を細かく絞れるためトークンを介するhttps接続の方が良いと思います

### https
下記記事のようにトークンを作成し、トークンを用いて認証します
この際、個人ユーザのトークンを利用するとアカウント削除時にトークンが使えなくなりそうなので、マシンユーザを作成してそちらでトークンを作成します。マシンユーザがcloneしたいリポジトリのread権限を持っている必要があります

Dockerfileのbuildで簡単にGithubのプライベートリポジトリをクローンする方法 - Qiita
https://qiita.com/Jah524/items/fa68f99c8b787f94b884

### ssh
GitHubに登録してある公開鍵に対応する秘密鍵をコンテナ内に入れてしまえば接続はできます
ただユーザに許可されている全権限がついてしまうので、トークンを利用したほうがより安全ですね

## Build-time secrets
リポジトリにトークンを直書きするのはもちろん、`docker build`の`--build-arg`に渡す方法も安全であるとは言えません
Build-time secretsという機能があるのでこちらを使いましょう

Docker Engine 18.09 から使える Build-time secrets を試してみた | はったりエンジニアの備忘録
https://blog.manabusakai.com/2018/12/docker-build-secret/

## コンテナにsecretsを渡す
下記ドキュメントに`repo-token`で渡すとあるので、そうすればいいのかと思ってしまいがちなのですが(１敗)、
この形で渡したトークンはActionsの中のcloneで使えるだけなので、コンテナに渡す場合は普通に`env`を使います

GITHUB_TOKENでの認証 - GitHub ヘルプ
https://help.github.com/ja/actions/configuring-and-managing-workflows/authenticating-with-the-github_token

```
    steps:
    - uses: actions/labeler@v2
      with:
        repo-token: ${{ secrets.GITHUB_TOKEN }}
```



# コード
事前にリポジトリのsecretsに`MACHINE_USER_TOKEN`の名前でトークンを入れておきます
下記のようにファイルの形でコンテナにトークンを渡します

```github_actions_ecr.yml
      - name: Build, tag, and push image to Amazon ECR
        id: build-image
        env:
          TOKEN: ${{ secrets.MACHINE_USER_TOKEN }}
        run: |
          echo $TOKEN >> .token
          DOCKER_BUILDKIT=1 docker build --secret id=token,src=.token \
            -t $ECR_REGISTRY/$ECR_REPOSITORY:latest \
            .
          rm .token
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
```

Dockerfileの１行目にマジックコメントを入れます

```Dockerfile
# syntax = docker/dockerfile:1.0-experimental
```

Dockerfileでは下記のように受け取ります
pyproject.tomlにはssh形式でパスを入れておく想定で(どちらでもいいのですが手元で作業する分にはsshのほうが便利)
それを置換してトークンを入れています

```Dockerfile
COPY pyproject.toml .
RUN --mount=type=secret,id=token TOKEN=$(cat /run/secrets/token) \
  && sed -i "s/ssh:\/\/git@github.com/https:\/\/$TOKEN@github.com/g" pyproject.toml \
  && pip install . && rm pyproject.toml
```

```pyproject.toml
[tool.poetry.dependencies]
＜tool_name＞ = {git = "ssh://git@github.com/＜org＞/＜repo＞.git", tag = "1.0"}
```

GithubActionsのログにも秘匿化された状態で出力されています

```
#18 9.075 Collecting ＜tool_name＞@ git+https://***@github.com/＜略＞
```

# まとめ
Build-time secretsでクレデンシャルを安全に扱いましょう
https://docs.docker.com/develop/develop-images/build_enhancements/#new-docker-build-secret-information
#1 Command Line

# touch
ファイル作成

```
~$ touch sample.txt
```

# cat
ファイルの中身を表示する
※ 存在しないファイルを指定するとエラーが出力される。

```
~$ cat sample.txt
```

# mkdir
新しいフォルダ作成
※ make directoryの力

```
~$ mkdir sample
```

# cd
ディレクトリを移動する
※ change directoryの略

```
~$ cd sample
~$ cd ..
~$ cd
```

1. sampleディレクトリへ移動
2. 親ディレクトリへ移動
3. ホームディレクトリへ移動

# pwd
作業中のディレクトリを把握する
※ print working directoryの略

```
~$ pwd
```

# ls
現ディレクトリの中身を確認する
※ list の略

```
~$ ls
```

# mv
ファイル/ディレクトリを移動させる。ファイル名を変える
※ moveの略

```
~$ mv sample.txt sample
~$ mv sample1 sample2
~$ mv sample1.txt sample10.txt
```

1. sample.txtをsampleディレクトリへ移動させる
2. sample1ディレクトリを、sample2ディレクトリへ移動させる
3. sample1.txtをsample10.txtに名前を変更する

# cp
ファイルのコピーを行う
※ copyの略

```
~$ cp sample.txt sample10.txt
~$ cp -r sample sample10
```

1. sample.txtのコピーをsample10.txtというファイル名で作成する
2. sampleディレクトリのコピーをsample10というディレクトリ名で作成する

# rm
ファイル/ディレクトリを削除する
※ removeの略

```
~$ rm sample.txt
~$ rm -r sample
```

1. sample.txtを削除する
2. sampleディレクトリを削除する
3.
onnxのネットワークを出力するメモ


```python:look.py
import onnx
import onnx.helper

model = onnx.load('model/yolov3/yolov3.onnx')
print(onnx.helper.printable_graph(model.graph))
```


```
graph model_1 (
  %input_1[FLOAT, unk__576x3xunk__577xunk__578]
  %image_shape[FLOAT, unk__579x2]
) initializers (
  %var69[FLOAT, 32]
  %var64[FLOAT, 64]
  %var6[FLOAT, 256]
  %var5[FLOAT, 128]
  %var22[FLOAT, 1024]
  %var21[FLOAT, 512]
  %scale9[FLOAT, 256]
  %scale8[FLOAT, 128]
  %scale71[FLOAT, 32]
  %scale70[FLOAT, 64]
  %scale7[FLOAT, 128]
  %scale69[FLOAT, 32]
  %scale68[FLOAT, 64]
  %scale67[FLOAT, 128]
  %scale66[FLOAT, 64]
  %scale65[FLOAT, 128]
  %scale64[FLOAT, 64]
  %scale63[FLOAT, 128]
  %scale62[FLOAT, 256]
  %scale61[FLOAT, 128]
:
:
:
```
ラズパイのコード編集にVisual Studio CodeのRemote Developmentを使ってみる

ラズパイで簡単なコードを書くときに、Windows上からラズパイにTeraTermなどでログインしてviを使っていたのですが、どうもしっくりこない。
で、ssh経由で直接ファイルをいじれるエディタがないかなと検索したらVSCodeでできるみたいなので試してみました。

ラズパイにubuntuをインストールしているので、以下の画面はubuntuになっていますが、Rasbianでも同じ操作でいけるはずです。

今回は認証カギを使っていませんが、使えば接続の部分はもう少し簡単にいけるかも。

## VSCodeにRemote Developmentをインストール
Extensionsから『Remote Development』を検索してインストールします。
![remoto.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28184/5189fe54-e7df-4a30-1f93-ce87be6ed6b8.png)


インストールし終わると左下に緑色の『><』というアイコン(?)が出てくるのでクリックします。
![remoto3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28184/af18a4f6-5fbc-3d28-0f51-a8919733fe2e.png)

クリックすると、赤枠のような選択肢がでてきますので『Remote-SSH: Open COnfiguration File...』を選択します。

![remoto4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28184/4289a498-63a7-3e48-7228-6ffffa7a7cf7.png)

どこの設定ファイルを開くか聞かれるので、赤枠のように設定ファイルの選択に変わります。
今回は、自分の.ssh/configを修正します。

![remoto5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28184/2147825b-9ede-2d17-128f-ae642b2e9155.png)

ファイルを開くと以下のような形になりますので、Host,Hostname,Userを自分の環境に合わせて修正します。
接続先が複数ある場合は同じように、追記していきます。

![remoto6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28184/e5e3f47d-3ce0-5ef7-6008-636a152c4ff2.png)

設定ファイルを保存した後に左側のアイコンからリモートエキスプローラを選択し、上のTargetでSSH Targetsを選択します。

![remoto7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28184/696fee26-e284-f4d1-ea69-dd1062a73e4f.png)

そうすると、設定ファイルに記述したHostと同じものが表示されますので、赤枠で囲んだところをクリックします。

![remoto7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28184/44c8c13e-6e9c-462b-f584-ebfc176a5ea8.png)

新しいVSCodeのウィンドウが開いてパスワードを求められるので、パスワードを入力すると接続します。

![remoto8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28184/0b9e8d1c-edbc-e32c-c8f6-6ac202fb713b.png)

接続が完了すると左下の緑の部分に接続先のホスト名が表示されます。

![remoto9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28184/34ea01d7-b720-7517-88f5-192a1e3d539e.png)

その状態でファイルを開くとすると、リモート先のホームディレクトリが見えます。

![remoto10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28184/00487fbc-5fa8-02f2-b3ee-dfb4ee58f932.png)

ここで、ファイルを選択するとリモート先のファイルが開けますし、新規ファイルを作って、保存すると保存先がリモート先のフォルダとなります。
bor7

@echo off

setlocal ENABLEDELAYEDEXPANSION
 
rem 拡張子が「.txt」のファイルを対象に繰り返す

set box=C:\Users\81805\Desktop\作業フォルダ\

set /a num=0

for %%a in (%box%%1*) do (

set /a num=num+1
 

echo  !num!:個目%box%%%a
   
)

echo　合計：!num!個
SpringでAOPをテストしたいときはProxyFactoryが便利！

# 概要
- AOPの設定が動くようになっているかをテストしたい
- しかし、中で外部への副作用（例: トランザクション、ログ出力...etc）を及ぼす処理を行っている場合テストがしづらい
- そんなときはProxyFactoryが便利

# まずは普通のテストコードを書いてみる
前提として以下のServiceクラスとMethodInterceptorを用意。

```kotlin:AOPの対象Service
@Service
class SampleService {
    fun execute() {
        println("SampleService#execute")
    }
}
```
```kotlin:Interceptor
class SampleInterceptor(
        private val name: String
) : MethodInterceptor {
    override fun invoke(invocation: MethodInvocation?): Any? {
        println("intercept by $name")
        return invocation?.proceed()
    }
}

class SampleServicePointCut : StaticMethodMatcherPointcut() {
    override fun matches(method: Method, @Nullable targetClass: Class<*>?): Boolean {
        return targetClass?.let { SampleService::class.java.isAssignableFrom(it) } ?: false
    }
}
```
```kotlin:config
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
class AopConfig {

    @Bean
    fun interceptorA(): Advisor {
        return DefaultPointcutAdvisor(SampleServicePointCut(), SampleInterceptor("configured interceptor"))
    }
}
```

```kotlin:テストコード
@SpringBootApplication
class SpringTestApplication

@RunWith(SpringRunner::class)
@SpringBootTest(classes = [SpringTestApplication::class])
internal class SampleServiceTest {
    @Autowired
    private lateinit var service: SampleService

    @Test
    fun test() {
        service.execute()
    }
}
```
```shell:実行結果
intercept by configured interceptor
SampleService#execute
```


# ProxyFactoryを使って以下のようにテストコードを書いてみる

```kotlin:ProxyFactoryを使ったテストコード
@Test
fun testByProxy() {
    val factory = ProxyFactory(SampleService())
    factory.addAdvisor(DefaultPointcutAdvisor(SampleServicePointCut(), SampleInterceptor("Proxy")))
    val proxy = factory.proxy as SampleService
    proxy.execute()
}
```
```shell:実行結果
intercept by Proxy
SampleService#execute
```

## KotlinならUtil関数つくってもいいかも
Extensionを使ってutil関数をつくっておくのもよい。

```kotlin:kotlinのextensionを使った例ProxyFactoryを使ったテストコード
@Test
fun testByProxy() {
    val proxy = SampleService().proxy {
        addAdvisor(DefaultPointcutAdvisor(SampleServicePointCut(), SampleInterceptor("Proxy")))
    }
    proxy.execute()
}

@Suppress("UNCHECKED_CAST")
fun <T : Any> T.proxy(settings: ProxyFactory.() -> Unit): T {
    return ProxyFactory(this).also { settings(it) }.proxy as T
}
```

# @Aspectを使っている場合はAspectJProxyFactory
```kotlin:ServiceとConfig
@Aspect
@Component
class SampleAspect(
        private val name: String = ""
) {
    @Before("execution(* SampleAspectService.*(..))")
    fun advice() {
        println("advice by $name")
    }
}

@Service
class SampleAspectService {
    fun execute() {
        println("SampleAspectService#execute")
    }
}
```
```kotlin:テストコード
@Test
fun testAspectProxy() {
    val factory = AspectJProxyFactory(SampleAspectService())
    factory.addAspect(SampleAspect("proxy"))
    val proxy = factory.getProxy() as SampleAspectService
    proxy.execute()
}
```
```shell:実行結果
advice by proxy
SampleAspectService#execute
```
zshとbashでdotfilesを共通化する

# 概要
かつて[macでzshがデフォルトになった](https://applech2.com/archives/201910-macos-10-15-catalina-default-shell-zsh.html)時に、
**「もうbash環境は卒業だー」**とばかりにdotfilesをzsh仕様に修正しました。

ですが現実は厳しく、未だにbash環境をいじる機会はなくなりません。
（特に業務時）

そこでzshとbashの両方で使えるように、dotfiles内をなるべく共通化してみました。


# zshとbashの違い

## ファイル構成が違う

* bash → .bash_profile / .bashrc
* zsh → .zprofile / .zshrc

zshとbashで配置するファイルを変える必要があります。
また、当然中身も違ってきます。

## 使えるaliasが違う

```
alias -g L='| less'
```
Global Aliasはzsh限定の機能です。
bashで上の宣言をするとエラーになります。


# 対応

## 1. 判定用functionを作る
```
function is_bash_shell() {
  if [ $SHELL = "/bin/bash" ]; then
    true
  else
    false
  fi
}
```
bashを使っているかどうかの判定には色々なやり方があるようですが、
もし他の処理が必要になった場合は、このfunctionを拡張していきます。


## 2. スクリプト内で処理を分ける

## install.sh
```
. ./.functions

# bash
if is_bash_shell; then
  ln -siv .profile ~/.bash_profile
  ln -siv .bashrc ~/.bashrc

# zsh
else
  ln -siv .profile ~/.zprofile
  ln -siv .zshrc ~/.zshrc
fi
```

zshとbashで配置するファイルを変えます。
シェル固有の処理は、.bashrcと.zshrcにそれぞれ書いておけばOK。

## .aliases
```
. ./.functions

# global aliases (zsh only)
if ! is_bash_shell; then
  alias -g L='| less'
  alias -g H='| head'
  alias -g T='| tail -n 200'
  alias -g G='| grep'
  alias -g S='| sort | uniq'
fi
```
これでGlobal Aliasはzshの時だけ宣言されるようになります。


# まとめ
「なるべく処理を共通化させたい」と考えた結果、スクリプト内で最低限の部分だけ分岐するという形になりました。

その後ごちゃごちゃやって、現在はこうなっています。
[https://github.com/zizochan/dotfiles](https://github.com/zizochan/dotfiles)

もっと良い方法があったら教えてもらえると嬉しいです。