OmniAICreator/Qiita-1.07M huggingface dataset:
kintoneのテーブルでアプリのレコードの登録・更新・削除をする（４）

今回はこのシリーズ最終回。
「登録用アプリ」からレコードを削除したときに「英単語アプリ」側もレコードの削除をする。
というカスタマイズをします。
削除するだけなので内容軽めです。

前回までの記事はこちらです。

* [kintoneのテーブルのレコードを別アプリのレコードに登録する](https://qiita.com/juri_don/items/eded7d5d8ce9ef2a3e8a)
* [kintoneのテーブルでアプリのレコードの登録・更新・削除をする（１）](https://qiita.com/juri_don/items/d69e375498e96847d4d9)
* [kintoneのテーブルでアプリのレコードの登録・更新・削除をする（２）](https://qiita.com/juri_don/private/2ddb06c5ddaade5b7209)
* [kintoneのテーブルでアプリのレコードの登録・更新・削除をする（３）](https://qiita.com/juri_don/private/70fa08591ac1304df36d)


# JavaScript

削除したい英単語はuidsフィールドで特定できるので
uidsから削除する英単語のレコードのレコード番号を取得します。
※レコード番号を指定しないとレコード削除できないようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/a7d418a6-8e82-9aae-fe88-d5c32c32eb67.png)

```js
kintone.events.on(
  ["app.record.detail.delete.submit", "app.record.index.delete.submit"],
  async (event) => {
    const client = new KintoneRestAPIClient();
    const appId = 英単語一覧アプリのID;

    // 英単語一覧アプリ検索
    const resGet = await client.record.getRecords({
      app: appId,
      fields: ["$id"],
      query: `UID in (${event.record.uids.value})`,
    });

    // 削除するレコードのレコード番号を配列に入れる。
    const delIds = [];
    resGet.records.forEach((r) => {
      delIds.push(r.$id.value);
    });

    try {
      const res = await client.record.deleteRecords({
        app: appId,
        ids: delIds,
      });
    } catch (e) {
      console.log(e);
      return false;
    }

    return event;
  }
);
```

# まとめと感想
テーブルから別アプリにレコードを追加するというだけならそんなに考える必要はないのですが、編集、削除、そもそもレコードが削除されちゃったとき等の場合には色々と考えなくてはいけませんね。
2つのアプリ間で不整合が起こる可能性大の割と危険なカスタマイズだなぁと思いました。

それと・・・ユニークキーで削除できたらいいのになぁ～。
次に同じようなものを作る時はレコードIDで特定するようにしようかなと思います。
migrationsディレクトリを削除してデータベースをDropして再作成したのにmakemigrationsするとNo changes detectedになる原因。

# やりたい事
マイグレーションとデータベースを完全にリセットし、プロジェクトを最初に作成した時のようにmakemigrationsとmigrateを実行したい。

# アプリ名を指定しないとmakemigrationsで『No changes detected』になってしまう。
データベースを完全にクリアして各アプリケーション内の__migrations__ディレクトリを丸ごと削除し__\_\_pycache\_\___も削除したが、__makemigrations__を実行すると__No changes detected__と出力される。もちろん、ここで__アプリ名を指定してmakemigrations__を実行すると正常にマイグレーションファイルは作成される。

```python:
python ./manage.py makemigrations appname
```

# 原因
そもそも__migrationsディレクトリ__と内包する__\_\_init\_\_.py__を削除してはいけなかった。

# 解決
__migrations__ディレクトリ内の__\_\_init\_\_.py__ファイル以外を削除するか、ディレクトリごと削除してから__migrationsディレクトリ__と__\_\_init\_\_.py__を作成する事で解決。

```bash:
rm appname/migrations/*.py
rm appname/migrations/__pycache__/*.pyc
touch appname/migrations/__init__.py
# 私のDBはMySQLです。
cat <<__END_DDL__ | mysql -uroot
DROP DATABASE appname;
CREATE DATABASE appname;
__END_DDL__
python ./manage.py makemigrations
python ./manage.py migrate

```
Locust (Python製の負荷試験ツール) のメモ

Python 製負荷試験ツールの Locust が良い感じだったので共有します。
ちなみに Locust っていうのはトビバッタの事らしくて、センスを感じますね。

![](https://locust.io/static/img/screenshot_0.12.1.png)
↑ image by https://locust.io/

# 負荷試験ツールに求めていたこと

簡単な試験をしたいので、ツールに求めていたのは以下の事柄です。

- 設定ファイルができればxmlでないもの
- 負荷を徐々に上げる設定ができるもの
- dokcerコンテナで動かせること
- ツールがメンテナンスされていること

# 手順
- locustfile.py(シナリオや設定)を準備する
- `docker run -p 8089:8089 -v $PWD:/mnt/locust locustio/locust -f /mnt/locust/locustfile.py` で実行する

# ref
- メンテされている
    - https://github.com/locustio/locust/graphs/commit-activity
- quickstart簡単
    - https://docs.locust.io/en/stable/quickstart.html
- dockerのリファレンス
    - https://docs.locust.io/en/stable/running-locust-docker.html
- 日本語記事
    - https://inside.pixiv.blog/east/5407
    - https://qiita.com/yamionp/items/17ffcc465272ad83c490
    - https://qiita.com/naoki114/items/7ae5a045bb08c4950b04
    - https://kazuhira-r.hatenablog.com/entry/2019/01/11/225059
Raspberry Pi 4 Model BでexfatのディスクをSMBでネットワーク共有する

# はじめに
現在リリースされているRaspberry Pi OSはexfatのカーネルモジュールが標準ではいっているようですが、何故か有効化されていません。
ここではOS標準のexfatの有効化とRaspberry Pi 4のUSB3.0と高速なネットワークアダプタを利用して、Windows、Linux、AndroidでexfatのディスクをSMBで共有する方法を試してみました。
またUSBの差し替えを便利にするためにautofsも使用します。（設定に応じてディスクを自動的にマウントしてくれます）

# exfatの有効化

最新版のOSイメージを使用する(2021/02以降のものを使用する)
Raspberry Pi OS Lite
https://www.raspberrypi.org/software/operating-systems/

インストール作成後bootドライブにsshファイルを作成する（導入直後にsshが使用できるようになります）

永続化手順

```
$echo exfat | sudo tee -a /etc/modules
$sudo reboot
```

# SMBの設定
ここではユーザをpiに設定していますが、適宜ユーザを変更するようにして下さい。

```
$sudo apt update
$sudo apt install samba
$sudo apt install autofs
$sudo mkdir /usb
```

```
$sudo nano /etc/auto.master.d/usb.autofs
```
usb.autofsの編集

```
/usb /etc/auto.usb
```

```
$sudo nano /etc/auto.usb
```
auto.usbの編集
ボリュームラベルがshare1のディスクをhdd1に
ボリュームラベルがshare2のディスクをhdd2に
マウントします。

```
hdd1 -fstype=exfat,rw,uid=pi,gid=pi :LABEL=share1
hdd2 -fstype=exfat,rw,uid=pi,gid=pi :LABEL=share2
```
autofs の有効化

```
$systemctl enable autofs --now
$sudo reboot
```
```
$sudo nano /etc/samba/smb.conf
```
smb.confの編集
ネットワーク共有名[pi01]

```
[pi01]
   comment = Welcome to pi Server
   path = /usb/hdd1
   browseable = yes
   wriable = yes
   guest ok = no
   force create mode = 0666
   force directory mode = 0777
   read only = no
   create mask =0777
   directory mask = 0777

[pi02]
   comment = Welcome to pi Server
   path = /usb/hdd2
   browseable = yes
   wriable = yes
   guest ok = no
   force create mode = 0666
   force directory mode = 0777
   read only = no
   create mask =0777
   directory mask = 0777
```
```
#共有のパスワードを設定します。
$sudo smbpasswd -a pi

$sudo systemctl restart smbd.service
```
これで設定は完了です。
ボリュームラベルがshare1のディスクを接続するとpi01
ボリュームラベルがshare2のディスクを接続するとpi02
に共有されます。
HDDであれば100MByte/sぐらいでファイル転送が可能なため、
簡易ファイルサーバとしては十分なスペックだと思います。
配列の中から偶数値を取得する

# 配列にある値の中から偶数の数をカウントして出力するメソッドを作る。

例
count_evens([2, 1, 2, 3, 4]) → 3
count_evens([2, 2, 0]) → 3
count_evens([1, 3, 5]) → 0


# 答えと解説

```
def count_evens(nums)
  count = 0
  nums.each do |num|
    if num.even?
      count += 1
    end     
  end
  puts count
end
```

変数countを用意　→ 理由　配列の中の偶数の数を出力するためにカウントした数を保持するための変数が必要だから
count_evens で　count=0 と記述するとループ処理となり、処理が繰り返されるたびにcount=０が実行されるのでcountの値が0に上書きされてしまう。
よってcountの定義はeach文のループ処理外で行う。

配列に入っている数字をeach文で取り出し、even?メソッドで一つずつ偶数かどうかを確認し、偶数だった場合はcountに1を足す

countに1を追加するための記述はcount = count + 1でも可能だが、自己代入演算子を使用するとシンプルに記述できるため

each文の処理が終了し、countに代入された最終的な数が偶数の数となる。
Springでのエラー（個人用）

# ①GradleでValidationを使用すると、「〇〇を型に解決できません」とのエラーが発生する
##解決方法
build.gradle の dependencies にて以下を設定する必要がある。
・javax.validation:validation-api (Bean Validation API)

```
dependencies{
  implementation 'javax.validation:validation-api:2.0.1.Final'
}
```

# ②@Controller is not an annotation
## 解決方法
クラス名を`Controller`以外にする。

# ③Repositoryのdeleteで自動生成メソッドが呼び出せないエラーの原因と対処方法
```
javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call
```
## 解決方法
Controllerクラスに@Transactionalをつける



# まとめ
これで、GradleでValidationを記載することができる。
色々と設定がする必要があって面倒だなと感じている今日この頃。
Swift　制御構文の遅延実行

Swift、遅延実行
--
ある一連の処理が終わった後に別の処理を実行させたいときにする

どういうときに使うか
---
必ず実行してほしい処理を中に入れる。
ファイルにアクセスした時やクロージングするときなど。



書き方
-----

```swift:遅延実行.swift
func test() {

    defer {
        print("deferだよ")
    }

    print("最初に実行されるよ")
}

test()

// 最初に実行されるよ
// deferだよ
```

複数のdefer文を書いた場合は下に書いてある文から実行される。
また、for　in文の中に書いてある場合は繰り返し処理が一回ずつが終わったタイミングで実行される。
AtCoderログ：0026 - ちょっとした方針変更

# 背景
[2021年07月01日からAtCoderに取り組んだログをQiitaに投稿しています](https://qiita.com/RoadLynton/items/82fa87b8b0cedb62d7e7)。もともとは毎日の鍛錬、例えば[AtCoderに登録したら次にやること](https://qiita.com/drken/items/fd4e5e3630d0f5859067)で紹介された問題を解いていました。

# 困ったこと
一方で ABC や ARC などのイベントに参加したため、その復習も記事にし始めたところ、本来の鍛錬がなかなか進まないという問題に直面しています。

# どうするか
## 案１：イベント参加をやめる、あるいは復習の記事をあきらめる
これは一案です。しかし、AtCoderのレーティングが安定するにはある程度の参加回数が必要らしいと言うこと、また、やはりイベントに参加した方がモチベーションが上がることから、この理由による不参加はないです。

復習を記事にすることをあきらめるのも一案ですが、せっかくイベントの問題に取り組んだのですから、そのときの脳の内容を記事にマッピングするのが効率的に感じています。

## 案2：記事を増やす
Qiitaをはじめた当初は、記事作成に必要な時間を見積もれなかったので、抑えめに「一日一Qiita」を目標にしていました (語呂、悪っ)。しかし実際には、記事のフォーマットが固まってきたこともあり、朝や夜のちょっとした時間で書けることがわかりました。
そこで、本来進めたかった鍛錬の記事と、イベントなどの問題の復習の記事をパラレルに進めようと思います。つまり、「一日二Qiita」にするということです。ちょっときつい気もしますが、遠出の予定もないので、夏休みを使って頑張ってみたいと思います。

# まとめ
「一日二Qiita」にするよという宣言でした。決意を固めるための記事なので、読まれた方にお伝えできる内容はなかったのですが、頑張っていこうと思います。

# リンク
- 前の記事 → [AtCoderログ：0025 - ABC 102 B](https://qiita.com/RoadLynton/items/69a457cd9fddd251737f)
GASでスプレッドシートに挿入した画像が印刷できない

# 発生した事象
GASで作成した画像を含むスプレッドシートをPDF化しようとしたときに、スプレッドシート上には画像がひょうじされているが、PDFに画像だけ印刷されなかった。

# 原因
根本的な原因は不明だったが、GASのinsertImageで、以下のようにoffsetXとoffsetYを指定したときに再現することが分かった。

```sample.gs
const ss = SpreadsheetApp.openById(ssId);
const sh = ss.getSheetByName(sheetName);
sh.insertImage(blob, 9, 3, -5, -5).setWidth(120).setHeight(160);
```

# 対応策
GASのinsertImageで、以下のようにoffsetXとoffsetYを指定せずに画像を挿入する。
画像の位置を変えたい場合、スプレッドシートのセルの配置を修正して対応した。

```sample.gs
const ss = SpreadsheetApp.openById(ssId);
const sh = ss.getSheetByName(sheetName);
sh.insertImage(blob, 9, 3).setWidth(120).setHeight(160);
```
riverpodを使ってインプットフォームに入力した文字を表示させてみた

#はじめに
今回はriverpodがとても便利という話をよく聞くのでflutter初心者ながらも少し触ってみました
パッケージは<a href="https://pub.dev/packages/hooks_riverpod">hooks_riverpod</a>を使用しました。

#やりたいこと
TextField()に入力した文字をriverpodを使って変更を監視してText()に表示していきます

![RocketSim Recording - iPhone 8 - 2021-07-21 08.30.22.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/970553/1fbc10e4-710e-3179-9429-9f51cb0ee394.gif)



#ソースコード
```main.dart
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:startup_namer/main_model.dart';
void main() {
  runApp(ProviderScope(child: MyApp())); // *1
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(primaryColor: Colors.white),
      home: _FirstView(),
    );
  }
}

class _FirstView extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final String sampleText = useProvider(sampleProvider).state; // *2
    String text = ''; //inputした文字を代入
    return Scaffold(
      appBar: AppBar(
        title: const Text('Google Maps App'),
      ),
      body: Center(
        child: Column(
          children: [
            Text(sampleText),
            TextField(
              enabled: true,
              style: TextStyle(color: Colors.red),
              obscureText: false,
              maxLines: 1,
              onChanged: (input) {
                text = input;
              },
            ),
            ElevatedButton(
              onPressed: () {
                context.read(sampleProvider).state = sampleStr(text); // *3
              },
              child: Text('文字を変更'),
            ),
          ],
        ),
      ),
    );
  }
}

```

１. まずはWidgetツリー上でProviderを利用するために根本のMyApp()をProviderScope()で包みます。
2. useProviderを使ってsampleProviderの値を取得します。
useProviderを使用するWidgetはHookWidget を継承している必要があります。
useProvider を使ってアクセスすることで、状態の変更通知がされた時にリビルドされるようになります。
今回はTextFieldで入力した文字を変数textに代入します。
3. onpressed()の中でsampleProviderの値を更新します。更新にはcontext.read()を使います。
引数として先ほど宣言した変数textを渡します。


```mainModel.dart
import 'package:hooks_riverpod/hooks_riverpod.dart';

final sampleProvider = StateProvider((ref) => sampleStr('')); // #4

String sampleStr(String text) { //*5
  return text;
}


```


4. 状態の入れ物としての Provider を、グローバルに final で定義します。
ここでは、StateProvider を使います。
今回はsampleStr()の返り値を値として保持します。String型で引数を渡してます。（最初は空文字）
StateProvider は、内部に state_notifier を使っているため、state を変更するだけで変更が通知されるようになっています。

5. sampleStr()にはTextField()で入力された文字が引数として渡されます。

#使ってみた感想
実装自体はとても簡単で使いやすいと思います。
まだまだ少し触っただけですが今後も少しずつ理解を深めていきたい。
AtCoderログ：0025 - ABC 102 B

# 問題：[ABC 102 B - Maximum Difference](https://atcoder.jp/contests/abc102/tasks/abc102_b)

## 問題文
> 長さ $N$ の整数列 $A$ が与えられます。$A$ の（添字の）異なる $2$ 要素の差の絶対値の最大値を求めてください。

## 制約
> ・$2 \le N \le 100$
> ・$1 \le A_i \le 10^9$
> ・入力はすべて整数である。

## 収録されている問題セット
- [AtCoder に登録したら次にやること](https://qiita.com/drken/items/fd4e5e3630d0f5859067) ([第03問類題](https://qiita.com/drken/items/fd4e5e3630d0f5859067#%E7%AC%AC-3-%E5%95%8F--abc-081-b---shift-only-200-%E7%82%B9))


# 回答1 (AC)
整数列 a() を受け取った後、添え字の異なる全ての $2$ 要素の差の絶対値 abs(a.at(i)-a.at(j)) を計算し、その中から最大値を求めれば良いでしょう。
コードは以下のようになりました。

```C++:abc102b-1.cpp
#include <bits/stdc++.h>
using namespace std;
 
int main() {
  int n;
  cin >> n;

  vector<int> a(n);
  for ( int i=0; i<n; i++ ) {
    cin >> a.at(i);
  }

  int dmax = -1;
  for ( int i=0; i<n; i++ ){
    for ( int j=0; j<n; j++ ) {
      if ( i!=j ) {
        dmax = max( dmax, abs(a.at(i)-a.at(j)) );
      }
   }
  }

  cout << dmax << endl;
}
```

# 回答2 (AC)
回答1では配列の添字 $i,j$ をともに $1$ から $N$ まで(コードでは 0 から n-1 まで)変化させましたが、$|A_i-A_j|=|A_j-A_i|$ という対称性があるので、片方だけ計算すれば十分です。従って、$i<j$ という条件を設定しても問題ありません。
このアイディアによるコードは以下となります。

```C++:abc102b-2.cpp
#include <bits/stdc++.h>
using namespace std;
 
int main() {
  int n;
  cin >> n;

  vector<int> a(n);
  for ( int i=0; i<n; i++ ) {
    cin >> a.at(i);
  }

  int dmax = -1;
  for ( int i=0; i<n; i++ ){
    for ( int j=i+1; j<n; j++ ) {
      dmax = max( dmax, abs(a.at(i)-a.at(j)) );
   }
  }

  cout << dmax << endl;
}
```

計算量(絶対値を計算する回数)を調べてみると、回答1は $(N-1)^2=N^2-2N+1$回、回答2では$(N-1)+(N-2)+\cdots+1=\frac{(N-1)N}{2}=\frac{1}{2}N^2-\frac{1}{2}N$ 回となっており、どちらも $O(N^2)$ となっています。この問題では制約として $N \le 100$ となっているので、計算量的には問題ありません。

# 回答3 (AC)
[AtCoder に登録したら次にやること 第03問](https://qiita.com/drken/items/fd4e5e3630d0f5859067#%E7%AC%AC-3-%E5%95%8F--abc-081-b---shift-only-200-%E7%82%B9) のコメントに「できれば for 文を 1 回だけ使って解いてみましょう」と描かれているので、それに挑戦します。回答1,2 は for 文を 3 回使用しているので、異なるアプローチが必要です。

そもそも「差の絶対値が最大」になるのは、数列の中で「絶対値が最大の値から絶対値が最小の値を引いたとき」ですが、この問題では数列の値はすべて正なので、これは「最大の値から最小の値を引いたとき」と言い換えることができます。つまりこの問題は、数列の中から最大値と最小値を求める問題なのです。このアイディアを使うと、以下のようなコードになりました。条件通り、for 文の使用は1回になっています。

```C++:abc102b-3.cpp
#include <bits/stdc++.h>
using namespace std;
 
int main() {
  int n, a, imin = INT_MAX, imax = -1;
  cin >> n;
  for (int i=0; i<n; i++) {
    cin >> a;
    imin = min( imin, a );
    imax = max( imax, a );
  }

  cout << imax-imin << endl;
}
```

# 調べたこと
## [AtCoder の解説](https://atcoder.jp/contests/abc102/tasks/abc102_b/editorial) → [コンテスト全体の解説](https://img.atcoder.jp/arc100/editorial.pdf)
回答3と同じ方針でした。今回のように最大値や最小値のとり得る値が決まっている場合には、初期値をとり得る値にするのが良いのですね。

# 学んだこと
- 最大値・最小値を求めるときの初期値の設定方法
 
# リンク
- 前の記事 → [AtCoderログ：0024 - ABC 210 C](https://qiita.com/RoadLynton/items/2afcfb40b491f276bd2d)
MVCについて簡単にまとめてみる

<a href="https://peaks.cc/books/iOS_architecture">iOSアプリ設計パターン入門</a>を読んだので、アウトプットも兼ねて記事にしたいと思います。
今回はiOSアプリ開発でよく耳にする（iOSアプリ開発以外でもよく耳にする）MVCについて書いていきます。
まだ、この辺は勉強したてなので誤りとかがありましたら、ご指摘いただけると幸いです。
# MVC
MVCはプログラムを「入力」「出力」「データの処理」と分けたときに、それぞれ<b>Controller</b>、<b>View</b>、<b>Model</b>と定義したものです。これらをレイヤーと呼ぶのですが、それぞれの役割は以下となります。

|レイヤー|役割|
|---|---|
|Controller|ユーザの入力を受け、Modelにコマンドを送る|
|Model|コマンドを受けて処理を行い、自信を更新|
|View|Modelの変更を監視し、自信を更新|

となります。これらを図にすると以下のように表すことができます。
![VCC構造.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1674071/41241c8d-c893-550f-2d20-88edfe1703f2.png)
図で分かる通り、ViewがModelを監視し、ControllerはModelへメッセージを送る（ここでは処理の依頼）をして、それを受けModelは処理を開始します。
これが、MVCの簡単な概略になります。
しかし、現在のアプリケーション開発はMVCが登場した時よりも複雑になり、元のMVCでは対応が難しくなったり、ViewはModelに依存しているため、Viewno再利用ができないといった不満が出てきました。
そこで、誕生したのがAppleの提唱したCocoaMVCというものです。

# CocoaMVC
CocoaMVCと元のMVCはどういった面で違いがあるのかを見ていきます。
まず、元のMVCは図で分かる通り、ViewがModelを監視している仕組みになっているため、これではViewは特定のModelに依存する作りになってしまいます。一方、CocoaMVCはViewとModelが完全に独立した形となっています。このためModelがどんな形になってもViewを変更せずに済むわけです。つまり、どんなModelでもViewを使うことができるわけですから、Viewの再利用ができるようになりました。

# 終わりに
さて、MVCとCocoaMVCの両方を簡単にまとめてみました。本当はコードを載せてMVCとCocoaMVCを比較したほうが良いのですが、それはまたの機会ということで今回はここまでにします。また、何回も書籍を読み直したり、調べたりして新しく知見を得られたら更新をしていきたいとい思います。
今回はここまで！！
UICollectionViewでcellを長押し

UICollectionViewのセルを長押しした時に任意のアクションを起動する方法を実装しました。

## view読み込み時にジェスチャを設定

```swift
override func viewDidLoad() {
        super.viewDidLoad()

        /// ロングタップ
        let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(onLongPressAction))
            longPressRecognizer.allowableMovement = 10
            longPressRecognizer.minimumPressDuration = 1
            self.collectionView.addGestureRecognizer(longPressRecognizer)
    }
```

## アクションの作成

```swift
@objc func onLongPressAction(sender: UILongPressGestureRecognizer) {
        let point: CGPoint = sender.location(in: self.collectionView)
        let indexPath = self.collectionView.indexPathForItem(at: point)
        if let indexPath = indexPath {
            //何らかの処理
            let vc = OrderMenuViewController.instantiate()
            let tableListDic = tables.sorted {
                (s1, s2) -> Bool in
                s1.name.localizedStandardCompare(s2.name) == .orderedAscending
            }
            vc.table = tableListDic[indexPath.row]
            navigationController?.pushViewController(vc, animated: true)
        }
    }
```


### 参考記事
[https://qiita.com/john-rocky/items/5ddff918a6f18607c65e](https://qiita.com/john-rocky/items/5ddff918a6f18607c65e)
[https://ofsilvers.hateblo.jp/entry/uicollectionview-and-long-press](https://ofsilvers.hateblo.jp/entry/uicollectionview-and-long-press)
plunkerでvue その51

# 概要

plunkerでvueやってみた。
box2dwebやってみた。

# 写真

![無題.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/95be27a5-f18b-e3e3-5043-98afca262616.jpeg)


# サンプルコード

```

new Vue({
  el: '#app',
　data: {
  },
	mounted () {
		this.init()
    this.createWorld()
    this.loop()
	},
	methods: {
		init () {
			this.canvas = document.getElementById('c')
      this.canvas.width = 456
      this.canvas.height = 456
      this.b2Vec2 = Box2D.Common.Math.b2Vec2
	    this.b2BodyDef = Box2D.Dynamics.b2BodyDef
	    this.b2Body = Box2D.Dynamics.b2Body
	    this.b2FixtureDef = Box2D.Dynamics.b2FixtureDef
	    this.b2Fixture = Box2D.Dynamics.b2Fixture
	    this.b2DistanceJointDef = Box2D.Dynamics.Joints.b2DistanceJointDef
	    this.b2RevoluteJointDef = Box2D.Dynamics.Joints.b2RevoluteJointDef
	    this.b2World = Box2D.Dynamics.b2World
	    this.b2MassData = Box2D.Collision.Shapes.b2MassData
	    this.b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
	    this.b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
	    this.b2DebugDraw = Box2D.Dynamics.b2DebugDraw
    },
    createWorld() {
      this.world = new this.b2World(new this.b2Vec2(0, 10), true);
      this.debugDraw = new this.b2DebugDraw();
      this.debugDraw.SetSprite(this.canvas.getContext("2d"));
      this.debugDraw.SetDrawScale(200);
      this.debugDraw.SetFillAlpha(0.9);
      this.debugDraw.SetLineThickness(1.0);
      this.debugDraw.SetFlags(this.b2DebugDraw.e_shapeBit | this.b2DebugDraw.e_jointBit);
      this.world.SetDebugDraw(this.debugDraw);
      this.bodyDef = new this.b2BodyDef;
      this.bodyDef.linearDamping = 0.1;
      this.fixDef = new this.b2FixtureDef;
      this.fixDef.restitution = 1;
      this.createWall(0, 0, 2, 0.1);
      this.createWall(0, 0, 0.1, 2);
      this.createWall(2, 0, 0.1, 2);
      this.createWall(0, 2, 2.1, 0.1);
      this.fixDef.shape = new this.b2CircleShape(0.1);  
      this.fixDef.density = 0.4;
      this.fixDef.friction = 0.9;
      this.fixDef.restitution = 0.9;
      this.bodyDef.type = this.b2Body.b2_dynamicBody;
      this.bodyDef.position = new this.b2Vec2(1.5, 1.0);
      this.bodyDef.linearVelocity = new this.b2Vec2(-5.0, -5.0);
      this.world.CreateBody(this.bodyDef).CreateFixture(this.fixDef);
    },
    createWall(x, y, w, h) {
    	this.bodyDef.type = this.b2Body.b2_staticBody;
    	this.bodyDef.position.x = x + w / 2;
	    this.bodyDef.position.y = y + h / 2;
    	this.fixDef.shape = new Box2D.Collision.Shapes.b2PolygonShape;
    	this.fixDef.shape.SetAsBox(w / 2, h / 2);  
    	this.world.CreateBody(this.bodyDef).CreateFixture(this.fixDef);
    },
    loop() {
    	this.world.Step(1 / 60, 10, 10);
    	this.world.DrawDebugData();
    	this.world.ClearForces();
    	setTimeout(this.loop, 15);
    },
  }
});




```

# 成果物

https://embed.plnkr.co/plunk/WEbsyQ6Ja39vnerg

以上。
文字の切り取り方

Javaで標準入力で入力した文字をsubstringとcharAtでとる方法です。

substringで1文字ずつとる場合

```Java
import java.util.Scanner;

public class flora2 {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		System.out.println("floraの文字を入力してください");
		String ans = sc.nextLine();
		int nagasa = ans.length();
		for(int i=0;i<nagasa;i++) {
			String c = ans.substring(i,i+1);
			System.out.println(c);
		}
	}
}
```

charAtで1文字ずつとる場合

```Java



public class flora3 {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		System.out.println("floraと入力してください");
		String ans = sc.nextLine();
		int nagasa = ans.length();
		for(int i=0;i<nagasa;i++) {
			char c = ans.charAt(i);
			System.out.println(c);
		}
	}
}
```

今回は1文字ずつ文字をとる部分にフォーカスを当てているので命名規則は無視しています。
Railsで架空のCafeのHPを作ってみよう!【9日目】『簡単にGoogleMapを導入』編

#概要
基本Railsの記法に則り書いていきます！
１から全ての説明ではなく
その中であれ？どうやるの？と
疑問に思った点や実装に困った箇所を
ピックアップして紹介していきます♩

#設定と準備
・Rails
・HTML
・CSS
・Javascript(jQuery)

↑上記の言語とフレームワークを使い
架空(自分で考えたテキトーなもの)のCafeの
HPを作っていこうと思います！

#9日目の作業内容:round_pushpin:
・ビューの作成

#9日目の気になった箇所:zap:
よく見るGoogleMapを導入したいけど
どうしたらいいだろうか。

#仮説:pushpin:
調べた結果APIKeyを導入したり
色々と設定しないといけないので時間がかかってしまうし
めんどくさい。

#結論:star:
トップページでGoogleMapを動かすことは不可能だが
GoogleMapに遷移してもらう方法を取ると
簡単に実装が可能！

まずGoogleMapに行き、自分で好きなポイントを選択！
![23ec4248b89f917bdade1b2dac907001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1678155/5d63cc6a-73ac-2138-c0fe-84dd98f24eb9.jpeg)
今回は左上の検索フォームからアメリカと検索しました！

その次は自分の目的地をクリック！
![3e7d8bc83c3e52f3f52556afd2d41103.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1678155/f79bb4fe-9bbb-35f9-7dec-d5fca742eb7c.jpeg)
今回目的地がないので
適当な箇所をクリックしました！

そしたら画面下にクリックした場所の詳細が出てきます！
![aa9c4da90b71a6287dbdfd57f7af154f.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1678155/70209cfc-50a8-681e-3922-0c052ab690a6.png)

この数字の部分をクリックしてください！

すると...

![ea5db0af3ad17c39de0c564d6f476fe8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1678155/e5d58262-f4a4-136b-55cd-651169433927.png)

目的地(今回は適当にクリックした箇所)の詳細が表示されると思います！

＊一度間違えて画像を消してしまったので違う箇所の詳細が出ていますが、
やり方は紹介した通りで問題なく遷移できるかと思いますのでご容赦ください。

そしたら画面左の共有をクリック！
![e309970980ca749c153c480702a0f400.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1678155/4c735e7a-14a3-df57-c2b6-4f42c0c376e2.jpeg)
すると共有リンクが出てくるので
リンクをコピーをクリックしておきます！

そのリンクを実際にコードでリンク先に指定します！

```index.html.erb
<%= link_to "GoogleMapへ",　"https://goo.gl/maps/1p9XESBtvHu6wZvF7" >

```

これでGoogleMapの自分の指定した箇所へ遷移させるリンクを作ることが可能に！
APIKeyなどを使っていないので凝ったやり方ではないですが
誰でも簡単にできるかと思いますので
ぜひ試してみてください♩
初学者による復習[if文を使って条件分岐しよう!]

今回はif文を使って条件分岐をしてみよう！

**if文とはなんぞや**
~だったらこのようにしてほしいといった記述のこと。
条件分岐することで
~でなかったらこのようにしてほしいといったことも可能。

https://gyazo.com/f7ff4068f83f2dd245ad0161d4132336

まず今私が実装しているオリジナルアプリのトップページにログインとあると思うが
これをログインした際には以下のように表示される項目を変えたい。

https://gyazo.com/e3e2f1ce7b0e8e12b386993fc6b49838

ログアウト、新規投稿が表示されログイン、新規登録が消えていることがご確認いただけるだろうか。

そのためにした記述が以下の記述である。

```ruby
<% if user_signed_in? %>
 <li><%= link_to "ログアウト",destroy_user_session_path, method: :delete %></li>
 <li><%= link_to "新規投稿",new_item_path %></li
<% else %>
 <li><%= link_to "ログイン",new_user_session_path %></li>
 <li><%= link_to "新規登録",new_user_registration_path %></li>
<% end %>
```

まず

```ruby
<% if user_signed_in? %>
```

この記述はもしユーザーがログインしていたら<% else %>までの内容を表示させるよという記述。

次に

```ruby
<% else %>
 <li><%= link_to "ログイン",new_user_session_path %></li>
 <li><%= link_to "新規登録",new_user_registration_path %></li>
```

このように記述することでログインしていない場合はログイン、新規登録を表示させてねという
条件分岐になっている。
OBSで透過(アルファ)付き動画を出力する

## 概要
カラーフォーマットを **RGB** に変えて**カスタム出力**から**適切な(=アルファチャンネルを扱える)コンテナとコーデックを選ぶ**と透過付き動画が出力できます。

## カラーフォーマットを変える
設定 > 詳細設定 > カラーフォーマット より、 **RGB** を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494881/46a82442-4bf0-3b9b-9b56-6416fbf6ce58.png)

## 適切なコンテナとコーデックを選択する
アルファチャンネル対応の**動画コンテナフォーマット**としてはMatroska、QuickTime、WebM、AVIなどがありますが、私がざっくり色々試したところVP9のエンコードがうまく行かなかったので、**Matroska**、**QuickTime**、**AVI**などを使うと良いと思います。
**映像エンコーダ**としてはpng、ayuv、dpx、v408などの書き出しが可能であることを確認しましたが、この中では動画サイズの観点で**png**が最も軽量(他と比較して約10分の1程度)です。

以上を踏まえた上での設定例です。画像のような詳細なエンコード設定をするには出力モードで「**詳細**」、種別で「**カスタム出力(FFmpeg)**」を選択してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494881/3165536f-dc69-cad4-b298-1a8d5ad53ec2.png)

### 組み合わせ例
| コンテナフォーマット | 映像エンコーダ | 備考 |
| --- | --- | --- |
| matroska | png | 比較的軽量です。品質が気になる場合は他の組合わせを試すと良いかもしれません。 |
| matroska | ayuv | Windows標準のプレイヤーで再生可能です。 |
| avi | ayuv | 同上 |
| mov | v408 |  |
LaTeX for Mac 図の挿入

## 図の挿入
```
%%%%%%%%%% 図:ラベル名 %%%%%%%%%%
\begin{figure}[H] %% 図の表示位置オプション
 \centering
 \includegraphics[keepaspectratio,scale=1.0)]{xxx.pdf}
 \caption{図の説明}
 \label{fig:ラベル名xxx}
\end{figure}
```

- 図の表示位置オプション
    - h:here 
    - t:top (ページ上部)
    - b:bottom (ページ下部)
    - t:top (ページ上部)
    - p:page


- 図のオプショn
    - keepaspectratio　　縦横の比を維持
    - scale　　　　　　　　図のサイズの変更(scale=0.1)
    - width　　　　　　　　横幅の指定(例:width=60mm)
    - height　　　　　　 　高さの指定(例:height=3cm)
    - angle　　　　　　　　回転角の指定(例:angle=90 -度-)　*度
    - clip　　　　　　　 　BoundingBoxからはみ出た分を切り取る
Before starting UiPath:条件(Condition)その１

## 皆もすなるQiitaといふものを、儂もしてみむとてするなり。

と言う訳で、こん＊＊は！はなっち！です。

★★★この記事はUiPathブログ発信チャレンジ2021サマー、7/21の記事です。★★★

今回は、ちょっとしたシリーズです。UiPathを使うに当たって、UiPathの機能ではなく、コンピュータ言語を使う上での基礎知識を展開していこうと思います。

#条件(Condition)その１

式の評価結果がBoolean型となるような条件に、.CompareTo()を使う例をご紹介しました。

　　　　数値.CompareTo(1).Equals(1)　　　　数値が1より大きいか？
　　　　数値.CompareTo(2).Equals(0)　　　　数値が2か？

慣れるまで、理解しやすい構文とは言えませんね。
こんな感じで覚えていく（呪文のように唱える）と、理解はしやすくなると思います。

　　　　”数値”は、"2"と比べて…　　　　1:大きい　　　　0:等しい　　　　-1:小さい

　　　　　　　　　　　　　　　２　　　　　　　　　　　　基準とする値
　　　　　　　　　　　　　　　↓
　　　　＝＝＝＝＝-1＝＝＝＝＞０＜＝＝１＝＝＝＝＝＝＝　　CompareToの結果値
　　　　　　　　　↑　　　　　　↑　　　　↑
　　　　　　　　　数値　　　　数値　　数値　　　　　　　比較する値
　　　　　　　　　　１　　　　　０　　　２　　　　　　　

可読性を考えると、

　　　　数値 > 1
　　　　数値 = 2

の方が分かりやすいですね。

人が作ったロボットなどを見て、知らないテクニックに遭遇するかもしれません。
その時に、ドギマギしないように、色々なやり方があることを覚えておきましょう。

##おわりに
いかがでした？

条件は、はい/いいえの世界です。CompareToは数値の比較は簡単にイメージできると思います。文字の比較でも使えるのですがそもそも文字の大小ってなんだ？って事になりますね。
まずは、数値の比較をしっかりイメージできればいいなと思います。

是非UiPathでのロボ開発の一助になればと思っています。

ありがとうございました！
叙勲、国宝級、まだある、古のフリーソフトたち

https://forest.watch.impress.co.jp/docs/serial/yajiuma/1319407.html

という記事を以前見かけたが、幸か不幸か弊社内のイチ界隈でかつて重宝されていたフリーソフト達の情報を文化財のごとく、ひっそりまとめておきたくて書いているもの。闇鍋というと言葉が悪すぎるが、ジャンルはごった煮。


## フリーソフトたち、from A to Z

### バイナリエディタ Bz
バイナリファイルのチェックが出来る
https://www.vcraft.jp/soft/bz.html

### Common SQL Environment
SQLエディター
http://www.hi-ho.ne.jp/tsumiki/

### FastCopy ver 3.92
2015大賞受賞
Windows系最速(?)のファイルコピー＆バックアップツール
https://fastcopy.jp/

### eve(Environment Variable Editor)

環境変数をGUIで編集できる。ワンタッチ一括切換えも
https://www.vector.co.jp/soft/dl/win95/util/se271898.html

### Executequery

バイナリデータが開けるDBツール。jdbcの設定が不要。
http://executequery.org/download.jsp

### マウ筋
マウスジェスチャーにより任意の命令を実行する。
https://www.vector.co.jp/soft/win95/util/se264166.html

### みっちーのデータベースメンテ

Access のデータを操作できる。
http://www.vector.co.jp/soft/win95/business/se258994.html 


### OSqlEdit
SQLエディター。PL/SQLのコンパイルに便利
http://www.hi-ho.ne.jp/a_ogawa/index.htm


### Processwalker
コンピュータ上で実行されているプロセスに関連する情報を表示・調査するGUIツール
https://freesoft-100.com/review/process-walker.php


### QTTabBar
タブやエクストラビュー、その他の機能を追加するエクスプローラー拡張。
http://qttabbar-ja.wikidot.com/

### Unlocker
指定フォルダ以下のロックファイルリストからロックを解除できる
https://forest.watch.impress.co.jp/article/2005/05/10/unlocker.html

### UWSC
https://qiita.com/takam_go/items/0aca1cddd005031f9593
https://oxynotes.com/?p=11298

### YPT HandleX
ローカル上でプロセスの立ち上がっているアプリケーションが使用しているハンドル・クラスを表示する。
https://www.vector.co.jp/soft/win95/prog/se067974.html

## 関連

https://qiita.com/e99h2121/items/7c00cbc6e98231b5df85


## まとめ

https://twitter.com/HazeinHeart/status/1382543166528626691?s=20

https://b.hatena.ne.jp/entry/s/forest.watch.impress.co.jp/docs/serial/yajiuma/1319407.html

これほど称えられる面々。ただ同時にそれらにいまだ頼る現場を憂える声にも私は共感しつつ、今日も保守作業をするのであります。

https://qiita.com/official-events/e55396f286b3e2b85a62

このテーマに乗るには少々闇深さを垣間見せている気がしなくもありませんが、ただの過去清算であり私の責任ではないという言い訳。以上面白がっていただければ、さいわいです。古文書を見つけたらまた増えるかも...。

### 追記

http://blog.livedoor.jp/bluejay01-review/archives/58366615.html
2021/07/19開催 Connpassイベントランキング

# 申込数TOP5

|順位|イベント名|グループ|申込数|定員|
|:---:|:---|:---|:---:|:---:|
|:one:|[J-Quants データ分析コンペティション表彰式](https://connpass.com/event/218668/)||1185||
|:two:|[日本ディープラーニング協会主催 CVPR 2021 技術報告会](https://jdla.connpass.com/event/214724/)|[日本ディープラーニング協会](https://jdla.connpass.com/)|1008||
|:three:|[AWSの基礎を学ぼう 第五十回](https://awsbasics.connpass.com/event/216373/)|[AWS エバンジェリストシリーズ　AWSの基礎を学ぼう](https://awsbasics.connpass.com/)|420||
|:four:|[mercari.go #16 オンライン開催](https://mercari.connpass.com/event/218434/)|[メルカリ／Mercari](https://mercari.connpass.com/)|242||
|:five:|[機械学習エンジニア/データサイエンティストの目指し方【 Live!人工知能 #58】](https://liveai.connpass.com/event/219022/)|[Live!人工知能](https://liveai.connpass.com/)|129||
文章のジャンルを推定する方法

## はじめに
この記事ではある文章からその文章が持つ**ジャンル**について推定する方法について複数の方法を記載します．

## 何故必要か
自然言語処理等や自動文章生成にて様々な文章を収集して利用するケースがあるかと思います．
しかし，その文章が必ずしもジャンル分けやタグ付けされているわけではなく，あるジャンルに特化した自然言語処理や自動文章生成を行う際にはまず前処理にてジャンルを推定する必要があります．

## 前提条件
- Python 3.7.9
    - FuzzyWuzzy
    - python-Levenshtein
    - mecab-python3
- Mecab
    - mecab-ipadic-NEologd

## ジャンルを推定する方法
### 正規表現から推定する方法
単純に**正規表現**等を利用し，ジャンル名がその文章に入っていれば同ジャンルと推定する方法です．
#### ソースコード
```python
import re

# ジャンル名
Genre_name = 'Qiita'

# 推定したい文章
Sentence = 'Qiitaは、エンジニアに関する知識を記録・共有するためのサービスです。'

regex_sentence = '.*{}.*'.format(Genre_name)
if re.search(regex_sentence, Sentence):
    print('{} は {}に含まれています'.format(Sentence, Genre_name))

```
### 形態素解析を利用して推定する方法
ジャンル名は基本的に名詞に分類されます．それも固有名詞に分類されるケースが多いです．
そのため文章を**形態素解析**し，固有名詞のみを取り出しジャンル名がその中にあれば同ジャンルと推定する方法です．
今回は**Mecab**と辞書に**mecab-ipadic-NEologd**を利用して行います．
#### ソースコード
```python
import MeCab


def mecab_list(text):
    # /path/is/select/は辞書の位置を指定してください
    tagger = MeCab.Tagger(r'-d /path/is/select/')
    tagger.parse('')
    node = tagger.parseToNode(text)
    word_class = []
    while node:
        word = node.surface
        wclass = node.feature.split(',')
        if wclass[0] != u'BOS/EOS' and wclass[1] == '固有名詞':
            word_class.append(word)
        node = node.next
    return word_class

# ジャンル名
Genre_name = 'Qiita'

# 推定したい文章
Sentence = 'Qiitaは、エンジニアに関する知識を記録・共有するためのサービスです。'

Sentence_list = mecab_list(Sentence)

if Genre_name in Sentence_list:
    print('{} は {}に含まれています'.format(Sentence, Genre_name))

```

### 類似度検索を利用して推定する方法
**レーベンシュタイン距離**を利用し，その文章とジャンルの**類似度**を推定する方法があります．
その類似度がα以上であれば同ジャンルと推定する方法です．
今回は**FuzzyWuzzy**というライブラリを利用して行います．
#### ソースコード
```python
from fuzzywuzzy import fuzz

# ジャンル名
Genre_name = 'Qiita'

# 推定したい文章
Sentence = 'Qiitaは、エンジニアに関する知識を記録・共有するためのサービスです。'

# 類似度の基準
Similarity_match = 90

ratio = fuzz.partial_ratio(Sentence, Genre_name)
if Similarity_match <= ratio:
    print('{} は {}に含まれています'.format(Sentence, Genre_name))

```

## 各方法の特徴
### 正規表現から推定する方法
比較するジャンル数が少ない場合かつジャンル名の文字数が少ない場合にはよさそうです．
しかし下記の記載にも書きますが，ジャンル推定の**誤認識**を招く恐れのある方法です．
またジャンル名の文字数が多い場合にはそのブランド名が短縮表現されていた際に検知することが出来ません．

### 形態素解析を利用して推定する方法
固有名詞を取り出すことでジャンルとしての表現かそれともそれ以外の表現かを分けることが出来るために**精度**が上がります．
> 「明治時代、学生たちの間で「アイス」と呼ばれた職業とは何？」という文章からジャンルを推定する場合，明治時代という表現がジャンル名「[明治](https://www.meiji.co.jp/ "明治")」と誤認識してしまう恐れがあります．そのため形態素解析を行うことで誤認識を減らすことが出来ます．

但し，形態素解析を行う辞書に**認識率**が依存する欠点もあります．そのためジャンル名が固有名詞として辞書に登録されていない場合精度が下がってしまいます．
（[mecab-ipadic-NEologd](https://github.com/neologd/mecab-ipadic-neologd)を利用することで緩和可能）
### 類似度検索を利用して推定する方法
正規表現と同じく比較するジャンルが少ない場合にはかなりよさそうです．また正規表現と違いジャンル名の文字数が多い場合にも対応できます．更に**類似度**の値を調整することで精度と検出率を調整することが可能です．

## 最後に
今回3つの方法を考えましたが，他にも様々な推定方法（**Doc2Vec**を利用する方法等）がありますので，
また近いうちに他の方法についても書いていきたいと思います！
choreonoidでVelodyneの点群データを可視化する

choreonoidでは2D・3D LiDARのシミュレーションが可能。またその時の点群をchoreonoid上で可視化することも可能。
ただちょっと可視化するのに自分が手順知らなくてちょっとハマったので一応メモしておく。

## 1. ロボットモデルにセンサアイテムを追加
とりあえずまずはセンサを追加する。対象のBodyファイルを開き、追加したい部分に以下の記述をする。

```
-
  type: RangeSensor
  name: Velodyne
  translation: [ 0, 1.2, 0 ]
  rotaion: [ [0, 1, 0, 90], [0, 0, 1, 180] ]
  yawRange: 360.0
  yawStep:  0.4
  pitchRange: 30.0
  pitchStep: 2.0
  scanRate:  5
  maxDistance: 100.0
  minDistance: 0.5
  on: true
-
```
translationとかrotationは位置姿勢なのでつけたいケースに合わせて変える。その他のパラメータはLiDARのシミュレーションに関するパラメータである。上記の例はVLP-16。32とかそれ以上のものを使いたい場合は仕様に合わせて調整する。(例えばVLP-32cとかは垂直方向の画角が40°とかなのでpitchRangeは40にする)

## 2. choreonoid側に可視化用のアイテムを追加する
choreonoidでプロジェクトファイルを開き、諸々追加する。センサ関連のシミュレータに必要なのはGLVisionSimulatorである。ファイル→新規→GLビジョンシミュレータを選択し追加。そうすると左側のアイテムビューに追加される。
![Screenshot from 2021-07-21 02-52-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60952/ba9824eb-f599-a981-e567-62ed7a8dc783.png)

↑で選択された状態で下の方のプロパティを見るとその中にビジョンデータの記録という項目があるのでそれを選択しfalseになってる場合はtrueに変更する。

その後アイテムビューでロボットボディアイテムを選択し、ファイル→新規→センサ可視化を選択し、SensorVisualizerItemを追加する。
![Screenshot from 2021-07-21 02-55-54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60952/a44e9f2a-e1c4-61c8-5cc2-07f403f95612.png)

アイテムを追加後、SensorVisualizerのツリーを開きその中から可視化したいセンサーのチェックボックスを入れる。今回はVelodyneを可視化したいため選択する。これで準備はOK。

## 3. シミュレーション開始
上タブのシミュレーション開始のボタンを押すとシミュレーションが開始され、点群がシーンビューにて可視化される。
![Screenshot from 2021-07-21 03-00-37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60952/09587b4b-31fd-b0f3-1b97-f3c68e42c450.png)

## 補足
GLVisionSimulatorアイテムでビジョンデータの記録をtrueにしてると毎フレームごとのセンサデータ情報を記録するようで、カメラやその他センサをもりもり積んでいるとメモリ消費が多くなり、処理が重くなってしまうかもしれないので注意(私の環境では少し放置していたらchoreonoidが落ちた)。普段のシミュレーションではfalseにしておく。
(GLVisionSimulatorに関する項目はこちらからhttps://choreonoid.org/ja/manuals/latest/simulation/vision-simulation.html)

あと今だとROSのプラグインを用いてROS側でもデータ見れるのでそちらでも良いと思う。下にchoreonoidとrosプラグインの環境を構築するreposファイルおいたのでぜひ。
https://github.com/RyuYamamoto/choreonoid_ros_setup
[2021Jul] Win32 SDK for Linux の消費ディスクスペース

# はじめに

どうしても手元で Win32 実行ファイルをビルドする必要があり、未体験だった Mingw-w64 on Linux を試してみることにしました。

が、どのくらいディスクスペースを必要とするのか不明です。不安です。なぜ不安なのかは[コチラ](https://qiita.com/teruo-oshida/items/567125c35c212c7dd9db)に書きましたので省略します。

ということで調べてみました。


# 結論

6.0.0-3 で 1GiB 弱でした。そんなに小さくなかった。


# 実験

幾つかのパターンがありそうなので、実験してみました。いずれも Docker で debian:10.9-slim に apt install したものです。

↓こんな感じ。

```Dockerfile
FROM debian:10.9-slim
RUN apt update && apt -y install mingw-w64
```

結果は下記です。

|指定パッケージ|サイズ|
|---|--:|
|mingw-w64 mingw-w64-tools|974|
|mingw-w64|884|
|gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64|539|
|gcc-mingw-w64-x86-64|407|
|gcc-mingw-w64-i686 g++-mingw-w64-i686|496|
|gcc-mingw-w64-i686|378|

gcc-mingw-w64-i686 だけでもビルドできます。binutils なども依存パッケージとしてインストールされますので。
Win32 で C だけで足りれば、かなり小さく済みますね。

コーディング中は 32bit/64bit の両方が必要ということも無いでしょうから、別々に docker image を作っておいたほうが便利でしょうか。どうでしょうか。


# 余談

MinGW って MinGW ですよね。Mingw-w64 は MinGW-W64 じゃないんですね。

いや、別に、大丈夫です。問題ありません。
間違えないように注意します。商標は正確に。大事。


# おわりに

ということで、手軽に Win32 できそうです。

当初は .NET 5 の倍かよ！と叫んでしまいましたが、ダウンサイズする余地も残されていて。

めでたしめでたし。


# ホントは

[GetCurrentProcessToken()](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocesstoken) を使いたかったのですけど、6.0.0-3 では使えませんでした。ざっくり v6 は Windows 8 未満ということですかね。

Mingw-w64 は [2021-05-22 に v9 がリリース](https://sourceforge.net/p/mingw-w64/mailman/message/37287751/)されてますから、Debian Buster のは結構古いってことですね。v6 のリリースは 2018-09-17 とのことです。

Ubuntu はというと、[Focal Fossa のは v7](https://packages.ubuntu.com/focal/mingw-w64) ですね。

よし、ネットワーク帯域が余っている日に v7 試してみよう。
//not foundから学んだSSR,SPA(CSR),SSG

![B3FF984C-60DE-4501-B15B-B9B1B2675E61_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/763535/572be722-6504-bed2-7c55-230caabcb4be.jpeg)
### きっかけ
ある日、ローカルで作業をしていて、キリのいいところまで終わったのでnetlifyにデプロイをして、きちんとビルドが通って、ワクワクしながらプレビュー画面を見てみるとこれでした。😱

### 結論
netlify cmsの記事を取得するにはSPA(CSR)ではだめでして、SSGにすると解決しました。

#### 簡単に知識を学ぶ
###### SPA(CSR)
クライアントサイドだけで動くやつに使える(静的ホスティング(netlify,vercelなど)ができる)
###### SSR
クライアントサイドに加えて、サーバーサイドも使うようなものに使える（node.jsなど使えるような環境が必要）
###### SSG
事前に```nuxt generate```でできた静的ファイル(dist)を静的ホスティングにあげる（事前に作成しているために、めっちゃ高速である）

### 具体的に
```nuxt.config.js
export default {
     target:'sever'//サーバーサイドの環境があるとき
}

```
```nuxt.config.js
export default {
    target:'static'//静的ホスティングを使うとき
}

```
```nuxt.config.js
export default {
     ssr:true//SSRを使うとき
}

```
```nuxt.config.js
export default {
     ssr:false//SSRを使わないとき
}

```
SSGは？？となりませんか？
実はこの四つの組み合わせなんですよ！！

```nuxt.config.js
export default {
     ssr:true,
     target:'static'//SSGの組み合わせ
}

```
```nuxt.config.js
export default {
     ssr:false,
     target:'static'//SPA(CSR)の組み合わせ
}

```
```nuxt.config.js
export default {
     ssr:true,
     target:'sever'//SSRの組み合わせ
}

```

###結論
ssr:trueでもgenerateできる。だから、静的ホスティング使える。
この３個の違いって微妙だよね？ってなるけど、知らないと結構はまるよ
by経験者は語る
Sageで離散フーリエ変換を代数的に定義してみた

# これは何？
ちょっとやってみたら離散フーリエ変換（以下DFT）を代数的に定義できたので嬉しくなって書いただけの記事です。Sageを触って１時間くらいで書いた記事なのでSage自体には詳しく無いです。

# Sageの処理系
[Sage Cell Server](https://sagecell.sagemath.org/)を使いました。

[T=16のDFT変換行列の定義](https://sagecell.sagemath.org/?z=eJzzK8kIys8v8U_zz0vVyNNUsFXQ0DXUjNMw0s_T5OUKAfINzXi5yoG0H7LKEKBkSlpJfG5iSVFmBVDWF8zQiI4uj9NI0yrRVEjLL1IoUcjMUyhKzEtP1TDQUQjRjAWLpqGLopgFABdOK5Q=&lang=sage&interacts=eJyLjgUAARUAuQ==)です。

# ソースコード
```python3
NthRootOfOne(n) = (-1)^(2/n)
T = 16
w = NthRootOfOne(T)
dft_matrix = Matrix([[w^(f*t) for t in range(0, T)] for f in range(0, T)])
dft_matrix # 表示用の値の評価
```

## NthRootOfOne
$1$の$N$乗根を求める関数です。$1$の$N$乗根とは、ここでは複素数平面上の単位円を$N$分割し、$(1,0i)$を起点として左に進んでいった時の1ピース目の複素数（代数的数）の事です。これを何度も掛けると単位円上を回転して進んでいき、$N$乗した時点で元の場所$(1,0i)$に戻ってきます。

## dft_matrixの計算
$w$は$0$を除いた最低の角速度です。ここでは$T=16$と定義しているので、$T$乗すると$(1,0i)$に戻ってきます。例えば$w^2$を$0 \cdots T-1$乗すると単位円をほぼ2回転します。$w^3$の場合はほぼ3回転、$w^4$の場合はほぼ4回転となり、$w^f$の$f$を周波数、$w^f$をその周波数の角速度とみなす事ができます。

DFT行列は単に、周波数毎に角速度$w^f$の$0 \cdots T-1$乗までの点列で$T$次元のベクトルを作り、それを並べて行列にしただけです。

## T=4の結果を見てみる
T=16を記事に載せるのはスペース的に無理があったので、わかりやすくT=4の結果を載せます。

```console
[ 1  1  1  1]
[ 1  I -1 -I]
[ 1 -1  1 -1]
[ 1 -I -1  I]
```
ちゃんと上から直流基底、周波数$1$の基底、周波数$2$の基底、周波数$3$の基底になっています。周波数$3$の基底は一度に$-i$進むので、ちゃんと角速度$i$で逆回転（右回転）している波に見えますね。

# 結論
多くのDFTの説明はフーリエ変換を周期化、離散化する過程でオイラーの公式なり$\cos$関数や$\sin$関数を導入しますし、δ関数の畳み込みなんかが登場して数学的議論がめちゃくちゃ難しいですが、代数的定義であれば非常にシンプルです。

DFTを代数的に定義すると、$\cos$関数や$\sin$関数は出てきませんし、$\cos$関数や$\sin$関数の超越性がオーバースペックだという事がわかります。

というか、DFTは代数的に定義可能なはずなので、とりあえず理論計算が不要で計算機上で扱えさえすれば良いという人は代数的定義から入るとわかりやすいんじゃないでしょうか？

とりあえずDFTを代数的に定義できたはずですが、代数計算のプロでは無いのでいろいろ教えていただけるとありがたいです。
ネットワークアドレスとブロードキャストアドレスを２進数変換なしで求める

#はじめに
10進数から2進数へ変換するのが苦手な人向けの、ネットワークアドレスとブロードキャストアドレスの求め方を紹介する記事。

#手順
① ネットワーク部とホスト部の境目があるオクテットを見つける
② ①で見つけたオクテットにホスト部となる部分が何ビットかあるを割り出す
③ ②で割り出したビット数を指数として、２の累乗計算を行う
④ ①で見つけたオクテットの数字を③の計算結果で割り、商を求める
⑤ ③の計算結果と④で求めた商を掛け合わせ、この値を①のオクテットの値とし、これより右のオクテットをすべて０にすることで、ネットワークアドレスとなる
⑥ ⑤で求めた数値と③の計算結果を足して−１した結果を①のオクテットの値とし、これより右のオクテットをすべて255にすることで、ブロードキャストアドレスとなる

以下、「***172.16.201.33/19***」のネットワークアドレスとブロードキャストアドレスを求めることとする。

##① ネットワーク部とホスト部の境目があるオクテットを見つける
プレフィックス長が 19 なので、ネットワーク部とホスト部の境界は第3オクテットとなる。
以降、このオクテットに注目していく。
![239EB0C8-36DB-428B-B2E6-2E8147D14F35_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1731510/43a0cf10-4755-1524-5274-5ccd27442d80.jpeg)
（※ホスト部「２３」は「１３」の誤り）

##② ①で見つけたオクテットにホスト部となる部分が何ビットかあるを割り出す
第3オクテットは、以下のようにネットワーク部３ビット、ホスト部５ビットとなっている。
ここでは、ホスト部が５ビットであることを記録しておく。
![64E0BBDE-0C34-4AA2-8C13-1AEDB4F741FD.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1731510/6a6d6cb8-c4c9-aada-e500-77f761117b80.jpeg)

##③ ②で割り出したビット数を指数として、２の累乗計算を行う
先程割り出したホスト部のビット数である「５」という数値を指数として、２の累乗の計算をする。
すなわち、２の５乗＝３２が求められる。
![2F85732E-963A-4A5C-935F-DB5471F013B3_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1731510/e5927690-8695-8dba-047d-e48dfa184d02.jpeg)

##④ ①で見つけたオクテットの数字を③の計算結果で割り、商を求める
①で見つけたオクテット、第3オクテットの数字は「２０１」であった。
この数値を先程の「３２」で割り、商を求める。この時、割り切る必要はない。
商は６と出るので、これを記録しておく。
![4A0C2B25-5062-4FA1-BEA9-AE1B312A822D_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1731510/c010a52d-a22b-7260-e7ec-74c5cae0212e.jpeg)

##⑤ ③の計算結果と④で求めた商を掛け合わせ、この値を①のオクテットの値とし、これより右のオクテットをすべて０にすることで、ネットワークアドレスとなる
③で求めた「３２」と④で求めた「６」を掛け合わせる。
これにより、「１９２」という数字が求まるので、これを第３オクテットの数値とし、これより右側のオクテット、すなわち第４オクテットを「０」にすると、ネットワークアドレスが求まる。
![E5A4C260-03CD-42D9-B78E-94A0AE09335F.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1731510/cb07fde5-5624-6c0e-02ad-874a881a4e6a.jpeg)

##⑥ ⑤で求めた数値と③の計算結果を足して−１した結果を①のオクテットの値とし、これより右のオクテットをすべて255にすることで、ブロードキャストアドレスとなる
⑤で求めた「１９２」と③で求めた「３２」を足して−１する。
これにより、「２２３」という数字が求まるので、これを第３オクテットの数値とし、これより右側のオクテット、すなわち第４オクテットを「２５５」にすると、ブロードキャストアドレスが求まる。
![25E0E3FA-1345-4726-8192-6C392B76B427.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1731510/eddd53d3-362d-4ee3-7e6b-8bb0e45134bc.jpeg)

##結果
以上より、
ネットワークアドレス：172.16.192.0
ブロードキャストアドレス：172.16.223.255
が求められた。

#最後に
2進数への変換が苦手な人には使えるかもしれないが、なにぶん手順が少々複雑なため、あくまで進数変換に慣れるまでの仮利用的な位置付けにとどめるのがよいと思われる。

#参考図書
https://www.amazon.co.jp/シスコ技術者認定教科書-CCNA-完全合格テキスト-対応試験-200-301/dp/4798165778
Git hooks pre-commitを使って意図しない変更のpushを防ぐ

## はじめに
突然ですが、こんな経験ありませんか？

:confounded:< featureブランチにpushしたつもりがdevelopにpushしてしまった！
:confounded:< pushしてからデバッグコードの消し忘れに気づいた！

どちらかを経験したことがある方は多いのではないでしょうか？(恥ずかしい話ですが私もあります...)
pushする前だったらローカルでcommitの取り消しができますが、
一度pushしてしまうとその差分は履歴として残るし、それを消すにしても消した履歴も残ってしまいますよね。
できることならpushする前に気づいてそっと修正したい...！
ということで、
今回は**「git commit した時にスクリプトを実行することで意図しない変更がpushされないようにする」**をやっていきます。

## やり方
やりたいことは「git commit した時に任意のスクリプトを実行する」ことなので、
今回は[Git フック](https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA-Git-%E3%83%95%E3%83%83%E3%82%AF)の`pre-commit`を使っていきます。
似たようなことができる機能で`GitHub Actions`がありますが、トリガーにcommitがないので今回は`pre-commit`を使っていきます。
pre-commitとは、以下[Git フック](https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA-Git-%E3%83%95%E3%83%83%E3%82%AF)より

> pre-commit フックは、コミットメッセージが入力される前に実行されます。 これは、いまからコミットされるスナップショットを検査したり、何かし忘れた事がないか確認したり、テストが実行できるか確認したり、何かしらコードを検査する目的で使用されます。 このフックがゼロでない値を返すと、コミットが中断されます。

つまり、コミット前に任意のスクリプトを実行し、そのスクリプトが0を返すとコミットがそのままされ、ゼロ以外を返すとコミットが中断されます。
今回はこれを利用します。

## 実装
まずはスクリプトを用意します。下記の通りにコマンドを実行してください。

```zsh
$ mkdir tmp
$ cd tmp
$ git init
```

適当なディレクトリを作成し、そこで`$ git init`を実行しました。これで`.git`という隠しディレクトリができているはずです。
続いて下記コマンドを実行します。

```zsh
$ cd .git
$ cd hooks
$ ls
applypatch-msg.sample		pre-applypatch.sample		pre-rebase.sample
commit-msg.sample		    pre-commit.sample		    pre-receive.sample
fsmonitor-watchman.sample	pre-merge-commit.sample		prepare-commit-msg.sample
post-update.sample		    pre-push.sample			    update.sample
```
隠しディレクトリ内のhooksディレクトリに移動します。そこの中身をみてみるといろいろなサンプルスクリプトがあることがわかります。
これらは末尾の`.sample`を消去すればそのまま動きます。
今回はここに自作のスクリプトを追加します。

```zsh
$ vi pre-commit
```

上記コマンドでpre-commitというファイルを作成してください。(拡張子はなくて大丈夫です)
作成したファイルに下記を追記してください。

```sh
BRANCH_NAME=`git symbolic-ref HEAD | sed -e 's:^refs/heads/::'`
if test $BRANCH_NAME = master -o $BRANCH_NAME = develop; then
    echo "${BRANCH_NAME} への直コミットは禁止されています。"
    exit 1
fi

files=`git status|grep -e "modified" -e "new file"|sed "s/new file:\(.*\)/\1/g"|sed "s/modified:\(.*\)/\1/g"|cut -f 2`

for file in $files
do
   result=`less $file | grep -e "console.log"`;

   if [ -n "$result" ]; then
       echo "${file} にデバッグ文が入っています。";
       exit 1
    fi
done
```

次に、作成したファイルに対して実行権限を与えてあげます。

```zsh
$ chmod +x pre-commit
```

pre-commitが動作しているか確認します。
ここでは例として`console.log('hoge');`と記載されたreadme.mdというファイルを用意します。

```zsh
$ git checkout master
$ git add readme.md
$ git commit -m 'test commit'
master への直コミットは禁止されています。

$ git checkout feature
$ git commit -m 'test commit'
readme.md にデバッグ文が入っています。
```

いずれの場合もコミットできていないはずです、
featureブランチにて`console.log('hoge');` を削除すると正しくコミットできるはずです。

## 参考
- [pre-commitでこんな自動レビューをしています！手戻りが少なくて最高！](https://tech.appbrew.io/entry/2020/05/15/132413)
徳丸本メモ #1(同一オリジンポリシー)

## はじめに
「安全なwebアプリケーションの作り方 第2版」の「3.2 同一オリジンポリシー」の学習を進めている中で、つまずいたところと解決法をメモしておきます。
具体的には、iframeの内側のコンテンツを外側からJavaScriptにより取得できる例にて、その通りに動かなかった時、どう解決したのかを説明します。

## 直面した問題
「3.2 同一オリジンポリシー」の「同一オリジンポリシー」項にて、仮想マシンでwebサーバを起動し ブラウザで/32/32-001.html を表示させる[1]と「パスワード→」ボタンをクリックできるページになります。この「パスワード→」ボタンを押せばiframeの内側のコンテンツすなわち「password1」が「パスワード→」ボタンの右隣に表示される、という例です。しかし、以下の画像のように怒られてしまいます。

![origin-policy.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1548368/ffc7b293-9b4c-156a-fcb7-6eba809134f3.jpeg)

「accessing a cross-origin frame」とあります。。。
[1]詳しくは本を買ってください。本記事はあくまで私自身のメモの役割が大きいです。。

## 解決
本を読み進めるときサンプルがうまく動作しないからといって立ち止まってはいけません。とりあえず、次に進みましょう。読み進めると「同一オリジンである条件」が記載されていました。

* URLのホスト(FQDN)が一致している
* スキーム(プロトコル)が一致している
* ポート番号が一致している

なるほど「URLのホストが一致している」を満たしてなさそうです。なぜなら、私は、URLを
http\://192.168.56.101/32/32-001.html
としていました。これが間違いです。32-001.html内でiframeタグは

```html
<iframe name="iframe1" width="300" height="80" src="http://example.jp/32/32-002.html">
</iframe><br>
```

となっていました。つまり、iframe内ではexample.jpのコンテンツを表示するようになっているのでブラウザに入力するURLにおけるホスト名もexample.jpとしなければなりません。hostsファイルで192.168.56.101とexample.jpを紐づける設定をしていたので、webページは正しく表示されたのですが、ホスト名が一致していないので同一オリジンである条件を満たしていなかったと考えられます。わざわざIPアドレスを打ち込んだ私のミスでした。
そこでURLを正しく、
http\://example.jp/32/32-001.html
としてアクセスし、「パスワード→」ボタンを押すと、以下の画像のように正しく動作しました。
![origin-policy_sucsess.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1548368/a709c1de-f11c-d830-8c59-dad554c2af50.jpeg)

蛇足かもしれませんが、これでもうまく動作しないときはURLが「http」じゃなくて「https」になっていないか、などの確認をしましょう。

## まとめ
「安全なwebアプリケーションの作り方 第2版」で学習を進めている中で、つまずいたところをメモしておきました。
本の学習でのコツは、「つまずいても、とりあえず先に進んでみる」ことです。そうすることで今回のように気がづくことがあるかもしれません。
STM32G031でPWMの測定を作てみた。


x シリアルを使うから上級者用

目的
シェフの気まぐれ
内部タイマーを使いusでオン時間を測る

結果
The time taken was 84 seconds
The time taken was 84560 seconds
The time taken was 84 seconds
The time taken was 84517 seconds
The time taken was 84 seconds
The time taken was 84523 seconds
The time taken was 84 seconds
The time taken was 84537 seconds
The time taken was 84 seconds
The time taken was 84521 seconds
The time taken was 84 seconds
The time taken was 84532 seconds
The time taken was 84 seconds
The time taken was 84514 seconds
The time taken was 84 seconds
The time taken was 84521 seconds
The time taken was 84 seconds
The time taken was 84493 seconds
The time taken was 84 seconds
The time taken was 84512 seconds
The time taken was 84 seconds
The time taken was 84508 seconds
The time taken was 84 seconds


```c++


#include <Arduino.h>

#define in_pwm   PA11 // 5pin

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

//1000の割り算 だいたい正しい
#define DVI1000(n) ((n>>10)+(((n>>10)*32)>>10))


//初期化
void setup() {

  delay(3000); //not Delete

  Serial.begin(9600);

  pinMode(in_pwm, INPUT); //pa11

} //setup

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

  int pwmco2;

  //PWMでデータ取得
  pwmco2=pulseIn(in_pwm,HIGH,2000000);
  printf("The time taken was %d seconds\r\n", pwmco2  );
  printf("The time taken was %d seconds\r\n", DVI1000(pwmco2)  );

  //0.1秒の待ち
  delay(100);

} //loop



```
テストコードでimageが空の時について、エラーになってしまう

```ruby:spec>models>item_spec.rb
it "imageが空だと登録できない" do
        @item.image = ''
        @item.valid?
        expect(@item.errors.full_messages).to include("Image can't be blank")
      end
```

 
:エラー文 
 Failure/Error: expect(@item.errors.full_messages).to include("Image can't be blank")
       expected [] to include "Image can't be blank"
     # ./spec/models/item_spec.rb:22:in `block (4 levels) in <top (required)>


```ruby:spec>models>item_spec.rb
it "imageが空だと登録できない" do
        @item.image = nil
        @item.valid?
        expect(@item.errors.full_messages).to include("Image can't be blank")
      end
```

### 「’’」を「nil」に変えるだけで解決しました。

※ これだけで1〜2時間もエラー解決に時間かかっちゃったよ〜〜〜（泣）