OmniAICreator/Qiita-1.07M huggingface dataset:
論文メモ：医療系生成AIプロダクトに対するUX調査


# 論文概要
User-Driven Research of Medical Note Generation Software
（医療ノート生成ソフトウェアに関するユーザー主導型調査）
Tom Knoll, Babylon
NAACL, 2022

https://arxiv.org/abs/2205.02549

:::note info
NAACLとは
The Annual Conference of the North American Chapter of the Association for Computational Linguistics
北米で開催される自然言語処理分野のトップカンファレンス
:::

# モチベーション
生成AIの出現により、AIやツールに頼れることの幅、曖昧さに対する許容度、他プロダクトとの親和性から多くの人の「よき友」として生成AIが浸透しつつある。
生成AIが組み込まれたサービス/プロダクトの利用機会は今後格段に増えていくことが想定されるが、開発側に回った際、ユーザーへの導入展開を図っていく際に押さえておくべきポイントは何なのか？


# どんな論文？
- 医師と患者の診察の音声記録から自動的にメディカルノートを生成するために、自然言語処理(NLP)法を用いる研究が増えている
- しかし、それを実際に臨床現場で扱う際にぶつかる課題や、ユースケースを考慮したシステム設計に関する研究は少ない
- 本論文では、医療メモ生成システムの開発を通じて実施された3ラウンドのユーザー調査と3週間のテストランについて紹介する

<img width="400" alt="スクリーンショット 2024-01-23 0.09.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632327/7571b7ac-af16-86cc-63f9-e07ec07b5ccd.png">



# 先行研究と比べて何がすごい？
- 従来の研究は音声認識およびNLPを用いた「医療相談の書き起こし」に対する精度向上を目標としており、人間とシステムとの相互作用（Human-Centered Interaction）には着目していない
- 医療ノート生成のような、自動生成されたコンテンツを人がチェックする"Human in the Loop"システムの開発・導入において、ユーザビリティに焦点を合わせることは重要


# 技術や手法（この場合は調査）のキモは？
- 3ラウンドのユーザー調査実施後に、ノート生成システムを開発し3週間の試用実験（テストラン）を実施
- ユーザー調査の一部では、WOZ（Wizard-of-OZ）を用いて、システムの裏側で臨床医があたかも自動生成したかのようなコンテンツを作成することで、アウトプットに対する評価・FBをクイックに実施した
<img width="500" alt="スクリーンショット 2024-01-23 0.09.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632327/4f380922-cdf3-7bfa-6c74-f2a12a03e593.png">



# 調査によって分かったことは？
- ユーザー観察によって、臨床医は会話メモを取る際に5つのペルソナに分類されることがわかり、そのうち90%が「Touch-Typer」「Sketcher」「Doodler」のいずれかに該当する。つまり**生成AIが支援する方法としてはこの３分類に沿う形でのコンテンツ生成方法が望ましい**
![スクリーンショット 2024-01-23 0.12.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632327/4af0814e-e6a3-2445-df27-00d68cee611f.png)
- 生成AIの精度を確認しながらヒアリングを進められることから、リアルタイムにコンテンツが生成されることがとても重要視される
- WOZ法による調査を通じて、引き続きノート生成のリアルタイム性が必要であることが強調されたほか、初めは生成AIの出力内容を見ながらヒアリングを実施していた医師たち大半は、彼ら自身でもメモを残すようになり、**生成AIは彼らの内容を補完・肉付けする用途で使われるようになった（作業の代替 < 協働・補完）**

# 調査結果に対する課題・議論は？
- ユーザー体験に着目した研究・調査の実施により、これまでは完全なスクリプトに対するノート生成が試みられていた一方で、会話の部分部分からリアルタイムに文章生成を行う必要性が明らかになった
- 患者は自分の症状を大袈裟に言う、都合の悪いことは隠してしまうことがあるため、会話内容をその言葉通りに捉えて要約するとミスリードした結果になってしまう可能性がある
- 専門用語に対する課題




# 所感
***生成AIがもたらすUXの観点***
生成AIに関しては、その多様な機能性とさまざまなツールとの高い親和性により、人間の業務を奪うのではないかという懸念が出ているが、生成AIはルールベースのシステムとは異なり、生成物には曖昧さや不確実性が含まる。そのため、この技術を利用するうえでの本質は、業務を完全に置き換えるのではなく、人間の作業を部分的に省力化するか、補完・補足することにあると考えられる。

生成AIの登場により、サービスやアプリ開発のハードルが下がり、低コストでの試作検証が容易になった。
この変化はユーザーにとっても同じことが言えそう。
プロダクト開発においては、ユーザーが生成結果を迅速に、できればリアルタイムで確認し、再生成や修正が行えることでストレスなく高品質なアウトプットを得ることができることが、ユーザーが求める体験である可能性がある。
（実際自分もChatGPTの出力結果をそのまま使うことはないし、使っていると出力速度に不満を感じてくるので、、、）



***生成AIプロダクト開発におけるUXリサーチの観点***
プロダクトやツールが実際に使われる現場・業務を理解することの重要性は以前から言われているが、生成AI(特にLLM)が「人のように」振る舞えるようになったことで、ローコストで実際の挙動に近いアウトプットを出せるWOZ法によるユーザーリサーチの重要性は増しそう。

今回は臨床医がWizardになったが、専門外の人を配置させることで「学習が不十分なモデル」を実装した場合の挙動の再現や、どの程度の品質までならユーザーも許容できるのかを調査できる可能性がある。



# 本記事のタグ
LTS データ分析・AI事業部では週に一度、ランチを食べながら各自持ち回りで読んだ論文をシェアする回を実施しています。
まとめた内容・メモを内部に留めておくのも勿体無いので、Qiitaを中心に情報発信していくことを見据えて、これらの記事を束ねるタグを考えました。


<font color="#3CB371">**#L4P (LTS Lunch Learn & Lightening talk of Papers)**</font>


まだまだ記事は少ないですが今後増やしていきたいですね。
Test with Mock or Stub


# Not use mock or stub

Here are example. When implemented code is such alogorithm, you can use mock.

```mermaid
graph LR
  Start([Start])-->testcode
  testcode --> implementedCode
  implementedCode--> NotImplementedCode
  NotImplementedCode --> implementedCode
  implementedCode --> testcode
  testcode --> End(End)
```

```mermaid
graph LR
  Start([Start])-->testcode
  testcode --> implementedCode
  implementedCode-->|DB access| DataBase
  DataBase -->|response| implementedCode
  implementedCode --> testcode
  testcode --> End(End)
```

```mermaid
graph LR
  Start([Start])-->testcode
  testcode --> implementedCode
  implementedCode-->|Web request| API
  API -->|response| implementedCode
  implementedCode --> testcode
  testcode --> End(End)
```

You cannot run test due to  **not implemented code**, **database**, **API**.

And when your code is perfect but web server such as DB or API server, you cannot get the correct result. Then, you are difficult to determine whether your code or server is couse.


# Use mock or stub

If you cannot or do not want to access **not implemented code**, **database**, **API**, you can use mock.


```mermaid
graph LR
  Start([Start])-->testcode
  testcode --> implementedCode
  implementedCode--> stub
  stub --> implementedCode
  implementedCode --> testcode
  testcode --> End(End)
```

```mermaid
graph LR
  Start([Start])-->testcode
  testcode --> implementedCode
  implementedCode-->|DB access| mock
  mock -->|response| implementedCode
  implementedCode --> testcode
  testcode --> End(End)
```

```mermaid
graph LR
  Start([Start])-->testcode
  testcode --> implementedCode
  implementedCode-->|Web request| mock
  mock -->|response| implementedCode
  implementedCode --> testcode
  testcode --> End(End)
```

You can run test when you do not implement all code.
Use ChatGPT with iPhone Shortcut App


# Install iPhone chatGPT and shortcut app

You can install app store. Shortcut app is default app, so  you may already installed.

# Open shortcut app

Tap ChatGPT icon on the home.


<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670528/e04166a6-c510-1fd0-14de-1301afaadd7d.jpeg"><img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670528/3a4388cb-fe73-3cf2-1556-3a6d9636f2ba.jpeg">


After that, add ChatGPT shortcut.



# Enable back tap

In setting, Accessibility -> Touch -> Back Tap -> Double[Triple] Tap -> Ask ChatGPT in "SHORTCUTS"



# Success

You can use ChatGPT when you tap the back of the iPhone

<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670528/6fe52f29-9871-193f-dcc7-5c063de37042.jpeg">

<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670528/47ab0323-90af-23b0-8cfe-71de27cc601b.jpeg">
Power AutomateのTeams投稿で絵文字（emoticon）を使う

# 結論
以下の形式のimgタグをTeamsのメッセージに埋め込みます。
```html
<img itemtype="http://schema.skype.com/Emoji" itemid="使いたい絵文字のid" >
```
変数として定義しておくと使いやすいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601327/58519c24-65e0-cb53-d889-894cb2bac8d4.png)
すると、こんな風に絵文字つきのメッセージが届きます。
写真ではわかりませんが、にょきにょき動いています。

![タイトルなし.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601327/068603b7-c181-e35d-27e4-5d0045815e66.png)

