OmniAICreator/Qiita-1.07M huggingface dataset:
【Java文法】ループ

こんにちは、コイキングです。

本記事では'ループ(繰り返し処理)'について、説明したいと思います。

******

# 1. ループとは?

ループは条件式の結果が偽になるまで、ループ構文内のコードを繰り返して実行する構文です。
Javaでループ構文はfor文、拡張for文(for-each文)、while文、do-while文があります。

# 2. for文
for文は**決まった回数分コードを繰り返して実行したい時に**主に使うループ文です。

```
// for ([初期値]; [条件式]; [増減演算]) {
//     条件式の結果が偽になるまで実行するコード...
// }

for (int su1 = 0; su1 < 5; su1++) {
    System.out.println("for 1 ["+ su1 +"] 回目出力");
}
```

# 3. 拡張for文(for-each文)
拡張for文は**配列やリストのようなデータを格納しているデータ構造から、格納されているデータを順次抜き出す場合**、主に使います。

```
ArrayList<String> list = new ArrayList<String>();
list.add("String 1");
list.add("String 2");
list.add("String 3");
list.add("String 4");

// for ([データ型] [リストor 配列の要素] : [リストor 配列など..]) {
//    .....
// }
for (String str : list) {
    System.out.println("for-each 1 " + str);
}
		
int [] array = new int[] {10, 11, 12, 13, 14};
for (int su : array) {
    System.out.println("for-each 2 " + su);
}
```

# 4. while文
while文は最も簡単なループ文です。**私の場合回数指定がない繰り返し処理が必要な場合**使っています。

```
int su4 = 0;
// while (条件式) {
//   条件式の結果が偽になるまで実行するコード...
// }
while (su4 < 10) {
    su4++;
    System.out.println("while文 1 ["+ su4 +"] 番目出力");
}

boolean flg = true;
su4 = 0;
while (flg) {
    su4++;
    System.out.println("while文 2 ["+ su4 +"] 番目出力");
    if (su4 >= 5) {
        flg = false;
        System.out.println("while文 脱出~!");
    }
}
```

# 5. do-while文
do-while文は**条件と関係なくwhile文内の内容を必ず1回は実行したい場合**使います。

```
int su5 = 0;
// 条件を満たさなくても1回はループ文内のコードが実行されます。
do {
    System.out.println("do-while文 1 ["+ su5 +"] 番目出力");
} while(su5 < -1);

// 条件を満すと while文と同じ結果が出力されます。
do {
    su5++;
    System.out.println("do-while文 2 ["+ su5 +"] 番目出力");
} while(su5 < 5);
```

# 6. breakキーワード(ループ脱出)
breakキーワードは条件式を満たすと**breakキーワードを囲んでいる最も近いfor文から脱出**させるキーワードです。

```
// 1番目 for文 
for (int i=0; i<5; i++) { 
    System.out.println("for文 i ["+ i +"] 番目出力");
    
    // 2番目 for文 
    for (int j=0; j<5; j++) {  
        if (j == 3) {
            System.out.println("jの値が 3のため for文 j 脱出");
            break; // 2番目 for文から脱出する
        }
        System.out.println("for文 j ["+ j +"] 番目出力");
    }
			
    if (i == 4) {
        System.out.println("iの値が 4のため for文 i 脱出");
        break; // 2番目 for文から脱出する
    }
}
```

# 7. continueキーワード(繰り返しスキップ)
continueキーワードは条件式を満たすと**continueキーワード以後のループ文内部のコード実行をスキップ**し、continueキーワードを囲んでいる最も近い **ループ文の最初に移動し、条件式の確認からループ文を再実行させる** キーワードです。

```
int su6 = 0;
while (su6 < 10) {
    su6++;
    if (su6 % 2 != 0) {
        continue;
    }
    System.out.println("while文  ["+ su6 +"] 番目出力");	
}
```

# 8. labelキーワード
ラベルはループ構文に名前を付けることで、breakやcontinueキーワードとともに使うと
多種ループ文から**特定のループ文を指定して脱出したり、スキップする**事ができます。

```
label_1 : for (int i=0; i<5; i++) {
    System.out.println("for文 i ["+ i +"] 番目出力");
			
    label_2 : for (int j=0; j<5; j++) {
        if (j == 3) {
            System.out.println("jの値が 3のため for文 label_1 脱出");
            break label_1;
        } 
    }
		
}
```

※ 例示コード
https://github.com/leeyoungseung/algorithmBasic/blob/master/algorithm/src/basic/B_06_Loop.java

※ 韓国語のポストは以下のURLで確認できます。
https://koiking.tistory.com/78?category=1068655
Railsのデザインパターン QueryObjectの作り方

# はじめに

Rails で利用されるデザインパターンの1つに Query オブジェクトがあります。
複雑な ActiveRecord モデルの絞り込みを簡単なクラスに閉じ込めることで、可読性と疎結合を保つことができます。
Query オブジェクトを Relation に対し結合や絞り込み、ソートなどの操作を定義し、Relation を返すクラスとして定義し、
モデルの scope として利用することで、メソッドチェーンが利用できるようになります。

# 動作環境

Rails 6.1.3
Ruby 2.7.4

# 前提

レビューに対する通報機能があり、アイテムの詳細ページでアイテムに紐づいたレビューを一覧表示しています。
そして、レビューに対して通報がすると表示がされないように、自分が通報していないレビューの一覧を取得します。

```ruby:app/model/review.rb
class Review < ApplicationRecord
  belongs_to :product
  belongs_to :user
  has_many :reports, dependent: :destroy

  def self.unreported_reviews(product_id) #誰にも通報されていないレビュー
    eager_load(:reports).where(product_id: product_id, reports: { id: nil })
  end

  def self.reviews_reported_other(product_id, user_id) #自分以外が通報したレビュー
    eager_load(:reports).where(product_id: product_id).where.not(reports: { user_id: user_id })
  end

  def self.exclude_reported_reviews(product_id, user_id)　#自分が通報していないレビュー
    self.unreported_reviews(product_id).or(self.reviews_reported_other(product_id, user_id))
  end
end
```

これだと1つのコントローラーでしか使われない複雑なメソッドができてしまいます。
ここにあるメソッドを Query オブジェクトとしてモデルから切り離します。

# Qurey オブジェクト

モデルから切り離すため app ディレクトリの下に qurey ディレクトリを作成し、ベースとなる Qurey クラスを作成し、それを継承した ExcludeReportedReviewsQuery クラスを定義します。

```ruby:app/query/query.eb
class Query
  class << self
    delegate :call, to: :new
  end

  def initialize(relation)
    @relation = relation
  end

  def call
    raise NotImplementedError
  end

  private

    attr_reader :relation
end

```

`ruby:app/query/exclude_reported_reviews_query.rb
class ExcludeReportedReviewsQuery < Query
def initialize(relation = Review.all)
super(relation)
@relation = relation
end

def call(product_id, user_id)
unreported_reviews(product_id).or(reviews_reported_other(product_id, user_id))
end

private

def unreported_reviews(product_id)
@relation.includes(:reports, :user, :product_review_likes).eager_load(:reports).where(product_id: product_id, reports: { id: nil })
end

def reviews_reported_other(product_id, user_id)
@relation.eager_load(:reports).where(product_id: product_id).where.not(reports: { user_id: user_id })
end
end

```ruby:app/model/review.rb
class Review < ApplicationRecord
  belongs_to :product
  belongs_to :user
  has_many :reports, dependent: :destroy

  scope :exclude_reported_reviews, ->(product_id, user_id) { ExcludeReportedReviewsQuery.call(product_id, user_id) }
end
```

このように定義することで複雑なクエリをコントローラーやモデルから分離できました。
ActiveRecord モデルに変更があって影響が少なくなります。

# まとめ

ひとつの責務をひとつのクラスに切り出し、単一責任の原則を守って設計することで、保守しやすいコードにできます。
このようなデザインパターンを使うことで Rails のフットモデルやフットコントローラーの問題を解決できます。
Sympyのシンボルの取りうる値を定義するAssumptionsについて

# SymPyのAssumptionsとは

```python
import sympy

x = sympy.Symbol('x')
y = sympy.sqrt(x**2)
print(sympy.simplify(y)) # sqrt(x**2)
```

デフォルトでSymPyのシンボルを定義した上記の場合、 $x$ は任意の複素数として解釈される。そのため、`simplify()` により数式 $y$ の簡略化を実行しても、正の実数の時に成立する $y = \sqrt{x^2} \Rightarrow y = x$ という変換は行われない。

```python
import sympy

