OmniAICreator/Qiita-1.07M huggingface dataset:
CircleCIでjob間でフォルダを共有する

# はじめに
JavaScriptフレームワークを使用しているプロジェクトでbuild job→deploy jobという流れで
CircleCIを通して行なった際にbuild jobで生成されたフォルダがdeploy jobにないため、failedしていた。
今回はその対処方法のメモ

# persist_to_workspace, attach_workspaceを使う

- persist_to_workspace
  別ジョブでもbuildしたフォルダを使用できるようにしてくれる。

- attach_workspace
  persist_to_workspaceで指定したフォルダを読み込む。

# 設定例

```yml
version: 2.1
orbs:
  node: circleci/node@3.0.0

jobs:
  build:
    docker:
      - image: 'circleci/node:14.3.0'
    working_directory: ~/dir
    steps:
      - checkout
      - run:
          name: npm install & build
          command: npm install && build command
      - persist_to_workspace:
          root: ~/dir
          paths:
              - build_folder

  deploy:
    docker:
      - image: ...
    working_directory: ~/dir
    steps:
      - checkout
      - attach_workspace:
          at: ~/dir
      - run:
          name: deploy
          command: deploy command
        
workflows:
  ...
```
LPICひたすら暗記（2日目）

#Apache関連の暗記を行う
Apache関連は暗記が多いので、理解と暗記をはかる

##インストール作業
■パッケージを利用する方法と、ソースからコンパイルする方法がある
■ソースからの場合は、configure - make - make installの流れ

confitureスクリプトの代表的なオプション

|オプション|説明|
|:-----------:|:------------:|:------------:|
|prefix=ディレクトリ|インストール先ディレクトリ||
|--sysconfdir=ディレクトリ|設定ファイルのディレクトリ||
|--enable-module=モジュール|標準モジュールを組み込む||
|--disable-module=モジュール|標準モジュールを組み込まない||
|--enable-shared=モジュール|標準モジュールをDSOで組み込む||
|--disable-shared=モジュール|標準モジュールをDSOで組み込まない||

■apacheのメイン設定ファイル:```https.conf```

■ソースからのインストールの場合のPATH　↓

|ファイル・ディレクトリ|説明|
|:-----------:|:------------:|:------------|
|/usr/local/apache2/conf/httpd.conf|メイン設定ファイル|
|/usr/local/apache2/conf/extra/|補助設定ファイルを格納するディレクトリ|
|httpd-laungages.conf|言語・文字コードの設定|
|httpd-userdir.conf|一般ユーザーのホームディレクトリ設定|
|httpd-info.conf|server-inifo、server-statusの設定|
|httpd-vhosts.conf|バーチャルホストの設定|
|httpd-default.conf|デフォルトサーバー全般設定|
|httpd-ssl.conf|SSL/TLSの設定|

■設定ファイル(Red Hat系)　↓

|ファイル・ディレクトリ|説明|
|:-----------:|:------------:|:------------|
|/etc/httpd/conf/httpd.conf|メインの設定ファイル|
|/etc/httpd/conf.d/|補助設定ファイルを格納するディレクトリ|
|ssl.conf|SSL/TLSの設定|
|php.conf|PHPモジュールの設定|
|perl.conf|Perlモジュールの設定|

■設定ファイル(Debian系)　↓

|ファイル・ディレクトリ|説明|
|:-----------:|:------------:|:------------|
|/etc/apache2/apache2.conf|メインの設定ファイル|
|/etc/apache2/|補助設定ファイルを格納するディレクトリ|
|/etc/apache2/ports.conf|ポートの設定|
|/etc/apache2/sites-enabled/|サイトの定義ファイルへのリンク|
|/etc/apache2/sites-avaliable/|サイト定義ファイル|

##httpd.confの詳解

■適用範囲を指定可能

```ruby:ディレクティブの書式
　　　　ファイル指定：<Files ファイル名>...</Files>
   ディレクトリ指定：<Directory ディレクトリ名>...</Directory>
　　　　　 URLを指定：<Location URL>...</locarion>
```

■主な設定例

|書式|説明|
|:-----------:|:------------:|:------------|
|Server Tokens Prod/Major/Minor/Min/OS/Full|HTTPヘッダに出力される情報|
|ServerRoot パス|httpdが利用するトップディレクトリ|
|ServerName サーバーのホスト名|Apacheが稼働しているホスト名|
|ServerAdmin メールアドレス|サーバー管理者の連絡先メールアドレス|
|StartServers 子プロセス|起動時の子プロセスの数|
|MinSpareServers 子プロセス|待機子プロセスの最少数|
|MaxSpareServers 子プロセス|待機子プロセスの最大数|
|ServerLimits 子プロセス数|生成されるプロセスの上限を指定|
|MaxClients 同時接続数|同時接続数(Apache2.2)|
|MaxRequestWorkers 同時接続数|同時接続数(Apache2.4)|
|Timeout タイムアウト時間|クライアントからの接続がタイムアウトになるまでの時間|
|KeepAlive on/off|TCO接続をキープするか否か|
|MaxKeepAliveRequests |上限リクエスト数|
|KeepAliveTimeout|タイムアウト時間|
|Listen [IPアドレス;] ポート番号|待ち受けるポート番号（デフォ80）|
|user|httpd子プロセスの実行ユーザー（apache,nobodyなどの一般ユーザーで動作）|
|group|httpd子プロセスの実行グループ|
|DocumentRoot ディレクトリパス|ドキュメントルートとなるディレクトリ|
|userDir 公開ディレクトリ/disable|一般ユーザの公開ディレクトリを指定|
|DirectryIndex インデックファイル名|インデックスとして返すファイル名|
|ErrorLog|ログファイルのパス|
|LogLevel ログレベル|エラーログのログレベルを指定|
|LogFormat "フォーマット" 書式|記載する項目と書式名を定義|
|CustomLog ログファイル名 ログ書式名|ファイル名と、LogFormatで定義した書式を指定|
|HostnameLookups on/off|DNSの逆引きからホスト名を取得するか|
|Alias ディレクトリ パス|ドキュメントツリー以外の場所を参照させる（サーバー側が処理）|
|rediret|指定したURLへて転送（クライアント側が処理）|
|ScriptAlias|CGIスクリプト用ディレクトリの指定|
|ErrorDocument エラーコード ファイル名/文字列/URL|エラーの発生時に表示されるドキュメントor文字列|
|options|ディレクトリごとにオプション異能を設定|
pycharmでpip installできないとかimport sslできない対策

◆前提
Windows: Windows 10 Home
anaconda: Anaconda3-2020.07-Windows-x86_64.exe
pycharm: PyCharm 2020.2.2 (Community Edition)
でpythonを利用

◆困った現象
(a) pycharmで
File -> Settings... -> Project: xxx -> Python Interpreter -> + -> Available Packagesが表示されない
(b)Dosプロンプトでpip install xxxすると
WARNING: pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available.
と言われインストールできない
(c)Anacondaプロンプトでpip install xxxは正常にインストールできる。
(d)Dosプロンプトからpythonを実行した対話型pythonシェルで、
\>\>\> import ssl
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\xxx\anaconda3\lib\ssl.py", line 98, in <module>
    import _ssl             # if we can't import it, let the error propagate
ImportError: DLL load failed while importing _ssl: 指定されたモジュールが見つかりません。
のようになる。
(e)Anacondaプロンプトから実行した対話型pythoシェルではimport sslしてもエラーがでない。

◆対策
libsslxxx.dllやlibcryptxxx.dllを読み込む場所がまずいみたいです。
環境変数のPATHを修正しましょう。

(1)Anacondaプロンプトで正常に動作するPATHを確認
Anacondaプロンプトで
\> echo %PATH%
これをメモする。
長いので
\> echo %PATH% > path.txt
とかして後でtxtファイルみるのもよし

(2)環境変数に上記のPATHを設定
環境変数の設定の仕方は割愛。
anacondaをインストールする際に環境変数を設定するを選択した場合は自動で追加されるので上記のような現象は発生しないと思います。
[Rails6]devise + paranoia + MySQL8系で実現するユーザー論理削除と一意制約の両立

# はじめに
タイトルの環境にて、ユーザーの論理削除および一意制約の両立にかなり苦戦したので
解決方法を軽くまとめます。
deviseの一意制約をオーバーライドして独自の制約を持たせる方法などが出ていましたが
どれも面倒でもっと簡単にできないかと思い調べた結果です。

# やりたいこと
先の環境にて(特にMySQL)、deviseデフォルトのユーザー物理削除ではなく論理削除を行い、
かつ退会したユーザーが同じアドレスやIDで再登録できるよう一意制約を担保したい。

