OmniAICreator/Qiita-1.07M huggingface dataset:
自分へのご褒美管理ツール「ご褒美ToDo」をリリースしました！

こんにちは！趣味で個人開発をしているりょうやです。
自分へのご褒美を管理するアプリをリリースしました。

https://apps.apple.com/us/app/%E3%81%94%E8%A4%92%E7%BE%8Etodo/id6474063404

# アプリ概要
このアプリは自分へのご褒美の予定を立てることができるアプリです。
例えば、「今週末、好きなアーティストのライブを見に行こう」だったり「ゴールデンウィークは海外に行きたい！」など自分が好きなことの予定を立てると思います。

そういう予定をアプリで管理できたら
・自分へのご褒美が見えるので、日々のモチベーションが上がる
・ご褒美の予定を立てるのが楽しくなる
・忘れることが無くなるなどなどメリットが多いと思い開発しました。
自分も仕事で疲れた時などは、週末の遊びに行く予定などを思い出して乗り切る時もあります笑
日々の生活を豊かにするためにも必要なアプリだと思っています。

# アプリの機能
## ご褒美一覧機能

予定を立てたご褒美を一覧画面で確認することができます。
日付、ご褒美のタイトル、詳細など予定を一目で確認することができます。
「カレンダーに登録」をクリックすると、端末のカレンダーにも連携して登録することができます。

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597913/e7b5fb93-3dda-ff86-667b-5dd5dade48e3.png)


## カレンダー機能

カレンダー上でご褒美の予定を確認することができます。
計画的にスケジュールを組むことができます。

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597913/ec17095a-6578-c385-a91e-4af0d71b37d5.png)

## キーワード機能
ご褒美に関連したキーワードを一覧で表示しています。
例えば「アウトドア」が好きな人には『旅行』『テーマパーク』『ハイキング』など自分に合ったキーワードを探すことができます。

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597913/7b964426-e801-633f-ab82-f32344a1154b.png)

## ご褒美一覧履歴機能
過去に投稿したものについては、再度投稿をコピーして予定を立てることができます。
気に入った予定を立てるときはこの機能を使うと便利です。

## 最後に
このアプリを通じて人生が豊かになればと思います。
今更だけどios14でSearchBarを使いたい (SwiftUI)

# ios14でSearchBarを使いたい (SwiftUI)

