OmniAICreator/Qiita-1.07M huggingface dataset:
PaddleOCR+Ktor+Next.jsで文字認識Webアプリ

## はじめに
### 概要
本記事では、OCRを実施する簡単なWebアプリケーションを紹介します。
コードは全てGithubにアップしております。

### OCRとは
OCRとは「Optical Character Recognition」の略で、光学式文字認識を指します。
今回のデモアプリでは、Clientのブラウザからサーバに画像ファイルをアップロードする事で、画像内から文字列を読み取ります。

### PaddleOCRとは
80言語以上をサポートしているPythonの軽量OSSライブラリです。
トレーニング済みのモデルが公開されてるため、すぐにOCRを試すことができます。

## 本編
### アーキテクチャ
フロントからバックエンドに画像を投げ、バックエンドで非同期にOCRを実行します。実行ステータスはバックエンドのPostgreSQLで管理し、フロントはポーリングで実行完了を待ちます。

**フロントエンド**
　Next.js + React

**バックエンド**
　KtorのWebアプリ + PythonのOCRロジック実行環境


### 環境
OSは一応Windowsですが、Linuxでも動作すると思います。
主なフレームワーク/ライブラリのみ記載します。

**フロントエンド** 
- Next.js / React： 14.2.15
- typescript： 5
- tailwind css： 3.4.14

**バックエンド** 
- ktor： 2.3.12
- exposed： 0.53.0
- postgres： 14.6
(下記よりpython環境)
- python： 3.9.6　
- PaddleOCR: 2.7.0.3
- paddlepaddle: 2.5.2
- loguru: 0.7.2

### ソースコード
GithubのRepositoryを参照ください。
OCRロジックは、バックエンドのリソースに含まれるocr_demo.pyに実装してます。このファイル単体でもOCRを試せます。
[フロントエンド](https://github.com/rioaki777/ocr-demo-frontend)
[バックエンド](https://github.com/rioaki777/OcrDemoBackend)

### OCRロジック
```python:ocr_demo.py (一部抜粋)
def run_ocr(img_path):
    # 画像パスからnumpy配列へ
    img = Image.open(img_path).convert('L')
    np_img = np.asarray(img)
    
    paddleOCR = PaddleOCR(
        use_gpu=False, # GPUは使用しない
        lang = "en", # 今回は英語の学習モデル
        det_limit_side_len=img.size[1], # 画像の圧縮防止
        max_text_length = 20, # 検知する文字列長
        show_log=False
        )
        
    result = paddleOCR.ocr(img=np.asarray(np_img), det=True, rec=True, cls=False)
    if not result or result[0] is None:
        logger.debug(f'not detected.')
        return
    
    #　検知文字列を画像に入力
    output_img = cv2.cvtColor(np_img.copy(), cv2.COLOR_BGR2RGB)
    for detection in result[0]:
        top_left = tuple([int(i) for i in detection[0][0]])
        bottom_right = tuple([int(i) for i in detection[0][2]])
        bottom_left = tuple([int(i) for i in detection[0][3]])
        found_text = detection[1][0]
        
        output_img = cv2.rectangle(output_img, top_left, bottom_right, (0, 255, 0), 1)
        output_img = cv2.putText(output_img, found_text, top_left, cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 1, cv2.LINE_AA)

    # 結果画像保存
    output_img = cv2.cvtColor(output_img, cv2.COLOR_RGB2BGR)
    cv2.imwrite(img_path, output_img)
```

### Demo
![OcrDemo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463879/55e743aa-a5a8-8d73-b69f-696ef4b9104c.gif)

### 躓いた点
PaddleOCRの実行環境構築に苦戦しました。
python・paddleocr・paddlepaddleのバージョンで、互換性の無い組合わせがあります。
[本家のGithub](https://github.com/PaddlePaddle/PaddleOCR)に従っても良いのですが、私は下記の組合せで動作確認してます。
```
※python 3.9.6
python -m pip install paddleocr==2.7.0.3
python -m pip install paddlepaddle==2.5.2 -i https://mirror.baidu.com/pypi/simple
```


### 蛇足
今回の仕様/コードでは、フロント・バックを分けてNext.jsを用いる意味が全くないのですが・・・
この構成をサッと試せる環境を作っておきたかったです。
環境設定: Windows画面キャプチャアプリを変更（そしてCtrl (Left) + \にRemapします）

Kali Linux環境でFlameshotを利用しており、Ctrl (left) + \ でスクリーンショットを取る設定にしています。ここでは、同様の設定をWindowsで利用する設定を紹介します。

環境： Windows 11 Professional

設定変更の目的：スクリーンキャプチャアプリをFlameshotに変更して、Print Screen ボタンではなく、Ctrl (Left) + \キーに変更すること。

# Flameshot
Flameshotはオープンソースのスクリーンショットアプリで簡単にスクリーンショットが取れ、追加書き込み等も同時に可能なので、私は既存のスクリーンショットアプリケーションとして愛用しています。

https://flameshot.org/

こんな感じでスクリーンショットの画像に直接書き込めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283070/1251c4ed-aca2-2e36-3776-37ab6c3a17c8.png)

# Windows 11で設定

- Flameshotの最新版をインストール  (現在(10/31/2024)ではv12.1.0が最新)

- PowerToysの最新版をインストール  (現在(10/31/2024)ではvv0.85.1が最新) 

https://github.com/microsoft/PowerToys

- Windows OSから"設定"->"キーボード"を選択し、"PrintScreen キーを使用して画面キャプチャを開く"をオフ　（写真は英語です）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283070/13a3c938-aa5d-c060-f066-6619ebfbb711.png)

- PowerToysを立ち上げて、Keyboard ManagerからShortcutsを選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283070/e3317b51-d749-40fc-c48b-77efa3cb0089.png)

詳細の設定は、以下になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283070/8c06ebec-36d5-8142-5f30-38d2713c8b58.png)

-最後にスタートアップのアプリにFlameshotを追加

このステップは以下のサイト等がわかりやすいです。

https://atmarkit.itmedia.co.jp/ait/articles/2308/28/news025.html

これで再起動すれば、自動的にFlameshotが起動して、Ctrl + \でスクリーンショット選択画面がでるはずです。

今回は以上になります。:grin:

Happy Hacking!
【新卒1年目】合格記① AZ-900 Microsoft Certified: Azure Fundamentals 

# はじめに
入社してから３ヶ月経ち仕事というものに慣れてきました。
社会人になり、初めての試験合格！**AZ-900合格！**
勉強録として記します。

# AZ-900 Microsoft Certified: Azure Fundamentalsとは
" クラウドの概念、Azure のコア サービス、および Azure の管理とガバナンスの機能とツールに関する基本的な知識を示します。 "

https://learn.microsoft.com/ja-jp/credentials/certifications/azure-fundamentals/?practice-assessment-type=certification

# 使用した教材

■ 合格対策Microsoft認定 AZ-900:Microsoft Azure Fundamentals テキスト＆問題集第2版
Amazon : https://amzn.asia/d/80ez7me 

■ Udemy「最短で合格！Azure Fundamentals AZ-900 試験対策問題集」
https://www.bing.com/ck/a?!&&p=c1fa7dc1c210ef1e67e84a0c9da50af59de1a04677f4818e4c308eb61508a5b5JmltdHM9MTczMDI0NjQwMA&ptn=3&ver=2&hsh=4&fclid=12549f4b-4c21-60a2-30e4-8b1f4de86166&psq=%e6%9c%80%e7%9f%ad%e3%81%a7%e5%90%88%e6%a0%bc%ef%bc%81Azure+Fundamentals+AZ-900+%e8%a9%a6%e9%a8%93%e5%af%be%e7%ad%96%e5%95%8f%e9%a1%8c%e9%9b%86&u=a1aHR0cHM6Ly93d3cudWRlbXkuY29tL2NvdXJzZS9taWNyb3NvZnQtYXp1cmUtYXo5MDAtZXhhbS8&ntb=1

# 学習方法
私の勉強方法なので合う合わないあると思います。悪しからずご了承ください。
### １． **インプット(約1.5時間)**

合格対策Microsoft認定 AZ-900:Microsoft Azure Fundamentals テキスト＆問題集第2版 を使用
流し見をする。ここでは内容を深く理解することよりも素早く全体に目を通すことを重視
これ以降は教科書的役割として使用

### ２．**アウトプット（約9時間）**
Udemy「最短で合格！Azure Fundamentals AZ-900 試験対策問題集」 を使用
演習テスト1-5において9割が取れるまで繰り返す。
手順は以下の通り
#### ①演習テスト実施
即答した問題 ->　何もせず
少しでも迷った問題 -> マークをつける
#### ②振り返り
演習終了後では「マークしていないが誤答」,「マークした問題」の２つで振り返りを行う。
解説を読みテキストで用語の確認を行う。このとき、そのページに**付箋を貼る**。

#### 隙間時間
テキストにて、付箋を貼ったページを読む。付箋の数が多いページほど優先度は**高**

# いざ受験
オンライン試験（自宅）にて受験しました。
日本語の試験監督を選択すると受験時間が限られてしまいますが、英語の試験監督を選択するとAM3:00でも受験ができます。枠数も多いため、好きな時間に受けることができオススメです。日常会話レベルの英語力でOKでした

# 結果
2024/7/8
合格点：700
得点：826

# まとめ
急ぎでさくっと資格取りたい方はUdemyのみで良さそうです。
テキストでインプットすることによって、イメージが付きやすくなると思います。
好みだと思います:)
Search GPT を試す

# イントロ
Search GPTはChatGPTのWebUI上で検索ができる機能です。
Search GPTがGA?されたので試します。執筆時点ではChatGPT Plusユーザーのみのようです。

# 使い方
新しく導入された地球儀のマークを押していつものようにメッセージを送信するだけです。
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698823/8f65c230-d487-2c6c-9fba-9723784a1841.png)

