OmniAICreator/Qiita-1.07M huggingface dataset:
Ruby の pack と unpack が分からない。あと日本語でエンコードが元に戻せない。

# pack / unpack とは何なのか？

公式の例で分かりやすそうなものをひとつ


文字列を数値(文字コード)の配列に変換する例

```rb
"Ruby".unpack('C*')    # => [82, 117, 98, 121]
```

数値(文字コード)の配列を文字列に変換する例

```rb
[82, 117, 98, 121].pack("C*")    # => "Ruby"
```

なんとなくpackとuppackの語感と役割が逆なような気がして戸惑うが

たとえば上記のように

- 文字列を数値(文字コード)に「ほどく」のが unpack 
- 逆に数値(文字コード)を文字列に「固める」のがpack 

ということだと受け止めた

## 参考

https://docs.ruby-lang.org/ja/latest/method/Array/i/pack.html
https://docs.ruby-lang.org/ja/latest/method/String/i/unpack.html

# 引き数のアルファベットと数字

unpack/packの引き数に指定するアルファベットと数字は何なのか？

アルファベットが種類で、数字部分が文字数指定のようだ
数字部分にアスタリスクを使うと可変長の変換をすることが出来る

以下はUTF-8の文字列から16進数への変換をした例

```rb
p '田中'.unpack('H1') # ["e"]
p '田中'.unpack('H2') # ["e7"]
p '田中'.unpack('H3') # ["e79"]
p '田中'.unpack('H*') # ["e794b0e4b8ad"]
```

以下はUTF-8の文字列からASCIIへの変換をした例

```rb
p '田中'.unpack('A1') # ["\xE7"]
p '田中'.unpack('A2') # ["\xE7\x94"]
p '田中'.unpack('A3') # ["\xE7\x94\xB0"]
p '田中'.unpack('A*') # ["\xE7\x94\xB0\xE4\xB8\xAD"]
```

# なぜ unpack で配列が返ってくるのか？

戻り値はすべて配列形式で返ってくるのだが、
変換形式によっては要素1個の配列になるものと、複数要素を持つ配列になる場合があるようだ

冒頭に書いた数字への変換の場合は配列内で複数要素が使われている

```rb
 "Ruby".unpack('C*')    # => [82, 117, 98, 121]
```

16進数変換の場合は 要素1個だけの配列だったり

```rb
p '田中'.unpack('H*') # ["e794b0e4b8ad"]
```

# エンコード問題 – unpackをpackしても文字列が元に戻らない

アルファベットであれば unpack / pack で元に戻ってくれるようなのだが

```rb
p 'ABC-XYZ'.unpack('A*').pack('A*') # ABC-XYZ
p 'ABC-XYZ'.unpack('H*').pack('H*') # ABC-XYZ
```

日本語だとそのままではうまくいかないようだ

```rb
p '田中'.unpack('A*').pack('A*') # "\xE7\x94\xB0\xE4\xB8\xAD"
p '田中'.unpack('H*').pack('H*') # "\xE7\x94\xB0\xE4\xB8\xAD"
p '田中'.unpack('C*').pack('C*') # "\xE7\x94\xB0\xE4\xB8\xAD"
```

この場合は force_encoding を指定するともとに戻すことが出来た

```rb
p '田中'.unpack('A*').pack('A*').force_encoding('UTF-8') # "田中"
p '田中'.unpack('H*').pack('H*').force_encoding('UTF-8') # "田中"
p '田中'.unpack('C*').pack('C*').force_encoding('UTF-8') # "田中"
```


なおコンソールの環境によっては `puts` すると元の文字列に見えるかもしれないが、実態は異なるようだ


```rb
puts '田中'.unpack('H*').pack('H*') # 田中
puts '田中'.unpack('H*').pack('H*') == '田中' #  false
```

puts ではなくデバッグ用の `p` で確かめたほうが良さそうだ



# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ


# プロフィール・経歴

https://github.com/YumaInaura/YumaInaura
ファイルアップロード時エラーの判定



+ [ファイルアップロード時の既に存在するエラーについて](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007ajHNo)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/60dadb04-575e-586c-0840-23cb3950d39e.png)


:::note
Apexトリガーでカスタムの入力規則が設定されていることはないでしょうか？

数式では添付ファイルの入力規則は作ることができない
:::


+ [JFIFファイルの扱い・対策について](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007ifvHQ)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/3d02712d-1e45-b446-6231-594d2a826903.png)


:::note
リファレンスを見てもFileExtensionが読み取り専用とは書かれてないみたいですが、不思議な項目のようです。

PathOnClientに拡張子として指定すると FileExtension を変更できるようなことが回答されています。

```
ContentVersion cv = new ContentVersion();
cv.Title = 'Test Image';
cv.PathOnClient = 'Test_Image.jpg';
```

https://salesforce.stackexchange.com/questions/265837/contentversion-fileextension
:::

+ [アップロードファイルに拡張子制限をかけたい](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX000000nqsJ)

:::note
Apex トリガーを使って特定の拡張子のみを添付の許可をする方法が提案されています。

https://sudipta-deb.in/2024/02/restrict-uploading-files-in-salesforce.html
:::
[TypeScript] まずは基本的な型宣言をマスターしよう！

## 概要
TypeScriptの基本的な型宣言について、まとめました。

