OmniAICreator/Qiita-1.07M huggingface dataset:
結局末尾再帰ってなんなんだよ！！って人は見て

#末尾再帰について書きたい！

はい。
末尾再帰ですね。

>末尾再帰（まつびさいき）とは、再帰的な関数やプロシージャにおいて、自身の再帰呼び出しが、その計算における最後のステップになっているような再帰のパターンのことである。再帰にかかわらず一般に、そのような最後の呼び出しを末尾呼び出し （en:Tail call）という。呼び出しではなく、戻り先を保存しないジャンプに最適化できるという特徴がある（#末尾呼出し最適化）。

wikipediaにはこうありますけど、これで納得してる人ならここに来ていないと思うので噛み砕きます。

スタックフレームやコールスタックがどうのこうのなど、実際の小難しい概念は一切出さずに説明したいと思います。

#伝言ゲーム(末尾再帰ではない再帰)

A,B,C,Dの4人が伝言ゲームをしています。

それぞれ左から[1][2][3][4]という数字が書かれた紙持っています。

Aさんに指令を出します。

指令
「全員の持ってる数字の総和を求めろ」

Aさんは考えた結果、Bさんに
「BさんCさんDさん全員の総和を教えて」と伝えました。

Bさんは、自分の数字はわかるが、CさんDさんの数字はわからないので
Cさんに
「CさんとDさんの総和を教えて」
と伝えました。

それを聞いたCさんは、Dさんに
「Dさんの数字を教えて」と伝えました。

