OmniAICreator/Qiita-1.07M huggingface dataset:
mac で pyenv で python 3.7.2 がインストールできないとき

社内で発生した問題をみんなで解決しました。メモとして残しておきます。

# 問題
mac で pyenv で python 3.7.2 がインストールできない

```zsh
$ pyenv install 3.7.2
python-build: use openssl@1.1 from homebrew
python-build: use readline from homebrew
Downloading Python-3.7.2.tar.xz...
-> https://www.python.org/ftp/python/3.7.2/Python-3.7.2.tar.xz
Installing Python-3.7.2...
python-build: use readline from homebrew
python-build: use zlib from xcode sdk

BUILD FAILED (OS X 11.5.2 using python-build 20180424)
```

# 環境
- macOS Monterey
- zsh

# 解決法
以下の方法で解決しました。

```zsh
$ pyenv install --patch 3.7.2 < <(curl -sSL https://github.com/python/cpython/commit/8ea6353.patch\?full_index\=1)
```

# 参考
こちらの記事はbash用のコマンドになっていたようでzshではそのまま実行できなかったのでzsh用のコマンドに変更しました。
https://teratail.com/questions/323061
gcloudコマンドを使ってenvファイルを共有できるようにする

## 困っていたこと

gitでコードを管理し、チーム開発する時に同じ`env`ファイルを参照して進めていきたい。
が`env`ファイルをgitには上げられない...

## keyringとkeyの作成
暗号化して使う時に必要らしいので作成

#### keyring
    gcloud kms keyrings create keyringの名前 --location 場所

https://cloud.google.com/sdk/gcloud/reference/kms/keyrings/create

#### key
    gcloud kms keys create keyの名前 --location global --keyring keyringの名前 --purpose encryption

keyはpurpose(目的)が必要らしいです。

https://cloud.google.com/sdk/gcloud/reference/kms/keys/create


## encrypt

まずは、envファイルをリモート上に上げるため`encrypt`

    gcloud kms encrypt --key key名 --keyring keyring名 --location 指定したlocation --plaintext-file .env --ciphertext-file .env.encrypted

- --key：作成したkey名
- --keyring：作成したkeyring名
- --location：リソースが保存されアクセスされる場所
- --plaintext-file：暗号化したいファイル名(今回はenv)
- --ciphertext-file：出力する暗号文のファイル名

※locationについては下記から選択

https://cloud.google.com/kms/docs/locations

## decrypt

次に、envファイルをリモートからローカルに持ってくるため`decrypt`

    gcloud kms decrypt --key key名 --keyring keyring名 --location 指定したlocation --plaintext-file .env --ciphertext-file .env.encrypted

※encryptの部分をdecryptに変換しただけ

## エラー起きたところ
keyringを作成する際にコマンドを打ったら、下記エラーが...

`(gcloud.kms.keyrings.create) Error parsing [keyring].The [keyring] resource is not properly specified.Failed to find attribute [project]. The attribute can be set in the following ways: `

プロジェクトの設定ができていないようだったので、下記コマンドを実行

    gcloud config set project プロジェクト名

※GCPのプロジェクト

## 最後に

私はJSで作成したので、`encrypt`と`decrypt`のコマンドを`script`でまとめました。
`gcloud:encrypt:env`とかでまとめると楽にenvファイルを出し入れできます。


## 注意事項
gcloudコマンドが使えない場合、下記のCloud SDKをインストールして下さい

https://cloud.google.com/sdk/docs/install?hl=JA

## 参考文献
https://cloud.google.com/sdk/gcloud/reference/kms/encrypt

https://cloud.google.com/sdk/gcloud/reference/kms/decrypt

https://qiita.com/takasp/items/99270cf3bf0546f7e55e
CentOS 8でyum/dnfに失敗！ Failed to download metadata for repo 'AppStream'

# 1. 突然のdnf/yumエラー
昨年（2021年）に構築したCentOS 8でyum/dnfを実行しようとしたところ、次のエラーが発生した。

```shell
# dnf install nc
CentOS-8 - AppStream                            105  B/s |  38  B     00:00
Error: Failed to download metadata for repo 'AppStream': Cannot prepare internal
 mirrorlist: No URLs in mirrorlist
```

**あれ、yum/dnfが効かない！**

CentOS 8が2021年末にサポート切れになったのは記憶も新しい。筆者もCentOS 8ネタを何本か書いていたにもかかわらず、パッケージのインストールに失敗して焦ってしまった。この対処法を説明したい。

## 1-1. 対象環境
* CentOS 8

# 2. 原因は何か？
このようなエラーが発生した原因は、サポート切れでミラーサイトが停止したからだ。エラーメッセージにも「No URLs in mirrorlist」と表示されている。つまり **「CentOS 8のサポート停止」** は次の2つのことを意味している。

* 今後新しいアップデートパッケージを提供しない
* ミラーサイトが停止する

# 3. 暫定対処に失敗

それならCentOS Streamリポジトリからwgetしちゃえと思ったら、wgetが入っていない。うーむ。

```shell-session
$ wget http://hogehoge
bash: wget: command not found...
```

残された選択肢は、次の2つ。

1. wgetなど最低限必要なパッケージを他サーバからscpで送り込み、以降はwgetで取得する
2. CentOS Streamに移行するか、AlmaLinuxやRocky Linux、Oracle LinuxなどのRHELクローンに移行する

1の方法は力技。自力で依存性を解決しなければならず、パッケージ数が多くなるとしんどい。

趣味で使っている環境だったこともあり、

**いっそのことCentOS Streamに移行してしまおう**

と、公開されている次の移行手順を実行すると、これまたエラーで実行できず。

```shell-session:CentOS-Streamへの移行手順
# dnf install centos-release-stream
# dnf swap centos-linux-repos centos-stream-repos
# dnf distro-sync
```

リポジトリにアクセスできないので、この手順通りでは無理なことはわかるだろう。だけれど`centos-release-stream`パッケージをwgetして、ローカルインストールすればいいと思っていたら、依存関係で`centos-release-stream`をインストールできず。クー。

# 4. 根本解決策は？
解決策は、参照先のリポジトリをミラーサイトから`http://vault.centos.org`に変更すること。つまり`/etc/yum.repos.d/CentOS*repo`ファイルの以下のように変更する。

```:一か所目
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=BaseOS&infra=$infra
↓ コメントアウト
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=BaseOS&infra=$infra
```

```:二か所目
#baseurl=http://mirror.centos.org/$contentdir/$releasever/BaseOS/$basearch/os/
↓ baseurlのコメントを外して、mirrorをvaultに置換
baseurl=http://vault.centos.org/$contentdir/$releasever/BaseOS/$basearch/os/
```

修正後のファイル全体の内容は次のとおり。

```:修正後：/etc/yum.repos.d/CentOS-Base.repo
[BaseOS]
name=CentOS-$releasever - Base
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=BaseOS&infra=$infra
baseurl=http://vault.centos.org/$contentdir/$releasever/BaseOS/$basearch/os/
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
```

```:修正後：/etc/yum.repos.d/CentOS-AppStream.repo
[AppStream]
name=CentOS-$releasever - AppStream
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=AppStream&infra=$infra
baseurl=http://vault.centos.org/$contentdir/$releasever/AppStream/$basearch/os/
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
```

手で修正するのが面倒な人は、次のコマンドで一括置換できる。

```
sed -i -e 's/^mirrorlist/#mirrorlist/g' -e 's/^#baseurl=http:\/\/mirror/baseurl=http:\/\/vault/g' /etc/yum.repos.d/CentOS-*repo
```

まあ「根本解決か」と聞かれると微妙で、根本解決はAlmalinuxやRocky Linux、CentOS Stream、Oracle Linuxなどに移行することなのだけれど...。今回は、CentOS 8のサポート切れは痛いほどわかっていたのに戸惑ったという話でした。
ArduinoUnoで16進変換して遊ぶ


目的
　ライブラリを使わないで16進変換


![o_con232.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/76bf17bf-045e-d068-4799-df08db42a1bb.jpeg)




ちょっと違うが使用例


STM32G031のI2Cでマスターから送られた来たデータを受信する。(STM32G031J6M6)(Arduino)


https://qiita.com/caa45040/items/6cf5eb09c6f769ac0e32





ArduinoUNOを使い1から4096を文字列に変換 例intの1234を文字列の「1234」


https://qiita.com/caa45040/items/1837fd01959df902c8de





バグ修正版


```c++



void setup() {

  Serial.begin(9600);

}

void loop() {

  char x = 0x12;

  char c_hex[] = {
    '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
  };

  char data_i[] = { c_hex[(x >> 4) & 0x0f], c_hex[x & 0x0f]  , 0 };

  Serial.println(data_i);
  delay(1000);

}





```


0から127まで　

```c++


void setup() {

  Serial.begin(9600);

}

void loop() {

  char x = 0x12;

  char c_hex[] = {
    '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
  };

  char data_i[] = { c_hex[x >> 4], c_hex[x & 0x0f]  , 0 };

  Serial.println(data_i);
  delay(1000);

}



```
javascript65_構造関数の修正

