OmniAICreator/Qiita-1.07M huggingface dataset:
【備忘録】面接質問の推論ロジックをどこに実装するか──クリーンアーキテクチャと共に考えた設計判断の記録

# ✍️ はじめに

自分が今取り組んでいるプロジェクトでは、`Node` というエンティティがあり、それには「質問」と「回答」が含まれている。  
今回実装したいのは、このNodeを受け取って「次に来そうな質問を推論する」というユースケースだ。

一見すると簡単な処理のようにも思えるが、「そのロジックをどこに書くべきか？」という問いに対して、意外なほど深い設計の思索に踏み込むことになった。

# 🔍 最初の構想：ユースケース層に直接ロジックを書く

最初に試みたのは、以下のようなユースケース層への直書きだった：

```go
func (uc *createExpectedTemplateInteractor) Execute(ctx context.Context, input CreateExpectedTemplateInput) (CreateExpectedTemplateOutput, error) {
	node, err := uc.nodeRepo.FindByID(ctx, domain.NodeID(input.NodeID))
	// ...
	if strings.Contains(node.Question(), "志望動機") {
		// 次の質問を追加
	}
}
```

このように `strings.Contains` を使っていくつかのルールベース推論を行い、次の質問候補を列挙して返すという構成だ。


# 🤔 でも、これはユースケース層の責務だろうか？

書いているうちに、ふと疑問が湧いた。

> 「この“次の質問を推論する”という処理は、ユースケースの責務なのか？」

クリーンアーキテクチャの原則に従えば、ユースケース層はあくまで「処理の流れ」を組み立てる場所。  
**「どう推論するか」まで担うのは過剰な責務**ではないか？

# 💡 気づき：これはドメイン知識に基づく判断だ

「質問と回答の内容から、次に来そうな質問を導き出す」というのは、人間の知識や経験に基づく**意味的な判断**である。

つまりこれは、**ドメインロジック（意味のあるビジネス判断）**なのだ。

だから、本来この処理は **エンティティにもユースケースにも属さず、ドメインサービスが担うべき**だという結論に至った。


# 🧱 ドメインサービスとして実装する

そこで設計したのがこのインターフェース：

```go
type QuestionInferenceService interface {
	InferNextQuestions(node domain.Node) []string
}
```

そしてドメイン層の `domain/service/question_inference_service.go` に、シンプルな初期ロジックを実装した：

```go
func (s *questionInferenceService) InferNextQuestions(node domain.Node) []string {
	q := node.Question()
	a := node.Answer()

	if strings.Contains(q, "志望動機") {
		return []string{"なぜその業界を選んだのですか？"}
	}
	// ...
}
```

これで、**推論ロジックをユースケースから完全に分離**できた。

# 🔄 そして、将来的な拡張の余地を残す

特に意識したのは、「将来この推論ロジックをAI化したくなるかもしれない」という点だ。

そこでドメインサービスをインターフェースとして定義したことが生きてくる。  
将来的には、例えばOpenAIのAPIを使ったインフラ実装を用意し、次のようにDIで差し替えるだけで済む。

```go
var svc QuestionInferenceService
if config.UseAI {
	svc = infra.NewAIBasedInferenceService()
} else {
	svc = service.NewQuestionInferenceService()
}
```

# ✅ ユースケース側はこうしてシンプルに

```go
func (uc *createExpectedTemplateInteractor) Execute(ctx context.Context, input CreateExpectedTemplateInput) (CreateExpectedTemplateOutput, error) {
	node, err := uc.nodeRepo.FindByID(ctx, domain.NodeID(input.NodeID))
	if err != nil {
		return CreateExpectedTemplateOutput{}, err
	}

	nextQs := uc.inferenceService.InferNextQuestions(node)
	return uc.presenter.Output(nextQs), nil
}
```

**何をするか（処理の流れ）だけに責務が限定され、読みやすく、拡張にも強くなった。**


# 🧩 学びとまとめ

- 「この処理は“意味”を扱っているか？」と考えることで、ドメインロジックかどうかが判断できる
- ユースケースはフローを制御するだけにすることで、責務が明確になる
- ドメインサービス + インターフェース + DI の組み合わせは最強
- インフラ層での差し替えを前提に作ることで、将来の柔軟性が担保される
本気でQiitaの投稿を始めます

# 決意表明
フルスタックエンジニアになるべくReactやTypeScriptを極めます！

# 現状
組み込みエンジニア3年目の終わりで、Web開発の経験は業務で簡単な画面をhtml,css,javaScriptで作成した程度。Reactは触ったこともなく、公式ドキュメントを少しかじった程度。チュートリアルも読んだは良いけど、文末のチャレンジ問題が一切分からずほぼ全然頭に入ってない始末。

コーディングも遅く、1人で環境構築をする時も調べるのにかなり時間がかかってしまうポンコツ。

エンジニアとしてスキルアップするべく、以下を実施してみたけど大してスキルアップせず
- ネットワークスペシャリストを半年勉強 → 挫折して不合格
- エンベデッドシステムスペシャリストを3カ月勉強 → 挫折して不合格
- ネットワークの本を2,3冊読んで身に付いた気になるも → 数か月後には忘れている
- C#の勉強を目的にゲームをUnityで開発 → 半年かけてなんとなくはできたけど、中身はぐちゃぐちゃの分かりにくいコードに
- UdemyでC#のゲーム講座を買ってみる →　少しやって、その後はやらずに放置
- 組み込みを本気で勉強しようとラズパイを買ってみる →　なにを作りたいかを決めずに買ってしまい気づいたら放置
- 仕事で開発を依頼される → 言語の知識や、コーディングの遅さ・デバッグの遅さで予定していた工数の2倍以上の工数をかけてしまう

# なぜ勉強することを決意したのか？
上司にも、「時間をかければ誰でもできるよ（笑）」と言われてしまう始末に、、

そんな不甲斐ない自分自身が許せない・周りから尊敬される技術者になりたい。そして、Googleエンジニアやベンチャーで活躍されてる強いエンジニアになりたいという思いから**こんな自分を変えたい！！！！と決意し、本気で自分の実力と向き合うことにしました。**

# 将来
短期：Reactの基礎を完璧に理解し、公式ドキュメントの内容が理解できている！
中期：React・TypeScriptで自分のやりたい処理が実現できるようになっている
長期：自身のフロンエンド・バックエンドの技術力に自信を持っている。世の中の課題を解決するようなサービスをReact・TypeSciptで作れるようになっている。
Macのゴミ箱とrmコマンドの違いについて


# 前書き
rmコマンドでファイルを削除すると、ファイルは基本的に復元できません。一方、通常WindowsやMac、LinuxでもGUI上で削除を行っても、ゴミ箱を空にしない限り復元できます。
今回はMacベースでこの違いについて整理し、rmコマンド前のバックアップが非常に重要であることを確認します。


# Macのゴミ箱と`rm`コマンドの違いについて

## はじめに  
Macでファイルを削除する方法には主に2つあります。

- **Finder（ファインダー）**を使って削除する方法  
- **ターミナル**で`rm`コマンド（removeの略）を使って削除する方法  

この2つは結果が似ていても、実際の動作やリスクが大きく異なります。この記事では、**初心者にも分かりやすく**、それぞれの仕組みと違いを解説します。

---

## Finderでの削除：ゴミ箱の仕組み

Finderでファイルを削除すると、「ゴミ箱」に入ります。これは単にファイルを**隠しディレクトリに移動**しているだけで、物理的には削除されていません。

### ゴミ箱の実体

| 削除元ディスク        | ゴミ箱のパス                              |
|----------------------|-------------------------------------------|
| 内蔵ディスク（Mac本体） | `~/.Trash/`                               |
| 外付けドライブ・USB    | `/Volumes/<ボリューム名>/.Trashes/<UID>/` |

※ `~` はホームディレクトリ（例：`/Users/username/`）を意味します。  
※ `<UID>` はユーザーIDです（数値例：501など）。

### ゴミ箱移動の流れ
```
┌────────────┐ ┌────────────────────┐ 
│ Finder操作 │ ───▶ │ ~/.Trash に移動 │ 
└────────────┘ └────────────────────┘ 
▼ ゴミ箱を空にする → 完全削除
```
実際、bashシェルでユーザーのホームディレクトリを確認すると、.Trashディレクトリの存在を確認できます
![ターミナルでls -a ~を実行すると.Trashディレクトリが表示されます](<https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/622672/0cc87133-9b0d-4490-b06b-910d635857ad.png>)

### 特徴

- ファイルはすぐには消えない  
- 誤って削除しても復元可能  
- システムが許す限りゴミ箱内に残る

---

## `rm`コマンドでの削除

UNIX系OSであるmacOSにはターミナル操作があります。ここで使われる`rm`（remove）コマンドは、**ファイルを即時・完全に削除**します。Finderのような「ゴミ箱」は関与しません。

### 主な使用例

```bash
rm file.txt          # 通常のファイル削除  
rm -r mydir/         # ディレクトリを再帰的に削除  
rm -f force.txt      # 強制削除（確認なし）
┌────────────┐       ┌────────────────────┐
│ ターミナル操作 │ ───▶ │ ディスクから即座に削除 │
└────────────┘       └────────────────────┘
                           │
                           ▼
                  通常の手段では復元不可
```

|項目	|Finderでの削除	|rmコマンドでの削除|
|---|-------------|---------|
|削除後の挙動	|ゴミ箱に移動	|即時削除|
|復元可能性	|あり（ゴミ箱から戻せる）	|なし（通常の手段では不可）|
|安全性	|高い（誤削除しても安心）|	低い（復元が困難）|