x = sympy.Symbol('x', positive=True)
y = sympy.sqrt(x**2)
print(sympy.simplify(y)) # x
```

シンボルの定義の際、引数に `positive=True` を与えることで、$x$ が正の実数として解釈され、正の実数の時に成立する $y = \sqrt{x^2} \Rightarrow y = x$ の変換が行われる。

このように、シンボルを定義する際にそのシンボルが取りうる値を定義することで、数式の変換などの処理が適切に行われる。このような定義のことをSymPyではAssumptionsと呼んでいる。

# 指定可能なAssumptionsの一覧
シンボルに指定できるAssumptionsは下記のようなものがある。
詳細は公式ドキュメントの一覧を参照のこと。

https://docs.sympy.org/latest/guides/assumptions.html


|Assumption predicate|日本語名称|集合|
|---|---|---|
|`commutative`|可換性||
|`infinite`|無限|$ \\{-\infty, +\infty, \tilde{\infty}\\} $ [^1]|
|`finite`|有限||
|`hermitian`|エルミート演算子の要素||
|`antihermitian`|反エルミート演算子の要素||
|`complex`|複素数|$ z \in \mathbb{C} $|
|`algebraic`|代数的数|$ z \in \mathbb{\overline{Q}} $|
|`transcendental`|超越数|$ z \in \mathbb{C} - \mathbb{\overline{Q}} $|
|`extended_real`|拡大実数|$ x \in \mathbb{\overline{R}} $ <br> $ (\mathbb{\overline{R}} = \mathbb{R} \cup \\{-\infty, +\infty\\}) $|
|`real`|実数|$ x \in \mathbb{R} $|
|`imaginary`|純虚数|$ z \in \mathbb{I} - \\{0\\} $|
|`rational`|有理数|$ x \in \mathbb{Q} $|
|`irrational`|無理数|$ x \in \mathbb{R} - \mathbb{Q} $|
|`integer`|整数|$ x \in \mathbb{Z} $|
|`noninteger`|非整数拡大実数|$ x \in \mathbb{\overline{R}} - \mathbb{Z} $|
|`even`|偶数|$ \\{2k \mid k \in \mathbb{Z}\\} $|
|`odd`|奇数|$ \\{2k+1 \mid k \in \mathbb{Z}\\} $|
|`prime`|素数|$ x \in \mathbb{P} $|
|`composite`|合成数|$ x \in \mathbb{N} - (\mathbb{P} \cup \\{1\\}) $|
|`zero`|ゼロ|$ \\{0\\} $|
|`nonzero`|非ゼロの実数|$ x \in \mathbb{R} - \\{0\\} $|
|`extended_nonzero`|非ゼロの拡大実数|$ x \in \mathbb{\overline{R}} - \\{0\\} $|
|`positive`|正の実数|$ \\{x \in \mathbb{R} \mid x \gt 0\\} $|
|`nonnegative`|非負の実数|$ \\{x \in \mathbb{R} \mid x \ge 0\\} $|
|`negative`|負の実数|$ \\{x \in \mathbb{R} \mid x \lt 0\\} $|
|`nonpositive`|非正の実数|$ \\{x \in \mathbb{R} \mid x \le 0\\} $|
|`extended_positive`|正の拡大実数|$ \\{x \in \mathbb{\overline{R}} \mid x \gt 0\\} $|
|`extended_nonnegative`|非負の拡大実数|$ \\{x \in \mathbb{\overline{R}} \mid x \ge 0\\} $|
|`extended_negative`|負の拡大実数|$ \\{x \in \mathbb{\overline{R}} \mid x \lt 0\\} $|
|`extended_nonpositive`|非正の拡大実数|$ \\{x \in \mathbb{\overline{R}} \mid x \le 0\\} $|

[^1]: $\tilde{\infty}$ はComplexInfinity https://docs.sympy.org/latest/modules/core.html#complexinfinity
UnityとROSで移動ロボットを動かしてみる


# やったこと

WindowsPCでUnityとROSをつかって、移動ロボットを操作するUIを作成。
チュートリアルが充実しているので初心者でも十分実行できます。
・Unity⇔ROSの通信
・マウスでUnity上のボタンをクリック。移動ロボットが動く。
・移動ロボットの軌跡(赤)、LiDAR点群(黄)、ロボット視点の画像(Unity画像右下window)

##Unity 
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483299/ac160cd6-d04a-3ff8-e156-ee8c09a209cb.gif "Unity" width=640> 
##ROS(Gazebo)
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483299/513ffa5c-251b-bc2a-b37b-6ba965f52763.gif width=640> 

# 実行環境
・PC (windows10)
　構成は下記図通り。
<img src=https://github.com/unity3d-jp/Unity-ROS-MobileRobot-UI-Tutorial/raw/main/docs/images/intro2-1.png width=520>  <img src=https://github.com/unity3d-jp/Unity-ROS-MobileRobot-UI-Tutorial/raw/main/docs/images/intro2-1-1.gif width=520>

# 内容
こちらのチュートリアルを実施
・[UnityとROSで学ぶ移動ロボット入門 UI作成編](https://github.com/unity3d-jp/Unity-ROS-MobileRobot-UI-Tutorial)
 <img src=https://github.com/unity3d-jp/Unity-ROS-MobileRobot-UI-Tutorial/raw/main/docs/images/intro0-1.png width=540>

# 所感
チュートリアルがきちんと整理、記述されているので
トラブル等はなく実行できた。

UnityやROSの基本がつかめる良い教材でした。
iTerm2について【備忘録】

iTerm2を使用することになったので、どんなアプリかなどを備忘録としてまとめました。


## iTerm2とは
Macのターミナルアプリのこと。macOS10.14以降を搭載したMacで動作できる。
Macの場合はターミナルをそのまま使用しているケースが多いが、iTerm2を使用すればターミナルにはない作業効率を向上できる多くの機能を使用できるのでオススメです。
（下記からダウンロードできます。）

- [iTerm2 - macOS Terminal Replacement](https://iterm2.com/)

## ショートカットキー
下記のQiita記事がオススメです。ショートカットキーを使用することで、効率よく作業ができるのでよく参考にしています。

- [iTerm2ショートカット備忘録](https://qiita.com/0084ken/items/8aefabef4fd3cfdf8fce)

## iTerm2の設定
下記の記事を参考にしながら、自分なりにカスタマイズしました。

- [iTerm2のおすすめ設定〜ターミナル作業を効率化する〜](https://qiita.com/ruwatana/items/8d9c174250061721ad11)

### 1.Native full screen windowsを無効にする

`メニューのiTerm2 → Preferences → General → Window → Native full screen windowsのチェックを外す`

![スクリーンショット 2022-01-09 18.19.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/923146/6039765f-7d4b-e31a-58d1-6bf2ded3c574.png)

### 2. 新しいウィンドウの設定を最大化にする

この辺は、好みで設定して下さい。
`メニューのiTerm2 → Preferences → Profiles → Window → Settings for New Windows → Style, Screen, Space`

![スクリーンショット 2022-01-09 17.43.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/923146/89fc9d49-705c-a148-484e-a52c7e017216.png)

## まとめ

普段、業務で使用しているアプリなので、自分の使いやすいように設定することは、非常に大事だと感じました。
透明化する設定がありますが、これも便利なので個人的にはオススメです。
最小コードで変化が分かるGithub Actions

（タイトルの比較対象は私の知る範囲です）

いろいろなページを斜め読みしてたんですが、結局よくわからなかったので、forkして設定すれば動くようなものを目指しました。
forkして後述のシークレット変数を設定すれば動くので、試してみてください。

https://github.com/shimajima-eiji/__Githut-Action_demo

ほとんどREADMEに書いてます。

## ソースコード
https://github.com/shimajima-eiji/__Githut-Action_demo/blob/main/.github/workflows/add_dummy.yml

``` /.github/workflows/add_dummy.yml
# 本稿で解説時点のコード
name: small git push  # (任意で変更できる。Github Actionsの一覧で表示される名称)

on: [push]

jobs:
  run-shell-command:
    runs-on: ubuntu-latest
    steps:  # name欄が実行結果を見れるものなので、何でもかんでもワンライナーにすれば良いというわけでもなさそう
      - uses: actions/checkout@v2  # git clone (this)
      - name: git setting
        run: |
          git config --global user.email "${{ secrets.EMAIL }}"  # Settings->Secretsで追加
          git config --global user.name "${{ secrets.USER }}"
      - name: Commit files
        run: |
          TZ=JST-9 date '+%Y/%m/%d %H:%M:%S (%Z)' >dummy  # Github ActionsのタイムゾーンがUTCなので、これをJSTに変更
          git add .
          git commit -m "Update dummy by Github Actions" -a
          git pull
          git push
