OmniAICreator/Qiita-1.07M huggingface dataset:
書くだけじゃダメ、利用者のことまで考えた docstring の書き方の提案。

## はじめに
docstringの使い方を思いついたので備忘録

## 本文

下記のような縦横のpxのサイズがあります。
```python
large  = [512, 512]
medium = [256, 512]
small  = [256, 256]
```
これをプロジェクト共通のサイズとして、色々なファイルで使うことになりました。

なので、下記のような Sizeクラス として適宜importして使うことにしました。
```python
# Size.py
class Size:
    large  = [512, 512]
    medium = [256, 512]
    small  = [256, 256]
```
<br>

そして、クラスの説明として、下記のようなdocstringを書きました。
```python
# Size.py
class Size:
    """
    共通して使うサイズ
    * large
    * medium
    * small
    """
    large  = [512, 512]
    medium = [256, 512]
    small  = [256, 256]
```
こうすることによって写真のような説明がされます。
![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832781/4532d0a5-acb7-ca71-2ea9-930db644506e.png)
<br>

**しかし!!**
large,medium,smallがそれぞれ何pxなのかがわかりません...
<br>

下記のようにすればいいと思った方がいるかもしれません。
```python
# Size.py
class Size:
    """
    共通して使うサイズ
    * large [512, 512]
    * medium [256, 512]
    * small [256, 256]
    """
    large  = [512, 512]
    medium = [256, 512]
    small  = [256, 256]
```
### 問題
これだと問題があります。それは...
    下記のような時、largeのpxを見るためにSizeをホバーしなければいけないんです。
```python
from Size import Size
print(Size.large)
```
![16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832781/463ef6bc-ab06-ce91-9388-d3f647508d2c.png)
それにlargeのpxだけ知りたいのに、他の邪魔な情報も出てきてしまいます。

### 解決策
そこで、今回思いついたのが、メソッド化するということです。
```python
class Size:
    """
    共通して使うサイズ
    """
    def large():
        """
        [512l, 512]
        用途 : 大アイコン
        """
        return [512, 512]

        
    def medium():
        """
        [256, 512]
        用途 : バナー
        """
        return [256, 512]

        
    def small():
        """
        [256, 256]
        用途 : 小アイコン
        """
        return [256, 256]
```
上記のようにメソッド化することによって、
それぞれ細かく説明でき、かつ必要な情報だけを見ることができるようになりました。

![17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3832781/7b144a49-0485-4c7a-86ff-4b737ad723c8.png)

## おわり
チーム開発を進めるときは、自分が作ったclassをどのように使われるか、
それを意識して開発すると良いと思います。
Python `os.path` vs `pathlib`: どちらを使うべきか

## はじめに
Pythonでファイルシステム操作を行う際、`os.path`モジュールと`pathlib`モジュールの2つの選択肢があります。この記事では、両者の違いと使い分けについて解説します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/099a3ab2-f632-836d-3d39-98f19b2f440f.png)
## 1. 概要

- `os.path`: Python 2から存在する古くからあるモジュール
- `pathlib`: Python 3.4で導入された比較的新しいモジュール

## 2. 主な違い

### 2.1 オブジェクト指向 vs 関数ベース

- `os.path`: 関数ベースのアプローチ
- `pathlib`: オブジェクト指向のアプローチ

### 2.2 パス操作の方法

```python
# os.path
import os
path = os.path.join('folder', 'file.txt')
dirname = os.path.dirname(path)
basename = os.path.basename(path)

print(f"Path: {path}")
print(f"Directory: {dirname}")
print(f"Filename: {basename}")

# pathlib
from pathlib import Path
path = Path('folder') / 'file.txt'
dirname = path.parent
basename = path.name

print(f"Path: {path}")
print(f"Directory: {dirname}")
print(f"Filename: {basename}")
```

出力例:
```
# os.path の出力
Path: folder/file.txt
Directory: folder
Filename: file.txt

# pathlib の出力
Path: folder/file.txt
Directory: folder
Filename: file.txt
```

### 2.3 ファイル操作

```python
# os.path
import os

filename = 'example.txt'
if os.path.exists(filename):
    print(f"{filename} exists")
    size = os.path.getsize(filename)
    print(f"File size: {size} bytes")
else:
    print(f"{filename} does not exist")

# pathlib
from pathlib import Path

filename = Path('example.txt')
if filename.exists():
    print(f"{filename} exists")
    size = filename.stat().st_size
    print(f"File size: {size} bytes")
else:
    print(f"{filename} does not exist")
```

出力例 (ファイルが存在する場合):
```
# os.path の出力
example.txt exists
File size: 1024 bytes

# pathlib の出力
example.txt exists
File size: 1024 bytes
```

## 3. どちらを使うべきか

### 3.1 `pathlib`を選ぶ理由

1. 直感的で読みやすいコード
2. オブジェクト指向的なアプローチ
3. 豊富な組み込みメソッド
4. Python 3.6以降では`open()`関数と互換性がある

### 3.2 `os.path`を選ぶ理由

1. 古いPythonバージョンとの互換性
2. `os`モジュールの他の機能と組み合わせて使用する場合
3. 既存のコードベースとの整合性

## 4. 結論

新しいプロジェクトや、Python 3.4以降を使用している場合は`pathlib`の使用をお勧めします。`pathlib`は直感的で強力な機能を提供し、コードの可読性を向上させます。

一方、古いPythonバージョンとの互換性が必要な場合や、既存のコードベースとの整合性を保つ必要がある場合は`os.path`の使用も検討してください。

最終的には、プロジェクトの要件と個人の好みに応じて選択することが重要です。どちらを選んでも、一貫性を保つことがコードの保守性向上につながります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/407b80dd-cb4d-efe9-362e-961beb7da368.png)

## 5. 参考資料