### PostgreSQL, SQLite
こちら2つのDBでは部分indexを活用することで簡単に実装できるそうです。
[・実装記事](https://qiita.com/QUANON/items/40f21e9f697221d03e60)
[・add_index仕様1](https://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/add_index)
[・add_index仕様2](https://railsdoc.com/migration#add_index)

この、部分indexをMySQLは採用しておらず簡単に扱えない模様。
(情報が5.7ばかりだから8系では採用しているのか？どちらにしろRailsのmigrateからは無理そう)
軽くアンチMySQLになるかも…

# 結論(MySQLでの実現方法)
この記事みたらいけると思います。
https://vanhuyz.com/how-to-apply-unique-restriction-with-soft-delete-in-rails/
1. deleted_atカラムを追加後、
2. deleted_atにNullではなくアクティブユーザーにも特定の値をもたせることで、
　2カラムでのUNIQUEを実現し(dleted_atがNULLだと実現しない)
3. シンプルに論理削除と一意制約を両立

最高に優良な記事をどうもありがとう！！

# 実装
[環境]
- Ruby：2.6.6p146
- Rails：6.0.3.3
- MySQL：8.0.21
- mysql2：0.5.3
- devise：4.7.2
- paranoia：2.4.2

## 準備
環境構築やdevise, paranoia導入等の基本部分の実装は他に沢山の記事が出ているので割愛します。
devise標準の会員登録・退会機能等が動作する前提です。


## 1. 設定ファイルparanoia.rbを作る

```config/initializers/paranoia.rb
# ユーザー退会後の論理削除・一意制約を両立させるための処理
# 非削除レコードはdeleted_at = '0000-01-01 00:00:00'
# 削除済みレコードはdeleted_at != '0000-01-01 00:00:00'
Paranoia.default_sentinel_value = DateTime.new(0)
```

## 2. migrationでのindex変更
#### デフォルトのUsersテーブルのindexを変更するマイグレーションファイルを作成

```Example
rails g Change_Index_To_Users
```

#### 作成したマイグレーションファイルを以下のように変更
一度デフォルトで作成されたemailインデックスを削除し
新たに[email, deleted_at]でのuniqueインデックスを作成

```db/migrate/202009220000000.rb
class ChangeIndexUniuqueToUsers < ActiveRecord::Migration[6.0]
  def change
    remove_index :users, :email
    add_index :users, [:email,:deleted_at], unique: true
  end
end
```

#### migration
```
rails db:migrate
```

## 3. Validation追加

#### アプリケーションレベルでのバリデーションを追加

```app/models/user.rb
class User < ActiveRecord::Base
  acts_as_paranoia
  validates :email, uniqueness: { scope: :deleted_at }	
```

## 4. 動作確認
railsコンソールもしくはrailsサーバーを立ち上げ実際にユーザー作成、退会、同アドレスでの再登録を実施してDBを確認します。
![論理削除_一意制約.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316090/c1b4956c-0742-a367-ca83-5ecfc656a3df.png)

無事同じアドレスでの再登録に成功しました！！
他何かいい方法などあれば是非教えてくださいませ～！
第1回：Web開発の変遷

[目次：Webアプリ開発ロードマップ](https://qiita.com/radiance1104/private/7c5b04ce4cf8ec7bd708)

# 第1回：Web開発の変遷
本ロードマップでは、Webアプリの開発を実践し学びます。
最後まで実施することでWebアプリのフロントエンドはどういうものか、
バックエンドではどういったことが行われているのかを具体的に理解することを目的とします。

## ブラウザの動き
ブラウザはhtmlファイルを読み込むことで動作します。
htmlファイルにJavaScriptファイルやcssファイル、画像や音声などのメディアファイルのリンクが書かれていれば読み込んで動作させます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202428/7a424862-2cf8-e5d6-1318-daad9214b239.png)

htmlファイルは文書のファイルです。
使い慣れた`<div>`タグを使い続けるようなことはせず、文章や意味合いを考えながら正しい文書を記載しましょう。
詳しくはSEO対策などを調べてみてください。

cssファイルはhtmlの見た目を飾るためのスタイルを記載するファイルです。
色をつけたり、マージンを取ったり、見た目を飾ることができます。
スタイルはhtml内に書くこともできますが、cssファイルに分離するようにしましょう。

jsファイルはhtmlに動きをつけるためのJavaScriptを記載するファイルです。
ページ内の要素を変更したり、サーバ通信をしたりすることができます。
JavaScriptはhtml内に書くこともできますが、jsファイルに分離するようにしましょう。

## Web開発の変遷
Web開発の変遷を簡単に説明します。

### 各々でシンプルなページを提供していた
インターネットをダイアルアップで行っていた頃は、静的なページか2chのような掲示板サイトが主流でした。
各々がサーバを構築しプログラムも各々の書きたいように書いていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202428/a5e95967-8385-c690-db63-0fac5c6de7c3.png)
WEBサーバはapacheやIISのようなHTTPリクエストを受け付け、ファイルを返したりperlやPHPなどのソースコードを実行するプログラムです。

### MVCデザインパターンフレームワークの導入
MVCデザインパターンというものが流行り始めます。
フレームワークを利用することで、どのサイトでも作っているような共通部分はフレームワークが提供し、各々で作らなくて良くなりました。
また、MVCデザインパターンが導入され、開発者に作り方を明示し、開発ルールから逸脱しにくくさせました。
開発を迅速に行うことができ、新しく入ってきた開発メンバにも読みやすいという利点があります。
ショッピングサイトなど、現在も使っているサービスは多いです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202428/08706bab-895a-edb7-77f4-6b9649eaa418.png)

#### MVCデザインパターンについて
MVCはModel・View・Controllerの頭文字を取っています。
Modelは、データベースを読み書きする役割を持ちます。
Viewは、クライアントに返すhtmlを作成する役割を持ちます。主にhtmlのテンプレートです。
JavaのアプレットのようなスクリプトレットがViewにあたります。
Controllerは、HTTPリクエストを受け付け、リクエストをハンドリングする役割を持っています。
Modelを使ってデータの読み書きを行ったり、Viewを使ってHTMLを作らせたり、作成したデータをクライアントに返信したりします。

#### フレームワークについて
JavaならStrutsやSpring、PHPならCakePHPやZendFramework、RubyならRuby on Rails等々、言語毎に多くのフレームワークが乱立しています。

### Webアプリの導入
上記までの開発ではユーザ毎の画面構築をサーバ側で行っていました。
また、サービスを運用する上での進行形のデータ（ログインが通った等）をセッションという形でサーバのメモリに保持していました。
それではクライアント数が多くなってくるとサーバは負荷に耐えられません。
Webアプリでは、アプリケーションとして動作する上で必要なファイルを最初にダウンロードさせ、Webアプリが動作する上で必要になるデータの遣り取りのみサーバにアクセスするようになりました。
画面の更新はWebアプリ内のJavaScriptプログラムで行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202428/a0bcc2d3-2c3a-b5ce-aa08-f419d3a39e0c.png)

## 最後に
今回はWeb開発の変遷について記載しました。
次回からはAngularを利用して実際にフロントエンドを作成していきます。
ExcelVBAを使った社内開発の心構え～未来の自分と後から来る誰かのために～

皆さん、4連休最終日はどうお過ごしですか？
TwitterやQiitaを見てると休日なのに熱心に学習されてる人がいて本当に感心です！

今日は社内で自動化ツールなどを開発しているワタシが、入社してからの失敗談をふまえながら
社内開発のやり方や大切にしていることを書きたいと思います。

VBAは誰もが勉強しやすくいろんなものを作れるだけに不都合も生まれやすいです。
善意で作ってあげたらそれが後々自分の首を締めることになるなんて事も…
VBAを勉強したての人や、これからVBAの開発の仕事につく方の参考になればいいなと思います。

#ワタシの仕事内容
社内の各部署から業務自動化の依頼を受けて、VBAでツール開発をするお仕事です。
社内の担当範囲となる部署は広範囲で、自分が所属する部署以外の部署の依頼も受けます。

#入社時の失敗談
###開発案件が話しかけられたベースで増えていく
「またマクロを作れる人が入社してくる」という噂が広がっていたのか入社してからすぐ、
「このマクロ直してください～」
「このマクロ作ってください～」
と知らないおじさんがどんどん話しかけてきます。
「まず順番にどこの誰か名乗ってください」状態。笑
仕事はどんどん溜まっていきました。

入社から2ヶ月後にVBA開発の同僚が入ってくるのですが
先にいたワタシの方が目についてしまうのかワタシの方に依頼が集中してしまってました。
同じ派遣社員に派遣社員が業務をふるのも忍びなくて、上司と相談する時間も取れないまま、
結局、ワタシの抱える案件の数は減りませんでした。

###あると思ってたらない！！
弊社はワタシが入社する前にも何人かVBA開発者がいたそうなんですが、入っては辞めを繰り返し、ワタシは何人目かのVBA開発者だったみたいです。
入社時は前任者からの引き継ぎ期間はなく、上司というポジションの方は居たものの一人で業務を始める感じでした。
__そして面談で存在するかと確認した「引き継ぎ資料」は存在しませんでした！__
（話が違うじゃないか！）
__なにも過去の資料がない__
__ツールの利用者も仕様を理解してない__
そんな状態から前任者の開発したツールのエラーをひたすら直す、というのが
初めてのお仕事になりました。

「うーん…まあ、あるあるだよね:joy:」

#開発業務を業務改善
流れに身をまかせるままに3ヶ月…
「このままでは五月雨式に仕事は降ってくるし、同僚との業務バランスも悪い。将来、開発したツールのメンテナンスもやりにくい。野良マクロがどんどん増えていく気がする！！」
当時の現状に危機感を感じて、自分たちの業務の交通整理をまず行うことにしました。

###上司を防波堤に！
まずは、話かけられたベースでなんとなく案件が増えていくという状況を変えるべく、
依頼を受けて整理し、担当を決めて割り振ってもらえるように上司に相談しました。
同僚B子さんも同じように危機感と不満を抱いていたらしく一緒に上司に相談しにいってくれました。
上司と__依頼発生から案件クローズまでの流れ__を相談して以下の図のようにしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700414/9d8279eb-341b-af35-646e-83e19ee104f1.png)
上司も面倒くさそうにしてましたがワタシ達の意見を取り入れてくれてルール決め出来ました。
まあ、今も上司は防波堤の役割を完璧に出来てるわけではないのですが、防波堤がいるといないでは全く違います！
###全社員に告ぐ！まずは自分たちで考えたまえ！！
話しかけられたベースでの依頼では、
__「この作業さ、なんか時間かかってるからいい感じにして」__というようなざっくりした相談内容の人もいて
業務コンサル的なところから入らなければいけないこともありました。
そうするとそのチームの業務内容を把握することから始まり、一つの案件にかなり時間も要します。
開発者2人でこなしていくのは大変なので、
__大体の仕様は自分たちで考えてください。話はまずそこからです！__というスタンスにすることにしました。