```

## 実行結果
`/dummy`に実行した時点の日付を出力する。  
試しに、README.mdを適当に変更してpushすればGithub Actionsが動いていることが確認できる。

## 開発アドバイス
とりあえず困ったら`pwd`したり`ls -l`で実行結果を見てみると何をすればいいかとっかかりが見えてくると思います。
コミットログが汚染されるので、開発用の捨てるリポジトリを一つ作ってガチャガチャやると精神的にも良いです。
群をwikipediaで読んでみた

purescriptを勉強している途中で少し群をwikipediaで読んでみた。前提として、群の前提知識はない。

https://ja.wikipedia.org/wiki/%E7%BE%A4_(%E6%95%B0%E5%AD%A6)

定義を読む。なるほど。群は集合Gと、Gの要素2つを引数にGの要素を返す演算μの組に対して定義されるのか。(G上のとかって表現、昔意味わからなくて苦しんだ。。）
具体例があるので定義に当てはめてみる。

## (G, μ)=(実数, 足し算)の場合
結合法則
a+(b+c)=(a+b)+c

単位元の存在
まあ0やな
a+0=0+a=a

逆元の存在
任意のaに対して、-aが逆元になりそう
a+(-a)=(-a)+a=0=単位元(単位元は0だったからokやな）

交換法則
a+b=b+a

## (G, μ)=(0を除く実数, 掛け算)の場合
結合法則
それはそう
a*(b*c)=(a*b)*c

単位元の存在
1やな
a*1=1*a=a

逆元の存在
任意のaに対して1/aがあって、
a*(1/a)=(1/a)+a=1(1は単位元)

交換法則
a*b=b*a

確かに成り立ってる。

## 条件ごとの名前
条件を満たすごとに出世魚のように名前が変わって、
結合法則だけ=>半群
結合法則と単位元だけ=>モノイド（実数に対する足し算をイメージ。ただし、-aがあるとは限らず、a+b=b+aとも限らない。0はある。）
結合法則と単位元と逆元だけ=>群(実数に対する掛け算のイメージ。実際μ(g,h)をghと書くらしい。と思ったけど、　a*b=b*aとも限らないし実数から0は除いて考えないといけない。1や1/aはある。）
結合法則と単位元と逆元と交換法則=>アーベル群（可換群）(実数に対する掛け算をイメージ。と思ったけど実数から0は除いて考えないといけない。）

（追記・他で読んだ）
結合法則と単位元と交換法則=>可換モノイド(モノイドに加えてa+b=b+aが成立する足し算をイメージ。と思ったけど、-aがあるとは限らない。）

というらしい。

一旦ここまで。

## purescript
ちなみにpurescriptで見てみると、半群はappendだけ定義が求められている。
(G, μ)=(a, append)っていうことだろう。

```purescript
class Semigroup a where
  append :: a -> a -> a
```

結合法則を満たすような実装が求められてると思うんだけど、そうであるならappendは書き方として中間演算子として使うと便利そう(`infixr 5 append as <>` なので、`<>`は実はappendだったんやな)
普通

```
append a (append b c)
```

中間演算子ver(結合法則を満たすなら左右どちらを先に評価しようが変わらないはず）

```
a `append` b `append` c
```

typescriptで書くと、

```typescript
interface Semigroup<T> {
  append: (a: T) => (b: T) => T
}
```

みたいな感じかな。
typescriptでも演算子を定義したい。
「JavaScript」Primitive Type vs Reference Type

JavaScriptはPrimitive TypeとReference Typeという二つのデータ型を提供し、オブジェクトを除いた全てのものはPrimitiveな性格を持っている。

Primitive Type : 基本型/データの情報が入っている
Reference Type : クラス型/オブジェクト実体の場所情報入っている

## Primitive Type (基本型)
Primitive Typeのデータは変数に割り当てられる時、メモリ上に固定された大きさで保存され、当該変数が原始データの値を保管する。 Primitive Typeのデータ型はすべて変数宣言、初期化、割り当て時に値が保存されたメモリ領域に<strong>直接的にアクセス</strong>する。 すなわち、変数に新しい値が割り当てられる場合、変数に割り当てられたメモリブロックに保存された値をすぐに変更する。

### Primitive Typeの種類
- Boolean
- number
- String
- undefined
- BigInt
- シンボル 

### Primitive Typeの変数の複製
各変数の間でPrimitive Typeのデータをコピーする場合、データの値がコピーされる。

```
var x = 100;
var y = x;

x = 99;

console.log(y);	// 100;
```

データの値をコピーするためconsoleを使う前にxを99に変わったが、以前の値である100をコピーしておいたので100が出力されることがわかる。

## Reference Type (クラス型)
Reference Typeのデータは、大きさが決まっておらず、変数に割り当てることになると、値が直接当該変数に保存されることはできず、変数にはデータに対する住所が保存されている。変数の値が保存されたヒープメモリの住所を保存する。<strong>Reference Typeは変数の値が保存されたメモリブロックの住所を持っていてjavascriptエンジンが変数が持っているメモリアドレスを利用して変数の値に接近する。</strong>

### Referece Typeの種類
Object ( array, function, object )

### Reference Typeの変数の複製
各変数の間でReferece Typeのデータをコピーする場合、データの<strong>住所がコピーされる。</strong>

```
var x = { count : 100 };
var y = x;

x.count = 99;

console.log(y);	// 99
```

変数xとyは同じ住所の値を持っている。したがって、同一のオブジェクトを指すことになる。

## Primitive Type vs Reference Type
### 例 1
```
var list1 = [1, 2, 3];	// メモリ住所 : 8765e と仮定する
var list2 = [1, 2, 3];	// メモリ住所 : 9524d と仮定する

var isSame = list1 === list2;	// 8765e === 9524d

console.log(isSame);	// false
```

list1、list2の中の要素は同じだが、配列を新しく作り変えて変数に埋めているため、それぞれ新しいメモリ住所を作って保存し、その住所を参照して変数に該当する住所の値を保存するのと同じである。 

したがって、結果はfalseとなる。

### 例 2
```
var list3 = [ 1, 2, 3];
var list4 = list3;

var isSame = list3 === list4;

console.log(isSame);	// true
```

上の例題とは異なり、新しく配列を生成せずにlist3の位置値をそのままlist4に入れるのであるから、メモリ住所が同じ場合と言える。 

したがって結果はtrueとなる。

### 例 3
```
var updateAge = function () {
	this.age++;
};	// 'メモリ住所 : 4737d' と仮定する

var son = {
	age : 3,
    growUp : updateAge
};

var daugther = {
	age : 7,
    growUp : updateAge
};

var mother = {
	age : 38,
    growUp : updateAge,
    children : [ son, daugther ]
};

var father = {
	age : 38,
    growUp : updateAge,
    wife : mother,
    children : [ son, daugther ]
};

// ### 例 3.1
if (father.growUp === son.growUp) {	// 4737d === 4737d
	console.log('成長可能');
}	// output : 成長可能

