OmniAICreator/Qiita-1.07M huggingface dataset:
AWS認定試験ことはじめ

先日、SAA-C03に合格しましたので、一連の流れを記載しておきます。初めて受験される方の参考になれば幸いです。

## AWSの利用経験

プライベートではMinecraftサーバーを身内向けにAWS上で動かしていました（コスト的にはかなり微妙で、現在は止めています）。
また、業務ではAWS上で動いているアプリを作る・改修する関係の仕事を2～3年ほどやってました。
仕事の守備範囲拡大と、真面目にAWS周りを勉強する目的を兼ねてとりあえず受験することにしました。

## 受験勉強
### 期間

11月半ばに勉強しようと思い立って年内に1度受験と決めたのですが、結局本格的に着手できたのは仕事の都合もあって12月半ばになってしまったので、1月受験を目標にしました。
が結局これも伸びて2月頭に...:sweat_smile:

### 参考資料

[AWS認定資格 ソリューションアーキテクト-アソシエイトの試験対策から学ぶ！超クラウド入門書](https://amzn.asia/d/50vAgsM)なる本(以下、入門書と表記します)を購入しました。
↑の書籍で紹介されている[ping-t](https://mondai.ping-t.com/g)なる無料の問題集があったのでこれを使いました。

:::note info
ping-tで公開されている問題集は2024年1月時点でSAAのみです。
今後別の資格を受験する時は問題集を別に探す必要がありそうです。
:::

### 勉強の手順

とりあえず入門書を読み、問題集を解きました。

最終的に、ある程度問題が残る形になりました。残ったのは模擬形式の問題が殆どです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210706/cf905178-4534-a42b-8b85-dd1a8aa4e512.png)

Glue/Athena/EMR辺りは業務で利用経験があったので飛ばして、機械学習も比重的に重くはならない読みで浅く、入門書を読んで用途の割に理解の薄いと分かったVPC・コンテナ・DB・認証関連を重めに解きました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210706/99f7fcbe-15a1-6766-49bc-a6c6e5367825.png)

## 受験申込

受験する上で事前に決めておきたいのは下の3つです。

* 受験する場所: オンライン・テストセンター
* 受験日時: 24時間以降(テストセンターはテストセンターが実施している時間)
* 持物

今回は下記の通り選択しました。

* 受験する場所: テストセンター
* 受験日時: 2/4
* 持物: 身分証明書・目薬etc

### 場所・日時

ネット試験も考えましたが、せっかくなのでピアソンVUEを利用したオンサイト受験を選択しました。
首都圏の大きめの駅なら大体徒歩数分の所に受験会場があります。
テストセンターの申し込みは前日（24時間前？）まで受け付けがあるようですが、余裕を持っておいた方がよいでしょう。2/1に申し込んで2/4に受験しました。
凄いことに、平日から土日まで毎日様々なテストセンターで受験可能なようです。資格周りはIPA・日商簿記・自動車免許くらいだったのでなかなか新鮮な受験になりました。

### 持物（:warning:要注意）