上の図にもあるように、ざっくりしすぎたゆるゆる依頼は上司のとこで止まることになっているか、もしくは上司が相談にのってあげるということにしました。
そのおかげで私達開発者は__作ることに注力できる__ようになりました。

#社内開発でのワタシ流ルール
以下は開発をするときに絶対にやるようにしていることです。

###仕様書・設計書は必ず残す！
当たり前のことかもしれませんが、弊社では過去、これが全く出来ていないようでした。
作ったらそれで終わりという感じです。
自分だけで使うならなくても良いのですが、社内開発においては、__作り出すなら最後まで面倒みれるようにする__のが開発者の責任だと思います。
またワタシはどんな小さいマクロでも仕様書は開発者のためだけでなく、利用者本人にも仕様を理解してもらうために必要だと思います。

あと、すぐ「バグだ！」といい出す人に、「仕様です。仕様書に書いてます。」とちゃんと言えます。
仕様書・設計書は未来の自分を守ってくれます:laughing:

###コードはパスワードで守れ！
自分が新規開発したツールのコードはVBEでパスワードを設定し、無断で転用、編集が出来ないようにしています。
以前、自分が開発したコードが他人によって色々付け加えられその後の改修が非常に大変でした。
たぶん良かれと思ってやったことなんでしょうがやはりワタシはコードをいじられるのがすごく嫌です。
依頼者には「改修はこちらですべて行いますのでコードは勝手に改修しないでください。」と伝えてます。

もちろん、自分がいつか辞めるときはパスワードを引き継ぎますし、非常時に備えて信頼できる人にだけ共有しています。
VBAはパスワードをかけても解除するコードなんかもネットで拾えてしまうので完全なガードではないのですが、大体の人はパスワードをかけることを理解してくれると思います。

###人のコードにはむやみに手を出すな！
始めの方にも書きましたが入社時の初仕事は前任者が作った何の資料もないツールのエラー修正でした。
他人が書いたコードを修正していくのはとても時間がかかってしまい、ワタシにとって普通に苦痛で
__新規で作成し直したほうが早い__と気づきました。

それからは、
__むやみに他人のコードを修正せず、新規開発を相談する__
__ツールのメンテナンス対応を他人から引き継ぐときは仕様書、設定書を必ず作ってもらう__
この2つを守るように上司に伝えました。

#最後に
今回書いた内容は上司や同僚に理解が得られないとできないかもしれません。
会社によっては「こうしてくれないと出来ません。」という意見さえ言えない会社もあると思います。

未来の自分と後から来る誰かが仕事しやすくできるようにするためにも改善することは必要だと思います。
業務改善のためのワタシたちだからこそどんどん良くしていきたいです。
上司への要望やもっとこうしたいという改善はまだまだたくさんあります。
ワタシの模索はつづきます。

もしよかったら社内開発で同じような経験やその他の改善策を行った方いらっしゃいましたら
教えて下さい:blush:

最後まで読んでいただきありがとうございました！！
クラウドの概念

#【クラウドの概念】

ポートフォリオにAWSを本格的に組み込んでいこうと思っているので、
その予備知識としてクラウドについて簡単にまとめました。

##クラウドコンピューティング

インターネット上にアクセスして、物理的に見えないコンピュータを利用すること。

ユーザーがインフラやソフトウェアを持たなくても、インターネットを通じて、サービスを必要な時に必要な分だけ利用できる特徴がもつ。

○○aaS（as a Service） と呼ばれるようなクラウドコンピューティングサービスがいくつかある。

##仮想化技術

仮想化技術とは、物理的なコンポーネントを、複数の論理的コンポーネントに分割して使用する技術である。
クラウドコンピューティングは、インターネット技術や仮想化技術が基本になっている。

１台の物理的コンピュータを、あたかも何十台といったコンピュータがあるかのように、論理的に分割して、何十台といったコンピュータを共有する。
これによって、提供者と利用者のwin-win関係ができる。

##○○aaS（as a Service）の主要例

・IaaS(Infrastructure as a Service)
　　サーバー（インフラ）を提供するクラウドサービス

・PaaS(Platform as a Service)
　　開発環境を提供するクラウドサービス

・SaaS(Software as a Service)
　　ソフトウェアを提供するクラウドサービス

##余談

Git,GitHubを使用せずにSVNを使用しているようなレガシーSIerでもAWSは使用されています。（私個人の範囲でのお話です。）

クラウドの知識、主にAWSの知識はITの世界では既に常識になっていると思います。
今後も引き続き、業務内外でしっかりAWSを学んでいきます。
Rustのプリミティブ型まとめ

# TL;DR
- Rustの**`プリミティブ型`**について、まとめてあります。

# 実行環境
**ハードウェア**

|項目|情報|
|:---------|:------------------|
|OS|macOS Catalina(10.15.4)|
|ハードウェア|MacBook Pro (13-inch, 2019, Two Thunderbolt 3 ports)|
|プロセッサ|1.4 GHz クアッドコアIntel Core i5|
|メモリ|16 GB 2133 MHz LPDDR3|

**ソフトウェア**

|項目|情報|
|:---------|:------------------|
|言語|Rust(1.46.0)|

# プリミティブ型
## bool
> The boolean type.

`true`と`false`で値を決定します。（Pythonでは、`True`と`False`）

```rust
fn main() {
    let bool_true = true;
    let bool_false = false;
}
```

## fn
> Function pointers, like fn(usize) -> bool.

## pointer
> Raw, unsafe pointers, *const T, and *mut T.

## reference
> References, both shared and mutable.

## slice
> A dynamically-sized view into a contiguous sequence, [T]. Contiguous here means that elements are laid out so that every element is the same distance from its neighbors.

## unit
> The () type, also called "unit".

## 文字列系
### char
> A character type.

### str
> String slices.

**`str`**は、**文字列スライス**と呼ばれており、メモリ上に存在する文字列データのスタート地点と長さを示しています。そのため、文字列自体の変更は出来ません。
変更可能な文字列型は、標準ライブラリ提供型の**`String`**です。

Pythonから流れてきた身としては、ここの使い分けのイメージがあまり分からない。。。


## 集合系
### array
> A fixed-size array, denoted [T; N], for the element type, T, and the non-negative compile-time constant size, N.

配列は、特定の型の値を連続に収めてある集合です。配列のサイズはコンパイル時に定まっている必要があります。そのため、Pythonの`.append()`や`.extend()`などを使って配列サイズを変更することは出来ません。

内部の値にアクセスする際には、`[]`を使います（Pythonなどと同じ）。また、配列を参照するときには、自動的にスライスとして扱われます。

配列それ自体には、イテレーターとしての機能を持ち合わせていないため、for文などで使用する場合には、**`iter()`**メソッドを使ってあげる必要があります。配列サイズが**32個以下**の場合には、**`IntoIterator`**で実装することも可能です。（なんで32個という制限があるんでしょうか。。。）

```rust
fn main() {
    let array_a: [i32; 6] = [0, 1, 2, 3, 4, 5];
    for x in array_a.iter() { }  // OK
    for x in array_a { }         // コンパイルエラー

    let array_b: [i32; 32] = [0; 32];
    for x in &array_b { }        // OK

    let array_c: [i32; 33] = [0; 33];
    for x in &array_c { }        // コンパイルエラー
}
```

### tuple
> A finite heterogeneous sequence, (T, U, ..).

`tuple`は生成された際に格納された値の型を全て含めて一つの型を構成します。そのため、**一度生成したタプル内の一部の型だけ変更することは出来ません。**内部の値にアクセスする際には、`.0`といった形式で指定します。

```rust
fn main() {
    let mut tuple = ("hello", 5, "c");
    
    tuple.0 = "hoge";  // 出来る
    tuple.0 = 0;       // コンパイルエラー
}
```

## 数値系
### 浮動小数点
#### f32, f64
> The XX-bit floating point type.

浮動小数点型。


### 整数
#### i8, i16, i32, i64, i128
> The XXX-bit signed integer type.

符号付き整数型。

#### u8, u16, u32, u64, u128
> The XXX-bit unsigned integer type.

符号無し整数型。

#### isize
> The pointer-sized signed integer type.

ポインタサイズの符号付き整数型。

#### usize
> The pointer-sized unsigned integer type.

ポインタサイズの符号なし整数型。