// ### 例 3.2
if (father.children === mother.children) {
	console.log('夫婦!');
}	// falseなので、console.logが実行されない
```

例3.１の場合、updateAgeという変数のデータ値に関数の住所の値を保存した。仮想メモリアドレスが4737dという仮定の下にobjectは、当該データの住所の値を保存するため、father.growUpとson.growUpの値は同じ値になる。

したがって、結果はtrueにconsoleに'成長可能'が出力される。


例3.2の場合、同じ変数が埋められているが、オブジェクト内で配列を新たに作ってmother.childrenとfather.childrenは異なる住所の値を新たに割り当てられたため、住所の値が異なる。

したがって、結果はfalseにconsoleに何も出力されない。


## 参考
https://developer.mozilla.org/ja/docs/Glossary/Primitive

## PR
不動産売却の時は LIFULL HOME'S へ！

https://lifullhomes-satei.jp/
iPadだけでアプリ開発をした話

2021/12/16 個人的には衝撃を受ける出来事がありました。

iPad版のSwift Playgroundsで遂に、iOSアプリが開発できるようになりました！！
当時の自分のツイートからも興奮が分かると思います。
https://twitter.com/mtj_j/status/1471269185951256577?s=21


このアップデートで、

- iPad上でSwiftファイルのコンパイルと実行
- より実用的なコード補完
- SwiftUIのライブプレビュー
- Swift PMのライブラリの読み込みのサポート

などなどかなり本格的な機能が導入されました。

一方でデータベースか使えない(これは正しくなくて実は使える)など、まだまだXcodeを普段から使ってるエンジニアからすればおもちゃ感が拭えません。

ですがこれはかなり重要なアップデートだと思っています。

普段から業務でmacを触ってるエンジニアはXcodeでこれからもアプリ開発をすればいいですが、例えば自分のPCを持ってないけどiPadは持ってるという学生といった人がアプリ開発をスタートできるようになりました。

このアップデートはiOSアプリ開発へのハード的な側面でハードルを大きく下げるものです。

しかしデータベースが簡単には使えない、などまだまだ技術的なハードルもあります。
それならせめて、普段からアプリ開発をしている人間がある程度の知見をためて共有していこう、と思い「**iPadだけでアプリ開発をする**」という縛りでアプリ開発をスタートしました。

AppStoreでリリースすることまでできた今、結論を言うと、**大変だけどiPadだけでアプリ開発は不可能じゃない**です。

この記事ではiPadでアプリ開発をしたときに見つけたtipsや良かった点、辛かった点を説明します。

# 作ったアプリ
iPadだけでアプリ開発をするのでせっかくならiPadにフォーカスしたアプリにしようと思い、PencilKitを使った絵日記アプリにしました。

[シンプル絵日記](https://apps.apple.com/jp/app/%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E7%B5%B5%E6%97%A5%E8%A8%98/id1603427728)

このアプリは全てiPadで作られています。
macは1度も触りませんでした。

# iPadでのアプリ開発
## 使ったアプリ
今回使ったアプリは次のとおりです。

- Swift Playgrounds
- Affinity Designer
- App Store Connect
- Safari

たったこれだけです。
お気づきかもしれませんが、Affinity Designerを使ってアイコンやAppStoreのスクショもiPadで作りました。


## DB問題
iPadでデータベースが使えない問題があります。
というのもCoreDataのテーブルを定義するために必要な`.xcdatamodeld`ファイルをSwift Playgroundsでは編集できないからです。

正直データベースなしでアプリ開発はかなり厳しいと思って、僕も他のSQLiteのライブラリやRealmが使えないかと調べたのですが良い方法がなく諦めていました。

しかし、実はこれは解決することができます。
僕も知らなかったのですが、CoreDataは手間をかければ`.xcdatamodeld`ファイルなしで扱えるらしいです。
そしてその手間を省いてくれる、CoreStoreというライブラリを見つけました。
このライブラリの簡単な使い方については[こちらの記事](https://qiita.com/matsuji/items/6d81c39e98b3394a2553)に書きましたので、そちらを参考にしてください。


CoreStoreを使えばiPadでデータベースが使えない問題は解決できます。


## Xcodeを上回るPencilKitとの相性の良さ
これはびっくりしました。
iOSが提供しているPencilKitを使えばメモアプリのようなお絵かきができるUIを3行で実現できます。

この3行というのはAppleが言っていることでして、実際には微調整が必要だったりします。
その時にSwiftUIのPreviewを使えば、なんと、Preview上でApple Pencilを使ったデバッグができます！！
今までのPencilKitを使ったデバッグはシミュレータでは動作確認できずに実機が必須で、macでビルドしてiPadで実行して、動作確認をする必要がありました。
しかし、Swift Playgroundsはコードを変更したその瞬間に右のPreview画面に反映され、そのままApple Pencilでタッチして動作確認できます。
これは正直業務レベルでApple Pencilを使っている人におすすめしたいくらい開発体験が良かったです。

## ここがつらいよ iPadでアプリ開発
あんまりネガキャンはしたくないのですが、やはり業務でアプリ開発をしている人間からすると不満は出てきます。
詳しくは書きませんが、列挙しておきます。もしこのあたりが気になる人はそれはもうmacを買ってXcodeでアプリ開発をする良いタイミングですのでぜひ！

- デバッガーが動かない。変数の確認にはprintデバッグをするしかない
- ウィジェットやSiriをサポートするためのapp extensionを使えない
- gitが使えない
- テストができない

# おわりに
iPadでアプリ開発をした話について書きました。
正直、iPadだけでアプリを作り切るのはまだまだ大変だと感じましたが、macを買ってアプリ開発をする勇気がない人が、アプリ開発を体験するのにはとても良いと思いました。
そしてその気になれば僕がやったように、iPadだけでアプリを開発してリリースしきることができるので、みなさんぜひトライしてみてください！！
マイコンを選定する～RP2040を選んだ理由～

使用するマイコンをどのように選択するかというのを一例として書いてみます。
ArduinoUnoやArduinoNanoに使われているATmega328やESP32、AttinyやSTM32などのマイコンがある中で使用するマイコンをどのように選定するでしょうか？

主に以下が選定基準としてあげられるのではないでしょうか？
##選定基準例
- 開発環境
- トータルサイズ
　・SoCのサイズ
　・クリスタルの有無
　・外付けFlash
- USBの有無
- 電圧と電流
- GPIOの数
- 性能
- 数量及び価格
- マイコン単体で完結すか？
- 開発ボードの有無

これらをRP2040,ESP32,Attinyu1616を基に選定してみましょう。
#数量
使用するデバイスがマスプロか試作かによっても大きく異なります。
ただ、試作と言えども大手の試作は500個の場合もありますし、小さい会社の量産は5個でも量産だったりしますが
数千、数万台以上を生産するか、数個だけかによっても選定基準が異なります。

今回は、数個から数十個までという前提で選定します。
この場合、秋月電子やMouser,DigiKeyでの価格を基に検討します。
#開発環境
プロであればIARやKailなどの開発環境を使うほうが良いようですが、個人で年間数十万円の更新費用を払うことは難しいですし
豊富なライブラリがあるArduinoを使うのが簡単だといえます。
今回は、Arduinoが使えるSoCとします。
#USBやGPIOの数
USBやGPIOの数はSoCのサイズにも直結します。
RP2040はGPIOの数は30本あり、USBも搭載しています。
GPIOについては、外付けのICで増やすことが可能ではありますが、やはりSoC内で完結することが望ましいといえます。
逆に、USBが要らず、GPIOの数も少なくていい場合、Attiny1616が良いでしょう。
#マイコンの性能とROM/RAM・フラッシュ容量
マイコン1個に対してどの程度の仕事をさせたいかを考えていくべきです。
複数の仕事をさせたい、画像や映像、音声を扱うといった場合、ＲＯＭ，ＲＡＭなどの容量が大きいものや、外付けのフラッシュメモリを選択することになります。
RP2040は2~16MBの外付けのフラッシュメモリを使用可能な反面、フラッシュメモリがないと使用できないというデメリットがあります。
#トータルサイズ
マイコン単体で動作可能なものから、外付けのフラッシュが必要なものまで存在します。
今回のRP2040は外付けのフラッシュメモリとクリスタルが必要です。
ESP32やRP2040は1ドルのマイコンといえども、フラッシュメモリとクリスタルで1ドル以上の費用が掛かる上に
トータルサイズはどうしても大きくなってしまいます。
逆にAttiny1616は外付け部品が不要です。
#電圧と電流
RP2040が発表された際にESP32の方が高性能で、良さがわからない
という方が居ました。
RP2040はESP32と同じくSoCとしての単価は1ドル程度です。
技適を考えない場合、両者は無線機能の有無と言えます。
しかし、ESP32は消費電流が大きいです。
使用するデバイスに対してどの程度の消費電流まで許容できるか
動作するデバイスが何Vで駆動させるかを考える必要があります。
#開発ボード
RP2040やESP32は開発ボードが安価で販売されています。
設計においてこの開発ボードが存在するかというのが大きな問題となることがあります。
RP2040のようにエラッタで使えないピンが無いか確認しないで設計するとトラブルの元となります。

以上から何が適切かを考えてSoCを選定してみましょう。
RP2040,ESP32,Attiny1616の3種類のメリット、デメリットは以下だと私は考えています。
###RP2040のメリット
- 電流
- 価格
- 開発環境
- GPIOの数
- USB対応

###RP2040のデメリット
- トータルサイズ

###ESP32のメリット
- 開発環境
- 価格
- 性能（無線）

###ESP32のデメリット
- トータルサイズ
- 電流

###Attiny1616のメリット
- 開発環境
- 価格
- トータルサイズ
- 電圧（３～５Ｖ）

###Attiny1616のデメリット
- 性能
- GPIOの数

以上から私は汎用としては、価格も安く、GPIOの数も多く、入手性の良いRP2040を私はお勧めします。
もちろん、無線が必要な場合は、ESP32が良いと思います。
#RP2040で必要な構成例
- 外付けフラッシュメモリ
- 12MHzクリスタル
になります。
![DSC_0804 (2).JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/d59fa6ad-48ac-d70c-aef5-03b1f262062a.jpeg)
これ以外に、コンデンサやUSBで使う場合はレギュレータやUSBコネクタなどが必要となります。
#用途に応じて
今回3種類のマイコンからRP2040の良さを説明しましたがこれは私の主観であり、用途によっては全く適さない場合もあるため
一つの情報に縛られず多角的に選定してみる事をお勧めします。
ArduinoでRaspberry Pi PicoやRP2040を使用する

ArduinoでRaspberry Pi PicoやRP2040を使う方法を書いてみます。
#初回起動時
RPI-RP2というドライブが見えます。
これがRP2040が展開しているフラッシュになります。
128MBですが、実際にこの容量のフラッシュがついているわけではありません。
また、この画面が出ない場合は、Flashのリセットボタン（GNDに落とす）を押しながらUSB接続すると見えると思います。
![スクリーンショット 2021-08-11 154627.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/53d0c6ef-491a-825f-9554-f5fb0b8a273e.png)
#Arduinoのボードマネージャーに追加する
ツール⇒ボード⇒ボードマネージャー
をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/89d4aa41-32fd-3b5e-cb78-47bdbb4305ed.png)
Arduino Mbed OS R2040 Boardsをインストールします。(別のパッケージでもRaspberry Pi Picoがインストールできます）
![スクリーンショット 2021-10-10 183217.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/6564a78f-9da8-a45f-6451-b9ff224ddf48.png)
USBのドライバーをインストールします。
![スクリーンショット 2021-10-10 183620.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/0dfcb105-4948-2b41-0d9b-332aa7221bbc.png)
これでRaspberry Pi Picoが使用できるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/076d6714-a4d9-8dc5-c291-7f8291b1f7a2.png)
#コンパイル
UnoやESP32などと同じようにコンパイルから書き込みまで自動で行う事が出来ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/8c207409-4c8e-40e8-9f3a-e01943d2557f.png)
書き込みはリセット⇒フラッシュからブート⇒書き込み⇒リセット
という流れのようです。
__万が一、書き込みが出来ない場合は、Flashのリセットボタンを押しながらUSB接続すると書き込めるようです。__

#ボードマネージャー
ボードマネージャーの登録されている場所を見てみましょう。
Arduino1.8の標準のインストールの場合は
C:\Users\user\AppData\Local\Arduino15\packages\arduino\hardware\mbed\2.5.2\variants\RASPBERRY_PI_PICO\ 
になります。
詳しくはMaker Pi RP2040用ボード設定をみてください。
https://qiita.com/usashirou/items/ab4ed1709c8c951f9d89#maker-pi-rp2040%E7%94%A8%E3%83%9C%E3%83%BC%E3%83%89%E8%A8%AD%E5%AE%9A
##カスタムボードを登録するには
RP2040のカスタムボードをArduinoで使用する為にボードマネージャーに登録してみましょう。
Nordic Nrf528xxを使ってみよう nrf52840 Uno Arduino編2　Arduinoにカスタムボードを登録しようを参考にしてください。
https://qiita.com/usashirou/items/eb856670476f5e885607
CoreDataのラッパーライブラリ CoreStore入門

CoreDataを扱ったライブラリとして[CoreStore](https://github.com/JohnEstropia/CoreStore)というライブラリがあります。

スター数も3,000を超えていたりと、比較的注目されているライブラリのようですが日本語で扱った記事がなかったので、まずは簡単な使い方について記事を書こうと思います。

# CoreStoreとは
CoreDataを扱いやすくするライブラリです。
SwiftUIやCombineのサポートはもちろん、thread safeだったり`.xcdatamodeld`ファイルなしで、スキーマが定義できたりします。

自分は諸事情で`.xcdatamodeld`ファイルを使わずにCoreDataを扱いたかったので今回CoreStoreを触りました。

# インストール方法
Swift PMで簡単にインストールできます。
CocoaPodsやCarthageも用意されているみたいですが試してません。

# 使い方
## テーブルの定義
CoreStoreでは`.xcdatamodeld`ファイルを用いて使用することも出来ますが、`.swift`ファイルのみでも使えます。

```swift
class Person: CoreStoreObject {
    @Field.Stored("name")
    var name: String = ""
}
```
こんな感じで保存したいプロパティをpropertyWrapperを使って定義します。
また、上で使った以外にも、`@Field.Relationship`リレーショナルな定義もできます。
コード上でテーブルの定義ができるのはいいですね。

## スキーマ定義とDataStackの初期化
さてさて、テーブルを定義したので次はスキーマの定義をします。
アプリの初期化時に以下のコードを実行します。

```swift
let schema = CoreStoreSchema(
    modelVersion: "V1",
    entities: [
        Entity<Person>("Person")
    ]
)
CoreStoreDefaults.dataStack = DataStack(schema)