書き方の改善：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/1149b867-25ea-32b3-1273-39224681d665.png)
下記をご参照ください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/712305fe-b570-cd71-ee11-6f2e47e8a7bc.png)

しかし、この書き方にも一つの問題があります。
public関数を書くのが非常にリスキーです。実際の開発では、チームが組んで一緒に開発しています。
同じ名前のfun関数を定義してしまった場合、自分が書いた関数が上書きされることがあり得ます。。

そこで、もっと良い書き方としては、クラスとなる対象オブジェクトのprototype属性内に関数を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/e1b5f696-554b-c069-176d-a39778685b2e.png)
php バリデーションルール

電話番号
         'phone_number' => ['required', 'regex:/^\d{2,5}-?\d{1,4}-?\d{4}$/', 'between:10,13'],

チェックボックス一つ以上選択

                $target_type_rules = [
                    'section1.is_full_time_employee' => 'required_without_all:section1.is_contract_employee,section1.is_part_timer,section1.is_part_time_job,section1.is_part_time_employee,section1.is_employment_type_other',
                ];
                $target_type_messages = [
                    'section1.is_full_time_employee.required_without_all' => '職種を一つ以上選択してください'
                ];
=IF(A1="","",A1)の""は「何もない」ではない。

Excelではお馴染みの=IF(A1="","",A1)。
IF関数が使えるエクセルユーザーであれば一度は書いたことがあるこの数式。今回は""を何もないと覚えているのは間違いですよというお話です。


##""ってなに？
いきなり答えを言ってしまいましょう。<strong>答えは0文字の文字列（空文字）</strong>です。
エクセルやスプレッドシートでは数式内で文字列を表現したい時に"で囲むというルールがあります。
例えばセルに="ダブルクォーテーション"と入力すると"は出力されず、ダブルクォーテーションと出力されます。
※ダブルクォーテーションを付けず=ダブルクォーテーションと入力するとエラーになります。


##プログラミングでの空白という言葉のややこしさ
空白という言葉はプログラミング的に言わせれば定義が広すぎてあまりにも使いにくい。
下の3つは明確な違いがあるにもかかわらず、どれを表現するにも空白という文字が当てはまってしまいます。


####1. スペース
半角スペース、全角スペースを指します。
ちなみにエクセル数式内でスペースを表現しようと思ったら半角スペースの場合=" "、全角スペースの場合="　"と書きます。表示されるセルには何も書かれていないように見えますが、きちんとスペースが入っています。※LEN関数で調べると1と返されます。

####2. 空文字
""←これ。0文字の文字列。=""と入力したセルにたいしてLEN関数で調べると0が返されます。しかし下のブランクとは異なります。<strong>0文字の文字列がセルに入っている</strong>のです。

####3. ブランク
何もない。セルに対して何も入っていない状態。""をこのブランクだと考えていらっしゃる方が多い。

:::note info
空白-wikipedia
文章中で語と語の区切りを表したり、段落を作るなどのために空けたりする空間のこと。→スペース、和字間隔
ある区域の中に物、人、情報などが入っていないこと。空っぽであること。→ブランク
:::



##""(空文字)とブランクは別物
さてスペースはわかるとして、空文字とブランクの違いについて検証したいと思います。
####ISBLANK関数
ここでExcelではあまりお目にかからないISBLANKという関数を紹介します。
ISBLANk関数は=ISBLANK(セル)と書き、引数で渡したセルがブランクかどうか返す関数です。
ブランクの場合はTRUE、ブランクじゃない場合はFALSEを返します。

| 数式 |  ブランクの場合 | ブランクじゃない場合 |
|:-:|:-:|:-:|
| =ISBLANK(セル) | TRUE |FALSE  |

<br>

では検証してみましょう。下をそれぞれエクセル、スプレッドシートどちらでもいいので書いてみてください。
■検証
A1セルに=""
A2セル何もしない
B1セルに=IF(ISBLANK(A1),"ブランク","ブランクじゃない")
B2セルに=IF(ISBLANK(A2),"ブランク","ブランクじゃない")

■結果
B1セル：ブランクじゃない
B2セル：ブランク


というように空文字とブランクは明確に違いがあります。


:::note info
<strong>補足</strong> エクセル、スプレッドシート共にISBLANKの関数説明ではブランクのことを空白と表現しています。

エクセルヘルプ（2013年version）：
ISBLANK 関数は、引数値が空白セルへの参照の場合に論理値 TRUE を返し、それ以外の場合に FALSE を返します。

Googleスプレッドシート：
ISBLANK 関数は値が空のセルか空のセルへの参照を含んでいる場合に TRUE を返し、データかデータへの参照を含んでいる場合に FALSE を返します。
:::


##ブランクセルはブランクと空文字を兼ねる

ただ1点ややこしいのはブランクセルは""空文字という意味合いも持っています。
A1セルに何も書いていない状態でB1セルに下の数式を交互に書いてみてください。
=A1=""
=ISBLANK(A1)

どちらも結果はTRUEです。このことからブランクセルはブランク、""空文字を兼ねるということがわかります。


##まとめ
・エクセル、スプレッドシートで数式内の文字列は""の間に書く。
・=""とダブルクォーテーションの間に何もかかなければ0文字の空文字がセルに入る。
・スペース、空文字、ブランクはそれぞれ違いがあり異なる。
・ISBLANK関数でブランクを検証できる。
・ブランクセルはブランクと空文字を兼ねる。

ちなみにGoogleスプレッドシートでは空文字、ブランクの扱いがとっても大事です。ここらへんはまた今度～
2022/2/18主にITとかセキュリティの記事

「Emotet」に感染、従業員なりすましメールが送信される - ワコール
https://www.security-next.com/134212

ROSを外部ネットワークと安全に接続する方法(2/16)
https://ascii.jp/elem/000/004/083/4083659/?rss

Google Chrome、すぐにアップデートを - 脆弱性の悪用確認(2/16)
https://news.biglobe.ne.jp/it/0216/mnn_220216_9937322835.html

ソフトバンク、メールでのパスワード付き圧縮ファイルを運用廃止。受信すると自動削除(2/16)
https://pc.watch.impress.co.jp/docs/news/1388648.html

Google Chrome、すぐにアップデートを - 脆弱性の悪用確認(2/16)
https://news.infoseek.co.jp/article/mynavi_2368934/?tpgnr=it

ウクライナの国防省サイトや大手銀行にサイバー攻撃(2/16)
https://japan.cnet.com/article/35183588/

マイクロソフト、サイバー攻撃を受けたウクライナ政府のために安全な通信チャネルを作成＝マスコミ(2/16)
https://jp.sputniknews.com/20220216/10186649.html

ノーコード／ローコードの威力はこう引き出せ、カギは業務部門との一体開発(2/16)
https://active.nikkeibp.co.jp/atcl/act/19/00351/012500003/?i_cid=nbpnxta_sied_pickup

ISMAPとは？制度の概要やクラウドサービスリストについてわかりやすく解説(2/16)
https://sakumaga.sakura.ad.jp/entry/ismap

病院の１割、対サイバー攻撃「脆弱機器」を未対策のまま使用…９割「脅威感じる」が対策追い付かず | ヨミドクター(読売新聞)(2/16)
https://yomidr.yomiuri.co.jp/article/20220215-OYT1T50269/

Gmailに届いた複数のメールをまとめて転送する方法(2/15)
https://dime.jp/genre/1318920/

“パスワードのいらない世界”への道　「パスワードレス認証」の第一歩とは(2/16)
https://techtarget.itmedia.co.jp/tt/spv/2202/16/news09.html

ウクライナ国防省にサイバー攻撃、ウェブサイトにアクセスできず(2/16)
https://newspicks.com/news/6703053/body?sentlog

東京海上、地銀に補償　サイバー攻撃や決済遅延(2/16)
https://www.nikkei.com/article/DGXZQOUB130SQ0T10C22A1000000/

ソフトバンク、パスワード付き圧縮ファイルの利用を廃止--「PPAP」廃止に追随(2/16)
https://japan.cnet.com/article/35183615/?utm_source=newspicks&utm_medium=news_distribution&utm_campaign=newsfeed_distribution

原因は汚れたデータ以外にも　NTT西の障害、遅延8カ月の真相(2/17)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020800017/020900678/

Coinhive無罪判決の意味　どこから「不正」なプログラムか(2/17)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020800017/020900676/

iPhoneのWi-Fi接続で使うMACアドレスが変わるのはなぜ？(2/17)
https://xtech.nikkei.com/atcl/nxt/column/18/01947/021500003/
クレヨンしんちゃん (HTML+CSS)

### Respect for

https://qiita.com/noppefoxwolf/items/b4376392d98cea2446dd

# キャンバスサイズの設定

