OmniAICreator/Qiita-1.07M huggingface dataset:
読みにくいMNISTの文字を見つける

突然ですが、私はリアルでは字が汚いと言われることがあります。どれくらい汚いかと言われると、大学院生時代に生協で備品を注文しようとした時に個数を数字で書くと職員さんが読めないレベルで字が汚いらしいです(自分は達筆なのだと思っています)。
ところで機械学習で字といえばMNISTを想像される方が多いと思いますが、あれって実際機械学習を勉強するにしては字がきれいなのかとふと疑問に思うことがあります。生データを見てみると「え、読めない」と思うことがあるのですが、じゃあ機械学習を使ってみるとどうなるのかちょっと覗いてみましょう。
# コーディング
## ライブラリのインポート
```Python3
from sklearn.datasets import load_digits
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import train_test_split as tts
import numpy as np
import matplotlib.pyplot as plt
```

## データの読み込み
今回は今までに書いた記事と違ってScikit-Learnのトイデータをそのまま使わせてもらいます。
```Python3
x = load_digits().data
y = load_digits().target
x = x / max(x.flatten())
```
一応前処理として大丈夫だとは思いますが正規化を行って取り得る値を0から1までに直します。

## 学習とスコア
では実際に学習させてみてスコアを見てみましょう。
まずデータは全て同じになるようにテストデータのサイズとランダムにテストデータと訓練データを分けるものは指定します。
ニューラルネットワークの中間層は500、1000、750、500にします(いつもだいたいこの数字)。
```Python3
x_train, x_test, y_train, y_test = tts(x, y, test_size=0.2, random_state=1)
model = MLPClassifier(hidden_layer_sizes=(500, 1000, 750, 500))
model.fit(x_train, y_train)
y_pred = model.predict(x_test)
model.score(x_test, y_test)
```
結果正解率は0.9861111111111112となりました。

## 間違えたデータを探す
精度は結果として100%ではありませんでした、つまりどこかしら数字を誤読したという事が考えられます。
ではどの数字をどのように間違えて、なおかつSoftmax関数の値からどのくらいの確率でそう答えたのか見てみましょう。
```Python3
miss = []
y_predSM = model.predict_proba(x_test)
for i in range(len(y_test)):
    if y_test[i] != y_pred[i]:
        miss.append([i, y_test[i], y_pred[i], max(y_predSM[i])])
df_miss = pd.DataFrame(miss)
df_miss.columns = ["ID", "real", "predict", "probably"]
df_miss
```
実行した結果、このようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782889/f81a2ede-83b5-4cb6-1d24-1a5d0d894e9e.png)

では一番上に表示されているID108番の文字について画像で出力してみましょう。
```Python3
plt.imshow(x_test[108].reshape(8, 8), cmap="gray")
plt.title("real=9 predict=5 probably=0.99")
plt.show()
```
結果このような文字が画像として出力されます。
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782889/af3b9bc8-8909-5edd-0e66-b1c8ac55be97.png)

確かに9とも5とも見て取れますね。ぶっちゃけこれだけ見せられたら答えに戸惑います。

# まとめ
というわけで今回はMNISTの誤読をテーマにしてみましたが、これは実際にはある意味ややこしいデータを見つけることもできます。そのややこしいデータを見つけてどうするかは作り手次第ですが、統計的にややこしい、画像がややこしい等、分類アルゴリズムは100%当たる訳でもありませんし、何％の確率でそう予測したかも出てきますのでMNISTに限らず様々な発見につながると思います。
Anacondaについて　インストールと起動について

Anacondaについて勉強をしました。初歩的なことをまとめさせていただきます。

Anacondaとは、ユーザーはライブラリやツールのインストール、環境設定の手間を大幅に削減することができます。

デメリットとしてはいかが考えられます。
・一括インストールのため容量が大きい（3GB以上の空き容量が必要、人にもっては数十GBの容量がかかっていたという話もあります）
・不要なライブラリがある
・Pythonの標準パッケージとは異なる動作を示すことがある



Conda環境を利用する時には、次のコマンドでConda環境を有効化します。
```
conda activate
```
このコマンドはConda環境を有効化し、Anacondaが提供する python コマンドなどが使えるようになります。
環境名を指定せずに conda activate コマンドを実行すると、デフォルトのConda環境である base 環境が有効になります。
各行の先頭に(base) が付きます。

Conda環境を終了するときは、次のコマンドを実行します。
```
conda deactivate
```



Conda環境で新しいConda環境を作成するには以下を実行します。
これで、py36 という名前で、Python 3.6の環境が作れます。
```
conda create --name py36 python=3.6
```

作成した環境は、conda activate 環境名 で有効化して使います。
```
conda activate py36
```
各行の先頭が(py36)になります。

base環境に戻す際は以下を実行して有効化すると戻ります。
```
conda activate base
```



また、Anacondaをインストールした際にAnaconda Navigatorも同梱されています。
Anaconda Navigatorとは、仮想環境の作成や管理、各種パッケージのインストールなどの操作を直感的に行うことができます。
例えば、先ほど作成したpy36環境もここで管理されています。



2024年4月時点でAnacondaをインストールする際は、以下の表示がされます。
Email Addressを入力して「Submit」をクリックするか
下部の「Skip registration」をクリックすることで
インストーラーをダウンロードできる画面に遷移します。
![anaconda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3122544/133ec1a0-0b9a-def4-9b62-32a3fa4ee618.png)

Anacondaのパッケージインストール画面は以下から遷移できます。
https://www.anaconda.com/products/individual
Javascriptで最頻値を求めようとした備忘録

※筆者はJavascript初学者

最頻値、厳密には「文字列が複数入った配列から、最も出現回数の多い文字列を返す」関数を作りたかった。
色々やり方はあると思うが、ちょっと躓いたところもあったのでメモしておく。

サンプルとした配列は下記。
```javascript:main.js
let arr = ["melon","apple","apple","pine","apple","melon","orange"]
```
"apple"が3回、"melon"が2回、"orange"と"pine"が1回ずつ出現する。

