OmniAICreator/Qiita-1.07M huggingface dataset:
新卒営業の僕が26歳でメガベンチャーエンジニア年収750万円で働いてる話

# はじめに
簡単に僕の経歴を書いておきます
- 2016年4月: **大学入学**（理学部で数学専攻。上の下くらいの国立大学）
- 2019年10月くらい: サークル活動ばかりしてた。就活始めたのが大学4年の9月くらい。後期募集してた**地元の営業会社に就職**。
- 2020年4月: **入社**。地獄の社会人編スタート。
- 2020年9月くらい: 仕事についていけず上司に病気呼ばわりされる。この辺りで転職を決意。
- 2022年12月: 営業じゃなきゃなんでもよかった。当時流行ってた**未経験エンジニアとしてSESに転職**。
- 2023年12月: 毎日死ぬほど勉強し転職に成功。メガベンチャーに転職成功。
- 現在: 中途入社時から年収も上がり懸命に生きてる（今ここ）。

今思えば本当に適当に生きてて猛省した方がいいレベル...
当時の想いを思い出しながら各章について雑な文で恐縮ですがつらつらと書いていきます。

# 営業会社から未経験エンジニアという道へ
大学時代、社会人というものや就職活動というものに全く関心がなく、大学4年生になってもサークル活動に打ち込んでいました。
なんとなく就職するしかないと思い立ち、大学のポータルサイトで求人を出してる企業を年収順にソートして上から順にエントリー。3社くらい受けてそのうちの1社に受かりそこで就活（？）を終了。
地頭は悪くないと謎の自信はありつつも、全く仕事ができず上司に病気・給料泥棒よばわりされ、鬱っぽい症状が出始めたのがこの時期。試しに転職エージェントと面談してみるとこんなにも知らない業界・職種が広がっていることを知った。
今度こそ死ぬ気で燃えられる仕事に出会いたい、スキルを身に付けたい、何者かになりたい、そんな思いで転職活動に打ち込み、未経験エンジニアを募集している企業の一社である東京のSES企業から内定をもらえた。

# 死ぬほど勉強したSES時代
転職後、僕は初心者だったので研修が用意されていた。プログラミングなど全くわからない僕でしたが、スキルを身に付けたい一心で終業後も自習時間を設け、必死に勉強した。研修の2ヶ月の間に基本情報、応用情報、JavaGoldを取得できた。
研修終了後、幸いにもガッツリプログラミングのできる案件にありつけた。研修や自学した内容と現場で求められる技術は全く違った。Githubやパブリッククラウド、コンテナ・仮想化技術など必要なスキルがプログラミングだけじゃないことも知った。
幸いプログラミングを含め、エンジニアリング技術は僕の肌に合っていることを感じていた。食事・睡眠時間以外の時間はプログラミングだけでなく周辺技術の書籍を買い込みかぶりつくように読み耽った。ハンズオンの口座にも申し込み自分で手も動かして勉強に充てた。
JavaとSpringBootをでのバックエンド開発が主な業務内容だったが、なぜかAI（機械学習、深層学習など）にも興味が出てきてライブラリをフルスクラッチで実装したりもした。（この辺りの知識は今は全く役立っていない笑）
そんな睡眠・食事以外を勉強するような生活を半年間続け、業務にも余裕が出てきて空き時間が出てきたりした。自分に必要なのはインプットよりもアウトプットだと気づき、副業を探し始める。
エンジニア経験1年未満の初心者で、しかも本業があり業務に充てられるのは平日の夜間と土日しかない負債でしかないような僕を雇ってくれる企業が見つかった。
時給1,000円ちょっとで学生インターンばかりの現場だったが、社長が人生変えたがっていた僕を気に入ってくれ、土日にわざわざ時間を作って現場の技術を教えてくれた。今ではその企業は卒業しましたが、当時のインターン生含め忘年会や定期的な飲み会は今でも誘っていただけます。
こんな感じで平日の9:00~18:00は本業を、平日19:00~24:00と土日9:00~22:00は副業の会社へ出社し学生インターンに混ざり仕事をするという新たな生活がスタートしました。朝は7:00に起き、本業・副業案件のキャッチアップや作業予定の確認、空き時間があればエンジニアリングの勉強に充て、それ以外の時間は全ての時間をプログラミングに充てていました。
ただ疲れるという感覚はあったものの、きつい、辛いという感覚はまったくなく、これほどまでに時間を忘れて打ち込める何かに出会えたことに心からの感謝と喜び、副業先の社長のような尊敬できる先輩との出会いに充実感が毎日感じられました。
副業案件にジョインして3ヶ月ほど経った頃、当時チームリーダだったインターン生が私用で卒業し、私がバックエンドチームリーダを任せられました。
この経験で自信をつけた私は、副業案件のリリースが目前に迫った時、2回目の転職を検討していました。

# 憧れのメガベンチャーエンジニアへの華麗なる転身
開発年数は浅かったものの、副業でのリーダ経験や技術力向上の姿勢、案件での貢献が評価され、メガベンチャー（身バレ防止のためぼかしていますが、DeNA, サイバーエージェント, LINE, ヤフーのどれかです）から内定を得ることができました。これをきっかけに副業を卒業することに決めました。
ただ前職、副業現場ではこれまでの働きや人間関係を評価していただき、今でも飲み会などで定期的に繋がりがもてていることは本当に恵まれていると感じます。
今では中途入社時と比べて給料も上がり、年収750万円ほどを本業だけで稼げている状況ですが、これまでの習慣は抜けず技術のインプットは必ず行っており、四半期に一度ほどはチーム内で勉強会を自主開催しています。

# 今後の展望
これ程までに自分に人生を変えてくれたエンジニアという職業に僕は本当に感謝しています。この軸は今後も変わらないと思います。またエンジニアリングについては自主的な勉強は欠かさずするほどには好きですし、今後も技術力は伸ばしていきたいと思っています。
いずれはGAFAMのような外資系にもチャレンジしたいとは思っていますし、30までに年収1000万が目標です。
DBについてのノート

# DBMSの障害対応について

## DBログ
- ログとはデータのACID特性を実現するための変更内容の記録のことである。 
- アルファペットは覚えられないので原子性（Atomicity）、一貫性(Consistency)、独立性(Isolation)、耐久性（Durability）、原一独耐で覚えることにする。 
- ログはログバッファとしてメモリ上に保存され、DBMSが記憶装置のログファイルに出力する。 
- ログには更新前ログファイルと更新後ログファイルの2種類がある。 
- ログにはデータ更新時に更新前の状態とと更新後の状態に加えてcommitもログに書き出す。 
- チェックポイントでハードディスクへの書き込みが行われる。 
- WAL（Write Ahead logログ先行書き出し）プロトコルとは更新前レコードの書き出し、更新後レコードの書き出し、実データの更新の順にデータ処理を行うことである。 

## 障害対応

- コミット前の障害は更新前ログからロールバックして復旧する。 
- コミット後の障害は更新後ログからロールフォワードして復旧する。 
- UNDOはロールバックの操作である。 
- REDOはロールフォワードの操作である。 
- UNDOとREDOは冪等性を持つ。 
- 冪等性(idempotent)とは1回実行したときと複数回実行した時が同じ結果になることである。 

## 障害回復処理

- トランザクション障害とはDBMSは正常に動いているが、DBMS内部で起きた障害のことである。ロールバックを行う。 
- ソフトウェア障害およびシステム障害はDBMS自体が正常に動かない場合やアプリケーションでの障害である。 
- ログを確認してコミットの状態に応じてロールバックまたはロールフォワードのどちらを行うか判断する。 
- ハードウェア障害およびメディア障害はバックアップを用いてロールフォワードを実行する。 
- バックアップにはフルバックアップ、差分バックアップ、増分バックアップがある。 

## 分散データベース
- 分散データベースとは複数のDBMSが連携して一つのDBMSとして動作するデータベースである。 
- ネットワーク経由で結合する（仮想サーバみたいだな）
- CAP定理とACID特性のうちC、Consistencyは共通のため、グラフになるのではないか
- これも覚えられないので一貫性(Consistency)、可用性(Availability)、分断耐性（Partition-tolerance）、一可分耐で覚える。 
- 分散データベースの位置に対する透過性とはユーザは一つのデータがどこに格納されているかを意識しなくて済むこと 
- 分散データベースの分割に対する透過性とはユーザは一つのデータが物理的に分割されていても意識しなくて済むこと 
- 分散データベースの複製に対する透過性とはユーザは一つのデータが複数のサイトに格納されていても意識しなくて済むこと 
- 2相コミットで担保する。
【効率化】SNSに疲れたあなたへ。RSSリーダーで効率的に情報収集する方法

## はじめに

「RSSリーダー」という言葉を耳にしたことはありますか？ 

かつて「古き良きRSSリーダー時代」と呼ばれる全盛期があったそうですが、最近ではその存在を知らない方や忘れられている方も多いかもしれません（実は私も最近まで知りませんでした・・）

しかし、SNSが主流になり情報があふれる現代において、今こそRSSリーダーを使うべきだと感じています。この記事では、その理由と具体的な活用法を解説します。

## RSSリーダーとは？

RSSリーダーは、ウェブサイトやブログの更新情報を一元管理し、ユーザーが効率的に情報をチェックできる便利なツールです。

