OmniAICreator/Qiita-1.07M huggingface dataset:
nextjs/jest でのユニットテスト

# 概要

小規模な案件で web application を作成する機会があり、単独で時間が限られていたため Next.js を利用することにした。私は FE に詳しくないため、0コンフィグとインターネット上の情報量の多さ、最悪社内の React ユーザーに泣きつくことを加味して Next.js を採用した。**それでも、ユニットテストをパスさせるまでに労力が発生したので設定ファイルと、スグに試せるユニットテスト付きのテンプレートプロジェクトを用意した。↓**

https://github.com/naru-hiyoko/my_nextjs_proj_template/tree/main/webapp

# トラブルポイント

私の場合、トラブルポイントは２つ存在した。

## `node-mysql2` を導入すると jest でエラーが起きる
* [こちらの記事と同じ問題](https://stackoverflow.com/questions/46227783/encoding-not-recognized-in-jest-js/46232762)
* [jest.config.js](https://github.com/naru-hiyoko/my_nextjs_proj_template/blob/main/webapp/jest.config.js#L12) と [jest.setup.js](https://github.com/naru-hiyoko/my_nextjs_proj_template/blob/main/webapp/jest.setup.js) に手を加える必要があった

## モックを使おうとするとエラーが起きる

* 時刻の固定や外部サービスの連携部分をモックできないのが困る
* 結局、`jest.spyOn`を利用するのを諦めた
  * 余裕が無く、条件付きでモジュールをモックできたので妥協した
* **後日調査から Jest の Typescript Usage に従えば良いことがわかった**
  * https://jestjs.io/docs/mock-function-api#typescript-usage
  * 当時このドキュメンテーションに気付けなかったのが辛い

### 時刻の mock

* 現在時刻の取得、時刻の操作には `moment` モジュールを利用している
* [この部分](https://github.com/naru-hiyoko/my_nextjs_proj_template/blob/main/webapp/tests/api/hello.test.ts#L7) のように `describe` ブロックの外で `moment` モジュールをモックしてしまう
  * ブロック内に記述するとエラーになる

### 特定のモジュールの関数のモック

* [FE 側ではAPI を利用する部分を関数化 (`postHello`)](https://github.com/naru-hiyoko/my_nextjs_proj_template/blob/main/webapp/repository/frontend/hello.ts#L15) して記述している
* [この部分のように](https://github.com/naru-hiyoko/my_nextjs_proj_template/blob/main/webapp/tests/home.test.tsx#L7)時刻の mock と同じように記述する
  * 異常系をテストしたい場合が課題
  * 数が少ない場合は引数の　params に応じて返却値を決める
* **後日調査を行い、下記のドキュメント通りに記述するのが良いことが分かった**
  * https://jestjs.io/docs/mock-function-api#jestmockedsource-options
  * モジュールか[このように関数単体を mock する](https://github.com/naru-hiyoko/my_nextjs_proj_template/commit/0459f2ef720cfe4a93d21b0e39cec65654e9fa58)か２通りの方法があった

# 補足

とりわけ FE は詳しくないのでライブラリ選定に時間を取られた。社内の他案件を見る限りテスティングフレームワークは jest の採用が多いが、それら案件の `package.json`　はボリュームがあり、必要最低限のライブラリがどれか分からない。

特に `testing-library` は下記の３つは始めから必要だと思うが、結局必要そうな場面で調査を行い、都度依存関係に加える対応となってしまった。

* `testing-library/react`
* `testing-library/jest-dom`
* `testing-library/user-event`

一方、 API Route (BE) の方は `next-test-api-route-handler` だけで良かったのでトラブルには見舞われなかった。

# 余談

* material ui の v5 は`@mui/material`, `@mui/icons-material`
* 始め v4 の `@material-ui` を依存関係に加えようとして間違えかけた
SQLでバルクインサートを使ってテストデータを大量に作成する

# この記事を書いた背景
RailsのAPIモードで開発していたのですが、動作確認のためステージング環境にテストデータを大量に作成する必要がありました。

その際、Railsコンソールからデータを増やそうと思い`each`で繰り返しデータの生成を実行したのですが、herokuのプランの都合上1時間に発行できるクエリ数が3,600に限られており、それがオーバーしたようでデータの作成が途中で終わってしまいました（泣）

しかも、一度クエリの上限数を超えると１時間ほどDBにアクセスできなくなるという問題も、、、
（現在は有料プランに切り替えたので恐らく問題ないですが、当時は無料プランだったためこうした制限に引っ掛かりました）

参考：https://zenn.dev/ttskch/articles/905ae809e29504

- 急に有料プランに切り替えたくても自社持ちの費用でないため承認まで時間がかかる可能性があること、
- リリースまで時間がなく、なるべくすぐにステージング環境で動作検証をしたかったこと
このような理由から、herokuが復旧後、SQLでバルクインサートを行い発行するクエリ数を最小限にしつつテストデータを大量作成する方法を取りました。

# やったこと
- 増やしたいデータの洗い出し
- sequel aceを使いdumpデータをexport
- テキストエディタなどでdumpを開き、書いてあるINSERT文を参考に必要に応じてVALUESw変数に置き換え
- macのターミナルでrubyを使いINSERTする内容を大量に複製する
（別に繰り返しできれば言語はなんでもいい）
- 複製した内容をINSERT文の中に入れてSQLでクエリ実行

とざっくりこんな感じです。

# INSERT文
```sql
INSERT INTO テーブル名
(カラム名1, カラム名2, ...) 
VALUES
(値1, 値2, ...); --VALUESの値をRubyを使って複製するイメージ
```

# Rubyを使って複製
- 例：100回複製する場合
- ターミナルを使い`irb`と入力しenter（Rubyがインストールされていることが前提）
- `irb(main):001`という表示が出てきたらRubyが実行できます

```rb
(1..100).each do |i| #1以上100以下でiを繰り返していく
puts "('name_#{i}', 'email_#{i}@example.com')," #この行が範囲指定した数だけ複製される
end
```

- ターミナルで複製された結果をコピペしてVALUESに入れる

## 結果
```sql
INSERT INTO テーブル名
(カラム名1, カラム名2, ...) 
VALUES
('name_1', 'email_1@example.com', ...),
('name_2', 'email_2@example.com', ...),　-- 途中の,を忘れないように
...
('name_100', 'email_100@example.com', ...); --最後は;を忘れないように
```


## やってみた感想
- コピペの手間もあるし普通に面倒
- でもこういう仕方ない場合に有効だな〜
- プログラムに働かせるということを覚えた
- ちなみに自分でこの方法を考えたっぽく言ってますが、教えてもらいました
PythonであるLambda関数から別のLambda関数を呼び出す

最近あるLambda関数から別のLambda関数を呼び出す処理をする際に少し調べたのでメモ。

# 環境

| ランタイム | Python 3.9 |
| --- | --- |

# 今回の想定ケース

特定のLambdaから別のLambdaをまるごと呼び出す処理を想定。
今回は、以下の`testFunction01`から`testFunction02`を呼び出すような処理を実現していく。

```python:testFunction01
def lambda_handler(event, context):
    print('[testFunction01] testFunction01')
```

```python:testFunction02
def lambda_handler(event, context):
    print('[testFunction02] Called by testFunction01')
    return {'message': 'called by testFunction01'}
```

# 実現方法

基本的には[invoke](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/API_Invoke.html)を呼び出してあげることで実現可能。

## IAMロールの修正

invokeを呼び出すことができるように、`lambda:InvokeFunction`アクションを実行できるようなポリシーを作成し、呼び出し元Lambda関数に紐づけているIAMロールに許可を追加する。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "lambda:InvokeFunction",
            "Resource": "arn:aws:lambda:*:[AWSアカウントID]:function:*"
        }
    ]
}
```

## コードの修正

`boto3`を使用してLambdaのクライアントを作成し、invokeを実行する。

```python
import boto3

def lambda_handler(event, context):
    print('[testFunction01] testFunction01')
    # 別Lambda関数の呼び出し
    response = boto3.client('lambda').invoke(
        FunctionName = 'testFunction02',
        InvocationType='RequestResponse'
        )
    print(response)
```

上記のコードを実行し、testFunction02のCloudWatch Logsを確認すると、呼び出されていることが確認できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157652/8231ac6e-e5b9-f0e6-7f90-936a2bd1e1fe.png)
また、呼び出した側には以下のような出力が出ている。

```
[testFunction01] testFunction01
{'ResponseMetadata': {'RequestId': 'XXX', 'HTTPStatusCode': 200, 'HTTPHeaders': {'date': 'Thu, 29 Dec 2022 00:28:12 GMT', 'content-type': 'application/json', 'content-length': '4', 'connection': 'keep-alive', 'x-amzn-requestid': 'XXX', 'x-amzn-remapped-content-length': '0', 'x-amz-executed-version': '$LATEST', 'x-amzn-trace-id': 'root=XXX'}, 'RetryAttempts': 0}, 'StatusCode': 200, 'ExecutedVersion': '$LATEST', 'Payload': <botocore.response.StreamingBody object at 0x7f17bf8e0970>}
```

responseのbody(`testFunction02`の実行結果)を取得したい場合は以下のようにJSONでコードして値を取り出すようにすれば良い。

```python
import json

body = json.loads(response['Payload'].read())
print(body)
```

### 補足(1) : InvocationType

InvocationTypeにはEventとRequestResponseの2種類があるので、用途に応じて使い分ける。

| InvocationType | 用途 |
| --- | --- |
| RequestResponse | 同期処理 |
| Event | 非同期処理 |

### 補足(2) : 呼び出されるLambdaに値を渡したい場合

invokeにはPayloadを渡すことができる口があるので、そこを使うことで呼び出されるLambdaに値を渡すことができる。

```python
payload = {"one": 1, "two": 2, "three": 3}
response = boto3.client('lambda').invoke(
        FunctionName = 'testFunction02',
        InvocationType='RequestResponse',
		Payload = payload
        )
```

渡されたPayloadはeventに入る。
[Memo] Azure DevOps Agile モード

内部用に簡単に整理。

Agileのワークフローはこれ。基本はUserStoryを中心にUserStory内にあるタスクを片付ける。　Bugは再現方法も含めて記載、再現されないことを確認してからclose。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241909/41cf4984-3abf-e215-2c46-db3c62d82a8a.png)

↑User Story内でフロー、Bugのフロー、Taskのフロー

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241909/96580814-5a52-fcfa-5474-e0f7a4952985.png)

各ブロックの関係値はこれ。

User StoryはFeatureに紐づける。

FeaturesはEpicに紐づける。

User Storyベースでテストプランを作る。

詳細はここ参照
https://learn.microsoft.com/en-us/azure/devops/boards/work-items/guidance/agile-process-workflow?view=azure-devops
getElementById()を使って入力フォームから値を取得する

## はじめに
入力フォームに入力した値を取得する方法を説明します。

## 結論

`getElementById()`と`valueプロパティ`を使うことで入力フォームの値を取得することができます。

## 説明
まずは、`getElementById()`を使って入力フォームの要素を取得します。
その際、引数には取得したいid属性名を指定します。

```jsx
let inputElm = document.getElementById('id属性名').value;
```
<br>

id属性の要素取得後、valueプロパティを使って入力フォームの値を取得します。

```jsx
let inputVal = inputElm.value

console.log(inputVal);   // 入力フォームの内容が表示される。
```

<br>
ちなみに「getElementById(’id属性名’).value」というように、まとめて記述することもできます。

## 参考サイト
[https://magazine.techacademy.jp/magazine/21069](https://magazine.techacademy.jp/magazine/21069)
gitに編集した差分が反映されなくなった

皆さんはgitでバージョン管理してますね。今回はgitで、私のその日の差分が反映されなくなったので、その時の状況と、その解決策をメモします。

##### 状況
いつものように分岐したブランチで作業をしていて、一区切りついてコミットしようとした時にその不具合は起こりました。
昨日はちゃんと作業した分が反映されていましたが、**今日になり**、差分が反映されなくなっていしまいました。
色々調べた結果、改めてファイル構成を確認しましたら反映されていないファイルには、なぜか.gitファイルが生成されていました。
>project1/
　├ project2/
　├ eproject3/
　├ project4/
　│　└ .git/  ←いつの間にか生成されていた.gitファイル
　└ .git

それがいつからあったのか、gitコマンド操作をミスして、作成してしまったのかは分かりません。
##### 解決方法
その.gitファイルを削除してみると、ちゃんと今日作業した分が反映されました！最悪今日の作業分が無しになるのかと思い、ちょっと萎えていましたが、直りましたのでよかったです。
今でも分からない部分があります。上図のディレクトリ構成の状態で作業をしていて、昨日まではちゃんと差分が反映されていました。今日になってから、急に反映されなくなった。←この部分は今も謎です。
##### 反省とまとめ
この不具合を解決してくださったのは、同じ部内の先輩です。ありがとうございます。私はgitについてまだわかっていない部分がたくさんありますので、もっと勉強しないといけないです。
でもなんとか直ったのでよかったです！ってことで**差分が反映されない**、って不具合になりましたら。同ファイル内に.gitファイルがないかを疑ってみましょう！
MRTKのStandardシェーダーを、実装ついでに学ぶ②

前回実装した「Gradient Mode」の負荷がどのくらいかかるのか
実験していきたいと思います。

https://qiita.com/hidaka2022/items/b438132566c15bb08153

簡単に確認をしたいのと、Linkの処理（Wifi接続的な）問題で
今回はUnityのプロファイラーではなくODHを使います。
（MQDH？今は名前が変わっています）

https://developer.oculus.com/blog/oculus-developer-hub-odh-21/?locale=ja_JP

使い方はこういった記事が参考になるのかなと・・・

https://note.com/npaka/n/nc18d61e25c85

自分の場合、apkファイルを投げ込むときによく使ってます
（SideQuestよりわかりやすいので）


## ODHからapkファイル取込＆実行
Onの場合。フレーム落ちてしまってますね....

![on.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3009807/d11fb3a0-a8ae-bd97-4dcf-7a28e6acd3c6.png)

OFFの場合。ONよりはましですが、GPU負荷が高いようですので
Bakeなどの作業が必須かもしれません。

![off.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3009807/33b6f617-198f-7016-9174-2cd784b7b292.png)

また、ライトの設定の問題なのか？と思い、シェーダー自体のライト設定を全て外して
ONの状態で試したのですが、以前処理が重いため、利用は難しいかもしれない
というのが今回の結論です。

![on2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3009807/97553756-66ea-b4e9-1477-50fe02049fe1.png)


## 結論
「Gradient Mode」のLinerはOculusQuest2では使うのが難しそうです。
残念。

ライトを入れてBakeして明るさ調整しようと思います。
Windows 11が起動できなくて困った話

# 久しぶりにWindowsを再起動したらブルースクリーンになってどうやっても起動しなくなった

めんどくさがりなので普段は電源を落とす代わりに休止状態で運用しています。
WSLが便利になったので家のPCでもWSL環境を入れようと久しぶりにいろいろ設定を変更して再起動をかけたところ、青い画面が表示され何度再起動してもOSが上がってきません。

下の青い画面で選択できることはほとんど全部やりましたが、起動できませんでした。試したのは「スタートアップ修復」、「システムの復元」、「以前のバージョンに戻す」です。
![blue_screen.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199416/046ef308-3c3a-560f-6931-6e2997db7f57.png)

## 初期状態にも戻せない...
どうやっても起動できないので意を決して「このPCを初期状態に戻す」を使って回復しようとしました。

ところが、
「PCを初期状態に戻すときに問題が発生しました」
というメッセージが出て初期状態にすら戻すことができません。


そこで
このPCの構成をよく考えてみると、Win11用のNVMeと、先代PCのOSが入っていたSSDが同じマシンの中に同居していて、Windowsボリュームが二つ存在していたのです。
![画像_2022-12-29_083512591.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199416/d51cb3fe-0c08-4360-694c-6e51f8eb64b4.png)
これではどのディスクを修復したらいいかOSにはわからないだろうということで、NVMe以外のHDD, SSDを全部外して電源を入れたところ、あっさり起動しました。

その後念のためもう一回再起動してから電源を落とし、すべてのディスクをつないで電源を入れると正常にOSを起動することができました。

残念ながら、「以前のバージョンに戻す」はちゃんと機能していたらしく、OSのアップデートは消えていました。

## 結論
初期状態を試す前に余計なHDDは外す。
（というより、Windowsというボリュームを二つもマウントしない、かな？）
さくらレンタルサーバーで ruby 3.2.0 をビルド

# はじめに
複数のトラップがあり，苦労したので記録する．以下は 2022/12/29 現在の話．

# 手順

## rbenv および ruby-build のセットアップ
今調べたら Homebrew 利用の方法ばかりヒットするので，念のため書いておく．`git pull`のエイリアスとして`rbenv_update`を登録しておく．新しい Ruby のバージョンがリリースされたら，`rbenv_update`してから`rbenv install`する．

```
$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
$ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(rbenv init -)"' >> ~/.bashrc
$ echo 'alias rbenv_update="cd $HOME/.rbenv; git pull; cd -; cd $HOME/.rbenv/plugins/ruby-build; git checkout .; git pull; cd -"' >> ~/.bashrc
$ . ~/.bashrc
```

## libyaml のインストール
[Ruby 3.2.0 から libyaml の別途インストールが必要になった](https://www.ruby-lang.org/ja/news/2022/12/25/ruby-3-2-0-released/) が，2022/12/29 現在，さくらレンタルサーバーでは libyaml はインストールされていないので，ホーム以下に自分でビルドする．以下では $HOME/local にインストールしているが，お好みで．

```
$ wget http://pyyaml.org/download/libyaml/yaml-0.2.5.tar.gz
$ tar xzf yaml-0.2.5.tar.gz
$ cd yaml-0.2.5
$ ./configure --prefix=$HOME/local
$ gmake
$ gmake install
```

## ruby 3.2.0 のインストール
各種環境変数を設定してインストール．

```
$ CC=gcc RUBY_CONFIGURE_OPTS="--with-openssl-dir=/usr/local --with-libyaml-dir=$HOME/local" TMPDIR="$HOME/tmp" rbenv install 3.2.0
```

### `CC=gcc`
デフォルトの clang ではエラーが出るので，gcc に変える．

試していないが，新しいバージョンの clang をローカルにインストールして使えばいけるかも？ 2022/12/29 現在インストールされているバージョンは 11.0.1．うーん……

### `RUBY_CONFIGURE_OPTS="--with-openssl-dir=/usr/local --with-libyaml-dir=$HOME/local"`
openssl, psychの各gemのビルドに指定が必要．--with-libyaml-dir の指定ディレクトリは configure の --prefix で指定したものと同じものを．

環境変数名，オプション名，指定ディレクトリがそれぞれ何が正しいのかを探すのに苦労した．openssl のビルドとかもしたが，システムのものでも大丈夫だった．

### `TMPDIR="$HOME/tmp"`
一時ディレクトリもデフォルトでは root 権限が必要なので，ホーム以下の適当なディレクトリを指定する．
著作権フリーの地図画像（著作権表示、中心ピン、地名入り）を一気に作成する [Python + staticmap + OpenStreetMap]

# 概要

- OpenStreetMap の著作権フリーの地図画像を取得したい
- Python 用ライブラリの staticmap を使って OpenStreetMap の画像を取得する
- 右下に著作権表示を入れる
- 中心にピン画像を表示
- ピンの上に地名を表示
- これらを一度に行なって、PNG画像で保存する

# 環境

- macOS Monterey + Python 3.9

# パッケージのインストール

```
pip install staticmap
```

# ピン画像の用意

- 著作権フリーのピン画像を用意してください。
- ファイル名を map_pin.png にして、プロジェクトルートに設置しておく
- 私が Affinity Designer でサクッと作ったピン画像を載せておきます。ご自由にお使いください。

![map_pin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320718/fb540542-bdd8-0467-c2a5-db90f369ca4c.png)


# 地図作成プログラム

地図サイズ、フォント、テキストの位置の微調整など、お好みで行なってください。matplotlib で画像を表示させて、地図画像の確認を行なっています。

* コメントいただきましたので、COPYRIGHT_TEXT を修正しました。

```make_map_with_text_and_pin.py
from staticmap import StaticMap
from PIL import Image, ImageDraw, ImageFont
import matplotlib.pyplot as plt

WIDTH, HEIGHT = 1000, 800
PIN_IMAGE_SIZE = (50, 50)
FONT_SIZE = 20
FONT = 'ヒラギノ丸ゴ ProN W4.ttc'  # Mac
# FONT = 'meiryo.ttc'  # Windows
COPYRIGHT_TEXT = '©OpenStreetMap(openstreetmap.org/copyright) contributors'
OUTPUT_IMAGE_NAME = 'nagoya_station'
PIN_TEXT = '名古屋駅'
MAP_CENTER = [136.882090, 35.170560]
# OUTPUT_IMAGE_NAME = 'tokyo_station'
# PIN_TEXT = '東京駅'
# MAP_CENTER = [139.76713552739628, 35.68145404891474]


def simple_map():
    # 地図オブジェクトを生成
    # 画像の横幅と縦幅と OpenStreetMap 標準タイルレイヤーのタイルURLを指定
    static_map = StaticMap(WIDTH, HEIGHT, url_template='https://tile.openstreetmap.jp/{z}/{x}/{y}.png')

    # 地図を描画した Pillow の PIL.Image オブジェクトを取得
    # ズームレベルと経度・緯度を指定
    image = static_map.render(zoom=17, center=MAP_CENTER).convert('RGBA')
    pin_image = Image.open('map_pin.png').convert('RGBA').resize(PIN_IMAGE_SIZE)

    # 背景と同サイズの透明な画像を生成
    img_clear = Image.new("RGBA", image.size, (255, 255, 255, 0))

    # 透明画像の上にペースト
    img_clear.paste(pin_image, (WIDTH // 2 - PIN_IMAGE_SIZE[0], HEIGHT // 2 - PIN_IMAGE_SIZE[1]))

    # 重ね合わせる
    image = Image.alpha_composite(image, img_clear)

    # Imageオブジェクトを引数に指定してDrawオブジェクトを生成。
    draw = ImageDraw.Draw(image)

    # テキストを描画。フォントを指定して使う。
    font = ImageFont.truetype(FONT, FONT_SIZE)
    draw.multiline_text((WIDTH - 10, HEIGHT - 10), COPYRIGHT_TEXT, fill=(0, 0, 0), font=font, anchor='rs')
    draw.multiline_text(
        (WIDTH / 2 - PIN_IMAGE_SIZE[0] / 2, HEIGHT / 2 - PIN_IMAGE_SIZE[1]),
        PIN_TEXT, fill=(0, 0, 0), font=font, anchor='ms')

    # 表示
    plt.imshow(image)
    plt.show()

    # 地図画像を保存
    image.save(f'output_images/{OUTPUT_IMAGE_NAME}.png')


if __name__ == "__main__":
    simple_map()

```

# 実行結果

![nagoya_station.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320718/7e2d992c-3e77-8c3e-f3ea-0446c6e1bb06.png)


# 参考

[Python + staticmap で OpenStreetMap や地理院地図の画像を取得する](https://qiita.com/niwasawa/items/bb86fc1fdf9aeb31d9f0)

[Pillowで透過画像を合成したら背景画像の一部も透明になってしまうときの対処法](https://qiita.com/iso12800jp/items/a74852ebfd3041065aeb)

[Pythonの画像処理ライブラリPillow(PIL)の使い方](https://note.nkmk.me/python-pillow-paste/)
wslでelixir その2

# 概要
wsl(wsl2じゃない)で、elixirやってみた。
mix使ってみた。
mix testやってみた。

# 参考にしたページ。

https://qiita.com/tbpgr/items/da834964cfb4a18ab8d3


# コンパイル手順

```
$ mix new fizzbuzz

```

# fizzbuzzを書く

```
defmodule Fizzbuzz do
	@moduledoc """
	Documentation for `Fizzbuzz`.
	"""
	@doc """
	Hello world.
	## Examples
			iex> Fizzbuzz.hello()
			:world
	"""
	def hello do
		:world
	end
	def fizzbuzz(from, to) when is_integer(from) and is_integer(to) do
		from..to 
		|> Enum.map(&fizzbuzz/1)
	end
	def fizzbuzz(_from, _to) do
		raise "invalid argument"
	end
	defp fizzbuzz(n) when rem(n, 15) == 0, do: "FizzBuzz"
	defp fizzbuzz(n) when rem(n, 3) == 0, do: "Fizz"
	defp fizzbuzz(n) when rem(n, 5) == 0, do: "Buzz"
	defp fizzbuzz(n), do: n
end

```

# testを書く

```
defmodule FizzbuzzTest do
	use ExUnit.Case
	doctest Fizzbuzz
	test "greets the world" do
		assert Fizzbuzz.hello() == :world
	end
	test "FizzBuzz from 1 to 15" do
		assert Fizzbuzz.fizzbuzz(1, 15) == [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14, "FizzBuzz"]
	end
	test "If from is not integer, raise Error" do
		assert_raise RuntimeError, "invalid argument", fn ->
			Fizzbuzz.fizzbuzz("1", 15)
		end
	end
	test "If to is not integer, raise Error" do
		assert_raise RuntimeError, "invalid argument", fn ->
			Fizzbuzz.fizzbuzz(1, "15")
		end
	end
end
```

# test実行

```
$ mix test
Compiling 1 file (.ex)
.....

Finished in 0.1 seconds (0.00s async, 0.1s sync)
1 doctest, 4 tests, 0 failures

Randomized with seed 550112

```

# 実行

```
$ iex -S mix
Erlang/OTP 25 [erts-13.0.4] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1] [jit:ns]

Compiling 1 file (.ex)
Interactive Elixir (1.13.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Fizzbuzz.fizzbuzz(1, 15)
[1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14,
 "FizzBuzz"]
iex(2)> Fizzbuzz.fizzbuzz(1, 100)
[1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14,
 "FizzBuzz", 16, 17, "Fizz", 19, "Buzz", "Fizz", 22, 23, "Fizz", "Buzz", 26,
 "Fizz", 28, 29, "FizzBuzz", 31, 32, "Fizz", 34, "Buzz", "Fizz", 37, 38, "Fizz",
 "Buzz", 41, "Fizz", 43, 44, "FizzBuzz", 46, 47, "Fizz", 49, "Buzz", ...]
iex(3)>
```

以上。
PostgreSQLのテーブルサイズをチェックする



# Tableサイズチェック (Top 50)

```sql
SELECT
  schema_name,
  relname,
  pg_size_pretty(table_size) AS size,
  table_size

FROM (
       SELECT
         pg_catalog.pg_namespace.nspname           AS schema_name,
         relname,
         pg_relation_size(pg_catalog.pg_class.oid) AS table_size

       FROM pg_catalog.pg_class
         JOIN pg_catalog.pg_namespace ON relnamespace = pg_catalog.pg_namespace.oid
     ) t
WHERE schema_name NOT LIKE 'pg_%'
ORDER BY table_size DESC LIMIT 50;
```
開発組織の新しいカタチ

CTO,VPoEはじめエンジニアマネジメント職としては、強い開発組織について、いつも頭を悩ましていると思いますが、ステレオタイプになっていないでしょうか？

# 柔軟な開発組織のススメ
以前下記の記事を投稿しました。
■モンゴルに開発部隊を求めた話
https://qiita.com/k1morikawa@github/items/61e5cf9520725b4706bb
■開発組織を全て業務委託メンバーで構築した話
https://qiita.com/k1morikawa@github/items/b2257661dd75e7365507
## １．海外に開発組織を構築する
コロナのお陰でリモートワークが進み、皆さん、リモートワークのノウハウが貯まり、エンジニアが隣にいないと駄目という状況でも無くなってきてます。東京でのみ採用という固定概念は捨てて、地方採用、海外採用を進めてみてはいかがでしょうか？
今年に入り急激に円安が進みました。これにより、日本人の単価は世界からみて安い方になってます。中国、韓国からみると日本は安い国となるので、それ以外の国でないとコスト的なメリットはありません。
しかし、コスト中心に考えるのではなく、開発組織が作れるかどうかにフォーカスして考えてみてください。
モンゴルでは、東大レベルの理系卒のエンジニアが採用できます。日本だと無名ベンチャーには絶対に無理でしょう。
## ２．業務委託メンバーで構築する
社員採用の様な昭和の終身雇用制度から脱却し、フレキシブルな雇用の業務委託メンバーで開発組織を作ることにより、より速くより高品質なサービスが出来上がる可能性もあります。業務委託メンバー型組織のメリットは山の様にあります。
## ３．縦割りから横割り組織へ
通常、開発本部という形で開発メンバーが集まっている組織を作ると思います。この組織は、上長がエンジニアでありエンジニアスキルに対して適切なジャッジ、評価が可能となり、部署のメンバーが全てエンジニアなのでスキル共有も進みやすいです。しかし、一方で売上に向けての意識やエンドユーザーに向けたサービス等は、組織が違うので温度感が伝わらず弱くなる傾向にあります。
エンジニアを各事業部側に配属し、事業意識をあげる形に変更してみましょう。そして横串でエンジニアの管理・評価をCTO、VPoEが担当します。
## ４．DAO型組織
エンジニアとしてマネジメント職は存在させながら、ステークホルダとなってもらい。サービスや組織に対しての意識をもってもらい。報酬も事業成功に連動させるモデルです。フラットの組織の場合は、色々と弊害があるため、DAOの良さをとった形になります。
フラット組織の弊害は下記をみてください
https://qiita.com/k1morikawa@github/items/7237dd1f61525abb3cb4
## 5.新卒エンジニアのみ
新卒エンジニアのみの組織。CTOのみ居て、採用は全て新卒エンジニアです。エンジニアって出来る奴は、１年あれば１０年目のエンジニアをあっという間に追い抜きます。新卒エンジニアで文化レベルも合わせながら育てていくのもやり方として面白いと思います
## ６．ラボ型、レベニューシェア型
全て外部委託にて開発してもらう組織です。ラボ型にする事により、見積もり等のオーバーヘッドを無くせますし、レベニューシェアなので事業部目線にもなりやすいです。昔からある形ではあります。

# 最後に
開発組織の新しいカタチについては、色々とアイデアが出てきそうですね。ゲーマーのみや女性のみ等の属性を絞ったエンジニア組織も無駄なコミュニケーションコストが省けて良いと思います。
ステレオタイプから脱却して新しいカタチの開発組織にチャレンジしてみてください！
フォロー、イイねお願いします。
肺水腫の病態 ARDSとの違い

前回の、[右心不全と左心不全の違い](https://qiita.com/Yoh_Yasushi/items/a2adf8ca51c157249f8b)と同様のシェーマを用いながら、今回は肺胞レベルに着目してみます。改めて前回と同じシェーマを提示しますね。

![JunkanBaseMojiAri.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2356942/11f199e1-cfb3-f209-7002-25947739f7d2.png)

# 肺水腫の病態 ARDSとの違い
そもそも疾患概念とかって、闇雲に覚えても仕方ありません。実践では役立ちませんし、国家試験でも最近は病態の理解を問われるので丸暗記勉強ではいかんでしょうね。
のて、敢えて、診断基準のコピペはしません(~~面倒だし~~)。

そもそも肺水腫ってどうなっているのでしょうか？病理画像では、

![03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2356942/a80c6af8-c40d-bead-7124-7c05b9f8e48a.jpeg)

肺胞が肺胞腔は液体（淡いピンク色）で満たされていますね。

シェーマで単純化すると、

![PulmonaryEdemaARDS.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2356942/1af8a199-81a5-a89e-89fa-87ed0e4e4b1c.png)

左心不全に由来する圧(緑)と、血漿浸透圧などのために肺胞から水を引く圧(赤)とのバランスが崩れ、**緑>赤**となると、肺水腫となります。めっちゃ単純な説明です。

![ARDS.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2356942/76d0f5a9-db27-3d0e-d80e-94636319ca0c.png)

~~これまた単純ですが~~、左心不全がないのに、炎症や感染などにより**緑>赤**となるのが、ARDSです。

**ARDSと肺水腫を区別できる要素はなにか**、わかりますか？

それが、**肺動脈楔入圧(PCWP)** です。

私も疑問に思い再度検索したのですが、[Pulmonary Capillary Wedge Pressure](https://www.ncbi.nlm.nih.gov/books/NBK557748/)の、`Indications`の`Measurement of the PCWP`において、

- **Differentiate between cardiogenic pulmonary edema and noncardiogenic pulmonary edema**
- Confirm the diagnosis of pulmonary arterial hypertension
- Assess the severity of mitral stenosis
- Differentiate between different forms of shock
- Measure key hemodynamic parameters and assess response to therapy

って書かれてました。

PCWPを非侵襲的に測定する方法**肺動脈弁拡張末期圧(PR-EDPG)**しかないです。(測定の限界はあること　Pubmeで引っ掛けたURLを貼ること、PR-EDPGで近似できるのかという詳細な議論は省きます。)


# そこでPEEP 陽圧換気とは
循環に関しての陽圧について着目します。陽圧換気が

- 前負荷への影響
    - 胸腔内陽圧による静脈還流の低下
    - 心臓の外表面に掛かる陽圧による心臓の拡張性の低下による心室充満の低下
    - **陽圧換気による肺血管抵抗の上昇**
    - 右室の拡張により心室中隔が左室を圧排することによる左室充満の低下

- 後負荷への影響
    - 胸腔内陽圧が経壁圧を減少させ後負荷を減少させる(要するに胸腔から心臓を圧すということ)

などがあります。`ICUブック`参照。

つまり、*前負荷の減少*と*後負荷の減少*のバランスによって心拍出量が決まるということです。

肺水腫については、肺血管抵抗の上昇にとくに着目したいですね(他の要因を低く評価するわけではありません)。

ここで改めてシェーマを提示します。

![PEEP.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2356942/55b6ced2-6cbb-d593-8d25-a7f1f2f8f7e9.png)

ごく単純化した図ですが。青矢印の圧が、*PEEP*、*陽圧換気*、です。左心不全による圧に拮抗している様子がわかるでしょうか。

# まとめ
肺胞レベルでの圧の動きに着目して、肺水腫からARDSまでの病態について切ってみました。疾患概念などは成書にありますのでそちらを参照していただくとします。

ある程度の病態を把握しておかないと、*「圧をかける」*とかいう発言を漠然として仕舞いがちですが、それはいけません。

再度、申し上げますが、PCWPの心エコーでの測定はかなり微妙な記述なんで、またUpDateしていくかもしれません。また、肺メカニクスにかかわるPEEPについてはここではまだ論じておりませし、私の中では、「肺血管抵抗の上昇と胸腔内圧の上昇」のどちらが循環に影響をあたえる要因として強いのかが疑問です。この点、もしわかる方がいたら教えてほしいです。

「*PR-EDPG*により*PCWP*を代用していいのか？いけないのか？」についてもご意見をお待ちしております。

よろしくお願いします。

## 参考文献
[ハーバード大学テキスト 心臓病の病態生理 第4版](https://amzn.asia/d/4cZijM2)

[ICUブック 第4版](https://amzn.asia/d/e7Xs1fX)

[臨床呼吸機能検査 第8版](https://amzn.asia/d/87sUUuA)

[ヘスとカクマレックのTHE人工呼吸ブック 第2版](https://amzn.asia/d/7Iu4WQm)

[Dr.竜馬の病態で考える人工呼吸管理〜人工呼吸器設定の根拠を病態から理解し、ケーススタディで実践力をアップ!](https://amzn.asia/d/5n9JBxv)

[5.肺・胸膜 (1)肺水腫|病理コア画像](https://pathology.or.jp/corepictures2010/05/c01/03.html)

[Pulmonary Capillary Wedge Pressure](https://www.ncbi.nlm.nih.gov/books/NBK557748/)

 ,etc.
引数とハッシュを使って要素を取り出す

# 問題
ある映画のハッシュを定義し、格納されている
「title」(タイトル）・「genre」(ジャンル)・「year」(公開年)の三つの要素の中から
一つを取り出すプログラムを作成してください。
##### 定義する変数
~~~ruby
①movie = {"title" => "ハリーポッター", "genre" => "ファンタジー", "year" => "2001年"}
~~~

②ユーザーが入力するキーを、getsメソッドを利用し定義しましょう

# 模範解答
~~~ruby
def movie_info(movie, data)
  puts movie[data]
end

movie = {"title" => "ハリーポッター", "genre" => "ファンタジー", "year" => "2001年"}

puts "以下から一つを選んで入力してください。
  ・title
  ・genre
  ・year"

info = gets.chomp

movie_info(movie, info)
~~~

# 解答
まず、ハッシュ名[キー名]と記述する事で、
そのキーに対応するバリューを取り出す事ができる。

例えばキーである「title」を指定した場合、
対応するバリュー「ハリーポッター」が取り出される。
今回の場合では、movie["title"]はハリーポッターと取り出される。

このようにハッシュからバリューを取り出すには上記のように記述して取り出すが、
キーが文字列の場合は、キーをシンボルに変更しないといけない。


従って、文字列をシンボルに変換する記述が必要である。
~~~ruby
info = gets.chomp.to_sym
~~~
to_symメソッドを用いて、返り値を文字列からシンボルに変換している。

# まとめ
ハッシュの定義方法によってバリューの取り出し方は異なる事を意識していく。
xkeropi をビルドする (Debian 32bit)

[xkeropi を Hyper-V 上の ubuntu 16.04 で動かした](https://qiita.com/nekopro2/items/68d76bfc21142ecb2a87) が、最近のVirtualBox では ubuntu 16.04 がインスコできないんでやんの(VirtualBox 5.2あたりからダメっぽい)。

なので Debian に引っ越すよー

# 環境構築

## OS をインストールする
[debian-11.6.0-i386-netinst.iso](https://cdimage.debian.org/debian-cd/current/i386/iso-cd/debian-11.6.0-i386-netinst.iso) を VirtualBox 7.0 にインストールする。

## sudo できるようにする (任意)
~~~
$ su -
# adduser yui sudo
# reboot
~~~

## あると便利な ssh (任意)
~~~
$ sudo apt install openssh-server
~~~

## あると便利な samba (任意)
~~~
$ sudo apt install samba
$ sudo vi /etc/samba/smb.conf

[homes]
  comment = Home Directories
  browseable = no
  writable = yes
  create mask = 0644
  directory mask = 0755

$ sudo smbpasswd -a yui
$ sudo service smbd restart
~~~

## 必要なライブラリなどをインストール
~~~
$ sudo apt-get install xutils-dev
$ sudo apt-get install libgtk2.0-dev
$ sudo apt-get install libsdl1.2-dev
$ sudo apt-get install nasm
~~~

## ビルド
$ wget https://www.nonakap.org/keropi/xkeropi-20101107.tar.bz2
$ tar xf xkeropi-20101107.tar.bz2
$ cd xkeropi-20101107
$ xmkmf -a
$ make
![err.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821068/13a4765d-7687-b669-3a57-2cc2c2585f1e.png)
おうふ。。。

コンパイル オプション に -Wno-narrowing を追加する。
~~~:config.tmpl
EXTRA_DEFINES+= -Wno-narrowing
~~~

ビルド エラー
~~~
/usr/bin/ld: x68k/68kem.o: in function `m68000_ICountBk':
x68k/68kem.asm:(.data+0x4): multiple definition of `m68000_ICountBk'; x68k/cpu_glue.o:/home/yui/xkeropi-20101107/x68k/cpu_glue.c:6: first defined here
/usr/bin/ld: x68k/68kem.o: warning: relocation in read-only section `.text'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status
make: *** [Makefile:1152: xkeropi] Error 1
~~~

ubuntu だと問題なかったけど、確かに多重定義。
仕方がなのでコメントアウト。
~~~:cpu_glue.c
// int m68000_ICountBk;
~~~

ビルドは完了。
![run.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821068/e18cedc8-403b-d01e-d35c-85ede372b54b.png)

## 32bpp対応版を動かす
https://gitlab.com/nekodevteam/xkeropi を clone して、上記2ヵ所を直して実行。
![run2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821068/adccecf0-09e5-f4d8-d650-c7f6cacf95b0.png)
あとでもろもろ直しておきます。
2022/12/29主にITとかセキュリティの記事

サイバー攻撃被害の情報共有でガイダンス - 政府がパブコメ実施
https://www.security-next.com/142549

食品通販サイトに不正アクセス - クレカ情報流出の可能性
https://www.security-next.com/142546

捜査機関に対する情報開示は1583件、前期比14％減 - LINE
https://www.security-next.com/142438

「FortiOS」の深刻な脆弱性、「FortiProxy」にも影響
https://www.security-next.com/142608

新手の攻撃手法「OWASSRF」 - 「ProxyNotShell」軽減策をバイパス
https://www.security-next.com/142582

「Trend Micro Apex One」に権限昇格の脆弱性 - アップデートで修正
https://www.security-next.com/142586

国内企業の4割がCISOを設置 - 経営層の専任は2.9％
https://www.security-next.com/142141

メールアドレスリスト含む異なるファイルをサイトに誤掲載 - 愛知県病院薬剤師会
https://www.security-next.com/142554

IoTを標的にするGoベースのボットネット、新たにApacheなどの脆弱性悪用(12/27)
https://news.mynavi.jp/techplus/article/20221227-2546633/

おもちゃに潜む危険？ IoTの脅威から家族を守るためにすべきこと(12/27)
https://news.mynavi.jp/techplus/kikaku/20221227-security_frontline_v191/

中小企業が限られた予算で最適なランサムウェア対策を講じるには(12/27)
https://news.mynavi.jp/techplus/article/20221227-2547295/

JPCERT/CCが10月〜12月の脆弱性・脅威振り返るレポート公開、ランサムウェアに注意(12/26)
https://news.mynavi.jp/techplus/article/20221226-2544819/

草津の温泉旅館にも小口投資できる「セキュリティ・トークン」とは?(12/26)
https://news.mynavi.jp/techplus/article/20221226-2547280/

経営者の4割弱が「ビジネスチャット」の効果に不満を持っている(12/26)
https://news.mynavi.jp/techplus/article/20221226-2547225/

AIなど先端技術に特化した用語解説サイト「WORLD」開設、大和総研(12/26)
https://news.mynavi.jp/techplus/article/20221226-2547041/

ロシア語を話すサイバー犯罪者グループ、世界で8000人超に被害与える(12/26)
https://news.mynavi.jp/techplus/article/20221226-2546634/

ビジネスメール詐欺で大量の食品および原材料の出荷を盗み出す事件に注意(12/26)
https://news.mynavi.jp/techplus/article/20221226-2540647/

FBIが広告ブロッカー利用呼びかけ、インターネット検索と犯罪に関する注意喚起(12/26)
https://news.mynavi.jp/techplus/article/20221226-2544598/

ディープフェイクを見破る新手法
「恐竜の鳴き声を再現する手法」を応用　99.9％の精度で偽物音声を判別(12/28)
https://xtech.nikkei.com/atcl/nxt/mag/nnw/18/041800012/121900195/

ローカル5G
通信事業者以外が構築する5Gネットワーク(12/28)
https://xtech.nikkei.com/atcl/nxt/mag/nnw/18/091900032/121900052/

［クラウド］プライベートクラウドが上位に、1位は「内製」に舵を切ったあの会社(12/28)
https://xtech.nikkei.com/atcl/nxt/column/18/02293/00015/

［ITセキュリティークイズ］尼崎市USBメモリー紛失事案、業務委託先はどこ？(12/28)
https://xtech.nikkei.com/atcl/nxt/column/18/02301/122300013/

高収益の野村総合研究所、新卒採用人事が学生に求める「2つの素養」とは(12/28)
https://xtech.nikkei.com/atcl/nxt/column/18/02286/122200006/

ネットに接続していないPCから情報窃取、低周波の電磁波を壁越しにスマホで傍受(12/28)
https://xtech.nikkei.com/atcl/nxt/column/18/00676/122000123/

過去に遡る情報窃取を阻止、TLS 1.3が重視する「前方秘匿性」とは何か(12/28)
https://xtech.nikkei.com/atcl/nxt/column/18/02306/121900004/

［ITセキュリティ］国内の病院を襲ったランサムウエア、マイナカード関連も上位に(12/28)
https://xtech.nikkei.com/atcl/nxt/column/18/02293/00012/

お役御免となったWindows 10パソコン、「検証機」として使い倒す(12/28)
https://xtech.nikkei.com/atcl/nxt/column/18/02267/111800017/

LAN中枢の障害で窓口業務がストップ　IEからEdgeへの移行が引き金に
立川市役所(12/28)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020600011/122200123/

仮想スレッド（Virtual Thread）(12/28)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020600009/122200131/

世界最大級のイベント主催　メタバース先駆者の志とは
舟越 靖 氏　HIKKY CEO(12/28)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020600002/122200066/
AWS Lambdaの再試行回数によって処理を分岐させる方法

業務の都合でAWS Lambdaの処理を再試行回数によって分岐させる必要が出てきたので、その実装を含めシェアします。

# 実現できることのイメージ
※Lambdaは最大2回まで再試行を設定可能です。

```Java
if (再処理回数 == 0) {
    // 処理A
} else if (再処理回数 == 1) {
    // 処理B
} else {
    // 処理C
}
```

# とりあえず考えられる方法
基本的に以下の方法は **「Lambdaが再試行されるときはリクエストIDが同じになるのでそれをどうカウントするか」** という考え方が基になっています。

なお、リクエストIDはUUIDっぽいので、他のリクエストIDとは被ることはないという前提です。

- CloudWatch Logsのログを読み取ってリクエストIDをカウントする ([参考](https://zenn.dev/shimo_s3/articles/c2895880138d19))
- DynamoDBなどのDBに記録しつつそれを読み取ってリクエストIDをカウントする ([参考](https://zenn.dev/shimo_s3/articles/c2895880138d19))
- S3にリクエストIDを含んだ名前の空ファイルを吐き、その数をカウントする
- **/tmpにリクエストIDを含んだ名前の空ファイルを吐き、その数をカウントする**

...など

これらの中でも一番下の方法は **「最小限の権限で」「最小限のリソースで」** この実装を行えるため、この方法を試してみます。なお、他の方法は余計な管理や権限付与が発生しますが、こちらの方法については権限が厳しい環境でも実現可能です。

# 実装
やることとしてはリクエストIDを受け取って/tmp内に空ファイルを吐き出すだけなので、おそらくどのランタイムでも実装できそうです。

とりあえず慣れているのでPython 3.9の環境の例を示します。

```Python
import json
import pathlib
from datetime import datetime
from glob import glob


def lambda_handler(event, context):
    aws_request_id = context.aws_request_id
    try:
        # 意図的に失敗させる
        x
    except Exception as e:
        # 再試行回数をprint
        print(count_tmpfile(aws_request_id))
        if count_tmpfile(aws_request_id) == 0:
            print('process A')
            make_tmpfile(aws_request_id)
            raise
        elif count_tmpfile(aws_request_id) == 1:
            print('process B')
            make_tmpfile(aws_request_id)
            raise
        else:
            print('process C')
            raise

    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

def make_tmpfile(request_id):
    now = datetime.now().strftime('%Y%m%d%H%M%S%f')
    tmpfilepath = pathlib.Path(f'/tmp/{request_id}_{now}')
    tmpfilepath.touch()
    return None

def count_tmpfile(request_id):
    return len(glob(f'/tmp/{request_id}_*'))
```

以下はS3のPUTトリガーで起動してみたときのログです。

![log](https://raw.githubusercontent.com/aoki-h-jp/qiita-sync/main/aws/lambda/WS000031.JPG)

こんな感じで再処理回数によって処理を分岐させたいときは参考にしてください。
[Flutter]TextFieldを使ったViewを作成したら時限爆弾みたいにクラッシュしまくった話


結論から伝えるとonChangeの中でFuture.delayedを使うと時限爆弾の如くいきなりアプリが落ちると言った内容です。

流れ

    1.textFieldが二つ以上並んだ共通部品を作成
    
    2.引数としてonChangeとtextEditingControllerを準備
    
    3.引数でFuture.delayedの入ったonChangeを渡すとログも出ずにlost connectionとだけ表示されアプリがクラッシュする

該当のonChangeメソッドはこんな感じ

      onChangeFunc(String text) {
        Future.delayed(Duration.zero,(){
    
          setState(() {
    
            //処理
    
          });
        });
      }


これを渡した時に謎に時限爆弾の如く1〜5分以内に落ちる

textFieldが一つの場合は落ちることなく処理できていたので何が問題なのかわからず1ヶ月弱、アプリが落ちるたびにデバックを再開するという地獄のような日々を送ってました

どこかの記事でonChangeを外から呼び出す時はFuture.delayedが必要みたいなことを書いてあった気がしてまさかそれがクラッシュの原因の一つになっているなんて思いもしませんでした。

こんな問題にぶつかる人は少ないと思いますが、こういったアプリを作るみたいな人は僕みたいな地獄のような日々を送ってほしくないなと思ったので忘備録としてここの残します。

クラッシュしないってだけでほんと幸せだなぁとつくづく思った日々でした
gpt-neox-japanese-2.7b 日本語 GPT モデルを扱うメモ(6 GB GPU で推論動くよ)

## 背景

ふえぇ... ChatGPT しゅごいね...
そのうち open に公開されそうだけど... それまでは open な日本語 GPT で頑張る!

今回は gpt-neox-japanese-2.7b 動かします.

https://huggingface.co/abeja/gpt-neox-japanese-2.7b

hugging face で試せますが, stable diffusion とかに計算リソース割かれているのか, なんかいまいち実行が成功せぬ :cry:

## 参考情報

https://huggingface.co/docs/transformers/model_doc/gpt_neox_japanese


gpt-neox-japanese-2.7b による日本語テキスト生成を試す
https://note.com/npaka/n/n4651ee6d3c51

ありがとうございます.

https://atmarkit.itmedia.co.jp/ait/articles/2106/07/news025.html

CPU でも動きますが, Colab 無償版メモリ 12 GB では動かないとあるので, 16 GB はいるっぽ?
(モデルが fp32 展開すると 11.5 GB は必要なため)

とりあえず今回は CPU 128 GB mem + Ryzen 3950X + 3090(24 GB GPU mem)で動かします.

## 動作

ぺろっと使うには transformers の `pipeline` がよいでしょう.
ただデフォルトは CPU 実行(`device=-1`)になります.
GPU ある場合は 0(`cuda:0`), 1(`cuda:`) を指定します(文字列指定もできるんじゃろか)

また, モデルデータは fp16 になっていて 5.3 GB くらいなのですが, デフォルトで fp32 に展開ににされますので 11.5 GB くらい必要になります.
元モデルが fp16 だとあんまり fp32 に展開する利点もありませんので, fp16 で動かします!
(Intel とかの最新 CPU だと, CPU 実行の場合 fp16 のまま実行してくれたりするんじゃろかね)
`torch_dtype` で `torch.float16` を指定します


```python
from transformers import pipeline
import torch

device = -1 # cpu
if torch.cuda.is_available():
    device = 0

# device 0 = cuda:0
generator = pipeline("text-generation", model="abeja/gpt-neox-japanese-2.7b", torch_dtype=torch.float16, device=device)
generated = generator(
    "優秀な若人さまが、人類史上最速で GPT をお極めなされるには、",
    max_length=300,
    do_sample=True,
    num_return_sequences=3,
    top_p=0.95,
    top_k=50
)
print(*generated, sep="\n")
```

```
{'generated_text': '優秀な若人さまが、人類史上最速で GPT をお極めなされるには、それなりの動機と目的意識と決意が必要ということでしょう
か。'}
{'generated_text': '優秀な若人さまが、人類史上最速で GPT をお極めなされるには、大いなる疑問が湧きますが…'}
{'generated_text': '優秀な若人さまが、人類史上最速で GPT をお極めなされるには、GT3の開発が終わってからが妥当な時期だろう。'}
```

Ultra super cooool!!! :tada: :tada:

GT3 の開発が終わってから... GPT3 の開発が終わってから GPT4 が出てくるタイミングということでしょうか...!!! 😳

GPU では 5.3 GB くらいしか GPU メモリ消費しないので, 6 GB くらいのミッドレンジ GPU でも動くと思われます!

多少時間かかってもいいなら CPU でメモリ 16 GB で動かすのも手かもしれませんね.
(Apple sillicon mac なら fp16 実行でメモリ 8 GB でもうごいちゃうやも?)

## TODO

* [ ] ファインチューンとかやってみる!
* [ ] ChatGPT っぽいのを作るためにプロンプトエンジニアリングをがんばる!
* [ ] Whisper で音声認識して受け答えしてくれたりするようにする!
* [ ] tdmelodic か, もしくはいい感じにピッチアクセント推定してくれる language model(e.g. https://arxiv.org/abs/2212.08321 ) と組み合わせて TTS(text-to-speech) をいい感じにする(GPT の感情推定と組みあわせるとか)
Q.\r（キャリッジリターン）とは？

コンソールでアニメーションするときに使うやつ。

# 結論
**カーソルを先頭に戻すよ**のサイン。
例えば、以下のような処理を想定する。
```test.py
print('hogege\rhuga')
```
今回は適当にPythonだけど、適宜ほかの言語に読み替えてもらって構わない。
さて、この出力の結果だが...

> hugage

となる。

この時点でお察しかと思うが、`\r`以後の文字列は**文頭から上書きする形で描写される**。
つまり、

> h|
 ho|
hog|
hoge|
hogeg|
hogege|
--
|hogege (<-カーソルが先頭に戻る）
--
h|ogege
hu|gege
hug|ege
huga|ge

といった感じで出力がされている。

# おまけ
もどるのはあくまで**カーソルのある行の文頭**なので、改行文字が入ってしまえばそれ以前の行は改変されなくなる。
ワイパー型マルウェア とは

## 勉強前イメージ

車のワイパー？みたいな？
収集とか？

## 調査

### ワイパー型マルウェア とは

ワイパー型と言うのは破壊だけを目的としたマルウェアになります。
露によるウクライナ進行でウクライナへのサイバー攻撃が同時に発生していることがあります。

身代金を要求するようなシステムが残るランサムウェアとは違い
データの破壊を行うので、データが破壊される前の侵略の段階などのログも残らず消えてしまうので
分析作業が難しいという特徴があります。

対策としては他のマルウェア同様ですが
セキュリティパッチを適応し、セキュリティソフトを最新にしておくこと、
変なメールの添付ファイルを開いたりしないことが大事です。

## 勉強後イメージ

セキュリティ対策ってほんとに基礎が大事なんだなって思ってる。
あと、ワイパー型するのってなんでだろうね？
なんかのイベントなどを停止させたいとかそういうの？

## 参考

- [ワイパー型マルウェアとは？攻撃の仕組みや特徴、対策について徹底解説](https://cybersecurity-jp.com/column/30236)
- [世界を脅かすワイパー型マルウェアとは？](https://www.securestation.jp/security/2912/)
Rustでweb-view

# 解説は多いけど結局どうするのかよくわからなかったので自分なりにまとめ。
Pythonとpywebviewでも十分便利そうではあるのですけれども、やっぱり単体バイナリにしたいよなーってところです。
RustかVか、はたまたgoかみたいなところで悩むのですけれども将来性はRustかなーとも思うのでRustとweb-viewで行ってみようと思いました。
尚RustとCargoのインストールについては端折ります。

## Windows 10 SDKのインストール
Windowsでweb-viewを使う場合MSHTMLかEDGEを選べるのですが、MSHTMLってIEのことです、IEはないよなーせめてEDGEだよなーってことで、EDGEを使うにはWindows 10 SDKが必要ですよと。
Visual Studio InstallerでWindows 10 SDKをインストールします。
![vsinstaller](https://user-images.githubusercontent.com/12671694/209842569-e9807169-5fe6-4bbb-9336-e16cd5ff586a.png)

## プロジェクトの作成など

```sh
cargo new webview_test
```

Cargo.tomlを編集
dependenciesに追加
```sh
web-view = { version = "0.7.3", features = ["edge"] }
```

web-viewをadd

```sh
cargo add web-view
```

## srcを編集

```rust:src/main.rs
#![windows_subsystem = "windows"]
use web_view::*;

fn main() {
    let html_content = "<html><body><h1>Hello!</h1></body></html>";

    web_view::builder()
        .title("hello world")
        .content(Content::Html(html_content))
        .size(320, 240)
        //.resizable(false)
        .user_data(())
        .invoke_handler(|_webview, _arg| Ok(()))
        .run()
        .unwrap()
}
```

## コンパイルと実行

```sh
cargo build
cargo run
```

![hello](https://user-images.githubusercontent.com/12671694/209844670-cace4896-1401-41ef-a37a-4be937002100.png)

無事に動作したようです。


## ポイントはcargo addとWindows 10 SDK
Windows 10 SDKのことはweb-vew公式のドキュメントにちらっとは書いてあるのですがRustに不慣れなこともあり cargo add の件を落としていてなかなか動作させることが出来ず悩みました。
*コンパイルは通るけれど実行が出来ないみたいなことになります*

このままでは使い物にならないので次回以降、外部ファイルから読み込む、HTML側とのやりとり、バイナリにリソースを含めてビルドする。あたりをやってゆきたいと思います。
EC2フリート とは

## 勉強前イメージ

EC2ってことはわかるけど・・・

## 調査

### EC2フリート とは

AWS EC2インスタンスをの起動する方法の一つで
スポットインスタンスとオンデマンドインスタンスを組み合わせて定義できるものになります。

EC2インスタンスを使用する方法に [スポットインスタンス](https://qiita.com/miyuki_samitani/items/4cebf928334ee60c5219) というものがあります。
スポットインスタンス とはスポットインスタンスとはEC2の空いているキャパシティを活用して
オンデマンドインスタンスより安く使用するインスタンスの購入方法を指します。
しかし、空いているインスタンスがスポットインスタンスになるため、空きがなくなると停止される可能性があります。

そこで有用になるのがEC2フリートになります。
AWSからスポットインスタンスを落とします、と通知があってEC2が止まってしまうとサービス自体が止まってしまう可能性があります。
それだと困ってしまうので、オンデマンドインスタンスを立ち上げる設定ができるのが `EC2フリート` になります。

もちろんすべてのスポットインスタンスが落ちてから・・・ではなく
10台サーバが必要な際は2割オンデマンド・8割スポットとかの設定も可能になります。

EC2フリートの費用は必要ないですが、EC2フリートで起動したオンデマンドインスタンスやスポットインスタンスに関しては支払いが必要になります。

## 勉強後イメージ

そんな便利な機能あるの・・！？
よいやん、、、

## 参考

- [AWSを活用してゲームサーバーのコストを劇的に下げるスポットインスタンスを効果的に使うための基礎知識](https://logmi.jp/tech/articles/321685)
SRAM とは

## 勉強前イメージ

DRAMともFRAMとも違う・・・

## 調査

### SRAM とは

Static Random Access Memory の略で、RAMの方式の一つで揮発性のメモリです。
主にキャッシュメモリとして使われるメモリになります。
キャッシュメモリとは、メインメモリとは違い高速に動作するためにアクセス時間が短縮されます。

同じ揮発性のメモリでも以下のような違いがあります。
DRAM → メインメモリで使用
SRAM → キャッシュメモリで使用

ではDRAMとどこが違うのかというと
速度が高速で単価が高く、一定期間ごとのリフレッシュが不要なものになります。
トランジスタを複数個使うので消費電力も大きくなってしまうが高速な動作が可能です。

## 勉強後イメージ

メモリメモリ言ってたけどメインメモリとキャッシュメモリって別なのね・・・・

## 参考

- [SRAM（スタティックRAM）とは](https://medium-company.com/sram/)
- [DRAM とは](https://qiita.com/miyuki_samitani/items/d1936d55bb88d0de8d9b)
Qiita に画像アップロードできなくなった（月間上限 100 MB）

## はじめに

今日も今日とて記事を書こうと、画像をアップロードすると、、、

![Something went wrong]()

```markdown
![Something went wrong]()
```

これである

Something went wrong

何かおかしいと言われましても、、、

## エラー詳細

ブラウザの開発者ツールで確認すると、 `https://qiita.com/api/upload/policies` への POST にステータスコード 413 がが返って来ている

413 は Payload Too Large

つまり、リクエストのサイズがサーバーの定める上限を超えている、というもの

https://developer.mozilla.org/ja/docs/Web/HTTP/Status/413

レスポンスボディーは以下の JSON でした

```json
{errors: {size: ["Monthly limit exceeded"]}}
```

Monthly limit exceeded なので、月間上限超過です

## エラー原因

Qiita ヘルプに以下の記載がありました

> アップロードできる画像の容量制限
>
> ファイル単体で10MBまでの画像ファイルをアップロードすることが可能です。
> 画像ファイルは月に合計100MBまでしかアップロードできません。
> 制限を超えてしまった場合は次の月までお待ちください。

https://help.qiita.com/ja/articles/qiita-image-upload

つまり、12月に画像をアップロードしすぎて 100MB を超えてしまった、ということです

## 根本原因

### 画像使いすぎ

ほとんどの記事に大量の画像を入れていました

過去投稿からの URL コピーであれば画像を入れられるので、いくつか抜粋

![スクリーンショット 2022-12-13 12.09.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/536b72ee-d729-ea31-12d7-db40b06d7dc2.png)

![スクリーンショット 2022-12-13 12.12.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/47b7b997-ff44-dfe8-6f0d-31be9a26ef14.png)

![スクリーンショット 2022-11-22 15.29.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/334c6312-2eaa-bb03-63f0-490f95de1847.png)

だってしょうがないじゃないですか

Elixir Livebook だと簡単に画像処理もできるし、グラフも作れてしまうんです

せっかく処理した結果は載せなきゃ損じゃないですか

https://livebook.dev/

### 大きなGIF使いすぎ

GIF ファイルを動画として大量に使いました

しかも結構長時間の GIF です

以下にいくつか載せておきます

![evision.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/0541d7d6-7bab-87d8-4bad-1208bb10fdd1.gif)

![exp.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/3bb7915d-f278-6c9e-2079-c8d3fa9615a5.gif)

![scatter.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/5859b2b4-9102-e212-8bcd-9002b4f5fb59.gif)

だってしょうがないじゃないですか

Elixir Livebook だと処理の経過が見れるばかりか対話的（インタラクティブ）に処理のON/OFF、並び替えまでできるんです

こんなの動画で見せるしかないじゃないですか

https://qiita.com/RyoWakabayashi/items/7d9eff9df1041c705713

### アドベントカレンダーに取り憑かれていた

2022年のアドベントカレンダーに私が投稿した件数は 60 件

2022年12月中に投稿した記事だけでも 31 件です

1 記事 3 MB 強で 100 MB を超えてしまう計算です

あまつさえ、実は過去記事の更新も行なっていました（使っていたモジュールの更新などのため）

100 MB を超えて当然です

アドベントカレンダー開催期間中に超えなくて幸いでした

だってしょうがないじゃないですか

Nx も Explorer も Livebook も Kino も Evision もすごい勢いで更新されて、まして Bumblebee まで登場するなんて

行列演算もデータ分析も画像処理も AI・ML も日進月歩で機能が、性能が、表現力が上がっているんです

記事を書かないわけにいかないじゃないですか

https://github.com/elixir-nx/nx

https://github.com/elixir-nx/explorer

https://github.com/cocoa-xu/evision

https://github.com/elixir-nx/bumblebee

## 対策

記事投稿を抑えるつもりは毛頭ないので、以下の対策をするしかないですね

- 小さくて良い画像はリサイズしてからアップロードする
- 使いまわせる画像は使い回す
- 間違った画像をアップロードしない

## まとめ

全部 Elixir のせいです

https://elixirschool.com/ja

年明けたらまた記事を投稿します
オールドプログラマーだったらDB以外の保存方法を考えてみろよ！

スマホだからDBに保存出来ない困ったとボヤいたら

「オールドプログラマーだったらDB以外の保存方法を考えてみろよ！」
的な返事をいただき、凹んだので書きます
※もっと優しく言われてますけど、DBしか使わないと言う発想が思考停止していた

前提条件
・sqlite3は使えますが諸事所により使えないという前提
・保存件数は１件（項目は多数あります）


この実験はスマホではないですが、手始めにバイナリー形式でファイル保存ができるか考えてみました


# みんな大好きJSON

```Elixir
Mix.install([{:json, "~> 1.4"}])

# セーブ
{_, x} =  JSON.encode([hoge: 123, hoge2: "hogehoge"])
File.write("hoge.txt", x)

# ロード
{_ ,y} = File.read("hoge.txt")
{_, j} = JSON.decode(y)
Map.get(j, "hoge")
```

# やっぱりバイナリーで保存したい！
@mnishiguchi　さんからのヒントを元に

https://www.erlang.org/doc/man/erlang.html#binary_to_term-1

https://www.erlang.org/doc/man/erlang.html#term_to_binary-1


```Elixir
# セーブ
:erlang.term_to_binary([hoge: 123, hoge2: "hogehoge"])
File.write("hoge2.txt", x)

# ロード
{_, z} = File.read("hoge2.txt") 
:erlang.binary_to_term(z) 
```

つづきの実験はありますが、今回はここまで

PS:めっちゃ悔し、思いつかなかったのが悔しい

# 別案
@piacerex さんの案
```elixir
Mix.install([{:jason, "~> 1.4"}])

[hoge: 123, hoge2: "hogehoge"]
|> Enum.into(%{})
|> Jason.encode!
|> then(& File.write!("params.json", &1))

File.read!("params.json")
|> Jason.decode!(keys: :atoms!)
|> Enum.into([])
```

更に悔しい（案ありがとうございました）
AtCoder ユニークビジョンプログラミングコンテスト2022冬 D問題解説(Python コード付)

# はじめに　

　本記事はPythonでAtcoderにチャレンジしているけど、Pythonはコード例がないため解説がわかりにくい！と感じている方を対象としています。 
　私自身大してレートも高くないので、不備等あるかもしれませんがご了承くださいm(_ _)m
## ユニークビジョンプログラミングコンテスト2022冬 D問題
　問題のURLはこちらです。[ユニークビジョンプログラミングコンテスト2022冬 D問題](https://atcoder.jp/contests/abc283/tasks/abc283_d)

## 使うデータ構造　
　**スタック**を使っていきます。　
　スタックとはなんぞや？って方は[こちら](https://qiita.com/drken/items/6a95b57d2e374a3d3292)の記事を見て見てください。非常にわかりやすく解説してくれています。
　また、今回私はlistを使ってスタックを実装しましたが、collections.dequeを使用した方が計算速度は早いそうです。[こちら](https://qiita.com/saba/items/107c4237206e31acdbef)の記事で検証されていました。
　初学者の方はcollections.dequeについても併せて勉強すると良いかもしれません。

## 考え方　
ざっくりとしたコンセプトを、以下のフローチャートに示します。
(パワポでささっと作ったので見にくくてすいません・・・・・・)
![図フローチャート.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3049543/c4a1e7a5-d9d9-fe32-5f01-4bb76eba7959.png)
また、これだけではわかりにくいと思いますので、**S=(ab(c)ab)** とした時のスタック内部の状態を図にしました。(これでもわかりにくかったらすいません・・・・・)
![図スタック.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3049543/1564f682-cf73-ea7c-29a4-b53b3db8ec7a.png)
あとはこれを実装していきます。

## 実装例　
```python
#標準入力
s = input()
#YesかNoかを管理するフラグ
booler = True
#スタックの高さを管理する値
deep_count = 0
"""
スタックの作成。
初期状態で空のリストを追加していますが、あまり気にしないでください。　
"""
stack = list()
stack.append(list())
"""
各アルファベットをキーとした辞書型の配列「al_dict」を作成。
これによって現在箱に各アルファベットが箱に入っているかを管理
(Trueなら箱にその文字が入っている。Falseなら入っていない。)
"""
al_dict = dict()
for i in range(26):
    al_dict[chr(ord("a")+i)] = False

#sの各文字について見ていく。なお、iは文字の位置を示すインデックスのため注意。
for i in range(len(s)) :
    #現在見ている位置の文字「moji」を取得。
    moji = s[i]
    if moji == "(":
        """
        mojiが「(」の場合、新たなスタックが追加される。
        スタックが追加されたため、deep_countを+1する。
        """
        stack.append(list())
        deep_count += 1
        
    elif moji == ")":
        """
        mojiが「)」の場合、スタックの最も上にある配列を取り出す。
        以降　この配列を「sweep」と名付けます。
        """
        sweep = stack[deep_count]
        """
        sweep内に格納されたアルファベットたちをキーとするal_dict[key]の値を　
　　　　　Falseに変更する。
        """
        for j in sweep :
            al_dict[j] = False 
        """
        上記の処理によって箱から取り出す処理が完了したので、stackからsweepを削除する。
        また、これによってstackの高さも低くなったため、deep_countを-1する。
        """
        stack.pop(-1)
        deep_count -= 1
   
    else :
        if al_dict[moji] :
            booler = False
            break
        else :
            stack[deep_count].append(moji)
            al_dict[moji] = True
        
if booler :
    print("Yes")
else :
    print("No")
```

## 計算量に関する記載
 今問題の制約は 1<=|S|<= 3×10^5となっています。　
私のコードでは一部二重ループとなっており、一見すると計算量はO(N^2)となりTLEになりそうですが、sweepをループさせる場合最大でも26回で済みます。(アルファベットの数は26個で、これ以上の値が各sweep内に格納されることはありません。)
sweep自体も問題文の条件から、大した量生成されないので、本問題では二重ループでも問題なく計算が終了します。
virt-managerでの主な操作

●virt-managerでの仮想マシンの管理
virt-managerはGUIで仮想マシンを作成・管理するツール。使用するにはvirt-managerパッケージをインストール。

virt-manager主な操作
・仮想マシンの管理
　　仮想マシンの作成・削除
　　メモリ、仮想CPUの割り当て
　　ゲストOSのインストール、起動・停止
　　パフォーマンスの監視
　　マイグレーション（仮想マシンを他の環境へ移行する）
・スナップショットの管理
　　スナップショットの作成・削除
bash の関数定義の本体は { ... } や ( ... ) などの Compound Commands

bash で関数を定義するときに `function name { ... }` 以外に `function name ( ... )` と書くことができると知りました。

bash のマニュアル ([Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html)) を参照したところ、関数の本体は `compound-command` と定義されており、他の書き方もできることが分かりました。

# bash の関数定義

bash では、関数を定義する構文が 2 種類あります。どちらの構文であっても、関数の本体は `compound-command` です。

[Bash Reference Manual > 3.3 Shell Functions](https://www.gnu.org/software/bash/manual/bash.html#Shell-Functions) より引用:
> Functions are declared using this syntax:
> ```
> fname () compound-command [ redirections ]
> ```
> or
> ```
> function fname [()] compound-command [ redirections ]
> ```

# bash の compound-command

`compound-command` は [Bash Reference Manual > 3.2.5 Compound Commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands) で説明されているとおり、Looping Constructs, Conditional Constructs, Grouping Commands のいずれかです。

 * Looping Constructs ... `for`, `while`, `until`
 * Conditional Constructs ... `if`, `case`, `[[ expression ]]` など
 * Grouping Commands ... `( ... )`, `{ ... }`

# Looping Constructs の使用例

Looping Constructs を用いると、関数定義を次のように書き換えることができます。

```bash
# before
function f
{
    for x in 1 2 3
    do  
        echo $x
    done
}
```

```bash
# after
function f
    for x in 1 2 3
    do  
        echo $x
    done
```

# Conditional Constructs の使用例

Conditional Constructs を用いると、関数定義を次のように書き換えることができます。

```bash
# before
function f
{
    case "$1" in
        1)  
            echo "one"
            ;;
        2)  
            echo "two"
            ;;
        *)  
            echo "many"
            ;;
    esac
}
```

```bash
# after
function f
    case "$1" in
        1)
            echo "one"
            ;;
        2)
            echo "two"
            ;;
        *)
            echo "many"
            ;;
    esac
```

# Grouping Commands の使用例

Grouping Commands には current-shell で実行される `{ ... }` と sub-shell で実行される `( ... )` があります。`( ... )` を用いると、意図せず current-shell の状態 (変数やカレントディレクトリなど) を変更してしまう心配が無くなります。

```bash
function f1
{
    x=777
    cd /tmp
}

# グローバル変数 x とカレントディレクトリを変更してから f1 を呼び出す.
# f1 の本体は current-shell で実行されるため "x=777, pwd=/tmp" が出力される.
x=123 && cd / && f1
echo "x=$x, pwd=$(pwd)" # => "x=777, pwd=/tmp"
```

```bash
function f2
(
    x=777
    cd /tmp
)

# グローバル変数 x とカレントディレクトリを変更してから f2 を呼び出す.
# f2 の本体は sub-shell で実行されるため "x=123, pwd=/" が出力される.
x=123 && cd / && f2
echo "x=$x, pwd=$(pwd)" # => "x=123, pwd=/"
```

# どの形式で関数を定義すべきか

ここまでの内容を踏まえて、bash の関数定義はどの書き方をすると良いのでしょうか。

積極的な理由がない限り `function name { ... }` 形式に統一すると良いと思います。理由は、他の形式を目にする機会が少ない (パッと見た時にバグの可能性を疑ってしまう) ためです。

```bash
# NG
function f
    for x in 1 2 3
    do
        echo $x
    done

# OK
function f
{
    for x in 1 2 3
    do
        echo $x
    done
}
```

```bash
# NG
function f
    case "$1" in
        ...
    esac

# OK
function f
{
    case "$1" in
        ...
    esac
}
```

```bash
# NG
function f
(
    ...
)

# OK
function f
{
    (
        ...
    )
}
```
今更 Visual Studio Express 2017 をインストールする

Visual Studio Express 2017 for Windows Desktop は利用条件が緩い環境だったため、以前この開発環境をよく利用していました。
現在の状況からみると扱える対象環境があまりに古く、今これで何か作り始めようとしたら初めから技術負債を抱えるようなものなので使いたい場面自体がほとんどありませんが、まれに古い環境の確認として利用したくなる場合があります。

ただ、これを記載している時点で Microsoft の Visual Studio Express とキャプションされたページから Visual Studio Express 2017 をダウンロードすることは出来なくなっています。Dev Essentials からも入手できないようです。
それならと諦めても良いのですが、ちょっと調べてみたら方法があったので書いています。

Express 2017 は Visual Studio Installer によって管理されていて更新等もそこから行う形になっています。
そのあたりを調べてみたら以下のようなページがありました。

- Visual Studio のインストールに使用するコマンド ライン パラメーターの例
    - https://learn.microsoft.com/ja-jp/visualstudio/install/command-line-parameter-examples?view=vs-2022

コマンドラインパラメータでインストール対象の指定などが可能であるようなので、上記で紹介されているリリースチャネルのフィード内容と合わせれば Express 2017 のインストール指定が可能になります。
インストーラ自体はたぶんどれでも良くて、Visual Sutudio 2022 などのWebインストーラをダウンロードしてきて、以下のようなコマンドラインを指定すると Express 2017 のインストール画面となります。

```
.\VisualStudioSetup.exe --channelUri https://aka.ms/vs/15/release/channel --productId Microsoft.VisualStudio.Product.WDExpress
```