# 参考
- [JavaScriptの配列の最頻値を求めるコピペ](https://qiita.com/yas-nyan/items/8afaf7a1fc5b2306354b)
- [javascript 配列の最頻値を求める](https://mebee.info/2022/05/29/post-66763/)
- (↑の解説記事) [JavaScript 自由研究 - 最頻値を求めるコード - カメリアの記事](https://camellia.hatenablog.jp/entry/2023/11/21/JavaScript_%E8%87%AA%E7%94%B1%E7%A0%94%E7%A9%B6_-_%E6%9C%80%E9%A0%BB%E5%80%A4%E3%82%92%E6%B1%82%E3%82%81%E3%82%8B%E3%82%B3%E3%83%BC%E3%83%89)
- [SIMPLE STATISTICS](https://simple-statistics.github.io/)　…統計分野向けライブラリ

# 結論
## 読みやすい方
```javascript:main.js
function mode(input){
    let index = new Map
    let countMax = 0
    let output
    input.forEach(value =>{
        let count = -~index.get(value)
        count ? count++ : count = 1;
        index.set(value,count);
        count >= countMax && (output = value,countMax = count);// 後から来た方を残す
    });
    return output
}
```
※複数結果がある場合、元の配列でindexが大きい方を残す
[SIMPLE STATISTICS](https://simple-statistics.github.io/)のmodeFastを参考に、ちょっと書き方を改めてみた形。


## 短い方
```javascript:main.js
const c = (x, i, v) => (x[i] ? x[i].add(v) : x[i] = new Set([v]), i);
const result = arr.reduce(function (x, v) { return (this.set(v, c(x, (this.get(v) + 1 || 1), v)), x); }.bind(new Map), []).pop();
console.log([...result]);
```
※すべての結果を配列に入れる
[javascript 配列の最頻値を求める](https://mebee.info/2022/05/29/post-66763/)の微改造

_訂正：2024/05/05_
- `count >= countMax ? (...) : null` ⇒ `count >= countMax && (...)`
- `input.map` ⇒ `input.forEach`

@mashuel 様、@juner 様よりご指摘いただきました。ありがとうございました。

# 躓いたとこ
## ['a','p','l','e']
[javascript 配列の最頻値を求める](https://mebee.info/2022/05/29/post-66763/)のコードをそのまま使った場合、`['a','p','l','e']`が出力結果になってしまった。
これは`c`のうち`Set(v)`で作られたもので、これがそのまま残ってしまうためである。
文字列を相手にする場合は`Set([v])`にしてやればよい。

## reduceのaccumulatorがわからない
上記でやりたいことができることは実験してわかったので、使う上ではこれでいいんだけど、理解ができていないところがある。
勉強のため、`arr.sort().reduce(function (x, v, i) { `と`return ...`の間に`console.log(x)`を入れて、accumulatorの中身を見てみたら、画像のようなのが見られた。（Chromeのコンソール)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3775044/7a7488de-440c-38d6-754e-b64560e65251.png)
index=0の時のもので、xにはinitialValueで指定した[]があるわけなんだけど、コレはどこから来たのか……
3Dプリントの造形方式による違いについて

# はじめに
自分で設計した物を速く形にしたいと思い2013年から3Dプリンタを導入
これまでの紆余曲折をメモ書きしていきます。

# 導入検討
光造形方式、FDM方式などある中で、この時点では光造形の後処理を面倒くさいなあと判断して候補から除外FDM方式から選択。

# 最初はメンデル型のRepRapに
ネットで6万円の破格で発見、直ぐにポチッとして確保
到着後、早速組み立てして試運転をするも設定に苦しむこと数週間
ようやく印刷成功するも積層跡や仕上がり精度に不満がでて、その後オートレベリン追加、テーブルの剛性強化、制御基板を交換など泥沼へ突入、材質もABS PET PCなど試しながらの試行錯誤続けながら使用する事になるが…

# 救世主？
やはり、光造形の方が仕上りは良いのかと、ネットを見ていると、水洗いレジンで強度の高いレジンの記事を発見　コレは‼️と思い光造形に気持ちが傾いて、Amazonでプリンタと高靭性レジンを購入
プリンタ　ANYCUBIC Photon M3 Plus
レジン　SK本舗　水洗い高靭性
https://skhonpo.com/products/sk-ultrawaterwashableresin

# やはり救世主だった
到着後、マニュアル通りに設定して印刷開始
想像以上の仕上がりに感動
ここから後処理の改良にハマる事に…

![IMG_0216.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3773328/1524c6d7-0b99-bbcb-2e89-a5c32fe3815d.jpeg)
左がFDMで印刷　PET素材
右が光造形で印刷　高靭性レジン
VSCodeでOpenAPIのYAMLをプレビュー・HTML出力する方法

この記事では、Visual Studio Code（VSCode）を使用してOpenAPI v3のYAMLファイルをHTMLドキュメントに変換するプロセスを詳しく説明します。例として、OpenAPIの公式GitHubリポジトリにある[Petstore APIのYAMLファイル](https://github.com/OAI/OpenAPI-Specification/blob/main/examples/v3.0/petstore.yaml)を用いて手順を示します。

## **VSCodeのプラグイン「OpenAPI (Swagger) Editor」のインストール**

- VSCodeを開き、拡張機能タブから「OpenAPI (Swagger) Editor」を検索してインストールします。これによりYAMLファイルの編集が容易になります。

## YAMLファイルのダウンロード

この記事では例として、[Petstore APIのYAMLファイル](https://github.com/OAI/OpenAPI-Specification/blob/main/examples/v3.0/petstore.yaml)を使用します。以下の手順でファイルをダウンロードしてください：

- リンクをクリックしてファイルページにアクセスし、「Raw」ボタンをクリック後、右クリックして「名前をつけてリンク先を保存...」を選択し、ファイルをローカル環境に保存します。

## プレビュー

### 方法1: VSCode内でのプレビュー

1. VSCodeでダウンロードしたYAMLファイルを開きます。
2. 「OpenAPI (Swagger) Editor」拡張機能により、右上に表示される「Preview」ボタンをクリックすると、サイドパネルにHTMLプレビューが表示されます。

### 方法2: コマンドラインを使用した変換
#### 必要なツールとプラグインの準備

##### 1. **Node.jsとnpmのインストール**

Redocly OpenAPI CLIを使用する前に、Node.jsとnpm（Node.jsのパッケージマネージャ）がインストールされている必要があります。最新バージョンのNode.jsをインストールすると、npmも一緒にインストールされます。

- [Node.jsの公式サイト](https://nodejs.org/) からインストーラをダウンロードし、指示に従ってインストールしてください。このプロセスにより、`node` と `npm` コマンドが利用可能になります。

##### 2. **Redocly OpenAPI CLIのインストール**

- ターミナルまたはコマンドプロンプトを開き、以下のコマンドを実行してRedocly OpenAPI CLIをインストールします：
  ```bash
  npm install -g @redocly/cli
  ```
  - このコマンドはグローバルにCLIツールをインストールします。
1. 保存したYAMLファイルがあるディレクトリにコマンドラインで移動します。
2. 以下のコマンドを実行してHTMLファイルを生成して、プレビューサーバーを起動します。
   ```bash
   openapi preview-docs petstore.yaml
   ```
3. ブラウザで、http://127.0.0.1:8080 を開きプレビューを確認します。
4. Ctrl + C で停止します。

## HTMLファイル出力
OpenAPI (Swagger) Editor を使用して直接 HTML ファイルを生成する機能は、標準の機能としては提供されていません。しかし、OpenAPI (Swagger) Editor で API ドキュメントを作成・編集した後、別のツールを使って HTML ドキュメントに変換することは一般的なアプローチです。以下はその方法をいくつか紹介します。

### @redocly/cli を使用して HTML ドキュメントを生成する
`@redocly/cli` は Node.js がインストールされている必要があります。まだインストールされていない場合は、Node.js の公式サイトからダウンロードしてインストールしてください。

1. Node.js をインストールします。
2. コマンドラインを開いて、以下のコマンドを実行し、@redocly/cli をインストールします

   ```bash
   npm install -g @redocly/cli
   ```

3. HTML ドキュメントの生成

   ```bash
   npx @redocly/cli build-docs <path-to-your-openapi-file.yaml> --output <output-directory>
   ```
   例えば、petstore.yaml というファイルから HTML を生成し、現在のディレクトリに出力する場合は以下のようになります（環境によって、数秒かかります）
   ```bash
   npx @redocly/cli build-docs petstore.yaml --output ./petstore.html
   ```
   これにより、`petstore.yaml` から `petstore.html` という HTML ドキュメントが作成されます。

### 3. Swagger Codegen を使用する
Swagger Codegen を使用しても、OpenAPI スペックから HTML ドキュメントを生成することが可能です。Java 環境が必要ですが、コマンドラインから HTML ドキュメントを生成することができます。使用方法は以下の通りです：

1. Swagger Codegen をダウンロードし、適切に設定します。
2. 以下のコマンドを使用して HTML ドキュメントを生成します：

   ```bash
   java -jar swagger-codegen-cli.jar generate -i your-openapi-file.yaml -l html
   ```

   このコマンドは、`your-openapi-file.yaml` から HTML ドキュメントを生成します。

これらの方法を利用することで、OpenAPI Editor で作成したスペックから HTML ドキュメントを生成することが可能です。各ツールの設定やインストール方法については、それぞれの公式ドキュメントを参照してください。

## まとめ

VSCodeのプラグイン「OpenAPI (Swagger) Editor」やRedocly OpenAPI CLIを使用することで、APIドキュメントの作成と管理が容易になります。これにより、API開発の透明性と効率が向上します。
Web上でPDFをHTMLに変換する

## はじめに
表題通り、PDFをHTMLに変換します。[サイト](https://www.idrsolutions.com/ja/online-pdf-to-html-converter)より変換ができたので、紹介します。

## やりかた
https://www.idrsolutions.com/ja/online-pdf-to-html-converter

①ファイルアップロード
![スクリーンショット 2024-04-21 15.37.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/dc4130df-6a2a-e0be-284c-a53858ca7146.png)

②変換処理を押す
![スクリーンショット 2024-04-21 15.38.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/2a155079-b445-d8a4-b2cd-d68c2f2b5a5a.png)

③数秒で完成。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/499c5db7-9e1d-93d6-d543-24c82f75d6cc.png)

④プレビュー確認
![スクリーンショット 2024-04-21 15.40.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/36f23b48-7341-80b4-cb3a-0b14299af10d.png)
今回は、結婚式の席次表をPDFからHTMLに変換する事が目的でした。

## 小話
利用規約上、商用利用する場合は「BuildVu」のロゴを削除する事はできないようです。
ただ、今回は私用での利用のため、ロゴを削除したいと思います。
![スクリーンショット 2024-04-21 15.43.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/aea891c0-5adf-1566-f89d-3679158b5fc6.png)

```html:index.html
<script>
window.addEventListener('DOMContentLoaded',
    function(){
    const    el=document.createElement("div");   el.innerHTML=atob("PGRpdiBzdHlsZT0icG9zaXRvYT48L2Rpdj4=");
    〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜[割愛]~~~~~~~~~~~~~~~~~
    );</script>

```
みたいなscriptが書いてあるので、これを削除すると消えます。
Crystalで作成したツールを配布する方法

## はじめに

Webアプリケーションの作成にCrystalを使っている人が多い。しかし、Crystal言語は、コマンドラインツールを作成するのに向いていると思う。ここではCrystalで作成したツールをどのようにして配布しているか、現時点でのやり方を書いてみた。

これが最良の方法という気は全くなくて、あくまで自分の現時点でのやり方を紹介するものです。（GUIツールは想定していない）

## 前提となる知識

### バイナリファイルの配布

Crystalではバイナリファイルを直接配布する。

### 現状ではパッケージマネージャーはない

したがって `cargo install` `gem install` `pip install` のような方法でコマンドラインツールをインストールすることはできない。

"依存マネージャー" として、`shard` が普及しているが、複雑なことはできない。複雑なことは `Makefile` を使うという暗黙の了解がある。

Crystalはコミュニティが小さいため、エコシステムの整備が進まない。現在のコアチームはパッケージマネージャーに否定的で、その理由の一つは、Crystalのコンパイルが非常に遅いことがある。

## おすすめの方法

:::note info
2024-04-21 時点の記事です。最新情報をチェックしてください。
:::

- ツールはGitHub Releaseで配布する。
- GitHub Release は、GitHub Actionsで作成する。
- `git tag v0.1.2` のように gitでタグをつけると自動で発火するようにしておく。

```yml
on:
  push:
    tags:
      - "v*"
```

### Linuxの場合

公式のDockerイメージ(alpine)を使って、静的にリンクする。

https://crystal-lang.org/reference/1.12/guides/static_linking.html

GitHub Actionsだとこんな感じ。

```yml
      - name: Run Linux Build
        if: matrix.os == 'ubuntu'
        run: |
          mkdir -p bin
          chmod 755 bin
          docker run -d --name alpine -v $(pwd):/workspace -w /workspace crystallang/crystal:latest-alpine tail -f /dev/null
          docker exec alpine shards install --without-development --release --static
          docker exec alpine shards build --release --static
          docker exec alpine chmod +x bin/deepl
          docker exec alpine gzip bin/deepl
          docker exec alpine mv bin/deepl.gz bin/deepl-linux.tar.gz
```

なお、静的リンクされたバイナリではなく動的リンクされたバイナリを配布したい場合は、`checkinstall` などを利用して deb パッケージを作成するという方法もある。この場合は、APTのパッケージなどに依存できるがLinuxのさまざまなディストリビューションやバージョンに対応するのは簡単ではない。

## Macの場合

- Macは[静的リンクをサポートしておらず](https://crystal-lang.org/reference/1.12/guides/static_linking.html#macos)、バイナリファイルを配布するのは難しい。
- かわりに `homebrew tap` を使ってユーザー自身にコンパイルしてもらう。
- GitHub上に `homebrew-brew` というリポジトリを作成する。そこに自作ツールのForlumaを配置しておく。
- 下は自分がやってる例。

https://github.com/kojix2/homebrew-brew

## Windowsの場合

- CrystalのWindows対応は現在も進行中であり、C言語の共有ライブラリを呼び出したり配布したりするベストプラクティスは定まっていない。
- プロジェクトがCrystalのみで書かれてる場合は `shards build --release --static` のように `--static` オプションをつけると単一のバイナリが出力さる。
    - `--static` をつけないと、`bin` ディレクトリに実行形式のファイルに加えて `*.dll` が出力される。
- dll を一緒に配布したいときは、結局 Inno Setup や NSIS などのインストーラー作成ツールを使用するのがいいのではないだろうか。
- 余談だが、GitHub Actionsなどで生成したバイナリはウイルス対策ソフトなどでマルウェアと誤認識される可能性がかなりある。

## GitHub Releaseへのアップロード

https://github.com/softprops/action-gh-release

上のGitHub Actionsを使うのが簡単である。

```yml
      - name: Upload Release Asset
        uses: softprops/action-gh-release@v2
        with:
          files: |
            bin/deepl-linux.tar.gz
            bin/deepl-macos.tar.gz
            bin/deepl-windows.zip
            LICENSE.txt
```

このActionを利用するときには、Workflowのパーミッションの設定が必要である。

この記事は以上です。
文字列から指定したクラスのインスタンスを作成する記事

# この記事について
インプットした内容の雑記です！
他にも使い方があったり、間違ってる使い方をしていたらコメントで教えてください！

# 参考記事
https://naoyu.dev/%E3%80%90unity%E3%80%91%E6%96%87%E5%AD%97%E5%88%97%E3%81%A7%E6%8C%87%E5%AE%9A%E3%81%97%E3%81%9F%E3%82%AF%E3%83%A9%E3%82%B9%E5%90%8D%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9/

https://atmarkit.itmedia.co.jp/ait/articles/0512/09/news136.html

# 実装コード
内容としてはこれだけです。
```C#
using System;
using UnityEngine;

public class Test : MonoBehaviour
{
    // クラスの名前
    private string className = "Message";

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // 文字列クラスの情報を含んだobjectを取得
            var classType = Type.GetType(className);

            // クラスのインスタンスを生成
            Message message = (Message) Activator.CreateInstance(classType);
            message.DebugLog();
        }
    }
}

public class Message
{
    public void DebugLog()
    {
        Debug.Log("hoge");
    }
}
```
Unityでの実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588406/f7e0ca79-da23-123d-ae64-3f08e0dcf589.png)
スペースキーを押すとログが返ってきました。

# 雑記
・Type.GetType
Type.GetTypeが文字列のクラスの情報？型？を取得してる。
変数定義見たいな事をしてるのかな？
情報を持っているだけで単体ではクラス利用は出来ないみたい。

・Activator.CreateInstance(クラス);
作成したインスタンスがObject型なので明示的なキャストをする必要がある。
引き数にクラスを渡すとそのクラスのインスタンスを作成してくれる。new()に近い？

分からない事が沢山増えた。

# ちょっと改良したバージョン
上コード実装だと他のクラスが利用できないので改良したバージョンです。
```C#
using System;
using UnityEngine;

public class Test : MonoBehaviour
{
    // クラスの名前
    [SerializeField] private string className;

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Message();
        }
    }

    private void Message()
    {
        // 文字列クラスの情報を含んだobjectを取得
        var classType = Type.GetType(className);
        
        // クラスのインスタンスを生成
        var message = (IMessageable) Activator.CreateInstance(classType);
        message.Message();
    }
}

public interface IMessageable
{
    /// <summary>
    /// メッセージを表示する
    /// </summary>
    void Message();
}

public class Banana : IMessageable
{
    public void Message()
    {
        Debug.Log("まだ半熟じゃないよ");
    }
}

public class Chocolate : IMessageable
{
    public void Message()
    {
        Debug.Log("甘くて美味しいよ");
    }
}     
```

Unityでの実行結果
classNameをBananaにすると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588406/5a8aa63d-a097-f672-9e0d-cbd815532c32.png)

半熟じゃないとコメントが返ってきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588406/1db89aeb-aa05-6399-0391-901ffdc803cc.png)

Chocolateにすると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588406/1e8c3ef3-f688-3a10-8d6a-5e3c2958d899.png)

ログが続き甘いと返ってきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588406/ec1797d7-9e3b-be3c-faae-63179ab4e918.png)

# まとめ
文字列からクラスのインスタンスを作成する事が出来た。
ただし、文字列なのでクラス名を間違えるなどのヒューマンエラーも起こりそう。
旧世代のDB インスタンスが廃止されるようです

# こんなタイトルのメール届いていませんか？
`[通知] RDS MySQL、MariaDB、および PostgreSQL 上の M4、R4、T2 DB インスタンスの廃止予定について`
- メールタイトルを一部抜粋していますが、こんなタイトルのメールが数通届きました

# メールを読んでみる
- メールのタイトル通りですが、M4/R4/T2 の旧DB インスタンスが2024/5/31 で廃止され、6/1 以降でM5/R5/T5 へそれぞれDB インスタンスタイプが変更されるようです
    - 正確な移行日はわからないようですが、おそらく6/1 以降の次のメンテナンスウィンドウで移行されるのではと考えています
- 対象DB エンジン:
    - Amazon RDS for MySQL
    - Amazon RDS for MariaDB
    - Amazon RDS for PostgreSQL
    * Aurora などは対象外のようです
- 移行先:
    - M4 -> M5
    - R4 -> R5
    - T2 -> T3
- 移行先にDB インスタンスサイズがないときは、1 サイズ落とした形で移行されるようです
    - 例: db.m4.10xlarge -> db.m5.8xlarge
        - この場合スペックダウンになるので、移行後のCPU 負荷やメモリ使用量は注視やアラートを気にしておく必要がありそうです
- 移行に際して特定のバージョンを使っておく必要もないようですが、移行と同時にマイナーバージョンアップがかかる可能性もありそうです
    - イマイチメールの書き方がわからず...です
- ダウンタイムあり
    - 5 分 〜 60 分のダウンタイムがあり、通常は30 分未満と書かれています
    - マルチAZ 構成の場合は1 台ずつの変更
        - 当然ですね...
- RDS のBlue/Green デプロイを使い、シームレスに対応するのが良いとメールに書かれています
    - 工数確保できるのなら、この手法を試してみたい

 # 注意点
 - RI を購入している場合は、6/1 以降のRI の扱いについては確認が必要です
     - 自分が面倒見ているaws アカウントではRI を買っていないので、メールを読み飛ばしています...

# ちょっと嬉しいこと
- インスタンスタイプが新しくなると若干時間単価が安くなる場合があるので、微々たるものですが費用削減につながりこの点は嬉しいです
    - 最近aws 費用が高いと言われているので、少しでも安くなるのは大変ありがたい
- 簡単に計算してみる
    - db.t2.micro: $0.026
    - db.t3.micro: $0.026
    - 時間単価変わらず...

# まとめ
- 古いものは使わず、新しい世代が出たらタイプ変更できないか、訴えていくしかなさそうです
- RDS で旧世代廃止なので、そのうちEC2 でも同様なことが起きないか気になります

# つぶやき
- DB インスタンスタイプ変更と一緒に、2024/8 に予定されているTLS 証明書更新も一緒にやりたい
メモリつよつよCPU弱者デスクトップ

# タイトル回収
さっそくタイトルの意味、所有するデスクトップPCのスペックを紹介します。

- Dell OptiPlex 3050 
    - メモリ … 32GB (16GB × 2)
    - ストレージ … 128GB 2.5inch SSD , 256GB M.2 2280 SSD
    - CPU … intel Core i3-7100 (2コア2スレッド)
    - USBポート … Type-A × 6 (うちUSB3.0 × 4)
    - WLAN … なし (M.2で増設可能)
    - 出力 … Display Port , HDMI

Dell製の小型デスクトップPCです。OptiPlexシリーズは法人向けに開発されたものらしく、3000/5000/7000と型番が高くなるにつれ性能が高くなっていきます。（IntelのCoreシリーズみたいな）所有してるOptiPlexはmicroという部類で20cm×20cmほどの小型デスクトップですが、ものによってはしっかりとした大きさのデスクトップもあります。

法人向けPCということもあり、拡張性・USBポートの多さが魅力的なPCです。ここに惚れてメルカリで購入しました。

最初はメモリ8GB,ストレージ128GBの標準的なPCでしたが、メルカリでクーポンが使えたのでお得に32GBメモリとM.2 SSDを増設しました。

## メリットと弊害
お分かりだと思いますが、偏りがすごいです。
あとCPUだけ変えようと思うのですが、どうにも高くて買えません…(´;ω;｀)

記憶装置だけ強くしたって頭が弱いとあんまり意味がない感じがしますよね。

いわば記憶力が最強になったナマケモノみたいなもんで、2年前のものを覚えていてもまあ早く動けるわけでもなく逃げる早さもかわらないようなものです。昨日食べた草を鮮明に覚えていてもそれを調理できるようになるわけでもなく、まさに猫に小判・豚に(略

こんなPCを使った感想を書いていこうと思います。
### メリット
- マルチにタスクを立ち上げても余裕
- ストレージに困らない
- 動画処理もけっこうできる
- PowerPointなどほとんどのアプリがスムーズに動く

当たり前かもしれませんがこんなもんです。OSのバックグラウンド処理とかでもメモリ食うので多ければ多いほど処理が軽くなっていると思います。

### 弊害(足りないこと)
- アプリの立ち上がり速度は変わらない
- インストール速度もあまり変わらない
- 動画編集などの出力時間もあまり変わらない

「弊害」とはちょっと意味が違うかもしれませんが、メモリがたくさんあってもやっぱ根本的な処理速度は変わらないというのがはっきりしました。ふつう、CPUとメモリは同じようなスペックのもので組まれるものではありますが、実際こんだけはっきりしてるとどうなるのかを確かめるためにメモリだけ増やしてみました。まあそのまんまですね。

## 結論：目的による
自分は、かなりの頻度でYoutubeを見ながらプログラミングの学習やWebを見たりしてるので正直メモリ増設だけで十分だと感じています。

すなわち、「複数のタスクを開きながらPCを触る」って人はメモリを重視してPCを組む
「ゲームやシンプルな処理速度を上げたい」って人はCPUを重視してPCを組む
ってことをした方がいいですね。

そんな人いないと思いますが、金のない私みたいな人はそこだけでも強くして目的に応じた自分なりのPCができるといいですね
【Unity】URPのCustom Shaderで影を落とす方法【URP】

# 自己紹介
URPのShaderLabの記事少なすぎ問題を提唱したい大学生

# 今回の題材
とあるサイトを参考にToonShaderを書いていたのですが、影が落ちない。
影落とすにはどうするんかなーと思って、色々調べてたのですがなぜか日本語の記事１個もなかったので、日本語の記事書きます。

# 実は超簡単
```HLSL
Pass {
    Name "ShadowCaster"
    Tags { "LightMode"="ShadowCaster" }

    ZWrite On
    ZTest LEqual

    HLSLPROGRAM
    #pragma vertex ShadowPassVertex
    #pragma fragment ShadowPassFragment

    #pragma shader_feature _ALPHATEST_ON
    #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A

    #pragma multi_compile_instancing
    
    #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SurfaceInput.hlsl"
    #include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl"
    ENDHLSL
}
  ```
上記のコードを２つ目のPassとして記述するだけです。
これはURPの`ShadowCasterPass.hlsl`に記述されているシェーダーを呼び出すだけのことをしています。

# おわり
こんだけ。誰かが救われますように（）

## 参考

https://www.cyanilux.com/tutorials/urp-shader-code/
npm ERR! code UNABLE_TO_VERIFY_LEAF_SIGNATUREの対処法

プロジェクトでnpmを使用しているときに「UNABLE_TO_VERIFY_LEAF_SIGNATURE」というエラーが発生した場合、これはSSL証明書の検証に問題があることを示しています。具体的には、SSL証明書チェーンの末端（リーフ）証明書が信頼できる認証局によって正しく検証されていない場合にこのエラーが発生します。以下に、この問題を解決するためのステップを説明します。

### 原因となる主な要因

1. **環境設定の問題**: ローカル環境またはプロキシサーバーが、SSL通信において中間証明書を適切に処理できていない可能性があります。
2. **セキュリティソフトウェアまたはファイアウォール**: これらが証明書の検証プロセスを誤って妨げている場合があります。

### 解決策

#### 方法1: npmの設定を調整する

1. **SSL証明書の検証を無効にする**:
    SSL証明書のエラーを回避するために、npmの設定で`strict-ssl`を無効にする方法があります。以下のコマンドを実行してください。

    ```bash
    npm config set strict-ssl false
    ```

    これにより、npmはSSL証明書の検証を行わなくなります。これは、特定の問題を解決するための一時的な解決策としてのみ使用してください。

2. **設定を元に戻す**:
    問題の解決後、セキュリティを確保するためには、設定を元に戻すことが重要です。以下のコマンドでSSL証明書の検証を再び有効にします。

    ```bash
    npm config set strict-ssl true
    ```
    問題の原因を特定した後は、セキュリティを保つためにこの設定を元に戻すことが重要です。

#### 方法2: 証明書を更新または再配置する

- **証明書の再インストール**: 使用しているシステムまたはサーバーでSSL証明書を再インストールすることで、証明書の問題が解決することがあります。
- **CA（認証局）証明書のインストール**: 不足している中間CA証明書をシステムにインストールすることも解決策の一つです。

#### 方法3: ネットワーク設定を確認する

プロキシやVPNが原因でこの問題が発生している場合、ネットワークの設定を見直し、必要に応じて調整することが有効です。

### セキュリティに対する注意

`strict-ssl`を`false`に設定することは、短期的な回避策としては有効ですが、長期的にはセキュリティリスクとなるため推奨されません。最終的には、証明書の問題を根本から解決することが最も重要です。

### まとめ

「UNABLE_TO_VERIFY_LEAF_SIGNATURE」というエラーは、SSL証明書の問題によって発生します。対策としては、npmの設定を調整する、証明書を更新する、ネットワーク設定を確認するなどがあります。エラー解決に向けて、セキュリティを損なわない方法を選ぶことが重要です。
微分先行型PID制御（PI-D制御）について

### はじめに
制御工学において、PID制御は最も基本的で広く使用されるフィードバック制御の一つです。しかし、従来のPID制御には、目標値が急変した際に操作量が急激に変化するという問題があります。これを「キック」と呼びます。微分先行型PID制御（PI-D制御）は、このキックを抑制するために開発された制御方式です。

### 微分先行型PID制御の原理
微分先行型PID制御は、従来のPID制御の応用形であり、目標値に対する実値の微分を微分項として加えることが特徴です。通常のPID制御では、目標値と実値の偏差に応じてゲインをかけますが、微分先行型PID制御では、実値を微分項への入力とします。これにより、目標値が急変した時に微分項の影響による操作量の急変を抑えることができます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3700895/71ce9df1-0aa0-ec87-75b1-79b287ae9d6b.png)


##### Q. 目標値がステップで上昇した場合、どういった流れになるのか？
目標値がステップ状に上昇した場合の微分先行型PID制御（PI-D制御）の応答について、順を追って説明します。

**目標値のステップ変化:** 目標値が突然上昇すると、制御システムはこの変化に応じて出力を調整しようとします。
**比例応答 (P項):** 目標値と実測値の差（偏差）に比例して、制御出力が増加します。この応答は即時的で、目標値の変化に迅速に反応します。
**積分応答 (I項):** 時間とともに偏差が続くと、積分項が累積され、制御出力をさらに調整して偏差をゼロにしようとします。
**微分応答 (D項):** 微分先行型PID制御では、実測値の変化率（微分）に基づいて制御出力を調整します。目標値のステップ変化による影響は、実測値を通じて間接的に微分項に影響を与えるため、通常のPID制御に見られるような急激な出力変化（微分キック）は発生しません。
**制御出力の調整:** 微分先行型PID制御は、目標値の急激な変化に対しても、制御対象の遅れを考慮しながら、より滑らかな制御出力の変化を実現します。これにより、システムに衝撃を与えることなく、目標値に対する追従性を向上させます。
**安定化:** 制御出力が調整された後、システムは新しい目標値に対して安定した状態を維持しようとします。積分項が偏差をゼロにするように作用し続け、比例項と微分項は新しい状態に適応します。

以上のステップにより、微分先行型PID制御は目標値のステップ変化に対して、過剰な応答を抑えつつ、効果的にシステムを制御します。この制御方式は、特に目標値の変化が頻繁に行われるシステムや、急激な変化に対して滑らかな応答が求められる場合に有効です。



### 微分先行型PID制御の利点
微分先行型PID制御の最大の利点は、目標値の急激な変化に対しても、操作量の急変を抑制できることです。これにより、システムへの負担を減らし、より安定した制御を実現することが可能になります。また、ディジタル制御においては、追加のコストなしで従来のPID制御と切り替えて使用することができます。

### まとめ
微分先行型PID制御は、従来のPID制御の問題点を解決するための有効な手法です。目標値の急変に対しても安定した制御を実現し、制御システムの性能向上に寄与します。今後も、この制御方式の応用範囲は広がっていくことが期待されます。







# 参考文献
https://www.maizuru-ct.ac.jp/control/kawata/study/book_lego/pdf_files/doc_003-1_PID.pdf

https://taketake2.com/N22.html
目標

## 現状
インフラエンジニア サーバー運用保守のみ
サーバー構築未経験

## 取得資格
LPIC level1
AWS SAA

## 目標
CCNA取得
業務で使用中のサービスをOutput
Python学習
Terraform学習

## １年後の目標
サーバー構築業務に携わる
平均年収を超える
ターミナルソフト【TeraTerm】を使いこなす

# Teratermとは

Teratermは、サーバやネットワーク機器にSSH・コンソール接続する際に使用するターミナルソフトである。cmdとか、windowsTerminalと同じような扱い。

https://teratermproject.github.io/

# インストール方法

gitのページにアクセスし、最新版のexeファイルをインストールし、実行する。

https://github.com/TeraTermProject/teraterm/releases

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/536d721c-558f-eb65-fe4c-bb8a22e51f7e.png)

その後、流れに沿ってセットアップを行う。細かい所は他の方が細かく記載しているのでこちらを参照に。

https://www.hc.itc.keio.ac.jp/ja/net_out_teraterm5.html


# Teratermの基本操作

- ホスト
    - 接続するIP又はドメイン
- TCPポート
    - 接続するポート。基本は22だと思うが変更している場合は変える事。
- サービス
    - 基本はSSHのはず。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/27c19450-8d83-ce2c-8bb2-cfa273733b35.png)

設定して、OKを押す。

- ユーザ名、パスフレーズの部分はそのサーバで設定しているものを選ぶ。
- 鍵認証を使っている場合は、RSA～～鍵を使うを選ぶ。
- ネットワーク機器など、パスワードがうまく入力できないものはキーボードインタラクティブを使う。
- Pagentは、秘密鍵をエージェントを起動して使用している場合に押す。
- エージェント転送は、鍵認証でアクセスするサーバから、さらに鍵認証で踏み台接続するときに使用する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/b2d82d29-45c2-2785-8710-37fe84db4c9b.png)

# 使えると便利な機能

## Scp
`ファイル/SSH SCP`から、SCP転送ができる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/42a1626d-85ef-7fcd-fba9-af59cb35e947.png)

