OmniAICreator/Qiita-1.07M huggingface dataset:
[小ネタ] Coqの eq が Leibniz Equality である件

よく知られているように、CoqをはじめとするLofの型理論では、等値性は「自身との間にのみ等値性が成立する」で定義されます。一方哲学者ライプニッツは「すべての性質を同じくするものの間には等値性が成立する」といったらしいです。

これも知られていますが、この二つの等値性は（型理論の枠組みでは）一致することが証明できるので、Coqの等値性とはライプニッツの等値性のことであるといわれます（特にJMeq などほかの等値性との比較の文脈で意識されます）。

にもかかわらず（いや、だからこそ？）Coqの標準ライブラリでは、ライプニッツの等値性に相当するInductiveな定義は存在しません。それに関連するLemmaが登録されているだけです（例えば `f_equal` や `eq_ind_r` がそれに相当するでしょう）。

本稿では、Coqのeq とライプニッツの等値性をCoq上で表現し、それらが同じものであることを直接証明してみます。なお、[SoftwareFoundationsでも似た議論がありますが](https://softwarefoundations.cis.upenn.edu/lf-current/ProofObjects.html#X:85) 、もう少し徹底してやってみます。

coqでのeq 相当を `eq1`, ライプニッツの等値性を `eq2` として同値性は以下のように証明できます:


```coq
(* Lob Equality = Definitional equality *)
Inductive eq1 {A:Type} (x:A) : A -> Prop :=
  | eq1_refl: eq1 x x.

(* Leibniz equality *) 
Inductive eq2 {A:Type} (x:A) : A -> Prop :=
  | eq2c y: (forall P, P x <-> P y) -> eq2 x y.

Theorem eq1_eq2_eq: forall (A:Type) (x y : A), eq1 x y <-> eq2 x y.
Proof. split.
 intros []. pattern x. constructor. intros. split;trivial. 
 intros [z [H _]]. pattern z. apply H. apply eq1_refl.
Qed.
```

所感

- 型理論の枠組みで、Definitionalな等値性とライプニッツの等値性が同じだと証明できるという事実は、型理論を扱う限りはそれらを同一視できることをいうだけであり、ライプニッツの等値性をその等値性に還元できたということを意味しない、と思われる。意地悪ないいかたをすれば、型理論においてライプニッツの等値性はそのようなものとして粗視化・単純化して扱われているだけだ、という主張は通るのだろうか？
- 例えば Coq 上では 1+1=2 だが左辺は簡約できるという性質をもっているが、右辺は簡約できないという性質を持っている。したがって1+1と2は異なる性質をもち、区別できるとライプニッツはいうことができるはず。だから何だという話ではあるが……
Microsoft Office アプリでの CO2 の入力方法

# 背景
CO2 (シーオーツー) をOffice 文書で入力する機会も増えたのではないかと思います。でも、実際には「2」は下の方に表現したいですよね。その方法を共有します。
ネットで検索してもなかなか出てこないと思いますので。

# 必要なもの

お手元の Microsoft Office!
バージョン不明...相当前のものでも出来ると思いますが調べきれん😅

# 手順

元のスライドです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108143/e210aeda-8f56-392b-fde8-4a540163b8d0.png)

1. 当該文字を選択します。この場合は「2」のみですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108143/df899215-54ff-d800-4630-417126b1f50f.png)

2. リボンメニューの[フォント]の拡張用表示を選択します。右クリックで表示されないので、毎回こちらで。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108143/0b3e3915-1b2a-517a-ec20-01b088f5fcec.png)

3. フォントダイアログの中の[文字飾り]-[下付き]を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108143/6f5d45a3-2609-40aa-1e46-3e9ae4632190.png)

完成!

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108143/f9c1422b-362c-cd79-c9de-beaec3dd9c3f.png)

ショートカットキーを覚えちゃったほういいですね。

```
Alt + H + F + N + B + Enter.
```

コナミコマンドの様だ...(どこが!?)

Word でも Excel でも Outlook でも操作方法は一緒。

Word:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108143/60d42757-5f89-8a40-bd0e-01a15f38e8dc.png)

Excel:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108143/e58fad45-bc8b-8feb-3154-25d3fb634674.png)

Outlook:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108143/bb1cf95b-12ea-606f-5751-be6f0afb8bf6.png)


# まとめ

知ってしまえば大したことはありません。簡単ですよね。

因みによく使う記号で、平方メートル (m2)とか、立方メートル (m3) などは「上付き」で出来ます。

是非試してみてくださいね。
Haar-like特徴量を用いた直線検出

# Pythonを使って直線検出をする
## 環境
・Ubuntu18.04
・Python3

```Python:
import numpy as np
import cv2
import itertools


def calc_haarlike(crop_img):
    crop_img = crop_img[:,::-1]

    threshold = 10
    rect_w = 8
    pattern_w = rect_w // 2 
    width = crop_img.shape[1]
    
    peak_index = 0
    max_value = 0
    
    for index in range(width-rect_w):
        a1 = np.mean(crop_img[:, index: index+pattern_w])
        a2 = np.mean(crop_img[:, index+pattern_w: index+rect_w])
        H = a1-a2 
        if max_value < H and H - max_value > threshold:
            max_value = H
            peak_index = index

    index = width - peak_index + rect_w
    return index if max_value > 0 else -1

def candidate_extraction(img):
    n = []
    h, w = img.shape[0], img.shape[1]
    wh = 8 #window height
    window_pos = np.array([
        [[400, 600], [500, 700]],
        [[380, 580], [520, 720]],
        [[360, 560], [540, 740]],
        
        [[700, 900], [800, 1000]],
        [[680, 880], [820, 1020]],
        [[660, 860], [840, 1040]],
    ])

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 
    for i, ((x1, y1), (x2, y2)) in enumerate(window_pos):
        crop_img = gray[y1: y2, x1: x2]
        peak_index = calc_haarlike(crop_img)
        cv2.imwrite("./img_gray_"+str(i)+".jpg", crop_img)
        cv2.line(img,pt1=(x1, y1), pt2=(x2,y2),color=(0,0,255),thickness=10)
        if peak_index != -1:
            cv2.circle(img, (x1+peak_index, y1), 5, (255, 0, 0), thickness=1)
            n.append([x1 + peak_index,y1])

    t = itertools.combinations(n,2)
    for i in t:
        print(i)
        a,b = linear(i[0],i[1])
        if a != 0:
            cv2.line(img,pt1=(0,b), pt2=(int((len(img) - b)/a),len(img)),color=(0,255,0),thickness=10)
            cv2.line(img,pt1=(0,b), pt2=(len(img[0]),int(len(img[0])*a + b)),color=(0,255,0),thickness=10)
        else:
            cv2.line(img,pt1=(0, int(b)), pt2=(len(img[0]),int(b)),color=(0,255,0),thickness=10)
    cv2.imwrite("./img.jpg", img)

def linear(n1, n2):
    a = (n2[1]-n1[1])/(n2[0]-n1[0])
    b = int((n2[0]*n1[1]-n1[0]*n2[1])/(n2[0]-n1[0]))
    return a, b

def main():
    img = cv2.imread("./data/path.jpg")
    candidate_extraction(img)

if __name__ == '__main__':
    main()
```
## 参考
こちらの方のプログラムを参考にしております。

> https://nsr-9.hatenablog.jp/entry/2021/08/15/120000
bouncyCastleのrijndealクラスをpowershellで再現する

## 目的

bouncyCastleで使われていたjavaのrijndealクラスによる暗号化、復号処理をpowershellで再現する内容

鍵長は256ビットになっていなければならない。

## rijndealとは
暗号化処理の規約になったAESとして採用された暗号化方法

IV（Initial Vector）と内容を暗号化した文字列を使って
暗号化文字列が作られている。

この時暗号化した文字列(鍵長)を128ビット、192ビット、256ビットの3つを選ぶことができた。

AESは統一化のために、128ビットで固定化されている。

## RijnDealクラスをPowerShellで実装する際の注意

powershellは.NETで構成されているが
AESの暗号化のロジックだとうまく作れない。
rijndealだと鍵長を256ビットにできるが、AESでは128ビット固定になってしまう。128ビット以外はエラーになってしまう。
https://sp.otsuka-shokai.co.jp/words/aes.html#:~:text=AES%E3%81%A8%E3%81%AF,%E9%AB%98%E3%81%84%E3%81%A8%E3%81%84%E3%82%8F%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%80%82

まあAES自体が鍵長を128bitに固定して、単純化と互換性を担保させてるらしいから、rijndealのように128bitや256bitも可能としてるのが古い規約のようだ。

今回はbouncyCastleで使われていたrijndealクラスによる暗号化処理をpowershellで再現する内容だったので
鍵長を32byte、つまりは256bitにしなくてはいけない。
だからAESクラスでは実装が不可能なので、推奨されていない昔のrijndealクラスを使って実装する必要がある。

