OmniAICreator/Qiita-1.07M huggingface dataset:
Redisはなぜシングルスレッドで高速に動作するのか


# 概要

先日[CodeCrafters](https://codecrafters.io/)というサービスで、RedisをGolangで開発しました。

https://github.com/kotobuki5991/go_redis_redevelopment

その中で、複数のクライアントからのアクセスを受け付ける必要があり、
解決策としてシングルスレッドのイベントループまたはマルチスレッドの並列処理が選択肢として挙げられました。

単純に考えると並列処理の方が多数のアクセスを捌くのに有利そうですが、Redis本家はシングルスレッドで動作しています。

実際に、1秒あたり100万リクエストに対応できるようです。

> 平均的なLinuxシステムで実行されているパイプラインRedisを使うと、1秒あたり100万リクエストを配信できる
> http://mogile.web.fc2.com/redis/redis608/topics/faq.html

ここで、なぜRedisがシングルスレッドを採用しており、高速に動作するのか気になったので調査してみました。

# Redisとは

https://redis.io/

インメモリのkey-valueデータストアです。
Redisサーバーがクライアントからのリクエストを受け付け、処理して、応答を返すという仕組みである<b>client/serverモデル</b>を採用しており、複数クライアントからのリクエストに対応できます。

# Redisがシングルスレッドを採用している理由

RedisのボトルネックとなりうるのはネットワークI/Oであり、CPUではありません。
そのためマルチスレッドにすることにより期待できるパフォーマンス向上よりも、

- スレッドの生成と破棄によるオーバーヘッド 
- スレッドのスケジューリングに関するオーバーヘッド

を抑えることのメリットが大きいためだと考えられます。

# Redisはなぜシングルスレッドで高速に動作するのか

以下の３つの理由が挙げられます。

### 1. インメモリである
RedisはRAM(Random Access Memory)を使用します。
RAMはデータへのランダムアクセスを得意としており、必要なデータを素早く見つけることが可能です。

一方で、ディスクへのI/Oではシーケンシャルアクセスが用いられることが多いです。
シーケンシャルアクセスでは、ディスク全体を順番に読み込んでいくので必要なデータに到達するまでに時間がかかってしまいます。
特にHDDでは物理的な移動（ディスクの回転など）が必要なので、さらに時間を要します。

> <img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2475547/e3642454-a6fc-0daf-5d15-68f3bd400a3d.png">

> [System Design: Why is single-threaded Redis so fast?](https://www.youtube.com/watch?v=5TRFpFBccQM)

### 2. データ構造が最適化されている
RedisはList、Set、Hashなどのデータ型をサポートしていますが、それらにアクセスする操作の多くはO(1)であり、効率的です。
これは、入力サイズに関係なく常に一定の処理時間であるということを示しています。
例えば、RedisのlistはLinked Listsによって実装されています。これによって、listに多くの要素がある場合でも
listの先頭・末尾に要素を追加する速度は同じとなっています。

[data types](https://redis.io/docs/data-types/)にそれぞれのデータ構造に関する説明が記載されています

### 3. ノンブロッキングI/O

Redisはシングルスレッドで動作するため、複数リクエストに対応するためにノンブロックキングI/Oを採用しています。
ノンブロックキングI/Oとは、I/O処理が完了する前に他のリクエストの処理を進めることができるものです。

Redisで使用されているイベントループと組み合わせることで、[C10K問題](https://ja.wikipedia.org/wiki/C10K%E5%95%8F%E9%A1%8C)を回避することが可能です。

[Redis クライアントの操作](http://mogile.web.fc2.com/redis/redis608/topics/clients.html)

# まとめ

Redisは

- インメモリであるためデータのI/Oが高速である
- データ構造が最適化されており多くの処理の計算量はO(1)である
- ノンブロッキングI/Oを採用しており、シングルスレッドで複数リクエストに対応している

という理由でシングルスレッドで高速に動作すると言えます。
【基本無料】SQL学習の教材まとめ

# はじめに
初めて「SQL」という単語を知ってから「自分で触れるようになる」までのロードマップとして、自分が使ってみて良かった教材やツールをまとめます。

# 構造化クエリ言語 (SQL) とは
データベースの定義や操作ができるデータベース言語。

# Step1: データベースが何か知る
ざっくりいうと「情報を置いておける場所」。
名簿や家計簿などもその一つと言える。

こういうと「表みたいな感じかな？」と思う人も多いと思うが、表っぽいものだけではなく色々な種類がある。
参考：[データベースとその種類 | Oracle](https://www.oracle.com/jp/database/what-is-database/)

SQLはRDB（リレーショナル・データベース）といった「列と行を持つテーブルの集合として表されるデータベース」で使われる。

# Step2: オンライン学習ツールでSQLを触る
データベースの勉強において、「データベースを構築してデータを用意する」のは結構ハードルが高い。
自分で環境構築する前にオンライン学習ツールで雰囲気をつかんだり、コードを打ってみたりするのがおすすめ。

おすすめサイトはProgate。スライドでSQLについての説明を見た後に予め用意されているデータベースを使って演習できる。自分のパソコン内で環境構築する必要が全くなく、色々な操作を試せる点が魅力。

参考：[SQL | Progate](https://prog-8.com/courses/sql)

# Step3: 自分でデータベースを構築する
基本的な操作がわかったら、データベースの定義やよく使われるツールについて知ると良い。
業務で使われているツールがあればそれを使うといいと思う。
初めはデータベースの中身が目で確認できた方がわかりやすいので、GUIのアプリがあるツールを使うのがおすすめ。

私は[SQL Server Management Studio](https://learn.microsoft.com/ja-jp/sql/ssms/download-sql-server-management-studio-ssms?view=sql-server-ver16)を使っていた。

# Step4: プログラミング言語と組み合わせる
最後に自分が使うプログラミング言語とデータベースを連携させてみる演習をする。
私はC#を使っているので[EFCore](https://learn.microsoft.com/ja-jp/ef/core/)を使っています。

EF CoreについてはHitoshi Kamadaさんの以下の投稿がとてもわかりやすかったです。
ありがとうございます。

https://qiita.com/hiesiea/items/4ea2aa545395338475e7

# Step5: 良いデータベースの設計や色々なコマンドを知る
データベースの設計には良し悪しがあり、それについてまとめて下さった本やサイトがたくさんある。
ミックさんのブログや書籍がとてもわかりやすいとのこと。
ご本人のホームページに書籍一覧やデータベースの設計・コマンドに関するブログが掲載されているので是非一読することをおすすめする。

参考：[ミックさんのホームページ](https://mickindex.sakura.ne.jp/)

# 最後に
まだまだ勉強中の身ですので、訂正や追加、これやると良いよ！というツールや書籍などありましたら是非ご連絡頂けると幸いです。
初めてSQLを触ったときプログラミング言語に比べて敷居が高く感じ戸惑ったので、どなたかのお役に立てれば嬉しいです。
WSLのGitからWindowsの資格情報マネージャ経由でAzureDevOpsの認証管理をする際の注意

Windows11に次のような構成を作成し、AzureDevOpsのReposに接続する際の資格情報を、Git Credential Manager（GCM）経由で管理しようとした際にぷちハマリした話です。
+ WSL
+ Ubuntu 22.04.2 LTS（記事投稿時点の最新盤）
+ Git for windows（ホストWindowsにインストール）
+ Git（Ubuntuにインストール）

# 結論
Ubuntu側のGitに次の設定をする必要がある。
~~~
git config --global credential.https://dev.azure.com.useHttpPath true
~~~
# 何にハマったか
公式の[こちら](https://learn.microsoft.com/ja-jp/windows/wsl/tutorials/wsl-git "Linux 用 Windows サブシステムで Git の使用を開始する")を参考にしながら冒頭の構成を作っていくと、GCMセットアップの項目でWindowsによる資格情報管理との連携についての記載に行き当たります。
ここには、WindowとUbuntuにGitをインストールした後、Ubuntu側のGit configにWindowsホストとのGCM連携のためのcredential.helperを設定する手順が書いてあります（詳細は直接確認いただければと思います）。

しかし、結論に書いた設定は「[Git for Windows を使用せずに WSL を構成](https://github.com/GitCredentialManager/git-credential-manager/blob/main/docs/wsl.md#configuring-wsl-without-git-for-windows "Git for Windows を使用せずに WSL を構成")」で誘導しているリンクの先に書いてあるのです。
しかも、リンク先にはしっかりWindows用Gitを使用するパターンとしないパターンが書いてあります。
そりゃないよ:joy:

# そんなこともあるよね
AzureDevOpsのRepsを使う際は設定が追加で必要になって、それがgit-credential-managerのドキュメント側では書かれていて、日本語翻訳されたサイトでは反映されていないだけかもしれません。
翻訳側は遅れるという話も聞きますしね。
もし同じようなことで困っている方の一時所になればと思います。
それにしてもちょっとなと思った出来事でした。

※記事が古くなるころには記載がなおっている可能性もあります
ログイン時に読み込まれるbash系ファイルは.bashrc

# 概要

- ログイン時に読み込まれるbash系ファイルが.bashrcであることを失念し若干詰まったのでまとめる。

# ログイン時に読み込まれるbash系ファイル

- .bashrcがログイン時に読み込まれる。
- 自身は.bash_profileが読み込まれるものと思っていた。
- なので、.bashrc内に下記の内容を記載し、「.bash_profileがあるなら読む」命令を書いてあげないとログイン時に.bash_profileは読まれない。

    ```.bashrc
    if [ -f ~/.bash_profile ]; then
        source ~/.bash_profile
    fi
    ```

- だいたい、.bashrc → .bash_profile → .bash_aliasesの順に読ませる気がするので.bash_profileに下記を記載する。

    ```.bash_profile
    if [ -f ~/.bash_aliases ]; then
        source ~/.bash_aliases
    fi
    ```
Pythonでnumpyを使用してランダムデータを生成し、matplotlibを使用して可視化してみた

# 概要
ディープラーニング学習の手始めとして、numpyを使用してランダムデータを生成し、matplotlibを使用して可視化してみました。以下のページを参考にしました。
https://colab.research.google.com/?hl=ja#scrollTo=C4HZx7Gndbrh

# 環境構築
colabでやるのもありですが、今回は自分のマシンに環境を構築して実行してみました。

まずはnumpyをインストールしました。
```
$ sudo pip install numpy
```
次はmatplotlibをインストールしました。
```
$ sudo pip install matplotlib
```
あと実行時にエラーが出て以下を入れてほしいと書いてあったのでインストールしました。
```
$ sudo apt install python-tk
```

# ソースコード
上記のページのソースコードをそのまま利用しました。

```sample_visualization.py
import numpy as np
from matplotlib import pyplot as plt

ys = 200 + np.random.randn(100)
x = [x for x in range(len(ys))]

plt.plot(x, ys, '-')
plt.fill_between(x, ys, 195, where=(ys > 195), facecolor='g', alpha=0.6)

plt.title("Sample Visualization")
plt.show()
```

# 実行結果

```
$ python sample_visualization.py
```

以下のウィンドウが開きました。
![Screenshot from 2023-04-19 16-55-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/267a27d0-66bf-9ff1-195b-b644327f0a3e.png)

ディープラーニングとはほど遠いですが、何かの役に立つかも。
Excel for Macでアクセスできるパス

# Excel for Macでアクセスできるパス

以前Excel for Macで利用するためのスクリプトフォルダに直接アクセスができないと書いたことがありますが、実際にはどこまでアクセスできるのか簡単に調べてみました。環境はMacOS bigsur,Excel2019 for Macです。

## 調査方法

以下のコードでパスを変えながら、コメントアウトでフォルダを開けるか、テキストファイルを保存できるかで判断していきました。

~~~
Sub checkMac()
Dim pth As String

pth = "/Users/"

'accessPathCheck (pth)
openDir (pth)

End Sub
~~~
### 保存用
~~~
Sub accessPathCheck(ByVal path As String)
Dim path1 As String, appscript As String

path1 = path & "fileaccessTest.txt"
appscript = "Write Success!!!!!"

Open path1 For Output As #2
    Print #2, appscript
Close #2

End Sub
~~~
### オープン用
~~~
Sub openDir(ByVal path As String)

ActiveWorkbook.FollowHyperlink Address:=path

End Sub
~~~

## 結果

結果をまとめたのが以下の表です。

|パス|オープン|書き込み|
|----|:----:|:----:|
|Users|OK|OK|
|Users/username|OK|OK|
|Users/username/library|OK|OK|
|Users/username/library/Application Scripts|OK|NG|
|Users/username/library/Application Scripts/com.microsoft.Excel|OK|NG|

/Users/name/library/まではファイル保存可能でした。それより先はエラーが表示され保存不可能でした。

![e75.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/965663/cb7f9df8-b99f-70d6-9ad2-d42a2a992235.png)

フォルダを開く方は/Users/name/library/Application Scripts/com.microsoft.Excel/まで可能でしたが、警告ダイアログが表示されてからユーザーの選択を待って開くという形になっていました。

![excelcaution.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/965663/e25dc071-9886-12a6-8b96-8c3b082caadf.png)


追記: Dir関数を使って AppleScriptTask用のスクリプトファイルの存在確認ができることを確認しました。

~~~
Dir(/Users/username/library/Application Scripts/com.microsoft.Excel/確認したいAppleScript.applescript)
~~~
<CSS>Position

# Position まとめ

## static
Positionプロパティの初期値となる値。
**top,right,left,bottomの指定ができない。**
また、Positionをリセットしたい時に使用する。
※実際に使うことがあったら追記します。

::: note
staticまとめ
* Positionプロパティの初期値
* 位置の指定ができない
* Positionリセットの際に使用する
:::

## relative
現在位置を基準に相対位置を指定する値。
**top,right,left,bottomの指定ができる。**
現在位置とは、本来要素が表示される位置
相対位置とは、実際に要素が表示される位置
![relative.image](https://zero-plus.io/media/wp-content/uploads/2022/07/relative.png)

また、基本的に単体で使用することはなくPosition: absoluteを使用したい場合に、使用したい要素の親要素に指定する。

::: note
relativeまとめ
* 要素の現在位置を基準に相対位置を指定する。
* 要素を移動した後でも元の高さを維持している
* absoluteとセットで使用する
:::

## absolute
要素を絶対配置する。
**top,right,left,bottomの指定ができる。**
親要素にrelativeを指定し、子要素にabsoluteを指定する。
absoluteを指定した要素は浮いた状態になり、他の子要素は左上に詰められる。
![absolute.image](https://zero-plus.io/media/wp-content/uploads/2022/07/relative-absolute.jpg)

:::note
absoluteまとめ
* 要素を絶対配置する
* absoluteを指定した要素は浮いた状態になる
* 親要素にrelativeを指定する
:::

## fixed
ウィンドウの左上を基準として絶対配置する。
主に固定ヘッダーを作成する時に使用する。
**top,right,left,bottomの指定ができる。**
relativeは上に要素があれば **その要素のbottom部分を基準として左上に配置される** が、fixedは **画面の左上** を基準として配置される
![fixed.image](https://zero-plus.io/media/wp-content/uploads/2022/07/fixed.jpg)
**元の高さは無くなってしまうため、後続の要素が埋まってしまう恐れがある。**
※後続要素にpadding等でヘッダー分の余白を取ると疑似的に回避できる。

:::note
fixedまとめ
* ウィンドウの左上を基準として絶対配置する
* 元の高さが無くなるため、後続の要素が隠れてしまう
* top,right,left,bottomの指定ができる。
:::

## sticky
親要素内の高さを基準として絶対配置する。
一般的に、スクロールの途中から要素を固定したい場合に使用されるプロパティ。
特徴は以下の通り
1. 初期位置は通常通りに配置される(親要素内に)
2. スクロールすると親要素の中で追従する
3. 重なり順を指定できる
4. **要素の高さを保持する**
![sticky.image](https://zero-plus.io/media/wp-content/uploads/2022/07/sticky-1.jpg)
※top,right,bottom,leftを指定して固定する。(最低限topは指定しないと動かないかも)

### fixedとstickyの比較
|値|基準とする固定位置|要素の高さ|位置の指定|
|:---|:---|:---|:---|
|sticky|親要素|無くならない|必須|
|fixed|ウィンドウ|無くなる|必要ない|

:::note
* 親要素の高さを基準として絶対配置
* スクロール途中から要素を固定する場合に使用
* 要素の高さは無くならない
* 最低限topの指定が必要
:::
【Renewal】Pythonを使って、頭にくるテキストデータのエンコードを一括で調べよう！

## テキストデータを扱ってると頭にくる！
:::note info
※2023/7/19 バグを見つけたので、改修しました。
ついでにフォルダ指定をGUIで出来るようにしました。
:::

テキストデータを解析しようとすると、めっちゃ頭にくることがありませんか？

そう。エンコーディングです。

なんでこんなに種類があるんだ！

windowsはshift-JISが多いけど、他にascii,unicode,utf-8,utf-16,utf-32さらに、BOM付とか無しとか、モー、わけわからん。

テキストを扱うプログラム作ると、予期せぬことの原因が、エンコードってよくありませんか？
文字化けすれば、なんとなくわかるけど、BOM付だと、最初に？が入ったり、utfだと、読み込みがエラーになったり。

プログラムのデバッグの最中に、関係ないところを変更して、悩んだことないですか？
私は結構あります。

ということで、このテキストのencodingは何だ？を表示するプログラムをpythonで作りました。
ちょっといじると、中身の表示もできます。


##  ということで、ソース公開します。

```py
import os.path
from glob import glob
import chardet
import tkinter
import tkinter.filedialog
import tkinter.messagebox

# フォルダ選択ダイアログの表示
root = tkinter.Tk()
root.withdraw()
tkinter.messagebox.showinfo('テキストファイルのエンコード表示', '確認対象ディレクトリを選択してください！')
scan_path = tkinter.filedialog.askdirectory()+"/*"
filepaths = glob(scan_path)  # globで一気に全ファイル名を取得できる。

# ファイルごとに処理
for file in filepaths:
    if os.path.isfile(file):
        # ファイルをバイナリモードで読み込む
        # print(f"・・・ファイル名: {file} 確認中")
        with open(file, "rb") as f:
            root, ext = os.path.splitext(file)
            data = f.read()
            # 文字コードやエンコーディングを推定する
            encoding = chardet.detect(data)["encoding"]
            # 文字コードやエンコーディングがNoneでない場合はテキストデータと判断する
            if encoding is not None:
                text = data.decode(encoding)
                print(f"ファイル名: {file}")
                print(f"文字コード: {encoding}")
#            else:
#                print(f"ファイル名: {file} は、テキストファイルではありません。")
```

## ちなみに、私のwindows10のhostsは、UTF-8-SIGって出るんです。
これって正常？

UTF-8-SIGって、よくわからないけどBOM付のUTF-8なのかな？
Node.jsのバージョン管理ツール【n】を使ってみた！

## 前提

もともと[本家サイト](https://nodejs.org/ja)からその時点の最新Verをダウンロード・インストールしていましたが、
プロダクトも増えてきて使用するNodeやnpmのバージョンに差が出てきたので【n】を入れてみました。

**インストール済みのNodeがあっても、アンインストール不要で大丈夫でした！**


## 1. Homebrewをインストールする

```
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```

　　＊もちろんインストール済みの方は不要です。


## 2. 【n】をインストールする

```
brew install n
```

↓
インストールができたか確認する
```
n -V
```

## 3. 権限周りを修正する

**・ここ大事！！**

1. パスを通す

```
sudo vim ~/.bashrc
```
　　＊このときパスワードが求められたらMacに設定したパスワードを入力する。

↓
下記の内容をコピペする
```
export N_PREFIX="$HOME/.n"
export PATH="$N_PREFIX/bin:$PATH"
```

↓
2. ディレクトリを作成＆現在のユーザーに権限を付与する

```
sudo mkdir -p /usr/local/n
sudo chown -R $(whoami) /usr/local/n

sudo mkdir -p /usr/local/bin /usr/local/lib /usr/local/include /usr/local/share
sudo chown -R $(whoami) /usr/local/bin /usr/local/lib /usr/local/include /usr/local/share
```

私は両方やりましたが、1 or 2 どちらかでもいいかもしれません。

## 4. 実際にNodeのバージョンを切り替えてみる
↓↓基本のキのみ↓↓

* 欲しいバージョンをインストールするとき
```
n {バージョン(XX.XX.XX)}
```

　　＊メジャーバージョンだけの入力でも大丈夫です。

* バージョンを切り替えるとき

```
n (->のあとにEnterキー)
```

↓
そしたらこんな画面に切り替わるので、↑↓キーで選択してEnterを押すだけで切り替わります。
![スクリーンショット 2023-04-19 16.41.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699952/0e796f32-0341-4017-e6cc-6f6767148934.png)
↓
![スクリーンショット 2023-04-19 16.42.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699952/6a466fdf-27f8-c67a-bea5-0839ade65e4d.png)


早い！便利！！


以上です！！
異なるスキーマの表を使って、Database Actions上で分析ビューの作成が行えるのか

# はじめに
Database Actionsにログインし、異なるスキーマにある表を参照して分析ビューの作成、データインサイトの検索を行ってみました。


# 本記事の前提条件 

* ADBインスタンスが構成済みであること
* ADBインスタンスに複数ユーザーを作成し、その一方に何かしらのデータをロードしてあること

# 作業の大まかな流れ
* ADMINユーザーでDatabase Actionsにログインし、違うスキーマ(本記事ではQTEAM)に格納してあるデータの分析ビューを作る。
* ADMINユーザーで、別のユーザー(本記事ではQTEAM_TARGET)に参照権限を付与する
* ADMINとは異なる別のユーザー(本記事ではQTEAM_TARGET)でDatabase Actionsにログインする。
* ログインしているのとは違うスキーマ(本記事ではQTEAM)に格納してあるデータの分析ビューを作成してみる

# 実際に異なるスキーマにある表で分析ビューを作ってみた

### ADMINユーザーでDatabase Actionsにログインし、違うスキーマに格納してあるデータの分析ビューを作る。
1. Database Actionsにログインし、起動パッドから、分析ビューを選択します。
1. 分析ビュー作成の画面において、以前まではログインしているユーザーのみしか選択できませんでしたが、アップデートにより、ADMIN以外のユーザーも選択可能になりました。
    ![異なるスキーマの表を使って、Database Actions上で分析ビューの作成、データインサイトの検索を行えるのか_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/a7b8bd92-bfb9-ebc7-7011-8ae807393634.png)

1. 対象となるスキーマ(今回はQTEAM)を選択し、格納してあるデータを選択します。
1. 階層とメジャーの生成を行ったあと、分析ビューの作成ができるはずです。
1. 作成した分析ビューはADMINに格納されます。

### ADMINユーザーから別のユーザー(本記事ではQTEAM_TARGET)に対し、参照権限を付与する
1. 次のステップで、ADMINではない別のユーザーでログインしている場合においても、別のスキーマの表の分析ビューを作成してみたいと思います。
1. 準備のためにユーザーQTEAM_TARGETに対し、以下を実行して別スキーマに対する参照権限を付与します。(Tera Term等で実行)
    ```
    GRANT SELECT ANY TABLE TO QTEAM_TARGET;
    ```
### ADMINとは異なる別のユーザーでDatabase Actionsにログインし、さらに別スキーマにあるデータの分析ビューを作成してみる
1. 一旦ADMINからログアウトし、QTEAM_TARGETユーザーでDatabase Actionsにログインします。先ほどと同様に、起動パッドから分析ビューを開きます。
1. 分析ビュー作成画面から、別スキーマを選択できることが分かります。(参照権限を付与していないと、別スキーマをタブから選択できまん)
    ![異なるスキーマの表を使って、Database Actions上で分析ビューの作成、データインサイトの検索を行えるのか_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/266f62ac-9d81-9b1a-10e4-71e3ff3eb678.png)

1. 階層とメジャーの生成をしたら、分析ビューが作成できるはずです。
1. 作成した分析ビューはログインしているスキーマ内に格納されます。

# まとめ
本記事では、Database Actionsから異なるスキーマの表を参照し、分析ビューの作成ができるのか検証しました。

<br>
【Ruby】ComparableをMix-inせずデリゲーションしよう

Rubyでクラスを作っている時に、それを比較演算子で比較したい、あるいはソートしたいなどのニーズが出てくることがあります。そんな時に使えるのが`Comparable`です(※ソートは宇宙船演算子さえあればできます) 

```ruby  
class MyClass  
  include Comparable  

  def <=>(other)  
    # put your code here...
  end
end  
```

こうすることで、`Comparable`モジュールで提供する機能を利用できるようになりました！詳しい説明は公式docに譲ります。  

## 問題点  

さて、こうすることで比較が可能になりましたが、例えば以下のような場合を考えてみましょう。  
```ruby 
class Human
  include Comparable
  attr_reader :income, :height


  def initialize(income, height) 
    @income = income # 年収 
    @height = height # 身長 
  end

  def <=>(other)
    @income <=> other.income 
  end 
end 
```

**年収で人間を比べるんじゃない！** というのは置いといて、人間がある一つの軸で比較可能であるという考え方は、素朴すぎるとは思いませんか？ 
実際のシステム開発においても、比較のロジックは欲しいけど、対象のクラスを比較する指標はいくつか考えられるし、とは言え毎度`sort_by`でブロックの中身を書くのもだるい...ということはよくあります。

## 解決策

**クラス自身をComparableにするのではなく、Comparableなフィールドに移譲しましょう**  
婚活女子の山本さんは、男は年収が6割、身長が4割であると主張しています。彼女の考えを数値化した山本指数が婚活界隈で有名になり、流行に敏感なあなたは自分が開発するシステムにこれを取り込もうと考えました。簡単に実装すると以下の通りです。 

```ruby
class Human 
  # 中略

  def yamamoto_index
    @income * 0.6 + @height * 0.4
  end
end

# Humanの配列を山本指数でソートする
humans.sort_by(&:yamamoto_index)

```

書いてみれば特に難しいことはしていませんが、他の指標を追加する時は同じようにメソッドを実装するだけで済みますし、比較する際の指標も明示的でいい感じですね。

## 比較ロジックの拡張性を高める 

筆者は、場合によっては上記のコードではまだ不完全であると考えています。
この実装では、比較ロジックを変更したい場合に手を加えられるのはHumanクラスです。しかし、この場合変わったのはHumanの振る舞いではなく評価指標です。であれば、変更を「評価指標クラス」に閉じたいのがプログラマの情ってもんですよね？ 
また、指標の実装が複雑な計算を伴うようになったとして、Humanクラスが肥大化していくのは避けたいです。


```ruby
class YamamotoInex 
  include Comparable

  def initialize(income, height) 
    @income = income # 年収 
    @height = height # 身長 
  end

  def <=>(other)
    comparator <=> other.comparator 
  end

  def comparator 
    @income * 0.6 + @height * 0.4
  end
end 
```
このクラスをHumanクラスのyamamoto_indexメソッドで初期化して返すようにしてあげましょう。
これで山本指標が見直された場合でも、Humanクラスに変更を施す必要がなくなりました！
【自分用メモ】Xcodeの画面を表示したままFinderも同時に表示する方法

《過去に悩んだ点》

　　『Xcodeの画面を表示したままFinderも同時に表示しようと
　　　FinderをタップするとXcodeの画面を表示した状態のまま「ではない」状態で
　　　Finderに移動してしまい、Xcodeの画面を表示したままFinderも
　　　同時に表示できずにいてXcodeのAssets.xcassetsにダウンロードした画像を
　　　ドロップアンドドロップできずにいた』


《解決した方法》

　　『Xcodeの最大化を解除』
C# 外部のアプリケーションのウインドウを無理やり前面に持ってくる

```.cs

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr FindWindow(
           string lpClassName, string lpWindowName);

        [DllImport("user32.dll")]
        private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
        [DllImport("user32.dll")]
        private static extern bool IsIconic(IntPtr hWnd);
        // ShowWindowAsync関数のパラメータに渡す定義値
        private const int SW_RESTORE = 9;  // 画面を元の大きさに戻す

 // 外部プロセスのメイン・ウィンドウを起動するためのWin32 API
        [DllImport("user32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);

```

```.cs

 //ウインドウ名は、ウインドウの左上に表示される文字列
 //WindowsFormApplicationでのForm.Textのこと

 //ウインドウ名からウインドウのハンドルを取得する。
 IntPtr hWnd = FindWindow(null, "ウインドウ名");
 if (hWnd != IntPtr.Zero)
 {
       //最小化されていれば元に戻す
       if (IsIconic(hWnd))
       {
           ShowWindowAsync(hWnd, SW_RESTORE);
       }
       //最前面に表示する
       SetForegroundWindow(hWnd);
 }

```
中学一年生がChatGPTにPython書かせてみた

**はじめに**

こんにちは。
中学一年生のikkun084です。
プログラミングが趣味です。
今回は、最近話題になっているChatGPTを使ってみました。
ChatGPTをつかうのは初めてだったので、下記のページを参考にアカウント作成から始めました。
https://aismiley.co.jp/ai_news/chatgpt-tsukattemita/

これが実際に使った様子です。
![スクリーンショット 2023-04-19 15.20.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335275/8917d9c4-c4ae-f727-8159-89da1867c71c.png)
計算が苦手だと聞いたので、実際にやらせてみました。
![スクリーンショット 2023-04-19 15.20.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335275/6fae2f1c-2f53-a034-3372-1ba16e62b006.png)
33×37は1221です。
![スクリーンショット 2023-04-19 15.51.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335275/91ee0bc0-0175-fd5b-934d-15a3452144a2.png)
148×123は18204です。
僕のほうが正確ですね。

**プログラムを作らせる**
本題に入りましょう。
いよいよChatGPTにコードを書かせます。
このように質問しました。
![スクリーンショット 2023-04-19 16.30.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335275/8563a880-75eb-5d8d-034e-0816abf13176.png)
実際に書かせたコードがこちらです。
```Python
import random

def get_user_choice():
    while True:
        user_choice = input("じゃんけんの手を入力してください（グー、チョキ、パー）：")
        if user_choice in ["グー", "チョキ", "パー"]:
            return user_choice
        else:
            print("入力が無効です。もう一度入力してください。")

def get_computer_choice():
    choices = ["グー", "チョキ", "パー"]
    computer_choice = random.choice(choices)
    return computer_choice

def determine_winner(user_choice, computer_choice):
    if user_choice == computer_choice:
        return "引き分け"
    elif user_choice == "グー" and computer_choice == "チョキ":
        return "勝ち"
    elif user_choice == "チョキ" and computer_choice == "パー":
        return "勝ち"
    elif user_choice == "パー" and computer_choice == "グー":
        return "勝ち"
    else:
        return "負け"

def play_game():
    print("じゃんけんを始めます！")
    user_choice = get_user_choice()
    computer_choice = get_computer_choice()
    print(f"あなたの選択：{user_choice}")
    print(f"コンピューターの選択：{computer_choice}")
    result = determine_winner(user_choice, computer_choice)
    print(f"結果：{result}")

play_game()
```
実行したらどうなるかも教えてくれました。
![スクリーンショット 2023-04-19 16.11.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335275/589241dc-497c-863f-d714-c606736c8b23.png)
紹介もしてくれました
![スクリーンショット 2023-04-19 16.12.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335275/f6ed4f46-1f3a-dee9-ca94-d37fbaa31fdc.png)

実行してみた結果がこれです。
実行は下記のサイトから行いました。
https://paiza.io/ja
![スクリーンショット 2023-04-19 16.14.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335275/66ae060a-5c7c-6200-3874-307ef40080e8.png)

**感想**
ここで紹介したもの以外にも、しっかりと受け答えしてくれて面白かったです。
次はもう少し複雑なコードを書いてもらおうと思います。
GA4でイベントカテゴリとイベントラベルを確認する方法


## 前置き

この記事では、「GA4上でイベントカテゴリとイベントラベルを、ディメンションの候補に追加する方法」を紹介しています。

「UA（ユニバーサルアナリティクス）のイベントをGA4のイベントとして測定する方法」については[こちら](https://developers.google.com/analytics/devguides/collection/ga4/translate-events?hl=ja#example_universal_analytics_event_with_no_google_analytics_4_equivalent)の公式ドキュメントをご確認ください。

## イベントトラッキングを設置しているのに、イベントカテゴリとイベントラベルがディメンションの候補にない

UAでは、`<a>`タグ内に下記のようなonclick属性を追記することで、Google Analytics側では何も設定せずともイベントカテゴリやイベントラベルを計測できていました。

```
onclick="gtag('event', 'Click', {'event_category': '○○','event_label': '××'});"
```

しかしGA4では、 `レポート` > `ライフサイクル` > `エンゲージメント` > `イベント` で、 `イベント名` 横の `+` からディメンションの候補を探しても、デフォルトではイベントカテゴリもイベントラベルも出てきません。


![capture_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241380/ec2252c7-f26f-3146-fd72-1db4102c59d5.png)

カスタムディメンションを作成することで、ディメンションの1番上に `カスタム` という項目が増えます。


## イベントカテゴリとイベントラベルをディメンションの候補に追加する方法

`管理` > `カスタム定義` でカスタムディメンションを作成します。

![capture_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241380/4d060c08-ca1e-3c1a-b867-d0dfd8a750e3.png)

`カスタムディメンションを作成` をクリックすると、このキャプチャと同じような画面が表示されます。

![capture_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241380/a1b7efec-be23-244e-06fd-5de31436fd4b.png)

下記の項目を埋めて、右上の `保存` を押すとカスタムディメンションが作成されます。

* ディメンション名 : 任意の名前を設定可
* 範囲 : プルダウンから `イベント` を選択
* 説明 : 任意で説明を記載可
* イベントパラメータ : プルダウンから `event_label` や `event_category` を選択

※カスタムディメンションとカスタム指標について、詳しくは[こちら](https://support.google.com/analytics/answer/10075209?hl=ja&utm_id=ad#best-practices)の公式ドキュメントをご覧ください。

## 計測できているかの確認

`レポート` > `ライフサイクル` > `エンゲージメント` > `イベント` でレポートを表示し、イベント名から `Click` を選択して詳細レポートを開く。

![capture_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241380/e5f2c03f-8772-f26d-285d-a7e856f7810e.png)


右上の `過去30分間のイベント` でプルダウンから該当のパラメータを選択すると、追加したディメンションの数値を計測できているかが確認できます。
![capture_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241380/347bdef6-69f6-af8c-61c3-e93bc6ec02be.png)
開発宛て問い合わせで問い合わせる側が気を付けるとよいこと

# 主にこんな方向け
開発に問い合わせをすることがあるコンサルやサポートセンターの方向けの記事です。

# 問い合わせの際にどこがポイントになるのか
よくある開発への問い合わせの際に意図しない形で返答がされることはないでしょうか。
例えば、
　・質問していることに質問で返されたり
　・急いでいるのにデータを取得依頼されたり
　・全く期限を守ってくれなかったり
　・思っていたのとは違っていた返答が来たり
問い合わせる側が抑えるべきポイントをまとめます。
ここで記載するのはあくまで理想論です。
必ずこの通りになるとは限りませんが、もしかすると意識すると今よりもよくなるかもしれません。

## 初回の時点で提供可能な情報を伝える

たまにある ___僕が考える最低な問い合わせ___ の一例
```
お客様環境でエラーが発生しました。
調査お願いします。
```
:::note alert
これは開発側からすると何が起こっているのかまったくもってわかりません。
そもそも調査するための情報が何もないのです。
:::

### 一つ情報を足してみましょう

```
お客様環境で○○画面でエラーが発生しました。
調査お願いします。
```
これでどの画面で起きたかわかりました。
ただし、これでは全く十分ではありません。

### 問い合わせ文を書いてみましょう
```
××様の検証環境で
本日(2023/02/28)の10:05頃、△△ユーザーでログインした状態で
○○画面で「CSV出力」ボタンを押したところエラーが発生しました。
エラーが発生した際の画面キャプチャとログを取得しましたので、調査お願いします。
```
これぐらい書いてあると何がわかるでしょうか。

### 初回の時点で提供可能な情報を伝えることでできるようになること

:::note info
1. ××様の検証環境で発生したこと
1. 2023/02/28 10:05頃に発生したこと
1. △△というユーザーログインし操作をしていること
1. ○○画面で「CSV出力」を押したタイミングで発生したこと
1. キャプチャが取得できていること
1. ログが取得できていること

5W1Hにある程度沿った情報が提供されていることで開発側としては調査を始めることができそうですね。
もちろんこれがすべてではないと思いますし、必要な情報があれば開発側からさらにデータの取得等の依頼をされることはあると思います。
ただし、この初動での情報がなければ上記のような情報の取得を依頼する必要がありますし、レスポンスタイムもかかり結果回答に時間がかかることになります。
:::


## いつまでに回答がもらえればいいかを伝える

:::note alert
上段で開発側にはある程度の情報が得られたので調査を始めます。
ただし、いつと指定されていないので開発は「検証環境で起きている問題だし、code freezeの後に見るか...」と思うかもしれません。
実は問い合わせている側には ___このエラーで検証のスケジュールがすでに押している___ という状況があったとします。
:::

### いつ期限なのか、それがどういった理由であるかを伝える
```
[事象]
××様の検証環境で
本日(2023/02/28)の10:05頃、△△ユーザーでログインした状態で
○○画面で「CSV出力」ボタンを押したところエラーが発生しました。
エラーが発生した際の画面キャプチャとログを取得しましたので、調査お願いします。

[期限]
現在、お客様の環境で検証を進めています。
このエラーが原因で次の検証に進めず、現状すでに一週間の遅れが出ています。
来週の朝の10時までに回答をいただけますでしょうか。
```

### いつまでに回答がもらえればいいかを伝えることでできるようになること
:::note info
これで開発はいつまでに調査をしなければいけないか把握することができます。
「来週の朝10時までに回答しないといけない」という状況になるので、タスクの優先順位を変更して優先で対応してくれる（かも）と思います。
:::

## 何ができればゴールなのかを具体的に伝える
:::note alert
開発が調査した結果、エラーは不具合でした。なので開発は不具合でしたと答えます。
問い合わせた側としては当然こう思います「いや、そうじゃない」。
:::

### 何をゴールとしているのかを伝える
```
[事象]
××様の検証環境で
本日(2023/02/28)の10:05頃、△△ユーザーでログインした状態で
○○画面で「CSV出力」ボタンを押したところエラーが発生しました。
エラーが発生した際の画面キャプチャとログを取得しましたので、調査お願いします。

[期限]
現在、お客様の環境で検証を進めています。
このエラーが原因で次の検証に進めず、現状すでに一週間の遅れが出ています。
来週の朝の10時までに回答をいただけますでしょうか。

[ゴール]
○○画面から、CSVを出力した結果と移行元のシステムから出力したCSVのデータに差異がないか比較をするために○○画面からCSVの取得したい。
○○画面で出すことができない場合、可能であれば何か代替の方法等ご提案いただけますと幸いです。
```

### 何ができればゴールなのかを具体的に伝えることでできること

:::note info
調査の結果CSVの出力でエラーになることが不具合とわかりました。
よって、○○画面からのCSVの出力はできません。
ただしCSVの出力ロジックをたどり、ロジックでSQLを作成し、それをお客様の環境のDBに流します。
その結果は○○画面で取ったCSVと同じになると思います。
ということで「○○画面の「CSV」出力でのエラー」自体は解消できませんでしたが、本来果たすべきの目的である「移行元のシステムとのデータの比較」は実現できます。
:::

# まとめ
これはあくまでも理想論です。
でも上記の例であれば最初の問い合わせではもしかするとデータの取得で月曜日の朝10時を迎えていたかもしれない状態からかなりの改善が見られたのではないでしょうか。
上記の例では代替案があるケースを記載していますが、当然ないケースもあります。
しかし早い段階で次のアクションへの相談などに移れるので悪いことはないと思います。

少しの手間で、意図した回答をもらえるようにできるように工夫していきましょう。
np.zerosとnp.zeros_likeはdtypeに気を付けよう

## Abstract

`np.zeros`と`np.zeros_like`はdtypeに関する振る舞いが異なるので気を付けようという話

## 環境

- Windows 10
- Python 3.10.9
- numpy 1.24.2

```python
import numpy as np
```

## 落とし穴

`np.zeros`と`np.zeros_like`は次のように相互に書き換えられるとよく紹介されます。

```python
>>> x = np.arange(0, 5, 0.2)
>>> x
array([0. , 0.2, 0.4, 0.6, 0.8, 1. , 1.2, 1.4, 1.6, 1.8, 2. , 2.2, 2.4,
       2.6, 2.8, 3. , 3.2, 3.4, 3.6, 3.8, 4. , 4.2, 4.4, 4.6, 4.8])
>>> a = np.zeros(x.shape)
>>> a
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0.])
# こう書いても同じだよ！
>>> b = np.zeros_like(x)
>>> b
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0.])
```

次の例では、同じ結果になっていません。
```python
>>> x = np.arange(5)
>>> a = np.zeros(x.shape)
>>> a
array([0., 0., 0., 0., 0.])
>>> b = np.zeros_like(x)
>>> b
array([0, 0, 0, 0, 0])
# dtypeが違う！！
>>> x.dtype, a.dtype, b.dtype
(dtype('int32'), dtype('float64'), dtype('int32'))

# dtypeを指定してあげないと同じ結果にならない
>>> b = np.zeros_like(x, dtype = np.float64)
>>> b
array([0., 0., 0., 0., 0.])
```

つまり同じ結果になるのは`x.dtype == np.float64`**の時だけです**。

`np.zeros`は`dtype`を引数で指定しない場合は`np.float64`になります。
一方で`np.zeros_like`は受け取ったnumpy配列の`dtype`を引き継ぐようになっています。

そのため、単に`shape`が同じ配列を作りたいという時は、意識しておいて`dtype`を指定しましょう。
[Unity]Reset()を使いこなしてアタッチ漏れをなくす

# Reset()を使いこなそう
Unityを使用して開発を行うプログラマ向けの内容です。

Unityを使っていて一番無駄な行為、それは **「アタッチ漏れによる起動のやり直し」** です。

GameObjectに追加したスクリプトのSerializeFieldへのアタッチ漏れが発生する機会は多々あります。

全体の構造として複雑になればなるほど、
構造を理解して適切なComponentをアタッチする必要があり
開発者が人間である以上、アタッチ漏れは発生します。

そのようなアタッチ漏れのリスクを可能なかぎり下げてくれるメソッド、
それが **MonoBehaviour.Reset()** です。

# Reset()とは
Resetメソッドを一言でいうと
「GameObjectにスクリプトを追加したときに自動で行われる処理」です。

つまりこのメソッドを駆使すれば、
追加したスクリプトがInspector上に表示されたときから
SerializeFieldが既にアタッチされているようになり、
アタッチ漏れの起きようがない状況を作り出すことができます‼

# 使い方
Resetメソッドの使い方は簡単です。
MonoBehaviourを継承したクラス内でResetメソッドを定義し、行いたい処理を書き込むだけです。
今回で言えば、アタッチしたいSerializeFieldをメソッド内で指定してあげます。

例えば、ビデオ再生を行う **VideoPlayer** とそれを受け取る **RawImage** を
RequireComponentを用いて同オブジェクト内で管理するクラスを以下のように記載するとします。
```C#:Sample.cs
[RequireComponent(typeof(RawImage), typeof(VideoPlayer))]
    public class SampleBehaviourScript : MonoBehaviour
    {
        [SerializeField]
        private VideoPlayer videoPlayer = null;

        [SerializeField]
        private RawImage rawImage = null;

        private void Reset()
        {
            videoPlayer = GetComponent<VideoPlayer>();

            rawImage = GetComponent<RawImage>();
        }
    }
```
上記のようにResetメソッドで必要なSerializeFieldを指定したスクリプトを
Add Componentするだけで
対象のSerializeFieldが既にアタッチされた状態になります！！

![Animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322340/0368174b-a278-bb36-8d15-3b11cac8b086.gif)

ちなみに...対象のComponentのResetメソッドを意図的に呼びたい場合は
右上のコンテキストメニューから[Reset]を選択すれば再度自動で反映されます。

# 最後に
今回はResetメソッドを使って「アタッチ漏れ」を減らす方法について紹介しました。

活用方法を工夫すれば更に効率的に開発できそうですね！！

# 参考

https://docs.unity3d.com/ja/2021.1/ScriptReference/MonoBehaviour.Reset.html


https://docs.unity3d.com/ja/2023.2/Manual/ExecutionOrder.html
Googleデータアナリティクス：ヘッドライン、サブタイトル、ラベルをつける

# はじめに

本記事は、Googleデータアナリティクスのプロフェッショナル認定証のプログラムより、参照させて頂いています。興味を持った方は、是非受講してみてください。

https://www.coursera.org/professional-certificates/data-analytics-japanese

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/05522c3b-515a-bc49-aaa3-a0489a6b12b1.png" width="800">

# ヘッドライン、サブタイトル、ラベルをつける

[以前取り上げた「5 秒ルール」を 覚えていらっしゃるでしょうか。 ][5sec]

[5sec]: https://qiita.com/mapami/items/405908bb38f3663890d1

データビジュアライゼーションを見てから 最初の 5 秒以内に 聞き手があなたが伝えようとしていることを 正確に把握する必要があります。 5 秒はあっという間かもしれません。 しかし、分かりやすい表現を加えることで 聞き手はデータを正しく解釈し 理解できるようになります。 皆さんが何を伝えたいのか、 何を共有しているのかについて 聞き手が疑問を抱かないようにするのに 役立つのが 

- ヘッドライン
- サブタイトル
- ラベル

です。 

### <font color="MediumBlue">ヘッドライン</font>

データビジュアライゼーションにおいて 重要なデータを強調するための 最も簡単な方法の 1 つは、 ヘッドラインをつけることです。 ヘッドラインとは、 ビジュアライゼーションの上部に 大きな文字で表示され どのようなデータなのかを伝えるものです。 聞き手がもっと読みたくなるように 注意を引くようにします。 

たとえばグラフです。 

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/096a6d1f-3dcb-782c-3ac0-b5d88de4e6a3.png" width="700">

ヘッドラインのないグラフは タイトルのないレポートと同じです。 何のグラフなのかを わかりやすくしたいものです。 簡潔かつ明瞭な表現を使って すべての情報をできるだけ、わかりやすく 説明しましょう。 略語や頭字語は、たとえそれが一般的に 知られていたとしても、 なるべく使わないようにしましょう。 

ヘッドラインの文字組や 配置も重要です。 シンプルにするのが一番です。 
- 太字にする。
- 他のテキストより数サイズ大きくする。
- グラフの真上に 左寄せで配置する。

### <font color="MediumBlue">サブタイトル</font>

サブタイトルは、より多くのコンテキストと 説明を追加することで ヘッドラインを補足するものです。 他のグラフにある要素と 同じスタイルのフォントを使って サブタイトルは見出しの直下に配置します。 

### <font color="MediumBlue">ラベル</font>

以前ご紹介した、ドナ・ウォン氏という ビジュアライゼーション・ジャーナリストの ガイドラインは、データ ビジュアライゼーションを より効果的なものにするのに有益です。 彼女は、凡例に頼るのではなく データ上に直接ラベルを使うことを 強く推奨しています。 なぜなら多くのグラフは、 データの異なる値を表現するために、 色や図形など異なるビジュアライゼーションの 特性を用いているからです。 

### <font color="MediumBlue">凡例やキー</font>

凡例やキーは、 データビジュアライゼーションの中の 要素の意味を明らかにするものであり データに直接ラベルを付ける代わりに 使うことができます。 しかしこのように直接ラベルをつければ 聞き手の注意をグラフィックに 集中させることができ、 データをすばやく識別できます。 

一方、凡例はグラフのデータから離れた場所に 配置されるため、聞き手側に 考えさせることも増えます。 つまり、聞き手に補足説明をすればするほど 聞き手がデータの意味を理解するのに 必要な作業が減り ストーリーのインパクトが増すのです。 

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/0bcb4be3-e211-844a-3a30-d2a3c08454aa.png" width="500">

# まとめ


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/12ac8fa1-24d3-ff33-cf31-64d4c1cc8c95.png" width="７00">

サンプル↓

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/5e3b829f-fe5a-d5d2-c01d-8500d8339255.png" width="７00">
VBAでエクスプローラーからファイルを選択したファイルをInputとし、最初の列をMD5でハッシュ化した上で今の日時をファイル名にして出力する

```vba
Sub HashFirstColumn()
    ' エクスプローラーからファイルを選択
    Dim fileName As Variant
    fileName = Application.GetOpenFilename("CSVファイル(*.csv),*.csv", , "ファイルを選択してください")
    If VarType(fileName) = vbBoolean And Not fileName Then
        ' ファイルが選択されなかった場合は何もしない
        Exit Sub
    End If
    
    ' ファイルを開く
    Dim csvFile As Object
    Set csvFile = CreateObject("Scripting.FileSystemObject").OpenTextFile(fileName, 1, False, -1)
    
    ' ファイル名を作成
    Dim fileDateTime As String
    fileDateTime = Format(Now(), "YYYYMMDD_HHMMSS")
    Dim newFileName As String
    newFileName = Replace(csvFile.Name, ".csv", "_" & fileDateTime & ".csv")
    
    ' ファイルを出力
    Dim newCsvFile As Object
    Set newCsvFile = CreateObject("Scripting.FileSystemObject").CreateTextFile(newFileName, True, False)
    
    ' ファイルの最初の列をMD5でハッシュ化して出力
    Do While Not csvFile.AtEndOfStream
        Dim line As String
        line = csvFile.ReadLine
        Dim columns() As String
        columns = Split(line, ",")
        Dim hashValue As String
        hashValue = HashString(columns(0))
        newCsvFile.WriteLine hashValue & "," & Join(columns, ",", 1)
    Loop
    
    ' ファイルを閉じる
    csvFile.Close
    newCsvFile.Close
End Sub

Function HashString(str As String) As String
    ' 文字列をMD5でハッシュ化する関数
    Dim md5 As Object
    Set md5 = CreateObject("System.Security.Cryptography.MD5CryptoServiceProvider")
    Dim bytes() As Byte
    bytes = StrConv(str, vbFromUnicode)
    bytes = md5.ComputeHash_2((bytes))
    Dim hash As String
    Dim i As Integer
    For i = 0 To UBound(bytes)
        hash = hash & LCase(Right("0" & Hex(bytes(i)), 2))
    Next i
    HashString = hash
End Function
```
【Tips】効率的かつ合理的なoutlet接続の方法

最初は「outlet接続」はStoryboardの中のスマホという形で見える
ViewControllerの中のLabelやButtonからしかできないと思っていたが

どうやらもう一個の折りたたみができるViewControllerの中のLabelやButtonからも
「outlet接続」はできるらしい。

前者の方からoutlet接続を試みるとズレる可能性があるので
後者の方からやる方が効率的で合理的。
IMS技術者の言うアクロニムが分からなかったときに見る記事

> **アクロニム（acronym）**
> 連なったアルファベットを通常の単語と同じように発音して読むもの。例： AIDS（エイズ）、OPEC（オペック）、NATO（ナトー）など。
*[頭字語 - Wikipedia](https://ja.wikipedia.org/wiki/%E9%A0%AD%E5%AD%97%E8%AA%9E)*

文字で読めれば検索したりなど調べようがありますが、音だけだと文字の想像がつかず何を指すアクロニムかわからなかったりしますよね。IMS技術者が口頭で発した用語の見当がつかなかった時のために、よく聞くIMS関連アクロニムを集めてみました。

基本的に頭字語でないものやアクロニムでないものは含めていませんが、/ERE・/NRE・SYSGEN・DBCTLは例外的に載せています。

### イーサフ = ESAF
External Subsystem Attach Facilityの略。IMS管理下で稼働するアプリケーションが外部のサブシステム(Db2,MQなど)にアクセスできるようにするもの。

### イーリ、イーレ = ERE
Emergency Restartの略、またはそれを行う/EREコマンドのこと。日本語訳は緊急再始動。正常終了していないIMSの再始動に用いる。

### エヌレ = NRE
Normal Restartの略、またはそれを行う/NREコマンドのこと。通常再始動。正常終了したIMSの再始動に用いる。

### オーサム = OSAM
Overflow Sequential Access Methodの略。IMS固有のアクセス方式で、全機能DBのDBデータセットに使用可能なアクセス方式のひとつ。

### オタマ = OTMA
Open Transaction Manager Accessの略。VTAM端末以外からのIMSトランザクション送受信を可能にする機能。たいてい普通にO-T-M-Aと読まれる。

### オルズ = OLDS
Online Log Datasetの略。DASD(ダスド; Direct Access Storage Device)に配置され、IMSがログを書き込む。

### サッズ = SADS
Single Area Datasetの略。DEDBのエリア・データセットで多重化していない場合こう呼ぶ。

### シスジェン = SYSGEN
System Generationの略。IMS生成によってIMSシステム全般を生成する、最も大規模なものを指すことが多い。フルジェン(Full Gen)、単にジェン(GEN)とも。

### スナ = SNA
Systems Network Architectureの略。IBMがTCP/IP以前に作った通信プロトコルで、IMSはSNAの実装であるVTAMを端末との通信に使用できる。

### ディビコン = DBCTL
Database Controlの略。IMSデータベース・マネージャー機能およびそれのみを使用するシステムのこと。

### ディフ = DIF
Device Input Formatの略。メッセージ形式サービスの制御ブロック群のうち、端末から入力を受け付ける画面のフォーマット情報を持つもの。

### ドフ = DOF
Device Output Formatの略。メッセージ形式サービスの制御ブロック群のうち、端末へ応答を表示する画面のフォーマット情報を持つもの。

### ドブフ = DOVF
Dependent Overflowの略。DEDBエリア・データセット中のCIのうち、ルート・セグメントの存在するCIに入りきらなくなった従属セグメント・データを格納する部分。

### ドラ = DRA
Database Resource Adapterの略。IMSと同一OS上で稼働する他ミドルウェアにIMSDBへのアクセスを提供する機能。

### ブイタム = VTAM
Virtual Telecommunications Access Methodの略。IMSが端末とのやりとりに使用できる通信。

### マッズ = MADS
Multiple Area Datasetの略。DEDBのエリア・データセットで多重化したものこう呼ぶ。

### ミッド = MID
Message Input Descriptorの略。メッセージ形式サービスの制御ブロック群のうち、アプリケーションの入力とするデータのマッピング情報を持つもの。

### モッド = MOD
Message Output Descriptorの略。メッセージ形式サービスの制御ブロック群のうち、アプリケーションの出力データのマッピング情報を持つもの。

### ワッズ = WADS
Write Ahead Datasetの略。物理書出しが必要になったIMSオンライン・ログの一時的な書き出し先となるデータセット。
ImGui::Imageでテクスチャ補間を無効化する

# はじめに
dear imguiではテクスチャサンプラ設定が固定されており、普通にスケーリングして描画すると下画像のようにバイリニア補間が掛かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730288/d98c76be-1420-5167-3921-7dc87942366a.png)
通常はこれで問題になることはありませんが、今回はスプライトエディタで拡大表示した場合にドット境界がわかりづらくなってしまうので補間をOFFにする必要が発生しました。
本稿ではその具体的な方法について記載します。

# 対処法
imguiではImGui::Imageなどのコマンドは内部的に配列に格納され、実行した順に描画されます。
ImGui::GetWindowDrawList()->AddCallback()によってコールバックを差し込むことで
ユーザ側から任意のタイミングで任意のコードを実行できます。
そこでスプライト画像を描画する直前にID3D11DeviceContext::PSSetSamplers()に別途作成した補間無し版のサンプラステートを設定することでバイリニア補間をOFFにすることができます。描画後はAddCallbackにImDrawCallback_ResetRenderStateを渡すことで適用した変更を戻しておきます。
具体的には以下のようになります。
```C++
# サンプラステートを変更
ImGui::GetWindowDrawList()->AddCallback(
  [](const ImDrawList* parent_list, const ImDrawCmd* cmd) {
    imm_context_->PSSetSamplers(0, 1, my_sampler_state.GetAddressOf());
  },
  nullptr);

# スプライト画像をスケーリングして表示
auto size = ImVec2(tex_rb_->width() * view_scale_, tex_rb_->height() * view_scale_);
ImGui::Image(tex_rb_->srv_.Get(), size);

# 変更した設定を元に戻す
ImGui::GetWindowDrawList()->AddCallback(ImDrawCallback_ResetRenderState, nullptr);
```

# 結果
無事補間をOFFにすることができました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730288/2f19d669-eaee-fc39-b7e1-a2f55c60e6c2.png)
【自分用メモ】XcodeのTARGETについて

『横展開アプリ』を作成する時に使える。

例）１個のクイズアプリを開発し終えた後に他のクイズアプリも作成したいけど
　　　　　実質的な違いが問題と答えくらいでCSVファイルの中身を書き換える程度で
　　　　　わざわざもう一度０から作成するのが面倒でな時といった状況で
　　　　　『TARGET』が使える。
ターミナルコマンドの基本

これらのコマンドは、ターミナルを使い始めるための基本的なものです。他にも多くのコマンドがありますが、これらをマスターすることで、より高度なターミナル操作に取り組む準備ができるでしょう。

* `mv`

ファイルやフォルダを移動したり、ファイルの名前を変更したりするために使用します。例えば、以下のように使います。

```bash
mv projects /c/Users/projects
```

これにより、"projects"フォルダがユーザーのフォルダに移動します。

* `cp -r`

ファイルやフォルダをコピーするために使用します。例えば、以下のように使います。

```bash
cp -r もともと場所 新しい場所
```

* `ls`

現在開いているフォルダの内容をリストアップします。オプション "-lah" を付けることで、より詳細な情報が表示されます。

* `pwd`

現在のディレクトリを表示します。

* `mkdir` 

新しいフォルダを作成します。例えば、以下のように使います。

```bash
mkdir Projects
```

* `cd` 

ディレクトリを変更するために使用します。例えば、以下のように使います。

```bash
cd Projects
```

これにより、"Projects" ディレクトリに移動します。

* `cd ..` 

親ディレクトリに移動します。

* `cd ~`

ユーザーのホームディレクトリに移動します。

* `rm -r`

ファイルやフォルダを削除します。"-r"オプションを付けることで、フォルダとその中にあるすべてのファイルを削除することができます。例えば、以下のように使います。

```bash
rm -r FolderName
```

* `code .`

現在のフォルダをVisual Studio Codeで開きます。


* `touch`

空っぽのファイルを作成するために使用します。例えば、以下のように使います。

```bash
touch index.html
```

これにより、"index.html"というファイルが作成されます。

* `cat`

ファイルの中身を表示します。例えば、以下のように使います。

```bash
cat index.html
```

これにより、"index.html"ファイルの中身が表示されます。

* `history`

過去に実行されたコマンドのリストを表示します。

* `clear`

ターミナルの内容をクリアします。
VBAでエクスプローラーからファイルを選択して、そのファイルをInputとして使用する方法

## 結論

```vba
Sub SelectFile()
    Dim fileName As Variant
    fileName = Application.GetOpenFilename("CSVファイル(*.csv),*.csv", , "ファイルを選択してください")
    If VarType(fileName) = vbBoolean And Not fileName Then
        ' ファイルが選択されなかった場合は何もしない
    Else
        ' 選択されたファイルを処理する
        MsgBox "選択されたファイル: " & fileName
        ' ここにファイルを読み込んで処理するコードを記述する
    End If
End Sub

```

## 解説

上記のVBAコードでは、`Application.GetOpenFilename()`メソッドを使用して、CSVファイルのみを選択できるように指定したファイル選択ダイアログを表示しています。fileName変数には、選択されたファイルのパスが代入されます。選択されたファイルがない場合は、VarType()関数を使用して、fileName変数がブール型かどうかを判定し、ファイルが選択されなかった場合は何もしないようにしています。選択されたファイルがある場合は、MsgBox関数を使用して選択されたファイルのパスを表示しています。そして、ファイルを読み込んで処理するコードを記述する箇所に処理を記述することができます。　
なお、`Application.GetOpenFilename()`メソッドでは、拡張子やファイル形式を指定することができます。上記の例では、CSVファイルのみを選択できるようにしています。また、`Application.GetSaveAsFilename()`メソッドを使用することで、ファイル保存ダイアログを表示してファイルを保存することもできます。
vscodeでgit rebaseで出るエディタを消してしまった時

vscodeのターミナルでgit rebase操作をする時、このような画面が出てくる…はず。

![スクリーンショット 2023-04-19 15.29.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636785/710c6ae1-46e6-0db3-a6e5-21380102755d.png)

これを、右下の「DISABLE REBASE EDITOR」を押すと、次回からテキストベースの画面が表示される。

この時、画像のエディタに戻したいと思った時に戻す方法のメモ。

# 方法1

- settings.jsonを開く
- git-rebase-todoを消す
  `workbench.editorAssociations`の項目を探し、その中にある`"git-rebase-todo": "default"`を削除する。
  (json形式なので、これが最終行の場合は一つ上の末尾のカンマも消す)
- 保存する

# 方法2

- 設定を開く
  `com+shift+p`から`Open user settings`
- editorAssociationsの項目へ行く
- 項目`git-rebase-todo`の行を削除

# 終わりに

settings.jsonでgit-rebase-todoの項目に入れることが出来る値を見ていると、gitlenの拡張機能のエディタっぽい？
gitの知識が多くないので、rebaseをしたらgit-rebase-todoというファイルが操作されているということ自体に気づいてなくて、この修正方法に気づくのに数ヶ月かかった。[^1]

[^1]:諦めて調べすらしていなかったのだが、ひょんなことから設定を開いた時に閃いて項目を見つけた。ただ、ひょんなことが何かを忘れた。なぜ今設定を開いたんだろう。。
VBAを使用して今の日時をファイル名にして出力する方法

## 結論

``` vba
Sub ExportData()
    Dim fileName As String
    Dim currentTime As String
    Dim data() As Variant
    Dim i As Long, j As Long
    Dim numRows As Long, numCols As Long
    Dim ws As Worksheet
    
    ' エクスポートするデータを配列に格納する（ここでは、Sheet1のA1:B10の範囲）
    Set ws = ThisWorkbook.Worksheets("Sheet1")
    With ws
        numRows = .Range("A1:B10").Rows.Count
        numCols = .Range("A1:B10").Columns.Count
        data = .Range("A1").Resize(numRows, numCols).Value
    End With
    
    ' ファイル名を設定する
    currentTime = Format(Now(), "yyyy-mm-dd_hh-mm-ss")
    fileName = "export_" & currentTime & ".csv"
    
    ' データをCSVファイルとしてエクスポートする
    With CreateObject("Scripting.FileSystemObject").CreateTextFile(fileName, True)
        For i = 1 To numRows
            For j = 1 To numCols
                .Write data(i, j)
                If j < numCols Then .Write ","
            Next j
            .WriteLine
        Next i
        .Close
    End With
    
    ' 完了メッセージを表示する
    MsgBox "データをファイル " & fileName & " にエクスポートしました。"
End Sub

```

## 解説

上記のVBAコードでは、Now()関数を使用して現在の日時を取得し、Format()関数を使用してファイル名に含めるためのフォーマットを指定しています。また、CreateTextFile()メソッドを使用して新しいファイルを作成し、データをCSV形式で出力しています。最後に、MsgBox関数を使用して完了メッセージを表示しています。
Xcodeで表示しているファイルの場所を開く方法

よく忘れるので備忘録です。

`cmd + shift + j`でXcode画面左のナビゲーションでファイルの場所が表示されます。

対象ファイルを閲覧中にそのファイルがどこに配置されているのか確認したいときに使います。
健康管理アプリにAIを導入するメリットとデメリット：所感

表題の通りです。
誰もが思い、5億回は結論として出てそうな気もしますが、先日便利そうなアプリを見たのでその所感です。

# 【メリット】

1. パーソナライズされたアドバイス
AIを利用することで、アプリはユーザーの健康状態を把握し、より個人に合わせたアドバイスを提供できます。例えば、ユーザーの病歴、生活習慣、運動量、食事などを継続的に追跡し、最適な健康管理計画を提供できます。

1. リアルタイム監視
AIを利用することで、アプリはリアルタイムでユーザーの健康状態を監視し、必要に応じてアドバイスを提供できます。例えば、運動中に心拍数が異常に高くなった場合、アプリはユーザーに休息をとるようにアドバイスできます。

1. 自動化されたデータの収集と分析
AIを利用することで、アプリはユーザーの健康状態に関するデータを自動的に収集し、分析できます。これにより、ユーザーは自分の健康状態に関する正確な情報を得ることができます。

一方、健康管理アプリにAIを組み込むことのデメリットは以下の通りです。

# 【デメリット】

1. プライバシー問題
AIを利用することで、アプリはユーザーの個人情報を収集することになります。そのため、ユーザーの個人情報が漏洩する可能性があります。

1. 信頼性の問題
AIは収集されたデータに基づいて自己学習を行います。しかし、この学習が誤った情報に基づいて行われる場合、アプリが提供するアドバイスが誤ったものになる可能性があります。

1. 人間の判断力を置き換える可能性
 健康管理アプリにAIを導入することで、人間が本来担っていた健康状態の判断やアドバイスの役割がAIに置き換えられる可能性があります。これにより、人々が自己判断力を失い、AIに依存することになるリスクがあります。

1. 既存のバイアスを強化する可能性
AIは、学習データに基づいて自己学習を行いますが、この学習データには、既存のバイアスが含まれることがあります。これにより、AIが提供するアドバイスや判断にも、既存のバイアスが強化される可能性があります。例えば、過去に偏った情報や偏見が含まれる医学書籍などが使用されている場合、AIが提供するアドバイスにもその偏りが反映される可能性があります。

健康管理アプリにAIを組み込むことは、多くのメリットがある一方で、デメリットもあることが分かります。そのため、健康管理アプリを利用する際には、AIが提供するアドバイスを鵜呑みにせず、常に自己判断力を持って利用することが重要です。また、プライバシーや信頼性の問題についても、アプリの開発元や運営元について十分な情報収集を行うことが必要です。

## 最後に

人間はAIに対して極端に信頼しすぎることがあります。AIが作業を完了する速さと精度に魅了され、自動化や予測の能力に頼りすぎることがあるためです。しかし、AIはまだ人間のように完璧ではありません。AIは特定の条件下で訓練され、限定的な能力しか持たないことがあります。また、AIにはデータを解釈する際の偏りや、予測の不確実性があることも覚えておく必要があります。

2016年のある研究のプレスリリースで、「人間は、ロボットが実際以上に世界をよく理解していて、決して間違いを犯したり、欠陥があったりはしないと信じているようでした」とAlan Wagner氏は語りました。

AIはツールとして有用であり、効率的な仕事をするのに役立ちますが、最終的な決定を行う前に人間が情報を検討することが重要です。
AIはあくまで補助ツールであり、人間が最終的な判断を下すことが重要かと考えます。
多分おそらくきっと、私を含め大多数の人はそれを正しく理解はできないまま使っていくであろということも予測しています。

AIが進化するにつれ、より強力で洗練されたモデルが開発され、より多くのタスクを自動化できるようになるかもしれません。しかし、私たちはAIがあくまでツールであることを理解し、AIの推奨する結果が妥当であることを確認するために、常にAIの出力を慎重に検討する必要があります。

私たちはAIを設計し、トレーニングする責任があります。私たちは、不公平や差別を避けるために、AIが利用するデータの品質と種類を慎重に選択する必要があります。AIが人間の仕事を奪うことがあるかもしれませんが、AIを活用して新しい仕事や新しい価値を創造することもできます。

最後に、私たちはAIと共存し、AIの可能性を最大限に引き出しながら、AIが人間を補完する方法を見つける必要があります。AIの進歩は間違いなく素晴らしいものであり、私たちに多くのチャンスを提供してくれます。しかし、AIが人間を超越することはないということを忘れてはいけません。

参考文献
In Emergencies, Should You Trust a Robot?
https://news.gatech.edu/news/2016/02/29/emergencies-should-you-trust-robot
reg-cli で画像比較結果をhtml出力する

## 実現したいこと
seleniumで取得した画面スクリーンショットを前回実行時との差分の有無を素早く確認したい
差分が有る場合に、対象のスクリーンショットの確認と差分内容を素早く確認したい


## 実現方針
reg-cliを使用して、該当の２つのディレクトリ配下の画像比較と比較結果のレポート出力を行う
 

### reg-cliとは

ビジュアルリグレッションテストのためのコマンドラインインターフェイスです。
アサートする画像を指定するだけで現在の画像と以前の画像を比較し、差分のHTMLを作成してくれます。

### ビジュアルリグレッションテストとは
ざっくりいうと「見た目の比較」をするテストのことです。
変更前のWebサイトのスクリーンショットを用意しておき、変更後のスクリーンショットを撮り比較することで、どこが変わったか差分を表示し確認することができます。


### 参考とした記事

https://suer.hatenablog.com/entry/2019/12/06/100747

https://zenn.dev/keita_hino/scraps/500f45bc926bc7

## 環境
```
mac os 13.0.1
npm  v6.14.15
node v14.18.1
```

## 手順

### ライブラリをインストールする

npm（Node.jsパッケージマネージャー）を使用して、インストールします。
コマンドプロンプトを開き、以下のコマンドを実行します。

```
npm install -g reg-cli

```

### スクリプトを作成

以下は、画像のビジュアルリグレッションテストを実行するシェルスクリプトです。

``` shell exec.sh
#!/bin/bash
# reg-cliを実行するシェルスクリプト

# reg-cliがインストールされているか確認する
if ! command -v reg-cli &> /dev/null
then
    echo "reg-cliが見つかりませんでした。インストールします..."
    npm install -g reg-cli
fi

# タイムスタンプを取得する
ts=$(date '+%Y%m%d%H%M%S')

# ディレクトリのデフォルト指定
diffDir="./diff"
actualDir="./actualDir"
expectedDir="./expectedDir"


# カレントディレクトリ内を探索して、フォルダのうちタイムスタンプが最も直近のものとその次のものを指定するように上書き変更

# mac用
dirs=($(ls -d */ | xargs -I{} stat -f '%m %N' "{}" | sort -r | awk '{print $2}' | head -n 2))

actualDir=${dirs[0]}
expectedDir=${dirs[1]}

# reg-cliを実行する
reg-cli $actualDir $expectedDir $diffDir \
        -C 4 \
        -M 0 \
        -T 0.1 \
        -A \
        -R "report_$ts.html" \
        -J "report_$ts.json" \
        -D

``` 
### 実行結果を確認

スクリプトを実行し、
出力されたレポート`report_yyyymmddhhmmss.html`を開いてみます。




### (補足)コマンドオプション

``` 
-U, --update：期待される画像を更新します。(実際の画像を期待される画像にコピーします)
-J, --json：指定されたJSONレポートのパスを指定します。省略すると、./reg.json に
-I, --ignoreChange：画像の変更が検出された場合、エラーが発生しないようにします。
-E, --extendedErrors：追加/削除された画像もエラーをスローするようにします。省略時は false 
-R, --report：HTMLレポートを指定されたディレクトリに出力します。例: -R "report_$ts.html" 
-P, --urlPrefix：すべての画像のsrcにプレフィックスを追加します。
-M, --matchingThreshold：一致しない場合のしきい値を設定します。0がデフォルトです。
-T, --thresholdRate：変更を検出するための比率のしきい値を設定します。
-S, --thresholdPixel：変更を検出するためのピクセル数のしきい値を設定します。画像の差分ピクセルが設定されたピクセル数よりも大きい場合、変更を検出します。この値は thresholdRate よりも優先されます。
-C, --concurrency：並列で実行するプロセス数を設定します。省略すると 4 になります。
-A, --enableAntialias：アンチエイリアスを有効にします。省略すると false になります。
-X, --additionalDetection：追加の差分検出を有効にします（高度な実験的機能）。 "none"または "client"を選択できます（デフォルトは"none"）。
```
nRF Connect SDK:Kconfigを使って動作モードを変更する

# 動作モードを変えたい
nRF5 SDK時代ではPreprocessor Definitionsという設定があり、ここに記述したものはDEFINEとしてプロジェクトに定義することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/674e20dd-459a-6a42-ff12-03ced8475b4e.png)
この機能を使うことで、同じソースコードで複数のプロジェクトを作り、特定の機能や動作を変えて動かすことができたりしました。

::: note
例えば実機と評価ボードによるエミュレーターなどです。
:::

ところがnRF Connect SDKではそのようなことができません。アプリケーションに対してビルドをいっぱい作ることはできますが、その中身を変えるというよりは使用するprj.confを変えることしかできません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/cf2af806-b78b-973c-6084-68d1dda73e1b.png)
しかしprj.confを変えたところでこの中に独自の定義をすることはできないため、どれを選ぼうがたいして差がありません。強いて言うなら使わない(あるいは直接関係ない)であろうコンフィギュレーションを各prj.confでそれぞれ選んでおくと、ソースコード中で

``` main.c
if (IS_ENABLED(CONFIG_XXX)) {
...
} else if (IS_ENABLED(CONFIG_YYY)) {
...
}

```

みたいな記述は可能ですが、なんていうかこう・・・いかにもIT土方がやりそうなやり方でセンスのカケラもないですよね(笑)。

::: note warn
辛口ですんません・・・でもいいモノを作りたいならちゃんとしっかり勉強しよう！
:::

# 独自の定義といえば
そうです、Kconfigを使います。ちなみにKconfigについては1年以上前に記事にしています。

https://qiita.com/yoshihiro0709/items/2222614b3633e2466e46

が、実は当時さっぱり分かっていなかったんですよね・・・。というよりは、Segger Embedded Studio時代においてはKconfigで独自のdefineを定義してもGUIとして表示されないので分かりづらかったと表現するのが正しいのかも知れません。
## Kconfigのメニューを増やす
NCS 2.0からはVSCode専用になったことでKconfigもGUI化しました。非常に分かりやすくなったと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/415b9fa5-afa2-b3f9-6495-bd2283182970.png)

::: note
なんであんなにも使うのを渋っていたのか・・・(笑)
:::

そして、なんと！Kconfigで記述した内容は実はここに追加することができるのです。最初に書いたようにビルドを使い分けたいのでラジオボタンを追加実装します。

``` Kconfig
source "Kconfig.zephyr"

choice
    prompt "Choose one of the following options"
    default SAMPLE_CHOICE_OPTION_A
    help
        Choose one.

config SAMPLE_CHOICE_OPTION_A
    bool "Option A"
    help
        Enable option A.

config SAMPLE_CHOICE_OPTION_B
    bool "Option B"
    help
        Enable option B.

config SAMPLE_CHOICE_OPTION_C
    bool "Option C"
    help
        Enable option C.

endchoice
```

このKconfigを追加して再度コンパイルします。KconfigのGUIに追加するにはVSCodeを再起動する必要がありますので、再起動してKconfigを確認してみると・・・。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/7ac0fca6-a8ac-5d3a-e1e9-f38611487dcc.png)
ちゃんと増えています。選択してprj.confを保存すると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/e53834c3-7ef2-3b13-069b-d328941f5030.png)
当然prj.confに追加されます。なお、Kconfigの中で

```
default SAMPLE_CHOICE_OPTION_A
```

を指定しているため、Aを選んだ場合はprj.confには何も追加されません。

::: note
明示的に記述されないだけでSAMPLE_CHOICE_OPTION_Aはdefineとしてプロジェクトに追加されます。
:::

# ChatGPTが吐き出してくれる(笑)
余談ですが、このKconfigはChatGPTがほぼ吐き出してくれます。ChatGPT震えるぜ・・・と思いましたが、これだけだとコンパイルエラーを起こしてしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/7af3274c-b5fe-aa49-0897-070e1171a91b.png)
上記のソースとどこが違うかと言うと・・・そうですね1行目にある

``` Kconfig
source "Kconfig.zephyr"
```

がありません。これがないとコンパイルエラーを起こしてしまいます。

::: note
ここに辿り着くまでにかなり苦労しましたよ、ええ・・・。
:::

# まとめ
これで従来と同じ使い方がまた一つできるようになりました(笑)