- 上の部分は、ローカルの手元PCから、今ログインしているサーバのToにあるパスに送付する。
- 下の部分は、Fromにあるパス（今ログインしているサーバのパス）から、手元PCの`To`の部分に送付する。（ダウンロードする。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/ef447b17-18b2-3c4f-a8ab-7ac977a146db.png)

## SSH転送
いわゆるポートフォワード。接続先のサーバからhttpsなどのポートにアクセスして、localhostでアクセスして確認するというような事を行う時に使える。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/3d9fdd22-39fe-16e7-1c14-91d8511e8a3e.png)

画像の内容をコマンドで再現するとこの形。
```
ssh jst-dito@192.168.162.243 -L 45123:192.168.150.11:443
```

この時、ChromeやEdgeなどで`https://localhost:45123、https://127.0.0.1:45123`を入れる事で該当のサイトにアクセスできる。

## ログ
その他の設定から、`ログ`を選ぶと指定のファイルに操作しているログを保存する事ができる。また、タイムスタンプを設定すると、日時もログに追加して保存できる。
`show running config`のようなネットワーク機器のログ表示をさせるときはタイムスタンプを消して実行する事。

設定を変更した場合は、`ok`の後に`設定の保存`を押す事。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/bdf12ef9-cdee-feb9-7162-878fb04e58ef.png)

