OmniAICreator/Qiita-1.07M huggingface dataset:
Julia: MariaDB のデータを読む (Read)

## プログラム

フォルダー構造

```text
$ tree -a
.
├── .env
├── config_mariadb.jl
└── maria_read.jl
```

```julia:maria_read.jl
#! /usr/bin/julia
#
#	maria_read.jl
#
#						Feb/24/2023
# --------------------------------------------------------------------
using MySQL
include("config_mariadb.jl")
# --------------------------------------------------------------------
println(stderr,"*** 開始 ***")

host, user, password, data_base = config_mariadb_proc()
conn =DBInterface.connect(MySQL.Connection, host, user, password)
command = "select * from " * data_base * ".cities;"
stmt = DBInterface.prepare(conn,command)
results = DBInterface.execute(stmt)
rowid = DBInterface.lastrowid(results)
for row in results
	print(row[1],'\t',row[2],'\t')
	println(row[3],'\t',row[4])
end
#
DBInterface.close!(stmt)
DBInterface.close!(conn)
#
println(stderr,"*** 終了 ***")
# --------------------------------------------------------------------
```

.env
config_mariadb.jl
はこちら
[Julia: MariaDB のバージョンを表示](https://qiita.com/ekzemplaro/items/dd01c569d386c44213b3)

## 確認したバージョン

```text
$ julia --version
julia version 1.8.5
```

## 参考ページ
[ JuliaDatabases / DBInterface.jl](https://github.com/JuliaDatabases/DBInterface.jl)
【PHP】.envで環境変数を扱いたいときの設定

## .envファイルから値を読み込むと接続できない

```
php_network_getaddresses: getaddrinfo failed: nodename nor servname provided, or not known
```

- なぜかuser名やpassword名を .env で読み込んで、変数に代入した値を使用すると、そんなファイルが無いと怒られてしまう。
- .env を経由せずに同じファイル上で指定するとなぜか接続できる。


## .envを読み込む設定

そもそも .env を読み込めないのは、デフォルトでは読み込む設定されていないから！！
phpdoenv をインストールする必要がある。

[GitHub – vlucas/phpdotenv: Loads environment variables from .env to getenv(), $_ENV and $_SERVER automagically.](https://github.com/vlucas/phpdotenv)

## 設定手順

①ターミナルで

```
$ composer require vlucas/phpdotenv
```

②そして .env を読み込みたいファイルで下記を追記

```
$dotenv = Dotenv\\Dotenv::createImmutable(__DIR__);
$dotenv->load();
```

③.env ファイルはこんな感じで設定 　なぜかpasswordは '' で括らないと denied された

```
DB_HOST = 123.45.67.89
DB_USER = honnban
DB_PASS = 'password'
DB_NAME = honnban_user
```

④　②を記載したファイルで、設定した環境変数を使用する

```
$host = $_ENV['DB_HOST'];
```
UnityでGameViewをフルスクリーンにする為にした事

先日、Unityで作った映像のGameViewをプロジェクターで投影し、周りの方に見せました！
しかし、フルスクリーンが出来ない。。。
周りの方に見せるのにメニューバーが表示されているとカッコ悪いので、どうしてもGameViewのみのフルスクリーンしたかったのですが、Unity初心者の私は修正方法に辿り着くまでに、だいぶ時間が掛かってしまいました:sweat:
結果、とても初歩的な事でしたが、今回は同じ問題でつまづく方が少しでも減れば嬉しいです。

# やりたかった事
オレンジで囲まれた枠を取り除き、GameViewのみをスクリーンに表示させる。
![Screen Shot 2021-12-26 at 19.44.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2413072/1245befa-7277-0d5e-fc8b-3244181138b9.png)

# 方法
`File > Build And Run`を使ってプロジェクトをBuildをする。
![Screen Shot 2021-12-26 at 20.02.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2413072/473383eb-a93d-9e58-09e7-6a5ebce44791.png)

そうすると（Buildには多少の時間がかかります）、、、

出来ました！！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2413072/1cfec1fb-8da6-aa79-b37c-7a39c8777c5b.png)

# まとめ
とても初歩的なことでしたが、何時間も格闘してようやく出来た時には感動してしまいました！
同じ問題でつまづいた方に少しでも参考になれば嬉しいです。
【PHP】MAMPでcURL関数を使いたい（CAファイルの追記が必要）

## 結論

- cacert.pem（CA証明書をダウンロードする）
- php.iniを書き換える

## そもそもcURLが使える状況かどうか

- php -m で確認
- phpinfo() で確認

[PHPでcurlを使えない場合の対処法 – Qiita](https://qiita.com/____easy/items/06406fc62bd7464ad651)

- grepで確認

```
% php -i | grep cURL
cURL support => enabled
cURL Information => 7.64.1
```

[cURLのインストール状況の確認](http://www.php-mysql-linux.com/linux/curl-install/)

- curl --version で確認

```
% curl --version
curl 7.78.0 (x86_64-apple-darwin20.4.0) libcurl/7.78.0 (SecureTransport) OpenSSL/1.1.1k zlib/1.2.11 brotli/1.0.9 zstd/1.5.0 libidn2/2.3.2 libssh2/1.9.0 nghttp2/1.44.0 librtmp/2.3 OpenLDAP/2.5.5
Release-Date: 2021-07-21
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp 
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz MultiSSL NTLM NTLM_WB SPNEGO SSL TLS-SRP UnixSockets zstd
```

→使える環境は整っていそう？？

だが、cacert.pemファイルが指定された場所になかったのが原因だった

[php – Windows MAMP-PHP CURLの問題](https://base64.work/so/php/2774928)

## エラー

`$curl_error = curl_error($ch)`  これでエラー内容をキャッチして表示する

```
“error setting certificate verify locations”
```

## 対応策

macのCAファイルの証明書が原因だった。

[PHPのcurlでSSL certificate problemが出る場合](https://tm23forest.com/contents/php-curl-ssl-certificate-problem)

- cacert.pem（CA証明書をダウンロードする）
- php.iniを書き換える

[MacOS – How to fix curl throwing “error setting certificate verify locations”](https://itectec.com/askdifferent/macos-how-to-fix-curl-throwing-error-setting-certificate-verify-locations/)

> 証明書が本来あるべき場所にない（または潜在的に読み取れない）と不平を言っている

## php.iniの場所を確認

`phpinfo()` で確認

[もういい加減覚えよう。php.iniはどこにあるのか – Qiita](https://qiita.com/ritukiii/items/624eb475b85e28613a70)

php.iniに下記を追記してMAMPを再起動。

curl.cainfo =" " には、 curl_error($ch) で表示したエラーに「どこにファイルがない！」と教えてくれているので、そのパスをコピペする。

```
[curl]
; A default value for the CURLOPT_CAINFO option. This is required to be an
; absolute path.
curl.cainfo = "/Applications/MAMP/Library/OpenSSL/cacert.pem"
```

## 最後に

自分の場合、MAMPをworkspaceにコピーして使用していたため（Railsとかをworkspaceで管理していたためそちらに合わせた）、User/名前/workspace/MAMP/〜の中のphp.iniを変更していたため、変更が反映されなかった。

エラーが教えてくれている通り、Applicationsフォルダの中のphp.iniに追記白ということだった。
祝 obniz DCMotor 1PWM化 メカナムも楽勝

![PXL_20211226_064023535.PORTRAIT.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234533/e0edda7d-24c8-e5ff-0dfc-7086ee4253ee.jpeg)

#■永らくDCMotor3台制約が!
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234533/d8aabb19-93e3-fe8c-000e-43a57b3ce4d9.png)
#■シンプルメカナムを夢見続けた日々
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234533/2b6c4b3e-0f2d-f9b7-da35-1f8d19b161af.png)

#■ついにobniz.js 3.18.0でDCMotor 1PWM化実現
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/obniz?src=hash&amp;ref_src=twsrc%5Etfw">#obniz</a> からの最高のクリスマスプレゼント。<br>1DCMotorの使用PWMが1つに改善。メカナムホイール4台もDCMotorで制御可能に!!!<a href="https://t.co/zT0DEztqQC">https://t.co/zT0DEztqQC</a> <a href="https://t.co/V51WyY70K9">pic.twitter.com/V51WyY70K9</a></p>&mdash; Google Homer (@google_homer_) <a href="https://twitter.com/google_homer_/status/1472867432322994177?ref_src=twsrc%5Etfw">December 20, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
#■メカナム動作の動画
<blockquote class="twitter-tweet"><p lang="und" dir="ltr"><a href="https://t.co/x6MvgE50X0">pic.twitter.com/x6MvgE50X0</a></p>&mdash; Google Homer (@google_homer_) <a href="https://twitter.com/google_homer_/status/1472868250019315715?ref_src=twsrc%5Etfw">December 20, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
#■超シンプルなプログラムでメカナム制御可能に
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234533/1621a604-1959-5c59-cc66-959eb3d04d7d.png)

#■(参考)メカナムの走行特性
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234533/d1305ede-95e9-1f5e-d728-4e5cb5eb3573.png)

#■新たなるチャレンジ
###メカナムホイール200個を大人買いし、メカナム車50台の一斉リモート制御にチャレンジ準備中。
![PXL_20211226_064018302.PORTRAIT.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234533/74179241-0a01-1ad0-9598-9640e1ff98c5.jpeg)

![PXL_20211220_233634457.MP.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234533/451c2bcb-a4e2-e127-1be9-c614974f6081.jpeg)
【PHP】cURL関数でPOSTする方法（送信＆受け取る（API））

## 送信側

```
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $json_params);
```

この2行でPOSTできるようにする。

送りたい値を連想配列にしたのち、json_encodeでJSON形式にして送る

```
  $base_url = 'api.php';
  $params = [
    'user_id'  => $user_id,
    'password' => $password
  ];
  $json_params = json_encode($params);

  $headers = [
    'Content-Type: application/json',
    'Accept-Charset: UTF-8',
  ];

  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $base_url);
  curl_setopt($ch, CURLOPT_POST, TRUE);
  curl_setopt($ch, CURLOPT_POSTFIELDS, $json_params);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  $login_info_return_value = curl_exec($ch);
  curl_close($ch);
```

###受け取るAPI側

JSONで受け取る

```
$user_id  = filter_input(INPUT_POST, 'user_id');
$password = filter_input(INPUT_POST, 'password');

$post_json_data = file_get_contents("php://input");
// JSONファイルを連想配列へ変換
$post_obj_data = json_decode($post_json_data, true);
```

> php://input php://input は読み込み専用のストリームで、 リクエストの body 部から生のデータを読み込むことができます。 php://input は、 enctype=”multipart/form-data” に対しては使用できません。 https://www.php.net/manual/ja/wrappers.php.php

- $post_json_data にJSON形式で格納される
- それを連想配列に戻して使用する
【PHP】LINEログインAPI – IDトークンを検証して正しいユーザー情報を取得する方法

## 流れ（PHPで実装）

1. トップページの `<script>` に `liff.login()` を設置して、アプリに訪れたユーザーにログインしてもらう。
2. ユーザーがログインボタンを押すと、トークン等やユーザー情報をJavascriptで取得できるようになる。（このタイミングでも `getProfil()` や `liff.getDecodedIDToken()` でユーザー情報を取得できるが、偽装されている可能性があり信用できないので、サーバーには送ってはいけない）
3. `getIDToken()` で、トークンを取得する。
4. 取得したトークンをPHP側に送信
5. PHP側で受け取ったトークンと、チャネルIDの2つをパラメータに含めて、`https://api.line.me/oauth2/v2.1/verify` にPOSTリクエストする。
6. トークンに基づくユーザー情報がレスポンスされる。（このユーザー情報は信用して良い）

リクエストとレスポンスの詳細は下記に記載。

[LINEログイン v2.1 APIリファレンス](https://developers.line.biz/ja/reference/line-login/#verify-id-token)

## 実装

### JavaScript

JavascriptでIDTokenを受け取って、PHPにIDTokenを渡す。

```
<script src="<https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js>"></script>
  <script charset="utf-8" src="<https://static.line-scdn.net/liff/edge/2/sdk.js>"></script>
  <script>
    liff
    .init({
        liffId: '***************'
    })
    .then(() => {
      if (!liff.isLoggedIn()) {
      liff.login()
      }
      const idToken = liff.getIDToken();
      console.log(idToken);
    })
  </script>
```
ページを読み込む順番は、PHP→HTMLなので、デバッグの時は注意！

PHPはHTMLを描画するためのものなので。

## PHP

getIDToken() でトークンを取得して、そのトークンとチャネルIDの2つをリクエストボディに含んでLINE APIにPOSTリクエストすると、そのユーザー情報がレスポンスされる。

http_build_query($params) これを忘れずに！

params のままリクエストしてもエラーになる。

```
$line_id_token = filter_input(INPUT_POST, 'id_token');

      // line_id_tokenをもとにユーザー情報をLINEプラットフォームから取得
      $base_url = '<https://api.line.me/oauth2/v2.1/verify>';
      $params = [
        'id_token' => $line_id_token,
        'client_id' => $_ENV['LINE_CLIENT_ID']
      ];
      $query_params = http_build_query($params);

      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $base_url);
      curl_setopt($ch, CURLOPT_POST, TRUE);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $query_params);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
      $return_value = curl_exec($ch);
      curl_close($ch);

      $ary_return_value = json_decode($return_value, true);
      $line_id = $ary_return_value['sub'];
```

## 注意

[LIFF v2 APIリファレンス](https://developers.line.biz/ja/reference/liff/#get-decoded-id-token)

`liff.getDecodedIDToken()`


> ユーザー情報をサーバーに送信しないでください このAPIで取得したユーザー情報をサーバーに送信しないでください。 サーバーでユーザー情報を使用する方法について詳しくは、『LIFFドキュメント』の「LIFFアプリおよびサーバーでユーザー情報を使用する」を参照してください。

上記のように注意書きされているが、`liff.getDecodedIDToken()` でユーザー情報を取得すると、その情報が偽装されている可能性があるため。

`getIDToken` で習得したトークンを検証して取得したユーザー情報が正しいので、サーバーに保存するならこっちを使うようにする。
【PHP】PHPのパスがデフォルトのmacになっているのでMAMPのPHPに通す

## エラー
`php -v` したら、

```
WARNING: PHP is not recommended
PHP is included in macOS for compatibility with legacy software.
Future versions of macOS will not include PHP.
PHP 7.3.24-(to be removed in future macOS) (cli) (built: Feb 28 2021 09:53:14) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.24, Copyright (c) 1998-2018 Zend Technologies
```
いろいろググったら、 7.3.24 は今後使えなくなるからバージョンアップデートしてね、という意味らしい。

## 結論
PHPのパスがmacのデフォルトのファイルを読みに行ってしまっていたので、MAMPのファイルを読むようにパスを変更する。

### 試したこと
MAMPで開発していたので、 `/Applications/MAMP/bin/php` のバージョンが書かれたファイル名に `_` アンダーバーのプレフィックスをつけて無効化させた。

MAMPの設定から、 PHP7.4.16 を有効化し、再起動！

したが、 `php -v` しても同じエラー。

さらに色々調べてみると、macにはそもそも元から入ってるPHPの設定があり、そのファイルを読み込んでしまっているとのこと。

```
$ which php
/usr/bin/php
```

こうなったらMAMPではなく、デフォルトのファイルを読み込んでいるようだ。

なので、MAMPのPHPを読み込むようにパスを通す必要がある。

自分は zsh 環境だったので、下記でパスを記入するファイルを開く。（bashの人は別コマンド）

```
% open ~/.zshrc
```
これでファイルを開いて、下記のパスを追記。

```
export PATH=/Applications/MAMP/bin/php/php7.4.16/bin:$PATH
```
そして追記した内容を反映させる。

```
source ~/.zshrc
```

これでターミナルを再起動すると反映されている。

```
% php -v         

PHP 7.4.16 (cli) (built: Mar 16 2021 12:33:53) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies

% which php      
/Applications/MAMP/bin/php/php7.4.16/bin/php
```

## 参考
[macOSでプログラミングの勉強をしているものです。 – PCにphp7.4を、ターミナルからHomebrew経由でインストール… – Yahoo!知恵袋](https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q14242920674)


[PHPのパスを通す – MAMPの使い方 – PHP入門 – Webkaru](https://webkaru.net/php/mamp-php-bin-path/)

[【zsh, bash】macでPATHを通す方法 – not found と出てしまったら](https://qiita.com/k3ntar0/items/eb8cdbd8eba9da388def)

[MAMPで、任意のPHPバージョンを切り替えできるようにする。画像付き](https://qiita.com/lv-kit/items/ead3a82ee487f4fecad5)
【PHP】日本語を含むCSVファイルの文字化け対策

お客さんに、CSVファイルを送付したかった。

ファイル数が多かったので、圧縮したZIPフォルダにまとめて送りたかったが、ファイル内の日本語部分が文字化けしてしまった。

その対策に手間取ったので備忘録。

## 作業環境

- PHP 7.4
- MAMP

## ファイルの中身

### 結論

`mb_convert_variables` を使ってCSVファイルに出力する。

### 課題

上記の環境で作成したPHPファイルに、データベースから必要なデータを抽出し、CSVファイルに落とし込む処理を実装した。

これによって作成されたCSVファイルをmac上で開くと問題なく表示される。

しかし、それをWindowsに送ってWindowsで開くと、日本語部分（正確には全角箇所？）が文字化けしてしまった。

### 原因

macでは文字コードがUTF-8となっており、WindowsではSJISとなっているためらしい。

macで作成したCSVファイルはそのままだとUTF-8となっているため、それをWindowsで開くとUTF-8に対応していないから日本語は文字化けしてしまうということ。

### 対策

`mb_convert_variables` を使った。

CSVファイルに出力する際に、SJISで出力するようにした。

macでSJISでCSVを出力し、それをWindowsに送れば、Windowsでも問題なく日本語部分も表示された。

また、mac上でもSJISで出力したCSVファイルを問題なく表示させることができた。

```php
// 文字コードShift-JISに変換して出力させる（Windowsでの文字化け対策）
mb_convert_variables('SJIS', 'UTF8', $data_ary, $csv_head);

$file          = new SplFileObject(__DIR__ . "/./csv/$filename", 'w');

// CSVファイルに書き込む
$file->fputcsv($csv_head);
foreach ($data_ary as $line) {
  $file->fputcsv($line);
}
```

**参考**

[PHP DBからCSV出力する際の文字化けにつきまして｜teratail](https://teratail.com/questions/111170)
【PHP】小数点以下の桁数を指定して切り捨てる方法（floor、number_format、sprintf）

## やりたいこと

ある桁数以下は切り捨てて、それより上の桁数で値を返したかった。

例えば、小数点第三位以下は切り捨てて、小数点第二位までを返したい。

## 課題

ググると、 `floor` と  `sprintf` と `number_format` があるよう。

`floor` だと小数点以下の切り捨てて整数にするのみで、桁数の指定はできない。

 `sprintf` と `number_format` なら、小数点以下の桁数を指定するだけなので簡単そう！と思ったが、よくよく調べてみると、なぜか四捨五入されてしまうらしい。

## 結論

結局、完全な切り捨てを行いたいなら `floor` を工夫して使うのが良さそうという結論に辿り着いた。


イメージは、元の数値を割って小数点の位置を左にズラしてから `floor` で小数点以下を切り捨てて、割った数分、倍にすれば良い。
例えば、小数第三位以下は切り捨てたいなら、ある数値を100倍してから`floor`で切り捨てて、その後で100で割って、小数第二位までに戻す。


- もし元の数字の小数点以下の桁数が動的であれば、その数字に `number_format` か `sprintf` で桁数を揃えてから（小数点第二位までにしたければ、第三位までを一旦揃える）、 `floor` を使って切り捨てれば良さそう。

- もし元の数字の小数点以下の桁数が動的であれば、その数字に `number_format` か `sprintf` で桁数を揃えてから、 `floor` を使って切り捨てれば良さそう。

下記のサンプルコードは下記サイトから引用。

[PHP 四捨五入/切り上げ/切り捨てのサンプル](https://itsakura.com/php-round)

```php
<?php

// 小数第一位で切り捨て
print floor(2.26);  // 2

// 小数第一位で切り捨て
print floor(2.82);  // 2

// マイナスの場合
print floor(-2.82); // -3

// 小数第二位で切り捨て
print (floor(12.262 * 10) / 10); // 12.2

// 小数第三位で切り捨て
print (floor(12.262 * 100) / 100); // 12.26

// 1の位で切り捨て
print (floor(122.2 / 10) * 10); // 120

// 10の位で切り捨て
print (floor(122.2 / 100) * 100); // 100

?>
```

> 小数第二位で切り捨てています。以下の操作です。
１．対象の値に10を掛けます。(12.262 * 10 = 122.62)
２．floorメソッドを使用して小数点以下を切り捨てます。(122.62 → 122)
３．対象の値を再度10で割ります。(122 / 10 = 12.2)
> 

## number_formatとsprintfの挙動

### number_format

[number_format](https://www.php.net/manual/ja/function.number-format)

```php
number_format(
    float $num,
    int $decimals = 0,
    ?string $decimal_separator = ".",
    ?string $thousands_separator = ","
): string
```

**`num`**

フォーマットする数値。

**`decimals`**

小数点以下の桁数。 `0` を指定すると、 返り値の **`decimal_separator`** は省略されます。

**`decimal_separator`**

小数点を表す区切り文字。

**`thousands_separator`**

千の位毎の区切り文字。

```php
$number = 1234.56;

// 英語での表記 (デフォルト)
$english_format_number = number_format($number);
// 1,235

$number = 1234.5678;

// 千位毎の区切りがない英語での表記
$english_format_number = number_format($number, 2, '.', '');
// 1234.57　　// ここ四捨五入されてる
```

[PHPで小数点以下を２桁とか３桁に切り捨てる２つの方法 | PisukeCode - Web開発まとめ](https://pisuke-code.com/php-ways-to-trunc-decimal-points/)

なぜか四捨五入されてる。切り捨てたい。

### sprintf

[【PHP】小数点以下まで出力する - Qiita](https://qiita.com/kazu56/items/b290f1a314b74d459464)

> sprintfとprintfの違いは、sprintfはフォーマットされた値が戻り値。printfはフォーマットされた値を標準出力する。
> 

```php
$num1 = 10;
$num2 = 1000.5;
$num3 = 0.456789;

echo sprintf('%.2f',$num1),PHP_EOL;
echo sprintf('%.2f',$num2),PHP_EOL;
echo sprintf('%.2f',$num3),PHP_EOL;
/*
10.00
1000.50
0.46    // ここがなぜか四捨五入されてる。
*/
```

なぜか四捨五入されてしまう場合もあるので、切り捨て対時は使えなさそう。
封筒の問題（変形版）

## 確率の問題

1または２が出るまでサイコロを振る。振った回数が$N$になったら1つの封筒に$2^N$の数字を、1つの封筒に$2^{N-1}$の数字を書いて入れる。
なお、回数が$N$になる確率は、$\frac{1}{3}(\frac{2}{3})^{N-1}$である。

もし、無作為に封筒を１つ選び開封して数字は2であった場合、次のことが言える。回数が１の確率は$\frac{1}{3}$で、回数が2の確率は$\frac{2}{9}$であるから、もう1つの封筒の数字が1である確率は$\frac{3}{5}$で、数字が4である確率は$\frac{2}{5}$である。したがって、交換したときの期待値は2.2となり交換した方が数字が大きくなる。

上記のルールで用意した２つの未開封の封筒があり、無作為に１つの封筒を選んだ。このとき、以下について答えよ。

- **問1**：未開封の場合、交換しても期待値は変わらないか？
- **問2**：開封して数字を確認した場合、（その数字を前提として）交換すると期待値は増えるか？ただし、開封した数字は２以上とする。
- **問3**：未開封のまま中味について、１つの数字$x$と仮定する。このとき、交換すると期待値は増えるか？ただし、$x$は２以上とする。

## 解答

### 問１

答えはYes。

２つの数字を$2x$と$x$とする。無作為に選んでいるので各々を選ぶ確率は$\frac{1}{2}$である。したがって、交換した場合の差分の期待値は、$\frac{x}{2}-\frac{x}{2} = 0$である。

### 問２

答えはYes。

開封して確認した数字を$x$とする。
もう１つの数字が$\frac{x}{2}$か$2x$かの確率の比は、$3:2$であり、交換した場合の差分の期待値は、$\frac{x}{10}$である。

### 問3

答えはYes。

たとえ未開封であっても、数字を１つ仮定した状況では、問２と同じ計算をすることができる。
開封したかどうかは関係なく、１つの数字を仮定したかどうかに意味がある。

## 考察

問１は数字を1つだけと仮定していない。問１と問３は同じ問いではないので、答えが違っても矛盾はない。

$N$ | そのときの確率 | 片方の数字 | もう片方の数字 |
| :-- | :-- | :-- | :-- |
１ | $\frac{1}{3}$ | 1 | 2 |
2 | $\frac{2}{9}$ | 2 | 4 |
3 | $\frac{4}{27}$ | 4 | 8 |
... | ... | ... | ... |
$n$ | $\frac{1}{3}(\frac{2}{3})^{n-1}$ | $2^{n-1}$ | $2^n$ |
$n + 1$ | $\frac{1}{3}(\frac{2}{3})^{n}$ | $2^{n}$ | $2^{n+1}$ |
... | ... | ... | ... |

まず、上記の表の各行で考えた場合、無作為に数字を選べば交換しても期待値は変わらない。どの行でも変わらないのだから行が無数にあっても変わらないと言える。

さて、開封した数字を$2^n$としよう。もう片方の数字が、$2^{n-1}$と$2^{n+1}$になるのは、$3 : 2$であるから、交換したときの期待値は0より大きい。

未開封の場合に、選んだ数字を$2^i$としよう。これを$i = 1 ... n$まで見れば、どの$i$についても期待値は交換した方が大きい。しかし、計算を省略した$i > n$についての分の期待値は、無視できない値となる。$i$をいくつまで計算しても、$i$を計算した範囲以外が無視できない誤差となって残る。したがって、任意の$i$で「交換した方がよい」と言えても、すべてを対象とするならば「交換してもしなくても同じ」と考えるべきである。

## 余談その１

問題を少し変えて、２人がそれぞれ封筒を受け取るとしよう。
１人が数字を仮定した状況は、問２（および問３）と同じである。
２人がそれぞれ数字を1つ仮定すると、どちらかが大きいかが決まることになる。したがって、交換するとどちらかが得をしどちらかが損をする。

## 余談その２

未開封の封筒が２つある状況を考える。このあと「無作為に封筒を１つ選んで開封して数字を確認し、その後封筒を交換する」つもりだとしよう。この場合、封筒を交換しても数字の期待値は変わらない。「任意の数字に対して交換する」ことが前提だからだ。
交換して期待値が増えるのは、「１つの数字を仮定する」という前提があって成り立つ。未開封の状態では、確認する数字は任意の可能性がある。その中の１つの数字の場合の可能性だけ取り出して期待値を求めれば、交換して期待値は増える。
最初の状況に対してこのことを言い直すと次のようになる。`無作為に封筒を１つ選び開封する。開封した数字をXとする。このとき、封筒を交換すると期待値は、Xより増える`[^1]。

[^1]: これが言えるのは、期待値を計算できるからだ。参考の「確率の問題 - お題その2（2つの封筒の問題）」は、「１つの数字を仮定」しても期待値は計算できないことに注意。

## 余談その3

数値は、Pythonの数値の範囲に収まると仮定しよう。
サイコロをシミュレートする場合、Pythonのintは4300桁までなので、サイコロを振る回数が4301桁になったところでValueErrorになる。さて実際に4301桁までシミュレートするのは膨大な時間がかかるので、確率から計算することにしよう。floatは5e-324まで表現できる。Nが1836のときに5e-324になり、1837では0となり1837以降は実現しないことになる。
すなわち、$x$が`2**1836`の場合、交換すると必ず`2**1835`に減ることになる。

## 参考

- [確率の問題 - お題その2（2つの封筒の問題）](https://qiita.com/SaitoTsutomu/items/9784e4451d6d510dbfbb#mag-%E3%81%8A%E9%A1%8C%E3%81%9D%E3%81%AE22%E3%81%A4%E3%81%AE%E5%B0%81%E7%AD%92%E3%81%AE%E5%95%8F%E9%A1%8C)
- [2つの封筒問題のパラドックス](https://qiita.com/SaitoTsutomu/items/63885dec1cca99acb7bb)

※ 本記事への誤ったコメントは、削除依頼をして無視します。

以上
CDNを何も知らない人に朗報です（後編）

この記事は[CDNを何も知らない人に朗報です（前編）](https://qiita.com/n-yama/items/daef395fed7d6e420a90) の後編の記事になります。

#キャッシュファイルの設定

CDNを利用することでオリジンサーバーへのアクセスをキャッシュサーバーが肩代わりして負荷を軽減するお話をしましたが、どうやってキャッシュサーバーからコンテンツを配信できるようになるのでしょうか。
本来オリジンサーバーへのアクセスであれば以下のようになると思います。

![(後編)CDN_DNS01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1237088/2f2e56e0-8a2b-b644-74c7-a6c8d1ba04f6.png)


キャッシュサーバーがコンテンツを配信する場合は以下のような感じになると思います。

![(後編)CDN_DNS02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1237088/505d2378-e2b4-e208-b7d6-81e59c14b934.png)


ここで関係してくるのは、**DNSの設定**になります（DNSについては[こちら](https://www.ntt.com/bizon/glossary/e-d/dns-server.html)を参照）。
##DNSの設定

先ほどの図にDNSの設定を追記します。

![(後編)CDN_DNS_Cname01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1237088/455ec829-78b3-746a-e5f5-f609db39c9d7.png)


いきなり出てきた**CNAMEレコード**ですが、例を出して解説します。
また、説明に際して必要な**Aレコード**についても加えて説明します。

**例: www.originexample.jp（オリジンサーバー）とcache.example.com（キャッシュサーバー）というドメインがあるとする。**

```DNSのAレコードの設定.
 www.originexample.jp IN A 192.168.0.1
```

**AレコードはドメインをIPアドレスに置き換えるレコード**です。
したがって上記のAレコードの設定は「 www.originexample.jp のIPアドレスは192.168.0.1です」という意味になります。
 →www.originexample.jpのコンテンツを取得する場合、IPアドレス「192.168.0.1」にアクセスしてコンテンツを取得します。

```DNSのCNAMEレコードの設定.
 www.originexample.jp IN CNAME cache.example.com
```

**CNAMEレコードはドメインを別のドメインに置き換えるレコード**です。
したがって上記のCNAMEレコードの設定は「www.originexample.jpのドメインを、cache.example.comというキャッシュサーバーのドメインに転送します」という意味になります。
→ www.originexample.jp のコンテンツをcache.example.com（キャッシュサーバー）から取得できます。


ここまでの解説を踏まえてwww.originexample.jpのサイトのページを見たい場合、CDNを経由して表示する流れは以下の図のようになります。
（見たいページはoriginIndex.htmlとします）

![(後編)CDN一連の流れ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1237088/bd8c661f-92f0-c43d-2857-79ea1cd95f38.png)




（補足: 上図の②なのですが、キャッシュデータを保持している時間はキャッシュ時間の設定により異なります。
例えばキャッシュ時間1hなら、キャッシュして1hの間はファイルデータをオリジンに取得しに行かず、キャッシュデータを返します。
[CDNを何も知らない人に朗報です（前編）](https://qiita.com/n-yama/items/daef395fed7d6e420a90)で説明しましたが、キャッシュサーバーの保持しているデータが古くなり、そのデータをユーザーに返してしまうことがあります。）

CNAMEレコードの設定によりキャッシュサーバーからファイルデータを返すというのは分かりましたが、
ここで気になるのは**「どんなファイルでもキャッシュするのか」**ということです。

##キャッシュファイルの選定

「キャッシュすればオリジンサーバーの負荷が軽減されるし高速表示されるなら全部キャッシュすればいいじゃん」、というわけではなく、どのファイルをキャッシュすべきか・どのファイルをキャッシュすべきでないか選定する必要があります。
キャッシュの対象となるファイルとキャッシュすべきでないファイルについて説明します。

###キャッシュ対象となるファイル

キャッシュすべきファイルは個人情報を扱わないファイル、かつ帯域を使いすぎている動画などのメディアファイルといったファイルが選定の対象となります。
もし、個人情報を扱わないサイトならばすべてのファイルをキャッシュする設定でもよいかと思います（その分費用はかかりますが）。


###キャッシュしてはいけないファイル

前述していますが、個人情報を扱うファイルになります。
例えば、会員情報などを保有しているサイトで自分の登録情報を閲覧した人がいたとします。そのHTMLがキャッシュされて他の方に配信されて閲覧されてしまうと個人情報が漏洩してしまいます（これを**「キャッシュ事故」**と呼びます）。


#おまけ: CDNサービスを提供している会社
他にもあると思いますが、気になったら調べてみてください。


[Amazon CloudFront](https://aws.amazon.com/jp/cloudfront/) 



[さくらのCDNウェブアクセラレータ](https://www.sakura.ad.jp/services/cdn/)
名前空間ってなんなんだろう？

##名前空間とは

名前空間は、クラスや関数の **`所属`** のようなもの。
例えば同じ会社に田中太郎さんが３人いたとします（奇跡的に）
この場合、社内で「田中太郎さーん」と呼んでも、どの田中太郎さんを呼んでいるのかわかりません。
ところが
「システム開発部の田中太郎さーん」
「企画部の田中太郎さーん」
「営業部の田中太郎さーん」
と呼んでやれば、どの田中太郎さんか区別できます。

どこに**所属**している〜〜さん。のようなくくりで表現するのが名前空間です。

***

会社の例をプログラムに置き換えて考えてみます。

名前空間を使用することのメリットは、PHPマニュアルに記載されている下記のような問題を解決することです。
https://www.php.net/manual/ja/language.namespaces.rationale.php
>1. あなたが作成したコードと PHP の組み込みのクラス/関数/定数 あるいはサードパーティのクラス/関数/定数の名前が衝突する

>2. 最初の問題を解決するためには、Extra_Long_Names のような長い名前をつけなければならない

名前空間を定義するには `namespace 名前空間名` のように指定します。

```php:MyClass.php
<?php
namespace App\Sample

class MyClass {
    
}
```
このようにファイルの先頭で名前空間を定義します。
\で階層を区切り、名前空間の階層はプロジェクトのフォルダの構造に合わせます。
構文規則ではないですが分かりやすいですし、オートロードでも利用できるのでフォルダー構造に従って定義するというルールを決めておいた方が良いかもしれません。
(オートロードについては[こちら](https://qiita.com/turituri08/items/08ee9a15b8cada9f6ba6)を参考にしてみてください)


##名前空間の定義

名前空間には、**非修飾名**、**修飾名**、**完全修飾名**という記法があります。
https://www.php.net/manual/ja/language.namespaces.rules.php

・非修飾名は名前空間を含まない名前のことで、グローバル名前空間に属します。
　名前空間を使用しない状態（通常時）での指定の仕方です。
`MyClass`

・修飾名は現在の名前空間を基点として\で区切ります。相対パスのようなものです。
`Sample\MyClass`

・完全修飾名は\で始まる名前空間のことです。絶対パスのようなものです。
`\App\Sample\MyClass`

あるクラスの名前空間の中からグローバル名前空間のクラスを使用したい場合
`new \SplFileInfo` や `throw new \RuntimeException` のように**先頭に \ をつけて完全修飾名で指定する必要**があります。


##名前空間の利用

名前空間を利用すると、クラスを呼び出す時には「Sample\Hoge\Myclass」のように指定しなければいけません。
このように長い名前を呼び出す度に毎回記述するのは面倒なので**use命令**でインポートします。

useでインポートした後は本来のクラス名だけでクラスを使用できます。

```index.php
<?php

require_once './sample/MyClass.php';

use App\Sample\MyClass;

$class = new MyClass();
```

また、名前空間を分けていても名前が衝突してしまうような場合は**as**で別名をつけます

```index.php
<?php

require_once './hoge/MyClass.php';
require_once './foo/Myclass.php';

use App\Hoge\MyClass as HogeClass;
use App\Foo\MyClass as FooClass;

$classHoge = new HogeClass();
$classFoo = new FooClass();
```

このように名前空間を定義することで、PHPの組み込みクラスやサードパーティのクラスとの名前の衝突を回避することができ、composerを利用したオートロードなども実装しやすくなります。

###まとめ

名前空間を簡単に一言でまとめると
**「名前が被らないように、フォルダー構造に従って名前をつけて、一意な修飾名として使えるようにする機能のこと」**でしょうか。
【Javascript】分割代入の方法まとめ

#Javascriptの分割代入とは

分割代入とは、配列の値やオブジェクトのプロパティを取り出して、個別の変数に代入する構文。

###配列の分割代入の構文
```javascript
{const|let|var} [代入先変数1,代入先変数2] = 代入元配列
```
###配列の分割代入の具体例

```javascript
const arr = [ruby,Javascript, python];
const [d1, d2, d3] = arr;
console.log(d1); // -> ruby
console.log(d2); // -> Javascript
console.log(d3); // -> python
```
上記のように、代入元配列の添字と新たに格納する変数の添字とリンクしていて、左辺の変数に格納されます。一方、分割代入で記述しない場合、以下になります。
記述量が減るため、バグの減少や可読性の向上に繋がります。

```javascript
const arr = ['ruby','Javascript', 'python'];
const d1 = arr[0]
const d2 = arr[1]
const d3 = arr[2]
console.log(d1); // -> ruby
console.log(d2); // -> Javascript
console.log(d3); // -> python
```
###オブジェクトの分割代入の構文
```javascript
[代入先変数1,代入先変数2]＝代入元オブジェクト 
```
###オブジェクトの分割代入の具体例
```javascript
const user1 = {id:1,name:'田中',age:24}
const {id,name,age} = user1
console.log(id); // -> 1
console.log(name); // -> 田中
console.log(age); // -> 24
```
上記の注意点として、代入元オブジェクトのプロパティ名と代入先の変数名が一致しなければ、値が格納されません。
AOJトライに関する知識知見の記録共有：Volume0-0056

# タスク概要

Goldbach's Conjecture

https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0056

 
# コード実装例
TIPS

1. 例外処理含む評価パターンを追加


```Python
import pprint, sys, time
import sympy

def core(arg, adv=True, n_eg=3):
    n = arg
    if adv:
        ip = [[i, n - i] for i in sympy.primerange(2, int(n/2) + 1) if sympy.isprime(n - i)]
    else:
        ip = [[i, n - i] for i in range(n) if sympy.isprime(i) and sympy.isprime(n - i) and i <= n - i]
    ret = [len(ip), ip[:n_eg]]
    return ret

def app(*args):
    ret = []
    for arg in args:
        s = []
        for adv in [False, True]:
            st = time.time()
            try:
                r = core(arg, adv=adv)
            except Exception as e:
                r = e
            s.append([adv, round(time.time() - st, 6), r])
        ret.append([arg, s])
    return ret

print(sys.version)
pprint.pprint(app(
    # basic examples
    10,
    11,
    # additional examples
    50000,
    50001,
    # exceptional examples
    "例外入力"
))
```

# 実行結果
各種補足情報出力（入力値FB、デバッグログ等）含む

```Python
3.8.10 (default, Sep 28 2021, 16:10:42) 
[GCC 9.3.0]
[[10,
  [[False, 1.9e-05, [2, [[3, 7], [5, 5]]]],
   [True, 0.000298, [2, [[3, 7], [5, 5]]]]]],
 [11, [[False, 1e-05, [0, []]], [True, 1.2e-05, [0, []]]]],
 [50000,
  [[False, 0.059492, [450, [[7, 49993], [43, 49957], [61, 49939]]]],
   [True, 0.018928, [450, [[7, 49993], [43, 49957], [61, 49939]]]]]],
 [50001,
  [[False, 0.05523, [1, [[2, 49999]]]], [True, 0.014913, [1, [[2, 49999]]]]]],
 ['例外入力',
  [[False,
    5e-06,
    TypeError("'str' object cannot be interpreted as an integer")],
   [True,
    4e-06,
    TypeError("unsupported operand type(s) for /: 'str' and 'int'")]]]]
```

# 補遺
残課題（Pull Request絶賛募集中！）

1. 実行速度の改善（枝刈り、データ構造の見直し等）
GR-ROSEでROS2 2021年12月末版

# はじめに

残念ながら、GR-ROSEの開発環境であるIDE for GRの最新版は2020年9月のVersion 1.13であり、1年ほど更新されていない状態です。

そのため、GR-ROSEのROS2ライブラリはDashing対応で止まっている状態です。

一方、ROS2のバージョンリリースについてはREP 2000で決められており、2021年12月時点での現行バージョンはFoxyとなっています。

- [REP 2000 -- ROS 2 Releases and Target Platforms](https://www.ros.org/reps/rep-2000.html)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51272/f7afce11-6292-7403-b1ae-16840ac9540e.png)

マイクロコントローラ向けのROS2環境は、現在はmicro-ROSが主流となりつつあります。

- https://micro.ros.org/

ルネサスさんもmicro-ROSへの対応をすすめているようですが、ターゲットのマイクロコントローラがGR-ROSEに搭載されているRX65Nではなく、ARM系のRA6M5になっています。

- [micro-ROSソリューション](https://www.renesas.com/jp/ja/application/technologies/robotics/robot-operating-system/micro-ros-solutions)

RA6M5ベースのがじぇるねボードが出てくること期待しています。

それまでは、このGR-ROSEをROS2で使っていきたいと思いますので、今回は現在（2021年12月末）もGR-ROSEのROS2環境を構築可能かを確認してみます。

# ROS2およびAgent環境の構築

GR-ROSEのROS2バージョンはDashingであるため、GR-ROSEのROS2ライブラリとやりとりをするAgentおよびROS2ノードが動作するOS(Ubuntuを想定)のバージョンは、18.04LTSになるかと思います。

幸い、Ubuntu 18.04LTSのEOLは2023年4月であり、今回使用するシングルボードコンピュータのJetson NanoのJetpack SDKですが、最新版であるJetPack 4.6はUbuntu 18.04ベースなので問題ありませんでした。

- [JETPACK SDK](https://developer.nvidia.com/embedded/jetpack)

JetPack 4.6を導入後、公式にあるDashingのインストール手順でROS2をインストールします。

- [Installation — ROS 2 Documentation: Dashing documentation](https://docs.ros.org/en/dashing/Installation.html)

次に、Agent側の準備ですが、GitHubのReadmeにリンクがかかれているInstallation manualに従って導入します。

- [eProsima/Micro-XRCE-DDS-Agent - GitHub](https://github.com/eProsima/Micro-XRCE-DDS-Agent)

ただし、GR-ROSEが対応しているバージョンは1.1.6のため

```
~/Micro-XRCE-DDS-Agent/$ git checkout 1.1.6
```

をした後にmake&ビルド、インストールします。

# 動作確認

動作確認は、IDE for GR 1.13のudp_talker_besteffortを使用しました。

- [IDE for Gadget Renesas](https://www.renesas.com/jp/ja/products/gadget-renesas/ide-gr)

サンプルプログラム udp_talker_besteffortのネットワークの設定のみを環境にあわせて設定し、ビルド＆書き込みします。

その後、Jetson Nano側でAgentを起動します。

```
~/Micro-XRCE-DDS-Agent/build$ ./MicroXRCEAgent udp --port 2020 -d
```

ポートは空いていればなんでもよいです。最後の、`-d`は重要で、Discovery serverを有効化します。GR-ROSEのROS2ライブラリは、Discovery（Agentの自動発見）が前提のためこの指定が必要です。

シリアルモニタを開いてログをみると、うまく動作している場合は以下のような出力になるかと思います。

```
Discovery Agent...
Found agent => ip: xxx.xxx.xxx.xxx, port: 2020
Chosen agent => ip: xxx.xxx.xxx.xxx, port: 2020
Sent topic: Hello DDS world!: 0
Sent topic: Hello DDS world!: 1
Sent topic: Hello DDS world!: 2
・・・
```

Jetson NanoにAgentを動作させているのと別のコンソールを開いて、

```
~$ ros2 topic echo chatter
```

とすれば、GR-ROSEからメッセージが送られてきているのが確認できます。

注意：別のコンソールを開いた際は、以下のROS2環境の有効化を忘れずに：）

```
$ source /opt/ros/dashing/setup.bash
```

# 最後に

既にGR-ROSEのROS2ライブラリが対応するバージョンの開発は終了しましたが、まだ使用することは可能です。

今後は、micro-ROSに開発環境は移っていくかと思いますが、現状、micro-ROSを利用するにはLinuxの環境が必要になると思います。手軽にROS2を試すという点で、GR-ROSEのROS2ライブラリを使うのはありかもしれません。
Kubernetes を今一度言語化し理解を深める#1

#はじめに
本記事は 抽象度の高い Kubernetes のアーキテクチャを言語化し理解する目的で作成しました。

#1.概要
Kubernetes はコンテナ( Docker )のデプロイやスケジューリングを自動化するためにグーグルが作成した **オーケストレーションエンジン**

* オーケストレーションエンジンとは… 
アプリやコンテナを複数のサーバマシンに配置してシステムを自動化する仕組み

* Kubernetesとは…
Docker や containerd などコンテナランタイムのオーケストレーションエンジンのデファクトスタンダードと言われ、次世代の Linux になるといわれている技術

:::note info  
Kubernetes のリソースは
ホストとなるサーバマシンや仮想マシンのことを Node
複数のコンテナを Pod として管理する

::: 
イメージ図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/612700/c75df79c-f453-d070-15f3-52aec20666fc.png)


###1-1. Kubernetes の機能
Kubernetes を使用することで以下のような機能が使用できる。

* **Infrastructure as Code(IaC)**
* コンテナのデプロイ、スケジューリング
* コンテナの死活管理
* 障害時の自動復旧
*  複数サーバー(Node)でのコンテナ管理
*  コンテナの負荷分散
*  負荷によってコンテナの数を調節するオートスケーリング
*  コンテナのリソース管理
…etc

特に重要なのが 宣言的設定(IaC)
Kubernetes における宣言的設定とは、 YAML 形式や JSON 形式で記述したコードによって「システムのあるべき姿」を定義するもの。

これによって、通常障害が起きると運用担当のエンジニアが手動で復旧作業を行い復旧するが、Kubernetes では IaC によって例えば Pod を常に 3 つ起動させておきたいといった場合には「ReplicaSet」 を使用することである 1 つの Pod が障害でダウンして 2 つになった際も自動的に Pod が作成され 3 つになるように自動復旧する。


■参考資料
<Kubernetesが必要な理由と提供する機能>

https://kubernetes.io/ja/docs/concepts/overview/what-is-kubernetes/#why-you-need-kubernetes-and-what-can-it-do

障害時の動作について以下のブログを参考にさせていただきました。

https://dr-asa.hatenablog.com/entry/2018/04/02/174006


### 1-2.  Kubernetes 採用時の注意点
完璧に見える Kubernetes さんだが注意すべきポイントが 4 点ある。

1) Kubernetes そのものが非常に複雑
2) Kubernetes の更新サイクルが早い
3) Kubernetes を運用するために周辺サービスが必要となる場合が多い
4) 大規模でなければコンテナ(Docker)を採用するだけでも十分



#2. Kubernetes のアーキテクチャ
Kubernetes は Master Node (別名:コントロールプレーン)と Worker Node の 2 種類あり
kube-apiserver を中心とした分散システムとなっている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/612700/5aaeb0c7-44ce-071f-f681-160be7bc4455.png)


### 2-1 Master Node
クラスタの制御を受け持つ。 スケジューリングやイベントの検知や対応など、クラスタの全体に関わる決定を行い、クラスタ内のどのノードでも実行可能。

* api (kube-apiserver)
Kubernetes API を提供するフロントエンドのコンポーネント。
ユーザから発行される kubectl コマンドを api に送ることでリソースの作成や削除ができる。その情報は etcd に登録される。

* etcd
全てのクラスタデータ保存用のデータベース。
Key-ValueStore (KVS) 形式で保存される。

* c-m (kube-controller-maneger)
様々なコントローラを実行するコンポーネント。
Deployment コントローラや ReplicaSet コントローラはリソースの状態によって  ReplicaSet や Pod の作成を行う。

* sched (kube-scheduler)
未割当状態の Pod をどのノードに配置されるかを決定する。
デプロイは kubelet が行う。


### 2-2 Worker Node
ノードで実行され、実行中のポッドを維持しコンテナランタイムを提供する。

+ kubelet
Node 上で動作をするコンポーネント。
コンテナランタイムと連携してコンテナのデプロイや停止などを行う。

* kube-proxy
Kubernetes クラスタ内の各ノードで実行されるプロセス
クラスタ内外のネットワーク通信を提供する。

# 最後に
参考資料を元に自分の言葉で言語化することで Kubernetes のアーキテクチャをより理解できた。引き続き勉強の過程でまとめていきたいと思う。

#参考資料
<a href="https://www.amazon.co.jp/dp/4295009792/ref=nosim/?tag=impressjapan-22&linkCode=as1">Kubernetes 完全ガイド 第2版</a> 

<a href="https://www.amazon.co.jp/dp/B07L94XGPY/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1">しくみがわかるKubernetes Azureで動かしながら学ぶコンセプトと実践知識</a>
sortコマンドが大量データで問題が出た話

Linuxサーバにて、NFS領域にあるファイルをsortコマンドでソートしている処理でエラーが発生し、バッチが異常終了。

No space left on device

というエラーが出ている。

ファイルサイズは日増しに増大しているとは言え、NFS領域のサイズを考えれば全然余裕なはずなのに、なぜ・・？？

適当にGoogle先生に聞いてみると、思いもよらぬことが判明。

Linuxの仕様上、sortコマンドは、メモリ上で処理しきれない場合は内部的に一時ファイルを作成して処理していて、その作成場所は/tmp配下とのこと。

というわけで、/tmpだとNFS領域ではなくアプリケーションサーバのローカル領域のため、容量が小さく、容量オーバーしていた。

-Tオプションで、一時ファイルの作成場所を指定できるため、NFS領域を指定して解決。

性能系の問題について、検証環境で同容量のデータを作成しての試験が難しい場合、スケールダウンして試したりとか、机上計算で検証したりするものの、こういう思いも寄らない系はなかなか検証段階で防ぐのが難しいと痛感。
Django REST frameworkのAuthenticationのカスタマイズ

# Authenticationについて
認証を行っている部分。
DRFではデフォルトで以下のパターンが用意されている。

- BasicAuthentication
- TokenAuthentication
- SessionAuthentication
- RemoteUserAuthentication

ただJWT認証などをやりたい場合は自作する or 外部ライブラリを使う必要がある。
今回は自作する場合について述べる。

#　BaseAuthentication
独自の認証方式を実装するには、`BaseAuthentication`をサブクラスにして、 `.authenticate(self, request)`メソッドをオーバーライドする。このメソッドは、認証に成功した場合は `(user, auth)` のタプルを、そうでない場合は`None`を返す。

例外は２種類ある。

- Noneをreturnする: 認証スキップする場合。ログインが必須でないときや他の認証方法も試すときはこっち
- AuthenticationFailed`をraiseする：　直ちにエラーメッセージを返す場合。ログイン必須はこっち


.authenticate_header(self, request) メソッドをオーバーライドすることもできます。実装する場合は、HTTP 401 Unauthorized レスポンスの WWW-Authenticate ヘッダーの値として使用される文字列を返す必要があります。

.authenticate_header() メソッドをオーバーライドしない場合、認証スキームは、認証されていないリクエストのアクセスが拒否されたときに HTTP 403 Forbidden レスポンスを返します。


```auth.py.py

from django.contrib.auth.models import User
from rest_framework import authentication
from rest_framework import exceptions

class ExampleAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        username = request.META.get('HTTP_X_USERNAME')# カスタムリクエストHeaderであるX_USERNAMEから値取得
        if not username:
            return None

        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            raise exceptions.AuthenticationFailed('No such user')

        return (user, None)
```


# 参考文献
- (参考文献)[https://www.django-rest-framework.org/api-guide/authentication/]
ありがとう leaf.el ……

[emacs Advent Calendar 2021](https://qiita.com/advent-calendar/2021/emacs) 18 日目の記事です。 (空いていたので埋めました) 。

新規性は無いですが、 [leaf.el] の話は何回挙がっても良いと思うので書いていきます。

[leaf.el]: https://github.com/conao3/leaf.el
[use-package]: https://github.com/jwiegley/use-package

# Emacs の勢い

最近の Emacs は情報が充実していると感じます。体系的に Emacs に入門するなら [Emacs実践入門][book] がありますし、最新のパッケージを紹介するネット記事もあります:

* [2020年代のEmacs入門](https://emacs-jp.github.io/tips/emacs-in-2020)
  満を持して [leaf] で入門！

* [Emacsの次世代ミニバッファ補完UI](https://blog.tomoya.dev/posts/a-new-wave-has-arrived-at-emacs/)
minad さんのツールが広まって、リッチな UI がお手軽に手に入るようになりましたね。

また leaf は最近出たパッケージなので、『[Emacs leaf 設定][s]』などと検索すればイケた設定がヒットします。最高です。

[s]: https://www.google.com/search?client=firefox-b-d&q=Emacs+leaf+%E8%A8%AD%E5%AE%9A
[book]: https://gihyo.jp/book/2017/978-4-7741-9235-2
[Evil]: https://github.com/emacs-evil/evil

# ありがとう [leaf]

## [use-package](https://github.com/jwiegley/use-package) → [leaf.el][leaf]

[leaf]: https://github.com/conao3/leaf.el

どちらもパッケージ管理の DSL で、概念も構文も似ています。 `use-package` のユーザは 1 日で `leaf` に移行できるでしょう。

僕の移行では、たったこれだけ変えました:

* 一部の `:config` を `:defer-config` に変えました。　leaf では遅延ロードが前提のためです。
* `:map`, `:custom`, `:custom-face` などの構文が `use-package` から変わりました。

[leaf] に移行すると起動が 2 ~ 3 倍速になりました。 Emacs を起動するたびに感謝しています。

## [leaf-manager.el][m]

[leaf-manager.el][m] は、 `leaf-manager` コマンドでパッケージを選択し……

![leaf-manager-select.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594390/664b63e9-4114-0410-4784-905111438602.png)

そのパッケージに関する `leaf` 式をバッファに切り出してくれます (画面右側):

![leaf-manager.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594390/bb4c6639-d457-65cb-1856-7073b70974ef.png)

ここで起こした変更を `C-c C-c` でコミットすれば、 `leaf-manager` 管理下のファイル (画面左側) に反映されます。管理下のファイルをパッケージの DB のようにみなすことができて、お気に入りの機能になりました。

[m]: https://github.com/conao3/leaf-manager.el

# まとめ

最近の Emacs しか知らないですが、最高の環境だなと思います。来年もよろしくお願いします。

> 唯一 [Evil] ユーザ向けの本は無いですが、たぶん Vimmer には必要ない……？
STM32G031でソフトウェアシリアル1000bps(STM32G031J6M6)(USART)2



目的
シリアルのテスト

![o_con179.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/0174c787-1159-c408-075e-9e0bf6f2e207.jpeg)


```c++


//s_ser_g031_2
//1000bps

//文字列の表示 nana_seg
int ns_printf(char *str1)
{

  //文字の中身がゼロか
  while (*str1) {
    
    digitalWrite(PB7, LOW);//Start
    delay(1);

    for (int ii = 0; ii < 8; ii++) { //Data x 8
      digitalWrite(PB7, ( (*str1) >> ii) & 1  );
      delay(1);
    }//for

    digitalWrite(PB7, HIGH);//Stop
    delay(1);

    str1++;//ポインターをカウントアップ

  } //while

  //戻り値
  return (0);

}//ns_printf


//初期化
void setup() {

  //仮想シリアルの出力ポートの初期化
  pinMode(PB7, OUTPUT);
  digitalWrite(PB7, HIGH);

}//setup


//無限ループ
void loop() {

  //一文字出力
  //pc_putc('A');

  //I2Cに文字列を出力
  ns_printf("hello world\r\n");

  //1秒の待ち
  delay(1000);

}//loop



```
Django REST frameworkのTokenAuthenticationについて

# TokenAuthenticationの仕組み
各ユーザごとにTokenという文字列を持っておく。
ログインエンドポイントが叩かれると、userIDとpasswordを認証してTokenを返す。
次回以降、フロントエンドはHeaderにToken情報を持たしてエンドポイントを叩く、
バックエンドはそのTokenを毎回確認することで認証ができる。

似たものにJWT認証がある。JWT認証と異なり、TokenはDBに直接格納されており、毎回参照して認証を行う。

# コード
authtokenを追加する

```settings.py
 INSTALLED_APPS = [
     ...
     'rest_framework.authtoken'
 ]
```

Authentication_Classesを定義する。

```settings.py
 REST_FRAMEWORK = {
     'DEFAULT_AUTHENTICATION_CLASSES': [
         'rest_framework.authentication.BasicAuthentication',
         'rest_framework.authentication.SessionAuthentication',
     ]
 }
```

Userのpost_saveシグナルをキャッチしてTokenを追加する
models.pyに追加しておく。

```models.py
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
]
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
    if created:
        Token.objects.create(user=instance)
```


ログイン時のToken発行Viewはデフォルトのものを使える。
obtain_auth_tokenはusernameとpasswordを受け取って`{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }`
をレスポンスで返す。

```urls.py
from rest_framework.authtoken import views
urlpatterns += [
    path('api-token-auth/', views.obtain_auth_token)
]
```

カスタムすることも可能

```auth.py
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response

class CustomAuthToken(ObtainAuthToken):

    def post(self, request, *args, **kwargs):
        serializer = self.serializer_class(data=request.data,
                                           context={'request': request})
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data['user']
        token, created = Token.objects.get_or_create(user=user)
        return Response({
            'token': token.key,
            'user_id': user.pk,
            'email': user.email
        })
```

カスタムした場合はurls.pyはこんな感じ

```urls.py
urlpatterns += [
    path('api-token-auth/', CustomAuthToken.as_view())
]
```



# 参考文献
- [公式ドキュメント](https://www.django-rest-framework.org/api-guide/authentication/#tokenauthentication)
HAVING句を使って集合を切り分けることで、NULLを含まない集合の探し方 〜 COUNT(*)とCOUNT(列名)の違い 〜 (Vol.2)

## はじめに

HAVING句を使って集合を切り分けることで、NULLを含まない集合の探し方を紹介します。

実行結果はこちらで確認できます。

https://www.db-fiddle.com/

本記事は、下記の記事の続きです。

https://qiita.com/ryosuketter/items/0302ca4bb4e250325d74

本記事では、CASE式による表現を扱っています。

Schemaや、クエリのお題は前の記事を参考にしてください。

Query

```sql
SELECT class
FROM members
GROUP BY class
HAVING COUNT(*) = SUM(
	CASE
  		WHEN submit_date IS NOT NULL
  		THEN 1 ELSE 0 END
)
```

Results

| class |
| ----- |
| A     |
| D     |


CASE式は、提出日がNULLでない行については1、NULLなら0というフラグを立てています。

CASE式は、各要素が特定の条件を満たす集合に含まれているかどうかを決める関数を表しています。

こういう関数を

- 特性関数
- 定義関数（集合を定義するという意味）

というそうです。

HAVING句は集合の性質を調べる道具として使えます。とくに集約関数（`SUM`, `COUNT`）やCASE式と組み合わせると便利です。

手続き型言語では、フローチャートが思考の補助線になりますが、集合指向言語では、HAVING句で集合を切り分けて問題を解く際には紙にベン図的なものを書くと効果的だと思います。

本日は以上です。

次は、特性関数やを利用して、複雑な条件を集合を切り分けて集計をしてみましょう！

## 参照

https://www.amazon.co.jp/dp/4798157821/

116p

## アウトプット100本ノック実施中

https://qiita.com/ryosuketter/items/713474dbd009870e4103
MAMPでSSLを使いたい(https)

localhostでhttps通信がしてみたくなったので様々な記事を参考にしました。


##下記の記事をベースにしました
https://web-niar.com/blog/mamp-ssl-for-mac/

##/Applications/MAMP/Library/bin/apachectl configtestをするとInvalid command 'SSLMutex'というエラーが出てしまう時の対応(MAMP 6.0以降)
https://yuki.world/mamp-invalid-sslmutex/


##キーチェーンに証明書を追加する(+αでやりました)
https://parashuto.com/rriver/tools/https-localhost-ssl-with-mac-mamp

##Google Chrome でのエラー

Google Chromeでhttpsで表示すると「このサイトへの接続は保護されていません」という表示が出る。



SAN（Subject Alternative Name）の設定と chrome://flags の「Allow invalid certificates for resources loaded from localhost.」有効化が必要だということが下記の記事でわかりました。

https://webgroove.work/mamp-local-openssl-tls-csr/

そこで下記の記事を参考にSANの設定を行い証明書を発行し直しました。
(最初の部分でテキストファイルを使う部分です)

https://m-lab.blog/web/mamp-https

https://qiita.com/t-kuni/items/d3d72f429273cf0ee31e



##最後に
コピペする時は("")がなぜか全角になるので半角に直すようにしましょう。
ファイルの内容を変更するときはあらかじめ元のファイルをコピーしておいて動かなくなっても戻せるようにしておきましょう。
Exchangeで使うコマンドをまとめてみた

## はじめに
ある案件でMicrosoft365上からExchange Onlineを使ったことがあり、PowerShellでいくつか使用したものもあるので、備忘録として簡単にまとめておきました。
ちょっとしたチートシートとしてよければ参考にしてください。

## コマンド一覧

| コマンド | 説明 | オプション |
| ---- | ---- | ---- |
| Install-Module -Name ExchangeOnlineManagement | (初回のみ)Exchange Onlineモジュールをインストールする |
| Connect-ExchangeOnline | Exchange Onlineに接続する |
| New-Mailbox | リソースメールボックスを作成する | -Name：リソースメールアドレスのローカル部分を指定する<br>-DisplayName：画面に表示させるリソースの名前<br>-PrimarySmtpAddress：リソースメールアドレスのドメイン部分 |
| Get-Mailbox | メールボックスの設定値を確認する | -ResultSize unlimited：組織内の全てのメールボックスの要約リストを取得<br>-Filter：フィルターをかけて絞り込む |
| Set-Mailbox | メールボックスの設定値を設定する | -Identity：変更するメールボックスを指定する<br>-RetainDeletedItemsFor：アイテムの保持期間を指定する |
| New-DistributionGroup | 会議室リストを作成する | -Name：リソースメールアドレスのローカル部分を指定する<br>-DisPlayName：画面に表示させる名称<br>PrimarySmtpAddress：リソースメールアドレスのドメイン部分を指定する -RoomList：会議室リストの意味 |
| Add-DistributionGroupMember | 会議室リストに会議室を追加する | -Identity：グループ名<br>-Member：追加するメンバー |
| Get-DistributionGroupMember | 会議室リストに登録されているメンバーとかを確認する | -Identity：グループ名 |
| Remove-DistributionGroup | 会議室リストを削除する時に使う | -Identity：グループ名 |
| Set-OrganizationConfig | 組織のパラメータを変更する時に使う | -FocusedInboxOn true/false：フォーカスされた受信トレイを有効/無効にする<br>-MailTipsExternalRecipientsTipsEnabled true/false：メールヒントを有効/無効にする |
| Get-MailboxFolderPermission | メールボックス内のフォルダレベルでアクセス権限設定値を確認する | -Identity：メールボックスフォルダを指定 |
| Set-MailboxFolderPermission | メールボックス内のアクセス権限設定値を設定する | -Identity：メールボックスフォルダを指定<br>アクセス権限の指定 |
| Get-Recipient | 組織内の既存の受信者オブジェクトを表示する | -ResultSize unlimited：組織内のすべての受信者の要約リストとして取得する

## 参考
1. [Set-OrganizationConfig - Microsoft Document](https://docs.microsoft.com/ja-jp/powershell/module/exchange/set-organizationconfig?view=exchange-ps)
1. [GetMailbox - Microsoft Document](https://docs.microsoft.com/ja-jp/powershell/module/exchange/get-mailbox?view=exchange-ps)
1. [Set-Mailbox - Microsoft Document](https://docs.microsoft.com/ja-jp/powershell/module/exchange/set-mailbox?view=exchange-ps)
1. [Get-MailboxFolderPermission - MicrosoftDocument](https://docs.microsoft.com/ja-jp/powershell/module/exchange/get-mailboxfolderpermission?view=exchange-ps)
1. [Set-MailboxFolderPermission](https://docs.microsoft.com/ja-jp/powershell/module/exchange/set-mailboxfolderpermission?view=exchange-ps)
1. [Get-Recipient - Microsoft Document](https://docs.microsoft.com/ja-jp/powershell/module/exchange/get-recipient?view=exchange-ps)
1. [Add-DistributionGroupMember - Microsoft Document](https://docs.microsoft.com/ja-jp/powershell/module/exchange/add-distributiongroupmember?view=exchange-ps)
1. [Connect-ExchangeOnline - Microsoft Document](https://docs.microsoft.com/ja-jp/powershell/module/exchange/connect-exchangeonline?view=exchange-ps)
Automation Anywhereで営業日のみ実行する処理

#はじめに
Automation Anywhereをお勉強中のものです。
以前投稿した「[Automation Anywhereで「ツェラーの公式」で曜日を求めてみた！](https://qiita.com/auco67/items/7f508af083c7d2c39e42)」や「[Automation AnywhereでVBScriptを使用して曜日を求めてみた！](https://qiita.com/auco67/items/b7bd6db6bf1da6974388)」を投稿させていただきましたが、なんと【１行で曜日を求める技】を@IQ_Bocchiさんからご教示いただきました。ありがとうございます！(涙)

https://qiita.com/IQ_Bocchi/items/43cafbc56cf609b7a959#comment-6d77d2e72f069fe1c402


#私がやりたかった事
私は、営業日判定を組み込みたかったのです！それには、曜日を求める必要があると思い、以前投稿した方法で考えていましたが、@IQ_Bocchiさんの【１行で曜日が取得できる技】を利用させていただき、今回はロボットを作成しました。

#今回の記事で割愛
以下については、前回記事と同様のため、割愛します。
**使用したAutomation Anywhereのバージョン**
**祝日または企業指定休日判定**
※祝日または企業指定休日判定は、「[Bizrobo!Basicで祝日時は処理をしないようにする処理](https://qiita.com/auco67/items/1cac4f101a723be4a33d)」に記載しています。

#ロボット構成
まず、「エラーハンドラー:試行」、「エラーハンドラー:キャッチ」でエラー時の対応をしておきます。

1. 「エラーハンドラー:試行」を挿入する　※「エラーハンドラー:キャッチ」も一緒に挿入される
1. 「エラーハンドラー:キャッチ」でエラーが発生した行番号とエラーメッセージを変数に設定する
1. 「エラーハンドラー:試行」でエラーが発生すると「エラーハンドラー:キャッチ」へ進むので、エラーメッセージをメッセージボックスで表示させる

![エラーキャッチ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477705/0e23f70c-764e-7af2-450e-847cca31cb5e.png)

#営業日実行処理手順
1. 現在日をシステム日時から取得する
1. 曜日をシステム日時から取得する
1. 現在日が平日であるか判定する
　平日である場合： 4へ進む
　平日でない場合：　処理を終了するメッセージボックスを表示する

1. 祝日および企業指定休日ファイル読込みデータテーブルへ値を格納する
1. 現在日がデータテーブルに存在するかデータテーブルをループして確認する
　存在する場合：　祝日フラグを「真」にする
　存在しない場合：　次行へ進む
1. 祝日フラグが「真」であるか判定する
　「真」である場合：　処理を終了するメッセージボックスを表示する
　「偽」である場合： 営業日のため実行を開始するメッセージボックスを表示する

#処理全体
![処理全体.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477705/30a92721-9850-23e8-f081-af3a18a98659.png)

#現在日時2021/12/26で実行した結果
![実行結果_20211226.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477705/923a11c3-5098-6c75-e402-09eb4e2e9491.png)

#他の日でテストするためにステップを以下の通り変更
1. 営業日である2021/12/27でテストする

　変更前：
　「日時:文字列に変換」でシステム日時を「yyyy/MM/dd」形式で文字列型sTodayに設定
　「日時:文字列に変換」でシステム日時を「EEE」形式で文字列型sWeekDayに設定

　変更後：
　「日時:追加」でシステム日時に１日増した日付を日時型dTestDateに設定
　「日時:文字列に変換」で日時型dTestDateを「yyyy/MM/dd」形式で文字列型sTodayに設定
　「日時:文字列に変換」で日時型dTestDateを「EEE」形式で文字列型sWeekDayに設定
![実行結果_20211227.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477705/e28a2683-7b10-32b2-dda2-50720353b028.png)

2. 企業定休日である2021/12/29でテストする

　変更前：
　「日時:文字列に変換」でシステム日時を「yyyy/MM/dd」形式で文字列型sTodayに設定
　「日時:文字列に変換」でシステム日時を「EEE」形式で文字列型sWeekDayに設定

　変更後：
　「日時:追加」でシステム日時に３日増した日付を日時型dTestDateに設定
　「日時:文字列に変換」で日時型dTestDateを「yyyy/MM/dd」形式で文字列型sTodayに設定
　「日時:文字列に変換」で日時型dTestDateを「EEE」形式で文字列型sWeekDayに設定
![実行結果_20211229.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1477705/257cea4e-71f0-74a5-c3ae-a7d543df2188.png)

#最後に
@IQ_Bocchiさんのお力をいただけたとこで、ステップ数が26行と短縮できました！私の方法で作成していた場合、相当数のステップ数となったことでしょう。今回は、幸いにして@IQ_Bocchiさんに記事を見ていただけた事、そして、技をご教示いただけた事の奇跡により、自分自身とても満足のロボットを作成できたと思います。

@IQ_Bocchiさんに感謝！
Qiitaのつながりに感謝！
食べたものを全てなかったことにするクソアプリ作ろうとしたけど諸々諦めた話

## はじめに
メリークリスマス！
いつの間にか26日ですね。（気づいたら26日になってました）
いろんな諸事情があって大遅刻です。

話は変わりまして、食べることは好きですか？
そして食べた満足感と一緒に上昇する体重を見て複雑な気持ちになってませんか？

今回はそんな複雑な気持ちを解消する、
摂取したカロリーを全てなかったことにするアプリを作りました。

こちらです🙋‍♀️
[0cal](https://something-kqg1awrju-ishimaruco.vercel.app/)


## アプリについて
「今日何食べたの？」と問われるので、
食べたものを素直に入力すると、0カロリー理論に基づいて、
0カロリーなので問題ない！カロリーは摂取していない！
と表示されます。

ただ、本当にそれだけです。
（しかも限定的）

![スクリーンショット 2021-12-26 0.15.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146512/5ec729a2-6e51-096e-1923-94b738898001.png)

### 使ったら痩せる？  
#### 思い込み：プラセボ効果
このアプリは[プラセボ効果](https://healthcare.ds-pharma.jp/health_info/guide/knowledge/08.html#:~:text=%E5%81%BD%E8%96%AC%EF%BC%88%E3%83%97%E3%83%A9%E3%82%BB%E3%83%9C%EF%BC%89%E5%8A%B9%E6%9E%9C%E3%81%A8%E3%81%84%E3%81%86%E8%A8%80%E8%91%89,%E3%81%A8%E3%82%82%E8%A8%80%E3%82%8F%E3%82%8C%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99%E3%80%82)を利用し、たくさん食べても「何も摂取してない」と思い込んで、
自分の思い込みでカロリーを握り潰すことを狙っています。
なので、強い気持ちを持って、「このカロリーは0......このカロリーは0......」と念じたら
脳を騙すことができるので、多分痩せます。保証はしないです😋

### 使ったもの
- 言語：TypeScript
- フレームワーク：Nuxt.js
- デプロイ先：Vercel

ソースコードについては自分でもドン引きするくらいにはクソみたいなの書いているので、
言及しません。（時がきたらするかもしれない）
が、技術記事なのに何も解説しないのはアレなので、「ここがクソだよ」を紹介します。

### クソポイント1：データ、手書き
![スクリーンショット 2021-12-26 0.28.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146512/b36768cb-5ac4-0903-6f10-d5339b885ab2.png)

データが手書きだと、以下のような問題があります。
- メンテナンスが大変（100個追加したいってなったときに辛い）
- ソースコードめちゃくちゃ長くなって見づらい
- データが足りてないので楽しんでもらえる要素が少ない

これじゃ私の独断と偏見で選んだ食べ物しか0カロリーにできないです。
結果太ってしまう、、、これではダメなので、痩せるために

- データを追加できるようにする
- APIから一覧で呼び出すようにする

ってやったらよりダイエットできるのではないでしょうか。

### クソポイント2:期限、間に合ってない
期限という名のお約束は守りましょう。
反省点は以下です。

- 事前準備が全然できていなかった
- 方針がギリギリまでブレて作るものが決まらなかった

事前準備については、12月入る前に作るものは決めてたのですが、
じゃあどうやって実現するか・どこにデプロイするか等を全然決めてませんでした。
また、作りたいものが12月になって変わったり、1週間前になって方向転換したり等で、
結局中途半端なものを遅刻して公開するということをしてしまいました😇

次回からは、1度決めた方針は変えず変えるなら作りきってから！！
という強い意志を持っていきます。


## このアプリの今後
実は完成はしていなくて、本当に「とりあえず動く」というところまでしかできていません。  
今後は以下の機能とインタラクションを追加する予定です。

- データに登録されていない食べ物の0カロリー理論を追加できるようにする
- 写真でアップロードしたものから食べ物を判定する
- カロリーを握り潰すインタラクションの追加


## 終わりに
やっぱり痩せたい（大声）