# 終わりに
上書きに関しても同様ですので、ファイルの上書きの可能性がある場合はバックアップを取得するように心がけると良いでしょう。
バックアップ用のシェルを用意しておくのも良いかと。

```bash
#!/bin/bash

# バックアップ先ディレクトリ
BACKUP_DIR="$HOME/backup_$(date +%Y%m%d)"
LOG_FILE="$BACKUP_DIR/backup.log"

# 引数チェック
if [ "$#" -lt 1 ]; then
  echo "Usage: $0 <file_or_directory_to_backup>"
  exit 1
fi

# バックアップ先ディレクトリを作成
mkdir -p "$BACKUP_DIR"

# バックアップ処理
for TARGET in "$@"; do
  if [ -e "$TARGET" ]; then
    BASENAME=$(basename "$TARGET")
    DEST="$BACKUP_DIR/${BASENAME}_$(date +%H%M%S)"
    cp -a "$TARGET" "$DEST"
    echo "[✔] Backed up '$TARGET' to '$DEST'" | tee -a "$LOG_FILE"
  else
    echo "[✘] '$TARGET' does not exist. Skipped." | tee -a "$LOG_FILE"
  fi
done
```
多くの場合は取り返しがつかないことになるので、気をつけましょう。ユーザーの編集権限を明確に定義するのが一番効果的ですが、多くの開発現場ではそうもいかない側面もあると思います。
誤削除は誰にでも起こり得ます。大切なデータを守るためにも、バックアップと操作前の確認を徹底しましょう。
EC2を作ってみると色々と知らないことが・・・

## EC2を作ると色々と疑問がでてきたのでまとめる

EC2を作成して自作のアプリを動かすための手順はこんな感じ
1️⃣	EC2インスタンスを作成
2️⃣	SSHで接続する
3️⃣	Python & FastAPI 環境構築
4️⃣	アプリファイルをEC2にアップロード
5️⃣	FastAPIを起動
6️⃣	外部からブラウザでアクセス確認


## EC2を作ってみた
手順はこんな感じ
1-1. AWSマネジメントコンソールでEC2を開く


1-2. 「インスタンスを起動」
名前：fastapi-server（何でもOK）
OS：Amazon Linux 2023（推奨）
インスタンスタイプ：t2.micro（無料枠対象）
キーペア：作成 or 既存のキーペアを選択（.pemファイルをダウンロード）
セキュリティグループ：ポート22（SSH）とポート8000（カスタムTCPルール） を追加
 1-3. 作成完了したら、パブリックIPv4アドレスをメモ！

## 躓きポイント１：接続できない
EC2ができたから、Tera Termでつなごうと思ったけどつながらない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4057141/edff7bf9-1f22-4818-bcf1-4dfd6ea2758b.png)
ホスト名のところにIPアドレスだけをいれていたので、ec2-user@IPアドレスという形にすると
上手く接続できた。
（EC2を実際に作るときの手順は別途写真付きでまとめる）

## 躓きポイント２：ローカルからEC2にファイルをコピーできない
自作APIをEC2にコピーしたいと思ったがやり方がわからない。
調べてみると以下のようだ

```
scp -i ＜キーペアファイル.pem＞ ＜送りたいファイル名＞ ec2-user@＜パブリックIP＞:＜送り先の場所＞
```
実際は以下のように作ったコマンドをPowerShellで実行。パスは適宜置き換えてください
```
scp -i "C:\Users\Test\Desktop\秘密鍵\Study.pem" "C:\Users\Test\Desktop\Study\main.py" ec2-user@EC2のIPアドレス:~/
```
## とりあえず動いた
コピーしてきたPythonのファイルがある階層で下記を実行したらとりあえず起動した
```
uvicorn main:app --host 0.0.0.0 --port 8000
```

次回記事ではよくわかってなかったAWS環境のことをまとめます。
【Git】VSCodeからGitHubへの接続で403エラーが出た時の対処法（Mac）

## はじめに
VSCodeやコンソールからGitHubに接続しようとした時に、トークンの認証画面が出てこず、リポジトリを追加できませんでした。
簡単なmacの設定で繋げることができるようになったので、手順をまとめておきます。

## 問題
以下のコマンドでGitHub上のリポジトリにリモート接続しようとしました。

```
git remote add origin https://username@github.com/username/sample.git
```

次に、`origin`リポジトリに`master`ブランチを`push`しようとしたところ、以下の認証エラーが表示されました。

```
git push origin master
remote: Permission to username/sample.git denied to username.
fatal: unable to access 'https://github.com/username/sample.git/': The requested URL returned error: 403
```

# 解決方法
"キーチェーンアクセス"にて、トークンを設定することで解決できます。


