OmniAICreator/Qiita-1.07M huggingface dataset:
Unity: VuforiaのImageTargetにオブジェクトを動的に追加する方法

How To Dynamically Add Content to Targets in Unity（https://library.vuforia.com/articles/Solution/Working-with-Vuforia-and-Unity.html）
を参考にしたがPrefabを表示出来なかったので、こうやったらOKだった。

環境
---
OS macOS 10.15.7
Unity 2019.4.2f1
Addressable 1.16.15

手順(上記マニュアルGoogleで直訳)
---
1. Unityプロジェクトを開くか作成し、ImageTarget GameObjectを追加します（メニュー：GameObject> Vuforia> Image）

2. 実行時に、カスタム3Dモデルを画像ターゲットに動的にアタッチするとします。

3. プロジェクトビューの[アセット]フォルダーの下に、Prefabというサブフォルダーを作成します（まだサブフォルダーがない場合）。

4. PrefabオブジェクトをPrefabフォルダーに追加します。 3Dオブジェクトを表すカスタムPrefabを作成する方法はたくさんあります。たとえば、次のことができます。

* シーンビューで単純なCubeゲームオブジェクトを作成し、それをシーンビューからプロジェクトビューのPrefabsフォルダーにドラッグします。
または
* Unityでサポートされている形式（FBX、OBJ、DAE、3DSなど）で3Dモデルをインポートします。さまざまなファイル形式の3Dモデルからプレハブを作成する方法の詳細については、UnityのWebサイトを参照してください。

5. たとえば、C＃スクリプトを作成し、それをLoaderと呼び、それをイメージターゲットオブジェクトにアタッチします。

次のコードをスクリプトに挿入して、スクリプトを保存します。

```C#
using UnityEngine;
using Vuforia;
using System.Collections;

public class loader : MonoBehaviour, ITrackableEventHandler {
    private TrackableBehaviour mTrackableBehaviour;

    public GameObject model;

    // Use this for initialization
    void Start ()
    {
        mTrackableBehaviour = GetComponent<TrackableBehaviour>();

        if (mTrackableBehaviour)
        {
            mTrackableBehaviour.RegisterTrackableEventHandler(this);
        }
    }               

    // Update is called once per frame
    void Update ()
    {
    }

    public void OnTrackableStateChanged(
              TrackableBehaviour.Status previousStatus,
              TrackableBehaviour.Status newStatus)
    {
        if (newStatus == TrackableBehaviour.Status.DETECTED ||
            newStatus == TrackableBehaviour.Status.TRACKED)
        {
            OnTrackingFound();
        }
    }
    private void OnTrackingFound()
    {
        if(model!=null){
            GameObject obj=GameObject.Instantiate(model);
            obj.transform.parent = mTrackableBehaviour.transform;
            obj.transform.localPosition= new Vector3(0f, 0f, 0f);
            obj.transform.localRotation = Quaternion.identity;
            obj.active=true;
        }
     }
}
```


![スクリーンショット 2021-01-24 20.06.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/658283/d995df10-6eee-52fa-8796-61c8193a0132.png)


---
なんでマニュアルでは`public Transform myModelPrefab;`ってしたんだろう。こうするとPrefabを登録できない。（マニュアルにはUIにPrefabを登録すると書いているのに）。公式マニュアルに掲載されているのでこれが正しいはずなのに。
【SAS】変数の値が文字なのか数値なのか判定する

先日、数値しか入ってこないと聞いていた変数に、**のっぴきならない事情**で文字が入っていた外部データ(csv)を扱う機会がありました。
文字が入っていた場合は例外対応をしたいな～と思い、その際に調べたことをまとめます。

# 前提
- SAS 9.4
- SAS/STAT 15.1

# compress関数を使う

## 前置き：そもそもcompress関数て何？？？？
[SAS compress関数](https://support.sas.com/documentation/cdl_alternate/ja/lefunctionsref/67960/HTML/default/n0fcshr0ir3h73n1b845c4aq58hz.htm)
公式ドキュメントによると、
>元の文字から指定した文字を削除した文字列を返します。

とあります。
### 構文
compress(*source* *<,characters>*, *modifier* )
第2引数と第3引数は省略可能です。

例えば、ABCDEからABCを削除したい場合は

```
X='ABCDE';
Y=compress(X,'ABC');
putlog(Y) // DE
```
となります。

ABCのみを保持して後は削除することもできます。

```
X='ABCDE';
Y=compress(X,'ABC',k); // 第3引数にkを指定すると「削除」ではなく「保持」される
putlog(Y) // ABC
```

## 本題：文字or数値判定
compress関数が、元の文字から**指定した文字を削除（あるいは保持）**することを利用します。

例えば、
「0123abc456」という数値と文字が混ざっているものから、数値のみ削除する場合、

```
X='0123abc456';
Y=compress(X,'','kf'); //アンダースコア、英文字を保持　※ [k]は保持、[f]はアンダースコア文字および英文字を表す
putlog(Y); // abc

X='0123456';
Y=compress(X,'','kf');
putlog(Y); // '' アンダースコア、英文字を保持（それ以外は削除）されるので空白''が返ってくる
```

## 使用例
以下のようなデータがあった場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/548237/d375acf0-b640-d39e-791a-6d9a51a806f4.png)
hogeが数値の場合と文字の場合で、格納変数を分ける。
※hogeは文字型で定義されているものとします。

```
// 数値と文字が混じったデータ
data A1;
	input hoge$;
	cards;
	0
	1
	2
	3
	4
	a
	b
	c
	;
run;

data A2;
	attrib num length=8;
	attrib char length=$20;
	set A1;
    // hogeに文字が含まれていればchar、含まれていなければnumに格納する
	if compress(hoge,'','kf') ^= '' then character=hoge; else number=hoge;

	keep num cha;
run;
```
### 実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/548237/c6cbd0a2-b6be-9464-59ae-6a580bfb1631.png)

# 補足
compress関数の第2引数と第3引数には変数、式、固定値を指定することができます。詳細は公式ドキュメントをご参照いただければと思います。
【PHP】初中級者が解くべき過去問精選 100 問を解いてみた【6問目/100】

アルゴリズムのアウトプット