## PowerShellで実装する際の注意
実装としては、AESとほぼ一緒で使うクラスがrijndealになり
keysizeを256にするだけである。
http://programmers.high-way.info/vb/aes.html

keysizeを128bit以外にしなくてはならない時はrijndealを使おう。

おそらく元々の仕様に合わせる…などない限りはAESの規格で暗号化することになると思う。

正直今回のニーズが出ること自体稀だと思うので、もし同じような人がいたら参考にして欲しい。

### おまけ
powershell coreで組むことは不可能です。
https://qiita.com/MuscleProgramer/items/99182c89a42a34826cd4
GNU

GNUについて。
音声ファイル。
オペレーティングシステムであり、かつコンピュータソフトウェアの広範囲に渡るコレクションと言えます。

ワードプレスを知る時に必須ではないですが、頭に入れておくとよいと思います。

GNUは完全にフリーソフトウェアから構成されているんです。
JPAとは

1. JPA（Java Persistence API）
   Javaプログラミング時、永久にデータを保存するために必要なAPI。
   API（Application Programming Interface）とは、ソフトウェアやアプリケーションの機能を他のソフトウェアと共有すること。共有する側とされる側の上下関係が存在。
   JPAはJavaでクラスを作成し、それに基づいてDBテーブルを自動生成する技術
2. ORM（Object Relational Mapping）機能
   オブジェクトをDBに接続する方法で、まずJavaでデータを作成し、それをDBデータにしてくれる機能。
3. JPA機能の1つであるCRUD関数を使用すれば、面倒なロジックを必要とせずにJavaからDBに簡単にデータ変換が可能になる。
   CRUD:Create(Insert),Read(Select, Select All),Update(Update),Delete(Delete)
4. Persistence Context 永続性コンテキスト
   contextはあるもののすべての情報を示す言葉。Javaは永続性コンテキスト機能でDBにデータを処理し、DBも永続性コンテキストを通してデータをJavaに渡す。
【VSCode】拡張機能の再インストールを回避する【Remote - Containers】

## 拡張機能の再インストールを回避する

### `Dockerfile`を編集する
ユーザーがroot以外の場合にはDockerfileに以下を追記します。
```Dockerfile
ARG USERNAME=ユーザー名
RUN mkdir -p /home/$USERNAME/.vscode-server/extensions \
        /home/$USERNAME/.vscode-server-insiders/extensions \
    && chown -R $USERNAME \
        /home/$USERNAME/.vscode-server \
        /home/$USERNAME/.vscode-server-insiders
```

### `docker-compose.yml`を編集する
`docker-compose.yml`を使用している場合には以下を追記します。
```docker-compose.yml
volumes:
  - vscode-extensions:/home/ユーザー名/.vscode-server/extensions
  - vscode-extensions-insiders:/home/ユーザー名/.vscode-server-insiders/extensions

volumes:
  vscode-extensions:
  vscode-extensions-insiders:
```


### `devcontainer.json`を編集する
`docker-compose.yml`を使用していない場合には`devcontainer.json`に以下を追記します。

ユーザーがrootの場合
```devcontainer.json
  "source=vscode-extensions,target=/root/.vscode-server/extensions,type=volume",
  "source=unique-vol-name-here-insiders,target=/root/.vscode-server-insiders/extensions,type=volume",
```

ユーザーがroot以外の場合
```devcontainer.json
  "source=vscode-extensions,target=/home/ユーザー名/.vscode-server/extensions,type=volume",
  "source=unique-vol-name-here-insiders,target=/home/ユーザー名/.vscode-server-insiders/extensions,type=volume",

```
deno deploy beta 4発表！価格設定をcloudflare workersと比較する

