OmniAICreator/Qiita-1.07M huggingface dataset:
レコメンド：ユースケースから考えるオフライン評価の使い方　

# 1.オフライン評価とオンライン評価の違い

## オフライン評価
 ECサイトを例にしてみましょう
 消費者が『購入した』『購入しなかった』や『クリックした』『クリックしなかった』
 などのサーバーに置かれた行動ログを使って評価を行うのがオフライン評価になります。

## オンライン評価
例えば、既存の予測モデルが既にデプロイされているとします。
それに対して、新たにモデリングしたモデルをデプロイしたいとします。
これらをA/Bテストなどで、実際の新たなモデルの効果を評価するのが
オンライン評価になります。

## オンライン評価とオフライン評価のメリットとデメリット
どちらも実際のECサイトで使われる評価にはなるのですが、
一長一短にはなります。

### オフライン評価のメリットデメリット
大規模なECサイトであれば、利用ユーザーも多く、行動ログが多いと思います。
そのため、利用できるデータが多く、バイアスも少ないです。

ですが、実際にオフライン評価が良くても実ビジネスにおいてあまり効果をなさないことが多いです

実装コストは安いが、正確なモデルになるかどうかは不透明ということです。

## オンライン評価のデメリット

対して、オンライン評価のメリットとしては、オフライン評価のデメリットをカバーしているとでも言えます。
なぜなら、実際に、A/Bテストなどを行って、モデルの実際の有意性を確かめることができるからです。

ですが、A/Bテストはオフライン評価のメリットをなくしているとでも言えます。

実装コストとモデル評価の正確性は反比例しています。


# 2.オフライン評価指標のユースケース

ECサイトにおいて、ユーザーの行動意図というのは３つに分類されます。

・誘導型
・情報収集型
・取引型

まず、誘導型というのは、１つの適合アイテムを得たい場合です。
時刻表のウェブサイトを検索する場合ですね。
１つ適合すれば、似たようなサイトは不必要ですよね。

情報収集型ですが、１つ以上の適合アイテムを得たい場合です。
大学の卒業論文などで、情報はあればあるほど嬉しいですよね。
複数のウェブサイトを行き来するような行動です。

取引型は、ウェブを仲介としたアクションを実行したという意図になります。
例えば、居酒屋を予約する場合などです。


評価指標というのは、全く万能ではありません

ユースケースに基づいて評価指標を変える必要があります。

## 2.1 ユースケース1 複数商品を購入する際
例えば、
ファッションサイトを例にしてみると、
おすすめの商品ランキングがあったとして、それに対して、クリックはした
また実際にコンバージョンまでしてくれたなどがあるかと思います。

クリックや購入などを利得と呼びます。

複数のアイテムを購入して欲しいというECサイトの営業目的があったとした場合、
これは情報収集型に分類されますよね。

そして、購入予測のランキング1の利得が高ければ、それは良いモデルであると言えます。

ではそれに対して、最適な評価指標とは何か？
それが事項で説明するnDCGになります。


### 2.1.1 nDCG

まずDCGというのは何か？
それはランキングの位置に対して、利得を分母のlogの項によってディスカウントさせます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526914/a14eae4a-a790-b731-5a82-5ebf715200a7.png)

ここで、r1というのは、予測ランキング1位に対しての利得になります。

例えばランキングが5つあったとして、理想的な順位と、実際の行動履歴があったとします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526914/7fd5090c-d40a-2b10-21fd-b9946a6b4fd3.png)

上記で記載したDCGを使って、理想的なDCGを分母におき、実際のDCGを分子にして計算します。
		
以下が計算式になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526914/2cb2c337-819b-8529-6fcf-b705220d259c.png)


## 2.2 ユースケースその２

ファッションサイトでも、高価格商品だったり、低価格の商品など多数あります。
高価商品をメインに抑えているサイトの場合、複数の商品を買わず
本当に気になった高価な財布だったりを買うケースなどがあるかと思います。

その場合、ランキングはどのように動けばいいでしょうか？

おすすめのランキングの１位のものを買ってもらえた方が正しいモデルと評価できますよね。

なぜなら、1位と2位の商品を同時に買うことがほぼないと仮定しているからです。

評価指標自体、ランキングの最初の適合位置が高い位置にあればあるほど良いモデルということになります。

それにはMRRを使うと良いと思います。

### 2.2.1 MRR(Mean Reciprocal Rank)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526914/601e7206-e12e-69c3-517b-1c28967d0572.png)

Uはユーザー集合であり、
kuは、ランキングk番目の適合位置になります。

10番目に適合したものよりも2番目に適合した方が寄与が大きくなります。

# 3.まとめ

他にも評価指標はあるかと思いますが、
伝えたいのは、ケースによって評価指標を使い分けようということです。
次は、オンライン評価についても解説していきたいです。

# 4.参考資料

https://www.oreilly.co.jp/books/9784873119663/
PreWorldPluginについて

https://www.spigotmc.org/resources/perworldplugins.96161/

## 使用方法について

data/plugins/PerWorldPluginの中にあるconfig.ymlを編集する

```java
# PerWorldPlugins Configuration
#
# Disable or Enable the message of new updates.
updateChecker: true
metrics: true
disabledCommandMessage: '&4[Server]: &fThis command is disabled for this world.'
blacklist: true
plugins:
	# サーバーに入ってるプラグインが自動で入力される
	# 使えないようにしたいワールドを各プラグイン名のしたに書く
  Example:
  - PutWorldInThisListForDisableThisPluginOnWorld
  - world
  Example2:
  - 'ignore:'
  LuckPerms:
  - world
  WorldEdit:
  - test
  Multiverse-Core:
  - world
  sampleTohu:
  - test
```

## PerWorldPluginのコマンド

- perworldplugin version
    - ただのバージョン確認
    - あんまり使わない
- perworldplugin reload
    - 編集したconfigを元に更新する
    - サーバー自体リロードしなくても反映させられるから便利

## 挙動について

- 不都合で使えなくなるプラグインは今のところなさそう。見つけ次第記入
- LuckPermsを使えなくしてるワールドでも、権限の内容は反映されてる
- プラグインで使うイベントは、無効化してるワールドではイベントの処理はされない
- 無効化してるプラグインのコマンドを使うときだけ警告文が出る。イベント処理の時は出ない
Excel VBAで Wordの文をすべて抽出

# 概要
* Excel VBAで指定されたWord文書からすべての文を抽出して、配列に格納するためのコードを作成しました。
* 以下の動画で使われているものです。

<iframe width="560" height="315" src="https://www.youtube.com/embed/NP9fSfG-8aY?si=B5pexXJUmJBY7RtX" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

# 実行環境
以下の環境で動作確認をしました。
* Windows11でのExcel 2021

# 注意点
* プログラムの実行については、すべて自己責任で行ってください。実行により発生した、いかなる直接的または間接的被害について、作者はその責任を負いません。

# コード


```vb:VBA
Option Explicit

Sub TestExtractSentences()
    ' Wordファイルのパスを指定
    Dim filePath As String
    filePath = _
    "Your_Path"
    
    ' ExtractSentencesFromWordFile 関数を呼び出して文を取得
    Dim sentencesArray As Variant
    sentencesArray = ExtractSentencesFromWordFile(filePath)

    Dim i As Integer
    For i = LBound(sentencesArray) To UBound(sentencesArray)
        ' 配列の各要素をデバッグ表示
        Debug.Print "Index " & i & ": " & sentencesArray(i)
    Next i
End Sub


' Wordファイルの文章の中から文をすべて抽出して、配列として返す
Function ExtractSentencesFromWordFile(ByVal filePath As String) As Variant
    Dim objWord As Object
    Dim objDoc As Object
    Dim sentenceArray() As String
    Dim sentenceCount As Integer
    Dim i As Integer
    Dim sentence As String    
    ' Wordファイルが既に開かれていないか確認
    Call CheckIfWordFileIsOpen(filePath)

    ' Wordアプリケーションの新しいインスタンスを作成
    Set objWord = CreateObject("Word.Application")

    ' Wordアプリケーションの表示を非表示にする
    objWord.Visible = False

    ' 指定されたWordドキュメントを開く
    Set objDoc = objWord.Documents.Open(filePath)

    ' 変数の初期化
    sentenceCount = 0
    
    ' ドキュメント内の各文に対してループ
    For i = 1 To objDoc.Sentences.count
        ' 文を取得
        sentence = objDoc.Sentences(i).Text
        ' 文のカウントを増やす
        sentenceCount = sentenceCount + 1
        ' 配列をリサイズして新しい文を収めるスペースを作成
        ReDim Preserve sentenceArray(1 To sentenceCount)
        ' 配列に文を格納（改行は削除）
        sentenceArray(sentenceCount) = Replace(sentence, vbCr, "")
    Next i

    ' Wordドキュメントを閉じる
    objDoc.Close

    ' オブジェクト参照を解放
    Set objDoc = Nothing
    Set objWord = Nothing

    ' 文の配列を戻り値として返す
    ExtractSentencesFromWordFile = sentenceArray
End Function


' 指定のWordファイルが既に開かれているか確認
Function CheckIfWordFileIsOpen(ByVal filePath As String) As Boolean
    ' 関連するオブジェクトの宣言
    Dim wordApp As Object
    Dim doc As Object
    
    ' エラーが発生してもスクリプトの実行を続行
    On Error Resume Next
    ' 既存のWordアプリケーションがあるか確認
    Set wordApp = GetObject(, "Word.Application")
    ' エラーハンドリングを元に戻す
    On Error GoTo 0
    
    ' Wordアプリケーションが存在する場合
    If Not wordApp Is Nothing Then
        ' 開かれている全てのドキュメントに対して処理を実行
        For Each doc In wordApp.Documents
            If doc.Path & "\" & doc.Name = filePath Then
                ' ファイルが開かれている場合はTrueを返し、関数を終了
                CheckIfWordFileIsOpen = True
                ' 既に開かれている場合はメッセージを表示し、処理を中止
                MsgBox "指定されたWordファイルは既に開かれています。ファイルを閉じてから再度実行してください。"
                End
            End If
        Next doc
    Else
        ' Wordアプリケーションが実行されていない場合
    End If
    
    ' オブジェクトの解放
    Set doc = Nothing
    Set wordApp = Nothing
End Function



```
Symbol SDK generator - DeepL和訳 -

