OmniAICreator/Qiita-1.07M huggingface dataset:
Jetson NanoにJetPackとjetson-inferenceをインストールしてみた


はじめに
----
Jetson Nano 4GBにJetPackとjetson-inferenceをインストールしてみました。参考になればと思い、実際に私が行った作業を記録として残すことにしました。
気づいた点があればコメントをお願いします。

環境
----
+ Jetson Nano 4GB
+ SD Card 64GB
+ Raspberry Pi Camera V2 (一つで大丈夫です)
![DSC_0385.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1615141/a60a9d5a-b775-51c4-5bcc-5816371ef8ae.jpeg)
![Something went wrong]()


JetPackのインストール
----
最初に以下のコマンドでJetPackをインストールします。
まずは、アップデートします。
```
$ sudo apt update
```

アップデートが終わったらJetPackのインストールです。
以下のコマンドでJetPackをインストールできます。
```
$ sudo apt install nvidia-jetpack
```

JetPackのインストールをしたあと、以下のコマンドでインストールされたJetPackのバージョンを確認できます。
```
$ sudo apt show nvidia-jetpack
```

参考：
https://docs.nvidia.com/jetson/jetpack/install-jetpack/index.html#install-jetpack

jetson-inferenceをインストール
----
Githubからjetson-inferenceをCloneします。
```
$ git clone https://github.com/dusty-nv/jetson-inference.git
```

jetson-inferenceフォルダーに移動する。
```
$ cd jetson-inference/
```

サブモジュールのアップデートをする。これをやらなかったらcmakeでエラーになってしまった。
```
~/jetson-inference$ git submodule update --init
```

buildディレクトリーを作り、移動する。
```bash
~/jetson-inference$ mkdir build
~/jetson-inference$ cd build/
```

cmakeを実行して、指示に従ってオプションを設定する。私の場合は全てデフォルトを選択しました。
```
~/jetson-inference/build$ cmake ../
```

cmakeでMakefileが出来上がったら、makeでコンパイルする。オプションはCPUのcoreの数を-jオプションで設定する。このオプションはなくても動くが、遅くなるようです。
```
~/jetson-inference/build$ make -j$(nproc)
```

makeが終了したら、インストールをします。
```
~/jetson-inference/build$ sudo make install
```
で、動かす前に重たいアプリケーションなのでデフォルトのSwap領域は1.9GBしか確保されていないので拡張しないと安定して動きません。
Swapファイルを拡張する必要がある場合には次の手順で拡張ができます。

````
$ cd ~
$ git clone https://github.com/JetsonHacksNano/installSwapfile
$ cd installSwapfile
~/installSwapfile$ ./installSwapfile.sh
````
Swapサイズの確認は
`````
$ free -h
`````
でこんな感じで1.9GBから7.9GBに増えました。
![free-h.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1615141/aab43ee8-4f3e-adb6-f57d-6dbd763132e0.png)


参考：
https://developer.nvidia.com/blog/realtime-object-detection-in-10-lines-of-python-on-jetson-nano/
https://github.com/dusty-nv/jetson-inference/blob/master/docs/building-repo-2.md
https://github.com/dusty-nv/jetson-inference
https://zenn.dev/sumire0204/articles/43b843492afcfd

使ってみる
---------
```
$ detectnet-camera.py
```
https://www.youtube.com/watch?v=CFNBMQUgNS8

```
$ posenet.py
```
https://www.youtube.com/watch?v=V9j8PLjluQE
変数を{ }で囲む (変数展開)

# はじめに
今回は、変数を文字列内で展開することができる変数展開の使い方について投稿します。

# 使い方
使い方は下記になります。
\\$(ドルマーク)の前に`{`をつけるパターンと、\\$の後ろに`{`をつけるパターンがあります。
結果はどちらも一緒なので好きな方を使ってください。

ただし、文字列は、ダブルクォーテーション("") または シングルクォーテーション('')で囲んでいると思います。
変数を文字列内で展開する際は、ダブルクォーテーションで囲ってください！

```php4
"文字列〜 ${変数} 〜 文字列"

or

"文字列〜 {$変数} 〜 文字列"
```

# 使用例
配列$examの値を文字列内で展開するケースを例に説明します。

下記のコードの場合、エラーになります。
理由は、\\$examのキーであるmathの前についているシングルクオーテーション( ’ )によって、
\$examの前にある「”」の終わりを表すクオーテーションと認識されるからです。

```php4
// エラーケース

$exam = ['math' => '90'];

echo "数学の点数：" . "$exam['math']";

//　出力結果はエラーになる
```



これを解決するために`{ }`で変数`$exam[’math’]`を囲みます。
それによりカッコで囲まれた個所が変数と認識されるためです。
```php
// OKパターン

$exam = ['math' => '90'];

echo "数学の点数：" . "{$exam['math']}";    // または、"${exam['math']}"

//　結果は、「数学の点数：90」となる
```

# 最後に
今回は　変数展開について説明しました。
普段変数を文字列内で使用する場合は、ダブルクォーテーションを使っていると思います。
複雑なケースでは変数展開 `{$変数}`を使用する場合が出てくると思うので
頭の片隅に入れておいてもらえればと思います。

# 参考サイト
https://atmarkit.itmedia.co.jp/ait/articles/1403/20/news105_2.html
【React】そのProvider、実は要らないかも？

## 結論：Providerは無くてもContextのdefaultValueなら参照できる

結論から言ってしまうと、Provider無くてもdefaultValueなら参照できます。
なのでReact Contextに定数を渡す場合、Providerは省略可能です。
その内容のツイートがこちら👇