例えば「Generative AIとは？」と打ってみるとこんな感じです。
文章の末尾にあるマークは生成文の引用元のようです。クリックしてそのページに飛ぶことができます。レスポンスはかなり早かったです。（体感1秒未満）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698823/2783559c-4699-b237-8b9a-42ef2c6b56f2.png)

最下段の「情報源」をクリックすると，引用だけでなく検索結果も表示できます。気になったので同様のワードをGoogleとBingで検索してみたのですが，比較的Bingの検索結果のほうがSearchGPTの「検索結果」に近かったです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698823/dd086d06-212e-08cf-0e28-12b5e4824d78.png)

# リリース文
リリース文を最後（How it works and what comes next）を翻訳すると以下のようになっていました。OpenAIのアセットを組み合わせて実現したみたいですね。
> 検索モデルは GPT-4o の微調整バージョンであり、OpenAI o1-preview からの出力を抽出するなど、新しい合成データ生成技術を使用して事後トレーニングされています。 ChatGPT 検索は、サードパーティの検索プロバイダーと、パートナーから直接提供されるコンテンツを利用して、ユーザーが探している情報を提供します。
SearchGPT プロトタイプからのフィードバックのおかげで、私たちは SearchGPT エクスペリエンスの最高のものを ChatGPT に取り入れました。私たちは、特にショッピングや旅行などの分野で検索を改善し続け、OpenAI o1 シリーズの推論機能を活用してより深い調査を行う予定です。また、将来的には、新しい検索エクスペリエンスを Advanced Voice と Canvas、さらに Free ユーザーとログアウト ユーザーにも提供する予定です。

引用元はこちら↓
https://openai.com/index/introducing-chatgpt-search/
[tips][cpp] std:: basic_string:: substr

tipsです。ほぼ標準リファレンスの輪読ですが、ここでは部分文字列を取得する問題など、問題解きやすくするtipsも込めています。

# std::basic_string::substr

`substr()`
第一引数 pos：開始位置
第二引数 npos：開始位置からの要素数

要件:
pos <= size()

もし、npos > size() - posならば、size() - posが指定される。
つまり、指定した開始位置からの要素数が、元の配列の末尾までの要素数を上回ると、末尾までの要素を指定したものとして値が返される。

**文字列内で、インデックス(i, j]の部分文字列を切り出したい場合は、`substr(i, i-j)`とすれば良い**


例
```cpp
#include <iostream>
using namespace std;
int main(){
    string a = "abc";
    // 0番目から要素数を指定する場合
    cout << a.substr(0, 2) << endl; // ab
    cout << a.substr(0, 3) << endl; // abc
    // １文字取り出す場合
    cout << a.substr(2, 1) << endl; // c 
    // 末尾の要素までの要素数を超える場合
    cout << a.substr(2, 4) << endl; // c 
}
```

output
```cpp
ab
abc
c
c
```
Kaigi on Rails 2024に参加して、自分なりのカンファレンスの楽しみ方が見えてきた気がする

# Kaigi on Rails 2024に参加してきました

Gakken LEAPで働いていますkoboriです。普段はShikaku Passというオンライン学習サービスの開発を行っております。2024/10/25~26で開催されたKaigi on Rails 2024というテックカンファレンスへ現地参加してきました。

今年の5月のRubyKaigi2024へは会社制度を利用して参加し、本ブログのエントリーとしても公開しました。今回も会社制度を利用することはできたのですが、個人の趣味として参加することにしてみました。今回参加してみて、趣味ゆえの気楽さによるものか、自分なりのカンファレンスの楽しみ方が見えてきた気がしています。今回は、その過程で試したことや考えたことを中心に記事にしていきます。恐らく、私と同じようにカンファレンス参加経験が浅い方に向けた内容になるかと思います。

また、最初に断りを入れておきますが、この記事は会社支援による参加を否定する意図もなければ、個人の趣味による参加を推奨する意図もありません。よろしくお願いします。

# Kaigi on Rails 2024の感想

非常に大事なことなので最初に書いておきます。Kaigi on Rails 2024では、学びと楽しい思い出の両方を持ち帰ることができ、参加して本当によかったです。

# カンファレンス参加における課題感

過去に何度かカンファレンスに参加したことがあり、いずれも楽しい思い出ではあるのですが、モヤモヤした感覚を持っていました。具体的には、聴講した発表を自分の中で整理しきれていない、圧倒的な疲労感、などです。

とはいっても、カンファレンス参加は私にとって楽しいイベントであることは間違いはないので、これらに対して何か策を講じなければ、ということで今回は臨みました。

# 発表は多少気楽に聞いた方が良いなと

私の場合、特に会社の支援を受けて参加する際、仕事という意識によるものか、「何か持って帰らねば」、「テックブログに書かねば」という気持ちが強くなります。そうなると、発表内容を逃さぬようメモをきっちり取らねば、少しでも多くの発表を聞かねばという行動に繋がります。

このスタンスを2日間続けようとすると、私の場合、内容も頭で整理しきれず、体力は尽きるという結果になりがちです。

月並みですが、やはりメモを取ることに一生懸命になりすぎると、書いている間に次の話を聞き逃してしまうことが多々あります。発表が終わり、完成したメモを見返してみると、スライドの内容をそのままコピーしたのか言わんばかりのメモと疲労感が残ります。一方で、頭にはそれほど残っておらず、後から公開された資料を一生懸命見返します。困ったものです。
他の参加者の方を見ていると、メモを取りながら上手に聞いている方も多くいるように思います。しかし、私はそれほど頭が良くないのだったと思い直すのでした。

それではどうするか。メモは取れる状態は整えつつ、基本メモを取らないスタンスを取ることにしてみました。特に印象深い部分、資料に対する口頭での補足、関連するリンク等に絞り、メモに残していきます。なんだか学生時代を思い出します。

加えて、発表内容を頭に残すための策として、発表内容とその場の体験を結びつけることを考えました。
私はPodcastを聞くことが好きで、家事や散歩をする際によく聞いています。Podcastを聞く際はもちろんメモなど取らないわけですが、ふとした瞬間に聞いた内容を思い出すことがあります。そしてそれは、「〇〇を歩いてる時に聞いた△△ね」と、その場の体験とセットで思い出されます。
カンファレンスでも同じように、体験との関連付けを考えてみました。具体的には、会場の景色、発表者のメタ情報、発表中の参加者の反応などが挙げられます。また、発表者の話し方や立ち居振る舞いも要素として考えられます。発表者の表情が見える距離で聴講することを徹底しました。

メモではなく発表内容に集中できることで、「自身の業務のここに活かせそう」などと考える余裕が生まれることも圧倒的に増えてきました。

実証することは難しいですが、これまでの自身の体験と比較して、かなり効果のある改善行動だった感じています。

# (特に)良かった発表

