OmniAICreator/Qiita-1.07M huggingface dataset:
Facenetを使った類似AV女優検索

## はじめに

[Facenetを使った類似顔画像検索のための特徴量抽出](https://qiita.com/zeze/items/09bdd7229713c0a6b090) の続きです。
今回は、タイトルの通り、AV女優の顔画像による検索サイトを作り、実力を測ります。

余談ですが、前回の記事を書いたあと、OSを飛ばし、データとコードの一部を飛ばしてしまいました。
そのため、コードの紹介はありません…

## なぜAV女優か

顔画像を元に、似てるAV女優を探すシステムは、「顔　似てる　AV　女優　検索」とかでググると、
顔を認識せずに行うものからディープラーニング使うものまで様々あります。

特に、ディープラーニング使ったものとして有名なものに
[chainerによるディープラーニングでAV女優の類似画像検索サービスをつくったノウハウを公開する](https://qiita.com/xolmon/items/0b82f4861cf93fd28e33)
があります。

AV女優検索、一体何番煎じだ、という感じなのと、（良し悪しとは別として）あまり健全じゃないですが、
以下のような要件を満たす顔画像のデータセットが他にあるかというと、私が知る限りはありません。

1. 適当な枚数がある （人数がいる）
  * かつ、データセットが極端に特定の属性に偏っていると実験としては面白い
1. データセットの取得が容易（API/クロールできるサイト等が存在する）
  * 今回は[DMM API](https://affiliate.dmm.com/api/)を利用しています
1. 肖像権などの権利関係がクリア
1. 日本人が中心
  * 外国の有名人のデータセットはいくつかありますが、よくわからないですよね
1. そこそこ注目されそう
  * 有名人じゃないとイマイチ結果を観察するモチベーションが薄い

[MS-Celeb-1M](https://www.microsoft.com/en-us/research/project/ms-celeb-1m-challenge-recognizing-one-million-celebrities-real-world/)の日本版、みたいなのがあればいいんですが、フェアユース無い、事務所強い、みたいな日本では実現は望み薄でしょう。

## 他の手法との違い

Facenetを使うことでのメリットは、以下が挙げられます。

1. 学習済みモデルが利用できる
2. ベクトルに落とし込むので、検索したい人の画像が少なくてすむ
3. 顔画像に特化している
4. お手軽

## 実装詳細

こんな感じで実装しました。

1. DMM APIを利用して、画像をクロール
2. Facenetで画像からベクトルを取り出す
3. annoyでk-NN
4. heroku + flask でWebサービスを実装

### DMM API

AV界の巨人のDMMは、APIも豊富です。
今回は、[女優検索API](https://affiliate.dmm.com/api/v3/actresssearch.html)を利用させていただきました。
これを使うと、女優名や属性、顔画像のurlが取得できます。

[SDK](https://affiliate.dmm.com/api/v3/affiliatesdk.html)も用意されていて、JS, PHP, Goで使えるようですが、
pythonで諸々記述しているので、おとなしくREST使ってクロールしました。

### annoy

今回、検索に使う画像は、1人あたり1枚なので、k-近傍法(k-NN)を使います。
もう少し枚数があれば、SVMといった識別モデルも利用できるかと思います。

近傍検索には、[annoy](https://github.com/spotify/annoy)が便利です。
使い方もシンプルなので公式ドキュメント読めばすぐ使えるかと思います。

### heroku + flask

jupyter notebookで個人的に楽しむ、でも良かったんですが、せっかくなのでWebアプリにします。
AWSとかGCPとかに押されてる気がするherokuですが、flaskと組み合わせると、
シンプルなWebアプリだとすぐに開発でき、かつ、タダ運用可能です。

## 結果

結果は、
サイトで確認してください！

## できたサイト

できたサイトは[こちら](https://av-face-search.herokuapp.com/)です。
適当に、人気女優の名前をクリックすると結果が出ます。
顔画像ポチポチクリックしていくといろんな人が出てくると思います。

サイトは研究目的で、予告なく停止します。ご了承ください。
タダのherokuなので、落ちたりしても多めに見てください。
UIとかがしょぼいのはお察しください。

## おわりに

逆に距離が遠い人順、みたいに並べるのも面白かったんですが、Webアプリまではちょっと実装できず。
サンプル実装も闇に屠られました。
あとは、画像アップロード機能つけると面白そうですが、
なんか変な画像投げられてもやだし、heroku無料枠でやるのはかなり大変そうだし、パスしました。

今回は、極力実装せずになんかサービス作る、というのがちょっとした目標でした。
内部の実装は色々サボりまくってます。
が、フレームワークは様々あって、頑張らなくてもなんかできる、というのを確認できたのも良かったです。

### 悩んだところ

1. ライセンス/肖像権まわり
1. UI/フロント周りの実装
1. 増田で書くかQiitaで書くか
node.js + ExpressでSCRF(csurf)とmulterが同時に使えない場合の対処 

ハマってしまったので覚書。
multerはscrfの前に書く必要があるよう。


[公式](https://github.com/expressjs/csurf)のSimple express exampleコードを参考にしてrouterファイルを使う場合

```javascript:app.js
const cookieParser = require('cookie-parser')
const csrf = require('csurf')
const bodyParser = require('body-parser')
const express = require('express')

// create express app
const app = express()

// parse cookies
// we need this because "cookie" is true in csrfProtection
app.use(cookieParser())

//router
app.use('/', index);
```

```javascript:index.js
const express = require('express');
const multer = require('multer');
const router = express.Router();
const upload = multer({dest: 'uploads/', limits: {fileSize: 5000000}});
//router内側で宣言
const bodyParser = require('body-parser');
const csrf = require('csurf');

// setup route middlewares
const csrfProtection = csrf({ cookie: true });
const parseForm = bodyParser.urlencoded({ extended: false });

router.get('/form',csrfProtection,function(req,res){
    // pass the csrfToken to the view
  res.render('send', { csrfToken: req.csrfToken() })
}

//multerはscrfの前！
router.post('/process',upload.fields([{name: 'image'}]),parseForm,csrfProtection, function (req, res, next) {
    res.send('data is being processed')
}
```

たったこれだけだが予想以上にハマってしまったので忘れないようにメモ。
app.js内でapp.use(csrf({ cookie: true })する方が楽で、その場合はその前にmulterを入れるのだろうが
app.use(multer())を使ったことが無いのでとりあえずこっちで。
Google Analyticsのアクティブユーザー数をRailsで表示させる方法

前提としてgoogle analyticsを使用しているRails(ruby)アプリがあること


目的は自分のwebサイト(マッチングサービス)で現在訪問しているアクティブのユーザの数を表示させたかったのです。
要所で参考にできる記事がなかなか見つからず苦労しました。。
Google APIの英語ドキュメントをたどたどしく読みながら実装できました。
ビギナーでもできるかと思います。


１. gemを使います。

```:Gemfile
gem "google-api-client"
```
[google-api-client](https://github.com/google/google-api-ruby-client)

```
$ bundle install
```

２. Analytics API の利用申請をする
[ここから → Google Cloud platform](https://console.cloud.google.com/apis/)

認証のJSonファイルをダウンロードするとこまで以下のリンクを参考にしてください
[→この記事がとてもわかりやすかったです](https://qiita.com/ryota-sasabe/items/a5efd2aac244cfcce5c7)


ダウンロードされる認証用のjsonファイルのサンプル


```client_secret.json

{
  "type": "service_account",
  "project_id": "hogehoge",
  "private_key_id": "asdjghjsdhgajsdhgakjsdhka",
  "private_key": "-----BEGIN PRIVATE KEY-----\nyw\sdfjghsdljkrghlwinevhrugilvjrsdhglnvjdhgvksjhgklvjsrhgklnvjdhvgklsjhkskdfjgskdfjsknfjlskdfjmsvlkdfjglskdjflmvkdjfmlgskflm;skjfdl;gkn\n-----END PRIVATE KEY-----\n",
  "client_email": "hogehogehgoehgoe.iam.gserviceaccount.com",
  "client_id": "11111111111111111111111111",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://accounts.google.com/o/oauth2/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/hogehoge.iam.gserviceaccount.com"
}
```

⚠︎client_secret.jsonとget_active.rbは同じ階層に置いてください。

３. rubyファイルでApiを使う

```ruby:get_active.rb

require "pry"
require 'google/apis/analytics_v3'


CREDENTIAL_PATH = 'client_secret.json' # 各自適切なパスにしてください
SCOPES =['https://www.googleapis.com/auth/analytics.readonly']
client = Google::Apis::AnalyticsV3::AnalyticsService.new

# 認証
client.authorization = Google::Auth::ServiceAccountCredentials.make_creds(
  json_key_io: File.open(CREDENTIAL_PATH),
  scope: SCOPES
)

data = client.get_realtime_data(
  "ga:1234567", # view_id ここは各々変更してください 
  "rt:activeUsers" # metrics

)


puts data.total_results
```

```
$ ruby get_active.rb
=> 17   
```

必要最低限ですがこんな感じで取得できます
他にもメソッドがあります。
下の公式ドキュメントがとても参考になるので見て見てください。

[参考1 AnalyticsV3/RealtimeData](http://www.rubydoc.info/github/google/google-api-ruby-client/Google/Apis/AnalyticsV3/RealtimeData)

[参考2 AnalyticsV3/AnalyticsService#get_realtime_data-instance_method](http://www.rubydoc.info/github/google/google-api-ruby-client/Google/Apis/AnalyticsV3/AnalyticsService#get_realtime_data-instance_method)
Node-Red 初心者 導入

# Node-Red 導入方法(debian系)

Ubuntu16.04とDebianに入れた時手こずったので導入方法書いておきます。

グーグル先生や他の人は、普通にうまくいけば以下のコマンドでいけるらしい？

```
$ sudo apt install nodejs npm

$ npm install node-red
```

私の場合は、うまくいかなかったので以下のようなやり方です。

```
$ sudo apt install nodejs nodejs-legacy curl

$ wget http://www.npmjs.org/install.sh --no-check-certificate
sudo sh install.sh

npmがインストールできたら

$ sudo npm install -g --unsafe-perm node-red
```
curl installしてwget使ってるあたり
本当にcurlインストールしてるのが謎なんでcurl入らないかも。
CloudWatchで定期的にLambdaを実行して、Slackにメッセージを送信する

# 今回のお題
Lambdaのトリガーはいくつか設定できますが、今回は毎週月曜日のAM9時にSLackに通知するのが目的なんで、CloudWatchでLambdaに設定した関数を実行する方法を試して見ます。

## LambdaにSlack送信するプログラムを作成する
[LambdaからWebHookを使用してSlackにメッセージを送信してみる
](https://qiita.com/seisyu1985/items/c59dbb8164b5332d5c9e)
別記事で書いたこちらを元に作成。
ちょっとだけ手を加えてあります。

```slack.py
# coding: UTF-8
import requests
import json
import os
from datetime import datetime

def lambda_handler(event, context):
    now = datetime.now()
    msg = now.strftime("%Y/%m/%d %H:%M:%S")
    msg += u'\r\nCloudWatch実行\r\nlambdaからのPython使ってのSlack送信・・・何たる僥倖・・・！悪魔的発想・・・！'
    # 実行
    send_slack(u'カイジ', u':kaiji:', 'チャンネル名', msg)

# slack送信、仮メソッド
def send_slack(user_name, icon, channel, msg):
    requests.post('WebHookのURL', data = json.dumps({
        'text': msg, # 投稿するテキスト
        'username': user_name, # 投稿のユーザー名
        'link_names': 1, # メンションを有効にする
        'channel': channel, # チャンネル
        'icon_emoji': icon, # アイコン
    }))
```

分かりやすくするために、Postするメッセージに日付を入れて見ました。
あとはまぁ、よくある悪ふざけですw
テスト実行で、Slackにメッセージが送信されるのは確認済みです。

## Lambdaにトリガーを設定する
トリガーを選択します、＋　トリガーを追加を押下します。
<img width="946" alt="スクリーンショット 2017-11-16 23.00.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/204329/365f3a37-f30f-37b9-f327-0a0c75d0357a.png">

図の空白になっている、左側をクリックします。
<img width="822" alt="スクリーンショット 2017-11-16 23.02.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/204329/4d1d33ab-1abe-42e2-ca42-98b95c447d5a.png">

すると設定できるリストが表示されるので、今回使用する「CloudWatch Events」を選択します。
<img width="314" alt="スクリーンショット 2017-11-16 23.03.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/204329/d7413670-45a8-9344-17e0-dfb629dbe450.png">

ルールで「新規ルールの作成」を選択し、必要な項目を埋めていきます。
スケジュール式は、今回はテスト的な内容なんで5分ごとに実行する式を記載しました。
<img width="783" alt="スクリーンショット 2017-11-16 23.05.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/204329/103376d3-ec86-8f96-8ba0-84625414be79.png">

トリガーの有効化のチェックがON担っているのを確認し、送信を押下する。
<img width="785" alt="スクリーンショット 2017-11-16 23.07.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/204329/3c3ff916-ef8f-1282-97b2-d3c617ac4512.png">

## トリガーの作成完了
<img width="942" alt="スクリーンショット 2017-11-16 23.07.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/204329/5d8f7b4a-a45f-a1b6-b6a2-8d3543374eb0.png">

## Slackを確認する
とりあえず問題なく定期的にSlackの送信ができました！！
<img width="716" alt="スクリーンショット 2017-11-16 23.19.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/204329/8b5f2d01-8b43-a625-647d-58b53b687428.png">

### 別枠で検証する事柄
時間がおかしいですね・・・？どこの時間ですかね？
とりあえず、日本時間での実行ができるように調べて見ます。

[AWSのLambdaのタイムゾーンをUTCから東京に変更](https://qiita.com/seisyu1985/items/2f3af4188a61dccf44b2)
こちらで、Lambdaでの時間についてはできました！

CloudWatchについては別途調査中です。
Azure Iot Edge v2のPublic Previewを使ってみる

#IoT Edge V2について
Azure IoT Edge v2のPublic Previewが始まりました。[Azure Blog](https://azure.microsoft.com/en-us/blog/azure-iot-edge-open-for-developers-to-build-for-the-intelligent-edge/)を参照ください。v1では[以前の記事](https://qiita.com/yasukit/items/6123fb3f167cbdc9c6e2)のようにかなり自力でBuildを行い導入する必要がありましたが、v2になりクラウドからDockerに対して展開するという方向で実行できます。展開はかなり簡単であり、Docker動作している環境であれば、即実行することが可能です。Windows10のDockerで実行してみました。サイトの手順を実行しただけですが、構成していたIotHubにすでにセットアップが可能となっており、10分程度で動作確認できました。
##iotedgectlの導入
[Python2.7](https://www.python.org/downloads/)を構成して下記のコマンドでコントロールコマンドをインストールします。

``pip install -U azure-iot-edge-runtime-ctl``

##IotHubの作成
IoTHubは月単位での課金ですので、展開すると一か月分の課金されることになります。Freeを使って作成しましょう。[こちら](https://docs.microsoft.com/ja-jp/azure/iot-hub/iot-hub-create-through-portal)を参考にしてください

##IotEdgeの展開
最初にIotHubからIotEdgeを追加して、接続文字列を取得します。IotEdge(Preview)のメニューが表れていると思いますので、下記の画面のようにDeviceIDを入力して保存をクリックします。


![IotEdge1.JPG](https://qiita-image-store.s3.amazonaws.com/0/177932/daf273fd-b726-836f-077b-318e0619871a.jpeg)


保存の後接続文字列が作成されますので、そちらを使い、下記コマンドを入力します
``
iotedgectl setup --connection-string "{接続文字列}" --auto-cert-gen-force-no-passwords
``
コマンドが成功すると下記のコマンド入力を促す表示が出ますので入力します
``
iotedgectl start
``
IotEdgeが無事立ち上がればモジュールを追加します。温度センサーのシュミレートデバイスを追加します
add IotEdge Moduleをクリックし、Nextを押して、最後にSubmitします。

![iotedge2.JPG](https://qiita-image-store.s3.amazonaws.com/0/177932/4536b85c-55fc-ee6a-1ac1-88040cd0c850.jpeg)
![iotedge3.JPG](https://qiita-image-store.s3.amazonaws.com/0/177932/c1d735ad-d7bf-6beb-63df-c0029e96f2f6.jpeg)
![iotedge4.JPG](https://qiita-image-store.s3.amazonaws.com/0/177932/e5ba84e5-770b-e425-1e6c-c3c5378eaf3e.jpeg)

無事展開が終わると下記のような画面になります。
![iotedge6.JPG](https://qiita-image-store.s3.amazonaws.com/0/177932/1801e21f-29b4-1306-1c31-40739c408376.jpeg)

ModuleTiwnをクリックすると状況の詳細が確認できます。

![iotedge7.JPG](https://qiita-image-store.s3.amazonaws.com/0/177932/7a3f7dc4-9c3f-7cf0-5ebe-1741034664ff.jpeg)
RailsTutorial (v.5.1) ハマりポイント 1章 1.4.3項

#この記事はなに
Rails Tutorial (v.5.1) をテキスト通りに進めていた中で、ハマったところの対処メモです。2017.11.16時点。
開発環境はcloud9。

#1.4.3 Bitbucket
##ハマったポイント

図1.15の後の段落に、以下の記載がありますが、[Command line]というものが見つかりません。Bitbucket側のUIが変更されているようです。

>[Create repository] をクリックしてリポジトリを作成したら、[Command line] > [I have an existing project] をクリックしてそこに表示される指示に従います (リスト 1.12)。

検索したところ、答えは下記にありました。
（Bitbucket運営元のAtlassian社のサイト）
https://confluence.atlassian.com/bitbucket/push-versioned-code-to-an-empty-repository-877177145.html

下記のコマンドを実行すれば良いようです。
該当部分だけ翻訳してみます。（意訳で）

>1.Navigate to the root directory of the repository you want to push.

プッシュしたいリポジトリのルートディレクトリに移動します。

>```ruby:console
$ cd ~/repos/<repo_name>
```

>2.Push the local repository up to Bitbucket.

ローカルリポジトリをBitbucketにアップします。

>```ruby:console
$ git push --mirror git@bitbucket.org:<account_name>/<repo_name>
```

実施してみた結果、無事にアップされました。
※ユーザ名とIPアドレスはマスクしました。

```ruby:console
user:~/workspace/hello_app (master) $ git push --mirror git@bitbucket.org:<user>/hello_app.git
Warning: Permanently added 'bitbucket.org,<IP addr masked>' (RSA) to the list of known hosts.
Counting objects: 85, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (71/71), done.
Writing objects: 100% (85/85), 21.46 KiB | 2.38 MiB/s, done.
Total 85 (delta 2), reused 0 (delta 0)
To bitbucket.org:<user>/hello_app.git
 + 205d098...6ae4746 master -> master (forced update)
```
CentOS7にMySQLをインストールする

## MariaDBの削除
CentOS7にはデフォルトでMariaDBがインストールされているので、削除しておく

```
$ sudo yum remove mariadb-libs
```

一部ファイルが残ってしまうので、手動で削除

```
$ sudo rm -rf /var/lib/mysql
```

## インストール

yumでインストールするにはyumのパッケージDBにインストール元の情報が必要だが、
MySQLはデフォで入っていないので手動で追加する。

```
$ sudo rpm -ivh mysql57-community-release-el7-11.noarch.rpm
```
ちなみに```-i``` でインストール、```v```でパッケージ検査、```h```でインストール状況を表示するオプション。

これで`yum install`できるようになったのでインストール。

```
$ sudo yum install mysql-community-server mysql-community-devel mysql-utilities
```

インストールされているか確認。

```
$ mysql --version
```

## 初期設定

初期パスワードがログファイルに出力されるので調べる。

```
$ sudo cat /var/log/mysqld.log | grep password
```

下記コマンドで初期設定を行う。

```
$ mysql_secure_installation
```

## rootにパスワードなしでログインできるようにする

ローカル開発用だと面倒くさいので（規約厳しいし）

```
$ mysql> use mysql
$ mysql> update user set authentication_string='' where User='root'
```

5.7からカラム名が変わったらしい
怠惰な FizzBuzz を作ってみた

# 怠惰な FizzBuzz

お風呂に入ろうとしていたら、突然思いついたのですかさず試してメモしました。

```rb
def genarate_lazy_list(number, word)
  loop.lazy.flat_map { [*Array.new(number - 1, nil), word] }
end

numbers = 1.step.lazy
fizzes = genarate_lazy_list(3,'Fizz')
buzzes = genarate_lazy_list(5,'Buzz')
fizzbuzzes = genarate_lazy_list(15,'FizzBuzz')

numbers
  .zip(fizzes, buzzes, fizzbuzzes)
  .map { |n, fizz, buzz, fizzbuzz| fizzbuzz || buzz || fizz || n }
  .take(30)
  .each { puts(_1) }
```

```:stdout
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
Fizz
22
23
Fizz
Buzz
26
Fizz
28
29
FizzBuzz
```

`fizzbuzzes` が冗長だと思う場合は

```rb
require 'active_support'
require 'active_support/core_ext/object/blank'

numbers
  .zip(fizzes, buzzes)
  .map { |n, fizz, buzz| "#{fizz}#{buzz}".presence || n }
  .take(30)
  .each { puts(_1) }
```

# 宣言的な FizzBuzz

お風呂の中で思いついた。

```rb
# say(Fizz or Buzz or FizzBuzz).for_multiples_of(number) と書けるようにする。
def say(fizzbuzz)
  def fizzbuzz.for_multiples_of(number)
    loop.lazy.flat_map { [*Array.new(number - 1, nil), self] }
  end

  fizzbuzz
end

numbers = 1.step.lazy
fizzes = say('Fizz').for_multiples_of(3)
buzzes = say('Buzz').for_multiples_of(5)
fizzbuzzes = say('FizzBuzz').for_multiples_of(15)

numbers
  .zip(fizzes, buzzes, fizzbuzzes)
  .map { |n, fizz, buzz, fizzbuzz| fizzbuzz || buzz || fizz || n }
  .take(30)
  .each { puts(_1) }
```

# おまけ

Python 版も実装しました。

:point_right: [怠惰な FizzBuzz を作ってみた (Python 版)](https://qiita.com/QUANON/items/f535f398c7b717bd35d1)
利用者が検証可能なガチャの実装

# はじめに

多くのソーシャルゲームでガチャが実装されていますが、サーバ側で確率を操作されているのではないか、といった疑念を抱く方は多いと思います。
プログラムのバグによって、ユーザごとに異なる確率を誤って表示してしまったゲームでは、多くの批判が集まっていました。

仮にガチャのシステムを含めてすべてのソースコードを開示したとしても、サーバ上で動くソースコードと一致しているか検証することは困難ですから、利用者の信用を得る根本解決にはならないと思います。
そこで、利用者が、ガチャの確率が改ざんされていないことを検証可能である仕組みを考えてみました。

ハッシュ関数を利用することで、以下を実現します。

* 利用者が、ガチャの終了後に確率が不正に操作されていないことを検証できる。
* 利用者は、どのようなアイテムが出るのか、推測することができない。

## 運用の流れ

ユーザには、ユーザごとに異なるユーザIDを割り当て、利用者に公開します。
フレンドなどの仕組みがあれば、そのIDをそのまま利用することもできます。
（ただし、他人のユーザIDを知ることができる場合、他人に前回のガチャでどんなものがひける/ひけたのかわかってしまいます。それが気になる場合は、他人には見せない形のユーザIDを別途割り当てる必要があります。）

ガチャごとに、アイテムの一覧テーブルを公開します。
テーブルの中に入っているアイテム個数の比率が、そのまま出現確率になります。
利用者はこのテーブルを見ることで確率を確認できますが、本当にそのテーブルが使われたのかは、ガチャが終了するまでは検証できません。

さらに、ガチャの実施ごとに、秘密キーを運用側で生成します。
このキーはガチャが終了するタイミングまでは秘密にしておき、終了後に利用者に公開します。
利用者はガチャ終了後、ユーザID・アイテムの一覧テーブル・秘密キーを入手すれば、そこからガチャの結果を計算でき、実際に得られたアイテムと差異がないか検証ができます。
（なお、もし更にガチャを回していればどんなアイテムが得られたかは、計算できてしまいます。）

常設のガチャの場合は、週次や月次で秘密キーを変えて公開し、そのたびにガチャを引いた回数をリセットすることで対応できます。

## アルゴリズム

ユーザIDと、ガチャを回した回数と、秘密キーを使ってHMACを計算し、その剰余を使ってテーブルからアイテムを求めます。

実際に例にして記載します。

Sレアを2%で2種類、レアを8%で2種類、ノーマルを20%で4種類排出するものとします。
提供確率に応じて、IDの範囲を決めておきます。
この場合、全部で50個のアイテムがあり、確率に応じてアイテム数が、Sレアなら1個、レアなら4個、ノーマルなら10個というように変わる形になります。

| アイテム | 確率 | ID |
|:--|--:|:-:|
|エンタープライズ(Sレア)	|2.000%	|0 ～ 0|
|フッド(Sレア)	|2.000%	|1 ～ 1|
|赤城(レア)	|8.000%	|2 ～ 5|
|加賀(レア)	|8.000%	|6 ～ 9|
|サンディエゴ(ノーマル)	|20.000%	|10 ～ 19|
|ジュノー(ノーマル)	|20.000%	|20 ～ 29|
|エイジャックス(ノーマル)	|20.000%	|30 ～ 39|
|ヴァンパイア(ノーマル)	|20.000%	|40 ～ 49|

秘密キーが "00112233445566778899001122334455" だったとすると、ユーザID が "1234-5678-9012" のユーザの1回目のガチャは、メッセージを "1234-5678-9012 1" として計算したHMACの剰余、2回目のガチャは "1234-5678-9012 2" として計算した剰余、といったように求めていきます。

| 回数 | HMACのメッセージ | ID | アイテム |
|--:|:-:|--:|:-:|
|1	|1234-5678-9012 1	|0	|エンタープライズ(Sレア)|
|2	|1234-5678-9012 2	|38	|エイジャックス(ノーマル)|
|3	|1234-5678-9012 3	|14	|サンディエゴ(ノーマル)|
|4	|1234-5678-9012 4	|39	|エイジャックス(ノーマル)|
|5	|1234-5678-9012 5	|6	|加賀(レア)|

## シミュレータ

上記のような計算を試せるJavaScriptを組んでみました。

http://www.mikage.to/gacha/

設定をかえて、シミュレートボタンを押すと結果が出ます。
ユーザID、秘密キー、テーブルが同一だったら、結果が一定になることなどが確認できると思います。

## まとめ

このような実装であれば、ソースコードを公開する等しなくても、運用が正しくされているか、利用者が検証可能になります。

ガチャだけじゃなくても、ドロップなどでも回ってるのに全然出ない、みたいなこともありますよね。
こういう仕組みを導入してくれたら、安心して課金・周回できるようになりそうです。

## （追記）運営側が予測できないようにした修正版アルゴリズム

コメントで、あらかじめ運命が決まっているものを売るのはガチャとはいえないのではないか、という指摘をいただきました。
ごもっともです。

そこで、運営側が結果を予測できないように、利用者側でくじ番号を指定するような形にすると、問題を解消できると思います。
ガチャを引く回数をメッセージに追加するのではなく、ユーザが指定した任意の番号を追加するようにします。
同一番号を複数回指定させないようにする必要はありますが、ユーザに指定させることで、ガチャを引くたびにランダムにできます。

現実でも、ロト6など利用者側がくじの番号を指定するものがありますから、受け入れやすいように思います。
（自分と彼女の誕生日の番号で毎回ガチャを1回引く、みたいな使われ方などもありそうです）

また、多くのガチャは10回連続で引くと、レア保証されるなどのボーナス要素があります。
レア保証のボーナス要素は、10回引いて、レアがでなかった場合に、レアがでるまで一定の法則でメッセージを変えながらガチャを引き、レアがでた段階でその1枚を排出するということにすれば実現できます。

上記の要素を実装したシミュレータを作ってみました。

http://www.mikage.to/gacha/index2.html
Web 地図用 Javascript ライブラリ

こんにちは。
Web 地図用 Javascript ライブラリは世の中に色々あるようですね。

- [Leaflet](https://en.wikipedia.org/wiki/Leaflet_(software))
- [OpenLayers](https://en.wikipedia.org/wiki/OpenLayers)
- [Mapbox](https://en.wikipedia.org/wiki/Mapbox)
  - [Mapbox GL](https://www.mapbox.com/mapbox-gl-js/api/)
- [Google Maps](https://en.wikipedia.org/wiki/Google_Maps)
- [Bing Maps](https://en.wikipedia.org/wiki/Bing_Maps)
- [HERE Maps API for JavaScript](https://developer.here.com/documentation/maps/topics/overview.html)
- [Yahoo! JavaScriptマップAPI](https://developer.yahoo.co.jp/webapi/map/)
CakePHP3でデータベース変更の後カラムが更新されない件について

一回の処理で、一つのDBにデータを持ってきて、また、違うDBに保存する時
もし、この二つのテーブルの構造が若干違うのであれば、変に、前のテーブルのカラムが残されちゃって、

```
Column not found: 1054 Unknown colum　`Articles.status`
```
このように、勝手に二つ目のDBのテーブルを操作しているのに、一つ目のDBのテーブルの構造で処理を続く
色々調べても出て来なかったので、CakePHPソースコードを読んで`_processSave()`も`find()`も`$this->getSchema()->columns()`というメソッドを呼び出していて、見ると、こんな感じです

```Cake\ORM\Table.php
class Table implements RepositoryInterface, EventListenerInterface, EventDispatcherInterface, ValidatorAwareInterface
{
.
.
.
529行目
　　　　　　　　　　/**
     * Returns the schema table object describing this table's properties.
     *
     * @return \Cake\Database\Schema\TableSchema
     */
    public function getSchema()
    {
        if ($this->_schema === null) {
            $this->_schema = $this->_initializeSchema(
                $this->getConnection()
                    ->getSchemaCollection()
                    ->describe($this->getTable())
            );
        }

        return $this->_schema;
    }
```
ここは、一回テーブルの処理によって、一回しか呼ばれないので、`schema`はインスタンス化により、キャッシュされてしまうので
DBを変更するとき、こんな感じで書けば、レセットできますね

```ArticlesController.php
    $connection = ConnectionManager::get('default');
    $this->Articles->setConnection($connection)->setSchema(null);

```
CSS transitionを往復で非対称にかける場合の注意点

CSS Transitionを使うことで、JavaScriptなしでアニメーションを行えるようになりますが、凝ったことをしようとするとハマることがあります。

なお、典型的なパターンとして、「1つのクラスを付け外しする」ことでアニメーションさせるようなものを前提とします。

# ふつうにクラスを切り替えた場合

ある意味当たり前なのですが、Transitionが絡まない状態でクラスを切り替えれば、すべての値が新しいクラスで適用されるものに切り替わります。

# CSS Transitionの場合

`transition:`を指定すると、指定したプロパティは即座に変化せず、`transition`の指定に従って変化します。ここで注意しないといけないのは、`transition`プロパティ**自体**は、クラスの切り替えと同時に変化してしまう、ということです。つまり、変更後のクラスで適用される`transition`の指定に従って、プロパティが変化していく、ということになります。

## 非対称なアニメーション

とはいえ、ふつうは変わらないクラスにまとめて`transition`をかけてしまうので、どちらの状態でもかかる`transition`は同じとなるため、意識する必要はありません。

ただし、クラスを付けた時と外したときで違う`transition`をかけたい場合には、この差が意味を持ってきます。

```css
div.hoge{
  transition: (a)
}

div.hoge.expanded{
  transition: (b)
}
```

`div.hoge`に`.expanded`を追加した場合、直感的には(a)のほうの`transition`が適用されるように思うかもしれませんが、実際には`.expansion`をセットした段階で(b)が適用されるので、(b)の設定で`transition`します。逆に`.expanded`を外したときには(a)が適用されます。

あと、詳細度の加減で(a)に指定すれば(b)にも継承されますので、(b)の側を`transition`なしにしたい場合、(b)で`transition: none`のように指定して止めておく必要があります。
(Twitter凍結を食らった人たちへ) twilog.orgから今までに会話したことある人を取得する

# アカウントが凍結されると
Twitterのアカウントが凍結されると、ログインはできますがフォローやフォロワーが0となりいままで交流があった人達を探すのが困難になります。

# 今までに交流があった人達を探す
新しくアカウントを作ろうとしても、いままで交流があった人を探すのはめんどうです。
そこでtwilog.orgでアーカイブされているツイートからリプライを送った人を抽出すれば、楽に探すことができそうです。

※この方法はtwilog.orgに登録してある必要があります。
**※Twitterの基本機能として自分の過去のつぶやきのアーカイブをzipで貰えるものがありますが、凍結されるそのアーカイブを貰えなくなります。**

# 方法
twilog.orgにログインして、いままでのツイートをまとめたcsvをダウンロードする。
以下のextract_accountsを実行する。`python extract_accounts.py`

※exclude_rt_idをFalseにすると、いままでRTしたこと人も一覧にまとめます。

```python:extract_accounts.py
import csv
import re
import codecs

fname = "twilog.orgからダウンロードした.csv"
exclude_rt_id = True
out_text = True

id_extractor = re.compile(r"@[a-zA-Z0-9_]{1,15}")
all_ids = set([])

with open(fname, 'r') as f:
    reader = csv.reader(f)
    for row in reader:
        tweet_text = row[2]
        match_ids = []
        if not (exclude_rt_id and tweet_text.find("RT @") != -1):
            match_ids = id_extractor.findall(tweet_text)
        all_ids |= set(match_ids)
all_ids = sorted(all_ids)

if out_text:
    text = ""
    for at_id in all_ids:
        pure_id = at_id[1:]
        text += f"https://twitter.com/{pure_id}\n"

    f = codecs.open(fname+".txt", 'w', 'utf-8')
    f.writelines(text)
    f.close()
```

# 結果
いままでリプライを送ったことがある人を一覧にすることができます。

![tmp.jpg](https://qiita-image-store.s3.amazonaws.com/0/91558/55bb7901-f7c5-269a-c3af-0cf9861e73b2.jpeg)

~~この記事が誰かしらの役に立てばいいなあ。~~

# その他
もしTwitter凍結を食らって、このスクリプトの使い方がわからなかったら教えます。連絡ください。

# 宣伝
フォローお願いします:boy_tone1: [@redshoga](https://twitter.com/redshoga)

*続編(それぞれのTwitterアカウントの生存確認)につづく。
pythonでマルチスレッドで処理して各スレッドの結果を受け取る

# やりたいこと
pythonでマルチスレッド処理を行う

# コード
`threading`という標準モジュールを利用してできるみたいなので試してみる
python2とpython3両方とも同じように利用できるみたいで便利！

```py
#!/usr/bin/env python

import threading
import time

__author__ = "oomori"
__version__ = "1.0.0"

def myfunc(test_arg, results):
    """
    This is a test function of multithreading
    :param test_arg
    :return: None
    """
    print("call target_func")
    print("thread name is %s" % threading.current_thread().name)
    print(test_arg)
    output = test_arg * 2
    print("output is %d" % output)
    results[threading.current_thread().name] = output

def main():
    """
    main
    :return: None
    """

    results = dict()
    for _  in range(0, 10):
        threadlist = list()
        for thread_num in range(0, 5):
            thread = threading.Thread(target=myfunc, args=([thread_num, results]),-
                    name="thread%d" % thread_num)
            threadlist.append(thread)

        for thread in threadlist:
            thread.start()

        for thread in threadlist:
            thread.join()

        print("=== show each thread results ===")
        for k in results:
            print(k, results[k])
        time.sleep(5)

if __name__ == "__main__":
    main()  
```

`thread=`の部分でターゲットとなる関数とその引数、
さらにそのスレッドの名前を定義しています。
スレッドの名前は定義しない場合、"Thread-1"というようにつけられるそうです。

いったん`threadlist`に格納してから、startさせている理由は、
ほぼ同時に目的となる関数を実行させたいためです。
（リクエストをほぼ同時に投げるなど）

また、`thread.join()`で各スレッドが終了するまで待機することで、
`results`に全てのスレッドの結果が格納されます。

出力はこんな感じになります。

```
call target_func
thread name is thread0
0
output is 0
call target_func
thread name is thread1
1
output is 2
call target_func
thread name is thread2
2
output is 4
call target_func
thread name is thread3
3
output is 6
call target_func
thread name is thread4
4
output is 8
=== show each thread results ===
thread0 0
thread1 2
thread2 4
thread3 6
thread4 8


call target_func
thread name is thread0
0
output is 0
call target_func
thread name is thread1
1
output is 2
call target_func
thread name is thread2
2
output is 4
call target_func
thread name is thread3
3
output is 6
call target_func
thread name is thread4
4
output is 8
=== show each thread results ===
thread0 0
thread1 2
thread2 4
thread3 6
thread4 8

...
```

まだまだ使い方とか注意点等いろいろあるとは思いますが、
とりあえず、自分がやりたいことはできたので、よしとしよう


# 参考
- https://docs.python.jp/3/library/threading.html
- https://stackoverflow.com/questions/17707775/get-thread-id-or-name-in-python-2-6
- https://stackoverflow.com/questions/41206378/threading-error-in-python
- https://stackoverflow.com/questions/6893968/how-to-get-the-return-value-from-a-thread-in-python
Udemyのコース「【作りながら覚える！】 Ruby on Rails を用いたウェブ開発(Airbnbを作ろう！) 」を学ぶ。part 4-7,8 5-1

Udemyの[こちらのコース](https://www.google.de/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&cad=rja&uact=8&ved=0ahUKEwjBu4a35bfXAhXQ16QKHfxQCvIQFggyMAE&url=https%3A%2F%2Fwww.udemy.com%2Fruby-on-rails-g%2F&usg=AOvVaw2zf_7hwBdtTsJHjjVy_0-6)を受けながら、疑問に思ったこと、調べた結果、大事なことをまとめていきます


#part4-7 リスティング編集ページ(price)

ここちょっとむずいな・・・。動画の流れをおおう。

#自己課題
- 配布されたviewページ　price.html.erbを作成。
- 料金がデータベースに保存されるようにする。
- 更新されたら、そのままのページに移動し、flashで「更新されました」メッセージを表示。


#part4-8 リスティング編集ページ(publish)
#自己課題
- publish, addressのページを貼り付け。

#part5-1,2 paperclipの導入

画像をアップロードするための便利なgem　paperclipを

[ここ](https://github.com/thoughtbot/paperclip)です。

##モデルを作成

上記のサイトに従うと、作成するモデルはuserモデルに紐付けされるが、今回はlistingモデルに紐付ける。なので新しいモデルを作成。

    rails g model Photo listing:references 

とすると、こうなる。

```ruby:photo.rb
class Photo < ApplicationRecord
  belongs_to :listing
end
```

<img width="474" alt="スクリーンショット 2017-11-16 12.55.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/163321/46a1d9ac-b55f-9e88-42ed-933dc1472bf9.png">

また、listingモデルにも 
    has_many :photo と記載する必要がある。

また、db上でimageモデルを結び助けるために下を実行。

    rails g paperclip photo image

すると、
<img width="817" alt="スクリーンショット 2017-11-16 13.16.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/163321/81397c41-577c-8af6-fc6d-d4317ac611a7.png">

dbに追加される。

#自己課題
- paperclipの導入。
awkで行の計算

#何がしたいか
とあるcsvには各行の各列に数値が入っていて、それらを合計したい
awkで出来そうだなぁ、と思ってはいたものの、なぜか躓いてしまい、すぐにやりたいことを出来なかったので自分用メモとして残しておく

データは下記のようなcsvフォーマットでたくさんのレコードがあるイメージ

```csv:test.csv
abc,123,0,234,0,0,456,567
abc,123,0,234,0,0,456,567
abc,123,0,234,0,0,456,567
abc,123,0,234,0,0,456,567
```

アウトプットのイメージ

```bash:output
abc,1380
abc,1380
abc,1380
abc,1380
```

#どう解決したか
```bash:sample.sh
#!/bin/bash

FILE="test.csv"
cat $FILE | while read LINE
do
  ID=$(awk -F, '{ print $1 }' <<< $LINE)
  SUM=$(awk -F',' '{ for(i=2; i<NF+1; ++i){ sum+=$i } print sum }' <<< $LINE)
  echo "$ID,$SUM"
done
```

1行ずつ読み込む。LINEが1行ごとの変数になる
`cat $FILE | while read LINE`

IDを抜き出す(例の場合は"abc")。こういう書き方じゃなくてもいいかも知れないけれど、次でawkを使うので統一した
`ID=$(awk -F, '{ print $1 }' <<< $LINE)`

forを使い、2列目から最後の列までを足し、printで結果を表示する。が、それは変数SUMに代入される
`SUM=$(awk -F',' '{ for(i=2; i<=NF+1; ++i){ sum+=$i } print sum }' <<< $LINE)`

#ちなみに
`<<<`はヒアストリング
`<<`はヒアドキュメント
読み方を知らなかったので、これもメモしておく
アニメーション起動中のみ攻撃判定を表示する

```GetAnimationInfo.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GetAnimationInfo : MonoBehaviour {

    public GameObject obj;
    private AnimatorStateInfo stateInfo;
    private Animator anim;

    // Use this for initialization
    void Start () {
        anim = GetComponent<Animator> ();
    }
    
    // Update is called once per frame
    void Update () {
        
        stateInfo = anim.GetCurrentAnimatorStateInfo (0);
        if(stateInfo.IsName("Base Layer.アニメーションの名前")){
            obj.SetActive (true);
        }else{
            obj.SetActive(false);
        }
        
    }
}
```
Macに Ruby2.3.1 & npm をインストールする

## 概要
MacOSX にrbenvをインストールする作業のメモ

## rbenvのインストール
```
$ brew update
$ brew install rbenv ruby-build
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile
$ rbenv --version
rbenv 0.4.0
$ rbenv rehash
```
##  Rubyの2.3.1をインストールする
```
$ rbenv install --list	
Available versions:
	     ・
	  2.3.0
	  2.3.1
	  2.3.2
	     ・
$ rbenv install 2.3.1
```

## 現在のRubyのバージョン確認
```
$ rbenv versions
	  1.9.3-p547
	* 2.3.1 (set by /home/user/.rbenv/version)
```

## Ruby バージョンの選択
```
$ rbenv global 2.3.1
$ rbenv version
2.3.1 (set by /home/hoge/.rbenv/version)
```

## Bundlerのインストール
```
$ rbenv exec gem install bundler
```

## node.jsのインストール
npmコマンドを使えるようにするために、node.jsをインストールする

```
$ brew install node
$ which npm
/usr/local/bin/npm
```

## 参考サイト
* [rbenv を利用した Ruby 環境の構築](https://dev.classmethod.jp/server-side/language/build-ruby-environment-by-rbenv/)
オブジェクト指向言語に慣れたプログラマが、Goのメソッドでレシーバーを書く時に気をつけたいこと

Goのメソッドは、構造体に関数を定義できる機能だ。
だが、Java、PHPやJavaScriptのプログラマが大いに気をつけるべきポイントがある。
それが「レシーバー」の扱いだ。

例えば以下のようなJavaScriptのコードを見てほしい。

```js
var obj = {}
obj.setName = function(name){
	this.name = name;
}

obj.getName = function(){
	return this.name;
}

obj.setName("Taro");
console.log( "Name => " + obj.getName() ); // Name => Taro
```
obj にSetter関数とGetter関数を用意して、Setterに "Taro" を入れて呼び出す。すると自分自身の name プロパティに名前がセットされ、Getterで取得すると、"Taro" が戻ってくる。特に不思議なことはないだろう。

# Goで書いてみる

では同じことを Go で書いてみる

```go
package main

import "fmt"

type Obj struct {
	Name string
}

func (o Obj) SetName(name string) {
	o.Name = name
}

func (o Obj) GetName() string {
	return o.Name
}

func main() {
	o := Obj{}
	o.SetName("Hanako")
	fmt.Println("name => " + o.GetName()) // name => [空]  (!!!)
}
```
最後の行を見てほしい。
なんと、o.SetName("Taro")したにもかかわらず、o.GetName() の結果が「空」になってしまっている。

なぜなのか。

# 原因はレシーバーの書き方にあった

もう一度レシーバーの定義を見てみよう。

`func (o Obj) SetName(name string)` 
`func (o Obj) GetName() string`

レシーバーを (o Obj) と書いているので、Obj型の定義になっている。

ところがだ。実はGoではメソッド呼出時に "レシーバーそのものがコピーされる" という仕様があり、o.SetName("Hanako") と呼出たときに、メソッドの中に渡される o という変数は、呼出元の o とは実態が異なるのである。コピーされた o のプロパティを変えているのだから、その次の行で o.GetName したときに、値が空のままなのは当たり前なのである。

# どうすればよいのか

結論から言えば、レシーバーの型をポインタ型で書けばこのような問題は起こらない。(o Obj) の部分を (o *Obj) にすれば、ポインターのコピーになるので実態は同じになるのである。

さきほどのコードを、以下のように書き直してみよう。

```go
func (o *Obj) SetName2(name string) {
	o.Name = name
}

func (o *Obj) GetName2() string {
	return o.Name
}

func main() {
	o := Obj{}
	o.SetName2("Hanako")
	fmt.Println("name => " + o.GetName2()) // name => Hanako
}
```

無事、SetNameした値が取得できた。

一般的なオブジェクト指向言語から来た人間にとっては、あまり想定もしない挙動だと思う。レシーバーは便利な表現だけに、気をつけたいところである。
Rtab-mapのソースコードをいじってみたい

#はじめに
今回はRtab-mapのソースコードを改造していくには、どうしたらいいのかについて書いていきます。
といっても、私はそんなにプログラミングとか強い人じゃないので、あまり期待しないでください。
個人的なメモみたいなものです。

いじる方法としては
・ライブラリを使用したexamplesをいじる
・Rtab-mapのライブラリをいじる
の２つの方法があると思います。
それぞれ見て行きましょう。

#examples
では1つ目。
Rtab-mapをダウンロードすると、パッケージの中にexamplesというのがあります。
これはRtab-mapを開発しているintrolabが用意しているライブラリ使用例です。
(公式ウィキ：https://github.com/introlab/rtabmap/wiki)
見てみると4つほど例がありますね。
・BOWMapping
・NoEventsExample
・RGBDMapping
・WifiMapping

BOWMappingはぶっちゃけ見ていないので分かりません、BagOfWordsをどうこうしてる例なんだと思います。
NoEventsExampleは並列処理を用いずに単スレッドで処理している例。この例での使用カメラはステレオカメラです。
RGBDMappingはRGBDセンサを用いている例。Kinectとかfreenectを用意してあるなら、これですぐにマッピングできそうですね。他のRGBDセンサでも環境が整っていれば大丈夫だと思います。
WifiMappingはユーザーデータを付け足す際の使用例です。ユーザーデータ用のスレッドを立ててマップに反映するという内容です。例としてWifiの強度を反映させています。

#ライブラリ
Rtab-mapではPCLやUtiliteライブラリを用いて、Rtab-mapの基本動作に必要なスレッドを立てるためのクラスなどが用意されています。
正直な話、このあたりを改造するのは自分の能力に見合ってないし、自分としては特に目的も無いなぁと思うのでさわっていません。
まぁ、基本的な処理内容(計算とか)を変更したい場合はここを変えていく必要があるのでしょう。
なお、exampleをいじる際にutiliteのUEventとかUThreadあたりを見ておくと、読み解きやすいと思います。

#おわりに
私も研究頑張るので、皆さんもがんばって！！
C#:逆ポーランド記法を利用した数式の計算(4)　Contextクラスを定義する


## 記事一覧

[逆ポーランド記法を利用した数式の計算(1) 逆ポーランド記法の計算](https://qiita.com/gushwell/items/a40f4119a35a1e6d7622)
[逆ポーランド記法を利用した数式の計算(2) 数式をトークンに分割する](https://qiita.com/gushwell/items/58572bcff11315d77738)
[逆ポーランド記法を利用した数式の計算(3) ReversePolishNotationクラスを改良する](https://qiita.com/gushwell/items/1f58e270a5430bcc5dba)
[逆ポーランド記法を利用した数式の計算(4) Contextクラスを定義する](https://qiita.com/gushwell/items/03ed3063e9eacef84695) ← 当記事
[逆ポーランド記法を利用した数式の計算(5) Interpreterパターンで数式を解析する](https://qiita.com/gushwell/items/f598ee4c3e3f1a22604b)
[逆ポーランド記法を利用した数式の計算(6) 最後の仕上げ](https://qiita.com/gushwell/items/b2649335392fd366a691)


これまで、以下の３つのクラスを定義しました。

[ReversePolishNotation](https://qiita.com/gushwell/items/1f58e270a5430bcc5dba) :　    逆ポーランド記法を表すクラス  
[RpnCalculator](https://qiita.com/gushwell/items/a40f4119a35a1e6d7622) :　           逆ポーランド記法で表された式を計算するクラス  
[Tokenizer](https://qiita.com/gushwell/items/58572bcff11315d77738) :　               式をトークンに分解するクラス  

今回は、このなかの、ReversePolishNotationとTokenizerの２つのオブジェクトをまとめて管理するContextクラスを定義します。

## Contextクラス


Contextクラスは、たぶん必須のクラスではないと思います。しかし、数式を解析する際は、Tokenizerクラスでトークンを取り出しつつReversePolishNotationオブジェクトを組み立てていくことになるはずです。そのため、この２つのオブジェクトをContextクラスとして纏めてしまったほうが、取り扱いが楽になると思います。

実は、初めて作成したときは、このContextクラスは存在しなかったのですが、次回で説明する予定の数式を解析するコードがちょっとごちゃごちゃした感じになったので、途中でこのContextクラスを追加したという経緯があります。

ということで、そのクラスを以下に示します。


```csharp
public class Context {
    private Tokenizer _tokenizer;

    // 逆ポーランド記法を表すオブジェクト
    public ReversePolishNotation Notation { get; } = new ReversePolishNotation();

    // トークンの取得が終わったかどうかを示す。
    // MoveNextを呼び出した後でも、このプロパティを見れば、次があるかどうかがわかる。
    public bool IsTerminate { get; private set; } = false;

    // コンストラクタ　加減乗除を行う数式を引数で受け取る
    public Context(string exp) {
        _tokenizer = new Tokenizer(exp);
        _tokenizer.MoveNext();
    }

    // 次のトークンを取り出すためにカレントを移動する
    // 最初のトークンを取り出すには、まず、MoveNextを呼び出す必要がある。
    public bool MoveNext() {
        if (_tokenizer.MoveNext()) 
            return true;
        IsTerminate = true;
        return false;            
    }

    // 現在のトークン
    public string CurrentToken {
        get { return _tokenizer.Current; }
    }
}
```

Tokenizerオブジェクトをpublicにして、MoveNextメソッド、CurrentTokenプロパティは定義しなくてもよかったかなと思いますが、IsTerminateというプロパティを定義したかったので、だたら、MoveNextとCurrentTokenもこのクラスのあった方がいいかなと思い、このような形にしています。

ReversePolishNotationは、そのままプロパティとして公開しています。


このクラスの実装詳細の説明は省略します。コードとコメントを読んでください。

---

これで、RpnCalculator, Tokenizer, ReversePolishNotation, Context という4つのクラスを定義しました。

次は、いよいよ、数式の解析を行うクラスを定義します。インタープリター・パターンを使う予定です。


([続く](https://qiita.com/gushwell/items/f598ee4c3e3f1a22604b))...

---

この記事は、Gushwell's C# Programming Pageで公開したものを大幅に加筆・修正したものです。
BigQueryでカラム（列）の型変換を行う方法

# BigQueryでカラム（列）の型変換を行う方法

## BigQueryには列の型そのものを変える機能は存在しない
BigQueryには型変換を行える機能やAPIは存在しないので、いざ型変換をしなきゃいけないとなると、簡単じゃないです。

型変換できないので、列の型変換ではなく、新しい型の列を作成する方法で対応ですね。

なんでSchema変える必要があるんだ、というツッコミもありそうですが、Integerしかありえないと思っていた列に後からFloatのデータもいれる必要があったとか、マイナーではありますが、それなりに発生しそうな要件。

## 列の型変換ではなく、新しい型の列を作成
説明を簡単にするために、変換前テーブルにはInteger型の列が1列だけ存在し、変換後テーブル（新しく作成したもの）のFloat型の列に、IntegerからFloatに変換したデータを、お引越しするというシナリオで行きたいと思います。

これを
<img width="105" alt="スクリーンショット 2017-11-16 20.19.17.png" src="https://qiita-image-store.s3.amazonaws.com/0/171002/59559649-7d08-418a-1531-8c672baa6864.png">

こうする方法ですね。
<img width="103" alt="スクリーンショット 2017-11-16 20.19.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/171002/726594b8-ecc5-0332-b95c-3fb43b6be97b.png">

やり方は簡単でCAST（）関数を使うだけです。

## BigQueryのCAST()関数を利用
BigQueryのヘルプページ
https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators#casting

具体的なSQLはこちら。

```
SELECT CAST(value as FLOAT64) from tablename
```

あとはこれを新しいTableにインサートするだけで、IntegerからFloatにしてお引っ越しは完了です。

## 新しいテーブルへ変換後のデータをインサート
インサートの方法は色々あると思いますが、簡単な方法は　bqコマンドを使う方法。

--destination_tablオプションで新しいテーブル名を指定します。

CAST（）関数は標準SQLでのみサポートされるので、--use_legacy_sql=falseも合わせて指定します。

```
bq query --destination_tabl=new_tablename --use_legacy_sql=false "SELECT CAST(value AS float64) as value FROM tablename"
```
SendGridでメールを受信できない場合の調査に関して

#それは、突然の出来事
　Azure Portal 経由で利用している、SendGridから発信されたメールを会社のアカウントで受信できなくなった。
　プライベートなメールアドレス（gmail）ではメールを受信できる状態であったので、メールが送られていないわけでは無い模様。

#結論
　SendGridで送信しているIPアドレスが、SPAMメールの送信先として扱われていた。
　そして、会社はSPAMHOUSEを使っているようで、スパムメールの発信者として設定されていたために
　会社のアカウントではメールが受信できなくなっていた。

#対応
　Planの変更を行い、自分たち専用の発信用の固定IPを取得した。
　専用の固定IPを取得することで、スパムとして登録される可能性が減ると考えています。
Kerasで画像分類を実装する時の参考資料

特にオススメの記事には ⭐️ をつけました。
# 画像分類

- [keras(tensorflow)で花の画像から名前を特定](https://qiita.com/tsunaki/items/608ff3cd941d82cd656b)
- [ディープラーニングで美人女優の顔検出に挑戦](http://ai-coordinator.jp/face-recognition)
- [Kerasは相撲取りとアイドルを見分けられるかやってみた](https://qiita.com/hiroeorz@github/items/2fbb3b8d12b0e20f0384#_reference-99be2aef3ed42fdd155a)
- [Kerasによる、ものすごくシンプルな画像分類（りんごとオレンジ）](https://qiita.com/hiroeorz@github/items/ecb39ed4042ebdc0a957)

# + CoreML

- ⭐️ [Keras + iOS11 CoreML + Vision Framework による、ももクロ顔識別アプリの開発](https://qiita.com/kenmaz/items/d416b191f79f60e07752)
- [[iOS 11] Core MLで焼き鳥を機械学習させてみた](https://dev.classmethod.jp/smartphone/iphone/ios-11-core-ml-2/)

# Tips

- [Keras tips: 様々な画像の前処理をカンタンにやってくれるkeras.preprocessingのまとめ](http://www.mathgram.xyz/entry/keras/preprocess/img)
- [指定した人物の顔画像を自動収集するプログラムの公開と使い方](https://qiita.com/Umemiya/items/747934f18b00c026ce83)
- [自力で機械学習の訓練画像データを収集する(Google Custom Search API ピカチュウ編)](https://qiita.com/kazuooooo/items/37abb45c7806dbe31f14)
- [Python: Keras/TensorFlow の学習を CPU の拡張命令で高速化する (Mac OS X)](http://blog.amedama.jp/entry/2017/03/08/223308)
- [ハイパーパラメータをいじってグラフを見てみる](https://qiita.com/hiroyuki827/items/213146d551a6e2227810)
Qiitaへの初投稿から1ヶ月を自問自答のQ&A形式で振り返る

- 【Q】初投稿のキッカケは？
	- 非常に特殊な環境で仕事をする事になったので、久しぶりのPHPについては初心にかえるつもりで初心者向けの情報を、その他の事については日本語の情報がほとんどない情報を、アウトプットしてみようと考えました。
<br><br>
- 【Q】目標はあった？
	- 1ヶ月で15個の記事を投稿する事を目標としました。
<br><br>
- 【Q】記事を作成する上で気をつけていた事は？
	- プログラミングの上で、"Beauty Is in Simplicity(美はシンプルさに宿る)"という言葉は至言だと思っているので、記事の作成にもその考え方を当てはめようと考えました。クラス名で何をしたいかが一目で分かり各メソッドが簡潔なクラス と 良い記事…共通点が多いと思います。
	- 実際は、あれも書きたいこれも書きたい…となり、簡潔にまとめるのは想像していた以上に大変だと感じました。
	- サーバー関係の記事は、予防成分として「最低限のLinuxの知識がある方」とは書いていますが、実際はコピペするだけで誰にでもすぐ結果が得られるようにしました。
<br><br>
- 【Q】作成した記事に対する反省点は？
	- 全てを網羅しようとすると徐々に趣旨からズレてくる事があるので、時にはバッサリ切り捨てる事が重要だと感じました。Simplicity Comes from Reduction.
	- ネタは幾らでもあった筈なのに、いざ記事にしようとすると大変で、目標の15記事/月を達成するために、最後の2つは完全に数合わせのネタ記事になりました。
	- 説明が冗長になったりポエムを書きたくなったりするのは、年をとった証拠だと確信しました。
<br><br>
- 【Q】Qiitaで記事を作成する際に感じた事は？
	- 「間違った事を書いてはいけない」というプレッシャーを感じました。Qiitaの仕様上決してそういう訳でもない筈なのですが、他所とは違い、「いい加減な事を書くな」という独特の雰囲気はあると思います。それを否定している訳ではなく、そういう場も必要であると思います。
<br><br>
- 【Q】Qiitaで記事を作成した後に感じた事は？
	- 意図的に切り捨てた部分を指摘されると、正直なところ「面倒くさい」です。「便宜上◯◯とします」「◯◯は意図的に切り捨てます」といちいち書くのも、正直なところ「面倒くさい」ですが、それはある程度は必要な事だと思います。
	- ただ、前後の文脈から簡単に類推できたりググればすぐ分かる略語の説明を、記事の出だしにいちいち書いていると、徐々に「面倒くさい」気持ちの方が勝ってくるというのが正直な気持ちです。もし今後記事を作成する機会があれば、そういう説明は省略すると思います。
<br><br>
- 【Q】これから初めてQiitaで記事を作成してみようと考えている方々へ何か一言
	- 第三者へ情報を分かりやすく伝えるために記事を書く事は、様々な面で非常に有意義だと思います。特に初学者が記事を作成するのは相当な勇気が要ると思いますが、絶対に無駄にはならないと思います。
	- カレーと同じで、書いた記事は一晩寝かした方が良いです。そしてこうやって二言目を書いてしまうのが年をとった証拠です。
<br><br>
- 【Q】Qiitaで記事を作成している方々へ何か一言
	- 不幸な事に、誰かに聞けば何かを教えてもらえる環境で仕事をした事がほとんど無いので、Qiitaの情報には何度も何度も助けられてます。素晴らしい情報を提供くださっている方々に、心より感謝します。
	- Wikipediaに対するアンサイクロペディアのような、Qiitaに対する何かを作ったら、面白いかもしれないですね。

--------------------------------------------------------------------------------

- 【Q】Qiitaへの要望は？
	- コードの折りたたみと展開ができると、~~ネタ記事作成が捗って~~良いかなと思います。
<br><br>
- 【Q】Qiita運営へのメッセージ
	- 良い意味でも悪い意味でも一般の人とは異なる感性を持つ人が多く集まるので、運営は大変だろうなぁ…と察します。
	- 情報の専門性が高いので、記事のチェックを素人に任せるわけにもいきませんし、チェック要員の人件費も一般よりはお高めなのかなぁ…とも思ったり。
	- 広告収入に頼らないビジネスモデルでどこまでいけるか、これからも注目しています。
<br><br>
- 【Q】Qiitaの規約について一言
	- 保険として、運営 ＝ 全知全能の神となれる内容を規約に書いておくのは当たり前だと思います。
	- 明確な線引きは非常に大切ですが、[イッヌハブ をつくってみよう](https://qiita.com/hiroshitoda/items/6df550386e83bc6f1474)のようなセンスを許容する懐の広さにも期待しています。
<br><br>
- 【Q】Kobitoについて一言
	- Windows版しか使ってませんが、Electronベースのメリットをもう少し活かして欲しい…。特にデータ構造については、シンボリックリンクで無理やりデータ同期させるのは怖いです。有料プランを使ってねという事で仕方ないのかな…。同じElectronベースのBoostnoteと、切磋琢磨して欲しいなと思います。
<br><br>
- 【Q】Qiitaへ最後に一言
	- 質問サイトではないのにQiita(キータ)というところに、Qiitaの奥深さを感じます。
詫びソースコード

※ただの写経です。

```cpp
CardDatas CardModel::getMasterCardDatasByIds(const std::vector<uint32_t>& masterCardIds) const
{
  vector<CardDataPtr> results;
  results.resize(masterCardIds.size());
  size_t exists = 0;
  
  for (int i = 0; i < masterCardIds.size(); i++) {
    auto p = results[i] = _cardDataCache.fetch(masterCardIds[i]);
    if (p != nullptr) {
      exists++;
    }
  }
  
  if (masterCardIds.size() == exists) {
    return results;
  }
  
  string sql = form("SELECT * FROM cache.cards where id IN (%s);", join(masterCardIds, ",").c_str());
  
  int i = 0;
  DatabaseManager::getInstance()->query(sql, [this, &results, &i](sqlite3_stmt* stmt) {
    if (results[i] == nullptr) {
      results[i] = _cardDataCache.store(make_shared<CardData>(stmt));
    }
    i++;
  });
  
  return results;
}
```
Gitlabのサーバサイドフックでユーザ名を得る

# はじめに

以前[Gitlabのサーバサイドフックでユーザ情報を得る](https://qiita.com/dalance/items/4350e7db0bd34df576ac)という記事を書きました。
サーバサイドフックから環境変数`GL_ID`でユーザIDを得られる、という内容でしたが
Gitlab10.1よりユーザ名を直接得られるようになりましたのでアップデートしておきます。

# 環境変数`GL_USERNAME`

[こちらのMR](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13264/diffs)にて環境変数`GL_USERNAME`が追加されました。
ユーザ`admin`だけ`master`ブランチにpushできるupdateスクリプトの例を示します。

```bash
#!/bin/bash

branch="$1"

if [ "$branch" == "refs/heads/master" ]; then
    if [ "$GL_USERNAME" == "admin" ]; then
        exit 0
    else
        echo "-------------------------------------"
        echo "[$branch] is protected"
        echo "-------------------------------------"
        exit 1
    fi
fi

exit 0
```
Azure App Service 価格レベル Standard、Premium、Premium V2 のストレージパフォーマンスを比較する

# はじめに
Azure App Service の新しい価格レベル Premium V2 は、ドライブが SSD 化されてますので、HDD とどのくらいパフォーマンスが違うのかベンチマークをしてみました。
# 比較方法
マイクロソフト製ストレージテストツール「[A Robust Storage Testing Tool](https://gallery.technet.microsoft.com/DiskSpd-a-robust-storage-6cd2f223)」を使います。コア数は1を選択しています。
テストは、以下の様の引数で実施しています。

`diskspd -c512m -b64k -d30 -w5 bench.bin`
(テストファイルサイズ 512MB、ブロックサイズ 64KB、書き込み割合5%)

# 結果
キャッシュ無しだと SSD と HDD の違いはほとんどなく、キャッシュ有りだと、Premium は Standard よりも1割程度早く、Premium V2は SSD 化されてあるのが効いていて、Premium の2.5倍程度高速という結果になりました。

### Premium V2 キャッシュ無し
|         |       bytes     |     I/Os     |     MB/s   |  I/O per s |
|-------: |----------------:|-------------:|-----------:|-----------:|
|Total IO |       167706624 |         2559 |       5.33 |      85.27 |
|Read IO  |       159252480 |         2430 |       5.06 |      80.97 | 
|Write IO |         8454144 |          129 |       0.27 |       4.30 |
### Premium V2 キャッシュ有り
|         |       bytes     |     I/Os     |     MB/s   |  I/O per s |
|-------: |----------------:|-------------:|-----------:|-----------:|
|Total IO |     65012236288 |       992008 |    2055.60 |   32889.59 |
|Read IO  |     61766107136 |       942476 |    1952.96 |   31247.38 | 
|Write IO |      3246129152 |        49532 |     102.64 |    1642.21 | 
### Premium キャッシュ無し
|         |       bytes     |     I/Os     |     MB/s   |  I/O per s |
|-------: |----------------:|-------------:|-----------:|-----------:|
|Total IO |       237240320 |         3620 |       7.54 |     120.66 |
|Read IO  |       224722944 |         3429 |       7.14 |     114.29 |
|Write IO |        12517376 |          191 |       0.40 |       6.37 |
### Premium キャッシュ有り
|         |       bytes     |     I/Os     |     MB/s   |  I/O per s |
|-------: |----------------:|-------------:|-----------:|-----------:|
|Total IO |     26608730112 |       406017 |     843.21 |   13491.36 |
|Read IO  |     25274482688 |       385658 |     800.93 |   12814.86 |
|Write IO |      1334247424 |        20359 |      42.28 |     676.50 |
### Standard キャッシュ無し
|         |       bytes     |     I/Os     |     MB/s   |  I/O per s |
|-------: |----------------:|-------------:|-----------:|-----------:|
|Total IO |       192479232 |         2937 |       6.12 |      97.85 |
|Read IO  |       182583296 |         2786 |       5.80 |      92.82 |
|Write IO |         9895936 |          151 |       0.31 |       5.03 |
### Standard キャッシュ有り
|         |       bytes     |     I/Os     |     MB/s   |  I/O per s |
|-------: |----------------:|-------------:|-----------:|-----------:|
|Total IO |     22426550272 |       342202 |     710.04 |   11360.72 |
|Read IO  |     21302411264 |       325049 |     674.45 |   10791.26 |
|Write IO |      1124139008 |        17153 |      35.59 |     569.46 |
adbコマンドを使ってファイルを端末に転送する


## やりたい事

- Androidアプリのファイルアップロードの検証をする際に、アップロード用のダミーデータを端末に送る
- 別途、ファイラなどのアプリをダウンロードはできればしたくない

## 環境

- Mac : MacBook Pro (Retina, 15-inch, Mid 2015)
- adb : 1.0.39

## adbとは

Android Debug Bridge（adb）は、
>
エミュレータ、インスタンスや接続されたAndroid端末と通信できる用途の広いコマンドラインツールです。

参考: https://developer.android.com/studio/command-line/adb.html?hl=ja

## adbのインストール

Android StudoをインストールするとGUI（SDK Manager）からAndroid SDKをインストールする事ができます。

![スクリーンショット 2017-11-16 14.35.21.png](https://qiita-image-store.s3.amazonaws.com/0/25250/9cb647ea-a0cc-50bf-899d-940680c1737b.png "スクリーンショット 2017-11-16 14.35.21.png")

SDKがどこにインストールされたかは、設定の「Appearance & Behaivior」->「System Settings」->「Android SDK」で確認する事ができます。

![Untitled11.png](https://qiita-image-store.s3.amazonaws.com/0/25250/3f3d5c87-739d-d4d6-026a-a1098ac9cc3a.png "Untitled11.png")


今回使用するadbは、android_sdk/platform-tools/ にあります。

次に上記のpathを通します。ご自分のシェルの環境にあった方法でPathを通してください。こんな感じですかね。

```.bashrc
export PATH=$PATH:/Users/<user name>/Library/Android/sdk/platform-tools
```

<user name>は適宜置き換えてください。

完了したら ```adb``` とターミナルに入力して、

```
$ adb
Android Debug Bridge version 1.0.39
...
```

みたいに応答があればOKです。
「command not found」が表示されてします時は、ターミナルの再起動または、リソースファイル ```.bashrc``` などの再読み込みをお試しください。

## adbコマンドを使う

[このように](https://developer.android.com/studio/command-line/adb.html#issuingcommands)adbには沢山の機能があります。また、端末でのデバッグはUSB経由とWifi経由とありますが、今回はUSB経由で行います。

### ・端末の照会

端末を確認するには、``` adb devices``` コマンドを使用します。

```
$ adb devices
List of devices attached
* daemon not running. starting it now at tcp:5037 *
* daemon started successfully *
<serial_number> device
```

### ・端末のディレクトリ構成を確認する

``` adb shell``` コマンドを実行すると（USBで繋いでいる端末が１つでemulatorが立ち上がっていない）端末にアタッチする事ができます。なので、そこで ```ls -la``` などを実行する事で構成を確認する事ができます。
今回は ```/sdcard/Download/``` にファイルを転送したいと思います。

### ・端末にファイルを転送する

開発PCから端末へファイルを転送する場合は ```adb Push``` コマンドを使用します。

```
$ adb push /path/to/local_file /path/to/device 
```

例えば

```
$ adb push ~/temp.png /sdcard/Download/
```

こんな感じです。

## :end: 終わりに

いかがだったでしょうか。まだまだ使いこなせているというレベルにはほど遠いですが、少しずつ試していきたいと思います。他に何ができるか知りたい方は[ドキュメント](https://developer.android.com/studio/command-line/adb.html)に記載されていますので、確認してみてください。
最後までお読みいただきありがとうございました。

## :paperclip: 参考

- [複数のAndroid端末をUSBで接続しているときのadbコマンドTips](http://yktmnb.b.osdn.me/?p=20)
- [Android Debug Bridge](https://developer.android.com/studio/command-line/adb.html?hl=ja)
- [macでandroidSDKを使う方法【pathを通すまで】](http://monozukit.hatenablog.com/entry/2017/01/29/mac%E3%81%A7androidSDK%E3%82%92%E4%BD%BF%E3%81%86%E6%96%B9%E6%B3%95%E3%80%90path%E3%82%92%E9%80%9A%E3%81%99%E3%81%BE%E3%81%A7%E3%80%91)
- [adb コマンド リファレンス](https://developer.android.com/studio/command-line/adb.html#issuingcommands)
Ruby 分岐ごとのカウントについて

Xを変数として①の条件により+1or-1を追加していき、Xの偏りが+5or-5になったら②or③に移行する。そして②、③に移行して条件をクリアしたらリセットされてまたループするというコードを書きたいのですがうまくいきません。
ここまでは雰囲気で書いたのですが思い通りに動いてくれません、いい方法ありませんか？よろしくお願いします。


loop do
X = 0
---------------------------------------------①
if ほげほげ
X = X - 1
elsif ほげほげ
X = X + 1
--------------------------------------------------②
if X > 5
X = X - 5
------------------------------------------------- ③ 
if X < -5
X = X + 5

end
bash > mail送信を検知する (誤動作するかもしれない) | cronによるメールで誤動作する

```txt:動作環境
Xeon E5-2620 v4 (8コア) x 2
32GB RAM
CentOS 6.8 (64bit)
openmpi-1.8.x86_64 とその-devel
mpich.x86_64 3.1-5.el6とその-devel
gcc version 4.4.7 (とgfortran)
NCAR Command Language Version 6.3.0
WRF v3.7.1を使用。
Python 2.6.6 (r266:84292, Aug 18 2016, 15:13:37) 
Python 3.6.0 on virtualenv
```

### 動作システム

- メールは送信のみを行い、受信はしない
- 特定のスクリプト（またはコマンド）でのみメール送信する

この状況において「メール送信した」を検知したい。

### 参考

https://serverfault.com/questions/434641/how-to-find-out-what-program-is-sending-emails

`/var/log/maillog`を使う。

ただし、中身を見るにはsudoなどが必要になるので、サイズだけをチェックする。

### code

```bash:check_sendmail_171116_exec
#!/usr/bin/env bash

# v0.1 Nov. 16, 2017

size1=$(ls -l /var/log/maillog | awk '{print $5}')

while [ 1 ];do
  size2=$(ls -l /var/log/maillog | awk '{print $5}')
  if [ $size1 -ne $size2 ]; then
    break
  fi
  sleep 1
done
```

### 使用例

一つのターミナル(ターミナルA)で以下を実行する。

```bash
$ bash check_sendmail_171116_exec && echo "mail sent"
(待機中)
```

もう一つのターミナル(ターミナルB)で以下を実行する (メールアドレスは仮)。

```bash
$ echo "TEST" | mail -s "test 18:44" 7of9@borg-corp.com
```

ターミナルA側に`mail sent`が表示される。

### 用途

一つのスクリプト終了後にmail送信するようにしているとする。
その作業が終わった時に、次のスクリプト（別のディレクトリにある）を開始したい時に使えるかもしれない。

一つのスクリプトに実行スクリプトを直接記載するよりも柔軟なシステムにできる。

### 懸念事項

`/var/log/maillog`に追記されるイベントがあると誤動作する。

毎日20:30にメールが届いている。バックアップ処理のメールのようだ。そのため誤動作する。

rsyncを使うcronがメールを送信していた。
[stackoverflow](https://serverfault.com/questions/224869/stop-rsync-from-sending-confirmation-e-mails)に記載の方法でメール送信しないように変更した。

### 関連

https://qiita.com/7of9/items/8a2cbeda4ea86bb5a115