### 手順
1. GitHubにログインし、[Personal Access Token](https://github.com/settings/tokens) を発行します。
2. macOSの"キーチェーンアクセス"を開きます。
"キーチェーンアクセス"はFinderの「アプリケーション」>「ユーティリティ」>「キーチェーンアクセス」から開くか、Spotlight検索（`command` + `space`）で「キーチェーンアクセス」と入力して開きます。
3. 検索欄に `github` と入力し、種類が"インターネットパスワード"の項目を探します。
4. 対象の項目をダブルクリックして開きます。
5. 「パスワードを表示」にチェックを入れてMacのログインパスワードを入力し、パスワード欄にGitHubで発行したトークンを貼り付けます。
6. これで問題のコマンドを入力すれば、エラーが出力されずに接続ができるようになっているはずです。

## 終わりに
認証エラーに遭遇したときは、まずキーチェーンの中にあるGitHub情報を確認・修正してみましょう。
M5StickC PlusでミニUDSサーバを実行（その１ 概要編）

自動車にはECU（Electronic Control Unite）という電子機器がたくさん搭載されており、エンジンの燃料噴射タイミングや窓の開閉等、様々な制御を行っています。
整備士がECUを点検する際は、専用の診断端末を自動車に接続し、ECUとUDS（Unified Diagnostic Service）という通信プロトロルでやりとりを行い、故障していないかチェックしています。
**本記事ではM5StickC PlusをECUに見立て、ミニUDSサーバを実装します。**

４つに分けて書きます。
- その１ 概要編
- その２ CAN通信編
- その３ ISO-TP編
- その４ UDS編

## ミニUDSサーバの概要
- CAN通信を行う
    - M5StickC Plusで[ESP32CAN（ESP32-Arduino-CAN）](https://registry.platformio.org/libraries/miwagner/ESP32CAN)を使用 
- トランスポート層プロトコルのISO-TPに対応
    - [iso-tp（ISO 15765-2 ISO-TP protocol implementation for Arduino）](https://github.com/altelch/iso-tp)をESP32CAN対応に改造
- UDSの```$11、$22```サービスに対応
    - $11を受信したら、M5StickC Plusをリセットする
    - $22を受信したら、M5StickC Plusのセンサーデータを応答する

## 使用機器
- [M5StickC Plus](https://www.switch-science.com/products/6470)
    - [M5Stack用CAN-BUSユニット（CA-IS3050G）](https://www.switch-science.com/products/6618)
    M5Stack用となっていますがM5StickC Plusでも使用できます
- Raspberry PI 4
    - [RS485 CAN HAT](https://www.waveshare.com/rs485-can-hat.htm)

## 接続構成
M5StickCPlus（ECU）とラズパイ（診断端末）をCAN接続

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269407/2a305a2c-c436-42e8-89bb-df4ccc1f8488.png)

## 開発環境
M5StickC Plusの開発環境はVSCode＋PlatformIOを使用しています。

https://qiita.com/ume-bar/items/234014927ae0857baf51

概要編 以上

[その２ CAN通信編へ](https://qiita.com/O_positive/items/c03971bd4d0d1ef3ef64)
matminerでマテリアルズインフォマティックス(2) データセットを前処理

## 簡単に使えそうなデータは・・
まずはテーブルで簡単に使えそうなデータを探してみました。
[Materials Project](https://next-gen.materialsproject.org/)のデータを元にしたmatbenchにある鋼材の元素組成と強度のデータがあります。
```python
from matminer.datasets import load_dataset
df = load_dataset("matbench_steels")
print(df)
```
matminerで簡単にpandasのデータフレームに保存はできるのですが、こういう形で保存されます。



```python
print(df.head(3))
```
|composition  |yield strength  |
|---|---|
|Fe0.620C0.000953Mn0.000521Si0.00102Cr0.000110N..  |2411.5  |
|Fe0.623C0.00854Mn0.000104Si0.000203Cr0.147Ni0....  |1736.3  |

組成のところに、元素と組成比が文字列で入っています。これを元素別に組成の表を作る必要があります。

## データの前処理

```python
import re
import pandas as pd

def extract_value(composition_string,element_name):
    pattern = r'{}(\d+\.?\d*)'.format(element_name)
    match = re.search(pattern, composition_string)
    if match:
        return float(match.group(1))
    else:
        return 0.0

elements = ["Fe","C","Mn","Si","Cr","Ni","Mo","V","N","Nb","Co","W","Al","Ti"]

for i in elements:
    comp_values = []
    for composition in df['composition']:

        value = extract_value(composition,i)
        comp_values.append(value)
        df[i] = pd.Series(comp_values)
print(df)

```
正規表現で、元素の文字列の次の数値を取り出す関数`extract_value`を作りました。
元素の文字列を渡して、その元素の後ろにある数値を取り出していきます。

これで取り出すと次のような元素組成のデータが取り出せます。

|yield strength  |Fe |	C |	Mn |	Si	 |Cr |	Ni |
|------|------|------|------|------|------|------|
|2411.5  |0.62	|0.000953|	0.000521	|0.00102	|0.00011|	0.192|
|1736.3  |0.623|	0.00854	|0.000104	|0.000203	|0.147|9.71E-05|

これで、元素組成から降伏強度を予測する準備ができました。
[Tips] IIS で Debug 版の ASP.Net を動かす設定

超ニッチな Tips です。

ASP.Net WebAPI を作っていて、IIS 上に展開する際、本番系だけでなく、テスト系（構成が Debug のやつ）も動作させたいことがあると思います。

開発時の appsettings.Development.json と appsettings.Production.json をそのまま生かして IIS に乗せる方法のメモを残しておきます。

# 前提条件

開発系と本番系でデータベースが異なっていて、Visual Studio の構成の切り替えで、接続を変えるようなWebAPI サーバだったとします。
なお、テスト系データベースサーバが 192.168.50.100、本番系データベースサーバが 192.168.100.100 とします。

## appsettings.json の設定

本番系とテスト系で appsettings.json の内容を切り替えるために、次のようになっているかと思います。

```json:appsettings.json
{
  "ConnectionStrings": {
  }
}
```

```json:appsettings.Development.json
{
  "ConnectionStrings": {
    "mydb":"Server=192.168.50.100;Database=MyAppDb;User Id=myuser;Password=mypassword"
  }
}    
```

```json:appsettings.Production.json
{
  "ConnectionStrings": {
    "mydb":"Server=192.168.100.100;Database=MyAppDb;User Id=myuser;Password=mypassword"
  }
}    
```

## IIS の設定

IIS には、本番系とテスト系のアプリを登録しておきます。
当然、ファイルパスやポートは別のものにしておきます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177616/b9879a14-766a-4a5d-90cc-98ccdaf4b2dc.png" width="400"></img>

図では webapp がテスト系で、webapps が本番系です。

Visual Studio の発行では、本番系とテスト系の２つの発行設定を作成します。
本番系の発行では、構成を「Release」、ターゲットを「webapps」に、テスト系の発行では、構成を「Debug」、ターゲットを「webapp」にしておきます。

## IIS のアプリごとの設定

IIS に登録したアプリが、指定の appsettings.*.json を読むように設定します。

テスト系の webapp で、appsettings.Development.json を読ませるため、次の設定を行います。

1. webapp の構成エディタをダブルクリックします
2. 構成エディタの設定画面でセクションを「system.webServer/aspNetCore」に切り替えます
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177616/793af699-de91-412d-8144-4b6b947ae0b3.png" width="500"></img>
3. environmentVariables を選択し「項目の編集」をクリックします
4. コレクションエディターが表示されます
  <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177616/a745b573-7be9-41c0-9f08-4bdee6fa6364.png" width="500"></img>
5. 名前に ASPNETCORE_ENVIRONMENT、値に Development を登録します
6. 設定を保存しアプリの設定をリロードします

この設定でテスト系のアプリで appsettings.Development.json を読んでくれるようになります。

本番系は ASPNETCORE_ENVIRONMENT が指定されていない場合、自動的に Production として扱われるため、明示的な設定は不要です。ただし、明示的に設定しておくこともできます。

# 追加メモ

IIS の構成エディタでの設定を記載しましたが、環境変数による切り替えですので web.config に直接記述する方法や、システム環境変数で設定する方法もあります。
Realmの遅延読み込みについて

## 始めに
以下のような処理を書いたときに、パフォーマンス面で問題がないか気になったため、調査してみることにしました。

```swift
let contensCount = realm.objects(Content.self).count
```

## Results Are Lazy
> Realm only runs a query when you actually request the results of that query. This lazy evaluation enables you to write elegant, highly performant code for handling large data sets and complex queries. You can chain several filter and sort operations without requiring extra work to process the intermediate state.

https://www.mongodb.com/docs/atlas/device-sdks/sdk/swift/crud/read/#results-are-lazy

### Realmでオブジェクトがロードされるタイミングについて
* プロパティアクセス時
 ```swift
   let dogs = realm.objects(Dog.self)  // 🙅‍♀️
   let name = dogs[0].name  // 🙆‍♀️
 ```
 
* イテレーション時
```swift
   let dogs = realm.objects(Dog.self)
   for dog in dogs {  // 🙆‍♀️
       print(dog.name)
   }
```

*　配列変換時
```swift
   let dogs = realm.objects(Dog.self)
   let array = Array(dogs)  // 🙆‍♀️
```

*　クロージャ処理時
```swift
   let dogs = realm.objects(Dog.self)
   let names = dogs.map { $0.name }  // 🙆‍♀️
```

Countは全件のデータを読み込んでカウントしているわけではないので、パフォーマンスに問題なかったです
```swift
let contensCount = realm.objects(Content.self).count
```

### Resultsはライブであることに注意
> Results to a query are not copies of your data: modifying the results of a query will modify the data on disk directly. This memory mapping also means that results are live: that is, they always reflect the current state on disk.

https://www.mongodb.com/docs/atlas/device-sdks/sdk/swift/crud/read/#results-are-not-copies

一度クエリを実行して取得した Results は、データベースの変更を自動かつリアルタイムで反映してくれます。その特性を活かすためにも、設計時には更新のタイミングや参照の仕方に注意が必要です。
```swift
let results = realm.objects(Content.self)
print(results.count) // この時点ではA件

try! realm.write {
    realm.add(Content(...)) // 追加
}

print(results.count) // 自動でB件に更新される
```


## 終わりに
RealmのResultsが遅延評価される仕組みにより、効率的な処理が行われていることがわかりました。ただし、全件読み込みを伴う処理では注意が必要な場面もあるため、常にデータの扱い方を意識しながら設計していきたいと思います。
離散的な数列の最大値を求めるマニアックな方法について

# はじめに
数列の漸化式という考えは特殊である。というのは、隣接する数の関連性を示すというものであり、一般項のようなそれ自体の値（絶対値）を表すものではないからだ。なので、あくまで数と数の相対的な関係性を示すものである。なので、漸化式を用いて問題を解く場合は、絶対性ではなく相対性を用いると効率的に解くことができる。それが漸化式というものだからだ。そこで今回は、コイントスを複数回するとき、表は何回出やすいのかについて反復試行の確率を用いたモデルで考察する。具体的には、まず題意の確率を反復試行の確率の公式を用いて求める。次に、その式から漸化式を導出して値を評価することを行う。

今回は、高校生向け（特に確率や漸化式に苦手意識を持つ）に書いたので、すこしまどろっこしい部分はあるがご了承いただきたい。

# 問題

表裏対称なコインを用いてコイントスを$2n$回行った場合、表と裏の対称性から$n$回表がでる確率こそ最も大きくなることを示せ。

例.以下は試行を100回行ったとき50回表がでる確率こそ最大になることを示した二項分布の様子である。

![二項分布反復試行回数100.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1686019/3dec1884-0ab6-af3e-9b68-226e2168ad83.png)

※1 二項分布とは、コイントスのような反復試行の確率がモデルとなっている目的となる変数（今回は表が出る回数）についての確率の分布を現したものである。


参考

https://qiita.com/arairuca/items/ecaf42ba67a563d32a52

# 証明

## 導入
反復試行の確率より、$m$回表が出る確率は以下のように表すことができる。


```math
P_m= _{2n}C_m (\frac{1}{2})^m (\frac{1}{2})^{2n-m}= _nC_m (\frac{1}{2})^{2n}
```

ただし、$0\le m \le 2n$とする。


したがって、コンビネーションの定義より、

```math
P_m= \frac{2n!}{m!(2n-m)!}\frac{1}{2^m}
```

と書き直すことができる。

ところで、分母の階乗を微分して最大値を求めるというオーソドックな考えはすごく難しそうである（絶対性）。

そこで、隣接する項の相対性を視る『漸化式』を使うことを考える。

（イメージとしては、$a_1<a_2<\cdot \cdot \cdot <a_n$となれば、この区間では$a_n$が最大となるという感じだ）

## 漸化式

とりあえず、隣接する項の差を考えてみる。

```math
P_{m+1}-P_m= \frac{2n!}{(m+1)!(2n-m-1)!}\frac{1}{2^m}-\frac{2n!}{m!(2n-m)!}\frac{1}{2^m}
```

なんとなく共通項でくくれそうであるので、くくる。

```math
P_{m+1}-P_m=  \frac{2n!}{ 2^m m!(2n-m-1)!}(\frac{1}{m+1}-\frac{1}{2n-m})
```

ここで、今回は大きさのみを比較したいため、$\frac{2n!}{ 2^m m!(2n-m-1)!}$は$m$ に依存せず常に正の値をとるため、目的から除外す。

なので、結局以下の関数の正負を調査すれば$P_m,P_{m+1}$の大小関係を比較することができる。

```math
f(m) =\frac{1}{m+1}-\frac{1}{2n-m}=\frac{(2n-m)-(m+1)}{(m+1)(2n-m)}
```

これの正負のみを知りたいため、常に0以上となる分母を除いて考えると以下のようになる。

```math
g(m) =(2n-m)-(m+1)=-2m+(2n-1)=-2(m-n-0.5)
```

したがって、


$g(m) > 0$のとき、$P_{m+1}< P_m$ となる。


ゆえに、$g(m) > 0$　のとき$m< n-0.5$となる。

ここで、$n,m$は正の整数なので、$g(m) > 0$のとき
```math
m\le n
```
が成立する。
（たとえば、$m>3.5$となるとき、$m$が正の整数という条件のもとで、$m\ge 4$ となることとニュアンスは同じである。）

これまでの考察をまとめると、

　$m\le n$のとき、$P_{m+1} > P_m$となる。

同様に、

$m> n $のとき、 $P_{m+1}<P_{m}$となる。

以上をまとめると、


```math
P_0<P_1< \cdot \cdot \cdot<P_{n-1} <P_n>P_{n+1}> \cdot \cdot \cdot> P_{2n-1}>P_{2n}
```

となる。（分かりやすさを優先したため、このような数学的にあまりよろしくない書き方をしてしまったことに注意していただきたい。）

上記の不等式から$m=n$のとき、確率は最大となる。

結論としては、$2n$回コイントスをした場合、$n$回表がでるという場合がもっとも生じやすいとなる。

これは、下記のグラフからも伺える。

![二項分布反復試行回数100.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1686019/3dec1884-0ab6-af3e-9b68-226e2168ad83.png)


# 補足
マニアな方向けの補足である。
今回は、$\frac{P_{m+1}}{P_m}$と$1$の大小関係を$P_{m+1}-P_{m}$と$0$の大小関係の代わりとしてもよい。

なぜなら、

```math

P_{m+1}-P_{m}=P_m(\frac{P_{m+1}}{P_m}-1)

```
とおけてかつ、$P_m\ge 0$となるからである。


# まとめ

今回は、高校数学の反復試行と漸化式を用いた問題を扱ってみた。連続関数の最大・最小問題は、微積などの解析的なツールを使っても求められるが、今回のような離散的なものは微積だと難しくなってしまうものがある。そんなときは、初歩に戻って、漸化式とは、隣接する項の関係性を調べるツールだということを思い出せば、数列の最大・最小問題はスムーズに解ける場合が多い。少し、発展的な内容にはなるが、微積分と数列、漸化式には凄い関係性があるのだが、それは大学以降で学ぶので高校生の読者は楽しみにするといいだろう。
PythonでOracleSQLのPL/SQL 関数がNULLを返し始めてSQLがバグった話

# うまくいっていた処理が突然バグった
今回わたしのRAGの技術を用いたシステムで起きた事象は、以下のOracleSQLを実行した際にdistanceが必ずNONEになってしまうというものでした。
~~~
    query = f"""
    SELECT co.SUMMARY_DATA AS SUMMARY_DATA, vector_distance(embed_vector, co.SUMMARY_VECTOR) AS distance
    FROM DOC_SUMMARIES co, doc_up du,
         (SELECT to_vector(et.embed_vector) embed_vector
          FROM dbms_vector_chain.utl_to_embeddings(:1, JSON(:2)) t,
               JSON_TABLE(t.column_value, '$[*]'
               COLUMNS (embed_id NUMBER PATH '$.embed_id', embed_data VARCHAR2(4000) PATH '$.embed_data', embed_vector CLOB PATH '$.embed_vector')) et)
    WHERE {where_clause}
    ORDER BY vector_distance(embed_vector, co.SUMMARY_VECTOR)
~~~
:1には実行したプロンプト、:2には{"provider": "ocigenai", "credential_name": "OCI_CRED", "url":
    "https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/20231130/actions/embedText", "model": "cohere.embed-multilingual-v3.0"}という設定が文字列で入ります。


ちなみにこの処理では実行したプロンプトをベクトル化しつつ、事前に取り込んでいた文書情報をベクトル数値から判断して近しい順番に抽出するものです。
その際、文書と一緒にプロンプトとのベクトル距離を取得して、ベクトル距離がほとんど同じものははじくようにしていました。
しかし、これまでずっと、このうまくいっていたこの処理が突如、distanceがNONEとなって抽出がうまくいかなくなりました。
その為、ChatGptと会話をして理由を探ってみると以下のような話になりました。



**ChatGptからの回答**
Oracle の PL/SQL 関数（特に dbms_vector_chain や JSON_TABLE など）は、デフォルトで CLOB/JSON 型として渡さないと silent fail（= NULL）になることがあります。

JSON(:1) に渡す値がstr（Pythonの文字列）の場合	
Oracleが文字列長・型チェックで失敗し、空ベクトル or NULL 返す場合がある。

JSON(:1) に渡す値がCLOB（cursor.var 経由）の場合
Oracle側でJSON型として解釈される。

これがわたしの質問に返ってきました。
つまり、文字列ではなく事前にCLOB型にしないとJSON型に対して渡すのには適切ではないらしいです。

これまでうまくいっていたのは、偶然だったのですね。
というわけで追加したのが、以下の処理。

~~~
clobVar = cursor.var(oracledb.CLOB)
clobVar.setvalue(0, embed_genai_params)
~~~

無事にdistaceの値がそれぞれ正しいものが返ってくるようになりました。
すでにリリース済みのシステムなので、肝が冷えました。

皆さんも気を付けてくださいね！
動的配列！push/popで配列を自在に操ろう！

Paizaで「動的配列」の問題をやってたときの話。Setで書いたら一見うまくいってたけど、「末尾削除(`pop_back`)」が面倒だった。

よく考えたら問題が動的"配列"なんだから、配列を使えばいいじゃん...。
ちゃんと配列(`Array`)を使えば一発だった。サクッと整理！


## <br>📘 問題概要

N個の要素を持つ配列Aに対して、以下の3つの操作を実行するプログラムを作成せよ。

- `0 x` : A の末尾に x を追加 （push_back）
- `1` : A の末尾を削除 （pop_back）
- `2` : A を半角スペース区切りで出力 （print）

<br>入力例
```
3 5
1 2 3
0 10
0 12
2
1
2
```
出力例
```
1 2 3 10 12
1 2 3 10
```

https://paiza.jp/works/mondai/data_structure/data_structure__array_boss


## <br>❌ NGコード（Setでやってみた）
```js
const rl = require('readline').createInterface({input:process.stdin});
const lines = [];

rl.on('line',(input)=>{
    lines.push(input);
});

rl.on('close',()=>{
    const arrayA = new Set();
    
    lines[1].split(' ').forEach(a => arrayA.add(a));
    
    lines.slice(2).map(a => a.split(' ')).forEach(num => {
        
        if(Number(num[0]) === 0){
            arrayA.add(num[1]);  // 追加はできる
        }
        
        else if(Number(num[0]) === 1){
            const arr = Array.from(arrayA);
            arrayA.delete(arr[arr.length - 1]);  // 末尾削除のつもりが微妙に面倒
        }
        
        else if(Number(num[0]) === 2){
            const arrA = Array.from(arrayA);
            console.log(arrA.join(' '));
        }
    });
});
```
- `.add()`で追加はできるけど、末尾から削除がめちゃくちゃやりにくい
- `Set`は`index`使えないから、`.pop()`みたいな操作がない
- `.delete()`で最後の要素を消すには毎回`Array`に変換する必要あり → 非効率＆読みにくい
- 重複NGなのも地味に面倒。今回の問題に向いてない


## <br>✅ OKコード（配列使ったver）
```js
const rl = require('readline').createInterface({ input: process.stdin });

const lines = [];

rl.on('line', (input) => {
    lines.push(input);
});

rl.on('close', () => {
    const arrayA = lines[1].split(' ');

    lines.slice(2).forEach(a => {
        const num = a.split(' ');

        if (Number(num[0]) === 0) {
            arrayA.push(num[1]);  // 末尾追加：これでOK
        }
        
        else if (Number(num[0]) === 1){
            arrayA.pop();  // 末尾削除：これだけ！
        }
        
        else if (Number(num[0]) === 2) {
            console.log(arrayA.join(' '));  // 出力もシンプル
        }
    });
});
```
- `push()` と `pop()`で末尾操作が直感的＆簡潔
- `join(' ')`で一発出力、余計な変換いらない
- `Set`使ってたときのゴチャゴチャが全部なくなった


## <br>💡まとめ
- `Set`じゃ末尾操作できない（`pop`がない！）
- 配列でやるのが正解：`push()`と`pop()`で完結
- 無理やりな変換は、たいてい設計ミスのサイン
- 出力は `.join(' ')` でスパッといける


「`Set`使った方がかっこよさそう」って思った(´;ω;｀)。でも今回みたいに素直に配列が正解な場面も多い！


<br><br>[僕の失敗談(´;ω;｀)🚀](https://paizabeginner.wordpress.com/2025/04/13/paiza%e3%81%a7%e5%9f%ba%e6%9c%ac%e3%83%9e%e3%82%b9%e3%82%bf%e3%83%bc-%e9%85%8d%e5%88%97%e6%93%8d%e4%bd%9c%ef%bc%9apush-pop%e3%81%a7%e9%85%8d%e5%88%97%e3%82%92%e8%87%aa%e5%9c%a8%e3%81%ab%e6%93%8d/)
M1 MacとLooking Glass Goを使って立体的なバーチャル嫁を呼び出した話

# 1.初めに

　2024年1月下旬にクラファンでLooking Glass Goが特集されていましたね。2024年の秋口に商品が届いてから普段個人開発で使用しているM1 Macと連携させてバーチャル嫁をLooking Glass Goで召喚したいなあと考えてその時に色々と研究やリサーチを繰り返し、召喚することには成功していましたが、その時どうやって作っていたかとかその際のMacの設定どうしてたかとか詰まるポイントが多々ありましたので今回備忘録として記事を書く気になりました。

# 2. 使用環境
 - M1 Mac(MacBook Pro)
 - Looking Glass Go
 - Unity(ChatdollKitが動作するバージョンなら何でも)
 - ChatdollKit(v0.8.0)2024年秋口当時の最新版を使って作りましたがここも使用バージョンはお好みで

# 3. 前提条件
 - ChatdollKitを用いてPC画面でキャラクターと会話できる状態にしてあること（デモもインストールしてあるならこの辺の構築は楽）

# 4. 構築手順
 ChatdollKitのドキュメントや解説動画を元にAIキャラクターと話ができる状態になっていることを前提に以下の話を進めていきます。
 
### 4.1. Looking Glass Unity Pluginの入手
　[公式サイト](https://lookingglassfactory.com/software/looking-glass-unity-plugin)からUnity専用のLooking Glassのプラグインを入手します。

### 4.2. 入手したプラグインをUnityのバーチャル嫁のプロジェクトでインポート
　入手したLooking GlassのUnity用プラグインをインポートすると、Unityに以下の形でLooking Glass用のメニューが追加されます。
![スクリーンショット 2025-04-13 16.34.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1167908/124d75d3-bbaf-43d2-8259-c10a171d3b2a.png)

### 4.3. サンプルの中からHologram Cameraを複製しバーチャル嫁の開発用シーンに入れる
　プラグインのインポートに成功するとAssets配下にLooking Glassのディレクトリが追加されます。Examplesの配下にいくつかサンプルシーンがありますので、その中の1 - Framing Content with Hologram CameraのHologram Cameraプレハブをバーチャル嫁開発シーンにコピペします。

### 4.4. Looking Glass GoとMacを接続
　Looking Glass GoはUsb Type-Cのポートに対応しています。Type-Cのポートが無いPCですとここで詰みます(PCと直に接続しないと拡張ディスプレイとして認識されないため、USBハブを経由するとアプリケーションとして認識されるっぽい？)。拡張画面として認識されると以下のようにMacのデスクトップがもう一つ用意されているような状態になります。
![IMG_7630.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1167908/0c7b9f7b-b329-4755-a95d-cce5cd87d0cf.jpeg)

### 4.5. バーチャル嫁のデモシーンがLooking Glass Goに投影されるか検証する
　Unityに戻り、Looking Glassのメニューを選択し、Toggle Previewを押します。すると、UnityのGameシーンと同様のウィンドウがLooking Glass Goに投影されます。投影されない場合一度Looking Glass GoのケーブルをType-Cポートから抜いて再接続してみましょう。
![IMG_7631.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1167908/1d60f4c8-96ce-40d4-9951-8791a67b48e8.jpeg)

### 4.6. 音声を認識するか検証する
　Looking Glass Goはマイクやスピーカーを内蔵していません。内蔵しているかと思ってどうして音声を拾わないのか色々調査するのに時間を使ったりしました。音声を拾ったり、キャラクターの声が聞こえるようにするためにはMac本体の内蔵マイクやスピーカーを使うか、別途マイクやスピーカーを用意しておく必要があります。また、Mac本体のマイクとスピーカーを使う場合UnityHubのデフォルト設定(標準)ですと、再生ボタンを押して実行した際、マイクやスピーカーの設定はLooking Glass Goの方が優先されます。マイクやスピーカーを内蔵していないLooking Glass Goにいくら話しかけてもキャラクターが無反応なのも納得です(ここでハマった)。ですので、UnityHubのマイク設定を変更してMac本体の内蔵マイクやスピーカーで音声処理を行うように設定変更しま

　まず、タスクバーにオレンジ色のマイクのアイコンがあるか探します。なかったら、PC画面のUnityプロジェクトをクリックすると出てきます。
![スクリーンショット 2025-04-13 17.08.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1167908/a85fc64f-9ef0-4cec-98cd-0f956ef3290e.png)

　マイクのアイコンを押して声を分離の方を選択します。こうすることでMac本体の内蔵マイクやスピーカーでも音声が拾えるようになります。
　動作確認を行い、音声が拾えるようになりますと、以下のようにキャラクターと話すことができるようになります。
デモンストレーションは[こちら](https://x.com/hexanitrobenzen/status/1911337067847794799)

# 5.まとめ
　Gateboxデベロッパーコンソールがサービス終了したためその代替品としてLooking Glassを使うのも選択肢の一つですが、ドキュメントが全部英語だったり仕様がわかりづらかったりで使いこなすまでに時間がかかるというのが私の印象でした。
　一方で、小型で持ち運びがしやすいので、小型PCとかで動かせるようにしてちょっとしたイベント施設に持って行って使うとかであればLooking Glassもありかなと思います。欲を言えばマイクとスピーカーも内蔵してクラウド上に成果物をアップロードできるようにし、制御用のPCや周辺機器すら要らなくなったらありがたい...。
CSSスタイルがなぜか適用されないを解決！CSSの優先度（詳細度）の計算方法

## はじめに
フロントでの開発をする上で、CSSを設定することは避けて通れないと思います。
そのような中で、cssファイルにスタイルを設定したにも関わらず、画面に反映されていないという経験をしたことがある人も少なくないのではないでしょうか。そこで、cssが適用される優先度（詳細度）を計算する方法をまとめました。思ったようにスタイルが反映されない場合はこの詳細度に基づいて正しく設定できているか見直してみてください！

## CSSの詳細度とは？
CSSで複数のルールが同じ要素に適用されそうなとき、どのスタイルが優先されるかを決めるものです。

## 詳細度の表し方
CSSの詳細度は、以下の 4つの値 `(a, b, c, d)` の形で表します。

| レベル | 説明                            | 対象                              |
|--------|---------------------------------|-----------------------------------|
| a      | インラインスタイル              | `<p style="color: red;">`        |
| b      | IDセレクタ                      | `#main`                           |
| c      | クラス、属性セレクタ、疑似クラス| `.btn`, `[type="text"]`, `:hover`|
| d      | 要素セレクタ、疑似要素          | `div`, `::before`                |

### レベルa（インラインスタイル）
style属性で直接書かれているスタイルです。
```html
<p style="color: red;">Hello</p>
```

### レベルb（IDセレクタ）
HTML要素に付けたidを対象にしたセレクタです。
```css
#main { }

#nav-bar .item { }
```

### レベルc（クラス、属性、疑似クラス）
(1) クラスセレクタ
```css
.button {}

.container-fluid {}
```

(2) 属性セレクタ
HTML要素の属性（`type=""`, `href=""`など）を指定してスタイルを当てます。
```css
input[type="text"] {}

a[target="_blank"] {}
```

(3) 疑似クラス
`:◯◯` の形で、ユーザー操作や構造的な条件を表します。
```css
a:hover {}

input:focus {}

li:first-child {}

:not(.active) {}
```

### レベルd（タグ（要素）、疑似要素）
(1) 要素セレクタ（HTMLタグそのもの）
```css
div {}

h1 {}

p {}

ul li a {}
```

(2) 疑似要素
要素の中の特定の部分や、実際にはHTMLに存在しない仮想要素にスタイルを当てます。
```css
p::before {}

a::after {}

input::placeholder {}
```

## 詳細度の計算方法
1. セレクタを分解する
2. それぞれの種類ごとに数をカウントする
3. 4桁の数値 `(a, b, c, d)` にまとめる

<br/>

例：`#main .card:hover h2`
```
#main → ID → b = 1
.card, :hover → クラス + 疑似クラス → c = 2
h2 → 要素セレクタ → d = 1
```
→ 詳細度：(0, 1, 2, 1)

## 詳細度の比較方法
詳細度`(a, b, c, d)`を、**左から順に比較**して、大きい方が優先されます。

比較ルール：
`a` の値を比較
→ (aが等しい場合) `b` を比較
→ (bが等しい場合) `c` を比較
→ (cが等しい場合) `d` を比較

<br/>

例：
```
#header .nav a ：(0, 1, 1, 1)
.main-content h1：(0, 0, 1, 1)
```
→ (0, 1, 1, 1) の方が、IDセレクタがあるため優先されます。

## 詳細度が同じだった場合
詳細度が完全に同じ場合は、CSSファイル内で後に書かれた方が優先されます。
```css
.button {
  color: red;
}

/* 同じ詳細度なら、これが優先される */
.button {
  color: blue;
}
```

## !importantは最優先
詳細度に関係なく、`!important` のあるスタイルが優先されます。
（スタイルの管理が難しくなるので、基本は避ける）
```css
/* これが優先される */
.button {
  color: red !important;
}

#main .button {
  color: blue;
}
```
※同じプロパティに複数の `!important` がある場合は、詳細度で比較されます。

## 自動計算ツール（Specificity Calculator）
詳細度を自動で計算してくれるツールもあるので、cssの構造が複雑で詳細度を考えるのが難しい場合は活用してみてください。
[Specificity Calculator](https://specificity.keegan.st/#google_vignette)

https://specificity.keegan.st/#google_vignette

## 最後に
詳細度の計算方法は理解できたでしょうか？CSSを当てる際にこの記事の内容を活用していただけたら幸いです。詳細度を計算するときに他に考慮するべき点などがあればぜひ教えてください。
複数種類のパーミッションを取得する

# はじめに
今回は、外部ストレージへのアクセスを加味した複数種類のパーミッションを実装していきます

### 実装
```kotlin
// ファイルアクセス用の設定画面に遷移させる処理
private fun intentManageExternalStoragePermission(context: Context) {
    val intent = Intent(
        ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,
        Uri.parse("package:${context.packageName}")
    )
    intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
    context.startActivity(intent)
}
// 取得したいパーミッションの一覧を作成する
private fun createPermissions(): List<String> {
    // bluetoothのパーミッションはバージョンによって異なるのでわける
    val bluetoothPermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        listOf(
            Manifest.permission.BLUETOOTH_SCAN,
            Manifest.permission.BLUETOOTH_CONNECT,
        )
    } else {
        listOf(Manifest.permission.BLUETOOTH)
    }
    val permissions = mutableListOf(
        Manifest.permission.CAMERA,
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.WRITE_EXTERNAL_STORAGE
    ).zip(bluetoothPermission).flatMap { it.toList() }
    return permissions
}
// 外部ストレージへのアクセス権限があるかどうかを判定するための変数
private val isExternalStorageManager: Boolean
    get() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) return true
        return Environment.isExternalStorageManager()
    }

// パーミッション申請を表示させるためのＵＩ
@OptIn(ExperimentalPermissionsApi::class)
@Composable
private fun PermissionContent(
    onAlreadyGranted: () -> Unit,
    onDenied: () -> Unit,
) {
    val context = LocalContext.current

    // 単数の場合はrememberPermissionStateでよい
    val permissionStates = rememberMultiplePermissionsState(createPermissions()) { result ->
        if (result.values.all { it } && isExternalStorageManager) {
            onAlreadyGranted()
        } else {
            onDenied()
        }
    }
    LaunchedEffect(Unit) { if (!isExternalStorageManager) intentManageExternalStoragePermission(context) }
    SideEffect { permissionStates.launchMultiplePermissionRequest() }
}
```
### さいごに
扱うバージョンが広ければ広いほど判定も多くなりますし、新しければ新しいほど厳しくなっていくのでパーミッション周りはなかなか難しいですよね。
あまりJetpackComposeでの記事が見当たらなかったので備忘録として書いてみました
どなたかのお役に立てれば幸いです
Ubuntu に Jw_cad をインストール

Ubuntu 24.10 に wine を利用して Jw_cad をインストールしました。

## ダウンロード

```bash
wget https://www.jwcad.net/download/jww825a.exe
```

## インストール

```bash
wine jww825a.exe
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/8ab4432a-cc6e-4140-9a29-8ccd12eeed6a.png)

文字化けがありますが、インストールは可能です。

## 起動

アイコンをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/6a487ece-f2b2-4586-b696-d35fd75252bb.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/87275640-b5ad-4f04-82b5-7b0c5dbc9f12.png)
Excel VBAで線形補間

# はじめに
最近仕事でExcelを触る機会が増えました。いろいろなところからデータを集めてくるのはいいものの、それぞれ刻み幅や最大最小値が異なっていて厄介。線形でもいいから補間できておけば便利かなと思い重い腰を上げVBAのFunctionを組んでみました。
「スプラインで補間しろ」「Python使え」とも思うのですが、環境構築がほとんどいらず、簡単に実装できるので何かと重宝するのではないでしょうか？

# 線形補間する関数

関数`LinearInterpolate`は1次元配列`x`と2次元配列`ref`を引数に取り、2次元配列を返します。

```vb
Option Base 1
Private Function LinearInterpolate(x As Variant, ref As Variant) As Variant
    '参照する定義域の処理
    Dim xref As Variant
    ReDim xref(UBound(ref))
    For i = 1 To UBound(xref)
        xref(i) = ref(i, 1)
    Next i
    
    '参照する値域の処理
    Dim yref As Variant
    ReDim yref(UBound(ref), UBound(ref, 2) - 1)
    For i = 1 To UBound(yref)
        For j = 1 To UBound(yref, 2)
            yref(i, j) = ref(i, j + 1)
        Next j
    Next i
    
    '結果の格納先
    Dim y As Variant
    ReDim y(UBound(x), UBound(yref, 2))
    
    '線形補間の計算
    For i = 1 To UBound(x)
        'xrefの中からxを超えない最大のインデックスを取得する
        On Error Resume Next
        match_i = WorksheetFunction.Match(x(i), xref, 1)
        On Error GoTo 0
        
        For j = 1 To UBound(y, 2)
            If x(i) < WorksheetFunction.Min(xref) Or WorksheetFunction.Max(xref) < x(i) Then
                'xがxrefの範囲外のときは処理しない
                GoTo next_x
            ElseIf x(i) = WorksheetFunction.Max(xref) Then
                'xがxrefの最大値のときは値をそのまま返す
                y(i, j) = yref(match_i, j)
            Else
                '線形補間を真面目に計算する
                x1 = xref(match_i)
                x2 = xref(match_i + 1)
                y1 = yref(match_i, j)
                y2 = yref(match_i + 1, j)
                
                y(i, j) = (y2 - y1) / (x2 - x1) * (x(i) - x1) + x1
                
            End If
        Next j
next_x:
    DoEvents '定期的にDoEventsしておくと応答なしを回避できる
    Next i
    
    '出力用の配列を定義
    Dim ret As Variant
    ReDim ret(UBound(x), UBound(y, 2) + 1)
    
    '出力用の配列に値を代入
    For i = 1 To UBound(x)
        ret(i, 1) = x(i)
        For j = 1 To UBound(y, 2)
            ret(i, j + 1) = y(i, j)
        Next j
    Next i
    
    LinearInterpolate = ret
End Function
```

`x`は求めたい独立変数
`ref`は1列目に参照する独立変数、2列目以降の参照する従属変数が格納された2次元配列です。
戻り値の配列も`ref`と同様、1列目に求めた独立変数、2列目以降に求めた従属変数が格納されます。

# 仕様

例えば、
`x`として
| x |
|:-:|
| 1 |
| 1.5 |
| 2 |

`ref`として
|xref | y1ref | y2ref |
|:-:|:-:|:-:|
| 1 | 1 | 3 |
| 2 | 2 | 4 |

というデータを入力した際、戻り値として次のような配列を返します。
| x | y1 | y2 |
|:-:|:-:|:-:|
| 1 | 1 | 3 |
| 1.5 | 1.5 | 3.5 |
| 2 | 2 | 4 |

`x`で指定された値が`xref`内に存在しない場合、`y`の値は`Empty`のままになります。

`ref`の列は`y1,y2,y3,...`と自由に増やすことができるので、シートを丸々配列に代入し、そのままこの関数に突っ込むという使い方ができます。
戻り値もrefと似た構造になるため、別シートにそのまま貼りつけることも容易です。
curlでAPI叩くとき、-X POST って本当に必要？警告の意味と対処法を解説

https://android.benigumo.com/20250413/curl-inferred/

cURLを使ったAPIリクエストで表示されるメッセージ
```
Note: Unnecessary use of -X or --request, POST is already inferred.
```
これは一体何を意味しているのでしょうか？今回はこの警告の意味と、それに対するベストプラクティスについて解説します。

### 🔍 1. 警告メッセージの意味とは？
cURLでAPIにPOSTリクエストを送信する際に、次のような書き方をしていませんか？

```sh
curl -X POST "https://httpbin.org/post" -H "accept: application/json"
```

このとき、以下のような警告が表示されることがあります：

```
Note: Unnecessary use of -X or --request, POST is already inferred.
```
これはcURLが「POSTメソッドはすでに推論されているよ」と教えてくれているだけで、エラーではありません。つまり、```-X POST``` を書かなくても cURL は POST リクエストだと自動で判断してくれています。

### 🧪 2. 実験：実際にリクエストを送ってみる

試しに、次のコマンドを実行してみましょう。

```sh
echo '{
  "user_id": 123,
  "name": "ワロ田",
  "age": 14
}' | curl -vs \
  -H 'Content-Type: application/json; charset=UTF-8' \
  -H 'Accept: application/json' \
  -d @- https://httpbin.org/post
```
  
このコマンドでは ```-X POST``` を使わず、標準入力からJSONを送信しています。するとcURLは、```-d``` オプションがあることから自動的にPOSTリクエストだと判断します。

ログの中には次のようなリクエストヘッダが含まれています：

```
> POST /post HTTP/2
> Host: httpbin.org
> Content-Type: application/json; charset=UTF-8
> Accept: application/json
```

つまり、わざわざ ```-X POST``` を明示しなくても、cURLが自動で正しく振る舞ってくれるというわけです。

#### ✅ 結論：
- ```-d``` や ```--data``` を使った時点でPOSTと見なされる

- ```-X POST``` は冗長である

不要なオプションは削除することでコマンドがシンプルに

### 🔒 3. TLS接続と通信の詳細
cURLで```-v```や```-vs```を指定すると、通信の詳細が表示されます。例えば、以下のような出力が得られます：

```
*   Trying 3.231.253.142:443...
* Connected to httpbin.org (3.231.253.142) port 443
* ALPN: server accepted h2
* Using HTTP2, server supports multiplexing
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
...
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* Server certificate:
*  subject: CN=httpbin.org
*  start date: Mar 27 00:00:00 2024 GMT
*  expire date: Jun 25 23:59:59 2024 GMT
```

この情報から分かること：

- 通信に使われたTLSバージョン（ここではTLS 1.3）

- 接続先のIPアドレスやポート

- 証明書の内容（開始日・終了日・Common Name）

これらの情報を確認することで、通信の安全性やトラブル時の診断に役立ちます。

### 🧼 4. cURLコマンドのベストプラクティス

#### ✂️ 不要なオプションを省く
以下のような書き方は冗長です：

```sh
curl -X POST -d '{"foo":"bar"}' https://example.com
```

ベストな書き方は次のようになります：

```sh
curl -d '{"foo":"bar"}' https://example.com
```

同様に、```PUT```や```DELETE```も ```-d``` を使えば推論されるケースがありますが、それらは用途に応じて ```-X``` を残すこともあります（明示的にしたい場合など）。

#### 🎯 APIとのやりとりでのポイント
- ```-d```を使えばPOSTになる

- ```-H```でContent-TypeやAcceptを適切に指定する

- ```@-```を使えば標準入力からデータを送れる

-vでTLSや接続情報を確認できる

### 🧾 まとめ：cURLは賢い。だからシンプルに書こう
今回紹介したように、cURLは送信内容からHTTPメソッドを推論できる賢いツールです。特に```-d```を使う場合、```-X POST```のような明示的指定は不要であり、かえって警告メッセージが出てしまいます。

ベストプラクティスは以下のとおり：

- ```-X```の使い過ぎを避ける

- ```-d```を使えばPOSTになることを理解する

- ```-v```で通信内容を確認する習慣をつける

冗長な指定を避け、簡潔で読みやすいcURLコマンドを書くことで、日々の開発やAPI連携がよりスムーズになります。ぜひ今日から試してみてください。

### ☝️ 参考

https://android.benigumo.com/20250413/curl-inferred/
【さようなら】アップデート方法や使い続けるリスクを解説します【Windows10サポート終了】

【免責事項】
本投稿は正確性・完全性を保証するものではありません。
万が一、本情報に基づいて被ったいかなる損害についても、一切の責任を負いかねます。
あらかじめご了承ください。

## 🎥 動画でわかりやすくチェックしたい方はこちら

【さようなら】アップデート方法や使い続けるリスクを解説します【Windows10サポート終了】
[![サムネイル](https://img.youtube.com/vi/_S8FFE85nTY/maxresdefault.jpg)](https://www.youtube.com/watch?v=_S8FFE85nTY)


---

## ✅ 概要

> 2025年10月14日、**Windows 10の延長サポートが終了**します。

この日を境に、Windows 10は「**セキュリティ的に無防備な状態**」になります。  
「まだ使えるから大丈夫」と油断していると、**ウイルス感染・情報漏洩**のリスクが一気に上昇します。

この記事では、以下のポイントをわかりやすく解説します：

- サポート終了の意味とリスク
- 企業・個人が取るべき対策
- Windows 11へのアップグレード計画

---

## 📌 Windowsサポートの基本

| サポートの種類 | 内容 | 状況 |
|----------------|------|------|
| メインストリームサポート | 新機能追加・不具合修正 | **既に終了**（Win10） |
| 延長サポート | セキュリティアップデート | **2025年10月14日まで** |

> つまり、**今のWindows 10は“最後の砦”だけが残っている状態**です。

---

## ⚠ サポート終了後のリスク

1. **セキュリティリスクの増加**  
　新しいウイルスや脆弱性に**一切対策されない状態**になります。

2. **ソフトウェアの非対応化**  
　プリンタードライバやZoom、Officeなど、**対応対象外に**なる可能性。

3. **トラブル時のサポート対象外**  
　メーカーもMicrosoftも「サポートできません」となり、**自己責任対応**になります。

4. **企業リスク（特に重要！）**  
　情報漏洩・マルウェア感染が起これば、**信用低下・賠償責任**の可能性も。

---

## 🛠 対策：何をすべき？

### ✅ 個人ユーザーの場合

- Windows 11へのアップグレードを検討
- 対応していないPCなら**買い替えを視野に**
- 必ず2025年夏までに準備開始！

### ✅ 企業・組織の場合

- 対象PCの洗い出し・リスト化
- IT部門やベンダーとの調整
- 必要に応じて**有償延長サポートの検討**
- ソフトウェアの互換性確認

---

## 📆 スケジュール感まとめ(Home and Pro)

| 内容 | 期限 |
|------|------|
| Windows 10 延長サポート終了 | **2025年10月14日** |
| Windows 11（23H2）のサポート終了 | **2025年11月11日** |


> 単に「11にしたら安心」ではなく、**バージョン管理と更新運用も必要**です！

---

## ✅ まとめ

- Windows 10のサポート終了日は**2025年10月14日**
- それ以降は**セキュリティリスクが急増**
- **個人も企業も早めの対応が必要**
- Windows 11も**バージョン管理が大切**
- 「まだ半年ある」は危険、**今から準備が必須！**

---

## 📺 関連動画リンク（再掲）

【さようなら】アップデート方法や使い続けるリスクを解説します【Windows10サポート終了】
[![サムネイル](https://img.youtube.com/vi/_S8FFE85nTY/maxresdefault.jpg)](https://www.youtube.com/watch?v=_S8FFE85nTY)

---

## 参考
<a href="https://learn.microsoft.com/ja-jp/lifecycle/products/windows-10-home-and-pro">Windows 10 Home and Pro</a>
<a href="https://learn.microsoft.com/ja-jp/lifecycle/products/windows-11-home-and-pro">Windows 11 Home and Pro</a>


## ✨ おわりに

この記事が役立ったら、**いいねやストック**いただけると嬉しいです！  

---
## 主のコンテンツ
<a href="https://www.udemy.com/course/active-directory-p/?referralCode=1BC8474484DCD2814852">Active Directoryの基礎※ハンズオン形式</a>
<a href="https://www.udemy.com/course/active-directory-n/?referralCode=FF51293A35539D596A6A">Active Directoryの基礎 グループポリシー編※ハンズオン形式</a>
<a href="https://www.udemy.com/course/joshishelp/?referralCode=7ECEB242C114FA3736F8">情シスヘルプデスク入門※一部ハンズオン形式</a>
<a href="https://twitter.com/naoya_itbegin">X</a>
<a href="https://www.youtube.com/@%E7%A4%BE%E5%86%85%E3%83%8D%E3%83%83%E3%83%88%E3%83%8A%E3%83%93">Youtube</a>
環境の変化と戦う①(AWS Lambda編)

# はじめに

スタックチャン(Stack-chan)で10分ポモドーロ(10分毎に時刻をお知らせ)を動かしています。

https://qiita.com/kitazaki/items/bef12c0958da0466194a

enebularのクラウド実行環境を使用して実装しています。

https://enebular.com

# 何もしていないのに動かなくなった

ある日(2025年4月10日くらいから)、スタックチャンが動かず、「分」の発話しかしなくなりました。

動き(上を向いて口を動かす)→発話(「時」と「分」)→動き(元に戻る)
から
発話(「分」)
のみに動作が変わってしまいました。

![スクリーンショット 2025-04-13 16.07.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/c97689f1-f8fd-413d-8b1c-8c70be26405f.png)

無意識的にいつもの期待している動作が変わると、違和感しかありません。
動きがなく、いきなり発話(しかも「分」のみ)されると、少し怖いです。

# 一時的だと思いたい

最初は「なんか、動かないなー」と様子見していましたが、常態化するとストレスを感じ始めます。
「AWS Lambda」の調子が悪いのかなー、とか「enebular」の不具合かなー、とか他人のせいを考えますが、同じ状態が1〜2日続くとなんとかしなきゃ、と考え始めます。

# 再デプロイ

enebularのアップデートでNode-REDの実行環境が変わることがある(バージョンアップなど)ので、同じフローをクラウド実行環境に再デプロイしました。
クラウド実行環境のログを確認して、AWS Lambdaも問題なく実行されていることを確認しましたが、動作が変わりません。

# 切り分け調査

enebularのフロー編集から実行すると動作は問題ありません。
クラウド実行環境のHTTPトリガー(HTTPリクエストでフローを実行)の動作も問題ありません。
クラウド実行環境のスケジュールトリガー(フローを時刻指定で実行)の動作だけ動作がおかしいことを確認しました。
(あとの調査で、HTTPトリガーの動作も時間を空けると動作がおかしいことを確認しました。)

# 原因を推察する

Node-REDのフローは以下のとおりです。

![スクリーンショット 2025-04-13 16.45.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/dfdf872a-b32d-4e3c-b3ab-fda127a620f6.png)

期待する動作の時系列は以下のとおりです。(AWS Lambdaの場合)

![スクリーンショット 2025-04-13 16.45.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/8ceef7a7-a792-4b66-9152-fc06669aee80.png)

通常、Node-REDは常時プロセスが起動していますが、AWS Lambdaの場合はNode-RED実行環境がデプロイされ、フローの実行(「LCDP in」ノードから「LCDP out」ノードまで)が完了すると、破棄(デストロイ)されます。
(デストロイされてから短い時間で再度フローを実行する場合は、キャッシュが利用され高速にデプロイ実行されます。)

切り分け調査の結果から、Node-RED実行環境がデプロイされ、フロー実行開始直後の動作に問題がありそうです。
仮説としてMQTT接続が遅れた場合、発話(「時」)と動作の情報が破棄される可能性があります。
Node-REDはNode.jsで動作しており、基本的に非同期処理でフローが進行するため、MQTT接続の完了を待ってからMQTT(Publish)の送信をしていません。

![スクリーンショット 2025-04-13 16.45.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/864c9630-a6ac-4990-aef2-abaa442e1dfd.png)

# 対策を考える

Node-REDには「complete」ノードが存在し、指定したノードの実行完了をトリガーにすることが可能ですが、「MQTT out」ノードのMQTT Brokerとの接続完了をトリガーにすることができないので、単純にMQTT(Publish)の送信の前に遅延(200 msec)を入れることにしました。

![スクリーンショット 2025-04-13 16.45.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/38b8d760-56c7-4490-8e02-469139b64001.png)

# フローを変更する

変更後のNode-REDのフローは以下のとおりです。

![スクリーンショット 2025-04-13 16.28.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/663ea0c9-bd19-441c-af81-47d778ddbdfb.png)

# あとがき

いまのところ期待通りに動作しています。
原因が分かってしまえば対策は簡単ですが、原因の推察〜仮説〜対策の検討〜検証までの一連のプロセスを経験することは、現実の世界で環境が変わった場合に適応するために重要です。
【React】位置情報がテーブルに保存されない症状の解決方法

# はじめに
取得した位置情報をsupabaseのテーブルへ保存しようとした際、以下のようなエラーが表示されました。
こちらの解決方法について躓いたため備忘録をふくｍ記事にします。


:::note warn
https://hogehoeehoge.supabase.co/rest/v1/post_locations 403 (Forbidden)
:::

アクセス権限がないようです。
既に以下のようにgisスキーマを作成しPostGIS拡張機能を許可していましたが、うまく挙動していないようでした。

```
CREATE SCHEMA gis;
```

```
create extension postgis with schema gis;
```



# 解決方法

作成したgisスキーマを削除し、extensionsスキーマにPostGIS拡張機能を許可

</br>


強制削除
```SQL
DROP EXTENSION postgis CASCADE;
```

extensionsスキーマにPostGIS拡張機能を許可
```SQL
CREATE EXTENSION postgis WITH SCHEMA extensions;
```

権限付与
```SQL
GRANT USAGE ON SCHEMA extensions TO anon, authenticated, service_role;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA extensions TO anon, authenticated, service_role;
```

テーブル再作成
```SQL
create table if not exists public.post_locations (
post_id int not null references posts(id) on delete cascade,
location geography(POINT) not null,
PRIMARY KEY(post_id)
);
```

Database反映
```tarminal
npx supabase gen types typescript --project-id "プロジェクトID" --schema public > utils/supabase-database.ts

```

# 参考
https://zenn.dev/ototrip/articles/tech-supabase-geolocation-1

https://supabase.com/docs/guides/database/extensions/postgis?queryGroups=language&language=data&queryGroups=database-method&database-method=sql


# おわりに
とりあえず今回はこちらの方法で解決することができました。
データベースの扱いももっと習熟したいと思いました。

<br>

[<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3707255/8bfacf61-7d8d-6d27-cb07-98d1f8f27fcd.png"  width=100%>](https://projisou.jp)
VSCodeのJupyterでインストールしたモジュールがインポートできない

# 症状
pandasをインポートしようとしたら、んなもんねえ！と怒られました。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4020495/c5ba81c6-6561-4565-ac8e-22d4e8d94573.png)

でも、ちゃんとインストールされています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4020495/d316785b-1e8b-402f-90db-1d5e9f0dc191.png)

# 原因
Jupyter上で右上にあるカーネルが、仮想環境を構築したカーネルと違ったようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4020495/a85475f3-591b-45ca-b398-4e99b044a713.png)

# 対処法
ここで仮想環境のカーネルを選択すればいいはずなのですが、VSCodeさんが私の仮想環境カーネルを表示してくれません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4020495/8f91268f-b913-423f-8f7a-be5ac186b040.png)

Chat-GPT先生もお手上げで、でもググったら仮想環境のカーネルを表示する方法が出てきました。**みんな同じ問題抱えてるんですね。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4020495/3df6a588-ba08-40a0-a3aa-ae91756491bb.png)

ipython kernel install はいまいちよくわかりませんが、オプションはそれぞれ以下のとおりのようです。
| オプション        | 説明                                 |
|------------------|--------------------------------------|
| `--user`         | 現在のユーザーのローカル環境に追加する |
| `--name`         | 自分の仮想環境の名前を指定する         |
| `--display-name` | VSCode上で表示されるカーネル名         |

だいぶはまりましたが**レコメンドしてくれるようになりました。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4020495/29242fcd-b3e0-44e3-a3ea-55587b006bbb.png)
JetpackCompose スワイプ中の指に追従する画像の実装方法

スワイプ中の指に追従する画像の実装方法です。

このような感じの実装です。
![Videotogif (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274615/dd20f0de-62dc-45f8-8068-9e536f2b45ea.gif)


```SampleImage.kt
@Composable
fun SampleImage() {
    var position by remember { mutableStateOf(Offset.Zero) }
    val imageSize = 64.dp
    val density = LocalDensity.current

    BoxWithConstraints(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.White)
            .pointerInput(Unit) {
                detectDragGestures { change, _ ->
                    position = change.position
                }
            }
    ) {
        val boxWidth = constraints.maxWidth.toFloat()
        val boxHeight = constraints.maxHeight.toFloat()

        LaunchedEffect(Unit) {
            position = Offset(boxWidth / 2, boxHeight / 2)
        }

        val imageOffset = with(density) { (imageSize / 2).toPx() }

        Image(
            painter = painterResource(id = R.drawable.baseline_back_hand),
            contentDescription = "追従する画像",
            modifier = Modifier
                .offset {
                    IntOffset(
                        (position.x - imageOffset).toInt(),
                        (position.y - imageOffset).toInt()
                    )
                }
                .size(imageSize)
        )
    }
}
```

`pointerInput`を使用してジェスチャーを取得し、スワイプしているポジションを取得します。
`var position by remember { mutableStateOf(Offset.Zero) }`　こちらが表示位置です。
`Image`の`Modifier.offset`に`position` から画像サイズの半分を引いた数値を設定することで表現してます。
【徹底比較】ChatGPTとDeepSeekを5つの質問で実際に比較してみた

【免責事項】
本投稿は正確性・完全性を保証するものではありません。
万が一、本情報に基づいて被ったいかなる損害についても、一切の責任を負いかねます。
あらかじめご了承ください。

## 🎥 動画版はこちら
[![ChatGPTしか使ったことない人に結論をお伝えします【DeepSeek比較】](https://img.youtube.com/vi/1svcBAnEc4o/0.jpg)](https://www.youtube.com/watch?v=1svcBAnEc4o)

---

## ✅ 概要

最近話題の中国製AI「**DeepSeek**」が「**ChatGPT**」より優れている？という声をよく耳にします。  
実際に両方を使って同じ質問をぶつけ、**回答の質や構成、実用性を徹底比較**してみました。

---

## 🔍 検証内容

以下の質問を両AIにぶつけて比較しました：

1. Active Directoryとは何か（初心者向けに）
2. Active Directoryのメリット・デメリット
3. PowerShellでユーザーを一括作成するスクリプト
4. 天安門事件とは何か
5. 中国のインターネット検閲について
6. 「ChatGPTとDeepSeek、どちらが優れている？」とAIに聞いてみた

---

## 💬 主な比較結果（ざっくりまとめ）

| 観点 | ChatGPT（GPT-4） | DeepSeek |
|------|-------------------|-----------|
| 回答の構成 | 見やすく丁寧（絵文字や表も使用） | 簡潔でやや淡白 |
| 初心者目線 | ◎ やさしい言い回し | △ やや機械的 |
| 技術質問の精度 | 高く、実務でも使える | 概ね正確だが汎用性は低め |
| センシティブな質問（例：天安門事件） | 回答あり | 回答拒否（制限あり） |
| セキュリティに関する配慮 | 中立的 | セキュリティ強化を強調する傾向あり |

---

## 🎯 結論

### ✅ ChatGPT（GPT-4）の方が優れている点

- 回答のわかりやすさ・構成力
- 実務利用を意識した具体例・補足
- 政治的・社会的質問にも中立な姿勢

### ⚠ DeepSeekの懸念点

- 一部の質問に回答制限（中国の歴史や検閲）
- プライバシーポリシーで「中国のサーバーにデータ保存」
- データセキュリティの不安（台湾など一部政府機関で使用禁止）

---

## 🛡 補足：業務での利用について

> 特に**個人情報や機密情報**を扱う場面では、セキュリティリスクを考慮し、**慎重にAI選定することが重要**です。

---

## 📝 最後に

ChatGPTとDeepSeek、どちらも一長一短はありますが、少なくとも現時点では  
**「業務用途」や「安心感」「日本語のやさしさ」でChatGPTが有利**という印象でした。

👇ぜひ、動画で詳しい比較をチェックしてみてください！

[![ChatGPTしか使ったことない人に結論をお伝えします【DeepSeek比較】](https://img.youtube.com/vi/1svcBAnEc4o/0.jpg)](https://www.youtube.com/watch?v=1svcBAnEc4o)

---


## ✨ お願い

この記事が役立ったら、**いいねやストック**いただけると嬉しいです！  

---
## 主のコンテンツ
<a href="https://www.udemy.com/course/active-directory-p/?referralCode=1BC8474484DCD2814852">Active Directoryの基礎※ハンズオン形式</a>
<a href="https://www.udemy.com/course/active-directory-n/?referralCode=FF51293A35539D596A6A">Active Directoryの基礎 グループポリシー編※ハンズオン形式</a>
<a href="https://www.udemy.com/course/joshishelp/?referralCode=7ECEB242C114FA3736F8">情シスヘルプデスク入門※一部ハンズオン形式</a>
<a href="https://twitter.com/naoya_itbegin">X</a>
<a href="https://www.youtube.com/@%E7%A4%BE%E5%86%85%E3%83%8D%E3%83%83%E3%83%88%E3%83%8A%E3%83%93">Youtube</a>
VSCodeのgit bashのコマンド履歴が残らない原因と対策

## テーマ
VSCodeのターミナルで実行したGit Bashのコマンド履歴が残らなくて困ったので、問題の原因と解決策について調査しました。


## 原因
- **ターミナルの終了方法:**
  いつもexitコマンドを使用してターミナルを終了していなかったので、git bashが正常終了せず、コマンド履歴の保存処理が行われなかったからでした。


## 解決策

- **コマンド履歴の保存タイミングの設定を変更する:**
  .bashrcファイルに以下の行を追加します。この設定により、コマンド実行後に履歴が自動的に保存されます。

  ```bash
  shopt -s histappend
  PROMPT_COMMAND="history -a; $PROMPT_COMMAND"
  ```

## 補足 
- **git bashのコマンド履歴保存のデフォルトの仕様**
  
  AIのFelo先生によると、デフォルトでは以下とのこと。 

    - コマンド履歴は`~/.bash_hisotory` に保存される。
    - git bashが正常終了したときのみ保存される。


## 参考
以下の記事を参考にしました。ありがとうございました。
https://qiita.com/hogeco/items/192d933c4290580238c7
https://qiita.com/misohagi/items/3ab0abc3bce2c11719c8
https://qiita.com/ko-zi/items/7563cae9d336626c3637