2022年5月、[deno deploy beta 4](https://deno.com/blog/deploy-beta4)が発表されました！

https://deno.com/blog/deploy-beta4

その中で、deno deployの価格設定も今回初めて公表されました。

まだ今後も機能が追加されるということで、詳細については発表されていない部分も多そうですが、とりあえず**同種のサービスである[cloudflare workers](https://workers.cloudflare.com/)と比較**してみたいと思います。


## 無料枠

それぞれのサービスには無料枠があります。
まずは無料枠の範囲内でできることを比較してみたいと思います。

:::note warn
公式ドキュメントを参照して書きましたが、リストは不完全です。足した方がいい情報や間違っている情報がありましたら、コメントしていただけると有難いです。
:::

||cloudflare workers|deno deploy|
|--|--|--|
|リクエスト数|10万/日|10万/日|
|CPU時間|10ms|10ms（平均とのこと）|
|データセンター数|270|32（今後追加するとのこと）|
|コールドスタート|0ms|?|
|メモリ|128 MB|?|
|プロジェクト個数|最大30（今後増加とのこと）|?|
|TypeScript実行|×（事前にトランスパイルすれば可）|○|
|wasm実行|○|○|
|JSX実行|?（事前にトランスパイルすれば可）|○|
|GitHub統合|?|○（privateリポジトリ可）|
|Worker間の通信|?|○（BroadcastChannel API）|
|対応Web APIの数|?|?|
|静的ファイル|×（kvを利用すれば可能）|○|
|環境変数|64個まで|?|
|デフォルトで与えられるドメイン|*.workers.dev|*.deno.dev|
|カスタムドメイン|○|○|

deno deployのCPU Timeについては、コアチームがdiscordに書いているところによると、平均で計算されるようです。つまり、CPU Timeが10msを超えたからといっていきなりシャットダウンされるわけではないそうです。（他のリクエストが10msを下回っている場合、それと平均して10ms以下になればOK）

## 有料枠

続いて、有料プランの価格設定と機能を比較していきたいと思います。

||cloudflare workers|deno deploy|
|--|--|--|
|基本価格|$5/月|$10/月|
|リクエストに対する価格|100万リクエストあたり$0.15/月|500万リクエスト/月までは追加料金なし。<br>500万リクエストを超える分は、100万リクエストあたり$2/月。|
|CPU時間|?|50ms（平均とのこと）|
|データ転送|?|100GiBまで追加料金なし。<br>超過分は1GiBあたり$0.3|
|プロジェクト個数|最大100|?|

有料枠の価格については基本価格+リクエストに対する価格で計算されます。
価格設定については**cloudflare workersのほうが安い**ようです。
ただしdeno deploy側は`More features coming soon`と書かれているため、機能面がこれから強化されていくものと思われます。

また後述の通りcloudflare workersは裏側のS3やlambdaやGoogle cloud Functionと連携するような使い方がメインになると思います。対するdeno deployは、それだけでバックエンドの処理を完結させるような使い方になるので、**全体でみるとdeno deployのほうが安くなる場面もなくはない**のだと思います。



## 使い方や特徴の比較

実際に両者を使ってみて分かったのですが、両者のサービス内容は似通っているものの、ターゲットとする領域がそれぞれ違うように感じられました。
以下にそれぞれの特徴を対比させて書いています。

|cloudflare workers|⇔|deno deploy|
|:-:|:-:|:-:|
|既存バックエンドに付加する形で使う|⇔|サーバーサイドの処理をここで全て捌く|
|（裏側にメインのサーバーがあることが前提で）**BFFとして使う**|⇔|（BFFではなく）**ここで処理を全て完結させる**|
|既存フレームワーク（Next.js等）からのスムーズな移行が可能|⇔|Freshなどの、CDN Edgeで動くことを前提とした新世代のフレームワークが使える|
|（npm installなど）他のNode.jsアプリケーションと同じような開発体験|⇔|import文にURLを書けばライブラリが使える＆TSが動くので、ローカルでビルドする手間が無い|
|kvやドメインなどの**cloudflare社のサービスとの連携**がスムーズ|⇔|**GitHubと連携**したサービスが便利<br>（例：GitHubリポジトリに置いたファイルが静的ファイルとして読める／連携にあたって特に設定の必要なし など）<br>（kvなどはこれから実装予定）|
|導入事例や日本語記事が多数ある|⇔|[deno deployの足回りを切り出してエンタープライズ向けに製品化したもの](https://deno.com/deploy/subhosting/docs/architecture)が、サーバーレスサービス各社のプラットフォームとして使われている|

基本的には **既存Node.jsアプリケーションに付加する形で使うcloudflare workers** と **Edgeで処理を完結させるdeno deploy** という使い分けになりそうです。
それに加えて、サービス継続年数の違いによる機能や情報量の差などが選択のポイントになってきそうです。

:::note
個人的にはdeno deployが足回りを同業他社向けに製品化しているのが面白いなと思いました。これで競合相手を減らしつつ、企業向けなので安定した収益が望めるということなのでしょう。
:::

## 感想

さて、ここまで両者を対立させるような形で書いてきましたが、実はcloudflare workersとdeno deployはアーキテクチャ上の共通点も多く、ほぼ同じようなことができます。
また、両者は[wintercg](https://wintercg.org/)というワーキンググループを共同で立ち上げ、Web APIの標準化/共通化にも取り組んでいます。

https://github.com/wintercg

両者とも普通のWebサービスを立ち上げるのには十分な機能がそろっており、production readyと考えていいです。

何よりEdge workerのいい点は**気軽にサーバーを建てたり消したりできる**という点です。
両者とも無料プランがあるので、迷ったらまずは試してみることをオススメします！
【ShellScript】意地悪なファイル名があっても安心してループ処理したい

## よく見る方法

`/path/to/hoge`内に存在するファイル１件１件に対して何らかの処理をしたい時。
素直に書くならこんな感じ。


```bash:proc_hoge.sh
#!/bin/bash 

find /path/to/hoge -type f | while read x; do
  echo "do something...: ${x}!!"
done
```

### 問題点

ファイル名に特殊な文字が含まれていた場合、、、？

```bash
tree /path/to/hoge
# => /path/to/hoge
#    ├── 'aaa   '     :末尾に空白が含まれている
#    ├── 'bbb\012ccc' :改行コード(\012)が含まれている
#    └── 'ddd\eee'    :バックスラッシュが含まれている
```

ファイル名を正しく取得することができなかった。

```bash
./proc_hoge.sh
# => do something...: /path/to/hoge/bbb!!
#    do something...: ccc!!
#    do something...: /path/to/hoge/aaa!!
#    do something...: /path/to/hoge/dddeee!!
```

### 要因

findによって各ファイル名が改行区切りで出力されているが、その内容をreadが正しく取得できていない。

- IFSの設定（デフォルトではスペース・タブ・改行）に従ってフィールドを分割するため、末尾の空白が無視されてしまっている
- 標準入力から内容を一行ずつ読んでいるため、改行が含まれるファイル名を一度に取得できていない
- エスケープ文字として認識されているため、バックスラッシュが無視されてしまっている


## 丁寧な方法

上で挙げた3つの要因を解決してあげると次のようになる。

```bash:proc_hoge.sh
#!/bin/bash

find /path/to/hoge -type f -print0 | while IFS= read -r -d $'\0' x; do
  echo "do something...: ${x}!!"
done
```

ファイル名が正しく取得できていることがわかる

```bash
./proc_hoge.sh
# => do something...: /path/to/hoge/bbb
#    ccc!!
#    do something...: /path/to/hoge/aaa   !!
#    do something...: /path/to/hoge/ddd\eee!!
```

### 解説

- `IFS=`としてreadを実行することで、フィールド分割されなくなっている
- findに`-print0`を指定することで、改行区切りでなくヌル文字`\0`区切りで出力されるようになる
- readに`-d $'\0'`を指定することで、一行ずつでなくヌル文字`\0`区切りで読み取るようになる
- readに`-r`を指定することで、バックスラッシュがエスケープ文字として扱われなくなる

## おまけ

上記の用にパイプ`|`でつないでwhile文を実行している場合、
while文はサブシェルで実行されるため、外の変数を変更することができない。

```bash
#!/bin/bash

count=0

find /path/to/hoge -type f -print0 | while IFS= read -r -d $'\0' x; do
  echo "do something...: ${x}!!"
  count=$(($count + 1)) # ここでの変更はwhile文の外に影響しない
done

echo $count
# => 0
```

このような処理をしたいときは、代わりにプロセス置換`<()`を用いてあげる必要がある。

```bash
#!/bin/bash

count=0

while IFS= read -r -d $'\0' x; do
  echo "do something...: ${x}!!"
  count=$(($count + 1)) # サブシェルで実行されていないため、ここでの変更が影響する
done < <(find /path/to/hoge -type f -print0)

echo $count
# => 3
```

## まとめ

シェルでイレギュラーなケースまで網羅するのは面倒で難しい。
stechstudio/laravel-zipstreamでファイル名に日本語を使えるようにする。

stechstudio/laravel-zipstream（v4.8）でファイル名に日本語を使えるようにする方法について記載。



stechstudio/laravel-zipstreamdeを使ってzipファイル保存するとファイル名に日本語が入っている場合、日本語名が消える。

例
1_日本語file.pdf
↓
1_file.pdf


以下のファイルを確認すると、ここでStr::asciiで文字コードをASCIIに変換しているので、日本語が消えている模様。
vendor\\stechstudio\\laravel-zipstream\\src\\Models\\File.php

```

 　　　　/**
     * @return string
     */
    public function getZipPath(): string
    {
        $path = ltrim(preg_replace('|/{2,}|', '/', $this->zipPath), '/');

        return config('zipstream.file.sanitize')
            ? Str::ascii($path)
            : $path;
    }



```

これを処理しないように.envに以下を追加

```

ZIPSTREAM_FILE_SANITIZE=false

```


以上。
Laravel S3にzipファイルを保存する

LaravelでS3上のデータを使用して、S3にzipファイルを作成する方法を記載。


ライブラリをインストール

```

composer require stechstudio/laravel-zipstream


```

zip作成。

$zip_listには['file_path'=>'/xxx/xxx','original_file_name'=>'name']
の形で配列を入れておく。
file_path：S3のパス
original_file_name:pdfのファイルネーム

```
use Zip;
use Storage;

 public function createZip(array $zip_list,string $zip_file_name)
    {
        $zipFilePath = '/zipdir';
        $bucket_private = config('filesystems.disks.private_s3.bucket');

        $zip = Zip::create($zip_file_name);
        $lengs = count($zip_list);
        for( $i = 0; $i < $lengs; $i ++ ){
            $file_path = $zip_list[$i]['file_path'];
            $original_file_name = $zip_list[$i]['original_file_name'];
            
            $contents = Storage::disk(config('filesystems.private_s3'))->get($file_path);
            $zip->addRaw($contents, $original_file_name);
        }
        $save_url = 's3://'.$bucket_private.$zipFilePath;
        $zip->saveTo($save_url);
    }

```


以上
エロマーケットプレースの研究シリーズ 4: MeCab にサイトに出てくる未知語のエロ単語を登録

# 前回は

https://qiita.com/c1z/items/8d9dee2029b937ccbe3d

MeCab のインストールと、ユーザー辞書を構築するための環境を作った

# 今回は

前々回クロールしたサイトのタグ情報を MeCab の単語として登録していく部分を作っていく

# コード書く

```python
import numpy as np
import pandas as pd
import re
import MeCab
import subprocess
import os

# MeCab のシステム辞書 NEOLogd のディレクトリを取得
dicdir = subprocess.run(['mecab-config', '--dicdir'], capture_output=True).stdout.decode('utf-8').strip()
neologd_dir = os.path.join(dicdir, 'mecab-ipadic-neologd')

# 前々回クロールしてきた情報を読み込む
df = pd.read_csv('output.csv')

# タグを数えるための辞書
tag_count_dict = dict()

for index, row in df.iterrows():
    tags_str = row['tags']

    # 空のカラムは nan になっちゃうので nan の場合は無視
    if type(tags_str) != str:
        assert np.isnan(tags_str)
        continue

    tags = tags_str.split(',')

    # タグの出現回数を数える
    for tag in tags:
        # 最初に # から始まるハッシュタグっぽいのは # を除外
        tag = re.sub(r'^#', '', tag)

        if tag not in tag_count_dict:
            tag_count_dict[tag] = 0
        tag_count_dict[tag] += 1

# 2 回以上使われてるタグを抽出
tag_list = np.array(sorted(tag_count_dict.items(), reverse=True, key=lambda pair: pair[1]))[:,0]

# 未知語抽出
unknown_tag_list = []
unknown_tagger = MeCab.Tagger(f'--unk-feature __unk__ -d {neologd_dir}')
for tag in tag_list:
    if unknown_tagger.parse(tag).split()[1] == '__unk__':
        unknown_tag_list.append(tag)

tagger = MeCab.Tagger(f'-d {neologd_dir}')
for tag in unknown_tag_list:
    # 未知語を MeCab で parse
    # (これで既知語の組み合わせとして取得できれば、そこから読みがわかる）
    tag_component_line_list = tagger.parse(tag).splitlines()

    # 既知語の単語を組み合わせて、雑に読みを作っていく
    yomi_component_list = []
    for tag_component_line in tag_component_line_list:
        if tag_component_line == 'EOS':
            break;
        tag_component, features_str = tag_component_line.split()
        features = features_str.split(',')
        
        # 既知語として取得できるか確認
        if len(features) > 7 and features[7] != '*':
            # 読みの情報を取得
            yomi_component = features[7]
        else:
            # 既存の単語から読みを持ってこれない場合はそのまま
            yomi_component = tag_component

        yomi_component_list.append(yomi_component)

    # 既知語の読みを組み合わせて、未知語の読みにする
    yomi = ''.join(yomi_component_list)

    # CSV として stdout に出す
    new_features = (tag, '', '', '', '名詞', '一般', '*', '*', '*', '*', tag, yomi, yomi, '追加エントリ')
    print(','.join(new_features))
```

こんな感じ

# 実行

さっき書いたコードを実行して、リダイレクトして ero_words.csv を作る

それを前回作った make_dic.sh を実行して dic ファイル化する

# 結果

mecab を実行してみる

```bash
$ mecab -d "$(mecab-config --dicdir)/mecab-ipadic-neologd"
彼女は彼のクリフェラでアヘオホとイキ声を晒した
彼女	名詞,代名詞,一般,*,*,*,彼女,カノジョ,カノジョ
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
彼	名詞,代名詞,一般,*,*,*,彼,カレ,カレ
の	助詞,連体化,*,*,*,*,の,ノ,ノ
クリフェラ	名詞,一般,*,*,*,*,クリフェラ,クリフェラ,クリフェラ,追加エントリ
で	助詞,格助詞,一般,*,*,*,で,デ,デ
アヘオホ	名詞,一般,*,*,*,*,アヘオホ,アヘオホ,アヘオホ,追加エントリ
と	助詞,並立助詞,*,*,*,*,と,ト,ト
イキ声	名詞,一般,*,*,*,*,イキ声,イキゴエ,イキゴエ,追加エントリ
を	助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
晒し	動詞,自立,*,*,五段・サ行,連用形,晒す,サラシ,サラシ
た	助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
```

このように、クリフェラ、アヘオホ、イキゴエなどが登録された

# これから

ero_words.csv は我が子のようにちゃんと育てて行こうと思う

# 次回は

https://qiita.com/c1z/items/ad4aa7427ed43abebc49

前々回クロールしたデータを LightGBM を使って、特徴の重要度を測っていく
mock-alchemyを使ってSQLAlchemy SessionのMockを作成し、SQLAlchemyステートメントを実行するときの備忘録

# 概要
- mock-alchemyの`UnifiedAlchemyMagicMock`を使用してSQLAlchemyステートメントを実行してみる。

# UnifiedAlchemyMagicMock とは
- SQLALchemyを組み合わせてsessionをmockできる
- 一般的なSQLALchemy session関数を統合してassertionを簡単にできる

# 参考
https://mock-alchemy.readthedocs.io/en/latest/user_guide/index.html

# 環境構築
```
# Pythonのバージョンを確認
python --version
> Python 3.7.3

# mock-alchemyのインストール
> pip install mock-alchemy
```
# ファイル構成
以下ファイル構成で実装。
```
.
└── mock_alchemy_test
    ├── main.py          # ここでORMを操作
    ├── models
    │   └── user.py      # userテーブルの定義
    └── setting
        └── setting.py   # Engine, Session生成など行う
```
# EngineとSessionを作成する
以下、`setting.py`に記述。
```python:setting.py
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
from mock_alchemy.mocking import UnifiedAlchemyMagicMock


# sqliteのインメモリデータベースを使用
engine = create_engine('sqlite:///:memory:', echo=True)

# SessionのMockを作成
session = UnifiedAlchemyMagicMock()

# classとDBをMapping
Base = declarative_base()
```

### 補足
- `インメモリデータベース`はデータをHDDやSSDに保存するのではなく、メモリ上に記録する。そのため、ファイル実行が完了した時や、Pythonコマンドラインを抜けるなど、一連の処理が完了したら記録されていたデータは消えるので簡単に操作を試すときに有用。
- `echo=True`とすることで、SQLを出力。

# モデルクラスを作成する（テーブル定義を書く）
以下、`db.py`に記述。
```python:db.py
from sqlalchemy import Column, Integer, String, Sequence
from setting import engine, Base
import sys


class User(Base):
    """
    UserModel
    """
    __tablename__ = 'users'
    id = Column(Integer, Sequence('user_id_seq'), primary_key=True)
    name = Column(String(50))
    email = Column(String(255))
    age = Column(Integer)


def main(args):
    Base.metadata.create_all(bind=engine)


if __name__ == "__main__":
    main(sys.argv)
```
# ORM操作をする
`main.py`にて、userモデルと設定情報を読み込み、ORM操作をしていく。

```python:main.py
from db import *
from setting import session
from sqlalchemy import *


# INSERT
user = User()
user.name = 'test'
user.email = 'xxx@xxx.com'
user.age = 28
session.add(user)
session.commit()

# SELECT
users = session.query(User).all()
for user in users:
    print(f'{user.name} {user.email} {user.age}')

# WHERE
users = session.query(User). \
    filter(User.age == 28). \
    all()
for user in users:
    print(f'{user.name} {user.email} {user.age}')

# DELETE
session.query(User). \
    filter(User.age > 10).delete()

session.commit()

# DELETEできたかの確認でもう一度SELECT
users = session.query(User).all()
for user in users:
    print(f'{user.name} {user.email} {user.age}')


session.close()
```
```:実行結果
test xxx@xxx.com 28
test xxx@xxx.com 28
```
# まとめ
上記のようにすることで、簡単にSQLAlchemyステートメントを実行することができた。
SQLによるWordPressデータの一括置換

WordPressリニューアル案件。本番運用中のデータを引き継ぎたいが、開発は別DBで行う必要がある。最初は本番DBのデータをエクスポートし、ローカルPC上で必要な置換を行って開発DBにインポートすれば良かろうと考えた。ところがデータサイズが巨大でインポート時にトラブル続出。
試行錯誤の結果、本番DBをコピーしてサーバー上でSQLによる置換を行った。
```sql
update テーブル名 set カラム名=replace( カラム名, "置換前の文字列", "置換後の文字列" );
```
本職の方には初歩の初歩だと思うが、ふだんクライアント側の作業ばかりなので勉強になった。
[Kotlin]data classにおけるコンストラクタとイニシャライザを使い分ける

kotlinのdata classで定義するプロパティと、コンストラクタ引数を変えたい場面がある場合に、コンストラクタとイニシャライザを使い分けて実現する方法について記載していきたいと思います。
ちょっと何を言っているか分からないですが以下の例を見てもらえると分かると思います。
# [おさらい] data classとは
data classを作成する場合、コンストラクタにプロパティを定義する事でパラメータへのゲッターやセッターなどを自動で定義してくれました。
```Addition.kt
data class Addition(val paramA: Int, val paramB: Int, val result: Int)
```
paramAとparamBの足し算の結果をresultとして持ちたいので、このクラスをインスタンス化しようとすると
```
val addition = Addition(85, 23, 108)
val resStr = addition.result.toString() // resStrは108
```
となります。
ですが、これだと結果も自分で計算して格納しているため面倒です。２つのパラメータを渡したら自動で足し算結果を格納して欲しいです。
そこで、コンストラクタとイニシャライザの登場です。
# [おさらい] コンストラクタとイニシャライザ
実は、コンストラクタとイニシャライザにはプライマリとセカンダリがあり、以下の順番で呼び出されます。
１．プライマリコンストラクタ
２．プライマリイニシャライザ
３．セカンダリコンストラクタ
４．セカンダリイニシャライザ
これらをコードで示すと以下のようになります。
```
data class sample(プライマリコンストラクタ) {
    init {
        プライマリイニシャライザ
    }
    constructor(セカンダリコンストラクタ) : this() {
        セカンダイニシャライザ
    }
```
# data classにコンストラクタとイニシャライザを使う
paramAとparamBと自分で計算した結果のresultを渡すのではなく、コンストラクタとイニシャライザを駆使する事で、２つのパラメータを渡すと足し算した結果が自動でresultに格納されるようなdata classを定義したい場合は以下のように書きます。
```Addition.kt
data class Addition(val paramA: Int, val paramB: Int, var result: Int) {
    constructor (paramA: Int, paramB: Int) : this(paramA, paramB, 0) {
        result = paramA + paramB
    }
}
```
使い方は以下のようになります。
```
val addition = Addition(85, 23)
val resStr = addition.result.toString() // resStrは108
```
セカンダリコンストラクタの引数でインスタンス化すると、プライマリコンストラクタで定義した各種プロパティが設定された後、セカンダリイニシャライザによって足し算結果をresultに格納しています。
これで、2つのパラメータを渡しただけで足し算結果を自動で格納できるようになりました。
# さいごに
このようなコンストラクタとイニシャライザのテクニックを応用する事で、データクラスの定義とそれをインスタンス化する際のIFに自由度が持たせられるようになりますので是非実践してみて下さい！
Springbootの特徴

1. Framework (フレーム内で動作)
2. IoC (Inversion of Control)
   人間が作成し、管理までしなければならない数多くのクラスやオブジェクトをスプリングがスキャンして人間の代わりに制御および管理。
     クラス：設計図
     オブジェクト：実体化が可能なもの。抽象概念はオブジェクトではない
　　　　　　　ex) 家具 - 抽象概念なので、オブジェクトではない。実体化が可能な椅子やベッドなどがオブジェクト
     インスタンス：実体化されたもの。 ex）実際に作られた椅子やベッドのこと
3. DI (Dependency Injection)
   IoCによって管理されているクラスとオブジェクトを他のところでも使える。
4. Springは多くのフィルターが存在。
   フィルターは城を守る警備員の役割
5. アノテーションをサポート。
   アノテーションは、注釈＋あらかじめ設定された約束情報をプログラムに知らせるメタデータ。＠を利用する。
6. メッセージコンバータをサポート。
![message.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2662678/d201a84d-b455-0db2-835e-4b2e45c679ba.png)
7. 内蔵トムキャット(Tomcat, Servlet Container)が存在。
クライアントがURIやJava・JSPでDataを申請→ApacheサーバーはURIやJava・JSPがわからない→トムキャットに渡す→トムキャットがそれをHTMLに変換し、Apacheサーバーに渡す→Apacheサーバーは受け取ったHTMLをクライアントに渡す
・ApacheサーバーはURLや静的データ処理可能なWebサーバー、Tomcatは動的な機能をコンパイル、HTMLにしてクライアントに提供。つまり、静的なデータのみ要求時にはトムキャットは動作しない。スプリングブートは資料要求時常にJavaを使うため、トムキャットがいつも動作する。
FreeBSDのマイナーバージョンアップグレードでpackageを強制アップグレードしたらエラーになった

# 今回は
13.0->13.1を、[本家の手順](https://docs.freebsd.org/ja/books/handbook/cutting-edge/)を見ながらやってみました。
基本的に順調だったのですが、`# pkg-static upgrade -f`でエラー発生。

# エラーの内容
```shell-session
(null) has no direct installation candidates, change it to python?
```
宙ぶらりんのpackageがあり、これはpythonでよいのかと聞かれてる感じか。
`y`でも`n`でも、結局`(null)`なpackageが消えないとダメそう。

# ググったら
そのものズバリの事例がうまく見つけられなかったものの、関連ありそうな[こちら](https://forums.freebsd.org/threads/after-freebsd-12-1-to-12-2-pkg-upgrade-f-failed.77733/)で、pkgにautoremoveがあることを知る。aptみたいだ。
manは[こちら](https://www.freebsd.org/cgi/man.cgi?query=pkg&apropos=0&sektion=0&manpath=FreeBSD+13.1-RELEASE+and+Ports&arch=default&format=html)。

# お試し
```shell-session
# pkg autoremove
```
したら
```shell-session
# pkg-static upgrade -f
```
できました!

# 原因
不明ですが、そもそも13.0->13.1以前に既に発生していたのかも。
VBA_03_Dictionary_Collection_Sample


Keyword
---
Dictionary & Collection
Array

Sample Code
---
```vb:Dictionary & Collection
Public Sub MySample20220524()
    
    Dim myArrList As Variant
    Dim i As Long, j As Long
    Dim myCollList As New Collection
    Dim myPrefList As New Dictionary
    Dim myPrefKey As Variant

    With ThisWorkbook.ActiveSheet
        myArrList = .UsedRange
    End With
    
    For i = LBound(myArrList, 1) + 2 To UBound(myArrList, 1)
        myPrefList.Add myArrList(i, 2), myArrList(i, 3)
    Next i
    
    For Each myPrefKey In myPrefList.Keys
        If myPrefList.Item(myPrefKey) > myPrefList.Item("全国加重平均額") Then
            myCollList.Add myPrefKey
        End If
    Next
    
    For i = 1 To myCollList.Count
        Debug.Print myCollList.Item(i)
    Next i

End Sub
```


Sample Data
---
![無題2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2672288/8a659181-e1bf-9ed6-0ee3-e19f6b87b412.jpeg)


参照先（サンプル全データ）
https://www.mhlw.go.jp/stf/seisakunitsuite/bunya/koyou_roudou/roudoukijun/minimumichiran/
【FirebaseSDK(v9)】FirestoreでドキュメントIDを複数指定して取得する方法

### 以下コレクションが存在する場合に各取得方法の簡単な例(JavaScript or TypeScript)
- [ドキュメントIDを1つ指定して、ドキュメントを1つ取得する場合](#ドキュメントIDを1つ指定して、ドキュメントを1つ取得する場合)
- [フィールドを指定して、複数ドキュメントを取得する場合](#フィールドを指定して、複数ドキュメントを取得する場合)
- [ドキュメントIDを複数指定して、ドキュメントを複数取得したい場合](#ドキュメントIDを複数指定して、ドキュメントを複数取得したい場合)

```
accounts
L XXXXXXXX
  L name = xさん
  L frendIds = [YYYYYYYY, ZZZZZZZZ]
L YYYYYYYY
  L name = yさん
  L frendIds = [XXXXXXXX]
L ZZZZZZZZ
  L name = zさん
  L frendIds = [XXXXXXXX]
```

#### ドキュメントIDを1つ指定して、ドキュメントを1つ取得する場合
```
import { initializeApp } from 'firebase/app';
import { doc, getDoc, getFirestore } from 'firebase/firestore';

const app = initializeApp();
const db = getFirestore(app);

const snapshot = await getDoc(doc(db, 'accounts', 'XXXXXXXX');
snapshot.exists() && console.log(snapshot.id);
// XXXXXXXX
```

#### フィールドを指定して、複数ドキュメントを取得する場合
```
import { initializeApp } from 'firebase/app';
import { collection, getDocs, query, where } from 'firebase/firestore';

const app = initializeApp();
const db = getFirestore(app);

const q = query(collection(db, 'accounts'), where('frendIds', 'array-contains', 'XXXXXXXX'));
const snapshot = await getDocs(q);
snapshot.forEach((doc) => {
  console.log(doc.id);
}
// YYYYYYYY, ZZZZZZZZ
```

#### ドキュメントIDを複数指定して、ドキュメントを複数取得したい場合
```
import { initializeApp } from 'firebase/app';
import { collection, documentId, getDocs, query, where } from 'firebase/firestore';

const app = initializeApp();
const db = getFirestore(app);

const q = query(collection(db, 'accounts', where(documentId(), 'in', [YYYYYYYY, ZZZZZZZZ]));
const snapshot = await getDocs(q);
snapshot.forEach((doc) => {
  console.log(doc.id);
}
//  YYYYYYYY, ZZZZZZZZ
```

不明点等ある方はコメントやSNSでDMいただけるとお答えいたします🙋‍♂️
楽しい開発ライフを！
気象庁の過去気象データをPandasでスクレイピング

https://qiita.com/Cyber_Hacnosuke/items/122cec35d299c4d01f10

https://qiita.com/TaichiEndoh/items/97eedb1899d38fe00711

Pandasでスクレイピング

その他内容は上記記事を参考

# 日ごと
```python
import time
import pandas as pd

# 札幌
prec_no = 14
block_no = 47412

# 期間　※年と月だけ利用
start = "2022/01/01"
end = "2022/04/30"

# 期間中の月初を作成
dt_range = pd.date_range(start, end, freq="MS")

dfs = []

for dt in dt_range:

    url = f"https://www.data.jma.go.jp/obd/stats/etrn/view/daily_a1.php?prec_no={prec_no}&block_no={block_no}&year={year}&month={month}&day=&view="

    tmp = pd.read_html(url)[0]

    # 列名の重複除去して結合
    tmp.columns = [
        "_".join(sorted(set(col), key=col.index)) for col in tmp.columns.values
    ]

    tmp["年"] = dt.year
    tmp["月"] = dt.month

    dfs.append(tmp)

    # 1秒待機
    time.sleep(1)

# 結合
df = pd.concat(dfs).reset_index(drop=True)

# CSVに変換
df.to_csv("result.csv", encoding="utf_8_sig")
```

# 10分ごと
```python
import time
import pandas as pd

# 横浜
prec_no = 46
block_no = 47670

# 期間
start = "2021/06/22"
end = "2021/06/30"

# 期間作成
dt_range = pd.date_range(start, end, freq="D")

dfs = []

for dt in dt_range:

    url = f"https://www.data.jma.go.jp/stats/etrn/view/10min_a1.php?prec_no={prec_no}&block_no={block_no}&year={dt.year}&month={dt.month}&day={dt.day}&view="

    tmp = pd.read_html(url)[0]

    # 列名の重複除去して結合
    tmp.columns = [
        "_".join(sorted(set(col), key=col.index)) for col in tmp.columns.values
    ]

    tmp["year"] = dt.year
    tmp["month"] = dt.month
    tmp["day"] = dt.day

    dfs.append(tmp)

    # 1秒待機
    time.sleep(1)

# 結合
df = pd.concat(dfs).reset_index(drop=True)

df[["hour", "minute"]] = df["時分"].str.split(":", expand=True).astype(int)

df["date"] = pd.to_datetime(df[["year", "month", "day"]])
df["time"] = df[["hour", "minute"]].apply(
    lambda x: pd.Timedelta(hours=x.hour, minutes=x.minute), axis=1
)

df["時分"] = df["date"] + df["time"]

df.drop(["date", "time", "year", "month", "day", "hour", "minute"], axis=1, inplace=True)

df.rename(columns={"時分": "日時"}, inplace=True)

# CSVに変換
df.to_csv("result.csv", encoding="utf_8_sig")
```
STM32G031でアナログサーボ信号(PWM)を受信して9600bpsハードウェアシリアルにバイナリ出力(トリガー付)


x 動作確認済み2022/5/25 8:05

目的
サーボ信号を受信してサーボ以外の別の機構を動かす。
使いがってが悪いのでトリガーを付ける

仕様

1 9600bps RX(トリガー(なんでもよい1文字))
2 VDD
3 GND
4 リセットオンリー

8 9600bps TX(0から255のPWMバイナリー値)
7
6 デバッグ出力 9600bpsソフトウェアシリアル
5 PWM(IN) 400uS=0,2400uS=255


デバッグ1

![o_con491.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/fafcd794-398b-28ec-c9bb-34111e6478c7.jpeg)


![o_con492.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/a7d86998-03d3-01d1-82a5-da9ed65c36b8.jpeg)


(成功)　138を送っている


![o_con493.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/b0c91c20-d8cb-d652-aa3f-5acf8b45e070.jpeg)




名前は、まだない



```c++

//PWM_to_SER_trigger_031_1


#include <Arduino.h>

#define in_pwm   PA11 // 5pin

//10の割り算 0から1028までは、正しい。主に0から999
#define DIV10(n) ((n*205)>>11)


#define DW   digitalWrite
#define TX1        PA12   // 6pin
#define UART_DELAY 106  // 002-110 9600bps ok 031

//仮想シリアルへの一文字出力 9600bps
void pc_putc(char ch)
{
  DW(TX1, HIGH);

  DW(TX1, LOW);//START
  delayMicroseconds(UART_DELAY); //START BIT WAIT

  for (int ii = 0; ii < 8; ii++) {
    DW(TX1, (ch >> ii) & 1  );
    delayMicroseconds(UART_DELAY); //DATA 1-8BIT WAIT
  }//for

  DW(TX1, HIGH);//Stop
  delayMicroseconds(UART_DELAY); //StOP BIT WAIT

}//pc_putc


//文字列の表示
int pc_printf(char *str1) {

  //文字の中身がゼロか
  while (*str1) {

    //一文字出力
    pc_putc(*str1 ++);

  } //while

  //戻り値
  return (0);
}//pc_printf


//初期化
void setup() {

  delay(3000); //not Delete

  Serial.begin(9600);

  pinMode(in_pwm, INPUT); //pa11

  //TXポートの初期化
  pinMode(TX1, OUTPUT);
  DW(TX1, HIGH);

  //pc_printf("<STgggg>\r\n"); //debug

} //setup


//メインループ
void loop()
{
  // pc_printf("L\r\n"); //debug

  //トリガー待ち
  // 受信データがあった時だけ、処理を行う
  while (!Serial.available()) {}      // 受信データがあるか？
  int u_key = Serial.read();            // 1文字だけ読み込む 空読み

  //PWMでデータ取得
  int pwmco2 = pulseIn(in_pwm, HIGH, 2000000);

  //  //データの表示 debug 1
  //  String thisString9 = String( pwmco2 );
  //  pc_printf( (char *)thisString9.c_str() );
  //  pc_printf( "\r\n" );

  if (pwmco2 <= 400  ) {
    pwmco2 = 400;
  }
  if (pwmco2 >= 2400 ) {
    pwmco2 = 2400;
  }

  pwmco2 = pwmco2 - 400;

  int vel = (pwmco2 * 523) >> 12;

  Serial.write(  (char)vel   );

  //  //データの表示 debug 2
  //  String thisString9 = String( vel );
  //  pc_printf( (char *)thisString9.c_str() );
  //  pc_printf( "\r\n" );

  // delay(500); //debug 後で消す

} //loop


```

arduino マスター


```c++



//PWM_SER_m_UNO_1


#include <Arduino.h>

#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3); // RX, TX


//初期化
void setup()
{

  //シリアルポートの初期化
  Serial.begin(9600);

  //ソフトウェアシリアルの初期化
  mySerial.begin(9600);

} //setup


//メインループ
void loop()
{

  //読み込み
  int b = 0;
  mySerial.print("S"); delay(5); //トリガーを投げる
  while ( mySerial.available() ) {
    //Serial.println( mySerial.available() );//debug
    b = mySerial.read();//受信
    //Serial.println( b );//debug

    Serial.print("255,"); //debug

    Serial.print( b ); //UNO

    Serial.print(",0");   //debug
    Serial.println();

  }//while

  delay(500); // 0.5秒の待ち

} //loop



```
【TypeScript】配列の要素をキーとするオブジェクトを生成したい

## お断り
2022/5/25現在未解決です。

## 対処フロー
1. 文字列リテラルの配列を定義する
1. 配列をもとにUnion Typeを定義する
1. 2.のUnion Typeをキーとするプロパティを持つTypeを定義する
1. 3.のTypeを満たすオブジェクトを、1.で定義した配列をもとに作りたい
1. 配列からオブジェクトにするには、Object.fromEntriesが有効
1. 作ってみたが、戻り値の型が{[x: string]: any}で型一致させられなかった

## コードベース
```ts
// 1.
const foo = ["a", "b", "c"] as const;
```
```ts
// 2.
type Foo = typeof foo[number];
// type Foo = "a" | "b" | "c"
```
```ts
// 3.
type Bar = {
   [key in Foo]: () => void // プロパティの型は適当
}
// type Bar = { "a" | "b" | "c" : () => void }
```
```ts
// 4.
// 1.の配列と別の何かしらを組み合わせて、こんな感じのオブジェクトを作りたい
// const bar = {
//   a: () => {console.log("hello")},
//   b: () => {console.log("hello")},
//   c: () => {console.log("hello")}
// }
```
```ts
// 5.
// Object.fromEntriesで作ること自体はできる
const greet = () => {console.log("hello")};
const bar: Bar = Object.fromEntries(foo.map(el => [el, greet]));
// 6.
// しかしObject.fromEntries()の戻り値の型は{[x: string]: any}だったので、type barと一致させられなかった
```
## 現状の結論: as
```diff_typescript
- const bar: Bar = Object.fromEntries(foo.map(el => [el, greet]));
+ const bar: Bar = Object.fromEntries(foo.map(el => [el, greet])) as Bar;
```
引き続き模索します。
AzureSDKについて 個人メモ

## SDK とは
- Software Development Kit(ソフトウェア開発キット)とは、プログラム・サンプルコード・APIをパッケージにしたもので、機能実現するための詳しい技術をしらずともアプリケーションに新機能を実装できる...らしいけど実際使ってみないとわからない
- コードでリソースの構成を記述でき、実行するとリソースが立ち上がる
## SDK経由でAzureサービスをなんか弄りたい
- [このサイトでチュートリアルを行った](https://azure.github.io/azure-sdk-for-js/index.html)
- [参考サンプルコード](https://github.com/Azure-Samples/azure-sdk-for-js-samples)
### 認証
1. AzureCLIをbrewでインストールする(CLIがないと)使えない
2. ターミナルで``` az login ```コマンドを使い認証を行う
### サービスの立ち上げ
- サブスクリプションID・クライアントID・テナントIDが必要だが、場合によって必要・不要がある...わからない
- ブラウザ経由で認証する```InteractiveBrowserCredential```の場合はサブスクリプションIDのみで認証ができるが、全てCLIで完結する```DefaultAzureCredential```はアプリ(資格？)発行したのち、クライアントID・テナントID・シークレットキーをenvファイルに記述する必要がある。
- ```ClientSecretCredential```はクライアントID・テナントID・シークレットキーを直接引数に指定することで認証が可能
oidc ID Token

End-User の認証を可能にするために OpenID Connect が OAuth 2.0 に追加する最大の拡張機能は ID トークンデータ構造である。

 ID Token は, ある Client を利用するというコンテキスト中での, Authorization Server による End-User 認証に関する Claim を含んだセキュリティトークンである。

潜在的にはその他の Claim を含む可能性もある。

ID Token は JWTである。

OpenID Connect で使用されている全ての OAuth 2.0 のフローで ID Token が利用される。

またID Tokenには以下のクレームが含まれる。

iss(必須)認可サーバの識別子

レスポンスを返したIssuerのIssure Identifier。

iss 値は, https スキーマで始まる大文字小文字を区別する URL であり, スキーマ, ホスト, そして任意でポート番号とパスを含む. クエリーとフラグメントは含まない。

sub(必須)ユーザの識別子

Subject Identifier. Client に利用される前提で, Issuer のローカルでユニークであり再利用されない End-User の識別子. (例: 24400320 や AItOawmwtWwcT0k51BayewNvutrJUqsvl6qs7A4 等) この値は ASCII で255文字を超えてはならない (MUST NOT). sub 値は大文字小文字を区別する.

aud(必須)RPの識別子

ID Token の想定されるオーディエンス (Audience). この値は Relying Party の OAuth 2.0 client_id を含まなければならない (MUST). 他のオーディエンスの識別子を含んでもよい (MAY). 一般的には aud は大文字小文字を区別した文字列の配列であるが, オーディエンスが単体の場合は aud 値を大文字小文字を区別した単一文字列としてもよい (MAY).

exp(必須)ID Tokenの有効期限

この有効期限以降に該当 ID Token を受け入れたり処理してはならない (MUST NOT). ある ID Token が有効期限内であるためには, この値が示す時刻より現在時刻が前でなければならない (MUST). 実装者は, 通常数分以内で, 時計のズレを考慮して多少の猶予期間を設けてもよい (MAY). この値は UTC 1970-01-01T0:0:0Z から該当時刻までの秒数を示す JSON 数値である. 詳細は RFC 3339 [RFC3339] を参照のこと.

iat(必須)レスポンスの発行時刻

この値は UTC 1970-01-01T0:0:0Z から該当時刻までの秒数を示す JSON 数値である。

auth_time認証した時刻(max_ageがリクエストにある場合必須それ以外の場合には任意となる)

End-User の認証が発生した時刻. この値は UTC 1970-01-01T0:0:0Z から該当時刻までの秒数を示す JSON 数値である. リクエストに max_age が含まれていた場合, この Claim は必須である (REQUIRED). その他の場合は任意 (OPTIONAL). (auth_time Claim は, OpenID 2.0 PAPE [OpenID.PAPE] auth_time レスポンスパラメーターに相当する)

acr(任意)

End-User の認証が発生した時刻. この値は UTC 1970-01-01T0:0:0Z から該当時刻までの秒数を示す JSON 数値である. リクエストに max_age が含まれていた場合, この Claim は必須である (REQUIRED). その他の場合は任意 (OPTIONAL). (auth_time Claim は, OpenID 2.0 PAPE [OpenID.PAPE] auth_time レスポンスパラメーターに相当する)

amr(任意)エンドユーザが認証に使用した方法(パスワード、ワンタイムパスとか)

Authentication Methods References. 認証時に用いられた認証方式を示す識別子文字列の JSON 配列. 例として, パスワードと OTP 認証が両方行われたことを示すといったケースが考えられる. amr Claim にどのような値を用いるかは本仕様の定めるところではない. この値の意味するところはコンテキストによって異なる可能性があるため, この Claim を利用する場合は, 関係者間で値の意味するところについて合意しておくこと. amr は大文字小文字を区別する文字列である.

azp(任意)認可の依頼者と認可されるアプリケーションが違う場合に使う(このような時はあまりないと思われる)

(例えばabcd.comからログインボタンを押し認証後に1234.comにリダイレクトされるような場合?)

ID Token 発行対象である認可された関係者 (authorized party). この Claim が存在する場合, その値は受け取り手の OAuth 2.0 Client ID でなければならない. この Claim は, ID Token のオーディエンス値が単一文字列であり, かつその値が azp の値と異なる場合にのみ必要となる. オーディエンスと azp 値が同値である場合にも, この Claim を含んでもよい (MAY). azp は大文字小文字を区別する文字列である.

使用されるクレームは以上である。

ID Token はその他の Claim を含んでもよい (MAY). 解釈不可能な Claim は全て無視すること。

ID Token は JWS [JWS] を使って署名されなければならない。

任意で JWS [JWS] によって署名し, JWE [JWE] によって暗号化することもできる。

ID Token を暗号化する場合は, 署名を行った後に暗号化しなければならない。

ID Token には, alg として none を用いてはならない

ID トークンは JWS あるいは JWE の x5u, x5c, jku と jwk ヘッダーパラメーターフィールド を使用すべきではない (SHOULD NOT). 代わりに, 使用される鍵への参照を Discovery と Registration parameters を用いて事前に伝えること。

まとめ
ID Token は, ユーザがサードパーティのアプリを使用するのに使う、認可サーバによる認証に関するクレームを含んだセキュリティトークンである。
細かいルールは他にもあるが「いくつかのクレームがあること」、「JWTを使用し署名、暗号化をするにはいくつかの決まりがあること」の２つだけを知っとけば後で詳細については調べれば良いと思われる。
OIDC イントロダクション

Oauth2.0の仕組みにアイデンティテーレイヤーを付加したもの。つまりOauth2.0ではユーザが誰かではなくそのクライアントが何が出来るかということしかわからなかったがクライアントが何が出来るかだけでなく、誰であるかということが特定できるようにした仕組み。(おまけに簡単なプロフィールの取得もできる。)

よって認可だけではなく認証にも使用可能なように拡張される。

oidcを使うにはclientはscopeにopenidを指定したAuthorization Request を送信する。

認証結果はIDTokenと呼ばれ、JWT形式で返される。

OpenID Connect をサポートする OAuth 2.0 Authentication Server は, OpenID Provider (OP) と呼ばれ、 OpenID Connect を利用する OAuth 2.0 Client は Relying Party (RP) とも呼ばれる.

前提としてRPで使うOpenID Providerの認証エンドポイント、トークンエンドポイントは取得済とする。

さらに、RPは OpenID Provider 利用に必要なクレデンシャルを取得し, OP 利用に必要な情報を登録しているものとする。

またJWS,JWEを使う場合にJSONシリアライゼーション形式ではなくコンパクトシリアライゼーション形式でなければならない。

大雑把な認証フロー

OpenID Connect プロトコルは, おおまかに言って以下のステップに従う.

RP (Client) が OpenID Provider (OP) にリクエストを送る.
OP は End-User を認証し, 認可を受ける.
OP は ID Token および (通常は) Access Token を返す.
RP は Access Token を添えて UserInfo Endpoint にリクエストを送る.
UserInfo Endpoint は End-User の Claim を返す
まとめ
OpenIDConnectとはOauth2.0を拡張し認可だけでなく認証もできるようにする仕組み。
oidcを使用するには認可リクエストのスコープにopenidを指定する。
レスポンスはID Tokenと呼ばれJWT形式のトークンで返される。
oidcをサポートする認可サーバをOpenID Provider(OP)という。
oidcをサポートするクライアントアプリをRelying Party (RP) と言う
RPで使うクレデンシャルは事前に取得している必要がある。
認証エンドポイント、アクセストークンエンドポイントは事前に取得している必要がある。
JWE、JWSではJSON形式ではなくコンパクト形式のシリアライズを使用する。
大まかなステップは把握しておく。
Solidityのコントラクトからコントラクトを叩くまたは送金するコードの覚書

## 特定のコントラクトのfunctionを叩く

Interfaceで実行したいコントラクトを定義して叩く方式
Interfaceで定義せずにabstractでも定義は可能


```solidity
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;

interface Telephone {
    function changeOwner(address _owner) external;
}

contract TelephoneAttack {
    address target = target contract address;
    function attack() external {
        Telephone(target).changeOwner(msg.sender);
    }
}
```

Abstractで定義する場合はこんな感じ

```solidity
abstract contract Telephone {
    function changeOwner(address _owner) public virtual;
}
```

## 特定のコントラクトへ送金する

payableなアドレスに対してcallで送金を行う
Clientからコントラクトに対して実行するときは引数の後にoptionを指定するが、Solidityからコントラクトを実行する時は先にvalueなどを指定する。

この例では引数は無いため `("")` となっているが引数がある場合はこのカッコ内で指定する

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

contract Attacker {
    function attack (address payable target) external payable {
        (bool res,)  = target.call{value: msg.value}("");
        require(res, "failed");
    }
}
```
遊興競技(game)断ちしてもうすぐ半年

ゲーム依存症から脱却するために

https://qiita.com/kaizen_nagoya/items/c952d6291fb881eb0985

遊興競技依存症拮抗

https://qiita.com/kaizen_nagoya/items/934b65ea3282f7e863fe

１月５日からゲーム断ちしてから、もうすぐ６ヶ月。
結果、Qiitaはじめてから、数だけは最大量になるかもしれない。

2022年5月308記事書いたはず。＆6月予定。

https://qiita.com/kaizen_nagoya/items/0f9e6cc3dcb51690dbf4

ゲームからSNSに移行しただけかもしれない。

「GAFA断ち」仮説（154）

https://qiita.com/kaizen_nagoya/items/5fb95a9df523721377f6

SNS依存症の分類

https://qiita.com/kaizen_nagoya/items/f8d99ee9c654dc27807b

プログラマが落ち入らない方が良さそうな依存症群調べ(一部自己対策含む)

https://qiita.com/kaizen_nagoya/items/4d00b3fc2d9f649e0a18
CVPR2022の量子化論文 (2)

# 概要

この記事では、[CVPR2022](https://cvpr2022.thecvf.com/)の量子化論文を紹介します。[^1]
[その1](https://qiita.com/kyad/items/8f15de6b914440a4f76c)

[^1]: 画像や数式は論文から引用しています。


# It's All In the Teacher: Zero-Shot Quantization Brought Closer to the Teacher

https://arxiv.org/abs/2203.17008

(CVPR2022 Oral)
概要：AIT. Zero-shot Quantization (教師画像不要の量子化)
研究機関：NAVER
新規性：複数のロスの和を考えなくてよく汎化性の高い方法を提案した。
キモ：ロスをKL Divergenceのロスの1つだけで表す。KL Divergenceは汎化性能が高い。
評価：CIFAR, ImageNet, ResNet, MobileNetV2で実験した。


# Mr.BiQ: Post-Training Non-Uniform Quantization based on Minimizing the Reconstruction Error

https://openaccess.thecvf.com/content/CVPR2022/papers/Jeon_Mr.BiQ_Post-Training_Non-Uniform_Quantization_Based_on_Minimizing_the_Reconstruction_Error_CVPR_2022_paper.pdf

概要：Mr. BiQ. 非一様(マルチレベル量子化)のPTQ
マルチレベル量子化とは、$\alpha_i$を量子化の深さによって変える。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2448931/e5c5d70b-81f6-2c98-2225-5688deffce6c.png)
研究機関：Samsung
新規性：マルチレベル量子化で、量子化パラメータを学習可能にした。
キモ：量子化パラメータを学習する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2448931/8d32a382-1f5d-3346-5341-650860dedffa.png)
評価：ResNet等で実験した。


# Learnable Lookup Table for Neural Network Quantization

https://openaccess.thecvf.com/content/CVPR2022/papers/Wang_Learnable_Lookup_Table_for_Neural_Network_Quantization_CVPR_2022_paper.pdf

概要：Look Up Table (LUT) による非線形量子化
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2448931/3015f31f-4854-e2fb-a34e-1cbea43c4f3f.png)
新規性：LUTを学習可能にした。
キモ：Fig. 2のように量子化をステップ関数の和で表し微分可能にする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2448931/aa582e72-4e51-930a-25ab-917f3a55e6c3.png)
評価：ResNet, VGGで従来の量子化と比較した。



# (おまけ・ICLR2019) Rethinking the Value of Network Pruning

https://arxiv.org/abs/1810.05270

概要：枝刈り後のFine tuningの必要性について調査した。
新規性：枝刈り後の学習の初期値は、枝刈り前の重みでも、ランダム重みでも変わらないことを実証した。
キモ：VGG, ResNet, DenseNet, CIFAR, ImageNetで実験した。ランダム重みの方が精度が良いこともある。
評価：宝くじ仮説でも見てみたけど、学習率が大きいと当たりくじでもランダム初期値でも変わらなかった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2448931/99ce394f-0dc4-8409-2d37-3bfbdd0147ce.png)
【TypeScript】条件判定結果

# はじめに
if文や三項条件式の条件を書く時に条件判定がどうだったか？たまに忘れるためまとめました。
※2022年7月16日に参考文献を追記しました。そちらに詳細な文献あります。

# 結論
|  type   |    value    |  result  |
| ------- | ----------- | -------- |
| number  |   -1        |   true   |
| number  |    1        |   true   |
| number  |    0        |   false  |
| string  |   ""        |   false  |
| string  |  "hoge"     |   true   |
| boolean |  true       |   true   |
| boolean |  false      |   false  |
| any[]   |   []        |   true   |
| {}      |   {}        |   true   |
| any     |  null       |   false  |
| any     |  undefined  |   false  |


# 詳細
※今回は三項条件式で書いてます。
```typescript
const number1  : number  = -1;
const number2  : number  = 1;
const number3  : number  = 0;
const text1    : string  = "";
const text2    : string  = "hoge";
const boolean1 : boolean = true; // 真偽値
const boolean2 : boolean = false; // 真偽値
const array    : any[]   = new Array();
const object   : {}      = {};
const other1   : any     = null;
const other2   : any     = undefined;

var test = number1? "true" : "false";
console.log(test); // true

var test = number2? "true" : "false";
console.log(test); // true

var test = number3? "true" : "false";
console.log(test); // false

var test = text1? "true" : "false";
console.log(test); // false

var test = text2? "true" : "false";
console.log(test); // true

var test = boolean1? "true" : "false";
console.log(test); // true

var test = boolean2? "true" : "false";
console.log(test); // false

var test = array? "true" : "false";
console.log(test); // true

var test = object? "true" : "false";
console.log(test); // true

var test = other1? "true" : "false";
console.log(test); // false

var test = other2? "true" : "false";
console.log(test); // false
```
# 参考文献
●追記　2022年7月16日
こちらの文献ですが、コメント欄で@shiracamusさんに教えて頂いた文献になります。
[mdn web docs:Falsy (偽値)](https://developer.mozilla.org/ja/docs/Glossary/Falsy)
[mdn web docs:Truthy (真値)](https://developer.mozilla.org/ja/docs/Glossary/Truthy)
JS 文章の一部を正規化で書き換える為に：1

この記事ではJSと正規化を用いて文章の一部を書き換えていきます。
## 今回のゴール：JSを用いて文章の一部分を取り出す
今回、業務の中で「正規化を使って文章の一部分を変更する」
という作業を行っているコードを見かけ、
「そろそろ正規化を使えるようになりたい」という思いから
まとめつつ勉強することにしました。

そこで、まずはJSをつかって自分の取ってきたい箇所を含む、全体の要素を取得してみましょう。

## 生JSの方法:document.getElementを用いる
こちらに関しては複数方法が考えられますね。
```html:HTML
<!DOCTYPE html>
<head>
    <title>
        練習用
    </title>
</head>
<body>
    <h1 id="h1_id" class="h1_class">
        あばたけたぶら
    </h1>
    <main>
        彼は背後にひそかな足音を聞いた。<br>
        それはあまり良い意味を示すものではない。<br>
        誰がこんな夜更けに、しかもこんな街灯のお粗末な港街の狭い小道で彼をつけて来るというのだ。<br>
        人生の航路を捻じ曲げ、その獲物と共に立ち去ろうとしている、その丁度今。<br>
        彼のこの仕事への恐れを和らげるために、数多い仲間の中に同じ考えを抱き、<br>
        彼を見守り、待っている者がいるというのか。<br>
        それとも背後の足音の主は、この街に無数にいる法監視役で、強靭な罰をすぐにも彼の手首にガシャンと下すというのか。<br>
        彼は足音が止まったことに気が着いた。<br>
        あわてて辺りを見回す。ふと狭い抜け道に目が止まる。<br>
        彼は素早く右に身を翻し、建物の間に消え去った。<br>
        その時彼は、もう少しで道の真中に転がっていたごみバケツに躓き転ぶところだった。<br>
        彼は暗闇の中で道を確かめようとじっと見つめた。<br>
        どうやら自分の通ってきた道以外にこの中庭からの出道はないようだ。<br>
        足音はだんだん近づき、彼には角を曲がる黒い人影が見えた。<br>
        彼の目は夜の闇の中を必死にさまよい、逃げ道を探す。<br>
        もうすべては終わりなのか。すべての苦労と準備は水の泡だというのか。<br>
        突然、彼の横で扉が風に揺らぎ、ほんのわずかにきしむのを聞いた時、彼は背中を壁に押し付け、追跡者に見付けられないことを願った。<br>
        この扉は望みの綱として投げかけられた、彼のジレンマからの出口なのだろうか。<br>
        背中を壁にぴったり押し付けたまま、ゆっくりと彼は開いている扉の方へと身を動かして行った。<br>
        この扉は彼の救いとなるのだろうか。
    </main>
    <script src="test.js"></script>
</body>
```
かなり長い文章ですね。どういう物かはあまり理解していないのですが...
実際に動かす前に、test.jsの中身をお見せしましょう。
```js:test.js
var h1_content = document.getElementById('h1_id').textContent;
console.log(h1_content);
```
今回はh1の要素を取得しようとしています。
取得する際、document.getElementByIdで取得しようとしていますが
```js:test.js
var h1_content_ID = document.getElementById('h1_id').textContent;
var h1_content_class = document.getElementsByClassName('h1_class')[0].textContent;
var h1_content_tug = document.getElementsByTagName('h1')[0].textContent;
console.log(h1_content_ID);
console.log(h1_content_class);
console.log(h1_content_tug);
```
上記様々な方法で可能です。
ただし、注意も必要です。
Idで指定すれば一意に定まります（帰り値は一つ）が、
**クラスメイとタグ名はリストで帰ってきてしまいます**。

ここを間違えるとundefinedとなってしまい、表示されません。
このようにJSでは様々な取得方法が与えられています。
是非一度ご自身で試してみてください！

次回は「与えられた文字列の中で該当する箇所が存在するか」どうかを
チェックしてみましょう！