ということで、
[レッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編：目指せ水色コーダー！】](https://qiita.com/e869120/items/eb50fdaece12be418faa#2-3-%E5%88%86%E9%87%8E%E5%88%A5%E5%88%9D%E4%B8%AD%E7%B4%9A%E8%80%85%E3%81%8C%E8%A7%A3%E3%81%8F%E3%81%B9%E3%81%8D%E9%81%8E%E5%8E%BB%E5%95%8F%E7%B2%BE%E9%81%B8-100-%E5%95%8F)@e869120さん



>AtCoder で水色コーダー、つまりレーティング 1200 を少ない問題数で達成するために、茶色コーダー・緑コーダーにとって適切な教育的良問を 100 問集めました。

こちらの記事の初中級者が解くべき過去問精選 100 問
をPHPで解いていきます。

```php
<?php

$s = trim(fgets(STDIN));
$number = trim(fgets(STDIN));
$numToArr = str_split($number);

$ansList = [];
$firstList = [];
$secondList = [];

for ($i = 0; $i < $s; $i++) {
    //１桁目の組み合わせが既に出てきている場合は飛ばす
    if(in_array($numToArr[$i], $firstList)) {continue;}
    $firstList[] = $numToArr[$i];
    for ($j = $i + 1; $j < $s; $j++) {
        //１桁目、２桁目の組み合わせが既に出てきている場合は飛ばす
        if ($i >= $j) {continue;}
        if(in_array($numToArr[$i] . $numToArr[$j], $secondList)) {continue;}
            $secondList[] = $numToArr[$i] . $numToArr[$j];
       for ($k = $j + 1; $k < $s; $k++) {
           if ($j >= $k) {continue;}
           $ansList[] = $numToArr[$i] . $numToArr[$j] . $numToArr[$k];
        }   
    }
}
```
既に出てきたものをチェックするために、配列を持っておくことで計算量をへらす。
STM32F303K8とOLED、OLED096UNO-AでHello Worldを表示

x mbedが使える人が対象

1.mbedでstm32f303k8をコンパイル出来るようにする。
2.新規プログラムを開く
3.下記のソースを書く。
4.oledのライブラリをインストールする。
Dai YokotaさんのSSD1306_128x64_I2Cライブラリを使用した。
https://os.mbed.com/users/jk1lot/code/SSD1306_128x64_I2C/file/46dcbaa430f4/SSD1306.h/

ライブラリのインストール方法は、
mbedの画面のプロジェクトディレクトリー名の所で右クリックして
新しいライブラリー...を選択してOLED検索画面からDai Yokotaを選択
する。
5.コンパイルする。
6.何らかの方法で書き込む。
7.Hello Worldが表示されたら終わり。

おまけ
8*8のフォントを変換して8*16にして見やすくした。


```c++

//
// 128*64
// 303 oled 8*16
//

#include "mbed.h"
#include "SSD1306.h"
#include "font_8x8.h"

DigitalOut myled(LED1);

#define ADDR     (0x3c<<1) //  address
//#define ADDR     (0x3d<<1) //  address
 
I2C i2c(I2C_SDA, I2C_SCL);

// Host PC Communication channels
Serial pc(USBTX, USBRX); // tx, rx

// Instantiate OLED
SSD1306 oled(i2c, ADDR);

char ddff[8*4];

void char11(int row,int com,int cha)
{
    
    int char_index1;

    char_index1 = cha-0x20;

    int ii;
    
for (ii = 0;ii<(8*2);ii++) {ddff[ii]=0;} // オールクリア
for (ii =0;ii<8;ii++){
ddff[ii+ 0] = ddff[ii+ 0] | ((font_8x8[char_index1][ii] >> 0) & 1)<<0; //1bit 1 
ddff[ii+ 0] = ddff[ii+ 0] | ((font_8x8[char_index1][ii] >> 0) & 1)<<1; //1bit 2
ddff[ii+ 0] = ddff[ii+ 0] | ((font_8x8[char_index1][ii] >> 1) & 1)<<2; //2bit 3
ddff[ii+ 0] = ddff[ii+ 0] | ((font_8x8[char_index1][ii] >> 1) & 1)<<3; //2bit 4
ddff[ii+ 0] = ddff[ii+ 0] | ((font_8x8[char_index1][ii] >> 2) & 1)<<4; //3bit 5
ddff[ii+ 0] = ddff[ii+ 0] | ((font_8x8[char_index1][ii] >> 2) & 1)<<5; //3bit 6
ddff[ii+ 0] = ddff[ii+ 0] | ((font_8x8[char_index1][ii] >> 3) & 1)<<6; //4bit 7
ddff[ii+ 0] = ddff[ii+ 0] | ((font_8x8[char_index1][ii] >> 3) & 1)<<7; //4bit 8
ddff[ii+ 8] = ddff[ii+ 8] | ((font_8x8[char_index1][ii] >> 4) & 1)<<0; //5bit 1
ddff[ii+ 8] = ddff[ii+ 8] | ((font_8x8[char_index1][ii] >> 4) & 1)<<1; //5bit 2
ddff[ii+ 8] = ddff[ii+ 8] | ((font_8x8[char_index1][ii] >> 5) & 1)<<2; //6bit 3
ddff[ii+ 8] = ddff[ii+ 8] | ((font_8x8[char_index1][ii] >> 5) & 1)<<3; //6bit 4
ddff[ii+ 8] = ddff[ii+ 8] | ((font_8x8[char_index1][ii] >> 6) & 1)<<4; //7bit 5
ddff[ii+ 8] = ddff[ii+ 8] | ((font_8x8[char_index1][ii] >> 6) & 1)<<5; //7bit 6
ddff[ii+ 8] = ddff[ii+ 8] | ((font_8x8[char_index1][ii] >> 7) & 1)<<6; //8bit 7
ddff[ii+ 8] = ddff[ii+ 8] | ((font_8x8[char_index1][ii] >> 7) & 1)<<7; //8bit 8
}//for

oled.writeBitmap((uint8_t*) ddff,
              row, (row + 2 - 1),   //y
              com, (com + 8 - 1));  //x

} //end chr11    

int main() {
    int ii;
    pc.baud(9600);
    
    char ss1[]="Hello World !";
    ii=0;
    while(ss1[ii]!=0){
        //     y  x    文字
        char11(0,ii*8,ss1[ii]);ii++;
    }
    pc.printf("Printed something\r");    
}


```

![oled_8x16_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/65f9e2fa-d208-df37-ccb7-54c9b5409090.jpeg)
Vimの機能紹介 | かごもく #40 開発環境の紹介

#この記事について
[かごもく #40 開発環境の紹介](https://kagoben.connpass.com/event/200585/)で，Vim の紹介をするために用意しました．

#Vimとは

> Vim（ヴィム）は、[vi](https://ja.wikipedia.org/wiki/Vi) から派生し、発展した高機能なテキストエディタである。
> -- [wikipedia](https://ja.wikipedia.org/wiki/Vim)より

#Why Vim?
- 軽くてCUI環境で動く
- キーバインドを覚えておくと色々なところで使える
  - [Vimium](https://chrome.google.com/webstore/detail/vimium/dbepggeogbaibhgnhhndojpepiihcmeb?hl=ja)（Chrome 拡張機能）
  - Shellでも使える（bash, zsh の [vi モード](https://www.techrepublic.com/blog/linux-and-open-source/using-vi-key-bindings-in-bash-and-zsh/)）
- Vim を使いこなしているとかっこいい
- Vim vs Emacs の[宗教戦争](https://ja.wikipedia.org/wiki/エディタ戦争)に Vim 勢として参戦できる

#便利機能紹介
Vim には色々な機能やプラグインがありますが，少ないキー打数で使用頻度が高いものを紹介します．

※　|　(vertical line) はカーソル位置を表します

##テキストオブジェクト
（参考：[text-objects | Vim日本語ドキュメント](https://vim-jp.org/vimdoc-ja/motion.html#text-objects)）

「ダブルクォーテーションに囲まれている文字を全て変更したいなぁ～」なんてしょっちゅうですよね．
そんなときに便利なのがテキストオブジェクトです．

```
# ノーマルモード
print("foo|bar")
```
↑ の状態で``ci"``と入力すると，

```
# 挿入モード
print("|")
```
サクッと編集できます．

ダブルクォーテーション以外にも，単語，文，段落，HTMLタグ, [], (), <>, {}, '', `` などのブロックの選択・変更が簡単に行えます．

最初の[オペレータコマンド](https://vim-jp.org/vimdoc-ja/motion.html#operator)は，``c``(変更)以外にも``d``(削除)，``y``(ヤンク)などのオペレータコマンドが使えます．

##矩形ビジュアルモード
（参考：[blockwise-operators | Vim日本語ドキュメント](https://vim-jp.org/vimdoc-ja/visual.html#blockwise-operators)）

「このクラスのメンバ変数を全部 const にしたいなぁ～」なんて毎日のことですよね．
そんなときに便利なのが矩形ビジュアルモードです．

```
# ノーマルモード
|var foo
var bar
var baz
```

↑ の状態で``<Ctrl>ve3jc``と入力すると，

```
# 挿入モード
| foo
 bar
 baz
```

↑ のように``var``が全て消えて挿入モードに入るので，挿入したい文字列（const）の後に``<Esc>``を入力すると，

```
# ノーマルモード
|const foo
const bar
const baz
```

簡単に変更が行えます．

##f, Fコマンド
（参考：[left-right-motions | Vim日本語ドキュメント](https://vim-jp.org/vimdoc-ja/motion.html#left-right-motions)）

「この関数の3番目の引数のところまで移動したいなぁ～」なんて日常茶飯事ですよね．
そんなときに便利なのが``f``, ``F``コマンドです．

```
# ノーマルモード
|def my_function (first, second, third):
    pass
```

↑ の状態で``2f,``と入力すると，

```
# ノーマルモード
def my_function (first, second|, third):
    pass
```

``f``に続けて移動したい先の文字を入力するとカーソルが移動します．
``F``の場合は，左の方向に検索してくれます．
派生コマンドとして``t``, ``T``, ``;``, ``,``があります．

#おわりに
まだまだ Vim を使いこなせていませんが，もっと精進したいと思います．
Safariでborder-radiusがhoverアニメーション中に効かない時の対処

##こういう時に

```html
// HTML
<div class="img_wrap">
  <img src="/img/sample.jpg" alt="">
</div>
```

```css

// CSS(SCSS)
.img_wrap {
  border-radius: 30px;
  overflow: hidden;

  img {
    transition: transform 0.3s;
    transform-origin: center center;
  }

  &:hover img {
    transform: scale(1.2);
  }
}
```

こんな感じでホバーした時に画像をズームアップして表示したい、そして角丸でマスクしたい、そういう時にSafariでのみアニメーション中に角丸がなくなってしまうことがあります。

##こうすると治るかも

```css

// CSS(SCSS)
.img_wrap {
  border-radius: 30px;
  overflow: hidden;
  will-change: transform; // 画像をラップしている要素にこれを付けます

  img {
    transition: transform 0.3s;
    transform-origin: center center;
  }

  &:hover img {
    transform: scale(1.2);
  }
}
```

画像をラップしている要素に **will-change: transform;** を付けましょう。
これで解決する場合がありますので、他の記事などによくある z-index を使用した方法で治らなかった方はこちらを試してみるといいかもしれないですね。


z-indexを使用した方法の記事
[safariでborder-radiusが動いてない時の解決法](https://qiita.com/Dyslexi4/items/8f6bd2b362adfcd2fd25)
会社用のgithubを作成した話

# はじめに
実際の現場ってgithubのアカウントが用意されており、入社したらそこに招待してもらうみたいな感じがありますよね。

先日会社用のgithubのアカウントを作成しました。
今までは個人開発で個人のgithubアカウントは持っていたのですが
会社用のアカウントはどう作成するのか、どのプランがいいのかなどネットにも賛否両論だったのでここでまとめます！

# ターゲット層
- これから会社を作りgithubのアカウントを作成したい
- 小さいコミュニティなどで使うアカウントが欲しい
などなど

# 結論
いきなり結論からいうと
1. 代表者の個人のアカウントを作成
2. 個人のアカウントの中でorganizationを作成
3. プランを決定する
4. リポジトリを作成
5. メンバーを招待していく
といった流れになります！！

会社によって色々な方法をとっていますが、今回はこちらの方法で説明していきます

## 個人用のアカウントを作成
こちらは個人用のアカウントを持っていれば一度行っていると思いますが一緒に行っていきます！
https://github.com/ 
こちらのページにいきましょう！

まずはsignUpでアカウントを新規に作成します！
自分が代表の場合は自分の情報で、
代表以外が管理する場合もここは代表者の情報で個人アカウントを作成する方がいいと思います！！

ひとまずメール認証などありますが
名前、メールアドレス、パスワードを入れてアカウントを作成しましょう！

## 個人のアカウントでorganizationを作成
organizationは組織という意味です！
![スクリーンショット 2021-01-24 19.11.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/825833/7d305afc-f719-c498-76d4-ad9c0e619522.png)
ここからYour organizationsをクリックです！

次にnew organizationsをクリック
次にプランを決めます！

![スクリーンショット 2021-01-24 19.13.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/825833/c2e8c7f8-f6e2-61f5-d6e2-8c9575cbdf73.png)
3プランありますが、organizationsを作るのであれば
TeamかEnterpriseを選択しましょう！！

Enterpriceは会社なのでこっちかなと思った方。
プランはいつでもアップデート可能なので、まずはTeamプランをお勧めします！

Teamプランではメンバーを一人招待するごとに4$ってイメージです。
小規模なチーム開発ならこちらで十分だと思います。

あとは組織のorganizationsの名前を決めて、カード情報を入れれば作成は完了です！
作成したものはこちらで切り替えられます！

![スクリーンショット 2021-01-24 19.17.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/825833/0f2e09ef-9f99-c73b-8aca-6ffcb649305c.png)

またメニューからYour organizationsをクリックすると
![スクリーンショット 2021-01-24 19.21.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/825833/f7700985-ecde-35f2-a46f-1e03900e4827.png)
こちらのsettingsから色々設定ができます！！


![スクリーンショット 2021-01-24 19.23.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/825833/881d415d-b4d3-46c8-51f3-9f2d81311f98.png)

ちなみにBilling & plansなどで月々の支払いや領収書なども見れるようになっています！！

## メンバーを招待する
organizationsの方でpeopleを開きましょう
そこのinvite memberで招待したい人のユーザー名、フルネーム、あるいはメールアドレスを入力して招待を送ります！
詳しくはこちらにhttps://docs.github.com/ja/github/setting-up-and-managing-organizations-and-teams/inviting-users-to-join-your-organization

これで完了です！！！
お疲れ様でした！！
Blenderで歯車

円→頂点48
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592705/13db3740-5b7c-269e-9cec-268388894eff.png)

編集モードで「2」を押して辺選択にした後に
「選択」→「チェッカー選択解除」で選択が一つ飛ばしになる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592705/5fb14756-5567-6087-fd16-66a19fb64c76.png)

ピボットポイントで「それぞれの原点」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592705/16b072d3-5d20-197f-1fd5-6353adacdb0d.png)

「S」で選択された辺の幅を縮める
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592705/976016db-fa38-cb53-db39-41b21e70762e.png)

「押し出し」ツールを長押しして法線方向に押し出しを選ぶ
黄色い球と細い棒みたいなやつが出てくる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592705/540b71a8-7b11-88c6-67a2-92f051c84be6.png)

「Ctrl＋i」で選択を反転させる（長い辺を選択した状態にする）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592705/f35261f8-9939-a01f-1b43-bf3c29573052.png)

法線方向に押し出しを選んだ時に出てきた黄色の玉みたいなやつを動かして外側に押し出す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592705/296397f6-7b52-31d1-6711-8a966a627abb.png)

「S」で辺を縮めて歯車っぽくする
（ピボットポイントは「それぞれの原点」を選択のままでやること！）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592705/c5717cde-1bd8-0dbc-554e-e443c488bd70.png)

「A」で全選択→「F」で面を張る
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592705/9f10578b-79ff-2d22-2100-2d416337f5c2.png)

「E」→「Z」で上方向に押し出し
（押し出しツールを押し出し（領域）に戻してやること）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592705/4e0a78b5-1369-1b0e-1a69-9497cceb3b3c.png)

「3」で面選択モードにして外側の一面だけ選択する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592705/d3817043-fa07-29d0-870c-a0113d2aed88.png)

「類似選択」で同じ形の面を選択する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592705/167ce072-99fc-c86c-bed6-05ef3eb85a43.png)