今回聴講した発表についても触れておきます。どれも学びがあって本当に良かったなと思いつつ、[osyoyuさんの発表](https://kaigionrails.org/2024/talks/osyoyu/)が、特に印象深かったです。

中でも、1プロセスあたりのスレッド数を増やしすぎることによるパフォーマンス悪化について、実際に計測したベンチマーク結果を踏まえて説明されている点は特に学びが大きかったです。私自身、GVLの制約という概念を知らなかったので、システムに対する解像度が少し上がりました。

トーク内でおすすめされていたRailsガイドのページも非常に勉強になって良かったです。発表資料では英語版が紹介されていたと記憶していますが、日本語訳ページを読んだのでリンクを貼っておきます。

https://railsguides.jp/tuning_performance_for_deployment.html

また、その後の島田さんの基調講演内の「システムコンポーネントを知る」話を聴きながら、こういう体験の積み重ねなのだろうなと思い返していました。(基調講演内でも触れられていました)

今週出勤して早速、担当しているサービスのI/O比率とPumaのスレッド数を確認してみました。現時点で修正は不要な設定となっているようでしたが、「この数値であること」の根拠を持つことができるようになりました。

印象に残ったという意味では、[Palkanさんの発表](https://kaigionrails.org/2024/talks/palkan/)は、確実に良い話をしていたことは分かりつつ、20%程度しか聞き取れなかったことに悔しさが残っています。みなさんの参加レポートとアーカイブ動画を見直して、出直そうと思っています。

RubyKaigiの時もそうですが、英語がある程度できるとカンファレンスは今より数倍楽しくなりそうだなと毎度思います。

# まとめ

来年はどういう形で参加するかは分かりませんが、今回得た教訓をもとに、また気楽に参加しようと思います。
また、聞くばかりではなくプロポーザルを提出して、次なる課題を見つけられると良いなと思っています。

# エンジニア募集

Gakken LEAP では教育をアップデートしていきたいエンジニアを絶賛募集しています。
ぜひお気軽にカジュアル面談へお越しください。

https://gakken-leap.co.jp/recruit/
Railsでパンくずリストを作成する方法 - Gretelを使った実装

# はじめに

こんにちは！
記事をご覧いただきありがとうございます。

本記事では、Railsでパンくずリストを作成する方法についてまとめます。

# 目次
1. [パンくずリストについて](#パンくずリストについて)
1. [手順](#手順)
1. [書き方](#書き方)
4. [最後に](#最後に)


# パンくずリストについて
パンくずリストとは、Webサイトの現在のページの位置を示すナビゲーションリンクのことです。ユーザーがどのページにいるかを視覚的に把握できるため、サイトのユーザビリティが向上します。今回は、Rails用のGretelというGemを使って、パンくずリストを簡単に実装する方法を以下にまとめています。


# 手順
#### Gretelをインストールする

まず、GretelをGemfileに追加し、インストールします。
```ruby
# Gemfile
gem 'gretel'
```
インストールコマンドを実行します。
```
bash
$ bundle install
```
#### 設定ファイルを作成する
次に、以下のコマンドを実行して、config/breadcrumbs.rbファイルを作成します。
```
bash
$ rails g gretel:install
```
このファイルに各ページのパンくずのパスを定義していきます。



# 書き方
### パンくずリストの定義
`config/breadcrumbs.rb`内にページごとのパンくずを設定します。

```ruby
# config/breadcrumbs.rb

# ホームページ
crumb :client_root do
  link 'ホーム', root_path
end

# トップページ
crumb :top do
  link 'トップ', top_path
  parent :client_root
end

# one
crumb :client_ads do
  link '詳細1', new_one_path
  parent :top
end

# two
crumb :manual_ads_input do
  link '詳細2', new_two_path
  parent :top
end

```

### ビューに表示する
ビューにパンくずリストを表示するには、レイアウトファイルや個別のビューに以下のコードを追加します。

```haml
= breadcrumbs separator: " > "
```


# 最後に
最後まで読んでいただきありがとうございました。Gretelを使えば、設定ファイルで各ページのパスを定義し、ビューで呼び出すだけで、ユーザーにわかりやすいナビゲーションを提供できます。
この記事を読んでRailsでパンくずリストを作成する方法について理解が深まれば幸いです。
休憩ってどうしてる？

エンジニアである以上、休憩が大切なのはわかります。
ただ自社で周りが仕事している環境で休憩するのって難しくないですか？

休憩に関する記事を読んでいて思ったので、いい方法があれば教えてください。
https://ntietz.com/blog/integrate-rest-into-your-work-and-practice/?ref=dailydev

自分の目標は、午前/午後のそれぞれ半分の時間に、５分ほど休憩しようと思います。
午前：10:30～10:05
午後：15:00～15:05
Ubuntuでchromeが強制終了して開けなくなった対処

# UbuntuでGoogle Chromeの「プロファイルが他のプロセスで使用されています」エラーの対処法

Ubuntu上でGoogle Chromeを開こうとした際、「プロファイルが別のプロセスで使用されています」というエラーメッセージが表示され、Chromeが起動できない場合の対処法について解説します。

## 前提

```bash
$ cat /etc/os-release 
PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.5 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy


$ cat /home/user-ubuntu/.config/google-chrome/Last\ Version 
130.0.6723.91
```

## エラーの詳細
このエラーは、Google Chromeのプロファイルが別のChromeプロセスで使用中と認識され、Chromeがプロファイルをロックしている際に発生します。具体的なエラーメッセージは以下のようなものです。

```bash
$ cat grep -i error /var/log/syslog

Nov 1 05:37:16 user-ubuntu google-chrome.desktop［4271］：［4264:4264:1101/053716.973363:ERROR:process_singleton_posix.cc （358）］プロファイル
は、別のコンピュータ（user-ubuntu）の別のGoogle Chrome プロセス（62289）で使用されているようです。Chrome はプロファイルが破損しないようにプロファイルをロックしています。他のプロセスがこのプロファイルを使用していないことが確実であれば、プロファイルのロックを解除してChromeを再起動できます。
```
### プロセスは無さそう
```bash
$ ps aux | grep 62289      #syslogにあったプロセスID
（何も表示されない）
```
psでプロセスが確認できればkillコマンドでプロセスをkillしてください。
今回は表示されなかったので次へ。

## 対処法
以下のファイルを削除し、chromeを再起動させました。
```bash
$ ls -l /home/user-ubuntu/.config/google-chrome/Singleton*
```
- `SingletonLock`
- `SingletonSocket`
- `SingletonCookie`

## 原因
Chromeのプロファイルが異常終了やクラッシュなどによって適切に終了できなかった場合、以下の一時ファイルが残ることがあります。

- `SingletonLock`
- `SingletonSocket`
- `SingletonCookie`

これらのファイルは一時的なロックファイルのため、削除しても問題ありません。ただし、削除前にChromeが完全に終了していることを確認してください。
Xcodeのシミュレータで選択したい機種が表示されない件

# はじめに
iOS18にした後（正確な時期は定かでないが）Xcodeのシミュレータの機種を選択する際に一部機種が表示されなくなって、経験が浅い私は原因がわからず調べても解決方法が見つけられなかったので忘れないように偶然上手くいった解決方法を記録しておきます。
同じ様な方がもしいらっしゃった場合への手助けになればと思います。
# 症状
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3867211/88b64a2c-013e-5d5e-95c6-3111359c884d.png" width = 60% >

普段はiphone12miniを使用しておりシミュレータに見当たらない（「ワイphone12」というのは私の実機の名前なのでスルー…）
# 解決策
.xcodeprojet>General>iOSに行く
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3867211/60791940-f17e-183e-0a2d-592a802ca198.png" width = 60%>

ここでiOS17に下げるのだが

<img width = 60% src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3867211/ad46d153-97bf-b7c8-4db7-d045c8502806.png">

その時には自動で**iOS17.6**が選択されてしまいこれではシミュレータに表示されませんでしたので手動で変更したら上手く行きました
> **:warning: 注意**
> この時にiOS17.5に手動で変更してください

# 結果
<img width = 60% src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3867211/cf5f9060-48ae-b352-13c3-dfdc8017574c.png">

機種の右側に「**17.5**」とあるように無事、iPhone12miniなどの過去の機種が選べる様になりました。
なんとか解決…
# おわりに
こんな形で終わるのは申し訳ないのですが、なぜ上手くいったのかは現状わかっていませんがとりあえず解決策です。
詳細が判明次第追記致します。
PythonでのLiteral型エラー解決方法まとめ：mypyエラーを解決するためのアプローチ

## はじめに
Pythonでは厳密な型定義を実施することが不可能であり、完全な型安全性を確保することはできません。しかし、型ヒントを用いることである程度の型安全性を確保することができます。型ヒントの一つである`Literal`を用いた際に`mypy`のエラーに遭遇し、その解決にすこし手間取ったので備忘録として残しておきます。  

## 問題となるコード
```python
from typing import Literal

var_type = Literal["apple", "orange"]

def func(var_type: var_type) -> None:
    print(f"This is the {var_type}")

fruit = "apple"
func(fruit)  # mypyエラーが発生
```
上記のコードを`mypy`でチェックすると以下のようなエラーが発生します。
```python
error: Argument 1 to "func" has incompatible type "str"; expected "Literal['apple', 'orange']"  [arg-type]
```
`fruit`は`apple`が格納されており、Literalの中身と一致しているため一見問題なさそうです。しかし`fruit`事態の型は`str`であるために`mypy`のチェックに引っかかってしまいます。

## 解決策
この問題を解決するための方法をいくつか検討しました。
### 1. `TypeGuard`を使ったチェック関数の導入
Python 3.10以降では`TypeGuard`を使用して、変数がリテラルのいずれかの値であるかを動的にチェックできます。これにより、動的な型チェックが可能になり、エラー処理が柔軟に行えます。ただし、チェック関数が必要となり、コード量が増加するというデメリットもあります。
```python:
from typing import Literal, TypeGuard

var_type = Literal["apple", "orange"]

def is_var_type(value: str) -> TypeGuard[var_type]:
    return value in ("apple", "orange")

def func(var_type: var_type) -> None:
    print(f"This is the {var_type}")

fruit = "apple"

if is_var_type(fruit):
    func(fruit)  # エラーなし
else:
    print("Invalid fruit type")

```

### 2. `Annotated`で型情報を追加する
Python 3.9以降で使用できる`Annotated`を用いて、str型でありながら特定のリテラル値だけを許可する型を定義できます。
```python
from typing import Literal, Annotated

VarType = Annotated[str, Literal["apple", "orange"]]

def func(var_type: VarType) -> None:
    print(f"This is the {var_type}")

fruit = "apple"  # 事前に値の検証を行うと安全

func(fruit)  # mypyでのエラーは発生しない
```

### 3. `Final`を使う
変数に`Final`を使用すると、変更不可の定数として宣言でき、`mypy`エラーを回避できます。

```python
from typing import Final, Literal

var_type = Literal["apple", "orange"]


def func(var_type: var_type) -> None:
    print(f"This is the {var_type}")


fruit: Final = "apple"
func(fruit)
```


### 4.別ファイルで型定義を行い、インポートして使用
型を別ファイルに定義しておくと、再利用性が高まり、型管理が簡潔になります。

```python:type.py
from typing import Literal

VarType = Literal["apple", "orange"]
```

```python:main.py
from .types import VarType

def func(var_type: VarType) -> None:
    print(f"This is the {var_type}")

fruit:VarType = "apple"
func(fruit)  # エラーなし
```

## それぞれの方法ごとの利点・欠点のまとめ


|方法|利点|欠点|
|:----|:---|:--|
|TypeGuardを使う|・動的な型チェックが可能<br>・エラーハンドリングの柔軟性が向上|・チェック関数の追加でコードが長くなる|
|Annotatedを使う	|・型ヒントが簡潔に	|・mypyのサポートが不完全な場合がある<br>・型チェックが不十分な場合がある|
|Finalを使う	|・定数としての意味合いが明確<br>・mypyの型チェックが簡単に通る	|・動的な値に適用が難しい<br>・他の用途には適さない|
|別ファイルで型定義	|・型の再利用性が向上<br>・大規模プロジェクトで保守しやすい|	・小規模プロジェクトでのオーバーヘッド<br>・型定義用のインポートが必要|


## まとめ
どの方法が適切かは、プロジェクトの規模や求める柔軟性に依存します。選択肢の中から最適な方法を見つけることで、型ヒントを活用し、コードのロバスト性を高めることができるでしょう。これらの方法を使いこなせるようになりたいですね。
【PostgreSQL】Serial型の自動採番を経ずにデータ投入した際、次回以降のINSERTで一意制約違反になるのを回避する

## 概要
Serial型カラムを持つテーブルにCSVインポートなどで直接データを投入した場合、シーケンス値が更新されず、次回のINSERT時に一意制約違反が発生します。そこで、次回の採番を`既存データのMAX値 + 1`の値からスタートするよう変更して、次回以降のINSERT処理で一意制約違反が起こらないようにします。

| 項目 | 内容 |
|------|------|
| **取り扱う内容** | • Serial型カラムを持つテーブルへの直接データ投入時の問題と解決方法 |
| **想定読者** | • CSVインポートやデータ移行作業を行う人<br>• Serial型カラムを含むテーブルを管理・利用する人<br>• データインポート後の一意制約違反で困っている人 |
| **ゴール** | • データ投入後のシーケンス値の不整合を理解できる<br>• 一意制約違反を未然に防ぐための対処方法を習得できる |

## Serialの自動採番を経ずにデータ投入した場合、Serial型の持っている連番のシーケンスは自動更新されない
以下のケースでは、Serial型の持っている連番のシーケンスは自動更新されません。
* CSVファイルからデータインポートした場合
* Serial型の列にsql文で直接、値を指定してinsertした場合

このまま新規データをinsertすると、CSVに存在した値を無視して採番処理され、ユニーク制約に違反する場合があります。

例：
1. createしたばかりのテーブルにおいて、Serial型として定義した`id`列の次回採番は1
1. ここへ`id`が100まで登録済のCSVをインポートした場合でも、`id`列の次回採番値は1のまま、変更されない
1. 次回新規レコードがinsertされる時の`id`は101となってほしいが、2のために、実際には1が採番される
1. 結果として、`id`列にユニーク制約がある場合、制約違反となってinsertエラーとなる

## 次回の採番を`既存データのMAX値 + 1`の値からスタートするよう変更する
```sql
-- <>内の値はテーブル定義に応じて変更のこと
SELECT setval('<tablename>_id_seq', (SELECT MAX(<Serial型の列名>) FROM 
<tablename>));
```

テーブルに対応するシーケンスの値を見ると、既存値のMAX値で`最大の値`が更新されています。次回insert時は、この値に1を加算した値で採番されるので、一意制約違反は発生しません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3906608/489c5d8e-1615-4b14-d37d-ef0c9a40e867.png)
von Bertalanffy式をタイムステップごとに簡便に逐次計算するための式

## 背景
先日、とあるPythonのライブラリを見ていたところ、von Bertalanffy式についての記述でしばし悩んだので備忘録がてら書き残しておきます。

### 前提
von Bertalanffy式 (VBGF)とは主に生物（特に魚類など）の成長をモデル化するために用いられる式で、以下のような形をしています。
```math
L(a) = L_\infty - (L_\infty - L_0)e^{-ka}
```
この式は生物のエネルギー収支に関する仮定を元に導出することができ、
- $ L(a) $はとある齢$ a $における体長
- $ L_0 $は$ a = 0$における体長
- $ L_\infty $は齢$ a $ が$ a \rightarrow 0 $のとき、体長が漸近する値
- $ k $はgrowth coefficientといい、成長率そのものを表すパラメータではないのですが、ざっくりと、生物がどれくらい急激に大きくなるかを制御するためのパラメータです。

#### 参考
- [ベルタランフィー成長式の理論的導出](https://m-kiyota.jimdofree.com/データ解析/rとデータ解析のメモ/vbgfの理論的導出/")
- [Growth (von Bertalanffy) Notes](https://derekogle.com/NCNRS349/modules/PREP/NOTES/Growth)

## 問題の式
問題の式は、ある年齢$ a $における体長$ L(a) $を表すvon Bertalanffy式を用いて、タイムステップ`dt`ごとにイテレーションを回す中で、次のタイムステップにおける体長を逐次的に求めるコードの中に現れました。
```python
def von_bertalanffy_01(Length, l_inf, dt):
    Length = Length + (l_inf - Length) * k * dt
```
`Length` を、$L_\infty$、$k$、およびタイムステップ幅`dt`を用いて更新しているのですが、一見上記の定義の式とは異なる形をしています。特に微分をした形跡もないのに$e$の肩に乗っていた$k$が降りてきてるあたりでちょっと戸惑いました。

そもそも、時刻`t`における年齢$ a $がわかっているのであれば、わざわざ現在の体長を使って逐次的に計算しなくても上記のvon Bertalanffy式をそのまま突っ込んで、

```python
def von_bertalanffy_02(a, l_inf, l_0, k):
    Length = l_inf - (l_inf - l_0) * exp(-k * a)
    return Length
```
とでもすれば良さそうです。
この式の理解にしばし悩んでしまったため、備忘録がわりに導出をメモすることを決めました。

### 導出
最初のvon Bertalanffy式の定義式を$ L_0$ について解いて、
```math
L_0 = e^{ka} L(a) - e^{ka} L_\infty + L_\infty
```
次に、年齢$a$から$\Delta t$だけ進んだ時刻における体長を$L(a+\Delta t)$として、差分を取って整理すると
```math
L(a+\Delta t) - L(a) = e^{-ka}(L_\infty - L0)(1-e^{-k \Delta t})
```
ここに$L_0$について解いたものを代入すると、
```math
\displaylines{
\ L(a+\Delta t) - L(a) = e^{-ka}(L_\infty - e^{ka} L(a) + e^{ka} L_\infty - L_\infty)(1-e^{-k \Delta t}) \\
\therefore L(a+\Delta t) = L(a) + (L_\infty - L(a))(1-e^{-k \Delta t}) \\
}
```
を得ます。式から$e^{-ka}$が消えているので、明示的に年齢を与える必要はすでになくなっているのですが、ここで関数$f(x) = 1 - e^{x}$について、$x \rightarrow 0$のとき$f(x) \approx x$であることを用いると、$1-e^{-k \Delta t} \approx k \Delta t$と書けることから、
```math
L(a + \Delta t) = L(a) + (L_\infty - L_(a))k \Delta t
```
を得ます。この式の$L(a)$は実際には$a$が不明の場合でもとにかく計測するなり仮定するなりすれば与えることが可能なので、年齢を明示的に与える必要がなくなっています。

実際に、von Bertalanffy式のパラメータ（とくに$k$）が年齢$a$について推定されている場合、タイムステップ$\Delta t$が1年よりも十分に小さなスケールであれば（例えば1時間後、あるいは1日後）上記の式は成り立つため、それくらいの時間幅で逐次的に計算する分には、ある程度はこのくらいの近似でも問題なさそうです。

## おわりに
━━という理由で、私の出会ったコードはそういう形になっていたのかな、と想像したことのメモでした。どれくらいいい近似になっているかなどは確かめていないので、実際に使われる際はご自身の責任でお使いください。
WinSCard.ini から Keys.cpp を生成するなどを考えてみる

WinSCard.ini を Keys.cpp のフォーマットに成形して出力するだけのシェルスクリプトを考えてみました。ファイル出力はリダイレクトなどで良いんじゃないでしょうか？

使い方
WSCard2kcpp.sh [入力ファイル]

~~~sh:WSCard2kcpp.sh
#!/bin/sh

FILE=$1

# ファイルのゴミ取り
  fload() {
    sed 's/[^ -~0-9a-Z=#]//g' ${FILE}
  }

# 配列にデータを格納
  IFS=$'\n'
  BID=($( fload | grep 'BID' | sed -e 's/.*BID[ =:]*\([[0-9a-fA-F]\{2\}\)/\1/g'))
  SID=($( fload | grep '\[.*\]' | sed -e 's/\[\(.*\)\]/\1/g'))
  NID=($( fload | grep '\[.*\]' | sed -e 's/\[\(.*\)\]/\1/g' | sed -e 's/Star Channel HD/StarChannelHD/g' -e 's/SkyPerfecTV E2 \/ 110CS/E2_110CS/g' -e 's/Safenet/Safetynet/g'))
  KW0=($( fload | grep 'KW0' | sed -e 's/.*KW0[ =:]*\([0-9a-fA-F]\{2\}\)[ ,]*\([0-9a-fA-F]\{2\}\)[ ,]*\([0-9a-fA-F]\{2\}\)[ ,]*\([0-9a-fA-F]\{2\}\)[ ,]*\([0-9a-fA-F]\{2\}\)[ ,]*\([0-9a-fA-F]\{2\}\)[ ,]*\([0-9a-fA-F]\{2\}\)[ ,]*\([0-9a-fA-F]\{2\}\)[ ,]*\([0-9a-fA-F]\{2\}\)[ ,]*/0x\1, 0x\2, 0x\3, 0x\4, 0x\5, 0x\6, 0x\7, 0x\8, 0x\9,/g'))
  KW1=($( fload | grep 'KW1' | sed -e 's/.*KW1[ =:]*\([0-9a-fA-F]\{2\}\)[ ,]*\([0-9a-fA-F]\{2\}\)[ ,]*\([0-9a-fA-F]\{2\}\)[ ,]*\([0-9a-fA-F]\{2\}\)[ ,]*\([0-9a-fA-F]\{2\}\)[ ,]*\([0-9a-fA-F]\{2\}\)[ ,]*\([0-9a-fA-F]\{2\}\)[ ,]*\([0-9a-fA-F]\{2\}\)[ ,]*\([0-9a-fA-F]\{2\}\)[ ,]*/0x\1, 0x\2, 0x\3, 0x\4, 0x\5, 0x\6, 0x\7, 0x\8, 0x\9,/g'))

# includeファイル定義
  echo -e "#include \"Global.h\"\n#include \"Keys.h\"\n#include \"Keyset.h\"\n"

# Keysetブロックの配列出力
  for ((i = 0; i < ${#BID[@]}; i++)); do
    echo -e "static const Keyset_t Keyset_${NID[i]} = {\n\t0x${BID[i]},\n\t// keys\n\t${KW0[i]}\n\t${KW1[i]}\n\t\"${SID[i]}\",\n};\n"
  done

# フッターブロックの配列出力
  echo -e "s32\nBCAS::Keys::RegisterAll (void)\n{"
  for ((i = 0; i < ${#NID[@]}; i++)); do
    echo -e "\tBCAS::Keyset::Register (&Keyset_${NID[i]});"
  done
  echo -e "\n\treturn 0;\n}"
  ~~~
クロスアカウントアクセスでAWS CLIを叩くときにexportするのが面倒だったので簡易コマンドを

# クロスアカウントアクセスでAWS CLIを叩くときにexportするのが面倒だったので簡易コマンドをメモ



### ■結論

これを叩く

```sh
ROLE_ARN=arn:aws:iam::${アカウント番号}:role/${ロール名};
STS=$(aws sts assume-role --role-arn $ROLE_ARN --role-session-name SESSION | jq '.Credentials');
export AWS_ACCESS_KEY_ID=$(echo $STS | jq -r '.AccessKeyId');
export AWS_SECRET_ACCESS_KEY=$(echo $STS | jq -r '.SecretAccessKey');
export AWS_SESSION_TOKEN=$(echo $STS | jq -r '.SessionToken');
aws s3 ls;
```



### ■何がしたかったのか

自己学習のためにクロスアカウントアクセスを使って色々と実験したかった。

AWSアカウントAからアカウントBに対してAdministrator権限ポリシーをアタッチしたIAMロールをPassRoleし、AssumedRoleなアカウントBからAWS CLIやCDKを使用してあれやこれややりたかった。

問題は、アカウントBのCLI上での設定のめんどくささ。

アカウントBからAssumeRoleしたあとに表示されるSTSトークンの中身を見て、ローカルのCLIで環境変数を3つもexportしないといけない。しかも、SessionTokenは文字列が長いので範囲選択も地味にめんどくさい。

```sh
% ROLE_ARN=arn:aws:iam::${アカウント番号}:role/${ロール名}
% aws sts assume-role --role-arn $ROLE_ARN --role-session-name SESSION
{
    "Credentials": {
        "AccessKeyId": "AXXXhogehoge",
        "SecretAccessKey": "secretaccesskeyhogehoge",
        "SessionToken": "sessiontokenhogehoge",
        "Expiration": "2024-10-31T18:34:23+00:00"
    },
    "AssumedRoleUser": {
        "AssumedRoleId": "AYYYYhogehoge:SESSION",
        "Arn": "arn:aws:sts::XXXXXXXXXXX:assumed-role/test-role/SESSION"
    }
}

# ここから先の貼り付け作業がめんどくさすぎる、、、
% export AWS_ACCESS_KEY_ID=AXXXhogehoge
% export AWS_SECRET_ACCESS_KEY=secretaccesskeyhogehoge
% export AWS_SESSION_TOKEN=sessiontokenhogehoge
```

学習中にいちいちこれやっていたらたまらないので、サクッとコピって実験できるスクリプトにしたかった。



### ■なぜ記事を書き始めたのか

エンジニアになってから３年弱、ずっとインプットと開発中心でやっていた。

インプットに関しては過多な状態で、行き詰まりを感じていた。

完璧思考だったり必要以上に準備をしてから物事に臨むためにそのせいで成長が遅い傾向があったので、どんな些細なことでもいいので、たとえ完成度が低くても、形や出来にとらわれずにアウトプットを始めてみたかった。

これを読んでくださってるみなさん、この記事はまったく社会貢献になっていないだろう、半分は自分の日記みたいなもんだとわかっています。成長の場として始めたいと思いました！
数理ファイナンス入門: 無裁定とプットコールパリティ

# Abstract
無裁定(リスクなしで儲ける手段は存在しない)を公理として認めると、
「一物一価の法則」と「単一金利定理」(市場の銀行預金の金利はただ一つに定まる)を導けることをしめす.
また、同一の行使価格のコールオプション・プットオプションについて、単一金利定理と無裁定原理を適用し、「プットコールパリティの等式」を導出する.

# 裁定
$Val_{t}(X)$を時刻$t$における$X$の価値(つまり、金融商品$X$を買うときに払うお金)とする.
いま、期首を$0$,満期を$T$として、$t \in  \\{ 0,T \\}$とする. 

裁定とは、金融商品$X$が存在して「$Val_{0}(X) \leq 0$ かつ$Val_{T}(X) > 0$」を満たすことをいう.


(砕けた言い方をすれば、確率1で儲けることができること、をいう)
# 無裁定原理
次を公理とする
**[無裁定原理]**
**任意の金融商品$X$について裁定は生じない** $\dots (1)$
($\Leftrightarrow$ **任意の金融商品$X$について「$Val_{0}(X) > 0$ または$Val_{T}(X) \leq 0$」** $\dots (1)'$)

# 一物一価の法則
(1)より次が言える(証明は省略):
**[一物一価の法則]**
**任意の金融商品$X,Y$について「$Val_{T}(X) = Val_{T}(Y) \Rightarrow Val_{0}(X) = Val_{0}(Y) $」** $\dots (2)$

ペイオフが同じ金融商品同士は、現在価格も等しいことを言っている.

# 単一金利定理
市場の銀行を全て集めた集合を$G$とし、$i \in G$の貸し出し金利を$R_i$とする.
**[単一金利定理]**
$R_i = R_j (\forall i,j \in G)$

(証明)
$R_i \neq R_j$と仮定して背理法により示す.
「銀行$i$が発行する額面10万円の銀行預金」を$X_i$,
「銀行$j$が発行する額面10万円の銀行預金」を$X_j$
とする. 
$X_i,X_j$の現在価格は
$(Val_{0}(X_i),Val_{0}(X_j)) = (\frac{1}{1+R_i} Val_{T}(X_i),\frac{1}{1+R_j} Val_{T}(X_j))$ 
である.
ここで$Val_{T}(X_i) = Val_{T}(X_j)$と仮定すると、すぐ上の式と背理法の仮定より$Val_{0}(X_i) \neq Val_{0}(X_j)$となり、一物一価の法則(2)に矛盾.
したがって、$R_i = R_j$ □



つまり、市場の貸し出し金利はただ一つの値$R \in [0,1]$に定まる.

# プットコールパリティ
いま、市場に原証券として「マイクロソフトの株」「金利」があるとし、さらにマイクロソフトの株についてのコールオプションとプットオプション、合計4個の金融商品のやりとりについて考える.

**[マイクロソフトの株]**
マイクロソフトの株の価値を$S_t$とする.$t=0,T$のみ考えているので、現在価値が$S_0$、期末価値が$S_T$である.
**[金利]**
**単一金利定理より、市場の貸し出し金利はただ一つ**であり、それを$R$とする.
**[コールオプションとプットオプション]**
マイクロソフトの株から派生したコールオプションとプットオプションを考え、それぞれ$Call$,$Put$とする.
簡単のため,行使価格を同一とし、$K$と置く. 
コールオプションの現在価値(契約時の売値)を単に$C$と書く. つまり、$Val_{0}(Call) = C$とする. 満期の価値は$Val_{T}(Call) = \max(S_T - K,0)$である.
同様にプットオプションの現在価値を単に$Val_0(Put)=P$と書く.満期の価値は$Val_T(Put)=\max(K - S_T,0)$.



いまから、上述の**一物一価の法則**を用いて、$K,R,C,P,S_0$の間に成り立つ等式を導出することを考える.具体的には、金融商品として後述の2つのポートフォリオ$A,B$を考え、これらに一物一価の法則の命題「$Val_{T}(A) = Val_{T}(B) \Rightarrow Val_{0}(A) = Val_{0}(B) $」を適用する.

### ポートフォリオA : コールオプションを買って、銀行からお金を借りる
ポートフォリオAは以下の2つの金融商品の組み合わせである:
- A1: $C$円を払って上記のコールオプションを買う.
- A2: 銀行で「年利$R$,額面$\frac{K}{1+R}$円の銀行預金(債券)」を購入する

A2は、1年後の価値が$K$に等しくなるように銀行にお金を預ける＝現在価値$\frac{K}{1+R}$円を銀行に預ける、ということを考える.
すなわち「年利$R$,額面$\frac{K}{1+R}$円の銀行預金」を購入する.(同額の債権を購入すると考えても良い)

### ポートフォリオB:プットオプションを買って、マイクロソフトの株を買う
- B1: $P$円を払って上記のプットオプションを買う
- B2: 単純にマイクロソフトの株を$S_0$円分買う

### 価値の推移
各ポートフォリオの価値の推移は以下のとおりである
| A | t=0 |  | t=T |
|:-:|:-:|:-:|:-:|
|  価値 | $C+\frac{K}{1+R}$  | $\to$  | $\max(S_T - K,0) + K$  |
|   |   |   |   |

| B | t=0 |  | t=T |
|:-:|:-:|:-:|:-:|
|  価値 | $P + S_0$  | $\to$  | $\max(K - S_T,0) + S_T$  |
|   |   |   |   |

### 満期価値の一致による現在価値の一致
ここで、
$ \max(S_T - K,0) + K = \max(K - S_T,0) + S_T = \begin{cases} S_T & (S_T \geq K) \\\ K & (K \geq S_T) \end{cases} $
であり、ポートフォリオA,Bの満期価値が一致する。よって一物一価の法則により現在価格も一致するから、(価値推移表から)
$C+\frac{K}{1+R} = P + S_0$

が成り立つ. これが、プットコールパリティである.
# まとめ
- 無裁定原理を公理として置く
- 無裁定原理から**一物一価の法則**が従う
- 一物一価の法則から**単一金利定理**(市場に金利は単一)が従う
- 単一金利定理のもと、市場の単一の金利を$R$とする
- 株とそのコールオプション・プットオプションの現在価値、行使価格を$S_0,C,P,K$とする
- 上記の金利、株、そのコール・プットオプションを組み合わせて2種類のポートフォリオA,Bを上手に構成して、一物一価の法則を適用する
- プットコールパリティ$C+\frac{K}{1+R} = P + S_0$が従う
ABC377 B - Avoid Rook Attack 

## はじめに
10/26開催のABC377のB問題の復習です。

https://atcoder.jp/contests/abc377/tasks/abc377_b

## 問題文の概要
チェスボードのような$8 \times 8$マスの盤上にあらかじめ置かれているルークに取られないようにコマを配置できる場所を出力する。

## 自分の回答
最近はB問題なら最初に思いついた方法で強引に解くようになりました。
まず最初に閃いたのは、左上からマスを全部ループさせて、縦横ともにルークがいなければ$+1$する。という方法でした。

```python:ABC377_B
m = []
ans = 0
for _ in range(8):
  m.append(input())

ng_i = []
ng_j = []
for i in range(8):
  for j in range(8):
    if m[i][j] == "#":
      ng_i.append(i) # 横のルークの位置を格納
      ng_j.append(j) # 縦のルークの位置を格納

for i in range(8):
  if i in ng_i: # 横にルークが存在する
    continue

  for j in range(8):
    if j in ng_j: # 縦にルークが存在する
      continue
    else:
      ans += 1

print(ans)
```

## さらに考察
コンテストが終わり、
この問題は縦横でルークが存在しない場所を掛ければ答えになると気づきました。

```python:ABC377_B_2
m = []
for _ in range(8):
   m.append(input())

row_with_sharp = [False] * 8
col_with_sharp = [False] * 8

for i in range(8):
   for j in range(8):
       if m[i][j] == "#":
           row_with_sharp[i] = True
           col_with_sharp[j] = True

available_rows = 8 - sum(row_with_sharp)
available_cols = 8 - sum(col_with_sharp)

print(available_rows * available_cols)
```
最初に思いついた方の計算量は$O(n^4)$。修正版は$O(n^2)$なので速度面でも早いですね。

## おわりに
解法がいろいろあって、楽しい問題でした。
後追いでも思いついたものは必ず実装していきたいと思います。
swaggerについて

swaggerについて
--
swaggerとは
Swaggerは、APIを設計、視覚化、文書化するための一連のツールを指す総称
Swaggerツールは、API仕様を視覚化し、動作確認を容易にするほか、APIドキュメントの生成によってチーム内の共有や開発効率の向上を支援する

openapiとは
--
OpenAPIは、RESTful APIを定義するための業界標準の仕様
この仕様に基づき、APIの構造、パラメータ、レスポンスなどをJSONやYAML形式で記述する
OpenAPIに基づいたAPI仕様を作成するための支援ツールとして、Swagger EditorやSwagger UIなどが使用される

swaggerツールのコンポーネント
--
Swagger Editor：OpenAPI仕様に基づいたAPIを設計するためのエディタ
Swagger UI：OpenAPI仕様に基づいたAPIを視覚的に確認し、APIリクエストのテストを行うためのツール
Swagger Codegen：OpenAPI仕様からコードを自動生成するためのツール


[Swagger エディタとは何ですか?](https://lab.wallarm.com/what/swagger-%E3%82%A8%E3%83%87%E3%82%A3%E3%82%BF%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%A7%E3%81%99%E3%81%8B/?lang=ja)

OpenAPIの動作確認方法
--
１．Swagger UIのセットアップ手順は以下
[setup手順](https://github.com/swagger-api/swagger-ui/blob/HEAD/docs/development/setting-up.md)

２．開発サーバーを起動する
```bash
npm run dev
```

３．以下メッセージを確認できれば起動できている
```bash
<i> [webpack-dev-server] Loopback: http://localhost:3200/
webpack 5.94.0 compiled successfully in 96554 ms
```

４．ブラウザにてSwagger UIが表示されることを確認する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3146038/d4089c85-b6cc-e144-26c5-b226bba85d74.png)


５．/pet/findByStatusエンドポイントに対してHTTPリクエストを送信する
```bash
curl -X 'GET' \
  'https://petstore.swagger.io/v2/pet/findByStatus?status=available' \
  -H 'accept: application/json'
```

出力内容は以下
```bash
[
	{
		"id": 9223372036854775000,
		"name": "doggie",
		"photoUrls": [],
		"tags": [],
		"status": "available"
	},
	{
		"id": 9223372036854775000,
		"category": {
			"id": 0,
			"name": "string"
		},
		"name": "doggie",
		"photoUrls": [
			"string"
		],
		"tags": [
			{
				"id": 0,
				"name": "string"
			}
		],
		"status": "available"
	},
```

感想
--
次はもうひと手間加えたいです
[LLM]ノーコードという名の技術負債

## はじめに

これを読んでいるのはおそらくほとんどがエンジニアだと思う。

だからこそ言う、LLMを使ったプロダクトは負債にしかならない。

その根拠を挙げていこう

## LLMは所詮関数である

OpenAIなどのAPIを叩く場合は厳密にはHTTPリクエストだがそこは置いておいてコードで見れば関数の一部でしかない。

関数とか何なのかちゃんと考えてほしい。

関数はあくまでも引数を関数内で定義したアルゴリズムを通してアウトプットをするものだ。

ノーコードであるフローはknowledgeBaseなどの機能があるとは言え、あくまでも関数の繋ぎ合わせでしかない。

改めて冷静に考えて欲しい。

関数を作るだけに本当にノーコードが必要か？

## 関数単体でのアウトプットが品質保証されない

今はもしかしたら解決したいかもしれないがDifyなどは関数をフローで繋げて動作することを目的としているから基本的に細かくユニットテストなどは書けない。

普通に考えて見て欲しい。

「複雑なアルゴリズムの関数をテストなしで使うバカがいるか？」

Difyであろうとあくまでもプロンプトを入れて実行し、アウトプットを得ているのは間違いない事実。

それぞれの関数のアウトプットがただでさえ自由度が高いのにそれらの関数に対して出力テストをテストケースを作らず自動化していないとか正気の沙汰ではない。


## 簡単なものを難しくしてベンダーロックイン

awsのbedrockでもそうだがプロンプト管理のツールなども出てきている。

だが普通に考えただのバージョン管理機能のあるテキスト保存ツールでしかない。

それをそのままノーコードに使えると言うがそれをやれば間違いなくベンダーロックインされるだろう。


黙ってgitを使った方が良い。

## LLMのノーコードはプロパガンダ

本当に冷静に考えて欲しい。LLMのお陰でコーティングの難易度も実装スピードも上がりつつある。

そんななかノーコードは新規でましてや自社プロダクト開発エンジニアがいるのに使うのはデメリットしかない。

特にLLMをプロダクトに入れ込むことはやりたいことが決まっているなこんな簡単なものはない。

わざわざベンダーロックインをされにいかないでほしい。
Linuc304対策　QEMU+KMVを使った仮想環境構築

# 用語
## QEMU
まざまなコンピュータシステムやアーキテクチャのエミュレーションと仮想化を行うためのオープンソースのソフトウェア。CPUやメモリ、ストレージなどのコンポーネントを仮想的に再現し、異なるOSを動かす「エミュレーター」として動作する。

## KVM
Linuxカーネルに組み込まれた仮想化インフラストラクチャで、Linuxを強力なハイパーバイザーに変える機能です。KVMは、物理CPUの仮想化機能（Intel VT-xやAMD-Vなど）を利用して、高速で効率的なハードウェア支援の仮想化を実現する。

# 構築
Linux環境で搭載しているCPUが仮想化機構（Intel VT、AMD-V）を備えているかどうかを確認します。

```
lscpu
```

```
Virtualization features:               
仮想化:                              VT-x
```

次に仮想化拡張機能が有効なCPUコア数を確認する
値が 1 以上の場合：CPUが仮想化機能をサポートされています。
```
grep -c '(vmx|svm)' /proc/cpuinfo
```

## QEMU、KVM、libvirt関連のツールをインストール
```
sudo apt install qemu qemu-kvm libvirt-daemon libvirt-clients bridge-utils virt-manager
```

qemu: QEMUの基本エミュレーションパッケージ。
qemu-kvm: KVMによるハードウェア支援仮想化のためのパッケージ。
libvirt-daemon, libvirt-clients: 仮想マシンの管理を行うためのLibvirtデーモンとクライアントツール。
bridge-utils: 仮想マシン用のネットワークブリッジを作成するためのツール。
virt-manager: 仮想マシンをGUIで管理するためのツール。

## 権限の設定
仮想マシン管理を行うには、ユーザーを libvirt グループに追加することで権限を与えます。
ユーザーグループの変更を反映させるためには再ログインまたはシステムの再起動が必要です。

```
sudo usermod -aG libvirt $(whoami)
```

## 仮想マシン作成

アプリケーション一覧又は下記コマンドで仮想マシンマネージャを起動する

```
virt-manage
```

左上のディスプレイのアイコンをクリックすると仮想マシンを追加することができます。

![Screenshot from 2024-11-01 01-06-17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3922547/96520534-6b6d-3523-ccab-c5d90ab4d534.png)


![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3922547/00672ef7-80b7-3461-70a8-65adcdbc41ea.png)

事前に用意しておいたISOファイルを選択
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3922547/34ad43b8-5aa2-0b4c-3ccc-c62e578e2ef6.png)


上記の様に仮想マシンマネージャーを使用すると、GUIで仮想マシンを作成できます。

![Screenshot from 2024-11-01 01-03-22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3922547/06a7faeb-22ac-1a55-08f8-58c69d3acaf3.png)


コンソールから確認

```
virsh list --all
```

```
Id   Name             State
---------------------------------
 -    centos-stream9   shut off
```
NeoForge 1.21.3 でデバッグしようとした時に躓いた話し

開発時のNeoForgeのバージョンは下記の通りです。

```gradle.properties
neogradle.subsystems.parchment.minecraftVersion=1.21
neogradle.subsystems.parchment.mappingsVersion=2024.07.28
minecraft_version=1.21.3
minecraft_version_range=[1.21.2,1.22)
neo_version=21.3.6-beta
neo_version_range=[21.3.6-beta,)
loader_version_range=[4,)
```

# 発生した事象
NeoForgeを用いてのMC1.21用に開発したModをMC1.21.3へ対応させた。
その後、Minecraft Clientを実行してデバッグすると下記のエラーが発生する。

> Exception in thread "main" java.lang.IllegalStateException: Module named org.objectweb.asm.tree.analysis was already on the JVMs module path loaded from C:\Users\norio\.gradle\caches\modules-2\files-2.1\org.ow2.asm\asm-analysis\9.7\e4a258b7eb96107106c0599f0061cfc1832fe07a\asm-analysis-9.7.jar but class-path contains it at location C:\Users\norio\.gradle\caches\modules-2\files-2.1\org.ow2.asm\asm-analysis\9.5\490bacc77de7cbc0be1a30bb3471072d705be4a4\asm-analysis-9.5.jar

文面を見た感じ、`asm-analysis-9.5.jar`と`asm-analysis-9.7.jar`が同時に読み込まれているためエラーになっていると思われる。

# 対処法
Gradleのバージョンを上げる
変更前
```gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
```

変更後
```gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
```

以上です。
WSL2/PodmanからGoogleDrive/Cryptomatorのファイルをマウントするまで

# はじめに
Google Driveのクラウド上にあるファイルやCryptomatorで暗号化してマウントしているファイルをWSL2から触る、もっと言うとPodmanのコンテナ上から触りたいなと思ったわけです。

厳密に言うと、最初は触れてたんですがとある時から触れなくなったのでそのトラブルシュートをまとめておきます。

# なんかマウントできない
最初は、docker-composeコマンドとかでうまいことマウントしてくれていたのでGoogleDriveの中のファイルとか普通にPodmanのコンテナの中から読めてました。
ある時 `docker-compose down` したあと`docker-compose up -d`すると起動しなくなりました。
色々と見ていくと以下のようなエラーが起きていました。

```
[+] Running 1/3
 ✔ Network docker_default  Created                              0.0s
 - Container komga_books  Creating                             0.1s
 - Container jellyfin    Creating                             0.1s
Error response from daemon: container create: statfs /mnt/f/hogehoge/Docker/path/config/cache: no such file or directory
```
要するに、ファイルが読めねーよっていうエラーです。

# 原因を切り分ける
Podmanが原因のエラーなのか、WSL2が原因のエラーなのか切り分けをしないといけないので、WSL2の中で動いてるpodman machineを見ていくことにしました。

```
> wsl -d podman-machine-default
$ ls /mnt/
c  d  f  g  wsl  wslg
$ ls /mnt/f
$
$ ls /mnt/g
$
```
なるほど、/mnt/f（CryptomatorでマウントしたFドライブ）や/mnt/g（GoogleDriveがマウントしているGドライブ）のマウントポイントはあるけど、マウントされてないっていうことらしい。
そもそもなんで最初はうまく言ってたのかはわからんですね。

ググりまくったけどまぁ解決しませんでした。

https://community.cryptomator.org/t/access-cryptomator-vaults-from-wsl/9684

https://github.com/cryptomator/cryptomator/issues/1033

# 解決
色々試してpodを消したりしたところ、/mntディレクトリ配下にマウントポイントすら作られなくなっていたので、手動でマウントポイント作って手動でマウントしてやることにしました。

```
sudo mkdir /mnt/f
sudo mkdir /mnt/g
sudo mkdir /mnt/h
sudo mkdir /mnt/i
sudo mount -t drvfs f: /mnt/f
sudo mount -t drvfs g: /mnt/g
sudo mount -t drvfs h: /mnt/h
sudo mount -t drvfs i: /mnt/i
```

はい。そんな感じで1ヶ月位解決に要したトラブルに関してのお話でした。
【ChatGPT】自作システムの要件定義

# はじめ
プログラマー1年生、@shika_ksです。
比較的新しい技術であるRPAと、RPAを動かすための画面ツールとしてMicrosoftOfficeのAccessを学習するため、ここに記録を残していきたいと思います。

# なにを学習するの
学習するからにはなにか目に見える成果を、と考えた結果、自作システムを開発することに決めました。

今回の学習のテーマは、主に2つ。
1.学生時代に学んだ顔認証技術 × RPA × Access
2.AI技術の精度の検証

そこで以下の内容を考案しました。

# システム案
・顔認証技術を利用して登録された人のみが入室できるアクセス管理システム
・特定のユーザのみが顔データの登録、管理を行うことが可能
・前営業日に入室したメンバーの記録をExcelに出力する流れをRPAで自動化

# 要件定義
システム案をもとに、ChatGPT-4oに要件定義の作成してもらいました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3855287/d4b323d9-dac7-b5bc-367c-49af296b8386.png)

# 使用するツール(仮)
| ツール |  |
|:-:|:-:|
|  顔認証技術 | OpenCV  |
|  言語 | Python  |
|  RPA技術 | UiPath無償版  |
|  Access | Access2016  |
|  ChatGPT | ChatGPT-4o  |

時間があるならOpenCV顔認証の機械学習を1から挑戦しようと考えていますが、現時点で未定です。
そんな高難度なことができるのかという不安を察してください。

また、今回はウォーターフォールモデルで開発をしていこうと考えています。

# 感想
要件定義が完成するのに、3ターンでした。
無償版と比較して、かなり精度が高いと感じました。

無償版はこちらが入力した要件と外れたことが多く、修正が多数必要でしたが、
有償版では、1ターン目から8割の完成度でした。
2ターン目からは不足していた箇所を指摘しただけです。
かなり精度が異なることを体感できました。
docker build -f prd/Dockerfile -t hoge_rails . error during connect: This error may indicate that the docker daemon is not running.: Post の対処法

Dockerをビルドする際に以下のエラーが発生しました。


```terminal:error
 docker build -f prd/Dockerfile -t hoge_rails .
error during connect: This error may indicate that the docker daemon is not running.: Post "http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.24/build?buildargs=%7B%7D&cachefrom=%5B%5D&cgroupparent=&cpuperiod=0&cpuquota=0&cpusetcpus=&cpusetmems=&cpushares=0&dockerfile=prd%2FDockerfile&labels=%7B%7D&memory=0&memswap=0&networkmode=default&rm=1&shmsize=0&t=blog_rails&target=&ulimits=null&version=1": open //./pipe/docker_engine: The system cannot find the file specified.
```

```terminal:バージョン
Docker -v
Docker version 20.10.13, build a224086
```

<h2>解決策</h2>

下記の右上の虫アイコンを押下し、Restart Docker Desktopを実行したら解決しました。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927880/dbfa3eca-ff52-376d-feec-d70d35634744.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927880/38379c73-9eb8-5a09-9c2d-51a97df22aed.png)


<h2>参考<h2>

https://lil.la/archives/6369
macOSのSafariでautoplayのvideoタグに再生ボタンが出てしまう件

最近あまりウェブやってなかったのですが、質問されたので調べました。

## 現象

論より証拠
https://tom-gs.com/demo/video/

```html
<video
  id="js-movieBgEmbed"
  class="movie"
  src="mov_hts-samp009.mp4"
  width="800"
  muted loop autoplay playsinline
>
</video>
```

動画
https://www.home-movie.biz/free_movie.html

![macos_safari_lowpowermode_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131218/e9264545-e509-02b8-d0c5-155af8f9d406.png)

## 原因

原因としては、低電力モードのmasOSのSafariだからです。
自分が確認したのはmacOSだったのですが、iOSでも出るっぽいです。

https://zenn.dev/rik9228/scraps/6972c4a1c5077a

https://qiita.com/mzmz__02/items/8c71cc257583fc8fb8a9

そうは言っても他サイトでは再生できているのでは？
なにか再生させる方法があるのでは？
と思って調べて見ました。

結論から言うと低電力モードのmacOSのSafariで、autoplayのvideoタグを再生させる方法はなく、再生ボタンが出てしまうのは仕方がないようです。

例えば[アニメ怪獣8号のウェブサイト](https://kaiju-no8.net/#movie)などを見てもらうとわかると思います。
再生ボタンが出ていて、クリックできなくなっていると思います。
サイトによってはレイアウトの都合で再生ボタンが押せなくなっていることがあるみたいです。
ほとんどそうなのではないかと思います。
映画やアニメのサイトに多いと思います。

## 解決方法

解決方法はありません。
`onload`イベントにフックして、JavaScriptで再生させようとしてもSafariがエラーを吐きます。
まぁ低電力モードで、電池が減るようなことができてしまっては元も子もない、ということなんでしょう。

ちなみにそれと気づかず報告されことがあるので、説明して納得してもらうしかないのかなと。

さらにこれはSafariだけで発生します。
Chrome、Firefox、Vivaldi、Edgeは動画が再生されてました。
USBとThunderboltの仕様まとめ

| **項目**             | **最大データ転送速度（Mbps）**            | **最大データ転送速度（MBps / GBps）** | **電力供給（PD）**     | **映像出力**           | **互換性**                       |
|----------------------|----------------------------------------|--------------------------------------|------------------------|------------------------|-----------------------------------|
| **USB 1.0**          | 1.5Mbps（Low Speed）/ 12Mbps（Full Speed） | 0.1875MBps / 1.5MBps                 | 最大2.5W               | なし                  | なし                              |
| **USB 2.0**          | 480Mbps                                | 60MBps                               | 最大2.5W               | なし                   | USB 1.1                          |
| **USB 3.0 / 3.1 Gen 1** | 5Gbps                               | 625MBps                              | 最大4.5W               | オプション             | USB 2.0, 1.1                     |
| **USB 3.1 Gen 2**    | 10Gbps                                | 1.25GBps                             | 最大100W               | オプション             | USB 2.0, 3.0                     |
| **USB 3.2**          | 20Gbps（Gen 2x2の場合）               | 2.5GBps                              | 最大100W               | オプション             | USB 2.0, 3.1                     |
| **USB4**             | 40Gbps                                | 5GBps                                | 最大240W（PD 3.1）     | DisplayPort Alt Mode   | Thunderbolt 3, USB 3.2           |
| **Thunderbolt 3**    | 40Gbps                                | 5GBps                                | 最大100W               | DisplayPort 1.2        | USB 3.1, DisplayPort             |
| **Thunderbolt 4**    | 40Gbps                                | 5GBps                                | 最大100W               | DisplayPort 1.4        | USB 3.2, Thunderbolt 3           |
| **Thunderbolt 5**    | 80Gbps（最大120Gbps）                 | 10GBps（最大15GBps）                 | 最大240W               | DisplayPort 2.1        | USB 4, Thunderbolt 3, 4          |
シェルスクリプトでPATHから重複したパスを削除する（POSIX準拠・高速）

## はじめに

シェルの設定ファイルで行うことの一つが環境変数の設定です。環境変数 `PATH` の設定はログイン時にのみ読み込まれるプロファイル（`.profile`、`.bash_profile`、`.zprofile` など）で行っていれば重複したパスが追加されることはまずないのですが、設定ファイルを rc ファイル（`.bashrc`、`.zshrc` など）で行うと、シェルを起動するたびに環境変数 `PATH` に同じものが追加され、パスが伸びてしまうことがあります。ここではこの問題を解決する方法を紹介します。

## 実装コード

この問題を解決する方法はいくつか考えられますが、ここで紹介するのは環境変数 `PATH` から重複したパスを削除する方法です。つまり

```sh
PATH='/usr/bin:/bin:/usr/bin:/bin:/usr/bin:/bin'
```

を

```sh
PATH='/usr/bin:/bin'
```

にするのが目標です。また `:` 区切りで設定する環境変数や環境変数ではないシェル変数は他にもあるため、それらにも対応可能にします。

### POSIX 準拠コード

POSIX で標準化された機能のみを使った実装で、**`sh` を含むすべての POSIX シェルで動作**します。外部コマンドを使っていないので、他の外部コマンドを使う実装よりも数ミリ秒程度ですが速いです。

```sh
# POSIX 準拠のコードで、どの POSIX シェルでも動作する
dedup_path() {
  set -- "$1" "$2:" ''
  until [ -z "$2" ] && eval "$1=\${3%:}"; do
    case ":$3" in
      *":${2%%:*}:"*) set -- "$1" "${2#*:}" "$3" ;;
      *) set -- "$1" "${2#*:}" "$3${2%%:*}:" ;;
    esac
  done
}

# "$PATH" から重複値を除いて PATH に代入する
dedup_path PATH "$PATH"

# 関数が不要になったら削除する
unset -f dedup_path
```

念の為ですが、`[` コマンドはすべての POSIX シェルでビルトインコマンドなので、`[` が `/bin/[` を呼び出すことはありません。`case` に置き換えれば `[` を取り除くこともできますが、そこまでする必要はないでしょう。

### Bourne シェル対応コード

もし Bourne シェルにも対応したいのであれば以下を使ってください。Bourne シェルとは Solaris 10 （Solaris 11 は違う）の `/bin/sh` のことで、`echo $((1))` がエラーになるシェルのことです。Linux、BSD 系 Unix、macOS の `/bin/sh` は Bourne シェルではありません。したがってこのコードを使う必要はまずないはずです。

```sh
# Bourne シェル対応版（POSIX シェルでも動作可能）
#  set -f / +f はパス名展開が行われないようにするため
#  厳密にやっているだけで、なくても問題になることはまずない
dedup_path() {
  set -- "$1" "$2" "$IFS" "$-" && IFS=: && set -f
  set -- "$1" "$3" "$4" $2 && IFS="$2"
  case "$3" in *f*) ;; *) set +f; esac
  eval "shift 2 && $1=''
    while shift && [ \$# -gt 0 ]; do
      case \":\$$1:\" in
        *:\"\$1\":*) ;;
        *) $1=\"\$$1\${$1:+:}\$1\"
      esac
    done
  "
}

# "$PATH" から重複値を除いて PATH に代入する
dedup_path PATH "$PATH"

# 関数が不要になったら削除する
#   Bourne シェルでは変数と関数の名前空間が同じで -f オプションが
#   存在しないため、エラーを想定してフォールバック処理を行う
{ unset -f dedup_path || unset dedup_path || true; } 2>/dev/null
```

`IFS` と `eval` を使ったコードが複雑なぐらいで、動作に違いはありません。無理せず変数を使えば `eval` は減らせるんですが趣味です。

### zsh 専用コード

重複するパスの除去は zsh では `typeset -U` を使うだけで簡単に実現できます。`typeset -U` は重複値を除くための属性で、配列または `:` 区切りを使う PATH などの変数に付けることがでます。

```sh
PATH='/usr/bin:/bin:/usr/bin:/bin:/usr/bin:/bin'
typeset -U path PATH
echo "$PATH" # => /bin:/usr/bin
```

上記のコードは `path` 配列と `PATH` 環境変数の 2 つに属性を付けています。zsh では `path` 配列は `PATH` 環境変数と紐づいており `:` で区切られた値が `path` 配列に反映されます。その逆もしかりですが、`path` と `PATH` のどちらから代入しても重複値を省くために両方に属性を付けています。

`typeset -U` は任意の環境変数にそのまま使えるわけではなく、`PATH` 変数に対応する `path` 配列のような紐づけが存在する場合に使えます。そのため `LD_LIBRARY_PATH` に対して有効にするには次のように紐づけを作る必要があります。少し分かりづらいですね。

```sh
LD_LIBRARY_PATH='/usr/bin:/bin:/usr/bin:/bin:/usr/bin:/bin'
typeset -TU LD_LIBRARY_PATH ld_library_path
echo "$LD_LIBRARY_PATH" # => /bin:/usr/bin
```

## さいごに

他にも、プロファイルの最後で別の環境変数やファイルに退避しておき、rc ファイルの最初でリセットする方法や、追加したいパスがすでに `PATH` に含まれていたら追加しない関数を作る方法も考えたのですが、既存のコードをほぼ書き換えずに、最後に少し処理を追加するだけで良いため、この方法を採用しました。
【Unity】OpenAI API 「Error: StatusCode: 429, ReasonPhrase: 'Too Many Requests'」

# エラー内容
UnityでOpenAIのAPIを触っていたら急に「Error: StatusCode: 429, ReasonPhrase: 'Too Many Requests', Version: 1.1, Content: System.Net.Http.StreamContent, Headers: ...」というエラーが出て使えなくなった。

# 解決法
残金がゼロになっていたのが原因。というよりチャージ制なのを忘れてた。
「Add to credit balance」を押してチャージ。あるいはEnable auto rechargeで解決。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3077175/221af1e1-e881-10b1-d9ab-feea92e8c81b.png)


# 余談
プロンプトを毎回更新して新規作成しているつもりが後ろに追記する仕様になっており、知らず知らずのうちに尋常じゃない文字数のプロンプトを送り付けてtoo many requestsになって、原因特定に悩んだこともありました。OpenAIくんごめんね
構成クエリの一括出力


:::note warn
とあるテーブルのカラムを削除します。
DBに影響する範囲を調べてください。
:::

という依頼がありまして、DB内で削除するカラムを使用している個所を調べる必要がありました。
ビューやプロシージャの構成クエリを抽出して、テーブル名とカラム名が含まれるかを調べれば完了です。

一つずつ構成クエリを調べていくのは大変ですし、漏れが出る可能性がるので一括でできないか調べたところ出来ました。

```sql
-- 構成クエリを抽出
SELECT O.type AS '種類'
     , O.name AS 'オブジェクト名'
     , M.definition AS '構成クエリ'
  FROM sys.objects AS O
 INNER JOIN sys.sql_modules AS M
         ON M.object_id = O.object_id
 WHERE M.definition LIKE '%テーブル名%'
   AND M.definition LIKE '%カラム名%'
 ORDER BY O.type
        , O.name
```
このクエリで該当のカラムを使用しているものが抽出されます。

typeの種類については下記サイトを参照ください。

https://learn.microsoft.com/ja-jp/sql/relational-databases/system-catalog-views/sys-objects-transact-sql?view=sql-server-ver16
ファイル高速検索ソフトEverything

# Windowsのファイルエクスプローラーの検索が遅すぎる！

というわけでファイルを高速で検索できるソフトである。Everythingの紹介？というか布教記事です。
なんか某格安スマホみたいなことに今気づきました。

### ダウンロードして初期設定など
下記のリンクからとりあえずダウンロードを初めてください。かなり昔のPCでもない限り64bitの方です。

https://forest.watch.impress.co.jp/library/software/everything/

ダウンロードが終わったらsetup.exeを実行します。
実行が終わったらウィンドウが出てきます。
インストールの言語は日本語、規約は同意で進めましょう。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853726/de281d37-e554-ffbb-1ba1-edfe183ec403.png" width="70%">

インストール先の選択はデフォルト、インストールのオプションもそのままで進めていいと思います。
オプションについて調べましたが、デフォルトのままで大丈夫なはずです。自分はそのままで問題なく動いています。
ただ、
- [x] デスクトップにショートカットを作成
↑はチェックした方がいいと思います。見つけやすいので。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853726/54bf0d86-c776-e5c0-889d-29e196067bb0.png" width="50%"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853726/45878d75-34b9-d7a5-c7b6-73af6ee6702f.png" width="50%">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853726/4b7c9d31-d572-4381-6255-a73dc9eff0c8.png" width="50%">


インストールしたらこれで終わりです。
試しにファイルを検索してみてください。Windowsのファイルエクスプローラーだと何十分かかることもありますが、Everythingなら爆速です。
初めてこのソフト使った際は爆速でめちゃくちゃ感動しました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853726/453f5844-4a3e-eb90-d7fa-0b0a648b353b.png" width="70%">


めちゃくちゃ短い記事になりましたが以上です！
みんなEverythingで快適なファイル検索生活を送ってね！