OmniAICreator/Qiita-1.07M huggingface dataset:
TypeScriptでデザインパターン[3.TemplateMethod]

# はじめに
Typescriptでデザインパターンをやっていくシリーズ（にする予定です）

結城 浩さんの「Java言語で学ぶデザインパターン入門」をTSに置き換えてTS力をあげていこうという趣旨です。

https://www.amazon.co.jp/%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82%E7%89%88Java%E8%A8%80%E8%AA%9E%E3%81%A7%E5%AD%A6%E3%81%B6%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E5%85%A5%E9%96%80-%E7%B5%90%E5%9F%8E-%E6%B5%A9/dp/4797327030

前回はこちら
- [TypeScriptでデザインパターン[1.Iterator]](https://qiita.com/zackey2/items/2fa106a8d68455b34c41)
- [TypeScriptでデザインパターン[2.Adapter]](https://qiita.com/zackey2/items/6c2292b88ff9f0cb2c91)

# メモ
- TSにはfinalがない
    - 継承させないようにするにはどうしたらいいんだろう？
- TSにはcharがない
    - JSにはcharしかないからしょうがない。富豪的だなあ。

# Code

```ts
namespace TemplateMethod {
  abstract class AbstractDisplay {
    abstract open(): void;
    abstract print(): void;
    abstract close(): void;

    // TSってfinal使えないんだ・・・
    display() {
      this.open();
      for (let i = 0; i < 5; i++) {
        this.print();
      }
      this.close();
    }
  }

  class CharDisplay extends AbstractDisplay {
    private c: string;

    // TSにはcharはないらしい。独自定義する必要がある
    constructor(c: string) {
      super();

      this.c = c;
    }

    open(): void {
      console.log("<<");
    }
    print(): void {
      console.log(this.c);
    }
    close(): void {
      console.log(">>");
    }
  }

  class StringDisplay extends AbstractDisplay {
    private str: string;

    constructor(str: string) {
      super();

      this.str = str;
    }

    open(): void {
      this.printLine();
    }
    print(): void {
      console.log(`| ${this.str} |`);
    }
    close(): void {
      this.printLine();
    }

    private printLine() {
      let line = "+";
      for (let i = 0; i < this.str.length; i++) {
        line += "-";
      }
      line += "+";
      console.log(line);
    }
  }

  class Main {
    main() {
      const displays: AbstractDisplay[] = [];
      displays.push(new CharDisplay("c"));
      displays.push(new StringDisplay("hello"));
      displays.push(new StringDisplay("こんにちは"));

      for (const display in displays) {
        displays[display].display();
      }
    }
  }

  new Main().main();
}

```
Rustのdotenvの使い方まとめ

# 概要
Rustで環境変数を扱う場合、一般的なライブラリはstd::envです。
しかし、

* 環境（開発/検証/本番）によって使い分けたい
* 環境変数が沢山あって手動で設定するのが大変

といったことがあります。

dotenvを使うと、.envというファイルで環境変数を管理することができます。
（dotenv自体はRuby発のOSSでNode.jsやRustに移植されています）

使い方を紹介していきます。

# dotenv
GitHubは[こちら](https://github.com/dotenv-rs/dotenv)
Referenceは[こちら](https://docs.rs/dotenv/0.15.0/dotenv/)

## 依存関係
現在（2021年3月22日）の最新バージョンは0.15.0です。
Cargo.tomlは以下。

```toml:Cargo.toml
[package]
...略

[dependencies]
dotenv = "0.15.0"
```

## 使い方
### 基本的な使い方
基本的な使い方はdotenv().ok()で.envファイルを読み込んでstd::envから環境変数を使うパターンです。
以下はDieselでデータベースの接続情報を取得している例です。

```rust:.env
DATABASE_URL=postgres://root:root@localhost/rust-dev
```

```rust:util.rs
use diesel::prelude::*;
use diesel::pg::PgConnection;
use dotenv::dotenv;
use std::env;

pub fn establish_connection() -> PgConnection {
    dotenv().ok();

    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    PgConnection::establish(&database_url)
        .unwrap_or_else(|_| panic!("Error connecting to {}", database_url))
}
```

### その他の使い方
.envファイルを読み込んで環境変数を使う方法です。

#### キーを指定して取得
dotenv::varで環境変数のキーを指定してバリューを取得できます。

```rust
use dotenv;

let key = "FOO";
let value = dotenv::var(key).unwrap();
```

#### 環境変数を全て取得
dotenv::varsで環境変数（キー・バリュー）を全て取得できます。

```rust
use dotenv;
use std::io;

let result: Vec<(String, String)> = dotenv::vars().collect();
```

#### iteratorで取得
dotenv::dotenv_iterでイテレータとして取得できます。

```rust
use dotenv;

for item in dotenv::dotenv_iter().unwrap() {
  let (key, val) = item.unwrap();
  println!("{}={}", key, val);
}
```

### ファイルを指定して読み込んで使う方法
.env以外のファイルを指定して読み込んで環境変数を使う方法です。

#### カレントディレクトリのファイルを読み込む
dotenv::from_filenameで読み込めます。

```rust
use dotenv;
dotenv::from_filename("custom.env").ok();
```

このあとはdotenv::dotenv()と同じようにstd::envで値を読み込めます。

dotenv::dotenv_iterと同じようにイテレータで取得することもできます。

```rust
use dotenv;
let iter = dotenv::from_filename_iter(".env").unwrap();

for item in iter {
  let (key, val) = item.unwrap();
  println!("{}={}", key, val);
}
```

#### パス指定してファイルを読み込む
dotenv::from_pathで読み込めます。

```rust
use dotenv;
use std::env;
use std::path::{Path};

let my_path = env::home_dir().and_then(|a| Some(a.join("/.env"))).unwrap();
dotenv::from_path(my_path.as_path());
```

from_filenameと同じようにイテレータで取得することもできます。

```rust
use dotenv;
use std::env;
use std::path::{Path};

let my_path = env::home_dir().and_then(|a| Some(a.join("/.env"))).unwrap();
let iter = dotenv::from_path_iter(my_path.as_path()).unwrap();

for item in iter {
  let (key, val) = item.unwrap();
  println!("{}={}", key, val);
}
```

## 実用的な使い方
.envファイルに一般的な設定（共通の設定）を行い、各環境固有の設定を各.envファイルに記載するのがよくある使い方のようです。
.envファイルは必ず読み込んで、各環境用の.envファイルを追加で読み込む形です。

dotenvで調べると、RubyやNode.jsでの使い方は出てくると思うので、参考にすると良いと思います。


# まとめ
ローカル/共有・開発/テスト/本番など、環境によって違う設定をファイル1つで変えることができるので便利ですね。
おまけにも書きますが、使い方のベストプラクティスは幾つかあるようなので各環境にあわせて考えることが大事かと。

# おまけ
dotenvで調べるとRubyとNode.jsで思想が違っていて面白かったです。
Rubyでは、

* 各環境ごとの.envファイルは作成してよい
* 共有の環境の.envファイルであればcommitしてよい

Node.jsでは逆に

* 各環境ごとの.envファイルは作成は非推奨
* .envファイルのcommitは非推奨

です。
RustのdotenvはRubyを参考にしているようなので、Rubyの思想にあわせるのが自然かなと思いました。
【初心者でもわかる】aタグで「現在のページ」・「現在のルートページ」・「非遷移」の設定方法

# どうも7noteです。微妙に違うaタグの遷移先の設定方法について解説

以下の4つの指定方法について解説していきます。

- ①`href="./"`　（現ディレクトリのルートに遷移）
- ②`href=""`　　（現在のページに遷移）
- ③`href="#"` 　 （現在のページのTOPに遷移）
- ④`href="#!"`　（ページ遷移をしない）

## ①`href="./"`　（ルートに遷移） 

ルートに遷移します。**勘違いされやすいですが、現在のページに遷移するわけではありません。**わかりやすいように問題にしてみます。

---

**前提：**ディレクトリ構造は以下とする。

> index.html
/news/index.html
/news/001.html

---

**問題１：**`/news/index.html`から`href="./"`ページ遷移した場合、どのページに遷移するか？

**答え：`/news/index.html`**

---

**問題２：**`/news/001.html`から`href="./"`でページ遷移した場合、どのページに遷移するか？

**答え：`/news/index.html`**

---

**「./」の設定は現ディレクトリのルートに遷移する。**ということです。
たとえ`/news/10000.html`からリンクしたとしても、`/news/`のルートファイル、つまり`/news/index.html`に遷移します。


## ②`href=""`　　（現在のページに遷移）

**`/news/001.html`から`href=""`にリンクしても`/news/001.html`のまま**です。

## ③`href="#"` 　 （現在のページのTOPに遷移）

`/news/001.html`から`href=""`にリンクしても`/news/001.html`のままですが、**強制的に現ページの一番上までスクロール**されます。

ページの最下部に設置するような「TOPへ戻る」ボタンなどは`href=""`が設定される場合が多いです。

- ④`href="#!"`　（ページ遷移をしない）

使う機会は少ないですが、あえて**ページ遷移をさせたくない時は`href="#!"`を指定**することでページ遷移を無効化させることができます。

## まとめ

- ①`href="./"`　（現ディレクトリのルートに遷移）
- ②`href=""`　　（現在のページに遷移）
- ③`href="#"` 　 （現在のページのTOPに遷移）
- ④`href="#!"`　（ページ遷移をしない）


「./」や「../」の違いを理解していても、油断すると指定ミスなどが起こりやすいところです。**常に自分がどの階層にあるどのファイルを触っていて、どこにリンクさせたいのかを把握する**癖を持っておきましょう。

#### おそまつ！
~ Qiitaで毎日投稿中!! ~
[【初心者向け】WEB制作のちょいテク詰め合わせ](https://qiita.com/7note)
M1搭載Macの環境を汚さずにDeep Learningしたい！（Docker, PyTorch, TensorFlow, VSCode, Jupyter）

# DockerでDeep Learningしたいが、、、

せっかく新しく買ったM1搭載Macなのだから、環境はできるだけ汚さずに開発したいなぁ、という方は少なくないと思われます。

環境をクリーンに保つ方法の一つがDockerですけれども、
Deep Learningに必要な[PyTorch](https://pytorch.org)と[TensorFlow](https://www.tensorflow.org)を両方利用できるいいarm64用Dockerイメージが見つからなかったので、なければ作れということで、作ってみました。
<img src="https://2.bp.blogspot.com/-wJDmO2MIKkg/U0pTOEZT-JI/AAAAAAAAfGc/p9uwt9Msq9A/s800/grade3_ganbarimashita.png" width=100>
（PyTorchのビルドは何の苦労もなくできましたが、TensorFlowのビルドが辛いこと辛いこと、、、ビルドを開始して数時間経過後にエラーやらOut of Memoryやらが発生してその原因を特定してという苦行を何回繰り返したことか・・・）

現状、PyTorch 1.6/1.12とTensorFlow 2.3/2.9が使えます。（2022/08/03現在）

動作確認を兼ねて、このDockerイメージを使って[Qiita記事の内容に合ったタイトルを数秒で10個くらい提案してくれるAI](https://qiita.com/sonoisa/items/30876467ad5a8a81821f)を作ったりしています。


# 作ったarm64 Deep Learning用Dockerイメージ

**特徴:**

* 次の2つのバージョンの組み合わせが利用可能です。
    * PyTorch 1.6.0とTensorFlow 2.3.0
    * PyTorch 1.12.0とTensorFlow 2.9.1 (🎉 2022/08/03追加)
   
以前公開していたPyTorch 1.9とTensorFlow 2.6.0のものは挙動が怪しいという報告を受け、非公開に変えました。


**pullの仕方:**

    docker pull sonoisa/deep-learning-coding:pytorch1.12.0_tensorflow2.9.1

や

    docker pull sonoisa/deep-learning-coding:pytorch1.6.0_tensorflow2.3.0

* **DockerHub**: https://hub.docker.com/r/sonoisa/deep-learning-coding
* [自分でイメージをビルドする方法](https://github.com/sonoisa/arm64-docker-pytorch-tensorflow)（MacBook Pro M1 16GBで4〜5時間、MacBook Pro M1 Max 64GBで2時間かかりました）


# VSCode Remote-ContainersでDeep Learningしよう

Dockerで作った環境で開発するなら、Visual Studio Codeの[Remote-Containers機能拡張](https://docs.microsoft.com/ja-jp/learn/modules/use-docker-container-dev-env-vs-code/1-introduction)を使って開発する方が便利ですよね。

上記のDeep Learning用Dockerイメージを使った開発プロジェクトのテンプレートも合わせて作りました。以下、使い方です。

## 1. M1搭載Mac用Dockerのインストール

Intel x86_64用ではなく、Apple Silicon（M1）用をインストールするようにご注意ください。
次のサイトからDocker Desktop for Apple Silicon（2021/03/22時点ではRC1）をダウンロードしてインストールします。

- Apple Silicon Tech Preview: https://docs.docker.com/docker-for-mac/apple-m1/


## 2. Visual Studio CodeとRemote-Containers機能拡張のインストール

* M1搭載Mac用のVisual Studio Codeのインストール方法:
    1. https://code.visualstudio.com からダウンロードします。

* Remote-Containers機能拡張のインストール方法:
    1. VSCodeのメニューの「Code」→「Preferences」→「Extensions」を選びます。
    2. Extensionsの検索フィールドで「remote-containers」を検索すると、Remote-Containers機能拡張が出てきます。
    3. 「Install」ボタンを押し、インストールします。

## 3. プロジェクトテンプレートをダウンロードして解凍

次のzipファイルをダウンロードして解凍します。
フォルダ名を適宜変更してください。

- プロジェクトテンプレート(zip): https://github.com/sonoisa/arm64-vscode-deep-learning-project-template/archive/refs/heads/main.zip


## 4. VSCode Remote-Containersでプロジェクトを開く

1. Visual Studio Codeの左下にある"><"のようなアイコンをクリックします。
2. 「Remote-Containers: Open Folder in Container...」を選択します。
3. 上記のプロジェクトを選択します。上記のDeep Learning用Dockerイメージのダウンロードとイメージのビルドが始まりますので少し待ちます。
4. Dockerコンテナの起動後に、利用するpythonを選択せよという警告が表示されたら"~/python3-venv/bin/python"を選択します。

これで開発環境の起動完了です。

JupyterLabを利用したい場合は、メニューの「Terminal」から「New Terminal」を選び、次のコマンドを実行し、表示されたURLをホスト側のブラウザで開けば利用できます。

    jupyter lab
Microsoft純正パッケージマネージャ"winget"を使おう

# Microsoft純正パッケージマネージャ`winget`を使おう

Microsoft社が公開しているパッケージマネージャツール。
Ubuntuなどで言う```apt```コマンドのようなもの。
インストール方法から使い方、コマンドチートを書き残します

[winget-cli](https://github.com/microsoft/winget-cli)

https://github.com/microsoft/winget-cli

## 実行環境

- OS: Windows 10 Pro (20H2) [19042.844]
- winget v0.2.10191 Preview

## インストール方法

インストール方法には以下の方法があります

1. Windows Insider Programへ登録後、Microsoft Storeからインストール
1. GitHubソースからビルドする
1. **インストーラをダウンロードしてインストール** <- 今回コレ

--- 
### インストーラをダウンロードしてインストール

以下のリンクにアクセスしてインストーラをダウンロードする

[Windows Package Manager](https://github.com/microsoft/winget-cli/releases)

`.appxbundle`がインストーラになります

![スクリーンショット 2021-03-22 233237.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171097/e9767347-56bc-3a34-5659-e4aa42f992d2.png)


## 使い方

- バージョンを確認

```powershell
> winget -v
v0.2.10191 プレビュー
```

- インストールできるパッケージを探す

```powershell
# すべてのインストールできるパッケージを表示する
> winget show
# 特定の名前のパッケージを表示する
> winget show | wsl grep <検索ワード>
```

- パッケージをインストールする

```powershell
> winget install <パッケージ名>
```

## 設定について

イニシャルでは`uninstall`や`upgrade`が無効化されているので有効にするために設定を行う

参考は以下
[Setting.md](https://github.com/microsoft/winget-cli/blob/master/doc/Settings.md)

`%LOCALAPPDATA%\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\settings.json`を
編集することでコマンドを有効にすることができます

- `uninstall`コマンドを有効にする

```json
    "experimentalFeatures": {
        "uninstall": true,
    }
```

使えるようになるコマンド

```powershell
# アンインストールできるインストール済みパッケージを表示する
> winget uninstall
# パッケージをアンインストールする
> winget uninstall <パッケージ名>
```

- `upgrade`コマンドを有効にする

```json
    "experimentalFeatures": {
        "upgrade": true,
    }
```

使えるようになるコマンド

```powershell
# アップグレードできるパッケージを表示する
> winget upgrade
# パッケージをアップグレードする
> winget upgrade <パッケージ名>
```
【iPhone8】りんごループのiOSアップデート（エラー14）対処法

iPhone8が突然、りんごループに、、、
そういえば、最近ストレージがいっぱいだったということを思い出し、まさかと思っていろいろググってみたところiOSのアップデートに失敗している可能性があるとのこと、、、
最終的にはiOSのIPSWファイルを手動ダウンロードしてインストールしたところ、なんとか復旧できました。

# 環境
　・iPhone8（りんごループになった機器）
　・WindowsPC（Windows10）

# 事前準備
  iOSのアップデートのために、WindowsPCにiTunesをインストールしておく。

# 復旧手順
1. iPhoneをリカバリーモードで起動

2. WindowsPCでiTunesを起動

3. WindowsPCとiPhoneをApple純正のライトニングケーブルで接続

4. iTunesの画面上に「アップデート」「復元」どちらかを選べと言われたので「アップデート」を選択
※「復元」を選択すると工場出荷時の状態に戻り、データは消えてしまうので注意！！

5. しばらく待つと、途中で「不明なエラーが発生しました(14)。」とでました、、、
ググってみるとどうやら、結構致命的なエラーで、ストレージがいっぱいのため、iOSのアップデートに失敗している可能性が高いということだそう、、、

6. あきらめずに、iTunesのiOSファームウェアのダウンロードに失敗しているのでは？と思い、iPhone8のIPSWファイルをダウンロード
※[IPSWダウンロードサイト](http://www.getios.com)
※[IPSWとは—IPSWの作用と保存場所](https://www.tenorshare.jp/ios-13/how-to-restore-iphone-using-ipsw-files.html)

7. iTunes上でSHIFTキーを押しながら「アップデート」を選択することで、IPSWファイルを指定してインストールできるのでダウンロードしたIPSWファイルを選択しインストール

8. 無事りんごループを抜け復旧しました！！

# まとめ
今回のりんごループはiOSのアップデートが失敗していたことが原因だったよう。iTunesでエラー14が出ましたが、手動ダウンロードしたIPSWファイルでiOSアップデートすることで何とか復旧できました。
エラー14が出た場合は試してみてもよいかもしれません。
Twitter_bot作成②

前回記事: https://qiita.com/takueha/items/621db4c41389bd38322a

前回までで簡単な環境構築までは完了しました。

今回は本格的な開発に入っていこうかと思います。

###本格的な実装を始める前に、実装手順をまとめてみた

今回のBotの仕様をどのようにするか簡単にまとめておこうかと思います。

今回のBotは自分がツイートした内容の中で一定以上のいいねを得たものだけを自動でデータベースに保存し、そのツイートを任意のタイミングで自動ツイートする機能をつける予定です。


必要なことをまとめると‥

1. ツイートを保存するデータベースの作成
2. ツイートをデータベースに自動保存する機能の実装
3. データベースから保存したツイートを自動ツイートする機能の実装

こんな感じで進めて行けばうまくいくのではないかと考えています。

この進め方で正しいのかどうかは不明ですが、大筋はこのように進めていき、間違っていたらその都度修正をしていこうと思います。


では進めていきます。


###config/application.rbの修正

以下のように修正しました。

```
require_relative 'boot'

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module TwitterBot1
  
  中略
  
    config.generators do |g|
      g.javascripts false
      g.stylesheets false
      g.helper false
      g.test_framework false
      g.template_engine false
    end

    config.api_only =true
  end
end
```

上記のように修正し、JavaScriptなどの余計なファイルを作成しないように変更しました。


###必要なgemの導入

```
gem "twitter"
gem "active_model_serializer"
```

上記を導入


###Tweetモデルの作成

```
$ bundle exec rails g model Tweet body:text
```
上記のコマンドでbodyカラムを持ったtweetmodelを作成しました。

その後
```
$ bundle exec rails db:migarate
```

でmigrationを進めました。

###コントローラーの作成

```
$ bundle exec rails g contorller Tweets
```

上記のコマンドでTweetsコントローラーの作成を行いました。

###ルーティングの調整

routes.rbファイルに以下の記述を追加

```
root to: 'tweets#callback'
```

###コントローラーの中身の調整


tweets_controller.rbを以下のように修正

```
class TweetsController < ApplicationController
  require 'twitter'
  before_action :twitter_client

  def callback
    #ツイート一覧を取得
    @client.home_timeline({ count: 200 }).each do |tweet|
      #ツイートのいいねの数が1以上の時
      if tweet.favorite_count >= 1
        #そのツイートを保存する
        Tweet.create(body: tweet.text)
      end
    end
    #このまま放っておくとエラーになってしまうのでjson形式で保存したツイート一覧を取得する
    tweets = Tweet.all
    render json: tweets, each_serializer: TweetSerializer
  end


  def twitter_client
    @client = Twitter::REST::Client.new do |config|
      config.consumer_key = ""
      config.consumer_secret = ""
      config.access_token = ""
      config.access_token_secret = ""
    end
  end
end
```

callbackの処理でデータベースにツイートを保存できるような機能を実装しました。

これで実装手順の1〜2は完了です。

あとはデータベースにあるデータから自動でツイートする機能を実装できれば、ひとまず完成です。

実装進んだらまた記事を書くので、よければご覧ください。
BeamerをPDF/Aに対応させる方法（LuaLaTeX使用）

## はじめに

備忘録です．
最善の方法ではないと思うのでより良い方法があれば教えてください．

## LuaLaTeXのテンプレート
次のテンプレートを使うとPDF/A対応スライドが作成できます．

注意：このテンプレートを使用してもPDF/Aに対応していない場合があります．
veraPDFなどのソフトを使うとPDF/Aに準拠しているかを確認できます．

よくあるミス：`includegraphics`でPDFを挿入するときはそのPDFもPDF/Aに対応している必要があります．

```tex
\RequirePackage{luatex85}
\begin{filecontents*}{\jobname.xmpdata}
  \Title{タイトル}
  \Author{名前}
  \Language{ja-JP}
  \Keywords{キーワード1\sep キーワード2}
\end{filecontents*}
\pdfminorversion=7
\documentclass[unicode, 12pt, aspectratio=169]{beamer}
\usepackage{luatexja}
\renewcommand{\kanjifamilydefault}{\gtdefault}
\usepackage{biblatex}
\usepackage{amsmath, newtxmath}
\usepackage[a-2u,pdf17]{pdfx}
\usetheme{Rochester}
\usefonttheme{professionalfonts} 
\usefonttheme[onlymath]{serif}
\setbeamertemplate{navigation symbols}{}
\setbeamerfont{page number in head/foot}{size=\small}
\setbeamertemplate{footline}[frame number]

\title{\bfseries タイトル}
\author{名前}
\date{2021-03-23}
\begin{document}
{
  \setbeamertemplate{footline}{}
  \maketitle
}
%\begin{frame}{目次}
%  \setbeamertemplate{section in toc}[sections numbered]
%  \tableofcontents
%\end{frame}
\begin{frame}{\bfseries 背景}
\end{frame}
\end{document}
```
## PDFをPDF/Aに簡易的に変換する方法（非推奨）

どうしようもないときは次のコマンドでPDFをPDF/A対応に変換できます．
使用するとしても図だけにしておいたほうが良いと思います．
実行環境はLinuxを想定しています．

- `input.pdf`: 変換前のPDFの名前
- `output.pdf`: 変換後のPDFの名前（このコマンドで作成される）

```shell
gs -dPDFA=2 -dNOOUTERSAVE -dBATCH -dNOPAUSE -sColorConversionStrategy=UseDeviceIndependentColor -sDEVICE=pdfwrite -dPDFACompatibilityPolicy=1 -sOutputFile=output.pdf input.pdf
```

## おわりに

何かあればコメントでご指摘よろしくお願いいたします．
NATゲートウェイ と NATインスタンスの違い

## 勉強前イメージ

あまり違いわからない...
NATインスタンスは自分で構築する？

## 調査

### NATゲートウェイ とは

- 概要

VPC内のプライベートサブネットからインターネットに接続するためのゲートウェイでAWSのマネージドサービスです。
パブリックサブネットに設置します。

![2実際に構築してみる(Internet gateway と NAT gateway) - Qiita.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129517/7d31154b-0e14-9e88-0d3d-a10f35a37264.png)


- 作成方法

NATゲートウェイはVPCから作成します。

![2NAT ゲートウェイ _ VPC Management Console - Google Chrom.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129517/748b8917-ed28-bc13-470d-7dfffc0b9aed.png)

- 管理

AWSが管理を行う

### NATインスタンス とは

- 概要

こちらもプライベートIPからグローバルIPへの変換を行います。
NATゲートウェイでは出来ない、ポート転送や踏み台サーバを行うことができる

- 作成方法

NATインスタンスはEC2から作成します

![2021-03-18インスタンスウィザードを起動 _ EC2 Management Console - Google C.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129517/de478027-77c2-3da7-8ca9-305ccd737c00.png)

- 管理

ユーザが管理を行う

### 違い

- 概要 : プライベートIPからグローバルIPへの変換を行う
- 管理
	- NATゲートウェイ : AWSが行う
	- NATインスタンス : ユーザが行う
- 踏み台やポート転送
	- NATゲートウェイ : 出来ない
	- NATインスタンス : 出来る

## 勉強後イメージ

管理をおまかせできるのがNATゲートウェイのいいところ
ただ、踏み台したい！とかいろいろカスタマイズしたい！とかであればNATインスタンスかな
まだいろいろ違いはありそうだけど一旦大きなところだけ。
あとNATインスタンスのが安くすみそう

## 参考

- [NAT インスタンスと NAT ゲートウェイの比較](https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/vpc-nat-comparison.html)
- [NATゲートウェイとNATインスタンスの違いについて](https://www.hatarakumama-pj.com/posts/ginzaitlab6907182/#:~:text=NAT%E3%82%B2%E3%83%BC%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A4%E3%81%AF%E3%80%81Amazon%E5%81%B4,%E9%81%8B%E7%94%A8%E7%AE%A1%E7%90%86%E3%82%92%E8%A1%8C%E3%81%84%E3%81%BE%E3%81%99%E3%80%82)
サクッとjQuery【入門】

```javascript
/* 呼び方 */
① $("text1") セレクタ
② .text() メソッド
③ "click" イベント
```

```javascript
/* メソッド一覧 「〜する。の処理」*/
/* テキストの値を取り出す */
var hoge = $("text1").text();

/* 指定したIDの属性を取り出す id=textのclassが入る */
var hoge = $("text1").attr(class);

/* 指定したIDの属性を設定する */
$("text1").attr(class, "sampleClass");

/* 指定したIDのラベルの文字列を先頭に追加 「うえお」→「あいうえお」になる。*/
$("label1").prepend("あい");

/* 指定したIDのラベルの文字列を前にラベル追加 「うえお」→「あい」「うえお」になる。*/
$("label1").before("あい");

/* 指定したIDのラベルの文字列を後にラベル追加 「うえお」→「うえお」「あい」になる。*/
$("label1").after("あい");

/* 指定したIDの子要素を削除 divなどに記載 */
$("label1").empty();

/* 指定したIDの要素を削除 */
$("label1").remove();

/* メソッドチェーン：たくさんのメソッドをくっつける */
$("label1").html("この部分が赤色で表示されます").css("color" ,"red");



/* イベント一覧 「〜したら、〜する」*/
/* ユーザー操作された後のイベントを設定できる */
$("checkbox1").on("click", function(){ // 「click」イベントプロパティと呼ばれる
   // その後の処理
})

/* これでも可 */
$("checkbox1").click(function(){ 
   // その後の処理
})


```
WindowsでPDFを画像データ化するのにCubePDFが神だった件

PDFをレタッチする必要があり、Adobeの純正ツールを買うお金もないので、フリーウェアで何とかしてみた。

## GIMPとかInkscapeとか

GIMPとかInkscapeとかで、埋め込みでない市販フォントのPDFをインポートするとフォントグリフが全く合わない。どちらも普段はとってもお世話になっているソフトウェアで感謝しかないのだが、PDFのインポートに関しては速攻断念する。

## 何とかPNGとかにできないものかな

ピクセル単位でレタッチしたかったので、何とかPDFをPNGに変換できないものかとWeb検索するものの、ヒットするのはオンラインツールばかり。レタッチしたいPDFは仕事絡みのものでオンラインツールを使うのは忍びなかったので、スタンドアローンのツールがないものかと探していた。

## CubePDFが神だった

色々と探している中で、以前から愛用していた[CubePDF](https://www.cube-soft.jp/cubepdf/)にPNGやJPEGへの変換機能があることを再発見。しかも仮想プリンタなので、何とアプリを選ばずにどんなソースでも画像に変換できるではないか。CubeSoftさん、素晴らしいソフトウェアを公開してくださってありがとう。

公式サイトによれば「PostScript、EPSなどのベクタ形式のファイルや、BMP、JPEG、PNG、TIFFなど」に変換できるとのこと。

PNGに変換したものをGIMPで編集してPDFにエクスポートすることで何とか目的を達することができました。

## 2021年3月24日追記

そうだ、GhostScriptもあったじゃないか。何で忘れていたんだろう。
まぁ、CubePDFなら手軽にWindowsだけで完結できるので…。

```shell-session
gs -sDEVICE=png16m -dTextAlphaBits=4 -r300 -o sample1.png sample1.pdf
```
SQLを学んでみた⑤(集計関数)

実務でSQLやDBについて理解していないといけないと思い勉強を始めました。
その内容をまとめます。

## 集計関数

Excelではよくお世話になっていたSUMとか。
SQLでもあるんですね。

| 関数名       | 使い方                |
|:-----------:|:-------------------:|
| SUM         | 値の合計を求める        |
| MAX         | 値の最大値を求める      |
| MIN         | 値の最小値を求める      |
| AVG         | 値の平均値を求める      |
| COUNT       | 該当する行数をカウントする |

調べた中で使いそうだなと思ったのが上記の集計関数です。


## 使い方

例えば売り上げデータのDBがあったとして以下のように使うことでそれぞれの値が求められます。
`AS`は分かりやすいように別名をつけているだけです。

```
SELECT
  SUM(売上) AS 売上金額の合計,
  MAX(売上) AS 最大の売上金額,
  MIN(売上) AS 最小の売上金額,
  AVG(売上) AS 売上金額の平均
FROM 売上管理簿
```

COUNTは少し使い方が違います。

```
SELECT COUNT(*)
  FROM 売上管理簿
WHERE 内容 = '機械'
```

`COUNT(*)`だとNULLも含めて行数を取得しますが、
`COUNT(カラム名)`にするとNULLは含めません。


以上です。
Python実践データ分析100本ノック作業記録【第一章～第二章】

# はじめに

第一章の残りだけで終わらせようと思ったら、大半が写経になったから第二章までやることにしました。

最近、チーム内でデータ解析を行うことが増えたのでこの経験を活かせるのがちょっと嬉しい。

# 第一章続き(抜粋)

## ノック8

試しに年齢で振り分けて消費金額の年代別の差を見たい場合、
次のようにすれば良い。
numpy由来ののブロードキャストのおかげで楽にかけて良いね。

```python
joined_dataframe["age_div10"] = joined_dataframe["age"] // 10

print(joined_dataframe.groupby("age_div10")["price"].mean())
```

## ノック10

matplotlibもまともに扱ったことがなかったので、試しにコンソールに[ユーザーガイド](https://matplotlib.org/stable/tutorials/introductory/usage.html#sphx-glr-tutorials-introductory-usage-py)のを書き込んでみたら、import後にこんな表記が出た。

> Backend TkAgg is interactive backend. Turning interactive mode on.

下記ソースをコンソールに流し込んだら勝手にグラフが出てきたけど、同じ内容のpyファイルを作ってそれを実行させたらグラフが出てこず、axes.show()をしなければグラフが出てこなかった。
インタラクティブモードってそういうことか。

```python
import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots()  # Create a figure containing a single axes.
ax.plot([1, 2, 3, 4], [1, 4, 2, 3])  # Plot some data on the axes.
```

ある程度は写経をしていたが、python自体はそれなりに触っているため、
最後のplotの部分は体が受け付けず、下記のような作りに書き換えた。

```python

month_axis = list(graph_data.index)
item_type_list = list(graph_data.columns)

for data_name in item_type_list:
    plt.plot(month_axis, graph_data[data_name], label=data_name)

plt.legend()
plt.show()

```

# 第二章

## ノック11
第一章のときのライブラリ構造に追加し、openpyxlとxlrdを追加。
例外のメッセージにに従って順番にインストールしていたので、xlrdはいらなかったかも。
xls形式のデータを読むときは必須だったはずだけど。

## ノック15

複雑そうな処理だけど、まずは自分の頭で考えてみたかったから考えてみた。
第一章のような商品マスタを作ることを意識して結合すればいけると思う。

まずは各商品ごとに値段候補を書き出してみる。

```python
import pandas as pd

customer_data = pd.read_excel("kokyaku_daicho.xlsx")
sell_report = pd.read_csv("uriage.csv")

sell_report["purchase_dt"] = pd.to_datetime(sell_report["purchase_date"])
sell_report["purchase_month"] = sell_report["purchase_dt"].dt.strftime("%Y%m")

sell_report["item_name"] = sell_report["item_name"].str.upper()
sell_report["item_name"] = sell_report["item_name"].str.replace(" ", "")
sell_report["item_name"] = sell_report["item_name"].str.replace("　", "")

item_price_table = sell_report[["item_name", "item_price"]]

for name in sell_report["item_name"].unique():
    price_list = item_price_table[item_price_table["item_name"] == name]["item_price"].unique()
    print(name, price_list, end=" / ")
```

下のような出力を意図通りに得られた。NaNではない方を抽出して商品名と価格のDataFrameを作って結合すれば良さそうだ。

> 商品A [100.  nan] / 商品S [  nan 1900.] / 商品Z [2600.] / 商品V [2200.   nan]

つまり、for文以下はこうなる。

```python

for name in sell_report["item_name"].unique():
    price_list = all_sell_item_price_table[all_sell_item_price_table["item_name"] == name]["item_price"].unique()
    item_price_table = item_price_table.append({"item_name": name,
                                                "item_price": [price for price in price_list if not math.isnan(price)][0]},
                                               ignore_index=True)

sell_report = pd.merge(sell_report.drop(columns="item_price"), item_price_table, on="item_name")

```

本の方ではlocプロパティが使われていた。そっちのほうが上のソースに相当する処理が1.5倍早かった。

第二章の残りは本とコードと変わらないので省略。

次回、第三章から。
もうそろそろ毎回printするのが面倒になってきたからJupyter Notebookを使うかな。
Codeforces Round #674 Div. 3E. Rock, Paper, Scissors を最大流で解く

https://codeforces.com/problemset/problem/1426/E

最大流に興味を持ったきっかけの問題です。

# 題意
- じゃんけんをします。グーチョキパー(G, S, P)があります。
- AさんとBさんがいて、n回試合をします。G,S,Pをそれぞれ、Ag, As, Ap回とBg, Bs, Bp回出します。(n試合なので互いの和はnです)
- あいこは引き分けです。
- Aさんが勝利する最大の数と最小の数を答よ

# 考え方
まず、問題を以下のように言い換えます。

- Aさんが勝利する最大の数と最小の数をこたえよ
- Aさんが勝利する最大の数と「n から 負け　か　あいこにできる最大の数を引いた回数」をこたえよ

次のように２つのグラフを作ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30938/80536c7f-f727-25c6-c828-e525682cf3f2.png)

<font color=red>ポイントは各手同士の辺のcapは無限大とすることです。</font>(本題意において、各手の数をcapとしても良いですが、問題として考える際に、capを制限する必要はなく、infとして扱ってよいです)

まず、左が勝利する最大の勝つ回数を求めるグラフです。

- 0,7を始点終点として、その他のノードは図のように各手のノードだとします
- スタート地点からAの各ノードに対してcap = 出せる手数とした辺を張ります
- 同様に、Bの各ノードからcap = 出せる手として辺を張ります
- A勝てる組み合わせの手(AグーならBチョキなど)に辺をはり、このcapは無限大とします。

sからtの最大流がAが勝てる最大の数です。

次に、右が「負け　か　あいこになる最大の数」を求めるグラフです。

- グラフの構成は同じです。
- A,B間の手の張り方が異なり、Aがあいこか負けになる手(Aグーなら、BグーあるいはBパー)に辺をはり、このcapは無限大とします。

sからtの最大流mfがAが「負け　か　あいこになる最大の数」です。なので、n - mf がAが勝利する最小の数です。

# 実装
ACL部分は省略します。

```C++
int main(int argc, char *argv[]) {
    mf_graph<ll> mfwin(8);
    mf_graph<ll> mflose(8);
    ll n,ar,as,ap,br,bs,bp;
    cin>>n>>ar>>as>>ap>>br>>bs>>bp;
    ll inf = 1e12;
    ll reswin, reslose;

    // 勝つ回数
    mfwin.add_edge(0, 1, ar); mfwin.add_edge(0, 2, as); mfwin.add_edge(0, 3, ap);
    mfwin.add_edge(4, 7, br); mfwin.add_edge(5, 7, bs); mfwin.add_edge(6, 7, bp);
    mfwin.add_edge(1, 5, inf);
    mfwin.add_edge(2, 6, inf);
    mfwin.add_edge(3, 4, inf);
    reswin = mfwin.flow(0, 7);
    // あいこ　か　まけ　の回数
    mflose.add_edge(0, 1, ar); mflose.add_edge(0, 2, as); mflose.add_edge(0, 3, ap);
    mflose.add_edge(4, 7, br); mflose.add_edge(5, 7, bs); mflose.add_edge(6, 7, bp);
    mflose.add_edge(1, 4, inf); mflose.add_edge(1, 6, inf);
    mflose.add_edge(2, 4, inf); mflose.add_edge(2, 5, inf);
    mflose.add_edge(3, 5, inf); mflose.add_edge(3, 6, inf);
    reslose = mflose.flow(0, 7);

    cout << n - reslose << " " << reswin << "\n";
}
```

https://codeforces.com/contest/1426/submission/110745795
ijenims record 2021.03.22

# Topics

>久しぶりに学習記録を残す。日記は間が空いたが、日々の記録はスマホアプリ「進捗どうですか？」で管理。

## 学習記録

### Python

- paiza ラーニング：レベルアップ問題集 配列メニュー（18／64問）
- paiza ラーニング：レベルアップ問題集 条件分岐メニュー（18／25問）
- paiza ラーニング：レベルアップ問題集 DPメニュー（2／21問）
- SIGNATE pandas入門道場（完了）

### HTML + CSS

- WEBCafe の内容変更
- K●●-F●●●● の内容解析～編集
- ウェブデザイン技能検定：過去問

### 統計学

- 統計WEB 統計学の時間 基礎編（13／32節）


### 線形代数

- ヨビノリ 線形代数入門（11／23節）- 

### EF

- 過去問：365／2560題

## その他

- "private" はメソッドで使われているようである。今後タグで使うのは控えようと思う。
git checkout コマンドを省略する

#rails tutorialで学習時に
学習を進めると git checkout のコマンドが頻繁に出るようになり、省略できないかと探してたらrails tutorial冒頭にありました

```:terminal
$ git config --global alias.co checkout  #checkoutのエイリアス（あだ名）をcoに変更する
```
こういう小さいことでも効率化をするのが大事

#参考にしたもの
https://railstutorial.jp/chapters/beginning?version=6.0#sec-what_good_does_git_do_you
[JS] 関数実行

引数
---
引数　＝　関数に渡される値


```
function hello(name) {
  console.log('hello' + name);
}

hello('Shun');
hello('Shun2');
```

[![Image from Gyazo](https://i.gyazo.com/9250d6c1b005880f29cbce63f2f21e23.png)](https://gyazo.com/9250d6c1b005880f29cbce63f2f21e23)

---

```
function hello(name, age) {
  console.log('hello' + name + age);
}

hello('Shun', 10);
hello('Shun2', 20);
```

[![Image from Gyazo](https://i.gyazo.com/d33986a7c02a27ab3a8911dde193cce9.png)](https://gyazo.com/d33986a7c02a27ab3a8911dde193cce9)

戻り値
---
戻り値　＝　関数から返される値

「関数が実行された結果」を、実行元に返却することができます。

```
function hello(name, age) {
  console.log('hello' + name + age);
  return name + age;
}

hello('Shun', 10);
const returnVal = hello('Shun2', 20);

console.log(returnVal);
```

[![Image from Gyazo](https://i.gyazo.com/946977baed4bc4c540094a5599c0b6b7.png)](https://gyazo.com/946977baed4bc4c540094a5599c0b6b7)

定義方法
---

```
function hello(name) {
  let name = 'Shun';
  console.log('hello' + name);
}

hello(　);
```


[![Image from Gyazo](https://i.gyazo.com/61350b2e06ecb3df9ef89e99f188eb46.png)](https://gyazo.com/61350b2e06ecb3df9ef89e99f188eb46)

---

```
const hello = function(name, age) {
  console.log('hello' + name + age);
  return name + age;
}

hello('Shun', 10);
const returnVal = hello('Shun2', 20);

console.log(returnVal);
```

[![Image from Gyazo](https://i.gyazo.com/fd6553d5084d2d6f0bf5d348f2ae0b34.png)](https://gyazo.com/fd6553d5084d2d6f0bf5d348f2ae0b34)

無名関数
---
無名関数　＝　関数名を持たない関数（左にconst hello =がない）

```
function(name, age) {
  console.log('hello' + name + age);
  return name + age;
}
```
テンプレートボタンを使いこなして効率化

#はじめに
データベースの項目は`+ボタン`で増やすことができますが、
データベースではない、項目を量産するにはどうしたらいいのか。
テンプレートボタンを使うと、クリックするたびに、
同じものを量産することができます。

##テンプレートボタンとは
よく使ういくつかの項目セットをクリック一つで量産することのできるボタンです。

`/tem`を打つと一番上に`Template Button`が出てきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/741816/dcb41daf-4475-fb5a-a2f3-12279400bb66.png)

##テンプレートボタンの内容

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/741816/0b538e86-cef5-4809-1ed0-1e96b3abc090.png)
歯車マークをクリックすると、編集画面が現れます。
`Close`を押すと、ボタンが小さく折り畳まれます。
`Button name`はそのボタンの名前です。折りたたむとこの名前だけが表示されます。
`Template`には、基本なんでも入れることができます。
ページのリンク、コードブロック、文章、などなど。

##注意
テンプレートの中身を左右のレイアウトにすることはできません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/741816/c0d04976-e88b-fd83-c6b2-ca744e611c4b.png)

ただ、予めテンプレートボタン自体を左右のレイアウトで寄せておくと、
生成されるテンプレートがテンプレートボタン元のレイアウトに依存します。
自分自身のフィールド値を利用したannotate処理

# やりたいこと

```python
class MyModel(models.Model):
    code = models.CharField(max_length=10)
```

こんな感じのモデルで、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58048/df8fd288-5f44-581c-b455-f79dce69e476.png)

こんな感じで同じコードが設定されている数をカウントしたい。


# ナイーブな解法と問題点

```python
class MyModelAdmin(admin.ModelAdmin):
    list_display = (
        'id',
        'code',
        'code_count',
    )

    def code_count(self, obj):
        return MyModel.objects.filter(code=obj.code).count()
```

adminやmodelにメソッド追加すればできはするが

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58048/23a15cd6-3ead-ad17-6039-3dfd31cbc9f1.png)

こんな感じでレコードの数だけsqlが発行される。
(n+1問題が発生する)


# SubqueryとOuterRefを使用したannotate

https://docs.djangoproject.com/ja/3.1/ref/models/expressions/#subquery-expressions

https://docs.djangoproject.com/ja/3.1/ref/models/expressions/#django.db.models.OuterRef

SubqueryとOuterRefを使って自分自身のcodeを使ったsubqueryを発行すればできる。

```python
from django.contrib import admin
from django.db.models import Count, OuterRef, Subquery

from .models import MyModel


@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
    list_display = (
        'id',
        'code',
        'code_count',
    )

    def get_queryset(self, request):
        sub_query = MyModel.objects.filter(
            code=OuterRef('code')
        ).values('code').annotate(count=Count('code')).values('count')
        return MyModel.objects.annotate(code_count=Subquery(sub_query))

    def code_count(self, obj):
        return obj.code_count
```

`code=OuterRef('code')` で外側にある自分自身のcodeで絞る。
codeの数でカウントを取りたいので `.values('code')`　と `annotate(Count('code'))` でGroup化する。

sub_queryの結果は1つじゃないといけないので、 最後に `.values('count')` で `code` のカウント数だけ返す。

あとは `code_count` という名前で先程作ったsub_queryをannotateしてあげる。

これで objectに `code_count` というcode件数のattributeがつくので、methodでその値を返してあげると完成。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58048/e2cf8c62-1273-f21e-80c5-93b303ef5466.png)

行数分発行されていたqueryが1つにまとまった。

発行されるsql文はこんな感じ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58048/2db5f809-59c0-83d0-8aa8-acd8673b2f82.png)

外側のcodeを使って、内側で `code_count` を作っているのがわかる。

---
調べてもわからなかったのにコード書いたあとで下記のキーワードでググったら同じようなコードが見つかった。

`django count outerref`

https://stackoverflow.com/questions/43770118/simple-subquery-with-outerref

https://www.javaer101.com/ja/article/3005204.html
julia v1.5.4から始めるJupyter Notebook



[前回](https://qiita.com/doorei/items/4498eb558e88f8fb378f)、juliaをインストールしたあと思うところあって、環境をきれいにしたところ、juliaがちょっとバージョンアップしていた。特に困ったことはないけど再々々々度書いてみる。

# やってみた環境
- Windows 10 Home (64bit) Ver 20H2 (OSビルド19042.868)
- julia v1.5.4 (64bit)

# やり方
1. juliaがインストール済みの前提(インストーラー版)
1. juliaのコンソールを起動(緑字のプロンプト)
1. `]`キーを押してパッケージモードへ(青字のプロンプトになる)
1. `add IJulia`を実行(Installing known registries into ～となり、しばらく時間がかかる)
1. Building IJulia → ～と出て完了
1. バックスペースでパッケージモードを抜ける(緑字のプロンプト)
1. `using IJulia`を実行(Precompilingされる)
1. `notebook(detached=true)`を実行
1. 「install Jupyter via Conda, y/n? [y]:」と聞かれるので`y`を入力
1. 関連パッケージがダウンロードとインストールされる。(結構時間がかかるので気長に待つ)
1. Jupyterが起動する！「New」のプルダウンにはJulia 1.3.1、Julia 1.4.2、Julia 1.5.4とpython 3が出てくる

juliaからインストールしたcondaはjulia専用(?!)なのでDisk容量がもったいないといえばもったいないけど、面倒なことを考えないでJupyterが使えるようになるのでまずやりたいことをやる！という場合にはいいんではないでしょうか？

ちなみにjuliaからインストールしたcondaでインストールしたパッケージの確認などは次のようにします。

```julia
julia> using Conda                    
Julia> Conda.list()                   # Condaでインストールしたパッケージ一覧 
Julia> Conda.update()                 # Condaでインストールしたパッケージのアップデート
Julia> Conda.install("package_Name")  # Condaで"package_Name"をインストール⇒「conda install -y package_Name」を実行
Julia> Conda.clean()                  # Condaでインストールされた不要なパッケージの削除
```

上記の`using Conda`でエラーが出る場合は一回だけ次のようにパッケージを追加する必要があります。
`]`キーを押してパッケージモードへ(青字のプロンプトになる)

```julia
(@v1.5) pkg> add Conda
```

# 普段のjupyter起動
念のためですがインストールが終われば次からは下記だけでjupyter Notebookが起動します。

```julia
julia> using IJulia
julia> notebook(detached=true)
```
pymooを使って多目的最適化のNSGA-Ⅱを実装してみた

* 製造業出身のデータサイエンティストがお送りする記事
* 今回はpymooを使って多目的最適化手法の中で、NSGA-Ⅱを実装しました。

##はじめに
過去に多目的最適化（[NSGA-Ⅱ](https://qiita.com/DS27/items/025a52b26a9f2471e67c)）をご紹介しておりますので、概要的な部分はこちらの記事を参考にしてください。
また、[deapを使ってNSGA-Ⅱ](https://qiita.com/DS27/items/dfedcad7ff48f09933a2)も記事として書いておりますので、参考にしてください。

##使用するライブラリー（pymoo）
今回は最適化ライブラリ[pymoo](https://github.com/msu-coinlab/pymoo)を使って実装したいと思います。

##NSGA-Ⅱの実装
NSGA-Ⅱの実装コードは下記の通りです。
今回は多目的最適化問題における一般的な問題の一つとして、[ZDT4](https://pymoo.org/problems/multi/zdt.html#ZDT4)を使用しました。

```php
# 必要なライブラリーのインポート
from pymoo.algorithms.nsga2 import NSGA2
from pymoo.factory import get_problem
from pymoo.optimize import minimize
from pymoo.visualization.scatter import Scatter

problem = get_problem("zdt4")

algorithm = NSGA2(pop_size=100)

res = minimize(problem,
               algorithm,
               ('n_gen', 250),
               seed=1,
               verbose=False)

plot = Scatter()
plot.add(problem.pareto_front(), plot_type="line", color="black", alpha=0.7)
plot.add(res.F, color="red")
plot.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/743453/92be6d1c-03cb-a669-6ebb-186c25c220cc.png)

deapと違って凄い簡単ですね。

簡単に何を行っているかを説明しますと、

* problem：問題を定義しております（今回はzdt4を使用）
* algorithm：アルゴリズムを定義しております（今回はNSGA2）
* minimize：目的関数の最適化方向を定義しております（今回は最小化）
* n_gen：世代数
* pop_size：集団内の個体数

あとは可視化を行っているだけです。

##さいごに
最後まで読んで頂き、ありがとうございました。
今回は、pymooを使って多目的最適化のNSGA-Ⅱを実装しました。
凄い簡単にできました。ただ、実際の業務で使用する際は、細かい制約条件をどこまで落とし込めるライブラリーなのかを調査する必要がありそうです。
時間があるい時に確認しようと思います。

訂正要望がありましたら、ご連絡頂けますと幸いです。
フォームに入力した値の重複の確認

写真のようにフォームに入力した値に重複があるのかを、保存前に確認する方法を見つけることができたので記録します。

![スクリーンショット 2021-03-22 21.54.52.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/292027/22d43c92-c598-9954-78cd-ba8024c37c83.jpeg)

ポジションは、f.selectを使用して、選択した値に対応する数字が、
選手名は、f.collection_selectを使用して、対応する選手ID(player.id)が送られるようにしています。

フォームから送られるparamsは、以下のようにタイトル、ポジション、名前が存在します。
(`starting`,`relief`,`closer`は、対応する投手の選手IDが入っています。)
今回は、`ポジション`と`選手名`に重複がないかを確認します。


```
{"title"=>"サンプル1",
 "one_position"=>"6", "one_name"=>"11",
 "two_position"=>"4", "two_name"=>"11",
 "three_position"=>"4", "three_name"=>"5",
 "four_position"=>"5", "four_name"=>"14",
 "five_position"=>"3", "five_name"=>"27",
 "six_position"=>"7", "six_name"=>"12",
 "seven_position"=>"9", "seven_name"=>"17",
 "eight_position"=>"8", "eight_name"=>"3",
 "nine_position"=>"2", "nine_name"=>"19",
 "starting"=>"20", "relief"=>"21", "closer"=>"22"}
```
## 手順
1.処理しやすいように`ポジション`と`選手名`を別々の配列に振り分ける
・ 交互にポジションと選手名に振り分けたいが、`starting`,`relief`,`closer`と連続で選手のIDが入っているので、一旦切り分ける
2.ポジションと選手名を分ける。
3.重複の確認

## 処理しやすいように分ける

```ruby
field_player = params[:best_nine].values[1..-4] #タイトルと投手情報以外をいれる
pitcher = params[:best_nine].values[-3..-1] #投手の選手IDをいれる
```

## ポジションと選手を分ける
<dl>
<dt><code>each_slice(n)</code>で、n要素ずつブロックに渡してを繰り返す。</dt>
<dd>・野手のポジションと選手IDがブロックに渡される(<code>[[one_position, one_name],[two_position, two_name], ・・・]</code>)</dd>
<dd>・ブロックの1つ目をポジション、最後(2つ目)を選手名として扱う</dd>
<dd>・<a href="https://docs.ruby-lang.org/ja/latest/class/Enumerable.html#I_EACH_SLICE">Ruby 3.0.0 リファレンスマニュアル</a></dd>
<dt><code>player</code>には、投手の選手IDを追加する。</dt>
<dd>・配列に配列をpushで挿入すると、二重配列のような形になるので、<code>flatten</code>を用いて平坦化する</dd>
</dl>

```ruby
position = field_player.each_slice(2).map(&:first)
# field_player.each_slice(2).map {|n| n.first}　と同じ処理
player = field_player.each_slice(2).map(&:last).push(pitcher)
player.flatten!
```

## 重複がないか確認
配列の要素の数と、配列内で重複していない要素の数の差が0でなければ、重複した値があると判定します。

```ruby
if ((position.count - position.uniq.count) != 0 || (player.count - player.uniq.count) != 0)
```

## 全てのコード
```ruby
field_player = params[:best_nine].values[1..-4]
pitcher = params[:best_nine].values[-3..-1]
  
position = field_player.each_slice(2).map(&:first).push(pitcher) # {|n| n.first}
position.flatten!
player = field_player.each_slice(2).map(&:last)

if ((position.count - position.uniq.count) != 0 || (player.count - player.uniq.count) != 0)
```

##参考記事
[Ruby | 配列の奇数番目と偶数番目を取り出す方法](https://qiita.com/ddd555/items/ed7db4b342b783620b41)
[Ruby 3.0.0 リファレンスマニュアル (※each_slice)](https://docs.ruby-lang.org/ja/latest/class/Enumerable.html#I_EACH_SLICE)
[Rubyで複数の配列を１つの配列に結合するために色々やってみた](https://qiita.com/na1412/items/65f883896c85011d6509)
[重複した要素を取り除く](https://www.javadrive.jp/ruby/array_class/index8.html)
シェルスクリプト　set -eux

# set -eux
dockerhubのshファイルを読んでいた際に`set -eux`というコマンドに何度も出くわしました。
調べると便利なオプションだったのでメモとして残しておきます。

シェルスクリプトの上部に`set -eux`記述しておくことで、その後のシェルの動作に対して、オプション的な効果を付与することができます。

オプション | 意味
- | -
-e | 何かのエラーが発生した時点で、処理を中断
-u | 未定義の変数を使ったらエラー
-x | 実行したコマンドを標準エラーに出力

# もっとこだわりたいあなたへ

`-o pipefail`というオプションを最近みました。

```
#!/bin/bash
set -euxo pipefail
```

パイプの途中でエラーがあった場合、そのコードの終了ステータスを引き継ぐというオプションです。
こちらもデフォルトで使用していいですね。

## 参考

[set man page](https://linuxcommand.org/lc3_man_pages/seth.html)

[初心者向け、「上手い」シェルスクリプトの書き方メモ - Qiita](https://qiita.com/m-yamashita/items/889c116b92dc0bf4ea7d)
Activityを保持しない場合のonActivityResult

# はじめに
Activityを保持しない場合も動作するように修正しようとしたら、ハマってしまい苦労したので備忘録として書きます。

Intentで他のアプリを起動した場合など、Activityがバックグラウンドに移動した時にメモリが足らなくなると、
もともと起動していたアプリ（Activity）を終了してメモリを確保するみたいです。
この時、アプリが復帰する動作が通常とは異なる動きをするので注意が必要です。
ただ、裏ですごいリッチでグラフィカルなゲームを起動してるとか、動かしてるアプリがめちゃめちゃメモリ喰うとか、端末が古くてすごい非力な場合とかの少し特殊な場合でしか起こり得ないことなので気づきにくいかもしれません。

# 結論
onActivityResultとonStartの呼び出し順が変わるので注意！！

## 再現方法

開発者向けオプションから「アクティビティを保持しない」の項目をONにすることで、Activityがバックグラウンドに移動した場合に常に終了するようにできます。

## ライフサイクル

Activityを保持する／しないの両方共動くようにしようとしたら、思ったように動かせなくてActivityのライフサイクルの動作を確認したら次のようになっていました。

### Activityを保持する場合（通常動作）

onCreate
→ onStart
→ onResume
→ (アプリを操作、startActivityForResult)
→ onPause
→ onStop
→ (Intentで他のアプリを起動) ※ここまでは一緒
→ **onActivityResult**
→ **onStart**
→ onResume

### Activityを保持しない場合

onCreate
→ onStart
→ onResume
→ (アプリを操作、startActivityForResult)
→ onPause
→ onStop
→ (Intentで他のアプリを起動) ※ここまでは一緒
→ onDestroy
→ onCreate
→ **onStart**
→ **onActivityResult**
→ onResume

上記の両者を比較すれば分かると思いますが、
onStartとonActivityResultの順番が変わっています。

Activityを保持しないのでonDestroyとonCreateが増えるのは想定通りでしたが、ライフサイクルの実行順が変わるのは想定外でした。
（ライフサイクルをちゃんと理解できていないだけ？）
とりあえずonStartで設定する内容は気をつけないといけない。。。
開発で庵野秀明を目指してはいけない

おそらくQiita読者の多くは今日3月22日に放送された「プロフェッショナル」を見たんじゃないかなと思います。
放送を見ながらメモった内容をもとに感想もとい開発で庵野監督がいるとを想像したので、記事にします。

# 結論
**開発で庵野秀明のような人がPO,PM,ステークホルダーになると終わる。**
※決して庵野監督を批判するわけではありません。
※庵野監督の生き様や苦労は想像を絶するものだと思っており、畏敬の念を抱いています。
※クリエイターの中にはこういうネガティブに振り切れた人がいるんだと再認識させられました。

# 理由
とはいえ、開発にあたって「終わらない」「区切り」が見えないのは真っ暗な闇の中を右も左も上も下もわからない状況にいるわけで。一種のアンチパターンだと思ったからです。
**一般人はマネしちゃダメ、絶対。**

## 1. 安心したいから設計図を作りたがる。だから絵コンテを作らない
設計書なかったら無理でしょ。
ちゃぶ台返しがありえるってことでしょ。
アジャイル開発、スクラム開発のように、変更を許容して作ったら終わりではなく、継続的な開発を進めていこうといえなくはない。
けど、最低限の設計書はないと無理だよね。
口頭だけで仕様伝えられて、「そうじゃない」と言われた日には。。。

## 2. 自分でやるとそれ以上が出てこない。けど、結局自分でやる
それぞれの分野でプロな人たちだから任せる。
アングルを探ったり、資料を集めたり。
けれど、最後は自分が動いて終わらせる。

チーム開発で「自分がやったほうが早い」は終わりの始まりだと考えている。
それは、属人的になるため知見が1人に偏るだけでなくタスクの比重も必然的にその人にのしかかる。
その人がいなくなったりつぶれたりした日にはプロジェクトそのものがとん挫する。

このあたりは山本五十六の名言「やってみせ～」が金言ではないでしょうか。

## 3. スタッフに高いハードルを課したのだから、自分はそれ以上でなくてはならない
自分でやろうとするからこうなるんだよね。
その責任感は尊敬する。

でも、開発では天才・鬼才・秀才と世間一般から言われる人を除き、自分より勉強している人は五万といるし、OSS開発のように集合知を目指すのが潮流だ。一般人でない人たちもこれを認識していて、むしろ自分が詳しくない分野については他人の意見を真摯に聞く。

自分1人で戦う必要は今の開発現場ではあまりない。

## 4. シトちゃん
三石さんのこの一言だと思った。
手足が長くて、飄々としていて愛らしく、顔はかわいい。でもすべてを破壊する。
これまでだと「鶴の一声」と表現されていたけど、これからは「パターン青！使徒です！」でいいんじゃないかな。

可愛ければすべてが許されると思うな。

# 唯一共感できたこと
「やるしかない」
「終わらせる義務」

これは業種問わず、仕事は最後まで終わらせることが絶対条件。
知り合いから教わった言葉で「早し良し。丁度危うし。遅し悪し。」というのがある。
全くその通りだと思う。
人間社会で仕事を請け負ったら期日を守るのは絶対。
間に合わないなら間に合わないで説明責任が求められる。

手塚治虫をはじめとした著名なクリエイターはこの辺りを蔑ろにするイメージがあるけれど、これを外さない庵野監督は凄い。
※手塚治虫は大好きです。

# 再三の確認
この記事は決して庵野監督を批難するものではない。
個人として、このようなクリエイターは好きだし、自分にできないことをやっている人全員を尊敬している。

ただ、仕事をするうえでアンチパターンは避けるべきで、その回避策としてアジャイルやスクラム開発といったテクニックや意識改革は必要だと考えている。

# 明日の自分に何ができるか
自戒を込めて、真摯に与えられた仕事を全うする。「終わらせる義務」ですね。
Codeforces Round #709 Div.1A. Basic Diplomacyを二部グラフの最大マッチングで解く ACL利用 (最大流/maxflow)

https://codeforces.com/contest/1484/problem/C

貪欲で解けるが、ACLを使った２部マッチングで解きます。ACLでの復元を行います。

# 題意
- n人の友達と、m日の遊びたい日があります
- i日目に遊べる友達は$a_1, a_2..a_k$ さん。という情報が与えられます
- あなたは毎日誰か１人のみと遊びたいです。別の日に同じ友人と遊んでも良いです。m日の間に遊ばない友達がいても良いです。
- ただし、ある友人と遊ぶ回数が$ceil(\frac{m}{2})$を超えてはいけないです
- 0人と遊ぶ(つまり誰とも遊べない)日があってはなりません。
- これが達成できる場合、その例を１つ挙げてください。無理なら、NOと答えてください。

# 方針
n人, m日とstart, end のノードs,t分のn+m+2の点を用意します。
辺の張り方は以下の通りです。

- sから各人に対して$cap = ceil( \frac{m}{2})$の辺を張ります。これは、あなたは各人にこれ超える回数遊ぶことはできないという制約になります。
- 各人から選択できる各日に対して$cap = 1$の辺を張ります。各人はあなたと遊ぶ最大の回数内($=ceil( \frac{m}{2})$)で日を選ぶことができます。
- 各日からtに対して$cap = 1$の辺を張ります。これによって、複数の人からこの日が選ばれることがなくなります。(人1と人2から日1が選ばれてしまっても日1からゴールへは1しか流せないので、どちらかは0でないといけません。)
この上で、sからtに最大流を流します。この最大流をmfとします。

mfは条件下で遊べる最大の日数になります。このため、$mf=m$でないとき、NOです。$mf=m$であるとき、現在、流せているフローが答えです。これを満たす条件を出力します。

n=4, m=6の時のグラフの例を示します。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30938/194c47c0-20b2-ae20-d6dd-58de71301b41.png width=50%>


# 復元
以下はACLの例を示します。

さて、流せたと気温このグラフの復元を考えます。ACLをはじめ、最大流のアルゴリズムはmaxflowを達成した瞬間の各辺の流量を保持しています。

今回の問題の場合、人から日につないだcap=1の辺にフローが流れていればその日はその人を使うことになります。各日はtに対してcap=1しか持たないため、複数の人からある日に対してフローが流れることはありません。

ACLでは、現在の辺の状況について、以下の２つの種類のクエリを発行できます。全部でn本の辺を追加したとすると(以下、iは0-indexedとします。)

- i番目に追加した辺をedge get_edge(x)で得る
- 追加された純のすべての辺 n個の要素からなるvector<edge> edges()で得る

今回は後者のedges()を使います。

```C++
vector<mf_graph<int>::edge> edges = mf.edges();
for (auto &e : edges) { ...}
```

edgesは次の4つの変数を持ちます。
- from, to, cap:　入力された点と流せる流量
- flow: 最大流の際に流れている流量

(ACLの)mfの辺の取得は少しコツが要ります。上記の結果は辺が追加されたに格納されています。このため、例えば、`点3から張られているすべての辺の情報を見たい`といった場合は、各点ごとの追加された辺を覚えていく必要があります。
さて、コードを振り返ると、今回は日ごとに順に候補となる人を選びました。そのため、すべての辺を見ていき、人と日をつなぐ辺（つまり、始点あるいは終点をfrom, toに含まない）で、flow=1が流れている点を順に選んでいけば、題意とグラフの作り方から、誰を選んでいけばいいかがわかることになります。
※丁寧に判断するなら、flow = 1 の時、to日目は人fromにすればよいです。



# 実装
ACL部分は省略します。

```C++
#define FOR(i, begin, end) for(int i=(begin),i##_end_=(end);i<i##_end_;i++)
#define REP(i, n) FOR(i,0,n)
using namespace std;
using namespace atcoder;
#define ceil(a,b) ((a) + ((b) - 1)) / (b)

int solve(){
    int n, m; // n人 m日
    cin >> n>>m;
    mf_graph<int> mf(1 + n + m + 1); // n+m + s,tのノードを作る
    int x, y;
    // スタートノードから人に対して割り当てていい最大数を張る
    REP(i, n) mf.add_edge(0, i+1, ceil(m, 2));
    REP(i, m){
        // 日からゴールにcap1だけをはる
        mf.add_edge(n+i+1, n+m+1, 1);
        cin >> x;
        REP(j, x){ // その日に対して
            cin >> y; // アサインできる人は
            mf.add_edge(y , i+1+n, 1); // 1を張ってもよい
        }
    }
    auto maf = mf.flow(0, n+m+1);
    // tに m 流れない場合、これは見たせない
    if(maf != m){ cout << "NO" << "\n"; return 0; }
    cout << "YES" << "\n";
    vector<mf_graph<int>::edge> edges = mf.edges();
    vector<int> res;
    for (auto &e : edges) { // 全部の辺を見て
        if(e.from == 0) continue; // sからは無視
        if(e.to == n+m+1) continue; // t へも無視
        if(e.flow == 1) res.emplace_back(e.from); // 制約的に日->tはcap1なのでこれでよい
        // 本来は、e.to に対するfromを知りたいのでそう書いた方がよいかもね
    }
    REP(i, m-1) cout << res[i] << " ";
    cout << res[m-1] << "\n";
    return 0;
}
```

https://codeforces.com/contest/1484/submission/110641955
C言語 バイナリファイルを扱う時の注意

#C言語でのバイナリデータの扱い
バイナリデータを取り扱う際に、あれprintfとかで表示できないぞ。なぜだ？となったので備忘録としてまとめます。
まず、別アプリから作成されたログファイルを読み込みために、C言語標準ライブラリ関数のfreadを使います。


```c:sample.c
#include <stdio.h>
size_t fread(void *buf, size_t size, size_t n, FILE *fp);
/*【引数】*/
/* void *buf     読み込みデータ格納先のポインタ */
/* size_t size   読み込みデータのバイト長さ */
/* size_t n      読み込みデータの数 */
/* FILE *fp      FILEポインタ */
```

バイナリファイルを取り扱う上でファイルの型情報などのデータ構造を知った上で、利用する必要があります。ただ私自身なんとなくの理解で、このログファイルを扱っていたので表示したいデータはの一部はchar型の4文字、つまりchar(1byte * 4)4byteと思い込んでいましたので以下のようにコードを書いていました。

```c:ahomatigai.c
char buf[5];
fread(buf, sizeof(char), 4, fp);
buf[4] = '¥0';
printf("%s", buf);
```
しかし、実際のデータ構造はchar文字ではなくlongの数だったので何も表示されなかったわけです。以下だと正しく表示されました。

```c:ahokaramashi.c
long buf;
fread(&buf, sizeof(long), 1, fp);
printf("%ld", buf);
```

#まとめ
かなり初歩的な間違いですが、データ構造が分からなかったり、バイナリファイルの生成処理を追えなかったり、今回のように思いこみがあると一瞬ん？となるのでまとめました。今回のバイナリファイルとは違ってデータの中に型情報が含まれたバイナリレイアウトもあるみたいですが、どちらにせよプログラマがデータの型情報を理解して取り扱う必要があると思います。
【備忘録】Visual Studio Code上でターミナル起動

# 方法
VScodeを起動している状態で「 ctrl, shift, ` 」の3つを同時に押す。

「`」のキーは、Macのキーボードであれば「P」の横にある。
（＠マークと同じキー）。


上記コマンドでVScode上でターミナルを実行できる。

# 余談
railsの勉強をしていた際にcloud9を使っていましたが、
すべてcloud9上で完結できていて大変便利だったため、
VScode上でも同じようにできないかな？と思い調べてみました！
Kotlinで文字列を選択できるドラムロールをシンプルに実装

#NumberPickerを使ってドラムロールを実装する
Kotlinならドラムロールはわりと簡単に実装できます。

##ドラムロールを実装

レイアウトにドラムロールとなるNumberPickerを追加します。

```Xml:activity_main.xml
<NumberPicker
    android:id="@+id/picker"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
```


onCreateメソッドでNumberPickerに文字列をセットしていきます。

```Kotlin:MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    //ドラムロール表示用の配列作成
    val fruits = arrayOf("りんご", "いちご", "みかん")
    //NumberPickerを取得
    val picker = findViewById<NumberPicker>(R.id.picker)
    //配列のインデックス最小、最大を指定
    picker.minValue = 0
    picker.maxValue = fruits.size - 1
    //NumberPickerに配列をセットする
    picker.displayedValues = fruits
```

##値の取得
決定ボタンのクリックリスナーなどで値を取得します。

```Kotlin:MainActivity.kt
//選択されている項目のインデックス番号を取得
val index = picker.value
//フルーツの配列からインデックス番号を指定して取得
val fruit = fruits[index]
React+Reduxについて簡単に解説してみた

#初めに(前書き)

最近では、Reactが３大Webフレームワーク(＝Vue＆React＆Angular)の中で、抜きに出ているとのことですが、いかんせん、浅学非才の私には(一般的に用いられる)ReactとReduxを組み合わせたシステムの構成や全体の動作が把握しづらいです・・・(泣)。したがって、(勝手ながら)この記事は、ReactとReduxについて学び始めた(自分と同じ立場の)方を対象にしたいと思います。



#本題(本文)

概説としては、ReactはWebアプリケーションのヴューを担当し、Reduxはモデルないしヴューモデルを担当します。※ちなみに、「ヴューモデル」とは、MVCに取って代わられるであろう「MVVM(Model-View-ViewModel)」と呼ばれるアーキテクチャーの構成要素です。

Reactでは、「コンポーネント」と呼ばれる単位でヴューを構成します。※ここでいう「ヴュー」とは、(大雑把にいえば)Webページのことです。
このコンポーネントは、jsファイル(やjsxファイル)に記述されます。このファイル内では、ヴューを構成する各コンポーネントに対応するrender関数内でタグ(やカスタムタグ)を書いて、これを呼び出し元にreturnします。こうすることで、実際のヴューが形作られて、描画されていきます。

Reduxでは、前述のコンポーネントに「ステート」と呼ばれる特定の状態があって、ユーザーからの入力(＝インタラクション)などによって起こるステートの変化を読み取って、これをヴューに反映させます。さらには、ステートに変更があった場合に、その変更を自動的に検知して、ヴューに対して、変更された内容の伝達および、更新の依頼をします。

従来のMVCアーキテクチャーでは、(通常は、)コントローラーがユーザーからの入力を受け付けて、モデル(≒ステートの集まり)(≒「Store」)の内容を参照し、その内容をヴューに伝達すると共に、ヴューに更新を依頼するという流れであり、これがMVC全体の仕組みでしたが、このヴューの更新を自動化することがMVVMの特徴であり、強味なのです。



#終わりに(後書き)
ReactとReduxを組み合わせたシステムは、私の想像以上に複雑で、ここでは簡単に解説させていただきましたが、実際には、「Action」「ActionCreator」「Store」「Container」「Reducer」・・・などといった構成要素・概念が絡んでくるので、・・・(私の非力ゆえに)割愛させていただきました(笑)。また、あまりゴチャゴチャとした解説では、却って、わかりづらくなるかとも思いましたので・・・(言い訳)。