ピポットポイントをそれぞれの原点にする→「S」で縮める
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592705/c47c9d94-16a5-e28c-6947-64f31097b614.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592705/ac590b31-9db5-171f-bc2c-eeacc911969c.png)
Dockerコンテナ間でのFTPアップロードするためのdocker-compose.ymlの書き方

PHP+ApacheからFTPサーバーへデータをアップロードするアプリケーションを開発するために、まずはDockerで開発環境を作ることになりました。
その際に、コンテナ(Apache + php)とコンテナ(Pure-FTPd)の間の通信でハマったので、記事にまとめます。

## バージョン情報
* PHP 7.4
* Apache 2.4
* Pure-FTPd v1.0.47

## 結論
最終的な`docker-compose.yml`とPHPのプログラムの記述はこんな感じ。

### docker-compose.yml
```docker-compose.yml
version: '3.7'
services:
  app:
    build: .
    ports:
      - 443:443
    volumes:
      - .:/var/www/html
  ftp-server:
    image: stilliard/pure-ftpd:latest
    ports:
      - "21:21"
      - "30000-30009:30000-30009"
    volumes:
      - ./ftp/data:/home/ftpusers
    environment:
      - PUBLICHOST=ftp-server
      - FTP_USER_NAME=ftp_user
      - FTP_USER_PASS=ftp_password
      - FTP_USER_HOME=/home/ftpusers
```