try! CoreStoreDefaults.dataStack.addStorageAndWait(SQLiteStore())
```

上のコードについて簡単に説明します。
`DataStack`はデータの操作をするクラスでして、それの初期化をしています。
初期化の際にはスキーマを渡す必要があり、それを渡しています。
そして、アプリの中では基本的に`CoreStoreDefaults.dataStack`を用いてアクセスするのでそこにセットしています。

最後にストレージのセットアップをしています。

## Read
一番シンプルなデータの読み込みは次の通りです。

```swift
let objects = try? CoreStoreDefaults.dataStack.fetchAll(From<Person>())
```

クエリなんかも書けます。
KeyPathでtype-safeにかけるのがいいですね

```swift
try? CoreStoreDefaults.dataStack.fetchAll(
    From<Person>()
        .where(\.$name == "matsuji")
)
```

## Create / Update
DBに対する書き込みは全て`perform`メソッドの中で行います。

```swift
CoreStoreDefaults.dataStack.perform(asynchronous: { transition -> Person in
    let person = transition.create(Into<Person>())
     person = "matsuji"
    return person
}) { result in
    switch result {
    case .success(let person): print("success")
    case .failure(let error): print("error")
    }
}
```
特徴的なのは、`perform`の中でobjectを生成して、書き込み、その結果をクロージャーで受け取っているところです。
`perform`の中で生成されたオブジェクトは書き込みのためだけに生成して使われる必要があり、クロージャーの外から参照してはいけません。
`perform`の中で生成したオブジェクトを他で使いたい場合、completion handlerの中のresultの中身を使う必要があります。

また、既存のデータに対して書き込みを行うときは`edit()`を用いて、トランザクションのためのオブジェクトに変更する必要があります。

```swift
let person: Person
CoreStoreDefaults.dataStack.perform(asynchronous: { transition -> Person in
    let person = transition.edit(person)
     person = "updated name"
    return person
}) { _ in }
```

## Delete
データの削除も`perform`内で行います。

```swift
let person: Person
CoreStoreDefaults.dataStack.perform(asynchronous: { transition -> Person in
    transition.delete(person)
}) { _ in }
```

# おわりに
今回はCoreStoreの簡単な導入を紹介しました。
CoreStoreは使いやすく良いライブラリですので、皆さん一度試してみてください。
今回は省略しましたが、CombineやSwiftUIに対しての機能も用意されているので興味があればそちらもみてみてください。
rails 7.0では`config/initializers`配下のファイルが少なくなっている

# 概要

`rails new`直後の`config/initializers`配下の初期化ファイルの数が多くユーザの認知的負荷につながっていました。
その中には必ずしもすべてのアプリケーションに必要とは限らない設定や、過去のアップグレードの中で既に不要になっている可能性がある設定が存在していたため、今回ファイル構成が見直され不要あったと判断されたファイルが削除されました。

## rails7.0以前

バージョン7.0以前のrailsでは`rails new`時にアプリケーションの初期設定のファイルが`config/initializers`配下に10ファイル作成されていました。

```
$ rails -v
Rails 6.1.4.4
$ rails new sample_app
$ tree config/initializers/
config/initializers/
├── application_controller_renderer.rb
├── assets.rb
├── backtrace_silencers.rb
├── content_security_policy.rb
├── cookies_serializer.rb
├── filter_parameter_logging.rb
├── inflections.rb
├── mime_types.rb
├── permissions_policy.rb
└── wrap_parameters.rb
```

## rails7.0

rails7.0では認知的負荷を低減するために初期化ファイルの構成をシンプルにするため、初期化設定の一部をフレームワーク内のデフォルトの設定として定義することで`rails new`時に生成される`config/initializers`配下ファイルの数を10ファイルから5ファイルに減らしています。

```
$ rails -v
Rails 7.0.0
$ rails new sample_app
$ tree config/initializers/
config/initializers/
├── assets.rb
├── content_security_policy.rb
├── filter_parameter_logging.rb
├── inflections.rb
└── permissions_policy.rb
```

# 削除されたファイルの解説

削除された初期化ファイルは以下の5つです。

* application_controller_renderer.rb
* backtrace_silencers.rb
* cookies_serializer.rb
* mime_types.rb
* wrap_parameters.rb

各ファイルについての役割は以下の通りです。

## application_controller_renderer.rb
コントローラーの以外でviewテンプレートをrenderする際のデフォルト設定値を指定します。
多くのアプリケーションでは不要な設定であることと、少なくとも`rails new`直後の状態では不要なため削除されたようです。

対応されたPR: https://github.com/rails/rails/pull/42538

## backtrace_silencers.rb

任意の処理でbacktrace表示を無効化する設定ファイルです。
削除された理由についての詳細は書かれていませんが、このファイルが存在することにより上記の機能が提供されるメリットよりもinitializersファイルが増えてしまうことによる認知的負荷のデメリットの方が大きいと判断がされたようです。

対応されたPR: https://github.com/rails/rails/pull/43237

## cookies_serializer.rb

cookieのシリアライザの方式を指定するファイルです。バージョン4.1以降のrailsアプリではデフォルトで`:marshal`が定義され`rails new`時に作成されるconfig/initializers/cookies_serializer.rbによっての値を`:json`に上書きされていました。
背景としては、過去rails4.0から4.1へのバージョンアップの際に、互換性を維持する為デフォルトの設定は変更せず、新しく作るアプリケーションのみ変更するためにconfig/initializers/cookies_serializer.rbで上書きしてた為です。
rails7になりinitializersファイルを減らすため、デフォルト値が`:json`に変更されconfig/initializers/cookies_serializer.rbは不要になりました。

https://qiita.com/SoarTec-lab/items/9832bb89402e452b20bb#railsapplicationconfigaction_dispatchcookies_serializer--hybrid

対応されたPR: https://github.com/rails/rails/pull/42538
## mime_types.rb

mime typeの追加を行うファイルです。アプリケーション固有のカスタムmime typeをすることも可能です。
削除された理由についての詳細は書かれていませんが、このファイルが存在することにより上記の機能が提供されるメリットよりもinitializersファイルが増えてしまうことによる認知的負荷のデメリットの方が大きいと判断がされたようです。

対応されたPR: https://github.com/rails/rails/pull/43237
## wrap_parameters.rb

これまでファイルの中で定義されていた以下の設定値がデフォルトの設定になりこのファイルは不要になりました。

```
ActiveSupport.on_load(:action_controller) do
  wrap_parameters format: [:json]
end
```

wrap_parameter自体については[こちらの記事](https://qiita.com/SoarTec-lab/items/a885e8f473ecab7748c7)でも書いています。

対応されたPR: https://github.com/rails/rails/pull/43237
Pulsar C#クライアントライブラリ(DotPulsar)を使ってみた

## はじめに
こんにちは。

本稿では、Pulsarの[C#クライアントライブラリ(DotPulsar)](https://github.com/apache/pulsar-dotpulsar)(以下、DotPulsar)を使って、簡単なメッセージの送受信をしていきます。

今回は、Windows10上に以下のサーバとクライアントを構築します。

* サーバ
  * Pulsar in Docker (standaloneモード) v2.9.1

* クライアント
  * .NET SDK 6.0.101(x64)
  * DotPulsar 2.0.1


## サーバの準備
PowerShellを開き、以下のコマンドを実行して、
Docker上に、Pulsarのstandaloneモードを構築します。

```powershell
PS> docker run -it -p 6650:6650  -p 8080:8080 `
--mount source=pulsardata,target=/pulsar/data `
--mount source=pulsarconf,target=/pulsar/conf `
apachepulsar/pulsar:2.9.1 bin/pulsar standalone -nss -nfw
```

