OmniAICreator/Qiita-1.07M huggingface dataset:
ホストマシンの環境変数(proxy周りとか)を反映したDockerfile/docker-compose.ymlを生成する

proxy環境下でdockerを使っているとあらゆるdocker imageにproxy設定しないといけないので面倒ですね。
ホストマシンの環境変数を反映したDockerfile/docker-compose.ymlを生成するツール、[docker-env](https://github.com/mpppk/docker-env)を使ってシュッと`docker run`しましょう。

## インストール

[このページ](https://github.com/mpppk/docker-env/releases)からプラットフォームにあったバイナリをダウンロードします。

golangの実行環境がある方は以下でもOKです。

```Shell
$ go get github.com/mpppk/docker-env
```

## 使い方
`docker-env <docker image名>`を実行すると、ホストマシンの環境変数がひたすら設定されたDockerfileが生成されます。
全部は要らないので、`--query`オプションで絞り込みましょう。`-q`でも大丈夫です。
例えばproxy関連の環境変数(`HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`)を設定したい場合、以下のようにします。(正規表現が使えます。)

```Shell
$ docker-env -q proxy node:latest
```

これで以下のようなDockerfileが生成されます。

```Dockerfile
FROM node:latest
ENV HTTP_PROXY http://proxy.example.com
ENV HTTPS_PROXY http://proxy.example.com
ENV NO_PROXY willbeignored.com
```

`--format`か`-f`オプションで`compose`を指定すると、docker-composeファイルが生成されます。

```Shell
$ docker-env -q proxy -f compose node:latest redis:latest
```

```yaml
services:
  nodelatest:
    environment:
      HTTP_PROXY: http://proxy.example.com
      HTTPS_PROXY: http://proxy.example.com
      NO_PROXY: willignore.com
    image: node:latest
  redislatest:
    environment:
      HTTP_PROXY: http://proxy.example.com
      HTTPS_PROXY: http://proxy.example.com
      NO_PROXY: willignore.com
    image: redis:latest
version: "3"
```

これでバリバリコンテナを立てられますね。
proxy周り以外でもdocker-envの良い使い方があれば教えてください。
【gitコマンド】いまさらのrevert

===== **20190430追記** =====
わかりにくい表現を修正しました。
内容の変更はしていません。
===== **追記ここまで** =====

ちょこちょこ使う機会はあったのですが、いまいち使いこなせていなかったrevertについて改めて調べました。

# revert とは
既存のコミットを取り消すためのコマンドです。
「取り消したいコミットを打ち消すようなコミットを新しく作成する」という処理によって、既存のコミットを取り消します。
新しくコミットを追加しているだけなので、既存コミットの履歴が消えるわけではありません(コミットログをみると残っています)。
どんな変更があったのかということが(revertしたということも含めて)残るので、リモートにpushされて公開されているコミットに対しても安全に使うことができます。

「既存のコミットを元に戻す」という点について、同じような機能を持つコマンドに`reset`が存在します。
`reset`コマンドは、「コミットを取り消した」というコミットが残りません。特にリモートにpush済みで公開されているコミットに関して使うことはNGです。気をつけて使用しましょう。

また、`revert`コマンドは`特定のコミット`を元に戻すコマンドです。
指定したコミット以降に行なわれた処理を全て戻す(この機能は`reset`コマンドに含まれます)わけではありません。

# コマンド

### 特定のコミットを取り消す

```bash
$ git revert <commit>
```

コミットIDを指定することで、そのコミットを打ち消すようなコミットが新しく追加されます。
コードは、そのコミットがなかったときの状態になります。
revertコマンドを実行するとエディタが開き、コミットメッセージを編集することができます。

### コミットメッセージ編集

revertコミットを行なうときに、コミットメッセージの編集を行なうかどうかをオプションで指定できます。

コミットメッセージを編集する(オプションを何も付けない場合はこちらになります)

```bash
$ git revert <commit> -e 

or

$ git revert <commit> --edit
```

コミットメッセージを編集しない(エディタを開かない)

```bash
$ git revert <commit> --no-edit
```

### コミットしない
revertコマンドを使用するとコミットまで行なわれますが、indexに戻すだけでコミットを行なわないようにすることもできます。
複数のコミットをrevertするときに、一度にコミットすることができるので便利です。

```bash
$ git revert <commit> -n

or

$ git revert <commit> --no-commit
```

### マージコミット

マージコミットを取り消そうとした場合、マージした2つのコミット(親)のうちどちらに戻すのかを指定する必要があります。
`-m`オプションの後に戻したい親を数字(基本的に1もしくは2)で指定し、`revert`を実行します。

```bash
$ git revert -m 1 <commit>
```

`git show`コマンドや`git log`コマンドで対象のマージコミットを見ると、親の数字(1もしくは2)がわかります。

```bash
$ git show
commit xyz
Merge: 1a1a1a 2b2b2b    #ここに注目
Author: xxxx xxxx
Date:   Thu Jul 13 09:00:00 2017 +0000

    Merge commit

```

`revert`したいマージコミットが、この「xyz」という番号のコミットだとします。
このマージコミットは、「1a1a1a」というコミットに「2b2b2b」というコミットがマージされてできたものです。

番号は、ログを表示させたときの、「Merge:」という行に書かれている順番に1、2とつきます。
この場合は、「1a1a1a」の番号が1、「2b2b2b」の番号が2となります。
Python で is と == の使いドコロを迷う人のためのTips

# is はオブジェクトの比較で使うべし
Pythonでは、 `if x is y` や `if x == y` と書くことがあります。
その際に、`is`と`==`で迷ってしまう場合があります。
そこで、`is`と`==`の違いを以下にまとめました。

- `is`はオブジェクトの比較を行う（同一のオブジェクトかどうか）
- `==`は値の比較を行う（同一の値かどうか）

※厳密には、オブジェクトの比較ではなく、「**オブジェクトのIDの比較**」であり、（結果的には）「**アドレスの比較**」となります。*脚注1,2*

## ユースケース（isを使う場合）
``x = <object A>``
を定義していて、
``if x is <object A>:``
という条件式を書きたい場合が該当します。

もちろん、True/False比較のために
``x = True``
を定義して、
``if x is True:``
と書くことは、間違いではありませんが、この場合に関してはpythonic的に
``if x:``
と書くのが良いです。

→なぜ、 x is Trueでも問題ないかというと、boolクラスのTrue/Falseは、常にひとつだけしか存在しないように設計されているためです。

    The values False and True will be singletons, like None.

引用元
[PEP285](https://www.python.org/dev/peps/pep-0285/)

## ユースケース（==を使う場合）
``x = 1234``
を定義していて、
``if x == 1234:``
という条件式を書きたい場合が該当します。

# まとめ
is と == を混同せずに、しっかりと使い分けましょう。 

# 脚注
脚注1
@7of9 さんよりコメント欄でご提示頂いた内容
> https://docs.python.org/3.6/library/functions.html#id

>>    id(object)
    ...
    CPython implementation detail: This is the address of the object in memory.

がドキュメントで根拠となる部分です。

脚注2
@shiracamus さんよりコメント欄でご提示頂いた内容
> CPythonのソースを調べてみました。
"is" は Is に置き換えられ、Is は PyCmp_IS に置き換えられ、PyCmp_IS は以下のようにアドレスを比較してました。
>
```c:Python/ceval.c
static PyObject *
cmp_outcome(int op, register PyObject *v, register PyObject *w)
{
    int res = 0;
    switch (op) {
    case PyCmp_IS:
        res = (v == w);
        break;
    case PyCmp_IS_NOT:
        res = (v != w);
        break;
```

のように、最終的にはアドレス比較を行っています。
PowerShellの多次元配列


#PowerShellの多次元配列の要素数を可変で使えないか試してみました。

##処理対象ファイル

```text:file1.txt
a1
a2
a3
a4
```

```text:file2.txt
b1
b2
b3
b4
```

##目標とする結果

```console:console
a1,b1
a2,b2
a3,b3
a4,b4
```

##実際に書いたコード
ファイル読み込みやループ処理を省略してます。

```powershell:sample.ps1
# 変数宣言
[string[][]]$columns = @()
[string[]]$column = @()

# file1.txtの内容を$columnに要素を追加して代入
$column += "a1"
$column += "a2"
$column += "a3"
$column += "a4"

# $columnを配列として$columnsを代入
$columns += ,@($column)

# $columnを空にする
[string[]]$column = @()

$column += "b1"
$column += "b2"
$column += "b3"
$column += "b4"

# 再度$columnを配列として$columnsを代入
$columns += ,@($column)

# 以降ファイル数分を繰り返す。

Write-Host ($columns[0][0] + "," + $columns[1][0])
Write-Host ($columns[0][1] + "," + $columns[1][1])
Write-Host ($columns[0][2] + "," + $columns[1][2])
Write-Host ($columns[0][3] + "," + $columns[1][3])
```

##出力結果

```console:console
a1,b1
a2,b2
a3,b3
a4,b4
```

##ポイント
配列を代入する時はこんな書き方するみたいです。

```powershell:point
$columns += ,@($column)
```
loggingによるログ出力でtracebackの表示が欲しいときにはstac_info,exc_infoを使うと便利

## はじめに

[logging](https://docs.python.jp/3/library/logging.html)のloggerを使ってログ出力をする際に、発生した場所を把握するために位置情報も一緒に出力したい時がある。有り体に言えば、tracebackが欲しい。
このtracebackをログに含める時にどうすると良いかという話。

## 愚直な方法

もちろん自分でtracebackをログに付加してあげれば良いという話もある。以下のように。

```python
import logging
import traceback
logger = logging.getLogger("sample")


def foo():
    bar()


def bar():
    logger.info("bar")
    logger.info("tb: %s", "".join(traceback.format_stack()))


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    foo()
```

一応tracebackは表示される。

```
INFO:sample:bar
INFO:sample:tb:   File "qr_64852sBQ.py", line 18, in <module>
    foo()
  File "qr_64852sBQ.py", line 8, in foo
    bar()
  File "qr_64852sBQ.py", line 13, in bar
    logger.info("tb: %s", "".join(traceback.format_stack()))
```

とは言えわざわざ自分でtracebackを取ってくると言うのは面倒ではあるし。そもそもログ出力のためだけにpythonの知識(`import traceback`)を要求するのはおかしい。

## stack_info

実際、上のようにtracebackを自分で付与する必要はなく、loggerでログ出力する時に `stack_info=True` をつけてあげれば良い。

```diff
--- 00sample.py	2017-07-13 08:06:10.000000000 +0900
+++ 01sample.py	2017-07-13 08:09:06.000000000 +0900
@@ -1,6 +1,5 @@
 # -*- coding:utf-8 -*-
 import logging
-import traceback
 logger = logging.getLogger("sample")
 
 
@@ -9,8 +8,7 @@
 
 
 def bar():
-    logger.info("bar")
-    logger.info("tb: %s", "".join(traceback.format_stack()))
+    logger.info("bar", stack_info=True)
```

実行してみると `Stack (most recent call last)` 以降にtracebackが表示される。

```
INFO:sample:bar
Stack (most recent call last):
  File "qr_64852GWc.py", line 16, in <module>
    foo()
  File "qr_64852GWc.py", line 7, in foo
    bar()
  File "qr_64852GWc.py", line 11, in bar
    logger.info("bar", stack_info=True)
```

## exc_info

ログの出力時の位置ではなく例外の発生位置のtracebackを表示したいこともある。例えば `logger.exceptipn()` などを使った場合と同様の形式。
これには `stack_info` の代わりに `exc_info` を渡せば良い。

```python
import logging
logger = logging.getLogger("sample")


def foo():
    bar()


def bar():
    logger.info("bar")
    1 / 0


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    try:
        foo()
    except:
        logger.info("hmm", exc_info=True)
```

zero division errorの発生位置のtracebackは例外オブジェクトから抽出され自動で併記してくれるようになる。

```
INFO:sample:bar
INFO:sample:hmm
Traceback (most recent call last):
  File "qr_64852t0u.py", line 18, in <module>
    foo()
  File "qr_64852t0u.py", line 7, in foo
    bar()
  File "qr_64852t0u.py", line 12, in bar
    1 / 0
ZeroDivisionError: division by zero
```
【画像付き】typetalkでRSS Feed botを作る方法

# なぜTypetalkでRSSの通知を受け取りたいか
slackとか楽に設定できるので、正直言って羨ましいと思いました。
業務の都合でtypetalkしか使えないので、なんとかできないものかと調べました。

# RSS Feed にはIFTTTを使う
RSSの通知がきたら、typetalkにPOSTする処理は、IFTTTを使えば簡単にできます。

### IFTTTとは

>このサービスを簡単に言うと、Webサービスの「仲介役」みたいな感じですね。

[作りながら覚える「IFTTT」レシピの使い方活用講座！（全5回）](http://hoomey.net/ifttt_study_1/)

### typetalkの設定
通知したいトピックに対して、botを設定しましょう

トピックの設定のボットの追加を選択
<img width="961" alt="スクリーンショット 2017-07-13 0.55.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/142918/bf7111a5-44ad-5a26-8885-c80097066304.png">

名前だけ入力する
<img width="966" alt="スクリーンショット 2017-07-13 0.55.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/142918/7d0c2f48-bd6a-8711-45a6-f78742a6990b.png">

「Typetalk Token」と「メッセージの取得と投稿のURL」が表示される
<img width="963" alt="スクリーンショット 2017-07-13 0.56.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/142918/84a77c77-5227-6291-d4a3-a883a7d39271.png">

### IFTTTの設定
`New Applet`を選択
<img width="1374" alt="スクリーンショット 2017-07-13 0.56.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/142918/9f477e0d-1d29-ad9b-0583-833e5de98bc4.png">

`this`は`RSS Feed`を選択
<img width="1368" alt="スクリーンショット 2017-07-13 0.57.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/142918/111044ab-4c8e-7111-28d4-5d4a177ca02c.png">

`RSS Feed`の中から、`New feed item`を選択
<img width="971" alt="スクリーンショット 2017-07-13 0.57.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/142918/8f5923ea-1e32-2a1c-c16b-233c5d3b41e8.png">

`Feed URL`にRSSのURLを入力して`Create trigger`をクリック
<img width="1361" alt="スクリーンショット 2017-07-13 0.58.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/142918/5957087e-ac3d-19c8-9e6c-0b1df5c635eb.png">

`that`は`webhook`を選択
<img width="1376" alt="スクリーンショット 2017-07-13 0.59.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/142918/65e74cef-9386-5d43-aa32-6cb147872ec1.png">

`URL`はTypetalkの「メッセージの取得と投稿のURL」をコピー
`Method`はPOST
`Content Type`はappliction/x-www-form-url...を選択

<img width="540" alt="スクリーンショット 2017-07-13 1.01.41.png" src="https://qiita-image-store.s3.amazonaws.com/0/142918/76889f01-0ed7-f418-4feb-d815d3fc0747.png">

`Body`は`message= {{EntryUrl}} {{EntryTitle}}`を入力
そして`Create action`をクリック
<img width="597" alt="スクリーンショット 2017-07-13 1.02.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/142918/74f6074e-1ab5-6115-e505-34dc669f3fdd.png">

しばらくするとTypetalkにbotからRSSの更新が通知されてます!!!!
<img width="1231" alt="スクリーンショット 2017-07-13 7.31.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/142918/7e66738d-90fc-8cff-4ebb-1bf6f4a53a04.png">

### 最後に
どうでした?簡単でしょう
ちなみに、この記事に記載しているbotは削除しているのでトークンは無効化されています。
ゲームジャム運営

ゲームジャム最高

と思うのは

- ゲームが出来上がる
- ゲーム作りのスキルが上がる
- ゲーム作り仲間が増える

そんな場を作る

「ゲームジャムを運営してみたい」という方もいそうなので、
まとめてみました。

# 運営としてやるべきことは

## 問題が発生したら対応する
　事故や体調不良などおきないように誘導する　万が一起きた時の対処を練っておく
　　➡︎基本自己責任だが、近くの病院、救急時の本名確認、出欠など参加者の把握しておく（名札など用意しておくとよい）
　盗難や不審者がいないかなど注意する
　　➡貴重品などの注意勧告しておく
　空調や騒音、照明など気を使う
　会場の注意などの説明
　　➡︎土日や時間外など入退場などに制限がかかる場合がある
　何かあった時のために会場に常時待機、問い合わせ可能な連絡手段を用意しておく
　連絡手段は複数あると安心（携帯、twitter、slack、Facebookグループ、skype、LINEなどなど）
　　➡︎今回はSlackでチャンネルが作られていたのでアナウンスがとても楽でした。
　　➡︎オープンな話題はtwitterのハッシュタグなど決めておくことであとで収集も楽になります。
　忘れ物などあった場合などにも、終わった後でも全員に連絡がとれるようにしておく。

## 場の雰囲気をよくする
　差し入れ（お菓子や飲み物）を用意する
　様子をたまに伺う
　制作におけるトラブル対処、ネットワークや持ち込み機材の不調など
　　➡︎余裕があれば予備の開発マシンやマウス、キーボードなど︎あると安心。

## 会場の設備確認
　テーブル、椅子、電源など数が足りているか
　ネットワーク環境が安定しているかなど

## 発表の場を設ける
　　➡︎機材トラブルなど事前の確認、プロジェクターへの接続や音響設備が機能するように準備しておく

## 懇親会の場を設ける
　　発表後の他のチームのゲームを遊ぶ機会など設けるとよい
　　余裕があれば軽食などのケータリングの用意があるとそれらしい

## 記録を残す
 写真撮影や[tweet](https://twitter.com/hashtag/sgj2017?src=hash)など。
 終わった後に思い出すためにも。
　　➡︎写真NGな人などはあらかじめ確認しておく。

# タイムスケジュールについて

- テーマ発表
- 企画発表
- 成果発表
- 試遊会＆懇親会

くらいあるとよい。

# 参加者個人の意気込みなど共有

## 場の雰囲気をよくする

非常に多くの人が「はじめまして」状態です。
ほっとくと「黙々と作業」しがちになります。

## リーダーを立てる

今回はチーム参加だったので、
テーマ発表後に、まずリーダーの方にチームの紹介をしてもらった。
会場にどんな人がどんな意気込みで集まっているかわかると少し安心する。
あと自己紹介や、人前で話すというのがどういうものか雰囲気が伝わる。

## チームの結束

その場でチームを組む場合は、
まずチーム内で自己紹介を済ませてもらって、チームリーダーが決まったころにやると良い。
この場合、企画発表とかのタイミングになる。

- 次回へ繋がる布石

成果発表時は、時間があるならメンバー全員からも一言、感想と反省点を言ってもらうと、十人十色のゲームジャム感があって、今後に繋がる。

# 思うところ

## 中間試遊会

本当は、ゲームが動きだしたころに一度　中間で試遊会（ロケテスト）ができると
制作にフィードバックがかかってより完成度が上がるはず。

- テーマ発表
- 企画発表
- 中間試遊会
- 成果発表
- 試遊会＆懇親会

とかが理想的。

### 参加者向けですが、
もし、中間試遊会の場が無かったとしても、他のチームの邪魔（^^;してまわるというのもお互いの励みになる場合もあるので良いかもしれません。
邪魔しないまでも、そっと様子を伺うと、他の人たちがどんな作業環境でどう作業しているかとかいろいろ参考になります。
それくらいの余裕をもって作業できるとより楽しめるかと。

# おまけ
今回は[「学生チーム対抗ゲームジャム2017」](https://picos.pickle.ne.jp/?p=4584)で運営としての参加でした。

<img src="https://qiita-image-store.s3.amazonaws.com/0/96808/f26f2bb1-bda4-b6c6-1f65-e6f25d8d8510.png" width=50%>

## 運営って暇な時間があります

みんなが集中して作業している時とか何もすることない。

そういった時に、自分の場合はサウンドとかできるので
![image.png](https://qiita-image-store.s3.amazonaws.com/0/96808/560ec92e-4507-0fcd-ab77-f44536ebbd5f.png)

といった感じで、運営の方もちょっとだけ参加してしまうというのもありかもです。
(企画相談とかテスターやるよーみたいなのでもよいかも）


## サウンドゲームジャムの宣伝

またいつかやります➡︎前回　https://effectorhack.connpass.com/event/40919/

以上
ユーザー入力input().split()で可変個数の変数を受け取る

#変数a, bに以下のような入力が必要な場合
insert 4　　　#変数が２つ
deleteFirst　　#変数が１つ

>>> **a, *b** = input().split()
insert 4
>>> print(a, b)
insert ['4']

>>> **a, *b** = input().split()
deleteFirst
>>> print(a, b)
deleteFirst []
micro:bit(chibi:bit)のスマホ連携機能 package "devices" の機種別テスト

# はじめに

先日、大人向けのmicro:bit ハンズオンで [package "devices"](https://makecode.microbit.org/reference/devices/) の説明をしたところ、急に盛り上がりました。(^^
この package "devices" は、IDE環境で、初めは標準だったのですが、いつのまにはオプションに退けられた機能になります。
ちゃんと動けば、

- 人が通ったら設置したスマホで写真を撮る
- お風呂が湧いたらスマホに通知
- スマホに着信があったらサーボモーターを動かす
- micro:bitのAボタンでスマホの音楽をスタート、Bボタンでストップ、shakeで次の曲

等は、誇張抜きで5分あれば作れる驚きのイージーさがあります。

# "devices" 機能一覧

![名称未設定 2.jpg](https://qiita-image-store.s3.amazonaws.com/0/44523/d3b0ebe3-fc0a-8a25-5302-879c1c46943b.jpeg)

やってみた事がある人はご存知かと思いますが、残念な事に、スマホの機種によって、動く機能と動かない機能があります。
特にiphoneユーザは、2017-07-13現在では、初めのペアリングすらままならないようです。

# スマホ機種別テスト結果

手が空いていた人にお願いをしてテストしてもらった結果が以下の表になります。

![test20170713.png](https://qiita-image-store.s3.amazonaws.com/0/44523/73aac304-17b7-caea-913e-847fb731db1d.png)
※[スプレッドシート](https://docs.google.com/spreadsheets/d/1PWTtMB61tjUAOab_ooyadvNRSh8b24p0g6sdr1_I4hs/edit#gid=0)

OSの制約もあると思いますが、全機能が全てのスマホで使えるようになったら楽しいですよね。
PHPで配列のキーにintにキャストできる文字列を指定するとintになる

# 仕様です
[PHP: 配列 \- Manual](http://php.net/manual/ja/language.types.array.php)

> integer として妥当な形式の文字列は integer 型にキャストされます。 つまり、キーに "8" を指定すると、実際には 8 として格納されるということです。一方 "08" はキャストされません。これは十進数として妥当な形式ではないからです。

なお、他にもいくつかキャストの例が載っています。PHPのリファレンス充実してる。

# 実行環境
PHPのREPL [PsySH](http://psysh.org/)で実行（PHP5.6。PHP7でも同様の結果）

# 実行結果
```php
>>>  $a['+1']['-2']['3']["4+5"]=['6']
=> [
     "6", # 値はもちろん文字列
   ]
>>> $a
=> [
     "+1" => [
       -2 => [ # string -> int
         3 => [ # string -> int
           "4+5" => [
             "6",
           ],
         ],
       ],
     ],
   ]
```
Elasticsearch5.5 X-Pack5.5をインストールしてみた



概要
centos7にElasticsearch 5.5とX-pack5.5をインストールしてみました。

環境
centos 7

####■まずはElastcisearchをインストール

```
yum install -y java java-1.8.0-openjdk-devel
yum -y update

wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.5.0.rpm
sha1sum elasticsearch-5.5.0.rpm
rpm --install elasticsearch-5.5.0.rpm

wget https://artifacts.elastic.co/downloads/kibana/kibana-5.5.0-x86_64.rpm
sha1sum kibana-5.5.0-x86_64.rpm
rpm --install kibana-5.5.0-x86_64.rpm

wget https://artifacts.elastic.co/downloads/logstash/logstash-5.5.0.rpm
sha1sum logstash-5.5.0.rpm 
rpm --install logstash-5.5.0.rpm 
```

####■次はymlを修正

```
vi /etc/kibana/kibana.yml
server.host:”0.0.0.0”
＃外部からのアクセスを受け付けるとき
```

同じくelasticsearch.ymlも修正する。

```
vi /etc/elasticsearch/elasticsearch.yml
network.host:"0.0.0.0"
＃外部からのアクセスを受け付ける
```

####■Firewallに追記
```
firewall-cmd --permanent --add-port=9200/tcp
firewall-cmd --permanent --add-port=5601/tcp
firewall-cmd --reload
```

####■続いてpublic.xmlで反映を確認
vi /etc/firewalld/zones/public.xml

```
 <port protocol="tcp" port="5601"/>
 <port protocol="tcp" port="9200"/>
```

####■Elasticsearchが起動するか確認
```
systemctl start elasticsearch
systemctl start kibana
systemctl start logstash
```
```
systemctl enable elasticsearch
systemctl enable kibana
systemctl enable logstsh
```
####■起動を確認
```
curl http://localhost:9200
curl http://localhost:5601
```

####■x-pack5.5はダウンロード個別にダウンロードしてSCPを使ってuploadいた方が早い。例は/tmpに一時保管してインストールしました。

```
/usr/share/elasticsearch/bin/elasticsearch-plugin install file:///tmp/x-pack5.5.0.zip
/usr/share/kibana/bin/kibana-plugin install file:///tmp/x-pack5.5.0.zip
/usr/share/logstash/bin/logstash-plugin install file:///tmp/x-pack5.5.0.zip
```

####■サービス再起動
```
systemctl restart elasticsearch
systemctl restart kibana
systemctl restart logstash
```

####■起動確認　x-packをインストールするとパスワードが必要になりました。
```
curl -u elastic:changeme localhost:9200
curl -u elastic:changeme localhost:5601
```

これで一通り完了、作業時間は３〜４時間くらいです。
NCMB PHP SDKの使い方: ユーザ管理

NCMB PHP SDKでのユーザ管理の方法を解説します。

## アカウント名とパスワードによるサインアップ

```php
use Ncmb\User;

$user = new User;
$user->userName = 'Ncmb_Tarou';
$user->password = 'password_for_MyApp_user';

$user->signUp();
```

アプリ(サービス)内のユーザを新しく作成するにはいくつかの方法がありますが、最も簡単なのはユーザ名とパスワードでのサインアップです。この場合 \Ncmb\User クラスのインスタンスを作り、userName と password プロパティをセットした後に signUp() メソッドを呼び出します。

サインアップが終了すると、そのユーザでログインした状態となります。

## メールアドレスに会員登録メールを送る

```php
use Ncmb\User;

$mailaddress = 'hoge@example.jp';
User::sendRegistrationMail($mailaddress);
```

会員登録要求のリンクを含んだメールを指定したメールアドレスに送信することもできます。これには sendRegistrationMail メソッドを用います。

## ユーザログイン/ログアウト

```php
use Ncmb\User;

// ログイン
$userName = 'Ncmb_Tarou';
$password = 'password_for_MyApp_user';
$user = User::login($userName, $password);

// ログアウト
User::logout();
```

ユーザごとの権限管理(ACL)を行う場合は、対象ユーザによるログイン処理を行います。ユーザ名/パスワードによるログインの他、Twitter/Facebook 認証もあります。(こちらについては別に記事を書く予定です)

## 現在のログインユーザの取得

```php
use Ncmb\User;

$user = User::getCurrentUser();
```

getCurrentUser メソッドを用いると、現在ログイン中のユーザ情報を取得できます。

## ユーザの削除

```php
use Ncmb\User;

$id = '94GpvfVnPz7pMo7c';
User::unregister($id);
```

ユーザのobjectIdを指定して、そのユーザを削除することができます。ただしユーザーには、標準でそのユーザしかアクセスできない権限が付加されているので、別に権限を設定しないと削除できないケースの方が多いでしょう。

## ユーザに付加情報を追加

\Ncmb\User クラスは \Ncmb\Object を継承しているので、任意のキーと値をセットすることが可能です。

```php
use Ncmb\User;

$userName = 'Ncmb_Tarou';
$password = 'password_for_MyApp_user';
$user = User::login($userName, $password);

$user->age = 24;
$user->gender = 'female';
$user->rank = 8;
$user->save();
```

上の例では、ユーザ名「Ncmb_Tarou」でログインした後に、そのユーザに対して

| key | value |
|:----:|:------:|
| age | 24 |
| gender | female |
| rank | 8 |

という値を追加しています。
form_tagをform_withで書き直す場合

# form_tagでの書き方
form_tagでこう書くと

```html+erb
<%= form_tag({ controller: :items, action: :search }, id: :header_search_form, class: :search_form) do %>
    <div class="icon_search_wrapper">
      <i class="icon-search" aria-hidden="true"></i>
    </div>
    <div class="search_input_wrapper">
      <%= text_field_tag :header_search, nil, {placeholder: :Search, class: :search_input, autocomplete: :off, maxlength: 250, 'aria-label': :Search} %>
    </div>
<% end %>
```

HTMLになると↓

```html
<form id="header_search_form" class="search_form" action="/items/search" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓"><input type="hidden" name="authenticity_token" value="ncYZH6RyeLPyWxFh4l9dNWV9XLweL99YIRspU+W8v4ifHJp1RFK7AcQST3M3KTCAm2QH26Eyh+sSTA++ZidiCQ==">
    <div class="icon_search_wrapper">
      <i class="icon-search" aria-hidden="true"></i>
    </div>
    <div class="search_input_wrapper">
      <input type="text" name="header_search" id="header_search" placeholder="Search" class="search_input" autocomplete="off" maxlength="250" aria-label="Search">
    </div>
</form>
```

# form_withでの書き方
上記のform_tagと同じような処理をform_withで書くとこうなる。

```html+erb
<%= form_with(model: :items, scope: :post, url: items_search_path, id: :header_search_form, class: :search_form) do %>
    <div class="icon_search_wrapper">
      <i class="icon-search" aria-hidden="true"></i>
    </div>
    <div class="search_input_wrapper">
      <%= text_field_tag :header_search, nil, {placeholder: :Search, class: :search_input, autocomplete: :off, maxlength: 250, 'aria-label': :Search} %>
    </div>
<% end %>
```

だが、これはHTMLになると↓

```html
<form id="header_search_form" class="search_form" action="/items/search" accept-charset="UTF-8" data-remote="true" method="post"><input name="utf8" type="hidden" value="&#x2713;" /><input type="hidden" name="authenticity_token" value="Gb4Ol3trq6vFCosVD2eTACr7OWtGhfI/L54+pQ7j1D1OSid2cfAz9Dk5ZcX1wVlz0BwBhdRTB7ZZ8lBICRCjpg==" />
    <div class="icon_search_wrapper">
      <i class="icon-search" aria-hidden="true"></i>
    </div>
    <div class="search_input_wrapper">
      <input type="text" name="header_search" id="header_search" placeholder="Search" class="search_input" autocomplete="off" maxlength="250" aria-label="Search" />
    </div>
</form>
```

となり、`data-remote="true"`が追加され、ブラウザ側の制御によるフォーム送信が行われなくなっている。

ここも同じにしたい場合は、`:local`オプションをfalseで指定すれば良い。

```html+erb
<%= form_with(model: :items, scope: :post, url: items_search_path, local: :false, id: :header_search_form, class: :search_form) do %>
    <div class="icon_search_wrapper">
      <i class="icon-search" aria-hidden="true"></i>
    </div>
    <div class="search_input_wrapper">
      <%= text_field_tag :header_search, nil, {placeholder: :Search, class: :search_input, autocomplete: :off, maxlength: 250, 'aria-label': :Search} %>
    </div>
<% end %>
```

# 参考
- http://api.rubyonrails.org/v5.1/classes/ActionView/Helpers/FormHelper.html#method-i-form_with
- https://railsguides.jp/working_with_javascript_in_rails.html
rubyのArrayで集合（和集合・積集合・差集合・対称差集合・補集合）

rubyのArrayで、
和集合・積集合・差集合をしたいとき、
それぞれ「 `|` 」・「 `&` 」・「 `-` 」でできます。
また、発展として対称差集合・補集合をしてみます。

ちなみに、rubyには、集合を表すSetクラスも用意されています。（[参考][Setクラスでの集合](http://qiita.com/namitop/items/238ebc4dfc1a367f87a8)）

# 和集合「`|`」

![](https://upload.wikimedia.org/wikipedia/commons/thumb/3/30/Venn0111.svg/300px-Venn0111.svg.png)

```rb
[1,1,2,3,3] | [3,4,5]
# => [1, 2, 3, 4, 5]
```

重複している要素は取り除かれます。

# 積集合「`&`」

![](http://upload.wikimedia.org/wikipedia/commons/thumb/9/99/Venn0001.svg/300px-Venn0001.svg.png)

```rb
[1,1,2,3,3] & [3,4,5]
# => [3]
```

重複している要素は取り除かれます。

# 差集合「`-`」

![](http://upload.wikimedia.org/wikipedia/commons/thumb/e/e6/Venn0100.svg/300px-Venn0100.svg.png)

```rb
[1,1,2,3,3] - [3,4,5]
=> [1, 1, 2]
```

左辺で重複していて右辺にある要素は重複している要素は取り除かれますが、
左辺で重複していて右辺にはない要素は除かれないです。

# 発展

## 対称差集合

![](http://upload.wikimedia.org/wikipedia/commons/thumb/4/46/Venn0110.svg/300px-Venn0110.svg.png) 

**差集合によって求めるパターン（重複あり）**

```rb
a = [1,1,2,3,3]
b = [3,4,5]
(a - b) + (b - a)
# => [1, 1, 2, 4, 5]
```

**和集合と積集合によって求めるパターン（重複なし）**

```rb
a = [1,1,2,3,3]
b = [3,4,5]
a_or_b = (a | b)
a_and_b = (a & b)

a_or_b.delete_if do |item|
  a_and_b.include?(item)
end
```



## 補集合

![](http://upload.wikimedia.org/wikipedia/commons/thumb/e/eb/Venn1010.svg/300px-Venn1010.svg.png)

```rb
u = [1,2,3,4,5]
a = [1,2]

u.delete_if do |item|
  a.include?(item)
end
```

[画像引用先]：http://www.wikiwand.com/ja/%E9%9B%86%E5%90%88#/.E5.A4.96.E9.83.A8.E3.83.AA.E3.83.B3.E3.82.AF
RasbianにインストールしたFlaskのmod_wsgi設定(Python2.7系)

Rasberry Pi上(Rasbian)のFlaskのmod_wsgi設定についてです。
（最近やっとできるようになったのでメモ）

# 環境
 - Raspberry Pi 3 (Rasbian 2017-07-05)
 - python 2.7.9
 - Flask
 - mod_wsgi
 - アプリの場所は/home/pi/flask/moi.pyを想定して記述していますので適宜読み替えてください。

# Flaskインストール

```
$ sudo apt-get update
$ sudo apt-get -y install ipython python-flask curl
```

# サンプル作成
```
$ cd /home/pi/
$ mkdir flask/ && cd flask/
$ vi moi.py
```
```py:moi.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
 return 'Hello Flask'

if __name__ == '__main__':
 app.run(host='0.0.0.0', port=5000)
```

# サンプルの動作確認
```
$ python moi.py
* Running on http://0.0.0.0:5000/
```
 - 別terminalからport5000にcurlでアクセスし、Hello Flaskと表示されればOKです。

```
$ curl http://localhost:5000
Hello Flask
```

# apache2インストール
```
$ sudo apt-get -y install apache2
```

# mod_wsgiインストール
```
$ sudo apt-get -y install libapache2-mod-wsgi
```

# apache2のコンフィグ設定
```
sudo vi /etc/apache2/sites-available/moi.conf
```

```/etc/apache2/sites-available/moi.conf
<VirtualHost *:80>
 ServerName 0.0.0.0

 WSGIDaemonProcess moi user=pi group=pi threads=5
 WSGIScriptAlias / /home/pi/flask/moi.wsgi

<Directory /home/pi/flask/>
 WSGIProcessGroup moi
 WSGIApplicationGroup %{GLOBAL}
 WSGIScriptReloading On

 Require all granted
</Directory>
</VirtualHost>
```

# VirtualHost設定反映
デフォルトのコンフィグから作成したコンフィグに切り替えます。
```
$ sudo a2dissite 000-default.conf
$ sudo a2ensite moi.conf
```

```
$ cd /home/pi/flask
$ vi moi.wsgi
```

```py:moi.wsgi
import sys

sys.path.insert(0, '/home/pi/flask/')
from moi import app as application
```

```
$ sudo service apache2 restart
```

# 設定結果の確認
 - 別terminalからport80にcurlでアクセスし、Hello Flaskと表示されればOKです。

```
$ curl http://localhost
Hello Flask
```
 - 最後にブラウザからRaspberry piのアドレスにアクセスしHello Flaskと表示されれば設定は正常です。


# 参考


- [Deploy Flask & Python3 on Apache2 & Ubuntu](http://terokarvinen.com/2016/deploy-flask-python3-on-apache2-ubuntu)
How to tmuxのvi modeのキーバインド変更 [翻訳]

# ```.tmux.conf```をコピペしまくっていたら詰まったので。
翻訳元はこのリンクをどうぞ。
https://github.com/tmux/tmux/issues/754

要約すると

これを　
```bind-key -t vi-copy v begin-selection```

こうしてね！　
```bind-key -T copy-mode-vi v send-keys -X begin-selection```


# 動機
新しいtmuxは（どのバージョンから変更されたのかは調べていないです。。。）vi modeのキーバインド変更方法が変わりました。なので、巷に溢れている他人の```.tmux.conf```を思考停止状態（自分が良い例😝 ）でコピペするとエラーが出ます！



# 新ルール
1. ```-t```を```-T```に変える
2. ```vi-<name>```を```<name>-mode-vi```に変える
3. ```send-keys -X```をコマンドの前につける。
4. 以上

# 例


```tmux:.tmux.conf
### OLD Version ###
bind-key -t vi-edit Up history-up
bind-key -t vi-edit Down history-down
unbind-key -t vi-copy Space     ;   bind-key -t vi-copy v begin-selection
unbind-key -t vi-copy Enter     ;   bind-key -t vi-copy y copy-pipe "reattach-to-user-namespace pbcopy"
unbind-key -t vi-copy C-v       ;   bind-key -t vi-copy C-v rectangle-toggle
unbind-key -t vi-copy [         ;   bind-key -t vi-copy [ begin-selection
unbind-key -t vi-copy ]         ;   bind-key -t vi-copy ] copy-selection
```

これを👆

こうしてね👇

```tmux:.tmux.conf
### NEW Version ###
bind-key -T edit-mode-vi Up send-keys -X history-up
bind-key -T edit-mode-vi Down send-keys -X history-down
unbind-key -T copy-mode-vi Space     ;   bind-key -T copy-mode-vi v send-keys -X begin-selection
unbind-key -T copy-mode-vi Enter     ;   bind-key -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "reattach-to-user-namespace pbcopy"
unbind-key -T copy-mode-vi C-v       ;   bind-key -T copy-mode-vi C-v send-keys -X rectangle-toggle
unbind-key -T copy-mode-vi [         ;   bind-key -T copy-mode-vi [ send-keys -X begin-selection
unbind-key -T copy-mode-vi ]         ;   bind-key -T copy-mode-vi ] send-keys -X copy-selection
```

# 🎉以上です🎉

思ってたよりそっけない投稿になってしまいましたが、備忘録としてとっておくことにします。😎
Herokuでrakeファイルを定期実行する

# rakeファイルを自動実行

Heroku Schedulerを使用して、コマンドを自動実行する

## Heroku Scheduler追加方法

Heroku SchedulerはResources->Add-onsから追加可能
Standard - Freeプランを選択

コマンドで追加する場合はこちら

```shell-session
$ heroku addons:create scheduler:standard
```

<img width="1240" alt="スクリーンショット 2017-07-12 8.35.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/142918/5abf3229-f763-e584-4a0d-57043e3e771d.png">

![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/142918/49066f42-b583-1006-d653-8e8b6e34aa6c.png)

## Heroku Schedulerで実行したいコマンドを指定する。

下のキャプチャでは、上の`$ rake destroy_past_post:destroy`を1日一回
下の`$ rake post:update`を10分間隔で実行しています。

実行間隔は、1日毎、1時間毎、10分毎と痒いところに手が届かない仕様なので、そこはrakeファイル内で制御しましょう。
なお実行時間はUTC時間なので、気をつけましょう。

UTC->JSTに変換
https://utctojst.appspot.com/utctojst

![キャプチャ2.PNG](https://qiita-image-store.s3.amazonaws.com/0/142918/0b4b778b-e648-0735-f494-9526bd93d2e7.png)

## ログの確認

```shell-session
$ heroku logs --ps scheduler.xxxx 
#(xxxxは実行時に割り当てられたプロセス番号)
```

プロセス番号は`logs -t`で探す

```shell-session
$ heroku logs -t
2017-07-12T15:40:20.722747+00:00 app[api]: Starting process with command `bundle exec rake post:update` by user scheduler@addons.heroku.com
2017-07-12T15:40:26.524329+00:00 heroku[scheduler.8540]: Starting process with command `bundle exec rake post:update`
2017-07-12T15:40:27.097075+00:00 heroku[scheduler.8540]: State changed from starting to up
```
AtCoder Grand Contest 017

2017年7月9日のAtCoderの記録。言語はRubyです。

問題はこちら：[AtCoder Grand Contest 017](http://agc017.contest.atcoder.jp/)
私の回答一覧はこちら：[自分の結果](http://agc017.contest.atcoder.jp/submissions/all?user_screen_name=Linus)
制限時間120分で、14分でA、38分経過でBを正解して2問完答。
残り1時間程度Cを考えていたが、まるで解けなくて諦めた。

#A問題
1次元DP。
e[i], o[i]を「1番めからi番目までに対して、問題の通り選んで合計が偶数/奇数になる場合の数」とする。
a_iが偶数ならば
e[i] = e[i-1] * 2
o[i] = o[i-1] * 2
a_iが奇数ならば
e[i] = o[i] = e[i-1] + o[i-1]
である。これを実装すればよい。

#B問題
最初「DPっぽいなー、今日DPばっかなのかな」と言いながら考えたが、DPだと上手く行かなかった。以下、DPの解法。
DP[len][dif] を、「両端の差がdifであり、全部でlen個のマスがあるときに、条件を満たす数の書き込みができるかできないか(true/false)」だと考える。(dif $\geq$ 0の部分だけ考えればよい。)
DP[2][dif] = true (C $\leq$ dif $\leq$ Dの場合) , false(それ以外の場合)である。
DP[n+1][dif] がtrueになるのは
「dif+C $\leq$ i $\leq$ dif+D または dif-D $\leq$ i $\leq$ dif-C 」かつ DP[n][i] = true となるiが存在するときである。それ以外のときはfalseである。
ここでDP表のサイズを考えると、nは2からNまで求めればよいが、問題はiだ。正確な条件は求めていないが、最大で $10^9$程度になるのは間違いない。これでは時間が足りない。この解法ではダメだ。

ここまで来て行き詰まって、サンプルを見て「1,−1,3,7,5」という答えに「何で最初から減少するんだよ、増加させればいいのに……あっ、増加と減少でまとめれば良いんだ」と気づいた。
増加と減少の個数は合計で(N-1)回。N' = N-1として、増加がp回、減少が(N'-p)回ならば、
B-Aの最大値はD*p - C*(N'-p)
B-Aの最小値はC*p - D*(N'-p) であり、
さらにこの2つの間の全ての数はB-Aの値として取ることができる。
あとはpが全ての場合について計算して、どれかのpに対して最大値と最小値の間に入ってたらYES、そうでなければNOだ。

```ruby
n,a,b,c,d = gets.split(" ").map { |v|
	v.to_i
}
 
n = n - 1
dif = b-a
 
for p in 0 .. n do
	difmax= d * p - c * (n-p)
	difmin= c * p - d * (n-p)
	if difmin <= dif and dif <= difmax then
		puts "YES"
		exit
	end
end
 
puts "NO"
```




#C問題
問題の意味は理解して、方針を立てて書いてみたものの、例題に対して違う結果が出てきて、「あっこれじゃダメじゃん」
大きい順にソートしてから数字を変換しようと思ったけど、数字を変換した後が大きい順になっているままとは限らないことに気づくのが遅れた。
解答にあるような「数直線上の区間をすべてカバーするか」という話に置き換えられないと、正答にたどり着くのは無理なのかなー。だとすると、結構難しい発想を要求している気がする。


パフォーマンスは過去最高の1888、レートは1349→1423(+74)。
今回は名前が黄色(2000~2400)やオレンジ(2400~2800)でも「30分以内にABを解いたけどその後C以降が解けない」という人がいたようだ。C問題以降がどれもだいぶ難しかったということだろう。
Windowsインストール不要アプリ


# 本記事について

* 主旨: インストール不要アプリの備忘録
* 想定環境: Windowsでインストール可能なアプリに制限がある場合
* 動作確認環境: Windows7 (32bit)

# アプリ一覧

## Terminal

### MobaXterm Portable 

* http://mobaxterm.mobatek.net/
* zshが使える
* マルチタブ対応
* cyg-getがapt-getライクに使える, 

## エディタ

### Sublime Portable 

* https://www.sublimetext.com/

### Atom

### VScode 

* https://code.visualstudio.com/
* VS派用
* アップデートがマメ
* 32bitには若干重いように思う

### Terapad 

* http://www5f.biglobe.ne.jp/~t-susumu/library/tpad.html
* とりあえずTerapadを入れてしまう

## 描画

### matplotlib (python)

* 導入方法
  - anacondaを用いる
  - pythonを入れてからモジュール用のzip, whl等を持ってきてpipする
     + zip, whlの入手元: https://pypi.python.org/pypi/matplotlib/
     + カレントディレクトリにmatplotlib-2.0.2.tar.gzがある場合は以下

     ~~~
       pip install matplotlib-2.0.2.tar.gz
     ~~~
* 後者の導入方法はanacondaにないモジュールを導入する際に役に立つ
  - tensorflow, chainer等の機械学習用モジュールも同様の方法で導入可能

### gnuplot 

* http://www.gnuplot.info/
* お手軽描画

## 仮想Desktop

### Desktops 

* https://technet.microsoft.com/ja-jp/sysinternals/cc817881.aspx
* Microsoft、標準装備にしてくれてもよくない？

## pdf viewer

### Sumatra pdf 

* https://www.sumatrapdfreader.org/free-pdf-reader.html
* 軽い
* リアルタイムでアップデートされるので、TeX使用時に助かる

## クリップボード

### CLCL 

* http://www.nakka.com/soft/clcl/

## ファイラ

### X-Finder 

* http://www.eonet.ne.jp/~gakana/
* マルチタブ

## ランチャ

### Wox 

* http://www.getwox.com/
* 32bitでは性能が活かしきれない感がある…

## Google calendar

### Gsync calendar 

* http://hp.vector.co.jp/authors/VA038716/GSyncCalendar.html
* いちいち共有する必要のない予定を書いておく用
serverless frameworkで今日コミットしてるか教えてくれるcron作ってみた

## なぜ作ったか

jqueryコミッターのgithubプロフィールに憧れて

![スクリーンショット 2017-07-11 02.12.08.png](https://qiita-image-store.s3.amazonaws.com/0/47896/b548f09f-5721-7b01-2ee2-82ad7640097a.png)

こんなありがたいブログ記事もあります
[write-code-every-day](https://johnresig.com/blog/write-code-every-day/)

毎日コードを書くように思い出させてくれるcron?bot?を作りました。
わざわざserverlessでlambdaでやる必要なくねっていうのは許してください。。
serverless framework好きなので使いました。

## やってくれること

いまのところ、4時間ごとと23時にコミット状況をslackに通知してくれます
こんな感じ
![スクリーンショット 2017-07-12 23.39.54.png](https://qiita-image-store.s3.amazonaws.com/0/47896/c1e5dd23-6db1-cca9-4965-d660240a9eb4.png)

## 成果物

[githubにおいてあります](https://github.com/KoheiMisu/serverless-notice-github)

## 必要なもの

### awsアカウント
→ lambdaを使うので

### serverless frameworkのインストール
(npm使うのでnodeが必要です。lambdaに合わせてv6.10がオススメ)

### githubアカウント
https://github.com/settings/developers からoauthアプリケーション登録して
client_idとclient_secretを取得しましょう

## 自分のコミットカレンダー

業務ではgithubを使っているのでprivateリポジトリ含めたものです

![スクリーンショット 2017-07-11 02.13.38.png](https://qiita-image-store.s3.amazonaws.com/0/47896/f7a641bc-d9d2-2d22-96b4-79696df5ceaa.png)

ただ、publicリポジトリ、要するに自分のリポジトリのみへのコミットに切り替えると。。

![スクリーンショット 2017-07-11 02.14.23.png](https://qiita-image-store.s3.amazonaws.com/0/47896/9ddd6c68-9cde-5099-2b00-1f0eaaaad76c.png)

はい、この通り、まだまだ精進していきます。
n..mやrange(n, m)など範囲型やrangeと呼ばれるものでmは含まれるかどうかを調べてみた

[この記事](http://qiita.com/aimof/items/af79c62a9e4cc0419959)のコメント欄でn..mでmを含むかどうか、みたいな話になったので、調べてまとめてみました。開区間、閉区間や[n, m), [n, m]で表されるものです。

普段使わない言語も含まれるため、誤りがあればご指摘をお願いします。

## 前提

n, mはオーバーフローしない範囲の自然数あるいは0でn<mとする。

## 様々な言語での結果

### Rust

```rust
n .. m // mは含まれない
```

Rustの記事からの派生なのでとりあえずRustを。mは含まれません。

### Python

```python
range(n, m)
```

mは含みません。ただし、random.randint(n, m)はmを含むみたいなややこしさがあります。

### Java

IntStreamクラスにRange関数とRangeClosed関数があります。

### JavaScript(ES2016以降), 0始まりのみ

```js
...Array(m).keys()
```

ちょっと特殊な例ですが、mは含まれません。

### Ruby

```rb
n..m # 含む
n...m # 含まない
```

しょしんしゃが最初に習うの点２個の方が多いイメージ

### Elixir

```ex
n..m # 含む
```

Elixirの範囲型はmを含みます。Rubyによく似ています。

### PHP

```php
range(n, m)
```

mを含みます

### Kotlin

```kotlin
n..m
```

mを含みます。

### Swift

```swift
n...m // 含む
n..<m // 含まない
```

### Scala

```scala
Range(n, m, 1) // [n, m), 間隔1
n to m // mを含む
n until m // mを含まない
```


## まとめ

似たような言語でも扱いが違って非常にややこしいです。

私が知っている言語を主に書いたので、偏りや抜けなどあるかもしれません。

追記：どちらが主流というのはあまり意味のない分類でしたので撤廃しました。
Facebook SDKを用いてFacebookログインの実装をするときに開発キーハッシュでつまづいた話（Androidアプリ版）

Fecebookログイン機能の実装は公式ドキュメントに沿ってください。笑
僕も本当に公式ドキュメントに言われるがまま実装しました。笑

##開発キーハッシュが原因でエラー
一意の開発キーハッシュを生成して登録する必要があるのですが、公式ドキュメントのキー生成方法は以下のように説明されています。

Mac版

```
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64
```

Windows版

```
keytool -exportcert -alias androiddebugkey -keystore %HOMEPATH%\.android\debug.keystore | openssl sha1 -binary | openssl base64
```

私はMac環境ですのでMac版のコマンドをターミナルに打ち込み、生成された開発キーを登録しました。
そしてごにょごにょしながらログインボタンを実装していざログインボタンを押すと、、、
https://stackoverflow.com/questions/23674131/android-facebook-integration-invalid-key-hash
この記事を投稿した方と同じエラーが出ました。
その開発キーハッシュは無効です。と怒られています。
ターミナルに打ち込んで生成したキーと登録したキーは間違えていませんでした。
そこで上記のサイトのAnswerに書かれているコードを適当なファイルに書き込み、Android Monitorで出力されたキーを確認してみると、ターミナルで生成したキーと異なっていました。
なので、Android Monitorで確認したキーを登録し直すと、エラーはなくなり無事成功いたしました。

とりあえず成功はしたので良いのですが、、、いったいなぜキーが異なったのですか、、、どなたか、、、教えてください。。。
macにpython3をインストールする手順



brewでpyenvをインストール

```
$ brew install pyenv
```

PATHを通す。
vim ~/.bash_profile
に以下を追加

```
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
```

反映
source ~/.bash_profile

echo $PATH
で確認


pythonをインストールする

```bash
$ pyenv install 3.6.1
```

> BUILD FAILED (OS X 10.12.5 using python-build 20160602)
hogehoge~~
zipimport.ZipImportError: can't decompress data; zlib not available
make: *** [install] Error 1

上記のようにインストールに失敗した場合はzlibをインストールして再度インストールを行う


```bash
$ xcode-select --install
```

再度インストールすれば完了！

verの切り替え
```
$ pyenv global 3.6.1
```

verの確認

```bash
$ python --version
```
Xcode バージョンをビルド時にチェックする

基本的に SDK バージョンやコンパイラのバージョンが変わらなければ Xcode バージョンが変わっても動作に変わりないため、Xcode バージョンをチェックするよりも SDK バージョンやコンパイラのバージョンを固定した方が良いと思います。

それを踏まえて、どうしても Xcode バージョンを固定したい場合に以下を参考にしてください。


tr;dr
---

### プロジェクトのルートディレクトリに `.xcode-version` という名称で、バージョン番号を書いたファイルを書き出す。

```
# Xcode 8.3.2 を使用する場合
echo '8.3.2' >.xcode-version
```
### Build Phase > Run Script に以下を記述する。

```shell-session
tr -d . < "$SCRIPT_INPUT_FILE_0" | xargs printf '%04d' | grep -q "$XCODE_VERSION_ACTUAL"
if [ $? -ne 0 ]; then
  echo 'Error: Detected wrong Xcode version. Please switch to the correct Xcode version.' >&2
  exit 1
fi
```

### Run Script の　Input Files に `$(SRCROOT)/.xcode-version` と書く。

### Run Script を Build Phase の先頭に移動する

何をやっているのか
---

### $XCODE_VERSION_ACTUAL

Xcode はビルド時に `$XCODE_VERSION_ACTUAL` に使用中の Xcode バージョンを出力する。

この変数は `xcodebuild -showBuildSettings` で確認できる。

```shell-session
$ xcodebuild -showBuildSettings 2>/dev/null | grep XCODE_VERSION_ACTUAL
    XCODE_VERSION_ACTUAL = 0832
```

このように右詰めでゼロパディングされた4桁の数字になる。

### xcenv

複数の Xcode を管理するための [xcenv](http://xcenv.org/) というツールがある。
rbenv や pyenv と似たようなツールで、 `.xcode-version` というファイルがあると、そのディレクトリ以下では指定されたバージョンの Xcode を使うようになる。

これを使うかどうかは別として、明示的に `.xcode-version` にバージョンを書いておくと fastlane 等からも参照できて便利になる。
web.configの暗号化

IISの制御ファイルである`web.config`にはDB接続情報などが平文のまま格納されていますので、流出しないように注意する必要があります。

この問題を回避するためにMicrosoftから`aspnet_regiis`という暗号化ツールが提供されています。

# aspnet_regiisとは何か
aspnet_regiisは.NET Frameworkに同梱されているツールで、主にASP.NETをIISに登録するためのツールですが、web.configファイルを暗号化する機能もあります。

# aspnet_regiis格納場所
.NET Frameworkの各バージョンのフォルダにあります。

| バージョン     |       格納場所 | 
|--------------:|:-------------------|
| 1             |%windir%\.NET\Framework\v1.0.3705 |
| 1.1           |%windir%\Microsoft.NET\Framework\v1.1.4322|
| 2.0(32ビット) |%windir%\Microsoft.NET\Framework\v2.0.50727|
| 2.0(64ビット) |%windir%\Microsoft.NET\Framework64\v2.0.50727|
| 4(32ビット)   |%windir%\Microsoft.NET\Framework\v4.0.30319|
| 4(64ビット)   |%windir%\Microsoft.NET\Framework64\v4.0.30319|
※ 3.0と3.5には同梱されていないので他のバージョンのaspnet_regiisを使うこと

# aspnet_regiisの使い方

```
aspnet_regiis -pef "<暗号化したいセクション>" "<web.configのあるフォルダパス>"
```

例）C:\Inetpub\wwwroot\web.configのconnectionStringsを暗号化したい場合

```
aspnet_regiis -pef "connectionStrings" "C:\Inetpub\wwwroot\web.config"
```

下層のセクションを指定したい場合は`system.web/machineKey`のように「`/`」区切りで指定します。

複合化するにはオプションを`-pef`から`-pdf`に変更するだけです。

## うまく動かなかった例

windows server 2016環境でDomain Adminsグループに所属するユーザで暗号化を行ったところ、こんなエラーが出ました。

```
Microsoft(R) ASP.NET IIS 登録ツール バージョン 4.0.30319.0
ローカル コンピューターの ASP.NET をインストールおよびアンインストールするための管理ユーティリティです。
Copyright(C) Microsoft Corporation.  All rights reserved.
構成セクションを暗号化しています...
connectionStrings のための構成セクション ハンドラーを実行中にエラーが発生しました。

プロバイダー 'RsaProtectedConfigurationProvider' を使用してセクション 'connectionStrings' を暗号化することができませんでした。プロバイダーからのエラー メッセージ: オブジェクトは既にあります。

失敗しました!
```

解決方法はなんてことはない、コマンドプロンプトを「管理者として実行」すればよかっただけでした。

※日本語で「オブジェクトは既にあります」をweb検索しても全然ヒットせず、UIを英語に変えて表示されるエラーメッセージ「Object already exists.」で検索したらすぐ見つかりました。
[Stack Overflow](https://stackoverflow.com/questions/15365210/failed-to-encrypt-the-section-connectionstrings-using-provider-rsaprotectedco)

# 参照URL
ASP.NET IIS 登録ツール (Aspnet_regiis.exe)
[https://msdn.microsoft.com/ja-jp/library/k6h9cz8h(v=vs.100).aspx](https://msdn.microsoft.com/ja-jp/library/k6h9cz8h(v=vs.100).aspx)
ExoPlayer2で `raw` ディレクトリに置いた動画ファイルを再生する


## ExoPlayer2でローカルのmp4を再生する

ExoPlayer2で `raw` ディレクトリに置いた動画ファイルを再生する

```kotlin

private fun startVideo(playerView: TextureView) {
  val defaultBandwidthMeter: DefaultBandwidthMeter = DefaultBandwidthMeter()
  val videoTrackSelectionFactory: TrackSelection.Factory = AdaptiveVideoTrackSelection.Factory(defaultBandwidthMeter)
  val trackSelector: TrackSelector = DefaultTrackSelector(videoTrackSelectionFactory)
  val loadControl: LoadControl = DefaultLoadControl()
  val player = ExoPlayerFactory.newSimpleInstance(context, trackSelector, loadControl)
  val dataSpec: DataSpec = DataSpec(RawResourceDataSource.buildRawResourceUri(R.raw.samplerial))
  val rawResourceDataSource = RawResourceDataSource(context)
  rawResourceDataSource.open(dataSpec)
  val rawResourceDataSourceFactory = DataSource.Factory { rawResourceDataSource }
  val extractorsFactory: ExtractorsFactory = DefaultExtractorsFactory()
  val videoSource: MediaSource = ExtractorMediaSource(rawResourceDataSource.getUri(), rawResourceDataSourceFactory, extractorsFactory, null, null)
  player.prepare(videoSource)
  player.setVideoTextureView(playerView)
  player.playWhenReady = true
}

```
本当に Windows10 のコマンドプロンプトはエスケープシーケンスをサポートしたのか？

したっぽい。実験してみよう

```windows10.go
package main

import (
	"fmt"
	"os"
	"syscall"
	"unsafe"
)

var kernel32 = syscall.NewLazyDLL("kernel32")

const STD_INPUT_HANDLE = uintptr(1) + ^uintptr(10)
const STD_OUTPUT_HANDLE = uintptr(1) + ^uintptr(11)
const STD_ERROR_HANDLE = uintptr(1) + ^uintptr(12)
const ENABLE_VIRTUAL_TERMINAL_PROCESSING uintptr = 0x0004

var procGetStdHandle = kernel32.NewProc("GetStdHandle")
var procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
var procSetConsoleMode = kernel32.NewProc("SetConsoleMode")

func Main() error {
	var mode uintptr
	console, _, _ := procGetStdHandle.Call(STD_OUTPUT_HANDLE)

	rc, _, err := procGetConsoleMode.Call(console, uintptr(unsafe.Pointer(&mode)))
	if rc == 0 {
		return err
	}
	defer procSetConsoleMode.Call(console, mode)

	rc, _, err = procSetConsoleMode.Call(console, mode|ENABLE_VIRTUAL_TERMINAL_PROCESSING)
	if rc == 0 {
		return err
	}
	println("\x1B[32;1mAHAHA\x1B[37;1m")
	return nil
}

func main() {
	if err := Main(); err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	} else {
		os.Exit(0)
	}
}
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/29454/d43e61d5-88b5-a9c1-7dcd-a33c9961159c.png)

SetConsoleMode で、`ENABLE_VIRTUAL_TERMINAL_PROCESSING` (0x0004) というビットを立てなくてはいけないようです。

くわしい仕様はこちら：

* [Console Virtual Terminal Sequences (Windows)](https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx)

Go でやる分には [go-colorable](https://github.com/mattn/go-colorable) があるので、あんまりすぐ使う必要性はありませんが、いつが Windows 7/8 がサポート切れになった時、使う日が来るかもしれません。
QueryRunnerでORA-01000が発生する

> new QueryRunner().update(WordCount.THREAD_LOCAL.get(), 
     INSERT_SQL, //実行するクエリ
     new Object[]{str,meaning,meaning.length()<=1000?meaning:meaning.substring(0,1000)} );

のようにQuerryRunner#updateでインサート文を発行する処理を実装しループでこの処理を繰り返し実行させると
「ORA-01000：最大オープン・カーソル数を超えました。」
のエラーが発生する
QuerryRunner#updateは内部的にSelect文を発行しResultSetをクローズ指定していないのだろうか？
ちなみに
>new QueryRunner()
.batch(WordCount.THREAD_LOCAL.get(), //コネクションの取得
INSERT_WEBPAGE_SQL, //実行するクエリ
params  );

のようにbatchメソッドを使って一括insertを行うと「ORA-01000：最大オープン・カーソル数を超えました。」は発生しない
Linuxでビープ音を出す際にハマったメモ

Linuxで`printf("\a\n");` でビープ音を鳴らす際にハマったのでメモ。

# 環境

- OS: Linux Mint 18.1 (多分Ubuntu系なら一緒と思います）
- デスクトップPCでスピーカはUSB接続

# やりたかったこと

- C言語の`printf("\a\n")`またはシェルの`echo -e "\a"`でビープを鳴らしたい

# 鳴らすためにやったこと


- /etc/pulse/default.pa で以下の行をアンコメント。（行は多少離れてるので bell で検索）

```
load-sample-lazy x11-bell /usr/share/sounds/freedesktop/stereo/bell.oga
load-module module-x11-bell sample=x11-bell
```

- １回再起動するまたは `pulseaudio --kill; pulseaudio --start`を実行

これで鳴りました。
なお、上記のbell.ogaのところのファイルパスを変えれば、好きな音をビープ音に出来ます。
ビープ音のサンプルは /usr/share/sounds/以下に色々とあります。

# 備考

検索すると以下をやれという説明がよく出てきますが、私の環境では不要でした。

- /etc/modprobe.d/blacklist.conf で blacklist pcspkr をコメントアウト
（一時的でいいなら sudo modprobe pcspkr を実行）

おわり
Xcode8.3.3でcordova run iosしたときに失敗する場合の対処法


##事象
```bash
cordova run ios
```

で実機デバッグする際に以下のエラーメッセージが出る。
Error: Error code 253 for command: ios-deploy with args: --justlaunch,--no-wifi,-d,-b,/Users/(MyApp)/MyApp.app

##環境　
macOS Sierra
Xcode:8.3.3
Cordova:7.0.1
Cordova iOS platform : 4.4.0
ios-deploy : 1.9.1

##対応方法
1.以下のディレクトリにシンボリックリンクを張る

```bash
cd /Applications/Xcode.app/Contents/Developer/platforms/iPhoneOS.platform/DeviceSupport

sudo ln -s 10.3.1\ \(14E8301\)/ 10.3
```

2.platforms/ios/配下のxcodeprojファイルを開いて以下の設定をする
General > Signing より、署名する
General > Deployment Info > Deployment Targetを10.3に設定　

3.cordova run ios を実行
 ※ただし、実行時に端末のロックが解除されていないと同様のエラーが発生する。

原因やなぜこれで治るかについては詳しく調べていないので、[こちら](https://github.com/phonegap/ios-deploy/issues/292)のやりとりを参照して下さい。
Emmetを導入してみよう


# 誰に向けての文章か？

この報告はわたしの担当してる大学での講義（デザイン系）のためのまとめです。対象はわたしの受講生です。講義中に話したことは省略してあることもあります。このため、一般の方が読まれると技術的に不明瞭・不正確な解説になっている部分もあります。

# Emmetについて
簡単にいうと、HTMLのタグやCSSの命令を書くときに「ずるっこ」ができる仕組みです。たとえば

```
div.hogehoge
```


と入力して<kbd><kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>e</kbd></kbd>を押下してください(MacでAtomを使っている場合)。そうすると

```
<div class="hogehoge">|</div>
```


と展開されます。｜はカーソルの位置を示します。どうですか？便利でしょ？「呪文」を展開するキー（トリガーキー）の組み合わせは環境により異なります。以下詳述していきます。

## Emmetが使える環境について
Emmetは様々な環境で使えますが、講義で扱ったものではすべて使えます。というより、プラグイン等によりEmmetを使える環境をご紹介しました。

- Mery
- Atom
- Monaca

Emmetが導入できるテキストエディタについては[Emmetの公式サイト](https://emmet.io/download/)を参照してください。

# エディタ別解説
## Mery
### 導入方法
[Mery公式サイト](http://www.haijin-boys.com/wiki/%E3%83%9E%E3%82%AF%E3%83%AD%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA)のマクロライブラリにリンクがあります。リンク先のGithubにemmet.jsがありますので、ローカルの任意の場所にダウンロードします。その後、Meryエディタがインストールされたフォルダ内に「MyMacros」等を作成し（フォルダ名は自由）emmet.jsを保存します。マクロメニューの「選択」から読み込みます。
（Meryをインストーラから標準インストールした場合、CドライブのProgram FilesフォルダのなかにMeryフォルダがあります）

### トリガーキー
残念ながらMeryではEmmetにあらかじめトリガーキーの割り当てはありません。このため、自分でEmmetを呼び出すショートカットキーを割り当てないといけません。

1. 「ツール」−「オプション」−「キーボード」を選択
2. カテゴリを「マクロ」にします
3. 「コマンド」の項目のなかにemmet.jsがありますので、選択しましょう
4. 「新しいキー」にカーソルを当てます
5. 好みのキーを入力します。わたしは<kbd><kbd>Ctrl</kbd>+<kbd>e</kbd></kbd>
を割り当てています。
6. 「現在の割り当て」とかぶらないように注意しましょう。
7. キーの組み合わせを入力後、「割り当て」キーを押します。
8. 最後に「OK」ボタンを押しましょう。

## Atom
### 導入方法
AtomでのEmmetはプラグインとして提供されています。プラグインのインストール方法は以下です。

<kbd><kbd>Ctrl</kbd>+<kbd>,</kbd></kbd>(Macの場合は<kbd><kbd>Cmd</kbd>+<kbd>,</kbd></kbd>)を押して設定画面を開きます。左側のメニューのところに「Install」がありますので、選択します。検索窓が出ますので、そこにEmmetと入力して「Packages」ボタンを押しましょう。emmetioから提供されているemmet（執筆時のバージョンは2.4.3）を選択してInstallボタンを押します。

### トリガーキー
Winの場合は<kbd><kbd>Ctrl</kbd>+<kbd>e</kbd></kbd>
Macの場合は<kbd><kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>e</kbd></kbd>

Windowsの場合は<kbd>Tab</kbd>でも展開するようです。

## Monaca
### 導入方法
ありません。デフォルトで使用できるようになっています。

### トリガーキー
<kbd>Tab</kbd>

# チートシート
公式サイトで「呪文」の一覧が用意されています。[こちらです。](https://docs.emmet.io/cheat-sheet/)

# Qiita内の解説
先人たちの素晴らしい仕事がたくさんあります。感謝しつつ参考にさせてもらいましょう。たとえば初学者には以下のような投稿が役に立つと思います。

- [Emmetを学習しましょう](http://qiita.com/tezuka_advance/items/bb3401ef178a1fd343c8)
- [備忘録_Emmet](http://qiita.com/y_sekitoba/items/73ca71efc232bd159b85)
変数スコープは複製できない

##JavaScriptでオブジェクトを複製してその中の関数を使ったら想定外の挙動をしてしまいました。
なんか初歩的な話のような気がするけど・・・
（コメントを受けて質問は削除しました備忘録という事にします）

###ソース1

```html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
  <script src="test.js"></script>
</head>
<body>
</body>
</html>
```

```js:test.js
var baseObject = baseFunction();

function baseFunction () {
  var _id = null;
  
  function setId (id) {
    _id = id;
  }
  
  function getId () {
    return _id;
  }
  
  return {
    setId: setId,
    getId: getId
  }
}

// baseObjectを複製
var object1 = {};
var object2 = {};
$.extend(true, object1, baseObject);
$.extend(true, object2, baseObject);

console.log("start▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼");

console.log(object1.getId());
console.log(object2.getId());

object1.setId(1);
console.log("object1 set/////////////////");

console.log(object1.getId());
console.log(object2.getId());

object2.setId(2);
console.log("object2 set/////////////////");

console.log(object1.getId());
console.log(object2.getId());

console.log("end▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲");
```
####予想
```
"start▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼"
null
null
"object1 set/////////////////"
1
null
"object2 set/////////////////"
1
2
"end▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲"
```
####結果
```
"start▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼"
null
null
"object1 set/////////////////"
1
1
"object2 set/////////////////"
2
2
"end▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲"
```
このような結果になるのが分からなくて悩みました。
とりあえず以下のようにしたら思い通りになったので良かったのですが、よくわかりません。
**追記：スコープは複製できないという事のようです**
上記のパターンを使うことがあれば気を付けようと思いました。
###ソース2
```js:test.js
function baseFunction () {
  var _id = null;
  
  function setId (id) {
    _id = id;
  }
  
  function getId () {
    return _id;
  }
  
  return {
    setId: setId,
    getId: getId
  }
}

var object1 = baseFunction();
var object2 = baseFunction();

console.log("start▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼");

console.log(object1.getId());
console.log(object2.getId());

object1.setId(1);
console.log("object1 set/////////////////");

console.log(object1.getId());
console.log(object2.getId());

object2.setId(2);
console.log("object2 set/////////////////");

console.log(object1.getId());
console.log(object2.getId());

console.log("end▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲");
```
####結果
```
start▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼
null
null
object1 set/////////////////
1
null
object2 set/////////////////
1
2
end▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲
```