### FTPでアップロードするプログラム
[参考にした記事](http://white-bear.info/archives/1338)

```ftp_upload.php
$ftp_server = 'ftp-server';
$ftp_port = 21;
$ftp_user_name = 'ftp_user';
$ftp_user_pass = 'ftp_password';
$ftp_send_file = '/hoge/data.txt';
$ftp_remote_file = '/hoge/data.txt';
 
// FTPサーバへ接続する
$conn_id = ftp_connect($ftp_server, $ftp_port);
if($conn_id == false){
    echo "FTPサーバへの接続失敗"."\n";
    exit();
}
 
// ユーザー名とパスワードでログインする
$login_result = ftp_login($conn_id, $ftp_user_name, $ftp_user_pass);
if($login_result == false){
    echo "FTPサーバへのログイン失敗"."\n";
    // 接続を閉じる
    ftp_close($conn_id);
    exit();
}
 
//パッシブモードに設定
ftp_pasv($conn_id, true);
 
// ファイルをアップロードする
if (ftp_put($conn_id, $ftp_remote_file, $ftp_send_file, FTP_BINARY)) {
    echo "UPLOAD 成功"."\n";
} else {
    echo "UPLOAD 失敗"."\n";
}
 
// 接続を閉じる
ftp_close($conn_id);
```

Dockerfileはあまり重要ではないので割愛します。

## 重要なところ
```docker-compose.yml
version: '3.7'
services:
# 省略
  ftp-server:
    # 省略
    environment:
          - PUBLICHOST=ftp-server
```

```ftp_upload.php
$ftp_server = 'ftp-server';
```

## 解説
[参考記事](https://knowledge.sakura.ad.jp/16082/)
Dockerは**コンテナ名**を使用してコンテナ間の通信をするそう。
なので、

```docker-compose.yml
version: '3.7'
services:
# 省略
    ftp-server:
```
ここで設定したサービス名(`container_name`で明示的にしていないとサービス名がコンテナ名となる)と、

```docker-compose.yml
# 省略
environment:
      - PUBLICHOST=ftp-server
```
この`PUBLICHOST`を合わせておかないと、Docker間でのFTP通信ができない。（ホスト名が違うとエラーが出る）

`docker-compose.yml`内の項目を合わせたら、これでFTPサーバー側の設定はできたので、

```ftp_upload.php
$ftp_server = 'ftp-server';
```
上記のようにアプリ側でコンテナ名(ホスト名)を指定して、プログラムを実行すれば無事アップロードが成功する。（はず）
Qiita(MathJax)でTeXを書く為の設定(マクロ)

# Qiita(MathJax)でTeXを書く為の設定(マクロ)

# TeXマクロ

ここより下に，実際にTeXマクロが書かれています。

$$\def\bm{\boldsymbol}
\def\DIS{\displaystyle}
\def\ff(#1/#2){\frac{#1}{#2}}$$

ここまで。


QiitaではMathJax（TeX）で数式を書く場合，TeX同様にマクロを設定することができます。各記事に`\def`を書いておくことでマクロが設定できます。QiitaやHackMDで使用しています。

マクロを設置するときは，ページの先頭である必要があります。ただし，`$ $`が記述されるため行を使ってしまい余計な空白ができてしまいます。`$$ $$`を使用して，1行で事足りるように工夫できます。

#### ディスプレイスタイル (簡略マクロ)
`$\def\DIS{\displaystyle}$`

`$\frac{a}{b}$` $\frac{a}{b}$

`$\DIS \frac{a}{b}$` $\DIS \frac{a}{b}$

#### ベクトル用の太字
`$\def\bm{\boldsymbol}$`

`\bm F`  $\bm F$

#### 分数 (簡略マクロ)
`$\def\ff(#1/#2){\frac{#1}{#2}}$`

`$\ff(a/b)$` $\ff(a/b)$
【Godot】キー入力に合わせたアニメーションをつける　

##やりたいこと
左右ボタンを押したらキャラクターが歩きアニメーションを開始し、止まったらアニメーションを歩きストップさせたい。

##手順
以下の画像をSpriteノードで読み込む
![walk_animsprite_small.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166869/c75895b9-e95e-37fa-a391-319d8bb30303.png)

Spriteのインスペクタ＞Animation＞Hframeを２にする。
これでスプライトを横に2分割し、フレームで切り分けることができる。

AnimationPlayerノードを作成し、「アニメーション」ボタン＞新規で「walk」を作成。
アニメーションの長さを0.4秒にし、ループボタンを押す。
![ループ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166869/f0c9da6f-a082-bc29-d22a-24dc97f3535c.png)


スプライトのインスペクタ＞Animation＞Frameの鍵マークを押し、0秒の位置にキーフレームを打つ。
タイムラインの0.2秒のところに移動し、Frameを1にして鍵マークを押す。
![キーフレーム.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166869/9f61f527-70a7-5aa9-335f-70b574d8febd.png)

再生ボタンを押すとプレイヤーが歩き始める。
![再生ボタン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166869/6d2439ca-5148-d56c-2615-353e9ec6a23b.png)

新規でアニメーション「stop」を作成。
Frameを0にして鍵マークを押し、自動再生ボタンを押す。
これでキャラクターは初期状態では止まっている状態になる。
![自動再生ボタン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166869/91c58f5a-2e4d-f0c6-c634-bb903775d3a1.png)

スクリプトで以下のようにAnimationPlayerを操作する。
（[Spriteをキー入力で左右反転させたコード](https://qiita.com/MONDY_W/items/1f9f40bdb9446e4d75dc)を利用）

```Player.gd
func _process(delta):
	if Input.is_action_pressed("move_right"):
		$AnimationPlayer.play("walk")
		$Sprite.set_flip_h(false)
	elif Input.is_action_pressed("move_left"):
		$AnimationPlayer.play("walk")
		$Sprite.set_flip_h(true)
	else :
		$AnimationPlayer.play("stop")
```


これで左右ボタンを押したら歩きアニメーションを開始し、止まったらアニメーションをストップさせることができる。
RHELのサブスクリプションの登録方法

#RHELのサブスクリプションの登録方法

RHELが個人向け無償のプログラムについて、使用できる端末数を16台に増やしたため、RHELをインストール。
しかし、サブスクリプションの登録方法が面倒かつわかりにくかったため、メモを残しておく。

Red Hatの公式HPのアカウント管理画面からは物理システムの登録ができない。
そのため、ターミナルを開き下記を入力する。


```
$ sudo subscription-manager register --auto-attach
```

すると下記が表示される。

```
登録中: subscription.rhsm.redhat.com:443/subscription
ユーザー名: ※サブスクリプションを所有しているRedHatアカウント名を入力
パスワード: ※パスワードを入力
```

上記のとおり、登録したRedHaのアカウント名とパスワードを入力する。
すると、下記が表示される。

```
このシステムは次の ID で登録されました: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx
インストール済み製品の現在の状態:
製品名: Red Hat Enterprise Linux Server(製品名はサブスクリプションにより異る)
状態:   サブスクライブ済み
````

状態がサブスクライブ済みになればOK。
HPのアカウント管理画面にもサブスクライブされたことが表示される。
【Python】リスト結合_備忘録

#【構文】joinメソッド
>i.join(iterable)
i:区切り文字
iterable:連結対象のリスト


~~~python
data = ['りんご','ゴリラ','ラッコ','コアラ']
print('⇒ '.join(data))　#結果:りんご⇒ ゴリラ⇒ ラッコ⇒ コアラ
~~~

~~~python

data1 = [1,2,3,4]
print('⇒ '.join(data))　#結果:TypeError: sequence item 0: expected str instance, int found
~~~


数値型リストをそのまま連結することができないためstr型に変更する必要がある。
※リスト内包表記

~~~python
data1 = [1,2,3,4]
print('の'.join([str(i) for i in data1]))
~~~
GitHub の Issueを元にリリースノートを自動作成する

## これはなに

git のタグをプッシュした際に gren と GitHub Actions を使いリリースノートを自動生成するワークフローを作成する手順をまとめたもの。

## tagのプッシュをトリガーにGitHub Actions を実行する

tagのプッシュをトリガーにするには以下のようにすると実現することができます。  

```yml
name: XXX

on:
  push:
    tags:
      - 'v*'
  workflow_dispatch:
```

GitHub Actions では他にも push や プルリク作成、スケジュール実行などの方法でワークフローを開始することができます。

https://docs.github.com/ja/actions/reference/events-that-trigger-workflows

## gren を使いリリースノートを作成する

リリースノートの作成を自動化するには、もとの情報を引っ張ってくる必要があります。
選択肢としては、GitHubのIssueとコミット履歴があったのですが Issue の方が重要な情報を記述していることや、コミット履歴だと順番がごちゃごちゃになる可能性があることがから今回は Issueの情報を元にリリースノートを作成しようと思います。

自前で作ることもできますが、今回は gren を使います。

https://github.com/github-tools/github-release-notes

`GREN_GITHUB_TOKEN` にトークンをセットして `gren release` とコマンドを実行することで直近のタグと前回のタグの間にクローズされたIssueをまとめてくれます。

Issue以外にもコミット履歴を元に生成したり他にもオプションがあるため好みに合わせてリリースノートをカスタマイズできます。

## gren を GitHub Actions に組み込む

GitHub Actions に組み込むためには gren が node.js に依存しているため設定を追加してコマンドw実行します。
最終的には以下のようになりました。

```yml
name: Release

on:
  push:
    tags:
      - 'v*'
  workflow_dispatch:

jobs:
  note:
    name: Release
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Use Node.js
        uses: actions/setup-node@v1

      - name: Install Gren
        run: npm install github-release-notes -g

      - name: Create a release
        run: gren release --username=xxx --repo=xxx--tags=${GITHUB_REF#refs/tags/}
        env:
          GREN_GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
```

`--tags` を指定しないと連続でタグを作成プッシュしたときに動作しなかったため明示的にバージョンを指定してコマンドを実行しています。

以下のようにリリースが自動生成されます。Issueが紐づき求めていた挙動を実現できました。

![Screen Shot 2021-01-24 at 18.59.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33499/a7bb9192-54fe-5f8b-4be0-0169792053b2.png)


## まとめ

- GitHub Actions を使って自動的にリリースノートを作成した
- gren では Issueやコミットログを元にリリースノートを作成できる

サンプルも作成したのでよければ参考にしてください
https://github.com/decoch/gren-example
[npm / serverless framework] Uploaded file must be a non-empty zip 発生

# 概要

`$ sls deploy`が突然できなくなった。
（`$ serverless deploy`のことね）

# エラー

エラー文はこんな感じでした
（もう手元には残ってなかったので、参考サイトに掲載されているエラー文を加工して貼り付けています）

```bash
$ sls deploy

.....

Uploaded file must be a non-empty zip (Service: AWSLambdaInternal; Status Code: 400; Error Code: InvalidParameterValueException; Request ID: xxxxxxxxxxxxxxxxxxxxx; Proxy: null)
```

# 原因

どうやら、`node`のバージョンが`15.6.0`などの新しいものになっていると、このエラーが発生するらしい。

ちなみに自分は...

## Version

```
$ node -v
v15.6.0

$ serverless --version
Framework Core: 2.20.1
Plugin: 4.4.2
SDK: 2.3.2
Components: 3.5.1
```

はい、今日構築した環境にnodeをインストールしましたからね >_<;

# 対処法

やむなく、`node`のバージョンを`14.14.0`に下げてエラーを回避しました。

これで3時間は食った気がします.....
う～んもったいない

```bash
# こんな感じの手順を踏みました
$ nodenv install 14.14.0
$ nodenv global 14.14.0
$ npm install serverless
```


# 参考サイト

* Github: aws / aws-cdk の issue#12536  
[aws-lambda: Uploaded Zips of Functions are Empty](https://github.com/aws/aws-cdk/issues/12536)  
ここを見ると、かなりの人が困っている様子でした。  
しかしまだ治っていない...。（2021/01/24 18:46 現在）

* 追記

> 2021/2/11
> The fix will be available in the next release.
> https://github.com/aws/aws-cdk/issues/12536#issuecomment-777288723

2/11のコメントに、「次のリリースで修正されるよ」と書いてあった。
その時点からすでに２～３回リリースされているので、そろそろ直っているかも...？
Pythonのunittestで「Exception has occurred: SystemExit」が出る

Pythonのunittestで「Exception has occurred: SystemExit」が出たときの対応。


## エラーが出る理由
文法等に誤りがあるわけではなく、例えば、デバッグモードで起動するなど、実行環境に問題があると、このエラーが出ます。unittestではデバッグモードで起動されることを想定していないようです。
デバッグなしで起動すると、このエラーは出てきません。

どうしてもデバッグモードでも使いたい方は以下の方法でエラーを消すことができます。

## デバッグモードでもエラーをなくす方法

```python
if __name__ == "__main__":
    unittest.main()
```

これを

```python
if __name__ == "__main__":
    unittest.main(exit=False)
```

に書き換えるとエラーが出なくなります。


##参考URL
https://stackoverflow.com/questions/9202772/tests-succeed-still-get-traceback
【PHP】初中級者が解くべき過去問精選 100 問を解いてみた【5問目/100】

アルゴリズムのアウトプット

ということで、
[レッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編：目指せ水色コーダー！】](https://qiita.com/e869120/items/eb50fdaece12be418faa#2-3-%E5%88%86%E9%87%8E%E5%88%A5%E5%88%9D%E4%B8%AD%E7%B4%9A%E8%80%85%E3%81%8C%E8%A7%A3%E3%81%8F%E3%81%B9%E3%81%8D%E9%81%8E%E5%8E%BB%E5%95%8F%E7%B2%BE%E9%81%B8-100-%E5%95%8F)@e869120さん



>AtCoder で水色コーダー、つまりレーティング 1200 を少ない問題数で達成するために、茶色コーダー・緑コーダーにとって適切な教育的良問を 100 問集めました。

こちらの記事の初中級者が解くべき過去問精選 100 問
をPHPで解いていきます。

```php
<?php

$s = trim(fgets(STDIN));
$s = str_replace(array("\r\n","\r","\n"), '', $s);
$s = explode(" ", $s);

$priceA = $s[0];
$priceB = $s[1];
$priceC = $s[2];
$needA = $s[3];
$needB = $s[4];

$priceList = [];

//AとBの少ない方の数分をABで買って足りないを買い足す場合
$buyA = 0;
$buyB = 0;
$buyC = min($needA, $needB) * 2;
if ($needA <= $needB) {
    $buyB = $needB - $buyC / 2;
} else {
    $buyA = $needA - $buyC / 2;
}
$priceList[] = $buyA * $priceA + $buyB * $priceB + $buyC * $priceC;

//Cを買わない場合
$priceList[] = $priceA * $needA + $priceB * $needB;

//Cのみ買う場合
$priceList[] = $priceC * 2 * max($needA, $needB);

echo min($priceList) . "\n";
}
```
全列挙するとオーバーフローするので、条件別に分けて計算量を減らす問題
App Service の おれおれ Worker Tier を削除する

## はじめに

[こちら](https://qiita.com/tkeydll/items/a17bafe8046d18d5aebb)では、カスタムイメージを使った「おれおれ App Service」を作りました。ここではその削除手順を示します。


## 手順

作成時とは逆の手順を踏む必要があります。以下ようになります。

- SKU の削除
- VMSS インスタンスの削除
- VMSS イメージの 2016-Datacenter への変更
- Worker Tier の削除


## SKU の削除

通常の Worker Tier の削除と同様に、まずは削除する Worker Tier を使用している SKU を削除します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64309/904e02e9-4f57-3964-3c09-68222bb58546.png)


## VMSS インスタンスの削除

稼働している VMSS インスタンスを削除します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64309/6d08c986-d9b6-af19-481f-a0b0b90795f0.png)

Worker Tier の Provisioning が完了し、インスタンスがゼロになっている事を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64309/58d05a8c-708c-ca97-eeb0-ff47a4241ff3.png)


## VMSS イメージの変更

作成時とは逆に、VMSS のイメージを「2016-Datacenter」に変更します。

```powershell
$vmss = Get-AzureRmVmss -ResourceGroupName appservice.local -Name CustomWorkerTierScaleSet
$vmss.VirtualMachineProfile.StorageProfile.ImageReference.Sku = "2016-Datacenter"
$vmss.VirtualMachineProfile.StorageProfile.ImageReference.Version = "Latest"
Update-AzureRmVmss -ResourceGroupName appservice.local -Name CustomWorkerTierScaleSet -VirtualMachineScaleSet $vmss
```

## Worker Tier の削除

VMSS イメージが「2016-Datacenter」に変更されたことを確認したら Worker Tier を削除します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64309/d79ae4b3-b871-1e20-82c0-469870bc4035.png)
これで Worker Tier 及び VMSS が削除されるはずです。


## VMSS イメージを戻さずに Worker Tier の削除を実行した場合

VMSS イメージを 2016-Datacenter に戻すのを忘れると、VMSS だけが削除され、Worker Tier が残ってしまいます。
App Service の Worker Tiers ブレードでは、以下の様な状態に。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64309/430b471c-bf94-e7f6-a017-e5443f50be32.png)

こうなった場合は、Delete の上の Rebuild を実行すると VMSS が再作成されます。そうしたら VMSS イメージを変更した上で削除を実行してください。


### Rebuild できない場合

Rebuild を実行できない場合は、手動で VMSS を作成します。
Web ポータルからでは文字数制限に引っかかって「なんちゃらWorkerTierScaleSet」という名前の VMSS は作成できないので、 Powershell や Azure CLI を使います。以下は Powershell の例です。

```bash
$vmssName = "CustomWorkerTierScaleSet"
$vmPassword = ConvertTo-SecureString -AsPlainText -Force
$vmCred = New-Object System.Management.Automation.PSCredential("workeradmin", $vmPassword) 

$vmss = New-AzureRmVmssConfig -Location "local" -SkuCapacity 1 -SkuName "Standard_A1" -UpgradePolicyMode "Automatic"
$PublisherName = "MicrosoftWindowsServer"
$Offer = "WindowsServer"
$Sku ="2016-Custom"
$Version = "latest"
Set-AzureRmVmssStorageProfile -Name "CustomWorkerTier"  -OsDiskCreateOption 'FromImage' -OsDiskCaching "None" -ImageReferenceOffer $Offer -ImageReferenceSku $Sku -ImageReferenceVersion $Version -VirtualMachineScaleSet $vmss
New-AzureRmVmss -Credential $vmCred -VMScaleSetName CustomWorkerTierScaleSet -Location local -ResourceGroupName appservice.local
```


## おわりに

カスタム Worker Tier の削除手順を示しました。
普通に削除できない事からも真っ当なやり方では無い事が明らかですが、やりようがあるなら何でも良いと思います。
管理するのはあくまで「Azure Stack Hub Operator」なので。
【Godot】Spriteをキー入力で左右反転させる

プロジェクト＞プロジェクト設定＞インプットマップから
「move_right」「move_left」のアクションを追加し、キーを割り当てる。
![keyshortcut.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166869/4df46332-bba1-f299-9954-9868dad06bd8.png)

スプライトを作成し、以下のスクリプトをアタッチ。
$Spriteはスプライト名を入れる。

```Player.gd
func _process(delta):
	if Input.is_action_pressed("move_right"):
		$Sprite.set_flip_h( false )
	elif Input.is_action_pressed("move_left"):
		$Sprite.set_flip_h( true )
```

これでSpriteを左右反転できる。
macOS / Windows の Git 用に GnuPG (GPG) を準備する

macOS や Windows の Git でタグやコミットに署名できるように GnuPG ([The GNU Privacy Guard](https://gnupg.org/)) をインストールする手順や注意事項です。

実際に署名を付ける手順は以下などを参照ください。

-   [Git - 作業内容への署名](https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%81%95%E3%81%BE%E3%81%96%E3%81%BE%E3%81%AA%E3%83%84%E3%83%BC%E3%83%AB-%E4%BD%9C%E6%A5%AD%E5%86%85%E5%AE%B9%E3%81%B8%E3%81%AE%E7%BD%B2%E5%90%8D)
-   [コミット署名の検証を管理する - GitHub Docs](https://docs.github.com/ja/github/authenticating-to-github/managing-commit-signature-verification)

# macOS

## 前提

-   Homebrew が使えること
-   Git が使えること

## インストール

### GnpPG のインストール

```zsh
brew install gnupg
```

### pinentry-mac のインストール

GnuPG をインストールしただけでは、Git の CLI を直接使わずにコミットするときなど、秘密鍵を使うときのパスフレーズの入力に難があるので [^pinentry]、 pinentry-mac を使うように構成します。

[^pinentry]: [MacVim by macvim-dev](https://macvim-dev.github.io/macvim/) で [tpope/vim-fugitive: fugitive.vim: A Git wrapper so awesome, it should be illegal](https://github.com/tpope/vim-fugitive) の `:Gcommit` を使うときにパスフレーズを入力出来ないなど。

```zsh
brew install pinentry-mac
```

インストール後に表示される以下のメッセージの通りに `~/.gnupg/gpg-agent.conf` を作成します。

```
==> Caveats
You can now set this as your pinentry program like

~/.gnupg/gpg-agent.conf
    pinentry-program /usr/local/bin/pinentry-mac
```

以下のコマンドをそのまま実行すれば良いでしょう。

```zsh
echo "pinentry-program /usr/local/bin/pinentry-mac" >> ~/.gnupg/gpg-agent.conf
```

これで、秘密鍵を使うときにパスフレーズの入力でウィンドウが表示されるようになります。

![スクリーンショット 2020-12-27 19.34.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/127827/dd7e0047-5d2c-eeef-119f-f4a88388c80c.png)

## 期待したとおりに動かないとき

`gpg-agent` を停止してから試してみると良いかも。

```zsh
gpgconf --kill gpg-agent
```

# Windows

## 前提

-   Git for Windows が使えること

## GnuPG のインストールについて

Git for Windows に GnuPG が付いているため、改めてインストールする必要はありません。 [^gnupg-win] [^gnupg-win-home]

[^gnupg-win]: `choco install gnupg` などとして GnuPG をインストールし、Git がこれを使うように `git config` で設定することもできるとは思いますが、`.gitconfig` のポータビリティがなくなるので私は諦めました。

[^gnupg-win-home]: Git for Windows 同梱版や Windows 以外の GnuPG で使用される `~/.gnupg` ディレクトリが、Windows 用の GnuPG では `%APPDATA%\gnupg` になるなどの違いがあります。コマンドオプションや環境変数を設定して同じディレクトリを参照するようにもできますが、同じディレクトリを共用するとファイルのロック操作に不具合を生じるようです。

## `gpg` コマンドの実行について

GPG キーペアの作成など、`gpg` コマンドは Git Bash 上で使います。

通常の状態では `gpg` コマンドは PowerShell やコマンドプロンプトから使えないので、普段これらのシェルから `git` コマンドを使っている場合は注意が必要です。 [^ps-cmd]

[^ps-cmd]: 環境変数 `PATH` を適切に設定すれば使えるようですが。
PDFファイル解析にあたって２　PDF.jsの実装

# 書いたこと

PDFファイルの解析にあたって、PDF.jsの実装をまとめる。

# PDF.jsの解析手順

0. streamオブジェクトの生成
1. ヘッダーを解析
2. startxrefの値の読み取り
3. 相互参照テーブルの取得
4. カタログ辞書の取得

![PDFDocument.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70065/fb772329-d82b-d59b-95df-f745e6f73a09.png)


## 0. streamオブジェクトの生成

物理的なPDFファイルデータはstreamオブジェクトとして抽象化されている。
これによってデータが必要になった際にファイルデータを随時取得が行えるようになっている。

## 1-4. ドキュメントのグラフ階層の生成

```src/core/worker.js
async function loadDocument(recoveryMode) {
  await pdfManager.ensureDoc("checkHeader");            // 1. ヘッダー情報の取得
  await pdfManager.ensureDoc("parseStartXRef");         // 2. startxrefの解析
  await pdfManager.ensureDoc("parse", [recoveryMode]);  // 3-4. 相互参照テーブルとカタログ情報の取得

  if (!recoveryMode) {
	// Check that at least the first page can be successfully loaded,
	// since otherwise the XRef table is definitely not valid.
	await pdfManager.ensureDoc("checkFirstPage");
  }

  const [numPages, fingerprint] = await Promise.all([
	pdfManager.ensureDoc("numPages"),
	pdfManager.ensureDoc("fingerprint"),
  ]);
  return { numPages, fingerprint };
}
```

# PDF.jsの構文解析と字句解析


```
<<  /Type /Annot
	/Subtype /Link
	/F 4
	/Border [0 0 0]
	/Rect [255.99998 189.7099 467.49997 206.2099]
	/A <</Type /Action
		/S /URI
		/URI (https://doc.rust-lang.org/stable/book/)
	    >>
>>
```

## 字句解析

字句解析にかけることによって以下のトークン列に変換する

![pdfオブジェクトトークン列.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70065/cb3d220f-93c3-43e3-e7ba-18291a18d132.png)

```src/core/perser.js
  getObj() {

    // Start reading a token.
    switch (ch | 0) {
      case 0x30: // '0'
      case 0x31: // '1'
      case 0x32: // '2'
      case 0x33: // '3'
      case 0x34: // '4'
      case 0x35: // '5'
      case 0x36: // '6'
      case 0x37: // '7'
      case 0x38: // '8'
      case 0x39: // '9'
      case 0x2b: // '+'
      case 0x2d: // '-'
      case 0x2e: // '.'
        return this.getNumber();  // 数字トークンを取得
      case 0x28: // '('
        return this.getString();  // 文字列トークンを取得
      case 0x2f: // '/'
        return this.getName();    // 名前トークンを取得
      // array punctuation
      case 0x5b: // '[
        this.nextChar();
        return Cmd.get("[");      // Cmd("[") トークンを取得
      case 0x5d: // ']'
```

## 構文解析

トークン列からプログラミング言語の型に変換する

![構文解析結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70065/3aec092b-63ee-a2eb-ce4a-bad17a30195d.png)

1. `<<` から辞書を生成する
2. `>>` までキーと値を交互に設定していく

2を繰り返すことで、トークン列から辞書インスタンスを得ることが出来る。


```構文解析結果.js
{ 
	Name("Type"): Name("Annotate"),
	Name("SubType"): Name("Link"),
	Name("F"): 4,
	Name("Border"): [0, 0, 0],
	Name("Rect"): [255.99998, 189.7099, 467.49997, 206.2099],
	Name("A"): {
			Name("Type"): Name("Action"),
			Name("S"):	Name("URI"),
			Name("URI"): "https://doc.rust-lang.org/stable/book/",
		}
}
```
Vagrant＆DockerのRailsの開発環境構築　メモ

開発環境
Winodows 1p Pro (64bit)
Vagrant 2.2.14
Ubuntu 16.04.6 LTS
Docker 18.09.7
ruby:ruby 2.7.2p137 
/rails_docker
Dockerの勉強の為、Railsの開発環境構築を構築しました。
Vagrantフォルダ配下にrails_dockerディレクトリを置きその下に、Docker,docker-compose-ymlを配置。
さらに下にsrcディレクトリ置き、Gemfileを配置。
sudo docker-compose run web rails new . --force --database=mysql　コマンドを実行したら以下のエラーが発生した。
````

````
Building web
ERROR: Error processing tar file(signal: killed):
````
ソースを修正しながら再度インストールを実行。すると次のエラーが・・・・。

````
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    current directory: /usr/local/bundle/gems/racc-1.5.2/ext/racc/cparse
/usr/local/bin/ruby -I /usr/local/lib/ruby/2.7.0 -r
./siteconf20210122-7-k0z1kz.rb extconf.rb
extconf failedCannot allocate memory - /usr/local/bin/ruby

Gem files will remain installed in /usr/local/bundle/gems/racc-1.5.2 for
inspection.
Results logged to
/usr/local/bundle/extensions/x86_64-linux/2.7.0/racc-1.5.2/gem_make.out

An error occurred while installing racc (1.5.2), and Bundler cannot continue.
Make sure that `gem install racc -v '1.5.2' --source 'https://rubygems.org/'`
succeeds before bundling.

In Gemfile:
  rails was resolved to 6.1.1, which depends on
    actioncable was resolved to 6.1.1, which depends on
      actionpack was resolved to 6.1.1, which depends on
        actionview was resolved to 6.1.1, which depends on
          rails-dom-testing was resolved to 2.0.3, which depends on
            nokogiri was resolved to 1.11.1, which depends on
              racc
ERROR: Service 'web' failed to build: The command '/bin/sh -c bundle config --local set path 'vendor/bundle'   && bundle install' returned a non-zero code: 5

```
「extconf failedCannot allocate memory」というメッセージが.
メモリ不足の可能性を教えてもらい。
[VSCodeの中、Vagrant に SSH-FS 接続よりいい方法があるかな？]("https://qiita.com/pineappledreams/items/c1a28bc95f5a11999600")を参考にして、Vagrantファイルにメモリーを増やしました。

````
Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/xenial64" # Ubuntu 16.04 を使用
  #config.vm.network "forwarded_port", guest: 3000, host: 3000 # Railsで利用するポートを設定
  config.vm.network "private_network", ip: "192.168.33.10"
  config.vm.provider "virtualbox" do |vb|
    vb.memory = "4096" # メモリを多めに設定
  
  end
end
```
環境を構築して再度実行したら、無事インストール完了しました。
LISPでGPGPU(3)　CNN用の関数

#移植作業
以前、Elixir用に書いたCUDAコードをISLisp用に移植しています。

#とりあえず
(gpu-smult  f farray)
(gpu-pooling farray (stride-h stride-w))
(gpu-unpooling farray1 farray2 (stride-h stride-w))

を追加しました。

Elixir用に書いたDeepPipe2では正確な動作に至るまでに試行錯誤、デバッグに多くの時間を割きました。今回はそうした部分はなく、ひたすらLispで動くようにする移植作業です。地味にやってます。

#効率
Elixirの場合だとNIFSにより他言語を呼び出すためにタイムロスがありました。今回はCUDAそのものでlisp処理系をコンパイルしますので、そうしたロスはありません。問題は大量のデータを扱ったときにGCがうまく機能してくれるかどうかです。

まずはDNN,CNNに必要な関数を実装してからです。その後に誤差逆伝播のコードもlispに移植の予定です。


https://github.com/sasagawa888/eisl
Node.js on Raspberry Pi Zero WHのはまりどころ

# 概要
Raspberry Pi Zero WH上にNode.jsの実行環境を構築しました。
やりたいことは**Raspberry Pi Zero上での形態素解析と画像生成**です。

しかしRaspberry Pi Zero WHはARMv6コア、RAM512MBと最小限のアーキテクチャです。
リッチなことをやろうとすると一筋縄ではいきません。
色々工夫して何とか実行環境を構築できました。

その時のはまりどころを情報共有します。

# Agenda
主に以下3つではまったので、それぞれを説明します。
1.  ARMv6コア起因
2.  RAM不足起因
3.  ライブラリ不足起因

# はまったところ
## 1. ARMv6コアによるnpmエラー
Raspberry Pi ZeroはARMv6コアを実装しています。
v6コアは2000年代前半のアーキテクチャでちょい古です。
なので、

* apt/apt-getのインストールしてもnpmがエラーを吐く
* スクリプトを使わず手動でインストールしようにもNode.js公式のTOPではv7以降しか載っていない

よって過去のdistを掘り当てて手動コピーしないといけませんでした。
手順は以下の通り。

```
$ wget https://nodejs.org/dist/v11.15.0/node-v11.15.0-linux-armv6l.tar.gz
$ tar zxvf node-v11.15.0-linux-armv6l.tar.gz
$ cd [解凍したディレクトリ]
$ rm CHANGELOG.md LICENSE README.md
$ sudo cp -R * /usr/local/
$ node -v
$ npm -v
```

v11.15.0を選んだのは、現状ARMv6対応してるlatest versionだからです。
（間違ってたら指摘お願いします…！）

## 2. メモリ不足によるmecab-ipadic-neologdコンパイル失敗

最適な形態素解析を行うため、mecabの新語辞書インストールの必要がありました。
しかしインストール時のコンパイルで2GBのRAMが要求されます。
Raspberry Pi ZeroはRAM512MBですｗ　／(^o^)＼

公式にもあるスワップを使用したメモリ拡張で解決しました。手順は以下。
今のところ全く問題なく、MeCabれてます。

```
$ sudo dd if=/dev/zero of=/swapfile1 bs=1M count=2048  # 2GBのスワップ領域を確保
$ sudo chmod 600 /swapfile1                            # パーミッション設定
$ sudo mkswap /swapfile1                               # スワップ領域を作る
$ sudo swapon /swapfile1                               # スワップ領域を有効化
$ ./bin/install-mecab-ipadic-neologd -n                # インストール
$ sudo swapoff /swapfile1                              # スワップ領域を無効化
$ sudo rm /swapfile1                                   # スワップ領域を削除
```

## 3. ライブラリ不足によるnode-canvasのコンパイルエラー

自分はRaspberry Pi OS Liteをインストールしました。
初期ライブラリが少ないからなのか、以下の通り色々はまりました。

* npm install canvas時にcanvas moduleが見つからない
* アプリ実行時にjsdom(nodeパッケージ)がimageをロードできない的なエラーを吐く

原因は、単にライブラリが足りてないだけでした。
以下ライブラリをインストール後、エラー関連のパッケージを再インストール&ビルドして解決。

```
$ sudo apt-get install build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev libjpeg8-dev libgif-dev g++
$ npm install canvas --build-from-source
```

# さいごに

本記事で実行しているアプリは、もともとherokuのslug size制限で動かせなかったものです。
こんな小さなボードでこれだけのことができることに驚愕です。
ラズパイゼロ最高。オンプレミス最高。

# 参考記事
1. https://www.taneyats.com/entry/install-nodejs-on-raspberrypi-zero
2. https://github.com/Automattic/node-canvas/wiki/Installation%3A-Ubuntu-and-other-Debian-based-systems
3. https://girliemac.com/blog/2016/06/13/kittycam-update-with-raspberrypi3/
UbuntuにVSCodeをインストールする至高の手順

#TL;DR
士郎、貴様のVSCodeインストール手順が間違いだとなぜわからぬ！
#すでに周知の事実だとは思うのだが
検索しても古い方法ばっかりヒットするもので。
2021/03/07 update
この方法でもapt updateでの更新はできる。
つまりめんどくさい手順は不要ということである。
#手順
１．アクセス
ブラウザで https://code.visualstudio.com/download を開く。

２．インストール
表示されている .deb のボタンをクリックする。

３．選択
プログラムで開く（ソフトウェアのインストール）を選択。
OKをクリックする。
#以上
いちいちdebパッケージのファイルをダウンロードしたり
MSのppaを登録する必要はない。
【jQuery】Ajaxで追加した要素へ正しくイベントセットする方法

#概要
自作ポートフォリオの買い物メモアプリで、Ajaxを用いてメモの投稿機能を非同期処理に書き換えたところ、bootstrap4(modal) & jQueryで実装していた、投稿に対するコメント投稿機能が動かなくなる不具合が発生しハマったため、備忘録のために投稿しました。

#結論
私の場合、modalに値を渡す処理を書いていたjQueryのコードを修正したことで改善出来ました。
Ajaxで実装した動的な要素(投稿)に対して、適切なイベントセットが出来ていなかったことが原因です。

```ruby:app/assets/javascripts/comment_modal.js
変更前
$(".js_comment_btn").on("click",function() {
$("#comment_id").val(this.value);
});

変更後
$(document).on("click",".js_count_btn",function() {
$("#comment_id").val(this.value);
});

```

```app/views/notes/index.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= full_title(yield(:title)) %></title>
     <%= csrf_meta_tags %>
     <%= stylesheet_link_tag    "application", media: "all", "data-turbolinks-track": "reload" %>
     <%= javascript_include_tag "application", "data-turbolinks-track": "reload" %>
  </head>

  <body>

    <!-- 以下、投稿用の部分テンプレート -->
    <div id="index">
     <%= render "shared/my_content", note: @own_notes %>
    </div>
    
    <!-- 以下、コメント投稿用のモーダル -->
    <%= render "shared/note_modal" %>

    <%= form_with model: @note do |f| %>
      <%= f.text_field :content,id:"note_create_form",class:"form-control" , placeholder: "商品名を入力" %>
      <%= f.submit "投稿", class: "btn btn-light form-btn" %></li>
    <% end %>            
  <body>
</html>
```

```app/views/notes/_my_content.html.erb
  <% note.each do |note| %>
    <ul>
      <li class="note_content">
        <%= note.content %>
        <button type="button" class="btn js_comment_btn" data-toggle="modal"
         data-target="#comment_Modal" value="<%= note.id %>" id="js_count">
        <i class="fa fa-sort" aria-hidden="true"></i></button>
      </li>
    </ul>
  <% end %>
```

```app/controllers/notes_controller.rb
  class NotesController < ApplicationController

    def create
      note = current_user.notes.build(note_params)
        if note.save
          @own_notes = current_user.notes.includes(comments: :user)
          render "create.js.erb"
        else
          flash[:danger] = "投稿に失敗しました"
          render "notes/index"
        end
    end
  end
```

```app/views/notes/create.js.erb
$("#index").html("<%= j(render "shared/my_content", { note: @own_notes }) %>")

```

##具体的解説

###エラー内容
①Ajax形式でPOSTリクエストを送り、notes_controllerで投稿オブジェクトを作成。
②jsファイル(create.js)にレンダリングし部分テンプレートの中身を更新
③作成されたオブジェクトのbuttonでモーダルを起動
④モーダルにテキスト入力してPOSTリクエストを送るとエラーが出る
(ActiveRecord::RecordNotFound (Couldn't find Note with 'id'=):)
⑤ページをリロードし再度、③④を実行すると動く

devtoolで調べた結果、modalに投稿のIDを渡す処理が走っていませんでした。
その後原因究明してわかったのは

部分的にしか更新しないAjaxだと、DOM要素の指定に工夫が必要だということ

今回の場合、
jsファイルで部分的にページの更新
↓
modalを呼び出すbutton要素部分(部分テンプレート内)とmodal本体(部分テンプレート外)の間で
整合性が取れなくなり④のエラーが発生
↓
ページ全体のリロードによりDOMが再構築されると問題なく動くようになる  

そのため、下記のように親要素の下のクラスという指定をしてあげることで
改善することが出来ました。

```ruby:app/assets/javascripts/comment_modal.js
$(document).on("click",".js_count_btn",function() {
$("#comment_id").val(this.value);
});
```

#実行環境
・Ruby v2.7.0
・Ruby on Rails v5.1.7
・jQuery-rails v4.4.0
・bootstrap v4.3.1
RailsにおけるSQLインジェクション対策について - Rails Tutorial リスト13.46

## はじめに
Rails Tutorialを進めていく中で気になったところを記事にして残してます。

記事に間違いがある場合は教えてください:grin:

## 用語解説

### SQLインジェクションとは

> SQLインジェクションは、Webアプリケーションのパラメータを操作してデータベースクエリに影響を与えることを目的とした攻撃手法です。SQLインジェクションは、認証をバイパスする目的でよく使われます。他にも、データを操作したり任意のデータを読み出したりする目的にも使われます。

> [Rails セキュリティガイド - Railsガイド](https://railsguides.jp/security.html#sql%E3%82%A4%E3%83%B3%E3%82%B8%E3%82%A7%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3)

つまり、ログインフォームや投稿フォームなどでSQLのデータを処理する際に、不正**（サーバーからみると正常）**なデータが実行されてしまうことです。
これによってログイン偽装やデータの抜き取りが起こります。

### エスケープ処理とは

「'」「"」「NULL」「改行」などのSQL文において都合の悪い文字を使えなくすることです。

## 悪い例

### 不正に認証が通ってしまう

以下のコードでログイン処理をするとしましょう。

```ruby:
User.find_by("login = '#{params[:name]}' AND password = '#{params[:password]}'")
```

一見問題はなさそうに見えますが、もし以下のパラメータが入っていたとします。

```ruby:
params = { name:     "' OR '1'='1",
           password: "' OR '2'>'1" }
```

これを実行すると以下のようなSQL文が呼ばれることになります。

```sql:
SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '' OR '2'>'1' LIMIT 1
```

要約すると`1=1`, `2>1`が成り立つ時usersから1人取り出してくださいということです。
ログインとしての機能を果たさなくなってしまいました。

## 原因

こうなってしまう原因はエスケープ処理をしていないからです。

## 対策

対策としてSQL文を書くときには直接文字列を代入するのではなく、**必ず**エスケープ処理をするようにしましょう。

エスケープ処理には以下の方法があります。

1. 配列、ハッシュとして渡す。（モデルのインスタンスのみ）
1. sanitize_sql()を使う。（それ以外）


### 対策１ 

モデルの場合はこちらが楽です。

```ruby:
# 配列
Model.where("login = ? AND password = ?", entered_user_name, entered_password).first

# もしくはハッシュ
Model.where(login: entered_user_name, password: entered_password).first
```
### 対策２

sanitize_sqlというメソッドでエスケープできます。

```ruby:
# 3つの例
sanitize_sql(["name=? and group_id=?", "foo'bar", 4])

sanitize_sql(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])

sanitize_sql("name='foo''bar' and group_id='4'")
```
## 参照

[Rails セキュリティガイド - Railsガイド](https://railsguides.jp/security.html#sql%E3%82%A4%E3%83%B3%E3%82%B8%E3%82%A7%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3)
[ActiveRecord::Sanitization::ClassMethods - Rails API](https://api.rubyonrails.org/classes/ActiveRecord/Sanitization/ClassMethods.html#method-i-sanitize_sql_for_conditions)
mac + docker でrosを設定できるところまでやる

## 概要
macとdockerを使ってrosを立ち上げる時の備忘録

## ros image + ubuntu vnc
https://qiita.com/karaage0703/items/957bdc7b4dabfc6639da

ここを参考にしてVNCを用いてrosイメージのubuntu環境を触ることができる。

## docker 環境にusbデバイスを認識させる

そもそもmacがどうusbデバイスを認識しているのかがわかっていないが、
docker コンテナにusbデバイスを認識させるためにはvirtualbox ベースのdocker hyperviser を使用する必要がある。

その際、https://qiita.com/shungok/items/6c5a7b5a9d63d51e6615#2-docker%E3%83%9B%E3%82%B9%E3%83%88os%E7%94%A8%E4%BB%AE%E6%83%B3%E3%83%9E%E3%82%B7%E3%83%B3dockervm%E4%BD%9C%E6%88%90
ここをフォローする。


https://github.com/jlhonora/lsusb
ここを使いmacからlsusbコマンドを実行できるようにすると便利。

## ハイパーバイザを用いる時のvncへのポートフォーワーディング
https://qiita.com/hnakamur/items/3640825cf2c5d2d4a52a
ここを参考にし、VMにポートフォーワーディングを設定する。



## よくわからないところ
vnc でros環境のubuntuに接続し、
/dev/video
などのデバイスドライバを確認しようとしても、見つからないこと。


## 使ったコマンド

```
docker-machine ls 
eval $(docker-machine env docker-host-default)
vboxmanage controlvm "docker-host-default" natpf1 "test,tcp,127.0.0.1,6080,,6080"
docker  run --rm --device /dev/usbmon0 --device /dev/usbmon1 --device /dev/usbmon2 -p 6080:80 --shm-size=512m tiryoh/ros-desktop-vnc:kinetic

docker run -p 6080:80 --shm-size=512m tiryoh/ros-desktop-vnc:kinetic

sudo apt-get update
sudo apt-get install -y usbutils

$ lsusb
$ sudo apt-get install ros-kinetic-uvc-camera
(console-1) $ roscore
$ rosparam set usb_cam/video_device "/dev/video1"
$ rosparam set usb_cam/pixel_format yuyv
(console-2) $ rosrun usb_cam usb_cam_node
(console-3) $ rosrun image_view image_view image:=/usb_cam/image_raw
(console-2) $ rosrun uvc_camera uvc_camera_node _device:=/dev/video1

camera callibration
(console-1) $ roscore
(console-2) $ rosrun usb_cam usb_cam_node
(console-3) $ rosrun camera_calibration cameracalibrator.py --size 9x6 --square 0.024 image:=/usb_cam/image_raw
$ cp /tmp/calibrationdata.tar.gz /path/to/destination
【Python】Atomで競技プログラミング用、linterの導入(警告非表示))【Mac】

[【Python】Atomで競技プログラミング用の環境構築(input()使えます！)【Mac】](https://qiita.com/rudorufu1981/items/26df16208efdd8d6bc13)

〜こちらの記事の続き〜
単体の記事としても成立します。

#linterを導入したい。
**linter**とは静的エラーチェックをいろいろやってくれるやつ。
デバッグ実行時前に、単純なスペルミスや構文チェックができたら嬉しい。

いろいろググったら、**pylint**というのがいいらしんだが、
**pylint**を導入すると、規約どおりのコードではない！と不要な警告が出まくるらしい。
競技プログラミングのコードは、規約に従ってなくても問題ないし、

- 単純なスペルミスや構文チェック

このチェックだけがしたい。
警告非表示とかでググっても、「pylintrc」ファイルをいじってねという記事しか見つからず、いじっても全然うまくいかなかったw
他いろいろググっても解決方法が見つからなかったので記事にしておく。

##手順
①linter-pylamaをatomからインストール。
設定から
②Ignore Errors And Warningsの箇所で、「W,C」と入力。
RとかE以外のやつも入れておいてもいいかも。
![スクリーンショット 2021-01-24 17.34.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/785975/0c198046-9931-5177-7cd5-a5a6ff61d7fc.png)


③Use PyLint のみにチェック、それ以外はチェック外す。

**以上！！！**

なんかエラーが出たら、前提となるものが色々足りないので、適当にインストールすればOK。
(atomでのパッケージインストール→linter、pip→pylamaとかそのあたり。ググればいろいろでてくる)

##実際の動き
###通常
![スクリーンショット 2021-01-24 17.41.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/785975/48dc932f-d55a-2e4b-9365-d2df56ca14ce.png)
警告0件。

###なんかスペルミスしてみる。
ファイルを保存したタイミングで、スペルミスの箇所が赤波線で表示された。
フッタをみると、エラーが1件と表示されている。
![スクリーンショット 2021-01-24 17.42.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/785975/e3b97c80-1434-a0bc-e8ae-385e6b9c64ba.png)


###if文の最後にコロン忘れた。
赤波線の行を疑えばコロンミスに気付く。
![スクリーンショット 2021-01-24 17.46.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/785975/3d70711a-5423-dd46-e76b-a8eee2ce10bf.png)


**以上！！！**