## クライアントの準備
### Consumer
```powershell
PS> dotnet new console -o Consumer
PS> cd Consumer
PS> dotnet add package DotPulsar
```

Program.csを開いて、以下のコードに修正する。

```C#
using DotPulsar;
using DotPulsar.Extensions;
using System.Text;
using System.Buffers;

await using var client = PulsarClient.Builder()
                    .ServiceUrl(new Uri("pulsar://localhost:6650")).Build();
await using var consumer = client.NewConsumer()
                     .SubscriptionName("MySubscription")
                     .Topic("persistent://public/default/mytopic")
                     .Create();

await foreach (var message in consumer.Messages())
{
    Console.WriteLine("Received: " + Encoding.UTF8.GetString(message.Data.ToArray()));
    await consumer.Acknowledge(message);
}
```


### Producer
```powershell
PS> dotnet new console -o Producer
PS> cd Producer
PS> dotnet add package DotPulsar
```

Program.csを開いて、以下のコードに修正する。

```C#
using DotPulsar;
using DotPulsar.Extensions;
using System.Text;
using System.Buffers;

await using var client = PulsarClient.Builder()
                    .ServiceUrl(new Uri("pulsar://localhost:6650")).Build();
await using var producer = client.NewProducer()
                     .Topic("persistent://public/default/mytopic")
                     .Create();
var text = "Hello World";
_ = await producer.Send(Encoding.UTF8.GetBytes(text));
Console.WriteLine("Sent: " + text);
```

## メッセージの送受信
Consumer側の画面

```powershell
PS> dotnet run
```

Producer側の画面

```powershell
PS> dotnet run
```

と実行すると、ProducerからConsumerにメッセージが流れ、
Consumer側の画面に`Received: Hello World`と表示されます。

## おわりに
本稿では、DotPulsarを使ったメッセージの送受信を行いました。

今回、シンプルなProducer、Consumerを作成して試しましたが、他にも様々な機能が実装されています。 
[公式ドキュメント](https://pulsar.apache.org/docs/en/client-libraries-dotnet/)が参考になるかと思います。

## 参考URL
- [Apache Pulsar](https://pulsar.apache.org/)
- [DotPulsar](https://github.com/apache/pulsar-dotpulsar)
【Mac＆Windowsユーザー】MacとWindowsで同じ設定のマウスジェスチャーを使いたい！

※随時アップデートします。
# 発端
<!-- 発端や概要を記載 -->
・パソコン作業をそろそろショートカットとか駆使して効率化してぇな
・MouseGestureL.ahkのマウスジェスチャー超便利！(記事作成予定...)
・MacとWindowsで同じマウスジェスチャーを使えるようにしたい！



<!-- 各チャプター -->
<a id="#Chapter1"></a>
# 結論
・Macは「BetterTouchTool」
・Windowsは「MouseGestureL.ahk」
を使って同じ動作で同じアクションになるように設定する。

キー設定の引き継ぎに関して、
「BetterTouchTool」はiCloudにバックアップできる機能があるらしい。
「MouseGestureL.ahk」はフォルダごとコピーすればどのPCでも使えるらしいので、OneDriveで管理する。
(検証次第追記します...)

<a id="#Chapter2"></a>
# MouseGestureL.ahk編
<a id="#Chapter3"></a>
# BetterTouchTool編

・BetterTouchToolをインストールします。(詳しくは他の方の記事を参考にしてください。)

```言語名:BetterTouchToolインストール
brew install --cask bettertouchtool 
```

以下各種設定
・初期設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/623093/e7106285-a4ec-af2f-ca1b-68aedfdec1a8.png)
・ジェスチャー時の背景を透明化
-[BetterTouchToolのマウスジェスチャーで、軌跡や背景を消したり、ジェスチャー後にマウスが戻るのを防ぐ方法](https://keikenchi.com/bettertouchtool-settings)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/623093/989ce3b7-8a88-2793-f27f-07191f0b2c38.png)
・ジェスチャーを登録
※まだこの段階ではジェスチャーを起動することができません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/623093/41b86c2b-6565-db84-d178-042e9e0f72f1.png)
・ジェスチャーの記録開始キーを登録
ここでは、MouseGestureL.ahkで設定したように、右クリックを押しながらマウスジェスチャーを行うように設定したいので、
「右クリック」を押した時に「マウスジェスチャーの記録を開始」と設定する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/623093/4174e855-1f55-877b-bbec-6cf96d013b2a.png)

・完成！
あとは、お好みでジェスチャーを登録するだけでWindowsとMacで同じマウスジェスチャーを扱うことができるはずです。(問題が起き次第追記します...)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/623093/adfe4d38-ae05-60be-b3c1-927830897202.png)

# うまく動かない場合
①「右クリック」を押してもマウスジェスチャーを実行できない。
＞「描画設定」のこの箇所にチェックが入っていることを確認してみてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/623093/d2edfaa4-fac7-9b24-4be4-1712c7ce699a.png)


# 参考文献
-
Unity で WebCamTexture を使用する際には初期化完了待ちが必要な件

# はじめに

WebCamTexture には `Play()` というメソッドがありますが、このメソッドは**実は非同期で実行されるメソッド**です。
非同期実行されるため初期化を待つ必要があるのですが、それを待つ方法を WebCamTexture にはありません。

では、どうするかというと…
**WebCamTexture.width の変化をポーリングする（※WebCamTexture.height でも良い）**
という方法を取ります。

この `WebCamTexture.width` ですが、`Play()` を呼び出す前には正確な値が入っておらず、また、`Play()` を呼び出した直後も非同期実行されるためポーリングで変化を待つ必要があります。
私の環境では初期化が完了するまでは常に 16 という値が入っておりましたが Unity 公式の Answers では 100 以下の値が返される間というのがありましたので、同じように設定しました。

https://answers.unity.com/questions/232168/how-can-i-get-the-device-camera-aspect-ratio.html?_ga=2.89405211.1877775720.1641634963-1296741758.1637720145

# ソースコード
初期化完了をポーリングするソースコードは以下のようになります。

```C#
using UnityEngine;

public class WebCamTextureSample : MonoBehaviour
{
    WebCamTexture _webCamTexture;

    IEnumerator Start()
    {
        _webCamTexture = new WebCamTexture();

        // WebCamTexture のプロパティは起動後でないと取得できないため、
        // まず、最初にカメラを起動させる。
        _webCamTexture.Play();

        // WebCamTexture.width が 100 以下の状況の場合は、
        // 初期化が完了していないので、100 以上になるまで待機
        while (_webCamTexture.width < 100) {
            yield return null;
        }

        // これ以降でカメラの情報が正しく取得可能
    }
}
```

初期化が完了後に正確なカメラの解像度などが取得できます。

# おわりに

対応方法は簡単なのですが、意外とネット上にある情報では WebCamTexture の初期化を待っているソースコードがなかったので、みなさんはどうしているのか？と思ったりしていたのですが、ある程度決め打ちでカメラを制御するのであれば、気にする必要はないかもしれません。
超簡単！Dockerを使ってサクッとAWS ECR + AppRunnerにPython + Flask環境

タイトルは昔の記事をパクりました。以下の記事でプロニチをGCP（Cloud Run）からAWS（App Runner）に移行するにあたりApp Runner周りをまとめてみました。所々、固有名詞でpronichiとなっていますが私のプロジェクト名になるので参考にする方は任意で変更してください。

https://qiita.com/morita-toyscreation/items/dc55b25ab4047c473837

# 事前準備

1. AWS Command Line 導入
2. DockerイメージとFlask作成

## AWS Command Line 導入

既に導入済だと思いますが念の為記載
AWS Command Line をインストール・更新

```bash
$ curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
$ sudo installer -pkg AWSCLIV2.pkg -target /
```

AWS Command Line 初期設定
予めIAMでユーザー作成しキーを取得してください。

```bash
$ aws configure --profile pronichi
AWS Access Key ID [None]: 
AWS Secret Access Key [None]: 
Default region name [None]: ap-northeast-1
Default output format [None]: json
```

Dockerfileイメージ作成、起動
DockerfileとFlask関連は以下を参照くださいませ。

https://qiita.com/morita-toyscreation/items/8471a2ccc481a840c372

```bash
$ docker build -t pronichi .
$ docker run --name pronichi -p 8080:8080 -v ~/Sites/pronichi/:/app -it --rm pronichi
```

# Amazon ECR リポジトリ作成、プッシュ

ECRリポジトリ作成

```bash
$ aws ecr create-repository \
    --repository-name pronichi \
    --image-scanning-configuration scanOnPush=true \
    --region ap-northeast-1 \
    --profile pronichi
```

認証

```bash
$ aws ecr get-login-password --region ap-northeast-1 --profile pronichi | docker login --username AWS --password-stdin 758610380088.dkr.ecr.ap-northeast-1.amazonaws.com
Login Succeeded
```

リポジトリにイメージをプッシュ

```bash
$ docker tag pronichi:latest 758610380088.dkr.ecr.ap-northeast-1.amazonaws.com/pronichi:latest
$ docker push 758610380088.dkr.ecr.ap-northeast-1.amazonaws.com/pronichi:latest
```