# シリアルポート

シリアルポートを接続した際、`スピード`の設定を変更する必要が出る場合がある。(症状としては、全く文字が表示されない。)
デフォルトは9600であるが、ネットワーク機器によっては`19200`などにする必要がある。*どの速度が正しいかは各ネットワーク機器によって変わる。変更したらOKを押す事。

# 接続の複製
新しいTerminalのウィンドウを出すには`Alt + N`を押す。
これで、新しいホストに接続するために別のウィンドウを出す事ができる。
すぐにウィンドウを出す事ができて便利。

また、同じホストであれば、`Alt + D`でセッションを複製できる。
別のウィンドウで、同じホストに接続して状態を見るという時に便利。

なおこの時、**認証情報の入力を省略する事ができる。**いちいちパスワードやユーザ名を指定して接続する事がないので便利。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/a0fc05ce-2bbf-5237-b350-656d98997615.png)

# ブロードキャストコマンド

複数のターミナルで同じ操作を同時に行うことができる。操作を間違えたくない時などに有効。

https://qiita.com/rubytomato@github/items/59d4515cf051fcf31732
【Laravel】AWS SESを使ってメールを送信

# 環境
・Laravel10
・Windows11

# ドキュメント
https://readouble.com/laravel/10.x/ja/mail.html

