OmniAICreator/Qiita-1.07M huggingface dataset:
競技プログラミング練習記 No.42【ABC120練習】

初回 -> [はじめに](https://qiita.com/easychoco/items/a9ae28aca97cfe47091f)

[ABC120](https://atcoder.jp/contests/abc120)を解きました。
4完です！

|問題|時間|アルゴリズム|実行時間
|:-:|:-:|:-:|:-:|
| A |  2min |-| 6ms|
| B |  6min |公約数| 6ms|
| C |  8min |popcount| 7ms|
| D | 80min |Union-Find木|37ms|


# まとめ
先にまとめを載せます。これ自体は最後に書いてます。

全完できました。嬉しい。
時間内に水色diffの問題を解けるようになってきました。

Union-Find木はグラフをつないでいくデータ構造ですが、逆に枝を切っていく操作にも使えることがわかりました。

## A - Favorite Sound

```c++
  int a, b, c;
  cin >> a >> b >> c;
  cout << min(b / a, c);
```
聞きたい回数は`C`回で、金額的な上限が`B / A`になりますね。
両方の小さい方が答えになります。

## B - K-th Common Divisor

``` c++
// 最大公約数 : 3,4 ->  1
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }

void solve()
{
  int a, b, k;
  cin >> a >> b >> k;

  int cd = gcd(a, b);
  for(int i = cd; i >= 0; --i)
  {
    if (cd % i == 0) k--;
    if (k == 0)
    {
      cout << i;
      break;
    }
  }
}
```
AもBも割り切る正整数というのは公約数のことですね。
公約数を英語にすると Common Divisor なので問題文そのままですw
その公約数の大きい方から`K`番目を求める問題です。

最大公約数を求めて、さらにその最大公約数の約数を大きい方から調べれば答えになります。

## C - Unification

``` c++
  string s;
  cin >> s;
  int p_count = 0;
  for(int i = 0; i < s.length(); ++i) p_count += s[i] == '1';
  cout << min(p_count, (int)s.length() - p_count) * 2;
```
細かい証明はできませんが、`0`と`1`をペアで消すときは配置に影響されません。
どのような配置になっていても、ペアがあれば消しきることができます。
つまり、`0`の個数と`1`の個数のみに着目すればいいです。
コードでは`p_count`が`1`の個数
`(int)s.length() - p_count`が`0`の個数です。
二つの数値の小さい方が消せるペア数になるので、それを2倍すれば答えになります。


## D - Decayed Bridges
Union-Find木のライブラリを使っています。

``` c++
  ll n, m, a, b;
  cin >> n >> m;
  vector<P> br(m);
  for(int i = 0; i < m; ++i)
  {
    cin >> a >> b;
    a--; b--;
    br[i] = P(a, b);
  }
  reverse(br.begin(), br.end());

  UnionFind uf(n);
  stack<ll> ans;
  ll e_cnt = n * (n - 1) / 2;
  for(const auto& p : br)
  {
    ans.push(e_cnt);
    if (!uf.same(p.first, p.second))
    {
      e_cnt -= uf.size(p.first) * uf.size(p.second);
      uf.unite(p.first, p.second);
    }
  }

  while(!ans.empty())
  {
    cout << ans.top() << endl;
    ans.pop();
  }
```
連結なグラフの枝を切りながら、連結されているグラフのまとまりごとに処理をする問題ですね。
問題の最終状態はノード間の全ての枝が切れている状態です。
問題の操作が逆だったら、つまり独立したノードをつないでいく操作だったら[Union-Find木](https://www.google.com/search?q=union-find&oq=union-find)を使えるな、、、って思ったので、逆にしてみました。
正直確信はなかったのですが、解けたのでうれしいです。

Union-Find木は木なので閉路がありません（あってもいいですが、意味はないです）。
そのため、閉路ができるつなぎ方は無視しました。

サイズ`N1`とサイズ`N2`である2つのグラフ同士が互いに独立の時につなぎます。
すると、つながっているノードの組み合わせが`N1 * N2`だけ増えますね。
ここまでわかれば実装です。

出力する数値は出力順と逆に得られるので、`stack`に詰めて出力しました。



以上です。ありがとうございました。
Wavefront で学ぶ分散トレーシング〜第一回　概要編

この文章は、Wavefrontで学ぶ分散トレーシング　シリーズの第一回目です。
# シリーズ

第一回　：　概要編　**← いまここ**
第二回　：　Spring Bootで分散トレーシング
第三回　：　REDメトリクスって何？
第四回　：　サービスをつなげてみる
第五回　：　Pythonで分散トレーシング
第六回　：　AMQPで分散トレーシング
第七回　；　サービスメッシュで分散トレーシング

# Wavefrontとはなんぞや？

WavefrontとはいうのはSaaSベースのクラウドおよびアプリケーションの監視プラットフォームです。
現在はVMwareに買収されており、Tanzu Observabilityと呼ばれています。
このシリーズでは、旧称のWavefrontを多用しますが同じものです。

# 分散トレーシングとはなんぞや？

分散トレーシングについては弊社のClement氏の動画がわかりやすいので貼っておきます。

[![IMAGE ALT TEXT HERE](http://i3.ytimg.com/vi/Z7mf_oZfcSE/hqdefault.jpg)](https://www.youtube.com/watch?v=Z7mf_oZfcSE)
https://www.youtube.com/watch?v=Z7mf_oZfcSE


はい、英語です。というわけで要点を説明すると…

* Lift（Uberみたいなもの）を例に考え方を説明
* 分散トレーシングは主に２つの要素がある、それがTraceとSpan
* Traceとは、あるまとまった一つの処理のこと、動画ではLiftで乗車を依頼してから降車するまでの処理として説明
* Spanとは、Traceを形成する個々の処理の単位のこと
* 分散トレーシングとは、**それらを視覚的に捉えやすくし**、どこに問題があるか、どの処理に時間がかかっているかなどをみること

Wavefrontは、この分散トレーシングを使ってアプリケーションの可視化を行えるようにしているツールです。

例えば、マイクロサービスでの各コンテナーどうつながっているかを以下のようなグラフで表現したり
<img width="1045" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666491/37e9ed2e-1d54-d559-95fd-b446bcb0623b.png">

どの処理がどのくらい時間かかったか、失敗している処理はなどがわかるようになります。

<img width="1766" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666491/8fdd3a19-aac6-3e9a-00fe-3580c5f27933.png">

# このシリーズはなんぞや？

さて、本題のこのシリーズについて、少し説明します。私自身、分散トレーシングに初めて触れたのは３年前でした。そして、その触れた要因がサービスメッシュ、Istioを勉強している時でした。他者さんの記事ですが、[こちら](https://qiita.com/asasaki/items/0d0710b0e73913b821ad#zipkinによる分散トレース)　のようにIstioとZipkin(やJaeger)をセットで紹介されていることが多かったです。ただ、正直よく理解はしていなかったです。また環境の用意にも重い腰があがりませんでした。

このシリーズは（きっと）私のようなレベルの方々に向けて、以下の点に注力して、分散トレーシングをとりあげたいと思います。

* なるべく、お金も時間もかけない
* アプリケーションがまったく書けない、という人向けにHello Worldレベルから解説する
* なんで、Istioと分散トレーシングが関連しているか理解する
* Wavefront/Tanzu Observabilityをしってもらう

というわけで、早速次回は「[Spring Bootで分散トレーシング](https://qiita.com/hmachi/items/d3ab73238b8c9e3b16c9)」です。
【Tips】JupyterLabで表示したグラフを保存・コピーする

# 現象
Jupyter notebook　を使っていた時の癖で右クリックで画像を保存しようとするがいつもの選択肢が出てこない。こんな感じ↓

![スクリーンショット 2020-07-13 11.04.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262969/357d7130-2cf4-d277-7e39-730df151da3b.png)

# 対処法
**Shift+右クリック**でいつものメニューが出てくる

![スクリーンショット 2020-07-13 11.04.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262969/af775fcd-c2ac-259f-1ac3-9cc5932f01db.png)
[CentOS 8]killコマンドによるプロセスの終了（メモ）

#はじめに
killコマンドについて改めて整理したので、そのメモ

##OSバージョン
```
$ cat /etc/redhat-release
CentOS Linux release 8.2.2004 (Core)
```

#プロセス管理シグナル
killコマンドはプロセス管理シグナルを指定する。指定しない場合はデフォルトの「SIGTERM」になる。

##コマンドで一覧を確認

```
$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
・・・
```
##よく使うシグナル
|シグナル番号|シグナル名|動作
|:---|:---|:---
|2|SIGINT|キーボード割り込みして、プログラムを終了する。Ctl+Cと同等
|9|SIGKILL|強制終了
|15|SIGTERM|プログラムを適切な方法で終了する。（デフォルト）
|18|SIGCONT|停止したプロセスを再開する。
|19|SIGSTOP|プロセスを一時停止する。
※シグナル番号はプラットフォームによって異なることがあるが、シグナル名は変わらない。

#プロセスの終了
SIGKILLは終了処理をせずに強制終了させるので、最後の手段とした方がよい。
SIGTERM → SIGINT → SIGKILLの順で試す。

##プロセスIDを指定して停止する

```
#プロセスIDを確認
$ ps aux | grep tail
user01      6385  0.0  0.0   5488   840 pts/0    S    10:39   0:00 tail -f /dev/null
user01      6386  0.0  0.0   5488   856 pts/0    S    10:39   0:00 tail -f /dev/null
user01      6387  0.0  0.0   5488   856 pts/0    S    10:39   0:00 tail -f /dev/null
user01      6389  0.0  0.0  10280  1064 pts/0    R+   10:39   0:00 grep --color=auto tail

#SIGTERMでkillする（シグナルを指定しないとデフォルトのSIGTERMになる）
$ kill 6385
[1]   Terminated              tail -f /dev/null

#SIGINTでkillする（シグナル名で指定）
$ kill -SIGINT 6386
[2]-  割り込み            tail -f /dev/null

#SIGKILLでkillする（シグナル番号で指定）
$ kill -9 6387
[3]+  強制終了            tail -f /dev/null
```

##プロセス名を指定して停止する
```
#プロセス名を確認
$ ps aux | grep tail
user01      6443  0.0  0.0   5488   760 pts/0    S    10:44   0:00 tail -f /dev/null
user01      6444  0.0  0.0   5488   832 pts/0    S    10:44   0:00 tail -f /dev/null
user01      6445  0.0  0.0   5488   856 pts/0    S    10:44   0:00 tail -f /dev/null
user01      6447  0.0  0.0  10280  1064 pts/0    S+   10:44   0:00 grep --color=auto tail

#プロセス名を指定してkill（3つkillされる）
$ pkill tail
```

##バックグラウンドジョブを停止する
```
# バックグラウンドジョブの確認
$ jobs
[1]   実行中               tail -f /dev/null &
[2]-  実行中               tail -f /dev/null &
[3]+  実行中               tail -f /dev/null &

#バックグラウンドジョブを一つkillする
$ kill -SIGTERM %1

#複数のバックグラウンドジョブをkillする
$ kill %2 %3
```

#プロセスの一時停止／再開
##一時停止
```
#jobの確認
$ jobs
[4]-  実行中               tail -f /dev/null &
[5]+  実行中               tail -f /dev/null &

#停止
$ kill -SIGSTOP %4

#確認
$ jobs
[4]+  停止                  tail -f /dev/null
[5]-  実行中               tail -f /dev/null &
```

##再開
```
$ kill -SIGCONT %4
$ jobs
[4]+  実行中               tail -f /dev/null &
[5]-  実行中               tail -f /dev/null &
```
Node.jsとMongoDBを連携

今回はnode.jsとMongoDBの連携方法について簡単に解説していきます。
##MongoDBの特徴
まず初めにMongoDBの特徴について解説します。

MongoDBの特徴として，以下のようなものが挙げられます。

**(1)スキーマレスであるドキュメント指向データベースであること**
・データはスキーマレスなドキュメントで格納され，任意のフィールドを好きなときに追加できる
・KVSでは苦手なValueを検索の条件としたり，ソート・集計を実現できる

**(2)スケーラビリティに関する機能を標準機能として備えていること**
・レプリケーション機能，オートフェールオーバー機能を備えている
・レンジパーティション機能，オートバランシング機能を備えている

**(3)RDBと比較してRead/Writeの性能が高い**
・トランザクション・リレーションを制限した結果，KVSに近いパフォーマンスを出せる

**(4)開発のしやすさ**
・どのような環境でもパフォーマンスが出るように，ネイティブソケットプロトコルを使用したドライバを主要なプログラミング言語で開発元が提供している
・RESTインターフェイスを標準で備えており，Webで広く普及しているJSONを使用してデータ送受信ができる

以上のような特徴があります。


##MongoDBのインストール

それでは実際にMongoDBをインストールしていきましょう。

今回はHomebrewを使ってインストールしていきます。
Homebrewがインストールされていない場合はインストールしておきましょう。

以下のコマンドでインストールしていきます。
`brew tap mongodb/brew`

続いて、以下のコマンドでソフトウェアのパッケージをインストールします。
`brew install mongodb-community`

これでインストールは完了です。

実際に起動できるか確認してみましょう。

起動
`brew services start mongodb-community`

停止
`brew services stop mongodb-community`

これで問題なければOKです。

詳しくはこちらの[公式](https://github.com/mongodb/homebrew-brew)を確認しましょう。

##Node.jsとの接続

それでは実際にNode.jsからMongoDBに接続してみましょう。
まず、Sample/index.jsを作成してください。

次に、以下のコマンドでpackage.jsonを作成します。
`npm init`

次に、以下のコマンドでMongoDBのパッケージをインストールします。
`npm install mongodb --save`

これで準備はOKです。
それでは、コードを書いていきます。

```javascript:index.js
var MongoClinet = require('mongodb').MongoClient;//MongoClient取得

var url = 'mongodb://localhost:27017/';　//今回はlocalhostなのでこの表記

MongoClinet.connect(url, (error, client) => {
  var db = client.db('sample');//どのDBを使用するか
  client.close();//操作を行った後に切断
});
```
以上が最低限必要なコードになります。
これでMongoDBの中のsampleというDBを操作する準備ができました。

しかしこのままでは接続できているのかよくわかりません。
なので実際に何か指示を書いてみましょう。

```javascript:index.js
var MongoClinet = require('mongodb').MongoClient;

var url = 'mongodb://localhost:27017/';

MongoClinet.connect(url, (error, client) => {
  var db = client.db('sample');
  db.createCollection('test', (error, collection) => {
    client.close();
  });
});
```
上記のコードはsampleというDBに対してtestというcollectionを作成するという指示です。

それでは実行してみましょう。
`node index.js`

**※実行前にローカル上でMongoDBが起動していることを確認しましょう
（起動していないと"TypeError: Cannot read property 'db' of undefined"というエラーが出ます）**

次に実行結果の確認をします。

ターミナル上でMongoDBに接続し、確認します。
[手順]
1.`mongo` でMongoに接続
2.`show dbs`でDBのリストを確認
3.`use sample` でsampleというDBにアクセス
4.`show collection`で中身を確認

"test"というcollectionが作成されていれば成功です。
これでNode.jsをMongoDBの接続し、collectionの作成に成功しました。
動画広告配信システムVeleTの紹介 

こんにちは、[アルファアーキテクト](https://alpha-architect.co.jp/recruit/)でVPoEをやっております胡 恩召（Ethan Hu）です。

今回は、アルファアーキテクトの動画広告配信システム[『VeleT』](https://velet.jp/)を紹介いたします。『VeleT』では、動画広告の最適化に必要な「定量×定性」効果をワンストップで分析することで、動画の活用目的や指標に適した運用を行います。

# システム構成

『VeleT』動画広告配信システムは、主に「管理画面」、「配信サーバー」、「バッチ処理」で構成されています。

## 管理画面

管理画面は、社内用の管理システムです。管理画面の役割は、配信管理・データ管理・請求・支払管理です。

配信管理は、クライアント様より配信広告を入稿し、その後配信プランを作成してから運用開始します。配信プランは、配信期間、予算、単価、ターゲティング情報の設定を行います。ターゲティングの設定は、在庫面（インバナー、インフィード、ネイティブなど）、メディア面（SSP、サイト/アプリカテゴリ、ドメイン/アプリリスト、枠リストなど）、ユーザ面（デモグラフィック、興味関心、位置、リタゲなど）、環境面（OS、デバイス、ブラウザなど）があります。運用中に、配信プランの効果に応じて、単価やターゲティング条件の調整が必要です。配信の実績は、レポーティングが必要です。

データ管理のデータとは、上記の配信管理で使うデータを指し、具体的には配信先関連データとユーザ関連データです。配信先関連データは、SSP情報、サイト/アプリカテゴリ情報、ドメイン/アプリリスト、枠リストなどがあります。ユーザ関連データは、ユーザのデモグラフィック、興味関心、位置情報、リタゲなどです。ユーザ関連データは、定期的な更新が必要です。

請求管理は、配信費用をクライアント様に請求のため、集計やレポートなど機能です。支払い管理は、配信で掛かったメディア原価のレポートや支払い情報です。

## 配信サーバー

配信サーバーの役割は、広告配信処理や配信ログ収集です。

広告配信処理は、SSPからのビッドリクエストを解析して、配信中な広告の配信条件（期間、予算、ターゲットなど）を確認する上で、一番適切かつ、収益が高い広告を選択して、SSPへ入札価格や広告のクリエイティブなど情報をレスポンスします。オークションに勝利すれば、SSPから勝利通知と落札価格を送られます。

配信ログは、SSPからの情報と計測ログの二種類があります。SSPからの情報は、ビッドリクエストログや入札ログやインプレッションログなどです、計測系ログは、クリックログ、視聴情報ログ、コンバージョンログなどがあります。これらのログはリアルタイムに収集され、バッチ処理や分析作業の元になります。

## バッチ処理

バッチ処理の内容は、主に配信サーバーで使う広告データの処理、配信ログ処理、レポート作成です。

広告データの処理は、広告配信情報（期間、予算、ターゲットなど）を配信サーバーへ取り込み、ユーザ関連データ作成（セグメント、リタゲ、フリークエンシなど）、メディア情報（カテゴリ、ドメイン、枠、キーワードなど）抽出です。

配信ログの処理は、生ログから必要な情報を前処理して、データウェアハウスに投入することです。ログ量が非常に多い中、広告の配信状況は可能な限り早く把握する必要があるため、処理パフォーマンスが大事です。

レポート作成は、データウェアハウスから広告配信データを集計し、様々な形のレポートを提供します。クライアント様、経営者、広告運用者、エンジニアなど、レポートを使う方によって必要な情報が異なるため、集計基盤の設計が重要になります。

# システムアーキテクチャ

『VeleT』のシステムは、全てクラウドで構築しています。管理画面や配信処理やバッチ処理は主にAWSを利用し、レポート集計、分析などにはGoogle CloudのBigQueryも利用しています。その上で、動画広告配信システムのアーキテクチャは、下記です。
![VeleT Sytem Architecture.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641596/d9f7bc8f-b015-3968-2df4-640ccbf57ce7.jpeg)
『VeleT』システムアーキテクチャ図

# 技術スタック

## 開発環境

* 仕様管理：JIRA
* ソース管理：Git
* インフラ：AWS、GCP
* 開発エディタ：自由（Vim、VSCode）
* 開発スタイル：スクラム

## 管理画面

* OS: Linux
* frontend：React + TypeScript 
* backend：Scala 
* database：MySQL 
* web server：Nginx  
 
## 広告配信

* OS: Linux
* load balancer：AWS ALB
* backend：golang
* cache：Redis
* database：DynamoDB
* web server: Nginx 

## バッチ・集計

* OS: Linux
* 言語：Python、Scala、golang、Perl、SQL
* 集計：Athena、BigQuery、Re:dash
* 処理：AWS Kinesis、Lambda、Fluentd 

# まとめ

広告配信システムは、高トラフィックや大規模データや低レイテンシーと厳しい要件が求められますが、エンジニアとしてのスキルアップ・レベルアップとして大きな成長ができることも魅力だと思います。

アルファアーキテクトは現在IPOを目指しています。『VeleT』の発展を加速させるため、 現在[インフラエンジニア](https://alpha-architect.co.jp/935/)、[バックエンドエンジニア](https://alpha-architect.co.jp/939/)、[テックリード](https://alpha-architect.co.jp/384/)などポジションを積極的に募集しています。動画広告サービス『VeleT』の開発を一緒に推進してくれる方、是非応募をお待ちしています！
ServiceAccount を作成し、Pod から `kubectl get pods` をやってみた時のメモ



-----

ServiceAccount を作成し、Pod から `kubectl get pods` をやってみた時のメモ。
検証のため、マニュフェスト作成はせず、コマンドでベースで確認した。

# やってみる

# 環境

```bash
$kubectl version
Client Version: version.Info{Major:"1", Minor:"16+", GitVersion:"v1.16.6-beta.0", GitCommit:"e7f962ba86f4ce7033828210ca3556393c377bcc", GitTreeState:"clean", BuildDate:"2020-01-15T08:26:26Z", GoVersion:"go1.13.5", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"15+", GitVersion:"v1.15.11-eks-af3caf", GitCommit:"af3caf6136cd355f467083651cc1010a499f59b1", GitTreeState:"clean", BuildDate:"2020-03-27T21:51:36Z", GoVersion:"go1.12.17", Compiler:"gc", Platform:"linux/amd64"}
```

## サービスアカウントの作成

`kubectl create serviceaccount`コマンドで実行

```bash
$kubectl create serviceaccount sample-service-account
```

## Role の作成

ただ、この状態では何も権限がないので Pod を確認するための Role を作る。
今回は検証なので以下を参考に `kubectl create` コマンドで作る

[kubectl create role ](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#kubectl-create-role)

```bash
$kubectl create role pod-reader --verb=get --verb=list --verb=watch --resource=pods
```

## RoleBinding の作成

次に上記ロールと作成したサービスアカウントを紐付けるために RoleBinding を作成する。
以下と `kubectl create rolebinding --help `を参考に実行。

[kubectl create rolebinding](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#kubectl-create-rolebinding)

```bash
$kubectl create rolebinding default-view \
  --role=pod-reader \
  --serviceaccount=default:sample-service-account
```

## 確認

Pod を作って検証する
`kubectl run` で `--serviceaccount` も指定できるのでこれでやってみる。
イメージは `kubectl`　が既にインストールされいる以下のイメージを使う

[lachlanevenson/k8s-kubectl](https://hub.docker.com/r/lachlanevenson/k8s-kubectl)

```bash
$kubectl run -i kubectl --image=lachlanevenson/k8s-kubectl --serviceaccount=sample-service-account --generator=run-pod/v1 --rm --restart=Never --command -- kubectl get pods
NAME                                READY   STATUS              RESTARTS   AGE
kubectl                             0/1     ContainerCreating   0          4s
(以下略)

```

無事 Pod の情報が取得できる事を確認。
念の為、`--serviceaccount` を指定しない場合、失敗することも確認する。

```bash
$kubectl run -i kubectl --image=lachlanevenson/k8s-kubectl --generator=run-pod/v1 --rm --restart=Never --command -- kubectl get pods
If you don't see a command prompt, try pressing enter.
Error attaching, falling back to logs: unable to upgrade connection: container kubectl not found in pod kubectl_default
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:default" cannot list resource "pods" in API group "" in the namespace "default"
pod "kubectl" deleted
pod default/kubectl terminated (Error)
```

サービスアカウントを指定しない場合でも `default` というサービスアカウントが設定されているようだが、この場合、権限がないのでエラーになる。
[CentOS 8]タイムゾーンの設定方法（メモ）

#タイムゾーンの設定方法の覚書
書いておかないとすぐに忘れてしまうので、メモを残しています。

##OS バージョン

```
$ cat /etc/redhat-release
CentOS Linux release 8.2.2004 (Core)
```

#設定方法
##現状のタイムゾーンの確認

```
$ timedatectl
               Local time: 月 2020-07-13 10:23:08 JST
           Universal time: 月 2020-07-13 01:23:08 UTC
                 RTC time: 月 2020-07-13 01:23:07
                Time zone: Asia/Tokyo (JST, +0900)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no
```
現状は「Asia/Tokyo」

##タイムゾーンの一覧を確認
```
$ timedatectl list-timezones
Africa/Abidjan
Africa/Accra
Africa/Addis_Ababa
Africa/Algiers
・・・
$ timedatectl list-timezones | grep Shanghai
Asia/Shanghai
```
ここでは「Asia/Shanghai」に設定する。

##タイムゾーンの設定

```
$ sudo timedatectl set-timezone Asia/Shanghai
```

##確認

```
$ timedatectl
               Local time: 月 2020-07-13 09:23:51 CST
           Universal time: 月 2020-07-13 01:23:51 UTC
                 RTC time: 月 2020-07-13 01:23:49
                Time zone: Asia/Shanghai (CST, +0800)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no
```
[Rails]schema.rbのコンフリクト解消

# はじめに
チーム開発をしている際に、プルリクエストを作成したところconflictが発生した。
他のファイルはメンバーに相談しながら解消できたが、
Railsで自動更新されるschema.rbファイルは勝手に修正して良いのだろうか？と詰まった。

# 解消
以下の記事を参考に修正を試みた。
[コンフリクトしたschema.rbをきれいにマージする手順](https://qiita.com/jnchito/items/494a0499b808f109e0a8)

```:ターミナル
$ git checkout master
```
を実行しようとしたところ、
```error: you need to resolve your current index first```
と出てしまい、ブランチの切り替えが出来なかった。

```:ターミナル
git merge --abort
```
これで一旦前の状態に戻すことで、ブランチの切り替えが可能になりました。以降は、上記の記事を参考にコンフリクトを解消し、マージすることが出来ました。

参考記事
[【git】マージしたけどやっぱりやめたい時のやり方4種類](https://qiita.com/chihiro/items/5dd671aa6f1c332986a7)
【Rails】ユーザー論理削除の実装

# 目標
![ezgif.com-video-to-gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579893/16abccaf-d365-c8cd-8264-4948d4fb3af0.gif)

# 開発環境
・Ruby: 2.5.7
・Rails: 5.2.4
・Vagrant: 2.2.7
・VirtualBox: 6.1
・OS: macOS Catalina

# 前提

下記実装済み。

・[Slim導入](https://qiita.com/matsubishi5/items/b639ffddecc626856039)
・[Bootstrap3導入](https://qiita.com/matsubishi5/items/b117c95f4dd71340cc80)
・[ログイン機能実装](https://qiita.com/matsubishi5/items/5bd8fdd45af955cf137d)
・[devise日本語化](https://qiita.com/matsubishi5/items/edff18194c1b060356f2)

# 実装

## 1.カラムを追加

```terminal:ターミナル
$ rails g migration AddIsValidToUsers is_valid:boolean
```

```ruby:~_add_is_valid_to_users.rb
class AddIsValidToUsers < ActiveRecord::Migration[5.2]
  def change
    # 「default: true」と「null: false」を追記
    add_column :users, :is_valid, :boolean, default: true, null: false
  end
end
```

```terminal:ターミナル
$ rails db:migrate
```

## 2.モデルを編集

```ruby:user.rb
# 追記
enum is_valid: { '有効': true, '退会済': false }

def active_for_authentication?
  super && self.is_valid == '有効'
end
```

### 【解説】

#### ① ユーザーの状態をenumで管理する。

```ruby
enum is_valid: { '有効': true, '退会済': false }
```

#### ② is_validが`有効`であれば`true`を返すメソッドを定義する。

```ruby
def active_for_authentication?
  super && self.is_valid == '有効'
end
```

## 3.`session_controller.rb`を編集

```ruby:session_controller.rb
# 追記
protected

  def reject_user
    user = User.find_by(email: params[:user][:email].downcase)
    if user
      if (user.valid_password?(params[:user][:password]) && (user.active_for_authentication? == true))
        redirect_to new_user_session_path
      end
    end
  end
```

### 【解説】

#### ① 入力されたメールアドレスに対応するユーザーが存在するかを確認する。

```ruby
user = User.find_by(email: params[:user][:email].downcase)
```

#### ② 入力されたパスワードが正しい場合かつ、`2`で定義したメソッドの返り値がtrueだった場合は、ログイン処理を行わずにログイン画面に遷移する。

```ruby
if (user.valid_password?(params[:user][:password]) && (user.active_for_authentication? == true))
  redirect_to new_user_session_path
end
```

## 4.ビューを編集

Bootstrap3のアラートコンポーネントを使用してフラッシュメッセージを表示する。

```sessions/new.html.slim
/ 追記
- if flash.present?
  .alert.alert-danger.alert-dismissible.fade.in role='alert'
    button.close type='button' data-dismiss='alert'
      span aria-hidden='true'
        | ×
    - flash.each do |name, msg|
      = content_tag :div, msg, :id => 'flash_#{ name }' if msg.is_a?(String)

      p
        a href='#' data-dismiss='alert'
          | 閉じる
```
Squid Ver4のアクセスログフォーマットを変更する

業務でSquidのアクセスログフォーマットを変更する案件があったがVer違いでドハマリしたので。

#情報
●Squidの設定ファイル
*/etc/squid/squid.conf*

●アクセスログのファイルパス
*/var/log/squid/access.log*

#ログフォーマットの指定
*logformat <名前> <ログフォーマット指定>*
詳細
https://www.robata.org/docs/squid/Reference/squid3.4.0.3/logformat.html

デフォルトはエポック秒しか記録されないので「%tl」を追加して見慣れた形式にするのが定石。

**Ver3まで**
設定ファイルに「logformat squid <ログフォーマット指定>」を追加して、<ログフォーマット指定>を任意の設定にしてやればよいだけ。

設定後は下記コマンドでファイルの再読み込み
*systemctl reload squid*

またはsquidの再起動
*systemctl restart squid*
状態確認
*systemctl status squid*


**Ver4だとデフォルトの識別名を追記するやり方では反映されない模様。**
なので、新たにログフォーマットの識別名を設定してやる。

具体的には、設定ファイルにログフォーマットの指定に加えてログ出力先の設定を追加。
ログ出力先の設定
*access_log "ログファイルパス" "フォーマット名"*
※フォーマット名はデフォルト以外の任意の名前

そして設定ファイルを再読み込みすれば以降のログに反映されるはず。


Ver4は日本語訳がないせいかナレッジが乏しくて苦労した…
Expressのルーティング

# これは何？
この記事はNode.jsのフレームワークのexpressのルーティングの自分がよく使う物を公式から抜粋したメモです。

# 基本的なルーティング

```
app.METHOD(PATH, HANDLER)
```

例：

```
app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.post('/', (req, res) => {
  res.send('Got a POST request');
});

app.put('/target', (req, res) => {
  res.send('Got a PUT request at /target');
});

app.delete('/target', (req, res) => {
  res.send('Got a DELETE request at /target');
});
```

# ルート・パス
正規表現などで引っかかっているパスにアクセスすることができる。(リクエストを実行できるエンドポイントを定義することができる)

例：

```
app.get('/random.text',　(req, res) => {
  res.send('random.text')
})

app.get('/ab*cd', (req, res) => {
  res.send('ab*cd')
})

app.get(/a/, (req, res) => {
    res.send('(Regular expression) path with "a"')
})

app.get(/.*fly$/, (req, res) => {
  res.send('(Regular expression) /.*fly$/')
})
```

# ルート・パラメータ
パスからパラメータ("key":"value")を取得ができる

```
app.get('/key1/:value1/key2/:value2', (req, res) => {
  res.send(req.params)
})
```

例：

```
ルート・パス: /flights/:from-:to
リクエストURL: http://localhost:3000/flights/LAX-SFO
req.params: { "from": "LAX", "to": "SFO" }
```

# 参考資料：
[Express公式](https://expressjs.com/ja/guide/routing.html)
動的言語と静的言語の違い

#結論
プログラム言語には、
`動的型付け言語`と`静的型付け言語`という2種類のタイプがあります。

####動的型付け
言語変数などのデータ型の宣言がいらないプログラミング言語。

Python
Ruby
JavaScript
PHP
など

####静的型付け言語
変数などのデータ型の宣言が必要なプログラミング言語

C/C++
C#
Java
Go
Scala
Swift
など

#動的型付け言語の特徴

###メリット

####１、記述量が大幅に減る
単純にデータ型の記載が不要になるので、圧倒的に書くコード量が減ります。

####２、比較的簡単にプログラムが書ける
あんまり難しいことを考えないでも手を付けられる言語

###デメリット

####１、プログラムを実行しないとバグがわからない
事前に分からないため、リリースしたあとに結構気がついたりすることが起きます。

####２、無駄なメモリが発生する
必要なメモリ数が把握できないので、余分に多くメモリ領域を取っておく等しておかないといけません。

補足：pythonならメモリ数が把握できるらしいです。

#静的型付け言語の特徴

###メリット

####１、コンパイル時にエラーが出てくれる
エラーが起きるので、コンパイル時にデバッグができます。

例：
・整数型変数なのに文字列を代入しようとしてる
・型の違うデータで関数を呼ぼうとしている

つまり、プログラム実行前に、バグやエラー箇所を特定できる。

####２、メモリ領域の最適化が行える
各変数がメモリをどれだけ必要とするかが分かります。


####３、パフォーマンスの向上が狙える
例えば
・整数と整数の計算が行われる
・小数と小数の計算が行われる

ということはデータ型を見て事前に判別がつくので、計算の最適化を行えます。

###デメリット

####１、必ず型名を書かなければならない
1つ1つ型名を添えなければいけないので、動的型付け言語と比べるとどうしても時間がかかります。

####2、やや固い印象がある
キャリアや面白さという意味で幅が狭まるかもしれません。

#参考
[動的型付け言語と静的型付け言語の違いとは？](https://jpazamu.com/dynamic-static/)
[動的型付けと静的型付け](https://qiita.com/toryuneko/items/c023031b61886cae2a99)
[オススメの言語はなにか？](https://qiita.com/FeelSoGood/items/b23246bb37e5cfa8e82e)
【初心者向け】よく使うGitのコマンド一覧 〜GitとGitHubの基本的な使い方〜

# はじめに
ここではGit初心者向けに、よく使うGitのコマンド一覧とGitとGitHubの基本的な使い方を紹介します。
また最初にGitを使う際の基本的なターミナルのコマンドも紹介しています。


# 環境
mac OS Catalina バージョン10.15.5
git version 2.26.2


# ターミナルの基本的なコマンド
`$ pwd`
現在のフォルダ場所(ディレクトリ階層)の表示

`$ ls`
ファイルやディレクトリの表示
`ls -l`
ファイルやディレクトリを詳細まで表示
`$ ls -a`
隠しを含んだファイルやディレクトリの表示
`ls -la`
隠しを含んだファイルやディレクトリを詳細まで表示

`$ cd ~~`
〜のフォルダに移動
`$ cd ..`
下の階層に移動

`$ history`
コマンド履歴の表示

`$ clear`
画面をリセット


# Gitのコマンド
### Gitのバージョン確認
```
$ git --version
```
### 作業ディレクトリにGitを使う宣言をする
```
$ git init
```
### GitHubのリポジトリをローカルにクローンする
```
$ git clone git@github.com:【ユーザー名】/【リポジトリ名】.git
```
### ブランチを確認する
```
$ git branch
```
### ブランチを新たに作成
```
$ git branch [ブランチ名] 
```
### ブランチを削除
```
$git branch -d [ブランチ名]
```
### 現在のステータス確認
```
$ git status
```
### ステージングエリアにあげる
```
$ git add [ファイル名]
$ git add .
```
git add [ファイル名]：[ファイル名]をステージングエリアにあげる
git add . ：全てをステージングエリアにあげる。
### コミットする
```
$ git commit "コミット名"
```
### コミットメッセージを１行以内に納める
```
$ git commit -m "コミット名"
```
### 一つ前のコミットと統合する
```
$ git git commit --amend -m "コミット名"
```
### Gitのログを確認する
```
$ git log
```
### Gitのlogを簡潔にまとめて表示する
```
$ git log --oneline
```
### GitHubに初めてプッシュする
```
$ git push origin [ブランチ名]
```
### GitHubにプッシュする（2回目以降）
```
$ git push
```
### ローカルリポジトリをリモートリポジトリに同期する
```
$ git fetch origin
``` 
### リモートブランチと同期したデータ、追跡ブランチをローカルリポジトリに取り込む
```
$ git merge origin / [ブランチ名]
``` 
### mergeとfetchをまとめて行う
```
$ git pull origin [ブランチ名]
``` 
### 前の状態に戻る
```
$ git checkout --[ファイル名]
```
### 編集した箇所を表示
```
$ git diff
```
### ステージングエリアにあげた場合、コミットで変更されるファイルが分かる
```
$ git diff --cached
``` 
### コミットした後の編集されたファイルが分かる
```
$ git diff -r [ID名]
``` 
### git addを取り消す
```
$ git reset HEAD
または
$ git reset HEAD [ファイル名]
``` 
### 直前に戻る
```
$ git reset --hard HEAD
```
### 1つ前に戻る 
```
$ git reset --hard HEAD^
``` 
### 指定されたlogに戻る
```
$ git reset --hard　[ID]
``` 
### マージを始めた頃のブランチに戻る
```
$ git reset --hard ORIG_HEAD
``` 
### リモートリポジトリ削除
```
$ git remote rm origin
``` 


## 参考にしたサイト
[Gitコマンド一覧](https://qiita.com/fukumone/items/73e1a9a62c5e4454263b)
Donkey car / Jetbot, プラレールでコースを作ってみた。

Donkey car用に新しいコースを作ってみました。
材料は手元にあったプラレール。
教師データはmax speed35%に設定、ハンドル操作に集中してコースを10周して取得。
機械学習後、自動運転します。こちらが自動運転の動画です。
https://www.youtube.com/watch?v=03wg67Q2aTg&t=2s
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617683/13ccae43-f9ce-04aa-830e-08d5fa473559.png)
8の字走行だ！（自動運転楽勝,速度もUP）
https://www.youtube.com/watch?v=Y7HSR-7bouw
![photo.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617683/47c196ef-3e1d-4827-02a9-696878796814.png)
パイロン走行
https://www.youtube.com/watch?v=Ci-GV2D7ljY
![Swan2Pylon.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617683/8eb69b9e-94a2-796d-7dc2-1423a7eb1dfa.png)
パイロン8字（難しい！）
https://www.youtube.com/watch?v=3VFw8KYgD4A
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617683/c3c6cb5f-d0d4-f74c-703e-d4763366fb5e.png)
。
GoでGORMとTwitterAPIを使ったミニミニアプリ作ってみた

プログラミング歴3ヶ月目に突入しました！
GoでGORMとTwitterAPIを使ったアプリと言っていいのかすらわからないアプリを作りました！
~~GitHubに置いてあるのでよかったら見てください！（https://github.com/souhub/buzz_tweet）~~ ※12/10追記 リポジトリ削除しました。
そんなミニミニミニアプリですが、Railsでツイッター風アプリを作ったぶりの達成感とワクワクを感じることができました。
TwitterAPIを使って任意のユーザーの直近100件のツイート（リプとRTを含む）を取得して、GORMでデータベース操作していいねやRTが多い順や少ない順に並び替えるアプリです。

##こんな感じ
これがホーム画面でここでツイートを並べ替えたい人のユーザーIDを入力し、何順に並びかえるか選択します。
<img width="1792" alt="スクリーンショット 2020-07-05 16.51.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649199/08ee3158-4198-bdf8-bfd6-72b1e003910d.png">
ここでは例として安倍首相のツイートをリツイートが多い順に並び替えるを選択しました。
<img width="1792" alt="スクリーンショット 2020-07-05 16.51.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649199/4f9be17a-92b1-c750-69f7-a3722140b448.png">
そして検索ボタンを押すとこのように並び替えられたページに移動します。
写真なのでこの下が全く見えないですが、スクロールするとズラーっと並んでいます。
<img width="1792" alt="スクリーンショット 2020-07-05 16.52.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649199/784a6cef-5e01-5708-bf92-3e30306efa91.png">

##反省点
- 並び替える直近100件をその人がツイートしたもののみ（リプ、リツイートしたものを含まない）にしたかったができなかった点。
- 一度検索してホーム画面に戻ったらデータベースを削除するようにしたかったが、アプリを落とさない限りデータベースを削除できなかった点。
- CSSファイルを別ファイルに書いて読み込もうとしたができなかった点。笑

##感想
初めてAPIを使ったアプリを作成したが本当に時間を忘れるくらい楽しかったです。
自分の思い通りに作成できなかった点がいくつかあり悔しかったのでもっと勉強して使いこなせるようになりたいです。

##参考
- https://github.com/souhub/buzz_tweet
- http://gorm.io/ja_JP/docs/index.html
- https://godoc.org/github.com/dghubble/go-twitter/twitter
HTML上でJavaScriptを使ってAPIコールし、レスポンスをCSVダウンロード（っぽく）取得する

## 目的

HTML上でJavaScriptを使ってAPIコールし、レスポンスをCSVダウンロード（っぽく）取得する。

## 方法

1. APIからのレスポンス値をCSV形式へ変換する。
2. 変換したCSVでdataURLを使ってリンク（aタグ）を生成する。

## 実装

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

<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<script>
    //実際にはAPIコールを行う
    // ※配列の配列（CSVの1行に該当）で返す
    const getApiResFromAPI = async () => {
        return [
            ['row 1 / cell 1', 'row 1 / cell 2', 'row 1 / cell 3'],
            ['row 2 / cell 1', 'row 2 / cell 2'],
            ['row 3 / cell 1', 'row 3 / cell 2', 'row 3 / cell 3', 'row 3 / cell 4']
        ];
    }

    // CSVダウンロードボタンを押下した際のclickイベント
    const csvdownload = async () => {

        // APIからCSVの中身を取得
        var apiResData = await getApiResFromAPI();

        // 取得した結果をCSV形式でつなげていく
        var csv = [];
        apiResData.forEach((row, index) => {
            csv.push(row.join(','));
        });

        // APIからダウンロードしたCSVファイルのデータURIを持ったaタグを生成
        var element = document.createElement('a');
        element.text = "テスト"
        element.href = 'data:text/csv;charset=utf-8,%EF%BB%BF' + encodeURIComponent(csv.join('\n'));
        element.setAttribute('download', 'basic.csv');

        // aタグを裏で追加して、クリック（csvファイルダウンロード用のダイアログが表示される）、裏で削除する
        document.body.appendChild(element);
        element.click();
        document.body.removeChild(element);
    }

</script>

<body>
    <input type="button" onClick="csvdownload(); return false;" value="CSVダウンロード">
</body>

</html>
```
iOS iBeaconは機内モードでも動く

https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/LocationAwarenessPG/RegionMonitoring/RegionMonitoring.html
The device is in Airplane mode and can’t power up the necessary hardware.

iOSで機内モードの場合 CoreLocation iBeacon　で Region への侵入を計測することは出来ません。
Bluetooth が機内モードで動くのは  CoreBuetooth　だからです。  CoreLoction は別扱い。
と書かれていますが、検出できます。
ただし、bluetoothが　接続解除状態の場合検出可能です。
<img width="375" alt="File (1).jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163299/0b37e6c6-c5fd-000e-9051-b897a989915c.jpeg">
上記完全オフ
<img width="375" alt="File (2).jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163299/d4ebaabb-1ce5-00b3-d1ff-053c9c4b2fa3.jpeg">
上記接続解除状態
proxy環境でWindowsにpipでJupiter Notebookをインストールする

# pipでJupiter Notebookをインストールする手順

この手順をproxy前提で実行する必要があり
https://qiita.com/adshidtadka/items/cb34d355a41a4a14ff68

要するに、git bashで以下をたたくだけでとても簡単だが、proxy前提に直す必要があり
```
$pip install -U pip
$pip install jupyter
$jupyter notebook
```

## proxy前提の手順
https://qiita.com/samunohito/items/40a03e1464899225e698
git bashなのでsetではなくexportにする
```
export HTTP_PROXY=http://user:pass@fooooo.proxy.local:8080
export HTTPS_PROXY=http://user:pass@fooooo.proxy.local:8080
```

## 権限エラーがなぜかでたので--userオプションを
https://qiita.com/HyunwookPark/items/4bad36ea9b5534594204
```
$pip install -U pip --user
```

## 所感
proxyにまだ消耗しているの？と言われそうだが、まぁ仕方なし。
PowerAppsでボタンが押されたら音を再生する方法２つ

# はじめに
PowerAppsのアプリでボタンが押されたら何かの音（音楽）を再生したいことがあって調べたので忘れないようにメモ

#PowerAppsで音を再生する方法
## メディアコントールを使う基本
Power Apps でのオーディオとビデオのコントロール
https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-audio-video

キャンバスアプリでオーディオコントロールを挿入します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176502/4e48ed00-2fa3-274b-24a1-e72aa0e5ed1d.png)
最初からサンプルミュージックが入っているので再生ボタンを押すと再生されます。
好みの音に変えたい場合はmp3などで音源を用意してアップロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176502/f287c031-45f9-9676-9b42-fd275722a591.png)
オーディオコントロールのMediaプロパティの内容をアップロードした音源のファイル名に変更できます。

##ボタンを押したら再生する方法１
ここからが本題です。メディアコントロールの再生ボタンではなく別のボタンを押したら再生する方法１

キャンバスにボタンを１つ挿入します。(ここではButton1と言う名前です)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176502/6be22c30-e9c4-6c9b-5d67-56175a7a6057.png)
メディアコントロールのStartプロパティを以下のように設定します。

```
If(Button1.Pressed,true,false)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176502/9d8ec642-e6d4-b657-c36b-af3c9d7a43cc.png)

これでボタンを押すと音の再生が始まります。
この方法簡単なのですが欠点があります。
・ボタンを離すと再生が止まる。再度押すと止まった場所から再生が始まる
・ボタンを押したときに状況によって再生する音の使い分けをしたい場合難しい(例えばクイズに正解したら「ピンポン」、間違ったら「ブー」のように)
このような問題を解決する為に次のように変数を使った再生が可能です。

##ボタンを押したら再生する方法２（変数を使う）
ボタンを押したときに変数を設定します。(ここではグローバル変数を使って解説しますがコンテキスト変数でも動作は同じです)
ボタンのOnSelectプロパティを以下のように変更します。

```
Set(playSound,true)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176502/cf503666-6cc3-ff97-9895-4267ee086c2d.png)

オーディオコントロールのStartプロパティを変更します。

```
If(playSound,true,false)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176502/c5edba5c-f4de-d7ab-bbf5-456fec113cc3.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176502/f3397356-1854-a920-072e-e095195e466d.png)

これでボタンを押すと音が最後まで再生されます。ボタンを押したときにif文などで条件を判断して再生する音を使い分けることも出来ます。
ここまでは簡単に出来たのですがハマった部分があります。
この設定だとボタンを押した1回目は音が再生されるのですが2回目以降はうんともすんとも音が出ません。
Resetプロパティとか色々いじったのですが解決せず。
ここで見つけたのがこのYoutube！神です。
https://www.youtube.com/watch?v=Tm_OtW1EQZ8&t=20s
MicrosoftのPowerApps担当の[Brian Dang](https://twitter.com/8bitclassroom)さんですね、ありがとうございます。

判明したのはここで使っている変数(playSound)の値をfalseに戻さないといけないという事です。
もう一つボタンを用意してset(playSound,false)のようにしても良いのですが、オーディオコントロールのOnEndプロパティを使う方法があります。OnEndには音の再生が終わった後の動作を書くことが出来ます。

```
Set(playSound,flase)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176502/dfbca32b-60a0-451a-6f71-7cfd827984a6.png)

これで何度ボタンを押してもちゃんと再生されるようになりました。

皆様良いPoweAppsライフを！
Containerの形を変形するやり方いろいろメモ

#### やりたいこと

下記のようなラベルを作りたい。
<img width="91" alt="ラベル" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81348/d6a054f9-d7e9-5bb8-add7-4c45f0145c5f.png">

#### CustomPaintを使ったやりかた

```dart:main.dart
Container(
  child: CustomPaint(
    painter: _TrianglePainter(),
    child: Container(
      padding: EdgeInsets.all(5),
      child: Text(
        "Label One",
        style: TextStyle(color: Colors.white),
      ),
    ),
  ),
),

class _TrianglePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    var paint = Paint();

    paint.color = Colors.blue;
    var path = Path();
    path.moveTo(0, 0);
    path.lineTo(size.width, 0);
    path.lineTo(size.width + 15, size.height / 2);
    path.lineTo(size.width, size.height);
    path.lineTo(0, size.height);
    path.lineTo(0, 0);
    path.close();
    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}
```

#### ShapeDecorationを使った例

```dart:main.dart
Container(
  padding: EdgeInsets.all(5),
  child: Text("Label Two", style: TextStyle(color: Colors.white)),
  decoration: ShapeDecoration(
    color: Colors.deepOrange,
    shape: _TagShape(),
))

class _TagShape extends ShapeBorder {
  @override
  EdgeInsetsGeometry get dimensions {
    return const EdgeInsets.only();
  }

  @override
  Path getInnerPath(Rect rect, {TextDirection textDirection}) {
    return getOuterPath(rect, textDirection: textDirection);
  }

  @override
  Path getOuterPath(Rect rect, {TextDirection textDirection}) {
    return Path()
      ..moveTo(rect.left, rect.top)
      ..lineTo(rect.left, rect.bottom)
      ..lineTo(rect.right, rect.bottom)
      ..lineTo(rect.right + 15, rect.top + rect.height / 2)
      ..lineTo(rect.right, rect.top)
      ..lineTo(rect.left, rect.top)
      ..close();
  }

  @override
  void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {}

  @override
  ShapeBorder scale(double t) {
    return null;
  }
}
```
【Breadcrumb NavXT】アクションフックによるパンくずリストのタイトル・URLの変更

※この記事では、Breadcrumb NavXT5.5.1を使用しています。


パンくずリストのカテゴリーのアーカイブページのURLを固定ページのURLに変更したいとの要望を受けたので、ちょっと調べて書いてみました。（というか、2年前にも同じ依頼があったので、公開できるように書き直しました。）

前提条件として、該当するカテゴリーのアーカイブページは使用しません。（表に出せない）



参考URL
http://notnil-creative.com/blog/archives/981


## タイトルのみ変更の場合

```function.php

<?php
function my_filter_breadcrumbs($bcnObj) {
    if ( count($bcnObj->trail) > 0 ) {
        for ( $i = 0; $i < count($bcnObj->trail); $i++ ) {
            if ( '変更前のテキスト' == $bcnObj->trail[$i]->get_title() ) {
            	$bcnObj->trail[$i]->set_template('変更後のテキスト');
            }
        }
    }
    return $bcnObj;
}
add_action('bcn_after_fill', 'my_filter_breadcrumbs');

```




## URLも変更する場合

```function.php

<?php
function my_filter_breadcrumbs($bcnObj) {
    $home_url = get_home_url( null, '/' );
    $url = $home_url . "/" . "変更したいURLのホームURL以下のアドレス";
    $new_url ='<a title="%ftitle%へ移動" href="'. $url .'"  class="%type%">変更後のタイトル</a>';
    
    if ( count($bcnObj->trail) > 0 ) {
        for ( $i = 0; $i < count($bcnObj->trail); $i++ ) {
            if ( '変更前のタイトル' == $bcnObj->trail[$i]->get_title() ) {
            $bcnObj->trail[$i]->set_template($new_url);
            }
        }
    }
    return $bcnObj;
}
}
add_action('bcn_after_fill', 'my_filter_breadcrumbs');

```
AWS基本事項メモ(主にEC2関連)

## AMI（Amazon Machine Image）とは
インスタンス軌道に必要なテンプレート情報が入ったOSのイメージ。サーバーのテンプレートのようなもの。

AWSやサードパーティのAMIの他、カスタムAMIを作ることも可能。
カスタムAMIから、複数のインスタンスを作ることも可能。

## インスタンス
EC2から立てられたサーバーのこと。
以下はインスタンスを立てる時に必要な事項


### インスタンスタイプ
サーバーのスペックを定義したもの。

「m5.xlarge」

 - インスタンスタイプの名前の表記の意味 
  - 「m」 インスタンスファミリー（どのような特徴を持ったインスタンスかの種類を表したもの）
  - 「5」 インスタンス世代（基本的に新しい世代がコスパが良い場合が多いので良い）
  - 「xlarge」　インスタンスサイズ(スペックの高低)

### ストレージ
サーバにくっつけるデータの保管場所。EC2には以下の２種類ある。

 - **EBS**
  - 高い可用性と耐久性を持つストレージ
  - 他のインスタンスにも付け替え可能。
  - EC2インスタンスをstop/terminateしてもデータの保持が可能。
  - S3を利用してスナップショットを保存可能
  - EBSの別料金がかかる
  - OSやDBなどの永続性と耐久性が必要なデータを置く
  - HDDのようなイメージ
 - **インスタンスストア**
  - インスタンス専用のストレージ
  - 他のインスタンスには付け替えられない。
  - EC2インスタンスをstop/terminateするとデータが消える
  - 追加料金はかからない。
  - 無くなってはいけないデータは置けない
  - 一時ファイル、キャッシュなど無くなっても構わないデータに向く

## ssh
SucureShellの略で安全に通信を行って、ネットワークに接続された機器を遠隔操作するための通信手段。
例えば自分のPCからssh接続でAWSのEC２インスタンス内に入って作業することができる。
サーバ内に公開鍵を置いておき、秘密鍵を持っているユーザーのみ接続できる。
EC2作成時に作成できるpemファイルなどはこの秘密鍵

## ポート番号
IPアドレスがインターネット上でコンピュータを識別するアドレスなのに対して、ポート番号は**プログラムのアドレス**と表現することができる。
プログラムごとにポート番号が決まっている。
IPアドレスで、コンピュータを指定し、ポート番号でそのコンピュータ内の通信したい対象のプログラムを指定するイメージ。

例えばssh接続であるサーバに接続したいとき、

 - 接続したいサーバAのIPアドレス　
  - 178.10.0.10
 - そのサーバで走っているプログラムにそれぞれ割り当てられているポート番号が
  - sshサーバ（具体的にはsshdプログラム）が22
  - httpプログラム（サーバ）が80
  - SMPTプログラム（サーバ）が25

の場合、 
自分のPCから「178.10.0.10」のポート番号22に接続する」と通信を送ると、サーバAにssh接続する。

ウェルノウンポート番号と言って大体のサーバで標準的なポート番号は決まっていて、例えば

  - sshサーバが22
  - httpサーバが80
  - httpsサーバは443
  - SMPTサーバが25
  - 
  以上となっている。逆にクライアント側ポート番号は動的に決められたりする。

##### サーバ内で以下のコマンドを実行すると、ポート番号一覧が取得できる。

```bash

sudo lsof -i -n -P //-n:ipアドレスとホスト名に変換しないオプション -P：ポート番号をサービス名に変換しないオプション。
```

## ファイアーウォール設定（セキュリティグループ）
ES２インスタンスではセキュリティ上デフォルトではインバウンド（サーバへのアクセス）はssh接続のポート番号２２への接続以外は受け付けないようになっている。サーバを外部公開するにはhttp８０かhttps４４３のポートへの接続を受け付けるようにセキュリティグループを設定する必要がある。

## Elastic IP アドレス
インターネット経由でアクセス可能な固定グローバルIPアドレスを取得し、インスタンスに付与できるサービス。

通常EC2インスタンス（サーバ）は停止、再起動をするとIPアドレスが変わってしまう為、これでIPアドレスを固定化する。
インスタンスを削除するまでは基本的にずっと固定のIPアドレスを使える。
**インスタンスと紐づけられていて、そのインスタンスが起動中は無料、そうでない場合は有料**。
逆に言うとElastic IPアドレスが使われていないと課金されるので、使わない場合は都度解放する。

## キャパシティの予約
例えばインスタンスを大量に立ち上げようとすると割り当ての容量が立ちなかったりするとうまくいかない場合がある。
事前にキャパシティの予約をすると、それを防ぐことができる。
【ふくだ学習録】インフラエンジニア教本-ネットワーク構築技術解説- part1【51日目】

# ふくだ学習録とは？
ふくだが学習したことの備忘録。
目に見える形で残すことによってやる気を出す個人的な作戦です。
他人に見せるように書いているわけではないので、すごく読みにくいです。

### 読了した本
[データベースエンジニア養成読本 [DBを自由自在に活用するための知識とノウハウ満載!]](https://www.amazon.co.jp/dp/4774158062/ref=cm_sw_r_tw_dp_U_x_FPphEbQFPPQRM )
[ゼロから作るDeepLearning](https://www.amazon.co.jp/dp/4873117585/ref=cm_sw_r_tw_dp_U_x_y3UiEbF3P6EG7)
[PHPフレームワーク CakePHP 3入門](https://www.amazon.co.jp/dp/4798048577/ref=cm_sw_r_tw_dp_U_x_.YIjEbS1K4JY2)
[SQLアンチパターン](https://www.amazon.co.jp/dp/4873115892/ref=cm_sw_r_tw_dp_U_x_XlokEbJQK2N7P )
[Docker入門](https://www.amazon.co.jp/dp/480071091X/ref=cm_sw_r_tw_dp_U_x_xVlmEbN8ZTHZK)
[PHPフレームワーク Laravel入門](https://www.amazon.co.jp/dp/4798052582/ref=cm_sw_r_tw_dp_U_x_zzeyEbJBSQ5Y0 )
[サブスクリプション](https://www.amazon.co.jp/dp/B07HY1TND9/ref=cm_sw_r_tw_dp_U_x_lp4NEb3EEGD7Y)
[Running Lean ―実践リーンスタートアップ](https://www.amazon.co.jp/dp/4873115914/ref=cm_sw_r_tw_dp_U_x_UsMREbV6Y41Y8)

### 今読んでいる本

[Running Lean ―実践リーンスタートアップ](https://www.amazon.co.jp/dp/4873115914/ref=cm_sw_r_tw_dp_U_x_UsMREbV6Y41Y8)

# ここ最近してたこと
ここ最近更新が止まってましたが、下記の理由からになります。

1. **本業で必要なことが汎用的な知識ではなかったので、インプットアプトプットがしずらかった。**（業務効率化や個別案件対応、ドメインが絞られた知識やレガシーな技術対応　などが多かった）
2. **ビジネス書を中心に読んでいて、Qiitaに都度書くような内容ではなかった。**（エンジニアリングの知識向上よりも、プロジェクトをどう回すかという視点が必要になってきたため）
3. **友人とのスモールビジネスの立ち上げ構想がちょっと立て込んでいた。**（プライベートな時間は結構ここに割いてた）

まぁ1.3に関しては、これからも割と継続的にそういった状態になると思うのですが、2のインプットを止めようと思う（現時点で必要な考え方とかは結構入れれた気がしている）ので、その分の時間を再度この学習録を再開しようと思います。

ちなみに読んでた本はこちら。

[amazon 世界最先端の戦略がわかる](https://www.amazon.co.jp/dp/4478105057)
[Lean Analytics ―スタートアップのためのデータ解析と活用法](https://www.amazon.co.jp/dp/4873117119)
[ピーター・ティール 世界を手にした「反逆の起業家」の野望](https://www.amazon.co.jp/dp/4864106010)
[Airbnb Story 大胆なアイデアを生み、困難を乗り越え、超人気サービスをつくる方法](https://www.amazon.co.jp/dp/4822255190)
[ユダヤの商法](https://www.amazon.co.jp/dp/4584139008)
[サーバ/インフラを支える技術](https://www.amazon.co.jp/dp/4774135666)
[How Google Works](https://www.amazon.co.jp/dp/4532198348)
[イシューからはじめよ](https://www.amazon.co.jp/dp/4862760856)
[ピクサー流　創造するちから](https://www.amazon.co.jp/dp/4478016380)
[ストーリーでわかる財務３表超入門](https://www.amazon.co.jp/dp/4478015619)

※なんか記載してみたら本数めちゃ少ない気がするから、覚えてないやつあると思う。



# 第1章 DHCPサーバーの教科書
### DHCPとは
Dynamic Host Configuration Protocolの略。
ネットワーク接続する際に、動的にIPアドレスを割り当てる仕組み。

# 今日の一言
振り返りしてたら全然読書できひんかった泣
明日からしっかり再開！
Rust言語の勉強を初めてみた。

# サンプルコード 2.Programming a Guessing Gameを実行してみる

プロジェクト作成

```
$ cargo new guessing_game
```

src/main.rsを編集

```rust:main.rs

use std::io;

fn main() {
    println!("Guess the number!");

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

    println!("You guess: {}", guess);
}
```

`use std::io;`

標準入出力を使用するライブラリ

`fn main() {`

fnで関数定義。
main()関数が最初に実行される。
{}の中に本体を記述する

`println!()`
改行付き標準出力するマクロ

`let mut guess = String::new();`

letで変数定義を行う。
mutは可変変数である事を示す。
mutが無い場合は不変である事を示す。
string::new()でstring型のインスタンスを作成する。

`io::stdin()`
`        .read_line(&mut guess)`
`        .expect("Failed to read line");`

io::stdin()で標準入力を使用する。
read_line()で標準入力を取得する。
&は引数の参照渡しである事を示す。
read_line()はio::Resultを返すので、Errが返ったときに、
expect()は標準入力にエラーが発生した場合のエラーメッセージを記述する
expect()を書かないとビルドエラーになる(めんどくさい)

`println!("You guess: {}", guess);`

文字列のフォーマット。
{}の部分に第二引数以降の値が挿入される。

```
PS C:\Users\...\source\repos\guessing_game> cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.04s
     Running `target\debug\guessing_game.exe`
Guess the number!
Please input your guess.
taki
You guess: taki

PS C:\Users\...\source\repos\guessing_game> 
```
フォークしたリポジトリを最新化する方法

## フォークしたリポジトリを最新化したい

GitHubでOSSに参加する時、

1. GitHub上の対象のOSSのリポジトリをフォークしリモートリポジトリを作成
2. GitHub上のリモートリポジトリをローカルにクローン
3. ブランチを切って修正して commit
4. リモートリポジトリへ push してプルリク

のような流れを踏むと思います。

ただ、自分が修正を進めている間にも、フォーク元のオリジナルのリポジトリでは開発が進んでいます。
プルリクがコンフリクトしないように4.の前にオリジナルのリポジトリの開発差分をマージする必要があります。

その方法を調べたのでメモします。

## 方法

```sh
# ローカルのリポジトリに移動
$ cd ローカルのリポジトリのパス

# GitHubのフォーク元のリポジトリをリモートブランチに追加する
$ git remote add upstream https://github.com/フォーク元オーナー名/フォーク元リポジトリ名.git

# リモートブランチの一覧を確認するとのフォーク元のリポジトリが追加されている
git remote -v
> origin    ...
> origin    ...
> upstream  https://github.com/フォーク元オーナー名/フォーク元リポジトリ名.git (fetch)
> upstream  https://github.com/フォーク元オーナー名/フォーク元リポジトリ名.git (push)

# フォーク元の master ブランチの変更差分をフェッチます。
# ※upstream/master に保管されます
$ git fetch upstream

# masterブランチをチェックアウトしフォーク元の差分をマージします。
$ git checkout master
$ git merge upstream/master

# 最後に自分の修正ブランチに master ブランチを取り込みます。コンフリクトが出たら解決します。
$ git checkout 自分の修正ブランチ
$ git merge master
```

上記でフォークしたリポジトリを最新化し修正差分も自分のブランチに取り込めたのでコンフリクトなくプルリクを送ることができます。


## 参考

フォークにリモートを設定する
https://docs.github.com/ja/github/collaborating-with-issues-and-pull-requests/configuring-a-remote-for-a-fork

フォークを同期する
https://docs.github.com/ja/github/collaborating-with-issues-and-pull-requests/syncing-a-fork
[JavaScript] express() で "express is not a function" ついでに TS の "export =" と "import = require()"

Expressを読み込んだ際に `express is not a function` と出たので原因と解決策を共有します。
あとついでに `export =`　と `import = require()` についてです。

## 環境
import側はJavaScript、トランスパイルとかは一切なし。
node: v14.1.0

```:package.json
{
  ...
  "type": "module",
  "dependencies": {
    "express": "^4.17.1",
    ...
  },
  ...
}
```

## 再現VTR

```js:index.js
import * as express from "express";
const app = express();
```

```shell
node ./index.js
```

実行すると

```
const app = express();
            ^
TypeError: express is not a function
```

エラー発生。
因みに、USAGEに従っていた。:thinking: [^1]

```js:@types/express/index.d.ts
/* =================== USAGE ===================

    import * as express from "express";
    var app = express();

 =============================================== */
```

[^1]: ある時点のソース  https://github.com/dfrankland/DefinitelyTyped/blob/06002c2c495359eb97c48f71f3f6bcbac25c7225/types/express/index.d.ts

## 原因
CommonJSとESModulesdでの返却される値に違いがあった。

```js
import * as express from "express";
console.log(typeof express); // => object
```

```js
const express = require("express");
console.log(typeof express); // => function
```


```:import
default: [Function: createApplication] { ... }
```

```:require
[Function: createApplication] { ... }
```

つまり、返却値が**default export**となっているため参照エラーを起こしていた。


## 対応策
こうする。

```js
import express from "express";
const app = express();
```

または、

```js
import * as express from "express";
const app = express.default();
```

そもそもトランスパイルもせずに使うなら、CommonJSモジュールならCommonJSのインポート （`const express = require("express");`） で読み込むのがベターっていうツッコミは覚悟してます！

## 以降おまけ

## `export =` 
名前は「エクスポート代入」。
Expressの型定義ファイルを覗くとこうなっている。

```js:@types/express/index.d.ts
...
/**
 * Creates an Express application. The express() function is a top-level function exported by the express module.
 */
declare function e(): core.Express;
...
export = e;
```
CommonJSとAMDを考慮せずにエクスポートできる便利機能。
言い換えればこれは、CommonJSまたはAMDのモジュールだよ。っていう印。

## `import = require()`
名前は「インポート代入」。
エクスポート代入（`export =`）を使用してエクスポートされたモジュールをインポートする際に使われていたらしい。
今ではあえて使う理由はなさそう。


## インポート代入、インポート代入の使用例

```js
// export側
function hoge() {
  return 'hoge';
}
const fuga = 'fuga';
export = {
  hoge,
  fuga
};

// import側
import hogefuga = require('./export');
console.log(hogefuga); // => { hoge: [Function: hoge], fuga: 'fuga' }
```

## 感想
地味に時間を溶かしたため記事にしてやったぜ。。
何かご指摘等あればコメントいただけると幸いです。

## 参考
[export = and import = require()](https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Modules.md#export--and-import--require)
ゼロから始めるLeetCode Day85 「6. ZigZag Conversion」

# 概要

海外ではエンジニアの面接においてコーディングテストというものが行われるらしく、多くの場合、特定の関数やクラスをお題に沿って実装するという物がメインである。

どうやら多くのエンジニアはその対策としてLeetCodeなるサイトで対策を行うようだ。

早い話が本場でも行われているようなコーディングテストに耐えうるようなアルゴリズム力を鍛えるサイトであり、海外のテックカンパニーでのキャリアを積みたい方にとっては避けては通れない道である。

と、仰々しく書いてみましたが、私は今のところそういった面接を受ける予定はありません。

ただ、ITエンジニアとして人並みのアルゴリズム力くらいは持っておいた方がいいだろうということで不定期に問題を解いてその時に考えたやり方をメモ的に書いていこうかと思います。

[Leetcode](https://leetcode.com/)

Python3で解いています。

[ゼロから始めるLeetCode 目次](https://qiita.com/KueharX/items/6ee2502c3b620c795b70)

前回
[ゼロから始めるLeetCode Day84「142. Linked List Cycle Ⅱ」](https://qiita.com/KueharX/items/5ce08b24aaf74e44951c)

[Twitter](https://twitter.com/kueharx)やってます。

**[技術ブログ](https://kueharx.blogspot.com/)始めました！！**
技術はLeetCode、Django、Nuxt、あたりについて書くと思います。**こちらの方が更新は早いので**、よければブクマよろしくお願いいたします！

# 問題
[6. ZigZag Conversion](https://leetcode.com/problems/zigzag-conversion/)

難易度はMedium。
前回に引き続き、[コーディング面接対策のために解きたいLeetCode 60問](https://1kohei1.com/leetcode/)からの抜粋です。

問題としては、まず、文字列、例えば`PAYPALISHIRING`という文字列が与えられます。それらの文字列は以下のように所定の行数にジグザグパターンで書かれています。(読みやすくするために、このパターンを固定フォントで表示するとよいでしょう)

```
P   A   H   N
A P L S I I G
Y   I   R
```

これらを一行ずつ読んでいくと `"PAHNAPLSIIGYIR"`となります。

これらの文字列を受け取り、行数を指定して変換するコードを書きます。

`string convert(string s, int numRows).`

>Example 1:

>Input: s = "PAYPALISHIRING", numRows = 3
Output: "PAHNAPLSIIGYIR"

>Example 2:

>Input: s = "PAYPALISHIRING", numRows = 4
Output: "PINALSIGYAHRPI"
Explanation:

```
P     I    N
A   L S  I G
Y A   H R
P     I
```

そういえば問題集のリストをクローンしたのですが、僕が解いた問題は今の時点で23/60でした。

こんな感じ↓
![スクリーンショット 2020-07-13 2.43.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/412789/a3af1253-bb83-bb9f-d094-d2b6ba30e70c.png)

楽しく解くことを大事にしているので無理しない範囲で全部回答していきたいですね。
あ、あと地味にサブスクライブしないと解けない問題があったりします。
それはそもそも僕がサブスクライブしていても問題を載せることは権利的にどうなんだってことでスキップさせてもらいます。


#解法

```python
class Solution:
    def convert(self, s: str, numRows: int) -> str:
        if numRows == 1 or numRows >= len(s):
            return s
        num,step,ans = 0,0,['']*numRows
        for w in s:
            ans[num] += w
            if num == 0:
                step = 1
            elif num == numRows - 1:
                step = -1
            num += step
        return "".join(ans)
# Runtime: 64 ms, faster than 65.13% of Python3 online submissions for ZigZag Conversion.
# Memory Usage: 14.1 MB, less than 10.13% of Python3 online submissions for ZigZag Conversion.
```

`numRows`の数の分だけ配列を用意してそれぞれ追加していきます。追加した後に`step`で増減を管理することで、例えばテストケースの

`PAYPALISHIRING`

場合は以下のように配列に挿入できていることが分かります。

```
['PAHN', 'APLSIIG', 'YIR']
```

これを最終的に`''`で``join()`してあげれば無事にstrの`"PAHNAPLSIIGYIR"`が返される、といった具合です。

これで24/60ですね。
では今回はここまで。お疲れ様でした。