![スクリーンショット_2022-01-09_15.34.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59264/5304b4ed-c716-5545-6037-e7efb39bea99.png)

# AppRunner作成・デプロイ

本当はCUIでやりたかったが、GUIで一旦

アップロードしたコンテナイメージ選択、他はデフォルト
確認画面にて「作成とデプロイ」ボタンを押すと初回のデプロイが開始

![スクリーンショット_2022-01-09_15.59.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59264/f1d9319a-f4ac-b785-fe25-9b09d19751e5.png)


デプロイ完了はログで確認

![スクリーンショット_2022-01-09_17.02.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59264/916173fb-1054-6631-f477-f1b9dce58503.png)
![スクリーンショット_2022-01-09_17.02.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59264/ac875304-2846-d7de-e70e-e7fb067b0cb5.png)

デプロイは初回は6分程度、２回目は5分程度かかっていた
GCPのCloud Runだともっと早い気がしたが正確に測っていないのでまた計測したいと思う。

デフォルトドメインにアクセスするとページが表示されることを確認

```bash
$ curl https://p6drxqx7ku.ap-northeast-1.awsapprunner.com/
TEST
```

以上

<!--

参考サイト

[Docker イメージをプッシュする](https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/docker-push-ecr-image.html)
-->
EditTextのカーソルの行の位置を取得するには

EditTextのカーソル位置を取得するのは、調べたりするとすぐ出てきたりしますが、カーソルが何行目なのかを取得する方法が、無かったので忘れないようにする為、書いておく事にします。


# EditTextのカーソルの行の位置を取得する方法

```
 val layout: Layout = binding.editText.layout
 val offset: Int = binding.editText.selectionEnd // カーソルの位置
 val line = layout.getLineForOffset(offset) //引数に指定したoffsetの行番号(カーソルが何行目か)
 val primaryHorizontal = layout.getPrimaryHorizontal(offset) // 引数に指定したoffsetの水平位置が返ってきます(カーソルが左端からの水平位置)
```

EditTextのLayoutを使う事で、取得することができました。

# カーソルを上に移動させる方法

```
binding.editText.setSelection(layout.getOffsetForHorizontal(line -1, primaryHorizontal))
```

実際に使用するときは一番上の行なのかを判定したり、工夫が必要になると思います。
nRF52832とnRF52840のプロジェクトファイルの差分

# 記事の概要
nRF52832用に作成したプロジェクトをnRF52840に移植する際に、各種設定を変更する必要がありました。
本記事では何を修正したかをメモします。

nRF52の他のシリーズやnRF53などに移植する際も、本記事が参考になると考えています。

# ソフトデバイスの違い
nRF52382に対応するソフトデバイスはS132、nRF52340に対応するソフトデバイスはS140になります。

# device_nameの差分

```console:nRF52832
arm_target_device_name="nRF52832_xxAA"
```

```console:nRF52840
arm_target_device_name="nRF52840_xxAA"
```

# c_user_include_directories

```console:nRF52832
../../../../../../components/softdevice/s132/headers;
../../../../../../components/softdevice/s132/headers/nrf52;
```

```console:nRF52840
../../../../../../components/softdevice/s140/headers;
../../../../../../components/softdevice/s140/headers/nrf52;
```

# c_preprocessor_definitions

```console:nRF52832
BOARD_PCA10040;
NRF52;
NRF52832_XXAA;
NRF52_PAN_74;
S132;
```

```console:nRF52840
BOARD_PCA10056;
NRF52840_XXAA;
S140;
```


# debug_register_definition_file

```console:nRF52832
debug_register_definition_file="../../../../../../modules/nrfx/mdk/nrf52.svd"
```

```console:nRF52840
debug_register_definition_file="../../../../../../modules/nrfx/mdk/nrf52840.svd"
```

# debug_additional_load_file

```console:nRF52832
debug_additional_load_file="../../../../../../components/softdevice/s132/hex/s132_nrf52_7.2.0_softdevice.hex"
```

```console:nRF52840
debug_additional_load_file="../../../../../../components/softdevice/s140/hex/s140_nrf52_7.2.0_softdevice.hex"
```

# linker_section_placement_macros
メモリマップの設定です。
`RAM_START`と`RAM_SIZE`は作成するアプリケーションにより変わるので、適当に設定します。
他の項目については、nRF52840はFLASHとRAMのサイズがnRF52832より大きいので必ず修正しないといけません。

```console:nRF52832
FLASH_PH_SIZE=0x80000;
RAM_PH_SIZE=0x10000;
FLASH_START=0x26000;
FLASH_SIZE=0x5a000;
RAM_START=0x200022f0;
RAM_SIZE=0xdd10"
```


```console:nRF52840
FLASH_PH_SIZE=0x100000;
RAM_PH_SIZE=0x40000;
FLASH_START=0x27000;
FLASH_SIZE=0xd9000;
RAM_START=0x20002300;
RAM_SIZE=0x3dd00"
```

# linker_section_placements_segments

```console:nRF52832
FLASH RX 0x0 0x80000;
RAM1 RWX 0x20000000 0x10000"
```

```console:nRF52840
FLASH RX 0x0 0x100000;
RAM1 RWX 0x20000000 0x40000"
```

# file_name

```console:nRF52832
<file file_name="../../../../../../modules/nrfx/mdk/ses_startup_nrf52.s" />
<file file_name="../../../../../../modules/nrfx/mdk/system_nrf52.c" />
```

```console:nRF52840
<file file_name="../../../../../../modules/nrfx/mdk/ses_startup_nrf52840.s" />
<file file_name="../../../../../../modules/nrfx/mdk/system_nrf52840.c" />
```
2値画像からピクセルが存在する部分だけ切り抜いてリサイズする


# 方針
ピクセルが0, 1の2値を取る画像から
ピクセルが存在する上下左右を使って切り抜き、元のサイズに戻す

1. 行、列ごとに値が1であるピクセルが何個か求める
2. 1が1つ以上である最大値、最小値を行、列ごとに求める
3. それぞれを上下左右に対応
4. トリミング
5. cv2でリサイズ

# 使用する関数
```python
np.count_nonzero(条件式, axis=0か1)
```

条件に合致する行、列の要素数を返す
axis=0なら列、1なら行

```python
np.where(条件式)
```
条件に合致するIndexを返す

# 作ったもの
```python
arr = np.load("path")

col = np.count_nonzero(arr == 1, axis=0)
row = np.count_nonzero(arr == 1, axis=1)

top    = np.where(row>0)[0][0]
bottom = np.where(row>0)[0][-1]
left   = np.where(col>0)[0][0]
right  = np.where(col>0)[0][-1]

trimed_arr = arr[top:bottom+1, left:right+1]
resized_arr = cv2.resize(trimed_arr.astype('uint8'), dsize=arr.shape)
```
Crystalでコマンドラインツールを作るOptionParserでハマったのでメモ

引数に数値を取るようなコマンドラインオプションを作成しようとしたが、
これが動かなかった。どこが悪いのだろうか？

```crystal
require "option_parser"

a = 1

OptionParser.parse do |parser|
  parser.banner = "Usage: hoge"
  parser.on("-a", "--aaa", "fuga") { |n| a = n.to_i }
  parser.on("-h", "--help", "Show this help") do
    puts parser
    exit
  end
  parser.invalid_option do |flag|
    STDERR.puts "ERROR: #{flag} is not a valid option."
    STDERR.puts parser
    exit(1)
  end
end
```

原因がわからなくて時間を消費した。答えは、

```diff
- parser.on("-a", "--aaa", "fuga") { |n| a = n.to_i }
+ parser.on("-a NUM", "--aaa NUM", "fuga") { |n| a = n.to_i }
```

のように引数を取ることを明示する必要があった。

エラーメッセージは invalid Int32 が云々みたいな感じだったので、これだけでエラーがどこから出ているのか見つけるのは難しい。OptionParserの全容を把握していないのでわからないが、引数の型を指定するオプションがあるのかどうかはわからない。（Rubyでは確か指定できたはず）

この記事は以上です。
Python備忘録

# ファイル操作
ディレクトリ・ファイルが存在するか
```os.path.exists```
ディレクトリが存在するか
```os.path.isdir```
ディレクトリを再帰的に作成
```os.makedirs('dirname', exist_ok=True)```

# numpy
### 保存・読み出し
numpy配列を保存
```np.save("dir",arr)```
numpy配列を読み込み
```np.load("dir")```

### 型
型を指定してnumpy配列を作成
```np.array(arr, dtype="")```
numpy配列を型キャスト
```[ndarray].astype([dtype])```


# matplotlib
### 保存・読み出し
numpy配列を画像として保存
```plt.imsave("dir", arr)```
ModuleNotFoundError: No module named 'tensorboardX'エラー時の解決策メモ

以下のようなエラーが出た場合の解決策メモ

```
Traceback (most recent call last):
  File "/Users/seigo/Desktop/python/tetris_game/game_manager/game_manager.py", line 22, in <module>
    from tensorboardX import SummaryWriter
ModuleNotFoundError: No module named 'tensorboardX'
```

```
pip install tensorboardX
```