**私物を持ち込めないテストセンターがあるようです。**
他記事など参考に目薬・無地の眼鏡拭き・無地のタオルハンカチを持参したのですが、**すべて不許可**でした。
[眼鏡拭きの代わりにティッシュ](https://www.taihodo.net/visio/24722)を勧められましたが、丁重にお断りしました:sweat_smile:
私が受験した会場では`ポケットを裏返して空であることを見せてください`と言われるほどで、持ち込めるものは皆無でした。
何か私物を持ち込みたい場合は事前に会場へ電話確認などすることをお勧めします。

というわけで、テストセンター受験の場合に必要な持ち物は身分証明書**2点**だけです。

### 受験

CBT方式で受験することになりますが、ピアソンVUEで受験する場合、[CBT試験のデモ](https://wsr.pearsonvue.com/demo-jp/)が用意されています。
一度確認しておくとよいでしょう。

### 受験結果

受験結果は7時間後に出ました。流石AWS、早いです。
点数は777/1000点でした。更新の時はもう少し取れるようにしておきたいです。

## まとめ

:heavy_check_mark: SAA-C03を受けてみた
:heavy_check_mark: 24時間前以降、ある程度自由な時間でテストセンター申し込みが可能
:warning: ただし、持ち込めない私物がないか確認しておく必要がある

といったところでしょうか。執筆現在、再受験無料バウチャーが配布されたりしているようなので、そういったタイミングで受験を進めていくのがよいかもしれません。
次はDVAを取る方向で勉強していきつつ、もう少し本格的にハンズオンしていきたいと思います。
LinuxでDockerを使いこなす：機械学習の環境構築マニュアル

Linux上でDockerを使用して機械学習の環境構築を行ったので、備忘録を兼ねて書きました。

とりあえずLinuxで機械学習したいけど上手く動かなくて困っているという方におすすめの記事です！

# Dockerとは
Dockerは、アプリケーションを開発、配布、実行するためのプラットフォームです。細かい説明は省きますが、Dockerを使用することで、アプリケーションを簡単に他の環境に移したり、一貫性のある環境で開発やテストを行うことが可能になります。

この記事ではDockerというサービスを利用して、Linux上に自分でカスタムした仮想環境のLinuxを置く、すなわち環境構築を行う方法が説明されています。

# LinuxにDockerをインストールする方法
以下の公式サイトの「リポジトリを利用したインストール」通りに入れればokです

https://matsuand.github.io/docs.docker.jp.onthefly/engine/install/ubuntu/

更に、Dockerのコマンドをsudo権限なしで使えるようにするために以下のコマンドを実行します。

```sudo usermod -aG docker <your-username>```

このコマンドの実行によって、your-usernameというユーザーをdockerグループに追加し、そのユーザーがsudoを使わずにDockerの機能を使えるようになります。

また、自分のyour-usernameが分からない場合は```whoami```コマンドをシェルに入力すれば確認できます。

# 機械学習の環境構築をする時にチェックすべき所
Dockerの話に詳しく入る前の知識として、機械学習の環境構築をする時にチェックすべき所があります。
それは自分が入れるべきCUDAとPytorchのバージョンをしっかりと特定することです。

ここの互換性がなければ機械学習モデルは上手く動きませんし、逆にここさえ上手く合っていれば修正は簡単です。

もう少し詳しくお話しすると、GPUとCUDAの互換性とCUDAとPytorchの互換性の２つがクリアしていればOKです。バージョンの探し方については以下の記事で詳しく書きましたので参考にして下さい。

[機械学習の環境構築ガイド！CUDAとPyTorchのバージョンが合わないエラーからの脱出](https://qiita.com/gureibu0326/items/7fee2bba2f254b20d8c3)


# 実例を通したDockerでの環境構築
さて前節まででDockerのインストールが完了し、自分が入れたいCUDAとPytorchのバージョンが特定できたはずです。

ここからは実際にDocker上で環境構築を行なっていきます。まず、以下のDocker Hubのサイトから自分がインストールしたいLinuxを探します。

https://hub.docker.com/r/pytorch/pytorch/tags

このページには様々なバージョンのCUDAとPytorchが搭載されたLinuxファイルが配布されています。

今回はCUDA=11.1, pytorh==1.8.0のバージョンをダウンロードしたいとして話を進めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3564290/80dc49a6-b5a8-7101-327e-ffc7a0937f31.png)

先ほどのサイトからCUDA=11.1, pytorh==1.8.0に合うtagを発見し、そのフォルダを自分のローカルフォルダにダウンロードします。
これを行うためには、上の写真の右側にある```docker pull pytorch/pytorch:1.8.0-cuda11.1-cudnn8-runtime```をshellに入力してあげれば大丈夫です。

これによってローカルにダウンロードが完了しました。ここでダウンロードされたものはDockerイメージと呼ばれるものなのですが、ここから更にDockerコンテナを作成します。

用語の解説として、Dockerイメージは家の設計図、Dockerコンテナは実際に建てられた家みたいなものです。
実際のプログラムを実行する環境はDockerコンテナで、それを作るための設計書がDockerイメージです。Dockerイメージからは複数のDockerコンテナを作ったり、逆に作ったDockerコンテナを少し改造してDockerイメージとして保存することもできます。

話を戻して、次のステップとしてDockerイメージからDockerコンテナを作成します。
基本的には次のコマンドで作成できます。
```docker run -it <イメージ名:タグ名> bash```
コンテナ名は自分で自由に付けられる名前です。

しかし実際にDockerコンテナを作成する場合は以下のようなコマンドになります。
```docker run --gpus all --shm-size <size> -it --name <コンテナ名> --mount type=bind,source=<place-of-model>.,target=/workspace pytorch/pytorch:1.8.0-cuda11.1-cudnn8-runtime bash```

これを実行することで、contena_youという名前のDockerコンテナが完成します。
それぞれの意味を以下に解説します。

>```--gpus all```: このオプションは、Dockerがホストマシンの全てのGPUにアクセスできるようにします。これは、コンテナ内でGPUを利用するために必要です。

>```--shm-size <size>```: コンテナの共有メモリ（SHM）のサイズをsizeに設定します。例えば```--shm-size 20g```ならば20GBが共有メモリとなります。共有メモリは、プロセス間通信（IPC）に使われるメモリのことで、データサイエンスや機械学習のタスクで大きなデータセットを扱う場合に重要になることがよくあります。


>```--name <コンテナ名>```: このオプションでコンテナに名前を割り当てます。<コンテナ名>には、コンテナを識別するための任意の名前を入れます。

>```--mount type=bind,source=<place-of-model>,target=/workspace```: これは、ホストマシンの特定のディレクトリ(今回はplace-of-modelに指定）をコンテナ内の/workspaceディレクトリにマウント（バインド）します。この方法で、ホストマシン上のファイルやディレクトリをコンテナ内で直接使うことができます。

以上で、環境構築は完成しました。後はこのDockerコンテナ内で通常通りpythonファイルなどを実行することで機械学習モデルを動かすことができます。
postgresqlの初期設定の仕方

postgresqlの初期設定のメモ
Ubuntu22.04

### 以下のコマンドを実行
```
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt update
sudo apt -y install postgresql-15
```

### postgres起動
```
sudo systemctl enable postgresql
sudo systemctl start postgresql
```

### 以下でpostgresユーザでpostgresに入る。
```
psql -U 自分のuser名 -d postgres
```

### /etc/postgresql/15/main/pg_hba.confを編集
```
# Database administrative login by Unix domain socket
local   all             all                                     password

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     peer
# IPv4 local connections:
host    all             all             127.0.0.1/32            scram-sha-256
# IPv6 local connections:
host    all             all             ::1/128                 scram-sha-256
```

### sudo -u postgres psqlをすると怒られる。
```
sudo -u postgres psql
postgres=# ALTER USER OSのユーザ PASSWORD 'パスワード';
ERROR:  ロール"OSのユーザ"は存在しません

postgres=# SELECT usename FROM pg_user;
 usename  
----------
 postgres
(1 行)

```
### 以下でOSのユーザ名のpostgresユーザ作成
```
postgres=# CREATE USER OSのユーザ WITH PASSWORD 'パスワード';
CREATE ROLE

postgres=# ALTER USER OSのユーザ WITH SUPERUSER;
ALTER ROLE
```

### postgresql再起動しOSユーザでpostgresqlに入る
```
sudo systemctl restart postgresql
psql -U OSのユーザ -d postgres
```
外部APIを利用するChrome拡張を作成する


# はじめに

自作した Chrome 拡張で外部 API をリクエストする処理を実装する場合の実装方法です。簡単にできると思いきや、結構めんどくさかったので備忘録となります。

# 実装

今回は動作確認用として、画面を開いたら Qiita の記事一覧取得 API をリクエストし、コンソールログに表示する拡張機能を作成する。

対象のAPI を curl で実行する場合は以下の通り

```bash:
curl -X 'GET' https://qiita.com/api/v2/items
```

## 全体構成

最終的なフォルダ構成はこちら

```sh
src ---  manifest.json
    | - content.js
    | - libs/
         | - jquery.min.js
```

## jquery のダウンロード

はじめに、外部 API 通信を行う際に Ajax を利用するたため、jquery のソースコードをダウンロードしておく必要がある

jquery は以下の URL からダウンロードする

https://releases.jquery.com/

拡張機能では CDN を利用することを推奨されていないので、ローカルにダウンロードしたものを読み込む。

ダウンロードしたら`libs/jquery.min.js`として保存する。

## `manifest.js`の作成

拡張機能の設定値となる `manifest.js`を作成する。

```json:manifest.js
{
  "manifest_version": 3,
  "name": "sample",
  "version": "1.0",
  "description": "sample for qiita",
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["libs/jquery.min.js", "content.js"]
    }
  ],
  "permissions": ["https://qiita.com/*"]
}

```

注意点は 2 つ

- `permissions` に接続先の API のドメイン名を指定する。今回の場合は`https://qiita.com/*`を指定している
- jquery を`content.js`より先に読み込む

## `content.js`の作成

画面をリロードするたびに Qiita の記事一覧を取得する処理を記載する。

```javascript:content.js
const settings = {
  async: true,
  crossDomain: true,
  headers: {
    accept: "application/json",
    "Content-type": "application/json",
  },
};

addEventListener("load", (event) => {
  $.ajax({
    url: "https://qiita.com/api/v2/items",
    method: "GET",
    ...settings,
  }).done(function (response) {
    console.log(response);
  });
});

```

## 動作確認

以下の記事を参考に`src`フォルダを拡張機能として登録する。

https://note.com/cute_echium873/n/n997dcf40b3a1

その後画面をリロードする。

![extension.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163680/57676c97-c367-ca47-d5bb-f04db6a7dc4d.png)

Qiita の記事一覧が取得でき、コンソールに表示されることまで確認できた。

# 終わりに

利用したソースコードは以下の Github で公開しています

https://github.com/sey323/chrome_extension_api_call_sample
postされた日付をexplode関数で区切りcheckdate関数でバリデーション

postされた日付などをcheckdate関数を使用してバリデーションを行いたい時にexplode関数を使います。
## 使ってみる
checkdate関数は引数1に月、引数2に日、引数3に年を渡さなければいけません。なので、explode関数を使用してcheckdate関数に合う形にします。
```php
$date = $_POST['date']; //'2024-02-12'が入っている
$array_date = explode('-', $date);
```
引数1に何事に区切るか(上記では-)、引数2に区切りたい文字列が入った変数を指定します。
var_dumpで出力して確認していきます。
```
array(3) { [0]=> string(4) "2024" [1]=> string(1) "2" [2]=> string(2) "12" }
```

## checkdate関数で正しい日付かチェック
checkdate関数で正しい日付化チェックしていきます。先ほど書いたようにcheckdate関数は引数1に月、引数2に日、引数3に年を渡さなければいけません。
```php
$validate = checkdate($array_date[1], $array_date[2], $array_date[0]);
var_dump($validate);
```
```
bool(true)
```
正しい日付なのでtrueが返ってきました。
Supabase Edge FunctionをDartで書くリンクとメモ(2024年2月版)

:::note alert
これは2024年2月現在の情報です。後には状況が大いに変わってる可能性が極めて高いです。
:::
# 概要

https://dartling.dev/full-stack-dart-with-flutter-supabase-and-dart-edge?utm_source=pocket_saves

DartでSupabaseのEdge Functionを書くための手順はこのリンクが一番詳しそうなんですが、このサイトも公式も他のサイトも情報が古いのかそのままでは上手く行かないので試行錯誤したリンクとメモ。


# 注意点

### ・Function作る度にsupabase initする必要はない

supabase initはローカルにsupabase環境を作るための初期化コマンドなので、手順通りにinitすると関数１つ作る度にsupabase環境を新規に作ることになってしまう。

### ・事実上edge.yamlを作る必要がある

https://github.com/invertase/dart_edge/issues/52?utm_source=pocket_saves

公式含めwebで見つかるドキュメントでは全く記載がないのですが、edge.yaml **(.ymlではダメです)** を記述することで関数名の設定やコンパイル(変換)したファイルの出力先を指定できるようです。何も設定しない場合はdart_edgeという関数名になり、出力先もカレントディレクトリのfunctionsフォルダに作られますが、2024年2月現在のsupabase edge functionsが想定するフォルダ構成とは異なる場所になるので設定なしでは手動で移動させない限り動きません。

edge.yamlをedge newコマンドで作られたedge functionフォルダのルートに配置すると設定を読み込んでくれるようです

```edge.yaml
supabase:
  #関数名とその関数のエントリーポイント(main関数)があるdartファイルを指定
  functions:
    test: "lib/main.dart"

  #supabase initしたフォルダ内のsupabaseフォルダを指定(絶対パス、相対パスどっちでもOK)
  project_path: "../../supabase"
```

その他のオプションについてのドキュメント

https://github.com/invertase/dart_edge/blob/main/docs/platform/supabase/config.mdx


### ・[期間限定] 旧バージョンのDartでコンパイルする必要がある

https://github.com/invertase/dart_edge/issues/50?utm_source=pocket_saves

  2024年2月現在のDart最新版(3.2.6)ではdart-edgeによるedge関数のコンパイル(JavaScriptへの変換)は上手くいきません。js相互運用の仕様が変わっているのですがdart-edgeがそれに追従出来ていないのが原因。
  今のところ追従するまではfvmを利用して古いバージョンのDartでコンパイルするしかないようです。

```bash
#fvmインストール
dart pub global activate fvm

#fvmによるバージョンの切り替え(fvm useしたフォルダ内のみ有効)
fvm use 3.7.1

#fvm経由でコンパイル
fvm dart run edge build supabase_functions
```
【AWS】AWS Lambdaで既存のLambdaを複製して作成する方法

## 背景
- [AWS Lambda](https://aws.amazon.com/jp/lambda/)はサーバーレスにプログラムを実行できるサービスです。
- `Lmabda`を使用していると既存の`Lambda`を複製して新しい`Lambda`を作成したいことがあると思います。
- この記事では
    1. AWSコンソールで複製する方法
    2. `AWS CLI`を一部活用する方法
    の2つをご紹介します。
- ※本方法は`python`かつ`zip`デブロイによって作成された`Lambda`でのみ検証した方法です。`python`以外の言語や`zip`デブロイ以外で作成された`Lambda`で使用できる方法かは不明です。

## 目標
- 既存の`Lmabda`を複製した新しい`Lmabda`を作成できるようになる。

## 1.AWSコンソールで複製する方法
### 既存の`Lambda`の`code`の`copy`方法
- 複製したい`Lambda`のコンソール画面を開きます。
- 「関数の概要」セクションの真横にある「ダウンロード」をクリックします。
- 「ファンクションコード.zipをダウンロード」をクリックします。
- ローカルPCの適当な場所に上記でダウンロードした`zip`ファイルを保存します。

### 新しい`Lambda`の作成方法
- `Lambda`のコンソール画面に移動します。
- 「関数の作成」をクリックします。
- 「一から作成」をクリックします。
- 新しく作成する関数名を「関数名」に入力します。
- 「ランタイム」・「アーキテクチャ」・「実行ロール」は複製元の`Lambda`に合わせて作成します。
- 「関数の作成」をクリックします。
- 作成された関数の画面の「コードソース」セクションで「アップロード元」をクリックします。
- 「.zipファイル」を選択し、先ほど複製元の`Lambda`からダウンロードした`zip`ファイルを`upload`します。
- 「保存」をクリックします。
- 複製元の`Lambda code`が反映されます。
- その他、実行時間やメモリの変更が必要であれば、設定から設定を変更します。

## 2. `AWS CLI`を一部活用する方法
### 既存の`Lambda`の`code`の`copy`方法（ここまでは同様です。）
- 複製したい`Lambda`のコンソール画面を開きます。
- 「関数の概要」セクションの真横にある「ダウンロード」をクリックします。
- 「ファンクションコード.zipをダウンロード」をクリックします。
- ローカルPCの適当な場所に上記でダウンロードした`zip`ファイルを保存します。

### `AWS CLI`で新しい`Lambda`を作成する方法
- 元の関数の設定を以下のコマンドで取得します。
```:bash
$aws lambda get-function --function-name {複製元の関数名} > original-function.json --region ap-northeast-1
```
- `AWS CLI`で新しい関数を作成します。
- 設定値は先ほど出力した`json`を参考にすると良いです。
```:bash
aws lambda create-function --function-name {新しく作成する関数名} \
--runtime "{ランタイム ex）python3.9等}" \
--role "{ロールのARN}" \
--handler "{ハンドラ　ex)lambda_function.lambda_handler等}" \
--zip-file fileb://{ZIPファイルのローカルのファイルパス} \
--timeout {タイムアウト時間} \
--memory-size {メモリサイズ} \
--region {リージョン名}
```
- ターミナルの出力が途中で止まるので最後に`q`を押して完成です。


## 参考資料
https://aws.amazon.com/jp/lambda/

## 個人ブログ

https://takuma-tech.com
失語症からSE復活の為にリハビリをする

# 何故、失語症になったか
高血圧→くも膜下出血→血管破れた事の弊害

詳しくはこちらに書いた

https://qiita.com/t-yamanashi/items/1d563a83d2713dccf669

# 失語症で困ること
・話せない
　　└言葉がでない 
・キーボードをまともに打てない
　　└言葉がでないのも原因
・短期記憶が極端に低下する
・処理速度の低下
・生活上レベルで使うマルチタクスができなくる
　└例　話しながら、歩くとか

つまり、どんな仕事でも支障がある
特にSEは、他人との会話が多いので、話せないと絶望的である
リハビリの最初はうまくできずイライラする
この記事を書いてる時は、回復してきた

# 話せるように練習

・口の運動
　└口の筋低下ではっきり話せない
　　└対策：「う」と「い」の口を全力を行う

・発音練習
　└「あえいうえおあお」のような言葉で練習
　　（小学生の時にやりましたよね）
  　└「ああ、あい、あう、あえ、あお：ああ、いあ、うあ、えあ、おあ」のパターンで練習
   　　（どこかで、発音しにくいパターンがあるので、そのパターンを主に練習）
・早口言葉で練習
　例えば
 　「生麦生米生卵」
  　「赤巻紙青巻紙黄巻紙」
   　はじめは、ゆっくり正確に、なれたら少しスピードをあげる
    
・短い文章を読む
・長い文章を読む
・自分の声を録音する
　└定期的に実施することで、上達を認識してモチベアップ
・スマホの音声認識で正しく認識するか試す
　└はじめは全く認識しなかったが、最近はまともに認識する
 
これらを「言語聴覚士」と一緒に行う
発音しにくい音を分析してくれる

----
（余談）
本気で復職したければ失語症になったら「リハビリ病院」に入院した方がよい
40代だと、失語症は介護保険対象外
なので、全力で治すしか選択しかないが
高額医療のMAX月実費10万がかかる、最短でも6ヶ月
なので、60万を払える貯金がないと死亡フラグ（最低貯金はしておいてよかった）
運良く生きても金で困る人は居ると思う
（ちなみに、保険前の支払い額は、脳の手術含めて1000万ぐらい）

# キーボードの練習
 ・ホームポジションの確認しキーボードを打つ（自主練）
 　└「a-z」を打つ
  　└「あ-ん」を打つ
  ・耳から聞いた単語を打つ
  ・目で見た絵の単語を打つ
  ・短い文章を打つ
  ・質問された事を声に出しながら打つ
  ・タイピングソフトを使う（自主練）
　　└ubuntuで動く良いタイピングソフトが無いからブラウザで動く「寿司打」で練習
  ![Screenshot from 2024-02-12 14-01-34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295814/d05f1c2c-68f3-6cbf-b1d1-15fe6a2b90aa.png)

 
# 短期記憶の練習
・数字を聞いて、並び変えて発音する
　　└例　「3465」→「5643]
   この桁数を増やす

・記憶練習アプリを使う（自主練）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295814/cbcc29fa-907d-ace4-66d0-1040043d63eb.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295814/c1ba7244-8185-4513-132e-dbc0fefff256.png)

（余談）
メインリハビリは「言語聴覚」で行うが
「理学療法」「作業療法」の時間でも実施してる
特に「理学療法」ではもう困らないレベルまで回復したため
歩きながら、計算したり、暗記問題をしてる

# 処理速度の練習
・計算問題プリント
　例　「3 + 4 + 1 + 3 + 4 + 1 + 3 」
 　　本来は前から計算する練習だが、(3 * 3 )+ ((4 + 1) *  2)と実施する面倒くさがりや癖は元のどうり
   　リハビリの先生にちょっと人と違う癖があると突っ込まれた
    
・問題を口頭で出されて
　└問題を暗記
 　└２桁の数字の足し算と引き算
  　└繰り上げ、繰り下げはあり
   　└短期記憶の練習にもなる
 
・計算練習アプリを使う（自主練）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295814/5ecbe3d4-0f01-bb25-13c0-9eb127d4d3d0.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295814/0d339a05-dad8-8d0c-136d-b9c1810bb781.png)

 
# 生活上レベルで使うマルチタクス
　・歩きなが
 　　└数字の並び替え
   　　└世間話
     　　└計算して答える


---
こんな感じにリハビリしてます
はじめは小学生でもできることが突然できなくなり、絶望的になるが
リハビリして何とかなってます
動的型付け言語と静的型付け言語の違い

動的型付け言語と静的型付け言語は、プログラミング言語が変数の型をどのように扱うかによって区別され、それぞれメリットとデメリットがあります。

## 静的型付け言語
Java、C、C++、Rust、Goなど。

**型のチェック時期:** コンパイル時。つまり、プログラムを実行する前に型の整合性が確認される。

**メリット:**
・型のミスが早期に発見されるため、実行時の型関連のエラーが少なくなる
・コンパイラが型情報を使用して最適化を行うことができるため、実行速度が向上する可能性がある
・大規模なプロジェクトやチームでの開発において、コードの明確性や保守性を高めることができる

**デメリット:**
・コードが冗長になりがちで、型を明示的に宣言する必要がある
・小規模なスクリプトやプロトタイピングには向いていない場合がある

## 動的型付け言語
Python、Ruby、JavaScript、PHPなど。

**型のチェック時期:** 実行時。つまり、プログラムが実行されるときに型の整合性が確認される。

**メリット:**
・コードが簡潔で読みやすく、型を明示的に宣言する必要がない。
・プロトタイピングや小規模プロジェクトに適しており、開発速度が速い。

**デメリット:**
・実行時に型エラーが発生する可能性があり、デバッグが難しくなることがある。
・コンパイラが最適化を行いにくく、実行速度が静的型付け言語に比べて遅くなる可能性がある。

## まとめ
静的型付け言語はコンパイル時に型チェックを行い、型安全性とパフォーマンスを重視します。
一方で、動的型付け言語は実行時に型チェックを行い、開発の柔軟性とスピード重視する場合にオススメということになります。

## 参考

https://qiita.com/wann/items/16920b070dc0483f152a
自己反省　boothネイルテクスチャ

とりま自己反省用です・・

boothネイルテクスチャのRTの伸びがおもったより低い
・新規フォローがいない（現時点で＋１）
・でも既存のフレンドさんはRTしてくれててそれが９９％（ありがてえ・・・

考えられる理由

・応募条件をRT＋いいねに自分で書いてた
フォロー＋RTにしろよ（呆れ）

・boothのショップのほうでプレゼント告知をだしてなかった

・そもそものバレンタインまでギリギリすぎる
1/12にRTやって締め切りが1/14 ０時まで
・０時までって書いてなかった・・

・スキ！２０個につき１個＋で当選枠を増やす→なくていい
やるならスキ！１０個


・そもそもネイルの需要が低い？
（いいねや検索ワードからある程度予想はしてたけど…）

・３０種類はつくりすぎィ！
商品画像が表示しきれない‥！

５種類×６個でやるべきだったかも
もしくは一旦無料で３種類だけとか

・マヌカ１体だけ
アバター１体だけ＆マヌカが発売から数か月、けっこうたっているので、すぐ購買に結び付かなかった

・できるならすでにboothでいくつか販売してる人に相談できればよかったかも
やはり求められてるのはテクスチャではなく服

・そもそもboothでは自分のショップが知名度がない（フォロー５５）
まあこれはtwitterフォロワーおおいけどboothはフォローすくないとかみんなあるあるだから…

・商品画像が微妙
これでも精いっぱいつくったんだけどやはり微妙
外注するべきか・・・でも外注するならお金かかるからある程度売れる！ってやつじゃないと外注できないっす…（猫ミーム）

結論
次はもっと考えて、売ろう！（提案）

ちなみにめっちゃ悩んでるネイルテクスチャはこちらっ
スキとかフォローとかよろしくお願いします（土下座）
https://machidasanshop.booth.pm/items/5492471
YOUTUBE自己反省（一か月目）

・YOUTUBE投稿を本格的（ほぼ毎日）はじめて約１っかげつめ

・2/10に動画を３つも投稿したのに登録者数が伸びなかった
（MMD２つ、１つ声入り）

原因として考えられる
・YOUTUBEが全体的に２０２４年はそもそもそういう環境
（再生数が多いのに登録者数が少ない）

・自分オリジナルのコンテンツが少ない
自分の声で、自分にしか話せない動画を求められている

・誰でもできる動画は求められていない？→ここは要検証

・そもそも今まで投下した動画の数が少ない
のでデータがすくない・・

・おそらくだけどVRChatterじゃない人の視聴者がかなり多い（見てる人はほぼ新規）

・VRChatterの人口は日本人は一万人と言われている
日本人の総人口は１億人
１億人にアピールしたほうがいい説


・男女比
![男女比.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3638409/f564b521-3c2d-e58f-5e5c-215c8b701e77.png)

・年齢
![年齢.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3638409/2f07af7a-67d0-f984-babd-470fef5938f5.png)


![ダッシュボード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3638409/32816428-c135-0b36-06ef-7050297c9b2a.png)

私がめっちゃ運営に悩んでるＹＯＵＴＵＢＥチャンネルはこちら

https://www.youtube.com/channel/UC6vg2h_A6Uj94MjY3CPXMLg

↑よかったらみてほしい！
もし余裕あれば高評価とかチャンネル登録よろしくお願いします…！
Javaのスコープ

# スコープとは
｛　｝で囲まれた部分をブロックと呼びます。
Javaでは、他言語と同様にブロック内で宣言した変数は、ブロックの外側では利用できません。

この時の変数が利用可能な範囲をスコープと呼びます。
スコープの外で変数を呼び出そうとすると該当の変数を見つけられずエラーが発生します。
Hexabase TypeScript SDKの使い方（データベース参照の操作）

[Hexabase（ヘキサベース）](https://www.hexabase.com/)は企業においても安心して利用できるBaaS（Backend as a Service）を提供しています。多くのBaaSがそうであるように、主にフロントエンド開発者に利用してもらいたいと考えています。そこで現在、TypeScript SDKの開発が進められています。

この記事ではHexabase TypeScript SDKのインストールと、データベース参照フィールドの操作について解説します。

## インストール

インストールはnpmやyarnを使って行います。

```bash
# npmの場合
npm install @hexabase/hexabase-js

# yarnの場合
yarn add @hexabase/hexabase-js
```

## インポート

インポートすると、 `HexabaseClient` というオブジェクトが取得できます。

```jsx
import { HexabaseClient } from "@hexabase/hexabase-js";
```

## 初期化

HexabaseClientを初期化します。

```jsx
const client = new HexabaseClient();
```

## 認証

Hexabaseでは業務利用を想定しているため、利用する際に認証情報が必須になります。最初はメールアドレスとパスワードで認証し、その後はトークンを使ってGraphQLにアクセスします。 `client` を使って処理します。

初回の認証は次のようになります。emailとパスワード、またはトークンが必須です。

```jsx
await client.login({email, password, token});
```

後はこの `client` に対して処理を行います。

## データベース参照とは

Hexabaseはクラウドデータベースを提供しており、データベース同士のリレーションも可能です。データベース参照はフィールド型の一つで、フィールドに対して別なテーブルのデータを紐付ける型になります。

1行のデータに対して1つのデータを紐付ける形なので、マスターデータの参照などに利用できます（顧客テーブルに対する都道府県マスターなど）。

## データを作る

データはワークスペース → プロジェクト → データストア → データアイテムの順番にデータを取得します。

```js
await client.setWorkspace(workspaceId);

// 対象プロジェクト
const project = await client.currentWorkspace.project(projectId);

// 対象データストア
const datastore = await project.datastore(datastoreId);

// 新規レコード
const item = await datastore.item();
```

## 保存について

保存時には、マスターデータ `masterItem` を `set` メソッドで特定のフィールド（今回は `test_dslookup` ）に対して紐付けます。

最後に `save` を実行すれば、関連データとして保存されます。

```javascript
// マスターデータを取得
const master = await project
    .datastore(DATASTORE_TEST_MASTER1);
const [masterItem] = await master.items();

// 値をセット
item.set('test_text_unique', name());
item.set('test_number', 100);
item.set('test_dslookup', masterItem);
// 保存
await item.save();
```

## 更新について

更新は先ほどと同じように、新しい紐付けるデータを指定して保存します。保存は `save` メソッドで変わりません。

```javascript
const [masterItem, masterItem2] = await master.items();
// 別なデータをセット
item.set('test_dslookup', masterItem2);
// 更新
await item.save();
```

## 紐付けの解除

紐付けを解除する場合には、 `null` を指定して保存してください。

```javascript
// 紐付け解除
item.set('test_dslookup', null);
// 保存
await item.save();
```

この場合、返ってくるデータ（ `get` メソッドで取得できます）も `null` になります。

```javascript
const obj = item.get('test_dslookup');
// obj === null
```

## まとめ

SDKを使うことで、データベース参照は簡単に操作できます。リレーションを使えば、データベースを正規化し、管理が簡単になるでしょう。

Hexabaseには他にもバイナリファイルのアップロードや認証などの機能があります。それらの機能も利用してください。

[Hexabase \| 新規事業向け開発・競争領域でのDX実現をサポート](https://www.hexabase.com/)
Avaloniaでドラッグ&ドロップでファイルを受け取る【Drag and Drop】

Avaloniaという、C#でマルチプラットフォームなGUIが作れるフレームワークを試していました
AvaloniaはWPFの精神的後継者を自称するなど全体的にWPFっぽく（WPF触ったことないけど）、簡単なことであれば「WPF + やりたいこと」で検索してそのとおりにやればできることが多いです

が、ドラッグアンドドロップはそうでもなかった上情報がかなり少なさそうなので書きます

# 完成品
起動すると文字入りのウィンドウが開き、ウィンドウにファイルやフォルダを投げると絶対パスを、テキストを選択して投げるとテキストを文字で表示します
MVVMテンプレートから作成しました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634181/51230670-bcc5-c0ab-6fa8-75b5bb8341a3.png)
画像はFドライブのProgram Filesフォルダを投げた場合です


GitHubに上げてます

https://github.com/SousiOmine/AvaloniaDragDrop_Demo

:::note warn
Windows11でしか動作確認していません
:::

# 参考
Mamma Mia Dev様の解説動画

https://www.youtube.com/watch?v=G1GwVlFQoS0

Redditで同じようなことをしていた質問

https://www.reddit.com/r/AvaloniaUI/comments/11lvy2r/files_dragndrop/

DragEventArgsの公式リファレンス
https://reference.avaloniaui.net/api/Avalonia.Input/DragEventArgs/
IDataObjectの公式リファレンス
https://reference.avaloniaui.net/api/Avalonia.Input/IDataObject/

# やる
まず、Avaloniaでドラッグアンドドロップを実装する方法は公式ドキュメントには全然情報がなく、上記参考をもとに適当に書いたものなので間違いあったらごめんなさい

```xml:MainWindow.axaml
<Window 省略～
	    DragDrop.AllowDrop="True">
```

ドラッグアンドドロップを受け入れたいコントロールのDragDrop.AllowDropプロパティをTrueにします

```c#:MainWindow.axaml.cs
public partial class MainWindow : Window
{
	public MainWindow()
	{
		InitializeComponent();
		AddHandler(DragDrop.DropEvent, DropFile); //登録を忘れない 多分xaml側では不可能
	}

	private void DropFile(object? sender, DragEventArgs e)
	{
		var dropData = e.Data;
		//ここに受け取った情報を使う処理を書く
	}
}
```

コードビハインドでファイルがドロップされたときの処理を書いていきますが、この処理をコンストラクタにて`AddHandler(DragDrop.DropEvent, DropFile);`で登録しなければいけないようです

# DragEventArgsの使い勝手がWPFのそれと若干違う？

あとはDragEventArgsから受け取ったやつ使って処理書けばOK！なんですが
このDragEventArgs型はWPFのそれとは別物で、受け取れるDataも別物になります
私はここで苦戦しました

このDataは`Avalonia.Input.IDataObject`型で、例えば受け取ったもののパスが欲しいなとなったときに、前述の公式リファレンスを見ると`GetFileNames()`といういかにもなメソッドが使えるようなのですが**実際には使えません**（なんでだ？）

手探りでやった結果このような形に行きつきました
CenterTextという名前のstring型プロパティにパスをセットするコードの一部です
```c#
if(data.GetText() != null)
{
    //もし受け取ったのがテキストであればそれにセット
	CenterText = data.GetText();
	return;
}
else
{
	if(data.GetFiles != null)
	{
        //複数ファイルをドロップしても全部のパスを読むようにする
		List<string> path = new();
		foreach (var item in data.GetFiles())
		{
			path.Add(item.Path.ToString());
		}
		if(path.Count > 0)
		{
			CenterText = string.Join("\n", path);
		}
	}
		
}
```

`GetFiles`というメソッドを使いファイルのコレクションを取得しています
ちなみにGetFilesメソッドですがこっちは使えるのに**なぜかリファレンスに乗っていません**

# まとめ
WPFは枯れた技術だからか情報が多くてすごい
リファレンスうんぬんは私が間違っている可能性もあるので、教えていただければ修正します

参考になれば幸いです

https://github.com/SousiOmine/AvaloniaDragDrop_Demo
[Android] Hiltを使ったテスト中のデータベース差し替え方法

## はじめに
ComposeのUIテストを行う際に、DBをin memoryに差し替えたいなと思って調べた結果を簡潔にまとめました！

## Custom test runnerの設定
HiltでのDB差し替えをテストで使うには専用のHiltTestApplicationを使う必要がある。なのでCustomTestRunnerを設定、gradleでそれを使うように指定する。

```kotlin
class CustomTestRunner : AndroidJUnitRunner() {
    override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
        // HiltTestApplicationをテスト用のApplicationクラスとして使用
        return super.newApplication(cl, HiltTestApplication::class.java.name, context)
    }
}
```
```kotlin
// build.gradle
defaultConfig {
    testInstrumentationRunner "com.example.myapp.CustomTestRunner"
}
```

## 対象のテストでModuleを差し替える

以下のDB用Moduleがあるとする。

```kotlin
@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {
    @Provides
    @Singleton
    fun provideDatabase(application: Application): KaimonoDatabase {
        return Room.databaseBuilder(application, MyDatabase::class.java, "kaimono_database")
            .fallbackToDestructiveMigration()
            .build()
    }
}
```

これを差し替えたいUIテストなどで以下のように設定するとHiltによるDB入れ替えを行える。やっていることとしては、`@UninstallModule`sで既存Moduleをuninstall, `@InstallIn`で再度テスト用moduleをinstall。

```kotlin
@HiltAndroidTest
@UninstallModules(DatabaseModule::class)
class RegisterItemTest {
    @get:Rule
    val composeTestRule = createAndroidComposeRule<MainActivity>()
    @get:Rule
    var hiltRule = HiltAndroidRule(this)
    // use createAndroidComposeRule<YourActivity>() if you need access to an activity

    // テスト用のDBモジュールを作成
    @Module
    @InstallIn(SingletonComponent::class)
    object TestDatabaseModule {
        @Provides
        @Singleton
        fun provideInMemoryDb(@ApplicationContext context: Context): KaimonoDatabase {
            return Room.inMemoryDatabaseBuilder(context, MyDatabase::class.java)
                .allowMainThreadQueries()
                .build()
        }
    }

    @Test
    fun myTest() {
			...
    }
}
```

## おわり
以上です！
何かコメントやもっとこうやったほうがいいなどあれば是非お願いします！
【Rails7.0】db:migrateを実行するもDBにカラムが追加されない

## 事象
dbにuser_idを追加したい。
migrationファイルを追加しrails cだから確認するも列が追加されない。

```terminal:ターミナル
$ rails g migration add_user_id_to_posts

＃追加したマイグレーションファイルに「add_column :posts, :user_id, :integer」を追加後に以下実行

$ rails db:migrate
$ rails c
Loading development environment (Rails 7.0.8)
irb(main):001> Post.new
=> 
#<Post:0x000000010858c948
 id: nil,
 content: nil,
 created_at: nil,
 updated_at: nil>
```

## 原因
-原因がわからなかったので解決策でマイグレーションファイルを再度作成し読み込んでみた。

## 解決策
1. 強制的にマイグレーションファイルを再度作成
```terminal:ターミナル
$ rails g migration add_user_id_to_posts --force
      invoke  active_record
      remove    db/migrate/20240212054457_add_user_id_to_posts.rb
      create    db/migrate/20240212055618_add_user_id_to_posts.rb

＃再度作成したマイグレーションファイルに「add_column :posts, :user_id, :integer」を追加後に以下実行

$ rails db:migrate
== 20240212055618 AddUserIdToPosts: migrating =================================
-- add_column(:posts, :user_id, :integer)
   -> 0.0026s
== 20240212055618 AddUserIdToPosts: migrated (0.0026s) ========================

$ rails c
Loading development environment (Rails 7.0.8)
irb(main):001> Post.new
=> 
#<Post:0x000000011247c670
 id: nil,
 content: nil,
 created_at: nil,
 updated_at: nil,
 user_id: nil>
```

## 最後に
今回はchatGPTを使って調べました。
原因についてはわからなかったので再度同じ事象が発生する可能性があります。
とはいえ、再度マイグレーションファイルを作成するコマンドや実行方法を覚えておいて損はないと思うので、
同じ事象で困っている人がいれば再度マイグレーションファイルを作成してみてください。
最後まで読んでいただきありがとうございました。
Pythonでウェブページのヘルスチェックをしてみる

## はじめに
ウェブサーバーの運用保守の一環で設定変更などを行う中で、作業のレベルにもよりますがウェブページが閲覧不可になるリスクは常に付き物です。
今回、Pythonを用いてウェブページのヘルスチェックを行い、閲覧不可のステータスに早く気が付ける仕組みを作ってみることにしました。

### プログラム概要・目標
一定間隔で、GETによるリクエストを発生させ、応答コードをコンソール出力することで現状のヘルス状態を確認するというプログラムを実装することとしました。
最終的なゴールは**一定間隔で複数のページを対象としたヘルスチェックを行う**ということにしています。

### GETリクエストを行うPythonコード
まず、**Request**モジュールでウェブページへのアクセスを行い、ステータスコードをコンソールに出力するコードを作成しました。

```python:title.single-get-python.py
import requests #requestモジュールをインポート

response = requests.get('{確認したいURL}') #URLを指定してGETリクエストを送信
status_code = response.status_code #ステータスコードを取得

print(f"Response code: {status_code}") #ステータスコードを表示
```

このとき、{確認したいURL}に、ヘルスチェックを行うURLを直接入力してください。
例：response = requests.get('https://qiita.com/')

こちらのコードで、[Response code: 200]と応答が帰ってくれば、PythonによるGETリクエストは成功しています。

### 複数のURLにまとめてリクエストする
先ほどの項目で、**1つ**のURLを対象としたリクエストを実行することに成功しました。
しかし、今回のゴールとして**複数**のURLにリクエストをかけることを設定したので、複数のURLにGETリクエストを行えるようにします。

```python:title.get-python.py
import requests
import time
import os

#検索URLのリストをファイルから読み込む
def get_url_list():

	#読み込み元のファイルを指定 /get-url-list/url_list.txt
	current_directory = os.path.dirname(os.path.abspath(__file__)) # カレントディレクトリを取得
	read_url_file = r"get-url-list\url-list.txt" # URLリストのファイル名を指定

	combined_read_file = os.path.join(current_directory, read_url_file) # CSVファイルのパスを作成

	with open(combined_read_file, 'r') as file:
		url_list = [line.strip() for line in file] # URLリストを読み込む、line.strip()で改行文字を削除
		file.close() # ファイルを閉じる

	return url_list

#URLリストを受け取り、URLにアクセスする
def get_page(url_list):
	url_list = get_url_list() # 検索URLをフォルダーから受け取る

	for url in url_list:
		response = requests.get(url) # URLにアクセスする
		status_code = response.status_code # HTTPステータスコードを取得する

		print(f"{url} : Response code: {status_code}") # HTTPステータスコードを出力する

		delay_time = 5 # 5秒待機
		time.sleep(delay_time)

#実行
url_list = get_url_list() # 検索URLをフォルダーから受け取る
get_page(url_list) # URLリストを受け取り、URLにアクセスする
```

以下のディレクトリ構成でページを作成しています。
関連するファイルを相対パスで読み込むため、**osモジュール**を用いています。
`Python_code/get-python.py`を実行ファイルとした場合、`Python_code/get-url-list/url-list.txt`に記述したURLを`[get_url_list]`にて読み込み、読み込んだリストをもとに`[get_page]`でGETリクエストを行うようにしています。
![ディレクトリ構成.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3661834/7530119e-c68e-c2ac-4a64-5dd5f41d200e.jpeg)

:::note alert
このプログラムにある`time.sleep(delay_time)`は絶対に削除しないでください。
遅延を入れないことにより、大量にGETアクセスを発生させてしまうことでDoS攻撃となる可能性があります。
電子計算機損壊等業務妨害罪（刑法第234条の2）により、5年以下の懲役または100万円以下の罰金を科せられる可能性があります。
:::

なお、URLリストは以下のような記述にしています。
```txt.Python_code/get-url-list/url-list.txt
https://example.co.jp
https://news.example.co.jp/
```

### 連続したアクセス実施
ループさせて監視し続けたいので、以下のように、**While文**を使って無限ループ化させることにしました。
やめるときは、Ctrl-Cでプログラム実行を止めています。

```
#実行
url_list = get_url_list() # 検索URLをフォルダーから受け取る

while True:
	get_page(url_list) # URLリストを受け取り、URLにアクセスする
```
他にキー入力を監視する`keyborad`モジュールなどもありそうですが、管理者権限が～などの制約があるようで今回時点での実装はやめにしました。

### 備考
Requestモジュールはデフォルトだとキャッシュ維持をする設定ではないようで、キャッシュについては気にする必要はなさそうです。

## まとめ
今回の実装で、各URLに対するGETリクエストによるヘルスチェックについて実装することができました。
コンソールログを見るというベースなので多少使いずらい点があるので、GUI化についても今後実装していきたいと考えています。

便利ツールやAWSに関する記事を上げていこうと思っていますので、よろしくお願いいたします！
Flutterで一時的にAndroidの戻る操作を無効にする方法

MainActivityのonBackPressedをオーバーライドしたら対応できました。

```
class MainActivity: FlutterFragmentActivity() {
    private var backDisabled = false

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        val mychannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.xxx/channel")
        mychannel.setMethodCallHandler {
                call, result ->
            if (call.method == "setBackDisabled") {
                backDisabled = call.arguments as? Boolean ?: false
            }
        }
    }
    override fun onBackPressed() {
        if (!backDisabled) {
            super.onBackPressed()
        }
    }
}
```


後はFlutter側でよんであげるだけ

```
  static const MethodChannel _channel =
      MethodChannel('com.xxx/channel');

  static Future<void> setBackDisabled(bool disabled) async {
    await _channel.invokeMethod('setBackDisabled', disabled);
  }
```
Apple Silicon + Android Studio2023.1 + Android OS9環境でのアプリインストール問題の解決

### 問題
MacのApple Silicon端末において、Android Studio 2023.1（SDK Platform Tools 34.0.5）の最新版を使用し、Android OS 9を搭載した端末にアプリをインストールする際、Studioから「Run」する方法やADBコマンドを使用した方法でインストールができないという問題が発生しました。ビルド自体は成功しているようですが、その後のインストールに問題が生じています。なお、Android 12や13ではこの問題は発生していません。


### 問題が発生した環境
端末: Mac（M2 Pro）Apple Silicon
Mac OS: Sonoma 14.2.1
エディター: Android Studio 2023.1（SDK Platform Tools 34.0.5）


### 参考になった情報元
https://issuetracker.google.com/issues/270205252


### 解決方法
ADBコマンドが使える前提となりますが、Google Issue Trackerに報告されているように、Macのターミナルで以下のコマンドを順番に実行し、その後にStudioで「Run」するか、コマンドで「adb install」を行うことで、Android OS 9を搭載した端末にアプリをインストールすることができるようになりました。

```
export ADB_LIBUSB=1
adb kill-server && adb root
```

Google側でこの問題は早急に解決されると期待されますが、同様の問題に直面している方々にとって参考になれば幸いです。
複数モダール画面の同時に開いている際の動作や表示不良問題の解決方法


## 根本原因
Bootstrapのモーダルコンポーネントでは、modal-backdropのdivが自動的に非同期で作成されるのはデフォルトの動作であるため、追加設定がうまく行かない場合たまたま発生する

## 解決方法
１．modal-backdropのdivが自動的に作成させないようにdata-bs-backdrop="static"⇒data-bs-backdrop="false"に設定
２．モーダルウィンドウが開くときにmodal-backdropクラスを持つdiv要素を作成し、それをモーダルウィンドウの直後に挿入しています。そして、モーダルウィンドウが閉じるときに、そのモーダルウィンドウの直後のmodal-backdropを削除しています。

### サンプルコード
<details><summary>クリックして開く</summary>

```sample.js
const _Com_Modal_EventHandler = {
    zIndex: 1040,
    eventHandler: {
        modalEvents: function () {
            $(document).on('show.bs.modal', '.modal', function () {
                _Com_Modal_EventHandler.zIndex += 10;
                $(this).css('z-index', _Com_Modal_EventHandler.zIndex);

                // Create a custom modal-backdrop and insert it after the modal
                $('<div class="modal-backdrop fade show"></div>').css('z-index', _Com_Modal_EventHandler.zIndex - 1).insertAfter(this);
            });
            $(document).on('hide.bs.modal', '.modal', function () {
                _Com_Modal_EventHandler.zIndex -= 10;

                // Remove the custom modal-backdrop of this modal
                $(this).next('.modal-backdrop').remove();
            });
        }
    }
};
```
</details>

### その他
>[Bootstrap v5.3.2](https://getbootstrap.jp/docs/5.3/getting-started/introduction/)で検証済
> [Bootbox.js](https://bootboxjs.com/)を利用する時、「backdrop: false」の設定も必要となる
Rubyで素朴なdiffコマンドを書いてみた（動的計画法）（移転済み）

引っ越しました。

[https://zenn.dev/sonota88/articles/6efd83d606cc2f](https://zenn.dev/sonota88/articles/6efd83d606cc2f)

https://zenn.dev/sonota88/articles/6efd83d606cc2f
electron-trpcでsuperjsonをtransformerに使用すると、40MBを超えるファイルの通信がめちゃくちゃ遅い

superjsonでserialize, deserializeするからめっちゃ遅くなる。
superjsonの使用をやめたら改善しました。というハマりの報告。

ついでに書いておくと、tauriも同様の問題を抱えていてipc通信の際にJSONにserializeしてから送信するので、重いファイルの送信は向いていない。v2.0から改善する予定らしい。
Excel でグラフ範囲を自動的に更新する方法、あるいは SERIES は関数なのか

Excel でグラフ範囲を自動的に更新したいことってありますよね。ここでグラフの系列を選択すると、数式バーに `=SERIES()` という数式が表示されています。

![excel-chart.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463374/a045cc2b-a938-257f-517d-02b84029737f.png)

この `=SERIES()` に対し動的に値を設定してあげたいということになります。しかし、`=SERIES()` のカッコ内に `OFFSET` などの関数をセットしても `その関数は正しくありません。` というエラーになってしまいます。

![excel-series-function.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463374/720e7b3a-ec1e-4589-6d9f-59eb21e281e4.png)

結論としては、[Microsoft Learn](https://learn.microsoft.com/ja-jp/office/troubleshoot/excel/defined-names-to-update-chart-range) の記事にある通り`名前の定義` 機能で参照範囲に名前を付け、それを `=SERIES()` に設定する必要があります。

![excel-defined-name.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463374/ef083726-f97f-1f62-b14b-d07824a365ba.png)

![excel-chart-dynamic.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463374/b9595d4c-d963-4697-e175-4723292644a4.gif)

## `=SERIES()` は Excel 関数なのか

ところで、この妙な制約がある `=SERIES()` は Excel 関数の一種なのでしょうか。少なくとも公式の [Excel 関数の一覧](https://support.microsoft.com/ja-jp/office/excel-%E9%96%A2%E6%95%B0-%E3%82%A2%E3%83%AB%E3%83%95%E3%82%A1%E3%83%99%E3%83%83%E3%83%88%E9%A0%86-b3944572-255d-4efb-bb96-c6d90033e188) には載っていません。もちろん、Excel VBA の [WorksheetFunction オブジェクト](https://learn.microsoft.com/ja-jp/office/vba/api/excel.worksheetfunction)から呼び出せるワークシート関数の一覧にもありません。

一方、Excel VBA のドキュメントには `Series オブジェクト` および関連する `SeriesCollection オブジェクト` が登場します。これらはグラフの系列を操作するときに参照するオブジェクトです。

```VisualBasic
Sub Change_Series()

    ActiveChart.SeriesCollection(1).Formula = "=SERIES(Sheet1!$B$1,Sheet1!$A$2:$A$4,Sheet1!$B$2:$B$4,1)"

End Sub
```

従って、系列の数式（Series Formula）が数式バーで表示・編集できるようになっているだけで、通常の関数とは異なるものという理解が良さそうです。ただ Microsoft の公式ドキュメントで明確にそう述べている箇所を見つけることはできず、[同様の解釈](https://peltiertech.com/chart-series-formula/)が [Microsoft Support Community](https://answers.microsoft.com/en-us/msoffice/forum/all/series-function/7cf3db58-c1da-427f-a2d2-9d8fadd43033) で紹介されていた程度です。もし真相をご存知の方がいらっしゃれば、コメント欄等でご教授いただけますと幸いです。

## 参考リンク

* [定義済みの名前を使用してグラフ範囲を自動的に更新する - Microsoft 365 Apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/office/troubleshoot/excel/defined-names-to-update-chart-range)
* [グラフの参照範囲を自動的に変更する](http://www.officetanaka.net/excel/function/tips/tips71.htm)
* [Excel 関数 (アルファベット順) - Microsoft サポート](https://support.microsoft.com/ja-jp/office/excel-%E9%96%A2%E6%95%B0-%E3%82%A2%E3%83%AB%E3%83%95%E3%82%A1%E3%83%99%E3%83%83%E3%83%88%E9%A0%86-b3944572-255d-4efb-bb96-c6d90033e188)
* [WorksheetFunction オブジェクト (Excel) | Microsoft Learn](https://learn.microsoft.com/ja-jp/office/vba/api/excel.worksheetfunction)
* [How to use a macro to add labels to data points in an xy scatter chart or in a bubble chart in Excel - Microsoft Support](http://support.microsoft.com/en-us/kb/213750)
* [Series オブジェクト (Excel) | Microsoft Learn](https://learn.microsoft.com/ja-jp/office/vba/api/excel.series(object))
* [SeriesCollection オブジェクト (Excel) | Microsoft Learn](https://learn.microsoft.com/ja-jp/office/vba/api/excel.seriescollection)
* [The Excel Chart SERIES Formula - Peltier Tech](https://peltiertech.com/chart-series-formula/)
AWS Amazon LinuxでWordPressを立てた時にカスタムパーマリンクが機能しない時の対処法

## この記事について
[前回の記事](https://qiita.com/yabish/items/fff120b622d723f0e7ce)の執筆後にWordPressのパーマリンクを変更すると「更新に失敗しました。返答が正しいJSONレスポンスではありません。」とのエラー...
WordPressを初めて触っていたこともあり、何が原因かわからないエラー文に悪戦苦闘しましたが、なんとなく状況が理解できてきたので得られた知見をまとめました。
おそらく、[really simple ssl](https://ja.wordpress.org/plugins/really-simple-ssl/)のプラグインが悪さしているのでしょうが...

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/412378/6d8437be-28f8-ba4f-b001-4b73259b1016.png)


## 問題が発生した状況
AWSのアーキテクチャ：[前回の記事](https://qiita.com/yabish/items/fff120b622d723f0e7ce)を参照
パーマリンク：基本以外の設定にした時

## 結論
1. 以下のように mod_rewriteモジュールの有効化
    ``` bash
    $ sudo su - 
    $ vi /etc/httpd/conf/httpd.conf
    ```
2. `mod_rewrite` を有効化する
    ```vim
    <Directory "/var/www/html">
        AllowOverride None  //この1行を以下のように修正
        AllowOverride All
    </Directory>
    ```
3. httpdサービスの再起動
    ``` bash
    $ systemctl restart httpd.service
    ```

おそらくこれでパーマリンクを変更しても無事に公開することができるようになります！

## 詳細
### そもそもの原因
`.htaccess` で制御している `Rewrite` が `mod_rewrite` モジュールの無効化によって機能していないことです。
ここでそれぞれのワードについて簡単にまとめておきます。
- `.htaccess`：Webサーバの動作をディレクトリ単位で制御するためのファイル。Apacheなどで使用されている。
- `Rewrite`： `.htacess` 内で記述・制御されるもので、特定のURL宛てに飛んできたリクエストを、ルールに従って異なるURLに転送することができる。`.htaccess` では以下のように記述されている。
    ``` .htaccess
    ~~ 省略 ~~
    RewriteEngine on
    RewriteRule リダイレクト元 リダイレクト先
    ~~ 省略 ~~
    ```
- `mod_rewrite`：`.htaccess` の `Rewrite` の処理で走る Apache のモジュール。

つまり、以下のようになっていることが問題です。（厳密ではないですが）
![WordPress-htaccess-mod_rewriteの関係性.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/412378/f79c472f-6863-e5da-01ac-a851bc8a1bb6.png)

### 対処法
#### 1. .htaccessが存在するか確認する
一応本題に入る前に前提のすり合わせです。
パーマリンクの変更に伴い、 `.htaccess` は上書きされます。
「基本」であれば記述はありませんが、カスタムパーマリンクであれば記述が加えられます。
以下のような内容が書かれていたら問題ありません。
``` .htaccess
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
```

#### 2. httpd.conf の設定を変更する
ここからは結論で述べたことと同じです。
1. 以下のように mod_rewriteモジュールの有効化
    ``` bash
    $ sudo su - 
    $ vi /etc/httpd/conf/httpd.conf
    ```
2. `mod_rewrite` を有効化する
おおよそ156行目周辺にあると思います。
    ```vim
    <Directory "/var/www/html">
        AllowOverride None  //この1行を以下のように修正
        AllowOverride All
    </Directory>
    ```
3. httpdサービスの再起動
    ``` bash
    $ systemctl restart httpd.service
    ```
これでパーマリンクの挙動が治ると思います。

### 補足：Rewrite と Redirect の違い
私も今回詳しく調査するまで曖昧だったのですが、`Rewrite` と `Redirect` は異なる処理を行います。
細かな違いは様々ありますが、一番の違いは`Rewrite` はURLが変更前と変更後で変わらず、 `Reedirect` はURLが変わるという点です。
![rewriteとredirectの違い.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/412378/20c864fd-d185-1803-afdc-7b92aa6fc578.png)

このことからも、WordPressのパーマリンクは `Rewrite` が使われていることが分かります。
参考：[RewriteRuleとRedirectの違い【Apache】](https://penpen-dev.com/blog/rewriterule-redirect-apache/)

## まとめ
Webサーバについてもっと勉強せねば...
ただ、WordPressももう少しエラー文わかりやすくしてくれ...
HTMLスライドショーのための雛形・サンプル

以下サンプルHTML＋CSS+JS

```slideshow.html
<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>画像スライドショー</title>
  <style>
    html,
    body {
      margin: 0;
      padding: 0;
      background-color: black;
    }

    .slideshow {
      width: 100vw;
      height: 100vh;
      overflow: hidden;
    }

    .slideshow img {
      width: 100%;
      height: 100%;
      object-fit: contain;
    }

    .slideshow img:first-of-type {
      display: block;
    }

    .slideshow img:not(:first-of-type) {
      display: none;
    }
  </style>
</head>

<body>
  <div class="slideshow">
    <img src="https://pbs.twimg.com/media/GF8-s-BbMAAsg0w?format=jpg&name=large">
    <img src="https://pbs.twimg.com/media/GF76rxVa4AAJz-q?format=jpg&name=large">
    <img src="https://pbs.twimg.com/media/GEvIzyTagAArYAU?format=jpg&name=large">
  </div>
  <script>
      const images = document.querySelectorAll('.slideshow img');
      let currentImage = 0;
      // 10秒間隔で画像を切り替える
      let intervalMS = 10000;
      setInterval(() => {
        // すべての画像を非表示にする
        images.forEach(image => image.style.display = 'none');
        // 次の画像のインデックスを取得
        currentImage = (currentImage + 1) % images.length;
        // 次の画像を表示
        images[currentImage].style.display = 'block';
        // 次の画像のアニメーションを開始(速いフェード・イン／アウト)
        images[currentImage].animate(
          {
            opacity: [0, 1, 1, 0],
            offset: [0, 0.05, 0.95, 1],
            easing: "ease-out",
          },
          intervalMS,);
      }, intervalMS);
  </script>
</body>
</html>
```

## 解説

### テストしやすいようにペラ１で

コピペしてファイルに保存するだけで動作するので試してみてください。

### 長辺に合わせて最大化する際には`object-fit: contain;`で

`object-fit: contain;`で画像の長辺に合わせて最大化してくれます。余白は出ます。余白を出さないように短辺に合わせ絵拡大縮小したい時には`object-fit: cover;` で実現できます。

### keyframes をJSで実現するためanimateメソッドを使った

◯秒おきに切り替えという操作をJSで設定したかったため、CSSにkeyframesを設定するのではなく、JSで`animate`を呼び出しています。機能としてはほぼ同等ですが、CSSでアニメーションを制御するかJSで制御するかはどっちかに寄せた方が修正が一箇所ですみます。

フェード・イン／アウトのアニメーションはなくてもいいのですが、一瞬で切り替わると変わったということが分からないので、少しだけアニメーションさせています。

## 拡張例

- IMGタグを複数出しているところをPHPなどで埋め込んでもよし
- JSでimg要素をappendして実行してもよし

画像とintervalMSの数字だけを変えればあちこちで使えます。

## なぜ作りたかったか

スマホでスライドショーのアプリを探していたのですが、「大量の風景画像があります」とかで自分で選べるものがなかったり、切り替えエフェクトが凝りまくっていてそこはそんなに頑張るところじゃぁないというものばかりだったので、仕方なく自作しました。

## 用途

- あまりスマホをフォトフレーム代わりに推し画像を表示させておく
- Tableauなどで作られたグラフのスクショを連続再生させる「手ぶらBIツール」をつくってオフィスに常に表示
- 店舗などの画面に映すデジタルサイネージ
Excel-VBA：エディタ（VBE）の見た目を調整

# 0. はじめに
Excel-VBAのコードを書く際、意外と気になる
エディタ(VBE)の見た目について、私なりの設定を共有させていただきます。

# 1. 編集方法
細かい編集方法は[はりぼなブログ様](https://masashi-blog418.com/?p=4692)をご参照ください
ここではざっくりの流れだけ紹介します。

1. [Alt] + [F11]でエディタを起動
1. [ツール/オプション]を開く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/753878/65bd61c8-6419-60c1-c0fc-7af2f81f2cdc.png)
1. [エディターの設定]でフォントの色やサイズなどを変更する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/753878/a7bb9722-b04e-cd70-f6ff-b5924142e4c9.png)

以上です。
あとは自分なりに見やすい設定を決めるだけです。

# 2. 個人的なおススメ設定
私なりのおすすめ設定をご紹介します。
最終的には以下のような見た目になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/753878/bc67144c-e486-1201-a479-efd611e41812.png)

1. 全体の設定：フォント Meiyo UI、サイズ 11
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/753878/7b870870-6669-e79d-a64a-140151e3001e.png)

1. 標準コード：黄色、背景黒
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/753878/ba225072-b751-952a-fd01-15cb396567d6.png)

1. コメント：灰色、背景黒
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/753878/2abc15a8-9ec8-4091-22fc-2d427dcfacfd.png)

1. キーワード：水色、背景黒
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/753878/d09553e6-cc67-749d-750d-a8d6a2132df4.png)

1. 識別子：白、背景黒
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/753878/ac5a785a-4d89-3784-f809-ecec4fe11b04.png)

残りはデフォルト通りです。

# 3. おわりに
パソコンを変えた際などに、自分の基本設定はなんだったっけ？となること多かったので投稿させていただきました。
もっと便利な設定があればご意見いただけますと幸いです。
Gmail を開いたタイミングで他のアカウントのメールを受信する

# 経緯
- Gmail 以外に届いたメールを Gmail で管理するためこれまで転送機能を使用していた
- 2023年4月頃から Gmail が転送メールを迷惑メール判定する、送信元にエラーメールを返すといった問題が発生するようになった
  - (参考) [さくらインターネットのサーバーからGmailへ転送したメールが不達となる場合](https://help.sakura.ad.jp/notification/n-2595/#04)
- 転送を止め Gmail から POP3 で受信する運用に変更した
  - (参考) [Gmail（ブラウザ版）の設定をしたい](https://help.sakura.ad.jp/mail/2140/)
- Gmail の仕様で受信は 1 時間に 1 回程度の頻度で行われるため今すぐ受信したい場合は設定画面から「メールを今すぐ確認する」をクリックする必要があり面倒だった
- そこで [Tampermonkey](https://microsoftedge.microsoft.com/addons/detail/tampermonkey/iikmkjmpaadaobahmlepeloendndfphd) を用いて Gmail を開いたタイミングで 1 度受信する仕組みを作った
- こんなことしなくても Gmail の設定で対応可能などあれば教えていただけると幸いです
- 環境は以下を用いた
  - [さくらのメールボックス](https://rs.sakura.ad.jp/mail/)
  - Windows 11
  - Edge v121
    - Chrome など Tampermonkey または同等の拡張機能が動くブラウザであれば同じことができるはず

# 手順
## 設定
- Gmail 上で受信設定を行う
  - (参考) [Gmail（ブラウザ版）の設定をしたい](https://help.sakura.ad.jp/mail/2140/)
- [Tampermonkey](https://microsoftedge.microsoft.com/addons/detail/tampermonkey/iikmkjmpaadaobahmlepeloendndfphd) をインストールする
- Tampermonkey のダッシュボードを開く
  - ![dashboard.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1060173/0e4fdab0-51b9-0dfc-8451-91ce16ed0993.png)
- 画面上の 「+」 をクリックする
  - ![plus.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1060173/9dfca80c-7bdc-820b-8f57-6cd9adc7563e.png)
- エディターに以下をコピペして保存する
```javascript:エディター
// ==UserScript==
// @name         GMail POP3 Checker
// @namespace    http://tampermonkey.net/
// @version      0.1
// @author       ArK38
// @match        https://mail.google.com/mail/u/*
// @grant        none
// ==/UserScript==

(function() {
  'use strict';

  if (/#settings\/accounts/.test(location.hash)) {
    var iid = setInterval(function () {
      // 日本語と英語に対応
      (document.evaluate('//*[@role="link" and .="メールを今すぐ確認する"]', document).iterateNext() || document.evaluate('//*[@role="link" and .="Check mail now"]', document).iterateNext()).click();
      location.hash = "#inbox";
      clearInterval(iid);
    }, 1000)
  };
})();
```
- Gmail の「アカウントとインポート」画面を開く Gmail のブックマークを作成する
  - Gmail に複数アカウントでログインしていて 2 つ目以降のアカウントを開きたい場合は `0` の部分を `1` などに変更する
```html:ブックマーク
https://mail.google.com/mail/u/0/#settings/accounts
```

## 動作確認
- POP3 で受信するメールアドレスに動作確認用のメールを送る
- 作成したブックマークを開く
- 「アカウントとインポート」画面が開くことを確認する
  - ![settings.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1060173/6500796f-b8c7-60e7-b692-aaf453877375.png)
- 数秒後に受信トレイへ遷移することを確認する
  - 受信トレイに遷移しない場合は登録したスクリプトが発動したか確認するため Tampermonkey のアイコンに「 1 」が表示されていることを確認する
  - Gmail を開いた状態でブックマークを開いてもスクリプトは発動しないため注意
  - ![icon.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1060173/043ad196-2bc9-1ad5-364e-6148156871d9.png)
- 動作確認用に送ったメールが受信されることを確認する
  - ![inbox.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1060173/5338b90d-124c-20d4-aa37-5c15f3b1e06e.png)
- 以上です
「現代数理統計学の基礎」演習問題解法メモ　第5章問15～18

やっと第5章が終わります。この辺りは何度やっても自力では解けず、なんとかいろいろ調べて解答を作りあげました。
![第5章問15 P1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576811/e5de2275-42fa-dd77-db70-b3ee63da5496.jpeg)
![第5章問15 P2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576811/a1ce6be8-f937-345b-be27-c0658e716ee3.jpeg)
![第5章問16　P1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576811/6ce0035e-4a19-5f37-b42b-57826e9b8438.jpeg)
![第5章問16 P2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576811/5049a7b8-72a6-988f-3e0c-3ed2ff724e4c.jpeg)
![第5章問16 P3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576811/bd0b9d9c-7bcc-43e5-6497-2b265e54bdff.jpeg)
![第5章問16 P4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576811/996ab3f0-fdad-f8f6-4537-9d18574a17df.jpeg)
![第5章問17 P1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576811/4ba7619d-ab80-9778-8562-e1fb19849a24.jpeg)
![第5章問17 P2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576811/9c3c7daa-9e6e-0bbc-1ed6-8cbd51933c3e.jpeg)
![第5章問18 P1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576811/4adac51e-a1da-b406-14ac-8456d80937b1.jpeg)
![第5章問18 P2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576811/f2525a35-f1a8-578b-2fb5-bb6a16e26648.jpeg)