# 参考情報
- [Rust - Primitive Types](https://doc.rust-lang.org/std/index.html#primitives)
phpで切り捨て処理を行う際、floor関数とintキャスト処理のどちらを使うのが適切か

# やりたいこと

浮動小数点数の切り捨て処理を行い、float 型を int 型にしたい。

- `floor()` 関数
- intのキャスト処理

のどちらを利用するのが適切か。それとも、どちらでも差異はないのか。

# 結論

- `floor()` 関数は返り値として float型を返すため、int型でキャストする必要がある。
- マイナス値を扱う場合において、 `floor()` と `(int)` の結果が異なってくる。

そのため、

- -1.1を切り捨てした際、-1になるのが自然と感じるのであれば、`(int)$num`とする
- -1.1を切り捨てした際、-2になるのが自然と感じるのであれば、`(int)floor($num)`とする

という選択肢の、どちらかを選ぶのがよいと考えられる。

## マイナス値を与えたときの処理の違いについて

### `floor()` 関数の場合

> value をこえない最大の整数の値を返します。 floor() の返り値は float 型のままとなります。
https://www.php.net/manual/ja/function.floor.php

マイナス値 (例: `-1.1`) を与えると、`-2`を返す

```
<?php

$float_vals = array(1.1, 1.8, -1.1, -1.8, 0.05, -0.05);

foreach ($float_vals as $v) {
    var_dump(floor($v));
}
```
https://3v4l.org/UMDvF

```
float(1)
float(1)
float(-2) *
float(-2) *
float(0)
float(-1) *
```

### `(int)` キャストの場合
> float から整数に変換する場合、その数はゼロのほうに丸められます。
https://www.php.net/manual/ja/language.types.integer.php

マイナス値 (例：`-1.1`) を与えると、`-1`を返す

```php
<?php

$float_vals = array(1.1, 1.8, -1.1, -1.8, 0.05, -0.05);

foreach ($float_vals as $v) {
    var_dump((int)$v);
}
```

```
int(1)
int(1)
int(-1) *
int(-1) *
int(0)
int(0) *
```

https://3v4l.org/oD9Cm

### マイナス値の切り上げ・切り捨て処理には、4種類あるみたい

- 0への丸め
- 無限大への丸め
- 負の無限大への丸め
- 正の無限大への丸め

[wikipedia - 端数処理](https://ja.wikipedia.org/wiki/%E7%AB%AF%E6%95%B0%E5%87%A6%E7%90%86)
※ wikipediaまでしか確認していないため、本当にこのような用語が定義されているかについてはわかりません。参考程度にお願いします。

# floor() 関数はなぜ値をfloat型で返すのか

表すことのできる数値の範囲を大きく取るためです。

以下記事のコメントにて、説明がありました。
https://qiita.com/takepan/items/ff8bba234fdc93a32791#comment-50d3e6e3db22ffdd53da

floorは返り値として float を返すため、取り得る値の範囲が広くなります。
floatであれば格納可能な値 9223372036854775808 をintにキャストすると、オーバーフローを引き起こします。

実際にコメントのサンプルコードを実際に実行すると、以下のようになります。

```php
<?php
$large_number = PHP_INT_MAX + 100.1;
var_dump(sprintf('%.30f', floor($large_number)));
var_dump(sprintf('%.30f', intval(floor($large_number))));
?>
```

```
string(50) "9223372036854775808.000000000000000000000000000000"
string(51) "-9223372036854775808.000000000000000000000000000000"
```

https://3v4l.org/fjNMs

オーバーフローしました。

# おわりに

思いの外深い話題でした。
もっと掘り下げられそうなのですが、下書きが10件溜まってしまったため一度記事にします[^1]
[^1]: この記事を書くのに疲れて、結局今日書こうと思っていた記事は書けそうにない。。。あとおなかすいた
Github CLIでPRの情報を取得する方法

### Github CLIを使って，PRの情報（URL，PRなど）を取得するには，どうすればいい？
実は，`gh pr view`コマンドで，PRの情報を出力できます．下記の例は，masterブランチをheadとするPRの情報を出すためのコマンドを示しています．

```shell
gh pr view master > pr.txt
```

出力したPRの情報（pr.txt）は以下のようです．

```
title:	Publish 2020/09/23
state:	OPEN
author:	willsmile
labels:	Publish
assignees:	
reviewers:	
projects:	
milestone:	
number:	20
url:	https://github.com/willsmile/gatsby-willsmile-portal/pull/20
--
```

これらの情報から，必要なものを取り出すために，grepとawkコマンドを活用できます．
例えば，PRのurlを取り出したい場合，このような書き方で実現可能です．


```bash
gh pr view master | grep url | awk '{print $2}'
```

### それらの情報を取得したら，何かできるのか？
一番想像しやすい実用方法は，コマンドで特定のPRをマージするということです．
以下のは，筆者が作ったMakefileです．それを使うと，masterブランチからdeploymentブランチへのPRを作って（make pr-create），作ったPRをマージする（make pr-merge）という処理を自動化することが可能になります．

```Makefile
NAME := portal-deployer
work_branch := master
publish_branch := deployment
pr_label := Publish
pr_title = Publish $(shell date +%Y/%m/%d)
pr_url = $(shell gh pr view master | grep url | awk '{print $$2}')

## Create pull request for publishing in Github
.PHONY: pr-create
pr-create:
  gh pr create --title "$(pr_title)" --body "Generated by $(NAME)" \
  --base $(publish_branch) --head $(work_branch) --label $(pr_label)

## Merge pull request for publishing in Github
.PHONY: pr-merge
pr-merge:
  gh pr merge $(pr_url) --delete-branch=false --merge
```

注意事項：`pr_url = $(shell gh pr view master | grep url | awk '{print $$2}')`での`$$2`はタイポではなくて，Makefileの変数として使えるために意図的に書いたものです．
Railsで時刻だけを比較する方法（何時〜何時までの間は、みたいなやつ）

## 問題の背景
深夜営業で23時〜翌5時までは15%増し、みたいなやつをどう書くか？という話。
「23時〜翌5時」という条件を上手く判定したい。

## 解決方法
Time型で比較しようとすると日付情報まで入ってきて面倒くさいので、`strftime`と`to_i`で時刻部分だけ数値化してあげてから比較すれば良い。

```ruby
def late_time?(time)
  num = time.strftime('%H%M').to_i
  [*0..459, *2300..2359].include?(num)
end

target_time = Time.zone.parse('2020/9/1 23:00')
p late_time?(target_time) #=> true

target_time = Time.zone.parse('2020/9/2 0:00')
p late_time?(target_time) #=> true

target_time = Time.zone.parse('2020/9/2 4:59')
p late_time?(target_time) #=> true

target_time = Time.zone.parse('2020/9/2 5:00')
p late_time?(target_time) #=> false

target_time = Time.zone.parse('2020/9/2 6:00')
p late_time?(target_time) #=> false

target_time = Time.zone.parse('2020/9/2 22:59')
p late_time?(target_time) #=> false
```
Prophetお試し 備忘録

#概要
時系列データ処理の調べものをしてたときに[Prophet](https://facebook.github.io/prophet/)を見つけた。動作確認したときの備忘録
いつか使う気がするから…
- 実施期間: 2020年9月
- 環境：Ubuntu18.04LTS

##動作確認用Conda仮想環境作成
[Miniconda Install 備忘録](https://qiita.com/Ihmon/items/11074e1a4c0e397d934f)の手順で新しい動作確認用仮想環境を作成しておく
その上で必要な下記パケージをインストールする

Prophetは[Pystan](https://pystan.readthedocs.io/en/latest/installation_beginner.html)に依存しているのでPystanをインストールするが、Pystanも下記に依存するのでそれらを先にインストールする。
まず、c++コンパイラ周りのインストールを仮想環境で実行する（但し書き注意）

```terminal
conda install gcc_linux-64 gxx_linux-64 -c anaconda
```

新しいターミナルを開いて、環境変数の設定

```terminal
export CC=gcc_linux-64
export CXX=g++_linux-64
resorse ~/.bashrc
```

但し、"export -p"コマンドを実行し、いまの環境変数に上記の２つが含まれていたらコンパイラのインストールは不要

仮想環境のターミナルに戻り、以降全てのパケージをインストール

```terminal
conda install -c conda-forge arviz
conda install -c conda-forge pystan
conda install -c conda-forge fbprophet
conda install -c conda-forge plotly
```

ちなみにconda仮想環境で、fbprophetをpipでインストールすると、盛大にエラーが発生した
conda-forgeのパケージでインストールすること


##サンプルコードの実行
[Quick Start](https://facebook.github.io/prophet/docs/quick_start.html)のコードをJupyterで確認する。
はじめに、使用する[サンプルデータ](https://github.com/facebook/prophet/blob/master/examples/example_wp_log_peyton_manning.csv)をexample_wp_log_peyton_manning.csvとしてローカルに保存しておく
Prophetが扱うデータはPandas型であることが仕様

```python
import pandas as pd
from fbprophet import Prophet
df = pd.read_csv('/home/hoge/example_wp_log_peyton_manning.csv')
```

Prophetのモデルを定義し

```python
m = Prophet(daily_seasonality=True)
m.fit(df)
```

なお、Plotlyをインストールする前に上記を実行すると下記のエラーが出た
**Importing plotly failed. Interactive plots will not work.**
下記をインポートすることで[回避可能](https://github.com/facebook/prophet/issues/832)だが、Plotlyが原因で発生したのかわからない

```python
from matplotlib import pyplot as plt
from matplotlib.dates import MonthLocator, num2date
from matplotlib.ticker import FuncFormatter
```


予測結果（yhat）を入れるdataframeを作成し、そこに予測結果を入れる

```python
future = m.make_future_dataframe(periods=365)
future.tail()
forecast = m.predict(future)
forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail()
```

![Screenshot from 2020-09-22 16-17-29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205257/38dd5bed-062c-2ea2-50e4-f946390f8c7a.png)
予測結果（青線）と実際の値（ドット）が重ねて表示できる

```python
fig1 = m.plot(forecast)
```
![Screenshot from 2020-09-22 16-19-36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205257/51c6e26c-037b-2f02-84c3-2b7c53e044e7.png)
Plotlyを使うと、かっこいいインタラクティブなチャート表示が可能
（Plotlyって知らなかった）

```python
from fbprophet.plot import plot_plotly, plot_components_plotly
plot_plotly(m, forecast)
```

![Screenshot from 2020-09-22 16-22-52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205257/76096eb6-7066-3371-4538-41c30cb56548.png)

終わり
来週は[tslearn](https://tslearn.readthedocs.io/en/stable/)を試してみる
名著『リーダブル・コード』に書いてある事をサックリまとめてみる


###例えそれが一般的な名前の付け方だとしても、もっとわかりやすい変数名、関数名にすること

例えばループメソッドのeach do 〜 |f| とかの"f"は使いがちだけど、もし"f"だとか"h"だとかの意味を明示しない変数名だとあとからバグが発見しずらくなる。

tmpやfooなどよく使う名前でも、もっといい名前があるならキチンと意味をなす名前にする事。
Vuetifyのv-file-inputでのテキストファイル文字化け対応法

# 概要  
Vuetifyの`v-file-input`コンポーネントを使って、shift-jisの文字コードで保存したcsvやtextファイルを単純に読み込んだ場合、文字化けしてしまうのでその対応法についてまとめました  

# 方法  
文字化けへの対応方法としては文字コード変換を行う[encode.js](https://github.com/polygonplanet/encoding.js)を使用し、importしたテキストファイルのエンコード処理をしています。

# ソース  

事前にencode.jsはimportしてください。

```html:html
<div id="app">
  <v-app>
    <v-row>
      <v-col>
        <v-file-input
          label="テキストファイルを選択"
          @change="getFileContent"
        />
      </v-col>
      <v-col>
        {{ content }}
      </v-col>    
    </v-row>
  </v-app>
</div>
```

```js:js
new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  data () {
    return {
       content: ''
    }
  },
  methods: {
    async getFileContent (file) {
      try {
        const content = await this.readFileAsync(file)  
        this.content = content
        
      } catch (e) {
        console.log(e)
      }
    },
    readFileAsync (file) {
      return new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.onload = () => {
          //文字列をバイト列に変換  
          var sjisArray = new Uint8Array(reader.result);
          //encode.jsを使い、文字コードを自動判別しUNICODEへ変換
          var unicodeArray = Encoding.convert(sjisArray, {
            to: 'UNICODE',
            from: 'AUTO',
            type: 'string'
          });
          resolve(unicodeArray);
        }
        reader.onerror = reject;
        reader.readAsArrayBuffer(file);
      })
    }
  }
})
```

# 参考  
- v-file-input
https://vuetifyjs.com/ja/components/file-inputs/

- encode.js
https://github.com/polygonplanet/encoding.js
Phalcon+Vagrant+centos+xdebug+phpstormのデバッグ環境構築

###1.xdebugのインストール

```
vagrant ssh
sudo yum install --enablerepo=remi-php72 php-xdebug -y
```
Xdebugモジュールの場所を確認


```
sudo find / -name "xdebug.so"
```

php.iniの編集

```
sudo vi /etc/php.ini
```
最後に下記で追記
vagrant の場合はホストは10.0.2.2と設定

```
[xdebug]
zend_extension=/usr/lib64/php/modules/xdebug.so
xdebug.remote_enable=1
xdebug.remote_autostart=1
xdebug.remote_host=10.0.2.2
xdebug.remote_port=9001
xdebug.idekey="phpstorm"
xdebug.remote_connect_back=1
xdebug.remote_handler=dbgp

```



サーバー再起動

```
sudo systemctl restart php-fpm
sudo systemctl restart nginx
```


php.infoファイルを作り、info にはxdebugの設定関連が表示されたら成功


###2.phpstormの設定
特に挫折しているので要注意

1.言語設定にリモートを選択
![スクリーンショット 2020-09-22 16.21.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117741/c9f787f5-76e2-e323-fd87-cd8b6a2b3a21.png)

2.Debug設定：

![スクリーンショット 2020-09-22 16.22.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117741/2ff929c7-f27a-749a-7fce-f7003efac1e6.png)


IDE　keyとホストの設定（ホストはvagrant.fileに設定済）
![スクリーンショット 2020-09-22 16.23.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117741/6812dc5e-ed34-70cf-b05d-8e8c60b1fc1d.png)

3.serverの設定
特に注意するのはフォルダーのパスをマッピングすることで、

![スクリーンショット 2020-09-22 16.25.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117741/ad5768a4-6dd2-af09-0e45-08d78555fb58.png)

ローカルのパスはプロジェクトのルートで、publicではない。
![スクリーンショット 2020-09-22 16.26.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117741/f8550b71-4da9-646e-cda0-bc74ba9ff90d.png)

仮想環境のパスもルートです。
![スクリーンショット 2020-09-22 16.27.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117741/1c2ae679-cb93-3f98-a929-ef917547f5b7.png)

4.Debugの設定を完了：
remote debugを設定する
![スクリーンショット 2020-09-22 16.28.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117741/a8658e16-27d1-156f-528f-053bbece7b9e.png)

起動：
![スクリーンショット 2020-09-22 16.35.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117741/ea73a004-a02a-65f8-5440-e33f7e3ce8e9.png)
Raspberry Pi 4とArch Linux(64bit)で作るKubernetesクラスタ

# はじめに

Raspberry PiでKubernetesクラスタを構築する記事はたくさんありますが、備忘のため試行錯誤してうまくいった結果をまとめます。アプリケーションをデプロイして動かせるように、FlannelやIngressのインストールまでしました。
64ビット版のLinuxを使っているのがポイントです。

# ハードウェア

以下の部品を組み合わせて作成。

* Raspberry Pi 4 Model B/8GB 3台
* microSDカード 32GB 3枚
* ケース https://www.amazon.co.jp/dp/B07TJ15YL1
* USB電源アダプタ https://www.amazon.co.jp/dp/B00Z8Z7WEE
* USBケーブル 3本 https://www.amazon.co.jp/dp/B0748DTSD2

![Raspberry_Pi_Kubernetes.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16224/0f3f3c06-3f12-1a90-edf6-546ebb25a978.jpeg)

1台がマスターノードで2台がワーカーノード。
一番下の段にUSB電源アダプタを入れようとしましたがサイズが合わず入りませんでした・・・

# ソフトウェア

## Arch Linux

どうせなら64ビット版のOSを使いたいと思いいろいろ調べた結果、以下を参考にしてArch Linux ARMの64ビット版のインストールに成功。
https://raspberrypi.stackexchange.com/questions/111011/aarch64-or-armv8-with-arch-on-a-raspberry-pi-4
[ここ](https://olegtown.pw/Public/ArchLinuxArm/RPi4/rootfs/)からファイルをダウンロードして、後は手順通りにSDカードに書き込み。

今見たら[公式ページ](https://archlinuxarm.org/platforms/armv8/broadcom/raspberry-pi-4)に「AArch64 Installation」の記述があったので、今は公式のファイルを使ってインストールできそうです。

あとはWiFi接続など一通りのOSの設定を実施。

## Docker / Kubernetes

全ノードに必要なパッケージをインストール。AURのパッケージもインストールする必要があるので、AURヘルパーであるyayを利用。

```
$ yay -S docker kubelet-bin kubeadm-bin kubectl-bin ebtables ethtool socat conntrack-tools
```

マスターノードでkubeadmを実行してKubernetesのクラスタを構築。

```
# kubeadm init --pod-network-cidr=10.244.0.0/16
```

kubectlを使えるようにするための設定方法が表示されるので、表示されたコマンドを実行。
また、ワーカーノードをKubernetesクラスタに参加させる方法が表示されるので、表示されたコマンドをワーカーノードで実行。

## Flannel

全ノードにcni-pluginsをインストール。

```
$ yay -S cni-plugins
```

cni-pluginsのインストール場所が異なるため、Flannelがうまく動くようにシンボリックリンクを作成。

```
# mkdir /opt/cni
# ln -s /usr/lib/cni /opt/cni/bin
```

Flannelをインストール。vxlanをhost-gwに変更するとうまくいきました。

```
$ wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
$ cat kube-flannel.yml | sed "s/vxlan/host-gw/" > kube-flannel-hostgw.yml
$ kubectl apply -f kube-flannel-hostgw.yml
```

## MetalLB

ServiceのタイプをLoadBalancerにした場合、クラウドでは外部公開用のIPアドレスを自動で割り当ててくれるようですが、オンプレではうまく動きません。そのため、オンプレでも外部公開用のIPアドレスが自動で割り振られるようにMetalLBを[手順](https://metallb.universe.tf/installation/)に従ってインストール。
[ここ](https://metallb.universe.tf/configuration/)を参考にL2モードの設定をして、LAN内のある範囲のIPアドレスが割り当てられるようにしました。

```
$ kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.3/manifests/namespace.yaml
$ kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.3/manifests/metallb.yaml
$ kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"
$ wget https://raw.githubusercontent.com/metallb/metallb/main/manifests/example-layer2-config.yaml
$ mv example-layer2-config.yaml metallb-config.yaml
$ vim metallb-config.yaml
$ kubectl apply -f metallb-config.yaml
```

## Ingress

リバースプロキシの構成を作れるようにするため、NGINX Ingress Controllerを[手順](https://kubernetes.github.io/ingress-nginx/deploy/)に従ってインストール。

```
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.34.1/deploy/static/provider/cloud/deploy.yaml
```

# さいごに

アプリケーションをデプロイして動かせる状態になったので、自宅サーバで動かしているアプリケーションはこのKubernetesクラスタに移すようにしていきたいです。
Ruby学習5

#メソッドとか色々5
現在、Ruby技術者認定試験silverを取得するべく勉強中です。
言語に対する理解がまだまだなので、基本的な事からアウトプットしていきます。

# 小数の切り捨て、切り上げ、四捨五入
###ceil = 小数点以下切り上げ

```ruby
irb(main):001:0> 1.9.ceil
=> 2
```

###floor = 小数点以下切り捨て

```ruby
irb(main):004:0> 1.9.floor
=> 1
```
###round =小数点以下四捨五入（厳密には最も近い整数を返す）

```ruby
irb(main):006:0> 1.5.round
=> 2
irb(main):007:0> 1.4.round
=> 1
```

##joinメソッド
配列の要素同士を、引数指定した文字で区切り、結合した文字列を返す。

```ruby
arr = ["無駄", "無駄", "無駄", "無駄ァ", "ッ!"]
p arr.join("!")

=> "無駄!無駄!無駄!無駄ァ!!"
```

##injectメソッドと条件演算子
模擬問題で詰まったところ。まずはコードから。

```ruby
numbers = [3,89,40,39,29,10,50,59,69]
num = numbers.inject do |i,j|
  i > j ? i : j
end
```

injectメソッドは、引数をつけた場合は引数を一つ目のブロック変数(i)に、配列の最初の要素を二つ目のブロック変数(j)に代入。do以降の式を実行。実行が終わったら計算結果をiに、配列の次の要素をjに代入。式を実行。以降繰り返して、配列の要素が最後までいったら結果を返す。引数をつけなかった場合は配列の最初と２番目の要素を代入してから開始する。

条件演算子

```ruby
 i > j ? i : j
```
真偽を問う左辺(?から左側)の式を実行後、trueの時に返す値を(:)の左に、falseの時に返す値を(:)の右に置く。なのでこの式は、「iがjより大きい場合はiを、そうでない場合はjを返す」という意味。
で、
これをinjectメソッドで実行するので、「配列の要素を比較して、大きい数値を残し、最終的に配列内で最も大きい数値を返すコード」になる。

##stepメソッド

```ruby
1.step(100, 2) do |n|
  puts n
end
```

レシーバの数値を起点として、第二引数の数値刻みで、数値が第一引数になるまで、式を実行する。ブロック変数nにはレシーバの数値が初期値として入る。コードを翻訳すると、
「レシーバの数値「1」を出力(puts n)した後、数値に2を足し再度実行(3を出力)。この数値が100に到達するまで実行する。」
今回は奇数が出力されていき、最後は99。2を足すと100を超えてしまうため、ここで終了。やってる事自体は、1から100までで奇数の出力。
iOS 14 Wi-Fi MAC address randomization

# Summary
This is a report of Wi-Fi MAC address randomization functionality of iOS14.

- iOS 13: Only Probe Request MAC address is randomized.
- iOS 14: Randomized Probe Request(Broadcast) MAC address is also used by auth/assoc/EAPOL.
- Roaming to identical SSID does not modify MAC address.
- Roaming to identical AP with different SSID modify MAC address.
- Disable/Enable Wi-Fi and rebooting iPhone does not modify MAC address.

# Detail
Detail of the investigation.

Assume the STA connected to AP1 (SSID=ssid1, BSSID=bssid1) with MAC address A.

Connected to AP2 with identical SSID (SSID=ssid1, BSSID=bssid2)
=> MAC address stays on A

Modify SSID of AP1 and connect to AP1 (SSID=ssid2, BSSID=bssid1)
=> MAC address is modified to B

Restore AP1 configuration (SSID=ssid1, BSSID=bssid1)
=> MAC address backs to A

Disable/Enable Wi-Fi and connect to AP1 (SSID=ssid1, BSSID=bssid1)
=> MAC address stays on A

Reboot iPhone and connect to AP1 (SSID=ssid1, BSSID=bssid1)
=> MAC address stays on A

※ Tested on iPhone 8 Plus with iOS 14
JAZUG(Japan Azure User Group)10周年記念！じゃずにゃん を作ってみた。

こんにちは、JAZUG(Japan Azure User Group) 10周年記念イベントでお話しした「じゃずにゃん」の使い方です。JAZUGのキャラクターは[Rinさん](https://www.rin.sc/?portfolio=%e3%80%8cjapan-windows-azure-user-group%e3%80%8d%e3%82%ad%e3%83%a3%e3%83%a9%e3%82%af%e3%82%bf%e3%83%bc)に描いて頂いたキャラクターです。

https://jazug.connpass.com/event/186235/

イベントのYouTube動画はこちら
https://youtu.be/a8KfSSF5q0I

JAZUG の LT や セッション で話したい方は、是非、PowerPointスライドでご利用ください。じゃずにゃん も応援してます！

# じゃずにゃん
JAZUG10周年記念に、2DイラストのJAZUGキャラクターを3Dモデル化しました。その名も「じゃずにゃん」。現段階では4種類のアニメーションを用意しています。(現在は5種類です(9/25))

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32580/9efb0a58-3934-90fe-2c63-0fc49f50da14.png)

3Dモデルなので裏側だって見ることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32580/ec26a5d3-bd63-a476-4baf-3a9753cb6af5.png)

# PowerPointのスライドに召喚してみる
以下のGithubからglTFのglb形式ファイルをダウンロードしてください。

https://github.com/KentaroAOKI/jaznyan/raw/master/jaznyan.glb

ダウンロードしたファイルをPowerPointにドラック＆ドロップするとスライドに「じゃずにゃん」が召喚されます。
「アニメーション」メニューからシーン（4種類）を選ぶことにより、歩いたり、踊ったり、動きを変えることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32580/017a6fbc-eb16-2e67-d39b-4765f081bee9.png)

マスタースライドにも入れることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32580/aeaca5da-b7a7-ca93-d2c1-d878540baf5c.png)

「画面切り換え」の「変形」では、スライドをコピーしたり、じゃずにゃんをコピーすることにより、スムーズにアニメーションしながら次のスライドに移ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32580/fc49e0d5-2845-38b1-5a3d-8b664ba50068.png)

# Unity のシーンに召喚してみる
以下のGithubからfbx形式ファイルをダウンロードしてください。

https://github.com/KentaroAOKI/jaznyan/raw/master/jaznyan.fbx

ダウンロードしたファイルをUnityのAssetにドラック＆ドロップした後に、InspectorのRigからAnimation TypeをHumanoidにして下さい。選択したらApplyを押します。これで使えるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32580/1b5dfb08-d2f8-b130-83d1-a21aceb95a0c.png)

試しにジャンプするアニメーションをTimelineで割り当ててみると、じゃずにゃんがジャンプしました。
あとは、[SunnySideUp UnityChan](https://unity-chan.com/contents/news/3878/)のアニメーションを使って、じゃずにゃんが踊ることも確認してます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32580/0f709d04-baca-5fc9-79e5-60d0aaccc37c.png)

これで、Unity ML-Agentsを使った強化学習をAzureで実行するデモなど、楽しくできそうですね。もしかしたら、Azure Project Bonsaiにも使える日が来るかもしれない…。

# さいごに
JAZUG 10周年！おめでとうございまーす！今後も、ゆるふわで楽しくやっていきたいと思います。引き続き、よろしくお願いします！

あと、JAZUGではAzureに関するLT枠なども用意していますので、是非応募してみてください。じゃずにゃんと一緒に登壇してみよう！

JAZUGの参加はこちらから～。
https://jazug.connpass.com/
https://www.facebook.com/jazug.jp

ではー。
Nuxtjsで静的サイト運用時、レイアウト変更したページで2回マウントされてた問題

* WebサービスのフロントでNuxtjs(v2.14)を使った。
* 複数のlayoutを使い分ける必要があったため、ファイルを作成し、pageファイル内で指定した。
* page内で、マウント時に外部サービスへデータをpost(記録)する処理を書いた。
* 公開はnuxt generateで静的なサイトを作成し、それをアップした。
* 実際に公開ページにアクセスして処理を走らせてみる
* **外部サービスのログを見ると、データが2回処理されている？？？**←問題発覚
* PC、android端末では同様の現象は起きていなかった。

より細かく動作を見ていくと、
iPhone端末のsafariで
Nuxtjsのライフサイクルが二周している。

Nuxtjsのgithubを見ると過去に似たような問題が挙がっていた。
https://github.com/nuxt/nuxt.js/issues/5703

記事中であるように、
「this.$nuxt.layoutName」が一旦defaultで表示されたのち、
指定したものに切り替わっていた。

試しにレイアウトの指定を解除、
または指定をdefaultにすると、問題は起きなくなった。

また、記事中にある「window.〇〇」に処理の経過を記録するやり方の流用で
非同期でのデータ取得をstoreに移し、
データ取得処理の進行状態をstateで記録するようにすると、
二回目の取得処理を回避できるため、
当面の問題(二重記録)はクリアできた。
（uglyな手法って書かれてるけど）

割とぶつかりそうなネタだけど、Qiitaで見当たらなかったので初投稿テストです。
iOS 14 での Wi-Fi MAC アドレスランダム化状況

English version https://qiita.com/masap/items/ac23ca133ee3845d4c24

# 概要
iOS14 から Wi-Fi MAC アドレスランダム化機能が変更されたので調査した。

- iOS 13: Probe Request 時のみ MAC アドレスランダム化を行っていた。
- iOS 14: Probe Request(Broadcast) に用いたものと同様の MAC アドレスで auth/assoc/EAPOL まで行うよう変更された。
- 同一 SSID 内のローミングであれば MAC アドレスは変わらない。
- 同一 AP への接続であっても SSID が変わると MAC アドレスも変わる。
- Wi-Fi の off/on や iPhone の再起動で MAC アドレスは変わらない。

# 詳細
調査の詳細は以下の通り。

SSID=ssid1, BSSID=bssid1 の AP1 に MAC address A にて接続したとする。

- 同一 SSID の AP2 (SSID=ssid1, BSSID=bssid2) に接続
=> MAC address A のまま

- AP1 の SSID を変更 (SSID=ssid2, BSSID=bssid1)
=> MAC address B に変わる

- AP1 の設定を戻す (SSID=ssid1, BSSID=bssid1)
=> MAC address A に戻る

- Wi-Fi off/on後 AP1 に接続 (SSID=ssid1, BSSID=bssid1)
=> MAC address A のまま

- iPhone 再起動後 AP1 に接続 (SSID=ssid1, BSSID=bssid1)
=> MAC address A のまま

※ iPhone 8 Plus の iOS 14 にて調査。
[Python] 累積和 ABC179D

#[ABC179D](https://atcoder.jp/contests/abc179/tasks/abc179_d)
素朴な解法としては、

```math
dp[i]:= マスiに到達するまでの操作列の個数
```
として、各場所 i についてすべての移動を試す動的計画法が考えられるが、時間計算量 $O(N^2)$ となります。更新式は次の通り。

```math
dp[i] = \sum_{v\in S} dp[i-v] \\
```

こんなときは累積和を用いて DP 高速化するのは定番ではある。まず、集合 S が K 個の区間からなることに着目すると、$\sum_{v\in S} dp[i-v]$ の部分は、K 個の区間についての「区間内の総和」を足し上げたものになることがわかる。
ここで一般に、配列 a の累積和を s としたとき、配列 a の区間 [l, r) の総和は s[r] - s[l] で表せることを思い出そう。よって配列 dp の累積和を sumdp とすると、更新式は次のように変形できる。

```math
dp[i] = \sum_{k=1}^K (sumdp[i-l_k+1]-sumdp[i-r_k]) \\
```
計算量は$O(NK)$となる。

（参考）[累積和を何も考えずに書けるようにする！](https://atcoder.jp/contests/abc179/tasks/abc179_d)

```python:サンプルコード
MOD = 998244353
N, K = map(int, input().split()) #Nはマス目の数、Kは区間の数(Kは10以下)
sec = [tuple(map(int, input().split())) for _ in range(K)]

dp = [0] * (N+1)
dp[1] = 1

sumdp = [0] * (N+1)
# i-l=1(現在いるマス)になったとき、dp[i]を1増やす
sumdp[1] = 1

# i=2からNまで順番にsumdp[i]を更新する
for i in range(2, N+1):
    # それぞれの区間で累積和sumdpによってdp[i]を更新する
    for l, r in sec:
        li = max(i - l, 0)
        ri = max(i - r - 1, 0)

        dp[i] += sumdp[li] - sumdp[ri]
        dp[i] %= MOD

    sumdp[i] = sumdp[i-1] + dp[i]

print(dp[N])
```
Optunaを使用したLightGBMハイパーパラメーター最適化やりかた調査ログ

# はじめに
最近JupyterLabを使って機械学習の勉強をやっている。
その中でGoogleでの検索結果が古かったOptunaのLightGBMハイパーパラメーター最適化についての調査を記事にしてみようかなと思いたった。

# 環境
- [jupyter/tensorflow-notebook](https://hub.docker.com/r/jupyter/tensorflow-notebook/tags/)
    - 上記Dockerイメージを起動時に`jupyter lab`を指定している
- optuna 2.1.0

# やったこと
## 最初にざっくりまとめ
- OptunaでLightGBMのパイパーパラメーターの最適化がしたい
- ググって出てきたコードをコピペしてもうまく動かない
    - なんかbest_paramsなんで引数ないぞと言われる
- 公式ドキュメントをあたる
- 公式ドキュメントのサンプルを試す → 動く
    - Githubから過去のサンプルを確認して1.3時点ではbest_paramsがあることを確認 1.4以降はなさそう
- 困った時は公式ドキュメントを読もうね
- ネットの情報と挙動が異なる場合はライブラリとかのバージョンを確認してみるのがいいよね

### OptunaでLightGBMのハイパーパラメーター最適化がしたい
まずググってみて出てきたのがこれ
[Optuna の拡張機能 LightGBM Tuner によるハイパーパラメータ自動最適化](https://tech.preferred.jp/ja/blog/hyperparameter-tuning-with-optuna-integration-lightgbm-tuner/)
以下上記ページ記載のコード抜粋

```py
booster = lgb.train(params, dtrain, valid_sets=dval,
                    verbose_eval=0,
                    best_params=best_params,
                    tuning_history=tuning_history)
```
上記を試してみたところ、以下のエラーが発生した。
`TypeError: __init__() got an unexpected keyword argument 'best_params'`

ふむ。`best_params`なんてキーワード引数ないよ。と言われている。

### 公式ドキュメント確認
引数が異なるってことはバージョン違いかな?と思ったので公式ドキュメントを確認してみることにした。
[Optuna](https://optuna.org/)
Githubへのリンクがあったのでそちらに移動
[optuna/optuna](https://github.com/optuna/optuna)

### 公式ドキュメントのサンプルを試す → 動く
[optuna/optuna](https://github.com/optuna/optuna)で`lightgbm`でリポジトリ内検索
[リポジトリ内検索結果](https://github.com/optuna/optuna/search?q=lightgbm)

検索結果からそれっぽいものないかなーと見てると[examples/README.md](https://github.com/optuna/optuna/blob/802d142a55f14b3b8abc3044da5a30411530d517/examples/README.md)がサンプルっぽかったのでそれを確認。

[examples/lightgbm_tuner_simple.py](https://github.com/optuna/optuna/blob/802d142a55f14b3b8abc3044da5a30411530d517/examples/lightgbm_tuner_simple.py)

```py
    # 引用元を比較しやすいように一部改行してます
    model = lgb.train(
        params, dtrain, valid_sets=[dtrain, dval], 
        verbose_eval=100, 
        early_stopping_rounds=100
    )
```

こちらには`best_params`がないので現行Verはこれっぽい。
上記で試してみたところ、無事動いた!やったぜ。

ちなみに確認したところ、[v1.3.0](https://github.com/optuna/optuna/blob/v1.3.0/examples/lightgbm_tuner_simple.py)までは`best_params`あり、[v1.4.0](https://github.com/optuna/optuna/blob/v1.4.0/examples/lightgbm_tuner_simple.py)からは`best_params`なしになっていた。

### 解決&感想
実際は上記以外のページとかも確認したりしたけど特に成果がなかったものは割愛している(というか失念しちゃってる)
こういう問題やエラーの調査・解決って成果が出ないときはとことん出ない。成果が出なくてつらくなる。
そういうときは日を改めたり他の人に軽く相談してみたりすると意外と活路が見えたりするのでオススメです。

## まとめ
ググった結果でうまく動かない場合は環境やバージョンが異なることが原因であることが多いよ。
仕様やサンプルが公開されているものであれば公式ドキュメントやGithubを参考にすると問題解決の近道であることが多いよ。
(今回は違うけど)エラーメッセージ内容を確認・メッセージで検索してみることも大事だよ。
引数の数や引数自体が違う場合はバージョンが異なる可能性が高いよ。
解決策が見つからないときは他の人に相談してみたり日を改めたりするのも大切だよ。
スマートウォッチ（組込みシステム開発）ー午後

応用情報技術者平成29年春期 午後1

ファイル：2017h29h_ap_pm_qs.pdf

参照：
https://www.ap-siken.com/kakomon/29_haru/pm07.html

メモを簡単に記載します。
詳細の分析は、上記リンクをご参照くだいますようよろしくお願いします。

**１、ウォッチのソフトウェアについて，(1)～(3)に答えよ。**
（１）、図2中のa～dに入れる適切なタスク名を答えよ。
**（２）、割込みマスクに関する次の記述中のe，fに入れる適切な字句を答えよ。**

　割込みハンドラとメインタスクは，どちらもタッチ操作割込みのマスクを操作している。これは，例えば，ウォッチがフォンからメール受信通知を受信した直後に，利用者がウォッチのタッチパネルにタッチした場合の不都合を避けるためである。その不都合とは，eを表示した後，利用者が表示されたeを確認する前に，画面がfに切り替わってしまうことである。

eメールの内容　（私の答え）
f新しいメール通知　（私の答え）
→
e：メールの内容（参考答え）
f：機能選択画面（参考答え）

ウォッチの動作仕様により、タッチパネルにタッチすると、「機能選択画面を表示する」。
なお、ウォッチの機能によりフォンが受信したらメールの内容を表示する。

そうすると、ウォッチがフォンからメール受信通知を受信した直後に、利用者がウォッチのタッチパネルをタッチをすると、受信したメールの内容を確認する前に機能選択画面に切り替わってしまいます。本問のウォッチでは、これを避けるために、各割込みハンドラの起動時にタッチ操作割込みをマスク（受付無効に）し、メインタスクが適切なタイミングで解除する仕様になっています。

＜＜ウォッチの機能＞＞
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/f8c648bc-1693-0012-e7c7-2f346932d2d0.png)


＜＜ウォッチの動作仕様＞＞
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/5ea54154-4601-28e7-7d54-c3d507911534.png)

**（３）、タスクの動作に関する次の記述中のgに入れる適切な字句を答えよ。また，次の記述中の，"意図しない現象"とはどのような現象か。15字以内で述べよ。**

　画面表示タスクが画面の更新を行っているときに，画面作成タスクが動作を開始すると，意図しない現象が発生した。この問題を解決するために，画面表示タスクが画面の更新を行っているときに，画面作成タスクが動作を開始しないように，それぞれのタスクのgを，適切に設定した。


g　起床条件　（私の答え）
現象：画面の更新ができない　（私の答え）
→
g　優先度　（参考答え）
現象：複数画面が混在表示される（参考答え）

表2より、
　画面表示タスクは「画面情報の内容を基に、画面の更新を行う」。
　画面作成タスクは「メインタスクから受けた情報を基に、表示画面を作成し、画面情報に書き込む。」とあり、画面情報を更新。

　つまり、「画面情報」というものがメモリにあります。
　正常な流れは、
　　・画面作成タスクが「画面情報」に書き込む⇒
　　・画面表示タスクが「画面情報」を読みだして表示する

　今回は画面表示タスクと画面作成タスクは並行して実行されるため、
　今回の流れは、
　　・画面表示タスクが「画面情報」を読みだして表示する　（まだ終わっていない）
　　・画面作成タスクが「画面情報」に書き込む
　
　そうすると、既に画面表示タスクが読み出した情報は正常に表示されたが、
　まだ読み出していない情報が上書きされて、画面表示タスクが読み出してしまった。
　
　そうすると、画面に表示されている情報は、複数画面が混在している情報ですね。
　混在するより、複数の各画面の一部だけ、同じ画面に表示されるとのこと。

　もし、メモリが大きいで、足りる場合、メモリには、両画面の情報が全て含んだとすると、画面表示タスクは複数画面を同じ画面に表示しますね。

この問題を解決するためには、それぞれのタスクに優先度を付けて、画面表示タスクを実行中に画面作成タスクを動作しないようにする必要があります。これらのことから、gは「優先度」、"現象"は「複数画面が混在表示される」が適切です。


＜＜ウォッチのソフトウェア構成＞＞
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/138dc050-787b-18da-528a-dfcf99eaa640.png)


＜＜表２　ウォッチのタスク一覧＞＞
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/167d73ae-3164-4519-0274-680d79955a20.png)


＜＜表３　ウォッチの割込みハンドラ一覧＞＞
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/d012d2e3-b5e3-e8ff-1c33-b5b8dbe32dcd.png)
【NestJS/GraphQL】queryの引数の設定方法

# 環境

- [NestJS](https://nestjs.com/) 7.3.7
- [@nestjs/graphql](https://www.npmjs.com/package/@nestjs/graphql) 7.3.7

# 引数の設定方法（2種類）

## やり方1： 引数に直接羅列する。

```javascript
async getUser(
  @Args('id') id: string,
  @Args('status', { type: () => UserStatus, nullable: true }) status?: UserStatus
): Promise<User> {
  ...
}
```

## やり方2： ArgsType デコレータを使って定義する。

```javascript
// 例えば src/modules/user/dto/get-user.args.ts に定義されている。
@ArgsType()
export class GetUserArgs {
  @Field()
  id!: string

  @Field(() => UserStatus, { nullable: true })
  status?: UserStatus
}
```

以下の通り使う。

```javascript
async getUser(
  @Args() args: GetUserArgs
): Promise<User> {
  ...
}
```
Ruby on Railsにて、Blocked Host:"ホスト名"が出てしまう時の対処法

#背景
Ruby on Railsチュートリアルの1章の演習で、ずっと「Blocked Host:自分のホスト名」というエラーが出てしまい、長時間色々なことを試した結果、ある根本的なことをしていなかったことに気づき解決できたため、この投稿をさせてもらいました。

#試したこと
Web上でよく書かれていることとしては、
1.environment/development.rbにて、「config.hosts.clear」を入れることです。
しかし、これを試しても、エラーが発生したままでした。
2.environment/development.rbにて「config.hosts<<"自分のホスト名"」を入れることです。
しかし、これを試しても、またしてもエラーが発生したままでした。

#原因
(Ⅰ).違うアプリケーションの中の、development.rbに1と2のプログラムを追加していた。
例えば、自分が作っているアプリケーションがhello_appだとすると、違うアプリケーション(例えば、a_app)の中のdevelopment.rbに1と2のプログラムを追加していたということです。
これでは、a_appのホストが許可されただけで、hello_appのホストは許可されません。
これを間違えた理由としましては、階層構造がenvironment/hello_app/config/environment/development.rb、とenvironmentが2つあることにより、同じ環境内であれば、一つのdevelopment.rbを書き直せば、全てに反映されると勘違いしたことによるものでした。
development.rbの階層は上の通りであり、１つのアプリケーションにつき一つのファイルとなります。
決して、共通ではありません。
自分が作成しているアプリケーション内の、config/environment内のdevelopment.rbを１と２のように書き直すようにしましょう。

(Ⅱ).development.rbを書き直した後に、「rails server」を実行しなかった。
順序としては、「development.rb内のプログラムを書き直す→rails serverを実行する」という手順を踏まないと、いくら(Ⅰ)の手順を踏んだとしましても、エラーが発生したままとなります。
Blender Render Farm on Azure Kubernetes Service 実現に向けて

こんにちは、Japan Azure User Group 10周年記念イベントでお話させいただく予定の内容の補足です。

## Blender Render Farm on Azure Kubernetes Service 実現に向けて
Blender 2.7系だとレンダーファームを作るアドオンは用意されているみたい。

Docker Blender Render Cluster
- DockerベースのBlenderレンダークラスター
- https://github.com/rndevfx/docker-blender-render-cluster
Kubernetes上にBlenderのレンダリングファームを作る（@inajobさんのQiita記事）
- https://qiita.com/inajob/items/bc43cb3f6506815870a2

Crowdrender 
- https://www.crowd-render.com/

Blender 2.8/2.9だとレンダーファームを作るアドオンが見つからないので作るしかないもよう。せっかく作るんだったら、Blenderのファイルが読めてレンダリングできる範囲でのバージョン差でレンダリングできるようにしたい。あと、レンダリング中にMasterと接続できなくなったり、MasterとSlaveが接続できなくなったりするのは困るので、疎結合な感じで考えたい。

時間があるときに作るために、メモを書き記しておこうと思う。

Workerコンテナ。Ubuntu 20.10のapt-getからインストールの場合。

```dockerfile:dockerfile
FROM ubuntu:20.10
RUN apt-get update
RUN apt-get install -y blender xvfb
```

Workerコンテナ。最新版本家からインストールの場合。

```dockerfile:dockerfile
FROM ubuntu:20.10
RUN apt-get update
RUN apt-get install -y wget xz-utils tar gzip libx11-6 libxi6 libxxf86vm1 libxfixes3 libxrender1 libgl1 xvfb
RUN wget https://ftp.nluug.nl/pub/graphics/blender/release/Blender2.90/blender-2.90.0-linux64.tar.xz
RUN tar Jxvf blender-2.90.0-linux64.tar.xz
```

Workerコンテナでレンダリングする場合のGPU設定。
参考：https://devtalk.blender.org/t/blender-2-8-unable-to-open-a-display-by-the-rendering-on-the-background-cycles/8607/18

```python:setgpu.py
import re
import bpy
scene = bpy.context.scene
scene.cycles.device = 'GPU'
prefs = bpy.context.preferences
prefs.addons['cycles'].preferences.get_devices()
cprefs = prefs.addons['cycles'].preferences
print(cprefs)
# Attempt to set GPU device types if available
for compute_device_type in ('CUDA', 'OPENCL', 'NONE'):
    try:
        cprefs.compute_device_type = compute_device_type
        print('Device found',compute_device_type)
        break
    except TypeError:
         pass
# Enable all CPU and GPU devices
for device in cprefs.devices:
    if not re.match('intel', device.name, re.I):
        print('Activating',device)
        device.use = True
    else:
        device.use = True
```

Workerコンテナでレンダリング実行

```sh
Xvfb :1 -screen 0 1920x1080x24 &
export DISPLAY=:1
blender -b <blenderファイル> -P setgpu.py -o <画像ファイル名> -f <レンダリング対象フレーム>
```

# さいごに

じゃずにゃんは次の記事にします。
Unity ML-Agents で実行条件を変えて学習時間を比較してみた 

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506075/0106ffe3-dbac-dd8b-f9c9-349ae4d45c1c.gif" width=640 />

CPU/GPU、UnityEditor/ビルド、ビルドの複数実行、Agentの複数実行と、
実行条件を変えて学習時間を比較してみました。
プログラムは[RollerBall](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Learning-Environment-Create-New.md)を参考につくっていて、[こちら](https://github.com/machidyo/RollerBall)に挙げてあります。

# 環境
* Windows10
* Python 3.7.9
* TensorFlow 2.3.0
* Unity 2019.4.10f1
* ML-Agents Release6

# 学習の高速化
* GPU で計算する（判断基準は下に記載）
* ビルドした exe で学習させる
    * --env
* --no-graphics オプション : 画面表示なし
* 複数同時起動、CPUのマルチコア（スレッド）が最大
    * --num-envs
* Agent を複製して実行する

## Behavior ParameterのInterface DeviceにGPUを設定する判断基準
単純な推論であればCPUのほうが早い。GPUを選択する基準は以下の通り。
1. Visual Observation
2. エージェント数 * Observation > 1024
3. エージェント数 * 隠れ層の数 > 1024

# 学習時間の計測結果
手法           | 学習時間（秒）  | CPU使用率  | 補足
---------------|-----------------|-----------|----------------------
UnityEditor    | 68             | 25        | CPU
GPU            | 71             | 25        | Behavior Parameter に GPU を設定、※1
ビルド         | 86             | 13        | ※2
複数同時起動    | 55             | 35        | --num-envs=4
Agent 複製     | 21             | 25        | 複製4つ、UnityEditor

※1 それぞれ1回の測定なので、数秒は誤差の範囲
※2 ビルド版が UnityEditor より遅くなっているのは、Python とビルドアプリがCPUを食い合っているためだと思われる