RSSは「Really Simple Syndication」または「Rich Site Summary」の略で、ウェブサイトが更新されるたびに専用のフォーマット（RSSフィード）でその情報が配信されます。

### 主な機能

1. **RSSフィードの購読**：好きなウェブサイトをRSSリーダーで購読し、更新情報を自動的に取得します。
2. **一元管理**：複数のサイトの更新情報を一つのアプリで管理し、各サイトを個別に訪問する手間を省けます。
3. **新着情報の通知**：新しい記事が投稿されると、すぐに通知が届きます。
4. **オフライン閲覧**：一部のRSSリーダーは、取得したコンテンツをオフラインで閲覧できる機能も提供しています

### 使い方の例
たとえば、複数のニュースサイト、技術ブログ、ポッドキャストなどをフォローしている場合、RSSリーダーを利用することで、それぞれの更新情報を効率的にチェックできます。特に技術者や開発者にとって、必要な情報だけを選別できるRSSリーダーは強力なツールです。

### 代表的なRSSリーダー
* Feedly：ラウドベースで、PCやスマートフォンで同期して使えるRSSリーダー。
* Inoreader：多機能なRSSリーダーで、キーワードによるフィルタリング機能などが特徴。
* The Old Reader：シンプルなインターフェースを持つRSSリーダー。

RSSリーダーを利用することで、わざわざ各サイトを個別にチェックする必要がなくなり、効率的に情報収集ができるようになります。

## いまRSSリーダーを使うべき理由

皆さん、こんな経験はありませんか？

・情報収集のためにX（旧Twitter）のアカウントをフォローしたけど、タイムラインにあふれる関係のない投稿に目移りしてしまう・・
・技術ブログをチェックする習慣をつけたいけど、ついつい他のSNSに時間を取られてしまう・・

私も同様にSNS疲れや情報収集の効率の悪さに悩んでいました。

そこで、たくさんの情報に簡単にアクセスできてしまう情報過多の今だからこそ、情報を取捨選択できる「RSSリーダー」が有効ではないかと考えました。

## 私のRSSリーダー活用法

一番代表的なのはFeedlyですが、無料版ではカテゴリが３つまでしか作れなかったのでInoreaderを使っています。
また、個人的に記事の一覧がInoreaderの方が見やすいです。

### 効率的な情報収集のステップ

1. **新着をチェック**: 各カテゴリの新着をざっと確認し、気になった記事には星をつける。
2. **カテゴリの整理**: すべてのカテゴリをチェックし終えたら、それぞれを既読にする。
3. **集中して読む**: 星をつけた記事だけを後でまとめて読む。

重要なのは記事のピックアップする工程と記事を読む工程を分けることです。
これにより、集中力を保ちながら効率よく情報収集ができ、どこまでチェックしたかわからなくなることも防ぐことができます。

また、RSSリーダーを開くことの心理的ハードルを下げるため、技術やニュースだけでなく、ゲームの最新情報などの趣味に関連するフィードも登録しておくことがおすすめです。

## まとめ

SNS全盛の時代において、情報が過剰に溢れている今だからこそ、自分に必要な情報を効率的に管理できる「RSSリーダー」が真価を発揮すると思います。

特に情報収集を効率的に行いたい方や、SNS疲れを感じている方には、RSSリーダーが情報整理と取捨選択において非常に役立ちます。

ぜひ一度、自分に合ったRSSリーダーを試してみて、情報収集のスタイルを見直してみませんか？
Substance3DPainterでテクスチャを作成しUnityで使用する

# Unityで自作のモデルとテクスチャを使用したい

アセット等を使えばモデルやマテリアルを作る手間を省けますが、どうしてもオリジナルのモデルを使いたい場合、<br> <Strong>Maya(モデル) ⇒ Substance3DPainter(テクスチャ) ⇒ Unity(モデルとマテリアル)</Strong><br> という手順がおすすめです。

自分用に作成した備忘録なので、かなり雑多ではありますがご容赦ください。


早速Mayaから出力したFbxに簡単なテクスチャを作成していきたいと思います。


  <img src="https://github.com/user-attachments/assets/46fd1963-77c9-4e40-8ac2-99b037a38783" width = 90%><br>
  
1. UV展開済みであることを前提とする。
2. ベイク処理。
<img src="https://github.com/user-attachments/assets/26719bdc-23c8-40b8-98a5-e5889cf1a9c4" width = 80%><br><br>
3. Maskごとにフォルダを作成する。今回は作成したい素材ごとに作成している。（Wood...）
<img src="https://github.com/user-attachments/assets/700d475f-6112-48b5-84ec-67ffb4690c8e" width = 80%><br><br>
4. 色を追加したい箇所に黒マスクを追加。まずはWoodに黒マスクを追加してみる。
<img src="https://github.com/user-attachments/assets/ba85f07d-aefa-4ccc-b200-9a675208c85f" width = 80%><br><br>
1. Materialをマスクしたフォルダに追加。今回は「Stylized Wood Bark」
<img src="https://github.com/user-attachments/assets/21320e91-bdef-4134-8cd5-eb2a7974860d" width = 80%><br>
1. 簡単に塗るためにPolygonFill(塗りつぶし)を使用する。あらかじめ左のプロパティを開いておく。
<img src="https://github.com/user-attachments/assets/b9ebc07d-6d86-4352-8a4a-e96bb275ff9b" width = 80%><br>
1. Materialをアタッチしたいポリゴンを選択し塗りつぶしていく。以降フォルダごとに同様の作業を行う。
<img src="https://github.com/user-attachments/assets/8a0c5206-97f4-4e2d-b6f1-a7ba69422fab" width = 80%><br>
1. ファイル -> テクスチャを書き出しでデフォルトの出力。（必要に応じて適宜設定してください。）
<img src="https://github.com/user-attachments/assets/0a7d9779-945e-4dd8-a4a0-785b11c154c8" width = 80%><br>
<img src="https://github.com/user-attachments/assets/f8a9916d-fd1b-4e01-a003-99288c99356a" width = 80%><br>
1. 元のFBXと出力したテクスチャをUnityにインポートし、新規Materialにテクスチャをアタッチすることで、Substance3Dを使ったMaterial完成です。
<img src="https://github.com/user-attachments/assets/a7d6e8c2-328b-46cb-adf3-daa467b9e582" width = 80%><br>

# まとめ
ここでは、自作モデルに簡易的なテクスチャを作成する流れを説明してきました。
かなり、各所で省いて点が多くありますので、気になる点はぜひ他の記事を参考にしてみてください。