https://laravel.com/docs/10.x/mail

# SES準備
SESを利用するため、IDを作成。
※今回はメールアドレス使用。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784589/749119c3-04d9-9be2-7333-ce03a5a9d574.png)

# SESドライバのインストール
SESドライバを使用するために、Amazon AWS SDK for PHPをインストール。
```
composer require aws/aws-sdk-php
```

# 環境設定
```php:.env
MAIL_MAILER=ses
MAIL_HOST=mailpit
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="送信元のメールアドレス"
MAIL_FROM_NAME="${APP_NAME}"

AWS_ACCESS_KEY_ID=アクセスキー
AWS_SECRET_ACCESS_KEY=シークレットアクセスキー
AWS_DEFAULT_REGION=ap-northeast-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
```

# Mailable作成
メール送信機能を簡単に実装するための`Mailable`クラスを生成。
```
php artisan make:mail MailTest
```
`app/Mail`配下にファイルが生成される。

## 実装
```php:MailTest.php
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class MailableObject extends Mailable
{
    private $email;
    /**
     * Create a new message instance.
     */
    public function __construct($email)
    {
        // コントローラーやバッジから受け取る値
        $this->email = $email;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function envelope(): Envelope
    {
        return new Envelope(
            to: $email,
            subject: 'メール送信テスト'
        );
    }

        public function content()
    {
        return new Content(
            // メール文のテンプレで使うviewファイル
            markdown: 'emails.test_content'
        );
    }
}
```

# view作成
Mailableで指定した`emails.test_content`を作成。
※今回はマークダウン形式
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784589/b85ed491-3d16-a666-2041-dbdf0e1841a6.png)

```PHP:emails/test_content.PHP
# Test email

### Thank you very much for visiting our office yesterday.
```

# メール送信処理
コントローラーやバッジに記述。
※今回はリスナに記述。

これで`MailableObject`が呼び出され、メールが送信される
```PHP:
Mail::to('送信先メールアドレス')->send(new MailableObject('Mailableのコンストラクタに渡す値'));
```

# 参考記事
https://qiita.com/hiroaki-u/items/986b523998d5e415fc4d
SLAMTEC PRLIDAR C1M1-R2のビューワーRoboStudioの使い方

単純すぎる話なのですが、何度も同じポイントにハマったので、同じところでつまづきそうな人の助けになれば本望です。

# SLAMTEC RPLIDAR C1M1-R2とは