最近クレヨンしんちゃんを見ていないので念の為[4:3にしましょう](https://developer.mozilla.org/ja/docs/Web/CSS/aspect-ratio)、自信があれば16:9でも良いと思います。

```html
<section>
  <p>Hello, world!</p>
</section>
```

```css
body {
  background: gray;
}

section {
  aspect-ratio: 4 / 3;
  background: #fff;
  display: grid;
  place-content: center;
  width: 600px;
}
```

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70467/88a92719-fbce-9f70-82b3-37ca439b0904.png)

# [色の定義](https://developer.mozilla.org/ja/docs/Web/CSS/Using_CSS_custom_properties)

```css
:root {
  --top-color: rgb(94% 51% 64%);
  --bottom-color: rgb(95% 95% 50%);
}
```

これらの色を[linear-gradient](https://developer.mozilla.org/ja/docs/Web/CSS/gradient/linear-gradient())で描画します。

```css
section {
  aspect-ratio: 4 / 3;
  background: linear-gradient(to bottom, var(--top-color), var(--bottom-color));
  display: grid;
  place-content: center;
  width: 600px;
}
```

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70467/fc30d0a0-8da7-b620-4f8b-42ffe29d3d45.png)

やりたいことが分かってきたでしょうか

# タイトルの設定

タイトルを追加します。

```html
<section>
  <h1>
    開発者ツールで<br />
    スロットリングを切り忘れるゾ
  </h1>
</section>
```

重要なのは以下の部分です。

```css
h1 {
  color: purple;
  font-size: 30px;
  padding-left: 1em;
}

h1::before {
  content: '';
  margin-left: -1em;
}
```

[`h1::before`](https://developer.mozilla.org/ja/docs/Web/CSS/::before)を使うことで１行目を少しズラしています。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70467/a4b03f2d-3981-8e10-5789-1712023e8990.png)

# ルビをふる

子供の番組なので、[ルビ](https://developer.mozilla.org/ja/docs/Web/HTML/Element/ruby)を振ります。

```diff_html
 <section>
   <h1>
-    開発者ツールで<br />
+    <ruby>開発者<rp>(</rp><rt>かいはつしゃ</rt><rp>)</rp></ruby>ツールで<br />
     スロットリングを切り忘れるゾ
   </h1>
 </section>
```

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70467/7e421fdc-ac03-bcb3-2f64-8e287113325e.png)

# フォントを変える

フォントも[丸っぽいやつ](https://fonts.google.com/specimen/Kosugi+Maru)にしておきます。

```css
@import url('https://fonts.googleapis.com/css2?family=Kosugi+Maru&display=swap');

h1 {
  color: purple;
  font-size: 30px;
  font-family: 'Kosugi Maru', sans-serif;
  padding-left: 1em;
}
```

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70467/0790c125-85ce-4545-9433-a3459d00b7f2.png)


これでHTML+CSSでクレヨンしんちゃんのタイトルコールを作ることが出来ました。

いかがでしたか？

# 完成品

https://jsfiddle.net/c8qe6nth/

## オリジナル

https://qiita.com/noppefoxwolf/items/b4376392d98cea2446dd

<details>
<summary>補足</summary>

- 原典と体裁を合わせるため、CSS変数などを無駄に使っています
- スロットリングを切り忘れるゾ ＝ デベロッパーツールで低速環境再現のためにセットした3Gのスロットリングを戻すの忘れがち

</details>
コマンド出力（標準出力・標準エラー出力）のリダイレクト

こんにちは。
コマンドの標準出力・標準エラー出力のリダイレクトとは、コマンド出力をファイルへ書き込みさせ、コンソールへは表示させません。

リダイレクトの記号は、

* ```1>```[^1] が標準出力
* ```2>``` が標準エラー出力

[^1]: 標準出力のリダイレクトは ```>``` との記述も許されます。
[^2]: Bash でのリダイレクトの記号の ```&>``` は、「標準エラー出力＋標準出力」を合体したリダイレクト出力を表すものです。

```stdout.sh
#!/bin/sh
my_command 1> std_out.txt 2> std_err.txt
my_command 1> std_out_err.txt 2>&1
```
* ```2>&1``` と後置記述すると、「標準エラー出力＋標準出力」を合体させることとなります[^2]。
* また、ファイルを指定する代わりに ```/dev/null``` と指定すると、どこへも書き込みは行われません。
【AWS】Reachability Analyzerを使ってみた

## 概要
AWS内のエンドポイントやVPC間での接続性診断ツールである Reachability Analyzer を使って実際に接続テストを行ってみます。

## 使ってみた
今回接続性テストに使用するのは、以前作成したピアリング接続された2つのVPCです。
ルートテーブル、セキュリティグループは設定済みでICMP及びssh接続が可能な状態となっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2519209/65488dda-cb70-d91c-66bc-4b26ef7cd67a.png)

### パスの作成
VPC画面の左メニューから、 Reachability Analyzer を選択し、「パスの作成と分析」をクリック。
![top.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2519209/c057d92d-9a1e-0dc3-2fc6-74b8f1bed8f3.png)

各種必要事項を入力します。今回はそれぞれのVPCに起動したインスタンスを指定します。
入力したら画面右下の「パスの作成と分析」をクリック。
![Create1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2519209/28e313df-51e0-d180-cd43-b16386bb6f64.png)

パスが作成されるまでしばらく待ちましょう。
![Create2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2519209/74527d6f-18f1-919f-0ac7-7f049a9e0ca8.png)

### 分析結果
数分以内には結果が表示されます。接続が可能な場合は以下のように「到達可能」と表示されます。
![result1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2519209/2e3ea6c3-b231-6aa9-73dd-9544e1e68b56.png)

画面を下部にスクロールしていくと、経由したホップ毎の表示があります。これはわかりやすいですね！
![result2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2519209/722c1886-4e41-159b-a0d3-973c68d15f8b.png)

### 到達できない場合は……？
先程は接続が可能な場合のケースを確認しましたが、次は接続不可のパターンも試してみましょう。

テストに使用したインスタンスにアタッチされたセキュリティグループは、ICMP及びsshしかインバウンドが許可されていません。
今回は送信先ポートに80を指定してみます。
![Create3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2519209/05022480-38ce-cca5-41ac-95c20ff6c537.png)

分析が終わると「到達不可能」と表示されました。
![result3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2519209/decdefd0-02e5-ed09-6299-9ded44fc32f4.png)

到達不可能の場合は、画面下部の分析エクスプローラーに理由が表示されます。
なぜ到達できなかったかが一目瞭然ですね。
![result4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2519209/77e16bba-53ed-d2ad-85c5-d883d2cb9489.png)

さらに、先程の状態から送信元VPCのルートテーブルを変更し、ピアリング接続に向けたルートを削除してみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2519209/b9c4a892-2367-4f7b-40e0-c58b8bfbc6c7.png)

この状態で再度分析を実行してみると、以下の通り「適切なルートがない」「セキュリティグループにの設定によりブロックされる」という二つの理由が表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2519209/a4de9fbe-8fea-82de-ecf6-71c20f2eece9.png)

つまり、複数の理由で到達不可となっている場合にはそれぞれの理由をきちんと表示してくれるということです。これは便利ですね。

今回の検証は以上になります。
AWS内での通信経路の把握や、上手く通信できない場合の問題の切り分けに大変役に立ってくれそうな Reachability Analyzer 、使い方はとても簡単なので皆様もぜひお試しください。

:::note warn
料金について
Reachability Analyzer は有料です。
東京リージョンの場合1分析あたり 0.10USD の料金が発生しますのでご注意を（2022/2/17現在）
:::
エラーに遭遇して解決した時のテンプレをテンプレから3分でつくってみた

# エラーに遭遇して解決した時のテンプレ
<!-- 発端や概要を記載 -->

もと記事

https://qiita.com/House-lovers7/items/debf78eece19e0b88a6c

業務で頻発するから、あった方が便利だなと思った