Substance3DPainterは無料で使用できるマテリアルやテクスチャが[3DAsset](https://substance3d.adobe.com/assets/)や[CommyunityAsset](https://substance3d.adobe.com/community-assets)として豊富に用意されていています。ぜひ活用してみてください。
ESETのせいでEdgeのIEモードが使えない場合の回避方法

# はじめに

一部の案件で、必要に迫られてEdgeのIEモードでアクセスしているサービスがあります。
ところが、ESET Internet SecurityをインストールしたあとにIEモードに切り替えようとすると、「このページを Internet Explorer モードで開くには、管理者特権で Microsoft Edge を再インストールしてください」と表示されて、IEモードが使えなくなってしまいました。
![CleanShot 2024-08-13 at 00.27.15@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24333/0e939a16-e123-ce33-781e-c9b5bbcac48a.png)

指示通りにMicrosoft Edgeを再インストールしてみても、全く効果なし。
でもESETの設定を調整することで回避できましたので、メモとして残しておきます。

# 注意

この記事の方法で回避すると、セキュリティ保護は少し緩くなります。
それが許容できるかどうかは、各自で慎重に判断してください。
もしこれが原因で何かしらのセキュリティ事故が起きても、筆者は責任を負いません。
（そもそも論として、IEモードを使うこと自体も慎重に・・・）

# 設定方法

1. 設定画面から「セキュリティツール」を選ぶ
![CleanShot 2024-08-13 at 07.46.15@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24333/cf275721-3d85-dc8b-355d-306fb9c53067.png)

2. デフォルトでは「すべてのブラウザーを保護」にチェックが入ってるので、外す
![CleanShot 2024-08-13 at 07.46.27@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24333/d1663a3e-f3e5-6d19-2885-1917d14823f2.png)

3. 警告が出たら「永久に無効にする」をクリック。「永久」とか物騒だけど、再度有効にすることは可能です。
![CleanShot 2024-08-13 at 07.46.41@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24333/e342c649-a455-5dd4-0385-7d15c11a321d.png)

4. 「このアプリがデバイスに変更を加えることを許可しますか？」で「はい」をクリック。
![CleanShot 2024-08-13 at 07.46.52@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24333/0ae92b18-25f4-f69b-7d4c-d101271a51ca.png)

5. こうなったら設定完了。**最後にWindowsを再起動する**
![CleanShot 2024-08-13 at 18.24.29@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24333/1ae8a67d-91de-c0cd-6de6-b135d34f457b.png)

これでIEモードが使えるようになるはずです。

# 検証環境
* MacBook Air M3 2024 macOS 14.5
* Parallels Desktop 19.4.1
* Windows 11 ARM版 ビルド:22631.3880
* ESET Internet Security 17.2.7.0

での結果です。

MacBook Pro上に構築したParallelsでもIEモードに切り替えられない現象は同じでした。
（回避方法はそこでは試してない）

ThinkPadの実機で動かしているWindows 11では、ESET入れて「すべてのブラウザーを保護」が有効な状態でもIEモードは使うことができました。
詳しくはわかりませんが、Parallels特有、もしくはARM版Windows特有の問題なのかもしれません。

ではまた！
AtCoder Beginner Contest 105

## A - AtCoder Crackers

https://atcoder.jp/contests/abc105/tasks/abc105_a

### 解説

- 余りが0なら差はない
- 余があるなら差は必ず1になる

### 実装例（C++）

```cpp
#include <iostream>

using namespace std;

int main() {
    int n, k;
    cin >> n >> k;

    int result = 0;
    if (n % k != 0) {
        result = 1;
    }

    cout << result << endl;
}
```

## B - Cakes and Donuts

https://atcoder.jp/contests/abc105/tasks/abc105_b

### 解説

- 有り得る通りの数を全探索する
-  0 <= ケーキの個数 <= N / 4、　0 <= ドーナツの個数 <= N / 7

### 実装例（C++）

```cpp
#include <iostream>

using namespace std;


int main() {
    int cake_price = 4, donut_price = 7;

    int n;
    cin >> n;

    for (int cake_num = 0; cake_num <= n / cake_price; cake_num++) {
        for (int donut_num = 0; donut_num <= n / donut_price; donut_num++) {
            int total_price = cake_num * cake_price + donut_num * donut_price;
            if (total_price == n) {
                cout << "Yes" << endl;
                return 0;
            }
        }
    }

    cout << "No" << endl;
    return 0;
}
```
パーフェクトRubyonRails備忘録2

# コントローラのアクションにおけるフック

- フックは3種類
    - before_action
        - アクション実行前に呼び出される
    - after_action
        - アクション実行後に呼び出される
    - around_action
        - アクションの前後で呼び出される
- around_actionの使い方
    - around_actionで定義したメソッド内で、アクション内に処理を戻す必要がある。
    - 例
    
    ```ruby
    class BooksController << ApplicationController
      around_action :action_logger
      # 略
      def action_logger
        logger.info "before_action"
        yield # ここで、アクションに処理が戻される
        logger.info "after_action"
      end
    end
    ```
    
    - ブロックを用いて書くことも可能
    
    ```ruby
    around_action do
      logger.info "before_action"
      yield
      logger.info "after_action"
    end
    ```
    

# ルーティングとリソース

- CRUD操作は `resources :publishers` の一行で定義できる
    - `rails routes`で確認できる
- 発展的な書き方

```ruby
resources :publishers do # publisherリソースに対するCRUDアクションが設定
  resources :books # GET /publishers/:publisher_id/books/:id などが設定
  member do
    # publisherリソース個別へのアクションを設定する
    get 'detail' # GET /publishers/:id/detailが設定
  end
  collection do
    # publisherリソース全体へのアクションを設定する
    get 'search' # GET /publishers/searchが設定
  end
end
```

- resources以外のルーティングパターン
    - １ユーザーから見て、リソースが１つしかない場合、 `resource` というヘルパーが使える

# 例外処理

- ユーザーに通知すべき例外処理は、基本的にコントローラが担当する
- 特定のステータスコードを返す、例外クラスの代表的なもの。

| ステータスコード | 英語名 | 例外の種類 |
| --- | --- | --- |
| 400 | Bad Request | ActionController::BadRequestなど |
| 404 | Not Found | ActiveRecord::RecortNotFoundActionController::RoutingErrorなど |
| 405 | Method Not Allowed | ActionController::MethodNotAllowedなど |
| 422 | Unprocessable Entity | ActiveRecord::RecordInvalid、ActiveRecord::RecordNotSaved、ActionController::InvalidAuthenticityTokenなど |
| 500 | Internal Server Error | 多くの例外 |
- rescue_from
    - 上記以外で、特定のレスポンスを使いたい時に使う
    
    ```ruby
    class LoginFailed < StandardError # 例外クラスを定義する
    end
    
    class ApplicationController < ActionController::Base
      rescue_from LoginFailed, with: :login_failed
      # withで例外発生時に呼ばれるアクションを指定
      
      def login_failed
        render template: "shared/login_failed", status: 401
      end
    end
    
    class LoginController < ApplicationController
      def create
        @user = User.where(name: params[:name], password: params[:password]).find
        raise LoginFailed unless @user
      end
    end
    ```
    
    - ここでは、ユーザーログインに失敗した場合、LoginFailed例外クラスを呼び出し、login_failedアクションにより、ユーザーにログイン失敗したことを示している。
Python FastAPIの環境構築

# Python FastAPIの環境構築

## なんで記載するのか？

仕事でpythonを利用する可能性が出てきたので、備忘録を兼ねて記載。

## 0. Project Settingup

### 0-1. Install Dependency

[pyenv](https://github.com/pyenv/pyenv)を利用するための環境構築

```terminal
% brew install pyenv
% make ${PROJECT_PATH}
% cd ${PRJECT_PATH}
% pyenv local 3.12.5
% pyenv version
pyenv version
3.12.5 (set by ${PROJECT_PATH}/.python-version)
```

[pipx](https://github.com/pypa/pipx)の設定

macの際にx-code-toolsが必要になる。

```terminal
% pwd
${PROJECT_PATH}
% brew install pipx
% pipx ensurepath
```

[poetry](https://python-poetry.org/)のinstall

```terminal
% pipx install poetry
```

## 1. Setting Up

```terminal
% pwd
${PROJECT_PATH}
% poetry init -n
% poetry config virtualenvs.prefer-active-python true
% tree -a .
.
├── .python-version
└── pyproject.toml

% poetry add fastapi uvicorn
% poetry install
..
├── .python-version
├── poetry.lock
└── pyproject.toml
```

```pyproject.toml
[tool.poetry]
name = "app"
version = "0.1.0"
description = ""
authors = [""]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.12"
fastapi = "^0.112.0"
uvicorn = "^0.30.5"


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
```

## 2. write sample app

```terminal
% pwd
${PROJECT_PATH}
% mkdir app
% touch main.py
```

```app/main.py
from fastapi import FastAPI


@app.get("/")
def index():
    return {"Hello": "World"}
```

### 2-1. 起動コマンドのためにMakefileをつくる

```terminal
% pwd
${PROJECT_PATH}
% touch Makefile
```

```Makefile
POETRY=poetry
PACKAGE=app

dev:
	${UVICORN} $(PACKAGE).main:app --reload
 
```

### 2-2. 起動する

```terminal
% make dev
oetry run uvicorn app.main:app --reload
INFO:     Will watch for changes in these directories: [${PROJECT_PATH}]
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [25050] using StatReload
INFO:     Started server process [25054]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
```
【Python】watchdogでファイルの変更を検知して非同期で処理する方法

ファイルの変更を検知して、サーバーにアップロードするような処理を書きたかったので調べました。
ファイルの変更検知は [gorakhargosh/watchdog | Github](https://github.com/gorakhargosh/watchdog) で簡単にできたのですが、アップロード処理を非同期で行う実装に手間取ったので残しておきます。

```python
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler, FileSystemEvent
from concurrent.futures import ThreadPoolExecutor


def upload_file(file_path):
    print(f"  [upload_file] start. (file={file_path})")
    time.sleep(5)
    print(f"  [upload_file] end. (file={file_path})")
    return "success"

def handle_future(future):
    try:
        success = future.result()
        print(f"  [handle_future] {success}")
    except Exception as e:
        print(f"  [handle_future] aught an exception: {e}")

class MyEventHandler(FileSystemEventHandler):
    def __init__(self, executor):
        # FileSystemEventHandlerを継承して、ThreadPoolExecutor をインスタンスに持たせる
        self.executor = executor

    def on_closed(self, event: FileSystemEvent):
        if event.is_directory:
            return
        print(f"[on_closed] start: {event}")
        # ThreadPoolExecutor で upload_fileを別スレッドで実行する
        future = self.executor.submit(upload_file, event.src_path)
        # コールバックでupload_fileの結果を処理する
        future.add_done_callback(handle_future)
        print(f"[on_closed] end")

# 最大3スレッドで処理する
with ThreadPoolExecutor(max_workers=3) as executor:
    event_handler = MyEventHandler(executor)
    target_dir = "/tmp"
    observer = Observer()
    observer.schedule(event_handler, target_dir, recursive=True)
    observer.start()
    try:
        while True:
            time.sleep(1)
    finally:
        observer.stop()
        observer.join()
```
Java Gold 例題 parallel 2

```java
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class GroupingByConcurrentExample {
	public static void main(String[] args) {

		Stream<Integer> numbers = Stream.of(1,3,5,10, -2).parallel();
  
		/* insert code */ nums = 
				numbers.collect(Collectors.groupingByConcurrent(n -> {
                    if(n % 2 == 0) return "even";
					else return "odd";
                }));
     
		System.out.println(nums);
	}
}
```

出力結果 1
```
{even=[10, -2], odd=[1, 3, 5]}
```

出力結果 2
```
{even=[-2, 10], odd=[5, 3, 1]}
```

insert codeに入力するコードとして適切なものはどれでしょうか
1. `Map<String, List<Integer>>`

1. `Map<String, Integer>`
1. `Set<String, Integer>`
1. `ParallelStream<Map<String, List<Integer>>`
1. `Optional<Map<String, List<Integer>>`

↓

↓

↓

↓

↓

↓

↓

↓

↓

↓

↓

↓

↓

↓

↓
正解
1 `Map<String, List<Integer>>`


groupingByConcurrentメソッドを使用することで、ストリーム内の要素を指定した分類関数に基づいてグループ化し、その結果をMapに収集します。
Mapのキーは分類関数の結果（この場合は "even" または "odd"）、値はそのグループに属する要素のリストです。

[Document groupingByConcurrent](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/util/stream/Collectors.html#groupingByConcurrent(java.util.function.Function))

`public static <T,​K> Collector<T,​?,​ConcurrentMap<K,​List<T>>> groupingByConcurrent​(Function<? super T,​? extends K> classifier)`

ストリーム内の要素を指定されたclassifier関数に基づいて分類します。
分類された結果はConcurrentMapとして返され、キーが分類結果、値がその分類に属する要素のリストになります。


`Collector<T, ?, ConcurrentMap<K, List<T>>>`
この部分は、メソッドがCollectorを返すことを示します。Collectorは、ストリームの要素を収集するためのオブジェクトです。返されるCollectorは、要素をConcurrentMap（並列で操作可能なMap）にグループ化し、キーKには分類の結果が入り、値には分類された要素Tのリストが入ります。

`groupingByConcurrent​(Function<? super T, ? extends K> classifier)`

要素を分類するための関数（分類関数）です。この関数は、ストリーム内の各要素Tを受け取り、それを分類するためのキーKを返します。


https://qiita.com/T-ngtj/private/df254aacd05be698199a
Go のリスト分割

リストを指定のサイズで分割して返す関数を用意した。
誰かが使うかも知れないので、共有しておく。誰も使わなかったら自分で使おう。

```go
func Split[T any](elms []T, size int) [][]T {
	if size <= 0 {
		return [][]T{elms}
	}
	var split [][]T
	for 0 < len(elms) {
		s := size
		if len(elms) < size {
			s = len(elms)
		}
		split = append(split, elms[0:s])
		elms = elms[s:]
	}
	return split
}
```

テストケースも併せて用意。
```go
func TestSplit(t *testing.T) {
	tests := []struct {
		name string
		elms []string
		size int
		want [][]string
	}{
		{
			name: "partial",
			elms: []string{"a", "b", "c", "d", "e"},
			size: 2,
			want: [][]string{{"a", "b"}, {"c", "d"}, {"e"}},
		},
		{
			name: "just",
			elms: []string{"a", "b", "c", "d", "e", "f"},
			size: 2,
			want: [][]string{{"a", "b"}, {"c", "d"}, {"e", "f"}},
		},
		{
			name: "single element",
			elms: []string{"a"},
			size: 2,
			want: [][]string{{"a"}},
		},
		{
			name: "empty",
			elms: []string{},
			size: 2,
			want: nil,
		},
		{
			name: "nil",
			elms: nil,
			size: 2,
			want: nil,
		},
		{
			name: "size: 1",
			elms: []string{"a", "b", "c", "d", "e"},
			size: 1,
			want: [][]string{{"a"}, {"b"}, {"c"}, {"d"}, {"e"}},
		},
		{
			name: "size: 0",
			elms: []string{"a", "b", "c", "d", "e"},
			size: 0,
			want: [][]string{{"a", "b", "c", "d", "e"}},
		},
		{
			name: "size: -1",
			elms: []string{"a", "b", "c", "d", "e"},
			size: 0,
			want: [][]string{{"a", "b", "c", "d", "e"}},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := Split(tt.elms, tt.size)
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("Split() = %v, want %v", got, tt.want)
			}
		})
	}
}

```
QiitaとZenn徹底比較。あなたはどちらを選ぶか?

# 趣旨
今までQiitaを使ってました。
最近はZennも使うようになり「どっちがいいのかな」と考えるようになりました。

異なりながら、コンセプトが似ているこの2サービス。

果たして、あなたは、どちらを選ぶのでしょうか?

# 比較軸
掃除機とっても、PCとっても、結局ものを選ぶ際には、「軸」を決めてその軸で選ぶことになります。

この軸は、選び始める時点で、明確ではないことも多く。この軸選びが、選択のすべてと言っても過言ではないと思っています。

最終的に、自分の軸としては3つの軸を設けました。

1. 書きやすさ
2. 読まれやすさ
3. それ以外の魅力

# ポイント1: 書きやすさ
これでいうと、Zennが一歩リードしてるのでは、というのが自身の見方でした。
**ただ、現状でいうとほぼ互角です。**

Zennが大きく優れていた点に、「GitHubで記事を管理できる」という点がありました。

https://zenn.dev/zenn/articles/connect-to-github

これにより、
- ローカルで記事管理できる
- バージョン管理できる
- 自分の記事を垢バンされても残すことが出来ます
- 今後自分のブログ作ったときに移すことだって容易

など様々な利点があります。

ただいつの間にか(やばいと思ったのか) Qiitaも2023にはGitHub管理の機能を実装、頂いておりました。

https://qiita.com/Qiita/items/32c79014509987541130

また、書き心地という意味だと双方マークダウン記法で、整備されており書きやすいです。
(細かいこというと、zennで画像を乗せる時5MB制限がありちょっとだけ面倒(自動で圧縮してほしい)ですが基本的には問題ないです。)

ということで、この部門では「互角」とさせていただきます。

# ポイント2: 読まれやすさ
これは、Zennが流行って来てるからとはいえ、歴史があるQiitaなのかなと想像はしてました。
ただ、検証しないと思い、全く同じ記事を3つQiitaとZennに掲載しその様子を調査しました。


具体的には
【個人開発でFlutterとFirebaseを使って60日でアプリをリリースした方法】
【怒ってます。Androidの新リリース基準酷すぎる】
【カレーちゃんから学ぶChatGPTの使い方】

約半年前にリリースしたこれらの記事の、
view数とイイね数を(2024/8/13時点)で集計します。

| 記事名  | Qiita view数  | Zenn view数 | Qiita イイね数 | Zenn イイね数 |
|---|---|---|---|---|
|個人開発でFlutterとFirebaseを使って60日でアプリをリリースした方法  | 8457 | 1205 | 20 | 33 |
|怒ってます。Androidの新リリース基準酷すぎる | 10940 | 460 | 34 | 10 |
|カレーちゃんから学ぶChatGPTの使い方| 652 | 191 | 2 | 4 |

こうやって見ると差は歴然です。
Qiitaの方が同記事をUPするにしても断然viewはもらえるようです。

但しもう一つ、びっくりするデータもあります。
イイね数は「Zenn」の方が多かったりするのです。

こうやって見ると
「Zennユーザの方があったかく、イイねを押してくれやすい」
ということができるのではと思います。

なので、
- 読まれやすさは「Qiita」
- イイね数は「Zenn」の方が優しめ

というのが自分の見解です。

# ポイント3: それ以外の魅力
ちょっとずるい観点ですが、それ以外の要素で特筆すべき点があれば、比較材料になりそうです。

Qiitaでいうと、「アドベントカレンダー」があげられると思います。
コミュニティが大きく育っている以上、それを使って12月頃に(最近はそれ以外もあるみたいですが)
「皆で記事書こう！！」みたいなのは素敵だと思います。

https://qiita.com/advent-calendar/2023

逆に、Zennは何でしょう?
「書籍を販売できる」というのがあると思います。
実際、FlutterやReactなどの技術書や、変わり種だと「自動競馬売買プログラム」とか色んな本が売られているようです。

https://zenn.dev/books/explore

ここの刺さり具合はユーザによるでしょう。
ちなみに自分は、Zennの自分の書籍が作れるは魅力に映りました。

# わたしの選択
3点を鑑みた結果自分は、「Zenn」を選択することにしました。
なぜなら、ポイント3の「書籍を販売できる」が刺さったからです。

「俺、本書いたんだぜ」ってかっこよすぎません?

正直ポイント2で書いたように多くの人に読まれるQiitaはその点で魅力的です。

ただ、自分としては「イイね」を比較的多くくれて、本をかけるかもしれない。
Zennをメインとして使っていきたいと思います。


# まとめ、あなたの選択は?
| ポイント  | Qiita | Zenn | コメント |
|---|---|---|---|
|1. 書きやすさ  | ◯ | ◯ | 両方GitHub管理でき、マークダウン記法書きやすい |
|2. 読まれやすさ | ◯ | △ | Qiitaの方がViewは多い。Zennは少ない割に「イイね」が多め |
|3. それ以外の魅力| △ | ◎ | Qiitaはアドベントカレンダー、Zennは自分の本かける |

自分としては、本かけるが決まり手になりましたが、
これ以外の観点もあると思いますし、いろんな意見があると思います。

ここから良サービスともにより進化していくと思いますし、自分も変わるかもしれません。

一旦自分の私見をまとめました。なにかの参考になれば幸いです。

**あなたは、どちらのサービスを選びますか? Qiita or Zenn?**
LINEBotを使って、明日の自分の勤務状況が簡単にわかるようにしたい！！

# こんにちは。初めまして。初投稿です。
@t0mohiです。

この頃、悩んでいることがありまして・・・。
情けないお話なのですが、毎日家でお酒を飲んでいると、
翌日以降の仕事のスケジュールがわからない？？？ってことがあるのです:scream:

勤務計画の紙を見るのが面倒くさいなぁと思うこともしばしば(´；ω；`)ｳｩｩ

そういえば、お酒を飲んでいる時には、片手には大好きな焼酎ハイボールのグラス、もう片方の手には、スマホを持っているなぁ:sweat:

スマホで簡単にポチっとしたら明日以降のスケジュールがわかればいいなぁと思い、
MAKEど素人のわたくしですが、作ってみました～:grin:

**動いてる動画**
 見えにくくてすみません。私のスマホに動画機能がないのです( ﾉД`)ｼｸｼｸ…

 https://x.com/i/status/1823266768162562282
![Screenshot_20240811-132648.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3840320/9f144e20-bbbe-937d-a75d-f9591700f568.png)

**1.使用ツール**
●Make
●LINEBot
●Googleスプレッドシート

**2.ポチっと日付を入れたら、その日の勤務時間と出張か会社に在社かわかるような、LINEBOTを作成してみました。**

●日付を入力⇒勤務時間＋在社or出張（出張の場所）が返ってくる:ok_hand:
これで明日の勤務間違えないぞー:ok_woman:

**MAKEシナリオ**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3840320/2220c3a1-2642-2d36-438b-70f82b1747bd.png)
こちらはラインに日付を入力するとその日のスケジュールが返ってくる詳細画像です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3840320/df9fe0cb-ba11-c400-2665-86582496b06b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3840320/e9b5c9cf-56e4-be1d-dad5-1c4457f43529.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3840320/79ab456a-ea6f-c32c-6143-1d2acc347287.png)

これで完成！！:punch:

**3.私のつまづきポイント**
●ラインに日付入力する時、全角で入力しエラー:disappointed_relieved:
半角になっていないことに気が付いて、ようやくデータが飛ぶようになりました:grin:

**4.日付を間違えた時に返信が来るようにしました**
日付を間違えた時には、日付が間違っていますよと返信が来るように設定。

![Screenshot_20240811-132941.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3840320/58426f2c-a6e2-0794-4222-182335edc7a5.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3840320/45222354-fe7f-8c43-fcf7-8c3a2b095caa.png)

## 最後に
初心者の私の初作品です:grinning:

まだまだな私ですが、さらに便利なツールが作成できるように、
挑戦していきたいと思います。
最近なんか忘れっぽいなぁ。そうだ、LINEでシンプルに操作できるToDoリストをつくろう！

## 問い合わせが多いよ。
仕事上、問い合わせなどの電話応対が多い私。
『はい、わかりました！　調べて折り返します！』は常套句。
でもいろいろな対応をしているうちに最初の問い合わせが抜け落ちていく・・・。
こんなことを防ぐため、自分用のシンプルなToDoリストをつくってみました。

## 今回使用したツール
**・LINE Developers**
　　　・・・　LINE Botを使用しました。
**・Googleスプレッドシート**
　　　・・・　ToDoリストの追加や削除に使用しました。
**・Make**
　　　・・・　LINE BotとGoogleスプレッドシートを操作します。

## 実際にできた動画がこちら！

https://x.com/i/status/1823204326887887113

**操作方法**
・『一覧』『削除』以外の文字列を入力をした場合は、ToDoリストに追加。
・『削除+ToDoの番号』でToDoリストの一覧から削除。
・『一覧』と入力すると現在登録されているToDoリストを一覧表示。

## Googleスプレッドシートがこちら
**・シート名：ToDo**
![sht_ToDo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3842559/867d3aad-292e-7b07-7592-e489d3456291.png)
・『追加』の場合、最終行の下に追加。
・『削除』の場合、メッセージをD1に入力。
　E1にはD1の数字部分-1の計算式が予め入力してある。

**・シート名：List**
![sht_List.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3842559/89c37b5e-bf56-ce1d-886e-b17f9da22566.png)
・『追加』の場合、ToDoシートの『C2:追加された行』範囲をJOIN関数で連結。
・『一覧』の場合、A1を返す。

## Makeの全体のシナリオがこちら
![make_全体コメント.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3842559/2c34e055-3e6b-7a31-f6cb-24379cabc049.png)

**『削除』の処理**
![スクリーンショット 2024-08-13 145144.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3842559/ec5540d0-4f0b-e73f-ed01-dae6dc29ad80.png)
・削除行の取得後、行の削除　→　削除したToDoの番号を通知します。

**『削除』以外の処理の下準備**
![スクリーンショット 2024-08-13 155145.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3842559/9644f3ed-3b1c-11d9-cf3a-d45dc46d7f06.png)
・最終行の取得後、変数endRowに最終行を、変数addRowに最終行+1をセット。

**『一覧』の処理**
![スクリーンショット 2024-08-13 160244.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3842559/ca707d97-6c18-5ae1-4db6-f8c61ee3d841.png)
・一覧の取得後、変数listDataにセットし、一覧を通知します。

**『追加』の処理**
![スクリーンショット 2024-08-13 163442.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3842559/5ccdd3ab-647b-b2da-8d4b-d7cc48aa9e73.png)
・それぞれ入力後、追加内容を通知します。

## 完成までの四苦八苦・・・
最初は **『一覧』の処理** の時に、スプレッドシートのセル範囲を配列に格納する方法を探りました。
その後でテキストにして **Send a Reply Messageモジュール** に渡して返答させようと試みました。
しかし **Array aggregaterモジュール** で、スプレッドシートのセル範囲を配列に格納するまではできたのですが、配列を改行でつなげてテキストのかたまりにする工程でうまくいかず・・・。
結局、スプレッドシート上でJOIN関数で連結した値を渡すことにしました（笑）
配列操作や文字列操作をもっとうまくマスターしていきたいなぁ。

## やったー！　動いたー！
今回、LINE Botを初めてつくってみました。
ほとんどスプレッドシート上の処理でごまかした感じです（汗）
シンプルすぎるToDoリストでしたが、日常のちょっとしたメモには使えそうです。
今後はリッチメニューに今回作成したToDoリストの機能を割り振ってみたいですね。
操作性が上がりますよね。

ではでは、今日はこのへんで！

**＝＝＝ 2024/08/18 以下修正・追加しました ＝＝＝**
・タイトルを修正しました。
　旧タイトル：『最近なんか忘れっぽいなぁ。そうだ、シンプルなToDoリストをつくろう！』
・ **『完成までの四苦八苦・・・』** の章を追加しました。
JupyterLabのインストールメモ

## 環境

* macOS Ventura 13.6.8

Pyhonとpipのバージョンは以下の通り

```shell-session
$ python3 --version
Python 3.9.6
$ python3 -m pip --version
pip 24.2 from /Users/maruyamahikaru/Library/Python/3.9/lib/python/site-packages/pip (python 3.9)
```

## インストール

初心者なのでまずはvenvを使わない方法で。Homebrew経由でインストールする。

```shell-session
$ brew install jupyterlab
```

## 起動

```
$ jupyter lab
[I 2024-08-13 16:48:43.018 ServerApp] jupyter_lsp | extension was successfully linked.
[I 2024-08-13 16:48:43.023 ServerApp] jupyter_server_terminals | extension was successfully linked.
[I 2024-08-13 16:48:43.034 ServerApp] jupyterlab | extension was successfully linked.
(...省略...)
```

ブラウザ上にJupyterLabの画面が表示されたら成功。
![Screenshot 2024-08-13 at 16.59.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79577/5fd25beb-37f1-69ef-116e-cc8190735697.png)

## トラブル

pipでインストールしようとすると、PEP668の制限によってブロックされる！

```shell-session
$ python3 -m pip install jupyterlab
error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try brew install
    xyz, where xyz is the package you are trying to
    install.
(...省略...)
```

これは、venvを使えば回避可能。venvの使い方は以下の記事がわかりやすかった。

https://www.python.jp/install/macos/virtualenv.html

## 他の方法

pipxを使ってインストールするのも良いかもしれない。

https://samedwardes.com/blog/2022-10-23-best-jupyter-lab-install/

## 参考

* https://jupyter.org/install
Lambdaレイヤーについて

## はじめに
Lambdaレイヤーについて、整理してみました。
Lambdaレイヤーとは、Lambda関数で使用するライブラリ、カスタムランタイム、設定ファイル、その他の依存関係をパッケージ化して共有するための機能です。レイヤーを使用することで、コードの再利用、デプロイの簡素化、バージョン管理が容易になります。レイヤーの作成と使用は、依存関係をディレクトリに配置し、ZIPファイルに圧縮してアップロードすることで簡単に行えます。また、不要になったレイヤーバージョンは削除することができます。これにより、複数のLambda関数で共通のコードやライブラリを効率的に管理できます。

## Lambdaレイヤーの利点

**1.コードを再利用することができる**
共通のライブラリや依存関係をレイヤーとしてパッケージ化することで、複数のLambda関数で再利用できます。

**2.デプロイをシンプルにすることができる**
レイヤーを使用することで、関数コードと依存関係を分離できます。

**3.バージョンを管理して、依存関係の変更が他の関数に影響を与えないようにすることができる**
レイヤーはバージョン管理が可能であり、特定のバージョンのレイヤーをLambda関数にアタッチできます。

## Lambdaレイヤーの作成

1. **依存関係の準備**
依存関係（ライブラリやカスタムランタイムなど）をディレクトリに配置します。例えば、Pythonのライブラリを使用する場合、`python`ディレクトリを作成し、その中にライブラリを配置します。

    ```bash
    mkdir -p layer/python
    pip install requests -t layer/python
    ```

2. **ZIPファイルの作成**
依存関係を含むディレクトリをZIPファイルに圧縮します。

    ```bash
    cd layer
    zip -r layer.zip .
    ```

3. **レイヤーのアップロード**
AWS Management Console、AWS CLI、またはAWS SDKを使用して、レイヤーをAWS Lambdaにアップロードします。

    ```bash
    aws lambda publish-layer-version --layer-name my-layer \
        --description "My layer" \
        --license-info "MIT" \
        --zip-file fileb://layer.zip \
        --compatible-runtimes python3.10 python3.11 \
        --compatible-architectures "arm64" "x86_64"
    ```

GUIの場合
![layer.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920213/74da69a5-e4b7-02d9-51f2-b9bfdca10dfc.png)

参考

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/creating-deleting-layers.html


## レイヤーの使用

1. **レイヤーのアタッチ**
Lambda関数にレイヤーをアタッチします。AWS Management Console、AWS CLI、またはAWS SDKを使用して、関数にレイヤーを追加します。

    ```bash
    aws lambda update-function-configuration --function-name my-function --layers arn:aws:lambda:region:account-id:layer:my-layer:1
    ```

2. **関数コードの更新**
関数コード内でレイヤーの依存関係を使用します。例えば、Pythonの`requests`ライブラリを使用する場合、以下のようにインポートします。

    ```python
    import requests

    def lambda_handler(event, context):
        response = requests.get('https://example.com')
        return response.text
    ```

## レイヤーバージョンの削除

レイヤーバージョンを削除するには、`DeleteLayerVersion` APIを使用します。以下のコマンドを使用して、AWS CLIでレイヤーバージョンを削除します。

```bash
aws lambda delete-layer-version --layer-name my-layer --version-number 1
```

レイヤーバージョンを削除すると、そのバージョンを使用するLambda関数を設定できなくなります。ただし、該当バージョンをすでに使用している関数は引き続きそのバージョンにアクセスできます。また、Lambdaはレイヤー名にバージョン番号を再利用することはありません。
mruby/cをmrubyで測定

いつ出るかわからないメッセージを目視してベンチマークをストップウォッチで測るのはたいへんなので、mrubyで測定してみました。

mrubyはMac miniのElCapitanで動かしました。ElCapitanではmruby3はビルドできなかったのでmruby2です。[mruby-serialport](https://github.com/monami-ya-mrb/mruby-serialport)を入れてビルドしました。

mrubyのスクリプトはこれです。

```ruby
sp = SerialPort.new("/dev/cu.usbserial-A1032R9E", 38400, 8, 1, 0)

count = 0
last = 0
while 1
  sp.getc
  if count != 0 then
    t = Time.now.to_i - last
    print count.to_s + ":" + t.to_s + "\n"
  end
  last = Time.now.to_i
  count += 1
end
```

シリアルから1バイト受け取るごとに経過時間を表示します。

mrubycのsupport_tools/benchmarks/merge_sort.rbを以下のようにして実機で動かします。

```ruby
serial_init()

while true
  serial_write('@'.ord)
  1000.times do
    merge_sort(ary)
  end
end
```

merge_sortの結果

|CPU|1回目|2回目|3回目|
|:--|:--|:--|:--|
|RX210|440|440|440|
|RX631|207|207|206|

hanoiの結果

|CPU|1回目|2回目|3回目|
|:--|:--|:--|:--|
|RX210|x|x|x|
|RX631|60|59|60|

RX210では動作しませんでした。メモリが足りないのかもしれません。

fib(32)

|CPU|1回目|2回目|3回目|
|:--|:--|:--|:--|
|RX210|192|191|191|
|RX631|90|89|90|

fib(32) - ビルド時にNDEBUGを設定してassertを無効化

|CPU|1回目|2回目|3回目|
|:--|:--|:--|:--|
|RX210|137|137|137|
|RX631|64|65|65|
参照渡しによってstd::bad_allocを解決

## はじめに

8年ぶりにC++を使ったプロダクト開発に携わることになり、
いわゆるモダンC++に慣れるためにサンプルプロジェクトを作ってみました!

https://github.com/smallriver0316/fx-cli

その過程で以下のエラーにはまりました。

```
terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Aborted
```

これを解決した方法を記載したいと思います。

## エラー原因

エラーが発生した箇所は明らかで、クラスのインスタンス作成時です。
コンストラクタに引数を渡していて、これを初期化子リストでクラスのメンバーに代入しています。

```.cpp
  // add input-currency option
  std::string input_currency;
  app.add_option<std::string>(
      "-i, --input-currency",
      input_currency,
      "Input currency, such as USD, EUR, JPY, and so on");

  // add output-currency option
  std::string output_currency;
  app.add_option<std::string>(
      "-o, --output-currency",
      output_currency,
      "Output currency, such as USD, EUR, JPY, and so on");

    ...

    auto fx = std::make_shared<Fx>(input_value);
    auto fx_interface = FxInterface(fx, input_currency, output_currency);
```

引数として渡しているのはコマンドラインから入力された文字列で、CLI11をつかってパースしています。

## 解決方法

タイトルにもある通り、コンストラクタの引数を参照渡しで受け取るようにしました。

```.cpp
FxInterface::FxInterface(std::shared_ptr<Fx> p_fx, std::string const &input_currency, std::string const &output_currency)
    : m_fx(std::move(p_fx)), m_input_currency(input_currency), m_output_currency(output_currency)
{
```

これによって見事に起こらなくなりました。
セオリー通りですね。

ただし、この記事を執筆する上で、同様の実装をしたサンプルプログラムを書いてみたのですが、同じ現象は起こせませんでした。
ですので、値渡しが直接の原因なのではなく、メモリ不足や不十分な最適化が原因の可能性もあります。

これに関してご知見ある方がいましたら、コメントいただけると幸いです。
StatcastでMLBのデータ解析をしてみた

# はじめに
Pythonのライブラリで[pybaseball](https://pypi.org/project/pybaseball/)というのがあり、これはMLBが公開しているStatcastのデータらしいので、このデータを使ってデータ解析してみました。

# 使用ライブラリ
``` shell
pip install pybaseball pandas matplotlib seaborn
```

# データ解析の目的
今回はヒットになりやすい打球速度と打球角度があるかどうかを調べてみました。

# データ解析スクリプト
``` Python
import pybaseball as pb
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# Statcastからデータを取得する期間を設定します（例: 2023年4月1日から2023年4月30日まで）
start_date = '2023-04-01'
end_date = '2023-07-30'

# Statcastデータを取得します
statcast_data = pb.statcast(start_date, end_date)

# 安打かどうかを示す列を作成します
# 安打（シングル、ダブル、トリプル、ホームラン）に該当する場合は1、その他は0
statcast_data['is_hit'] = statcast_data['events'].apply(lambda x: 1 if x in ['single', 'double', 'triple', 'home_run'] else 0)

# 必要な列を抽出し、欠損値を削除します
data = statcast_data[['launch_speed', 'launch_angle', 'is_hit']].dropna()

# データをビン（区間）に分けて、打球速度と角度の組み合わせごとにヒット率を計算します
data['speed_bin'] = pd.cut(data['launch_speed'], bins=10)
data['angle_bin'] = pd.cut(data['launch_angle'], bins=10)

# 各ビンごとのヒット率を計算します（observed=Falseを明示的に指定）
hit_rate = data.groupby(['speed_bin', 'angle_bin'], observed=False).mean().reset_index()

# 最適な打球速度と角度を特定します
best_combination = hit_rate.loc[hit_rate['is_hit'].idxmax()]
print(f"最適な打球速度範囲: {best_combination['speed_bin']}")
print(f"最適な打球角度範囲: {best_combination['angle_bin']}")

# データを可視化します
plt.figure(figsize=(12, 8))
pivot_table = hit_rate.pivot(index='angle_bin', columns='speed_bin', values='is_hit')
sns.heatmap(pivot_table, cmap="YlGnBu", annot=True, cbar=True)
plt.title("Hit Probability by Launch Speed and Launch Angle")
plt.xlabel("Launch Speed (mph)")
plt.ylabel("Launch Angle (degrees)")
plt.show()
```

# 結果
すごく当たり前かもしれませんが、打球速度が100mph前後で打球角度が-18°〜54°だとヒットになりやすいことがわかりました。打球速度が85mphを切ると、打球角度によらずヒットになる確率が低くなるので、日本からMLBへ行く打者は100mphの打球速度が出せないと厳しいということなのかも。
![data.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512829/ffbc38dd-3d86-be1d-8832-1cdf66f20bd9.png)

# 感想
APIでアクセスするのかと思いましたが、Pythonのライブラリが提供されていて簡単にデータ解析が始められました。データはダウンロードする必要があり、１ヶ月分くらいは適度な時間でダウンロードできますが、２ヶ月くらいになるとワーニングがでます。長期間のデータ解析をする場合は、１ヶ月単位くらいで細切れにダウンロードしたほうがよいかもしれません。いろいろがんばってたらMLBからデータアナリストとしてのジョブオファーがくるのかもしれない。
ubuntu 24.04 LTSで自宅サーバー構築（PHP8.3の取得）

<a href="https://px.a8.net/svt/ejp?a8mat=3ZDGSF+2SMFEY+CO4+60OXE" rel="nofollow">初期費用無料、月額990円から、高速・多機能・高安定レンタルサーバー『エックスサーバー』</a>
<amp-pixel src="https://www19.a8.net/0.gif?a8mat=3ZDGSF+2SMFEY+CO4+60OXE" layout="nodisplay"></amp-pixel>
taisuke@ubuntuserver:~$ sudo apt -y install php8.3 php8.3-mbstring php-pear
taisuke@ubuntuserver:~$ sudo php -v
PHP 8.3.6 (cli) (built: Apr 15 2024 19:21:47) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.6, Copyright (c) Zend Technologies
    with Zend OPcache v8.3.6, Copyright (c), by Zend Technologies
#テストスクリプトを作成して動作確認
taisuke@ubuntuserver:~$ sudo echo '<?php echo `php -i`."\n"; ?>' > php_test.php
taisuke@ubuntuserver:~$ sudo php php_test.php | head
phpinfo()
PHP Version => 8.3.6
System => Linux www.srv.world 6.8.0-31-generic #31-Ubuntu SMP PREEMPT_DYNAMIC Sat Apr 20 00:40:06 UTC 2024 x86_64
Build Date => Apr 15 2024 19:21:47
Build System => Linux
Server API => Command Line Interface
Virtual Directory Support => disabled
Configuration File (php.ini) Path => /etc/php/8.3/cli
Loaded Configuration File => /etc/php/8.3/cli/php.ini
<a href="https://px.a8.net/svt/ejp?a8mat=3ZDGSF+2SMFEY+CO4+60OXE" rel="nofollow">初期費用無料、月額990円から、高速・多機能・高安定レンタルサーバー『エックスサーバー』</a>
<amp-pixel src="https://www19.a8.net/0.gif?a8mat=3ZDGSF+2SMFEY+CO4+60OXE" layout="nodisplay"></amp-pixel>
IBM Security Verify APIを使ってユーザー名とパスワードで認証する

# はじめに
ユーザー認証のログを意図的に生成するために、ユーザー名とパスワードで認証するAPIを試しました。


https://docs.verify.ibm.com/verify/reference/authenticatewithpassword

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678692/db0292de-e577-edb9-da2a-7d10b4d6184e.png)


# 1.Cloud DirectoryのID確認
APIで利用するため、Cloud Directoryに割り当てられたIDを確認します。
[認証] -> [アイデンティティ・プロバイダー] -> [Cloud Directory]を開き、コピーします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678692/22a01ff0-8f61-75fc-340d-38977378a0a7.png)

# 2.ユーザー名とパスワードによる認証

当該APIを使ってみます。

`````
curl -X  POST "https://<テナント名＞.verify.ibm.com/v1.0/authnmethods/password/<Cloud DirectoryのID>" --header "Content-Type: application/json" --header "Authorization: Bearer <アクセストークン>" --data-raw "{\"username\": \"<ユーザー名＞\",\"password\": \"<パスワード>\"}"
`````

ユーザー認証が成功すると、200 OKで、以下のようなレスポンスが返ってきました。
```
{"groups":[],"attributes":[{"values":["<email>"],"name":"email"},{"values":["<username>"],"name":"name"},{"values":["<familyName>"],"name":"familyName"},{"values":["<givenname>"],"name":"givenName"},{"values":["<username>"],"name":"username"},{"values":["cloudIdentityRealm"],"name":"realm"},{"values":["regular"],"name":"userCategory"}],"id":"<userid>"}
```

ユーザー認証が失敗すると、400 Bad Requestで以下のレスポンスが返ってきました。
```
{"messageId":"CSIBH0044E","messageDescription":"The system cannot authenticate the user because the username or password was incorrect."}
```

# 3.存在しないCloud DirectoryのIDを指定した場合
正しいIDを指定した場合は、Realmが「cloudIdentityRealm」となります。
存在しないIDを指定した場合は、Realmが「UNKNOWN」としてに判断されます。

レポート出力例）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678692/89a7fda4-5de1-bf59-8c83-05f8912c7926.png)



# 最後に
今回は、ユーザー名とパスワードで認証するAPIを試しました。
ユーザーがどのアイデンティティー・プロバイダーに所属しているか指定するため、Cloud DirectoryのIDを指定するという点がポイントとなりました。
ubuntu 24.04 LTSで自宅サーバー構築（NFSクライアントの設定）

<a href="https://px.a8.net/svt/ejp?a8mat=3ZDGSF+2SMFEY+CO4+60OXE" rel="nofollow">初期費用無料、月額990円から、高速・多機能・高安定レンタルサーバー『エックスサーバー』</a>
<amp-pixel src="https://www19.a8.net/0.gif?a8mat=3ZDGSF+2SMFEY+CO4+60OXE" layout="nodisplay"></amp-pixel>
taisuke@ubuntuserver:~$ sudo apt -y install nfs-common
taisuke@ubuntuserver:~$ sudo vi /etc/idmapd.conf
#5行目 : コメント解除して自ドメイン名に変更
Domain = ご自身のサーバーのドメイン名
taisuke@ubuntuserver:~$ sudo mount -t nfs ご自身のサーバーのドメイン名:/home/nfsshare /mnt
taisuke@ubuntuserver:~$ sudo df -hT
Filesystem                        Type   Size  Used Avail Use% Mounted on
tmpfs                             tmpfs  392M  1.1M  391M   1% /run
/dev/mapper/ubuntu--vg-ubuntu--lv ext4    28G  6.1G   20G  24% /
tmpfs                             tmpfs  2.0G     0  2.0G   0% /dev/shm
tmpfs                             tmpfs  5.0M     0  5.0M   0% /run/lock
/dev/vda2                         ext4   2.0G   95M  1.7G   6% /boot
tmpfs                             tmpfs  392M   12K  392M   1% /run/user/0
ご自身のサーバーのドメイン名:/home/nfsshare      nfs4    28G  6.1G   20G  24% /mnt
#NFS 共有がマウントされた
#NFSv3 でマウントしたい場合は [-o vers=3] オプション付加
システム起動時に自動的に NFS 共有をマウントするには、以下のように設定します。
taisuke@ubuntuserver:~$ sudo mount -t nfs -o vers=3 ご自身のサーバーのドメイン名:/home/nfsshare /mnt
taisuke@ubuntuserver:~$ sudo vi /etc/fstab
#最終行に追記 : マウントするホームディレクトリを NFS 共有に変更
ご自身のサーバーのドメイン名:/home/nfsshare /mnt               nfs     defaults        0 0
システム起動時にではなく、マウントポイントへのアクセス時に、動的に NFS 共有をマウントする場合は以下のように設定します。
taisuke@ubuntuserver:~$ sudo apt -y install autofs
taisuke@ubuntuserver:~$ sudo vi /etc/auto.master
#最終行に追記
/-    /etc/auto.mount
taisuke@ubuntuserver:~$ sudo vi /etc/auto.mount
#新規作成 : [マウントポイント] [オプション] [ロケーション]
/mnt   -fstype=nfs,rw  dlp.srv.world:/home/nfsshare
taisuke@ubuntuserver:~$ sudo systemctl restart autofs
#マウントポイントへ移動して確認
taisuke@ubuntuserver:~$ sudo cd /mnt
taisuke@ubuntuserver:/mnt~$ sudo df -hT /mnt
Filesystem                   Type  Size  Used Avail Use% Mounted on
ご自身のサーバーのドメイン名:/home/nfsshare nfs4   28G  6.1G   20G  24% /mnt

taisuke@ubuntuserver:/mnt~$ sudo grep /mnt /proc/mounts
/etc/auto.mount /mnt autofs rw,relatime,fd=7,pgrp=2863,timeout=300,minproto=5,maxproto=5,direct,pipe_ino=14799 0 0
ご自身のサーバーのドメイン名:/home/nfsshare /mnt nfs4 rw,relatime,vers=4.2,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=10.0.0.51,local_lock=none,addr=10.0.0.30 0 0
<a href="https://px.a8.net/svt/ejp?a8mat=3ZDGSF+2SMFEY+CO4+60OXE" rel="nofollow">初期費用無料、月額990円から、高速・多機能・高安定レンタルサーバー『エックスサーバー』</a>
<amp-pixel src="https://www19.a8.net/0.gif?a8mat=3ZDGSF+2SMFEY+CO4+60OXE" layout="nodisplay"></amp-pixel>
ubuntu 24.04 LTSで自宅サーバー構築（NFS サーバーの設定）

<a href="https://px.a8.net/svt/ejp?a8mat=3ZDGSF+2SMFEY+CO4+60OXE" rel="nofollow">初期費用無料、月額990円から、高速・多機能・高安定レンタルサーバー『エックスサーバー』</a>
<amp-pixel src="https://www19.a8.net/0.gif?a8mat=3ZDGSF+2SMFEY+CO4+60OXE" layout="nodisplay"></amp-pixel>
taisuke@ubuntuserver:~$ sudo apt -y install nfs-kernel-server
taisuke@ubuntuserver:~$ sudo vi /etc/idmapd.conf
#5行目 : コメント解除して自ドメイン名に変更
Domain = ご自身のサーバーのドメイン名
taisuke@ubuntuserver:~$ sudo vi /etc/exports
#最終行に設定を記述
#例として [/home/nfsshare] を NFS 共有に設定
/home/nfsshare 192.168.3.0/24(rw,no_root_squash)
taisuke@ubuntuserver:~$ sudo mkdir /home/nfsshare
taisuke@ubuntuserver:~$ sudo systemctl restart nfs-server
<a href="https://px.a8.net/svt/ejp?a8mat=3ZDGSF+2SMFEY+CO4+60OXE" rel="nofollow">初期費用無料、月額990円から、高速・多機能・高安定レンタルサーバー『エックスサーバー』</a>
<amp-pixel src="https://www19.a8.net/0.gif?a8mat=3ZDGSF+2SMFEY+CO4+60OXE" layout="nodisplay"></amp-pixel>
【MySQL】 SELECT list is not in GROUP BY clause and contains nonaggregated column 'XXX' which is not functionally dependent on columns in GROUP BY clause

## はじめに
MySQLを5.7にアップデートしたときにgroup byを使用しているコードでエラーが発生したので、その対応をまとめます。

## 問題
エラー文
```sql
select id, company_id from users group by company_id;

SELECT list is not in GROUP BY clause and contains nonaggregated column 'users.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
```

原因は、GROUP BYで指定されていないカラムを、SELECTで指定することによるエラーです。
MySQL5.6までは、ONLY_FULL_GROUP_BYという設定が無効でしたが、MySQL5.7からこの設定がデフォルトで有効になっているためエラーが出るようになります。

## 解決方法
### ANY_VALUEを使用する
ANY_VALUE関数を使用することで、GROUP BYで指定されていないカラムを、SELECTで指定することができるようになります。
```sql
select any_value(id), company_id from users group by company_id;
```

### 設定を変更する
ONLY_FULL_GROUP_BYの設定を無効にして、エラーが出ないようにします。

## 参考
https://bayashi.net/wiki/linux/mysql/mysql_group_by_sql_mode_any_value
ubuntu 24.04 LTSで自宅サーバー構築（OpenSSHの設定）

<a href="https://px.a8.net/svt/ejp?a8mat=3ZDGSF+2SMFEY+CO4+60OXE" rel="nofollow">初期費用無料、月額990円から、高速・多機能・高安定レンタルサーバー『エックスサーバー』</a>
<amp-pixel src="https://www19.a8.net/0.gif?a8mat=3ZDGSF+2SMFEY+CO4+60OXE" layout="nodisplay"></amp-pixel>
taisuke@ubuntuserver:~$ sudo apt -y install openssh-server
taisuke@ubuntuserver:~$ sudo vi /etc/ssh/sshd_config
#33行目 : コメント解除して [no] に変更すると root ログイン一切禁止
PermitRootLogin no
taisuke@ubuntuserver:~$ sudo systemctl restart ssh
SSH クライアントの設定です。Windows を例にします。
Windows クライアント側で SSH で接続可能なソフトウェアを用意します。 
当例では Putty を使用します。
Putty を上記サイトからダウンロード/インストールして起動したら、以下のような画面になるので、 Hostname にサーバーの ホスト名または IP アドレスを入力し [Open] をクリックして接続します。
![putty.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3858002/ba36baf7-146d-0a96-c374-35af4a6a2c94.jpeg)
ユーザー名とパスワードを入力して認証すると、以下のように Ubuntu サーバーにリモートログインすることができます。
![putty2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3858002/e50c02ff-e13c-9109-3c6e-50621f0e28f9.jpeg)
<a href="https://px.a8.net/svt/ejp?a8mat=3ZDGSF+2SMFEY+CO4+60OXE" rel="nofollow">初期費用無料、月額990円から、高速・多機能・高安定レンタルサーバー『エックスサーバー』</a>
<amp-pixel src="https://www19.a8.net/0.gif?a8mat=3ZDGSF+2SMFEY+CO4+60OXE" layout="nodisplay"></amp-pixel>
Unityでビルドエラー"CommandInvokationFailure: Gradle build failed."がでたときの対処法

初めての記事投稿なので稚拙な部分がありますがご了承ください。

UnityでXREALデバイス向けにゲーム開発をしていたところ、ビルドエラーが出るようになったのでその対処法と、実行してみて何も変わらなかったことなどを共有します。

かなり長い時間をかけて解決に取り組んでいたなおかつ、メモを適宜とっていなかったせいで何を境目にビルドエラーが出るようになったのか覚えてないです。すみません、、、。

# 実行環境
Windows11
Unity 2021.3.29f1
NRSDKForUnityAndroid_2.2.1
ImmersalSDKv1_20_0
Shopify SDK for Unity

# 解決方法：Gradleをアップグレードした
こちらの記事を参考にさせていただきました。
https://qiita.com/3BMKATYWKA/items/467c6c9a57fd8ace7948

### 必要なGradleバージョンを確認する。
Unityのコンソール画面の中に、必要なGradleバージョンが書かれているはず。
「必要なバージョンは～～なのに、あなたのGradleバージョンは～～ですよ」と。

私の場合はGradle v6.7.1が必要だと言われていました。

### Gradleをダウンロードする
こちらから必要なGradleをダウンロードします。
https://gradle.org/releases/
binary-onlyかcompleteを選べますが、**complete**を選びます。

### Gradleを解凍してUnityフォルダに移動する
解凍したフォルダを、旧Gradleが置かれているフォルダに移動させます。

私の場合は
（somepath）\2021.3.29f1\Editor\Data\PlaybackEngines\AndroidPlayer\Tools\gradleに置きました。
gradleフォルダの中にgradle-6.7.1を入れたということです。

:::note
UnityのGradleはデフォルトではProgram Files以下のパスに入っています。
例
(somepath)\Program Files\Unity\Hub\Editor\
2021.3.29f1\Editor\Data\PlaybackEngines\AndroidPlayer\Tools\gradle
:::

### PrefencesからGradleのパスを指定しなおす
Edit > Prefencesを開きます。
Enternal Toolsの項目を開きます。
ここにはJDKやAndroid SDKなどのAndroid向けビルドに必要なSKDのパスが保存されています。

ここで、Gradleのパスを指定し直します。
✅️Gradle Installed with Unity(recommended)のチェックを外します。
Browseから、新Gradleフォルダを指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3860179/e02fbcd8-972e-9183-bd8d-5e7efd2f3d2b.png)

### ビルドして終了
私の場合はこれでビルドが通るようになりました。

# 未解決点
一応、上記のようにやればビルドが通るようになります。
1点気がかりなのは、エラーでOpenJDKがどーのこーのと言っていた点です。
結果的にOpenJDKには触れずに解決できたので「あれはなんだったんだろう‥‥？」で終わっちゃいました。有識者がいらっしゃいましたらコメントなどつけてくれると嬉しいです。

# やったけどダメだったこと
## ・Unityをインストールし直す
どうせこういうのはインストールし直しておけばどうにかなるんだよ～！と思ったけどダメでした。
アンインストールしたあとにちゃんとエクスプローラから残っていた2021.3.29f1フォルダを削除してからやり直しましたが無理でした。

## ・Unityの場所を半角スペースの入っていない場所に移動させる。
調べていく中でこちらの記事を見つけました。
https://zenn.dev/kaibaoke/scraps/fb46e170b72993
「Unityのインストール先にスペースを含まない。**特にC:\Program Files\以下にインストールしてないか注意する**」の解説を見て、
Program Files以外の、半角スペースが入らないパスになるようにインストールし直しましたがこちらも同様のエラーが出ました。

## ・Unity Hubからモジュールを追加し直す
これはやったというか、そもそもできなかったんですがUnity Hub上ではきちんとOpenJDKといったモジュールが正しくインストールされている表示になっていたので確認だけで終わりました。

# 原因が判明したので追記
XREALデバイス向け開発に使われるNRSDKを2.2.1にバージョンアップする際に、Gradleのアップグレードが必要と書かれてありました。確認していない自分が悪い。

▼証拠となるNRSDK2.2.1のリリースノートはこちら
https://xreal.gitbook.io/nrsdk/nrsdk-fundamentals/release-note/nrsdk-2.2.1
以下、上記ページより引用
>Upgrade Android Gradle (3.4.3 -> 4.2.2). Note: Gradle version must be 6.7.1 or higher to avoid build errors. If you are using Unity 2022 or later, the included Gradle version is sufficient. For versions earlier than 2022, you will need to download the appropriate Gradle version.

訳：
Android Gradleを3.4.3から4.2.2にアップグレードしろよな。
注意：ビルドエラーを避けるためにはGradleバージョンが6.7.1以上必要だぜ。もしUnity2022以降を使っているなら梱包されてるGradleバージョンは十分だ。2022以前のものなら適切なGradleバージョンのダウンロードが必要だぜ。