# itemidの調べ方
やり方は色々ありますが、ブラウザ版Teamsを使うと楽だと思います。
itemidを知りたい絵文字を入力し、ブラウザの検証ツールでのぞくと見つかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601327/b17fe0e9-d479-4916-6ba9-7e52fa2d0222.png)
猫が笑い泣きしている絵文字のitemidは`1f639_catwithtearsofjoy`のようです。

# 参考記事
[Inserting the emoji's on MS teams to end users using microsoft flows](https://powerusers.microsoft.com/t5/General-Power-Automate/Inserting-the-emoji-s-on-MS-teams-to-end-users-using-microsoft/td-p/1399687)
viteでimportのエイリアス設定

# viteでimportのエイリアスを設定
エイリアス設定
```typescript:vite.cofig.ts
export default defineConfig({
	plugins: [react()],
	resolve: {
		alias: {
			'@': '/src',
			'@components': '/src/components',
		},
	},
});
```

エイリアスを使ってimportしてみる
```typescript
import { Index } from '@components/index';
```

エイリアスでimportできるようになった。

# 問題発生
が、VsCode上では、モジュールが見つからないと言われた:frowning2:

![スクリーンショット 2024-01-23 0.24.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603181/c9e45678-02ce-fc60-41fe-d096d9a3cef1.png)
```
Cannot find module '@components/index' or its corresponding type declarations.
```

# 解決
tsconfig.jsonにも設定してあげないといけないらしい。
```json:tsconfig.json
	"compilerOptions": {

		"baseUrl": ".",
		"paths": {
			"@/*": ["src/*"],
			"@components/*": ["src/components/*"]
		}
  }
```
解決！
![スクリーンショット 2024-01-23 0.28.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603181/85901d64-1e72-70e2-b90f-0256e43bfe68.png)
ProxmoxのVM上で構築したSoftEther VPNのスループットをちょっとだけ上げたりする

# SoftEther VPN について
有名なオープンソースのVPNソフト。

ESXiなどのVM上でVPNサーバーを構築すると、プロミスキャスモードがなんちゃらで怒られますがProxmoxは問題ありません。
しかしやたらめったらスピードが遅いので調べてみたところ、VMに割り当てているNICを`VirtIO`から`Intel E1000`にすると速くなるらしいので設定してみます。

ついでに、デフォルトのNICの設定だとサーバーと同一セグメントにあるローカルデバイスにアクセスできないので、こちらも修正します。

## 作業
### ネットワークデバイスの追加
まず、VPNサーバーを置いているVMにNICを1つ新規に追加します。
このとき、追加するNICは`Intel E1000`を指定してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532025/17e63f57-ca94-5c09-b641-ae2ef56949b7.png)

今まで接続されていたNICは`VirtIO`のままでも構いません。
NICを追加し終わったら、一度再起動してください。

### VPNサーバー NIC設定の変更
次に、VPNサーバー側のNIC設定を変更します。
`nmtui`やらなんやらで、ネットワーク設定を変更します。

先ほど追加した`Intel E1000`に新規コネクションを追加し設定します。
IPv4アドレスを`0.0.0.0/24`に設定し、デフォルトゲートウェイやDNSサーバーの設定は現在の環境に合わせて設定してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532025/e3b645fd-7e42-75da-7530-0a6ca112e6a2.png)

### VPNサーバー SoftEther VPN設定の変更
最後に、サーバーと同一セグメントにあるローカルデバイスにアクセスできるようにします。
SE-VPN管理クライアントか、vpncmdで設定を変更します。

今まで設定されていたローカルブリッジを削除し、新しく追加したNICに向けてローカルブリッジを新規作成します。
作成後は、今まで使っていたNIC宛てのブリッジが削除されているか確認してください。

## 結果
### 設定前のスピードテスト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532025/d9b826e5-5aad-b47b-ba12-ad9fc4454749.png)

上り: 15.81Mbps
下り: 2.54Mbps

### 設定後のスピードテスト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532025/0cdc574c-a990-5232-9aa6-3e42322b5cae.png)

上り: 54.41Mbps
下り: 59.12Mbps

結構スピードが上がりました。
これならば日常使用にも耐えられる程度のスピードだと思います。

### VPN非使用時のスピードテスト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532025/a3b12888-6389-bcae-477a-968211c0b0af.png)

上り: 699.80Mbps
下り: 841.00Mbps

# 参考にしたサイト

https://cd-sioremon.hatenablog.com/entry/2022/06/22/002513

https://zenn.dev/seiwell/articles/e32341cad5ab2a
Softmax関数　コード付き

# はじめに
softmax関数に関して具体的にどのように使われて、どのような特徴があるのか、最後にソフトマックス関数のフォワードメソッドとバックワードメソッドも含めて計算していきます。
# Softmax関数
多クラス分類問題に使用されます。この関数の１つの特徴として
1.全出力層のニューロンが情報を交換し合うことによって確率を定めます。
2.値域は(0,1)
3.入力ベクトルの要素間の差に依存する関数
4.softmax(x) = softmax(z-max zi) 　　（オーバーフローを回避）


出力層のニューロン数をＫクラス数をＣとしたときに、第L層の出力層の入力の総和を用いると式は以下のようになります。(分母がすべてのニューロンを加算している。)
```math
g_k^{(L)} = \frac{exp(h_k^{(L)})}{\Sigma_{n=1}^K(h_n^{(L)})}
```
# ソースコード
```ruby:qiita.rb
import numpy as np
from common.functions import softmax
class Softmax:
    def __init__(self):
        self.params,self.grads =[],[]
        self.out =None
    def forward(self,x):
        self.out = softmax(x)
        return self.out
    def backward(self,dout):
        #Softmax関数の勾配計算
        dx= self.out * dout
        # dxに対して、各データごとに勾配の総和を計算
        sumdx= np.sum(dx,axis=1,keepdims=True)
        # dxを正規化する
        dx -= self.out * sumdx
        # 正規化された勾配を返す
        return dx
```
このメソッドでは、まず self.out に保存されているSoftmax関数の出力に対する勾配 dout を受け取ります。そして、以下の具体的な手順に従って逆伝播を行います。

1.dx を計算:

dx は Softmax 関数の出力と逆伝播してきた勾配の要素ごとの積です。
2.sumdx を計算:

dx の各データごとに、勾配の総和を計算します。これは Softmax 関数の微分において正規化を行うための補正項です。
3.dx を正規化:

dx から self.out * sumdx を引いて正規化を行います。これにより、Softmax 関数の逆伝播が行われます。
4.正規化された勾配 dx を返す:

正規化された勾配が、この Softmax レイヤーの入力に対する勾配として前の層に伝播されます。
nmcliのメモ

nmcliはネットワークインターフェース管理するNetworkManagerの設定をファイルを変更することなくコマンドで設定できるにするためのものです。
（NetworkManagerの設定ファイルは、/etc/NetworkManagerのディレクトリにあります。）

```
cd /etc/NetworkManager
conf.d  dispatcher.d  dnsmasq.d  dnsmasq-shared.d  NetworkManager.conf  system-connections
```

Wi-FiのSSIDを調べる
```
nmcli device wifi list
```

Wi-Fiネットワークに接続する。
```
nmcli device wifi connect [SSID] password [PASSWORD] ifname wlan0
```

コネクション一覧（コネクションのUUIDを調べる。）
```
nmcli connection
NAME                UUID                                  TYPE      DEVICE  
Wired connection 1  d8a5eqcd-164f-355e-9wdc-971bd9w671q0  ethernet  eth0  
```
または省略して
```
nmcli con
```

接続に失敗したりすると接続のUUIDが複数できた場合は不具合が生じやすい、その場合は、UUIDを削除する。
```
nmcli connection delete [UUID]
```
Kubernetes: Fleet + Helmでアプリケーションをデプロイしてみる

# TL;DR