![SLAMTEC RPLIDAR C1M1-R2](https://mm.digikey.com/Volume0/opasdata/d220001/medias/images/5752/MFG_101090061.jpg)
比較的小型で安価（現時点で14,000円程度）、性能もそこそこな二次元のLiDAR（レーザー測距センサー・測域センサー）です。
日本では[マルツ](https://www.marutsu.co.jp/GoodsDetail.jsp?q=C1M1&salesGoodsCode=46728700&shopNo=3)や[DigiKey](https://www.digikey.co.uk/en/products/detail/seeed-technology-co-ltd/101090061/22109373)、SeeedStudioからの海外発送などで購入できます。小型移動ロボットの研究開発やROSなどの実験に使えそうです。
自分も[つくばチャレンジ](https://tsukubachallenge.jp/)へ出る機体に搭載する目的で購入しました。

![IMG_5923.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33040/8a85edfe-30c2-c2d3-cf92-53278fe5fa9b.jpeg)


購入後ビューワーを使おうとしたところいきなり表示できずマニュアルを読んでも解決できなかったためハマりましたが、非常に単純でした。

# ビューワーRoboStudioのハマりポイント

RPLIDAR C1M1-R2はRoboStudioというWindows用の標準ビューワーを使うと簡単に点群を表示することができます。
[ダウンロードサイト](https://www.slamtec.ai/downloads/)でDTOF LIDARを選択し、下のほうにファイルがあるので、ダウンロードして([7-Zip等のアプリ](https://7-zip.opensource.jp/))を使って解凍します。基本的な使い方はマニュアル通りで、Windows用の[CP210x VCPドライバ](https://jp.silabs.com/developers/usb-to-uart-bridge-vcp-drivers)などもインストールしておきます。

ハマりポイントはデバイスをつないで上のメニューから「Lidar」を選んでも**左側のリストに何も表示されない**点です。
そこで**左側のリスト上で右クリック**すると（これに気付くまでにいろいろ試行錯誤してしまってハマりました）、メニューが出てくるので「Manual Connnect to Lidar...」を選びます。

![スクリーンショット 2024-04-21 125031.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33040/bc7c53ae-66be-7742-e29f-d9c25709b6b8.png)

すると接続したデバイスを指すCOMポートが表示されるので、選択します。

![スクリーンショット 2024-04-21 124902.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33040/ec9dc8ac-0adc-8001-82d2-0d22e16880df.png)

すると無事にPRLIDAR C1M1-R2が認識され、再生ボタンを押すと点群が表示されます。

![スクリーンショット 2024-04-21 132226.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33040/c875c6c0-83df-490c-79ec-76da29bc88a4.png)

以上で問題なく使用できることが確認できました。

上記のハマりポイントはマニュアルではわからなかったため、同じようなところで躓いてしまいそうな方のお役に立てれば幸いです。
Python で heapq.nlargest/nsmallest を使う

# 動機

ある配列の top largest/smallest K elements を得たい場合、

- sort だと time = O(NlogN)かかってしまう。
- 自前で heapを使って実装するのはめんどくさい。

# 仕様

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534442/d21bbedc-8e85-38dd-31fb-c4a0fa37e163.png)

TODO: 返される配列はkeyでソートされてそう？
TODO: タイの場合はどのelementが選ばれるのだろう？元のindexが最も小さいものか？


# 使ってみた

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534442/8cff9fea-3fdb-3776-e0c8-809ed5b5b64a.png)


# Ref.

https://docs.python.org/3/library/heapq.html
iTerm2 でタブの番号をプロンプトに表示する

iTerm2 で、タブの番号をプロンプトに表示します。

(注意) 
iTrem2 のタブに表示される tab numbers は、タブの追加・削除をする度に、⌘1 からrenumber されますが、これは、タブを開いた順番の番号です。
私にとっては、こちらの方が都合が良いです😊

```bash
# .bash_profile

# iterm2 の ITERM_SESSION_ID を取得
SESSION_ID="$ITERM_SESSION_ID"

# 例: ITERM_SESSION_ID=w0t0p0:EE4A9197-47EB-4369-B046-0101A23BE89D
# ":" で分割して配列に格納
IFS=':' read -ra SESSION_PARTS <<< "$SESSION_ID"

# 配列の要素を取得して必要な部分を抽出
PART_WITH_T1="${SESSION_PARTS[0]}"

# "t0" の部分を抽出
T_PART="${PART_WITH_T1#*t}"

# ":" で分割して "0" の部分を取得
IFS='p' read -ra T_PARTS <<< "$T_PART"
TAB_NUM="${T_PARTS[0]}"

export PS1="\[\e[0;34m\]\h \[\e[0;36m\][$((TAB_NUM + 1))]\[\e[0m\] \w:$ "
```
cscの作法 その497

# 概要
cscの作法、調べてみた。
DesktopMascotを、見つけたので、コンパイルしてみた。

# 参考にしたページ

https://github.com/MURA-omura/DesktopMascot

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/a88ab330-c3c5-f608-1467-bb45dd99c82e.png)

# 環境
- windows11

# コンパイル手順

```

>set PATH=C:\Windows\Microsoft.NET\Framework\v4.0.30319;%PATH%


>cd csc\DesktopMascot-master\DesktopMascot-master


>csc /out:mscot.exe  /target:winexe /r:DxLibDotNet.dll CameraDialog.cs DisplayDialog.cs Form1.cs Form1.Designer.cs ModelDialog.cs MotionDialog.cs PhysicDialog.cs Program.cs 

>mscot
```

以上。
トランプゲームでのカード強さ判定の仕組みと連想配列の活用

現在エンジニアになるためにプログラミングの学習をしています。

それにあたって、今回は戦争ゲームをPHPで作成してみました。
作成にあたって難しかった点について共有します。

# ゲームの流れ
1.カードの生成とシャッフル: ゲームの最初にトランプカード(52枚)を生成し、シャッフルします。

2.プレイヤーへのカード配布: 生成したカードをプレイヤーに配布します。

3.ゲームの開始: プレイヤー同士でカードを比較し、数字が大きいカードを所持している人が勝者となり、相手のカードを獲得する事ができます。

4.1-3までの工程をカードが無くなるまで行い、最終的に獲得枚数が多い人が勝者になります。

# 作成にあたって難しかった点。
トランプの強さを比較する点です。

#### 連想配列を用いたカードの強さ表現
トランプゲームにおいて、カードには数値と強さがあります。例えば、「A（エース）」や「K（キング）」は特定のゲームルール下では他のカードよりも強いとされることがあります。そのため、カードの強さを数値で表現する必要があります。

以下は、カードの強さを連想配列で表現する例です。
```
$values = ['A' => 14, 'K' => 13, 'Q' => 12, 'J' => 11, '10' => 10, '9' => 9, '8' => 8, '7' => 7, '6' => 6, '5' => 5, '4' => 4, '3' => 3, '2' => 2]; 
```
この連想配列では、カードの値（A, K, Q, ...）をキーとし、そのカードの強さを表す数値を対応付けています。これにより、プログラム上でカードの強さを簡単に判定することができます。

#### 連想配列の降順ソートと最大値の取得
ゲーム中には、プレイヤーが持つカードの中で最も強いカードを特定する必要があります。その際には、連想配列を強さの降順にソートし、最大値を取得することが便利です。
```
arsort($values);// 連想配列を降順にソート
$maxValue = reset($values); // 最大値を取得
$keys = array_keys($values); // ソート後のキー配列を取得
```

このように連想配列をソートし、最大値を取得することで、プレイヤーが持つカードの中で最も強いカードを素早く特定することができます。これにより、ゲームの進行や勝敗の判定がスムーズに行えました。

以上で今回は終わります。
これからも学んだことを投稿していこうと思います！
yarn create next-app --ts + Storybook v8 で Tailwind CSS が適応されない問題への対処

# 問題の経緯
```console
$ yarn create next-app --ts      # Typescript を使用する設定で Next.js プロジェクトを作成
$ # 
$ # ↑ Tailwind CSS を使用するか？：Yes を選択
$ #
$ # プロジェクトのディレクトリに移動
$ # 
$ yarn set version stable        # Storybook v8.x.x は yarn v4.x.x 以上が前提らしい
$ yarn dlx storybook@latest init # このコマンドは yarn v1.x.x では構文が違うと言われる
```
...として環境構築すると、`yarn dev` で立ち上がる Next.js 開発環境内では Tailwind CSS がバッチリ適応されるのに対して、
__Storybook 内では Tailwind CSS スタイルが適応されない__ という問題に当たりました。

一応、以下の 2つ の Tailwind CSS のための設定は完了していました。
```Typescript:.storybook/preview.ts
import type { Preview } from "@storybook/react";
import '../src/app/globals.css'; // ← ### Tailwind CSS 用の設定を追加 ###

const preview: Preview = {
  parameters: {
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/i,
      },
    },
  },
};

export default preview;
```
```Typescript:tailwind.config.ts
import type { Config } from "tailwindcss";

const config: Config = {
  content: [
    "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/stories/**/*.{js,ts,jsx,tsx}", // ← ### Storybook 用の設定を追加 ###
  ],
  theme: {
    extend: {
      backgroundImage: {
        "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
        "gradient-conic":
          "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
      },
    },
  },
  plugins: [],
};
export default config;
```

# 解決方法

2024/04/28：別の問題（Next.js へのスタイルが適応されなくなる）に気付いたので修正
~~自動生成されている **`postcss.config.mjs` の拡張子を .mjs → .ts に変更する**と、Tailwind CSS が Storybook 内で適応されるようになります。~~

2024/04/28：拡張子が js だと Next.js のコンパイル時に ESM 形式じゃないとかエラーが出るので再度修正......

```Typescript:postcss.config.mjs → .cjs に拡張子変更
// 内容を以下のもので置き換え
module.exports = {
  plugins: {
    tailwindcss: {},
  },
};
```
上記の様に、拡張子変更と内容の修正でスタイルが Next.js にも Storybook にも適応されるようになりました！

### 解決方法を見つけるまでの道筋
[先人の記録](https://zenn.dev/jnpi/articles/a47caef6e7eb87) を調べていると、プロジェクト生成時に作られる `postcss.config.mjs` ファイルの拡張子を .mjs → .js に変更すると良い、とのことでした。

今回は Typescript 使用設定をしていますので、`postcss.config.mjs` ファイルの中身が Typescript 仕様になっています。特に `export` 文周りの構文が気になりました。

今回のケースでは拡張子変更だけでは問題が解決しませんでしたが、
上記の様に、拡張子を `.cjs` に変更 + export 文を修正すると動作することに気付きました。

~~その後ファイルの拡張子を `.ts` に変更するだけ、という手段を試したところ動作しましたので、解決策として紹介することにしました。~~
この方法をとると、Next.js 側にスタイルが適応されなくなってしまいました...

### 試行錯誤の記録
最初は Storybook の webpack 周りの設定を疑って、
```Console
$ yarn dlx storybook@latest add @storybook/addon-styling-webpack
```
として `.storybook/main.ts` も編集し、Storybook の webpack 設定をカスタマイズすることも試しましたが効果がありませんでした...

### 余談
対処法を見つけた後、「.mjs ってなに？全部 .ts にしよう！」と思って `next.config.mjs` の拡張子も `.ts` に変更したら、Storybook 起動時（`yarn storybook` 実行時）に「その拡張子には対応していない」旨のエラーが出ました...

## 感想
恐らくフロントエンド技術の進化が速いためでしょう、各種ツールの最新版を組み合わせて使うと特にですが、公式ドキュメントがカバーしていない問題に当たる確率が高いように思います。
（C++ や .NET に触っていた時間が長いのでそう感じるのかも...？）

Storybook や webpack 周りの挙動がデフォルトでどうなっているか、`.mjs` 拡張子はこの場合適切なのか等気になったので、このネタで OSS コミュニティに貢献出来たら嬉しいです。
Issue があるか確認してみようかな...

https://github.com/storybookjs/storybook/issues/26869

↑ 近いものがありましたが、思ったのとちょっと違うことが書いてあるような...
もう少し読み込んでみます。
CSS入門（最近覚えたこと）

## 左の余白を消す方法
CSSを書いていて左の余白を消したいときは下記のコードを使う
```
タグ名{
        padding-left:0;
      }
```

## 幅と高さを指定する
幅と高さを指定したいときはwidth(幅)とheight（高さ）を使う
```
タグ名{
        width:50px;
        height:50px;
      }
```
## リストのデフォルトの余白を取り除く
一般的なブラウザでは、リストの周り（外側）に余白がつくそうだがそれを取り除きたい場合はmarginを使う
（marginは外側の余白で、paddingは内側の余白なので使い方には気を付ける）
→サルワカさんのがわかりやすい
https://saruwakakun.com/html-css/basic/margin-padding
```
ul {
        margin: 0 0 5px 0;
        (margin: 上 右 下 左;)
      }
```
## CSSで横並びにする方法
単純な横並びであればflexboxを使う
（floatというものもあるが、使い分けはこれから勉強します）
```
親タグ　{
        display: flex;
      }
```
$_FILES

## はじめに

$_FILESについて整理する。

### $_FILES

アップロードファイルの情報を取得する。

```
$_FILES['要素名']['情報名']
```

要素名は<input type="file">タグのname属性で指定された値
情報名には以下のような値を設定できる。

name: ファイル名
type: コンテンツタイプ
size: データサイズ
tmp_name: サーバー上に仮保存された時の一時ファイル名
error: アップロード時のエラーコード


### アップロードの実処理

move_uploaded_file関数で一時フォルダーから本来の保存先にファイルを移動する。
(PHPでは、クライアントからファイルを受け取ると暫定的に仮のフォルダーに保存している。)
name, temp_nameキーから、それぞれオリジナルのファイル名と一時的なファイルの保存先を保持している。


```php:move_uploaded_file.php
$src = $_FILES['upfile']['tmp_name'];
$dest = $_FILES['upfile']['name'];
if (!move_uploaded_file($src, $dest)) {
    echo "Failed to upload file";
}
```
Chat GPTを使ってアプリ開発のアイデア(案)を考えてみる

## はじめに
いいアプリのアイデアがないか考えている方へ向けて書いてます。

アプリのアイデアを出すのに活用すべきなのは、やはりAI。Chat GPTです。

自分もアイデアに悩んでいたのですが、Chat GPTを活用したところ良いアイデアがもらえて、無事作りたいアプリを決めることができました。

ぜひ、アプリ案にお悩みの方は、
ChatGPTを有効に活用していただきたいと思ってます。

https://chat.openai.com/

## やり方

### 1\.簡単に考えて、幅を狭めていく
漠然と好きなことや趣味からこんなアプリあったらいいなと考えてから範囲を絞ります。

例えば、料理のアプリを考えてみます。

料理のアプリといっても
- レシピ検索・登録・共有
- 買い物リスト
- 栄養情報
- レシピ共有機能
 
など幅広くアイデアが考えられます。

これではアプリの方向性が定まらないので、
料理でも何の分野にしたいか考えると良いと思います。

その分野を決めるのにもChat GPTを活用するといいかもしれません。

以下Chat GPT3.5に聞いてみた結果になります。

```Chat GPT:質問
「料理のアプリだと分野は何が考えられますか？」
```

```Chat GPT:回答
料理のアプリは、さまざまな分野に分類することができます。以下にいくつかの分野を挙げてみます：

レシピ管理アプリ: ユーザーが自分のレシピを管理し、お気に入りのレシピを保存できるアプリです。また、買い物リストの作成やレシピのカテゴリー分けなどの機能も備えています。

料理動画アプリ: 料理の手順を動画で視覚的に学ぶことができるアプリです。料理家や料理愛好家が自分のレシピや料理のテクニックを動画で共有します。

レシピ共有コミュニティアプリ: ユーザーが自分のレシピを共有し、他のユーザーのレシピを見ることができるアプリです。ユーザー同士が交流し、レシピのアイデアやテクニックを共有します。

栄養管理アプリ: ユーザーが摂取したカロリーや栄養素を追跡し、健康的な食事を計画するためのアプリです。栄養バランスや食事の目標を設定し、それに基づいてレシピを提案することもあります。

食材管理アプリ: 冷蔵庫や食品庫の在庫を管理し、食材の賞味期限や使用期限を管理するアプリです。レシピに必要な食材を把握し、無駄なく使い切ることができます。

食レポアプリ: ユーザーがレストランやカフェで食べた料理のレビューを投稿し、他のユーザーと共有するアプリです。レビューには写真や評価、感想などが含まれます。

これらは一部の分野の例ですが、料理のアプリはさまざまな側面やニーズをカバーすることができます。
```

かなりの機能を提案してくれることがわかったと思います。

こんな感じで趣味の中の分野を絞ります。

Chat GPTに質問する際には、上記のままでも良いですが、
料理の部分を自分の趣味などに置き換えて、質問の仕方も変えたりしていただければと思います。

### 2.そこに「独自機能が欲しい」と聞く

分野を提案してもらったら、
そこに「**独自の機能が欲しい**」と聞くとさらにアイデアがもらえます。

では、仮に「レシピ共有できる料理のアプリ」を作るとします。
以下はChat GPT3.5に聞いてみた結果になります。

```Chat GPT:質問
レシピ共有ができる料理のアプリを作りたいです。
他のアプリと被らないように独自の機能を入れたいので、アイデアを考えてください。
```
```Chat GPT:回答
素晴らしいアイデアです！料理のアプリでレシピ共有機能を持つことは素晴らしい方法ですね。
他のアプリとの差別化のために、以下の独自の機能を提案します：

AIレシピマッチング機能：ユーザーが特定の食材や料理の写真をアップロードすると、AIがそれに最も適したレシピを提案する機能です。これにより、ユーザーは材料を有効活用しながら新しい料理を試すことができます。

地域別レシピマーケット：ユーザーが特定の地域の伝統的な料理や地元の食材を共有し、その地域の料理文化を広めることができる機能です。ユーザーは他の地域の料理を発見し、交流することができます。

料理交換プラットフォーム：ユーザーが自分の得意な料理を提供し、他のユーザーと交換することができる機能です。これにより、ユーザー同士がお互いのレシピを試すことができます。

グループ料理イベント：ユーザーが友人や家族と共有の料理イベントを計画し、一緒に料理を楽しむことができる機能です。グループ内でレシピを共有し、料理のテーマや競争を設定することができます。

レシピストーリー機能：ユーザーがレシピに関連するストーリーや思い出を共有することができる機能です。料理の背景や特別なイベントで作られた料理についてのエピソードを共有することができます。

これらの独自の機能を組み合わせることで、料理のアプリを他と差別化し、ユーザーにより豊かな料理体験を提供することができます。
```
こちらもなかなか良い回答をくれました。

こんな感じで、「**独自の機能**」や「**オリジナル機能**」を考えてくださいみたいに質問すると他のアプリには無さそうなものを提案してくれます。

もし良いアイデアがもらえなかったら、「ほかにも10個くらい考えてください」みたいに質問するとさらにアイデアがもらえるので、試してみてください。

## まとめ 
Chat GPTを活用して

「**趣味(好きなこと)**　+ **分野**　+ **独自機能**」

これらを明確にすることで、作りたいアプリを決めやすくなります。

それでも他と被る可能性が0ではないので、競合を調査したり、自分で考えたり、他の人からアドバイスをもらいながら、アプリのアイデアを決めていくことをオススメします！

https://chat.openai.com/
【Rust】数値のオーバーフローへのハンドリングまとめ

## 動機

まずは以下のコードを見てみましょう。

```rust
fn main() {
    let mut num1 = 10_u8;
    num1 *= 80;
    println!("{}", num1);
}
```

`num1` に関してはu8が指定されているのでnum1の最大値は255です。
しかしこのコードでは10 * 80の演算をしているので、結果は800となり、オーバーフローするでしょう。

実際に実行してみましょう。

```
cargo run
```

```
thread 'main' panicked at src/main.rs:3:5:
attempt to multiply with overflow
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```

しっかりエラーはいてくれました。
では、これをリリースモードで実行するとどうなるでしょう。

```
cargo run --release
```

```
32
```

今度はpanicせずになぞの数字32が得られました。
実はこれは、800 % 256した結果です。

こんな感じで、Rustは整数型のオーバーフローをエラー検出できません。
しかし、実際にはこれをハンドリングする必要があると思いますので
今回はこれをまとめてみました。

## saturating

まずはsaturatingです。
saturatingはその型の最大値を超える値は 最大値に張り付きます。

```rust
fn main() {
    let num1 = 10_u8;

    // オーバーフローしないときはそのままの結果が得られる
    assert_eq!(num1.saturating_mul(10), 100);
    // オーバーフローする場合は型の最大値に張り付く
    assert_eq!(num1.saturating_mul(80), 255);
}
```


## checked

checkedは結果がOption型で渡されます。
オーバーフローするときはNoneが返されます。

```rust
fn main() {
    let num1 = 10_u8;
    // 結果がOption型で渡される。
    // オーバーフローしないときはSomeに、
    assert_eq!(num1.checked_mul(10), Some(100));
    // オーバーフローするときはNoneが返される
    assert_eq!(num1.checked_mul(80), None);
} 
```


## wrapping

wrappingはその型の最大値 + 1で割ったあまりを返します。
cargo run --release したときのデフォルト挙動はこれなのかな？と勝手に理解していますが、
裏どりしてません。

```rust
fn main() {
    let num1 = 10_u8;
    // オーバーフローしないときはそのまま
    assert_eq!(num1.wrapping_mul(10), 100);
    // オーバーフローするときはその型の最大値で割ったあまりになる
    let amari = 10_usize * 80 % 256;
    assert_eq!(num1.wrapping_mul(80), amari as u8);
} 
```

## overflowing

タプルで返され、1-indexedで2番目の要素にtrue, falseでOverflowしているか否かが返されます。

```rust
fn main() {
    let num1 = 10_u8;
    // オーバーフローしないときは(戻り値).1にfalseが渡される
    assert_eq!(num1.overflowing_mul(10), (100, false));
    // オーバーフローするときは(戻り値).1にtrueが入る
    let amari = 10_usize * 80 % 256;
    assert_eq!(num1.overflowing_mul(80), (amari as u8, true));
} 
```



## おわりに
今回は掛け算の例で紹介したのでsuffixが_mulでしたが、足し算(_add)や引き算(_sub)等の演算にもちゃんと対応しています。

ここをちゃんとハンドリングしてないと、「なにもしていないのに壊れた」になりかねないので注意しておくと後々役立つことがあるかもです。
【備忘録】NullPointerException との付き合い方

<h1>【備忘録】NullPointerException との付き合い方
<p>今まで、NullPointerExceptionが出てきたときは何となくで対応していました。
<p>業務をこなしていく中で段々と何について怒られているのか分かってきたので、整理もかねて纏めていきます。

<h2>NullPointerException とは</h2>

NullPointerException とは『NullPointerException は、java.lang. RuntimeException クラスのサブクラスで Java プログラムの実行中に参照型変数を参照しようとした時に null 値であった場合、スローされるランタイム例外です。』とのことです。

要は、nullのものからはgetterとかは使えませんよ～みたいなことです。
次で具体的にまとめていきます。


<h2>具体例</h2>
例えば、下記のようなクラス型の配列を使用するとします。

```java:sampleClass.java
public class nullpo {
    public static class inClass {
        private Integer id;
        private String subject;
    }
    
    public List<inClass> nullpoList;    
}
```
このListの中身がきちんと入っている場合を考えます。

```java:mainClass
public class NullPointerExceptionSample {
  public static void main(String[] args) {

      nullpo nullpo = new nullpo();
      List<inClass> nullpoList = new ArrayList<>();
      inClass inClass = new inClass();
      // List1つ目
      inClass.setId(1);
      inClass.setSubject("sample");
      nullpoList.add(inClass);

      // List2つ目
      inClass.setId(2);
      inClass.setSubject("sample2");
      nullpoList.add(inClass);

      // Listの中身を入れる
      nullpo.setNullpoList(nullpoList);

```
中身が入っている状態で、参照するコードを書きます。

```java:mainClass
        // Listを取得
        List<inClass> List = nullpo.getNullpoList

        // Listの中身を取得
        int Id = List.get(1).getid();
        String subject = List.get(1).getSubject();
```
この場合は、どのgetterも問題なく処理することが出来ます。

次にListがnullの場合を考えます。

```java:mainClass
public class NullPointerExceptionSample {
  public static void main(String[] args) {

      nullpo nullpo = new nullpo();
      List<inClass> nullpoList = new ArrayList<>();
      nullpoList = null;

      nullpo.setNullpoList(nullpoList);
```
Listがnullの状態で、中身を参照します。

```java:mainCalss

      // Listを取り出す⋯⓵
      List<inClass> List = nullpo.getNullpoList();

      // Listの中身を取り出す⋯⓶
      int id = List.get(1).getId();
      String subject = List.get(1).getSubject();
```

この場合⓵は正しく処理することが出来、nullが変数Listに代入されます。
しかし、⓶の処理ではnullポで落ちてしまいます。

<strong>つまり、参照した結果「null」が返ってくる場合はOkなのですが、
「null」事態を参照することはNGですよ、ということです。

nullポをネット上で調べると、nullのString型変数の長さを参照する例が多かったです。
ここで書いたことと言ってることは同じなのですが、座学をすっ飛ばして業務に入った自分自身はnullを参照するまでのステップが多くなるとそのシンプルな理屈と結び付けられなかったので、今回はあえて少し複雑目な例を考えてみました。

<h2>まとめ</h2>
今回はNullPointerExceptionを纏めていきました。

開発中にnullポが発生してしまったら、以下の2つの観点で対応が必要かと思います。
1つ目が、仕様上は参照元がnullにならないはずである場合です。この場合はテストデータや、そこに至るまでのプログラムコードが間違っていることが考えられるので、それについての見直しが必要です。
2つ目が、仕様上その参照元がnullになる可能性を孕んでいる場合です。その場合、nullポチェックを実装する必要があります。

nullポチェックについては次回、纏めていきたいと思います。
ulの「・」を消す方法

### ulの「・」を消す方法

ulの「・」を消す方法はとても簡単で下記のコードを書くと消せる
```
ul {
    list-style:none
    }
```

[before]
![スクリーンショット 2024-04-21 133726.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3669426/d270e115-df32-f941-088c-c72960cb5035.png)

[after]
![スクリーンショット 2024-04-21 133709.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3669426/7a60e10b-f953-7be6-728c-1983c3ee0de9.png)
Todoアプリ(Java)

chatGPTにJavaで作らせたTodoアプリです。以下の３つのソースファイルを作ります。

~~~java:TodoItem.java
public class TodoItem {
    private String task;
    private boolean completed;

    public TodoItem(String task) {
        this.task = task;
        this.completed = false;
    }

    public void complete() {
        this.completed = true;
    }

    public boolean isCompleted() {
        return completed;
    }

    @Override
    public String toString() {
        return (completed ? "[完了] " : "[未完了] ") + task;
    }
}
~~~

~~~java:TodoList.java
import java.util.ArrayList;
import java.util.List;

public class TodoList {
    private List<TodoItem> items;

    public TodoList() {
        this.items = new ArrayList<>();
    }

    public void addTodoItem(String task) {
        TodoItem newItem = new TodoItem(task);
        items.add(newItem);
    }

    public void completeTodoItem(int index) {
        if (index >= 0 && index < items.size()) {
            items.get(index).complete();
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < items.size(); i++) {
            sb.append(i).append(": ").append(items.get(i).toString()).append("\n");
        }
        return sb.toString();
    }
}
~~~

~~~java:TodoApp.java
import java.util.Scanner;

public class TodoApp {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        TodoList todoList = new TodoList();
        System.out.println("ToDoアプリへようこそ!");

        while (true) {
            System.out.println("\n[操作] 1: タスク追加, 2: タスク完了, 3: リスト表示, 0: 終了");
            System.out.print("操作を選択: ");
            int operation = scanner.nextInt();
            scanner.nextLine(); // 改行文字を消費

            switch (operation) {
                case 1:
                    System.out.print("追加するタスク: ");
                    String task = scanner.nextLine();
                    todoList.addTodoItem(task);
                    break;
                case 2:
                    System.out.print("完了するタスクの番号: ");
                    int index = scanner.nextInt();
                    todoList.completeTodoItem(index);
                    break;
                case 3:
                    System.out.println("\nToDoリスト:");
                    System.out.println(todoList);
                    break;
                case 0:
                    System.out.println("アプリを終了します。");
                    return;
                default:
                    System.out.println("無効な操作です。");
                    break;
            }
        }
    }
}
~~~

ファイルが作れたらターミナルで以下のコマンドを実行すればいいです。
~~~
javac TodoItem.java TodoList.java TodoApp.java
~~~

~~~
java TodoApp
~~~
【Laravel】編集機能完了→詳細ページへのリダイレクト方法

## コード
```SpendingController.php
    public function update(Request $request, int $id)
    {
        $record = Auth::user()->spending()->find($id);

        $record->amount = $request->amount;
        $record->date = $request->date;
        $record->title = $request->title;
        
        Auth::user()->spending()->save($record);

        ⭐️これ↓
        return redirect()->route('spending.show', ['spending' =>  $id]);
    }
```

## 参考サイト
https://qiita.com/manbolila/items/767e1dae399de16813fb