本記事は以下をDeepLによって和訳したものです。

https://github.com/symbol/symbol/tree/dev/sdk/python/generator

# python catbuffer generator

## Generate catbuffer

```sh
./scripts/run_catbuffer_generator.sh
```

テストベクタを実行する（`sdk/python` 作業ディレクトリを想定）：

```bash
PYTHONPATH=. SCHEMAS_PATH=../../tests/vectors  python3 -m pytest ./tests/vectors/catbuffer.py
```

## Generator overview

1. YAML ファイル内のすべての型には型オブジェクトのいずれかが割り当てられる： `EnumObject`、`ArrayObject`、`IntObject` または `StructObject` です。
2. これらの型のうち、`StructObject` だけはより複雑で、後述する処理が必要になる。
3. すべての型オブジェクトには、それぞれ対応する型フォーマッタがある：

   * `EnumObject` - `EnumTypeFormatter`.
   * `ArrayObject` と `IntObject` - `PodTypeFormatter`.
   * `StructObject` - `StructFormatter`.

実際の生成は汎用的な `TypeFormatter` を通して行われ、特定の型フォーマッタを使用して出力を生成する。

## Struct processing

1. 構造体内のフィールドには、フィールドの `disposition` と `type` に応じて、いずれかの型オブジェクトが割り当てられる。
2. フィールドには適切なプリンタが割り当てられ、構造体の出力を生成するときにフィールドがどのように処理されるかを処理する：

   * `intPrinter` - 単純型用。
   * `ArrayPrinter` と `TypedArrayPrinter` - バイト配列と型付き配列。
   * `BuiltinPrinter` - 組み込み型（既に処理された型）用。

すべてのプリンタはいくつかの "固定"フィールド名を持つ。
Pythonの場合、固定される名前は2つある：

* `size` - すでに異なる処理をしている。
* `type` - `type()` 組み込み関数と衝突しないようにする。

構造体処理の最後で、'size' フィールドは可変長フィールドにバインドされる。
例えば、 `TransferTransaction.message_size` は `TransferTransaction.message` にバインドされます。これにより、`message_size` フィールドに対して次のようなシリアライズを生成することができます：

```py
bytes_ += len(self._message).to_bytes(2, byteorder='little', signed=False)
```
llama.cppのインストール方法

## 前提条件
Windows11にllama.cppをインストールする方法についてまとめます

## llama.cppのクローン
以下のGithubのページからllama.cppをクローン、もしくはZip形式でダウンロードして解凍してください。

https://github.com/ggerganov/llama.cpp


## w64devkitのインストール

下記のサイトからw64devkitの最新版をダウンロードして解凍してください。

https://github.com/skeeto/w64devkit/releases


## w64devkit.exeを起動

解凍したファイルに含まれているw64devkit.exeを起動させ、llama.cppのファイルまで移動してください。


## ビルド
llama.cppで以下のコマンドを実行することでllama.cppをビルドさせることが出来ます
```
make
```


## サーバー機能

w64devkitを用いて以下のコードを実行するとサーバーを立てることができる
```
$ ./server -m models/vicuna-7b-v1.5.ggmlv3.q4_K_M.bin -c 2048
```

-m 以降のパスにmodelsフォルダに配置したLLMモデルのパスを指定することで、指定したLLMモデルを扱うことができる。

![スクリーンショット 2024-01-16 155324.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634370/60a4a7be-ba10-6a45-0460-2f1ebfd2954d.png)

![スクリーンショット 2024-01-16 155206.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634370/70c842d9-c833-09f9-4e74-1d1d304348df.png)

上記の画像は起動したサーバーにブラウザからアクセスした際の画像です。
llama-cpp-pythonのインストールの仕方

## 前提条件
Windows11に対するllama-cpp-pythonのインストール方法をまとめます。

## 目次
・環境構築
・インストール
・実行


## 環境構築

1. CMakeのダウンロード