![スクリーンショット 2018-10-29 15.28.03.png](https://qiita-image-store.s3.amazonaws.com/0/211255/3e7c9ee3-558e-705e-d7a1-a548401a17c6.png)

![スクリーンショット 2018-10-29 15.31.09.png](https://qiita-image-store.s3.amazonaws.com/0/211255/3a83bcc1-2cbe-c2dc-502f-25c82e98dcb1.png)

とこのようにしてAさんは指令の答えである10を導き出すことができました。

これは末尾再帰ではありません。

##何がいけないのか

この方法だと、Aさんは伝言ゲームがスタートしてからDさんにまで指令が届くのを待って、さらにもう一度自分の元に総和が届くのを待たないといけません。
つまりAさんはBさんに「総和を教えて」と言った後に、さらにその返答がくるまでその場にいないとダメなんです。
Bさんも、Cさんも同様です。

Dさん以外の全員が「質問をしてから、その答えを元にさらに計算する」と言う二つの仕事を与えられてしまっています。
これはよくありません。

#伝言ゲーム(末尾再帰)

Aさんは考えました。
いかにすれば自分の仕事を減らせるか。

一番効率がいいのは、自分の数字を横の人に伝えるだけで終わることです。

Aさんの数字は1です。
それをBさんに伝えて、さらに指令も伝えれば、もうAさんの仕事はありません。
だって全ての情報をBさんに伝えてるからです。
Bさんが代わりに指令に答えればAさんの仕事は無くなります。帰っていいんです。

と言うわけでAさんはBさんにこういいました。

A「今から言う数字と自分の数字を足して、その数をDさんにまで伝えて言ってください。そしてDさんまで行ったらDさんはその数を答えてください」

Bさんは全て理解しました。

同様に自分も処理をしたら帰宅できる方法でCさんにタスクを投げました。

B「数字と自分の数字を足してね。Dさんまでよろしく」

Cさんも同じことをDさんに伝えて、

最終的に
Dさん「10です！」
となって、伝言ゲームが完了しました。

![スクリーンショット 2018-10-29 15.43.29.png](https://qiita-image-store.s3.amazonaws.com/0/211255/44abd3b2-87ca-375d-890e-15c207a2992c.png)

一度仕事を終えた人が、さらに何かを処理するためにその場に待機している必要が無くなりました。

これが末尾再帰です。

#書きたくなかったけど、実際のコード

1から10までの総和を求めるコードを通常再帰と末尾再帰で書いてみます。

##通常再帰

```ruby:qiita.scala
def sum(start: Int, end: Int): Int = {
  def loop(i: Int):Int = {
    if(i <= end) i + loop(i + 1)
    else i
  }
  loop(start)
}

sum(1,10)
# 55
```

##末尾再帰

```ruby:qiita.scala
def sum2(start: Int, end: Int): Int = {
  @tailrec #末尾再帰なのかどうかをコンパイルにチェックしてもらえるアノテーション。末尾再帰でないとコンパイルエラーになる
  def loop(i: Int, acc: Int):Int = {
    if(i <= end) loop(i + 1, acc + i)
    else acc
  }
  loop(start,0)
}

sum2(1,10)
# 55
```

上の例だと
i + loop(i + 1)が処理を待っている例ですね。
loop(i + 1)の結果を待ち続けて、その後iを足してあげる仕事が待ってますね。

下の例だと
loop(i + 1, acc + i)で全てを次のloop君に投げてます。
もう待っている必要はありません。


#どうして末尾再帰じゃないとダメなの？

技術的な話をすると、関数を呼び出すと関数自身の情報やどこに戻るかなどを格納したスタックフレームが生成されます。
末尾再帰でないと、このスタックフレームがどんどん生成されていき、処理が完了するまで存在し続けます。

今回は4人の例だったからそこまで影響はなかったのですが、これが数万とかのレベルになってくるとそんな人数を収容できる施設はありません。

末尾再帰ですと、伝えた人はもう帰っていいので、人数は一人分で済みます。
なぜ二人(例でいうとAさん、Bさん)でなく、一人分でいいのかと言うと、同時に二人がその場に存在する必要すらないからです。
(一人が書き置きして、その人が帰った後に次の人がその書き置きを見れば済むから)

というわけで末尾再帰は、大変エコロな仕組みでした。
フィーチャーフォン(ガラケー)向けWebサイトのスクレイピング

にわかには信じがたいが、FPサイトを運用するシーンは少なからず存在する。

運用担当としては
効率的に表示確認ができるようにスクレイピングツールで一括スクレイピングして
「とりあえずページがぶっ壊れてない」ということを担保したい。

# 課題

### XML文法エラー

素直にpuppeteerでスクレイピングすると
XMLの文法エラーによって正しく表示できないFPサイトがいくつかある。

著名なエミュレータ（FireMobileSimulator）では
Content-TypeをHTMLに強制変換してXMLの文法エラーを回避しており、
puppeteerでスクレイピングするには同じことをやってあげる必要がある。

### UIDの設定

FPサイトは基本的にUIDを使ったログイン処理を行なっているので
UIDを偽装して会員専用ページなどをスクレイピングしたいシーンがある。
UIDはRequestHeaderにいくつかのプロパティを追加する必要がある。

# fp-scraper

上記の課題をもろもろ解消したNodeモジュール。
数行のコードを記述するだけで任意のWebページを一括でスクレイピングできる。

### インストール

````bash
mkdir scraping
cd scraping

npm init -y
npm i -S fp-scraper
vi app.js
````

### プログラム作成

````app.js
const FpScraper = require('fp-scraper');

(async() => {
  const scraper = new FpScraper({
    // スクレイピングしたいURLを配列で渡す
    urls: ['http://k.nhk.jp/'],
    // UIDの設定
    uid: 'MY_CUSTOM_UID'
  });

  await scraper.scrape();
})();
````

### 実行

````bash
// resultディレクトリにキャプチャが格納される
node app.js
````

### 余談

fp-scraperと組み合わせてpixelmatchで差分比較すると
より効果的なスクレイピングツールによる表示確認が実現できる。


# URL

* fp-scraper
  * https://github.com/cc822jp/fp-scraper
* pixelmatch
  * https://github.com/mapbox/pixelmatch
Ruby on Railsで動的にattributeに値を代入する方法

# 動的にattributeに値を代入する方法

```ruby:example.rb
hoge = Hoge.new
hoge.fuga = piyo
```
上記のように、インスタンス（hoge）のattribute（fuga）に値（piyo）を代入する際、attribute（fuga）、値（piyo）を動的に（変数で）代入したい場面があると思います。

## 使用例
例えば、Userモデルにexperience_categories（経験カテゴリー）といったattributeがあるとします。
sendメソッドを使うことで動的にattributeを定義できます。

```ruby:uses_controller.rb
  user = User.new
  experience_category_params = [['deep_learning', 'has_exp'], ['block_chain', 'ovr_3yrs']] # とりあえずこのような配列で代入します
  
  # 以下のようにattributeに値を代入していきます
  experience_category_params.each do |experience_category_param|
    user.send("#{experience_category_param[0]}=", experience_category_param[1])
  end
  
  puts user.deep_learning # =>'has_exp'
  puts user.block_chain # =>'ovr_3yrs'
end
```
User.send(experience_category_param[0]) = experience_category_param[1]
としがちですが、syntax_errorとなります。

## sendメソッドを使うとできます

sendメソッドは、第一引数にメソッド名、第二引数はそのメソッドに渡すパラメーターを定義します。

第一引数のメソッド名に目的のattribute名を書き、後ろに'='を加えることで、attributeに第２引数の値が代入されます。なお、'='を追加するためには、式展開（"#{}"）することが必要です。

結構使う機会がありそうなので、メモしてみました。
数学が超苦手な人におすすめ！再帰関数の流れを階乗を使って理解してみる


# 概要
自分が今作っているプログラムで再帰を知る必要がありそうだなと思ったので、階乗を用いてどういう処理が流れているのが改めて学習してみました。

# 対象読者
- 学生時代に数学が赤点
- 数式を見ても意味がわからない
- 学生時代に再帰を学んだがよくわかっていないままの人
![saiki.png](https://qiita-image-store.s3.amazonaws.com/0/264910/594ed406-81aa-5173-bc44-e3290a497417.png)


## 階乗とは？

$n! = (n-1) × (n-2)・・・$で表されるものです
詳しくはここらへんを見るといいと思います
[階乗]
(http://www.geisya.or.jp/~mwm48961/kou2/factorial1.htm)
ここも結構わかりやすかったです
[再帰呼出し](https://ylb.jp/2006b/proc/recursion/)


nは任意の数字です プログラムで `factorial(5)`とかで実行したら$n=5$になります
ビックリマーク(エクスクラメーションマーク)は階乗を意味する記号だと思ってください

$5!$の$!$は$5×4×3×2×1$を省略した書き方なだけです

これだけだとよくわかりませんが実際に例を見てみるとわかりやすいと思います

たとえば、5の階乗を求める場合の答えは5を含めた5~1までをすべて掛けたものの合計です

```math

5! = 5 × 4 × 3 × 2 × 1 = 120

```

```math

4! = 4 × 3 × 2 × 1 = 24

```


# 再帰のサンプルプログラム(階乗)

実行環境を用意するのが簡単なのでjsで書いてみました
[PlayCode - Code Sandbox. Online Code Editor.](https://playcode.io/)
このサイトがお手軽に実行できるので楽です

```javascript
function factorial(n){
  if(n == 0) return 1;
  
  return n * factorial(n - 1);
}

// 120
console.log(factorial(5));
```

ググるとこういうプログラムがいっぱい出てきます
ちなみに再帰は終了条件を書かないと永遠に返ってこないので軽く死にます
階乗のパターンだと$n-1$で呼び続けるので、最終的に0を呼ぶことになります
その時に`return 1`をしてあげて終わらせていますね
コピペだけだとどういう過程で120になるのかよくわかっていないので処理を追ってみます

## 処理を追う

とりあえず標準出力を増やしてどういう流れで動いているのか可視化してみることにします

```javascript
function factorial(n){
  console.log('factorial(' + n + ')実行');

  if(n == 0) {
    console.log('n==0で再帰終了');
    return 1;
  }

  const ans = n * factorial(n - 1);
  console.log(ans);
  return ans;
}

factorial(5);

```

### 結果
```
factorial(5)実行
factorial(4)実行
factorial(3)実行
factorial(2)実行
factorial(1)実行
factorial(0)実行
n==0で再帰終了
1 // 1 × 1
2 // 1 × 2
6 // 3 × 2
24 // 4 × 6
120 // 5 × 24
```

こうなりました

よくわからないので標準出力の最初のansから追ってみます

- 1回目は$1 × 0 = 0$ 
    - ではなく0の場合`return 1`しているので$1 × 1 = 1$ 
- 2回目は$2 × 1 = 2$
- 3回目は$3 × 2 = 6$
- 4回目は$4 × 6 = 24$
- 5回目は$5 × 24 = 120$

こう見るとなんかわかってきた気分になれますね
要はこの数式の

```math

5! = 5 × 4 × 3 × 2 × 1 = 120

```
右の部分から掛け算していってるってことです
最終的にこうなるってことですね

```javascript
 const ans = n * factorial(n - 1);
 // 5の階乗を再帰関数で求めた場合の最終的な掛け算は↓と同義
 const ans = 5 * 24;
```

# まとめ
- 再帰がどういう流れで動いているかわからない場合は標準出力を駆使しよう
- 先人のプログラムをコピペして実装するのもいいが、流れを理解しないと自分のやりたいことがやりにくいぞ！
- 学生のうちに数学を勉強しておかないと後悔するぞ！

# 感想
再帰関数の流れがどういう過程で求められるのかよくわかっていませんでしたが、こうやって標準出力で追ってみるとなかなかおもしろい動きをしていることがわかりました
こういう学習過程をちゃんと記録していきたいですね
IoT Edgeを使ってデバイスで画像認識してみよう

<p>この投稿は東京渋谷ヒカリエ 8 階にある <a href="http://aka.ms/aajp" target="_blank">Azure Antenna</a> で実施された「今から始めるAzure 機械学習入門 「IoT Edgeを使ってデバイスで画像認識してみよう」」で使われた資料のご案内です。</p>

- <strong>IoT Edgeを使ってデバイスで画像認識してみよう</strong><br />
https://speakerdeck.com/asashiho/iot-edgewoshi-tutedebaisudehua-xiang-ren-shi-sitemiyou-00f32ec7-a96b-4c9d-bbd4-f8f3ba9379eb


<p>:satellite:&nbsp;&nbsp;新しいイベントの情報は、Connpass の <a href="http://aka.ms/aajp" target="_blank">Azure Antenna サイト</a>よりご確認いただけます。</p>

<p>:arrow_backward:&nbsp;<a href="https://qiita.com/AzureAntenna/items/71f394c100fbfa8ddcc8">「Azure Antenna 実施イベント資料（カテゴリ別）」に戻る</a></p>
Smartyでhtml_optionsを使ってdisabledさせたかった。

# Smartyでhtml_optionを使ってル状態でdisabledさせる

Smartyのhtml_optionsは普段の様に`disable`と書くだけでは許してくれませんでした。

参考
https://cloudpack.media/986

そこで上記のサイトに従ってこう書いてみる。

```html

<select>
    {html_options options=$options disabled=$disabled}
</select>
```

できた。
よく調べて無いのでリンク先をしっかり読むほうがいいと思います。
TabBarControllerを使ってみたけど，Referenceを用いるとitemが表示されなかった話

# TabBarControllerを使ったお話
TabBarControllerの使い方は以下を参考に．．．
> http://hajihaji-lemon.com/smartphone/swift/uitabbarcontroller/

画面は5つくらいになりそうだからStoryBoardReferenceを使って綺麗な開発をしたかった．
StoryBoardReferenceの使い方は以下を参考に．．．
> https://qiita.com/_CHUBURA/items/03a3cddf8fe17361c5b2

んで問題発生したんですけど，Referenceで繋げたStoryBoard内のVCに表示されるはずのTabBarItemが非表示だった．

解決策：Reference側のVCには**TabBar**を追加するのではなく**TabBarItem**のみを乗っけるといい感じに表示してくれた．
久々にRealmSwiftをpod installした時にうっかりしてたよって事の備忘録

# RealmSwiftをpod installした話
注意事項をまとめる．

1. pod initは.xcodeprojのファイルがあるところで行う．
2. Podfileに追加するpodを書き加える場合はuse_frameworks!の下．
3. platform :ios, '9.0'とかはコメントアウトから外してあげて．
4. pod install後は.xcworkspaceで作業再開する．＊.xcodeprojのままじゃinstallしたpodを使用できないよ．
5. 基本的に2回目以降はpod updateでいいよね．
毎朝大相撲力士の画像を送ってくれるLINE BOTを作った

<img src="https://qiita-image-store.s3.amazonaws.com/0/224315/134f6b98-4051-11a6-0ad7-7064fe077ccf.jpeg" width=50%>


# はじめに

朝起きるのがとても苦手です。
スマホのアラームを複数セットしていますが、聞き飽きているので、ん、と思うだけでまたすぐ寝てしまいます。

そこで、
アラーム設定時刻に定期実行する、自分の好きなものの画像をネットから拾って送信してくれるLINE BOT
を作ってみました。




# ネットの画像を拾う（Google Custom Search API）
[Google Custom Search APIを使って画像収集](https://qiita.com/onlyzs/items/c56fb76ce43e45c12339)を参考に、
APIを有効にして、自分の好きなものの画像をネットから取得しました。

```python

def get_image_urls(search_word):
    CUSTOM_SEARCH_API_KEY = "**********"
    CUSTOM_SEARCH_ENGINE_ID = "**********"
    custom_search_url = f"https://www.googleapis.com/customsearch/v1?key={CUSTOM_SEARCH_API_KEY}&cx={CUSTOM_SEARCH_ENGINE_ID}&searchType=image&q={search_word}&num=10"
    img_urls = []
    res = urllib.request.urlopen(custom_search_url)
    data = json.loads(res.read().decode('utf-8'))
    for i in range(len(data["items"])):
        img_urls.append(data["items"][i]["link"])
    return img_urls

def get_image(search_word):
    img_urls = get_image_urls(search_word)
    img_url = random.choice(img_urls)
    img = requests.get(img_url).content
    return img
```



# 自動でLINEを送信する（LINE Notify）

[PythonからLINE NotifyでLINEにメッセージを送る](https://qiita.com/tadaken3/items/0998c18df11d4a1c7427)を参考に、
LINE Notifyに登録し、画像を送信するBOTを作りました。

```python
def line(img):
    LINE_NOTIFY_TOKEN = "**********"
    line_notify_url = 'https://notify-api.line.me/api/notify'

    message = '起きる時間です'
    payload = {'message': message}
    headers = {'Authorization': 'Bearer ' + LINE_NOTIFY_TOKEN}
    files = {"imageFile": img}
    r = requests.post(line_notify_url, headers=headers, params=payload, files=files)
```



# プログラムを定期実行する（AWS Lambda + CloudWatch）

[12時になったらお昼を告げるためSlackに「ごはんですよ」の画像を投稿させたい](https://geeknavi.net/aws/posting-image-of-gohandesuyo-at-noon)を参考に、Lambda Functionを作成し、CloudWatchで定期実行させました。
サードパーティのライブラリであるrequestsを使う際、[【AWS】Lambdaでpipしたいと思ったときにすべきこと](https://qiita.com/Hironsan/items/0eb5578f3321c72637b4)を参考にしました。

```python
import json
import random
import urllib.request
import urllib.parse
import requests

def lambda_handler(event, context):
    search_words = ['千代丸', '遠藤関', '高見盛', '朝青龍', '寺尾力士', 'わんぱく相撲']
    search_word = random.choice(search_words)
    search_word = urllib.parse.quote(search_word)
    img = get_image(search_word)
    return line(img)
```


# まとめ

これで毎朝スッキリと目覚めることが出来そうです。

コードは全て[github](https://github.com/a0082273/line_bot)にあります。間違い等ご教授いただけたらうれしいです。
Lambda Functionの一覧を取得する

# Lambda Functionの一覧を取得する

Lambda Functionの一覧を取得したい時があります。
SDKがデフォルトで、50個しか返してきませんので、再帰的に検索する必要があります。
SDKのレスポンス中に、「NextMarker」プロパティが存在した場合は、返せなかったリストが残っているのでlistFunctionsの引数に、「NextMarker」プロパティを渡す事で、残りのリストを取得する事ができます。

## 全てのリストを取得するサンプル

```
const AWS = require('aws-sdk');
const _ = require('lodash');

const lambda = new AWS.Lambda({
  maxRetries: 10,
  retryDelayOptions: {
    base: 1000,
  },
});

/**
 * 全てのlambda関数名を取得します。
 * Lambda.listFunctionsは一度に取得できるデータが限られるため、
 * NextMarkerが無くなるまで再帰的に処理を行います。
 *
 * @param lambda AWS.Lambda
 * @param prefix フィルタするプレフィックス文字列
 * @param nextmarker 取得可能な関数名が残っている場合に利用する、ページング用トークン
 * @return array of string FunctionName
 * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Lambda.html#listFunctions-property
 */
function listFunctionsAll(lambda, nextmarker) {
  const params = nextmarker ? { Marker: nextmarker } : {};

  const p1 = lambda.listFunctions(params).promise();
  return p1.then((returnData) => {
    console.info('Functions.length:', returnData.Functions.length);
    console.info('NextMarker:', returnData.NextMarker);
    // これ以上取得できるデータがない場合、配列を整形して返す。
    if (!returnData.NextMarker) {
      return returnData.Functions.map(f => f.FunctionName);
    }

    // 一度で取得できなかった場合、NextMarkerが無くなるまで再帰的に取得する。
    const p2 = listFunctionsAll(lambda, returnData.NextMarker);

    // 取得したデータを整形し、再帰的に取得したデータと結合する。
    return p2.then(returnData2 =>
      _.concat(returnData.Functions.map(f => f.FunctionName), returnData2));
  });
}

/**
 * Lambda関数の検索結果を配列で返します。
 * Lambda関数名を全て取得した後、prefixでフィルタリングします。
 *
 * @param lambda AWS.Lambda
 * @param prefix フィルタするプレフィックス文字列
 * @return array of string FunctionName filterd by prefix
 */
function listFunctions(lambda, prefix) {
  const exclude = [
    process.env.AWS_LAMBDA_FUNCTION_NAME,
  ];
  const result = listFunctionsAll(lambda);
  return result.then(data =>
    data.filter(f => f.startsWith(prefix) && !exclude.includes(f)));
}

listFunctions(lambda, prefix)
　　　　.then((res) => {
    console.info(res);
  })
  .catch((err) => {
    console.error(err);
  });
```
RxJSのretryWhen Operatorで複数回の遅延リトライ

## はじめに

次のようなユースケースを想定してタイトルの通り `RxJSのretryWhen Operatorsで複数回の遅延リトライ` を試してみたいと思います。

- rateLimitがあるWebAPIの呼び出し
- AWS SDKなどの呼び出し時にリトライ処理に遅延時間を設定したい場合
    - 今回この記事を書こうと思ったきっかけも AWS Cognito SDKのrateLimit制限を避けるためでした...


## たまに失敗して、連続で呼ぶとrateLimitで弾かれるソースを作成

リトライ処理の例を示す前に、リトライ対象となるモックのソースを作成します。
rateLimitがあるWebAPIをイメージするとわかりやすいかと思います。
以下の例では、次のようなエラーが発生するようにしています。

- たまに `andom error` が発生
- 1sの間に5回以上の呼び出して、`ate limit error` が発生

```ts
const rateLimitPerSec = 5;
let rateHistory: number[] = [];

// mock source
const source$ = () =>
  of(Math.floor(Math.random() * 5)).pipe(
    mergeMap((value) => {
      rateHistory = [Date.now(), ...rateHistory];
      rateHistory.length = rateLimitPerSec;

      const callTimeRateLimitAgo = +rateHistory[rateLimitPerSec - 1];

      if (!Number.isNaN(callTimeRateLimitAgo)) {
        const sec = 1 * 1000;
        if (Date.now() - callTimeRateLimitAgo <= sec) {
          return throwError('rate limit error occur');
        }
      }

      return of(value);
    }),
    mergeMap((value) => {
      if (value === 0) {
        return throwError('random error occur');
      }

      return of(value);
    }),
  );
```

※ ちょっと雑なところはありますが、雰囲気が伝わればいいかと

## 普通にretry Operatorでn回のリトライをする

上で作成したmockのソースに　リトライ回数を指定して retry Operator　を挟む

```ts
const retryCount = 5;

// retryを使用
source$()
  .pipe(
    retry(retryCount),
  )
  .subscribe((value) => console.log(value), (e) => console.log(e));
```

## retryWhen Operatorでn回、1回失敗するごとにn * 1sずつ遅延リトライ

上で作成したmockのソースに　retryWhen Operator　を挟む
`retryWhen` でリトライ条件を指定することができるので、試行回数がretryCount以下だった場合は、
試行回数 * 遅延時間秒数待ってリトライするように条件指定をすることで実現できる。

```ts

const retryCount = 5;
const retryExecuteDuration = 1000;

// retryWhenを使用
source$()
  .pipe(
    retryWhen((errors) => {
      return errors.pipe(
        mergeMap((e, index) => {
          if (index > retryCount) {
            return throwError(e);
          }

          return timer((index + 1) * retryExecuteDuration);
        }),
      );
    }),
  )
  .subscribe((value) => console.log(value), (e) => console.log(e));
```

## 試したソース

https://stackblitz.com/edit/rxjs-retry-vs-retrywhen?file=index.ts

## 参考

- https://www.learnrxjs.io/operators/error_handling/retrywhen.html
- https://qiita.com/bouzuya/items/5e068659d63d9961a260
abc 102

C

```
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<list>
using namespace std;
using ll = long long;
const int INF = 1e8;
int N;
vector<int>c;
int main() {
	int N;
	int A[200001];
	cin >> N;
	int bmax = 0;
	for (int i = 0; i < N; ++i) {
		cin >> A[i];
		bmax = max(bmax, A[i]);
	}//iでbを回す
	int ans = INF;
	for (int i = -bmax; i <= bmax; ++i) {
		int sum = 0;
		for (int j = 0; j < N; ++j) {
			sum += abs(A[j] - (i + j));
		}
		ans = min(ans, sum);
	}
	cout << ans << endl;
	return 0;
}
```
TLE

```
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<list>
#include<cmath>
using namespace std;
using ll = long long;
const int INF = 1e8;
int main() {
	int N;
	int A;
	cin >> N;
	vector<int>B(N,0);
	for (int i = 0; i < N; ++i) {
		cin >> A;
		B[i] += A - (i+1);
	}
	sort(B.begin(), B.end());
	ll ans = 0;
	ll b;
	
	if (N % 2) {
		b = B[N / 2];
		for (int i = 0; i < N; ++i) {
			ans += abs(B[i] - b);
		}
		cout << ans << endl;
	}
	else {
		b = round((B[N / 2] + B[N / 2 - 1]) / 2);
		for (int i = 0; i < N; ++i) {
			ans += abs(B[i] - b);
		}
		cout << ans << endl;
	}
	return 0;
}
```
これ、中央値をとればよくて当然Nが偶数の時どうするかっていうはなしになってくるけどこのコードみたいに場合分けする必要はないっぽい
https://yamakasa3.hatenablog.com/entry/2018/07/02/041604
ここに詳しく乗っていた。なるほどって感じ

```
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
#include<map>
#include<math.h>
#include<queue>
#include<deque>
#include<stack>
#include<cstdio>
#include<utility>
#include<set>
#include<list>
#include<cmath>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int INF = 1e9;

int main() {
	int N;
	cin >> N;
	vector<int>A(N);
	for (int i = 0; i < N; ++i) {
		cin >> A[i];
		A[i] -= (i + 1);
	}
	sort(A.begin(), A.end());
	int b = A[N / 2];
	ll ans = 0;
	for (int i = 0; i < N; ++i) {
		ans += abs(A[i] - b);
	}
	cout << ans << endl;
	return 0;
}
```
だから普通にこれで通った
TeraTermでローカルPCからファイルを送り込んで実行するバッチファイル

#要件
ローカルPCでcsvファイルを作成して、Linuxサーバに送信してそのファイルをインプットに処理を実行したい。
引数とかちょっと苦労したので備忘録。

#やり方
Tera Termのマクロ機能で自動ログイン→scpでファイル転送→コマンド実行→exitを実行する。

```teralogin.ttl
;=====================================================================
; 接続情報 引数[2]～[4]からセット
;=====================================================================

; コマンドオプション組立て
COMMAND = params[2]
strconcat COMMAND ':22 /ssh /2 /auth=password /user='
strconcat COMMAND params[3]
strconcat COMMAND ' /passwd='
strconcat COMMAND params[4]

; 接続
connect COMMAND
;; 接続完了を待つ(環境に合わせて待つ文字列は調整必要)
wait '$'
sendln '. ~/.bashrc'

; ファイル送信完了を確認する
SOURFILE = 'D:\DATA.csv'
DESTFILE = '~/DATA'

;; ファイル送信
scpsend SOURFILE DESTFILE
;; ファイル送信プロセス確認
do
; 5000ミリ秒休止
  mpause 5000
  sprintf2 str 'ps -ef |grep -v grep |grep -c scp'
  sendln str
; 文字列を含む行を待つ
  waitln '0' '1'
loop while result != 1
;; ファイル送信が完了すると次のマクロを実行
;;; 実行したいコマンドを記述。
sendln 'chgrp datagrp ' DESTFILE
sendln 'ls -l' DESTFILE
;; マクロ終了

;; 画面を閉じる
;;sendln 'exit'
end
```
※並行で長時間のscpが動いているとそれに合わせてwaitが掛かり続けます(状況に合わせて調整してください)。

ttlファイルに合わせて、引数を渡す。
マクロファイル名が引数[1]となります。
引数[2]=ホスト名
引数[3]=ユーザー名
引数[4]=パスワード

```kidou.bat
rem @echo off
set macro="D:\teralogin.ttl"

"C:\Program Files\teraterm\ttpmacro.exe" %macro% 192.168.77.123 USERID PASSWD
```

#パスワードを都度入力するには
入力ボックスを出して、params[4]の代わりに編集する。
工夫すれば、引数で渡されたら入力は無しにも出来る。

```teralogin.ttl(一部)
;; パスワードを要求
inputbox 'パスワードを入力してください' 'TeraTerm ログイン'
PASSWORD = inputstr 
strlen PASSWORD
if result = 0 then 
    messagebox 'パスワードが入力されていません' 'TeraTerm ログイン' 
    end 
endif

; コマンドオプション組立て
COMMAND = params[2]
strconcat COMMAND ':22 /ssh /2 /auth=password /user='
strconcat COMMAND params[3]
strconcat COMMAND ' /passwd='
strconcat COMMAND PASSWORD
;;strconcat COMMAND params[4]

; 接続
connect COMMAND
(以下同じ)
```
csvに含まれるnanを無視する

#背景
50音のローマ字をcsvでデータにまとめ、pandasで読み取るとき、
や行やワ行に含まれる空白が「NAN」という文字として扱われてしまう
空白は空白として扱いたい。
#解決方法
df = df.fillna('')
を使って、読み取ったデータフレームのスペースを''に変換すればOK

nanの削除などは複数記事があったが置き換える記事がなかったのでメモ
Windows 7 > ウィンドウを上下に並べて表示 > 4つのウィンドウの配置を意図的にする

```txt:動作環境
Windows 7 Pro (32bit)
```

### したいこと

- 4つのソフトが起動している
- 4つのソフトの位置を意図的にしたい
    - 例：
        - 1: 左上
        - 2: 左下
        - 3: 右上
        - 4: 右下

### 手順

1. 4のソフトをクリック
2. 3のソフトをクリック
3. 2のソフトをクリック
4. 1のソフトをクリック
5. タスクバー > ウィンドウを上下に並べて表示

![2018-10-29_14h40_03.png](https://qiita-image-store.s3.amazonaws.com/0/32870/0d36c239-7513-ada7-b6c3-0982f1d08cb1.png)

### 備考

「ウィンドウを左右に並べて表示」では振舞は異なる。

### この機能を使う利点と制限

複数ソフトの整列をする場合、ソフトを順次フォーカスをあててから「ウィンドウを上下に並べて表示」すれば自前ソフトからの実行はできそう。

その場合の利点と制限。

- 利点
    - 幾何的な計算を自前で実装しなくていい
- 制限
    - 全てが同じウィンドウサイズになる
サイト上部に"Fork me on GitHub"を表示するやつ

# 概要

タイトルの通りです。探してたアレ。

## リボン風のやつ

[GitHub Ribbons | The GitHub Blog](https://blog.github.com/2008-12-19-github-ribbons/)

## Octocatくんが動くやつ

[tholman/github-corners](https://github.com/tholman/github-corners)
[GitHub Corners](http://tholman.com/github-corners/)

# まとめ

かわいい。
PointCloud Upsamplingについて

PointCloudのUpsampling方法についてまとめました。

## Upsampling
今回は[mesh_sampling](https://github.com/PointCloudLibrary/pcl/blob/master/tools/mesh_sampling.cpp)によりUpsamplingを行います。
Mesh Samplingは点群に対してPolygon Meshを施し、各Polygonの内部を補完する処理を行っています。

## 環境
- OS Ubuntu16.04
- PCL1.8

## 目標
今回はオリジナルのPCDデータに対してDownsamplingしたデータをUpsamplingでどの程度復元可能か確認することを目標とします。
PCDファイルは[table_scene_lms400.pcd](https://github.com/PointCloudLibrary/data/tree/master/tutorials)を使用しました。
ダウンサンプリングにより、点群数は460400 pointsから41049 pointsに削減されました。

```bash
pcl_voxel_grid table_scene_lms400.pcd table_scene_lms400_downsample.pcd -leaf 0.01 0.01 0.01
```

|Raw Data|Downsample|
|---|---|
|![raw.png](https://qiita-image-store.s3.amazonaws.com/0/239786/257b33d7-6ed0-b0c1-5d9b-554f41e40c57.png)|![downsample.png](https://qiita-image-store.s3.amazonaws.com/0/239786/d8e750a2-41c6-9a36-c25c-ad62bb9e9608.png)|


## PolygonMeshを生成
Mesh Samplingを行うためにはplyファイルを作成する必要があります。
まず、Upsamplingを行いたいデータのPolygonMeshを生成します．
PolygonMeshについては[VM LABさんの記事](http://virtuemarket-lab.blogspot.com/2015/03/blog-post_49.html)を参考にしてみて下さい。

## vtkファイルをplyファイルに変換
PolygonMeshはvtkファイルにて保存されるので、plyファイルに変換します。
vtkファイル名はmap.vtkとしています。

```bash
pcl_vtk2ply table_scene_lms400.vtk table_scene_lms400.ply
```

## Mesh Samplingを実行
plyファイルを用いてMesh Samplingを行います。

```bash
pcl_mesh_sampling table_scene_lms400.ply table_scene_lms400_meshsample.pcd 
```
Mesh Samplingのパラメータはサンプル数を指定する-n_samples、ダウンサンプルのサイズを指定する-leaf_sizeがあります。
必要に応じてパラメータ調整を行って下さい。
今回はダウンサンプルする前の点群数である460400 pointsにしました。

```bash
$ pcl_mesh_sampling -h
Convert a CAD model to a point cloud using uniform sampling. For more information, use: pcl_mesh_sampling -h
Syntax is: pcl_mesh_sampling input.{ply,obj} output.pcd <options>
  where options are:
                     -n_samples X      = number of samples (default: 100000)
                     -leaf_size X  = the XYZ leaf size for the VoxelGrid -- for data reduction (default: 0.010000 m)
```

## Result
Downsamplingにより間引きされた空間をUpsamplingにより補完することができました。
より良いUpsamplingをするためには、点密度やPolygonのサイズなどパラメータ調整、ノイズフィルタリングをする必要がありそうです。

|Polygon Mesh|Mesh Sampling|
|---|---|
|![polygonmesh.png](https://qiita-image-store.s3.amazonaws.com/0/239786/6c45a362-a953-0666-a34e-cf6a00667fb9.png)|![upsample.png](https://qiita-image-store.s3.amazonaws.com/0/239786/e45f665b-b890-835c-c44f-2c097aef9197.png)|
Roomを使ったAndroidアプリをアンインストールしてもデータが残っている気がする

## 対象ケース
- アプリをアンインストールをして再インストールしてもデータが残っている気がする
- アンインストール前に設定->アプリから`ストレージを消去`をしてるのに再インストール時にデータが残っている気がする

## `android:allowBackup="false"`が必要
Android 6.0 (API 23)からデフォルトで有効になっているAuto Backupがtrueになっているかもしれません。
`AndroidManifest.xml`を確認してみてください。

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.shanonim.sample">

    <application
        android:name=".SampleApp"
        android:allowBackup="false"
```

## RoomもAuto Backupの対象
デフォルトのAuto Backup対象ファイルは次のとおりです。（引用元: <a href="https://developer.android.com/guide/topics/data/autobackup" target="_blank">Back up user data with Auto Backup</a>）

```
By default, Auto Backup includes files in most of the directories that are assigned to your app by the system:

- Shared preferences files.
- Files saved to your app's internal storage, accessed by getFilesDir() or getDir(String, int).
- Files in the directory returned by getDatabasePath(String), which also includes files created with the SQLiteOpenHelper class.
- Files on external storage in the directory returned by getExternalFilesDir(String).
```

Roomはそもそも、Android Architecture Componentsの中で「SQLiteを扱いやすくするための機能」として提供されています。つまり、上記の対象ファイル説明の中で`also includes files created with the SQLiteOpenHelper class.`と説明されている部分にRoomで作成したデータベースも含まれているようです。

Roomを使っている、かつAuto Backupを使用する必要のない場合は、`android:allowBackup`を確認してみましょう。

## 参考リンク
- Remove room database on app uninstall  
https://stackoverflow.com/questions/49677497/remove-room-database-on-app-uninstall
pyenvでインストールしようとした際のBUILD FAILEDエラー対処


## 環境
Linux, macOSも同様かと

## エラー
```sh
$ pyenv install anaconda3-5.2.0
Downloading Anaconda3-5.2.0-Linux-x86_64.sh...
-> https://repo.continuum.io/archive/Anaconda3-5.2.0-Linux-x86_64.sh
error: failed to download Anaconda3-5.2.0-Linux-x86_64.sh

BUILD FAILED (CentOS 7.3 using python-build 1.2.7-11-g835707da)
```

どうやらダウンロードに失敗しているみたい。

`-v`オプションで詳細表示

```sh
$ pyenv install -v anaconda3-5.2.0
/tmp/python-build.20181029135756.168580 ~
Downloading Anaconda3-5.2.0-Linux-x86_64.sh...

10/29 13:57:56 [NOTICE] Downloading 1 item(s)

10/29 13:57:56 [ERROR] CUID#7 - Download aborted. URI=https://pyenv.github.io/pythons/09f53738b0cd3bb96f5b1bac488e5528df9906be2480fe61df40e0e0d19e3d48
Exception: [AbstractCommand.cc:351] errorCode=1 URI=https://pyenv.github.io/pythons/09f53738b0cd3bb96f5b1bac488e5528df9906be2480fe61df40e0e0d19e3d48
  -> [SocketCore.cc:1015] errorCode=1 SSL/TLS handshake failure: unable to get local issuer certificate

10/29 13:57:56 [NOTICE] Download GID#305477b33aa2574c not complete:

Download Results:
gid   |stat|avg speed  |path/URI
======+====+===========+=======================================================
305477|ERR |       0B/s|https://pyenv.github.io/pythons/09f53738b0cd3bb96f5b1bac488e5528df9906be2480fe61df40e0e0d19e3d48

Status Legend:
(ERR):error occurred.

aria2 will resume download if the transfer is restarted.
If there are any errors, then see the log file. See '-l' option in help/man page for details.
-> https://repo.continuum.io/archive/Anaconda3-5.2.0-Linux-x86_64.sh

10/29 13:57:56 [NOTICE] Downloading 1 item(s)

10/29 13:57:56 [ERROR] CUID#7 - Download aborted. URI=https://repo.continuum.io/archive/Anaconda3-5.2.0-Linux-x86_64.sh
Exception: [AbstractCommand.cc:351] errorCode=1 URI=https://repo.continuum.io/archive/Anaconda3-5.2.0-Linux-x86_64.sh
  -> [SocketCore.cc:1015] errorCode=1 SSL/TLS handshake failure: unable to get local issuer certificate

10/29 13:57:56 [NOTICE] Download GID#220ed3b0beb09342 not complete: /tmp/python-build.20181029135756.168580/Anaconda3-5.2.0-Linux-x86_64.sh

Download Results:
gid   |stat|avg speed  |path/URI
======+====+===========+=======================================================
220ed3|ERR |       0B/s|/tmp/python-build.20181029135756.168580/Anaconda3-5.2.0-Linux-x86_64.sh

Status Legend:
(ERR):error occurred.

aria2 will resume download if the transfer is restarted.
If there are any errors, then see the log file. See '-l' option in help/man page for details.
error: failed to download Anaconda3-5.2.0-Linux-x86_64.sh

BUILD FAILED (CentOS 7.3 using python-build 1.2.7-11-g835707da)
```

インストールしていた高速ダウンローダー aria2 が正常に動作していないようだ。

## 解決策
aria2へのPATHを外して使用されるのをやめさせる。aria2が入っていなければ代わりにcurlを使ってくれるようになる。
私の場合は，linuxbrewでaria2cをインストールしていたので，一時的にPATHを外す。

```sh
$ export PATH=$(sed 's|:\?/home/user_id/.linuxbrew/bin||;s/^://' <<< $PATH)
```

```sh
$ pyenv install anaconda3-5.2.0
```
正常にインストールできた。

## 参考
https://qiita.com/uesseu/items/a860a54d245b96ea459d
ドメインとは

![ドメイン.jpg](https://qiita-image-store.s3.amazonaws.com/0/290624/4ae6c998-5e5c-a696-ff8c-59eae90e825a.jpeg)


#目次
+ ドメインとは
+ ドメインの使用場面 
+ サブドメインとは
+ 独自ドメインとは
+ 独自ドメインを作るメリット
+ DNS


#ドメインとは
![domain2.jpg](https://qiita-image-store.s3.amazonaws.com/0/290624/ed2c7125-2a18-06fe-fda3-b5ddbe74098b.jpeg)

ドメインはインターネット上のコンピュータの住所を簡略した名称のことをいいます。
インターネット上のコンピュータの住所を`IPアドレス`といいます。IPアドレスは192.XXXXXX...などと数字で表記されます。
IPアドレスをドメインと結びつけるしくみをDNS(ドメインネームシステム)といいます。
すでに公開されている〇〇〇〇.comといったものは、DNSを使用していることになります。


##ドメインの使用場面
ドメインは主にURLとメールに使われています。

**URLに使われるドメイン**
Qiitaでいうと、薄グレーで引いたところが`ドメイン名`になります。
https://`qiita.com`

https://``www``.aaaaといったように、`www`があるものとないものがありますが、あってもなくてもSEOにも影響がないようです。
https://satopedia.com/www/

**メールに使われるドメイン**
xxx＠`docomo.jp`


#サブドメインとは
ドメイン名の頭にサブドメインをつけることで分野別にドメインを分けることができます。
メインのドメインとは独立したものになります。

https://`shop`.ドメイン名
https://`news`.ドメイン名
https://`co`.ドメイン名



#独自ドメイン
ドメインはレンタルサーバーでは初めから用意されていますが、自分で購入することもできます。
TLD(トップレベルドメイン)や期間にによって値段は変わります。
https://www.onamae.com/service/d-price/?btn_id=02_top_pricearea_domainprice_20180330

**TLD**
TDL(トップレベルドメイン)とは、ドメイン名の後にドットで連記された部分で、サイトの種類を示す部分になります。
〇〇.`com`や〇〇.`jp`がTDLにあたります。TDLにも種類があり、gTLD(ジェネリックトップレベルドメイン)、sTLD(スポンサー付きトップレベルドメイン)、ccTLD(国別コードトップレベルドメイン)などがあります。
SEOにそこまで関係ないそうです。

ーーー
ちなみに、日立のTLDは`hitachi`
http://nic.hitachi/jp/news/20150915.html
日本ネットワークインフォメーションセンターが募集した時に応募して採用されるとできるみたいです。
前回は2016年だったみたいです。
https://www.nic.ad.jp/ja/question/domain10.html


#独自ドメインを作るメリット
##SEO
![domain3.jpg](https://qiita-image-store.s3.amazonaws.com/0/290624/e78b9b96-16a6-ddfb-be3a-df4e3aa7c3af.jpeg)
独自ドメインを作るからSEOの良いサイトになるわけではありません。結局はコンテンツですが、Googleでは１ページ目に表示される同一のドメイン名のページが制限されるそうです。
また、サブディレクトリでブログを書く場合、ドメインのSEOも影響されます。
見てほしい内容の記事を書く場合、SEOがいいドメインのサブディレクトリで書くのがいいでしょう。

https://ferret-plus.com/1830


##ドメイン名の存続
もし利用しているレンタルサーバーがなくなったとしても、ドメイン名は残り続けるため、新しいサーバーに結びつければSEOも引き継いでくれます。


#DNS
初めての機械学習〜Python, Tensorflowのインストールまで〜

# 初めての機械学習
この記事は、初めて機械学習に触れる初心者を対象に書かれています。

# Anaconda Navigaterを使おう
- Anacondaを使用したPythonのインストール（3.6）
https://www.anaconda.com/download/#macos

- Anaconda Nabvigaterで新しいユーザーの作成をしよう
    - 仮想環境ごとにパッケージを管理することで、バージョン管理ができる

# Jupyter Notebookを使ってみよう。
## Jupyter Notebookとは？
仮想環境を提供してくれるやつ。ここでPythonの命令が打てます。

## インストールするには
- 実行環境のJupyter Notebookのインストール
    - 上記でインストールしたAnaconda Nabvigaterからインストールしてください。


# TensorFlowのインストール
- Anaconda NavigatorのEnvironmentsから先ほど作成された環境を選択。
- 三角のボタンから「open terminal」を選択

ここでTensorFlowをインストールしよう

```
$ pip install tensorflow
```

インストールされているか確認しよう。Pythonの命令をターミナル上で動かしてみる。

```
$ Python
Python 3.6.5 |Anaconda, Inc.| (default, Mar 29 2018, 13:14:23) 
[GCC 4.2.1 Compatible Clang 4.0.1 (tags/RELEASE_401/final)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
```


ここで、

```
>>> import tensorflow
```

と打ってみる。これでエラーが吐かれずにコマンドプロンプトが帰ってきたらTensorFlowのインストールが完了しているということになる。


```
exit()
```
として、Pythonの対話環境から抜けよう。


# これから始まる、機械学習へ
Anaconda NavigatorからJupyter NotebookにLaunchして、機械学習を始めましょう！
javascriptとは

![javascript.jpg](https://qiita-image-store.s3.amazonaws.com/0/290624/776d8e2e-a4c4-e204-e36e-a58f7207f413.jpeg)

ウェブ制作に欠かせないjavascriptですが、今までなんとなく使っていたため、ちゃんと調べてみました。


#目次
+ Javascriptとは
+ 開発領域
+ 言語の仕様
+ Javascriptの人気




#Javascriptとは
WEB上でインタラクティブな表現をする為に開発されたオブジェクト指向のスクリプト言語


**オブジェクト指向**
何かの対象に対して操作していくイメージ

**スクリプト言語**
プログラミング言語のうち、プログラムの記述や実行を比較的簡易に行うことができる言語の総称
コンパイルが必要ない


#開発領域
JavaScriptはブラウザに実行環境が搭載されていることから、WebサイトやWebアプリ・サービスなどを開発する際によく利用されています。
Javascriptはクライアントサイドとサーバーサイドの両方で使用されています。
ゲーム開発でも「Unity」が増えてきていてその中で使用できるものとしてJavaScriptが採用されているようです。

####クライアントサイド
クライアントサイドでは、Javascriptを簡単にしてくれるライブラリとしてjQueryがよく使用されています。HTMLやCSSだけではできない動きなどを表現することができます。フォーマットがJSONであるAPIもjavascriptで利用することができます。

####サーバーサイド
サーバーサイドはPHP、perl、ruby などが有名だが最近では`サーバーサイドJS`と呼ばれるものがでてきてJavaScriptによる開発が進んでいて、有名な言語としてnode.jsがあげられます


#言語の仕様
JavaScriptにはECMAScript（エクマスクリプト）という仕様があります。
普段記述しているJavascriptはブラウザに対してECMAScriptを標準化したものです。

ECMAScriptはEcma International(http://www.ecma-international.org/default.htm) という団体によって標準化されている仕様です。 Ecma InternationalはECMAScript以外にもC#やDartなどの標準化作業を行っています。 Ecma International中のTechnical Committee 39（TC39）という技術委員会が中心となって、ECMAScript仕様についてを議論しています。 この技術委員会はMicroSoft、Mozilla、Google、AppleといったブラウザベンダーやECMAScriptに関心のある企業などによって構成されます。

####仕様の策定プロセス
ECMAScriptにプロポーザルが査定されるには５段階のプロセスがあります。

| Stage | 名称 | 状態 |
|--:|:--|:--|
| 0 | Strawman | ただのアイデア |
| 1 | Proposal | アイデアがプロポーザルとして認められ、議論を進めている |
| 2 | Draft | ドラフトとして、APIや構文についての仕様書がある |
| 3 | Candidate | 仕様としては完成しており、実装やフィードバックを待っている |
| 4 | Finished | 仕様の策定が完了している |
現在は１年ごとにバージョンをリリースしています。4以降からその年のES20XXといったバージョンでリリースされます。その後ブラウザにその使用が搭載されることで使用できるようになります。
従って、ECMAScriptに新しいプロポーザルが策定されることでJavascriptの仕様も変化することとなります。

**現時点でのプロポーザル**
https://github.com/tc39/proposals

####バージョンによる変化
ES5(ES2014)から ES6(ES2015)で追加された内容が以下になります。

+ classによるクラス構文
+ let・constよる変数宣言
+ Template strings
+ importとexport
+ アロー関数（Arrow Functions）
+ Promise
+ ジェネレータ関数（Generator Funcitons)

####アロー関数
変わった仕様の一例としてアロー関数について紹介します。

**ES5**

```

//引数が１つ
function str(arg1) {
  console.log('hoge');
}
//引数が複数
function(arg1, arg2) {
  console.log('hoge');
}

```

**ES6**

```
//引数が１つ
var str = arg1 => console.log(arg1);
//引数が複数
var str = (arg1, arg2) => {
  console.log('hoge');
}
```

+ ES6では、アロー関数を利用することで関数をシンプルに記述できる。
+ 引数が1つの場合は()を省略できる
+ 1行に収まる場合{}も省略できる


まだまだ追加された仕様はありますが次回以降紹介します。


#javascriptの人気
StackOverflowによるとjavascriptが一番人気のようです。
![js_graph_02.jpg](https://qiita-image-store.s3.amazonaws.com/0/290624/f7e8c37b-a6f0-e7a1-81d9-86eb09279e70.jpeg)

回答者はバックエンドが多いようなのでバックエンドでもフロントエンドでも利用されているということかもしれませんね。
![js_graph_01.jpg](https://qiita-image-store.s3.amazonaws.com/0/290624/d24fb41a-7770-28b7-3606-c4ae9b231b8e.jpeg)
ORACLEでレコードが存在するテーブル一覧を出力する

```sql
SET SERVEROUTPUT ON
DECLARE
  N NUMBER;
BEGIN
  FOR C IN (SELECT TABLE_NAME FROM USER_TABLES) LOOP
    EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ' || C.TABLE_NAME INTO N;
    IF N > 0 THEN
        DBMS_OUTPUT.PUT_LINE(C.TABLE_NAME || ': ' || N);
    END IF;
  END LOOP;
END;
/
```

[実行例]

```
M_CODE: 100
T_FILE: 3
```
さくらのレンタルサーバでアカウント名が他のユーザーに晒されている件


常識的な内容かもしれないですが
自分含めあまり認識していない方もいるかもしれないので記載します。

# 前提
今回、自分が確認したレンタルサーバは「さくらのレンタルサーバー」になります。
ほかの会社が運営しているレンタルサーバでも見れる可能性があるかと思います。

# アカウント見えてるよ

共用レンタルサーバーではHome配下に専用のディレクトリが切られており、その配下を自由に使っていいよ。というのが恐らく一般的かと思います。
> `/home/example`

さくらサーバー側が提供しているGUIツール(ファイルマネージャー)ではこの専用ディレクトリより上には行けないようになっています。

ではsshやftp接続ではどうなるでしょう?
なんとhomeに入れてしまいます。
> `/home`

共用サーバーなので当然同居しているユーザーがいるわけです。
そんな中、homeディレクトリに入れてしまうとどうなるでしょう。

そうですね。home配下のディレクトリ名がばっちり見えてしまうのです。

こんな感じのディレクトリがずらっと並んでる訳です

```
***group
tanaka-tarou
<サイト名>-dev
```

アカウント名もろばれですね。

# サイトも見られてるぞ
そしてこのアカウント名、けっこうな人がそのままサイトのURLに使っているんですね。

上の例でいうと
`tanaka-tarou.sakura.ne.jp`
的な。

これ、見に行こうと思えば見に行けちゃうわけです。
見られて困るサイト作ってる人は気を付けましょう。

まあ、Webに公開してる時点で気を付けておけって話ですが。

# おわり
共用なのは理解しておりましたがhomeディレクトリに行けるとは思っておりませんでした。
おわり。
EgretEngineで超簡単なゲームを作ってみよう(5) 書き出し編 

# 連載記事「EgretEngineで超簡単なゲームを作ってみよう」

[(1) プロジェクト作成＆ビルド] (https://qiita.com/motoyasu-yamada/items/584c46ea7e05601e96fa)
[(2) 物理エンジン エクステンションの導入](https://qiita.com/motoyasu-yamada/items/c5facea39b0102d7a365)
[(3) タップでの操作の実装](https://qiita.com/motoyasu-yamada/items/2ff3762015daccd4ebd3)
[(4) 障害物の実装と各種カプセル化](https://qiita.com/motoyasu-yamada/items/6963428cb3ba5ffc7a64)
[(5) 書き出し編](https://qiita.com/motoyasu-yamada/items/013909a29e65f1f4ec1d)

---
# (5) 書き出し編

前回までで作成したHTML5アプリを配信するための書き出しを行います。

# 手順
## 1. Egret Launcherから書き出し設定画面を開く

Egret Launcherを起動して、プロジェクト一覧にある、下記の赤丸の箇所のボタン「Publish Setting」を選択します

![image.png](https://qiita-image-store.s3.amazonaws.com/0/302078/5532ce88-a90a-b064-8e20-eeeb1df2bc85.png)

## 2. 配信方法としてHTML5を選択
配信方法としては、HTML5 / iOS / Android / Wechat game / Facebook / QQ Play / Blockchain gameが選べます

![image.png](https://qiita-image-store.s3.amazonaws.com/0/302078/81b5088b-f257-1246-9d06-bc4ed9f7cda1.png)

## 3. バージョン番号を入力して書き出し
HTML5アプリを書き出す際は、適当なバージョン番号を入力して、Confirmボタンを押してください。

文章的に違和感ありますが、このように「Publish Setting」から「Confirm」を押すと書き出しが実行されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/302078/81b5088b-f257-1246-9d06-bc4ed9f7cda1.png)

## 4. 書き出し結果の確認
プロジェクトディレクトリの、`bin-release\web\<バージョン番号>\`に以下のように書き出されます
![image.png](https://qiita-image-store.s3.amazonaws.com/0/302078/45052128-65a1-9993-36aa-dc1a883c51d1.png)

# 番外編
Androidアプリなどはネイティブアプリにコンパイルされるわけではなくて、いわゆるガワネイティブに変換されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/302078/3baa8789-7f69-3e17-f8ea-dc1ac961a92d.png)
VM setup on headless KVM (Ubuntu)

[前回](https://qiita.com/kwi/items/4d4b0ee44d467ce75b4a)の続き。現時点では ubuntu のイメージ限定（他の配信インデックスがあればいいな）だけど、もっと手数を減らせた。`uvtool` を入れる。

```bash
uvt-simplestreams-libvirt sync release=bionic arch=amd64
```

`uvt-kvm` は `ssh-keygen` で手元に用意した鍵を使うことを期待していて、`uvt-kvm ssh` で簡単にログインできるようになっている。

console でやりくりするなら、ssh 鍵は用意しなくても良い。console を取りたいときは `uvt-kvm create` の `--user-data` で[以前と同様の設定](https://qiita.com/kwi/items/161625127302c24961c7)を渡せば簡単。

```bash
uvt-kvm create testvm release=bionic
uvt-kvm ssh testvm
# virsh console testvm
```


## daily image

`label=release` なイメージは URL を指定しなくてもダウンロードできる。`label=daily` なイメージは次のように `source` オプションを付ける。[simplestreams 仕様](https://git.launchpad.net/simplestreams/tree/doc/README)にあるように、[index.sjson](http://cloud-images.ubuntu.com/daily/streams/v1/index.sjson) があるので、これで対応できる。

```bash
uvt-simplestreams-libvirt sync --source http://cloud-images.ubuntu.com/daily/ 

# SEE /usr/share/doc/uvtool/contrib/uvt-show-images
sudo ls /var/lib/uvtool/libvirt/images

virsh vol-list uvtool
```

uvtool で管理しているファイルはこちらのディレクトリに入る。libvirt の pool としても見える。uvtool で作ったマシンのディスクイメージはこの pool に入る。ファイルを直接消せばディスク領域は解放されるが、libvirt が定義情報を持っているので、`virsh vol-delete` 経由で消したほうが良いだろう。
HTMLとは


###HTMLとは
HTML(Hyper Text Markup Language)はマークアップ言語の一つでWebページを作成するために作られた言語である。
ハイパーテキストとは文字情報を載せたり、***ハイパーテキストリンク***を埋め込んでブラウザに表示できるものである。

###マークアップ
Markupは目印をつけるという意味で、テキストがどのような役割かを割り当てること。
アンカーリンクであればaタグであったり、文書であればpタグで目印をつける。
![スクリーンショット 2018-10-22 11.59.18.png](https://qiita-image-store.s3.amazonaws.com/0/290624/ac2afb66-23fe-6f62-73e1-adca8b18da36.png)

###ハイパーテキストリンク
文書中にリンクをつけて外部の情報と繋げることができる。
aタグであれば外部のページへリンクをつけることができ、imgタグでは画像のリンクをつけられる。

###マークアップ言語の種類
HTMLの他にXML(eXtensible Markup Language)、SGML(standard generalized markup language)などがある。
HTMLやXMLはSGMLを元に作られた言語で、SGMLは構造が複雑
なために普及しにくかったが、現在ではDTD（Document Type Definition)という異種のコンピュータ間で文書の互換を行うための定義が不要となったHTMLがウェブの標準言語となった。
macのアップデートをしたらvagrant upができなくなった

# 立ち上がらなくなったvagrant
開発のメインマシンがwinなのでフランクにmacのアップデートをしがちです。
フランクにアップデートしたら立ち上がらなくなりました。
OSのバージョンは `10.14 Mojave` です。
`vagrant up` をしようとしたのは会社で使っているローカル環境ですが
元ネタは以下のリポジトリです。
https://github.com/htanaka0828/devbase_for_htanaka0828

エラーの内容としてはこちらと同じ
https://ja.stackoverflow.com/questions/3123/vagrant-up-%E3%81%8C-failed-to-open-dev-vboxnetctl-no-such-file-or-directory-%E3%81%A7%E5%A4%B1%E6%95%97%E3%81%99%E3%82%8B

```
There was an error while executing `VBoxManage`, a CLI used by Vagrant
for controlling VirtualBox. The command and stderr is shown below.

Command: ["hostonlyif", "create"]

Stderr: 0%...
Progress state: NS_ERROR_FAILURE
VBoxManage: error: Failed to create the host-only adapter
VBoxManage: error: VBoxNetAdpCtl: Error while adding new interface: failed to open  /dev/vboxnetctl: No such file or directory
VBoxManage: error: Details: code NS_ERROR_FAILURE (0x80004005), component   HostNetworkInterface, interface IHostNetworkInterface
VBoxManage: error: Context: "int handleCreate(HandlerArg*, int, int*)" at line 66 of     file VBoxManageHostonly.cpp
```

# 解決方法
ググると色々情報が出てきますが結論だけ書くと
1. VirtualBoxのアップデートをする
2. vagrantのアップデートをする
3. vagrantのプラグインを再インストールする

という感じです。

## VirtualBoxの更新

公式からインストールパッケージをダウンロードしてインストール
https://www.virtualbox.org/wiki/Downloads

## vagrantの更新

公式からインストールパッケージをダウンロードしてインストール
https://www.vagrantup.com/downloads.html

## vagrant pluginの更新(をしなかったために出たエラーとか)
この状態で `vagrant up` を試みたところ

```
Vagrant failed to initialize at a very early stage:

The plugins failed to initialize correctly. This may be due to manual
modifications made within the Vagrant home directory. Vagrant can
attempt to automatically correct this issue by running:

  vagrant plugin repair

If Vagrant was recently updated, this error may be due to incompatible
versions of dependencies. To fix this problem please remove and re-install
all plugins. Vagrant can attempt to do this automatically by running:

  vagrant plugin expunge --reinstall

Or you may want to try updating the installed plugins to their latest
versions:

  vagrant plugin update

Error message given during initialization: Unable to resolve dependency: user requested 'vagrant-hostsupdater (= 1.0.2)'
```

こんな感じのメッセージが出ました。
ご丁寧に「vagrantのアプデしてこれが出てるならこのコマンドを打ってくれよな！(意訳)」って書いてあるので
ありがたく `vagrant plugin expunge --reinstall` って打ったら解決しました。
このコマンドもすごくご丁寧に

```
This command permanently deletes all currently installed user plugins. It
should only be used when a repair command is unable to properly fix the
system.

Continue? [N]:
```

みたいな確認をしてきます。
「一回全部プラグイン消してもう一度インストールするんだけど大丈夫だよな？(意訳)」って確認なので
スマートにエンターキーを打ち抜きましょう。

# 最後に
あとは厳かに `vagrant up` を打って
立ち上がる仮想マシンと共に過ごす時間に想いを巡らせながらログを眺めてください。
現場からは以上です。
React Hooks導入の動機

Hooksは、classを記述せずにstateやその他のReactの機能を使えるようにしたもので、今は提案段階です。RFCは[こちら](https://github.com/reactjs/rfcs/pull/68)

現在はReact v16.7.0-alphaで試せます。

Hooksの概要はこちら
https://qiita.com/tatane616/items/01d6358be5e4e4779232

[Introduction](https://reactjs.org/docs/hooks-intro.html)にHooks導入の動機が書いてあったので理解しようとしてみました。

## ステートフルなロジックをコンポーネント間で再利用するのが難しい
Reactではストアに接続するなどと言った再利用可能なロジックを実装する方法がなく、[render props](https://reactjs.org/docs/render-props.html)や[HOC](https://reactjs.org/docs/higher-order-components.html)を使うしかなかった。ただ、これらはコンポーネントを再構築しなければならず面倒で、Reactはステートフルなロジックを再利用するより良い方法が必要。
Hooksを利用することで、コンポーネントからロジックを抽出して個別にテストしたら再利用が可能になる。Hooksではコンポーネント階層を変更せずにステートフルなロジックの再利用が可能。

## 複雑なコンポーネントになると理解が難しい
Reactを書いていると、最初はシンプルだったがステートフルなロジックと副作用で混乱状態になったコンポーネントをメンテナンスしていかなければならない時がある。
ライフサイクルメソッドには無関係のロジックが紛れ込んでいることがある。
例えば、`componentDidMount`と`componentDidUpdate`でデータをフェッチしてくるが、`componentDidMount`ではイベントリスナーを設定する無関係なロジックが含まれていて、`componentDidUpdate`でそれを解除するなど。一緒に変化し、相互関連のコードが分割されて、無関係のコードが1つのメソッドに統合されてしまう。
これがバグなどを起こりやすくしてしまう。

多くの場合では、ステートフルなロジックが全体的に存在するせいでより小さなコンポーネントに分割しテストを行うことも難しい。これがReact本体とは別の状態管理ライブラリを使う理由の１つとなっている。
しかし、抽象度を高すぎると異なるファイル間のジャンプが発生しコンポーネントの再利用が難しくなる。

これを解決するために、Hooksではライフサイクルと言うよりは関連する動作部分に基づいて1つのコンポーネントをより小さな関数に分割していける。さらに、コンポーネントのローカルステートをReducerで管理することができる。

## Classが人間とマシン両方を混乱させている
Reactのチームが観察したところ、Reactを学ぶ上で最大の障壁画Classの存在。JavaScriptのClassは他の言語とは動作が異なり、JavaScriptでどのように動作するかをしっかり理解しておく必要がある。イベントハンドラのバインドなどは冗長になる。
propsやトップダウンのデータフローなどは理解できるが、Classの理解が難しいらしい。
ReactでのClassと関数コンポーネントの区別やそれらを使うタイミングは、経験者でも意見が食い違うことがある。

また、AngularやGlimmerなどが示すように、[ahead-of-time compilation](https://en.wikipedia.org/wiki/Ahead-of-time_compilation)（事前にコンパイルしておく）は将来性がある。[Prepack](https://prepack.io/)を利用したコンポーネントの折りたたみなどは早い段階でいい結果が得られている。
しかし、Classはこれらの最適化を遅くしてしまう可能性があることがわかった。さらにClassには現在利用されているツールでの問題もあり、例えば圧縮が不十分でホットリロードが不安定になる。

このような問題を解決するために、HooksはReactの機能をClass無しで利用できるようにしていく。Reactは関数的なものに近く、Hooksは関数を取り入れるが、Reactの実用的な部分を犠牲にすることはない。



と言うことらしいです。

最後の、Classが学習コストをあげている点とパフォーマンスやツールのためにClassを排除しようとしている点は知らなかったです。
vueで管理しているElementのScrollTopをあれこれしたかった備忘録_( _*°ヮ。)_

# vueで管理しているElementのscrollに合わせて処理をしたかった。

※自分用に書いているので雑です。
## 説明
1.リストのスクロール位置を監視しておきたかった。
2.ElementはVueに握られてて``$('#app').on('scroll', e => {});``とかでは取れない。
3.ハマった ／(^o^)＼ﾅﾝﾃｺｯﾀｲ

ということがあったのでメモ書き。

## 結果
```
<script>
    list = new Vue({
        el: '#app',
        data: {
        items: []
        },
        methods {
            handleScroll (e) {
                //処理
                console.log(e.target.scrollTop);
            }
        }
    });
</script>
<body>
    ...
    <div id='app' class='scrollable' v-on:scroll="handleScroll">
        <div v-for="item in items"></div>
    </div>
    ...
</body>
```
こんなふうにするとvueのスクロールとれた。

## 備考
``list.$el.scrollTop``でもとれました。
昼間だけ新着メールを音声で教えてくれる仕組みを作ってみた

Gmailに届いた新着メールのタイトルをスマートスピーカーで読み上げる仕組みが出来ました。未来の自分がこの仕組みを忘れても思い出せるように、記録を残します。

# 全体の流れ
![流れ図](https://i.gyazo.com/e6058860962ee6a21fd864cef4091e38.jpg)

# 使ったもの
- IFTTT
- NODE-Red
    - GUIでプログラミングできるnode.jsの実行環境
    - 夜中にメールが届くとうるさいので、ここで時刻判定を行なっている
    - IBM Cloud で動作
- [GHKit](http://ghkit.jp/)(有料)
    - スマートスピーカーに発話させるための仕組み。IFTTTや他サービスと繋ぐ事が出来る
    - Raspberry PIで動作するOSとして販売されている
- スマートスピーカー
    - 今回はClovaを使用。
    - GoogleHomeやAlexaでもできるはず

# Node-REDとIBMCloud部分
Node-REDは初めて使うので、Node-RED日本ユーザ会の[ドキュメント](https://nodered.jp/docs/)を参考にしました。この中にある[Running on IBM Bluemix](https://nodered.jp/docs/platforms/bluemix)の、「ボイラーテンプレートアプリケーション」と「フローエディタにログイン認証を追加」までやりました。
ドキュメントには「IBM Bluemix」とありますが、これは今は「IBM Cloud」と名前が変わっています。（参考：[BluemixはIBM Cloudとして生まれ変わりました。](https://www.ibm.com/blogs/solutions/jp-ja/bluemix-is-now-ibm-cloud/)）

時刻チェック部分は、JavaScriptでの時刻の扱いが意外にめんどくさかったので、[javascriptで日本時間とハワイ時間を表示する](https://hyperbanana.net/1385)を参考にごにょごにょしています。

GHKitのAPI呼び出しは、[GHKitとIFTTTの連携手順](https://qiita.com/tetotetote/items/a7ba4d464e8574d452fd)を参考にしました。

<img width="698" alt="Node-RED___paminggmailtoghkit_mybluemix_net.png" src="https://qiita-image-store.s3.amazonaws.com/0/7946/59a9f088-a26c-e748-dd8c-1975e493d583.png">

https://gist.github.com/taktamur/00b0714bfa893a92dd6131482c10420b

# IFTTTからNode-REDの呼び出し部分
Node-RED部分では、「受け取ったbodyをそのままGHKitのAPIに渡す」ようにしているので、GHKitのAPIに渡すのと同じbodyを組み立てています。このbody組み立ても、[GHKitとIFTTTの連携手順](https://qiita.com/tetotetote/items/a7ba4d464e8574d452fd)を参考にしています。

<img width="456" alt="新着メールが届いたらClovaが喋る_時間判定付き__-_IFTTT.png" src="https://qiita-image-store.s3.amazonaws.com/0/7946/a96e0525-ada9-4e8c-fb20-97331df1d0da.png">

# GHKitとRaspberryPI部分
GHKitのセットアップは、http://ghkit.jp/ こちらに詳しく書かれています。ですがちゃんとできているのかどうかの確認がやりにくいです。
RaspberryPIにディスプレイとキーボードマウスを接続すれば、Linuxなデスクトップを操作できます。またログインユーザ/パスワードはGHKitを購入した時の書類に付いてくるので、Wifiの接続やスピーカーとの接続(Bluetooth)は、デスクトップ環境で設定する方が楽です。
(このログインユーザ/パスワードにはsudo権限が無いみたいで、かつrootパスワードは記載が無いので、rootとして何か仕込んだりするのは出来なさそう）

# 感想
- Node-REDがとてもやりやすい実行環境だった
    - 作っている最中のトライ&エラーがとても簡単
        - テストしたい処理にinjectを差し込んで試して、終わったら消しても良いし残しておいても良い。
    - ネット上で動作させると、ネットサービスの糊付けに良さそう
        - IFTTTではちょっと足らない用途向け
        - Unixでのshellやperlの立ち位置
    - ハマり所もあまりなかった。「デプロイ」ボタンを押し忘れてた、ぐらい
    - ただしここで頑張ると、簡単にスパゲッティ化しそう
- GHKitの中の仕組みは分からないけど、なかなか良さげ
    - 遅延もあまりなく、ネットワークが切れた後の再接続も良さげ
    - 一応有料の品なので、中身はあまり探る気はなし
- インターネットから自宅内部にコールバックしたい場合の他の方法
    - MQTTという仕組みが良さそうだった
管理画面に新しいメニュー項目を追加する

管理画面に新しいメニュー項目を追加します

#1. 管理画面に新しいメニュー項目を追加します
- 追加したいテーマのfunctions.phpに下記を追加します

```php:管理画面に新しいメニュー項目を追加
add_action( 'admin_menu', 'add_new_menu_item_and_new_page' );
function add_new_menu_item_and_new_page() {
    add_menu_page('メニューと画面を追加', '追加されたメニュー', 'manage_options', 'manual', 'add_new_page', '', 10);
}

function add_new_page() {
	include("new_page.php");
}

```

- 以下のファイルをfunctions.phpと同じフォルダに用意します

```php:new_page.php
<div class="manual-contents">
    <h1>新しく追加されたページ</h1>
    <p>これは新しく追加されたページです。</p>
</div>
```

#2. 管理コンソールでアクセスしてみます
- 管理コンソールで新しく追加したメニューにアクセスして用意した画面が表示されるのを確認します

<img width="678" alt="スクリーンショット 2018-10-29 11.33.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/125842/d5c412ca-23fb-9013-cbcc-a226ada2f316.png">
forEach と async function

先日、API Gateway + Lambda な環境で JavaScript のコード内で `Array#forEach` に渡した `async function` が実行されずにハマってたので書いておきます。

### TL;DR

- `Array#forEach` に `async function` は渡さない方がいい
- `Promise.all` と `Array#map` を組み合わせると良さげ

### 現象

`for` 文は冗長な気がして、`Array#forEach` や `Array#map` を使いたい今日この頃。
配列に対して返り値を必要としない処理を行う場合、`Array#forEach` を利用したくなりますが、 `async function` を渡した場合にうまくいきません。
例えば、以下のコードを実行します。

```ts:foreach0.ts
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

const asyncFunc = async (sec: number, idx: number) => {
  await sleep(sec * 1000);
  console.log(`sleep[${idx}]: ${sec} sec`);
};

const a = [4, 3, 2, 1];
a.forEach(asyncFunc);
console.log('finish');
```

```console
$ ts-node foreach0.ts
finish
sleep[3]: 1sec
sleep[2]: 2sec
sleep[1]: 3sec
sleep[0]: 4sec
```

`forEach` 内の処理が終わる前に、**finish** が表示されています。
これは `forEach` が返り値を全く考慮しない、たとえ `Promise` が帰って来ようが処理を進めてしまうのが原因です。

### 対策

対策方法は、処理を順番に行いたい場合と、並列に行いたい場合で異なります。

1. 順番に行いたい場合

配列の要素を順番に処理したい場合は `for` 文を使いましょう。

```ts:foreach1.ts
type AsyncFunc<T> = (x: T, i: number) => Promise<void>;

const asyncForEach = async <T>(ary: T[], fn: AsyncFunc<T>) => {
  for (let i = 0; i < ary.length; i = i + 1) {
    await fn(ary[i], i);
  }
};

const main = async () => {
  const a = [4, 3, 2, 1];
  await asyncForEach(a, asyncFunc);
  console.log('finish');
};

main();
```

```console
$ ts-node foreach1.ts
sleep[0]: 4 sec
sleep[1]: 3 sec
sleep[2]: 2 sec
sleep[3]: 1 sec
finish
```

配列の順番に処理が実行されています。
インデックスが不要な場合は `for..of` 文を利用してもいいでしょう。

2. 並列に行いたい場合

配列の順番に関係なく、並列に処理したい場合は `Promise.all` と `Array#map` を組み合わせます。

```ts:foreach2.ts
const parallelForEach = <T>(ary: T[], fn: AsyncFunc<T>) => (
  Promise.all(ary.map((x, i) => fn(x, i)))
);

const main = async () => {
  const a = [4, 3, 2, 1];
  await parallelForEach(a, asyncFunc);
  console.log('finish');
};

main();
```

```console
$ ts-node foreach2.ts
sleep[3]: 1 sec
sleep[2]: 2 sec
sleep[1]: 3 sec
sleep[0]: 4 sec
finish
```

並列に処理が実行されています。

### まとめ

- `Array#forEach` は `async function` を待ってくれない
- 順番に処理したい場合は `for` 文を使う
- 並列に処理したい場合は `Promise.all` と `Array#map` を組み合わせて使う

ちなみにハマった件では `Promise.all` と `Array#map` の方で何とかしました。