# 目次
<!-- タイトルとアンカー名を編集 -->
1.[エラーメッセージ](#エラーメッセージ)
2.[考えてトライしたこと](#考えてトライしたこと)
3.[役に立った情報](#役に立った情報)
4.[次に活かすこと](#次に活かすこと)

# 開発環境

共有する必要があれば書く
あまり書かないかも

<!-- 見出しー -->

##エラーメッセージ
```html:エラーメッセージ
##エラーメッセージを書く
```

##考えてトライしたこと

##役に立った情報

カードリンクや記事をペタペタする。

# 次に活かすこと

次に活かすための知見をまとめる。
サクッと記事つくるテンプレ探しの旅

# はじめに/想定読者/なんで書いたのか
<!-- 発端や概要を記載 -->

真っ白なキャンバス
思うがままにブレスト

# 目次
<!-- タイトルとアンカー名を編集 -->
1.[見出し1](#見出し1)
2.[見出し2](#見出し2)
3.[リンク見出し](#リンク見出し)
4.[まとめ/感想](#まとめ)

# 開発環境

共有する必要があれば書く
あまり書かないかも

<!-- 見出しー -->

##見出し1
```html:見出しの書き方
##見出しの書き方
```

##見出し2

# [リンク見出し](URL)

- [リンクテキスト](URL)

# まとめ

最後に締める
RTK Thunk のfulfilled/rejected/pending関数


RTK の`createAsyncThunk` にて`fulfilled`/`rejected`/`pending` が生成される。
これらは関数として実行できるのだが、ドキュメントに使い方がなさそうだったので手もとで動かしてみて確認してみた。

- ポイント
  - thunk関数の引数の有無によって、actionの第3引数の有無も変わる
  - 各action+dispatch ごとに割り振られるrequestIDがあるが、これを明示的に割り当てないといけない

## 引数がないケース

```tsx

export const thunkWithoutArgs = createAsyncThunk('thunkWithoutArgs', async () => {
  return 'actual return payload';
});

describe('async without args', () => {
  it('can create fulfilled', () => {
    const action = thunkWithoutArgs.fulfilled(
      'this is fake return payload',
      'this is fake request id',
    );
    expect(action).toEqual({
      meta: {
        arg: undefined,
        requestId: 'this is fake request id',
        requestStatus: 'fulfilled',
      },
      payload: 'this is fake return payload',
      type: 'thunkWithoutArgs/fulfilled',
    });
  });
});
```

## 引数があるケース

```tsx
export const thunkWithArgs = createAsyncThunk('thunkWithArgs', async (arg1: string) => {
  return arg1 + ':actual return payload';
});

describe('async with args', () => {
  const action = thunkWithArgs.fulfilled(
    'this is fake return payload',
    'this is fake request id',
    'fake arg',
  );
  expect(action).toEqual({
    meta: {
      arg: 'fake arg',
      requestId: 'this is fake request id',
      requestStatus: 'fulfilled',
    },
    payload: 'this is fake return payload',
    type: 'thunkWithArgs/fulfilled',
  });
});
```

## REF

- [redux-toolkit/createAsyncThunk.ts at db0d7dc20939b62f8c59631cc030575b78642296 · reduxjs/redux-toolkit · GitHub](https://github.com/reduxjs/redux-toolkit/blob/db0d7dc20939b62f8c59631cc030575b78642296/packages/toolkit/src/createAsyncThunk.ts#L625-L628)
- [redux-toolkit/createAsyncThunk.test.ts at db0d7dc20939b62f8c59631cc030575b78642296 · reduxjs/redux-toolkit · GitHub](https://github.com/reduxjs/redux-toolkit/blob/db0d7dc20939b62f8c59631cc030575b78642296/packages/toolkit/src/tests/createAsyncThunk.test.ts#L95)
- [redux-toolkit/createAsyncThunk.ts at db0d7dc20939b62f8c59631cc030575b78642296 · reduxjs/redux-toolkit · GitHub](https://github.com/reduxjs/redux-toolkit/blob/db0d7dc20939b62f8c59631cc030575b78642296/packages/toolkit/src/createAsyncThunk.ts#L381-L395)
heroicons でラクラクアイコン実装

TailwindCSS の作成者によって作られており簡単に便利なアイコンの実装ができます！

[https://heroicons.com](https://heroicons.com/)

[https://github.com/tailwindlabs/heroicons](https://github.com/tailwindlabs/heroicons)

# 1. SVG 画像から実装する方法

![heroicons.png](https://raw.githubusercontent.com/mitochon9/qiita/main/image/heroicons/heroicons.png)

```jsx
// 貼り付け

export const Index = () => {
  return (
    <div>
      <svg
        xmlns="http://www.w3.org/2000/svg"
        className="h-5 w-5"
        viewBox="0 0 20 20"
        fill="currentColor"
      >
        <path
          fillRule="evenodd"
          d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-11a1 1 0 10-2 0v2H7a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V7z"
          clipRule="evenodd"
        />
      </svg>
    </div>
  );
};
```

## メリット

- ライブラリのインストールが不要

## デメリット

- コードが長くなる

（icons ディレクトリなどにコンポーネント作成しインポートするとコードがスッキリする）

例）

```jsx
// iconsディレクトリ内にコンポーネント作成
export const PlusIcon = () => {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      className="h-5 w-5"
      viewBox="0 0 20 20"
      fill="currentColor"
    >
      <path
        fillRule="evenodd"
        d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-11a1 1 0 10-2 0v2H7a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V7z"
        clipRule="evenodd"
      />
    </svg>
  );
};

// インポートする
import { PlusIcon } from "../icons/PlusIcon";

export const Index = () => {
  return (
    <div>
      <PlusIcon />
    </div>
  );
};
```

# 2.ライブラリをインストールして実装する方法

```bash
npm install @heroicons/react

or

yarn add @heroicons/react
```

```jsx
import { PlusCircleIcon } from "@heroicons/react/solid";

const Index = () => {
  return (
    <div>
      <PlusCircleIcon className="w-5 h-5" />
      <p>...</p>
    </div>
  );
};
```

## メリット

- コードがスッキリする
- ディレクトリを増やさなくて済む

## デメリット

- ライブラリのインストールが必要

# まとめ

シンプルでおしゃれなアイコンがラクラク実装できます！
謎の余白

background-size:coverで指定した背景画像に謎の余白ご発生。
##対策:
*{margin:0}を全体に当てて個別でmarginを指定しない要素意外のmarginをリセット。
##原因:
使用した背景画像はmacのプレビューにてクロップしてサイズを調整した物であり、その際にmarginが生じたと推測。画像を別のものにしたところ*{margin:0}を充てなくても余白は出なかった為。
react hook form で快適フォーム実装

Reactでのフォーム実装にはreact hook formが便利でした！

- 無駄なレンダリングを減らしてくれてパフォーマンスアップ
- ルールに合わない場合のreturn処理やエラーメッセージを表示するなどの処理が簡潔に書ける
- useStateの量が減るのでコードがスッキリする

などなど、メリットがたくさんです！

[https://react-hook-form.com/](https://react-hook-form.com/)

# インストール

```bash
npm install react-hook-form

or

yarn add react-hook-form
```

# 実装

inputの入力内容をalertで表示させる機能を実装します。

その際に、

- 入力がなければreturnしてエラーメッセージを表示する
- 入力があればエラーメッセージを表示させない
- alert表示後はフォーム内はリセットする

機能も実装しました。

## useStateでの実装

[https://codesandbox.io/s/usestate-form-381rvq](https://codesandbox.io/s/usestate-form-381rvq)

```jsx
import { useState } from "react";

export default function useStateForm() {
	const [text, setText] = useState("");
  const [error, setError] = useState(false);

  const onSubmit = () => {
    // フォーム未入力の場合にerror === trueにする
    if (!text) {
      setError(true);
      return;
    }
    alert(text);
    // 送信完了後にフォームリセット、error === falseにする
    setText("");
    setError(false);
  };

  return (
    <form method="dialog" onSubmit={onSubmit}>
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
      />
      {/* フォーム未入力＋error === trueの場合にエラーメッセージ表示 */}
      {!text && error && <span>入力してください</span>}
    </form>
  );
}
```

## react-hook-formでの実装

[https://codesandbox.io/s/react-hook-form-wedw9k?file=/src/App.js](https://codesandbox.io/s/react-hook-form-wedw9k?file=/src/App.js)

```jsx
import { useForm } from "react-hook-form";

export default function ReactHookForm() {
  const { handleSubmit, formState: { errors }, register, reset } = useForm();

  const onSubmit = (formData) => {
    alert(formData.text);
    reset();
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("text", { required: true })} />
      {errors.text && <span>入力してください</span>}
    </form>
  );
}
```

onSubmitの関数部分が簡潔にできました！

主にエラー時のreturn処理やエラー状態の制御、送信後のフォームリセットの記述が削減できています。

その他にもinputの数が増える場合などでuseStateの数が増えることを防げることや、エラーメッセージを出すための処理が簡単に実装できます。

開発が進んで処理が増えていくごとにコード量に差が出てくるのですごく良かったです！

# まとめ

react hook form最高！
MLflow Projects と Tracking を同時に使う

[MLflow](https://www.mlflow.org/) の [Projects](https://www.mlflow.org/docs/latest/projects.html) は実験の再利用をしやすくするためのツールで、 [Tracking](https://www.mlflow.org/docs/latest/tracking.html) はパラメータや計量を記録するためのツールです。それぞれを紹介している記事はよく見るものの、合わせて使っている記事を見かけなかったのでやり方をメモしておきます。

## tl;dr

`mlflow run` で起動した run ID を Tracking でも使用するようにします。そのためには環境変数 `MLFLOW_RUN_ID` を使います。

## 確認した環境

- Python 3.9.10
- mlflow==1.23.1

## 動かし方

以下で動かすサンプルコードは GitHub の[このリポジトリ](https://github.com/pn11/MLflow-Projects-and-Tracking)にあります。

まず Python 3.8 以降 ([`f'{expr=}'`の記法](https://docs.python.org/3/whatsnew/3.8.html#f-strings-support-for-self-documenting-expressions-and-debugging)を使用しているため) で、 mlflow と tqdm をインストールします。

```sh
pip install mlflow tqdm
```

その上で以下を実行すると `MLproject` に書かれた内容が実行されます。MLflow はデフォルトで conda 環境を使いますが、今回は使わずに実行しています。

```sh
mlflow run --no-conda .
```

`MLproject` は以下のような内容になっています。

```yaml
name: projects-and-tracking

entry_points:
  main:
    command: |
      env | grep MLFLOW && \
      python track.py --mlflow_run_id $MLFLOW_RUN_ID
```

miflow 内で `env | grep MLFLOW` が実行されると `MLFLOW_RUN_ID`, `MLFLOW_TRACKING_URI`, `MLFLOW_EXPERIMENT_ID` が表示されて、これらの環境変数が定義されていることが分かります。このうち `MLFLOW_RUN_ID` を使えば、今まさに実行中の run に対して parameter や metric を記録できます。そこで `python track.py --mlflow_run_id $MLFLOW_RUN_ID` のように引数で run ID を渡します。(なお Python 内で 環境変数から run ID を取得することもできますが、明確に引数に入れたほうがジョブスケジューラ経由で実行する場合などにも使えて汎用性があると思われます。)

`track.py` の中身は以下のようになっています。

```python
import argparse
import os
import random
import time

import mlflow
import tqdm

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--mlflow_run_id')
    args = parser.parse_args()

    print(f"{args.mlflow_run_id=}")
    print(f"{os.environ['MLFLOW_RUN_ID']=}")

    if args.mlflow_run_id:
        client = mlflow.tracking.MlflowClient()
        client.log_param(run_id=args.mlflow_run_id, key="test_param", value=100)
        for step in tqdm.tqdm(range(10)):
            v = random.random()*100
            client.log_metric(run_id=args.mlflow_run_id, key="test_metric", value=v, step=step)
            time.sleep(1)


if __name__ == '__main__':
    main()
```

実行に10秒かかるので、終わったら以下のコマンドで UI サーバーを起動します。

```sh
mlflow ui
```

5000番ポートが空いていない場合は `mlflow ui --port 空いているポート番号` のようにします。ブラウザで <http://localhost:5000> を開くと、以下のようになっているかと思います。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63995/3ccb5037-627a-3919-e54f-8f29d0eb9f7a.png)

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63995/8b17f272-e83c-02a1-f0c6-17ef154fa95b.png)

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63995/4b681801-a5a5-0d84-a85b-04f9116cc0d0.png)


なお MLflow の run の記録方法は色々ありますが、今回はローカルに記録しています。

## 参考リンク

- [python 3.x - Can MLFlow log new metrics in a terminated run? - Stack Overflow](https://stackoverflow.com/a/67473790/11480802)
【React】TODOアプリ

## はじめに
　本記事は、プログラミング初学者が、学習を進めていて疑問に思った点について調べた結果を備忘録も兼ねてまとめたものです。
　そのため、記事の内容に誤りが含まれている可能性があります。ご容赦ください。
　間違いを見つけた方は、お手数ですが、ご指摘いただけますと幸いです。

## ReactによるTODOアプリ

```.jsx

class App extends Component {
  constructor() {
    super()
    this.state={
      count: 0,
      todoList: [],
      value: "",
    }
  }

  onChange(key_value) {
    this.setState(key_value)
  }

  add(todoElement) {
    this.setState({
      todoList: this.state.todoList.concat(todoElement),
      value: "",
    })
  }

  handleDelete(id) {
    let todoList = this.state.todoList.concat()
    let index = 0
    todoList.map((element, idx) => {
      if (element.id == id) {
        index = idx
      }
    })
    todoList.splice(index, 1)
    this.setState({todoList: todoList})
  }

  render() {
    const todoListNode = this.state.todoList.map(element => {
      return (
        <TodoElement
          key={element.id}
          element={element}
          onDelete={this.handleDelete.bind(this)}
          {...this.state}
        />
      )
    })
    return (
      <div>
        <h1>TODO App</h1>
        <AddTodo
          {...this.state}
          onChange={this.onChange.bind(this)}
          add={this.add.bind(this)}
        />
        <ul>
          {todoListNode}
        </ul>
      </div>
    );
  }
}

class AddTodo extends React.Component {
  onChange(e) {
    const key_value = {
      value: e.target.value,
    }
    this.props.onChange(key_value)
  }

  add() {
    if (this.props.value) {
      const todoElement = {
        content: this.props.value,
        id: this.props.todoList.length + 1,
      }
      this.props.add(todoElement)
    }
  }

  render() {
    return(
      <div>
        <input
          type="text"
          value={this.props.value}
          onChange={this.onChange.bind(this)}
        />
        <button onClick={this.add.bind(this)}>追加</button>
      </div>
    )
  }
}

class TodoElement extends React.Component {
  onDelete() {
    this.props.onDelete(this.props.element.id)
  }
  render() {
    return(
      <li>
        <span>{this.props.element.content}</span>
        <button onClick={this.onDelete.bind(this)}>削除</button>
      </li>
    )
  }
}
```
pandas DataFrameの条件にあてはまる値のindexとcolumnを取得する方法

# はじめに
Excelだと条件に当てはまるcellに何かしらの変更を加えるのは簡単
なのにpandas dataframeだと直感的な関数がない！
ということで二通りの方法を考えてみました

#サンプルデータ
```python
import numpy as np

 timestep = [[np.random.randint(2) for _ in range(9)] for __ in range(3)]
 index = [f'station{i}' for i in range(3)]
 columns = list(map(str, np.arange(9)))
 df = pd.DataFrame(timestep, index=index, columns=columns)
```

サンプルデータは0と1のみで構成されたデータフレームです

# 1. 全探索型
```python
 ls = []
 for i in df.index:
     for j in df.columns:
         if df.at[i,j] == 1:
             ls.append([i,j])
```

データフレーム内の値が1である部分だけのindexとcolumnを取得しています
全要素について1であるかどうかの判定を行います
Trueであったものをリストに格納します
なにか変更を加えるときは`df.at[ls[0,0],ls[0,1]]`みたいな感じで取り出せます

# 2. ランダム型
```python
import random

ls = []
sample = 3
for _ in range(sample):
    req_columns = df.loc[:, (df == 1).any()].columns.tolist()
    rand_column = random.choice(req_columns)
    req_index = df[df[rand_column] == 1].index.tolist()
    rand_index = random.choice(req_index)
    ls.append([rand_column, rand_index])
```

random型ははじめに条件を満たすcolumnを抜き出します
そこから今回はランダムな一列を抜き出しています
選ばれた列に対し、再度条件を判定します
条件を満たす行のみが抜き出されるはずです
このなかから、再度ランダムに一行を取り出します
これにより条件を満たすランダムな1cellを抜き出すことに成功したことになります
その値をリストに格納し、sample数だけループを行います

ランダム型は、まどろっこしいですが、各セルごとに確実に変更を加えることに向いています
こんかいはランダムに1列、1行を選びましたが、列ごとに操作したり、行ごとに操作したりがしやすいです
for loopないに変更を加えるコードを足してあげれば、逐次的に変更が可能で、意外とこっちのほうが便利かと思います
40 代おっさん GASのjavascriptの組み込みオブジェクトについて学ぶ

## 本記事ついて

本記事は プログラミング初学者の私が学習していく中でわからない単語や概要をなるべくわかりやすい様にまとめたものです。
もし誤りなどありましたらコメントにてお知らせいただけるとありがたいです。

## 組み込みオブジェクト

組み込みオブジェクトとは
javascriptにおいて良く利用されるクラスについて用意されている。
標準組み込みされているクラスを組み込みオブジェクトと呼ぶ。

|組み込みオブジェクト|説明|
|--------------------|----|
|Number|数値を使うラッパーオブジェクト|
|String|文字列を扱うラッパーオブジェクト|
|Boolean|審議値を扱うラッパーオブジェクト|
|Array|配列を扱うオブジェクト|
|Function|関数を扱うオブジェクト|
|Object|すべてのオブジェクトのベースとなるオブジェクト|
|Date|日付や時刻を扱うオブジェクト|
|RegExp|正規表現を扱うオブジェクト|
|Error|例外情報を扱うオブジェクト|
|Math|数学的な定数と関数を提供するオブジェクト|
|JSON|JSON形式のデータを操作する機能を提供するオブジェクト|

## 組み込みオブジェクトのインスタンス化とラッパーオブジェクト

使用する場合はインスタンス化が基本となる。

構文

```javascript
new 組み込みオブジェクト名(引数1, 引数2, ...)
```

Number, String, Booleanはプリミティブ型と対応するオブジェクトに関しては、その限りでない。

この3つはラッパーオブジェクトというもので各データを包み込むことでオブジェクトとして操作を可能にしている。

## 参考資料

<https://www.amazon.co.jp/s?k=google+apps+script+%E5%AE%8C%E5%85%A8%E5%85%A5%E9%96%80&adgrpid=110264232688&gclid=CjwKCAiA9aKQBhBREiwAyGP5lSl7AJJLCvOEHb4wQgMlyqW1fll5X8GDTT_Rkd1_soUAyIPMXQr26hoClHEQAvD_BwE&hvadid=553833563682&hvdev=c&hvlocphy=1009076&hvnetw=g&hvqmt=b&hvrand=4378489642044417389&hvtargid=kwd-594191211348&hydadcr=4106_13159878&jp-ad-ap=0&tag=googhydr-22&ref=pd_sl_2x1owglv0s_b_p52>
【Unity】PUN2でPhotonView,PhotonTransformを配置しているのに位置同期がとれない場合に確認する箇所

# はじめに
PUN2を利用した開発時にPhotonView,PhotonTransformをアタッチしても位置同期がとれないケースがあったので対象方法を記録します。

# 事象
PhotonView,PhotonTransformをアタッチしたオブジェクトのTransFormのPositionが(0,0,0)となっている。

# 原因
PhotonView,PhotonTransformをアタッチしているゲームオブジェクトが親オブジェクトを持っている状態でPhotonTransFormのUseLocal（LocalのTransformをWord座標として同期する）のチェックがONにしていた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1336886/836466c1-51dd-e70b-b629-276479ba9dfb.png)

# 対処方法
PhotonTransFormのUseLocal（LocalのTransformをWord座標として同期する）のチェックを外す。
個人開発しくじり先生 技術的負債でシステムが死んだ話


# 年利30%

![スライド4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234396/c6a3ad4d-7c2a-6405-ace0-0cf5020769ff.png)

大学生の頃に、寝食を忘れて機械学習による株の自動売買システムを作ったことがありました。
上記の画像は、実際にそのシステムを稼働させて実際にあげた利益になっています(記録にないだけでもう少し続けていたはず)

今回の記事は、そんな手塩をかけて育てたシステムが技術的負債で死んだ話です。

# 抱えていた技術的負債の一覧
プログラミング経験ほぼなしの中自己流で作っていたため、今思うとかなり厳しい問題をたくさん抱えていました。

* R言語で全部作ってる
* 変数名が全部日本語😓
* R AnalyticFlowというマイナーなツールで開発
* 運用に手動オペレーションが多数存在
* コードベースが数万行(検証コード含む)
* 株価データを取得するスクレイピングがライブラリ依存
* ファイルパスなどが絶対パスで指定されていて自分のPCでしか動かない
* 実行完了まで2~3時間かかる(毎日)
* 実行が日を跨ぐとエラーが起きる

## R AnalyticFlowを使っていた

![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234396/86392b9e-0264-9a3a-0286-b0e25936ffeb.png)

R AnalyticFlowはRスクリプトを矢印で繋いだり外したりして処理フローを制御できるとんでもなく素晴らしいツールです。
これは試行錯誤には非常に使いやすく、株価予測をするシステムを作るときにたくさん検証コードを書き、繋いだり外したりして効果を測定するなどの使い方をしていました。
ただ、データサイエンスをすることにはとても効果的であったものの、システムの実行を自動化ができない、API経由で呼び出せないなど、運用をすることは難しい状態でした。

また繋ぎかえが簡単にできてしまうが故に、運用の中に手動オペレーションが多数存在していました。

* データチェックに失敗したら処理を繋ぎかえて中身を確認
* 売買がうまくいっていない時はパラメータ調整して繋ぎかえ
* 注文失敗したら注文取り消し処理につなぎ変えて実行

などなど。
R AnalyticFlowで矢印を繋ぎ合わせて様々な処理、分析を手動で行なっていました。

# 状況の変化
## R言語の衰退

![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234396/d28652e3-f25e-0223-2a3f-98407b4941bd.png)


僕がこのシステムを作っていた頃は、時期的にはDeepLearningが流行りだしてRよりもJupyter Notebook(Python)が急成長していた時期でした。
それによりR言語コミュニティの縮小が始まったと思っています。

## 依存していたライブラリの死

Yahoo Financeから株価情報を取得するのに「RYahooFinanceJ(正式名称は失念)」というライブラリにに依存していました。
しかしYahoo FinanceのUI更新によりライブラリが動かなくなることがたまに発生するようになりました。
最初の頃は数日でアップデートがされていたのですが、徐々にメンテがされなくなり、最終的に機能停止しました。

当時は自分はスクレイピングの知識がなく(CSSの知識すらなかった！)、このライブラリに完全に依存していたため、動かなくなった日の夜は叫びました。

## 生活の変化
個人的に家庭教師のバイトを始めて生活が忙しくなりました。
手動のオペレーションがあって毎日2~3時間かかるプログラムを実行していられなくなってきました。

# 状況の変化に耐えられずシステムが死亡
## R言語の衰退
依存ライブラリのメンテナンスがされなくなり、代替案を調べてもPythonを勧められました。
今からPythonでリプレイスするのか？　そんなのは無理でした。

## 依存ライブラリの死
上記に被りますが、システムの根幹にあったスクレイピングを完全に外部に依存していたため、そこが死ぬとシステムが死にました。

## 生活の変化
毎日数時間のオペレーションをする時間がなくなりました。
今から完全自動化させるための改修をすることは無理でした。

## 最終的に
MacのOSアップデートとともにR AnalyticFlowも開けなくなり、完全に闇へと葬られました


# しくじりから学んだこと
![スライド20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234396/5911473b-2b35-b038-603b-ab1ddafa57fc.png)

技術的負債やシステムの欠陥は安定稼働しているうちは大きな問題を引き起こしませんが、状況が変化することでシステムの死を招きます。
今回の件から以下のことが言えるのではないかと思います。

① 落ち目のコミュニティに乗るのは危険
② システム根幹に関わるライブラリが死んだ時のリスクは考えておく
③ オペレーションのことまで考えてシステムを作る


以上です。　ありがとうございました！
.gitignoreで除外できないとき

開発途中でGit管理から除外したいファイルがでてきたのですが、ちょっと戸惑ったのでメモ

#認識
ワークツリー => インデックス（ステージング） => ローカルリポジトリ => リモートリポジトリ
.gitignore に記載された対象ファイルは、`git add` の実行時に、インデックスに追加されない

=>まだ追跡されていなければ、`git add` の実行時に追跡から除外できる

=>すでに追跡されていれば、インデックスから削除してから

#解決方法

```
# インデックスからファイルを削除
$ git rm --cached <FILE_NAME>

# インデックスからディレクトリごと削除
$ git rm --cached -r ディレクトリ名
```
Gitのエラー：Permission denied (publickey). を解決した備忘録

`git push origin master`
で吐かれたエラーを解決した方法の備忘録です

# やろうとしていた事
新しいリポジトリを作成したので、ファーストコミットをリモートにpushしようとしていました

# 現象
```
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.
```

# それまでにやっていた事の時系列

### ①ローカルでリポジトリの作成
`$ git init`
→無事完了していました

### ②変更点の登録
`$ git add .`
→問題なし

### ③コミット
`$ git ci -m "Feat:first-commit"` ... エイリアスつけてます
→問題なし

### ④masterにpush
`$ git push origin master`
→ここでエラー！


# 原因
**githubに公開鍵の設定ができていない、又は再度pushが必要とのことでした**

### エラーの確認① githubとSSH接続できるかチェックしてみる
`$ ssh -T git@github.com`

成功例は↓こうなります↓

```
Hi 俺君! You've successfully authenticated, but GitHub does not provide shell access.
```

エラーを吐くと↓こうなります↓

```
Warning: Permanently added the RSA host key for IP address '俺君のIPアドレス' to the list of known hosts.
git@github.com: Permission denied (publickey).
```

上記と同じエラーを吐いていました

# 解決手順

### ① 鍵のペアがあるか確認してみる
移動します
`$ cd ~/.ssh`

ここで、配下にファイルが存在するか確認しましょう
`$ ls`

```
id_rsa id_rsa.pub
```

このペアが存在すればOKです
　無ければ生成しましょう！

### ①-2 鍵のペアを生成する（必要な人だけ）
`$ ssh-keygen -t rsa`

Enter連打して完了です

### ② その公開鍵（id_rsa.pub）をgithubに登録する
まず、githubを開きます
自分のアイコンをクリックして、「Settings」を押します

↓この画像↓の色が変わっている「SSH and GPG kes」をクリックします
![スクリーンショット 2022-02-17 23.26.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1422262/e440c495-fda6-02d7-d57e-714fb22fbfe2.png)


次に、画面右側あたりの緑色のボタン、「new SSH key」をクリックします

すると、↓こんな画面に変わります↓![スクリーンショット 2022-02-17 23.28.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1422262/0d0ae50d-4838-c9c5-f992-ee49f91b4835.png)

【Title】
公開鍵の使用用途を書きましょう、忘れないために...

【Key】
ここに公開鍵の情報をバシッとコピペするのですが、下記2パターンで取得できます

```
①ターミナルから、クリップボードにコピーさせちゃう方法
$ pbcopy < ~/.ssh/id_rsa.pub

②catして、自分でコピー取る方法
$ cat ~/.ssh/id_rsa.pub
```

どちらでも大丈夫ですが、**改行や空白が入らない様に気をつけてください**

では、取得した情報をバシッと貼り付けて保存しましょう

### ③もう一度ssh接続をテストする
先ほどと同じコマンドです
`ssh -T git@github.com`


うまくいけばこれで完了です
ちゃんとpushできるようになりました

# 参考文献
https://qiita.com/tetsu-upstr/items/e72147250701cf30ee72

https://docs.github.com/ja/authentication/troubleshooting-ssh/error-permission-denied-publickey

https://ormcat.net/blog/20210509_github-denied-publickey/
VSCode で filesystem (C++17由来) が使えない時の対処法

## はじめに
こんにちは、かべです。先日 C++ で競プロ用ツールを書いていた時にタイトルのことで少しハマったため解決法について書きます。

## 環境
- WSL Ubuntu 20.04
- Visual Studio Code 1.64.2
- g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0

## 問題
C++17 から使用できるようになった [filesystem](https://cpprefjp.github.io/reference/filesystem.html) を利用してコードを書いていると、VSCode からエラーが検出されました。

![problem.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/721694/32a0e3ac-38b8-f7d1-01aa-08041d2bc8ff.png)

C++17 が使えるか確認するために `g++ --std c++17 ...` でビルドと実行をしてみましたが特に問題なく動きます。恐らく VSCode 側で何か問題があるのでしょうが、クイックフィックスなどでは解決できませんでした。

## 解決策
https://stackoverflow.com/questions/49397233/how-to-enable-c17-support-in-vscode-c-extension

こちらが解決策です。拡張機能 C/C++ の設定にある cppstandard からデフォルト値を目的のバージョンにすることで補完などがちゃんと効いてくれるようになります。

![setting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/721694/c131334c-18ce-16e0-cb5b-9e9268b08660.png)

![done.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/721694/d2d1dce7-703b-9f3f-a1f9-a80f8974f25c.png)

おしまい。
今あるGCP環境をTerraform化するときにやったことメモ

## 前提
- terraform 初心者

## 背景
- 今あるGCP環境をTerraform化したい
- 環境は以下のような感じ

```plantuml

:User:
cloud LoadBalancer
cloud GCS
cloud CloudRun_API
cloud CloudRun2
cloud CloudRun3

User -> LoadBalancer : request
LoadBalancer -> GCS : html/js/css
LoadBalancer <--> CloudRun_API
CloudRun_API --> CloudRun2
CloudRun_API --> CloudRun3
```

## やったこと
### terraformer
- [terraformer](https://github.com/GoogleCloudPlatform/terraformer) というツールを使うと、既存のGCP環境をtfファイルに落としてくれるらしいので、これを使った
- 結論からいうと非常に役立った。ただ、複数環境構築などをしたい場合、インポートしたそのままでは使えないので、環境依存の項目値は適宜variablesに切り出す、などが必要だった。
- あと、cloud runなどは一部対応していないサービスもあった

### terraform import
- terraformerが対応していないものは、terraformコマンドで、`terraform import`というものがあるのでこれを使用した
- ただしこれはtfファイルではなく、tfstateファイルを吐き出すので、そのままでは使えない
- 参考にするだけ

### state管理どうするか
- 調べた感じ以下のようなパターンがあった
    - GitLab Managed
    - Terraform Cloud
    - GCS
    - Git
- Terraform Cloudが一番とりあえず楽そうな感じだったので、それを採用

### ディレクトリ構成
- 以下のような構成にした(ちょっとぼかしてます)。調査したがおおむねの構成はみんな似てるものの、最適解はチーム毎に違いそうということなので、対象のサービスでやりやすい単位で組むのがよさそう

```
envs
  - develop
  - staging
  - production
modules
  - loadbalancer
  - api
  - gcs
    - bucket1
    - bucket2
  - iam
```

- envsの配下が環境毎に分かれていれば、terraform cloud側でワークスペースを分けて管理できて便利

### 実際の落とし込み
- ひたすらちまちまtfコードかいて、少しずつ動作確認して・・を繰り返していく
- terraformerなどを使用したとはいえ、かなり地道で時間がかかった

## その他
### terraformのバージョンとproviderのバージョンは別である
- terraformが最新のバージョンだったので、自分は最新版でterraformしてるつもりだったが、providerが古かった。現時点(2022/01/28)の最新版である4.8.0にした。tfファイルを修正してから、`terraform init -upgrade` することで解消。

```tf
terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "4.8.0" # 3.5.0になってたのを修正
    }
  }
}
```

```tf
terraform init -upgrade
```

### gcpのロードバランサをどう書くかわからない
- 調べてみると、複数のサービスに分かれているとのこと
- このあたりが関係してくる。iamあたり絡めるともっといろいろ設定があるかも。
    - google_compute_backend_bucket
    - google_compute_backend_service
    - google_compute_global_address
    - google_compute_global_forwarding_rule
    - google_compute_target_https_proxy
    - google_compute_target_http_proxy
    - google_compute_url_map
    - google_compute_region_network_endpoint_group

### terraformでどこまでできるのか
- gcpプロジェクト作成とかもできるので、ほぼなんでもできる
- ただterraformの権限がデカくなりすぎるので、けっこう怖い
    - iam全部消すとかもできちゃうし、他のプロジェクト消すとかもできそう。

### gcloudとの分担
- terraform用のサービスアカウントというものは必ず必要になる
- 事故を防ぐために対象プロジェクトに対してのみterraformを実行させたい
    - 誤って別プロジェクトに対して操作してしまうとか恐ろしすぎる
- つまりgcloudコマンドは必ず実行することになる(コンソールポチポチしてもいいけど)
    - あとAPIの有効化周りはterraformではちょっとやりづらい

- gcloudのスクリプトで最初のプロジェクト作成と、terraform用のサービスアカウント作成、APIの有効化をやらせることにした

## 感想
- 結構大変だったけど、GCP環境が全部コード化されて管理下におかれるのはなんか楽しさがある
- アプリばかり開発してきたけど、こういうのはわりと面白いなと感じた
Mac コマンドでChromeを開く（エイリアス）

htmlファイルを作成していてちょっと確認したい際などにいちいちFinder開いてChromeで開くのが面倒であった。
コマンドで開けたら便利だと思ったのでちょっと調べたら簡単にできた。

Chromeで開く際はこのコマンドで開ける。

```sh
open -a google\ chrome hoge.html 
```

ちょっと長いのでエイリアスで開けるようにする。

```sh
#zshの場合
vim ~/.zshrc 

#bashの場合
vim ~/.bashrc 
```
```sh
#以下を追加
alias chrome='open -a google\ chrome'
```

これでエイリアスを使って開けるようになった。

```sh
chrome hoge.html 
```
コードカバレッジの超初歩知識

##カバレッジ（coverage）とは

所定の網羅条件がテストによってどれだけ実行されたかを割合で示したもの（網羅率）。

網羅性の水準により、大きく分けて次の3種類があります。

###命令網羅（C0、statement coverage）
すべての命令（実行行）を少なくとも1回は実行すればOKという網羅水準。
分岐網羅、条件網羅に比べて網羅性は最も低いです。

###分岐網羅（C1、branch coverage）
判定条件の真偽を少なくとも1回は実行すればOKという網羅水準。
分岐網羅が100%になれば命令網羅は必然的に100%になるという関係にあります。

分岐が論理和（OR）や論理積（AND）を含む複合条件の場合、
各条件式の組み合わせは考慮されないため、
分岐網羅では判定式内にある不具合を検出できないケースがあります。
そこで次の条件網羅という網羅水準の考え方がでてきます。

###条件網羅（C2、condition coverage）
それぞれの条件式における真偽が少なくとも1回は実行されればOKという網羅水準。
条件網羅で100%になっても必ずしも命令網羅と分岐網羅が100%になるわけではありません。
とはいえ、3つの網羅水準の中では最も厳格なコードカバレッジです。

##カバレッジの注意点
**①カバレッジ100%＝しっかり品質保証がされている、というわけではない**
（例）a = b / c という1行だけのコード
cに0がきた場合、ZeroDivisionErrorが発生する（Pythonの場合）が、try-exceptをしていない。
しかし数字上はカバレッジ100%です。
カバレッジは、テストが不十分であることは示せますが、十分であることの保証にはなりません。

**②カバレッジを上げることがテストの目的と化してはならない**
一般に、カバレッジが上がれば上がるほど、バグは検出されにくくなります。
40%から70%に上げることは容易ですが、80%から100%にあげる作業には、
それ以上に工数がかかる場合があります。
不必要に厳しいカバレッジ水準を追求したり、なにがなんでも100%を目指すと、
品質が成熟していてバグの検出が見込まれないと思われる部分も数多くテストしなければならないことにつながり、工数に無駄が生じてしまう恐れがあります。
（典型例としては、まず起こり得ない例外処理を頑張って通しても品質向上にはつながらない）
【JavaScript関数ドリル】Lodash関数の実装【勉強用】_.nth関数


#【JavaScript関数ドリル】初級編のnth関数の実装のアウトプット

       
```javascript:nth関数の挙動
var array = ['a', 'b', 'c', 'd'];
 
_.nth(array, 1);
// => 'b'
 
_.nth(array, -2);
// => 'c';
```
第2引数が負の場合、配列から第2引数個目の値を返す

##nth関数の課題内容
https://youtu.be/RYeY83dTQPE

##nth関数に取り組む前の状態
- 正負を判断する方法を検索すればよさそう

##nth関数に取り組んだ後の状態
- 三項演算子の使い方をよく理解できた
- 解答が２行に対し自分は７行も書いてしまった
- 今後も三項演算子について積極的に使用したい



##nth関数の実装コード

```javascript:
const nth = (array, index) => {
    if (Math.sign(index) === 1) {
        return array[index]
    } else if (Math.sign(index) === -1) {
        const absNumber = Math.abs(index);
        const returnNum = array.splice(-absNumber, 1);
        return returnNum.shift();
    };
};

console.log(nth([1, 2, 3, 4, 5], 3));
// => '4'

console.log(nth([1, 2, 3, 4, 5, 6, 7, 8], -4));
// => '5'

```



##nth関数の解答コード

```javascript:
function nth(array, n = 0) {
  return n >= 0 ? array[n] : array[array.length + n];
}

const array = ['a', 'b', 'c', 'd'];

console.log( nth(array, 1) );
// => 'b'

console.log( nth(array, -2) );
// => 'c';
```
40 代おっさん GASの静的メンバーついて学ぶ

## 本記事ついて

本記事は プログラミング初学者の私が学習していく中でわからない単語や概要をなるべくわかりやすい様にまとめたものです。
もし誤りなどありましたらコメントにてお知らせいただけるとありがたいです。

## 静的メンバーとは

インスタンス化をしなくとも直接的に使用できるプロパティおよびメソッドを定義できる。
それを静的プロパティ、静的メソッドと呼び、まとめて静的メンバーと言う。

構文

```javascript
クラス名.プロパティ名
```

```javascript
クラス名.メソッド名(引数1, 引数2, ...)
```

クラスに静的メンバーを追加すには、クラス定義するクラス分の後に、以下の記述をする。

```javascript
クラス名.プロパティ名 = 値
```

```javascript
static メソッド名(仮引数1, 仮引数2, ...) {
  //処理
}
```

class分のあとに関数リテラルを代入することでも、静的メソッドの定義は可能

構文

```javascript
クラス名.メソッド名 = function(仮引数1, 仮引数2, ...) {
  //処理
}
```

お試し

```javascript
function toshiki() {
  class Person {
    constructor(name, age) {
      this.name = name;
      this.age  = age;
    }
  
    static greet(name) {
      console.log(`こんにちは、私は${name}君です。`);
    }
  }

  Person.favorite = 'みかん';

  console.log(Person.favorite);
  Person.greet('利樹');
}
```

## プロトタイプメソッドの変更

静的メンバーの追加の方法を応用して、class分による定義のあとに、プロトタイプメソッドの変更をすることが可能

構文

```javascript
クラス名.prototype.メソッド名 = function(仮引数1, 仮引数2, ...) {
  //処理
}
```

お試し

```javascript
function toshiki() {
  class Person {
    constructor(name, age) {
      this.name = name;
      this.age  = age;
    }
  
    greet(name) {
      console.log(`こんにちは、私は${this.name}君です。`);
    }
  }
  Person.prototype.greet = function() {
    console.log(`${this.name}はかわいいです。`);
  };

  const p1 = new Person('利樹', 4);
  p1.greet();
  
  console.log(Person.prototype.greet.toString());
  console.log(p1.greet.toString());
}
```

コンソールログは
23:20:11	情報	利樹はかわいいです。
23:20:11	情報	function() {
    console.log(`${this.name}はかわいいです。`);
  }
23:20:11	情報	function() {
    console.log(`${this.name}はかわいいです。`);
  }
になっていると思います。
このようにプロトタイプメソッドの変更が行われたことになります。

## 参考資料

<https://www.amazon.co.jp/s?k=google+apps+script+%E5%AE%8C%E5%85%A8%E5%85%A5%E9%96%80&adgrpid=110264232688&gclid=CjwKCAiA9aKQBhBREiwAyGP5lSl7AJJLCvOEHb4wQgMlyqW1fll5X8GDTT_Rkd1_soUAyIPMXQr26hoClHEQAvD_BwE&hvadid=553833563682&hvdev=c&hvlocphy=1009076&hvnetw=g&hvqmt=b&hvrand=4378489642044417389&hvtargid=kwd-594191211348&hydadcr=4106_13159878&jp-ad-ap=0&tag=googhydr-22&ref=pd_sl_2x1owglv0s_b_p52>
attributesとは？attributes の登録・更新メソッドあり

##attributes とは
``attribute``とは、オブジェクトが抱えるデータのことで、``属性（attribute）``と呼ぶことがある。

例えば「human」というクラスがあったとしたら、「name」や「job」などの「属性（attribute）」を追加して使用することができる。

インスタンスメソッド``attributes``で「属性名 => 値」のハッシュが取り出せる。

```rb
form.attributes
#=> {"title"=>"Foo", "body"=>"bar", "draft"=>false, "published_at"=>2019-12-15 15:40:00 +0900, "tags"=>["Ruby", "Rails"]}
```

インスタンスメソッドまたはクラスメソッドの``attribute_names``で属性一覧が取り出せる（Rails 6で追加）

```rb
form.attribute_names
#=> ["title", "body", "draft", "published_at", "tags"]
```

##attributes の登録・更新メソッド
###・ attributes=
特定の``attribute``を変更する。
オブジェクトの変更をしただけで、**DBには保存されていない。**
DBに保存する場合は``save``を実行する必要がある。

###・ assign_attributes
特定の``attribute``を変更するためのメソッド。
``attributes=``と同じく**DBには保存されない**。
DBに保存する場合は``save``を実行する必要がある。

###・ update_attibutes
複数の``attribute``をまとめて変更して**DBに保存する**。

###・ attr_accessor
``attr_accessor``は、型に縛られず値を入れることができる。　

型に縛られず値を入れることができる、とは？
[Railsから入った人へ【attr_accessor】って？](https://qiita.com/Hassan/items/0e034a1d42b2335936e6)の記事が理解しやすかったのでここからの引用。

例えば、name,descriptionという属性を持つUserオブジェクトを定義しようとすると

```rb
class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name
      t.string :description

      t.timestamps null: false
    end
  end
end
```

Railsでは上記の様な migration ファイルを作成するが
DBを扱わない純粋なRubyのコードでは下記のように記入ができる。つまり、型に縛られず値を入れることができる。

```rb
class User
  attr_accessor :name, :description
end
```

##参考記事
[Rubyでattributeを使う方法を現役エンジニアが解説【初心者向け】](https://techacademy.jp/magazine/49649)
[Railsから入った人へ【attr_accessor】って？](https://qiita.com/Hassan/items/0e034a1d42b2335936e6)
[Active Recordのattributesの更新メソッド](https://morizyun.github.io/ruby/active-record-attributes-save-update.html)
[update_attibutesとassign_attributes違い](https://qiita.com/ryuuuuuuuuuu/items/d0819cc46976cdde8731)
[ActiveModel::Attributesを使う](https://qiita.com/kazutosato/items/91c5c989f98981d06cd4)
資格勉強にはlearningBOXがおススメ

# 前置き~~(言い訳)~~

~~転職活動で慌ただしくて、2022年が始まってから2か月もQiita放置してしまったので~~
最近使い始めましたが、[learningBOX](https://lms.quizgenerator.net/)が資格勉強にもってこいだったので紹介記事を書きます。

# learningBOXとは

検定や研修に使える、e-learning作成システム。
公式の導入説明によると、社内研修や検定試験、塾や大学でも使えるとの事です。

# learningBOXを活用

特筆すべきは、**個人利用は無料**、**WEB上でe-leraningで教材やテストが作れる**ことです。

つまり、一度暗記カードのようなテストや、模擬試験のような理解度を問うテストを作成すれば、タブレットなりスマホなりPCなりからアクセスして簡単に勉強できる。

すなわち、**スキマ時間に勉強しやすい**。

また、問題作りながら覚えられるので、一石二鳥。

# あとがき

詳細な情報は大分省いています。
が、ノートに書いて覚えて理解するだけが勉強じゃない！と言いたかっただけです。

これ使って、直近のWEBデザイン技能検定と、情報処理安全確保支援士と、pentest+の勉強してます。