OmniAICreator/Qiita-1.07M huggingface dataset:
【UE4】Unreal engine4で長押し処理を実装する

# 結論
Unreal engine4にはデフォルトで長押しを処理してくれるノードが存在しません。
代わりに以下のノードで長押し処理が実装可能です！3秒以上長押しすると文字が出力されます。（最後に追記で書きましたが、Timelineを使った実装方法のほうがスマートだと思います。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115955/d53f8a0f-053a-442b-0fbc-49f381ca948c.png)

# 簡単に解説
Pressすると、GateがOpenになります。3秒後にまだGateがOpenであれば、Print stringが実行されます。
3秒以内にReleasedしてしまうと、GateがCloseされます。

* Tickは使いたくない
* 長押し機能のためだけに変数やカスタムイベントは増やしたくない

と考えて作った実装ですが、そもそも大して重くないので自己満足かもしれません。
でもキレイな実装だと思いませんか？（にっこり）

# なぜDelayではなくRetriggerable Delayなのか
DelayではなくRetriggerable Delayを使っている理由は、以下の例を考えるとわかるかと思います！

**もしDelayだった場合、「1秒間押す。1秒間離してまた押す。さて2回目押してから何秒後に長押し判定されるでしょうか？」**

長押し判定されるべきはもちろん2回目押してから3秒後です。
しかし、Delayを使ってしまうと2回目押してから1秒後に文字が出力されてしまいます。

:::note 
Retriggerable Delayについては以下の記事がわかりやすかったです。
http://meganeo.blog.shinobi.jp/ue4/%E3%80%90ue4%E3%80%91delay%E3%81%AE%E4%BB%B6
:::

# 最後に
何秒間長押しされているかの情報がとりたい、といった場合だと別の方法を使ったほうがいいと思います。
とりあえず単純に長押し判定をしたいときにぜひ使ってみてください！

また、UE5にはデフォルトで長押し処理の機能（Enhanced Input）が実装されているので、この記事はいづれ化石となることでしょう...（）

::::note
短押しと長押しを区別したい場合はこの記事が参考になるかもしれません。マクロにしちゃえば何回も使い回せて素敵ですね。
https://qiita.com/mu-777/items/52e9d6f92034521f26a0
::::

それでは、良きUnrealライフを！

# 追記（2023/12/31）
あれ...？TimeLineでよくね？？？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115955/80415320-624c-e80a-2a19-db75cefa293f.png)
良いコード悪いコードで学ぶ設計入門 【1~3章】 まとめ