# 参考
[ImportError: No module named 'tensorboardX' #255](https://github.com/lanpa/tensorboardX/issues/255)
VSCodeにおいてpytestでdebugを実行した際、エラー箇所でbreakしないことについて

# 課題
VSCodeにおいてpytestでdebugを実行した際、エラー箇所でbreakしないこと。
![demo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/861809/635e5f30-a03a-6ff9-b498-26ee5a7a2af0.gif)

画像は以下URLから参照
https://stackoverflow.com/questions/62419998/how-can-i-get-pytest-to-not-catch-exceptions/62563106#62563106

# 解決方法
Debugの画面で、BREAKPOINTSの「User Uncaught Exceptions」にチェックボックスを入れるだけ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/861809/05b39725-5c9a-1437-6271-43a30383e8e6.png)

ここに解決方法が書いてありました。
https://github.com/microsoft/debugpy/issues/364
【備忘録】サーバーレスなお問い合わせフォームを実装してみた

##背景
友達が申し込みフォームを利用してイベントの参加を募っていたが、申し込みの後、アドレスに返事がなくて文句言われてるのを聞いて、自動返信も大切なんだなぁと思い、作ろうと思いました。

#サーバーレスなお問い合わせフォームって何？
AWS上のサービスを利用し、サーバーレス（サーバーを構築しないで）実装してみました。
AWSで言うとEC2を利用せずに、アプリを構築したイメージです。
Webサーバーを「S3」と「Lambda」、「API Gateway」、メールサーバーを「SES」で設定をしました。

##やり方
下記サイトを見たらできました。
ネットの海は宝物庫

S3 静的ウェブサイトにサーバーレスなお問い合わせフォームを実装してみた（Amazon SES + AWS Lambda + API Gateway）
https://dev.classmethod.jp/articles/serverless-mailform-s3-website-hosting/

筆者が作ってみたサイト
http://portfolio-regards.s3-website-ap-northeast-1.amazonaws.com

##終わりに
サーバーを借りることなく、サービスが公開できる時代になったんですね〜
C言語で文字列リテラル又はマクロを連結する方法

以下のように連続して書ける マクロが連続する場合はスペースを1つ空ける


```c
#include <stdio.h>
#define PRIuPTR "lu"

int main()
{
  char *a1 = "abc""" PRIuPTR PRIuPTR ""   "def";
  printf("%s\n", a1);
  return 0;
}
```

```sh
$ clang main.c
$ ./a.out
abcluludef
```
Pythonで文字列からBool型に変換する方法


# Background

`str = "True"` と文字列になっている値をBoolにしたい場合があります。 直感的に`bool(str)` にしたいのですが思った結果が出ません。ここでは、`True`や`False`などの文字列からBool型にする方法を紹介します。

# 組み込み関数 bool

組み込み関数として `bool` が存在します。 `"True"` となっているものは `True`(bool)に、 `"False"` となっているものは `False`(bool)に変換しそうですが実際は違います。 なぜなら、`bool`関数は入力値が空文字以外は`True`と出力され、空文字の場合は`False`と出力されます。そのため、`bool("False")`としても空文字以外と判定されてしまうため`True`と出力されます。

```python
>>> str_true = "True"
>>> str_false = "False"
>>> bool(str_true)
True
>>> bool(str_false)
True
```

# Method

解決方法として `distutils.util` パッケージ内の `strtobool`関数を使います。
公式のドキュメントを見ると、

*真偽値をあらわす文字列を真(1)または偽(0)に変換します。*
*真の値は y, yes, t, true, on そして 1 です。偽の値は n, no, f, false, off そして 0 です。 val が上のどれでもない時は ValueError を起こします。*

と書いてあります。

Pythonでは `True`は `1`、 `False`は`0`と同義なので`strtobool`をそのまま使って問題ないです。

# Development

```Python
>>> from distutils.util import strtobool
>>> str_true = "True"
>>> str_false = "False"
>>> strtobool(str_true)
1
>>> strtobool(str_false)
0
#数字だと不安に思う人はbool関数でラッピングする
>>> bool(strtobool(str_true))
True
>>> bool(strtobool(str_false))
False
```

びっくりすることに、上記でチラッと書いた通り、`yes/no`や`on/off`も`True/False`(bool)に変換してくれます。

```Python
>>> str_yes = "Yes"
>>> str_no = "No"
>>> str_on = "On"
>>> str_off = "Off"
>>> bool(strtobool(str_yes))
True
>>> bool(strtobool(str_no))
False
>>> bool(strtobool(str_on))
True
>>> bool(strtobool(str_off))
False
```


# Supplement

開発するひとによっては`TRUE` `True` `true`といろんな書き方のパターンがあります。どれでも対応できるのか確認すると、`strtobool`内で引数に`lower`を使って小文字に変換しています。なのでどれでもOKです。

```Python
def strtobool (val):
    """Convert a string representation of truth to true (1) or false (0).
    True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
    are 'n', 'no', 'f', 'false', 'off', and '0'.  Raises ValueError if
    'val' is anything else.
    """
    val = val.lower()
    if val in ('y', 'yes', 't', 'true', 'on', '1'):
        return 1
    elif val in ('n', 'no', 'f', 'false', 'off', '0'):
        return 0
    else:
        raise ValueError("invalid truth value %r" % (val,))
```
https://github.com/python/cpython/blob/main/Lib/distutils/util.py#L308


# Reference

- [Pythonの真偽値bool型（True, False）と他の型との変換・判定](https://note.nkmk.me/python-bool-true-false-usage/)
- [文字列からbool値に変換する](https://python.civic-apps.com/str-to-bool/)
Node.js: MySQL 8.0 で接続エラーが出た時の対策

次のエラーが出た時の対策です。

```text
  code: 'ER_NOT_SUPPORTED_AUTH_MODE',
  errno: 1251,
  sqlMessage: 'Client does not support authentication protocol requested by server; consider upgrading MySQL client',
  sqlState: '08004',
  fatal: true
```

エラーが出るプログラム

```js:maria_version.js
#! /usr/local/bin/node
// ---------------------------------------------------------------
//	maria_version.js
//
//					Jan/09/2022
//
// ---------------------------------------------------------------
'use strict'

// ---------------------------------------------------------------
console.error ("*** 開始 ***")

var mysql = require('mysql')
// var mysql = require('mysql2')

var connection = mysql.createConnection ({
	host: 'localhost',
	user: 'scott',
	password: 'tiger123'
	})

connection.query("select version()", function (err, rows)
	{
  	if (err) throw err
  	console.log (rows.length)
  	console.log (rows[0])

	connection.end()
	console.error ("*** 終了 ***")
	})
```

解決方法

>```text
require('mysql')
```

>を

>```text
require('mysql2')
```

>にすれば、問題は解決します。

実行結果

```text
*** 開始 ***
1
{ 'version()': '8.0.27-0ubuntu0.20.04.1' }
*** 終了 ***
```

MySQL サーバーで対処することもできます。

```text
mysql> ALTER USER scott@localhost IDENTIFIED WITH mysql_native_password BY 'tige
r123';
```
データサイエンス数学ストラテジスト 中級を受験してみた。

# データサイエンス数学ストラテジストってなに？？
2021年9月からスタートした比較的新しい資格試験になります。

公式サイト（https://ds.su-gaku.biz/） では以下のように定義されています。

>
「データサイエンス数学ストラテジスト」は、
データサイエンスの基盤となる数学スキル、リテラシーを学び、
その理解度・習熟度を測定することで、データサイエンスにおける数学を扱う技能を認定する資格です。

中級、上級と2段階の試験が用意されていますが、それぞれの内容については公式にまとめられています。
また、どちらもオンライン上で多肢選択で解答するIBT形式を採用されているので、申し込みから受験までサクッと出来るのが嬉しいところです。

# 学習方法
公式が学習方法として**[「e-ラーニング」](https://www.agaroot.jp/datascience/ds_strategist/)**と**[「公式問題集」](https://www.amazon.co.jp/gp/product/429610988X/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=suken06-22&creative=1211&linkCode=as2&creativeASIN=429610988X&linkId=0602c46d24cb5a64c8a8e0a7b392427e)**を用意してくれています。
ただ、中級であれば問題集だけでいいかと。
解説はそこまで丁寧ではないですが、中級は高校数学（数学Ⅰ・A）までが範囲なので、内容を思い出すきっかけになれば十分かと思います。
参考書の問題は全80問あるので、それらを理解できればまず合格は出来るでしょう。

# 試験本番
試験は90分で30問の選択式になっており、IBT形式で自宅のPCから受験できます。
また、計算機やExcelの仕様はOKですが、受験中における検索や参考書の確認は**不正行為**となります。（厳密にチェックされるわけではないですが、注意書きが表示されていました。）

難易度ですが、体感では問題集と大差ないと感じました。
時間も一通り見直しができる程度には残りました。

# 結果
無事合格することが出来ました。
問題の分野ごとに採点、評価された結果が返ってきます。
自分の理解力がどのレベルにいるかが分かりますね。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621838/a4917371-0ae0-81a5-74c6-86b0f795a04d.png" width=50%>

# まとめ
世界的にAI戦略、データサイエンス、IoTなどに対応できる人材の需要は高まりつつある今、こういった資格が増えてきましたね。
自分の力試しも兼ねて受験するにはちょうどいい資格かと思います。

基礎数学や機械学習の入門レベルの内容がカバーされているので、これからデータサイエンスなどに興味がある方は受けてみてはいかがでしょうか。