### 【目次】
[まずは基本的な型宣言をマスターしよう！](https://qiita.com/PenPe/items/c189a611b3407b7ceef9) ←🈁
[オブジェクト型の型宣言をマスターしよう！](https://qiita.com/PenPe/items/440683cc6e5e4ac0ca32)
[関数の型定義をマスターしよう！](https://qiita.com/PenPe/items/bff109c6f83f46d191a7)
[配列をマスターしよう！](https://qiita.com/PenPe/items/7e056eec2cce76a745a9)
[インターフェースをマスターしよう！](https://qiita.com/PenPe/items/f9314aeccb2f23cac382)
[クラスを使いこなそう！](https://qiita.com/PenPe/items/c8e6289f386c4db23a6f)
[型修飾子についての理解を深めよう！](https://qiita.com/PenPe/items/7af57861e93830fe9a79)
[ジェネリックをマスターしよう！](https://qiita.com/PenPe/items/fde6a342e746e265d907)
[独自の構文拡張をマスターしよう！](https://qiita.com/PenPe/items/c79cdb5bc4a68a1dfd7f)

---

### 型アノテーション
変数にデータ型を指定したい場合に用いる型宣言
`let 変数名: 型（string, number, boolean...）`


```typescript
let hoge: string
```


#### :memo: 以下の型定義はTypeScriptがデータ型を推測できるため冗長
```typescript
let foo: string = "value";
```


#### :memo: 型エラーと構文エラーについて
 型エラー -> JavaScriptへのコンパイルは問題なく実施できるケースが多い
 構文エラー -> JavaScriptへのコンパイルが失敗するケースが多い

---

### 合併型
 変数が複数のデータ型を取りうる場合に使用する
 `let 変数名: 型1 | 型2 …`
```typescript
let bar: string | number;

// 以下は型エラーが発生しない
bar = "string"
bar = 1234
```

#### 合併型で利用できるプロパティ
 利用できるすべてのデータ型で所持しているプロパティのみ


```typescript
let baz: string | number;


baz = Math.random() > 0.5 ? "string" : 1234
baz.toString // OK（string, number型で所持しているプロパティ）
baz.length // NG（lengthはstring型のみ保持） Property 'length' does not exist on type 'string | number'. Property 'length' does not exist on type 'number'.
```

#### :memo: 上記のようなケースでは変数が取りうるデータ型を限定させてあげれば良い。
```typescript
let qux: string | number;
qux = Math.random() > 0.5 ? "string" : 1234


if (typeof qux === "string") {
 qux.length // OK（quxが必ずstring型であることがこのコンテキストでは保証されているため）
}
```

---

### リテラル型の型アノテーション
リテラル型(`string, number…`)は特定の値をデータ型として指定できる


```typescript
let firstName: "takashi" | "taro";
firstName = "takashi" //OK
firstName = "tomoya" //NG: Type '""' is not assignable to type '"takashi" | "taro"'.
```

---

### 型エイリアス
 TypeScriptではデータ型に対して名前をつけて管理できる
 同じ構造のデータ型が多い場合は、コードの可読性が上昇する。


```typescript
type anyData = string | number | boolean;
let dataFirst: anyData; // let dataFirst: string | number | boolean と同意義;
let dataSecond: anyData; // let dataSecond: string | number | boolean と同意義;
```

### まとめ

以上です！
型宣言について理解を深めていきましょう〜


#### 追記
編集リクエストありがとうございます🙏
DatabricksでLlama3を動かしてみる

LLMの進化がすごいです。

https://llama.meta.com/llama3/

こちらからモデルへのアクセスを申請します。氏名、誕生日、国、所属などを入力します。

https://huggingface.co/meta-llama/Meta-Llama-3-8B

審査が通ると以下のようなメールが届きます。数分でメールが届きました。
![Screenshot 2024-04-19 at 8.18.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/35b02059-6e22-1eda-48f9-2e7588964db3.png)

アクセスが許可されています。
![Screenshot 2024-04-19 at 8.18.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/0220a91d-4f17-4c12-853a-7b991234bfef.png)

GPUクラスターを準備します。
![Screenshot 2024-04-19 at 8.33.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/5c25dfb0-9cb5-2fd2-d82d-d908236b5840.png)

アクセストークンを用いてHugging Face Hubにログインします。

```py
from huggingface_hub import login
login()
```

サンプルコードを実行します。

```py
import transformers
import torch

model_id = "meta-llama/Meta-Llama-3-8B"

pipeline = transformers.pipeline( "text-generation", model=model_id, model_kwargs={"torch_dtype": torch.bfloat16}, device_map="auto")
pipeline("Hey how are you doing today?")
```

動きました！

```
[{'generated_text': 'Hey how are you doing today? I am doing well. I am a little bit tired because I'}]
```

あとで、日本語や`Meta-Llama-3-8B-Instruct`も試します。色々工夫が必要らしいです。

https://huggingface.co/meta-llama/Meta-Llama-3-8B-Instruct

そして、こちらのブログにあるように、DatabricksはMetaとのパートナーシップを結び、Databricksの[基盤モデルAPI](https://docs.databricks.com/ja/machine-learning/foundation-models/index.html)を通じてLlama3を提供する予定です！

https://www.databricks.com/blog/building-enterprise-genai-apps-meta-llama-3-databricks

こちらの機能も利用できるようになったら試してみます。

### はじめてのDatabricks

[はじめてのDatabricks](https://qiita.com/taka_yayoi/items/8dc72d083edb879a5e5d)

### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)
リーダブルコード11章 12章要約

リーダブルコード11章と12章を超簡単に要約してみた

# 11章　一度にひとつのことを
- コードは１つづつタスクを行うようにしなければならない
- タスクは小さくできる
    - 複数のタスクをこなす関数を一つの小さな関数に分けることで読みやすくなる
- オブジェクトから値を抽出する
    - 一度に１つのタスクを適用する
    - タスクを分割する
- もっと大きな例
    - 領域で考えると分けやすい
- さらなる改善
    - 問題を異なる方向にスライスすることで、解決策が生まれる

# 12章　コードに思いを込める
- コードは簡単なことばで書く必要がある
    - おばあちゃんでもわかるように説明する
    - コードの動作を簡単な言葉で同僚にもわかるように説明する
    - 使っているキーワードやフレーズに注目する
    - 説明に合わせてコードを書く
- ロジックを明確に説明する
    - 簡単な言葉でコーディングする
        - 例）否定形を使わないとか
- ライブラリを知る
    - 簡潔なコードを書くのに欠かせないのは、ライブラリが何をしているかまで知る必要がある
- この手法を大きな問題に適用する
    - 解決策を言葉で適用する
        - わかりにくいロジックを抽出する
    - 手法を再帰的に適用する
- まとめ
    - 12章ではプログラムのことを簡単な言葉で説明する技法を紹介した
    - 説明することでコードは綺麗になる
    - 説明することで分割するべき下位問題がわかる
    - 問題を声に出して解決策が見える
    - プログラムの形を明確にする
[Meta] 新しいLLM の Llama 3 が発表されました

## Meta Llama 3の登場！業界をリードする最新AIモデル
Metaから「Meta Llama 3」がリリースされました。
なかなか凄そうなのですが、まとまった情報が日本語で無さそうなのでまとめてみます。

https://ai.meta.com/blog/meta-llama-3/

## とりあえず試したい方用

https://huggingface.co/chat/models/meta-llama/Meta-Llama-3-70B-Instruct

## 特徴

### 利用可能なプラットフォーム
Llama 3は、AWS、Google Cloud、Microsoft Azure、NVIDIA NIMなどの主要なクラウドプラットフォームに加え、Hugging FaceやKaggleなどのプラットフォームで利用可能になる予定。

### Claude 3 Sonnet・Gemini Pro 1.5を上回るスコア
![438037375_405784438908376_6082258861354187544_n.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3741957/a27fa457-485d-9c05-3b64-c029ba11424e.png)
[出典](https://ai.meta.com/blog/meta-llama-3/)

ほとんどの部分でClaude 3 Sonnet・Gemini Pro 1.5を上回るスコアを出しています。
ただ、Claude 3にはSonnetより上のOpusもおり、こちらのスコアよりは低め。
改めてClaude 3 Opus凄いな・・・。

![9ad98d612086fe52b3042f9183414669b4d2a3da-2200x1954.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3741957/3218d981-3d62-d180-529f-cc75a6640d22.png)
[出典](https://www.anthropic.com/news/claude-3-family)


### パラメータサイズ
Llama 3は、8B（80億パラメータ）と70B（700億パラメータ）のモデルを提供し、非常に幅広いユースケースに対応しているとのこと。

### 推論とコード生成の改善
Llama 3は、推論能力とコード生成能力を大幅に向上させており、より複雑な指示に応じて動作する能力がある。

### 多言語・マルチモーダル対応の計画
将来的には、多言語対応や画像とテキストを組み合わせたマルチモーダルなモデルを提供予定とのこと。

### モデルアーキテクチャ
標準的なデコーダのみのトランスフォーマーアーキテクチャを使用し、新しいトークナイザーやself-attentionを採用して効率と性能を向上させている。
デコーダのみにすることで、入力データから直接的にテキストを生成する際の効率が向上するようです。

### トレーニングデータ
公開されているソースから集められた15T（15兆トークン）以上のデータセットで訓練されており、その規模と質は前世代のモデルを大きく上回っている。

### スケーリングと効率性
新しいスケーリング法則に基づいて訓練が行われ、大規模なGPUクラスタを使用することで訓練効率が大幅に向上。
カスタムビルドされた24K GPUクラスタを使用して訓練しているとのことで、想像できないようなリソースの投資が行われていることがわかります。

### Building with Llama 3: 開発者向けのフレームワークとツール
#### Llama Guard 2
最新のセキュリティ要件に対応したモデルのセーフティを強化するツールで、プロンプトとレスポンスの安全性を向上。

#### Code Shield
コード生成の際に不安全なコードをフィルタリングすることで、より安全な使用が可能になるガードレール機能。

#### CyberSec Eval 2
セキュリティ評価ツールを使用して、モデルのセキュリティリスクを評価し、適切な対策を講じることができる。

#### torchtune
PyTorchネイティブのライブラリで、LLMのファインチューニングや実験が容易になります。このライブラリはメモリ効率が高く、様々なプラットフォームと統合されている。

## まとめ
広範なプラットフォームへの対応や、推論性能の向上、セキュリティと安全性を考慮した新しいツールの提供は、大きなメリットになりそう。
Meta AIで利用できるとのことですが、日本ではまだMeta AIが利用できないので、Meta AIが早く利用できるようになるといいのですが・・・。
AWS Bedrockで利用できるようになったら色々と試してみたいですね。
インスペクタでListを編集した際に、条件で要素を取り除く

# 目的

SerializeField属性でインスペクタにListを表示している場合、編集時に条件で要素をフィルターしたい。

# 結論

ISerializationCallbackReceiverを実装し、OnBeforeSerialize()内でListを編集する。
ScriptableObjectをインスペクタで表示した場合を想定したコードを下記に示す。

```c#
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public enum SomeEnum
{
    None, 
    A, 
    B, 
    C
}

[Serializable]
public class SomeSerializableClass
{
    [SerializeField]
    SomeEnum type;
    public SomeEnum Type => type;
}

public class SomeScriptableObject : ScriptableObject, ISerializationCallbackReceiver
{
    [SerializeField]
    List<SomeSerializableClass> list;

    int prevCount;

    void Awake()
    {
        prevCount = list.Count;
    }

    public void OnAfterDeserialize() { }
    public void OnBeforeSerialize()
    {
        if (list is null)
        {
            return;
        }
        
        if (prevCount < list.Count)
        {
            list[^1] = new();
        }

        list = list
            .GroupBy(x => x.Type)
            .Select(x => x.First())
            .ToList();

        prevCount = list.Count;
    }
}
```

# 失敗例

下記コードは、インスペクタ上で要素の追加ができなくなる。
インスペクタで要素を追加した時、最後の要素が複製されて追加されるが、それが重複とみなされ排除されてしまうためである。

```c#
public class SomeBehaviour : MonoBehaviour, ISerializationCallbackReceiver
{
    [SerializeField]
    List<SomeSerializableClass> list;

    public void OnAfterDeserialize() { }
    public void OnBeforeSerialize()
    {
        if (list is null)
        {
            return;
        }

        list = list
            .GroupBy(x => x.Type)
            .Select(x => x.First())
            .ToList();
}
```

# 参考

https://docs.unity3d.com/ja/2019.4/ScriptReference/ISerializationCallbackReceiver.html

https://atmarkit.itmedia.co.jp/ait/articles/1703/29/news027.html#:~:text=%E3%81%9D%E3%81%93%E3%81%A72%E3%81%A4%E7%9B%AE%E3%81%AE%E6%96%B9%E6%B3%95%E3%81%A8%E3%81%97%E3%81%A6%E3%81%AF%E3%80%81LINQ%E3%82%92%E5%B7%A5%E5%A4%AB%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%A0%E3%80%82
ITパスポート ソフトウェア開発手法

・ウォータフォールモデル
開発工程を要件定義、設計、開発、テストと上流工程から下流工程へと順番に進めていく開発手法

・アジャイル開発
短い開発工程を何度も繰り返し、迅速かつ段階的に完成度を高めていく開発手法
・エクストリームプログラミング（XP）
イテレーションと呼ばれる固定された短いサイクルで、動作するプログラミングを作ることを繰り返す
２人１組となってプログラミングをする（ペアプログラミング）
外部から見たプログラムの動作を変更せずに、内部構造を改善する（リファクタリング）
プログラムを書く前にテストケースを作成する（テスト駆動開発）
・スクラム開発
製品の責任者で機能の優先順位を決めるプロダクトオーナー、スクラム開発のプロセスを円滑に進めるスクラムマスター、実際の開発担うチームで構成される
スプリントと呼ばれる固定された短いサイクルで、動作するプログラムを作ることを繰り返す
毎日のミーティング（デイリースクラム）を重ねることで、問題が発生しても早めに解決できる

・その他の開発手法
・プロトタイピングモデル
システム開発の早い段階から試作品（プロトタイプ）を作成して、利用者の確認を得ながら開発を進めていく開発手法
・スパイラルモデル
システムをさらに独立性の高いサブシステムに分割し、サブシステムごとに要件定義や設計、開発、テストを繰り返しながら段階的にシステムを完成させていく開発手法
・RAD
利用者を含む少人数で、GUIなどによる開発支援ツールを活用してプロトタイプを作成し、評価と改良を繰り返すことで開発期間を短縮する開発手法
・DevOps
開発部門と運用部門が協力しながらシステムの改善を進めようという考え方
・リバースエンジニアリング
既存のプログラムを解析してそのプログラムの仕様書を導き出すこと

・ソフトウェアの見積り
・ファンクションポイント法
ソフトウェアの仕様から機能数を洗い出し、機能ごとの複雑度を考慮した重み付けをしてソフトウェアの規模を見積もる手法
・類推見積法
過去の類似したシステムの実績値を基にソフトウェアの規模を見積る手法
最低限でいいからVim操作したい

## はじめに
稀ですが業務内でVimを使用することがあります。最低限でいいのでVimの操作方法をまとめようと思います。

# そもそもVimとは
高機能テキストエディタのことです。

コマンドで操作するためとっつきにくさがありますが使いこなせればスピーディーにテキスト編集ができるメリットがあります。

## 確認 
ターミナルでVimがインストールされているか確認しておきましょう。Macには標準でインストールされているはずです。
```bash
vim --version
```

# 操作方法
最低限の定義としてはテキストの閲覧、編集、保存が行えれば十分かと思います。

## ファイル閲覧
```bash
vim ファイル名
```

## インサートモードへの移行
i　を押下するとインサートモード(編集可能モード)に移行します。
```bash
i
```

## 編集、保存
編集後は Esc キーでノーマルモードに戻ります。
ファイルを保存して終了するには :x を入力します。:xを使用すると編集していなくても閉じられるので便利です。

```bash
:x
```

## まとめ
Vim使いの事をVimmer（ビマー）というそうです。
これで駆け出しVimmerと名乗れるでしょうか。

## 最後に
私の働いている会社で経験の有無を問わず採用を行っています。
興味のある方は是非カジュアル面談から応募してみてください！

https://jambo-support.com/recruit_engineer/
Dartでリストからnullを削除する方法(Swift, Kotlin比較つき)

# 状況
例えば以下のような配列があって、`null`を排除したい

```dart
final list = [1,null,3,null,5]
```

Dartにはビルトインの高階関数が用意されていないが、`whereType`を用いたnullの排除ができる

```dart
final nonNullList = list.whereType<T>().toList() // 今回は`T=int`
```

# 補足

Swiftは`compactMap`が用意されている

```swift
let list = [1,nil,3,nil,5]
let nonNilList = list.compactMap{ $0 }
```

Kotlinは`mapNotNull`が用意されている
```kotlin
val list = listOf(1,null,3,null,5)
val nonNilList = list.mapNotNull{ it }
```
【Next.js】Storybookテストの書き方まとめ


# はじめに

初めまして。エンジニアのkeita_Maxです。

備忘録としてStorybookでテストを書く際によく使うものを置いておきます。


# 文字が表示されていることを確かめる

```react
await screen.getByText("テキスト")
```

# 要素が存在していることを確かめる

```react:storybook
await expect(screen.queryByTestId('text')).toBeTruthy()
```

# 要素が存在しないことを確かめる

```react:storybook
await expect(screen.queryByTestId('text')).toBeFalsy()
```
※ getByTextやgetTestIdだとエラーになってしまうので注意


# クリックする

```react:storybook
await fireEvent.click(screen.getByTestId('text'))
```

# 文字を入力する

```react:storybook
await userEvent.type(await screen.getByTestId('input'), 'input')
```

# 関数が呼ばれたことを検知する
```react:storybook
await waitFor(() => expect(args.handleClick).toHaveBeenCalled())
```



# 値の取り方について

以下公式ページにまとめてありました。

https://testing-library.com/docs/react-testing-library/cheatsheet


# おわりに

今後も自分で使用したテストの書き方を更新していきますので、良ければいいねとストックをしていただけると更新に気が付けると思います。

この記事での質問や、間違っている、もっといい方法があるといったご意見などありましたらご指摘していただけると幸いです。

最後まで読んでいただきありがとうございました！

# 参考

https://qiita.com/punkshiraishi/items/da9a221c7423f6f2d1c9
【コツコツ学習】バッチ処理とは

# バッチ処理とは
バッチ処理は大量の反復的なデータジョブを定期的に完了するためのコンピューターが使用する方法です。
バックアップ、フィルタリング、ソートなどの特定のデータ処理タスクは、個々のデータトランザクションで実行するには計算負荷が高く、非効率的となる場合があるので、そのようなタスクをバッチで処理します

まあ簡単に言えば、バッチ処理とは“一定量の(あるいは一定期間の)データを集め、一括処理するための処理方法です。

https://aws.amazon.com/jp/what-is/batch-processing/


# バッチ処理の歴史

>バッチ処理の歴史は汎用コンピューター(※1)時代まで遡ります。
今でこそ業務で使用するコンピューターは複雑なネットワークによって繋がっていますが、汎用コンピューター時代ではネットワークで共有するのではなく端末を共有するのが当たり前でした。
当時は端末自体かなり高価かつ貴重なものだったので、「計算機センター」といった特別な施設にのみ配備されていたのです。
汎用コンピューターにて業務データを処理する際は、都度データを運んでいては非効率的であるため、ある程度データ量が集まった段階で処理を依頼していました。
故にバッチ処理は“一括処理”とも呼ばれることがあります。

なるほどー
そんな歴史があったんですね

https://www.imkk.jp/blog/what-is-batch-processing.html

# なぜバッチ処理を使うのか？
効率的にするため

### 処理の流れ
1.処理の呼び出し
2.メモリの準備
3.データの読み込み
4.処理の実行
5.エラーハンドリング
6.メモリの解放


### バッチ処理
一括して処理するために一度で大量のデータをメモリに読み込みます。
そのため、メモリの使用量が増加し、システムのリソース（メモリ、CPU、ディスク容量など）に影響を与えます。
また、一括して処理が行われるため、処理時間も一定の時間がかかる場合もあります。

### 個別処理
個別のデータごとにメモリを確保し、処理を行うため、一度に処理するデータの量に応じてメモリ使用量が変動します。そのため、バッチ処理よりも柔軟にメモリを使用できる場合もあります。ただし、個別のデータ処理には処理単位ごとにメモリの割り当てや解放のオーバーヘッドが発生するため、処理速度が若干遅くなる可能性もあります。


>例えば、人気記事の月次ランキング機能は、アクセスごとに集計するのは非効率です。このような場合は、毎月決まったタイミングでバッチ処理を起動し、上位10件のリストを作成することによって高速なレスポンスを実現します。
メールを一斉送信する場合は、Webの画面で受け付けて送信後にレスポンスを返す実装にすると、リクエストしたユーザを長い時間待たせてしまいます。このような場合は、リクエストによってバッチ処理を開始し、ユーザにレスポンスを返します。裏側のバッチ処理でメールを送信すれば、ユーザを待たせずにレスポンスを返すことができます。
読み出す際の処理をできる限り抑えるために、事前に表示するデータを用意したり、時間のかかる処理を裏側で実行するために、バッチ処理を書く必要が出てきます。

なるほど
上記のようにメモリの確保や解放などが一回で済むなどで素早いレスポンスを返したりできるのか


# バッチ処理の基本
以下が分かりやすいです

https://qiita.com/utisam/items/1d15f95c933b7d39c556


特に勉強になったのは以下ですね


>## バッチ処理の抽象化
>起動
パラメータの解釈
データの読み込み
データの処理
処理結果の書き出し
成功・失敗の通知

# バッチ処理とトランザクション
トランザクションとは「全てが成功するか、全てが失敗するか」の原子性を持ちます。

一方、バッチ処理は一括して処理を行うことが特徴であり、複数のデータやタスクをまとめた処理を行いますが、個々のデータやタスクごとに完全なトランザクションの原子性を持つわけではありません。バッチ処理の中で一部のデータやタスクが成功し、一部が失敗することもあります。

なので理解を一緒にしてはいけないです。
node.jsでalexaスキル その7

# 概要
node.jsでalexaスキル、やってみた。
練習問題やってみた。

# 練習問題
Alexa-hostedで、ズンドコ、インテントを追加せよ。

# 方針
「ズンドコ」で、「ズン　ドコ キヨシ」

# ズンドコ、インテントを追加
- 対話モデル>インテント>インテントを追加>カスタムインテントを作成
ZundokoIntent

- 対話モデル>インテント>インテントを追加>サンプル発話
" ズンドコ "

- 対話モデル>インテント>インテントを追加>保存>スキルをビルド

# コードを追加

```
const ZundokoIntentHandler = {
	canHandle(handlerInput) {
		return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'ZundokoIntent';
	},
	handle(handlerInput) {
		let speechText = '';
		let j = 0;
		while (j < 4)
		{
			if (Math.floor(Math.random() * 2) < 1)
			{
				j++;
				speechText += "ズン ";
			}
			else
			{
				j = 0;
				speechText += "ドコ ";
			}
		}
		speechText += "ドコ キヨシ";
		return handlerInput.responseBuilder.speak(speechText).withSimpleCard('Alexaズンドコ', speechText).getResponse();
	}
};
```

# テスト

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/1188a146-da8e-57ac-45a5-3e82159fdb2b.png)



以上。
node.jsでalexaスキル その6

# 概要
node.jsでalexaスキル、やってみた。
練習問題やってみた。

# 練習問題
Alexa-hostedで、じゃんけん、インテントを追加せよ。

# 方針
- 「じゃんけんグー」で、「私もグー、あいこだね」

# じゃんけん、インテントを追加
- 対話モデル>インテント>インテントを追加>カスタムインテントを作成
GuuIntent

- 対話モデル>インテント>インテントを追加>サンプル発話
" じゃんけんグー "

- 対話モデル>インテント>インテントを追加>保存>スキルをビルド

# コードを追加

```
const GuuIntentHandler = {
	canHandle(handlerInput) {
		return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'GuuIntent';
	},
	handle(handlerInput) {
		const array = ["私もグー、あいこだね", "私はチョキ、くそ、負けた", "私はパー、あんたの負け"]
		const min = Math.ceil(0);
		const max = Math.floor(array.length - 1);
		let randomInt = Math.floor(Math.random() * (max - min) + min);
		const speechText = array[randomInt];
		return handlerInput.responseBuilder.speak(speechText).withSimpleCard('Alexaグー', speechText).getResponse();
	}
};

```

# テスト

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/d0e77726-015d-e379-c09d-d9db116f06c3.png)


以上。
MLVPNをインストールする話

# MLVPNをインストールする話

## 序論
### SoftEtherでMPTCPしても思ったようにスループットが上がらない……
必要に迫られてひかり電話を引くことになったのでNGNのVDSL回線が1本我が家に生えました。
理想論から言えば、既設回線（マンション共有無料インターネット回線）６０Mbps + 新設回線（VDSL）70〜99Mbpsは出て欲しいところですが、条件が良くて瞬間的に150Mbps、平均して80Mbps程度に止まっていました。
通信を片寄せした場合には各々50〜70Mbps程度出ますので、単純にレイテンシの差が響いて遅い方の回線に律速され、通信のやり直しが行われている気もします。

### 実験結果＝結論から述べると
**既設の「MPTCP化したSoftEtherVPNでTCP接続するのが一番スループットが出ました（爆）」**

## 以下、結局無駄となったけど今後何かに使うかと思ってMLVPNのインストールの備忘録を残しておきます

MPTCP化したSoftEtherでスループットが上がらない。ではどうするか。
### Multipath UDPでスループットが上がるか試せばいいんじゃね（脳筋的発想
というわけでMultipath UDPに対応したVPNソフトを試してみます。

## MLVPNをインストールする
~~筆者は偏屈なので~~先行例があるっぽいGloryTUN（自分でも試したけど上手く動かなかった）ではなくMLVPNを動かしてみたく思います。

https://github.com/zehome/MLVPN/

https://zehome.github.io/MLVPN/

https://mlvpn.readthedocs.io/en/latest/

以下、UbuntuServer22.04LTSでの話になります。

### インストール
必要なライブラリをダウンロードしてインストールします（サーバー・クライアント共に）。
```
wget http://security.ubuntu.com/ubuntu/pool/main/libs/libsodium/libsodium18_1.0.8-5_amd64.deb
wget https://github.com/zehome/MLVPN/releases/download/2.3.5/mlvpn_2.3.5_amd64.deb
sudo apt install libev4
sudo dpkg -i libsodium18_1.0.8-5_amd64.deb
sudo dpkg -i mlvpn_2.3.5_amd64.deb
```

### 設定ファイルを書く
設定例を「/etc/mlvpn/」にコピーします。Debian/Ubuntuなら下記のコマンドで良いはずです。
```
sudo cp /usr/local/doc/mlvpn/examples/mlvpn.conf /etc/mlvpn/mlvpn.conf
sudo cp /usr/share/doc/mlvpn/examples/mlvpn_updown.sh /etc/mlvpn/mlvpn_updown.sh
```
最低限、下記の内容を記述します。
```/etc/mlvpn/setting.conf（サーバー側）
mode = "server"
tuntap = "tap"
password = <YourConfigurationPassword>

[Route1]
bindhost = "<YourServerIP1>"
bindport = <YourServerIP1'sListenPort>
remotehost = "<YourClientIP1>"
remoteport = <YourClientIP1'sListenPort>

[Route2]
bindhost = "<YourServerIP2>"
bindport = <YourtServerIP2'sListenPort>
remotehost = "<YourClientIP2>"
remoteport = <YourClientIP2'sListenPort>
```

```/etc/mlvpn/setting.conf（クライアント側）
mode = "client"
tuntap = "tap"
password = <YourConfigurationPassword>

[Route1]
bindhost = "<YourClientIP1>"
bindport = <YourClientIP1'sListenPort>
remotehost = "<YourServerIP1>"
remoteport = <YourServerIP1'sListenPort>

[Route2]
bindhost = "<YourClientIP2>"
bindport = <YourtClientIP2'sListenPort>
remotehost = "<YourServerIP2>"
remoteport = <YourServerIP2'sListenPort>
```

設定が完了したら、

```
sudo systemctl enable mlvpn.service
sudo systemctl start mlvpn.service
```

この時点でEthernetデバイス「mlvpn0」がシステムに追加されているはずです。

```
sudo ip a
```

接続デバイスはtapデバイスにしてあるので、既にこの時点でL2ブリッジが成立しています。
あとはこのtapデバイスをNetplanでBridgeに取り込むなり何なりと煮るなり焼くなり好きにしてください。
AWS の EC2(Amazon Linux 2023) で React 実行

AWS の EC2 の Amazon Linux 2023 に node インストールして react(create-react-app) を実行した時の備忘録メモ
実施したのは 2024/04/19 時点です

- ざっくり流れ
    1. EC2インスタンス起動
    1. EC2ログイン
    1. nvm, nodeインストール
    1. react(create-react-app)作成・起動
    
- 環境情報
    - EC2 (Amazon Linux 2023)
    - nvm v0.39.7
    - node v18.17.1
    - Windows11
    - Tera Term v5.2

# EC2インスタンス起動

AMI は Amazon Linux 2023にして ssh で接続するのでキーペアとかちゃんと設定する
React で使うポート(デフォルトは 3000 )を外部から接続できるようにセキュリティグループを追加
そのほかは自由でOK

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51758/1cb90019-e4e7-66f0-ced6-4d60f20534e3.png)


# EC2ログイン

windows pc でTeraTerm でアクセス
ホストに EC2 の パブリックIP、ユーザ名 `ec2-user`、認証方式を RSA 鍵使うにして秘密鍵に pem ファイルを指定 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51758/90cc5329-1f0c-5c2b-9942-5060dfeceb8e.png)

- root に変更
    ```terminal
    sudo -i
    ```


# nvm, nodeインストール
- nvm インストール
    ```terminal
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
    ```

- nvm ロード
    ```terminal
    source ~/.bashrc
    ```

- node インストール
    ```terminal
    nvm install 18.17.1
    ```

- node バージョン確認
    ```terminal
    node -v
    ```

- 補足：複数バージョンで node 切替えたいとき
    nodeの他のバージョンで使いたいときのた
    バージョンの確認、切替など
    - インストールできるバージョン確認
    ```terminal
    nvm ls-remote
    ```
    - インストールしてるバージョン確認
    ```terminal
    nvm list
    ```
    - バージョン指定してインストール
    ```terminal
    nvm install バージョン
    ```
    - バージョン切替
    ```terminal
    nvm use バージョン
    ```


# react(create-react-app)作成・起動

- react(create-react-app)作成
    ```terminal
    npx create-react-app プロジェクト名 -y
    ```

- 作成したプロジェクトに移動
    ```terminal
    cd プロジェクト名
    ```

- 作成したプロジェクト起動
    ```terminal
    npm start
    ```

- アクセス確認
    外からつながるようにしてたらアクセスできるはず．．．
    http://パブリックIP:3000
[Colab][Python3]日経平均株価の推移を可視化する方法

## 要　旨
Colabを使用してPython3において日経平均株価の推移を可視化する方法について記述します。
## 実施要領
必要なライブラリのインストール等
```python3
!pip install japanize_matplotlib
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import japanize_matplotlib
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
```
日経平均株価の取得
```python3
from pandas_datareader import data as web
from pandas import Series, DataFrame
import datetime

nikkei_df = web.DataReader("NIKKEI225", "fred", start=datetime.date(1900, 1, 1))
nikkei_df = nikkei_df.dropna()
```
日経平均株価の可視化
```python3
plt.figure(figsize=(10, 6))
plt.plot(nikkei_df.index, nikkei_df['NIKKEI225'], label='日経平均株価', color='b')
plt.title('日経平均株価の推移', fontsize=16)
plt.xlabel('日付', fontsize=12)
plt.ylabel('株価', fontsize=12)
plt.grid(True)
plt.legend()
plt.show()
```
実行例
![](https://appliin.tokyo/others/img/nikkei.png)
## 結　言
今回は、Colabを使用してPython3において日経平均株価の推移を可視化する方法について記述しました。
node.jsでalexaスキル その5

# 概要
node.jsでalexaスキル、やってみた。
練習問題やってみた。

# 練習問題
Alexa-hostedで、九九、インテントを追加せよ。

# 方針
- 「一の段」で、「いん いち が いち　・・・」

# 九九、インテントを追加
- 対話モデル>インテント>インテントを追加>カスタムインテントを作成
NumIntent

- 対話モデル>インテント>インテントを追加>サンプル発話
" {num} の段 "

- 対話モデル>インテント>インテントを追加>インテントスロット
num AMAZON.Number

- 対話モデル>インテント>インテントを追加>保存>スキルをビルド

# コードを追加

```

const NumIntentHandler = {
	canHandle(handlerInput) {
		return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'NumIntent';
	},
	handle(handlerInput) {
		var speechText = 'くく';
		const num = handlerInput.requestEnvelope.request.intent.slots.num.value;
        if (num === "1") 
        {
            speechText = 'いん いち が いち いん に が に いん さん が さん いん し が し いん ご が ご いん ろく が ろく いん しち が しち いん はち が はち いん く が く';            
        }
        if (num === "2") 
        {
            speechText = 'に いち が に に にん が し に さん が ろく に し が はち に ご じゅう に ろく じゅうに に しち じゅうし に はち じゅうろく に く じゅうはち';            
        }
        if (num === "3") 
        {
            speechText = 'さん いち が さん さん に が ろく さ ざん が く さん し じゅうに さん ご じゅうご さぶ ろく じゅうはち さん しち にじゅういち さん ぱ にじゅうし さん く にじゅうしち';            
        }
        if (num === "4") 
        {
            speechText = 'し いち が し し に が はち し さん じゅうに し し じゅうろく し ご にじゅう し ろく にじゅうし し しち にじゅうはち し は さんじゅうに し く さんじゅうろく';            
        }
        if (num === "5") 
        {
            speechText = 'ご いち が ご ご に じゅう ご さん じゅうご ご し にじゅう ご ご にじゅうご ご ろく さんじゅう ご しち さんじゅうご ご は しじゅう ごっ く しじゅうご';            
        }
        if (num === "6") 
        {
            speechText = 'ろく いち が ろく ろく に じゅうに ろく さん じゅうはち ろく し にじゅうし ろく ご さんじゅう ろく ろく さんじゅうろく ろく しち しじゅうに ろく は しじゅうはち ろっ く ごじゅうし';            
        }
        if (num === "7") 
        {
            speechText = 'しち いち が しち しち に じゅうし しち さん にじゅういち しち し にじゅうはち しち ご さんじゅうご しち ろく しじゅうに しち しち しじゅうく しち は ごじゅうろく しち く ろくじゅうさん';            
        }
        if (num === "8") 
        {
            speechText = 'はち いち が はち はち に じゅうろく はっ さん にじゅうし はち し さんじゅうに はち ご しじゅう はち ろく しじゅうはち はち しち ごじゅうろく はっ ぱ ろくじゅうし はっ く しちじゅうに';            
        }
        if (num === "9") 
        {
            speechText = 'く いち が く く に じゅうはち く さん にじゅうしち く し さんじゅうろく く ご しじゅうご く ろく ごじゅうし く しち ろくじゅうさん く は しちじゅうに く く はちじゅういち';            
        }
		return handlerInput.responseBuilder.speak(speechText).withSimpleCard('Alexaくく', speechText).getResponse();
	},
};


```

# テスト

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/e75922f9-96a1-b86b-676b-7bec5153dd9d.png)

以上。
Slack Boltの開発ローカル環境構築

# slack Bolt ローカル開発環境

slack cliの時代のようですがそれを知らずににドッカーでローカル環境構築してしまいました..


# slack Bolt ローカル開発環境

ソースコードはこちらをcloneしてください。
https://github.com/SphereStacking/slack-app-template

## 初期セットアップ

### コンテナのbuildとmoduleのインストール

``` sh
docker-compose build
docker-compose run app npm init
docker-compose run app npm install @slack/bolt
```

## envの作成

1. .env.exampleをコピーして.envを作成

## ngrok.ymlの作成

1. ngrok.yml.exampleをコピーしてngrok.ymlを作成

## Slack Appのセットアップ

1. アプリの作成
   1. https://api.slack.com/apps
   2. Create New App を押下
   3. From scratch を押下
   4. App Nameの記入
   5. Pick a workspace to develop your app in: でセットアップ先のワークスペースを選択
   6. 画面下部のSAVEボタンを押下し保存
2. Slack Appの情報を控える
   1. Basic Information->App Credentialsに移動 
   2. Client Secretを控える
   3. Signing Secretを控える。
3. Slack Appの認証情報を控える
   1. OAuth & Permissions
   2. Bot User OAuth Tokenを控える
4. Slack Appのが操作可能な範囲を設定
   1. OAuth & Permissions->Scopes
   2. Add an OAuth Scope ボタンを押下して適宜追加
5. リポジトリのセットアップ
   1. 控えた値をenvに書き込む
      1. 2-1 SLACK_BOT_TOKEN
      2. 2-3 SLACK_SIGNING_SECRET
      3. 5-3 NGROK_AUTH_TOKEN

### ngrokのセットアップ

1. ngrokのAuthtokenを取得
   1. [ngrok](https://dashboard.ngrok.com/get-started/your-authtoken)にアクセス
   2. アカウントがない場合は作成
   3. Your Authtokenを控える
2. アクセス用のURLを固定する。
   1. https://dashboard.ngrok.com/cloud-edge/domains
   2. Create Domainを押下
   3. Start a Tunnel?
   4. セレクトボックスから[Start a tunnel from a config file]を選択
   5. 表示されたymlファイルからhostname部分を控える。
3. リポジトリのセットアップ
   1. 控えた値をngrok.ymlに書き込む

### 一度起動

```
$ docker-compose up
```

### Slack Appのコマンド設定

1. ngrokのローカル環境ステータス画面をに移動
   1. http://localhost:4040/status
   2. Request URLを確認し控える。
      1. ngrokのセットアップの2-5の値と違っていたら何か間違ってるかも
2. コマンド設定
   1. https://api.slack.com/apps/{your-app}/slash-commands
   2. Create New Command
      1. Command: /kiminikimeta
      2. Request URL: 1-2で控えたurl + /slack/events
      - 残りの必要項目は適宜埋める。
   3. Saveを押下

### Slackのチャンネルにてコマンドの実行

1. チャンネルに作成したappをし追加
2. Slack Appのコマンド設定の2-2-1で設定したコマンドをチャット欄に記述し送信
3. 予定通り返信が来たら完了

## 参考

- [【Slack】OOOoO、キミにきめた！Bolt on Docker + Cloud Runでユーザーをランダムで選んでくれるSlash commandを作った話](https://qiita.com/at-946/items/e70cac96c03f911454ab)
コマンドライン引数を受け取るPythonスクリプトのAliasについて

# コマンドライン引数を受け取るPythonスクリプトのAliasについて

この記事では、Shellスクリプトを使用してPythonスクリプトを実行し、実行時に動的にコマンドライン引数を渡す方法を詳しく解説します。この方法は、スクリプトを柔軟に再利用可能にし、さまざまなパラメータで簡単に実行できるようにするために役立ちます。

## 必要な環境

- Unix系OS（Linux, macOSなど）
- Python 3.x
- BashまたはZsh

## スクリプトの解説

### 1. Pythonスクリプトの準備

まず、以下のような簡単なPythonスクリプトを用意します。このスクリプトはコマンドラインから引数を受け取り、それらを出力します。

```python
# filename: example.py
import sys

if __name__ == "__main__":
    print("受け取った引数:", sys.argv[1:])
```

### 2. Shellスクリプトの作成

次に、このPythonスクリプトを実行するShellスクリプトを作成します。このスクリプトは任意の数の引数をPythonスクリプトに渡します。

```bash
#!/bin/bash
# filename: run_python.sh

# スクリプトに渡す引数を全てPythonスクリプトに渡します。
python /path/to/example.py "$@"
```

### 3. スクリプトの実行権限設定

Shellスクリプトに実行権限を与えます。

```bash
chmod +x run_python.sh
```

### 4. スクリプトの実行

実際にスクリプトを実行してみます。

```bash
./run_python.sh arg1 arg2 arg3
```

出力例:

```
受け取った引数: ['arg1', 'arg2', 'arg3']
```

## 解説

このShellスクリプトは、`"$@"`を使用しているため、スクリプトに渡された全ての引数をそのままPythonスクリプトに渡します。`"$@"`は、全引数を個別の文字列として保持し、スペースや特殊文字が含まれている引数も正確に扱います。

## まとめ

この方法を使用すれば、さまざまなパラメータでPythonスクリプトを簡単に実行できます。引数を動的に扱うことで、スクリプトの汎用性が高まり、自動化やテストなど幅広い用途で利用することが可能です。
Pythonで「条件に一致するファイルやディレクトリの一覧を取得する(pathlibモジュール)」の動作を確認してみた

# 概要

Pythonで「条件に一致するファイルやディレクトリの一覧を取得する(pathlibモジュール)」の動作を確認してみました。
以下のページを参考にしました。

https://www.javadrive.jp/python/file/index15.html

# 実装

以下のファイルを作成しました。
```sample.py
import pathlib

p = pathlib.Path('./test')
for name in p.glob('**/*.txt'):
  print(name)
```

以下のコマンドを実行しました。
```
$ mkdir test
$ touch test/book.png
$ touch test/book.txt
$ touch test/cup.png
$ touch test/cup.txt
$ touch test/pen.png
$ touch test/pen.txt
$ mkdir test/back
$ touch test/back/2018.txt
$ touch test/back/2019.txt
$ touch test/back/2020.txt
$ mkdir test/back/old
$ touch test/back/old/2017.txt
$ mkdir test/img
$ touch test/img/profile.png
$ touch test/img/room.png
$ python3 sample.py 
test/pen.txt
test/book.txt
test/cup.txt
test/back/2020.txt
test/back/2019.txt
test/back/2018.txt
test/back/old/2017.txt
```

# まとめ

何かの役に立てばと。
[debug] QUERY OKの抑制の方法

コンソールに「[debug] QUERY OK」が大量に表示してIO.inspectの結果が隠れて困ったこ
ありませんか？

対象方法

```elixir:dev.exs
config :logger, level: :warning
```

config :logger, level:を設定すると抑制できます
解析が終わったら元に戻します

以上
[備忘録]std::stoiは"文字列を数値に変換"だと思ってた

# ”数値に変換できなかったら例外"ではない

普通に使うと↓
```C++:実装
#include<iostream>

int main(){
    std::string str1 = "100";
    std::string str2 = "test";
    int num1 = 0;
    int num2 = 0;
    try {
        num1 = std::stoi(str1);
    } catch(...) {
        num1 = 0;
    }
    try{
        num2 = std::stoi(str2);
    } catch(...) {
        num2 = 0;
    }
    printf("%d\n",num1);
    printf("%d\n",num2);
}
```
```txt:実行結果
100
0
```

# 先頭が数字であれば例外は投げない
先頭が数字で、以降文字だったとしても例外は投げない

```C++:実装
#include<iostream>

int main(){
    std::string str1 = "test100";
    std::string str2 = "100test";
    int num1 = 0;
    int num2 = 0;
    try{
        num1 = std::stoi(str1);
    } catch(...) {
        num1 = 0;
    }
    try{
        num2 = std::stoi(str2);
    } catch (...) {
        num2 = 0;
    }
    printf("%d\n",num1);
    printf("%d\n",num2);
}
```
```txt:実行結果
0
100
```

# 先頭が数字かつ以降文字であれば失敗or例外処理したい
**変換後の数値の桁数**と**元の文字列の長さ**が一致しなければ失敗にする

```cpp:判別関数
bool judgeConvert(int ConversionNum,std::string originStr) {
    bool judge;
    if (std::to_string(ConversionNum).length() == originStr.length()) {
        judge = true;
    } else {
        judge = false;
    }
    return judge;
}
```

```cpp:main関数
int main(){
    std::string str1 = "100";
    std::string str2 = "100test";
    int num1 = 0;
    int num2 = 0;
    try {
        num1 = std::stoi(str1);
        if (judgeConvert(num1,str1) == false) {
            num1 = 0;
        }
    } catch(...) {
        num1 = 0;
    }
    try {
        num2 = std::stoi(str2);
        if (judgeConvert(num2,str2) == false) {
            num2 = 0;
        }
    } catch(...) {
        num2 = 0;
    }
    printf("%d\n",num1);
    printf("%d\n",num2);
}
```
```txt:実行結果
100
0
```

# 今後の課題
ほかの例外ケースもありそうなので、もう少し検討したい
pytketで行うVQEで求める水素分子のエネルギー

今回は、pytketを用いてVariational Quantum Eigensolver(VQE)を行い、水素分子における基底状態エネルギーを様々な原子間距離に対して計算した結果を示します。

今回は[ この記事 ]( https://qiita.com/HWakaura/items/77fa07a275815e6f54a4 )と同じように、基底はSTO-3G、変数は1電子励起クラスター2個と2電子励起クラスター1個の合計3つとします。

最適化手法にはnumpyのNelder-Mead法を用います。その結果、図1に原子間距離を0.1から0.1刻みで2.5まで取ったものを示します。これを見ると、原子間距離1.6(Å)までは精度良く厳密解が計算されていますがそこからずれます。しかし、これはクラスターの深さが足りない場合に特有のもので、精度の問題ではありません。

![blogpytketvqe1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/844109/502e2263-a022-8834-db5c-f47ac30acde4.png)


図1 計算された水素分子における基底状態のエネルギー。

pytketはblueqatと一部の機能がローコードで実装可能です。前述の記事とこの記事のために使ったプログラムの間には大差ありません。これは次回で詳しく述べますが、様々なアルゴリズムの実装に適しています。pytketはバックエンドを使用しているのにもかかわらず、計算が早く終わるようですが、むしろこれの良さは回路最適化と圏論量子力学的記法にあります。これも近いうちに記事にします。
40代のIT未経験の転職について改めて考えてみた。

40代IT未経験のあまねです。
今、職業訓練校に通ってJava, SQL, Pythonの基礎やってます。

Git, GitHubとデータサイエンス独学中です。
## 書類選考の通過について考えてみた
とりあえず、今まで持ってた履歴書と職務経歴書を最新版にお直しして転職サイト経由で提出してみたんだけど、今のところ全滅ざます。

まぁそうだよね。
ITの経験ないしね。

どうしたら書類選考通るかなあ～？

と、ゆーわけで
さらなるお直しポイントを考えてみた

注）トライアルアンドエラーのトライアル中なので、結果が分かるのは数か月後です。試行錯誤の記事としてお楽しみください。

### 履歴書
ざっくりとした住所、大学と顔を確認したくらいで、前半は見ていないと思われる。
志望動機欄があるので、そこは企業ごとに変えて提出することにした。

もしかしたら、ここで年齢を見た時点ではじかれているかもしれない。

### 職務経歴書
当然と言えばそうなのだけど、社会人経験の長さに比例して職務経歴書も長い。
事務職の業務内容を詳しく書いても、こんな情報いらねぇって思われる気がしてきた。

詳しい業務内容は大幅に削って
- 仕事に対する姿勢
- これまでどんな評価を受けてきたのか

なんてことを書いてみようと思う。
これなら、業種・職種関係なく書ける。

そして1枚にまとめたい。

なんだか、昔から「口和災いのもとだからだまって黙々と仕事しよう」みたいなことが美徳とされてきたのに、あなたのキャリアの棚卸をして企業にアピールしましょうって言われても

「言わない」を徹底してきた身としては、言語化がとても難しい。
くだらないことならいくらでもアウトプットできるんですがねぇ。。。


### 年齢対策はあるのか？
最大のネガティブポイントである年齢についてなんだけど
対策はあるのか？

答えは、Unknown(不明)である。

なぜ年齢がひっかかるのか考えてみた。とゆーか感じたことがある。

とある求人で「30代前半まで」となっていたことについて企業側の考えを知れるチャンスがあった

どうやら「在籍している社員さんが20代から30代前半なので同じ価値観の中で仕事をしてほしい」らしい。
多分、社長を含めみんな若い。


「同じ価値観」という言葉はものすごいオブラートに包んでいると感じたし
もっともな表現だとも思った。

それだけ、昭和と平成には大きな違いがある。

わかる。すごくわかる。

10歳くらい年上の部下とか後輩とか扱いにくいし
立場が下なのにマウント取ってきそうだよね
なんかこちらが気を遣っちゃう
（あくまでも個人のイメージです）

要は相手にするのに疲れて周りの仕事のパフォーマンスが下がるリスクがあるから
年齢、社会人経験ともに今の社員さんと同じくらいか、下の方が立場が明確でわかりやすいし仕事がやりやすいってことなんだと理解した。
（あくまでも個人の見解です）

まぁ、これなら納得。

過去の職場で新しい人採用するときも45歳がボーダーだったかな。
理由は新しいことを覚えるにはそれくらいが限界だろうってことだった。

かくも年齢とは残酷な現実なのだ。

新しいことを覚えて一人で仕事を回せるころには更年期障害が襲ってくるかもしれない。

江戸時代ならとっくに死んでいる。

キャリアプランにとどまらないが、何かを始めるときにゴールから逆算して所要時間を確認するのはとても大事だと改めて気が付いた。

 **それでも、わたくしは人生の残り時間をITに費やしたいと思って今ここに筆を執っているのでありまっす！！！！** （敬礼）

ここに一つの結論を得た。
「若い企業は未知なる生物としておじさん、おばさんを恐れている」


よし、年齢はアレだけどマウントとか取らないから安心してくだい。と職務経歴書に書くぞ。

実際、上司も先輩も年下だったことあるし。
 **もーんだーいないさぁぁぁぁぁぁぁ**

ここに「40代怖くないよ作戦」が誕生したのでした。

あまねの書類が通ることを祈って、本日の結びとさせていただきます。

最後までよんでいただきありがとうございました。
Zustandの使い方

# Zustandの使い方

`Next.js` で、状態管理のライブラリ `Zustand` があることを知り、使ってみたいと思い色々調べました。

## Zustand

`Zustand` とは、状態管理のライブラリです。  
たとえば、ログイン情報など各ページやコンポーネントなどグローバルに必要なデータを保持する際に使われます。

### 特徴

>- シンプルなコード
>- Reactのための軽量なライブラリ
>- Hookを利用
>- グローバルに保持
>- どのコンポーネントからもアクセスできる

参照：[Zustand - 公式](https://github.com/pmndrs/zustand)

### 使い方

1\. **ストアの作成**
下記の例では、カート情報のストアを作成しました。  
 - 初期状態が `0` の `count`
 - `count` に1を足して更新をする `addItem` 関数
 - `count` から1を引いて更新をする`minusItem` 関数

関数では、`set` コールバック関数を使い、`count` を更新します。
`export` することで、グローバルにこのストアの使用や更新をすることができます。

~~~javascript

// カートのストア作成
export const useCartStore = create((set) => ({
 count: 0,
 addItem: () => 
  set((state) => ({
   count: state.count + 1
  })),
 minusItem: () =>
  set((state) => ({
   count: state.count - 1
  }))
}))

~~~

2\. **ストアの使用・更新**

下記では、カートのページのように商品の現在の数量（`count`）を表示し、「-」と「＋」ボタンで数量の増減（`addItem`, `minusItem`）をして、`count` を更新しています。

~~~react

const cart = () => {
 const { count, addItem, minusItem } = useCartStore()

 return (
  <div>
   <h1>Zustand Cart</h1>
   <div>Count Item {count}</div>
   <div className='d-flex gap-3'>
    <button 
     type='button'
     variant='danger' 
     onClick={useCartStore((state) => state.minusItem)}>
    -
    </button>
    
    <button 
     type='button'
     variant='danger' 
     onClick={useCartStore((state) => state.addItem)}>
    +
    </button>
   </div>
  </div>
 )
}

~~~

## まとめ

`Zustand` は、Reactを使う上で、コード量も少なく、学習コストも少なくシンプルで使いやすい状態管理ライブラリということが分かりました。  
`Zustand` 以外にもいろんなライブラリがあるので、機会があれば勉強してみたいと思います。

---

**ご覧いただきありがとうございました**

今後もフロントエンド開発で気になったことや分からないことを調べて、共有していきたいと思います。
間違いなどございましたら、教えていただきたいです。
UI.Visionでiframe内の要素をクリックする

## UI.Visionでiframe内の要素をクリックする

RPAツール：UI.Visionを使ってiframeに対する処理をします。
XModulesをつかって画像認識で無理やり要素をクリックすることも可能です。
### やり方
iframe内の要素に何かする前に
```
selectFrame | index or id
```
で指定するだけです。
以降のiframe内の操作は通常通りDOMやidなどで指定できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477548/f803829a-231b-d6e4-8811-9920681272cd.png)


```html:iframe.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>IFrame Example with Button</title>
    <style>
        /* iframeのスタイル */
        #externalPage {
            border: 2px solid lightgreen;
            border-radius: 5px;
        }
    </style>
</head>
<body>
    <h2>トップページ</h2>
    <h2>iframe外のコンテンツ</h2>
    <!-- iframeを含むdiv -->
    <div id="iframeContainer" style="display: none;">
        <h2>iframeを含むdiv</h2>
        <iframe id="externalPage" src="iframe_content.html" width="800" height="600" frameborder="0"></iframe>
    </div>

    <button id="showIframeButton">iframeを表示する</button>

    <script>
        // ボタンをクリックしたときにiframeを表示する
        document.getElementById("showIframeButton").addEventListener("click", function() {
            var iframeContainer = document.getElementById("iframeContainer");
            var iframe = document.getElementById("externalPage");
            
            // iframeを表示する
            iframeContainer.style.display = "block";

            // iframeがまだ読み込まれていない場合は読み込む
            if (!iframe.src) {
                iframe.src = "iframe_content.html";
            }
        });
    </script>
</body>
</html>

```

```html:iframe_content.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>IFrame Content</title>
</head>
<body>
    <h2>iframe内のコンテンツ</h2>
    <!-- ボタン -->
    <button id="buttonInsideIframe">ボタン</button>

    <script>
        // ボタンをクリックしたときのアクション
        document.getElementById("buttonInsideIframe").addEventListener("click", function() {
            alert("ボタンがクリックされました！");
        });
    </script>
</body>
</html>

```

モーダルも同様かもしれませんが検証していません。
Node.jsを削除し、nvmで再インストールする

Next.jsで開発をしていたらNode.jsのバージョンが古いと怒られたので、どうせならバージョン管理ツール(nvm)を導入しようと思ったので、やり方をメモ。

# 環境
- Windows11

# Node.jsのアンインストール
インストール済みのNode.jsを削除します。
参考→[How to completely remove node.js from Windows](https://stackoverflow.com/questions/20711240/how-to-completely-remove-node-js-from-windows)

1. npmパッケージのキャッシュを削除
    ```posh
    npm cache clean --force
    ```
2. Node.jsをアンインストール
コントロールパネルからアンインストール
3. 再起動
4. 関連フォルダの削除
`C:\Program Files (x86)\Nodejs`
`C:\Program Files\Nodejs`
`C:\Users\{User}\AppData\Roaming\npm` (or `%appdata%\npm`)
`C:\Users\{User}\AppData\Roaming\npm-cache` (or `%appdata%\npm-cache`)
`C:\Users\{User}\.npmrc`** (and possibly check for that without the `.` prefix too)
`C:\Users\{User}\AppData\Local\Temp\npm-*`
5. 環境変数PATHを確認
環境変数PATHにnodeやnpmに関するものが残っていないか確認
6. nodeの場所を確認
`$ where node`
7. 再起動

# nvmのインストール
参考→[nvm-windowsでNode.jsバージョン管理](https://qiita.com/akuden/items/a88630de9624039c4135)
以下のURLからnvm-setup.zipをダウンロード、解凍し、インストーラーに従いインストール。
https://github.com/coreybutler/nvm-windows/releases

## 確認

```posh
nvm version
```

# Node.jsのインストール
```posh
nvm list available
```
でインストール可能なバージョン一覧が表示されます。
バージョン20.12.2をインストール：
```posh
nvm install 20.12.2
```
バージョン切り替え：
```posh
nvm use 20.12.2
```
インストール済みのバージョン一覧表示：
```posh
nvm list
```

新しいバージョンを使う必要に迫られたら、またnvmでインストールしてバージョンを切り替えるだけです。
不要になったバージョンは以下のコマンドでアンインストールできます：
```posh
nvm uninstall 20.12.2
```
【Laravel6】ログイン認証機能 / Validationなどの日本語化

## 手順
https://laraweb.net/tutorial/6949/

### 注意
パスワードリセットとして通知されるメール文章も日本語化について以下の記述があるが

× 　↓これだとうまくいかなかった
```resources/lang/ja.json
"If you’re having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser: [:actionURL](:actionURL)": "\":actionText\"ボタンを押しても何も起きない場合、以下URLをコピーしてWebブラウザに貼り付けてください。\n[:actionURL](:actionURL)",
```

○ 　↓これでうまくいった
```resources/lang/ja.json
"If you’re having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": ":actionTextボタンをクリックできない場合は、次のURLをコピーしてブラウザに貼り付けてください。\n",
```

参考サイト

https://php-junkie.net/framework/laravel/email-ja/

## validationMessageを日本語化
### 手順
https://qiita.com/Rock22/items/1db96c801d78ebafb7cc

#### 日本語翻訳のみはここにもあり！
https://laraweb.net/tutorial/6949/
ベロシティへの執着：アジャイルの欠陥のある指標　読んでみたメモ

以下の記事を読んだメモ

https://medium.com/@alexponomarev/velocity-obsession-the-flawed-metric-of-agile-9006358acb29

---

* ベロシティを過度に重視することは、実際の生産性を捉えることができず、多くの問題を引き起こす可能性がある
* もっとバランスの取れたアプローチが必要
* 表面的にストーリーポイントの数を増やすために、バグの多いコードを書いてしまうケースも発生する
* 経営陣が毎回のスプリントのベロシティを上げることに焦点を当ててしまうと、開発チームは難しい状況に陥る
* ベロシティ目標に執着し続けると、ストーリーポイントを増やし、未完成のコードを急いでリリースすることを強いられる
* 開発チームは、質と長期的な持続可能性に焦点を当てるための余裕が必要
* ベロシティレポートを振り回すのではなく、チームが成果を出すことを妨げている要因について理解を深めることのほうが、マネージャーは多くの理解を得ることができる
* 鞭を打つのではなく、障害を取り除くことで開発者を支援するべき
* 本当に生産性を測定したいなら、機能の開始からデプロイまでどのくらいかかっているか、サイクルタイムのようなものを見た方がいい
* 指標よりも透明性が勝る
* 開発プロセスに関与することで、チームがどこで行き詰まっているかを理解できる
* エンジニアは高い不確実性に直面していることを念頭におく必要がある
* 全てのスプリントが異なる
* 新機能を開発するスプリントと、技術的負債を返済するスプリントではベロシティは異なる
* 賢明なマネージャーは過去に依存するのではなく、各スプリントの目標を見て期待を設定する
* ベロシティを見ることも大事だが、チームが日々何をしているのか理解することに焦点を当てるべき
StampS3を1LEDスレーブI2Cして遊ぶ。


目的
I2Cのテスト

![o_coq044.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/2f7c30a2-ada2-de36-d5f0-485d99bd2455.jpeg)


受信 I2Cスレーブ StampS3


```c++


//I2C_1GPIO_V1_M5S3_1


//インクルド
#include <Arduino.h>
#include <Wire.h>


//初期化
void setup() {

  //i2cの初期化
  Wire.begin( (0x80)>>1 );      //I2Cスレーブアドレスの設定
  Wire.onReceive(receiveEvent); //データが来ると呼ばれる関数

  //GPIOの初期化
  pinMode(21, OUTPUT);

} //setup


//メインループ
void loop() {
  delay(1); //ダミー
} //loop


//レシーブイベント
void receiveEvent(int howMany) {

  int x = Wire.read(); //I2C受信データの読み込み

  //LEDの点滅
  if( (x & 0x01) == 0 ) {

    neopixelWrite(21,0,0,0);

  } else {

    neopixelWrite(21,64,0,0);

  } //endif

} //receiveEvent

```


送信 I2Cマスター Arduino UNO


```c++



//I2C_PUT_0_1_UNO_1


//インクルド
#include <Arduino.h>
#include <Wire.h>


//初期化
void setup() {

  Wire.begin();    //I2Cの初期化

} //setup


//メインループ
void loop() {

  //'0'転送
  Wire.beginTransmission( (0x80)>>1 );
  Wire.write('0');
  Wire.endTransmission();
  delay(1000); //1秒待つ

  //'1'転送
  Wire.beginTransmission( (0x80)>>1 );
  Wire.write('1');
  Wire.endTransmission();
  delay(1000); //1秒待つ

} //loop



```
【Next.js】Dynamic Import を使用してSSRしないようにする方法

以下のようにDynamic Inportの際に　`{ssr: false}` を第二引数に渡すとSSRされず、React単体のようにクライアントでのみレンダリングされます。

```jsx
import dynamic from 'next/dynamic'
 
const Sample = dynamic(
  () => import('../components/Sample'),
  {
    ssr: false,
  }
)
 
export default function Page() {
  return (
    <div>
      <Sample />
    </div>
  )
}

```
【mlflow】TypeError: 'NoneType' object does not support item assignment

# mlflowが突然動かなくなった...
以下がエラーの内容。
```
Traceback (most recent call last):
  File "/usr/local/lib/python3.10/dist-packages/flask/app.py", line 1473, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python3.10/dist-packages/flask/app.py", line 882, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python3.10/dist-packages/flask/app.py", line 880, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.10/dist-packages/flask/app.py", line 865, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)  # type: ignore[no-any-return]
  File "/usr/local/lib/python3.10/dist-packages/mlflow/server/handlers.py", line 508, in wrapper
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.10/dist-packages/mlflow/server/handlers.py", line 549, in wrapper
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.10/dist-packages/mlflow/server/handlers.py", line 1438, in _search_experiments
    experiment_entities = _get_tracking_store().search_experiments(
  File "/usr/local/lib/python3.10/dist-packages/mlflow/store/tracking/file_store.py", line 302, in search_experiments
    exp = self._get_experiment(exp_id, view_type)
  File "/usr/local/lib/python3.10/dist-packages/mlflow/store/tracking/file_store.py", line 396, in _get_experiment
    meta["tags"] = self.get_all_experiment_tags(experiment_id)
TypeError: 'NoneType' object does not support item assignment
```
# 解決策
mlruns/{experiment_id}/meta.yamlが空白のものが含まれているフォルダが原因だった。なぜ、こんな空のファイルが生成されたかわからないが、これを手動で除くことで解決できた。
私も JavaScript でキューを実装してみた

# これは何？

https://qiita.com/mikecat_mixc/items/05491cf98392fb803f66

が面白かったので勝手に参加してみた。

# 書いたコード

こんなのを書いてみた。

```javascript:javascript
class QueueNabe {
    slotSize = 0xfeff;
    constructor(){
        this.headNode = this.tailNode = { prev:undefined, next:undefined, slot:Array(this.slotSize) }
    }
    head = 0;
    tail = 0;
    enqueue(x) {
        if (this.slotSize === this.tail) {
            this.tailNode.next = { prev:this.tailNode, next:undefined, slot:Array(this.slotSize) }
            this.tailNode = this.tailNode.next
            this.tail = 0;
        }
        this.tailNode.slot[this.tail++] = x;
    }
    dequeue() {
        if (this.head === this.slotSize) {
            this.headNode = this.headNode.next
            this.headNode.prev = undefined
            this.head = 0
        }
        return this.headNode.slot[this.head++];
    }
}
```

というわけで、前述の記事で最強っぽかった QueueStack と勝負。

# 結果

node で試した。
環境は、 MacBook Pro (Apple M1 非 Max）

## グラフ

両対数グラフ注意。
最初の何件かは捨てた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6854/05dc731d-a92b-e6a7-d84c-8b0e5d034aca.png)

## データ

|size|QueueStack|QueueNabe:|
|:--|:--|:--|
|100|0.6666666666666666|0.3333333333333333|
|200|0.3333333333333333|0|
|400|0.3333333333333333|0.3333333333333333|
|800|0|0.6666666666666666|
|1600|0.6666666666666666|1|
|3200|1.6666666666666667|1|
|6400|2.6666666666666665|1|
|12800|5.666666666666667|7.333333333333333|
|25600|14|6.333333333333333|
|51200|22.333333333333332|13|
|102400|47|27|
|204800|91.33333333333333|53.666666666666664|
|409600|186.66666666666666|111.66666666666667|
|819200|335.3333333333333|134.66666666666666|
|1638400|674|216.33333333333334|
|3276800|1358|436.3333333333333|
|6553600|2650.6666666666665|882.6666666666666|
|13107200|5353|1770.3333333333333|
|26214400||3730|
|52428800||7612|

# 解説

中身は、連結リスト。
連結リストの中に固定長の配列がある。

配列の長さが怪しい定数だけど、ハウスキーピングを入れて 2の16乗 という気分。ハウスキーピングのサイズは知らないので適当に減らした。

で。

enqueue 連結リストの末尾にある配列に追加。配列の末尾を超える場合は連結リストに配列を追加。
dequeue 連結リストの先頭にある配列から取得。配列末尾に到達したら、連結リストの先頭から撤去。

という作戦。

いくら enqueue / dequeue してもコピーは発生しない。

# 補足

アロケータの感じによっては固定サイズよりうまい方法があると思うけど、固定長にすることでアロケータが速くなりがち。

あるいは。不要になった配列を積極的に再利用するコードを入れると速くなることもあると思う。

あと。
連結リストで書いたけど、リストが大して長くないので、配列で持ってもそんなに変わらないと思う。
短い場合は配列のほうが速いかもね。

# 最後に

以上です。