- [Python公式ドキュメント: os.path](https://docs.python.org/3/library/os.path.html)
- [Python公式ドキュメント: pathlib](https://docs.python.org/3/library/pathlib.html)
- [PEP 428 -- The pathlib module -- object-oriented filesystem paths](https://www.python.org/dev/peps/pep-0428/)

以上の情報を参考に、あなたのプロジェクトに最適なモジュールを選択してください。
Poetry executableってどこ？～PycharmにおけるPoetryの導入について～

# 背景
Pycharmにおいてパッケージの管理を行うのはデフォルトでpipである。
ところが調べたところによるとpoetryだとパッケージ間の依存関係の問題を解決しやすいため、poetryを導入することを決意した。

# 動作環境
動作環境は下記のとおりである。
OS：windows10
Pycharm Community Edition 2022.3.2

# 課題
[Pycharm公式ドキュメント](https://pleiades.io/help/pycharm/poetry.html)に従ってpoetryを導入した。
途中まで順調だったが"Poetry環境を作成する"の
>Python インタープリターの追加ダイアログの左側のペインで Poetry 環境を選択します。

で躓いてしまった。
具体的には、リンク先の画像を見てもらうと分かるだろうが、Poetry executableにどこのPathを指定するのかがわからなかった。

# 解決方法
[Pycharm公式コミュニティの掲示板](https://intellij-support.jetbrains.com/hc/en-us/community/posts/4404727949714-Which-file-is-the-poetry-executable-file)に同じような質問があった。
Andrey Resler氏の回答によるとpoetry.exeへのPathを入力するらしい。
poetry.exeは[公式ドキュメント](https://pleiades.io/help/pycharm/poetry.html#install-poetry)の"Poetryをインストールする"の
>macOS と Windows では、インストールスクリプトは、詩の実行可能ファイルを含むフォルダーを PATH 変数に追加することを提案します。これを行うには、次のコマンドを実行します。

で指定したPathに格納されている。
そのため上記のPathをpoetry executableに入力し、poetry.exeを指定すれば正常に動く。

# おわりに
日本語で同様の問題に対する解決手段が紹介されている記事がなかったため、本記事を投稿した。
初歩的な問題かもしれないが、同じような問題に遭遇した人の役に少しでも立てるとうれしい。

またPoetryはpipと比較してコマンドや設定が多いと聞いた。
個人的な抱負となってしまうが、今後エラーが発生したとしても解決できるよう粘り強く調べていきたい。
【SWR】早期returnがうまくいかない！条件付きフェッチで解決

## 発生した問題
個人開発したアプリのGETリクエストはすべてSWRを使ってfetchしたのですが、fetcher関数内で早期returnしているはずなのに出来ていなくて意図するレスポンスが得られないということが起こりました。


```ts:useFetch.ts
import useSWR from "swr";
import { useSupabaseSession } from "@/app/_hooks/useSupabaseSession";

export const useFetch = <T>(path: string) => {
  const { token, isLoading } = useSupabaseSession();
  const fetcher = async () => {
    if(isLoading) return;
    if (!token) return;
    const prams = {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
        Authorization: token,
      },
    };
    const resp = await fetch(`/api/${path}`, prams);
    if (!resp.ok) {
      const errorData = await resp.json();
      throw new Error(
        errorData.message || "An error occurred while fetching the data."
      );
    }
    const data: T = await resp.json();
    return data;
  };
  const results = useSWR(`/api/${path}`, fetcher);
  return results;
};

```

このカスタムフックの下記の部分ですが、
```ts
if(isLoading) return;
if (!token) return;
```
セッション情報の取得が終わり、tokenが存在する状態で初めてfetch処理をしたいのですが、なぜかtokenが存在しない状態でfetch処理が実行されてしまいました。


## 原因(？)
詳しいことはわからないのですが、理由はSWRの仕様上、一度400(error)が返ってきた後に再度リクエストが走って200になった場合、errorの中身が残っていてdataとerror両方に値が入っていることが起こり得て、それが原因になっていた可能性がありました。
実際に条件がそろっていないのに実行されてしまい意図しない挙動をしてしまうことがありました。


## 条件付きフェッチ
条件付きフェッチを教えていただいて即解決しました。

https://swr.vercel.app/ja/docs/conditional-fetching

```ts:useFetch.ts
import useSWR from "swr";
import { useSupabaseSession } from "@/app/_hooks/useSupabaseSession";

export const useFetch = <T>(path: string) => {
  const { token, isLoding } = useSupabaseSession();
  const shouldFetchData = !isLoding && token;
  const fetcher = async () => {
    if (!token) return;
    const prams = {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
        Authorization: token,
      },
    };
    const resp = await fetch(`/api/${path}`, prams);
    if (!resp.ok) {
      const errorData = await resp.json();
      throw new Error(
        errorData.message || "An error occurred while fetching the data."
      );
    }
    const data: T = await resp.json();
    return data;
  };
  const results = useSWR(shouldFetchData ? `/api/${path}` : null, fetcher);
  return results;
};

```

useSWRの第一引数に三項演算子が使えて、条件がそろっていない時はエンドポイントではなくnullを渡すとfetcherは実行されません。

これですべて解決しました！！


下記、公式のサンプルコードです。

```ts
// 条件付きでフェッチする
const { data } = useSWR(shouldFetch ? '/api/data' : null, fetcher)
 
// ...または、falsyな値を返します
const { data } = useSWR(() => shouldFetch ? '/api/data' : null, fetcher)
 
// ...または、user.id が定義されてない場合にスローします
const { data } = useSWR(() => '/api/data?uid=' + user.id, fetcher)

```

公式によると三項演算子以外にも条件付きフェッチする書き方はあるみたいですが、私は好みで三項演算子を使って書きました。
ROS2でのXtion環境構築

やほっ！
web記事の第一歩を踏み出しましょうか。

## ROS2でのXtion環境構築
ubuntuとROS2の環境構築が完了しているものとして話を進めます。


私は以下の環境で動作させました。
OS: Ubuntu 22.04
ROS2 Distribution: Humble

もし、ROS2Humbleの導入がまだの方がいれば公式の英語マニュアルのDebian packagesの項目を上から順に実行することをおすすめします。最後のどのROS2 Humbleをインスールするのかの項目で、
&emsp;"Desktop"(GUIあり) &emsp; "ROS-Base"(GUIなし) &emsp;"Development"
の3つのパッケージがありますが、すべて実行せず、"Desktop"**↓**だけを選びましょう。
```
sudo apt install ros-humble-desktop
```
##### 参照：　ROS2 Documentation: Humble / Ubuntu(Debian packages)
https://docs.ros.org/en/humble/Installation/Ubuntu-Install-Debians.html

# ~手順~
つまずいたら、それまでの手順に不備やらエラーやらがあると思われますので、どの時点で駄目だったのかを探してみてくださいね。
<br>
⓪ROS2の有効化
```
source /opt/ros/humble/setup.bash
```
<br>

①ROS2ワークスペースを用意 (本記事ではyour_wsと表記した)
既にxtionを使いたいワークスペースがあれば、新たに作成する必要はありません。
```
mkdir -p ~/your_ws/src
cd ~/your_ws/src
```
<br>

②srcディレクトリ上でopenni2_cameraのリポジトリをクローン
```
git clone https://github.com/mikeferguson/openni2_camera.git
```
##### 参照:openni2_camera / ros2-branch
https://github.com/mikeferguson/openni2_camera/tree/ros2

<br>

③自身のワークスペースのビルド
```
cd ~/your_ws
colcon build
```
<br>

④自身のワークスペースを有効化
```
source install/setup.bash
```
<br>

⑤usbポートにxtionを指し、xtionを起動させる
```
ros2 launch openni2_camera camera_only.launch.py
```
<br>

⑥新しいターミナルを開き、改めて⓪④を実行して、
受け取った画像データをrqtで表示する
```
ros2 run rqt_image_view rqt_image_view
```
おそらく、こんなグレーの画像しか表示されないと思いますが、左上の**⇔**(更新マークっぽいの)を押したあと、すぐ隣の長細い選択メニューを押すと、受け取った画像データの選択ができるようになるはずです。
<br>
**rqt_image_viewの初期状態**
![Screenshot from 2024-08-02 06-29-36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3851113/588e4d71-edb4-d049-0cdd-3913ec644473.png)
<br>
**ドロップダウンメニュー**
![Screenshot from 2024-08-02 06-31-38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3851113/2e68148c-65c2-a9cb-3bb1-f6f0e33bc3a7.png)

画像データを見たければ、
"/camera/rgb/image_raw"
深度(depth)画像では、
"/camera/depth/image_raw"
を選択してみてくださいね。
<br>
⑦実行したopenni2_cameraとrqt_image_viewのプロセスの終了
    →各々のターミナルで、**control + C** と入力

***
ふぅ、記事書くの難しかった。
ImageMagick を使って、PNG ファイルから PDF ファイルに変換する

# ImageMagick を使って、PNG ファイルから PDF ファイルに変換する

https://imagemagick.org/

## はじめに

はじめまして。

この記事では、[ImageMagick](https://imagemagick.org/) というソフトウェアを使って、PNG ファイルから PDF ファイルに変換しようと思います。

この記事が、「ファイル形式の変換が楽になった」などの貢献ができれば幸いです。

## ImageMagick とは

https://imagemagick.org/

ビットマップ画像の作成、編集、合成、変換に使用できるソフトフェアです。無料でオープンソースのソフトウェアスイートです。JPEG や PNG など多くのファイル形式に対応しています。

## 前提

使用する PC やパッケージ管理は、以下に示します。

### PC

https://www.apple.com/macbook-air/

- 機種：MacBook Air
- チップ：M1
- メモリ：8 GB

### パッケージ管理

https://brew.sh/

- Homebrew

## ImageMagick のインストール

次のコマンドを実行すれば、ImageMagick のインストールが始まります。

```bash
brew install imagemagick
```

## インストールした ImageMagick のバージョンの確認

インストールが成功していれば、次のコマンドで、ImageMagick のバージョンを確認することができます。

```bash
magick --version
```

### PNG ファイルから PDF ファイルに変換

以下のコマンドで `image.png` を `image.pdf` に変換することができます。

```bash
magick image.png image.pdf
```

---

IMv7 では、以下のコマンドは非推奨です。

```bash
convert image.png image.pdf
```

このコマンドを実行すると、私の場合は、以下のような警告が出ます。

```text
WARNING: The convert command is deprecated in IMv7, use "magick" instead of "convert" or "magick convert"
```

## おわりに

この記事では、ImageMagick を使って、PNG ファイルから PDF ファイルに変換する方法について書きました。

この記事が、エンジニアとして活躍する皆様に貢献できたら幸いです。
Learning Causality-inspired Representation Consistency for Video Anomaly Detection

## はじめに

本研究は，ACMMM 2023 に採択されたものであり，本記事では，技術的な内容についてまとめる．

```
Liu, Y., Xia, Z., Zhao, M., Wei, D., Wang, Y., Liu, S., ... & Song, L. (2023, October). Learning causality-inspired representation consistency for video anomaly detection. In Proceedings of the 31st ACM International Conference on Multimedia (pp. 203-212).
```

## 概要

ビデオにおける異常検知では，従来，正常なイベントの中から抽象的な時空間特徴を学習し，その分布からの逸脱を検知することで異常を検出してきたが，これらの手法は多様な正常イベントに対して信頼性が低いことに加え，過度な一般化による高い誤報率を引き起こす可能性がある．
これらの問題に対処するために，本研究は，異常イベントによって顕著な変化を示すような因果変数が存在すると仮定し，正常ビデオから直接観察可能な因果変数を暗黙的に学習し，異常イベントを検出する手法を提案する．

## 技術的なポイント

### Causality-inspired Representation Consistency, CRC）フレームワーク

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2116254/e8445a1c-a2cc-2857-aa85-5bab3da3cfc1.png)

上図が，本論文で提案されるフレームワークである．
以下の流れで処理が行われる．

1. 入力されたビデオフレームから時空間特徴 ($F$) を抽出する (Feature Extractor)
1. 抽出された特徴をメモリに記録する (Memory Module)
1. メモリに保存されたプロトタイプから，特徴を共有部分の特徴（$F_s$）と独立部分の特徴（$F_p$）に分解する (Prototype Decomposer)
1. 分解された特徴を因果変数として学習する．この過程で，特徴の中から因果関係を表す変数を特定する．(Causality-inspired Characterizer)
1. 因果変数の独立性を利用して，相関行列を計算する．この相関行列により，特徴の因果的な一貫性を取得する．(Correlation Matrices)
1. 得られた因果表現をクラスタリングする．(Clustering)

### Casuality-inspired Characterizer

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2116254/f2627c12-8b5b-39e4-2f90-8e6c20b165df.png)

因果関係は，上図(b)のようなモデル化を行う．「観測変数$X$と$Y$の間には，両者に因果的な影響を与え，かつそれを条件としたときに，両者を独立にする変数$Z$が存在する」として，この関係性を取得する．
これにより，上図(c)に示すように，異常イベントは因果関係の一貫性において有意な差異を示すと考えられる．

## 本研究の重要性

### 精度の向上

<img width=80% alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2116254/263e7e20-0302-dad4-573c-c17872c99d5c.png">

上の表に示される通り，UCSD Ped2[^1], CUHK Avenue[^2], ShanghaiTech[^3]データセットにおける異常検知のAUCのスコアは，最新手法 (DeepRL) と レガシー手法 (特徴量を手作業で設定し，抽出するもの) と比較し，それらを上回る結果を出していることが確認できる．

### 因果表現の異常検知への利用

<img width=80% alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2116254/dec08df2-f319-97a6-d683-ed1c3dc864f0.png">

上図は，ShanghaiTech[^3]データセットの正常サンプル(a)と異常サンプル(b)に対する相関行列を部分的に可視化したものである．正常サンプルに対して，異常サンプルは，全体的に相関が高いことが確認でき，因果変数が分離できておらず，因果表現の一貫性を欠いていることが確認できる．これより，因果表現を用いることが正確な異常検知に寄与することが示唆されている．

### 異常イベントに対する高速なレスポンス

<img width=80% alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2116254/a462ecc2-73cd-7606-f9b6-b1307ec724a8.png">

上図は，UCSD Ped2[^1] データセットに対する結果のサンプルである．異常イベントが起きたタイミングでAnomaly Scoreが直ちに上がり，異常イベントが無くなった段階で，直ちに下がっていることが確認できる．

[^1]: [Anomaly Detection and Localization in Crowded Scenes](https://ieeexplore.ieee.org/abstract/document/6531615/)
[^2]: [Abnormal event detection at 150 fps in matlab](https://www.cv-foundation.org/openaccess/content_iccv_2013/html/Lu_Abnormal_Event_Detection_2013_ICCV_paper.html)
[^3]: [A Revisit of Sparse Coding Based Anomaly Detection in Stacked RNN Framework](https://openaccess.thecvf.com/content_iccv_2017/html/Luo_A_Revisit_of_ICCV_2017_paper.html)
【AWS】エラー「CloudFormation cannot update a stack when a custom-named resource requires replacing」の対処法

# 概要

AWS Batchを記述したCloudformationテンプレートを更新したら、以下のエラーになりました。こちらの原因と対処法を解説します。

> CloudFormation cannot update a stack when a custom-named resource requires replacing. Rename arn:aws:batch:ap-northeast-1:1234567890:compute-environment/xxxx-compute-environment and update the stack again.

# 原因
このエラーは、CloudFormationがカスタム名を持つリソースを置き換える必要がある場合に発生するもの。
今回で言うと、AWS Batchの`compute-environment`が該当します。

Batchの`compute-environment`の中身を少し変えたので、そうするとCloudFormationは新しいリソースを作成し、古いリソースを削除しようとしますが、カスタム名を持つリソースは直接置き換えることができない、という現象が起きていました。

# 対応
一時的にリソース名を変更すればスタックを更新できます。

`Type: 'AWS::Batch::ComputeEnvironment'`の`ComputeEnvironmentName`のところですね。
もし元の名前に戻したい場合は再度その名前にしてスタックを更新すればOKです。
Rubyではメソッドの最後の引数がハッシュの場合、{}を省略できる

## はじめに
メソッドの引数に関するRuby特有の挙動がわからずハマったため、備忘録としてまとめます。

なおこの記事におけるRubyのバージョンは**3.3**です。


## 問題
以下のようなコードを見かけました。

```ruby
class A
  attr_reader :arg1, :arg2, :arg3

  def initialize(arg1:, arg2:, arg3:)
    @arg1 = arg1
    @arg2 = arg2
    @arg3 = arg3
  end

  def execute
    # キーワード引数で３つの引数を渡しているつもり
    B.execute(param1: arg1, param2: arg2, param3: arg3)
  end
end


class B
  # 引数は１つだけ、キーワード引数でもない
  def self.execute(params)
    params.each do |key, value|
      puts "#{key}: #{value}"
    end
  end
end

a = A.new(arg1: 'a', arg2: 'b', arg3: 'c')
a.execute
#=> param1: a
#=> param2: b
#=> param3: c
```
Aクラスの`execute`メソッドでBクラスの`execute`メソッドを呼び出しています。

メソッド呼び出し側ではキーワード引数で３つの引数を指定しているのに対し、Bクラス内の`execute`メソッドで指定されている引数は`params`１つだけです。

キーワード引数にもなっていません。

こんな状態なのにも関わらず正常に動いていたため、疑問が湧きました。

## 解決策
Rubyではメソッドに引数を渡す際、最後の引数がハッシュであれば、波括弧`{}`を省略できます。

```ruby
# メソッドの定義
def print_person_info(name, age, options = {})
  puts "名前: #{name}"
  puts "年齢: #{age}"
  options.each do |key, value|
    puts "#{key}: #{value}"
  end
end

# 通常のハッシュ表記
print_person_info("山田太郎", 30, {職業: "エンジニア", 趣味: "読書"})
#=> 名前: 山田太郎
#=> 年齢: 30
#=> 職業: エンジニア
#=> 趣味: 読書

# 最後の引数がハッシュの場合、波括弧を省略できる
print_person_info("佐藤花子", 25, 職業: "デザイナー", 趣味: "絵画")
#=> 名前: 佐藤花子
#=> 年齢: 25
#=> 職業: デザイナー
#=> 趣味: 絵画
```
冒頭のサンプルコードにもこの波括弧の省略が適用されています。

キーワード引数を３つ指定したつもりでしたが、実際には１つのハッシュとみなされていたのです。

つまり、次のように記述したのと同じ扱いになっていました。

```ruby
class A
  attr_reader :arg1, :arg2, :arg3

  def initialize(arg1:, arg2:, arg3:)
    @arg1 = arg1
    @arg2 = arg2
    @arg3 = arg3
  end

  def execute
    # ３つの引数を渡しているように見えて、実際は１つのハッシュを渡している扱いとなる
    B.execute({ param1: arg1, param2: arg2, param3: arg3 })
  end
end


class B
  # 引数は１つだけ
  def self.execute(params)
    # ...
  end
end
```

これで謎が解けました。

## おわりに
最後の引数がハッシュであれば省略可能なことは知っていたものの、３つの引数が１つのハッシュとみなされてるとは気づきませんでした。

Rubyの柔軟な挙動は良い時もある反面、コードリーディングをする際は苦しめられることもあるなと感じました。
複数のRestControllerでswaggerのApiResponseを共有

下記キャプチャのように、swagger-uiにおいて複数の`@RestController`で共通の`@ApiResponse`を定義したい場合は`@ApiResponses`を定義するカスタムアノテーションを作成する。

![customanno.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31934/a5a096ab-9b0a-5d30-6dc8-62bda8858a35.jpeg)

カスタムアノテーションの例は以下の通り。

```java
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@ApiResponses({
    @ApiResponse(responseCode = "500", content = @Content(
        schema = @Schema(implementation = ErrorResponse.class)
    ))
})
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomApiResponses {

}
```

これを`@RestController`で使用する。


```java
@RestController
@CustomApiResponses
public class FooRestController {
  // 以下省略
```

```java
@RestController
@CustomApiResponses
public class BarRestController {
  // 以下省略
```

```gradle:build.gradle
plugins {
	id 'java'
	id 'org.springframework.boot' version '3.3.2'
	id 'io.spring.dependency-management' version '1.1.6'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
	toolchain {
		languageVersion = JavaLanguageVersion.of(17)
	}
}

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0'

	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks.named('test') {
	useJUnitPlatform()
}
```
Athena x Identity Centerの罠

Athena を Identity Center、Lake Formation と組み合わせて以下のようなことができるのだが、妙なトラップがあったのでメモ。

認証： AWS IAM Identity Center
認可： AWS Lake Formation
実行： Amazon Athena　※実際には EMR Studio

### 前提条件
- Identity Center の事前セットアップが必要
- Identity Center 連携を有効化した Athena Workgroup の作成が必要
- EMR Studio 限定（通常の Athena コンソールではできない）

### トラップ
デフォルトでは IAM 連携型の Workgroup を使うのだが、Lake Formation + Identity Center 連携の場合は、Identity Center 連携型の Workgroup を作る必要がある。
...のだが、どこかおかしい。
![japanese.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609876/07f39036-0eb3-8bd3-a6da-b4772ca91ff6.png)

このまま保存しようとすると、やはり以下のようなエラーになる。
正規表現がどうのとあるが、要するに Identity Center のインスタンス ID を取れていない。もしや。。。
![error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609876/8c67d625-c5c0-472b-4ed7-9e8697787970.png)

### 原因と解決策
結論から言うと、やはりというか、コンソールの画面言語が日本語であることが原因だった。
英語に変更する。
![change.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609876/9c895737-7a56-20e2-e49a-c43edc68fe31.png)

今度はインスタンス ID が表示されている。
![english.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609876/dfcc55bc-c1c7-9082-9295-4f332cdb685b.png)

無事作成できた。
![success.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609876/0469ce8d-26e0-59f4-c0df-8ca4be867061.png)

# まとめ
EMR Studio からのクエリーはまだ成功していないが、とりあえず Workgroup の作成には漕ぎ着けた。
ちょっと原因に辿り着きにくいエラーなので、どなたかの参考になれば幸いです。
NeoPixel制御ボードのサンプルプログラム②

- ## 商品概要
RP2040マイコンを使って、NeoPixelを制御して楽しもうというコンセプトです。
右のコネクタは、電池をつないでパソコンから離れても動くようにしています。
左のジャックから音声信号を入力してADコンバータに入力できるようになっています。
ボタンが1つ　or　2つ搭載しているので、外部入力または切り替えなどで使えます。

- ## プログラム環境
ArduinoIDE バージョン：2.3.2
Adafruit NeoPixel Library 1.12.2（2024.8.1時点の最新版は1.12.3でした）

- ## ハードウェアの主な仕様
　①マイコン:PR2040-ZERO（RaspberryPiPico系）
　②NeoPixel接続ピン　 GPI08（R1でショート）
　③プッシュスイッチ１：GPI07
　④プッシュスイッチ２：GPI06（2個搭載しているボードのみ）
　⑤音声入力：GP26

 - ## メインプログラム（ArduinoIDEのスケッチ）

シリアルモニタから数字を送信して、LEDを光らせるサンプルプログラムです。

```c++
// シリアルで数字を1から6を送ると
// LEDをイコライザのように光らすプログラム
// 1から3は、緑色、4と5は黄色、6は赤色で光らす


#include <Adafruit_NeoPixel.h>

#define LED_PIN    8    // NeoPixel信号接続端子
#define LED_COUNT  6    // LEDの数を指定　6個

Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  Serial.begin(9600); // シリアル通信を開始
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  strip.setBrightness(24);  // 明るさ倍率設定 (RGB設定値 * 明るさ倍率、最大255)  
}

void loop() {
  if (Serial.available() > 0) { // シリアルポートからデータが受信されたかどうかを確認
    int data = Serial.parseInt(); // 受信したデータを整数として読み取る
   
    strip.clear(); // すべてのLEDを消す
   
    // データが1から6の場合、指定された数のLEDを点灯
    if (data >= 1 && data <= 6) {
      for (int i = 0; i < data; i++) {
        // 各LEDを点灯させる
        if (i < 3) {
          strip.setPixelColor( i, 0, 255, 0); // LED1からLED3は緑色
        } else if (i < 5) {
          strip.setPixelColor( i, 255, 255, 0); // LED4からLED5は黄色
        } else {
          strip.setPixelColor( i, 255, 0, 0); // LED6は赤色
        }
      }
    }
   
    strip.show(); // LEDを更新
    delay(100); // 20m秒待機
  }
}
```

- ## このプログラムを作成した時の考え方。
シリアルモニタから数字を入力して任意のLEDを光らせたい。
シリアルモニタから数字1から6の値を一つを送ると0.5秒点灯して消灯します。

やりたかったのは、音声入力をGP26に1.65V基準で入力できるボードにしています。
GPIO26は、ADコンバータにつながっており
ADコンバータで得られた値をFFT（高速フーリエ変換）してイコライザみたいに光らせて
遊んでみたかったので、まずは数値を送って表示の原型として試行したプログラムです。

- ## シリアルモニタのイメージ
4を送信したら、NeoPixelが4つ点灯します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3782127/f503fa2a-6d45-c457-1481-60f7ac4bda6a.png)

![1722512886276.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3782127/6fdd4043-1865-8efa-25ce-786b46641e28.jpeg)
AccessのVBAで日付時刻型カラムを文字列化する時はCStr()ではなくFormat()が良い

# CStr()で予期しない動作になる例

テーブルのカラムがこうなっている時…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/fa10485d-e729-0bad-465d-cbc1c90e131b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/7fe6a018-06db-de23-41e3-413b6cae61be.png)

下記の結果は…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/62561970-6f37-8fb3-1506-ff41bfcfe5f5.png)

こう！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/34adbbcf-4421-28e8-75e0-082c6e9015de.png)

**午前0時ちょうどの時だけ時刻情報が無い文字列** になってしまいます。

文字列化する時は『文字列切り出し』も一緒に行われることが多いです。
CStr()の結果が想定外の文字列になってしまうことで例外になる可能性があります。
しかも発生するのが午前0時の時だけということもあり、テストパターンから漏れる可能性も高いです。

# Format()で文字列化した方が良い

下記のようにFormat()でしっかりフォーマット形式を指定した方が良いです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/ea15af07-2948-20ef-b72a-045ba14b803a.png)

# 蛇足

CStr()で文字列化した時に、年、月、日の区切りはスラッシュですが、これは **この先もずっとこの結果なのでしょうか？** 
『この文字で区切れ』という **明確な指定が無い** 以上、ある日突然『2024-01-01 12:00:02』となってしまう心配は消せません。
そういった意味でもFormat()で明確にフォーマットを指定するのが良いと思います。

# バージョン

Microsoft Windows [Version 10.0.22631.3958]
Microsoft Access for Microsoft 365 MSO (バージョン 2406 ビルド 16.0.17726.20078) 32 ビット
[mac]devcontainerで立ち上げたローカルサーバーに接続できない（built-in port-forwarding）

結論：Docker Desktopのせい。

# 事象

`astro` で静的サイトを作るため、VSCodeの `devcontainer` を利用しました。

`npx astro dev` で起動するとこんなのが出るので

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27987/6b4be5fe-e28c-ecfe-1d9d-560baa0780cf.png)

「ブラウザーで開く」をクリックしても、ブラウザーはグルグルするものの何も表示されない。
開発者ツールで見てみると `<html>` などはレスポンスで来ている様子。

コンテナ内からの `curl http://localhost:4321/` は正常に返ってくる。

VSCodeのポートタブで以下を削除した状態した状態でブラウザから確認すると、ロードもなく即時404が返ってくる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27987/a2601977-2fc6-3096-4b73-2a1573698db8.png)

別途、手動でポート指定してコンテナを立ち上げると外からも正常にアクセスできる。

この時点で、devcontainer側の問題かなあ、とあたりをつけるも該当する情報もなく困り果てていました。

# 原因

根本的な原因はわかりませんでしたが、
「mac vscode devcontainer built in port forwarding not working」キーワードでgoogleさんにきいたところ、以下のissueにたどり着きました。

[4.30.0 update breaks VScode devcontainer auto port forwarding #7276](https://github.com/docker/for-mac/issues/7276)

# 対応

close されていないのでとりあえず記載のワークアラウンド通り、Docker Desktop のバージョンを下げることで対応。

## 手順

[Can I roll back to a previous version of Docker Desktop?](https://stackoverflow.com/questions/62217678/can-i-roll-back-to-a-previous-version-of-docker-desktop) を参考に

[release note](https://docs.docker.com/desktop/release-notes/) から `4.29.0` をダウンロードして、展開後、`Applications` に突っ込んで `replace` を選択。

無事に繋がるようになりました。
AtCoder ABC【D問題 K-th Nearest】Pythonで解説してみた

## D問題　K-th Nearest
この記事では、AtCoderの問題を解説します。
「二分探索」を応用する問題でした。今の私にぴったりな問題なので、記事としてメモ化しておきます。Pythonでの解き方を知りたい方にもピッタリです。


## 問題
https://atcoder.jp/contests/abc364/tasks/abc364_d

公式による解説はこちら
https://atcoder.jp/contests/abc364/editorial/10549

## 解き方のポイント
* 「k番目に○○な△△を求める」という問題には、二分探索が有効。
* fj(x)という関数を考える。これは、a1からa2のうち、Bjとの距離がx以下であるようなものの数。
* fj(x) >= kjを満たす最小のxを求めればいい。
* xの範囲を絞り込むために二分探索が使える。

### 注意
二分探索を行う際、以下の条件が満たされることを保証する必要がある。
* ng は常に不適切な値（条件を満たさない値）を表す。
* ok は常に適切な値（条件を満たす値）を表す。

具体的には、探索中の距離xが ng と ok の間に位置しているとき、条件を満たすかどうかを確認する。
初期状態で ng = -1 とする理由は、距離xの最小値が 0 だから。


## Pythonのコード
```python
import bisect

# 入力を受け取る
n, q = map(int, input().split())
a = list(map(int, input().split()))

# リストをソート
a.sort()

# クエリ処理
for _ in range(q):
    b, k = map(int, input().split())

    def f(x):
        # [b-x, b+x] の範囲内にある点の数がk以上かどうかをチェックする
        lb = bisect.bisect_left(a, b - x)
        ub = bisect.bisect_right(a, b + x)
        return ub - lb >= k

    ng, ok = -1, int(2e8) + 10
    while ok - ng > 1:
        mid = (ok + ng) // 2
        if f(mid):
            ok = mid
        else:
            ng = mid

    print(ok)



```


## 関連記事
* [論理学って何を扱うの？論理学入門](https://qiita.com/kyogoku_meta/items/7dc1c17f78bc36d11346)
* [線形代数の「背景と概念」をもっとよく分かりたい【数学弱者・凡人のための線形代数入門】](https://qiita.com/kyogoku_meta/items/077e9972dcd7b08d6473)
* [弱者・凡人のための競技プログラミング入門『競技プログラミングの鉄則』本を使いこなしたい ①二分探索](https://qiita.com/kyogoku_meta/items/91b32773f045d0ef560b)
* [弱者・凡人のための競プロ入門『競技プログラミングの鉄則』を使いこなしたい！『グラフ』編](https://qiita.com/kyogoku_meta/items/1c735119b0b93cb24d0e)
Javaで改行コード、特殊文字のエスケープをせずに文字列を扱う方法について

# 文字列の扱いについて
Java15からテキストブロック(TextBlocks)が追加になりました。
今まで文字列の扱いについては、改行が必要な場合は改行コードを含める必要があり、特殊文字はエスケープする等の制御が必要でしたが、テキストブロックを利用することで文字列のそのまま表現できるため読み易さが格段に上がります。

正規表現を取り扱う場合は読み易くなったと感じます。
ということで、今回はテキストブロックについて紹介したいと思います。

# テキストブロックについて
テキストブロックはJavaの文字列表現の代替形式となります。**従来の2重引用符が使用できる場所**であればどこでも使用できます。
テキストブロックの使用例について紹介します。

* テキストブロックの構文
```java
    public static void main(String[] args) {

        // 従来の文字列
        String dqName = "Pat Q. Smith";
        // テキストブロック
        String tbName = """
                Pat Q. Smith""";

        System.out.println(dqName.equals(tbName));
        System.out.println(dqName == tbName);
    }
```

* Stringとテキストブロックを結合する場合
```java
    public static void main(String[] args) {

        // 結合
        String str = "The old";
        String tb = """
                the new""";
        System.out.println(str + " and " + tb + ".");
    }
```

* テキストブロック内で改行をする場合
```java
    public static void main(String[] args) {

        System.out.println("""
                This is the first line
                This is the second line
                This is the third line""");
    }
```

* substringを使用する
```java
    public static void main(String[] args) {

        System.out.println("""
                        John Q. Smith""".substring(8).equals("Smith"));
    }
```

* formatted(string.format)を使用する
```java
    public static void main(String[] args) {

        String output = """
                        Name: %s
                        Phone: %s
                        Address: %s
                        Salary: $%.2f""".formatted("John Doe", "555-1234", "123 Main St",
                        50000.00);
        System.out.println(output);
    }
```
# まとめ
Java15から導入されたテキストブロックについて紹介しました。
改行文字を扱う場合にはとても有用な機能となりますので、機会があれば利用してきましょう。
Accessで日付時刻型カラムに午前0時ちょうどの値をセットすると時刻表示が消えてしまう件の対応

# 『時刻表示が消えてしまう』とは

こういったカラムで…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/aaf5bdbe-487b-177a-1177-5dff376013f7.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/b5ed25e8-4e64-e4d9-2c72-934e31a8c909.png)

午前0時ちょうどの値を入力すると…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/04bbfc07-58b7-9fa1-9fcd-af6f0443d6ac.png)

こうなってしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/f85d7808-98e9-8236-8984-e7baefbd8445.png)

これだと『時刻情報が誤りで消えてしまった（欠落してしまった）』とも見えてしまいます。

**午前0時ちょうどでも時刻情報まで表示させたい。**

# やり方

書式に『yyyy/mm/dd hh:nn:ss』を設定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/1160e6fa-bfde-6d1e-0932-83d54d842fbb.png)

設定後はこう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/4ee1d363-4eeb-33cc-7988-8d5334447857.png)

# バージョン

Microsoft Windows [Version 10.0.22631.3958]
Microsoft Access for Microsoft 365 MSO (バージョン 2406 ビルド 16.0.17726.20078) 32 ビット
yukicoder No.52 よくある文字列の問題　解説

[問題文](https://yukicoder.me/problems/no/52)

# 解説
まずは、制約を見てみよう。すると$N\leqq 10$と書いてある。
これを見ると大分bit全探索とかが怪しくなるため、bit全探索が使えるか考えてみる。
ここで、$S$を操作してできた文字列を$T$とする。
今回$T$にpushする方法は$S$の先頭または$S$の末尾。つまり **$2$通りある**。よって**bit全探索の問題**だと確定した。
bitの0,1を次のように仮定する。
・bit=0 → $S$の先頭をpushする。
・bit=1 → $S$の末尾をpushする。
こんな感じで$T$を生成していく。
何種類できるかの重複管理はsetを使うのがおすすめ。
こんな感じで操作を行っていき、最終的なsetのサイズを出力すればOK。
実行時間は$O(2^NN)$。
bitによる管理は$S$の何文字目まで行ったかを管理する変数を用いると実装が楽。

```C++
#include <bits/stdc++.h>
using namespace std;

int main() {
  string s;cin>>s;
  int n=s.size();
  set<string> ans;
  for(int i=0;i<(1<<n);i++){
    bitset<10> bit(i);
    //lおよびrはSのそれぞれ何か所目まで探索したかを表す。
    string t="";int l=0,r=n-1;
    for(int j=0;j<n;j++){
      if(!bit[j]){
        t.push_back(s[l]);
        l++;
      }
      else{
        t.push_back(s[r]);
        r--;
      }
    }
    ans.insert(t);
  }
  cout<<ans.size()<<endl;
}
```
Spring Securityよく使用するアノテーション&メソッド

# Spring Securityでよく使用するアノテーション
1. @EnableWebSecurity
Spring SecurityのWebセキュリティサポートを有効にする。
通常、セキュリティ設定クラスに付与する。

2. @Secured
メソッドレベルのセキュリティ制御を行うためのアノテーション。
指定されたロールを持つユーザーのみがメソッドにアクセスできる。

3. @PreAuthorize
メソッドが呼び出される前にアクセス権をチェックする。
SpEL（Spring Expression Language）を使用して条件を記述する。

4. @PostAuthorize
メソッドの実行後にアクセス権をチェックする。
SpELを使用して条件を記述する。

5. @RolesAllowed
Java EEの@RolesAllowedアノテーションをSpring Securityでサポートする。
指定されたロールを持つユーザーのみがメソッドにアクセスできる。

6. @AuthenticationPrincipal
コントローラメソッドの引数に認証されたユーザー情報を直接注入する。

# Spring Securityでよく使用するメソッド
### 1. HttpSecurity
セキュリティ設定をカスタマイズするために使用されるメインクラス。
各種設定メソッドをチェーンして使用する。
```java
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .antMatchers("/public/**").permitAll()
            .anyRequest().authenticated()
            .and()
        .formLogin()
            .loginPage("/login")
            .permitAll()
            .and()
        .logout()
            .permitAll();
}
```
### 2. AuthenticationManagerBuilder
認証の設定を行うために使用されるクラス。
ユーザー情報の設定やパスワードエンコーダーの設定などを行う。
```java
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
        .inMemoryAuthentication()
            .withUser("user").password(passwordEncoder().encode("password")).roles("USER")
            .and()
            .withUser("admin").password(passwordEncoder().encode("admin")).roles("ADMIN");
}
```
### 3. UserDetailsService
カスタムユーザーデータをロードするためのインターフェース。
loadUserByUsernameメソッドを実装して、ユーザー情報を取得する。
```java
@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // ユーザー情報の取得とUserDetailsオブジェクトの作成
    }
}
```
### 4. PasswordEncoder
パスワードをエンコードするためのインターフェース。
通常、BCryptPasswordEncoderなどの実装クラスを使用する。
```java
@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
```
### 5. SecurityContextHolder
認証情報を取得するためのクラス。
現在の認証されたユーザー情報にアクセスできる。
```java
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String currentPrincipalName = authentication.getName();
```
# 【参考】
https://spring.pleiades.io/spring-boot/
今さらマイクラ1.12でModding(=Mod作成)するための記事

# 全員Mod作れ！！！！！！！！！！
マイクラの動画はたくさんありますが、やはりMod入りは最高。
どうせなら全人類がModを作りやがれ！！と思い立って今さらチュートリアル作り始めました。

私はバージョン1.12(1.12.2)で一番Modを作っていて、今回の開設も1.12.2なので、最新バージョンではない点には注意が必要です。
マイクラのModは基本的に別バージョンでは使えず、1.12.2はModが非常に多いバージョンとなっているので、1.12.2での開発はぜひオススメ。
（最新版の解説もいずれ作りたいところ）

**プログラミング初心者でもModを作れることを目指して記事を書いています。**
**が、やる気のない人は対象ではありません。**

# 必要なもの
- Windows PC(最低メモリ8GB)
    Win11時代は8GB無いとそもそもWindows動かすだけで辛い。
    ただ、8GBだとデバッグが厳しいので12GB以上推奨。
    Macは色々と面倒事に巻き込まれやすいので非推奨。
- Minecraft Java版のアカウント
    現在はJava版と統合版(Bedrock版)のうち片方持ってれば(or買えば)もう片方も無料というありがたすぎる状況なのでぜひ手に入れよう。
- ある程度のPCスキル
    - 「エクスプローラーでファイルの拡張子を表示する」
    - 「ターミナル（Power Shell）で新しいファイルを作成する」
    - 「テキストファイルをUTF-8で保存する」
    等ができない人は訓練してから戻ってきてください。
        少し厳しい物言いにも感じますが、ModdingはPCスキルに乏しいと環境構築（＝スタートラインに立つ）ことすらままなりません。
- 英語が読める（翻訳含む）
    ニッチなプログラミングは日本語の資料が無いことがほとんど。
    Google翻訳でいいので英語の文を読んで理解しよう。
- ネットでの検索力
    上で書いた「ある程度のPCスキル」ができなくても調べてできるようになる人。
- 可能であればプログラミングスキル
    初心者でもできる内容にしてあるが、あるに越したことはない。
    マイクラのコマンド経験でも可。
- プログラムを書くことに抵抗が無い
    マイクラのコマンドに抵抗が無ければ問題なし。

# 環境構築編
Moddingを始めるのに必要な準備
- [ツールの準備](https://qiita.com/Katatsumuri_Pan/items/b6b76096adc9243a49cb)
- [とりあえず動かす（開発環境編）](https://qiita.com/Katatsumuri_Pan/items/10ef2c7ba4e95f01cdfa)
- [とりあえず動かす（ランチャー編）](https://qiita.com/Katatsumuri_Pan/items/706db6223bbf74807797)
- [バージョン管理（Gitローカルリポジトリ編）](https://qiita.com/Katatsumuri_Pan/items/bf544822d79e92fdf009)
- [あった方が良い（が今すぐとは限らない）道具等](https://qiita.com/Katatsumuri_Pan/items/195fa7d94264440ba1b5)

# 初心者研修編
これさえわかれば最低限のModが作れる~~はず~~
- [新規Modの作成からビルドまで](https://qiita.com/Katatsumuri_Pan/items/fec8344fa51b79261381)
- [早速プログラムを書いてみる](https://qiita.com/Katatsumuri_Pan/items/21f4b5f08aadec2eac17)
- [ItemとItemStackの違い](https://qiita.com/Katatsumuri_Pan/items/c5c04973e66a42451c63)
- BlockとIBlockStateとTileEntityは一緒です（大嘘）
- Modを国際化する
- デバッグを実践する
- イベント購読
- 音とか粒子とか
- ワールドとディメンションは何が違うんだ
- 「メタデータ」ってかっこいいな
- Entityを少しだけ操る
- 強敵NBT

# 初級編
コピペするだけで効果付きの新要素を追加できる
- アイテムの追加（無効果アイテム編）
- ブロックの追加（無効果フルブロック編）
- 強化版エンチャントの追加(これは無理かも)
- アイテムの追加（カスタネット編）
- アイテムの追加（食べ物編）
- アイテムの追加（ツール編）
- ブロックの追加（フルブロック光源編）
- ブロックの追加（ハーフ・階段編）
- ブロックの追加（ドア編）
- ブロックの追加（柵編）
- ブロックの追加（花編）
- ブロックの追加（作物編）
- レシピの追加
- Modリリース編
等々…

# 準中級編
ちょいと複雑、自分なりにコードを書く必要が出てくることも
- アイテムの追加（防具編）
- ブロックの追加（アイテム収納編）
- アイテムの追加（バックパック編）
等々…

# 中級編
割とコード書く
- オリジナルエンチャントの追加
等々…

# 準上級編
ここで挫折する人多いから解説したいのさ
- エンティティの追加
- タイルエンティティの追加
- GUIの追加
- 他Mod連携
等々…

# 上級編
ガチプログラミング
- Capabilityの追加
- Message(通信)の追加
等々…

# 特級編
変態
- Mixin
- ASM(CoreMod)

# 記事案
- リソパ復習編
- GitHub編

# Modding用知識
- マイクラバニラ編
- マイクラMod編
- Java編
- Git編
- GitHub編
yukicoder No.51 やる気の問題　解説

[問題文](https://yukicoder.me/problems/no/51)

# 解説
愚直に一日ごとに確かめていくのがいいだろう。
具体的には次のようにする。
まず、これは$i$日目（$i\leqq D-1$）の計算を説明する。
・$floor(W/(D-i+1)^2)$を$W$から引く。
これだけ。
そして、求めたい最終日の作業量についてだが、$floor(W/1)$=$W$なので、$D-1$日目までの作業の残りが答えになる。
よってどんどん$W$を引いていって残った$W$が答えになる。
実行時間は$O(D)$。
$D*D\geqq 2^{31}$なので、long long型を使おう。

# C++での解答例
```C++
#include <bits/stdc++.h>
using namespace std;
using ll=long long;

int main() {
  ll w,d;cin>>w>>d;
  for(ll i=1;i<d;i++)
    //引き算
    w-=floor(w/((d-i+1)*(d-i+1)));
  cout<<w<<endl;
}
```
NooBaa UI (MCGコンソール) が ODF 4.10 まで存在していた

NooBaaについて調べていると、OpenShiftコンソールから起動できるNooBaa UI (Multicloud Object Gateway コンソール) というものが存在しているようでした。しかし、どうしてもそのメニューを見つけられませんでした。

Red Hatのドキュメントから、NooBaa UI は `ODF 4.10` まで存在していましたが、`ODF 4.11` 以降廃止されたことがわかりました。残念

https://docs.redhat.com/en/documentation/red_hat_openshift_data_foundation/4.11/html/4.11_release_notes/removed_functionality


ODF 4.10 のdocumentより　（こんなUIがあったようです）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164968/daf14472-2885-8aff-b37a-929e452a9006.png)

このページの最後にこのような注記があるので、これが原因かもしれないと憶測したりしてます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2164968/743bf579-a36b-1367-7ee7-358264e154bd.png)
【備忘】Codepipelineについて調べてみた

# はじめに

業務の中で CodePipeline に触れる機会ができたので**活動記録** 兼 **備忘**として記事を残すことにしました。

# 前提
AWSについての私の知識量は、Cloud Practitioner 合格に向けて勉強中！のレベルです。
業務で AWS には触れますが仕組みや各サービスの内容について詳しく理解できていません。

https://aws.amazon.com/jp/certification/certified-cloud-practitioner/

# CodePipelineってなに

前提にもあるとおり私は AWS の各サービスの内容について理解が浅いです。
Codepipeline と聞いた時に「なにそれ？」となったので調べて自分なりに解釈しました。

https://docs.aws.amazon.com/codepipeline/latest/userguide/welcome.html


AWS CodePipeline は、コードの作成からデプロイまでのプロセスを自動化し、短時間で確実に、そして効率的にソフトウェアをリリースできる便利なサービスだと理解しました。

# CodePipeline と連携させたいサービス

CodePipeline がどんなサービスかは分かりましたが、今回の業務では実際に使ってみる必要があるので、連携させたいサービスとその連携によって何ができるかを調べてイメージしてみました。

### Cloud9
Cloud9 はアプリケーションの開発やデータベースなどの利用がクラウド環境でできる AWS サービスです。

システム開発の際に開発する人それぞれの環境に同じ開発環境を構築するのが手間なので
せっかくなら Cloud9 で環境構築してその環境で開発したものをコードからデプロイまで CodePipeline でやってしまいたい。

:::note warn
 Cloud9 は現在新規利用について利用制限があります。
同時期に他のサービスについても利用制限が生じているものがあるようです。
:::

上記の状況を受けて、今回は Cloud9 との連携は諦めることにしました。
そこで、環境構築を1から行なって CodePipeline を使用するためだけの環境を作成するのはやめて、すでにシステム開発がスタートしている既存の環境を使用することにしました。

### CodeCommit

> AWS CodeCommit は、プライベート Git リポジトリをホストする、安全で高度にスケーラブルなフルマネージド型ソース管理サービスです。

CodeCommit とはドキュメントやソースコードなどを保存、管理してくれるサービスです。
CodeCommit 上に作成したリポジトリ内へ作成したシステムのコードを置いておきソース管理を行うとともに、リポジトリが更新されたタイミングをトリガーとしてパイプラインの起動を開始したい。

:::note warn
 CodeCommit も Cloud9 と同じく現在新規利用について利用制限があります。
:::

上記の状況を受けて、CodeCommit を使用するのはやめて GitHub へのプッシュをトリガーとしてパイプラインの起動を行うように変更します。

### CodeBuild

>AWS CodeBuild は、ソースコードをコンパイルし、テストを実行し、すぐにデプロイできるソフトウェアパッケージを生成する、フルマネージド型の継続的インテグレーションサービスです。

CodePipeline からのトリガーを受けてビルドを起動し S3 からソースコードを取得してビルドとテストを行いたい。テストが通らなかった場合に CodeDeploy へのトリガーもここで中止したい。


### CodeDeploy
> AWS CodeDeploy は、Amazon Elastic Compute Cloud (EC2)、Amazon Elastic Container Service (ECS)、AWS Lambda、オンプレミスサーバーなどのさまざまなコンピューティングサービスへのソフトウェアデプロイを自動化するフルマネージド型のデプロイサービスです。

CodeBuild と同じく CodePipeline からのトリガーを受けて自動でデプロイしたい。

# まとめ
上記のサービスと CodePipeline を連携させて、社内でサーバレスの web システムを作る場合のテンプレートを作成するのが目標です。

テンプレートを作成して CI/CD を導入し自動化することで、手作業によるミスや削減できる作業を減らし開発の生産性を向上させるために頑張ります。

私個人の目的として AWS のサービスに触れて知識をつけるというのがあるので、CodePipeline 意外にも AWS のサービスを触ってみようと思います。


# 終わりに
まだ実際に触っていないので調査段階ですが、やってみたいことは基本できそうでした。
次は CodePipeline を触る予定があるのでそれについての記事も書く予定です。

今後は使ってみたいサービスに利用制限がかかりませんように、、、、。
Blueskyの埋め込みタイムライン出力サービスを作ってみた（BlueskyTimeline）

## Blueskyの埋め込みタイムライン機能が公式で提供されていない
X（旧Twitter）では埋め込みタイムラインウィジェットが提供されていますが、いつのタイミングからかログインしていない状態ではタイムラインが表示されない仕様になっており、公式サイトなどで日々のお知らせなどを告知する媒体としてはイマイチな状態になってしまいました。

Xと近しいサービスで移住先としてBlueskyがひそかに注目されていましたが、執筆時点でもまだまだ発展途上で整備されていない機能も多く、埋め込みタイムラインウィジェットの提供もない状況であったため、ないなら作るか、ということでAPIを通じてタイムラインを出力するサービスを用意してみることとしました。
## BlueskyTimeline はどんなサービス？
本サービスはBlueskyの非公式タイムラインウィジェットの出力サービスです。
以前にWordpress向けプラグイン **BlueskyTimeline for WordPress** をリリースしていたのですが、Wordpressユーザ以外にも手軽に利用いただけるようコードを張り付けるだけで表示できるようにしたものです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3851019/6735d7da-599d-f3c3-229a-6209cebfa7f7.png">

URL : https://blueskytimeline.com/jp/

## 利用方法
タイムラインを取得したいBlueskyのハンドル名を入力いただきプレビューボタンを押すとタイムラインのプレビューと埋め込み用のコードが出力されますので、適宜サイズやテーマカラーを調整いただき、コードをコピーの上、サイト上のタイムラインを表示させたい位置にペーストするだけで利用いただけます。

<img width="340" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3851019/ebdc7966-f47d-e4c4-84aa-399545373177.png">

<img width="340" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3851019/a60e6955-ad92-dd06-d2f3-d5c6228e2cf2.png">

## 主な仕様
~~タイムラインの取得は本サービス用のアカウント経由でリクエストし、出力させています。~~
cloudflare（CDN）にてキャッシュさせておりAPIに負荷をかけすぎないように~~30分間隔で更新されるようにしています。~~
※無料で利用できるようにしておりますので、ひとまずこれが限界ということで何卒。

:::note info
2024.10.11追記
10分間隔で更新されるように改定しました。
（負荷が高くなった際は戻すかもですが）
2024.10.19追記
リクエストは公開API（public.api.bsky.app）に対して行っている為パスワード認証不要となっています。
:::

### Bluesky公式とタイムラインの表示内容の違い

<dl>
  <dt>投稿日時</dt>
  <dd>Bluesky公式では、現在からの経過時間を <strong>1時間、2日、6か月…</strong> のように表記するのに対して
BlueskyTimelineでは24時間以内の記事は経過時間を表示し、それ以上前の投稿は投稿日 <strong>MM月DD日</strong> と表記するようにしています。<br>これは、サイトの更新情報代わりにタイムラインを利用することを想定した場合に、現在からの経過時間の表記では逆にわかりにくくなる可能性があるためあえて表記を変更しています。<br>
<em>Bluesky</em>
<br>
<img width="320" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3851019/677b67c8-0b1c-1f17-c9fe-d174b443a11a.png">
<br>
<em>BlueskyTimeline</em><br>
<img width="320" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3851019/b52eee4d-3dd4-ed4a-3af8-a8541a834e0b.png">
</dd>
<dl>
  <dt>画像の表記サイズ</dt>
  <dd>投稿された画像が1枚の時は極力トリミングせずに表示させるようにしていますが、ウィジェットの高さを超える場合はスクロールで上下させないと全体を見渡せなくなるため、ウィジェットの高さの85％より大きくなる場合はトリミングするようにしています。
<br>
<img width="320" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3851019/13acc984-31da-99f7-5daa-e2d6d0ed79e2.png">
  
  </dd>
</dl>
<dl>
  <dt>リポスト内の動画</dt>
  <dd>自身が投稿した動画はインライン再生できるようにしていますが、
  あくまで自身のタイムラインを掲載するサービスと割り切って、負荷をあげないよう引用リポスト内の動画はサムネイル表示となり、引用元をBluesky上で再生いただく形式にしております。<br><br>
  <img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3851019/8340b574-2d1b-bed9-00b6-c0e1a6e74447.png">

  </dd>
</dl>

## あとがき
CDNは強力だけどオリジンがしょぼいので利用者が増えてくると結構しんどいかもしれないので、そのうちオリジンサーバの移転も視野に入れるかも。

あと、あくまで非公式サービスなのでAPIのリクエスト数の兼ね合いで万一止められてしまったりすればそれまでなので、そのあたりはご容赦ください。
※というか公式で用意してほしい…！
ゲームデザインにおける3Dスキャンの魅力

ゲームデザインの世界では、リアリズムと独創性が求められます。そこで、3Dスキャン技術が登場し、デザイナーにとって強力なツールとなっています。この技術を使用することで、現実世界のオブジェクトをデジタルに取り込み、ゲームの中でリアルに再現することができます。

例えば、古い城の廃墟や美しい彫刻を3Dスキャンすることで、その細部まで忠実に再現されたモデルをゲーム内に導入することができます。これにより、プレイヤーはまるで現実世界を冒険しているかのような体験ができます。

また、3Dスキャンはキャラクターデザインにも革命をもたらしています。実際の人物をスキャンすることで、顔の表情や体の動きをより自然に表現することが可能です。これにより、キャラクターがより生き生きと感じられ、プレイヤーの感情移入を深めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3851025/9809b1e4-fd58-a37c-ece4-8c4e3e5627b6.png)

さらに、3Dスキャンを使用することで、デザイナーは新しいクリエイティブなアイデアを探求する余裕が生まれます。例えば、自然界のユニークな形状やテクスチャをスキャンし、それをファンタジーの世界に取り入れることで、これまでにない新しい視覚体験を提供することができます。

総じて、3Dスキャンはゲームデザインにおいて、現実の美しさと創造力を結びつける架け橋となります。この技術を活用することで、ゲームの世界はますます豊かで魅力的なものとなり、プレイヤーに驚きと感動を与えることができるのです。

画像と内容の参考：https://www.revopoint3d.jp/blogs/blog/3d-scanners-for-game-development
GitHub Actionsを使ってAWSにSSOログインする方法

# はじめに
今回は、GitHub Actionsを使用してAWSにSSOログインする設定手順を紹介します。
最近、SSOでAWSのログインを行うように切り替えました。切り替えたことにより、GitHub Actionsの設定を変更する必要が出てきたので、GitHub Actions内でSSOのログイン方法を紹介していきます。

Github Actionsを導入しており知見がある方を対象に書いているので、Gihub Actionsnの詳しい設定などは省きます。


# 手順
1. Assume RoleするためのIAMロールの作成
1. Github Actions用のymlファイルの設定


# IAMロールの作成

1. IAM コンソールから「ID プロバイダ」→「プロバイダを追加」を選択します。
2. プロバイダのタイプに「OpenID Connect」を選択します。
3. プロバイダの URL に `https://token.actions.githubusercontent.com` を入力します。
4. 「サムプリントを取得」をクリックします。
5. 対象者には `sts.amazonaws.com` を入力します。
5.  IAM コンソールから「ロール」→「ロールを作成」を選択します。
2. 信頼されたエンティティタイプに「カスタム信頼ポリシー」を選択し、以下のカスタム信頼ポリシーを入力します。


```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::<accountID>:oidc-provider/token.actions.githubusercontent.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
                },
                "StringLike": {
                    "token.actions.githubusercontent.com:sub": "repo:<ユーザーの名前>/<リポジトリ名>:*"
                }
            }
        }
    ]
}
```
:::note warn
上記の権限でログインはできますがリソースの操作に関しては追加で権限をアタッチする必要があるので、使用する際は注意してください。
:::

# GitHub Actionsの設定

```yml
name: aws-login
#トリガーは自由に設定してください
on: [push]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: <environment-name> # 使用する環境の名前を指定します
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-region: 'ap-northeast-1'
          role-to-assume: 'arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.ASSUME_ROLE_NAME }}'

```

# 環境変数の設定

- GitHubに移動し、Github Actionsのenvironment-nameで設定した名前で環境を作成します。
    -  Settings → Secrets and variables → Actions → Manage environment secrets → New environment
-  環境（Environment）を設定します。
    -  環境に名前を付け、必要なシークレットを追加します。
        -  AWS_ACCOUNT_ID（AWSのアカウントID）
        -  ASSUME_ROLE_NAME　（作成したIAMロールの名前）


# まとめ
これにより、指定した環境のシークレットを参照してGitHub Actionsを使用してAWSにSSOログインできるようになります。
例えば、Github Actionsのファイル内で environment: dev を指定すると、dev 環境に設定されたシークレットが参照されます。
DOMとは何か？

# 仕様から考える
DOMの仕様は２つあり、メインは[WHATWG](https://html.spec.whatwg.org/)のものでサブとして[W3C](https://www.w3.org/TR/)の仕様があります。WHATWGの仕様では[1.8 HTML vs XML syntax](https://html.spec.whatwg.org/#html-vs-xhtml)に
> This specification defines an abstract language for describing documents and applications, and some APIs for interacting with in-memory representations of resources that use this language.
The in-memory representation is known as "DOM HTML", or "the DOM" for short.

> (日本語)この仕様は、文書やアプリケーションを記述するための抽象言語と、この言語を使用するリソースのメモリ内表現と相互作用するためのいくつかのAPIを定義しています。
このメモリ内表現は「DOM HTML」、または略して「DOM」として知られています。

となっています。引用先の前後の文脈から抽象言語はHTMLやXMLなどを表すので、DOMとはHTMLやXMLなどにおけるメモリ内での表現の仕方を示す、と考えられます。
一方で歴史的に最初にDOMの仕様を作成したW3Cの[What is the Document Object Model?](https://www.w3.org/TR/WD-DOM/introduction.html)のページによると
>The Document Object Model (DOM) is a programming API for HTML and XML documents. It defines the logical structure of documents and the way a document is accessed and manipulated. 

>(日本語)ドキュメントオブジェクトモデル（DOM）は、HTMLおよびXML文書のためのプログラミングAPIです。DOMは文書の論理的な構造と、文書へのアクセス方法および操作方法を定義します。

となっています。つまりHTMLやXMLの構造を表すのはWHATWGと同じで、W3Cの定義ではそれに追加でAPIの部分もDOMに含んでいる、つまりdocument.querySelector()のようなプログラムもDOMの１つとして定義しています。

## 実装側から考える
実際のブラウザを作成しているブラウザベンダーではどのような解釈をしているのかも考えます。Chrome for Developersの[HTML versus the DOM](https://developer.chrome.com/docs/devtools/dom#appendix)では
>The browser parses the HTML and creates a tree of objects like this: 略 This tree of objects, or nodes, representing the page's content is called the DOM. 

>(日本語)ブラウザはHTMLを解析し、このようなオブジェクトのツリーを作成します：略 このオブジェクトのツリー、つまりノードは、ページの内容を表しており、これをDOM（Document Object Model）と呼びます。

となっており、実際に、ブラウザがHTMLを解析して内部で作成したデータ自体をDOMと表現しています。

## まとめ

以上のことからDOMとは、HTMLなどの情報をメモリ内に保存する際のデータの表現方法、を表すこともあれば、実際にそこから実装されるデータ自体を表すこともあれば、API自体を含むこともあると言えます。そのため文脈によっていろいろ変わるので、結局DOMって何？って聞かれたら、難しいですね。個人的にはHTMLを変換してプログラムから操作しやすいような形にしてメモリに記録したもの、みたいに解釈するのが一番自然で良いのかなと思ったりします。
pnpm execとpnpm dlx

よく`pnpm exec`と`pnpm dlx`のどっちがどっちなのか分からなくなるのでまとめる。

## pnpm exec
プロジェクト内で`node_modules/.bin`に置かれたコマンドの実行や、スクリプトの実行を行うコマンド。

```sh
# node_modules/.bin/vitestを実行
pnpm exec vitest
# websiteワークスペース内でlsを実行
pnpm -F website exec ls
```

既存の名前と衝突しなければ、`exec`は省略できる。
```sh
# node_modules/.bin/vitestを実行
pnpm vitest
# インストールされたパッケージ一覧が表示される(pnpm exec lsではない)
pnpm ls
```

## pnpm dlx
プロジェクト内の依存関係に関係なく、パッケージをレジストリから取得してデフォルトのコマンドを実行するコマンド。

```sh
# storybookの初期化
pnpm dlx storybook@latest init
```

`pnpx`は`pnpm dlx`のエイリアスとして使える
```sh
pnpx storybook@latest init
```

:::note
このコマンドについて調べていると`pnpx`が非推奨になったという話題があった。

確かに`pnpx`は[v6.13.0](https://github.com/pnpm/pnpm/releases/tag/v6.13.0)時点で非推奨になるv7.0.0で削除される予定だったようだが、[v7.0.0](https://github.com/pnpm/pnpm/releases/tag/v7.0.0)では`pnpm dlx`の単なるエイリアスとして使えるように変更されていた。
そのため`pnpx`と代わりに利用することは現在では問題ないと考えている。
:::

特定のワークスペースで実行したい場合は、`--filter`や`-F`で指定するのではなく`--package`を利用する。
```sh
pnpm --package=website dlx storybook@latest upgrade
```

## 判別する

[pnpmとYarn、npmの比較表](https://pnpm.io/ja/feature-comparison)で`pnpm dlx`は`npx`と同列にされていることから、`npx`と同じように末尾が`x`で終わるコマンドは、外部パッケージを利用するみたいに覚えると良さそうに感じた。

:::note warn
`npx`と`pnpm dlx`は「パッケージの直接実行」として同列に挙げられているので、`npx`はローカルに対象のパッケージがあればそれを参照してコマンドを実行する。

同様に[`npm exec`](https://docs.npmjs.com/cli/v10/commands/npm-exec)も`pnpm exec`とは動作が異なるので注意すること。
:::