簡単なPythonスクリプトを[Fleet](https://fleet.rancher.io/) + [Helm](https://helm.sh/)でMinikube環境にデプロイしてみました。
この記事ではデプロイ方法およびFleetについて解説します。

この記事で取り扱うPythonスクリプトやマニフェスト等の設定は、以下のリポジトリにあります。
https://github.com/cacapouh/simple-app

# Fleetのインストール

ざっくり言いますと、
Fleetは、GitOpsを活用してコンテナおよびデプロイメントを効果的に管理するエンジンです。

[公式サイトの説明](https://fleet.rancher.io/quickstart)にあるように、以下のコマンドでKubernetes環境にデプロイすることができます。

```shell
helm -n cattle-fleet-system install --create-namespace --wait fleet-crd \
    fleet/fleet-crd
helm -n cattle-fleet-system install --create-namespace --wait fleet \
    fleet/fleet
```

# デプロイ対象のPythonスクリプト

サンプルアプリとして、環境変数`VERSION`を標準出力するものをデプロイします。
以下はそのためのPythonスクリプト, Dockerfileです。

```main.py
import os

if __name__ == '__main__':
    print("version: {}".format(os.environ["VERSION"]))
```

```Dockerfile:Dockerfile
FROM python:3.9

RUN mkdir /work
WORKDIR /work
COPY main.py /work

ENTRYPOINT python main.py
```

以下のコマンドでDockerビルドできます。

```shell:
docker build . -t simple-app
minikube image load simple-app:latest  # イメージをロードしておく
```

# PythonスクリプトをHelm Chartに

`helm create`コマンドでHelm Chartを初期化します。

Helmのテンプレート機能につきましては、以下の記事でも説明していますので宜しければご一読お願いします。

https://qiita.com/tobita_yoshiki/items/b6c37681d43c6ac8a85a

https://qiita.com/tobita_yoshiki/items/ba0513a84c2c8e86f8dd

以下は、Helm Chart内で扱う`deployment.yml`と`Chart.yaml`の設定です。

`deployment.yml`ではPythonスクリプトの方で読み込む環境変数`VERSION`を設定しています。
`VERSION`には`Chart.yaml`の設定値`version`が設定されます。

```yml:deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: simple-app-deployment
  labels:
    app: simple-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: simple-app
  template:
    metadata:
      labels:
        app: simple-app
    spec:
      containers:
      - name: simple-app
        image: "simple-app:latest"
        imagePullPolicy: IfNotPresent
        env:
          - name: VERSION
            value: "{{ .Chart.Version }}"
```

```yaml:Chart.yml
apiVersion: v2
name: helm
description: A Helm chart for Kubernetes
type: application
version: 0.0.1
appVersion: "0.0.1"
```

最後に上記のファイル群を適当なGitリポジトリにpushします。
この記事では、以下のリポジトリにpushしました。

https://github.com/cacapouh/simple-app

# FleetにGitリポジトリを監視させる

Fleetの`GitRepo`というカスタムリソースをデプロイすることによって、
Gitリポジトリの監視設定を加えることが可能です。

以下は https://github.com/cacapouh/simple-app を監視するマニフェストです。
デフォルト設定は`main`ではなくて`master`ブランチを監視するようになっているため注意が必要です。

```yaml:repo.yml
apiVersion: fleet.cattle.io/v1alpha1
kind: GitRepo
metadata:
  name: simple-app-repo
  namespace: fleet-local
spec:
  repo: https://github.com/cacapouh/simple-app
  branch: main
  paths:
  - helm
```

```shell:マニフェスト適用
kubectl apply -f repo.yml
```

# デプロイ結果

以下は、リポジトリ監視設定追加後にデプロイされたアプリケーションのログを確認した結果です。
Podのログに`version: 0.0.1`が出力されましたので、正しく動いていそうです。

```shell
$ kubectl get pods
NAME                                     READY   STATUS      RESTARTS     AGE
simple-app-deployment-75d7b88c6c-2c5k2   0/1     Completed   1 (3s ago)   3s

$ kubectl logs simple-app-deployment-75d7b88c6c-2c5k2 
version: 0.0.1
```

# おわりに

簡単なPythonスクリプトをHelmパッケージとしてまとめて、
Gitリポジトリにpushして、それをFleetに監視させることによってデプロイができました :thumbsup:
Python 3 エンジニア認定基礎試験対策4 - その他の制御フローツール

# はじめに
Python3エンジニア認定基礎試験の対策として自分用に執筆しています。

:::note warn
この記事はPython・執筆学びたての初心者が執筆しています。間違いがあるかもしれませんので、ぜひ各項目にある**参考文献**も確認してください。また、誤りがあればコメントで教えていただけると幸いです。
:::


# その他の制御フローツール  
## if  
```Python  
>>> x = 1  
>>> if x < 0:  
... x = 0  
...     print("Negative changed to zero")  
... elif x == 0:  
...     print("Zero")  
... elif x == 1:  
...     print("Single")  
... else:  
...     print("More")  
```  

0個以上のelifと1個以下のelseを使用できる。

## for

反復を行う。whileとの違いはシーケンス（リストや文字列）にわたって反復を行うこと

```Python
>>> animals = ['cat', 'dog', 'bird']
>>> for i in animals:
...     print(i)
cat
dog
bird
```

`range()`関数を使うと数列を使った反復が可能になる
```Python
>>> for i in range(5)
...     print(i)
0
1
2
3
4

>>> list(range(5,10))
[5, 6, 7, 8, 9]
>>> list(range(0, 10, 3)
[0, 3, 6, 9]
>>> print(range(10)
range(0, 10)
```

## break文、continue文、ループのelse節

`break`文はfor, whileのループを中断する
```Python
>>> a = 0
>>> while a < 10:
...     a += 1
...     print(a)
...     if a == 5:
...         break
1
2
3
4
5
```

`else`節はbreak文でループを終了しなかった場合に実行されるコードグループ。

```Python
>>> count = 0
>>> for i in range(5):
...     if i % 2 == 0:
...         count += 1
...     if count > 4:
...         break
... else:
...     print("偶数は4個以下です")
偶数は4個以下です
```

`continue`文は実行されると現在のループを終え次のループを開始する

```Python
>>> for i in range(1, 5):
...     if i % 2 == 0:
...         print("偶数", i)
...         continue
...     print("奇数", i)
奇数 1
偶数 2
奇数 3
偶数 4
```

## pass文は何もしないという処理を行う

```Python
>>> while True:
...     pass
```

## 関数の定義

```Python
>>> def add(a, b):
...     print(a + b)
>>> 
>>> add(2, 3)
5
>>> print(add(2, 3))
None
```

`def`の後ろには関数名、引数を入れる丸括弧が必要。
関数内での変数は全て新しいシンボルテーブルに入れられる、つまり関数内部だけのローカルの変数になる。

```Python
>>> def mult(a, b):
...     return a * b
>>>
>>> ans = mult(2, 3)
>>> print(ans)
6
```

```return```文を利用すると関数を呼び出したあとに出力ではなく値を返すようにすることができる

### 関数の引数

```Python
>>> def dinner(food, drink="juice", sweet="cake"):
...     print(food, drink, sweet)
>>>
>>> dinner("steak", "milk")
steak milk cake
```

### lambda
名前のない小さな関数を生成する

```Python
>>> lam = lambda i: i * 2
>>> print(lam(10))
20
```

### ドキュメンテーション文字列
```Python
>>> def sample():
...     """Sample Documentation
...     this func is none
...     """
...     pass
>>> print(sample.__doc__)
Sample Documentation
    this func is none
```

## コーディングスタイル
スタイルガイド **PEP8** を守ると読みやすいコーディングができる。
 - インデントには空白4つを使い、タブを使わない
 - ソースコードの幅が79文字を超えないように行を折り返す
 - 関数やクラスや関数内の大きめのコードブロックの区切りに空行を使うこと
 - 可能ならコメントは行に独立させる
 - docstringを使う
 - 演算子の前後とコンマの後に空白を入れ、括弧類のすぐ内側には空白を入れない
 - クラスや関数に一貫性のある名前を付ける。
	 - クラス：UpperCamelCase
	 - 関数名やメソッド名：lowercase_with_underscores
 - UTF-8, ASCIIを使う

# 参考文献
https://docs.python.org/ja/3.9/tutorial/controlflow.html
【React】コンポーネントを初期化する方法


以下のように`key`を使用することで`userId`が更新された際に全てのstateをリセットすることができます。

```jsx
export default function Parent({ userId }) {
  return (
    <Child
      key={userId}
      userId={userId}
    />
  );
}

function Child({ userId }) {
  const [comment, setComment] = useState('');
}


```
初めてのuseEffect。いつ実行されてる？

# useEffectって？
そもそもuseEffectって何？いつ使うの？という疑問があるかと思います。ここでは僕流の考え方で話します。まだまだ浅いですが、内容整理のためのメモを覗いていると思ってください。

useEffectは非同期処理をする際に学びます。※今はuseSWRやNext.jsのSRCなど便利なものが出ていますが、とりあえずここを通りました。

まず、Reactで非同期処理を実装したい場合にasync awaitが使えませんよね。ではどうするか、useEffectの中にasync関数を書きます。形式上はそれで問題ないです。

そもそもなぜuseEffectの中に入れないとダメなのか？それにはuseEffectの中に書いた処理がどのタイミングで実行されるかを見ていきましょう。

useEffectの中に書いた処理がユーザーの画面に表示されるのは、仮想DOMの構築が終わって、実際のDOMへ反映された「後」です。

はい？
仮想DOM？
そうですよね。まずはReactがDOMを構築するまでの流れを知りましょう。

以下のコードがあります。※伝えたいことのためにエラーハンドリング等は省いています。

```react:test.jsx
import React, { useState, useEffect } from "react";

const fetchName = async () => {
  const response = await fetch("http://example.com/api/name");
  // ・・・取得した名前「太郎」を返すAPI
  return response.json().name;
};

export const Test = () => {
  const [name, setName] = useState("");

  useEffect(() => {
    const fetchData = async () => {
      const nameData = await fetchName();
      setName(nameData);
    };
    
    fetchData();
  }, []);

  return (
    <>
      <p>{`apiで呼び出した名前： ${name}`}</p>
    </>
  );
};

```
上記はどのような順番で処理が実行されるでしょうか。

関数はまず初めに以下のようなDOMを構築します。

apiで呼び出した名前：　
※空文字

これはuseEffectが最初のDOM構築の後に実行される、と言う性質のため起こる結果です。
イメージで言うと先ほどの関数から以下の部分のみを実行したようなものです。

```react:test.jsx
export const Test = () => {
  const [name, setName] = useState("")
  
  return (
    <>
      <p>{`apiで呼び出した名前：　${name}`}</p>
    </>
  )
}
```

最初のDOM構築時点でuseEffectは実行されないので、nameの値はuseState("")部分で定義した初期値の空文字になります。そして「apiで呼び出した名前： (空文字)」がDOMへ反映された後、useEffect内の以下の部分が動きます。

```react:test.jsx
const fetchName = async () => {
  const response = await fetch("http://example.com/api/name");
  // ・・・取得した名前「太郎」を返すAPI
  return response.json().name;
};

const [name, setName] = useState("")

 useEffect(() => {
    const fetchData = async () => {
      const nameData = await fetchName();
      setName(nameData);
    };

    fetchData();
  }, []);
```

ここで初めてfetchが行われ、setNameに取得した値がセットされます。そしてこの後Reactは再度DOMを構築します。すると出力結果は以下です。

`apiで呼び出した名前： 太郎`

useEffectの中身が実行され、`setName(nameData)`の部分で更新されています。では、ここまでのレンダリングの流れで最低限この流れがわかれば理解が進むことを書きます。


## useEffectが実行されるまでの流れ
ｓ1\. ReactはuseEffect以外の部分を読み取ってDOMを構築します。(初期レンダリング)
2\. useEffectの中身を実行、完了後3へ
3\. 再レンダリング（DOMの再構築）を行い、2の実行結果がDOMへ反映される。
***

このような流れになります。
タイミングの感覚だけでも掴んでもらえれば十分です！
お次はそもそもなぜuseEffectが必要なの？
useEffectの第二引数にある配列[]って何？
などについて書ければと思います。

独学でまだまだわからないことだらけなのでエンジニアの皆様方ご指摘等ぜひお待ちしておりますm(__)m
チベット語モデルTiBERTは「དཔོན་དམག་དང་གཟིགས་སྐྱོང་བྱེད་ཡུལ་གྱི་ཁྲིམས་མཐུན་ཁེ་དབང་ལ་སྲང་སྐྱོབ་བྱེད་པ།」をどうトークナイズするのか

[一昨日の記事](https://qiita.com/KoichiYasuoka/items/1c51fe9bd4ce16476470)の`BertTokenizerFast`を改造して、Yuan Sun, Sisi Liu, Junjie Deng, Xiaobing Zhao『[TiBERT: Tibetan Pre-trained Language Model](https://arxiv.org/abs/2205.07303)』のTABLE Iの例文「དཔོན་དམག་དང་གཟིགས་སྐྱོང་བྱེད་ཡུལ་གྱི་ཁྲིམས་མཐུན་ཁེ་དབང་ལ་སྲང་སྐྱོབ་བྱེད་པ།」を、何とかトークナイズできるようにしてみた。Google Colaboratoryだとこんな感じ。

```
!test -f TiBERT.zip || curl -LO https://tibert.cmli-nlp.com/model/TiBERT.zip
!test -d TiBERT（初始） || unzip TiBERT.zip
from transformers import BertTokenizerFast
from tokenizers.pre_tokenizers import Metaspace
tkz=BertTokenizerFast(vocab_file="TiBERT（初始）/vocab.txt",strip_accents=False)
tkz.backend_tokenizer.pre_tokenizer=Metaspace(add_prefix_space=False)
tkz.backend_tokenizer.model.continuing_subword_prefix=tkz.backend_tokenizer.decoder.prefix=""
print(tkz.convert_ids_to_tokens(tkz("དཔོན་དམག་དང་གཟིགས་སྐྱོང་བྱེད་ཡུལ་གྱི་ཁྲིམས་མཐུན་ཁེ་དབང་ལ་སྲང་སྐྱོབ་བྱེད་པ།")["input_ids"]))
```

私(安岡孝一)の手元では、以下の結果になった。

```
['[CLS]', 'དཔོན་དམག', '་དང་', 'གཟིགས་སྐྱོང་', 'བྱེད', '་ཡུལ་གྱི་', 'ཁྲིམས་མཐུན་ཁ', 'ེ', '་དབང་', 'ལ་', 'སྲང', '་སྐྱོབ', '་བྱེད་པ', '།', '[SEP]']
```

うーん、論文では「དཔོན་དམག」「་དང་」「གཟིགས་སྐྱོང」「་བྱེད་ཡུལ」「་གྱི་」「ཁྲིམས་མཐུན་」「ཁེ་དབང་ལ་」「སྲང་སྐྱོབ」「་བྱེད་པ」「།」とトークナイズされているので、かなり結果が違う。やっぱり、ちゃんとSentencepieceのトークナイザを手に入れないとダメかな。
C++で後の行でのみ宣言している関数をそれより前の行で呼び出す

例のSNSで、こんな投稿を見つけました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">C++って後の行で宣言してる関数をそれより前で呼び出せないのか<br>やり方あるのかもしれんけど意外</p>&mdash; とろちゃ🐉 (@trooooche) <a href="https://twitter.com/trooooche/status/1749399536840368233?ref_src=twsrc%5Etfw">January 22, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

これに対しプロトタイプ宣言に言及する返信が複数ついていますが、**プロトタイプ宣言は宣言です。**
よって、「後の行で宣言してる関数」という条件を骨抜きにしてしまい、つまらないです。
とはいえ、「後の行で宣言してる関数」という条件だけで、「前の行で宣言していない」という条件は無いので、無効ではないでしょう。

さて、C++で後の行で宣言し、前の行では宣言していない関数を呼び出すことはできます。
たとえば、**条件付きコンパイル**と**自分自身のインクルード**により、宣言を呼び出しより先にコンパイルするようにすることで実現できます。

```c++
#include <iostream>

#ifdef IS_2ND_STAGE

int main() {
    test(); // 関数の呼び出し
    return 0;
}

#else

void test() { // 関数の宣言 (兼定義)
    std::cout << "hello from test()!\n";
}

#define IS_2ND_STAGE
#include __FILE__

#endif
```

[[C++] gcc 13.2.0 - Wandbox](https://wandbox.org/permlink/vgvxPo2qkvdlDsnK)

また、**関数ポインタ**を使うことでもできます。

```c++
#include <iostream>

void helper(void (*func)()) {
    func(); // 関数の呼び出し
}

void test() { // 関数の宣言 (兼定義)
    std::cout << "hello from test()!\n";
}

int main() {
    helper(test);
    return 0;
}
```

[[C++] gcc 13.2.0 - Wandbox](https://wandbox.org/permlink/vYxdfDbIXqbcsAsV)

なお、これらしか方法が無いとは限らないでしょう。
Seaborn HeatmapのRGB値を取得する方法

## 概要
seabornで作成したheatmapのRGB値を取得します。
matplotlibのcmでheatmapと同じcolormapを指定し、cmapにheatmapで描画したデータをあてはめることでRGB値を取得しています。

## 実装

```Python
import numpy as np
from scipy import stats
import seaborn as sns
from matplotlib.colors import Normalize
import matplotlib.cm as cm

matrix = np.random.rand(3, 3)

heatmap = sns.heatmap(matrix,
                      cmap='plasma',
                      xticklabels = [""],
                      yticklabels = [""],
                      cbar_kws={"orientation": "horizontal"},
                      annot=True,
                      fmt=".1f")

cmap = cm.get_cmap('plasma')
data = matrix.flatten()
norm = Normalize()
rgba_values = cmap(norm(data))
for i in range(len(rgba_values)):
  print([round(255*x) for x in rgba_values[i]])
```

## おわりに
Heatmapのobjectから直接RGB値を取得する方法があればよかったのですが、ここは調査不足で、また調べてみようと思います。
関数をリスト変数の要素とする（AppleScript）

こんにちは。
AppleScript のリスト変数の中に関数 (user handler) を入れ、動作することを確認しました。


```applescript
set i to 1
set f_ to item i of {f1, f2}
get f_("w") -- results in "w_1"

-- functions (user handlers)
on f1(x)
	return x & "_1"
end f1

on f2(x)
	return x & "_2"
end f2
```

* なお下記のように書くと構文エラーとなりました（```end of line などがあるべきところですが“(”が見つかりました。```）。
```applescript
get (item i of {f1, f2})("w")
```
コンポーネントの凝集原則 - リリース・再利用等価原則(RREP)

# 背景
コンポーネントが運用していく中で徐々にメンテ性だけではなく、
再利用性の観点(全再利用原則)の考察の優位性も出てきた際に、
再利用の単位でまとまっていてほしいし、かつ
メンテ性も維持しておきたいので、コンポーネントが細かく分割せざるを得ない。
すると、分けたコンポーネントのリリースの単位の考察も必須になってくる。
この考え方は、サービス設計の際にも重要な思想。

# 再利用・リリース等価原則
アジャイルソフトウェア開発の奥義には以下のように書かれている。

--引用--
「再利用の単位が、リリースの単位よりも小さくなることはない。」
--引用終わり--

再利用したいコンポーネントの内部の要素に、再利用したいものとそうでないものとが
混在しないように設計せよ　っていう思想はCRPと共通であると感じる。

## 何をいってる原則か？
ここでは簡単のためにパッケージの事例で考えることにする。
クライアントパッケージaから見て再利用したいあるパッケージbのうち、
クラスX、Yはリリースver1.0のを使いたいけども、
クラスSはver1.2のを使いたいとかっていう時、
当たり前だけどクラスXとYは同じパッケージにまとめるけども、
Sに関しては別のパッケージに分けたくなるっていう原則。

<img width="400" alt="リリース等価.png (8.3 kB)" src="https://img.esa.io/uploads/production/attachments/5827/2024/01/22/135213/76ea5b9e-13ba-4575-af2d-7855c601b63d.png">


つまり、再利用という観点でまとめられたコンポーネントを構成する
クラスやモジュールなどは、まとまってリリース可能な単位でないといけないってこと。

## なぜ守るべきなのか？
もしも纏められ方が、同じVer番号を共有し、同じリリースプロセスを経ていなかったら
どのような問題があるだろうか？

そもそもリリース番号が振られていなかったりすると、互換性の担保ができなかっらりするし、
変更が何度か入ったコンポーネントの何個手前までのものならサポートしているのか？
さえ追跡が不可能になってしまう。

仮にVerが振られている状態でも、
パッケージaから見て、パッケージbのVerがVer1.2となっていたとしたら、
aからすれば詳細な中身を見ずとも、中身のクラスはすべてVer1.2の状態であってほしい。
にもかかわらず、一部分だけ古いVerのものが混在するような状態では、
めちゃくちゃ再利用時の使い勝手が悪いことは自明である。

それこそ、パッケージbの蓋開けて、すべてのクラスを見るんかい！ってなるから
情報隠ぺいもへったくれもない。
クライアントパッケージはそんなこと意識せずとも、利用したいわけだから、
リリース番号が同じ単位で、クラスやモジュールはまとまっているべきという、
この原則の発想はいたって自然なことである。
Visual Studio Codeが仮想環境のカーネルを認識してくれない

# 環境
- Macbook Pro 
- Visual Studio Code
- Poetry 
- Pyenv

# 仮想環境をactivateしたのに、カーネル一覧に今さっきactivateしたカーネルが出てこない

以下のように仮想環境をactivateした
```bash
$ poetry shell
(virtual-env-name)$
```

なのにカーネル一覧に今activateした`virtual-env-name`くんが出てきてくれない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1152756/86f36fa3-62c1-7fd8-ccc6-8eb68e45b43a.png)

# 開いてるフォルダ階層をプロジェクトフォルダにしたら認識してくれた

vscode上で開いているディレクトリ階層が親すぎた。
`project_dir`の一つ上のフォルダ階層を開いていた。
```
.
├── other_dir0
├── other_dir1
└── project_dir
```

VSCode上の`open folder`からちゃんとプロジェクトフォルダを開き直したらプロジェクトフォルダでactivateした仮想環境のカーネルを認識してくれた！
WSL2 を使わずに、Docker CLI の準備

# 背景

- [Azure Sample](https://dev.azure.com/Remote4M/AzureSamples/_git/jp-azureopenai-samples.git?path=/5.internal-document-search&anchor=%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E5%88%9D%E6%9C%9F%E5%8C%96) を実行する際に、Docker が必要だった。
- 条件に、wsl2 では動かない、とあったので、Windows powershell 上で Docker を使う必要があった。
- 会社で使うことを想定していたので、無償版の Docker CLI で準備したかった。サンプル用なので。本気で使うなら有償版使いましょう。

ということで、
- Docker CLI の準備の記録
    - ただ、本当の CLI は、DockerCLI.exeであって、現状は Docker Desktop にバンドルされているので、これを何と呼ぶのかよくわからず・・

実際には、以下理由でお蔵入り・・
- Linux Container on Windows は開発中止していた
- 動かそうとしてたプロジェクトには Docker 不要だった。（個別に動かせばよかったので・・ :(

そう、これは愚かな備忘録 :p

# Docker インストール

Docker Desktopでインストールすると、有償となる為、Docker CLI としてインストールする

## docker 事前準備

- 以下二点を☑してから再起動
  - Hyper-V
      - [Windows 10 上に Hyper-V をインストールする](https://learn.microsoft.com/ja-JP/virtualization/hyper-v-on-windows/quick-start/enable-hyper-v)
  - Container: こっちは本当に必要か？はよくわからず・・
      - [作業の開始:コンテナー用の Windows を準備する](https://learn.microsoft.com/ja-jp/virtualization/windowscontainers/quick-start/set-up-environment?tabs=dockerce#prerequisites)

:::note warn
- Windows Home では不可
:::

## Docker CLI? インストール

1. 利用可能なバージョンを以下で確認してダウンロード
   - [windows stable の一覧](https://download.docker.com/win/static/stable/x86_64/)<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/66c6045a-f945-c923-dc09-61048aa11bb2.png)

       - Linux や preview などもある
1. zip ファイル内の docker フォルダを、`%programfiles%\docker` へ解凍
    - 中に含まれるのは、以下三ファイル<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/86bfc072-4762-5704-4a5e-d5e83db20b02.png)
       
1. 環境変数 path に `%programfiles%\docker` を追加


## Docker を動かす

1. Docker 起動
   - `dockerd` を管理者権限のある powershell ターミナルで実行
   - 別の Powershell ターミナルで `docker run hellow-world` で動作確認

## Linux Container on Windows (LCOW)

こちらは wsl2 以前に開発が進んでいたけど、結局開発中止になっていた

dockerd -experimental=true 関連で動かそうとしたけど、不要になったので未調査
dockercli であれば、SwitchDaemon でやれそうだったので、その辺りが関係するのかも

https://forums.docker.com/t/cli-to-switch-between-linux-and-windows-images/30297/8

https://github.com/linuxkit/lcow

# あとがき

おそらく二度と使うこともないであろう機能だけど、せっかく調べたというだけで備忘録にした。
他に Azure とかまとめるべきなのに、ね・・ :p

いいなと思った、チートシートへのリンクで締め [Docker CLI cheat sheet](https://docs.docker.com/get-started/docker_cheatsheet.pdf?_gl=1*thh703*_ga*ODkxNjUxNjkuMTcwNTg5MTMyMw..*_ga_XJWPQMJYHQ*MTcwNTg5MTMyMy4xLjEuMTcwNTg5MjA4OC4zMi4wLjA.)
【学びメモ】 enumで2つのクラス共通で利用するメソッドを定義する

# 本記事について

この記事では、2つのクラスで共通して利用するメソッドを定義する方法について紹介します。

提案するアプローチはあくまで一つの意見であり、唯一の正解ではありません。プロジェクトや状況によって最適な方法が異なることを考慮に入れ、参考にしていただければと思います。

# シナリオ


画面Aと画面Bでは、特定の要素のカウント数に基づいて、デフォルトで用意された数値の配列を返すメソッドが定義されています。

```swift
func makeDefaultNumbers(elementCount: Int) -> [Int] {
    switch count {
    case 1:
        return [100]
    case 2:
        return [50, 50]
    case 3:
        return [34, 33, 33]
    default:
        return []
    }
}
```

課題として、仕様変更時に変更を見逃す可能性を減らすために、共通化したいです。

# アプローチ


enumを使用しています。具体的な実装は以下の通りです。

```swift
enum ElementPercentageGenerator {
    static func makeDefaultPercentages(count: Int) -> [Float] {
        switch count {
        case 1:
            return [100]
        case 2:
            return [50, 50]
        case 3:
            return [34, 33, 33]
        default:
            return []
        }
    }
}
```

```swift
let percentages = ElementPercentageGenerator.makeDefaultPercentages(count: 3)
// [34, 33, 33]
```


ケースの定義はありませんが、static funcを使用して静的メソッドを定義しています。これにより、インスタンスを生成せずにメソッドにアクセスできます。

「インスタンス化しない」とは、状態を持たなくなることを意味し、そのenumがイミュータブルであることを示します。イミュータブルな状態は副作用を防ぎ、バグの可能性を低減させる利点があります。


以上です。
Stripe 公開可能キーやシークレットキーを確認する方法

## 概要

Stripeの公開可能キーやシークレットキーを確認する方法を簡単にまとめる。

## 方法

1. Stripeにブラウザからログインする。
1. ダッシュボード上部の「開発者」をクリックする。

    ![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3330363431372f65303332386136302d383735342d663863312d373930302d6539626135653736633435342e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/b5d3b5cd-374e-d4c6-95cf-b832c579fd2c.png)

1. 「APIキー」をクリックする。

    ![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3330363431372f33613331663831652d316561642d663134322d626163302d3861313161653166326661372e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/2adc56c3-0614-2cae-8fac-062a9fcc8568.png)

1. 「標準キー」の部分に公開可能キーとシークレットキーが表示されている。

    ![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3330363431372f61396364343034332d326133362d353336372d653735352d3163313734373039626533392e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/0825f9f2-a183-234f-fea0-4437a0e86f91.png)
Tera Term によるクリップボードの内容の破壊を防ぐ

[Tera Term](https://teratermproject.github.io/) を使っていると、時々**クリップボードの内容が破壊される**ことがありました。

よく見てみると、操作のブレなどにより Tera Term の端末画面内のテキストが選択されると、**勝手に**クリップボードの内容を選択された文字列に置き換え、**もとのクリップボードの内容を消し去る**という**迷惑行為**が行われているらしいことがわかりました。

この記事では、そんな**迷惑行為**を止める方法を紹介します。

# 環境

* Windows 11 Home 22H2
* Tera Term Version 5.1 (Git 9ffa9f6b0)

# 方法

1. Tera Term を起動している場合は、終了します。
2. テキストエディタでファイル `C:\Users\(ユーザー名)\AppData\Roaming\teraterm5\TERATERM.INI` を開きます。
3. `AutoTextCopy=on` という行を探します。
4. `on` を `off` に書き換え (`AutoTextCopy=off` にし)、上書き保存します。

# ポイント

今回用いる `AutoTextCopy` の設定は、`[Tera Term]` セクション内にあります。

Tera Term をインストールしたディレクトリにもファイル `TERATERM.INI` がありますが、これではなく前述の `AppData` 内のファイルが参照されるようです。
Kaggle参戦記：締め切り前夜、最終提出の作戦を練る

こんばんは。「[AIの文章と人間の文章を見分けるコンペ](https://www.kaggle.com/competitions/llm-detect-ai-generated-text)」も、いよいよ明日の朝が締め切りです。私はすでに最後のsubmitも使い果たし、あとはどのバージョンを最終提出版とするか決めるだけになっています。スコアは0.973で打ち止め。パブリックLBでの順位はじりじりと下がって今は21位。残念ながら金メダル圏内にはとどまれませんでした。まあ、それでも初参加にしては上出来だったかな。

# LBスコアの推移
ここまでのLBスコアの推移を振り返ってみます。[1/6に参加を決めて](https://qiita.com/rho-guy/items/e6bc4ce56a4d85c01655)、初回に[スコア0.96のNotebook](https://www.kaggle.com/code/erikfolkesson/detailed-explanation-custom-tokenizer)でエントリーを開始。そこから諸々の改良を加えながらLBスコアを伸ばしてきました。グラフの横軸はSubmitの回数です。Submitは1日5回までなので、横軸の数値を5で割ればだいたい参加からの日数が分かります。グラフを見ると、時々数値を乱高下させながらも、じわりじわりとLBスコアを伸ばしてきた様子が分かります。

![LBスコアの推移.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3666147/8da82b86-dc43-a334-98bf-d2eefb203809.png)

最初の2～3日は、まだ感覚がつかめてなくて、「正常動作させる」ことにも一苦労していました。投稿後の[Out of Memoryエラーによるノースコアに泣かされていた](https://qiita.com/rho-guy/items/8668827513f226920310)のもこの頃です。また、張り切って追加した特徴量やロジックの改造なども逆効果になって、LBスコアを大幅に下げてしまうこともたびたびありました。

5日目くらいから、ちょっと慎重にパラメータ選択をやるようになって、少しコツをつかんできた感じです。着実にLBスコアを刻み始め、ランキングをぐんぐん上昇させることができました。今振り返ると、この頃は非常に楽しかったですねえ。

そして10日目あたりから、再びメモリの壁にぶち当たりました。これまで行ってきた精度改善方法がスケールしなくなり、新たな方策を模索する必要が出てきました。また、この頃から「LBよりもAUCを優先したチューニング」を志向するようになってきました。そのあたりの方針転換と試行錯誤の跡がLBスコアのダウンという形で見えていますね。

そして最後の3日間くらいは、「[運営が仕込んだノイズ](https://qiita.com/rho-guy/items/26c53dd8ecb2e50e4be7)への対応策」などで右往左往していたため、LBスコア的には停滞していました。この頃はデータの前処理に、仕込まれたノイズを除去するロジックを実装しながら「俺は一体、何をやらされているんだろう」と、モチベーション的にもかなり停滞していました。

当初は「ベースラインにしたNotebookからまったく精度が上がらない」事態もありうると想定していましたが、結果的に、0.96⇒0.973と、0.013ポイント、なんとか自力でアップできて良かったです。

# AUCの推移
Submit前に手元データをtrainとtestに分割し、Save&Run環境で走らせた時のAUCの推移は下記のグラフのようになっています。最初の数日間は、この「ローカルでの検証精度」も計測していなくて、いきなりぶっつけ本番でSubmitしていたため、AUCのスコアが残っているのは20回目くらいのSubmitからです。今思えば、もっと早くから手元でAUCを評価・記録しておけばよかった。

![AUCの推移.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3666147/d1fbf3e4-89a9-fad1-6d35-bde6f6ae9ca9.png)

先日、「[AUCの値がどうどうめぐりしていた](https://qiita.com/rho-guy/items/dec24d32bc6ed366e9c2)」とお伝えした通り、わき目もふらずLBスコアを改善していた頃（10日目あたりまで）は、AUCの値は0.999付近で行ったり来たりしていました。で、そこから頭を切り替えて、AUCの方をチューニングしていき、最終的には0.9997あたりまで向上させることができました。9が並びすぎて、過学習かデータリークを疑うレベルなのですが、自信をもって状況を改善できる策もなく、そのまま突き進んできました。ええい、ままよ。

# LBスコアとAUCの分布
LBスコアとAUCを散布図にしてみると、下記のようになります。大きな外れ値が2、3ありましたので、それらは除外しています。相関係数は0.4くらいで、それほど強い相関は見られません。ただ、AUCを高めていこうとすると、ちょっと負の相関っぽい傾向が見えてきます。そのため、10日目くらいまでは「AUCが高くない！これはLBスコアが期待できる！」とまで思い込んだりしていました。そのため、AUCがどうどうめぐりをしていたわけです。
![散布図②.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3666147/315e4def-2440-0cbc-c3c4-97b050070e68.png)

10日目以降、ソースコードをLBスコア追及用、AUC追及用と、2つに分けて、別々の指標でチューニングしていったので、LBスコア追及用はAUCが0.9990あたりに落ち着くかなと思っていました。しかし、結果的に、LBスコア追及用でも0.9995を超えるAUCが出せたので満足です。最終的に、赤丸で囲ってある3つを最終版として提出することにしました。これら3つのバージョンは、(A)LBスコア最高版、(B)AUC最高版、(C)AUC最高版に運営のノイズ対策を施した版となりました。どれも、それぞれに思い入れの強いバージョンです。プライベートLBでも、ぜひ、健闘して欲しいものです。

また結果が出たら、詳しいロジックや手法などもお話しようと思います。まずは、Kaggle初参加のコンペを完走できたことに感謝です！
WordPressでwp-adminが急に無効化された時の対応

# 結論
[WPS Hide Login](https://ja.wordpress.org/plugins/wps-hide-login/) が悪さをしていたっぽい

# 環境
Windows11
Docker Desktop
AWS Lightsail（今回トラブルの原因とは特に関係ない）

# 経緯
1. Docker Desktopを使って `localhost:8000` にWordPressを構築
1. ある程度コンテンツも固まったので、AWS LightsailでWP環境を作成する
1. ローカルからデータ引っ張ってくるのを忘れたので、 `localhost:8000/wp-admin` にアクセスしようとしたら __404に飛ばされた__

## なんでそんな七面倒なことを
AWS Lightsailを使わずローカルで構築すれば無用な課金を避けられるからです。
まあ、1カ月かけて作ったところでドメイン代込みで(.net や .com程度なら) __$5.00程度__ で収まるんですけどね！！

# 解決方法
[こちら（外部サイト）](https://auto-worker.com/blog/?p=6336) が参考になりました。

1. コンテナ名をクリックする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2919779/75516094-d7c4-1afc-e613-c5d68a0d851b.png)
　
1. ターミナルで開く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2919779/bc418f63-4c12-662e-612e-6d15c0c82091.png)
　
1. wps-hide-loginをリネームする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2919779/2e471c28-c720-d80c-4f63-831af69997a2.png)
`mv wp-content/plugins/wps-hide-login wp-content/plugins/wps-hide-login_`
docker desktopで構築しようとしている人でしたら理解できると思いますので、詳細は省きます。
　
1. 管理者ページにログインする
`localhost:8000/wp-admin` にアクセスできるようになります。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2919779/40af337a-56c0-a534-98b6-fb460d4e673e.png)

# 原因はよく分からない
このプラグインはwp-adminを無効化することでセキュリティを上げる効果が見込めるものです。
ただ前回ログインから24時間経過することで認証用のCookieが消えるらしいので、それが原因でリダイレクトされなくなった？？
詳しい方いたら教えていただけると助かります。ちなみに管理ページを手動で変えた記憶はありません･･･。
【SwiftUI】safeAreaPaddingでScrollViewの開始位置と終了位置を調整する

# はじめに
`ScrollView`に`padding`を付けると`ScrollView`が小さくなってしまいます。
しかし、`safeAreaPadding`だと`ScrollView`は小さくならず、コンテンツのみにスペースが入っているっぽいです。

# 比較
`padding`だと`ScrollView`が小さくなって途中で途切れているのがわかります。
|`padding`|`safeAreaPadding`|
|-|-|
|![Simulator Screen Recording - iPhone 15 - 2024-01-22 at 21.59.53.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/8b3cb8e8-1e23-420e-62e8-b074853d90c5.gif)|![Simulator Screen Recording - iPhone 15 - 2024-01-22 at 22.00.31.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/fa0de703-4f7e-1bbd-238e-18aa80e4cd62.gif)|

# paddingでの実装
```swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            HStack(spacing: 10) {
                ForEach(0..<10) { _ in
                    RoundedRectangle(cornerRadius: 10)
                        .foregroundStyle(.cyan)
                        .frame(height: 60)
                        .containerRelativeFrame(.horizontal)
                }
            }
        }
        .padding(.horizontal, 20)
    }
}
```

# safeAreaPaddingでの実装
```swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            HStack(spacing: 10) {
                ForEach(0..<10) { _ in
                    RoundedRectangle(cornerRadius: 10)
                        .foregroundStyle(.cyan)
                        .frame(height: 60)
                        .containerRelativeFrame(.horizontal)
                }
            }
        }
        .safeAreaPadding(.horizontal, 20)
    }
}
```

# 公式ドキュメント
https://developer.apple.com/documentation/swiftui/view/safeareapadding(_:)-5lh9p?changes=_6
オブジェクト指向とクリーンアーキテクチャ: ソフトウェア開発の基本原則

## はじめに
みなさんこんにちは！
今回は初心に戻ってオブジェクト指向とクリーンアーキテクチャについてまとめようと思います！
ソフトウェア開発において、オブジェクト指向とクリーンアーキテクチャは基本的な原則として広く受け入れられています。本記事では、これらの概念について解説し、ソフトウェアの設計やメンテナンスにおいてなぜ重要なのかについて考察します。
オブジェクト指向の基本原則
カプセル化: データとそのデータを扱うメソッドをオブジェクトにまとめ、外部からのアクセスを制限すること。これにより、オブジェクトの内部実装を隠蔽し、変更に強いコードを実現する。
## オブジェクト指向の基本原則
### 継承: 
既存のクラスを利用して新しいクラスを作成すること。コードの再利用性を高め、階層構造を形成する。
### ポリモーフィズム: 
同じメソッド名を使用し、異なるクラスで異なる実装を提供できること。これにより、柔軟で拡張性のあるコードを構築できる。
### 抽象化:
共通の特徴や振る舞いを抽出し、抽象クラスやインターフェースを定義すること。これにより、プログラムの理解が容易になり、変更がしやすくなる。
## クリーンアーキテクチャの原則
### 依存性逆転の原則（DIP）:
上位のモジュールは下位のモジュールに依存すべきではなく、どちらも抽象に依存すべきである。具体的な実装に依存しないことで、柔軟性が向上する。

### 単一責任の原則（SRP）: 
一つのクラスは一つの責務を持つべきであり、変更の理由は一つであるべき。これにより、クラスの保守性と理解が向上する。

### 開閉原則（OCP）:
ソフトウェアのエンティティ（クラス、モジュールなど）は拡張に対して開かれ、修正に対して閉じられるべきである。新しい機能の追加は既存のコードを変更せずに行えるようにする。

### リスコフの置換原則（LSP）:
基底クラスのインスタンスは、派生クラスのインスタンスと置換可能でなければならない。これにより、継承の安全性が確保される。

## なぜこれらの原則が重要なのか？
### 保守性の向上: 
オブジェクト指向とクリーンアーキテクチャに基づくコードは、変更に強く、保守性が高まります。変更が生じた際に特定の部分のみを修正できるため、予測可能で迅速な開発が可能になります。

### 可読性と理解容易性:
カプセル化や単一責任の原則は、コードの可読性を向上させ、理解しやすい構造を生み出します。これにより、開発者はコードの挙動を迅速に理解できます。

### 柔軟性と拡張性: 
クリーンアーキテクチャの原則に従うことで、システムは柔軟かつ拡張可能なものとなります。新しい機能の追加や変更が容易に行え、システム全体の安定性が保たれます。

## 結論
オブジェクト指向とクリーンアーキテクチャは、ソフトウェア開発において持続可能なコードを構築するための重要な原則です。これらの原則に従うことで、保守性の向上や柔軟性の確保が可能となり、高品質なソフトウェアの開発を加速することができます。開発者はこれらの原則を理解し、実践することで、より効果的なソフトウェア設計が可能です！
Next.jsで起動ポートを変更する簡単な方法

どうもこんにちは、たくびー(@takubii)です。

Next.jsプロジェクトを作成して開発サーバーを起動する時のポートの変更方法を調べたので共有したいと思います。

# デフォルトの挙動

```bash
$ npm run dev
```

上記のコマンドで起動するとport3000でlocalhostが起動すると思います。

![localhost3000img](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557065/de92ca2f-7ba6-f9c7-cebe-3dcac2cdcfa4.png)

# 起動ポートの変更方法

`package.json`の`scripts`セクションで以下のように指定するとポートを変更することができます。

```json
"scripts": {
  "dev": "next dev -p 3030",
}
```

この状態で`npm run dev`を打ってみると開発サーバーは以下のようなURLで開くと思います。

![localhost3030img](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557065/d018c958-7368-f89b-7925-f1c3a1258961.png)

# 終わりに

複数のNext.jsプロジェクトで開発サーバーを起動しながら作業したいという場面に出くわした際に、ポートの衝突で同時に1つしか画面が見られないことがありました。

そのような場合に上記の手順で起動ポートを変更すれば複数の画面を見ながら開発することができます。

簡単な内容ですがお役に立てていただければ幸いです。
ParentNodeとParentElementの違い

Javascriptの課題で子要素から親ノードを取得したいときに多用してしまったParentElement(ParentNode)について。この二つの違いがわからなかったので調べてみました。

# DOM、ノード、Elementの違い
ParentNodeとParentElementの理解の前に、DOM、ノード、Elementの関係性について理解する必要がありました。

### DOM
>ウェブ上の文書のコンテンツと構造からなるオブジェクトのデータ表現です。


つまりHTMLなどをオブジェクトで表現し、Javascript等で読み込める形にしたWeb API。
Document Object Modelの頭文字でDOM。

### ノード
DOMの要素のインターフェイスになっているクラス。

DOMノードが作られて、葉っぱのようにぶら下がっているのがノード。

### Element
日本語で表記すると要素。要素を表現するオブジェクトのこと。（ここにきてやっとオブジェクトが出てくる）

* * *

違いとは書いたものの、インターフェイスに違いはあるが、以下の通りDOMを使用する場合はノード=Elementという理解で問題なさそう。

>DOMを使用するコードの大部分は HTML 文書の操作を中心としているため、DOM 内のノードを要素と呼ぶのが一般的ですが、厳密にはすべてのノードが要素というわけではありません。

https://developer.mozilla.org/ja/docs/Web/API/Document_Object_Model/Introduction#dom_%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9

## ParentNode、ParentElementの違い

では、本題である二つのプロパティの違いですが、それぞれの意味としては下記の通りです。
ParentNode・・・特定のノードの親のノードを返す。 
ParentElement・・・特定のノードの親ノードのElementを返す。

前述の通り、ノード=Elementなので全てのDOMノードが読み込まれた後であれば、結果としては同じになります。

ただし、読み込まれていないタイミングではParentElementだとNullとなってしまうことがあります。

下記のようなケースはDOMツリーの途中で処理が入るのでNullになってしまいます。


```html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>JavaScript</title>
    <script type="text/javascript" src="javascripts/sample.js"></script>
    <!--↑ここにスクリプトが入るような場合 -->
  </head>
  <body>
    <div>
      <h1 class="title">JavaScript</h1>
      <button id="btn">ターゲット</button>
      <p class="display_none">クリックされました！</p>
    </div>
    <style>
      .display_none {
        display: none
      }
    </style>
  </body>
</html>
```
とはいえ、基本的にはstyleタグのあとにscriptタグを入れるため、このようなケースは稀なのかなとおもっています。

以上、違いとしてはノードか要素か、どちらを取得するか違いはあるが、結果として子要素から親ノードを取得したい場合はどちらを使っても変わりはないようです。

# 参考文献
https://developer.mozilla.org/ja/docs/Web/API/Document_Object_Model/Introduction
https://developer.mozilla.org/ja/docs/Web/API/Node
https://developer.mozilla.org/ja/docs/Web/API/Element
https://developer.mozilla.org/ja/docs/Web/API/Node/parentNode
https://developer.mozilla.org/ja/docs/Web/API/Node/parentElement
gRPCとは？ フロントエンドエンジニアのための基本ガイド

## はじめに

みなさんこんにちは！二段階認証でgithubとQiitaになかなかログインできず記事が書けませんでした。^^;
今日はgRPCについて調べたことをまとめたいと思います。
gRPC（gRPC Remote Procedure Calls）は、Googleが開発したオープンソースのRPC（Remote Procedure Call）フレームワークです。gRPCは、異なるプログラミング言語で書かれたアプリケーション間での通信を効率的に行うための仕組みを提供しています。本記事では、フロントエンドエンジニア見習いがgRPCを理解し、活用するための基本的なガイドを紹介します。
## gRPCの特徴
### 言語中立性:
gRPCは、Protocol Buffers（protobuf）を使用しており、言語中立のデータ交換フォーマットを提供します。これにより、異なる言語で書かれたクライアントとサーバーが互いに通信できます。
### 性能の向上:
HTTP/2をベースにしたgRPCは、多くの点でHTTP/1.xよりも優れた性能を発揮します。多重化やヘッダーの圧縮などの機能により、リクエストとレスポンスの効率的な転送が可能です。
### 自動生成されたコード: 
Protocol Buffersを使用することで、gRPCはクライアントとサーバーのスタブ（stub）コードを自動生成します。これにより、開発者は通信のためのコードを手動で書く必要がなくなります。
## gRPCの基本概念
Service Definition: gRPCのサービスは、Protocol Buffersを使用したIDL（Interface Definition Language）で定義されます。サービスのメソッドやデータ型などがこの定義に含まれます。
```protobuf:protobuf
syntax = "proto3";

service MyService {
  rpc GetData (DataRequest) returns (DataResponse);
}

message DataRequest {
  string user_id = 1;
}

message DataResponse {
  string data = 1;
}
```
### Protocol Buffers: 
データのシリアライズやデシリアライズのための形式です。メッセージの定義やスキーマを提供し、言語中立性を実現します。

## gRPCの利用例
マイクロサービスアーキテクチャ: gRPCは、マイクロサービス間の通信において優れた性能を発揮します。各サービスは独自のgRPCサービスを提供し、他のサービスと通信します。

リアルタイム通信: gRPCは双方向通信をサポートしており、リアルタイムなアプリケーションやストリーミングデータの処理に適しています。

## gRPCの実装とフロントエンドへの統合
gRPCの実装: gRPCは多くの言語でサポートされています。サーバーサイドではJava、Go、Pythonなどがあり、クライアントサイドでもJavaScript、TypeScript、Swiftなどが利用できます。

フロントエンドへの統合: フロントエンド開発者は、gRPCのクライアントスタブを使用してサーバーと通信します。一般的に、gRPC-WebやgRPC-Gatewayを利用して、ブラウザ上での直接的な通信を可能にします。

## 結論
gRPCは効率的なRPC通信を実現する優れたフレームワークであり、マイクロサービスアーキテクチャやリアルタイム通信の実現に適しています。フロントエンドエンジニアもgRPCを利用することで、クライアントとサーバー間の効率的な通信を実現できます。 gRPCの学習は新たな技術の導入になるかもしれませんが、その恩恵は大きいと言えるでしょう。
Python学習3日目～for文～

## for文
繰り返し処理が出来る

~~~Python:example.py
for 変数 in イテラブル:
    文...
~~~

色んなイテラブルから要素を取り出すことができる
~~~Python:example.py
for x in 'Python':
    print(x)
~~~
~~~:実行結果
P
y
t
h
o
n
~~~
こんな感じで実は文字列でも要素を取り出せる(知らんかった)
リストやタプルから要素を取り出すとシンプルに要素がそのまま変数に入る。
辞書の場合は以下の通り

~~~Python:example.py
meat = {'beaf': 199,'chicken': 68,'pork': 138}
for x in meat:
    print(x)
~~~
~~~:実行結果
beaf
chicken
pork
~~~

値も出したいときは以下の通り
~~~Python:example.py
meat = {'beaf': 199,'chicken': 68,'pork': 138}
for x in meat:
    print(x,'is',meat[x],'yen')
~~~
~~~:実行結果
beaf is 199 yen
chicken is 68 yen
pork is 138 yen
~~~

itemsメソッドを使えばもっと簡単に取り出せる
~~~Python:example.py
meat = {'beaf': 199,'chicken': 68,'pork': 138}
for x in meat.items():
    print(x)
~~~
~~~:実行結果
('beaf',199)
('chicken',68)
('pork',138)
~~~

## range関数

for文でn回繰り返したいときに使える

~~~Python:example.py
range(開始値,終了値,ステップ)
~~~

## enumerate関数
何番目に取り出した要素なのかというのをわかりやすくしてくれる
~~~Python:example.py
for 変数A,変数B in enumerate(イテラブル,開始値):
    文...
~~~

~~~Python:example.py
lang = ['EN','JP','CH']
for i,x in enumerate(lang,1):
    print(i,x)
~~~
~~~:実行結果
1 EN
2 JP
3 CH
~~~

## While文
for文と同じく繰り返し処理をする
for文の違いとしては式の値に応じて繰り返し処理を出来るところ

~~~Python:example.py
catalog = []
while len(catalog) < 3:
    item = input('item: ')
    catalog.append(item)
print('catalog:',catalog)
~~~
~~~:実行結果
item:apple
item:banana
item:coconut
catalog:['apple','banana','coconut']
~~~

### continue文
continue文を使うとループ内部にある残りの文を実行せずに次のループに移行できる

~~~Python:example.py
catalog = []
while len(catalog) < 3:
    item = input('item: ')
    if item in catalog:
        print(item,'is on the catalog')
        continue
    catalog.append(item)
print('catalog:',catalog)
~~~
~~~:実行結果
item:apple
item:banana
item:banana
banana is on the catalog
item:coconut
catalog:['apple','banana','coconut']
~~~

### break文
break文を使うとループ内部にある残りの文を実行せずにループ処理を終了する

~~~Python:example.py
catalog = []
while len(catalog) < 3:
    item = input('item: ')
    if item in catalog:
        print(item,'is on the catalog')
        break
    catalog.append(item)
print('catalog:',catalog)
~~~
~~~:実行結果
item:apple
item:banana
item:banana
banana is on the catalog
catalog:['apple','banana']
~~~

### else節
ifの場合は条件がFalseの場合にelse内の処理がされる
while,for文の場合はbreak文で処理が終了しなかったときにelse内の処理が行われる

~~~Python:example.py
catalog = []
while len(catalog) < 3:
    item = input('item: ')
    if item in catalog:
        print(item,'is on the catalog')
        break
    catalog.append(item)
else:
    print('catalog:',catalog)
~~~
~~~:実行結果
item:apple
item:banana
item:banana
banana is on the catalog # リスト内が表示されない
~~~
~~~:実行結果
item:apple
item:banana
item:coconut
catalog:['apple','banana','coconut'] #リストが表示される
~~~
Astro + Tailwind CSS + Cloudflare Pages のプロジェクトを準備する

こんにちは。ご無沙汰しております。

先日、静的サイトジェネレーター [Astro](https://astro.build/) で web ページを作って、[Cloudflare Pages](https://www.cloudflare.com/ja-jp/developer-platform/pages/) にアップしたいと、ふと思い立ちました。今回 CSS フレームワークには [Tailwind CSS](https://tailwindcss.com/) を使ってみたいと思います。

参考文書は以下の通りです。

1. [Deploy an Astro site · Cloudflare Pages docs](https://developers.cloudflare.com/pages/framework-guides/deploy-an-astro-site/)
1. [@astrojs/tailwind | Astro Docs](https://docs.astro.build/ja/guides/integrations-guide/tailwind/)
1. [Install Tailwind CSS with Astro - Tailwind CSS](https://tailwindcss.com/docs/guides/astro)

上の資料を読めばプレビューやデプロイまで進めるのですが、一部つまずいた部分もあったので、手順を整理してみました。下記の通りに進めてみます。

（2024.1.22 現在の情報です。将来、後述の手順が変更になる可能性があります）

## 必要なもの

- お好みのターミナル
- [Node.js](https://nodejs.org)
- Cloudflare のアカウント

## 実践

まず、ターミナルでご希望のディレクトリまで移動して、Astro のプロジェクトを作成します。テンプレートに with-tailwindcss と指定することで、Tailwind CSS 機能拡張が入り、軽いサンプルページが作られるので、丁度いい足掛かりになると思います。

`npm create astro@latest -- --template with-tailwindcss`

ダイアログで「dir: Where should we create your new project?」と聞いてくるので、お好みのディレクトリを指定します。終わったら、指定したディレクトリに移動します。your-apps はディレクトリ名ですので適宜置き換えてください。

`cd your-apps`

次に Astro の Cloudflare Pages 用機能拡張を追加します。

`npx astro add cloudflare`

次に Cloudflare の CLI ツール、[Wrangler](https://developers.cloudflare.com/workers/wrangler/) をインストールします。

`npm install -D wrangler`

ここまで終わったところで、package.json の scripts 部分を以下のように書き換えます。

```json:package.json
  "scripts": {
    "dev": "astro dev",
    "start": "astro dev",
    "build": "astro check && astro build",
    "preview": "astro preview",
    "astro": "npx astro",
    "wrangler": "npx wrangler",
    "pages:dev": "wrangler pages dev --compatibility-date=2024-01-17 -- astro dev",
    "pages:deploy": "astro build && wrangler pages deploy ./dist"
  },
```

"pages:dev"、"pages:deploy"を加えることで Astro と Wrangler が連携して、ローカルプレビューと公開ができるようになります。

次に Wrangler で Cloudflare にログインします。

`npx wrangler login`

ここまでで準備はおわりです。`.astro`ファイルなどをお好みに書き換えてみてください。ローカル環境でプレビューするには、次のコマンドを入力します。

`npm run pages:dev`

[http://localhost:8788/](http://localhost:8788/) で確認してみましょう。

![localhost_8788_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35987/6018c7d8-f3ab-f64c-b6fb-156bda6b65db.png)

上のようなページが表示されれば大丈夫です。公開は下記のコマンドを入力します。

`npm run pages:deploy`

ダイアログに従い、新しいプロジェクトを作成、または既存のプロジェクトを選んで、公開します。

以上です。

## 備考

通常、Astro でビルドすると、dist フォルダにファイル一式を出力 (SSG) しますが、Cloudflare 機能拡張を使う場合、Astro は server モードで出力 （SSR） します。