[CMake](https://cmake.org/download/)

![スクリーンショット 2024-01-20 141140.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634370/a23ef75f-f401-4ae1-7c8a-4ad6b0875edb.png)

上記の画像の赤枠の欄からCMakeをダウンロードし、Cドライブの直下に配置する。

2. 環境パスを通す

![スクリーンショット 2024-01-20 141715.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634370/5f0f7dc8-4f23-39b0-24ca-59a97fc11ec0.png)

![スクリーンショット 2024-01-20 141913.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634370/c6f64043-64aa-fc3c-7b00-3a7fcc3f4600.png)
![スクリーンショット 2024-01-20 142044.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634370/c0f791d6-0a52-3a96-69df-f95932af7d0e.png)

システム環境変数に
```
C:\cmake-3.28.1-windows-x86_64\bin
```
を追加してください

3. visual studio build tools 2022のダウンロード

[visual studio build tools 2022](https://visualstudio.microsoft.com/ja/downloads/)
![スクリーンショット 2024-01-20 143000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634370/3f9f6e37-eb1f-aa4c-7766-0b1e80e8f41c.png)

上記のリンクからvisual studio build tools 2022をダウンロードし、パソコンにインストールを行ってください

![スクリーンショット 2024-01-20 143353.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634370/5c966f99-3580-506d-9f50-7a94981cbf99.png)

インストールを行う際はC++によるデスクトップ開発にチェックを入れてください


## llama-cpp-pythonのインストール

```
pip install llama-cpp-python
```

上記のコードをコマンドプロンプトで実行することでインストールが行えます


## エラーが出た場合

w64devkitをインストールする必要があるかもしれない。

'nmake' '?' or CMAKE_C_COMPILER

と言ったエラーが出た場合には、w64devkitをインストールしてパスを通した後に、下記のコードを実行してください。

$env:CMAKE_GENERATOR = "MinGW Makefiles"
$env:CMAKE_ARGS = "-DLLAMA_OPENBLAS=on -DCMAKE_C_COMPILER=C:/w64devkit/bin/gcc.exe -DCMAKE_CXX_COMPILER=C:/w64devkit/bin/g++.exe" 


## GPUに関するエラーが出た場合
CUDAとcudnnをインストールしてください
【JavaScript】継続渡しスタイルでFizzBuzz


```js
function fizzbuzz(num) {
    if (num % 15 === 0) {
        console.log("FizzBuzz");
    }
    else if (num % 3 === 0) {
        console.log("Fizz");
    }
    else if (num % 5 === 0) {
        console.log("Buzz");
    }
    else {
        console.log(num);
    }
    return cont => cont(++num);
}

(fizzbuzz)(1)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz)(fizzbuzz);
```

## 参考

https://qiita.com/culage/items/07cee25e64878d2f38b3
Cognitoの不満点

AWS Cognitoを少し触ってみての不満点を書き残しておきます。（遠い将来まったく同じことでハマりそうな気がしたので）

# 連携できるソーシャルログインが少なすぎる

下記の4つのみ。

- Google
- Facebook
- Apple
- Amazon

個人的にはGitHubが欲しいです。

上記以外でもOpenID Connectで連携すればLinkedInやPayPalが連携できるよ、って書いてあるんですけど、課金体系が変わるので採用のハードルは上がりますね。

# OpenIDのイシュアーURLがわかりにくい

公式ページの記述は見つけられませんでした。

`https://cognito-idp.<リージョン名>.amazonaws.com/<ユーザープール ID>`

# リフレッシュトークンを使ったトークン再発行が遅い

何回か試したんですが、Google連携しているユーザーのトークンの更新に2秒くらいかかってギョッとしました。

Google直でトークン再発行すると250ms程度で終わるので、連携しているせいではなさそうです。

# トークンのrevokeがデフォルトで無効

下記URLの手順で有効化する必要がある模様。

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/token-revocation.html

他のIDaaS/IdPではあまり見ない対応だと感じました。

# Googleとの連携がイマイチ

Googleの認可エンドポイントに遷移するときに `prompt=consent` をつけてくれないので、Googleアカウントが複数ある場合にアカウント切り替えができなくて不便です。
ブラックジャックの期待値(全体の期待値)をセル関数だけで計算する

<font color=red>**以下の記事の続編となります。**
**先にこちらの記事を読まれていることを大前提にしています。**</font>

https://qiita.com/takahasinaoki/items/1d4eedfdda5ae10fb40b


# はじめに
上記の記事のコメントで全体の期待値を計算してほしいという要望があったのでチャレンジしてみました

## 前提
以下のルールの内①のルールが前提となります

親のオープンカードがAまたは10の時
①特別なことは何もせず子のタームに移ります。
②親はもう一枚のカードを確認し、ＢＪが成立していないことを確認してから子のタームに移ります。ＢＪが成立している場合は、子のタームを行わず勝敗を決します。

参考
②のルールで個別の期待値をもとめた計算シートは
[こちらの記事のコメント欄](https://qiita.com/takahasinaoki/items/1d4eedfdda5ae10fb40b#comment-057d7983d9edb20c3c90)参照

# 計算結果

(1)[スプリット、ダブルダウンがないルール](https://qiita.com/takahasinaoki/items/094da1dd64c6273502dd)の場合は、
<font color=red>**97.58%**</font>
(2)[スプリット、ダブルダウンを適用したルール](https://qiita.com/takahasinaoki/items/1d4eedfdda5ae10fb40b)の場合は、
<font color=red>**99.20%**</font>
となりました

計算方法は、こちらの[Googleスプレッドシート](https://docs.google.com/spreadsheets/d/1sOBaPriOR80Q9x_g-R_Q3Joj2_Gj_T54/edit?usp=drive_link&ouid=101280236584502897763&rtpof=true&sd=true)で公開しています。
以下の2種類のシートがあります
・「W無」※(1)スプリット、ダブルダウンなしで計算
・「W有」※(2)スプリット、ダブルダウンありで計算
クリックしても閲覧しかできませんので、中身をいじったり確認したい方は、[Googleスプレッドシート]のメニューの[ファイル]から[ダウンロード]を選択し、[Microsoft Excel形式]でダウンロードしてください。
Excelソフトをお持ちでない方は、再度自身のアカウントの[Google Drive]にアップロードすれば、[Googleスプレッドシート]で開くことが出来るのではないかと思います。


## シートの見方

### 全体の期待値

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411106/92d4c27f-6b6c-7255-f017-e6a97574ba22.png)

確率
　親のオープンカードそれぞれの確率
 期待値
　親のオープンカードそれぞれの期待値（算出方法は後述）
確率 * 期待値
　上記２つの値の積
全体の期待値
　確率*期待値 の合計 および その値をパーセンテージで表現したもの

### 個別のオープンカードの期待値

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411106/2bde080c-1fb8-880b-aa7d-ecdc45c012c5.png)

確率
　1枚目の確率：文字通り該当のカードがでる確率（Aは11の列に設定）
　2枚目の確率（ハードハンド・ソフトハンドそれぞれで計算）
　　2枚で該当の値になる確率（ただしペアハンドになる可能性のある組み合わせはペアハンドになる確率を除いたもの）
　ペアハンドの確率：該当のペアハンドになる確率

期待値
　ハードハンド・ソフトハンド
　　max(ステイ,ヒット,ダブルダウン) × 上記で求めた確率
　ペア
　　max(ステイ,ヒット,ダブルダウン, スプリット) × 上記で求めた確率
<font color=red>期待値合計</font>（これが求めたい値）
　上記で求めた期待値の合計


# 関連記事
https://qiita.com/takahasinaoki/items/a345bd145f9fa281b352

https://qiita.com/takahasinaoki/items/094da1dd64c6273502dd

https://qiita.com/takahasinaoki/items/1d4eedfdda5ae10fb40b

https://qiita.com/takahasinaoki/items/f92b4cc3d41ac22a7ae3

https://qiita.com/takahasinaoki/items/a3e023ede0545582cf52
Cloud Functions と ChatGPT を用いたスクレイピングのアーキテクチャ紹介

個人開発でスクレイピングをしています。
今回は Cloud Functions と ChatGPT を用いたスクレイピングのアーキテクチャを紹介します。

過去に書いたスクレイピングに関する記事はこちらです。

https://qiita.com/ogatango/items/df7b42e05a5852c665d0

https://qiita.com/ogatango/items/3ef5e7e87f66ed971411

https://qiita.com/ogatango/items/44b72b060b2767ae2fa4

https://qiita.com/ogatango/items/78fde1c22c382c002376

## 経緯

- もともと各 Web サイトに対して1つ1つスクレイピングのプログラムを実装していた。
    - Web サイトのちょっとした変更に弱く、すぐに動かなくなってしまっていた。
    - AI を導入することで変更に強い仕組みにした。
- DBへの挿入などの共通の仕様を変更するときの影響範囲がわかりにくいという問題もあった。
    - AI を導入することを機にアーキテクチャを見直した。



## 過去のアーキテクチャ

次のように Web サイトの数だけ Scheduler や Functions を用意し、デプロイしていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415560/f65c111b-dcd5-3ab2-2d78-bf727b304352.png)

このアーキテクチャでは1つのFunctions が1つのWeｂサイトの抽出・変換・読み込み・挿入などの処理を担当しています。
### このアーキテクチャの問題点

この実装の場合は各 Functions のコードで重複が大量に発生していました。
例えば データベースへのデータの挿入や、データのバリデーションは全 Web サイトで共通の仕様です。
そういった共通の仕様をシェルスクリプトでコピーしてデプロイしており、共通仕様にちょっとした変更が入るだけでもすべての Functions をデプロイし直す必要がありました。terraform を使っているとは言え、あまり健全な感じがしませんでした。

また Git の履歴が scraping のすべてのファイルに残って汚くなりますし、ちょっとした変更が及ぼす影響範囲がつかみにくいところもあり、アンチパターンっぽさが漂っていました。

## 現在のアーキテクチャ

ChatGPT を導入することにしたときについでにアーキテクチャを見直すことにしました。
アーキテクチャは次のとおりです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415560/dbc10623-a19e-ec98-b441-1f8dbc6c53ca.png)


それぞれの役割を持った Functions が GCS にデータを置き、 GCS トリガーで次の処理が動くようになっています。

### 改善された点
- 共通の処理を抽出の Functions から切り離したことにより、変更を入れるときの変更箇所が明瞭になりました。
- フォーマット変換を AI に任せられるようになったため、実装の時間を大幅に短縮できるようになりました。

### 各Functions の役割

#### コンサート情報抽出
変換元となるコンサート情報を抽出します。1つの Functions 内に各 Webサイトの抽出スクリプトを用意し、Scheduler で渡されたWebサイト名に基づいて読み込むスクリプトを変更します。

抽出するのはWebサイトの URL, コンサート会場の ID、コンサート情報の生テキストです。
コンサート会場の ID はこのシステムが導入しているものであり、AIではどうしようもないため抽出時点で情報を付加します。
URL もページのテキストに含まれないため、この時点で付与します。

生のテキストはあとで AI で処理するため、雑に丸ごと取得できることが多く、この部分の実装は1つの Webサイトで早ければ数十分で終わります。

#### フォーマット変換

ChatGPT に対して生のテキストを渡し、テキストのフォーマットを変換します。
もともと手作業で Web サイトごとに日付やタイトルなどの情報の抽出を実装しており、1つのWebサイトで最低でも3時間程度、長いと1日かかっていましたが、テキストさえ渡せば良くなったのでとても楽になりました。

AI で取得した情報が間違っている場合に備えてバリデーションも同時にしています。

#### 情報付加
コンサートのテキストを元に情報を付加しています。
例えばテキスト中にオーケストラという文字があれば、ジャンルにオーケストラを加えるといった処理です。


### DB挿入
DBにデータを挿入します。

## 感想

もともと DRY 原則に反する感じのコードになっていましたが、改善できてよかったです。
terraform を使っているので別にデプロイの手間はかからないし、まあいっか！と思っていたのですが、コードを変更するのが徐々に難しくなっていくのを体感してアンチパターンを踏まないことは重要なんだなと思いました。

自然にアーキテクチャを考えたら ETL のパイプラインになっていて、データエンジニアとして自分自身も成長していることを実感しました。
今後も自分の作ったコードの面倒を見て技術力を上げるためにも個人開発を続けていきたいです。
bash: rails: コマンドが見つかりません

# 初めに
普段使用している railsコマンド が使えなかったので書きます。

# 問題

① ```docker compose exec web bash``` をターミナルで実行してコンテナ内部に入る
② ``` rails console``` 実行
③ bash: rails: コマンドが見つかりません

# 解決方法

```bin/rails console``` rails consoleの前に ```bin/``` を付ける

原因はrails new で新規プロジェクトを立ち上げたときに binディレクトリが生成されてその配下にrailsコマンドを格納しているファイルを読み込んでいるから。

# 終わりに

普段何気なく、railsコマンドを使っていたので勉強になりました。
llama-cpp-pythonをつかったサーバーの立て方

## 前提
llama-cpp-pythonを既にインストールしている

## サーバーの立て方
```
python -m llama_cpp.server --model K:\llama.cpp\models\ELYZA-japanese-Llama-2-7b-instruct-q8_0.gguf 
```
モデルのPathを指定する関係から、llama.cppフォルダから起動する。
モデルの指定を絶対パスにすればどこからでも起動可能

## サーバーへのアクセス
```
curl -X POST http://localhost:8000/v1/chat/completions -H "Accept: application/json" -H "Content-Type: application/json" -d @request2.json
```
@request2.jsonでプロンプトをJson形式で保存してある。

## Jsonの形式
```
{
    "messages": [
      {
        "content": "あなたは猫又の「にゃこ」です。
        にゃこは猫又です。
        にゃこは300歳です。
        にゃこは語尾に「にゃん」「にゃ」などをつけます。
        にゃこは天然でよく失敗します。
        にゃこは魚が好きです。
        [チャットなのでできるだけ30文字以下で1文で答えてください。]",
        "role": "system"
      },
      {
        "content": "あなたの年齢を教えてください?",
        "role": "user"
      }
    ]
  }
```
データ分析初学者がAWSを使って3時間本気で学習してみた

# はじめに

データレイクという言葉も下火になりつつありますが、今更ながらデータレイク初学者が3時間で学習した内容をまとめます。

# やりたいこと

- データレイクやデータ分析初学者向けのコンテンツの作成
- ハンズオン体験でデータレイクの概要を理解する

# 実際やったこと

#### １．公式動画視聴
まず手始めにAWS SkiilBuilderでデータ分析入門用の動画を見ました。
https://explore.skillbuilder.aws/learn/course/606/play/38824/data-analytics-fundamentals-japanese-112

データ分析における考え方や課題解決について包括的に効率よく学習することができます。

:::note info
ちなみに日本語吹き替え版なのも良ポイント！
:::


#### ２．データ分析の勉強法
こちらも調べるときは基本的に情報ソースが大事なので公式サイトから探します。
いくつかありますが以下が勉強法についてわかりやすく纏まっているのでおすすめの記事です。
https://aws.amazon.com/jp/builders-flash/202209/way-to-learn-data-analysis/?awsf.filter-name=*all

公式以外だとクラスメソッドさんのDevelopersIOもおすすめ。
私の場合は実際に手を動かしながら学習した方が効率的に知識として定着するので、以下の動画を見ながら実践してみました。
https://dev.classmethod.jp/articles/developersio2021-decade-try-aws-lake-formation-for-the-first-time/

#### ３．知識のアウトプット
ここまでで、データ分析についての概要をなんとなく理解したので、初学者向けに、どのようなコンテンツにするか検討。

- データ分析とは
- なぜデータ分析が必要なのか
- 身近にあるデータ分析の事例紹介
- データレイクについて
- データレイクのつくりかた
- データレイクVSデータウェアハウス
- データレイク設計のベストプラクティス
- AWS Glueについて
- ハンズオン

このような構成でコンテンツを作成していくのですが、知識として足りない部分は必要に応じてGPTへ聞いたり、公式サイトを確認しながら作成していきます。

詳細はここには書けないのであしからず・・・。

ハンズオンとしては購買履歴のダミーデータを使って以下を実施することにしました。
- 実運用でのデータインプットの代わりにLambdaでダミーデータ作成
- 作成したダミーデータをS3へ保存(csv,tsv,jsonそれぞれの形式で)
- 保存されたデータをGlueでクローリング
- クローリングしたテーブル情報をLakeFormationで読み込み
- Athenaで分析
- QuickSightで可視化

テーブルレイアウトは以下（※ダミーデータです）

| Name | Address | Age | Sex | Product | Price | PurchaseDate |
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| Ryan Gentry  | 447 Foster Place Apt  | 72  | Female  | such  |  38.5 | 2023-01-22  |



構成図は以下
![データ分析構成図.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2840619/bc5d9bb1-1f24-7b2b-3d83-a95789c81570.jpeg)


大半はCloudFormationで自動作成します
![構成図１.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2840619/d815c81b-30dd-0d82-28f6-14505b6e8795.jpeg)

作成したダミーデータをQuickSightで可視化し、購入した商品の年代を分析したり、性別を分析したりして、実際のデータ分析の活用方法をイメージできるようなコンテンツにしました。

あとは全体のバランス(章ごとのスライド枚数)を整えて社内の初学者を集めて講義を行いました。

# 実際やってみて得られたこと
以上が私なりの新しい分野に対する勉強法ですが、膨大な情報量がネットですべて解決できる現代において、以下がスピードラーニングであると考えています。
- いかに自分にとっての必要な情報を見つけられるか
- その情報を基にアウトプットできるか。

こうすることで知識が技術となって自身のスキルとして蓄えられることを体験できました。

:::note info
みなさんの新しい分野に対する勉強法があればコメントで教えてください！
:::
Mindと日本語（プログラミング言語と自然言語と人間）について

昨年末から考えるところがあって発言を取り消し、言語についての積ん読本を整理していました。読んでない本で部屋を占領し家人から小言を貰っているからでもあります。

おかげでこの年になって目から鱗が落ちるような体験をしました。その一つとして、『記号と再帰』田中久美子著、東京大学出版会から孫引きしますと、「思考がすべて記号であるという事実を人生は思考の連続であるという事実と共に考えると人は記号であるということになる」p.26。

チャールズ・サンダース・パースの言葉です。

自分自身は「シンギュラリティは到来しない」と思っていましたが、一挙に怪しくなりました。150年以上前のアメリカの思想家が実に示唆に富んだ著作を残していたのですね。シンギュラリティが到来しないどころかそもそも人は記号だというのですね。

ならばMindと言語と人についても、もう一度考え直す必要があります。Mindと日本語と日本語話者は同じ地平に立って処理される必要があります。
Coding Chronicles: Unraveling the Mysteries of Tech on Qiita

Welcome to the exciting world of Qiita, fellow tech enthusiasts and hackers! I cordially invite you to embark  on a voyage into the wide realms of technology, programming, and the infinite knowledge-sharing tapestry that is Qiita, where bits and bytes harmonize to form symphonies of code.https://www.prestigesomerville.org.in


Qiita is centered around a digital canvas on which programmers, developers, and tech enthusiasts collaborate to create a knowledge mosaic. This never-ending work of art gains a new stroke with each article, weaving a rich tapestry across languages, frameworks, and the newest technological developments.
TypeScriptに安易にResult型を導入しない方がいい

TypeScriptへRustのようなResult型の導入をお勧めする記事や言説が多いので導入してみましたがあまりよくなかったです。という共有になります。

## Result型を導入しても try-catch からは逃れられない

これに尽きます。

Result型を導入したあと、try-catch を末端に押しやってそこ以外はResult型のみの世界を実現しようと、おそらくみんな考える。でもそれは機能しない。

実際にやってみるとこんなコードが多発する。

```typescript
function hoge():Result<void> {
  try {
  　　　　// fn()は綺麗な世界の実現のためResult型を返すようにしてある。この関数のようにね。
    const result = fn()

    // ここで例外が発生する処理が必要になる

    return ok(result)
  } catch(e) {
    return err(e)
  }
}
```

## 副作用部分だけ閉じ込めれば綺麗になるか

API呼び出しなどの副作用だけ閉じ込めればこんなコードは避けられると考えるかもしれないが現実は甘くない。

JavaScriptのさまざまな組み込み関数が例外を発生させるし、nullやundefinedへのアクセスも例外を発生させる。これらの多くはRustやGoで言うところのpanic扱いだから戦略を分けて、ユーザー定義のエラーだけResult型を使うという考え方はできる。それはある程度うまくいくだろうが、例外を発生させる外部ライブラリなどの導入が起きると破綻する。

## Result型がうまく機能する場所はあるか

### Server Actions

マッチするというか使わざるを得ないのはServer Actions。Server Actionsはセキュリティ上の理由からプロダクション環境においては例外の情報が削除される。例外を使ったエラーハンドリングは使い物にならない。

### その他

他は思いつかない。

綺麗にtry-catchを末端に押しやれば使えるかもしれないが、TypeScriptの採用が多いフロントエンド開発においてはそこまで実装がレイヤー化されることは珍しいだろう。
バックエンド開発であれば、ユースケース層に近い部分では綺麗になるかもしれない。が、レイヤーによって戦術を変えてまで導入したいだろうか。チーム開発の場合、どこでtry-catchを使ってどこでResult型を使えばいいかわからなくなって余計な混乱を招きそう。

## X(Twitter)上のResult型導入に対しての懸念

これ本当にみんないいと思って使ってるのか？と思ってXで検索したらやはり懸念を覚えている方が結構いらっしゃる。

https://twitter.com/cubbit2/status/1576237743926960128

https://twitter.com/mizchi/status/1653698788941520898

https://twitter.com/suin/status/1448807628646223882

https://twitter.com/zaru/status/1707524378685210776

## まとめ

Server Actionsなど限定的にメリットが大きいケースはあるが、Result型の導入がクリーンな理想的な世界を生み出すことはない。

Goに入りてはGoに従えという言葉があります。
同じ話ではありますが、言語はエラーハンドリングを規定するのだと思い知らされました。
ダブルクォートありのCSVを読み込む

```awk
cat sample.csv | awk -vFPAT='([^,]*)|("[^"]+")' '{print $1}'
```

カンマじゃない文字の連続 または ダブルクォート内のダブルクォートじゃない文字 を一塊として扱っているようです。
FPAT変数はField PATternらしい。
ただ、ダブルクォート内にエスケープされたそれがあるときは対応できない気がします。

否定の文字クラスってこういう使い方するんだなあ、と感動した話でした。
学校管理端末でChrome拡張機能を入れる方法

皆さん。こんにちは！今回は、タイトルの通り、「学校管理端末でChrome拡張機能を入れる方法」を解説します！皆さん、こんなことは思ったことはないでしょうか？「家のパソコンではあんなのとかこんなのとかあってクソ楽だからこれにも入れたいんだけどなぁ...」って思っている人もいるんじゃないんでしょうか？そんな人たちに向けて、今回は、"ほぼ"全ての拡張機能がバンバン入れられる方法を紹介します！


筆者の環境
・ASUS Chromebook Flip C214MA
・ロイロWebフィルター
・ドメイン管理済み(Gmailなどはブロックされる。)

1.https://myaccount.google.com/?utm_source=OGB&utm_medium=app に行く。(行けない方はhttps://www.google.co.jp/ から Google アプリ→アカウントに行く)
2.右上にある学校アカウントのアイコン→別のアカウントを追加 で、学校アカウントではないアカウント(@Gmailなど)にサインインする
3.今サインインしたアカウントで先程のページに行く→3×3の点「Google アプリ」をクリック→「Chrome ウェブストア」をクリック

これで完了です！あとは通常のパソコンと同じように拡張機能やブラウザ テーマをインストールしたり、コメントもできるようになります！ですが、一部の拡張機能は、学校アカウントでのインストールを求められるので、全部ではないのが欠点ですね...

ついでに入れるとおすすめな拡張機能を3つご紹介します！

1.uBlock Origin
これは有名な広告ブロッカーです。広告以外にも、ポップアップ、追跡トラッカーなどを防いだり、ブラックリスト/ホワイトリストを個別に決めることができます！

2.Scratch Addons
Scratchをやってる人はマストアイテムとして常備していた方がいいです。拡張機能上から、お知らせの確認、コメントの返信ができます！ただこの拡張機能は、有志の方々が作ってくれた非公式アプリです。また、Scratch財団及びマサチューセッツ工科大学メディアラボ ライフロングキンダーガーデングループは、このアプリに関してのScratch上での議論は禁止しており、拡張機能制作側もこれに同意しています。

3.LINE
多分説明はいらないでしょう。ただ、拡張機能版LINEは、チャットしかできません。電話機能もありません。スマホがないと使えません。


これでこの記事は終わりです！ハートやフォロー宜しくお願いします！
つい忘れがちなuseとかnamespaceのエラーをさらっともう一度

## あなたは Target class [App\Http\Controller\UserController] does not exist.　とエラーを起こしたことはあるだろうか

どうせ予測変換が自動で補完しているから覚えるだけ無駄。そう思った瞬間にあなたはこのエラーに出会う確率はぐっと高くなる。
仮に見出しのようなエラーがでたとしてもuseやnamespaceによる原因だとは考えもしないから「コードがどっか悪いのかな？」と全然違う場所を調べて時間を使ってしまう。（かつての自分）

確かに自動補完で勝手に書いてくれるならそれはそれで嬉しいし楽だ。しかし、もしControllersフォルダの中にフォルダを作成してその中にControllerファイルを入れた場合、もちろん自動補正なんて効いちゃくれない（こんな形で補正してくれるプラグインがあるなら教えてほしい）。

自分も全てを理解しているわけではないが、と予防線を張りつつありがちなミスを列挙していこう。

##
## よくやりがちなミス


#### パスのタイポに気付けない
はい早速でました。見出しのエラー文の原因がこれです。
use 文を書き換えててよく犯すミスがこちらです。

use App\Http\Controller\UserController;　誤

一見正しそうに見えますがControllerはControllersが正しいです。といってもこれを見ているあなたは違いが腑に落ちていないでしょう。
おすすめは正しそうな似ているパスをコピーして下の行に貼ってみることです。１文字違うから視覚的におかしいと理解できるでしょう。

use App\Http\Controller\UserController;　 誤
use App\Http\Controllers\UserController;　正 

#### そもそもパスの階層がおかしい
ありがちなミスです。UserControllerをUserフォルダに入れてしまってuse文を書き忘れるミス

use App\Http\Controllers\UserController;　誤
use App\Http\Controllers\User\UserController;　正 

また、このミスはパスを書き換えるから誤って先ほどのSを消しちゃってまたエラーを作っちゃう無限ループが発生する地獄になるパターンあり。気を付けよう。

#### そもそもnamespaceをどこに書くのかわかっていない
namespaceはコントローラーに書きます。（ただフォルダ名まででおｋ）
もちろんフォルダ階層が変わるなら修正も必要です。

#### Facadesを読み込んでない

ファイル内でDB::~~など書いててuse文を書き忘れちゃったパターン。Facadesを使う場合はuse文に一言書きましょう。

##

ミスは誰でもしてしまう事です。とにかくロジックを理解しようとすることは立派なことではありますが、上記のようなことを疎かにしてエラーが発生することは多々あります。エラーが起こった原因とその対処法をまとめておくのも良いでしょう。良いプログラミングライフを。
AWS Transfer FamilyでVPCにホストしつつインターネットアクセスを可能にする話

# 要約

「VPCでホスト」で「インターネット向け(internet-facing)」を選ぶとAZ分のElasticIPが必要になるので、「内部」を選択しつつNetworkLoadBalancer(NLB)を使ってインターネットからのアクセスを可能にしました。

# AWS Transfer Family

S3やEBSへのSFTP等のアクセスを可能にするサーバー。
作成時の「エンドポイントを選択」で「パブリックアクセス可能」を選べば本当に簡単に作成できます。

# 構築してみた手順

1. VPCを準備
    1. パブリックサブネットとプライベートサブネットを2つ
    1. NATゲートウェイとElasticIPを2つ
1. アクセスをさせたいS3を作成
1. IAMロールとIAMポリシーを作成、対象のS3アクセスをしつつTransferFamilyを信頼する
1. TransferFamily用のセキュリティグループを作成
    1. 22,2222を開ける
1. TransferFamilyを作成
    1. プロトコルはSFTP
    1. 「ID プロバイダーを選択」では"サービスマネージド"
    1. 「エンドポイントを選択」
        1. "VPCでホスト"
        1. "内部"
        1. 作ったVPCとプライベートサブネット
    1. 「ドメインを選択」は"Amazon S3"
    1. あとの詳細設定は適当に...
1. NLB用のセキュリティグループを作成
    1. 22,2222を開ける
1. NLB用のターゲットグループを作成
    1. "IP アドレス"
    1. 「プロトコル」は"TCP"、「ポート」は"22"
    1. 「ターゲットを登録」
        1. TransferFamilyに払い出されたプライベートIPを指定
        1. 「保留中として以下を含める」で追加
1. EC2のロードバランサーからNLBを作成
    1. "インターネット向け"
    1. さっき作ったターゲットグループとセキュリティグループを設定

# その他

1個しか立ち上げなくていいならElasticIP使えばNLBいらない気がします。
【PHP】リフレクションを使えばクラスやメソッドの構造を取得することができる

# 初めに
リフレクションという機能をご存じでしょうか？
私は名前は聞いたことあるけどよくわかっていませんでした。
改めて調べてみてすごい便利だなと感じたので、今回はリフレクションについてアウトプットします。

# リフレクションとは
リフレクションとは、クラスやメソッドなどの構造をデータとして扱うことができる機能です。
この機能を使うと以下のような情報を取得することができます。
* クラスの属してる名前空間
* クラスに定義されているメソッドの一覧
* クラスが利用しているtraitの一覧
* クラスの継承元やインターフェース

などなど。

## 使い方

PHPには```ReflectionClass```というクラスがあり、以下のようにReflectionClassのインスタンスを作成することでクラスの情報を扱うことができるようになります。

```php
// ReflectionClass型のインスタンスを作成
// 引数にはクラス名を指定
$reflectionClass = new \ReflectionClass('TargetClassName');
```

```ReflectionClass```クラスにはクラス構造を取得する様々なメソッドが定義されいます。
以下のようなイメージです。
```php
// TargetClassNameクラスで定義されている定数一覧を取得する
$constants = $reflectionClass->getConstants();

// TargetClassNameクラスで定義されているメソッドの一覧を取得する
$methods = $reflectionClass->getMethods();
```


## 様々なリフレクション
リフレクションはクラス情報だけでなくメソッドや定数の情報を取得ためのクラスも用意されています。
定数の情報を扱う```ReflectionClassConstant```クラスやメソッドの情報を扱う```ReflectionMethod```クラスなど、その他にも様々なクラスが定義されています。
詳しくは[公式ドキュメント](https://www.php.net/manual/ja/class.reflection.php)をご覧ください。

```php
// 定数情報を取得
$reflectionConstant = new \ReflectionClassConstant('TargetClassName', 'TARGET_CONST');
$reflectionConstant->isFinal()


// メソッド情報を取得
$reflectionConstant = new \ReflectionMethod('TargetClassName', 'targetMethodName');
$reflectionConstant->isProtected();
$reflectionConstant->isPublic();
```


## リフレクションの使い道
普段書く機会が少ないリフレクションですが、以下のような場面で使われています。
（といっても半分は想像です）

### 1. フレームワークの内部処理
フレームワークは、そのフレームワークを利用している側が書いたクラスを適切に扱う必要があります。利用者が書いたソースコードはフレームワーク開発者からすると動的なものです。リフレクションを用いれば、そのソース情報をうまく扱うことが可能です。

### 2. コードの解析
クラスやメソッドの情報を取得すればコードの解析を行うことができます。
アクセス修飾子や親クラスを取得することで、書かれたコードがルールに沿っているかどうかなどを調べることができそうです。


### 3. ドキュメント自動生成
リフレクションを用いればクラスやメソッドのDocの情報を取得することが可能です。
その情報を元にドキュメント生成が可能になるのかなと思います。


## 終わりに
今回はリフレクションについて調べてみました。
普通の開発でリフレクションを使う機会は少ないですが、「フレームワークの内側ってどうなってるんだろう」を解消していくのは楽しそうなので、知っておいて損はないですね。
リフレクションを使えば独自の解析ツールなんかも作成できそうで可能性を感じました。

また何か分かったらアウトプットします！
ここまで読んでいただきありがとうございました！
[初心者向け]コンピュータサイエンスにおけるキューとスタックの考え方

こんにちは。Kaneyasuです。
年が明け、また情報処理技術者の季節が近づいてきましたね。
今日は午前の問題にたまに出てくるキューとスタックについての記事を書いてみました。

# 本記事のターゲット

本記事はプログラミングの初心者の方に向けて記述したものです
プログラミングしているとしばしば出てくるキューとスタックについて、考え方を述べています。
概念を述べており、実際のコードは記述していません。
教科書の言葉だけではいまいちピンとこない時に、理解を深めるために役立つと幸いです。

# キューとスタックとは

キュー（Queue）とスタック（Stack）は、コンピュータサイエンスにおける2つの基本的なデータ構造です。
これらはデータを格納し、取り出す方法が異なる特徴を持っています。

どちらも、データの格納と取り出しに明確なルールが設けられており、いちいち格納位置・取り出し位置を指定しないのが特徴です。

# キュー(First-In-First-Out)

キューは先入先出し方と呼ばれるデータ格納方式です。
First-In-First-Out、略してFIFOと言われます。

現実世界で言うと、病院の受付番号などがこれにあたります。
病院に行って受付すると、受付番号をもらいます。
もらった受付番号順に診察は行われます。

![キューは受付番号のようなもの](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1959433/905766f6-e2e1-77bf-c90d-c7af7c31857a.png)

この時、受付番号を発行せずに、来た人をいきなり診察するような仕組みだと、待っている人は自分はいつ診察されるのかわからず混乱が生じます。
診察が一瞬で終わるならいきなり診察でも対応し切れるかもしれませんが、診察は時間が読みにくいので受付番号のような仕組みが必要になります。

システム開発においては、キューは以下のような状況での採用が考慮される方式です。

- 処理時間が読みづらく、リアルタイム処理ではタイムアウトの可能性がある
- 同時実行を許すと負荷が高まり過ぎる
- 同時実行を許すと矛盾が生じうる

私が関わることの多い開発においては、帳票出力などで採用されることがあります。
帳票出力が重たく、リアルタイム処理かつ多重実行を許すと、サーバーに負荷が高まりすぎる可能性がある場合、キューを使って以下のような仕様を設計します。
前提として、システムの画面にはユーザーに向けての通知機能があるとします

- ユーザーが帳票出力ボタンをクリックしたら、キュー登録。
- 画面上に受け付けたことを通知。
- バックグラウンドプログラムが、キューに登録された順でひとつずつ帳票出力、完了したら通知
- 通知を受け取ったユーザーは、通知画面から帳票をダウンロード

即時性よりも、確実にこなすことを優先するためにキューを活用する設計です。

# スタック(Last-In-First-Out)

スタックは後入先出し方と呼ばれるデータ格納方式です。
Last-In-First-Out、略してLIFOと言われます。

こちらは、現実世界で例えず、最初からコンピューターの世界でイメージした方が理解しやすいと思います。
以下の使い方が挙げられます。

- エディタのUndo機能
- ブラウザの戻る
- プログラムでエラーが発生した時のスタックトレース

この中から、エディタのUndo機能を例にしてみます。
操作履歴が下記の表だとします。

| 操作番号 | 実施した操作         |
|--------|--------------------|
| 10     | 画像Dを削除          |
| 9      | 「明日も晴れるといいですね」を追加 |
| 8      | 文字のフォントを変更 |
| 7      | 段落Aを削除          |
| 6      | リンクCを挿入        |
| 5      | 新しい段落Bを追加     |
| 4      | 「こんにちは」を「こんばんは」に変更 |
| 3      | 画像Aを挿入          |
| 2      | 「今日はいい天気です」を追加 |
| 1      | 「こんにちは」を入力  |

エディタでUndo機能を使用した場合、操作番号：10のデータを取り出し、その内容を取り消します。
操作番号：10のデータは消されます。
（実際には、Redoがあるので、どこかには残すとは思います。）
さらにUndoした場合は、操作番号：10のデータを取り出し、その内容を取り消します。

このように、スタックは辿ることが重要なデータにおいて採用が考慮される方式です。

# 最後に

以上、キューとスタックの考え方について述べさせていただきました。
プログラミングの学習において、ただ覚えるだけの作業は苦痛を伴います。
何に使われる技術なのかがイメージできれば、学習の苦痛は少し和らぐと思いますので、本記事が誰かの役に立てることを願います。
リリース前の複数人での差分確認

開発ベンダに開発を依頼し、その後リリース前に差分をチェックするということをやっています。 複数案件一度にリリースすることもあるので、複数人で差分をチェックすることもあります。

そんなとき、私は [pgdf](https://github.com/KenjiOhtsuka/pgdf/tree/main) を使って差分を抽出して、他の人に確認を依頼します。

## 環境

* Python 3.10

## 差分の抽出

ここでは差分の例示のために、1ファイル`text.txt`を持つリポジトリを作成しました。
次のようなGitのLogになるよう変更を行いました。

```
* b579d39 (HEAD -> main) change lines
* d943a53 add 2 lines
* 1e5de18 add one line
* fc12d8b initial
```

最新の2つのコミットについて差分を抽出してみます。

次のコマンドを実行すると、 Excelファイルが生成され、 "diff_d943a53..b579d39.xlsx was generated." と出力されます。

```sh
$ pgdf d943a53 b579d39
```

ここで次のコマンドを実行してExcelを見てみましょう。

```sh
$ open diff_d943a53..b579d39.xlsx 
```

出力されるシートは2つです。

### Summary

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25846/eb1755b7-b560-e4e5-4b23-478f03b2f3e9.png)

`git diff --stat` の結果がまとめられています。

### Diff

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25846/4dad43a6-fef6-89b7-8ff1-2d0f668e863b.png)

`git diff` と、 変更箇所についての `git blame`, `git log --format=%s` の結果がまとめられています。
この右側の列に、チェックした人がマークを記入していけば、全てチェックされているのか確認できます。

## 補足

* コミットハッシュをブランチに変えれば、ブランチとの差分を出力できます。
* 特定のファイルの差分のみを見たければ、 `pgdf revision_1 revision_2 file_path` のように実行します。 ワイルドカードも使えます。
Google Cloud 認定資格の Associate Cloud Engineer  受験メモ

# Google Cloud 認定資格の Associate Cloud Engineer 

 - [公式サイト](https://cloud.google.com/learn/certification/cloud-engineer?hl=ja)
 - 試験時間が2時間、受験料は税別$125（受験時のレートだと18000円くらい）

# やったこと
- 一冊参考書を読む(1~2週間くらい)
- 試験申込
- 公式サイトにある「模擬試験」を受ける
- 受験！！

# 取得理由
- 現在GCPを使用しているサービスに携わっているから、個人的な勉強のため
- これまでAWSを使用していた（といっても基本的なことを把握しているくらいのレベル）


# 受験してみて
- 全体的に基本的な概要を把握していれば良さげ
- どのサービス・プロダクトを選択すればいいのかをざっくり把握しておくと良さげ
- 点数は知ることができないので、どれだけ点数とれたかはわからない、、
未来のレジ：AWS Rekognitionと画像分析の力

# はじめに

日本の人口減少とAIの進化が目まぐるしい昨今ですが、単純作業は無人化されていくだろうという推論のもと、未来レジについて考察してみた！

# やりたいこと

Amazon Rekognitionを用いて、画像認識した商品を、データベースにある価格表と照らし合わせて商品の価格を表示する。
これを構成図で書くとこんな感じ。

![構成図.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2840619/a4cc4730-7f4d-af56-249b-a591886fc8a6.jpeg)

# やってみた

さて、数々の困難を乗り越えほぼ完成したのですが
膨大な構築作業となるので需要がありそうでしたら続きをまた書きます。

続きが気になる方は、いいね、コメントいただけますと幸いです
fake-clientのRESTClientをIndexerInformerに渡すとpanicする

Kubernetes client-goのIndexerInformerを使った実装で、以下のようにclientsetからRESTClientを取得してcacheを作成しようとしたところ、go testでfake clientを渡した際にclient内部でパニックが発生していた。
```go
	cachedLw := cache.NewListWatchFromClient(clientset.CoreV1().RESTClient(), resource, "", fields.Everything())
	indexer, informer := cache.NewIndexerInformer(cachedLw, &v1.Pod{}, 0, cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
      // on create...
    },
    UpdateFunc: func(old interface{}, new interface{}) {
      // on update...
    },
		DeleteFunc: func(obj interface{}) {
      // on delete...
    },
  }, cache.Indexers{})
```
↓
```
E0119 10:17:45.953340   57221 runtime.go:79] Observed a panic: "invalid memory address or nil pointer dereference" (runtime error: invalid memory address or nil pointer dereference)
```


fake clientではなく実際のkubernetesに接続すると問題なく動作していた。原因を調べてみると[古いissue](https://github.com/kubernetes/client-go/issues/352)が見つかった。
どうもfake-clientのRESTClient()メソッドは動作する形では実装されていない模様。以下のような実装で、`RESTClient()`は常にnilを返す。

- https://github.com/kubernetes/client-go/blob/v0.29.1/kubernetes/typed/core/v1/fake/fake_core_client.go#L97-L99
```go
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeCoreV1) RESTClient() rest.Interface {
	var ret *rest.RESTClient
	return ret
}
```


go testでcacheは必要ないので、`cache.ListerWatcher`をmockにする形で解決した。
```go
import (
	"context"
	"testing"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/watch"
	k "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/fake"
	"k8s.io/client-go/tools/cache"
)

type podListWatcher struct {
	clientset k.Interface
}

// ListFunc knows how to list resources
func (w *podListWatcher) List(options metav1.ListOptions) (runtime.Object, error) {
	return w.clientset.CoreV1().Pods("").List(context.TODO(), options)
}

// WatchFunc knows how to watch resources
func (w *podListWatcher) Watch(options metav1.ListOptions) (watch.Interface, error) {
	return w.clientset.CoreV1().Pods("").Watch(context.TODO(), options)
}

func Test(t *testing.T) {
	clientset := fake.NewSimpleClientset()
	// prepare fake client...

	lw := &podListWatcher{clientset: clientset}
	indexer, informer := cache.NewIndexerInformer(lw, &v1.Pod{}, 0, cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			// on create...
		},
		UpdateFunc: func(old interface{}, new interface{}) {
			// on update...
		},
		DeleteFunc: func(obj interface{}) {
			// on delete...
		},
	}, cache.Indexers{})
}
```
既存のVPCにSSMで接続するcloud9環境を構築したら躓いた

AWS上でIDE環境を構築できるサービスとして、Cloud9が提供されています。  
このCloud9はIDE用の環境を自動構築してくれるのですが、その裏ではCloudFormationのStackが新規作成され、それによってVPCやEC2などのインスタンスが作成されます。

今回、デフォルトのVPCではなくVPCウィザードを利用して事前にVPCを作成した後、cloud9のコンソールからIIDE環境を自動構築したところ、なぜかEC2に接続できないエラーが発生してしまい、IDE環境に接続できず少しハマってしまいました。

自身の備忘としても、解決方法を記録しておきます。

# 事象
- VPCウィザードを利用してVPCを作成
  - パブリック/プライベートサブネット,インターネットゲートウェイあり
- Cloud9コンソールから環境を自動作成
  - EC2への接続オプションはSSMを選択
- Cloud9によって作成されたCloudFormationのStackのステータスは`CREATE_COMPLETE`になっていた
- Cloud9側では、以下の通りEC2に接続できないエラーが出ていた
![4-cloud9-error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601535/f4307d26-8595-17b0-11e9-8075bd621823.png)


# 原因
- EC2を作成したサブネットの`パブリック IPv4 アドレスの自動割り当てを有効化`にチェックが入っておらず、EC2にパブリックIPが割り当てられていなかった

## 詳細
- Cloud9による自動構築でSSMを利用する際はEC2インスタンスがパブリックIPを持っている必要があった
  - (SSMを利用したEC2への接続自体には、パブリックIPは必須ではなく別の構成もありえるのでご注意を...)
- パブリックサブネットにインスタンスを配置していたが、サブネット側でパブリックIPを自動割当しない設定になっていたので、インスタンスにパブリックIPが割り振られていなかった
- それによってCloud9とEC2が通信できずに接続エラーになっていた

# 解決方法
以下の流れで対応していきます。  
(VPC自体は作成されている状態からスタート)
1. サブネットの設定を修正
2. 再度Cloud9の環境を構築

## 1. サブネット設定を修正
まずは事前に作成しているVPC内のパブリックサブネットを選択し、`サブネットの設定を編集`をクリックします。
![1-subnet.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601535/c9eb0858-3814-5841-0fed-488a45ba5c76.png)


遷移後の画面で、`パブリックIPv4アドレスの自動割り当てを有効化`にチェックを入れ、画面下部の`保存`をクリック
![2-subnet.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601535/a145bbcb-5fc8-ce06-f04c-05011c5427b6.png)

![3-subnet.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601535/9210fb18-e95e-0868-d34a-12d96fd7006f.png)

## 2. 再度Cloud9の環境を構築
この状態で再度Cloud9から環境を構築すると、正常に環境を作成することができます。
![5-cloud9-retry.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601535/ea1f9952-b264-de6a-c000-2e5f055083a1.png)

![6-cloud9-retry.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601535/48e58136-bfd3-b085-9c44-7c544ffb9742.png)

![7-cloud9-retry.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601535/a98cc9f4-3fb9-7aef-f77d-71e8e7d554bd.png)

![8-cloud9-retry.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601535/4c67f11b-486f-4596-4e70-956f5b810558.png)


# まとめ
無事に既存のVPCへCloud9の環境を構築することができました。  
ちなみに今回のエラーは、サジェストされている注意事項をきちんと確認すればすぐに解決する内容でした。。。
![9-cloud9-confirm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601535/e4e95584-95d4-2372-ec22-f37a7039ba1f.png)

今後はしっかりと確認しながら構築を進めたいと思います！

なにか間違いやご指摘がありましたらコメントいただけますと幸いです！
Ubuntuのopensshを9.6.1pにアップデート

opensslの脆弱性対応で9.6.1にアップデートしたのでメモ

### 環境
仮想化：VMware Workstation-playre17またはVMware workstation-pro17
ホスト：Windows10またはWindows11pro
ゲスト：ubuntu22.04.3TLS

環境構築時のメモ
[VMでWindowsホストにUbuntuをゲスト起動する](https://qiita.com/38pinn/items/a136ed43457562c8471d)
[dockerコンテナ環境作成](https://qiita.com/38pinn/items/92a65ea162098f7a9859)


### アップデート手順
``` sh:現状のsshのバージョン確認
ssh -V
```

``` sh:パッケージリストを更新
sudo apt-get install aptitude
```


``` sh:aptitudeのインストール
sudo apt-get install aptitude
```

``` sh:必要なパッケージのインストール
sudo aptitude install build-essential zlib1g-dev libssl-dev libpam0g-dev libselinux1-dev libkrb5-dev
```

``` sh:ディレクトリ作成と設定
sudo mkdir /var/lib/sshd && sudo chmod -R 700 /var/lib/sshd/ && sudo chown -R root:sys /var/lib/sshd/
```

``` sh:作業用ディレクトリ移動
cd ~/Downloads/
```
任意のディレクトリを指定

``` sh:ソースのダウンロードと展開
wget -c http://mirror.exonetric.net/pub/OpenBSD/OpenSSH/portable/openssh-9.6p1.tar.gz
```
2024/1/20現在の最新版を指定

``` sh:ソース展開
tar -xzf openssh-9.6p1.tar.gz
```

``` sh:ディレクトリ移動
cd openssh-9.6p1
```

``` sh:OpenSSLの位置を確認
which openssl
```

``` sh:結果確認
/usr/bin/openssl
```

``` sh:configure
./configure --with-kerberos5 --with-md5-passwords --with-pam --with-selinux --with-privsep-path=/var/lib/sshd/ --sysconfdir=/etc/ssh --with-ssl-dir=/usr
```
`--with-ssl-dir=/usr`は、opensslがあるディレクトリのヘッダを指定

``` sh:make
make
```

``` sh:インストール
sudo make install
```

``` sh:再起動
shutdown -r now
```

``` sh:バージョンアップされていることを確認
ssh -V
OpenSSH_9.6p1, OpenSSL 3.0.2 15 Mar 2022
```

``` sh:サービス起動起動していることを確認
sudo systemctl status ssh.service
```
active(running)を確認する

### 参考にしたページ

https://manualmaton.com/2023/12/21/ubuntu20-04%e3%81%aeopenssh%e3%82%928-2p1%e3%81%8b%e3%82%899-6-1p%e3%81%ab%e3%82%a2%e3%83%83%e3%83%97%e3%83%87%e3%83%bc%e3%83%88%e3%80%82/
当方の環境差分以外は、ほぼコピペさせていただきました。

### UnuntuのCVE(脆弱性)データベースのステータス見方
[The Common Vulnerabilities and Exposures (CVE) system](https://ubuntu.com/security/cves?q=openssh&package=openssh&priority=&version=&status=)
**Released**: 脆弱性に対するアップデートがリリースされており、適用可能であることを意味する。
**Needs triage**: 分析中であり、Ubuntuセキュリティチームが対応を決定する前の段階。脆弱性の影響や重要度に応じて、対応の優先度が決定される。
**Needed**: 脆弱性に対する修正が必要だが、まだ具体的なアップデートはリリースされていない。
**Deferred**: 現時点での対応は延期されていることを示す。これは、リソースの制限や脆弱性の影響が限定的であるなど、さまざまな理由による。
**Not vulnerable**: このステータスは、該当バージョンのUbuntuがその脆弱性の影響を受けないことを示す

### 当方の環境での設定log

[xxx設定ログ](xxxx)
Next.jsにvitest入れ、テスト時のエラー回避

## テスト実行に発生するエラー内容

```bash
Error:~~~hogehoge /src/test/setup.ts). Does the file exist?
```

`src`ディレクトリ配下に`test`ディレクトリを作成して`setup.ts`がないと言われている。早速、作成してみる。

## 解決策

```tsx
import '@testing-library/jest-dom';
```

上記を記述。早速、こちらでエラー回避することができました。

## 実行

```tsx
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "test": "vitest watch",
    "coverage": "vitest run --coverage",
    "lint": "eslint src",
    "lint:fix": "eslint src --fix",
    "format": "prettier . --write",
    "prepare": "husky install"
```

```tsx
pnpm test
```
chromiumいつもログアウト問題

# なんちゃってダッシュボード
ラズパイ+GUI+ディスプレイで、常時カレンダー表示の
簡易ダッシュボードを作っています。

# バージョンたちの確認

    $ more /proc/device-tree/model
    Raspberry Pi 3 Model B Rev 1.2

    lsb_release -a
    No LSB modules are available.
    Distributor ID: Raspbian
    Description:    Raspbian GNU/Linux 10 (buster)
    Release:        10
    Codename:       buster

    $ chromium --version
    Chromium 90.0.4430.212 built on Debian 10.2, running on Debian 10.13

# 毎回ログアウトされる問題
クッキーをもうすべて許可にしているのに、毎回ログアウトさせられてしまう。
![スクリーンショット 2024-01-20 10.12.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675614/db89bccd-e715-0f27-5255-b25a10e3d6a3.png)

クッキーはすべて許可（通常はサードパーティのみ除外）、クッキー許可にgoogleも追加
閉じたときに、クッキー削除はもちろんオフ
![スクリーンショット 2024-01-20 10.25.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675614/27989fbe-034b-1040-74ec-a27c82c39824.png)

新プロファイルにて挑戦
![スクリーンショット 2024-01-20 10.12.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675614/b364e611-e2aa-a215-9c24-53c4d9d72bb7.png)

念のため、プライバシーセッテイング内容
![スクリーンショット 2024-01-20 10.15.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675614/b164eca0-119b-94ca-86e0-d5053cdea24a.png)


起動時にログイン画面が出て、IDとパスは覚えてくれているけど、毎回ログインさせられてしまう。。。

再起動しなければ一旦いいけど、ほっとくと勝手にログアウトされたこともありました。

# 再インストール
今日は再インストールしてみます。
設定ファイルを含めたパッケージ完全削除

    $ sudo apt-get --purge autoremove chromium
    $ sudo apt update # 念のためアップデート
    $ sudo apt install chromium -y

だめでした。。。。
作業用スクリプトをPrologで書く

# 作業用スクリプトをPrologで書く

スクリプトファイル単体でいい感じに実行できたので導入手順とテンプレートを載せておきます。

## インストール

Prologの処理系はいくつかあるが、恐らく一番メジャーな`SWI-Prolog`を利用します。

```sh
brew install swi-prolog
```

## スクリプトファイル作成

拡張子は `.swi`か`.pl`のいずれかであるが、`.pl`だとPerlの拡張子と被るので `.swi`の方が良いです。
`sample.swi`というスクリプトファイルを作成して、以下の内容を記述します。

```prolog
#!/bin/sh swipl

% プログラム実行時の引数を取得する
get_command_line_arguments(Arguments) :-
    current_prolog_flag(argv, Arguments).

% 外部コマンドを実行する
run_command(Command, ExitCode) :-
  shell(Command, ExitCode).

% 外部コマンドを実行する(実行結果も取得)
run_command_with_output(Command, Output) :-
    process_create(
        path(sh),
        [ '-c', Command ],
        [ stdout(pipe(Out)), stderr(pipe(Err)) ]
    ),
    (
        read_string(Out, _, Output),
        close(Out),
        read_string(Err, _, ErrorOutput),
        close(Err),
        (ErrorOutput == '' -> true ; writeln(ErrorOutput))
    ).

% 実際の使用例
main :-
    % 外部コマンドを実行
    writeln('コマンドの実行結果'),
    run_command('ls', ExitCode),
    write('コマンドのExitCode : '),
    writeln(ExitCode),

    writeln('コマンドの実行結果(結果も取得)'),
    run_command_with_output('ls', Output),
    writeln(Output),

    write('プログラム引数 : '),
    % 引数を取得
    get_command_line_arguments(Arguments),
    writeln(Arguments).

% メインを実行
:- initialization(main, main).
```

この状態で以下のコマンドを実行してファイルに実行権限を与えます。

```sh
chmod +x sample.swi
```

あとは以下のように実行すればOKです。

```sh
sample.swi
```

プログラムのベースはChatGPTを利用して作成しましたが、そのままだと動かなかったので一部修正してあります。
ChatGPTが取り込んだサンプルにPrologが少なかったのかは不明ですが、「実際のSWI-Prologのバージョンで使われるのは`create_process/4`です」などと嘘を言ってくるので、完全に信頼はできないですね。。。

## 参考

- https://www.swi-prolog.org/build/macos.html
- https://www.swi-prolog.org/pldoc/man?predicate=process_create/3
testing-library/user-eventでネイティブHTMLのchangeイベントを再現できるのか

# 1. 結論
再現できませんでした。。

以下の記事でまとめられていました。

https://zenn.dev/ikuma/articles/vue-cannot-use-user-event-as-change

- testing-library/user-eventはReactを想定して各イベントの発火処理をラップしています。
- ネイティブHTMLのchangeイベントは要素が確定したタイミングで発火します。
これは一般的にinput要素からフォーカスが外れたタイミングです。
- 一方ReactにおけるonChangedは要素の値が変更されたタイミングで発火されます。

# 2. 背景
業務でjestを使ってテストコードを書いています。
あるプロジェクトではノーフレームワークでソースは書いています。
そのプロジェクトでテストコードを書いている時にユーザのinput要素へのchangeイベントを再現する記述が冗長なので何か良い方法はないかなと考えていた為です。

```ts:ユーザのinput要素へのchangeイベントを再現する関数
const dispatchUserChangeEvent = (elm: Element, value: string) => {
    if (!(elm instanceof HTMLInputElement)) {
        console.error('elm is not HTMLInputElement');
        return;
    }
    const inputElm = elm;
    inputElm.value = value;
    inputElm.dispatchEvent(new Event('change'));
};
```

testing-libraryの存在は知っていましたがネイティブHTMLでもテストできる*ことを知ったので、検証してみます。
*inputイベントを起こすことはできました。
後述するコードの
```
inputElement.addEventListener('change'
```
changeをinputに変えるだけで確認できます！

# 3. 検証

https://www.npmjs.com/package/@testing-library/user-event

```terminal
npm install @testing-library/user-event @testing-library/dom
```

以下のように実装しましたがchangeイベントを起こすことはできませんでした。

```ts
import { waitFor } from '@testing-library/dom';
import { userEvent } from '@testing-library/user-event';

const consoleLogMock = jest.spyOn(console, 'log').mockImplementation();

test('example test with jest-environment-dom', async () => {
    // Create an input element and append it to the body
    const inputElement = document.createElement('input');
    inputElement.setAttribute('type', 'text');
    document.body.appendChild(inputElement);

    inputElement.addEventListener('change', () => {
        console.log('input changed');
    });

    userEvent.type(inputElement, 'Hello, World!');

    await waitFor(() => {
        expect(consoleLogMock).toHaveBeenCalledWith('input changed');
    });
});

```
初学者に向けたブラウザの仕組み

## はじめに
フロントエンド開発を進める中で、ブラウザは欠かすことのできないものです。
マークアップ言語(今回はHTMLに限定)のファイルがブラウザに届いたら、なぜ画面にちゃんと表示されるのか？
そのなぞについて初学者が、過去のさらなる初学者の自分へ伝わるように抽象化したブラウザの仕組みをまとめてみます。まだなぞな部分が多いため、認識のずれもあるかもしれません...

## 画面に表示するまでの流れ
ブラウザのレンダリングエンジンによってマークアップ言語ファイルを読み取り、画面に表示してくれる大まかな流れは以下の通りです。
```mermaid
graph LR;
パース　-->スタイル-->レイアウト-->ペイント-->コンポジット;
```
これがブラウザの初期表示する際の処理フローとなります。
JavaScriptなどによって表示内容が更新された場合は、変更に必要な工程から処理を行います。そのため再処理が不要な工程は省略されます。
それでは、以下にそれぞれの工程の中で、どのような処理をされているのかを大まかにまとめます。

### 1. パース
この段階でまずは、レンダリングエンジンのパーサーによってHTMLファイルとCSSファイルの解析をして、以下のような構造体に変換します。
HTMLファイルは、ブラウザエンジンのHTMLパーサーによってDOMツリーという構造体へ。
CSSファイルは、ブラウザエンジンのCSSパーサーによてスタイルルールズという構造体へ。


### 2. スタイル
パースで作成されたHTMLのDOMツリーと、CSSのスタイルルールズのツリー構造をひもづけていきます。
このふたつの構造体が結びつけて、一つのレンダーツリーを作成します。
この処理によって、HTMLのどの要素にどのようなスタイルが適用されるかを割り出します。

### 3. レイアウト
レンダーツリーの上位の要素から、要素の位置と大きさを再起的に計算していきます。

ここでは、CSSの以下のようなプロパティがレイアウトの処理に関わります。
```css
height, width, padding, margin, top, right, left, bottom, box-shadowなど
```

この処理でレイアウトツリーに変換します。

### 4. ペイント
この処理では、ペイントレコーズとレイヤーツリーを作成します。

- **ペイントレコーズ**
レイアウトツリーをもとに画面上への描写指示となるもの
- **レイヤーツリー**
Webページの要素がどのように画面に描画されるかを管理するために使用されるもの
ここでは、CSSの以下のようなプロパティがレイアウトの処理に関わります。
```css
position, z-index, opacity, transform, filterなど
```

### 5. コンポジット
ラスタライズ処理を行います。
ラスタライズ処理は、画像や文字などを表示するための処理です。ペイントレコーズとレイヤーツリーの情報をもとに処理されます。
この処理によってピクセル単位のビットマップに変換され、最終的な画像が得られます。


## まとめ
抽象的ですが、ブラウザがHTMLファイル、CSSファイルから解析して画面に表示されるまでの流れをまとめました。このようにブラウザどのように処理を進めていくかを理解することで、CSSの当て方やアニメーションの実装にも役立つなと感じました。
CSSの上書きされてしまう場合やアニメーションの実装も処理を軽量にするプロパティを選定することができるなと感じました。かなり抽象的な内容でしたが、全体像だけでも理解していると今後のコーディングに役立つかと思います。