[良いコード/悪いコードで学ぶ設計入門](https://www.amazon.co.jp/%E8%89%AF%E3%81%84%E3%82%B3%E3%83%BC%E3%83%89-%E6%82%AA%E3%81%84%E3%82%B3%E3%83%BC%E3%83%89%E3%81%A7%E5%AD%A6%E3%81%B6%E8%A8%AD%E8%A8%88%E5%85%A5%E9%96%80-%E2%80%95%E4%BF%9D%E5%AE%88%E3%81%97%E3%82%84%E3%81%99%E3%81%84-%E6%88%90%E9%95%B7%E3%81%97%E7%B6%9A%E3%81%91%E3%82%8B%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E6%9B%B8%E3%81%8D%E6%96%B9-%E4%BB%99%E5%A1%B2/dp/4297127830)を読んだので、内容をアウトプットとしてまとめていこうと思います。一旦1~3章のまとめです。
サンプルコードが豊富で読みやすく、非常に参考になる良書だと思いました。

## 1章 悪しき構造の弊害を知覚する
データクラスや生焼けオブジェクトなどの悪しき構造と、それによって起こる弊害を紹介されている章です。

### 紹介されていた悪しき構造まとめ
1. 意味不明な命名    
1. 条件分岐のネスト
1. 生焼けクラス

※ 生焼けクラス・・・初期化しないと使い物にならないクラス。未初期化エラーやデータとロジック計算ロジックがばらばらな**低凝縮**な状態となる。

### 感想
この生焼けクラスというのは本書で初めて意識しました。。

## 2章 設計の初歩
大きな設計の前に変数名やメソッドなどの小さい単位の良い設計が紹介されている章です。

### 紹介されていたものまとめ
1. 変数名は丁寧につける
1. 変数を使い回さない
1. メソッドが長くなったとき切り分けて可読性をあげよう
1. データとメソッドは一つのクラスとしてまとめると良い
(↑ほとんど目次の内容ですね...w)

### 感想
普段から意識できる重要なことが紹介されていました。
この章は本当に大切かつ普段から気をつけている内容が書いてあったので、明日からも意識していきたいと思います。

## 3章 クラス設計
2章よりも広い概念であるクラスの設計についての設計で意識すると良い点が紹介されている章です。

### 紹介されていた技法まとめ
1. 生焼けオブジェクトにしない
1. データと計算ロジックをひとまとめにする
1. インスタンス変数を不変する。変更するときは新しくインスタンスを作って変更する
1. 引き数に型をつける
1. 現実の営みにないメソッドは追加しない
1. 値オブジェクト + 完全コンストラクタ = オブジェクト指向設計の基本的な形

### 感想
私は普段Railsを用いて開発をしているのですが、値オブジェクトを知らなかった私は値オブジェクトってRailsのレールに乗っかった形なのか..?と読んでいるときに疑問に思いましたが、ばりばり実装されているのですね。[2,3]

FatModelの解消に導入するか検討していきたいですね〜〜。


### 参考文献
[Rails: Value Objectを検討してみよう（翻訳）](https://techracho.bpsinc.jp/hachi8833/2020_10_27/82304)

[パーフェクト Ruby on Rails](
https://www.amazon.co.jp/%E3%83%91%E3%83%BC%E3%83%95%E3%82%A7%E3%82%AF%E3%83%88-Ruby-Rails-%E3%80%90%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82%E7%89%88%E3%80%91-Perfect/dp/4297114623/ref=pd_lpo_1?pd_rd_w=iHsu9&content-id=amzn1.sym.d769922e-188a-40cc-a180-3315f856e8d6&pf_rd_p=d769922e-188a-40cc-a180-3315f856e8d6&pf_rd_r=J7FXPPZ3AWH275KQ1TG4&pd_rd_wg=ZUFa0&pd_rd_r=edbab17a-568d-44d4-9da2-7bef7ce5fdf4&pd_rd_i=4297114623&psc=1)
指定したキーでレジストリエディタを開くバッチファイル！！！


「 これでどうだ！」 の完成型 . . . と おもいたい。
```OpenRegKey.cmd
@REM Open RegEdit to a Specific Key.
@REM It might be better to run it as administrator.
::https://www.ka-net.org/blog/?p=11415
::https://kimama9.blog.fc2.com/blog-entry-981.html
@if not "%~0"=="%~dp0.\%~nx0" start /max %ComSpec% /c ^""%~dp0.\%~nx0" %*^" & exit
@echo off
setlocal
set HKCR=HKEY_CLASSES_ROOT
set HKCU=HKEY_CURRENT_USER
set HKLM=HKEY_LOCAL_MACHINE
set HKU=HKEY_USERS
set HKCC=HKEY_CURRENT_CONFIG
set localize=1
set chk_args=%1
if defined chk_args set keys=%* & goto :set_keys
for /f "tokens=2,* delims= " %%A in ('reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Applets\Regedit" /v "LastKey"') do set "LastKey=%%B"
:last_key
cls
echo.
if defined LastKey (
    echo "Last referenced key = %LastKey%"
    echo.
    set q=
    set /p q="Open just it [Y,N]? "
) else (
    set q=n
)
if /i "%q%"=="y" (
    set "k=%LastKey%"
    goto :set_last
) else if /i "%q%"=="n" (
    goto :set_keys
) else (
    goto :last_key
)
:set_keys
cls
echo.
set k=
set /p k="Which key to open?: "
:set_last
if defined k set "k=%k:^=^^%"
if defined k (
    set /a localize=localize+2
    if %localize% lss 31 (
        set "k=%k:!=^!%"
        setlocal enabledelayedexpansion
        for /f "tokens=1,2,* delims=\" %%A in ("!k!") do (
            for %%X in (HKCR HKCU HKLM HKU HKCC) do (
                if /i "%%A"=="%%X" set "k=!%%X!\%%B\%%C" & goto :break
                if /i "%%B"=="%%X" set "k=%%A\!%%X!\%%C" & goto :break
            )
            set "k=%%A\%%B\%%C"
        )
        :break
        setlocal disabledelayedexpansion
        call :rmbks
    )
    if %localize% geq 31 (
        echo Full stomach, surrender.
        pause >nul
        echo I can't eat anymore, take a break.
        pause
        start /i %ComSpec% /c ^""%~f0" %keys%^"
        exit
    )
)
set ^"keys=%keys%"%k%" ^"
echo.
echo [ %keys%]
:loop
echo.
set q=
set /p q="More [Y,N]?: "
if /i "%q%"=="y" (
    goto :set_keys
) else if /i "%q%"=="n" (
    call :opn_reg %keys%
) else (
    goto :loop
)
exit
:opn_reg
echo.
:loop2
set "target=%1"
if not defined target exit /b
call :hold
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Applets\Regedit" /v "LastKey" /t REG_SZ /d %target% /f
start regedit /m
shift /1
goto :loop2
:hold
set ep=%time:~-2,1%
if %ep% lss 5 (set /a ep=ep+5) else (set /a ep=ep-5)
:adjust
if %ep% equ %time:~-2,1% exit /b
goto :adjust
:rmbks
if not defined k (
    exit /b
) else if not "%k:~-1%"=="\" (
    exit /b
) else (
    set "k=%K:~0,-1%"
    goto :rmbks
)

```
【Linux】sedコマンドで複数の条件を指定する方法


## 指定方法

以下の方法で複数の条件を指定できます。  

```terminal
sed -e 's/sample/EXAMPLE/g' -e 's/example/SAMPLE/g'
```

```terminal
sed -e 's/sample/EXAMPLE/g; s/example/SAMPLE/g'
```
php基礎_phpmyadmin①

# 画面を開く
### http://localhost/phpmyadmin/　
![スクリーンショット 2023-03-26 23.44.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193952/d916ca11-328c-cf94-f01b-dabed471b7f1.png)
![スクリーンショット 2023-03-26 23.44.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193952/707b3f5c-3e33-50c3-8a7f-dba5d2388a75.png)
# データベース作成
![スクリーンショット 2023-03-26 23.51.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193952/13c73a21-6e1c-422f-1bd3-0adb7ff24380.png)
![スクリーンショット 2023-03-26 23.51.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193952/a3f2de18-a376-bef2-3a20-ac1472b8d1dc.png)

# テーブル作成
![スクリーンショット 2023-03-26 23.53.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193952/c7aa576e-6f33-68bc-e8de-f80f8327822e.png)

![スクリーンショット 2023-03-26 23.53.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193952/ec5ca2d1-efda-9e1b-fc20-c2f710a3d0ae.png)
Pythonで二分木（バイナリツリー）を描く方法

# 二分木（バイナリツリー）とは
各ノードが最大で2つの子ノードを持つ木構造のデータ構造です。
![binary_tree.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3299009/7189f9d8-8400-a665-7ecd-c7a59beb9e7b.png)
解説例：

https://qiita.com/uniTM/items/324883808a62c6938ac0

# Graphvizとは
Graphvizというライブラリを使用します。
DOT言語を使用してグラフを描画するためのオープンソースライブラリです。

Wiki:

https://ja.wikipedia.org/wiki/Graphviz

# 作図プログラム
```Python:Python
from graphviz import Digraph

class Node:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

def draw_tree(node, dot=None):
    if dot is None:
        dot = Digraph()
    if node is None:
        return
    dot.node(str(node.value), label=str(node.value))
    if node.left:
        dot.edge(str(node.value), str(node.left.value))
    if node.right:
        dot.edge(str(node.value), str(node.right.value))
    draw_tree(node.left, dot)
    draw_tree(node.right, dot)
    return dot

root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.left.right = Node(5)
root.left.right.right = Node(6)
root.left.right.left = Node(7)
root.left.right.left.right = Node(8)
root.left.right.left.left = Node(9)

draw_tree(root)
```

出力：
![tree.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3299009/f92f53f0-e992-fe22-0cd5-0c3951471607.png)
サーチコンソールにインデックスが登録されなかった時の話 `$_SERVER`を使うときにはちょっと注意が必要

# ことの始まり
わけあって､ユーザーが使用する言語によって表示するページを変えようとした｡
方法としては､`$_SERVER['HTTP_ACCEPT_LANGUAGE']`を使い以下のような感じで分岐させていた
```php:※実際のコードとは異なります
if ((substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0,2)) == 'ja'){return redirect()->route('日本語のページ');}
return redirect()->route('英語のページ');
```
上記の方法で､**本番環境でも一切の問題なくwebアプリは正常に動作した**｡
しかし､サーチコンソールではインデックスが登録されない｡

# 原因(ただの推測)
`$_SERVER['HTTP_ACCEPT_LANGUAGE']`の部分を消してみたらインデックスが登録されたので｡
クローラーでは`HTTP_ACCEPT_LANGUAGE`にあたる値がなかったため`$_SERVER['HTTP_ACCEPT_LANGUAGE']`の部分で止まってしまったと推測

# 対処
今現在はバックエンドで自動的に分岐するのは諦めて､ユーザーが言語を選ぶようにした｡

`try catch`を使った以下のような対処もあるが､訳あって今回はこの方法は取れなかった｡
```php
try {
    if ((substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0,2)) == 'ja'){return redirect()->route('日本語のページ');}
} catch (\Throwable $th) {
    //エラーが起きてしまった時
    return redirect()->route('英語のページ');
}
    // ユーザーの言語が日本語ではない
    return redirect()->route('英語のページ');
```


# 駄文
クローラーの仕様を全く知らなかったため､このエラーの解決に1週間以上かかってしまった｡
かなりマイナーなエラーかもしれないが､他の人が､私と似たようなエラーが起きた時にこの記事を参考にして短い時間でエラーが解決すると私としても嬉しい

フロントエンドの方にvueを使っているのだが以下の方法をこの記事を書いているうちに思いついた｡
1. 英語のページに遷移
2. mountedでユーザーの言語を判断して
2. ユーザーの言語が日本語の場合､日本語のページに遷移する
メソッドのアクセス権

## メソッドのアクセス権について

メソッドのアクセス権は、そのメソッドがどの範囲から呼び出されることができるかを制御するもので、主に3つの種類があります。

- public：publicなメソッドは、どこからでもアクセス可能です。これがデフォルトのアクセス権です。
- protected：protectedなメソッドは、同じクラスまたはサブクラスからのみアクセス可能です。
- private：privateなメソッドは、同じクラス内からのみアクセス可能です。サブクラスや外部からのアクセスはできません。

## RUBYにおけるメソッドのアクセス権の文法

RUBYでは、メソッドのアクセス権を指定するには、以下のように記述します。

```ruby
class MyClass
  def public_method
    # パブリックメソッドの処理
  end

  protected

  def protected_method
    # プロテクテッドメソッドの処理
  end

  private

  def private_method
    # プライベートメソッドの処理
  end
end
```

## JavaScriptにおけるメソッドのアクセス権の文法

JavaScriptでは、メソッドのアクセス権を指定することはできません。しかし、オブジェクト指向プログラミングの概念を利用して、プライベートメソッドを実現することができます。以下は、クロージャを利用したプライベートメソッドの例です。

```javascript
function MyClass() {
  var privateMethod = function() {
    // プライベートメソッドの処理
  }

  this.publicMethod = function() {
    // パブリックメソッドの処理
    privateMethod();
  }
}
```

このように、JavaScriptではクロージャを使って、プライベートメソッドを実現することができます。


※クロージャーとは

クロージャ（closure）とは、関数とその関数が参照する変数（自由変数）を一体化したものです。

JavaScriptなどの言語では、関数内で定義された変数は、その関数のスコープ内でのみアクセス可能です。しかし、クロージャを使用することで、関数のスコープ外でその関数が参照する変数を保持することができます。

具体的には、関数の中で別の関数を定義し、その中で外側の関数の変数を参照するようにすることで、クロージャを作成することができます。

以下は、JavaScriptでのクロージャの例です。

```javascript
function counter() {
  var count = 0;

  return function() {
    count++;
    console.log(count);
  }
}

var c = counter();
c(); // 1
c(); // 2
c(); // 3
```

上記の例では、counter関数の中でcount変数を定義しています。そして、counter関数内で無名関数を定義し、その中でcount変数を参照しています。最後に、counter関数から無名関数を返すことで、クロージャを作成しています。

これにより、c関数を呼び出すたびにcount変数が1ずつ増えていき、値がコンソールに出力されます。

クロージャは、変数のスコープをより柔軟に制御することができるため、関数型プログラミングやオブジェクト指向プログラミングなどで幅広く活用されます。
[Vue warn]: Do not use built-in or reserved HTML elements as component id: xxx

# エラー

コンポーネントを作成していたら、急にこんなエラーが。

```
[Vue warn]: Do not use built-in or reserved HTML elements as component id: main
```

簡単に訳してみると、
```
「main」はHTML要素で使われるので、コンポーネントとして使用できません
```
となっています。

# 原因
原因と言っても、書いている通りなのですが、
HTMLでタグ名として使われている命名はコンポーネント名として使うことができません。

`main`同様に`header`,`input`,`footer`みたいなものも使うことができないです。

# 解決方法
全く関係ない命名に変えることでも可能ですが、
どうしても使いたい場合は、コンポーネント名を`TheMain`とかにするとエラーは出なくなりました。

そもそもなのですが、コンポーネントの規約として、
ページごとに一つしか使われないコンポーネントは、プレフィックスに`The`をつけること
というルールがあるようです。

公式より↓
>常に 1 つのアクティブなインスタンスしか持たないコンポーネントは、1 つしか存在しえないことを示すために The というプレフィックスで始めるべきです。

https://jp.vuejs.org/v2/style-guide/index.html#%E5%8D%98%E4%B8%80%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E5%90%8D-%E5%BC%B7%E3%81%8F%E6%8E%A8%E5%A5%A8
ABC294-D BankをPythonで

# 0 はじめに

## 0-1 こんにちは

こんにちは。
結構前にABCの解説を投げてた人です。
久し振りに、ABC解説以外に投稿するネタが降って来たので書きます。

## 0-2 この記事について

先日のABCのD問題「Bank」―C++だとsetで殴れる問題―をPythonでデータ構造を使って解いてみます。
確認していないので分かりませんが、既にこのような記事を書いている方がいたらごめんなさい。

# 1 本題

# 1-1 問題の概要
N人の人と受付があります。
次のようなクエリがたくさん降ってくるので随時処理して答えて下さい。

`1` : 受付に呼ばれていない人の中で最小番号の人が呼ばれる
`2 x` : 人`x`(既に呼ばれている)が初めて受付へ行く
`3` : 既に受付に呼ばれているが行っていない人のうち最小番号の人が呼ばれる
　　その番号を出力せよ

要は、**最小値の取得**と**任意の要素の削除**が高速に出来ればよいです。

# 1-2 C++だとどのように殴れるか

C++だと`std::set`を用いて殴るだけです。
`std::set`はつよくなった「集合を扱う型」みたいな感じで、こんなことが出来ます : 

(Sはset型の変数、nは引数とします)
| できること| 記述| &nbsp;&nbsp;&nbsp;計算量&nbsp;&nbsp;&nbsp; |
| :---- | :---- | :----- |
|  要素の挿入  |  `S.insert(n)`|$O(\log N)$|
|  要素の削除 | `S.erase(n)` | $O(\log N)$ |
|要素の存在判定|`S.count(n)`|$O(\log N)$|
|最小値の取得|`*begin(S)`|$O(1)$|
|最大値の取得|`*rbegin(S)`|$O(1)$|

**つよいですね。**

要素の挿入/削除などに$O(\log N)$掛かるのは少し気になってしまいますが(?)、それでいて最大値/最小値も$O(1)$で取得できるのはつよいです。

実装例を貼っておきます。

<details>
<summary> C++での実装例 </summary>

```cpp
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
#define rep(i,N,M) for(int i=N; i<M; i++)

int main(){
  int N,Q; cin>>N>>Q;
  
  set<int> called;
  int uncalled=0;
  rep(i,0,Q){
    int n; cin>>n;
    if(n==1){
      called.insert(uncalled);
      uncalled++;
    }else if(n==2){
      int x; cin>>x; x--;
      called.erase(x);
    }else{
      cout<<*called.begin()+1<<endl;
    }
  }
}
```

受付に呼ばれる人の番号は絶対に$1,2,3,...$となっている為、呼ばれた度に$1$増やせばよく、
`set`で管理する必要があるのは呼ばれた人の番号のみです。
上のように殴れます。
計算量は$O(Q \log N)$で十分高速ですね。(実行時間 : 717ms)

</details>

## 1-3 Pythonでは？

**残念ながら、C++の`std::set`に相当するものが`Python`の標準ライブラリにはありません！！！**

え、Pythonの`set`はC++だと何なんだって？
それがね、`unordered_set`なんですね....(哀)

最小値の取得と要素の削除が**両方**高速に行えるデータ構造はPythonにはありません。

## 1-4 Pythonではどうできるのか

そこで、`heapq`と`set`を併用することを考えます。(`heapq`ベースで書くことを前提とします)

この問題を解く上で足枷となっているのは`2`のクエリです。
`1`は、C++のところで述べた通りで、簡単です。
`3`も、最小値を取得するだけなので`heapq`を用いるなら簡単です。

うーん、
あ！天啓が降って来た━━━━(ﾟ∀ﾟ)━━━━!!

こんな工夫をしてみましょう。
> `heapq`で既に呼ばれた人(受付に行ったとは限らない)を、`set`で既に受付に行った人を管理する。
`2`のクエリでは受付に行く人を`set`に追加する。
`3`のクエリでは、最小値を取得&`heappop`を、`set`に含まれない要素が出て来るまで繰り返し、`set`に含まれない最小の値が答え。

こうすることで、`set`と`heapq`だけで殴ることができます。
`heappop`の回数も多くても$N$回なので計算量は$O(Q \log N)$で十分高速です。
賢くないですか？

実装してみました。
<details>
<summary> Pythonでの実装 </summary>

```py
from heapq import heapify, heappop, heappush

N,Q=map(int,input().split())
uncalled=0
called=[]; heapify(called)
gone=[0]*N;
for i in range(Q):
  query=list(map(int,input().split()))
  c=query[0]
  if c==1:
    heappush(called,uncalled)
    uncalled+=1
  elif c==2:
    n=query[1]; n-=1
    gone[n]=1
  else:
    while True:
      mn=called[0]
      if not gone[mn]:
        print(mn+1)
        break
      heappop(called)
```

特にクエリ`3`の芸術点高いと思います。
実行時間は328msでした。はやい！！(入力高速化しましたが)
</details>

# 2 おわりに

もっと速い別解があると知って虚無の顔をしています。
こんな感じのことをしてPythonでも解けるよ～みたいな問題には[ABC253-C](https://atcoder.jp/contests/abc253/tasks/abc253_c)がありましたね。

いいね頂けると幸いです。
お読み頂きありがとうございました。
GitHubへSSH鍵を登録する方法

# GitHubへSSHで接続する
GitHubへSSHで接続するために、SSH鍵を生成、生成した公開鍵をGitHubへ登録する。その手順を下記に示す。

## SSH鍵の生成  
MacでSSH鍵を生成する。  
まずは、ホームディレクトリ以下にある.sshディレクトリへ移動する。
```
# ホームディレクトリへ移動
$ cd ~
# カレントディレクトリの確認
$ pwd
/Users/ユーザー名
# .sshディレクトリへ移動
$ cd .ssh/
```
続いて、SSH鍵を生成する。
`-t`で鍵のタイプ、`-b`で鍵のビット長、`-f`で、保存するファイル名を指定する。
今回は、RSA 4096bitで、ファイル名をid_rsa_github とした。
途中、Enter passphraseと、パスフレーズを求められる。任意のパスフレーズを設定することができるが、今回は何も設定せず、Enterを入力した。

```
$ ssh-keygen -t rsa -b 4096 -f id_rsa_github
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): <-- Enterを入力
Enter same passphrase again: <--Enterを入力
Your identification has been saved in id_rsa_github
Your public key has been saved in id_rsa_github.pub
The key fingerprint is:
SHA256: (省略)
The key's randomart image is:
+---[RSA 4096]----+
(省略)
+----[SHA256]-----+
```
コマンド実行後、id_rsa_github(秘密鍵)、id_rsa_github.pub(公開鍵)の2つが生成されていることを確認する。  
```
$ ls
id_rsa_github	id_rsa_github.pub
```

## SSH公開鍵の確認
生成したSSH公開鍵を確認する。
```
$ cat id_rsa_github.pub
ssh-rsa AAA...(省略)
```

## GitHubへの登録
GitHubのトップページ、画面右上の自分のアイコンから   Settings > SSH and GPG keys > SSH keys > New SSH key  
から、生成したSSH公開鍵を登録。

## configの設定
続いて、configの設定
```
$vim ~/.ssh/config
Host github github.com
    User git
    HostName github.com
    IdentityFile ~/.ssh/id_rsa_github
    IdentitiesOnly yes
```
`IdentitiesOnly`は、`IdentityFile`で指定した秘密鍵のみを使用するかどうかを意味するので、yesとしておく。


## GitHubへの接続確認  
次のコマンドで、接続できることを確認する。  
下記のようなメッセージが出てくれば接続確認完了。  
`-T`は、仮想端末の割り当てをしないオプション。
sshでGitHubにシェルアクセスすることはできないので、そんな時につけるオプションくらいに覚えておけばOK。

```
$ ssh -T git@github.com
Hi (GitHubアカウント名)! You've successfully authenticated, but GitHub does not provide shell access.
```
フロートスイッチを使って水位を測定してみた

# はじめに
ラズパイで水位がどれくらいかを検知するためにフロートスイッチを使ってみました。

ラズパイでフロートスイッチを使って水位の測定を行う方法についての記事が少なかったので、備忘録もかねて投稿します。

# フロートスイッチとは
![フロートスイッチ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3300264/f8aa2d1d-54e2-7343-109e-f50694b148f9.jpeg)
水位を検出するために使用される機器です。

### 動作原理
![フロートスイッチ原理.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3300264/6aa3788f-f8e8-4713-ff24-858eae8967ad.jpeg)

水位によって浮きの高さが変わり、その位置によって電気回路が開閉されます。
今回はラズパイを使って電気回路の開閉を検知することで水位の測定を行いました。

詳しい動作原理は下記サイトを参照ください。
[フロートスイッチの動作原理](https://www.nippo-co.com/odm-kiban/odm-014/)

### 水位センサーモジュールと比べて
![水位センサーモジュール.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3300264/ba150cb2-9216-58af-593b-d027c24100b0.jpeg)
電子工作で水位を測定できるセンサーとして、水位センサーモジュールがあります。
しかし実際に使ってみたところ、水から出した後も表面に残っている水滴によって水中にいると判定されました。
"水分があるかどうか"の検知は水位センサーモジュールの方が得意ですが、
"水面がどこにあるか"の検知はフロートスイッチの方が得意だと思われます。

# テスト
コップにフロートスイッチを設置し、あふれない程度の高さに水位が達するとLEDが点灯するようにしました。

### フロートセンサー
今回使用したフロートセンサーのスペックの一部を載せます。
詳しくはリンクを参照ください。

メーカー：SEAFRONT
動作電圧(おそらく最大電圧)：110V
動作電流(おそらく最大電流)：3A
[フロートスイッチ販売ページ](https://www.amazon.co.jp/gp/product/B07T55CKTP/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1)

### 電子回路
![回路解説.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3300264/c45ebd65-0a41-91b3-1e39-d528b350446b.jpeg)

写真で申し訳ないです、、、

フロートスイッチの抵抗値は1000Ωにしましたが、電流が3Aを超えないものであればなんでもいいと思います。
(100Ωでも動作しました。)

### プログラム
```python
import RPi.GPIO as GPIO
import time

FloatSwitchGpio = 18        #フロートスイッチ用端子番号
LedGpio = 16                #LED用端子番号
MeasurementTime = 60        #測定回数
WaitTime = 1                #測定待ち時間

#端子の設定
GPIO.setmode(GPIO.BCM)
GPIO.setup(FloatSwitchGpio, GPIO.IN)
GPIO.setup(LedGpio, GPIO.OUT)

#フロートスイッチのテスト
for i in range(MeasurementTime):
    #フロートスイッチの状態を取得
    #水位が足りていないと0が、水位が目標に達すると1が代入される
    WaterLevel = GPIO.input(FloatSwitchGpio)
    #コンソールに出力
    print(WaterLevel)
    #水位に応じてLEDを点灯
    if WaterLevel == True:
        GPIO.output(LedGpio, True)
    else:
        GPIO.output(LedGpio, False)
    time.sleep(WaitTime)

GPIO.cleanup
```
1秒ごとにフロートスイッチの状態を取得し、コンソールへ値を出力し、状態に応じてLEDを点滅させています。

# 結果
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">フロートスイッチのテスト動画 <a href="https://t.co/hXGIJjzXo9">pic.twitter.com/hXGIJjzXo9</a></p>&mdash; Nanana (@hamadaira0412) <a href="https://twitter.com/hamadaira0412/status/1639994006355734534?ref_src=twsrc%5Etfw">March 26, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

水面の上昇に伴い、浮きも上昇しました。
浮きがフロートスイッチの上限に達するとLEDが点灯しました。

動画に映っていませんが、一定時間放置するとフロートスイッチが上限に達しているにもかかわらずLEDが点滅を繰り返しました。
コンソールを確認すると0(水位が足りていない)との表示もされていました。

フロートスイッチが安かったので感度が悪いのかなと思っています。
次は少し高いものを買って試してみようと思います。
GitHub ActionsでTerraformのplanなどを検証する

terraform plan などをGitHub Actionsで実行する方法を紹介します。
AWSリソースを扱う場合、stepを追加する必要もあるので、そちらを併せて紹介できたらと思います。

## はじめに

筆者のTerraform のリポジトリのディレクトリ構造は下記のようになっています。

```
.
├── .github
│   └── workflows
│       └── test_terraform.yml # これを扱います
├── .gitignore
├── docker-compose.yml
└── src
    ├── .terraform/
    ├── .terraform.lock.hcl
    ├── module_aws.tf
    ├── modules
    │   └── aws/ # この配下にresource などを記載したファイルを置いている
    └── providers.tf
```

## GitHub Actionsのworlflow を作成する

```yaml:.github/workflows/test_terraform.yml
name: Terraform test

on:
  pull_request:
    types: [opened, synchronize, reopened, ready_for_review]

jobs:
  terraform_test:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v3
      - name: configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1
      - name: terraform setup
        uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.4.2
      - name: Terraform fmt
        id: fmt
        run: terraform fmt -check
        continue-on-error: true
        working-directory: ./src
      - name: Terraform Init
        id: init
        run: terraform init
        working-directory: ./src
      - name: Terraform Validate
        id: validate
        run: terraform validate -no-color
        working-directory: ./src
      - name: Terraform Plan
        id: plan
        run: terraform plan -no-color
        continue-on-error: true
        working-directory: ./src
```

workflowは、terraformの`fmt`、`validate`、`plan`、そしてそれらを実行するための`init` を実行しています。
.ymlファイルを簡単に解説します。

- `on`
  - workflowを実行するタイミングを設定しています。
  - 今回の場合、Pull Requestの作成、更新、reopen、DraftからReady for reviewに変更された時にworkflowが実行されます。

```yml
on:
  pull_request:
    types: [opened, synchronize, reopened, ready_for_review]
```

- `uses: actions/checkout@v3`
  - GitHub公式に提供されているActionになります。
  - check outを実施して、workflowでアクセスすることができるようになります
- `uses: aws-actions/configure-aws-credentials@v2`
  - 筆者の環境では、AWSのリソースを利用しているため、credential設定が必要でした
  - こちらはAWSよりActionが提供されています
    - これにより、AWSの認証情報とリージョン環境変数を設定し、他のアクションでを利用することができます。
    - (今回はsecretsでAWSのアクセスキーを渡していますが、外部にキーを渡しているため、`role-to-assume`などを利用する方がよさそうです。[参考](https://github.com/aws-actions/configure-aws-credentials))
- `uses: hashicorp/setup-terraform@v2`
  - Action内でTerraform 利用に必要な設定を行なっています。
    - 引数で`terraform_version`　を渡します。自分の使っているTerraform のバージョンと合わせましょう。

公式で提供しているActionを組み合わせることで、手軽に作ることができました。
次は、planの結果の出力などをやってみたいと思います。
mysqlで擬似materialized_viewを作ろうとした時のDuplicate entry '207453' for key 'xxx.PRIMARY' の解決例

## 何があった？
[こちらの記事](https://it7c.hatenadiary.org/entry/20100730/1280501734)に従って、mysqlで擬似materialized_viewを作り、triggerの設定（下記sql）後にinsertを試したところ出現しました。

エラー文を調査すると、auto_incrementの値がおかしい、または設定がされていないことが原因という記事がヒットしました。

しかし今回新しく作成したテーブルは全てのカラムにコピーしてきた値を入れる想定しており、primary_keyからauto_incrementを意図的に外していたので、題名のエラー文が出る意味がわかリませんでした。

とりあえず指示通りauto_incrementを設定して値も揃えてみましたが同じエラーが出続けました。


```sql:失敗したsql
DELIMITER //
CREATE TRIGGER test_table_insert
AFTER INSERT ON organizations FOR EACH ROW
BEGIN
  INSERT INTO test_table (
    organization_id,
    organization_name,
    prefectures.id,
    prefectures.name
  )
SELECT  -- ←prefecturesテーブルをjoinして一緒にinsertしたいので、selectを使ってinsertしている
  NEW.id,
  NEW.name,
  prefectures.id,
  prefectures.name
FROM
  organizations
  left join prefectures on prefectures.id = organizations.prefecture_id
where
  organizations.deleted_at is null  
order by
  organizations.id;
END;
//
DELIMITER ;
```



## 結論（原因）
triggerに使ったselect文が間違っていました。

具体的には新しくinsertする値をselectで指定する場合はwhere区で`organizations.id = NEW.id`と指定しなければならなかったのに気づかず、その記述なしで設定していました。

結果としてNEW.id以外のidがinsertで重複してしまっていたのですが、一番idの大きいNEW.idがエラーの代表idとして重複のエラー文に表示されていました。


## 解決方法

triggerでselectを使ってinsertする場合は、where区に`organizations.id = NEW.id`を指定することで解決できました。

```sql:成功したsql
DELIMITER //
CREATE TRIGGER test_table_insert
AFTER INSERT ON organizations FOR EACH ROW
BEGIN
  INSERT INTO test_table (
    organization_id,
    organization_name,
    prefectures.id,
    prefectures.name
  )
SELECT
  NEW.id,
  NEW.name,
  prefectures.id,
  prefectures.name
FROM
  organizations
  left join prefectures on prefectures.id = organization.prefecture_id
where
  organizations.deleted_at is null
  AND organizations.id = NEW.`id`  -- ←ここを追加したらエラーが解消された
order by
  organizations.id;
END;
//
DELIMITER ;
```
Ubuntu (Jetson) で初心者用備忘録

# 背景

Jetson を使うことになって、Ubuntu をよく使う。
初期状態からや、ある環境を復活させて、とか。
で、そうなるとやっぱりショートカットやよく使う機能を調べて記録しておく必要があるよねってお話

専用環境でずっと作業出来ることばかりじゃないので、最近はデフォルトのショートカットを利用するようにしてる

## 環境
JetPack 4.6.3
Ubuntu 18.04

# ショートカット

|キー|目的|補足|覚える？|
|-|-|-|-|
|CTRL + Z|Undo||必須|
|CTRL + Shift + Z|Redo|Windows とは違うのね|必須|
|CTRL + L|アドレスバー表示|File Manager でパスコピーや編集する際に便利|必須|
|CTRL + C<br>CTRL + Shift + C|コピー||必須|
|CTRL + V<br>CTRL + Shift + V|貼り付け|Terminal 等では Shift を追加する。Windows の感覚で書式なし貼り付けで失敗することも<br>クリップボード履歴も欲しいね。Clipboard Indicator や clipit をデフォルト対応されないのかな|必須|
|CTRL + ALT + T|Terminal 起動|地味に便利|必須|
|CTRL + Shift + N|ファイルマネージャーを開く||必須|
|Suprt(win) + N|ネットワーク設定を開く||時には|
|CTRL + Num|Window スナップ|テンキーの番号に応じた位置へWindow を移動する|必須|
|Super(Win) + Arrow|Window スナップ|Arrow Key の方向に応じて Window を移動する|必須|

## terminal 用
|キー|目的|補足|覚える？|
|-|-|-|-|
|CTRL + Shift + N|CWD での新しいTerminal起動|同一フォルダでTerminal 開始出来て便利。<br>ただしsetup.bash 等は実施必要|必須|
|CTRL + Shift + F|Terminal 内での検索|ビルド結果等での検索に便利|必須|


# 便利機能
この辺は、もっと使いやすいようになんとかならないのか・・とも。
VS Code とかツール入れると容量圧迫が頭の痛い問題

## 標準出力＋ファイル出力
> command | tee -a logfile.txt

ビルドログなどをそのままログに残したい時などに便利

## ビルドしてインストールした場合のアンインストール方法　python: setup.py の場合

PyTorch とかビルドしてインストールする場合パッケージ管理してないので、アンインストールが大変・・。

1. ソースコードのディレクトリに移動して、インストールしたファイルの一覧を保存
    - ex.) `sudo python3 setup.py install --record files.txt` 
1. files.txt の内容を使って、インストールしたファイルをすべて削除する。
    - ex.) `cat files.txt | xargs sudo rm -rf`

## バージョン切り替え

以下、Python の切り替え例

> - sudo update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.8 1
> - sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 2
> - sudo update-alternatives --config python3

ただ、このやり方でもインストールフォルダが違うと、ダメな気がする。
その場合、PATH の左方優先を利用して、優先順位を調整してやる

## 容量調査

良く圧迫されるので、不要なものを判断して削除していかないと・・

> ncdu

をインストールすると、ファイラーみたいにして見られる。
標準の視覚化の方がよいかも？

## システムモニター

> gnome-system-monitor

吹っ飛んでるのか、ビルドしてるのか判断がわかんないときとかに

## ファイル検索
> `find /path/to/dir -type f -mtime 0 -name "*.txt"`
>   - mtime 日付
>   - mmin 分
>   - **M**dify time , **C**hange time, **A**ccess time という三種類
>       - ex.) atime, cmin
>   - 負数：以内
>       - ex.) mmin -10: 10 minutes 以内
>   - 正数：以前

https://qiita.com/baku2san/items/5ccc091478cd37e66460

## 標準出力に出しつつ、ログファイルへ書き込み
>  command | tee -a logfile.txt

# あとがき

Ubuntu version をアップデートしたらどうすっかな。
[drogon]mariaDBと接続するまでの道のり


## 当時の状況
POSTリクエストからデータを受け取り、drogonのDB接続機能をつかって、mariaDBにデータを格納する部分をつくっていたところです。
drogonの情報はあんまり見られない（特にmariaDBとは）のでここにまとめてみます。間違ってたらごめんなさい。

## 環境
OS :Debian10 (chromebookのlinuxモード)
DB :mariadb Ver 15.1
開発言語 :c++(フレームワークはdrogon)
ファイアウォール :ufw

## エラーに詰まるまでの経緯
こちらのチュートリアルが充実していて大いに参考にしました。
https://github.com/drogonframework/drogon/wiki/ENG-08-1-DataBase-DbClient

c++でありながら、さすがフレームワーク。これによると、データベースの接続はこの一文だけ書けば、接続が済んだオブジェクトを提供してくれるみたいです。

```c++
    auto clientPtr = drogon::app().getDbClient();
//↑これでデータベースに接続する
```

上記のgetDbClient()と簡単なSQLを実行するプログラムを書いて、buildディレクトリでコンパイル。アクセスしてみて返却値が得られれば成功のはずでしたが。。

## FATAL no database is supported by drogon

![databasenotsupport.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1161923/f4e0811b-8b62-ae3c-ed8b-34a48fe87b42.png)

drogonが接続先のデータベースを見つけてないんですね。それはあるかもな〜ってなります。でも、じゃあどうやってdrogonに認識させるのかわからない。

答えはマニュアルのインストール要件の箇所に書いてました。
https://github.com/drogonframework/drogon/wiki/ENG-02-Installation#System-Requirements　
（日本語訳を↓に引用）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1161923/34dc1766-d4e2-6e66-dbd2-736b734b460c.png)

どうやらdrogonを再インストールしないといけないらしいです。
また、mariadbとの接続をサポートするライブラリもインストールされてなかったので、まずこちらからやりました。
```shell
sudo apt install libmariadbclient-de
```

これが完了してるとdrogon再インストール時にこんな文章が出てきます。
![findmariadb.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1161923/014570f7-c8c1-abe1-922f-44220b7904e3.png)

おっ、読み込んだって！これは期待♬

## Lost connection to MySQL server at 'handshake: reading initial communication packet'

再びdrogon::app().getDbClient()を使ってDB接続を試みます。今度は「接続が切られた」とエラーが出ました。

![lostConnection.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1161923/97ca17eb-bfe4-89c6-d3d8-bb8f4ccbe98f.png)
 
※[余談]しかも、この状態になるとプロセスはctrl+Cを押しても終了できなくなるので厄介です。（http接続の途中なので切れないとか？）仕方なくより強力なプロセスの終了を調べにひとっ走りしてきたので↓を参照

```shell
ps -aux | grep [実行ファイル名]
#私の場合はbackBookというなのでこれで検索しました
kill -9 [プロセス番号]
#このプロセスは強力なので-9オプション(SIGKILL)をつけないところせない
```

さて、コネクションの問題ということなので、まずはconfig.jsonを見直します。間違えてもピンポイントで指摘してもらえないやつなのでややこしい思いをしました...。mariaDB用の正しい形式は以下のとおりです。

```json:config.json

    "db_clients": [
        {
            "rdbms": "mysql",
            "host": "localhost",
            "port": 3306,
    //mariadbのポート番号はこれらしい
            "dbname": "databasename",
            "user": "username",
    //最初localhost@usernameにしていた..
            "passwd": "ユーザーのログインパスワード",
```

また、DBのログイン情報を事前に仕込んでおくファイルとして、config.jsonが設置されています。実はデフォルトではコメントアウトされてて読み込まれなかったり、パスが違う（なぜ？？）ので直しておきましょう

```c++:main.cc
#include <drogon/drogon.h>
using namespace drogon;

int main()
{
    /*コメントがずらっとならんでる*/
    LOG_INFO << "Server running on 127.0.0.1:8848";
    app().loadConfigFile("../config.json");
    //↑ここです
    app().addListener("127.0.0.1", 8848).run();
}
```

たぶん、ここまでくれば接続できる人が多い気がしますが、
それでもなお接続できないとしたら、ファイアウォールの設定を見直すべきかもしれません。
筆者はufwを使用しているのでufwnの設定を残します

```shell
sudo ufw allow from 127.0.0.1
#↑drogonで使用しているアドレス（main.ccで設定中）を許可する。
sudo ufw enable
```

そうこうしている内に接続が確立しておりました。
webpack.config.jsの記述について

`webpack.config.js` は `webpack`の設定ファイルです。エントリーポイントや、ファイルの出力場所、プラグインなどを設定することができます。

## サンプル

```webpack.config.js
const path = require('path')
const webpack = require('webpack')

module.exports = {
  entry: {
    index: path.join(__dirname, 'src', 'index.js')
  },
  output: {
    path: path.join(__dirname, 'out'),
    filename: 'main.js'
  },
  devtool: 'cheap-module-eval-source-map',
  target: 'node',
  module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: "babel-loader",
      },
    },
    {
      test: /\.css$/,
      use: ["style-loader", "css-loader"],
    }
  ]
}
````
`entry` : バンドルの起点となるファイルを指定します。ファイルの依存関係を解決しながらバンドルされます。
`output` : 出力先のディレクトリやファイル名を指定します。特に指定しなければ、distディレクトにmain.jsが出力される初期設定です。

---

`path.join` : Node.jsの標準モジュール`path`が提供しているメソッドです。ディレクトリ名を表すパスと、ディレクトリ名あるいはファイル名を結合します。
`__dirname` : 現在実行中のソースコードが格納されているディレクトリパスが格納されています。
```
path: path.join(__dirname, 'out'),
```
---

`devtool` : ソースマップ生成の有無と、どのように生成されるかを指定するオプションです。細かい設定を行うにはソースマップのスタイルを選択します。
※SourceMap = オリジナルファイルから変換されたソースをマップしたファイルで、ブラウザがオリジナルのソースを再構築し、その再構築されたオリジナルを表示できるようにするもの

---

`module`: Webpack がどのようにモジュールを読み込み、変換するかを定義するためのオブジェクトです。
`rules` : 読み込まれたモジュールに対して、どのような変換を適用するかを定義するためのプロパティです。

この例では、JSファイルの変換に `babel-loader` を使用し、CSSファイルの変換に `style-loader` と `css-loader` を使用するルールが定義されています。`test`プロパティは、ファイルのパスに正規表現を適用して、対象のファイルかどうかを判別します。`use`プロパティは、各ルールがファイルに適用する`loader`を定義します。`exclude`プロパティは、変換の対象から除外するディレクトリを指定するためのオプションです。


## 最後に
ReactやTypescriptなどを使用する場合の記述は異なるので、使いながら学んでいこうと思います。
ラズベリーパイゼロを用いた環境モニタリングシステム

![awsiot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1194297/164a9e58-053e-9289-c1a9-b8d9415313b3.png)

## プロジェクト概要
https://github.com/T44M/portfolio
このプロジェクトでは、ラズベリーパイとBME280センサーを使用して、室内の温度、湿度、気圧をリアルタイムで監視する環境
モニタリングシステムを構築しました。データはAWS IoT Coreを経由してInfluxDBに保存され、Grafanaで可視化されます。さらに、自宅にVPNサーバーを設置して、スマートフォンから安全にネットワークにアクセスし、ラズベリーパイにSSH接続できるようにしました。また、Terraformにて、AWS構築を簡易にしています。

## 技術スタック
![AWSIoT.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1194297/71cef338-25c6-036d-e0fc-08bcdee1dd28.png)


ハードウェア: Raspberry Pi, BME280（温度、湿度、気圧）
インフラ: AWS IoT Core, AWS Lambda, EC2, Cloudwatch, 
DB: InfluxDB
言語: Python, Json, hcl
プロトコル: MQTT, SSH, VPN(Wireguard)
その他: Grafana, Github, Slack 


## 主要機能

1, ラズベリーパイで環境データの収集: Pythonスクリプトを使用して、ラズベリーパイに接続された環境センサーから温度、湿度、気圧のデータを定期的に収集します。
2, AWS IoT Coreとの通信: MQTTプロトコルを使用して、ラズベリーパイから収集されたデータをAWS IoT Coreに送信します。これにより、データは安全かつ効率的にクラウドに転送されます。
3, データ処理と保存: AWS Lambdaを使用して、IoT Coreから受信したデータを処理し、InfluxDBに保存します。このデータベースは、時系列データの効率的な格納とクエリに適しています。
4, データの可視化: Grafanaを使用して、InfluxDBに格納されたデータをリアルタイムで可視化し、ダッシュボードを作成します。これにより、ユーザーは環境データの変化を簡単に把握できます。
5, 自宅のVPNサーバーの構築: 自宅ネットワークにVPNサーバーを設置し、スマートフォンや外部デバイスから安全にアクセスできるようにしました。これにより、ユーザーは外出先からもラズベリーパイにSSH接続し、システムの状態を確認できます。

このプロジェクトは、スマートホームや管理システムへの応用が可能であり、IoTデバイス、クラウドサービス、データベース、データ可視化ツールを組み合わせたソリューションの構築経験を提供します。

## こだわり
1, InfluxDBを採用し、時系列データの取得ができるようになった。

2, データ連携が止まると、Slack通知。(ラズパイのスペック問題で休止)

3, 自宅ネットワーク内のラズベリーパイに、外部からSSH接続できるようにVPNサーバを構築。


## 運用上の問題
1, ラズベリーパイゼロのスペックが低すぎて、スクリプトがクラッシュする。
→ラズベリーパイ4の入荷待ち

2, EC2のコスト
→Fargateなどで、DBのコンテナ化と、AWS マネージド Grafanaの使用。

## 今後の展望：

1, Fargateへの移行: 現在のEC2インスタンスを考慮し、コストや運用面での効率化を図るため、AWS Fargateに移行することを検討しています。これにより、サーバーの管理やスケーリングが不要になり、より簡単な運用が可能になります。

2, IoTトリガーの実装: 温度、湿度、気圧で閾値を決め、アレクサなどスマートホームデバイスから、エアコンのオンオフなどを実装し、快適なら生活をサポートできます。

3, 機械学習モデルの実装: 収集されたデータを基に、機械学習を用いた天気予報モデルを開発し、短期・長期の予測精度を向上させることを目指しています。この機能は、自動化された気象予報や、農業・エネルギー業界での利活用に繋がる可能性があります。例えば、水質センサーを接続し、水耕栽培のデータを分析できれば、どの環境での収穫量が最大になるのかがわかり、2のIoTトリガーで安定した環境を実装できます。
【Linux】sudoを利用する為の設定

# sudoの権限をユーザーに付与する。
一般ユーザーが管理者権限や他ユーザー権限で、コマンドを実行する為には`sudo`コマンドを利用します。
sudoコマンドを利用する為には、いくつか方法があるので記載していきます。

## wheelグループに属するユーザーを作成する。
wheelグループに属するユーザーは、sudoコマンドを利用する事が出来ます。
`visudo`コマンドを実行して、ファイル内に下記のような記載がある事を確認します。

```shell-session:sudoersファイル
## Allows people in group wheel to run all commands
%wheel  ALL=(ALL)       ALL
```
既存のユーザーを、wheelグループに加入させます。

```shell-session:セカンダリにwheelグループ追加
[root@localhost /]# usermod --append -G wheel test_01
[root@localhost /]# id test_01
uid=1000(test_01) gid=1000(test_01) groups=1000(test_01),10(wheel),1001(test_02)
```

追加されたユーザーで、sudoコマンドを利用出来る事を確認して下さい。

## /etc/sudoers.dに設定ファイルを作成する。
`/etc/sudoers.d`配下に、設定ファイルを作成します。
設定ファイル内に、`ユーザー名 ホスト名=(実行ユーザー名) コマンド Or %グループ名 ホスト名=(実行ユーザー名) コマンド`を記載します。
試しに `test_01ユーザーがroot権限 localhost.localdomain上で cat /var/log/messages` を出来るようにします。

```shell-session:設定ファイル作成
[root@localhost /]# visudo -f /etc/sudoers.d/test_01
```

```shell-session:test_01の中身
[root@localhost sudoers.d]# cat test_01
test_01 localhost.localdomain=(root) /bin/cat /var/log/messages
```

上記を作成後に、コマンドを実行出来るかを確認します。
tailとcatを使用して、messagesを見てみます。
tailを実行許可していないので失敗しましたが、catは実行出来ています。

```shell-session:コマンド実行
[test_01@localhost ~]$ sudo tail /var/log/messages
[sudo] test_01 のパスワード:
残念ですが、ユーザー test_01 は'/bin/tail /var/log/messages' を root として localhost.localdomain 上で実行することは許可されていません。

[test_01@localhost ~]$ sudo cat /var/log/messages
[sudo] test_01 のパスワード:
Mar 26 21:25:35 localhost systemd-logind: Removed session 4.
Mar 26 21:25:35 localhost systemd: Removed slice User Slice of test_01.
Mar 26 21:25:39 localhost systemd: Created slice User Slice of test_01.
Mar 26 21:25:39 localhost systemd: Started Session 5 of user test_01.
Mar 26 21:25:39 localhost systemd-logind: New session 5 of user test_01.
Mar 26 21:25:50 localhost su: (to root) test_01 on pts/0
```

また`sudoersファイル`を直接編集する方もございます。
`visudo`コマンドを実行して、下記のような文言を追記します。

```shell-session:sudoersファイル追記
test_01 localhost.localdomain=(root) /bin/cat /var/log/messages
```


vimで編集する事も出来ますが、`visudo`コマンドが推奨されています。
`visudo`コマンドは構文チェック機能がございます。
エラーがある状態でファイル保存すると、`sudo`コマンドが恐らく使えなくなる可能性があります。なので、構文チェック機能がある`visudo`コマンドが推奨されているのかと思われます。

試しにエラーのある状態でファイルを保存すると、`sudo`コマンドが使えなくなりました。※rootユーザーだったので、再度ファイルを編集して使えるようにしました。

```shell-session:エラー
[root@localhost ~]# visudo
>>> /etc/sudoers: 構文エラー near line 1 <<<
次は何でしょうか?
オプション:
  e -- sudoers ファイルを再度編集します
  x -- sudoers ファイルへの変更を保存せずに終了します
  Q -- sudoers ファイルへの変更を保存して終了します (*危険です!*)

次は何でしょうか? Q

[root@localhost ~]# sudo
>>> /etc/sudoers: 構文エラー near line 1 <<<
sudo: /etc/sudoers 内 1 行付近で構文解析エラーが発生しました
sudo: 有効な sudoers のソースが見つかりません。終了します
sudo: ポリシープラグインを初期化できません
```

## sudoコマンドの実行履歴
`/var/log/secure`にsudoコマンドの実行履歴が確認出来ます。
test_01ユーザーが、catコマンドでmessagesを確認した事が記録されています。

```shell-session:log
[root@localhost ~]# cat /var/log/secure
Mar 26 22:46:17 localhost sudo: test_01 : TTY=pts/0 ; PWD=/home/test_01 ; USER=root ; COMMAND=/bin/cat /var/log/messages
```
【SwiftUI】一時的にタップをできなくする

# 実装
有効か
```swift
.allowsHitTesting(true)
```

無効化
```swift
.allowsHitTesting(false)
```

# おわり
`disabled`でも良さそうに思いますが、ちょっと違うみたいです。

https://kentaro.app/posts/fix-swiftui-allows-hit-testing-sample
ABC 295 備忘録

## 概要
　今回はABC295（2023年3月25日21:00 ~ 22:40）の参加記録について自分の実装と感想について記していこうと思う。
## A問題
https://atcoder.jp/contests/abc295/tasks/abc295_a

### ポイント
- 入力をN個の文字列として標準入力から受け取る
- 条件である `and`, `not`, `that`, `the`, `you` に一致するものがあるかどうかをこれらを配列としたものの中に一致するものがあるかどうかを判断させる
- 一致するものがあった時点で`Yes`と出力し、プログラムをループ中で終了
- ループ中で終了しなかった場合は、ループ後に`No`と出力し、プログラムを終了

### 自分の実装
```Python:ABC_295.py
import sys

N = int(input())

W = list(map(str, input().split()))

judge = ["and", "not", "that", "the", "you"]

for word in W:
    if word in judge:
        print("Yes")
        sys.exit()

print("No")
```
## B問題
https://atcoder.jp/contests/abc295/tasks/abc295_b
### ポイント
- マス目をR行のリストとして標準入力から保存
- 爆弾のマス目の場合、つまり数字のマス目の場合は壁（ = `#`）のマス目を壊す処理を実行
- マンハッタン距離の処理の際に実際に配列のインデックスから出ないように条件文で制限することを忘れない
- `#` 以外のマス目には爆弾の処理が行われないようにする
- 爆弾自体のマス目の処理はそのマス目の爆弾処理が全て終わった場合に行われるようにする（ = ループを抜けた時に初めて行われるようにする）
- 最後に行区切りで文字列となるように出力
### 自分の実装
```Python:ABC_295_B.py
R, C = map(int,input().split())

walls = list(list(input()) for i in range(R))

for line in range(R):
    for row in range(C):
        if walls[line][row] != "." and walls[line][row] != "#":
            attack = int(walls[line][row])
            for i in range(attack+1):
                for j in range(attack+1-i):
                    if i != 0 or j != 0:
                        if line-i >= 0 and row-j >= 0:
                            if walls[line-i][row-j] == "#":
                                walls[line-i][row-j] = "."
                        if line+i <= R-1 and row-j >= 0:
                            if walls[line+i][row-j] == "#":
                                walls[line+i][row-j] = "."
                        if line-i >= 0 and row+j <= C-1:
                            if walls[line-i][row+j] == "#":
                                walls[line-i][row+j] = "."
                        if line+i <= R-1 and row+j <= C-1:
                            if walls[line+i][row+j] == "#":
                                walls[line+i][row+j] = "."
            walls[line][row] = "."


for line in range(R):
    print("".join(walls[line]))
```
## C問題
https://atcoder.jp/contests/abc295/tasks/abc295_c
### ポイント
- 靴下の色を標準入力からキューとして格納し、照準となるようにソート
- 最終的に何個のペアが作れるかどうかは、ある色の靴下の個数が2で割って何個のペアを作れるかどうかを加算
- リストでその個数を数えると計算量がオーバーしてしまうため、キューで前の靴下の色と同じかどうかを判断
- 色の判断時にすでにペアとなってしまっている場合は。前のペアなしとして `""` 判断させるようにして区別
### 自分の実装
```Python:ABC_295_C.py
from collections import deque

N = int(input())

A = sorted(list(map(int,input().split())))

A = deque(A)
count = 0

ex_color = ""

for i in range(N):
    present_color = A.popleft()
    # 色が一緒ならば、ペアを作る（前の色が残っている場合）
    if ex_color == present_color:
        count += 1
        ex_color = ""
    else:
        ex_color = present_color

print(count)
```

## D問題

### ポイント
- 嬉しい列になるためには、部分文字列の中の各位の数字の個数が偶数個であることが条件
- 部分文字列中の数字の個数は`l-1`文字目までの数字の個数と`r`文字目までの数字の個数を比較すれば良い

### 自分の実装
- まだ実装できていない。

## 感想
B問題にかなり時間がかかってしまった。爆弾の処理の条件分岐を正しく実装するのに手間取ってしまったことが原因と考えられる。具体的には、条件分岐の際に爆弾の処理が及ぼすマス自体についてフォーカスしてしまったので、そのマスがインデックス内であるかどうかを考慮する必要が出てきてしまった。公式の解答にあるように元々のマス目自体にフォーカスしてやればよかった。個人的には、C問題の方が条件分岐を考えなければ良い分、簡単だった。D問題については、考え方はわかったものの計算量を時間内に収めるのにどう実装すれば良いかわからなかったのでコンテスト中に解ききれなかった。
[AWS]SESによるメール送信をやってみた

## 目次
1. Amazon SESとは
1. どうしてSESを使うのか？
1. 実際に使ってみた
1. NestJSによる実装
1. 結果
## Amazon SESとは
Amazon SESは(Amazon Simple Email Service)の略で、既存ドメインまたは既存メールアドレスを利用して、メール配信をしてくれるサービスです。

## どうしてAmazon SESを使うのか？
Amazon SESを使うことでEC2や様々なAWS内のサービスと連携され、機能の追加であったり管理が簡単になるためです。また、通常大規模なEメールプラットフォームは複雑でコストがかかったり、各社のメールボックスプランのSMTPを利用して大量のメールを送信する場合は通信制限などの問題があったりします。そこでAmazon SESを用いることでメール送信機能における費用対効果を向上させることができるといったメリットが存在します。

## 実際に使ってみた
今回はEC2、NestJS（TS）を用いて、NestJSからSESに対してメール送信の要求を行います。

1. AWS management consoleにサインインし、SES consoleにアクセス
2. サイドバーの「検証済みID」> 「IDの作成」をクリックすると以下の画面が表示される
2. IDタイプを今回は「Eメールアドレス」に指定して、メールの送信に用いるEメールアドレスを以下の赤枠に入力し、「IDの作成」をクリック
![Screen Shot 2023-03-26 at 13.37.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/756995/90998161-8a11-0a96-2e8f-e41718345d1d.png)

4. 先ほど作成したEメールアドレスのIDステータスが「検証保留中」の状態で追加されていることを確認（この時点ではまだEC2を介してメール送信はできない）
![Screen Shot 2023-03-26 at 22.08.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/756995/6f4b9240-7f0b-ca75-502b-a93305a9858d.png)

4. 作成したメールアドレスに確認メールが届くため、その本文に添付されている確認URLをクリックすることで検証が完了
4. 先ほど「検証保留中」だったIDステータスが「検証済み」に変わっていたらそのメールアドレスは利用可能
![Screen Shot 2023-03-26 at 22.04.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/756995/732e86f6-5ec1-5db6-5c7f-fd90ede49e14.png)

これにてメールアドレスの検証が完了しました。

## NestJS設定
AWS SDKをインストール
```
yarn add aws-sdk
```

## 環境変数名
自分のプロジェクトのenvファイルを参照してください
```
# aws ses 
ACCESS_KEY=""
SECRET_KEY=""
REGION=""
```

## ラッパー関数作成
自分は共通関数をまとめるためのutilsディレクトリを作成し、その配下のファイルにこのラッパー関数を記述しました。

```typescript
import AWS from 'aws-sdk';

export async function sendEmail(from: string, to: string, subject: string, body: string) {
    if (process.env.NODE_ENV === 'development')
    // ローカル環境ならメールを送らない
    return console.log({ from, to, subject, html });
    
    const ses = new AWS.SES({
    accessKeyId: process.env.ACCESS_KEY,
    secretAccessKey: process.env.SECRET_KEY,
    region: process.env.REGION,
    });

    const params: AWS.SES.SendEmailRequest = {
      Destination: {
        ToAddresses: [to],
      },
      Message: {
        Body: {
          Text: {
            Data: body,
          },
        },
        Subject: {
          Data: subject,
        },
      },
      Source: from,
    };
    
    return this.ses.sendEmail(params).promise();
} 
```

あとは任意のタイミングでこの関数に適切な引数を渡して呼び出すだけです。

## 結果
きちんとSESによるメール送信ができていることを確認できました！
![Screen Shot 2023-03-26 at 22.45.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/756995/f24d7fc1-55e4-9501-6551-45d56897a86f.png)
Fsharp 指定されたディレクトリ下にある、ある拡張子の行数のランキングが知りたい

リファクタリングのため、行数のランキングがほしいときがあります。
基本的に、行数の多いファイルは分割がうまくいっていないとか、処理が複雑になりすぎている傾向があります。

そういうわけで、Fsharpでプログラムを書いてみました。

ひとまず、こういう結果を返せるようにしてみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679704/9b28d4eb-7698-e099-14eb-01c7ae58cf81.png)

※bashでやるならこんな感じです
```bash
find ./src -type f -name "*tsx" | xargs wc -l | sort -r | head
```

Fsharpだと非常に簡単につくれたので紹介してみます。

```Fsharp:cli.fs
open System.IO
open Spectre.Console

// ファイルの行数を取得する関数。引数1はファイルの絶対パス。そのファイルの行数をカウントして返す。
let countLines (path: string) =
    // ファイルを開く
    use sr = new StreamReader(path)
    // ファイルの行数をカウントする
    let rec loop count =
        match sr.ReadLine() with
        | null -> count
        | _ -> loop (count + 1)

    loop 0

//エントリポイント
[<EntryPoint>]
let main argv =
    let path = Array.tryHead argv |> Option.defaultValue "."

    // 配列の範囲外のときはNone
    let extname =
        match Array.tryItem 1 argv with
        | Some ext -> ext
        | None -> "ERROR"

    // 上位何件を表示するのか
    let top =
        match Array.tryItem 2 argv with
        | Some top -> int top
        | None -> 10

    (* エラーのときは終了する *)
    if extname = "ERROR" then
        printfn "ERROR: No extension name"
        1
    else
        // pathから再帰的にファイルの絶対パスを取得する。ただし、ファイルの拡張子がextnameのものだけを取得する。
        let files =
            Directory.EnumerateFiles(path, "*." + extname, SearchOption.AllDirectories)
            // 絶対パスにする
            |> Seq.map Path.GetFullPath
            // ファイルの行数をカウントし、ファイル名、絶対パス、行数のタプルを作る
            |> Seq.map (fun path -> (Path.GetFileName path, path, countLines path))
            // 行数で降順にソートする
            |> Seq.sortByDescending (fun (_, _, count) -> count)

        // 上位top件を取得する
        // ただし、filesの長さがTOPより短い場合はそのまま表示する
        let limitedFiles =
            if Seq.length files < top then
                files
            else
                Seq.take top files

        let table = Table()

        //tableにcolumnを追加する。columnのタイトルは"File"、"Path"、"Lines"。
        for column in [ "FileName"; "Path"; "Lines" ] do
            table.AddColumn(column) |> ignore

        // tableに行を追加する filesを使用して追加する
        for (file, path, count) in limitedFiles do
            table.AddRow(file, path, count.ToString())
            |> ignore

        // tableを表示する
        AnsiConsole.Write(table)
        0

```
使い方は、このスクリプトに3つの引数を渡します。
1つ目が探索するパス、2つ目がextname（拡張子）、3つ目がTOP何件を表示するか、ですね。

Spectre.Consoleというライブラリを使えば綺麗に整形できて良いですね～。
書いていて非常に楽しい言語です。

あ、そういえば distとかnode_modulesとか省かないといけないですね。--ignoreみたいなものをつくるか、.coutIgnoreファイルに書いてそれを処理しないようにするか、とかいろいろ考えないと……。まあつくりたいものはつくれたから、いっか。./src以下を検索してもらえばよいだけのことではあるし。
v-forでMapオブジェクトを対象にするケース

## 記述例

```sample.vue
<script>
const map = new Map([
  ['key': 'value'],
  ...
]);
</script>
<template>
  <div
    v-for="([k, val],index) of Array.from(map)"
    :key="index"
  >
    {{ k }} / {{ val }}
  </div>
</template>
```

## 解説
### Array.from(map)
Mapオブジェクトは、配列との互換性があります。
```Array.from(map)```とすることで、Mapオブジェクトのコンストラクタ（2次元配列）を取得しています。

【参考】Array オブジェクトとの関係

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Map#array_%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%A8%E3%81%AE%E9%96%A2%E4%BF%82

### v-for="([k, val],index) of ..."
2次元配列から、要素とindexを取り出しています。
取り出す要素は配列なので、キーと値を角括弧の配列形式で受け取ります。
v-forの中で展開されるので、例のようにkとvalを直接使用できます。

indexは無くても可能ですが、keyディレクティブの設定は忘れずに。
[UE5]UE5.1のNiagara Fluidsでプレイヤーの当たり判定をとってくる

UE5.1だと前に書いた方法でプレイヤーの当たり判定取れないっていうのを見ていろいろ模索したよ。（PhysicsAssetのチェックボックスがない）

Niagara Fluids入れたりとかの工程はこっちで説明してるから参考にしてみてね。

https://qiita.com/Noirkun/items/c18cd2f56f68f9dcb6e3

早速やっていくよ
ちなみにめっちゃ簡単だった

## タグをセットする

実はやることこれだけです。


ユーザーパラメーターの中にある"**Collide_Meshes**"の中に「collider」というタグがあるのでこれを取ってきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2818152/ef2f7d4d-b7f7-a2c9-d888-aa03cf159102.png)

そしたらLevelBPのほうでこんな感じにつけてあげる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2818152/945797a4-a515-81f5-f55e-69da99b914d7.png)

これだけで実はできちゃいます。（モジュールの中見てたりしたのがバカらしい…）

## PhysicsAssetの仕様変更
なんで変わったのかというと[UE5.1のリリース](https://docs.unrealengine.com/5.1/ja/unreal-engine-5.1-release-notes/)を見た感じこんなことが



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2818152/90898e35-b905-a335-366e-39bd7078140b.png)
翻訳に入れてみると

**「Physics Assetは、スケルタルおよびスタティックメッシュのコリジョン用のリジッドメッシュコリジョンデータインターフェイスに置き換えられました。すべてのNiagara Fluidsテンプレートが更新されました。」**

まぁ要約するとPhysicsAssetの機能が変わったってことだよね。
けどタグでできる分こっちのほうが楽かも

ちなみにUE5.0でこのやり方でできるか試したけどできなかったよ。まぁ当たり前だね


困った人は参考にしてね
循環参照について理解する

## 循環参照とは
循環参照とは、2つのオブジェクトがお互いに強い参照を保持し合っている状態のことを指します。お互いにインスタンスを参照しあうため、どちらも解放されずにそのまま残り続けてしまうという現象です。

循環参照が発生してしまうと、メモリリークが発生し、プログラムのパフォーマンスが低下したり、クラッシュする原因となります。

### 発生例
例）AクラスとBクラスが存在し、それぞれ参照し合っている場合を考えてみましょう。

```
class A {
    var b: B?
    init() {
        b = B()
        b?.a = self
    }
}

class B {
    var a: A?
}
```
上記のコードを見ると、Aクラスのインスタンスが作成されると同時に、Bクラスのインスタンスも作成され、Aクラスのbプロパティに代入されているのが分かります。
また、Bクラスのインスタンスは、Aクラスのインスタンスを参照するように設定されます。

このように、AクラスとBクラスのインスタンスがお互いに**強参照**を保持し合っている場合に循環参照となりメモリリークが発生してしまいます。


## 強参照と弱参照
解説でも出てきた**強参照**というワードですが、それぞれ**強参照**(strong reference)と**弱参照**(weak reference) というものが存在します。

**強参照**とは、端的に言えば親子関係のようなもので、
親が解放されると子も自動的に解放されます。
上記のようにお互いが親としてインスタンスを保持すると循環参照が起きるわけです。

この循環参照を防ぐために**弱参照**というものがあります。

**weak**と**unowned**と言った装飾子を使用します

弱参照を使用することで、参照先のオブジェクトが解放された後も、参照が残っているオブジェクトに対して安全にアクセスできます。



## 弱参照のweakとunownedについて
### weak

```
observable
    .subscribe(onNext: { [weak self] string in
        self.textLabel.text = string
    })
```
**weak self**の場合は、selfにnilが代入される可能性があるのでweakを使うときはOptionalでないといけません。
そのため、オプショナルバインディングでnilチェックをする必要がある為、コードがやや冗長になることがあります。

### unowned
```
observable
    .subscribe(onNext: { [unowned self] string in
        self.textLabel.text = string
    })
```
unowned selfは常に非nilであることが保証されています。
nilチェックが不要であるため、コードが簡潔になるメリットがありますが、非nilであることが保証されているため、nilが代入される可能性がある場合にunownedを参照すると落ちます。


### weakとunownedの使い分け
nilの可能性がある場合には、**weak self**を使用、
非nilの場合は、**unowned self**を使用するという使い分けで良いと思います。


それぞれにメリット、デメリットはありますが、基本的にはweakを使っておけば問題になることはないと思います。
ただ、インスタンスが必ず存在すると保証できる時にweakを使うとアンラップが必要な分、記述が冗長になってしまいます。
GPT-4がドラえもんになってシステム開発の悩みに答えてくれた

プロンプトエンジニアリングの練習をしてみました。

### GPT-4 に頼んだこと
あなたはドラえもんの仮想人格です。あなたの会話は「どうしたの、のび太君。」ではじまり「大丈夫だよ、のび太君」で終わります。のび太君はWeb開発についてPL/SQLとJSPが混在しているレガシーシステムからマイクロサービスに置き換えようとする顧客にどのように応えていけばいいか悩んでいます。それに対してドラえもんは優しく導きます。

### ドラえもんになった GPT-4 が教えてくれたこと
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225878/2a8ceda9-3522-6829-e332-9cf0dcd8a768.png)