ios15以降では[searchable](https://developer.apple.com/documentation/swiftui/view/searchable(text:placement:prompt:)-18a8f)を使えば、簡単に実装できそうです。ただし、ios14でも使いたい場合は自作する必要があります。

なので、[UISearchBar](https://developer.apple.com/documentation/uikit/uisearchbar)を元に作りました。

コピペで動きます:
```swift
import SwiftUI
import UIKit

struct SearchBarUIView: UIViewRepresentable {
    @Binding var text: String
    let placeHolder: String?
    init(text: Binding<String>, placeHolder: String? = nil) {
        self._text = text
        self.placeHolder = placeHolder
    }
    func makeCoordinator() -> Coordinator {
        Coordinator(text: $text)
    }
    
    func makeUIView(context: Context) -> UISearchBar {
        let searchBar = UISearchBar()
        searchBar.searchBarStyle = .minimal
        searchBar.delegate = context.coordinator
        if let placeHolder = self.placeHolder {
            searchBar.placeholder = placeHolder
        }
        return searchBar
    }
    func updateUIView(_ uiView: UISearchBar, context: Context) {
        
    }
    class Coordinator: NSObject, UISearchBarDelegate {
        @Binding var text: String
        public init(text: Binding<String>) {
            self._text = text
        }
        func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
            self.text = searchText
        }
        func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
            searchBar.showsCancelButton = false
        }
        func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
            searchBar.showsCancelButton = true
        }
        func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
            searchBar.showsCancelButton = false
            searchBar.searchTextField.endEditing(true)
            self.text = ""
            searchBar.searchTextField.text = ""
        }
    }
}

struct SearchBarView: View {
    @Binding var text: String
    public let placeHodler: String?
    var body: some View {
        SearchBarUIView(text: $text, placeHolder: placeHodler)
    }
}

struct SearchBarView_Previews: PreviewProvider {
    static var previews: some View {
        SearchBarView(text: .constant(""), placeHodler: nil)
    }
}

```

それぞれのアプリでの要件を満たせるようにDelegateの部分をいじってください。

使用例:
```swift
struct ContentView: View {
    @State var text: String = ""
    var body: some View {
        VStack {
            SearchBarView(text: $text, placeHodler: "hello")
            Spacer()
        }
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

```

結果:

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373549/f8a59576-0acf-7d4a-60ad-f55d92809756.png)


ちなみに、`UIKit`と`SwiftUI`の連携方法を毎回忘れるたびに、以下のチュートリアルを見てます😅
https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit
Rubyの「＊」記号ってなんなの、という疑問について調べた

Rubyにおいて、「＊」記号は以下の用途で使われます

## 数値の乗算
Rubyでは、他の多くのプログラミング言語と同様に、「＊」記号は数値の乗算に使用されます。

```ruby
result = 3 * 4  # resultは12になります
```

## 配列の繰り返し
「＊」記号を使って、配列内の要素を指定した回数だけ繰り返すことができます。

```ruby
repeated_array = [1, 2, 3] * 3  # repeated_arrayは[1, 2, 3, 1, 2, 3, 1, 2, 3]になります
```

## 文字列の繰り返し
「＊」記号を使って、文字列を指定した回数だけ繰り返すこともできます。

```ruby
repeated_string = "abc" * 2  # repeated_stringは"abcabc"になります
```

# 可変長引数
メソッド定義において、引数の前に「＊」を付けることで、任意の数の引数を受け取ることができます。
これは「スプラット演算子」とも呼ばれます。可変長引数については別途まとめを作成する予定です。

```ruby
def sum(*numbers)
  numbers.reduce(0, :+)
end
sum(1, 2, 3)  # 6を返します
```

## 配列展開
配列の要素を個別の引数や別の配列の要素として展開する際にも「＊」を使用します。

```ruby
a = [1, 2, 3]
b = [-1, 0, *a, 4, 5]  # bは[-1, 0, 1, 2, 3, 4, 5]になります
```

# 応用的な使い方

## 配列の要素の分割
配列を「＊」を使って部分的に分割することができます。

```ruby
first, *middle, last = [1, 2, 3, 4, 5]
# firstは1、middleは[2, 3, 4]、lastは5になります
```

## 複数の配列の結合
複数の配列を「＊」を使って結合することもできます。

```ruby
a = [1, 2]
b = [3, 4]
c = [*a, *b]  # cは[1, 2, 3, 4]になります
```

# まとめ
「＊」は便利な記号ですが、初心者には使いにくさがあるのかなと思いました。
柔軟な活用方法があるため、可読性を持ちながら実装をしていくのが良いと思いました。
今更だけどDocker+WindowsでLaravel環境構築2024

# 前置き
最近はどの案件にアサインされてもMacを使用するのが当たり前になってきたが、我が家のPCは未だにWindowsである。だってPCゲーム遊びたいじゃん。
で、最近アサインされたLaravel案件はエンジニアさんがとても優秀で、久々に知見をきちんと取り入れたくなり、自分でも何かLaravelで作ろうと思った訳です。
で、Windows環境で真面目に環境構築しようとすると結構面倒くさい。
Windows環境の環境構築の面倒臭さのおかげでDockerfileやらdocker-compose.ymlやらはサクサク書けるようになルのは良い事なんだけど、WSL2からWindowsのディレクトリにLaravelのプロジェクトを置こうものならブラウザで開くと遅くて叶わん。
で、こうすると便利だわ～って調べた結果を歳でだいぶ忘れっぽくなってるので残しとく。

## WSL2とDocker Desktopの連携してからの
いきなりリンクに頼るけど、[この辺（WSL2内でDockerコマンドを使えるようにする）](https://learn.microsoft.com/ja-jp/windows/wsl/tutorials/wsl-containers) はMicrosoftの公式ページに任すとして。
最近知ったのはネットワーク経由でWSLにアクセス出来るって事。

```
\\wsl$
```

![2024-01-17 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39934/04849040-f64f-2ad2-242a-3dbf5810eb2f.png)

ネットワークドライブに割り当てて、適当なディレクトリを作成してあげて、開発時のターミナルはWSLを使用すれば速度が落ちないらしい。WindowsでLaravel開発ガチでやってる人には常識かもしれんけど。

って事でWSLで早速試してみた。

```bash
tomo@tomo-PC:~$ cd /
tomo@tomo-PC:/$ sudo chmod 777 WorkSpace
tomo@tomo-PC:/$ cd WorkSpace/
tomo@tomo-PC:/WorkSpace$ 
tomo@tomo-PC:/WorkSpace$ 
tomo@tomo-PC:/WorkSpace$ curl https://laravel.build/laravel | bash

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1185    0  1185    0     0   1456      0 --:--:-- --:--:-- --:--:--  1455
latest: Pulling from laravelsail/php83-composer
Digest: sha256:bf8d87c943cacab0e04ead85e8c1db440e345fd1456714f736786ef4d18ba7b7
Status: Image is up to date for laravelsail/php83-composer:latest
   WARN  TTY mode requires /dev/tty to be read/writable.

    Creating a "laravel/laravel" project at "./laravel"
    Installing laravel/laravel (v10.3.2)
    ...(省略)
Get started with: cd laravel && ./vendor/bin/sail up
tomo@tomo-PC:/WorkSpace$ 
tomo@tomo-PC:/WorkSpace$ 
tomo@tomo-PC:/WorkSpace$ 
tomo@tomo-PC:/WorkSpace$ cd laravel && ./vendor/bin/sail up
```

今は便利な時代になったね。 

```
curl https://laravel.build/<自分の作りたいプロジェクト名> | bash
```
このコマンドを実行すると一時的なコンテナをダウンロードしてlaravelがcomposer installされた状態にまでしてくれる。

あとはlaravelのsailコマンドで環境を作り、WindowsからはZドライブのディレクトリに対してVSCodeなりPHPStormなりで開いて開発すればいいだけ。

PHPStormの場合は、[File] -> [Setting] -> [Tools] -> [Terminal] -> [Shell path]の所をwsl.exeに変更する。
この時、WSLがデフォルトで何を使うか確認しといた方が良いかも。

```
C:\Users\tomo>wsl -l -v
  NAME                   STATE           VERSION
* Debian                 Running         2
  docker-desktop-data    Running         2
  Ubuntu                 Running         2
  docker-desktop         Running         2

C:\Users\tomo>
```
他のディストリビューションに切り替えたい場合は以下のコマンド
```
wsl --set-default Ubuntu
```
PHPStorm用の参考はここがいいかも
[Docker Desktop WSL 2 ＋ PhpStormでPHP開発環境を作ってみる](https://qiita.com/odaryo/items/98499ca4894388187a40)
How To Write Blogs with Qiita-CLI


# 1. Install qiita-cli

Please referense to [official setup document][qiita-setup-document]

https://github.com/increments/qiita-cli?tab=readme-ov-file#qiita-cli-%E3%81%AE%E5%B0%8E%E5%85%A5%E6%96%B9%E6%B3%95%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6

[qiita-setup-document]: https://github.com/increments/qiita-cli?tab=readme-ov-file#qiita-cli-%E3%81%AE%E5%B0%8E%E5%85%A5%E6%96%B9%E6%B3%95%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6


# 2. Set Setting

```
---
title: [title]
tags:
  - tag
  - tag
private: true (when true, only you and those who have the link can see it)
updated_at: ""
id: null
organization_url_name: null
slide: false
ignorePublish: false (when false, this blog will be published)
---
```

Some tags may finish with comma in Qiita Preview, but that is deleted when published.


# 3. Write a blog

You can referense to markdown cheetsheet when writing md file.

https://qiita.com/Qiita/items/c686397e4a0f4f11683d


# 4. See preview

Type this command

```
npx qiita preview
```

and visit [localhost:8888](http://localhost:8888)

:::note warn
### attention
To see the preview, localhost:8888/items/show?basename=[filename]
or
Push menu button(≡), and select Ariticles/not posted/your file
:::


# 5. Publish articles

You can publish the article using the button in Qiita Preview, or this command
```
npx qiita publish [filename]
```

When you set Github Actions, you can also publish articles with push

If you want to publish all article
```
npx qiita publish --all
```

When you want to reflect the article by force
```
npx qiita publish filename -f [--force]
```

# 6. Delete articles

Articles cannot be deleted using qitta-cli or Qiita Preview.
The article in Qiita will not be deleted if you delete it in your publish repository.
Visualforceでjqueryの使用

Salesforceに以下のような押すと日付が変わるボタンを実装したので書いておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2817833/74a85b12-f448-b252-2c8c-8439c9a3424d.png)
# 目標
- Salesforceにて、「前日へ」ボタンを押すとtable内にある`apex:inputField`フォームの日付が前日になる仕組みを作りたい。（ほかのボタンも同様）
- その際にjQueryを使用する。


# やったこと
### 1. ボタンを押した時に反応してほしい
- いきなり実装することは難しかったため、まずはボタンクリック時に`console.log()`でコメントを出力できるようにしました。
- 最初に書いたコードは以下の通りです。
```apex
<apex:form id="form1">
    <apex:commandButton value="hoge" id="btn1"/>
    ～省略～
    <script>
        $("#btn1").on("click", function(){
            console.log("ボタンが押されました");
        }
    </script>
</apex:form>
```
##### idを指定したはずなのにボタンが反応しない…:cry:

#### 原因：
- Visualforce特有のコンポーネント（```<apex:なんとか>```みたいなやつ）のidをjQueryコードのトリガーに使用したい場合はフルパスで指定する必要がある

#### 解決方法：
- 仕方ないからidをフルパスで記述しようとするも、Visualforce上で指定されたidをhtml化すると、コロン```:```が含まれている
- jQueryのセレクタで、コロンはフィルタという特別な役割を持つため、`\\`をコロンの前につけてエスケープしなくてはならない
- idが長くなってしまうため、`$("[id$=btn1]")`と記述することで、id名の末尾に```btn1```がある要素を取得する


- 書き直したコードは次の通り。これでクリック時にログが表示されるようになった。
```apex
<apex:form id="form1">
    <apex:commandButton value="hoge" id="btn1"/>
    ～省略～
    <script>
        $("[id$=btn1]").on("click", function(){
            console.log("ボタンが押されました");
        }
    </script>
</apex:form>
```
### 2. テーブルの値を取得
- まず、今回使用するテーブルのコードは以下の通り（※不要な部分は省略しました）。
```apex
<apex:pageBlock id="pb">
    <apex:PageBlockTable id="pbt" value="hoge" var="h">
        <apex:column value="{!h.hoge1}"/>
        ～省略～
        <apex:column headerValue="稼働日">
            <apex:inputField id="workingday" value="{!h.hogex}"/>
        </apex:column>
    </apex:PageBlockTable>
</apex:pageBlock>
```
- このコードで、`id="workingday"`の部分の値を取得したい。画像だと以下の赤丸部分。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2817833/e219f9f7-ca0b-4601-ea70-62dfebb64c0a.png)
- 

### 3. 日付の計算
- ボタンを押すと一日前の日付が表示されてほしい → Date() オブジェクトの使用

### 4. ボタンを押した時にフォーム内の日付が一日前になってほしい
- 最初に書いたボタン実装部分のコードは次の通り。
```apex
<apex:commandButton value="前日へ" id="btn1"/>
```
##### ボタンを押すと一瞬前の日付が表示されるが、クリックのたびにリロードされて初期画面が表示されてしまう…:cry:
#### 原因：
- apex:commandButton タグの type 属性が原因。
- オプションを指定しない場合、上記のコードはhtml上で `type="submit"` と表示される。
- この submit という属性によってフォームが送信され、ページがリロードされてしまう。
#### 解決方法：
- コードをHTMLで出力した際に、`type="button"` と表示されればよい。
    - button 属性には送信機能が付いていないため、ただのボタンとして実装できる。
- 以下のようにコードを書き換えた。
```apex
<apex:commandButton value="前日へ" id="btn1" rerender="form"/>
```
以上より、HTMLでは`type="button"`と表示されるようになり、ボタンクリックの際に画面全体がリロードされなくなった。
SQLを復習する 〜その3〜

# NULLとは
テーブルの中に数値や文字列のような具体的な値ではなく、NULLという何も示さないことを意味するものになることがある。
実際にはそこに何もデータが格納されていないことを***NULLが格納***されていると表現したもの。

### NULLの判定
NULLかどうかを判定するには、WHERE句を使い`=演算子`や`<>演算子`を利用することができないため以下のようにして判定を行う。

NULLであることを判定する
```sql
式 IS NULL
```

NULLでないことを判定する
```sql
式 IS NOT NULL
```

# LIKE演算子
文字列があるパターンに合致していることをチェックすることを***パターンマッチング***という。SQLではLIKE演算子を使用することでパターンマッチングを行うことができる。
```sql
式 LIKE パターン文字列
```
### パターン文字列に使用できる文字
| パターン文字 | 意味 |
|:-:|:-:|
|  % | 任意の0文字以上の文字列  |
|  _(アンダースコア) | 任意の１文字  |


# BETWEEN演算子
ある範囲内に値が収まっているかを判定する。

```sql
式 BETWEEN 値1 AND 値2
```
BETWEEN演算子では、値が「値１以上かつ値２以下」の場合に真になる。

# IN, NOT IN演算子
IN演算子は、値がカッコ内に列挙した複数の値（値リスト）のいずれかに合致するかを判定する演算子。
```sql
式 IN (値1, 値2, 値3・・・)
```

NOT IN演算子はIN演算子の逆で、カッコ内に列挙した値のどれとも一致しないものを判定する。

# ANY, ALL演算子
IN演算子では、ある値が複数の値のどれかと等しいかを判定することができた。複数の値の大小を比較したい場合には、ANY演算子やALL演算子を利用することができる。
しかし、これらの演算子は単体で利用するメリットは少なく、服問い合わせを行う際の道具として利用すると最大限に活かすことができる。
C言語におけるメモリ操作：構造体のコピーとサイズ情報の保存

C言語では、メモリ操作は非常に重要なテーマです。この記事では、特定のメモリ領域に構造体をコピーし、その構造体のサイズ情報を領域の末尾に保存する二つの異なる方法を紹介します。

# 方法1: memcpy と直接代入を使用

まず、構造体Aを定義します。その後、指定されたメモリ領域にこの構造体をコピーし、構造体のサイズを領域の末尾に直接代入する方法を見ていきます。

**構造体Aの定義:**
```c
typedef struct {
    char data[128];  // このサイズは変更可能
} StructA;
```
**関数の実装:**
```c
void copyStructAAndSaveSize(void *ptr, size_t size) {
    StructA a;
    size_t structSize = sizeof(a) > size / 2 ? size / 2 : sizeof(a);

    // 構造体Aを領域の先頭にコピー
    memcpy(ptr, &a, structSize);

    // 構造体Aのサイズを領域の末尾に保存
    size_t *sizePtr = (size_t *)((char *)ptr + size - sizeof(size_t));
    *sizePtr = structSize;
}
```
# 方法2: memcpy と memset を使用
次に、memset 関数を使用して構造体Aのサイズ情報をメモリ領域の末尾に保存する方法を紹介します。ここでは、サイズ情報をバイト配列に変換してから memset でコピーします。

**関数の実装:**
```c
void copyStructAAndSaveSizeUsingMemset(void *ptr, size_t size) {
    StructA a;
    size_t structSize = sizeof(a) > size / 2 ? size / 2 : sizeof(a);

    // 構造体Aを領域の先頭にコピー
    memcpy(ptr, &a, structSize);

    // 構造体Aのサイズをバイト配列に変換
    unsigned char sizeArray[sizeof(size_t)];
    for (int i = 0; i < sizeof(size_t); ++i) {
        sizeArray[i] = (structSize >> (i * 8)) & 0xFF;
    }

    // memsetでサイズ情報を領域の末尾にコピー
    for (int i = 0; i < sizeof(size_t); ++i) {
        memset((unsigned char *)ptr + size - sizeof(size_t) + i, sizeArray[i], 1);
    }
}
```
# 結論
これら二つの方法は、C言語におけるメモリ操作の基本的なテクニックを示しています。直接代入を使う方法はよりシンプルで直感的ですが、memset を使用する方法はサイズ情報をバイト単位で操作する際に有用です。プログラミングにおいては、状況に応じて最適な方法を選ぶことが重要です。
最悪の事態を想定する事の難しさ

# 「最悪の事態」に想定せよ？
何か大きな事件や事故、災害が発生するたびに「なぜ最悪の事態を想定して準備しないのか」という批判が目に付く。「怠慢だ」「無責任だ」「責任をとれ」と厳しい言葉が続くことも珍しくはない。
確かにこれは一理ある。どんなプロジェクトマネジメントやリスクマネジメントの教科書を開いて見ても、起こり得る最悪の事態を想定すべきと書いてあるだろう。でも実際、最悪の事態に備えられる組織は少ないと思う。今日はちょっとそのことについて個人的な考えを飲みながら書いてみようとおもう。

# リスクの評価
プロジェクトマネジメントの教科書には、あらゆるリスクについて予め洗い出し、事前にリスクの回避/軽減/転嫁/受容などの対応の策を決めるべきだと書いてあるだろう。
多くの（？）健全なプロジェクトでは、プロジェクトの立ち上げ時や、フェーズの切れ目など適当なタイミングでリスクについてブレストし、その対応策を考えているとおもわれる。
本当にあらゆるリスクを考え始めるときりがない。
リリース直前に使ってるOSSに脆弱性がみつかったらどうする？とか、インフルエンザやコロナが流行って誰も開発できなかったらどうしよう？とか、なんだかあり得そうなものもあれば
使ってる製品やサービスが突然なくなったら？とか、会社の経営がマズくなったら？とか、地球に隕石が降ってくるかも？とかまず有り得ないと思われるようなものまでリスクは考え始めればきりがない。
なので、多くの場合はリスクについてブレストし、洗い出されたものを評価して、取り扱う価値がないと思われるものは積極的にリスクを受容するということが多いようにおもわれる。
リスクの評価はわかりやすくいうと期待値的な計算がされることが多く、リスクが顕在化する確率と、リスクが顕在化した際の影響度合い（多くの場合金額に換算する）の掛け合わせで計算する。
そうして現実的に向き合わなければいけないリスクに立ち向かうわけだ。
しかし、これが曲者である。
誰だって地震の起こる確率などわからないし、開発メンバー全員がインフルエンザに倒れる可能性の度合いなど見積もることはできない。
だから、取り敢えず極小の発生確率として見積もってしまう。数学の極限と同じで、n→∞になると1/nは0に近似するのだ。極小の発生確率にしてしまうと、その事象は起きない前提で話をせざるを得なくなってしまう。
このやり方じゃあ最悪の事態に前もって備えるのは無理がある。

# それでも最悪の事態は起きるのだ
で、最悪の事態が発生しなければいいし、多くの場合は現実的に発生しないのだ。プロジェクトマネージャーも、開発メンバーも、あらゆる人が「最悪の事態にはならなかった」という成功体験を積み上げてきている。
そんな人たちが集まったところで、油断と慢心を天高く積み上げたとき、最悪の事態は発生する。
それは震災であったり、情報流出事故だったり、コロナの流行だったり、色々な形で突然発生する。
そして後悔する。何故前もって備えておかなかったのか、何故こんなことになってしまったのか、と。
でもそれも喉元を過ぎれば忘れてしまう。社会に大変なことが起きたとき「あれは仕方なかった」、「事前に予見するのは難しかったよ」と自分で自分を慰める。きっと心の防衛反応みたいなものが働いているのだ。
そして次のプロジェクトでも「これは考えても仕方のないリスクだから、一旦外そう。」と平気な顔をして言うのだ。
だって仕方がない。あらゆるプロジェクトや組織のリソースには限りがあるし、超低確率な事態に対して金をつかっていたら予算なんてものは簡単に吹き飛んでしまうもの。
そうしてまた油断と慢心の積み上げを始める。たくさん積み上がったときにまた起こる事態を想定できずに。
そんなことないだろう？バカだなーとおもう人は自分の家を見てほしい。どれだけの人が災害や疫病、戦争などに備えてしっかりと準備できているのか。あらゆる自治体から準備をしろといわれても、できていない人が如何に多いか。
緊急時の避難先や経路、連絡手段。貯蓄などの備え。勤めている会社がなくなった際の転職ルート、自己研鑽。自身が急に倒れた際の遺書、遺産の相続…
完璧に準備できている人など、かなりの少数派のはずだ。

# じゃあどうすればいいのって話
現実的に、また短期間で見た経済合理性から考えて、こうした異常事態とも言える「最悪の事態」に備えるのは難しいことを認めよう。仮にこれを読んだ人が明日職場で「最悪の事態に備えなければいけない！」と唱えたところで付いてきてくれる人もいない。大人しく「最悪の事態に備えるのは難しい」という現実を受け止めるべきだ。（と、私は考える。）
むしろ、そのようなリスクは積極的に受容して、コストを削減し、挑戦するほうがビジネス的には成功だろう。世の中いつもリスクなしに成功は有り得ないのだ。リスクを嫌っていては挑戦などできようがない。
一番最悪なのはリスクばかりに目を向け、新しいことが何もできなくなることだ。誰もが階段を1つ2つ飛ばしで登れるわけではない。いや、多くの人は一段ずつしか登れない。
そんな中で新しくことから逃げ続けていては、いつの日か一歩も動けない化石と化してしまうのだ。目先のリスクにばかり目を向け、いつまでたっても新しい技術を導入せず、古いまま固まってしまったあなたや、あなたのプロジェクトのことだ。
だから、組織やチームとしては多少のリスクを受容するのはしかたないと割り切ろう。「最悪の事態」を想定し、準備するのは国家や自治体、会社といった大きなバジェットを持つ人達と、あなた個人や家族という小さなコミュニティに留めるのがいい。そういった単位であれば論理よりも感情が優先できる。経済合理性を度外視してでも投資すべき大事なものが考えられる単位で、考えるべきだ。
あなたがもし経済合理性を無視できるだいじなもの、たとえば家族や恋人、何にも代え難いものを持つのであれば今すぐにでもあらゆる投資をしよう。そこには損得などは存在しない。で、あれば期待値じゃなくて起きうる最悪の事態を考えるだけで、できるだけの投資ができるでしょう？
Eclipse と IntelliJ IDEA で Java コンパイラの仕様に差異がある

# 概要
Eclipse から IntelliJ IDEA に乗り換えた時に、
Java コンパイラの仕様の差異で、落とし穴にハマったのでメモ程度にまとめます。

# 詳細
- Eclipse では **Eclipse Compiler for Java** （略してECJ）を使用している
- IntelliJ IDEA では **javac** を使用している

基本的には ECJ の方が制約が緩い傾向にあるため、
ECJ では通っていたソースコードが javac では通らないということが発生しうる

**Eclipse どっぷり Java エンジニア**の場合、知らず知らずのうちにECJでしか通らない構文でコーディングしている事があるので、
同じソースコードを IntelliJ IDEA に持っていた時にビルドエラーが発生して困ることがあります。。（悲しい）

# 解決策
IntelliJ IDEA において Eclipse と同様のコンパイルにしたい場合は、
Java コンパイラの設定を開き、使用するコンパイラを **javac** → **Eclipse** に変更する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3656310/96b0761b-1f42-b110-99ca-86c60d2bb0c2.png)

# 参考リンク
- [ECJ (Eclipse Compiler for Java) は面倒見が良すぎ…](https://imatake.hatenablog.com/entry/20100702/1278038592)
- [Java コンパイラー | IntelliJ IDEA ドキュメント](https://pleiades.io/help/idea/java-compiler.html#compiler)
Unity(C#)でProcess.Startしようとしたときに躓いた話

# 事の起こり
ゲームやツールに存在する自動アップデート機能。
1度は自分で実装してみたいと思い、やってみようとしたときの話です。

## 構成
- 0.exe
  - 本体。これをアップデートするのが目標。
- 1.exe
  - 0.exeをアップデートするためのexe。
### 手順
1. 0.exe: 起動したときに更新があるかどうかを確認する
1. 0.exe: 最新版のzipを1つ上のディレクトリにダウンロードする
1. 0.exe: 1.exeを1つ上のディレクトリにコピーする
1. **0.exe: 1つ上のディレクトリの1.exeを起動する(今回の問題)**
1. 1.exe: zipを解凍して上書きする
1. 1.exe: 解凍した0.exeを起動する
1. 0.exe: 起動したときに更新があるかどうかを確認する(なければOK)

# 問題
4.の手順にて1.exeが起動しなかった。
# 原因
`ProcessStartInfo.FileName`で指定するプロセス名は`/`ではなく`\\`で区切らなければいけないというWindowsの問題でした。
実行ファイルのディレクトリとファイル名とで`Path.Combine`した後のを`[string].Replace("/", @"\\")`してあげればいいですね。
ESP32を用いたSPI通信（マスター）

# ESP32を用いたSPI通信
## Masterにした理由
本当は、スレーブで通信を行いたかったのだが、自作のプログラムでは動かず、先達のプログラムをコピペしてもスレーブが一切動かなかったため、仕方なくマスターとして通信を行う。
ESP32の利用途が、センサのデータを統合して、メインマイコンに送信し、メインマイコンでデータ処理を行うことを想定していたため、ESP32はメインマイコンからの送信リクエストを受け取り、必要なデータを送るという動作を行わせたかった。

## testプログラムの処理内容
スループットで0に数をひたすら足して、それを送信するプログラムである。
テストプログラムを作るのは、マスターだと簡単だった。

## プログラム
```cpp
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "esp_log.h"
#include "driver/spi_slave.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include <Arduino.h>
#include <SPI.h>

#define GPIO_MOSI           12
#define GPIO_MISO           13
#define GPIO_SCLK           15
#define GPIO_CS             14

#define GPIO_MOSI GPIO_NUM_12
#define GPIO_MISO GPIO_NUM_13
#define GPIO_SCLK GPIO_NUM_15
#define GPIO_CS GPIO_NUM_14

spi_device_handle_t handle;
float rxdata = 0;
float txdata = 0;


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);

  int ret = 0; //診断用バッファ
  

  spi_bus_config_t buscfg = {
        .mosi_io_num = GPIO_MOSI,
        .miso_io_num = GPIO_MISO,
        .sclk_io_num = GPIO_SCLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
    };

    spi_device_interface_config_t spicfg = {
      .command_bits = 0,
      .address_bits = 0,
      .dummy_bits = 0,
      .mode = 0,
      .duty_cycle_pos = 128,
      .clock_speed_hz = 1000000,
      .input_delay_ns = 0,
      .spics_io_num = GPIO_CS,
      .queue_size = 2,
    };

    ret = spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO);
    assert(ret == ESP_OK);
    ret = spi_bus_add_device(SPI2_HOST, &spicfg,&handle);
    assert(ret == ESP_OK);

}

void loop() {
  // put your main code here, to run repeatedly:
    int ret = 0;

    spi_transaction_t t;
    memset(&t, 0, sizeof(t));
    t.length = 32;
    
    t.rx_buffer = &rxdata;
    t.tx_buffer = &txdata;
    
      ret = spi_device_transmit(handle,&t);
      
      Serial.printf("%f\n",txdata);

      txdata = txdata+0.0001; 
      delay(1);

}

```

## 参考文献
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/spi_master.html#driver-features

https://github.com/espressif/esp-idf/blob/cb66d6f6faca7091bd8cc5578f54628aa88054fc/examples/peripherals/spi_slave/sender/main/app_main.c
実務未経験30代からエンジニアを目指して

## 現状
- 昨年の5月からエンジニア転職を目指して、PHP、そしてLaravelの勉強を始めました。
現在はPostmanを使った実務案件に携わっています。

## 投稿について
- 11月までチーム開発演習で投稿アプリの制作をしておりました。
そのチーム開発演習で携わった内容と現在携わっている実務案件の内容を復習を兼ねて、
投稿していきたいと思っております。
[Ruby]optparseでコマンドラインから引数を受け取る

## optparseとは
Rubyのoptparseはコマンドラインのオプションを取り扱うためのライブラリです。
プログラム実行時に指定されたコマンドを解析し、フラグや引数をプログラム内で扱う事が出来ます。

## 使い方

まずは基本的な使い方です。
```ruby:test.rb
# 標準ライブラリを読み込む
require 'optparse'

# OptionParserクラスのオブジェクトを作成する
opt = OptionParser.new

# onメソッドでオプションを定義する
# オプションが指定されていればブロックが実行される
opt.on('-m') { |v| p v }

# parseメソッドでコマンドラインの解析を行う
opt.parse(ARGV)
p ARGV
```

オプションを指定してプログラムを実行します。
```bash:
# オプション付きで実行する
$ ruby test.rb -m 10

true  # vの出力
["-m", "10"]  # ARGVの出力
```

- オプション付きでプログラムが実行された場合はブロックパラメータにtrueが渡されます。
- `ARGV`はコマンドラインからプログラムに渡される引数を配列で保持しています。
- `parse`メソッドが実行されないとブロックの処理は行われません。
<br>

複数の引数を指定しても`ARGV`に格納されます。
```bash
$ ruby test.rb -m 10 20 30

true  # vの出力
["-m", "10", "20", "30"]  # ARGVの出力
```

`ARGV`にオプションを含めたくない場合は`parse!`メソッドを使用します。
```ruby:test.rb
opt = OptionParser.new
opt.on('-m') { |v| p v }
opt.parse!(ARGV)
p ARGV
```
```bash:
$ ruby test.rb -m 10

true  # vの出力
["10"]  # ARGVの出力 （オプション文字列が含まれない）
```

### ブロックに引数を渡す
ブロックにtrueでは無く、引数を渡す場合はオプションの後に文字列を追記します。
このケースで`parse!`メソッドを使用した場合は、`ARGV`から引数も取り除かれ空になります。
```ruby:test.rb
opt = OptionParser.new

# vに引数の値が格納される
opt.on('-m VAL') { |v| p v }
opt.parse!(ARGV)
p ARGV
```
```bash
$ ruby test.rb -m 20

20 # vの出力
[] # ARGVの出力
```

引数無しでプログラムを実行するとエラーになります。
```bash
$ ruby test.rb -m

missing argument: -m (OptionParser::MissingArgument)
```

引数を任意にする場合は、文字列を`[]`で囲みます。
```ruby:test.rb
opt = OptionParser.new
opt.on('-m [VAL]') { |v| p v }
opt.parse!(ARGV)
p ARGV
```
```bash
$ ruby test.rb -m

nil # vの出力
[] # ARGVの出力
```

## 引数を限定する
`on`メソッドには特定の値のみを受け取るための引数が用意されています。

### 正規表現で限定する
第二引数で指定した正規表現に引数がマッチしない場合は例外を投げます。
例えば1桁の数字のみを引数として受け取りたい場合は以下のように記述します。
```ruby:test.rb
opt = OptionParser.new
opt.on('-m VAL', /[0-9]/) { |v| p v }
opt.parse(ARGV)
```
```bash
$ ruby test.rb -m 9
9

$ ruby test.rb -m 10
invalid argument: -m 10 (OptionParser::InvalidArgument)
```

### クラスで限定する
第二引数で指定したクラスと引数のクラスが異なる場合は例外を投げます。
次の例では受け取る引数をIntegerクラスに限定したものです。
```ruby:test.rb
opt = OptionParser.new
opt.on('-m VAL', Integer) { |v| p v }
opt.parse(ARGV)
```
```bash
$ ruby test.rb -m 9
9

$ ruby test.rb -m a
invalid argument: -m a (OptionParser::InvalidArgument)
```

### 独自に条件を設定する
引数をブロック内に渡して、特定の値のみを通す条件を設定しましょう。
次のコードは1~12の数字のみを受け取るバリデーションです。
```ruby:test.rb
opt = OptionParser.new
opt.on('-m MONTH', Integer) do |month|
   p month if month.between?(1, 12) 
end
opt.parse(ARGV)
```
```bash
$ ruby test.rb -m 12
12

$ ruby test.rb -m 13

```

## 参考
https://docs.ruby-lang.org/ja/latest/library/optparse.html
確率過程の最良予測は, ガウス過程回帰の平均値と一致する

# はじめに

確率過程の最良予測は, ガウス過程回帰の平均値と一致することを確認しました。
リンク先が記事内容となります。ご了承いただければ幸いです。

https://mathlog.info/articles/S2a5cmaqMIvp1xQterAT
Tips: iTerm2を使っている & KeyRepeat を1にしているときは、キーボードの設定に注意した方が良い

## はじめに
僕はMacでiTerm2を使っています。
MacでiTerm2を使っている上で何年も悩んでいた問題が解決したので、記事としてまとめます。

## 起こっていた問題
Macにはキーリピートを高速化するコマンドが存在します。
設定方法は以下の記事にまとまっています。

https://qiita.com/risto24/items/35190fe3601681cf4915

僕はキーリピートを高速化したいので、KeyRepeatを最速の`1`にしていたのですが、設定を`1`にすると、iTerm2で以下のようなカクつきが発生する問題を抱えていました。

![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/158501/4488b67f-5340-c455-e44c-1369e999b628.gif)

こちらの画像の通り、キーボードを入力している間はなぜかカーソルが移動せず、キーを離すと一気に入力がされる、という
かなりストレスフルな挙動になっていました。

調べてみると同じような問題に遭遇している方を何人か見つけることができましたが、抜本的な解決方法はずっと分からずじまいでした。

https://blog.nijohando.jp/post/mojave-key-repeat-problem/

https://gitlab.com/gnachman/iterm2/-/issues/7084

https://gitlab.com/gnachman/iterm2/-/issues/2859

iTerm2のリポジトリで`key repeat`と調べてみると、多くの似たようなIssueがたくさん出てきます。

https://gitlab.com/gnachman/iterm2/-/issues/?sort=created_date&state=opened&search=key%20repeat&first_page_size=20

## 解決方法
最近この問題の解決方法がついにわかりました。
解決方法は「日本語 - ローマ字」入力のキーボードの英字モードではなく、英字キーボードを使うことでした
なぜか「日本語 - ローマ字」入力の英字モードは入力がリアルタイムでレンダリングできないようです。（原因の解明まではできませんでした）

![スクリーンショット 2024-01-17 21.24.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/158501/f60aad63-9cb9-311f-04b4-569d13ac2fc1.png)

同じような問題で悩んでいる方の助けになれば幸いです。
「ド初心者」がプログラムコードを知らないでも使える「Make」というサービスを使えるようになるまで参考にした記事紹介

# 「Make」を知り、「LINE Bot」を自分で作れるようになるまでの参考記事を紹介
おはようございます、こんにちは、こんばんは！
いつでも笑顔、いけちゃんです。

別記事で「Make」と「LINE Bot」を学びました！と書きましたが、その道が苦難の連続だったので、
プログラミングのpの字も知らない「ド初心者」の私が参考にした記事を紹介します。
この記事の順番どおりに進んでいくと、初めてのLINE Botを作るときにも迷わず進められると思います。

もちろん、これ以外にも皆さん自身で検索しまくることが大事。
だから、どんな検索をして出てきたか、も書いておきますので参考にしてください。


# そもそもMakeって何？どうやったら使えるの？

[ノーコード開発をはじめよう！最強ツールmake(旧integromat)の登録方法を分かりやすく解説します](https://note.com/nocode_dev/n/n4498ead35102)
検索ワード「Make 使い方 ノーコード」

プログラミングのpの字も知らない「ド初心者」にとって、何をどうすれLINE Botのような革新的な仕組みを作れるのか、スタートすらわかりません・・・
まずは皆さん、Makeのアカウント登録から躓くはずなのでぜひご覧ください。


# Makeを使って簡単なLINE Botを作り上げる方法

[【ノーコード入門】LINEを使った簡単な設定で最強ツールmakeを試してみる](https://note.com/nocode_dev/n/nb2d4e39eb25f)

Makeの登録ができたけど、一体どうやって使うの？とお悩みになったそこのあなた！
私も同じです（汗）
実は上で紹介した方の記事の続編で、そのままMakeを使った簡単なオウム返しをするLINE Botの作り方が掲載されています。
やりたいと考えている高い目標にいきなり挑むのではなく簡単に手慣らしをしていくと良いです。

# Makeの動作の流れを理解する

[LINE botとMakeでOCRに挑戦！LINEで食品表示の画像をテキストに変換しよう](https://qiita.com/murakani/items/26326010a72697ca55ad)
検索ワード「LINE Bot 画像 テキスト化」

一番最初に、LINE Botで画像データをテキスト化して返信するやり方や流れをとにかく検索しようとして発見した記事です。
"作りたかった流れ"の見出しと"Makeのシナリオ画面"の見出しを見比べて、Makeの動作について理解しやすかったので紹介します。

# MakeでGoogle Cloud VisionやOpenAI（ChatGPT）を使用する

[夏休みの宿題自動化くん（画像認識＆文字起こしシステム）](https://note.com/bread_water/n/ne73079efec8d)
検索ワード「Cloud Vision テキスト化 integromat」

今回のLINE Botを作るにあたり、APIキーの取得から流れまで最もわかりやすく解説されていました！
APIキーの取得については、次の記事でも紹介しますので、この記事で分かりにくかった場合は次の記事をご覧ください。

ところで「ド初心者」の皆さん、ぜひ知っておいてください。
Makeって旧名を`integromat`っていうんです。
なぜMakeではなく旧名の`integromat`で検索したかというと、Makeは2022年に名称変更し開始したサービスで、比較的新しいためなのか日本語の記事がほとんど見つからないんです（汗）
「ド初心者」の方は、Makeではなく`integromat`から調べる方が良いかも？

# Makeで必要になるAPIキーを発行する

[API Keyを発行する手順 for Google Cloud API](https://zenn.dev/tmitsuoka0423/articles/get-gcp-api-key)
検索ワード「Google Cloud Vision APIキー 取得」

`OpenAI（ChatGPT）`のAPIキー発行は上記の記事通りに進められました。
が、`Google Cloud Vision`のAPIキーの取得が上記だけでは進められず、さらに検索したところ発見しました。

# Qiitaの記事（マークダウン記法）について

[Qiitaに投稿するマークダウン（Markdown）記法のメモ](https://qiita.com/maboy/items/bbfea777544b96b57cda)
検索「文章 折り畳み Qiita "リスト"」

せっかくできた技術を皆に発信したい・・・そんな時にはQiitaというサービスがおすすめです！
Qiita (キータ) は、エンジニアに関する知識を記録・共有するためのサービスです。（ホームページ説明文抜粋）

ですが、「ド初心者」はQiitaで皆に伝えたい記事を書くことも「ド初心者」だと思います。
Qiitaは記事の書き方が独特で`Markdown記法`という記法を用います。
普段の暮らしではあまり目にしたり、実践するものではありません。

初めてQiitaの記事を書いているときに、絶対にそばに置いておきたいのは間違いなく`Markdown記法`の一覧です。
私が記事の書き方に悩んでいた際に検索し、発見したこの記事が最もわかりやすく解説されていました。


さて、ここまでの記事で、Makeを活用したLINE Botの作り方や仕組みはなんとな～くわかってきたでしょうか？

# いよいよOCRとChatGPTを活用したLINE Botづくりに挑戦！

[ノーコード？モジュール？？API？？？「ド初心者」がOCRとChatGPTを活用し、個性溢れる文書を整理・返信するLINE Botを作りデータ化してみた話](https://qiita.com/ikeuchi-kota/items/b5673052a3a782f9bb94)

この記事で、私が初めて挑戦したLINE Botについて触れています。
Google cloud VisionのOCRやOpenAI（ChatGPT）を使ったLINE Botの作り方を掲載していますので、ぜひご覧ください。

# 御礼
この度、初めてのことばかりで戸惑っていた中でも、わかりやすい記事を投稿された皆さまに様々な知識をいただき仕組みを作り上げることができました。
本記事を同じく「ド初心者」の皆さまに勝手ながら紹介するとともに、御礼申し上げます。

本当にありがとうございました。
jsdom で css nesting が Error: Could not parse CSS stylesheet になる件

jsdom のバージョンは ```20.0.0``` です

https://github.com/jsdom/jsdom/issues

css nesting 使えるようになってほしいですね！

# 参考
css nesting

https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_nesting/Using_CSS_nesting
「OCI Cloud Free Tier」に登録してみた。

# OCI Cloud Free Tier

Oracleが提供するクラウドサービスの無料利用枠です。
AWSにもありましたが、期限の制限なくサービス利用できる部分があるようで、
業務でもOCIを使う機会が出てきそうなので、試してみることにしました。

## 登録する

[こちら](https://www.oracle.com/jp/cloud/free/?intcmp=ohp052322ocift)から登録できます。
「無料で始める」から登録してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132118/636111c9-a5da-dd42-45ca-76c4891fdc96.png)

### アカウント情報の入力
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132118/9f354b61-7fa2-3152-58dc-367256850b01.png)

必要な情報を入力して、「電子メールの検証」を押下してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132118/00a5b06d-0983-ccea-e154-fd93cded8260.png)
そうするとメールが来るので、「Verify email」をクリックしましょう。
さらに詳細の情報を入力する画面が表示されますので、もろもろ入力していきましょう。

### Customer type
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132118/f0cb1a0b-13ce-5c3c-b5e8-de1421c98c6b.png)

individual(個人)を選んでも、なぜか部門名が必須になりますね。適当に入力して進めます。

### ホームリージョン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132118/2c13ebba-b8e7-dc47-39f0-49ad323edf84.png)

可用性が制限されるらしいですが、個人の検証用なので、東京を選んでおきました。
その後、住所、クレジットカードを登録して、最後に「無料トライアルの開始」をクリックしてアカウントの登録まで完了

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132118/3e820baa-e430-8d72-386b-1677db8552a2.png)
こんな画面が出てきてしばらくすると、完了します。

## ログイン
登録まで完了すると自動的にログイン画面に遷移します。
クラウド・アカウント名を入れて、「次へ進む」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132118/b310fd87-deef-7d9c-eeb5-31592b9cbdb8.png)

そうすると、再度下記の画面でログインを促されますので、
ユーザ名、パスワードを入力して、「サイン・イン」を押下

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132118/0fb6280d-8f2c-469f-7ff4-d33ef82e2aee.png)

以下の画面が出るので、「セキュアな検証の有効化」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132118/6612dba8-475f-9b08-31dd-38663a47a3ae.png)

こんな画面になるので、お好きな方法を選択してください。
私は、「モバイル・アプリ」を選んで携帯に、「Oracle Mobile Authenticator」を入れて登録しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132118/092b2548-e497-2c6f-dfd3-f753f63d9277.png)

登録されるとこんな感じ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132118/4a44e3ed-79df-eb33-ab7c-b192699c970a.png)

## ダッシュボード
ここまで来てやっと、ダッシュボードが表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132118/557acc5b-0c7c-0c8f-3df1-df4083e2df6e.png)


# さいごに

とりあえずアカウントの登録からダッシュボードの表示まで完了しました。
無料枠だけでいろいろできるので、これから試していきたいと思います。
災害ボランティア情報クローラーを作ってLambdaで更新差分チェックをした


今後も似たようなことをやりたくなることはありそうなので、備忘録的にもここに記載
クローラーや差分チェックの話ではなくLambdaの定期実行の話

# やったこと

災害ボランティアの更新情報があると良いなぁと思ったため、その情報を取得するための定期的な情報取得で更新があるかを確認しようというものです。

https://docs.google.com/spreadsheets/d/1ToG7uRymeSZbEJbecd-wvBb3XtXP6t2iXsBAfrwhtd4/

こちらにあるリンク集のURLで、更新があったかどうかを確認を行います。
更新があったか、なので更新前の状態を取っておく必要があります。
更新前の状態と結果のファイルが生成されます。

結果のファイルから見られるようにしたサイトが下記になります。

https://miyawa-tarou.com/lab/volunteer_links.php

# コード

https://github.com/miyawa-tarou/volunteer_crawler

言語はPython
URLリストは公開しているSpreadSheetにあるのでそれをダウンロードし、pandasで開き、URLそれぞれにアクセスしてBeautifulSoup4で中身をみてタグを除いた文章にして行単位でCSVとして保存。
前回の分と比較して、増えたものがあれば差分として出す、というものです。
タグは除いただけなので、リンクのURLだけ変わったとかは今回のものでは検知できません

# 定期実行のために行ったこと

## 実行するサーバー：AWS Lambda

慣れた方法なら、借りているVPSでcron回したほうがやりやすいですが、今回は使ったことがないからという理由でAWS Lambdaを使ってみます

ただ、これは作ったものを実行するだけなのであまり語ることがないです
とはいえスクリプト作ったけどどうやってpipの各種ライブラリ取ってくんねん？ってので困ったところはありました。
これは次の章

## Lambdaで動かすスクリプトをDockerで作成し、AWSで使えるようにする

他の方法もあるかもしれませんが、Dockerで上げることにします。
Dockerfileでよしなにpip insatllするようにしておけば良いようです

https://github.com/miyawa-tarou/volunteer_crawler/blob/main/Dockerfile

その上でDockerビルドして、
Amazon ECR（Elastic Container Registry）に上げます
まずAWSコンソール上でPrivate registryにリポジトリを作成します。そのあとAWS CLIを使いアップロードするわけですがコマンドがよくわからんねぇな？と思ってたら
「プッシュコマンドの表示」というところに、「これをコピペして実行しろ」的に記載されていました。
そのままやれば登録されます。

https://docs.aws.amazon.com/AmazonECR/latest/userguide/getting-started-cli.html

全体的には下記を参照しました

https://qiita.com/sasaco/items/b65ce36c05c50a74ac3e

## ファイルの入出力部分

Lambdaでサーバー内に書き出してもどうしようもないので、出力してのファイルの保存はS3に行います
Lambda内の一時ファイルの置き場は `/tmp/以下` である必要があるようです

LambdaからS3にファイルの読み書きをするにあたって権限周りが必要です。
実行するLambdaの設定時にポリシーを付与する形になります。

```:ポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": "arn:aws:s3:::{bucket名}"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::{bucket名}/*"
        }
    ]
}
```

ローカルで試す場合はそのポリシーを当てはめたユーザーを作成する必要がありそうです。
Lambdaの関数登録時にこのポリシーも付与する必要があります。

これで手動で実行するのはできるようになったはずです。

## cronの部分

Lambda上では定期実行などはできないので、Amazon EventBridgeの「EventBridge スケジュール」を使います。
AWS LambdaのInvokeでLambdaのを指定すれば実行できます。

https://docs.aws.amazon.com/ja_jp/eventbridge/latest/userguide/eb-create-rule-schedule.html

## サーバーに取ってくる

表示用のファイルは上書き更新しているわけではないのに、毎回S3から取ってくるのは転送量の無駄なので、定期的にVPSで立てているサーバーに持ってくるようにします。
差分だけ取るコマンドが有るのでそれを使います

```
aws s3 sync s3://{bucket name}/ {置き場所のパス}
```

`--dryrun` をつければドライランもできます
これをcronで実行するようにすれば定期的にとれます。

これでサーバーに定期的に更新されるようになりました
あとは、よしなに表示系でいじるだけです。
【SwiftUI】円グラフを選択した時に選択要素を強調したい

# はじめに
本記事は前回の記事の実装を元に実装しています。

https://qiita.com/SNQ-2001/items/febe2fbda89db6e0fdb6

円グラフの要素を選択した時に動きを付けてみます。

# サンプルアプリ
![Simulator Screen Recording - iPhone 15 - 2024-01-17 at 20.46.14.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/9a7221b0-0a8b-6b9e-3e7f-a28125637bd2.gif)

# 実装
`onChange`内に書いてあるロジックはわかりにくいのでコメントを追加しています。
`chartAngleSelection`に設定できるのは`Plottable`に準拠しているもののみです。

```swift
import SwiftUI
import Charts

struct FavoriteFruit: Equatable {
    let name: String
    let count: Int
}

struct ContentView: View {
    @State private var favoriteFruits: [FavoriteFruit] = [
        .init(name: "りんご", count: 15),
        .init(name: "いちご", count: 12),
        .init(name: "さくらんぼ", count: 6),
        .init(name: "ぶどう", count: 5),
        .init(name: "バナナ", count: 4),
        .init(name: "オレンジ", count: 2)
    ]
    
    @State private var selectedCount: Int?
    
    @State private var selectedFruit: FavoriteFruit?

    var body: some View {
        Chart(favoriteFruits, id: \.name) { favoriteFruit in
            SectorMark(
                angle: .value("count", favoriteFruit.count),
                innerRadius: .inset(50),
                outerRadius: selectedFruit == favoriteFruit ? .automatic : .inset(10),
                angularInset: 3.0
            )
            .foregroundStyle(by: .value("name", favoriteFruit.name))
            .opacity(selectedFruit == nil ? 1.0 : selectedFruit == favoriteFruit ? 1.0 : 0.5)
        }
        .chartAngleSelection(value: $selectedCount)
        .onChange(of: selectedCount) {
            // selectedCountがnilの時は、指がグラフに触れていない時
            guard let selectedCount else {
                // 指がグラフに触れていない時は、選択されたフルーツを解除する
                self.selectedFruit = nil
                return
            }
            // 累積カウント
            var accumulatedCount = 0
            // favoriteFruitsの先頭から順番に累積カウントを加算していく
            let selectedFruit = favoriteFruits.first {
                accumulatedCount += $0.count
                // 累積カウントがselectedCountを超えた時点で、そのフルーツを選択されたフルーツとする
                return selectedCount <= accumulatedCount
            }
            // 選択されたフルーツをselectedFruitに設定する
            self.selectedFruit = selectedFruit
        }
        .frame(width: 300, height: 300)
    }
}
```

# おわり
めっちゃかっこいい動きですね！

# 参考記事
https://www.appcoda.com/swiftui-chart-ios17/

# 公式ドキュメント
https://developer.apple.com/documentation/swiftui/view/chartangleselection(value:)
私がAIエンジニアを名乗りたがらない理由

機械学習・画像認識の技術を長年いくつか開発してきたが、なかなかAIエンジニアと名乗れずにいる。

## 分類の基本は統計的手法
- 機械学習での分類の多くの要素は統計的な手法である。
- 機械学習以前から存在する線形分離問題が、手法の進展によって非線形の分野に拡大したと考えている。

## 機械学習の大半は、学習データをどう与えるのかという、学習者側の作業に依存している。
- 大半の学習は、機械学習エンジニアの用意する学習データセットに依存している。
- 学習時のデータセットによってモデルが作成されて、利用環境にdeployされる。
- 利用環境では、学習済みのモデルによって推論するだけであって、**利用環境の中で賢くなることはない。**

## 少ない事例を元に知識を獲得したい。
- 取っ手付きのコーヒーカップがあるとしよう。
- その１つを元にコーヒーカップというのを幼児に教えたとしよう。
- そうすると、別の形のコーヒーカップ、別の模様のコーヒーカップでも、コーヒーカップと理解するだろう。
- しかし、現状の画像認識の機械学習では、**少ない枚数でコーヒーカップを学習させるのは、とても難しい**。

## 少ない事例を元に知識を獲得したい。（その２：強化学習）
- けん玉を成功させるために、強化学習を繰り返すのはいいだろう。
- でも、「安全に横断歩道を渡る」ことを強化学習で習得するのは避けたい。
- 生身の体では失敗するわけにいかないからだ。
- シミュレーション環境の中でならば誰も傷つかないからいいが、それでも、失敗事例を何千、何万と繰り返すことはさけたい。
- リッチなシミュレーション環境だと、シミュレーションの実行にも時間がかかる。
- だから、少ない事例を元に知識を獲得したいのである。

## 身体性が知能には重要であると考える
- 物体検出プログラムは、２次元画像中で対象物を含む長方形の枠を算出するものである。
- 物体検出プログラムには、２次元画像があっても、**空間という考えはない**。
- 物体検出プログラムでは、ネコを検出しても、かわいいとか、ふわふわしているとか、あたたかいとか、そういった属性を何も想起しない。
- 身体性がないので、**空間としての理解がない**。
- 身体性がないので、触れることがないので、**やわらかい、ふわふわしているという感覚**と結びつくことがない。
- 身体性がないので、ネコにふれたときの**暖かさを感じることもない**。
- 人がネコを見たときに、**腰を低くして、手のひらを上にむけて、おいでおいでするような人の行動**と、物体検出プログラムとは何ら関係をもたない。

## 空間の中で行動していく中で、自発的に学習できることを期待する。
- 知覚、予測、行動、観察のループの必要性
- 予測誤差を修正する仕組みがほしい。

# こういったことを満たしたときに十分に知性的であると考える
そのような状況を満たすのにいたっていないので、
今はまだ人工知能を名乗れない。
SSL 自己証明書作成(ワンライナー)

ずばり。

一発作成。
　
OpenSSLの場合
サーバー証明・クライアント証明・コード署名・電子メール保護 目的の場合
```
openssl req -x509 -nodes -sha256 -days 3650 -newkey rsa:2048 -keyout server.key -out server.pem -subj "/CN=www.example.com" -addext "subjectAltName = DNS:www.example.com" -addext "basicConstraints=CA:FALSE" -addext "extendedKeyUsage = serverAuth, clientAuth, codeSigning, emailProtection" -addext "keyUsage = nonRepudiation, digitalSignature, keyEncipherment"
```

サーバー証明・クライアント証明 のみ場合
```
openssl req -x509 -nodes -sha256 -days 3650 -newkey rsa:2048 -keyout server.key -out server.pem -subj "/CN=www.example.com" -addext "subjectAltName = DNS:www.example.com" -addext "basicConstraints=CA:FALSE" -addext "extendedKeyUsage = serverAuth, clientAuth" -addext "keyUsage = digitalSignature, keyEncipherment"
```

CommonNameにwildcard指定も可能。 その場合は ```/CN=*.example.com``` になる。
CommonNameにIPアドレスを指定することもできる。
  
`-addext`オプションはOpenSSL 1.1.1 以降で対応しており、
以下のように指定することで SAN をコマンドライン引数で指定可能。
```
-addext 'subjectAltName = DNS:<ドメイン名>'
-addext 'subjectAltName = DNS:<ドメイン名1>, DNS:<ドメイン名2>'
-addext 'subjectAltName = IP:<IPアドレス>'
```
こちらもwildcard指定する場合は```DNS:*.example.com```となる。
 　
_server.key = 秘密鍵_
_server.csr = サーバー証明書（自己証明書）_
　
```
-addext 'crlDistributionPoints = URI:http://example.com/myca.crl'
-addext 'crlDistributionPoints = URI:http://example.com/myca.crl, URI:http://example.org/my.crl'
```
 　
秘密鍵と公開鍵をPKCS#12形式に変換
```
openssl pkcs12 -export -inkey server.key -in server.pem -out server.p12 -name "FriendlyName"
```
秘密鍵と公開鍵をPKCS#7形式に変換
```
openssl pkcs7 -export -inkey server.key -in server.pem -out server.p7b -name "FriendlyName"
```

_フレンドリ名指定は任意（省略可能）。_
_パスワード入力を求められたら入力（省略可能）。設定した場合はWindowsでインポートする時に入力する。_
　
秘密鍵の内容確認
```:RSAの場合
openssl rsa -in server.key -noout -text
```
```:ECDSAの場合
openssl ec -in server.key -noout -text
```
　
証明書の内容確認
```
openssl x509 -in server.pem -noout -text
```
　
サーバーに設定されている証明書を確認
```
openssl s_client -connect localhost:443 -showcerts
```
PKCS #12 形式の証明書から、各種情報（PEM形式）をまとめて取り出す
```
openssl pkcs12 -in server.pfx -nodes -out fullchain.pem
```
出力には、サーバ証明書と中間CA証明書の「-----BEGIN CERTIFICATE-----」がそれぞれ含まれ、秘密鍵の「BEGIN PRIVATE KEY」も含まれます。
-nodes オプションを指定しなければ、秘密鍵は暗号化された状態で出力されます（暗号化に必要なパスフレーズが求められます）。
 　
PowerShellの場合
```
PS> New-SelfSignedCertificate `
-FriendlyName "<フレンドリ名>"
-DnsName "<ドメイン名>" `
-KeyAlgorithm RSA `
-KeyLength 2048 `
-CertStoreLocation "Cert:\LocalMachine\My" `
-NotAfter (Get-Date).AddYears(100) 
```
 自己署名証明書を作成
 -FriendlyName (フレンドリ名)
 -DnsName (DNS名)
 -KeyAlgorithm (RSA | ECDSA)
 -KeyLength (鍵長)
 -CertStoreLocation (証明書ストア)
 -NotAfter (有効期間 : 例は 100年)
CSVファイルをExcelで文字化けさせないために知っておくこと（エンコードとBOM）

あるデータを処理し、CSVファイルを出力する自作ツールを開発したときの話です。
ExcelでCSVを開いたときの文字化け対策で、SJISを使用してファイルを出力していましたが
最近、BOM付きのUTF-8に変更しました。

エンコードとBOMについて調べた自分なりの理解を簡単にまとめてみました。

## encodeとは？
ファイルに書き込まれている文字コードの種類のこと。
自然言語でいう「何語か？」のようなもので、エンコードに基づいて文字データをどのようにバイトデータに変換するかを決定するものです

## BOM（Byte Order Mark）とは？
BOMは、ファイルがどのエンコードで書かれているかを表すマーカーのこと。
システム環境によってはエラーの原因になる事もあり、通常BOM無しで保存される事が多いです

## Excelにおける選択：SJIS vs BOM付きUTF-8
Excelでcsvファイルを保存する際には、デフォルトでSJISでエンコーディングされます。
SJISは日本語特有のエンコーディングのため、主に日本国内でしか使用されず。
一方、UTF-8は国際標準のUnicodeをベースにしたエンコーディングであるため、
世界的に広く使われており、多言語対応しています。
ただExcelではUTF-8をそのまま読み込むと文字化けするため、BOM付きで保存して上げる必要があります。

どちらを採用するにしても問題が生じる事はあるみたいですが、SJISの方が問題が多そうなため、
BOM付きUTF-8で出力するようにしました。
reduceメソッドについて


## 目次
1. [reduceメソッドとは](#reduceメソッドとは)
2. [サンプルコード](#サンプルコード)
3. [使用例](#使用例)

## reduceメソッドとは
JavaScriptの`Array`オブジェクトのメソッドの一つで、配列の各要素を累積的に処理して最終的な単一の値を生成するメソッドです。このメソッドは、反復処理を要素の合計や最大値、最小値などを計算するのに役立ちます。

```javascript
array.reduce((accumulator, currentValue) => {
  // コールバック関数の処理
}, initialValue);
```
`accumulator`: 前回のコールバックの戻り値または初期値。
`currentValue`: 現在の要素。
initialValue: 最初のコールバック呼び出しのときに使用される初期値（オプション）。

## サンプルコード
以下は、`reduce`メソッドを使用した基本的なサンプルコードです。

```javascript
const numbers = [1, 2, 3, 4, 5];

// 配列の要素を合計する
const sum = numbers.reduce((accumulator, currentValue) => {
  return accumulator + currentValue;
}, 0);

console.log(`合計: ${sum}`);//表示される結果は"合計: 15"
```
`numbers.reduce((accumulator, currentValue) => {...}, 0);`

reduceメソッドが呼び出されています。引数として、累積の初期値となる 0 が入力されています。
`accumulator`は累積される値を表し、`currentValue`は各要素の値を表します。
`(accumulator, currentValue) => { return accumulator + currentValue; }`

アロー関数内で、各要素の値 (currentValue) を累積値 (accumulator) に加算しています。
`console.log(合計: ${sum});`

最終的な合計値をコンソールに表示しています。


## 使用例
以下は、reduceメソッドを使った製品の価格に割引を適用して合計金額を計算する例です。
```javascript
const products = [
  { name: 'ノートパソコン', price: 1200 },
  { name: 'スマートフォン', price: 800 },
  { name: 'ヘッドフォン', price: 100 },
  { name: 'マウス', price: 30 },
];

// 初期値を0として、商品価格に10%の割引を適用した合計金額を計算する
const totalPriceAfterDiscount = products.reduce((total, product) => {
// 各商品に10%の割引を適用した価格を計算
  const discountedPrice = product.price * 0.9;
  // 累積された合計金額に現在の商品の割引後の価格を加算
  return total + discountedPrice;
}, 0);

console.log(`割引後の合計金額: ￥${totalPriceAfterDiscount}`);
//表示される結果は"割引後の合計金額: ￥1917"
```

`const totalPriceAfterDiscount = products.reduce((total, product) => { ... }, 0);:`
reduceメソッドを使用して、商品の価格に10%の割引を適用した合計金額を計算しています。`total`は累積される合計金額を、`product`は現在の商品の情報を表します。初期値は0に設定されています。
`const discountedPrice = product.price * 0.9;`
各商品に対して10%の割引を適用した価格を計算しています。
`return total + discountedPrice;`
各商品の割引後の価格を累積された合計金額に加算しています。
【TailwindCSS】lg.hiddenの挙動について

## はじめに
画面を最大化した時にメニューボタンが表示されなくなったので、いろいろ調べていました。

原因としては、buttonのクラスに「lg:hidden」を設定したのが原因でした。

## 環境
Laravel 9.52.16
Tailwind CSS 3.1.0

## 問題
* 期待する表示画面

![c054bdb7033e76bd4386cf810d9e812a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620668/caafae09-20a6-1a08-bf4b-206a4b5d6f66.png)

* 画面を最大化すると右上の「ゲスト」が消える

![6b765728bebdc4b8be9e197436dd43c5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620668/a97c706a-4cba-cd9b-6a12-a91a821af4c3.png)

ちなみにこの「ゲスト」部分のボタンはこのような記述。

```php: navbar.blade.php
<button type="button" tabindex="0" class="btn btn-ghost normal-case font-normal lg:hidden">
```


## 解決法
* buttonの「lg:hidden」クラスを削除する

```php: navbar.blade.php
<button type="button" tabindex="0" class="btn btn-ghost normal-case font-normal"> // 'lg:hidden'を削除
```

今回の「画面を最大化するとボタンが見えなくなる」問題は、やはりレスポンシブデザインの設定に関連していました。

具体的にはlg:hiddenというクラスが原因となっています。

このlg:hiddenクラスは、画面の幅がlg（large）ブレークポイント以上になったときに、要素を非表示にするためのものです。

したがって解決策として、lg:hiddenクラスを削除することが正解となります。
history

```
[linuxstudy@xxxx ~]$ echo $HISTFILE
/home/linuxstudy/.bash_history
[linuxstudy@xxxx ~]$ history 10
 1087  man -k chmo
 1088  man -f chmod
 1089  man -f chmo
 1090  man -k chmo
 1091  man -f chmod
 1092  man 1 chmod
 1093  tail -10 ~/.bash_history 
 1094  set -o | history
 1095  set -o | grep history
 1096  history 10
[linuxstudy@xxxx ~]$ less /home/linuxstudy/.bash_history | tail -10
set -o | grep history
man set
set +o history
a
history 5
set -o noclobber
echo aaa > aaa.txt
echo aaa >| aaa.txt
set -o | grep noclo
exit
```
gymnasiumでの動画ファイル出力(google colab)

# 概要
:::note warn
警告
初めての投稿なので読みにくい記事かもしれませんがご容赦ください。
:::

本記事ではcolab上でgymnasiumを使って動画ファイルを出力する方法を簡単にまとめています。正直gymnasiumの公式ドキュメントを探せば見つかるのですが、不備もあったりするので、一応差別化は出来ているかなと思います。
~~英文読みたくない人もいるだろうし...~~
ある程度勉強している人を読者の対象として考えているので、基本的なことの解説は省略したいと思います（gymとは何かみたいな）。

# 環境
・google colab
・Python 3.10.12

# 本題
早速コードを見ていきましょう。まずは```gymnasium```のインストール
```
!pip install gymnasium
```
次に今回```LunarLander-v2```を環境として使うため```gymnasium[box2d]```をインストールするのですが、それだけではエラーが出るので下記のサイトを参考に`swig`を追加

https://stackoverflow.com/questions/76222239/pip-install-gymnasiumbox2d-not-working-on-google-colab

```
!pip install -q swig
!pip install -q gymnasium[box2d]
```
### 本体
以下のサイトにあるものを少し変えたものです。

https://gymnasium.farama.org/main/api/wrappers/misc_wrappers/#gymnasium.wrappers.RecordVideo

`from gymnasium.wrappers import RecordVideo`は公式のドキュメントにはなかったのですが、必須です。
`video_folder="./"`も公式のものとは少し変えています。どっちでもいいと思います。
```python
import os
import gymnasium as gym
from gymnasium.wrappers import RecordVideo 
env = gym.make("LunarLander-v2", render_mode="rgb_array")
trigger = lambda t: t % 10 == 0 #動画を保存をするエピソードの指定
env = gym.wrappers.RecordVideo(env, video_folder="./", episode_trigger=trigger, disable_logger=True)
for i in range(50):
  termination, truncation = False, False
  _ = env.reset(seed=123)
  while not (termination or truncation):
    obs, rew, termination, truncation, info = env.step(env.action_space.sample()) #policyはランダム

env.close()
```
こうすることで以下のように出力を得ます。本当はダウンロードせずに動画を見たかったのですが、関数が分かりませんでした。（追記（1/18）：以下のgithubにディスプレイで表示させる方法が載ってました。今後また更新します）

https://github.com/hom-bahrani/Gymnasium-Colaboratory-Starter

![スクリーンショット 2024-01-17 12.32.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3677607/2e8f6d5b-c2e0-38ae-9396-fb7540b9bab4.png)
ゲーム画面はこんな感じです
![スクリーンショット 2024-01-17 12.32.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3677607/0941a3c5-1f86-35f0-e40d-314eb7119bc4.png)

# gymとの変更点
いくつかあると思いますが、記事下部の参考文献のコードと、今回載せているコードを比較した上での変更点を一部紹介していきます。
+ close()の必要化
公式のサンプルコードを見ていると```env.close()```が必ず入っています。
colab上で試したところ、なくても動きますが、たった1行ですし、入れるにこしたことはない気がします。
[公式のドキュメント](https://gymnasium.farama.org/main/api/env/)では以下のように述べられています。
> Closes the environment, important when external software is used, i.e. pygame for rendering, databases


+ stepの返り値の数が増加
以前は```observation, reward, done, info```と4つでしたが、```done```が```termination, truncation```に分けられています。今まではとりあえず終わったら```done```に返り値を渡していたが、終わり方によって返り値を渡すものを変えようといった取り組みでしょうか。
僕の解釈違いの可能性もあるので詳しくは[公式](https://gymnasium.farama.org/main/api/env/)を見てもらうのがいいと思います。

# 終わりに
私自身まだまだ公式のドキュメントの読み込みが足りないので、とても簡単な解説の記事になってしまいましたが、いかがだったでしょうか？今後も学習し、適宜改善しようとは思っていますが、記事の不備、補足、書き方の改善点など意見、アドバイスがあればコメントで教えていただけると幸いです。
# 参考文献
https://qiita.com/Uenosita/items/ab4674dea1c1918670d0

https://zenn.dev/ymd_h/articles/36eff0de1ee5ed

https://gymnasium.farama.org/main/
MySQLのデータ保存先をSQLで確認

# やり方

```sql:
SELECT @@datadir;
```

結果例。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/aa6cab2e-f8c5-baa5-2420-658b31436986.png)

# 参考サイトさん

https://oreno-it3.info/archives/435

# バージョン

Microsoft Windows [Version 10.0.19045.3930]
MySQL 5.7.38-log