[https://twitter.com/DavidKPiano/status/1535976454353215489?s=20&t=0wP0skZFW1Jvr0__tWmP2g](https://twitter.com/DavidKPiano/status/1535976454353215489?s=20&t=0wP0skZFW1Jvr0__tWmP2g)

### 試してみる

👇CodeSandbox

[https://codesandbox.io/s/no-provider-react-context-obhtgu?file=/src/index.js](https://codesandbox.io/s/no-provider-react-context-obhtgu?file=/src/index.js)

```tsx
import { StrictMode, createContext, useContext } from "react";
import { createRoot } from "react-dom/client";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <StrictMode>
    <App />
  </StrictMode>
);

// 適当なContextにdefaultValueを渡す
const SomeContext = createContext("no provider")

function App() {
  // Providerが無くてもvalue === "no provider"
  const value = useContext(SomeContext)
  return <div>{value}</div>
}
```

このように `createContext` に渡したdefaultValueはProviderが無くてもアクセス可能です。

### 活用方法

一例としてAxios Instanceのように参照が変わらない性質の値で、環境によって異なるものを渡したい場合（依存注入が活きる場合）に効果を発揮すると思います。（そもそもProvider有り無し関係なく正しい使い方ですけど）

グローバル定数を何でもかんでもContextに包んで都度Consumeするのは、直接importするよりは結合度を下げられるメリットはあるものの、ちょっと考え物かなぁ。。。ぜひ皆さんのご意見、コメントお待ちしています。
Nuxt.jsに@nuxtjs/google-fontsを使ってGoogle Fonts（Webフォント）を設定する方法

Nuxt.jsに[@nuxtjs/google-fonts](https://github.com/nuxt-community/google-fonts-module)を使ってGoogle Fonts（グーグルフォント）を設定する方法になります。
フォントをダウンロードして使用する方法についても解説しています。

## @nuxtjs/google-fontsをインストールする
まずは、@nuxtjs/google-fontsをインストールします。
```
npm install --save-dev @nuxtjs/google-fonts
# OR 
yarn add --dev @nuxtjs/google-fonts 
```
## nuxt.config.jsで設定をする
続いて`nuxt.config.js`で設定を追加していきます。

```nuxt.config.js
export default {
  buildModules: [
    '@nuxtjs/google-fonts' //追加
  ],
  googleFonts: {
    families: {
      Roboto: [100, 400, 500, 700] //読み込みたいGoogle　Fontsを指定
    }
  },
}
```
これでGoogle Fontsの設定は完了です。cssで`font-family`を設定します。


```css
body {
   font-family: "Roboto", sans-serif;
}
```

## Google Fonts（グーグルフォント）をダウンロードして利用する
@nuxtjs/google-fontsにはGoogleFontsをダウンロードして利用できるオプションがあります。
```nuxt.config.js
export default {
  googleFonts: {
    families: {
      Roboto: [100, 400, 500, 700],
      download: true, //追加
      inject: true
    }
  },
}
```
`nuxt.config.js`に上記のオプションを設定します。
![nuxtjs:google-fonts.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559347/935d6ba3-5af4-1710-5fd2-2b19b1b02204.png)
Nuxtをビルドすると、assetsフォルダに`fonts.css`と`fonts`が生成されます。
Google Fontsをダウンロードして使用したい場合には、かなり便利かと思います。

https://google-fonts.nuxtjs.org/downloading/
CDN版 Vue3.xでPiniaを動かす

常識的にはvite環境でやるべきなんですが、ちょっとワケありなのでVue3.xのcompositionAPI CDN環境でPinia使います
さすがに危篤すぎてWeb漁っても全くHITしなかったので備忘録代わりに

Piniaの基本的な使い方は他にも沢山記事はあるので割愛
## 要点
* [vue-demi](https://github.com/vueuse/vue-demi)が要る
* .use(createPinia())する

```pinia.html
<!DOCTYPE html>
<html>
  <head></head>
  <body>
    <div id="app">
      <span>🍍Pinia!🍍</span>
      <button @click="counter.increment()">
        Count is: {{ counter.count }}
      </button>
    </div>

    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vue-demi"></script>
    <script src="https://unpkg.com/pinia"></script>

    <script>
      const { createApp } = Vue;
      const { defineStore, createPinia } = Pinia;

      const useCounterStore = defineStore("counter", {
        state: () => {
          return { count: 0 };
        },
        actions: {
          increment() {
            this.count++;
          },
        },
      });

      const App = {
        setup() {
          const counter = useCounterStore();
          return {
            counter,
          };
        },
      };
      const app = createApp(App);
      app.use(createPinia());
      app.mount("#app");
    </script>
  </body>
</html>
```

## オチはない

堕ちたな
【Nuxt.js】Nuxt.jsでの画像の指定方法

# 画像の格納場所
２通りがあります。
<ul>
<li>/assets</li>
<li>/static</li>
</ul>

## 格納場所の使い分け
### webpackのモジュールとして扱いたい
この場合は /assets に画像を保管
```
<img src="~/assets/my-image-2.png" />
```

### 静的画像として扱いたい（webpackしたくない）
この場合は /static に画像を保管
```
<img src="/my-image.png" />
```



■参考にしたサイト
https://nuxtjs.org/docs/directory-structure/static/
Reactでjsonファイルを読み込む

# はじめに
Reactを使っている際に、動作テスト用にローカル配下のjsonデータを読み込むことがあったので、やり方を備忘録程度に書き残す。
（他にやり方あれば是非コメント欄に）

# やり方
## 方法
import文を使って読み込む
```
import Data from "[jsonファイルのパス]"
```

## 例
カスタムフックでの利用を想定した場合のものを以下に記載する。
```useAllUser.ts
import { useState } from "react"
import { User } from "../types/api/User"
import Data from "../_test_data/user.json"; // これを追加する

export const useAllUser = () => {
    const [users, setUsers] = useState<Array<User>>([])
    const getUsers = () => setUsers(Data)
    return { users, getUsers }
}

```
【便利すぎる】DockerでMySQLを動かす&初期データも入れる方法

## 概要
開発でMySQLをローカル環境で構築する必要があったため、Dockerを使って試してみました。
また、初期データを入れる方法も調べて検証してみました。

## 準備

### フォルダ構成

```
dev/01.sql
docker-compose.yml
```

### docker-compose.yml 
```docker-compose.yml
db:
  image: mysql:latest
  volumes:
    - ./dev:/docker-entrypoint-initdb.d
  environment:
    - MYSQL_ROOT_PASSWORD=root
```

上記のように、`./dev`下にsqlファイルを置くことでDocker compose実行時に実行してくれます。
フォルダ名は`./dev`である必要はありません。

> When a container is started for the first time, a new database with the specified name will be created and initialized with the provided configuration variables. Furthermore, it will execute files with extensions .sh, .sql and .sql.gz that are found in /docker-entrypoint-initdb.d

https://hub.docker.com/_/mysql/


### 初期データ登録用SQL
今回は以下のSQLを用意し、後でテーブル内の値を取得してみます。
```01.sql
DROP SCHEMA IF EXISTS sample;
CREATE SCHEMA sample;
USE sample;

DROP TABLE IF EXISTS employee;

CREATE TABLE employee
(
  id           INT(10),
  name     VARCHAR(40)
);

INSERT INTO employee (id, name) VALUES (1, "test1");
INSERT INTO employee (id, name) VALUES (2, "test2");
```

## 検証結果

### Docker compose 起動
以下コマンドでDockerを起動してみます。
```bash
docker-compose up
```

ログをみると、01.sqlが実行されていることが分かります。
```bash
db_1  | 2022-06-14 12:58:54+00:00 [Note] [Entrypoint]: /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/01.sql
```

### MySQLへの接続確認
以下コマンドでコンテナIDを確認します。

```sh
docker ps
```

以下コマンドでコンテナ内のMySQLにログインしてみます。
パスワードは**docker-compose.yml**内の**MYSQL_ROOT_PASSWORD**の値を指定します。
```sh
docker exec -it コンテナID mysql -u root -p
Enter password:
```

ログインに成功すると、以下のような画面になります。

```sh
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 10
Server version: 8.0.29 MySQL Community Server - GPL

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>
```

### 初期データの確認
データベースを選択します。

```sh
mysql> use sample;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
```

テーブル内の値をselectしてみると、01.sqlで流したデータが格納されていることが分かります。

```sh
mysql> select * from employee;
+------+---------+
| id   | name    |
+------+---------+
|    1 | test1 |
|    2 | test2  |
+------+---------+
2 rows in set (0.00 sec)
```

## まとめ

DockerでMySQLを動かす&初期データも入れる方法を紹介しました。
この方法により、自身のPCを汚すことなく、MySQLをガチャガチャ触れるようになります。
開発の効率が上がると思うので、ぜひ試してみてください。

## 参考
https://qiita.com/NagaokaKenichi/items/ae037963b33a85df33f5
【1-3年目までのプログラマー向け】基礎的な内容を網羅するための月1勉強会の内容、教材

## 目指す状態
- 全く知らない分野がない。
  - iOSはできるけど、テスト計画は立てられないような状態にならない。
- 幅広い分野に対応できる基礎的な知識がある。
- 複雑でサイズの大きいタスク・問題も整理して進められる。

## 学習内容
- 問題解決、仕事の進め方
- コードの書き方、アーキテクチャ
- テスト
- アルゴリズム
- ネットワーク, サーバー
- スクラム
- RDB
- セキュリティ

## 学習の進め方
- 定期的（1ヶ月に1回）に事前に決めた課題の勉強会を開き、アウトプットする。
    - 勉強会は1時間程度。アウトプット40分。答え合わせ、振り返りを20分。
- 勉強会までに指定した教材で学ぶ。
- 1年以内に基礎的な内容を全体的に網羅する。

## 勉強会で出す課題
- できるだけ仕事に近い、実践的な課題を出す。
- アルゴリズムのように仕事に近い課題を用意しづらいものは除く。
### 例：テスト
自社で運用しているサービスを新規開発した場合のテスト計画を立てる

### 例：問題解決
「実際に発生しそうなインシデントの問題解決のシミュレーションをロジックツリーを書きながら進める」

## 分野と教材
- 「問題解決、仕事の進め方」 * 「各分野の基礎的な知識」を学ぶ。
- できるだけ理解しやすい基礎的な内容のものを選んだ。
- 教材の大半は書籍を使う。アルゴリズムだけはサイトも利用する。

### 問題解決、仕事の進め方
- コンサル一年目が学ぶこと
- 新版 問題解決プロフェッショナル―思考と技術

### コードの書き方、アーキテクチャ
- リーダブルコード
- CODE COMPLETE 第2版 完全なプログラミングを目指して
- Clean Architecture 達人に学ぶソフトウェアの構造と設計

### テスト
- はじめて学ぶソフトウェアのテスト技法
- テストから見えてくる グーグルのソフトウェア開発

### アルゴリズム
- アルゴリズム図鑑 絵で見てわかる26のアルゴリズム
- 問題解決力を鍛える!アルゴリズムとデータ構造 (KS情報科学専門書)
- [AtCoder 競プロ典型 90 問](https://atcoder.jp/contests/typical90)
- [コーディング面接対策のために解きたいLeetCode 60問](https://1kohei1.com/leetcode/)

### ネットワーク, サーバー
- スラスラわかるネットワーク&TCP/IPのきほん 第2版
- Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂3版
- Linuxコマンドブック ビギナーズ 第5版

### スクラム
- SCRUM BOOT CAMP THE BOOK

### RDB
- SQL 第2版 ゼロからはじめるデータベース操作
- 達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ

### セキュリティ
- 体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 脆弱性が生まれる原理と対策の実践
TECH CAMP　9日目 ~N+1問題~

# 学習内容
- Ruby on Rails
　- `max-length`オプション・・・inputタグを生成するヘルパーメソッド
　- `devise-parameter-sanitizer`メソッド・・・devise特有のパラメーターを取得するメソッド
　- `configure-permitted-parameters`・・・deviseでは新たに定義するメソッドをこの様に定義するが、本当は何でもいい。語尾に**s**がついているので注意
　- リダイレクト・・・本来受け取ったパスとは別のパスに転送する。
　- `unless`・・・falseの時elseまたは、endまでの処理を行う。ifと反対
　- `merge`メソッド・・・ハッシュを結合させるメソッド
　- アソシエーション・・・`has_many`メソッドと`belongs_to`メソッドがある。モデルに記載する。
　- N+1問題・・・引数に指定された関連モデルに一度のアクセスでまとめて情報を取得できる。
　- ルーティングのネスト・・・モデルと結びついている別のモデルのid情報を含めることができる

# 苦労したこと
アソシエーションはモデルに、N+1問題はコントローラーに書くというのが自分の中で腑に落ちなかった。
↓
アソシエーション⇨モデル同士がお互いに情報を結びつけられる様にするものなのでモデルにかく。
includesメソッド⇨変数に値を入れるときなどに、複数モデルに何回もアクセスしないといけない。その問題を解決するために、**Tweetモデルに情報取りに行くならUserモデルのやつもついでに全部取ってきて**みたいな感じでコントローラーにかく。
↓
includesメソットをモデルに書かない理由としては、毎回TweetモデルいくときにUserモデル行かんでええで？問題が発生するから（多分違う）と考えている。
徳島市ごみ収集日をPandasでデータクレンジングしてCSVに変換

https://qiita.com/ffggss/items/96fbcd196c01f6345318

を参考に作成

https://www.city.tokushima.tokushima.jp/smph/kurashi/recycle/gomi/r4gomischedule.html

# スクレイピング
+ ごみ収集日程表をスクレイピング
```python
import re
import pandas as pd

year = 2022

url = "https://www.city.tokushima.tokushima.jp/smph/kurashi/recycle/gomi/r4gomischedule.html"

# thousandsを設定しないとカンマが消える
dfs = pd.read_html(url, thousands=None)

len(dfs)
```
# データクレンジング
## ごみ収集地区割表
+ 五十音順に分割されているのでひとつに結合
```python
def get_area(dfs):

    df0 = pd.concat(dfs).reset_index(drop=True)

    df1 = df0.loc[:, ~df0.columns.str.endswith("行")].copy()
    df1["五十音"] = (
        df0.loc[:, df0.columns.str.endswith("行")]
        .fillna("")
        .apply(lambda x: "".join(x), axis=1)
    )

    result = df1.reindex(["五十音", "区域", "収集担当", "収集地区"], axis=1)

    return result


area_df = get_area(dfs[:8])
area_df

area_df.to_csv("ごみ収集地区割表.csv", encoding="utf_8_sig")
```

## 家庭ごみ収集日程表
+ 燃やせるごみは曜日しかないので曜日から日付を作成
+ 燃やせるごみ以外は月と日が分かれているので結合して日付を作成
```python
# 曜日

subs = {
    "月曜日": "Monday",
    "火曜日": "Tuesday",
    "水曜日": "Wednesday",
    "木曜日": "Thursday",
    "金曜日": "Friday",
    "土曜日": "Saturday",
    "日曜日": "Sunday",
}

# 4/1から3/31までの1年間
dt_range = pd.date_range(f"{year}-04-01", f"{year + 1}-03-31", freq="D")

# 正月三が日
newyear = pd.date_range(f"{year + 1}-01-01", f"{year + 1}-01-03", freq="D")

# 正月三が日を除いた1年間の燃やせるゴミ
calender = pd.DataFrame("燃やせるゴミ", index=dt_range, columns=["分別区分"]).drop(newyear)


def gomi_calender(df0, area):

    df1 = df0.set_index("分別区分").copy()

    # 燃やせるゴミ
    s = df1.iat[0, 0]

    # 曜日抽出
    youbi = re.findall("[月火水木金土日]曜日", s)

    # 曜日を英語に変換
    weekday = [subs.get(i, i) for i in youbi]

    # 指定曜日を抽出
    df2 = calender[calender.index.day_name().isin(weekday)].copy().reset_index()
    df3 = df2.rename(columns={"index": "日付"})

    # 燃やせるごみ以外
    df4 = df1.iloc[1:].stack().reset_index()
    df4.set_axis(["分別区分", "month", "day"], axis=1, inplace=True)

    # 月
    df4["month"] = df4["month"].str.rstrip("月").astype(int)

    # 複数日を分解
    df4["day"] = df4["day"].str.split(",")
    df5 = df4.explode("day").reset_index(drop=True)
    df5["day"] = df5["day"].astype(int)

    # 年
    df5["year"] = year

    # 1～3月は年 +1
    df5["year"].mask(df5["month"] < 4, df5["year"] + 1, inplace=True)

    # 年月日から日付に変換
    df5["日付"] = pd.to_datetime(df5[["year", "month", "day"]])

    df6 = df5.reindex(["日付", "分別区分"], axis=1)

    # 結合
    result = pd.concat([df3, df6]).reset_index(drop=True)
    result.to_csv(f"{area}.csv", encoding="utf_8_sig")

    result["収集地区"] = area

    return result


data = []

for df, area in zip(dfs[8:], ["A地区", "B地区", "C地区", "D地区"]):

    data.append(gomi_calender(df, area))

result = pd.concat(data).reset_index(drop=True)

result

result.to_csv("家庭ごみ収集日程表全データ.csv", encoding="utf_8_sig")
```
AzureBackup設定時にTLSエラーでハマッた件

# はじめに
WindowsAdminCenter(以下WAC)でオンプレサーバーにAzureBackup設定したときにハマッたエラーをここにに綴る。
WACでAzureBackup設定に必要なパラメータを入力し、あとは「適用」でエージェントが自動インストール♪…とは簡単にいかず、以下エラーが表示された、、
` The request was aborted: Could not create SSL/TLS secure channel. `


# エラーが起きる環境・前提条件
エージェント適用先に古いWindows OS を使用していると起きる。
自分の場合は **Windows Server 2016**

# 原因・対応
Azure Backupは2021年9月1日から**TLS 1.2**が必須となった。
つまりは古いOSの場合はTLS 1.2 に対応させる必要がある。
Windows Server 2016の場合は以下コマンドライン（管理者実行）を実行するだけで解消した。

```bat
REG ADD "HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.30319" /v SystemDefaultTlsVersions /t REG_DWORD /d 1 /f 
REG ADD "HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.30319" /v SchUseStrongCrypto /t REG_DWORD /d 1 /f
REG ADD "HKLM\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319" /v SystemDefaultTlsVersions /t REG_DWORD /d 1 /f 
REG ADD "HKLM\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319" /v SchUseStrongCrypto /t REG_DWORD /d 1 /f
```

それ以外の古いOSは以下記事を参考にしてみてください。
https://atmarkit.itmedia.co.jp/ait/articles/2106/15/news008.html
山市様、いつも大変参考になり助かっております！🙇‍♂️🙇‍♂️
【Java】SpringでInterceptorを使って特定のセッション情報がないときのリダイレクトを行う

# はじめに
本記事ではSpringで`Interceptor`を使用し、特定のセッション情報が存在しない場合のトップページへのリダイレクト処理を行う方法を紹介します。

# 経緯
Spring Web MVCで各`Contorller`がセッション情報を利用した処理を行う場合、適切なnullチェックを行わないとすぐに`NullPointerException`が吐かれます。
そこで、`Interceptor`を使った前処理でセッション情報がnullのパターンを弾き、適当なページにリダイレクトする方法を調べました。

# 結論
以下の2つのクラスを作成すればOKです。
```java:Interceptorによる処理を実装するクラス
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;

public class SessionCheckInterceptor implements HandlerInterceptor {

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {// 各Controllerの処理が始まる前に呼ばれる

		if(request.getSession().getAttribute("sessionInfo") == null) { // 必要なセッション情報を確認
			response.sendRedirect("index"); // 無ければトップページにリダイレクト
			return false; // Controllerは起動しない
		}

		return true;
	}

}
```
```java:InterceptorをSpringの設定に追加するクラス
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import jp.co.example.interceptor.SessionCheckInterceptor;

@Configuration // アノテーションを忘れずに
public class WebMvcConfig implements WebMvcConfigurer {
	@Bean
	public SessionCheckInterceptor SessionCheckInterceptor() {
		return new SessionCheckInterceptor();
	}

	public void addInterceptors(InterceptorRegistry registry) {
		registry
			.addInterceptor(SessionCheckInterceptor()) // interceptorを追加
			.addPathPatterns("/*") // 全てのパスパターンを対象に追加
			.excludePathPatterns("/index", "/login"); // トップページとログイン画面を対象から除外
	}
}
```
`"sessionInfo"`や`"/index"`などのパラメータは各自の仕様に合わせて変更してください。

# 解説
## SessionCheckInterceptor
`HandlerInterceptor`を`implements`し、`Interceptor`としての機能を実装します。
`preHandle`メソッドは各`Controller`の前処理として呼ばれるので、ここに今回実装したい処理（セッション情報の確認）を書きます。
ちなみに、他にも後処理用のメソッドである`postHandle`や`afterCompletion`が存在しますが、今回は使わないので省略しています。

## WebMvcConfig
`WebMvcConfigurer`を`implements`し、Springの追加設定を記述します。
今回は`SessionCheckInterceptor`を有効にしたいので`addInterceptors`メソッドを実装します。
`InterceptorRegistry#addInterceptor`で`SessionCheckInterceptor`を追加し、続く`addPathPatterns`と`excludePathPatterns`で有効なパス/無効なパスを指定します。
なお、パスを指定しない場合は全てのパスで`SessionCheckInterceptor`が有効になります。
今回の実装ではパスの指定を忘れるとトップページで無限ループが発生するのでご注意ください。

# まとめ
- 各`Controller`に共通の前処理を行いたい場合は`HandlerInterceptor`の実装クラスを作成しよう
- 作成した`Interceptor`クラスは`WevMvcConfigurer`の実装クラスで設定しよう
- `addInterceptors`の有効パスの指定忘れに注意！

# 参考文献
[SpringBoot メソッドの前後で処理する(インターセプター)](https://itsakura.com/sb-handlerinterceptor)
[SpringでInterceptorを使う](https://qiita.com/kyabetsuda/items/78a61bfff859fbc9c63f)
C++ で Solid.JS ぽいことをする遊び

[Solid.JS の仕組みの説明](https://www.solidjs.com/guides/reactivity)を読んでいたら面白そうだったので、C++ で実装してみる遊びです。

スレッド安全とか難しいことは無視しています。

たったこれだけでリアクティブ遊びができるのは面白いですね。

## コード

```c++
#include <string>
#include <iostream>
#include <functional>
#include <optional>
#include <vector>

using Subscriber = std::function<void()>;
static std::optional<Subscriber> subscriberContext;

template <typename T>
class Signal {
	T v;
	std::vector<Subscriber> subscribers;
public:
	Signal(T initial) : v(initial), subscribers() {}
	T get() {
		if (subscriberContext) {
			subscribers.push_back(subscriberContext.value());
		}
		return v;
	}
	void set(T const& v) {
		this->v = v;
		for (auto&& each : subscribers) {
			each();
		}
	}
};

class Effect {
public:
	Effect(std::function<void()> on_mount) {
		subscriberContext = on_mount;
		on_mount();
		subscriberContext = std::nullopt;
	}
};

int main() {
	Signal<std::string> s("Hello");
	Effect e([&]() {
		auto a = s.get();
		std::cout << a << std::endl;
	});
	s.set("World!");
}
```

## 実行結果
```
Hello
World!
```
Dockerイメージを作成し、ECSにコンテナを作成する。

## Dockerイメージを作成し、ECSにコンテナを作成する。
標題通り、Hands-onを実施する。

## Dockerイメージを作成する。
[前回記事](https://qiita.com/kouji0705/items/e3dce07ff4bc4d24da1d)で、ECRにDockerイメージを登録。今回はECRに登録したDockerイメージからコンテナを作成し、ECSにデプロイする。
以下、ざっと記載して行きますが、詳しい説明は[前回記事](https://qiita.com/kouji0705/items/07a28d57b31bb8b012ca)に作成しています。詳しく知りたい人はこの記事を読んでね！！

### クラスターを作成する
![スクリーンショット 2022-06-14 21.26.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/88ca78f2-0607-1f4b-567a-0892a003aa4d.png)
![スクリーンショット 2022-06-14 21.26.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/42e6d726-7826-e35c-20ef-842c4f57af1b.png)
![スクリーンショット 2022-06-14 21.28.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/56a84e87-f14a-3b80-6816-aca75bfe3087.png)

### タスクを定義する
![スクリーンショット 2022-06-14 21.29.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/30bca46b-36aa-c292-6406-a7587251c1f6.png)
![スクリーンショット 2022-06-14 21.31.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/531e6b85-4466-9baf-623e-985c07559efa.png)
![スクリーンショット 2022-06-14 21.32.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/9c69eb56-f5cb-8f09-7e38-b35c999e48c7.png)
![スクリーンショット 2022-06-14 21.32.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/a285bf92-2542-49d6-437b-cc4d7814d35a.png)
![スクリーンショット 2022-06-14 21.35.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/97207489-3cb7-8013-aeae-76a231121af7.png)
![スクリーンショット 2022-06-14 21.36.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/327ea48d-c087-8d0d-9871-a78eb20d7b66.png)

### サービスの作成
![スクリーンショット 2022-06-14 21.37.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/54629813-ad6d-9c0b-c669-24648f4592d8.png)
![スクリーンショット 2022-06-14 21.38.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/7ef0d45c-7421-1c98-44d3-d61e48edddd7.png)
![スクリーンショット 2022-06-14 21.38.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/0bac058f-7c32-2b81-e6a5-4705518ae2f7.png)
![スクリーンショット 2022-06-14 21.38.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/3a23f27d-eef1-4a1d-32d2-eda9c8361d41.png)
![スクリーンショット 2022-06-14 21.39.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/7db272a3-af18-c557-20f6-a147ae8ef58c.png)
![スクリーンショット 2022-06-14 21.39.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/5b76e66f-619d-9ddb-5380-ab1869631c95.png)

### 実行結果の確認
![スクリーンショット 2022-06-14 21.40.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/ba0016bf-d1c2-287e-32ee-caa5441f57be.png)
→ログの中に、「Hello　Docker」が表示されていれば成功です！！！
JS,TSのCollection関連クラスの相互変換方法まとめ


Array, Iterable, Iterator, Generator, AsyncGeneratorのそれぞれのループの回し方と相互変換のコードです。
意外にまとまっている資料がないので、最小限のコード片だけをまとめました。


## ループの回し方

```typescript
// ## Array ##
for(const a of array) {
  ...
}
// または
array.forEach((a, index) => { ... })

// ## Iterable ##
for(const a of iterable) {
  ...
}
// ## Iterator ##
let n = iterator.next()
while(!n.done) {
  const a = n.value
  ...
  n = iterator.next()
}
// ## Generator ##
function* generatorFunc() {
  yield "value"
}
const generator = generatorFunc()
for(const a of generator) {
  ...
}
// ## Async Generator ##
async function* asyncGeneratorFunc() {
  yield "value"
}
const asyncGenerator = asyncGeneratorFunc()
for await(const a of asyncGenerator) {
  ...
}
```

## 相互変換
### Arrayへの変換
```mermaid
flowchart LR

Iterator -->|Code1| Iterable
Iterable-->|"Array.from(iterable)"|Array

Generator -->|"Array.from(generator)"| Array
AsyncGenerator -->|Code2| Promise
Promise["Promise＜Array＞"] -->|await promise| Array

```
### Arrayからの変換
```mermaid
flowchart LR

Array -->|"array.values()"| Iterator
Array -->|array as Iterable| Iterable
Iterable -->|"iterable.iterator()"| Iterator

Array --> |Code3| Generator
Array --> |Code4| AsyncGenerator

```



```typescript:Code1
const iterator = {
  next: () => {
    return {
      value: "iterator",
      done: true
    }
  }
}

const iterable = {
  [Symbol.iterator]: () => iterator
}
```

```typescript: Code2
async function asyncGeneratorToArray<T>(generator: AsyncGenerator<T>): Promise<T[]> {
  const array: T[] = []
  for await(const e of generator) {
    array.push(e)
  }
  return e
}
```
```typescript: Code3
function* arrayToGenerator<T>(array: T[]) {
  for(const e of array) {
    yield e
  }
}
```

```typescript: Code4
async function* arrayToAsyncGenerator<T>(array: T[]) {
  for(const e of array) {
　　　　　　　　// awaitする処理を記述可能
    yield e
  }
}
```

※Arrayは無限長のCollectionを扱えないので、無限長のIterableやGeneratorから変換しようとするとハングアップします。ご注意ください。

## おまけ

Map,SetとArrayの変換

```mermaid
flowchart LR

Map["Map＜K,T＞"] -->|"Array.from(map.entries())"|Array["[K,T][]"]
Array -->|"new Map(array)"|Map
Map -->|"Array.from(map.keys())"|ArrayK["K[]"]
Map -->|"Array.from(map.values())"|ArrayT

Set["Set＜T＞"] -->|"Array.from(set)"|ArrayT["T[]"]
ArrayT-->|"new Set(array)"|Set

```
JetPack5.01DP

# Ubuntu20.04LTS
### パワーモード変更
|現在Mode|変更後Mode|再起動|
|:-:|:-:|:-:|
|10W|Any|有|
|Any|10W|有|
### VNC
HDMIポートにディスプレイを接続していないと利用できない。

### 無線LAN
intel9260が動作した。ただし10秒に1回SW errorでリスタート。個体の問題？
BluetoothはFiletransferのMACへの送信は成功。受信は使い方が不明で未確認。
### Thunderbolt
ThunderBolt3デバイスのORICO Docking Station TB3-S3で認識を確認
データレートは未確認。
TB-S3のカードリーダ、Ethernet、USB2.0HUB、USB3.0HUB、USB3.1HUB、Audio IN/OUTを認識

認識しない時、PowerModeを変えると認識することがあるようだ。（再現性なし。）
# Linux host computer 
* MacBookPro15'2013lateを使用。
* 念のために内蔵SSDをTime Machineでバックアップをとる。
* 外部USBにOSX(今回は10.9Mavericks)をインストール。
* OSXでubuntu用にパーティションを仮に作成し、そこにUbuntu18.04LTSを追加インストール。
* Ubuntuインストーラが入ったメディアから起動
* Ubuntuインストーラ開始。ただしインストーラでBootrecordをいじらせると、OSXすらブートできなくなるので、インストーラをターミナルから
```
sudo ubiquity -b
```
で起動する。
### ubuntuインストールは:
https://kanzmemo.cocolog-nifty.com/blog/2020/08/post-87edd7.html
### を参考にした。
ただし、OSXがインストールしてあるので、diskutilを使用したパーティションの作成や、GRUBに関する設定は必要ない。
インストーラの起動手順と、インストールの種類で、「それ以外」を指定する部分を確認するだけ。

### ブートセレクタの 'rEFInd'をインストール。
* OSXでブート
* refind-installを実行する。 このコマンドでは、繋がっている全てのディスクにインストールされるようだが、内蔵ディスクには不要なので、EFIパーティションをマウントし、EFI/refindディレクトリを削除すれば、無効化できる。
* USBディスクのrEFIndを呼び出す時は、ブートサウンドが鳴っているうちにoptionキーを押して、USBディスクのEFIを指定する。
## まとめ
バーチャル環境でなく、ネイティブなUbuntuを使用するので、USBポートにEthernetプロトコルを流す低速で不安定な問題はない。とは言っても、USB2.0ではなく、USB3.0を使用すべきだが。→lsusb -t で確認すると12mbpsのフルスピード。USBのOTG規格の問題。Ethernetを使用すべき。
XavierにLinuxを入れた後は、Ethernetも利用できるようになるので、sdkなどはEthernet経由でインストールするのが良い。IPアドレスはUSB経由のシリアルコンソールで確認する。たしかUbuntuのデバイスファイルは/dev/ttyACM?でポートスピードはなんでも良い。
外部ディスクを使用するメリットは大きい。Windowsの起動も確認したい。2013lateのThunderBolt経由で起動できれば最高だが、TB3-S3経由では認識しなかった。
unibody MACmini2011のメモリーを8GBにした場合も成功したが、インターフェースがUSB2.0では遅い。→lsusb -t で確認すると12mbpsのフルスピード。USBのOTG規格の問題。Ethernetを使用すべき。
テスト71

```vb
'Excelファイルを開かずにシートの項目チェックをする方法
Sub ExcelADO4()
 
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim strSQL As String
Dim str As String

str = "C:\Users\User\Desktop\VBA練習\test.xlsx"

'ポイントはHDRをNOにすること...〆('ω' )ｶｷｶｷ

Set cn = New ADODB.Connection
cn.Open ("Provider=Microsoft.ACE.OLEDB.12.0;" & _
                "Data Source=" & str & ";" & _
                "Extended Properties=""Excel 12.0;HDR=NO""")

strSQL = "SELECT * " & _
               "From [テスト3$A1:H1];"

With rs
    .Source = strSQL
    .ActiveConnection = cn
    .LockType = adOpenDynamic
    .Open
End With

If rs.Fields(0).Value <> "項目A" Then

    MsgBox "A列の項目が項目Aではありません。確認をお願いします。", vbCritical, "項目相違"

ElseIf rs.Fields(1).Value <> "項目B" Then

    MsgBox "B列の項目が項目Bではありません。確認をお願いします。", vbCritical, "項目相違"

ElseIf rs.Fields(2).Value <> "項目C" Then

    MsgBox "C列の項目が項目Cではありません。確認をお願いします。", vbCritical, "項目相違"

ElseIf rs.Fields(3).Value <> "項目D" Then

    MsgBox "D列の項目が項目Dではありません。確認をお願いします。", vbCritical, "項目相違"

ElseIf rs.Fields(4).Value <> "項目E" Then

    MsgBox "E列の項目が項目Eではありません。確認をお願いします。", vbCritical, "項目相違"

ElseIf rs.Fields(5).Value <> "項目F" Then

    MsgBox "F列の項目が項目Fではありません。確認をお願いします。", vbCritical, "項目相違"

ElseIf rs.Fields(6).Value <> "項目G" Then

    MsgBox "G列の項目が項目Gではありません。確認をお願いします。", vbCritical, "項目相違"
    
ElseIf rs.Fields(7).Value <> "項目H" Then

    MsgBox "H列の項目が項目Hではありません。確認をお願いします。", vbCritical, "項目相違"
    
End If

rs.Close
Set rs = Nothing

cn.Close
Set cn = Nothing
    
End Sub
【GO】外部ライブラリを自力で、モック化してみる

### 今回は、テストコードを簡単にするためにTestifyを使いますが、Testifyの機能で、モック化するわけではないので、Testifyを導入しなくてもモック化の実現が可能です。

# 今回利用する技術
- 言語:**GO**
- モック化する外部ライブラリ:**Agouti** Seleniumみたいなライブラリ
- テストライブラリ:**Testify** assertなどの検証関数を利用するため

# 実況開始
## まず初めは、GOのプロジェクトの初期化してみる
```
go mod init go-testify-mock
```
正直、go modのことをが詳しくないから、`npm init -y`と同じ感覚として、やっていいのかわからない....

## Testifyのインストール
```
go get github.com/stretchr/testify
```

## Agoutiのインストール
```
go get github.com/sclevine/agouti
```
~~ライブラリの公式が、メンテナンスしないことを発表して、3年立つらしい...~~

## モック化するために、外部ライブラリの構造体のクロージャーのラッパー関数を作成する
GOの標準のテスト機能やTestifyでは、Jestの`fn()`や`spyOn()`のように、外部ライブラリを直接モック化できない。
そのため、モック化するために、構造体のプロパティにラッパー関数として、クロージャーを定義し、関数の代入をするための初期化関数を作成する。
~~めんどくさい、直接モック化させてくよぉ......。~~
~~ていうか、これ略したら、DIだよな......。~~
```go
import (
	"github.com/sclevine/agouti"
)

type Scraping struct {
	// スクレイピングするページのタイトルの取得
	GetPageTitle func(url string) (string, error)
}

/*
初期化処理
*/
func (scraping *Scraping) Init() {
	// エラー処理は、めんどくさいので飛ばします。
	scraping.GetPageTitle = func(url string) (string, error) {
		options := agouti.ChromeOptions(
			"args", []string{
				"--headless",
				"--disable-gpu",
			})

		driver := agouti.ChromeDriver(options)
		defer driver.Stop()
		driver.Start()

		page, _ := driver.NewPage()
		page.Navigate(url)

		return page.Title()
	}
}

```

## テストコードを作成する
`mockInit()`で、`GetPageTitle`の中身を書き換えます。
~~やり方が、脳筋に思えてしまう...~~

```go
package test

import (
	"go-testify-mock/src/modules"
	"testing"

	"github.com/stretchr/testify/assert"
)

func mockInit() *modules.Scraping {
	scraping := &modules.Scraping{}

	scraping.GetPageTitle = func(url string) (string, error) {
		return "title", nil
	}

	return scraping
}

func TestGetPageTitle(t *testing.T) {
	title, error := mockInit().GetPageTitle("url")

	if assert.NoError(t, error) {
		assert.Equal(t, "title", title)
	}
}

```
C言語のポインタの恐怖と戦ってみた

学習３週間目にてポインタと対峙。
C言語のポインタが難しいとは聞いていましたが、実際最初に見たときはなんのこっちゃと思いました。
悔しいので、ポインタ＆配列＆値渡しを使用することで、理解できた気になりました。
忘れないようにしつこいくらいコメントも記述。

配列をポインタで渡したいよ！という方の参考になれば。
```pointa_hairetu.c

#include <stdio.h>
#include <stdlib.h>
#define A 2//配列の数


int arr_change(int p)
{
	//いろいろいじりましたが、毎回*でポインタの定義が関数ごとに必要なようです。
	//上記の理由のため、毎回アドレスを渡す必要があります。
	int *p2;//*をつけると整数型ポインタ変数（ポインタの数値の中身）
	p2 = p;////pのアドレスをp2に代入（アドレス渡しの場合は*はいらない）
	printf("配列ｐ２のアドレスは%p",p2);
	printf("\n");

	for(int i = 0 ; i < A ; i++){
		printf("arr_change関数の*p2:[%d]の中身は%d\n",i, *(p2 + i));
			}
	printf("\n");
	for(int i = 0 ; i < A ; i++){
		*(p2 + i)=*(p2 + i)*2;//配列の中身を２倍する
			printf("*p2:[%d]を２倍した中身は%d\n",i, *(p2 + i));
				}
	printf("\n");

}
int main(void){
	int x[A]; //整数型配列
	int *p; //*をつけると整数型ポインタ変数（ポインタの数値の中身）
	//配列に数値を代入
	x[0] = 1;
	x[1] = 100;
	p = x; //xのアドレスをpに代入（アドレス渡しの場合は*はいらない）

	printf("「p」のアドレスは「 %p 」です\n",p);   //「p」の中身 = 「x」のアドレスを表示
	printf("「x」のアドレスは「 %p 」です\n",&x);  //「x」のアドレスを表示
	printf("「x」の中身は「 %d,%d 」です\n",x[0],x[1]);   //「x」の中身を表示(10)
	printf("\nポインタの中身の取り出し方\nfor(i = 0 ; i < a ; i++)\n{ *(p + i)}\n");
	for(int i = 0 ; i < A ; i++){
		printf("main関数の*p:[%d]の中身は%d\n",i, *(p + i));
		}
	printf("\n");
	arr_change(p);//配列の引数はポインタを使う

	for(int i = 0 ; i < A ; i++){
			printf("arr_change関数使用後のmain関数の*p:[%d]の中身は%d\n",i, *(p + i));
			}
	printf("\n");
	printf("この結果をまとめるとなんとarr_changeにて結果をreturnせずとも、\narr_change関数にてポインタ内の数値を直接いじるため\nmain関数の配列も２倍されるのです!\n");

return 0;

}

```
C言語初心者でも自作関数が理解できるよう頑張ってみた

C言語を学び始めて２週間ほどで、C言語の自作関数を理解すべく、しつこいばかりに説明を添えたコードです。
自作関数の引数と戻り値の流れが大まかにわかるかと思います。
自作関数に壁を感じる人も多いと思うので参考になれば幸いです。
じゃんけんなのでゲーム感覚でやってみてください。
```jisaku_kansu.c
#include<stdio.h>//入出力
#include<stdlib.h>//一般
#include<time.h>//時間（ランダム初期化用）

//関数は基本main関数の上に書く
int randam(void)
//関数rand（void)
//main関数から呼び出されたら初めて実行される
//※voidは引数なしの関数です
//そのためmain関数での呼び出しに引数を使うことができません
{
	srand((unsigned int)time(NULL));
	//乱数初期化

	int rnum;//関数内での変数宣言

	//乱数0-2を変数に代入
	rnum = rand()%3;

	//main関数にrnumの数値を返す。戻り値という
	printf("②randam関数を呼び出しました\n③randam関数の引数はvoidのためありません。\n④rnumの中身：%dを戻り値としてmain関数のxに返します\n",rnum);
	return rnum;
}

int judge(int xx)
//main関数から呼び出されたら初めて実行される。
//引数xをint　xxに代入して実行される。
//（int xx）関数内であつかう仮の関数定義。
//特に定めがなくmain関数との兼ね合いもない。
//同じ名前も使えるが、わかりづらくなるので変えたほうが無難
{
	if(xx==0)
		printf("⑥judgeに引数（ひきすう）として代入したxがxxに代入されました。\nxxの中身：%d\n結果は：グー",xx);
	else if(xx==1)
		printf("⑥judgeに引数（ひきすう）として代入した数字xがxxに代入されました。\nxxの中身：%d\n結果は：チョキ",xx);
	else
		printf("⑥judgeに引数（ひきすう）として代入した数字xがxxに代入されました。\nxxの中身：%d\n結果は：パー",xx);

}
int main(void)
//main関数に引数を設定することはできないため常にvoidとなっています。
{
	int x;
	printf("①mainが実行されます\n");
	x = randam();//randam関数呼び出し。void引数なし。戻り値をxに代入
	printf("⑤randamからの戻り値は%dです。judgeに代入します\n",x);
	judge(x);//judge関数を呼び出し。引数ｘ（randamの数値が入っていいるもの）


	getchar();
	getchar();
	return 0;
}
```
【C】初めてのC言語（9. 関数のプロトタイプ宣言）

# はじめに

* これまでの学習に続いて、今回は2次元配列について学んだ結果をまとめてみました。
  * [【C】初めてのC言語（1. Hello world）](https://qiita.com/nkojima/items/518c1a4ec06861d74b3c)
  * [【C】初めてのC言語（2. 四則演算とフォーマット指定子）](https://qiita.com/nkojima/items/36ea93b7296ce9e8a55b)
  * [【C】初めてのC言語（3. ポインタ）](https://qiita.com/nkojima/items/825bb9f931016faccd68)
  * [【C】初めてのC言語（4. 構造体）](https://qiita.com/nkojima/items/40a1101f2f6f5800bc2d)
  * [【C】初めてのC言語（5. 関数）](https://qiita.com/nkojima/items/1fa3317c04f25fe24276)
  * [【C】初めてのC言語（6. 配列）](https://qiita.com/nkojima/items/eda3ff6675b47e7ab5f0)
  * [【C】初めてのC言語（7. for文）](https://qiita.com/nkojima/items/a5659098f7e5275a96ec)
  * [【C】初めてのC言語（8. 2次元配列）](https://qiita.com/nkojima/items/f5eb25a4f8d1725faa89)

# 学習環境

* 今回は[paiza.io](https://paiza.io/ja/projects/new)のC言語のエディタを使いました。

# これまでに感じていた疑問

* [【C】初めてのC言語（5. 関数）](https://qiita.com/nkojima/items/1fa3317c04f25fe24276)で学習していた時、以下のようにmain関数の下にtestという名前の関数を作って、main関数内からtest関数を呼び出すとコンパイルエラーが発生しました。
  * そこで試しにtest関数をmain関数よりも上に持ってくると、無事にコンパイルが通りました。
* このように「ある関数を呼び出す時は、その関数を定義した箇所より後ろでないとコンパイルが通らない」というのはさすがに変だと思いましたが、この時は解決策が分からずに「main関数を先頭に持ってくる」という対応で済ませてしまいました。

```c:main.c
#include <stdio.h>

int main(void){
    test();
    return 0;
}

void test(void) {
    printf("This is test.");
}
```

```text:コンパイルエラーの内容
Main.c:4:5: warning: implicit declaration of function 'test' is invalid in C99 [-Wimplicit-function-declaration]
    test();
    ^
Main.c:8:6: error: conflicting types for 'test'
void test(void) {
     ^
Main.c:4:5: note: previous implicit declaration is here
    test();
    ^
1 warning and 1 error generated.
```

# プロトタイプ宣言

* 上記のように関数を定義する順番を縛られるのではなく、自由に関数を定義するには「プロトタイプ宣言」という構文を利用します。

```text:プロトタイプ宣言の構文
{戻り値のデータ型} 関数名({引数1}, {引数2}, ...);
```

# サンプルコード

* 上記のコンパイルエラーとなったコードと似ていますが、プロトタイプ宣言をすることで正常にコンパイルすることができました。
  * `void test(void);`の箇所がプロトタイプ宣言です。

```c:main.c
#include <stdio.h>

// 関数のプロトタイプ宣言
void test(void);

int main(void){
    test();
    return 0;
}

// test関数の呼び出し箇所より後ろでtest関数を宣言できる！
void test(void) {
    printf("This is test.");
}
```

# 参考URL

* [関数のプロトタイプ宣言 - C言語入門](https://webkaru.net/clang/function-declaration-prototypes/)

# 参考資料

* [スッキリわかるC言語入門 第2版](https://www.amazon.co.jp/dp/4295012785/)
  * p.272～p.273
初心者がC言語の構造体の使い方を考えてみた

C言語学習１か月程度で、構造体理解をするため作成したコードです。
変数の数だけscanfで構造体を登録できるため、何かに使えそうな気がしますがひらめきません。
始めたばかりで構造体に悩まれている方の参考になれば。

```struct.c
#include <stdio.h>
#include <string.h>

struct schedule {//構造体の定義（一番上）
    char name[100];//文字列は配列にすること
	int year;        /* 年 */
    int month;       /* 月 */
    int day;         /* 日 */
};

//配列の参照はvoid
//配列の値渡しはstruct+構造体名＋*（ポインタ）定義名
void arr_change(struct schedule *schedules)
//構造体をプリント
{
	for (int i = 0; i < 2; i = i + 1)
	    {
	        printf("関数上は\n%s/%04d/%02d/%02d\n"
	        		,schedules[i].name
					,schedules[i].year
					,schedules[i].month
					,schedules[i].day);
	        printf("\n");
	    }
}

int main() {
	//構造体の定義はstruct+構造体名＋任意の文字（配列なら[n])
    int x =2;//今回登録する数
    int i;
	struct schedule schedules[x];

    for(i=0;i<x;i++){
	   printf("名前を入力："); scanf("%s",schedules[i].name );
	   printf("年を入力："); scanf("%d",&schedules[i].year );
	   printf("月を入力："); scanf("%d",&schedules[i].month );
	   printf("日を入力："); scanf("%d",&schedules[i].day );
	   printf("\n");
    }
   arr_change(&schedules);
   getchar();
   getchar();
   return 0;
}
```
MATLABでCNN

# モデル
学習モデルは、MATLABのDeep Learning Toolboxのアドオンから入手できるものを用いる。
[事前学習済みの深層ニューラル ネットワーク](https://jp.mathworks.com/help/deeplearning/ug/pretrained-convolutional-neural-networks.html)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579192/b35187b1-c480-8d80-f4df-a380d61aa894.png)


今回は、VGG16とResNet50を例に説明する。

# VGG16
```matlab:VGG16.m
% --- 学習データと検証データの読み込み
% フォルダ名がクラスになる。
imds = imageDatastore('./pictures', 'IncludeSubfolders', true, 'LabelSource', 'foldernames');
% 学習用と検証用にデータを分割
p = 0.6;    % 学習用に使うデータの割合
[trainData, testData] = splitEachLabel(imds, p, 'randomized');

% ---- 事前学習済みのネットワークの読み込み。
% アドオンになければ、ダウンロードリンクが表示される。
net = vgg16;

% このままでは編集できないので、コピーのような操作を行う。
lgraph = layerGraph(net);

% ---出力数が合わないので、最後の出力層を変更する。
% 最後から二番目 (fullyConnectedLayer)
LayerName = lgraph.Layers(end-2).Name;
newLayer = fullyConnectedLayer(出力数, 'Name', LayerName);
lgraph = replaceLayer(lgraph, LayerName, newLayer);
% 最後 (classificationLayer)
LayerName = lgraph.Layers(end).Name;
newLayer = classificationLayer('Name', LayerName);
lgraph = replaceLayer(lgraph, LayerName, newLayer);

% ---ネットワークのインプットサイズに合わせて画像をリサイズ
% trainDataとtestDataの読み込みは割愛。
inputSize = lgraph.Layers(1).InputSize(1:2);
augTrainData = augmentedImageDatastore(inputSize, trainData);
augTestData = augmentedImageDatastore(inputSize, testData);

% ---学習オプション
InitialLearnRate = 1e-4;
MaxEpochs = 6;
MinBatchSize = 10;
ValidFreq = floor(length(augimdsTrain.Files) / MinBatchSize);    % 1エポックに1回の検証
options = trainingOptions('sgdm', ...
    'MiniBatchSize', MinBatchSize, ...
    'MaxEpochs', MaxEpochs, ...
    'InitialLearnRate', InitialLearnRate, ...
    'Shuffle', 'every-epoch', ...
    'ValidationData', augTrainData, ...
    'ValidationFrequency', ValidFreq, ...
    'Verbose', false, ...
    'Plots', 'training-progress');

% ---学習
% training-progressウィンドが開く。時間がかかるので待つ。
netTransfer = trainNetwork(augimdsTrain, lgraph, options);
% 検証データで確認
[predLabels, scores] = classify(netTransfer, augTestData);

% ---モデル精度の検証
testDataLabels = testData.Labels;
DataCount = numel(testData.Labels);
CorrectCount = nnz(testDataLabels == predLabels);
ErrorCount = DataCount - CorrectCount;
Accuracy = CorrectCount / DataCount;
ErrorRatio = ErrorCount / DataCount;
```

# ResNet50
VGG16とほぼ同じだが、ResNetはConnections情報が必要になる。
VGG16のlgraphと比較すると、構造がひとつ増えているのがわかるので、確認すること。
参考先だとtrainNetworkにlayerのみ入れているが、ResNetだとlayerのみでは動かないので、layerGraph構造で入れる必要がある。

```matlab:ResNet50.m
% VGG16と異なる箇所のみ記述
% ---- 事前学習済みのネットワークの読み込み。
% アドオンになければ、ダウンロードリンクが表示される。
net = resnet50;

% このままでは編集できないので、コピーのような操作を行う。
lgraph = layerGraph(net);

% VGG16のlgraphと比較すると、構造がひとつ増えているのがわかるので、確認すること。
```

# trainingOption
```matlab:opt.m
options = trainingOptions("adam", ....
    "InitialLearnRate", 0.0001, ....
    "Plots", "training-progress", ....
    "ValidationData", val)
% 最急降下法の種類はadamが一般的。
% InitialLearnRateの初期値は0.001。これだと大きいことが多いので、1/10や1/100にしてみる。
% 乱数制御を制御したい場合は、rng()を実行する。
```

# 参考
[matlabで始めるディープラーニング 画像分類(1)](https://qiita.com/kaito_111/items/ea9241fcb8c784429df1)
[matlabで始めるディープラーニング 画像分類(2)](https://qiita.com/kaito_111/items/6ae28ee1b137183f8696)
【Ruby on Rails】APIのエンドポイントにケバブケース（kebab-case）を設定する

# この記事について
RailsでAPIを作成する際、エンドポイントを設定しますが、 `/api/v1/sample-apis` のように複数単語（「sample apis」）の部分はkebab-caseを使用することが多いかと思います。
しかし、Rubyの命名規則的には、ファイル名はsnake_caseかと思いますので、snake_case→kebab-caseの変換を行う必要があります。
この記事では、Railsのエンドポイントでsnake_case→kebab-caseの変換方法を記録として残したいと思います。

# コントローラクラスの作成
エンドポイント `api/v1/sample-apis` に対するコントローラクラスを作成します。
RailsではControllerクラスのフィル名をsnake_caseで記述し、 `<エンドポイントのsnake_case>_controller` となるため、 `sample_apis_controller.rb` としております。
`api/v1/` の部分はmoduleを使用することとします。

```rb:app/controllers/api/v1/sample_apis_controller.rb
module Api
    module V1
        class SampleApisController < ActionController::API
            def index
                render json: {
                    data: { message: 'this is sample apis action' }
                }
            end
        end
    end
end
```

# `config/routes.rb` でのパス設定
`resources` にはコントローラクラスのファイル名（から `_controller.rb` を除いた部分）、`:path =>` にはエンドポイントとして設定したい文字列を記述します。

```config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :sample_apis, :path => 'sample-apis'
    end
  end
end
```

# 動作確認
`rails s` でRailsサーバを起動してブラウザで `/api/v1/sample-apis` にアクセスしてみます。
（画像では3100ポートを起動していますが、3000ポートを起動している場合は `http://localhost:3000/api/v1/sample-apis` にアクセスしてください。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234275/06fc0d95-3659-dff8-9ab9-1ea00c485462.png)

# 参考
[Routes with Dash `-` Instead of Underscore `_` in Ruby on Rails](https://stackoverflow.com/questions/5334465/routes-with-dash-instead-of-underscore-in-ruby-on-rails)
「ssh-keygen -R」を間違えて実行しちゃった！

そんなことある？って言わないで、分かるから。

まあターミナルで↑キーを連打してるとたまにこうなる。

`ssh-keygen -R` は　実行するとメッセージが出る。

翻訳すると、known_hosts.old に古いやつ書いといたで～。となるので、私の場合は
`User/.ssh`フォルダに移動。

known_hostsファイルにknown_hosts.oldの該当行をコピペ。

これで`ssh-keygen -R`を実行する前に戻る。
TypeScript メモ#1

# TypeScriptとは
ざっくり言うとJavascriptの機能を拡張したもの
# 個人的に思う特長3つ
# ①静的型付け
 コードの可読性が高まりバグの発見が早くなることにより、修正コストの軽減につながる。コードレビュー等の負荷が大きい大規模開発に向いている。
# ②フロントエンドとバックエンドで採用できる
 TypeScriptと同じく、Javascriptと類似したNode.jsのエコシステムに乗っ取ることができる。フロントとバックの両方でTypeScriptを採用できる。
# ③サーバーレス
 AWSのLambdaなど、JavaScriptをサポートしているクラウドサービスを用いるとTypeScriptでサーバーレスな開発ができる。
【C】初めてのC言語（8. 2次元配列）

# はじめに

* これまでの学習に続いて、今回は2次元配列について学んだ結果をまとめてみました。
  * [【C】初めてのC言語（1. Hello world）](https://qiita.com/nkojima/items/518c1a4ec06861d74b3c)
  * [【C】初めてのC言語（2. 四則演算とフォーマット指定子）](https://qiita.com/nkojima/items/36ea93b7296ce9e8a55b)
  * [【C】初めてのC言語（3. ポインタ）](https://qiita.com/nkojima/items/825bb9f931016faccd68)
  * [【C】初めてのC言語（4. 構造体）](https://qiita.com/nkojima/items/40a1101f2f6f5800bc2d)
  * [【C】初めてのC言語（5. 関数）](https://qiita.com/nkojima/items/1fa3317c04f25fe24276)
  * [【C】初めてのC言語（6. 配列）](https://qiita.com/nkojima/items/eda3ff6675b47e7ab5f0)
  * [【C】初めてのC言語（7. for文）](https://qiita.com/nkojima/items/a5659098f7e5275a96ec)

# 学習環境

* 今回は[paiza.io](https://paiza.io/ja/projects/new)のC言語のエディタを使いました。


# サンプルコード

## 基本的な2次元配列の使い方

* 2次元配列は`データ型名`と`配列名`に続いて、`行数`と`列数`を指定して宣言します。
* 普通の配列（1次元の配列）では「配列全体のデータサイズ÷データ型のデータサイズ」で配列の長さを求めましたが、2次元配列では行数と列数を求める方法が分からなかったので微妙なコードになりました。
  * ~~どなたか良い方法をご存じでしたら教えてください。~~
  * 2次元配列の行数と列数を求める方法は、以下のコメント欄に2通りの方法が書かれています。
  * @angel_p_57 さん、 @SaitoAtsushi さん、ありがとうございました。

```main.c
#include <stdio.h>
int main(void){
    // 3行2列の2次元配列の宣言
    int ary2D [3][2];
    
    ary2D[0][0] = 0;
    ary2D[0][1] = 1;
    ary2D[1][0] = 1;
    ary2D[1][1] = 2;
    ary2D[2][0] = 3;
    ary2D[2][1] = 5;
    
    int rowCount = 3;   // 行数
    int colCount = 2;   // 列数
    
    for (int i=0; i<rowCount; i++) {
        for (int j=0; j<colCount; j++) {
            printf("%d行目の%d列目の値は%dです。\n", i+1, j+1, ary2D[i][j]);    
        }
    }
}
```

```text:実行結果
1行目の1列目の値は0です。
1行目の2列目の値は1です。
2行目の1列目の値は1です。
2行目の2列目の値は2です。
3行目の1列目の値は3です。
3行目の2列目の値は5です。
```

## 配列の宣言と同時に初期化する

* 2次元配列を初期化するには、中カッコ（ブレース）で囲まれた中に行単位で初期値を設定します。

```main.c
#include <stdio.h>
int main(void){
    // 3行2列の2次元配列の初期化
    int ary2D [3][2] = {
        {0, 1},
        {1, 2},
        {3, 5}
    };
    
    int rowCount = 3;   // 行数
    int colCount = 2;   // 列数
    
    for (int i=0; i<rowCount; i++) {
        for (int j=0; j<colCount; j++) {
            printf("%d行目の%d列目の値は%dです。\n", i+1, j+1, ary2D[i][j]);    
        }
    }
}
```

# 参考URL

* [多次元配列 - Let'sプログラミング](https://www.javadrive.jp/cstart/array/index5.html)

# 参考資料

* [スッキリわかるC言語入門 第2版](https://www.amazon.co.jp/dp/4295012785/)
  * p.237～p.240
消したはずのscssファイルがブラウザで反映され続けていた話

## はじめに

scssファイルを編集したのに、ブラウザ（Chrome）で反映されない、ということがありました。
初めて遭遇した現象だったので、勉強になりました。

## 起きたこと

scssファイルを編集したのに、ブラウザ（Chrome）で反映されなかった。
scssを削除したりしても、効き続けてしまっていました。

対処法をググって以下を試したのですが、反映されず、、、

- webpack serverの再起動
- rails serverの再起動
- ブラウザキャッシュクリア
- PC再起動

結論としては、
`tmp/cache`ディレクトリを削除 でとりあえず解決しました。
過去にコンパイルした古いファイルが残ってしまっていた、ということだったのでしょうか。

## 最後に

なぜこうなったのかきちんと理解はできていないんですが、とりあえずの対処法として,自分用のメモも兼ねて記載しておきます。
顧客から「〇〇エラー」のエラーメッセージってどの段階でリリースしたものですか？と聞かれたときの調査用コマンド（git log）


# 顧客から「〇〇エラー」のエラーメッセージってどの段階でリリースしたものですか？と聞かれたときの調査用コマンド（git log）

# 結論（どうすればよいか）

```src
git log -S "〇〇エラーメッセージ" 
```
あとはハッシュから逆算してリモートのリポジトリのグラフなりコミットログを追ってどうぞ。

~~git log -S で調べると幸せになれるかもしれませんが、
私はここに書くのが面倒なので、各自調べてどうぞ~~
コミットメッセージというよりは、変更内容に引っかかったものが表示されるっぽい

# 余談
①そもそも、ちゃんとリリースノート的なものに
　・〇〇のエラーメッセージが追加されました
（エラー発生の条件）
※リリースノートの参考はみんな大好きVSCodeのリリースノートを参考にしてみてはいかがだろうか（戒め）

みたいにすればいいのでは ~~（顧客が結局丸投げして聞いてくるのはナシで）~~

~~②そもそも、エラーメッセージのマッピング表をちゃんと作るべきでは~~
【Laravel】特定の条件のときのみRequestクラスのバリデーションをする方法3つ


## 環境
Laravel v9.5.1 (PHP v8.1.3)

## 特定の条件のときのみRequestクラスのバリデーションをしたいとき
- FormRequestの```required_if```を使う方法
- バリデーションを適応しない場合は条件文で除外する方法
- バリデーションはnullableにしてコントローラーでエラーを返す方法

今回は、「ユーザーが最初の登録ユーザーだったときのみ」という特定の条件でバリデーションしたいとする。
(いい例ではないけどすみません！)


### ```required_if```を使う方法
```genre```が```Fantasy```のときに```title```のバリデーションをする。
ただこれは一緒に渡されたパラメータの値を見て、特定の条件だったらバリデーションするものなので今回は使えない。

```php
class CreateBookRequest extends FormRequest
{
  public function rules()
  {
    $rules = [
      'title' => 'required_if: genre, Fantasy | string | max:20'
      'genre' => 'required | integer',
    ];

    return $rules;
  }
}
```

https://readouble.com/laravel/9.x/ja/validation.html


### バリデーションを適応しない場合は条件文で除外する方法
```unset```でバリデーションを除外している。
できないことはないけど、CreateBookRequestクラスに条件文を書くのはあまりきれいじゃない気がする。

```php
class CreateBookRequest extends FormRequest
{
  public function rules()
  {
    $rules = [
      'title' => 'required | string | max:20',
    ];

    if (最初の登録ユーザーだったらの条件文) {
      unset($rules['title']);
    }
    return $rules;
  }
}
```

https://blog.websandbag.com/entry/2019/07/24/002457

### バリデーションはnullableにしてコントローラーでエラーを返す方法
Requestクラスでは```nullable```にして、コントローラーの条件文の中でエラーを返す。

役割が明確に分かれていてきれいな気がする！

```php
class CreatePostRequest extends FormRequest
{
  public function rules()
  {
   return [
      'name' => 'nullable | string | max:30',
    ];
  }
}
```

```php
class BookController extends Controller
{
  ...
  public function store()
  {
    ...
    if (最初の登録ユーザーだったらの条件文) {
      throw new ValidationException();
    }
    ...
  }
}
```
基本情報（CPUとコンピュータの5大装置）

コンピュータのハードウェアは大きく分けると下記の５つの装置で構成されています。

**1. 出力装置**
紙や画面などに処理の結果を出力（印刷したり表示したり）する
**2. 入力装置**
文字や位置情報、画像や音声など様々なデータをコンピュータに入力する装置
**3. 制御装置**
各装置を制御する。指揮者的な役割。
**4. 演算装置**
命令に従って演算を行う。
**5. 記憶装置**
動作に必要な情報を保持したり、ファイルとして保存したりする。

上記の5つの装置を総称して、**コンピュータの５大装置**と呼びます。

|  装置名称        |  代表的な機器とその役割   |
| :---: |  :--- |
|   制御装置（中央処理装置）  |  CPUはコンピュータの中枢部分で、制御と演算を行う装置です。制御装置ではプログラムの命令を解釈して、コンピュータ全体の動作を制御します。  | 
|   演算装置（中央処理装置）  |  CPUはコンピュータの中枢部分で、制御と演算を行う装置です。演算装置では四則演算やデータの演算処理を行います。  | 
|   記憶装置（主記憶装置）  |  動作するために必要なプログラムやデータを一時的に記憶する装置です。代表的な例としてはメモリがあります。コンピュータの電源を切ると、その内容は消えてしまいます。  | 
|   記憶装置（補助記憶装置）  |  プログラムやデータを長期に渡り記憶する装置です。長期保存を前提としているので、主記憶装置のようにコンピュータの電源を切ることで内容が破棄されたりするようなことはありません。代表的な例としてハードディスクのほか、CD-ROM、DVD-ROMのような光メディア等があります。| 
|   入力装置  |  コンピュータにデータを入力するための装置です。代表的な例として　①キーボード　②マウス　③スキャナ　があります。  | 
|   出力装置  |  コンピュータのデータを出力するための装置です。代表的な例として ①ディスプレイ　②プリンタがあります。  |

装置間の制御やデータの流れは下記のようになります。

```
プログラム読み込み　　　→　　　③制御装置　　　
 ↑    　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　↓　　　　　　　　　　　　　　　　　　　　　　　　　
 ↑ 　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　④演算装置　　　　　　　　　　　　　　　　　　　　
 ↑    　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　↕　　データ演算　　　　　　　　　
 ↑ 　　←　　　　　←　　　←　　　　←　②記憶装置（主記憶装置⇄補助記憶装置 // データの読み書き)
　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　↑　　　　　　　　　　　　　　　　　↓
　　　　　　　　　　　　　　　　　　　　　　　　　　　　　入力データ　　　出力データ
　　　　　　　　　　　　　　　　　　　　　　　　　　　　①入力装置　　　　⑤出力装置
```

## まとめ
ほとんどのアプリケーションは上記の流れをしているはずなので
構造を理解しながら設計することは大事ですね。