OmniAICreator/Qiita-1.07M huggingface dataset:
Elixirでアスキーアートしたい

先日[Nerves電子ペーパーでHello world](https://qiita.com/mnishiguchi/items/f709d6c211cf41078f2f)に取り組んで、文字列をピクセルに変換するテクニックを学んだので、メモしておこうとおもいます。

## IExでデモ

- フォントは[olikraus/u8g2](https://github.com/olikraus/u8g2/tree/master/tools/font/bdf)からダウンロード。
- BDFフォントしかつかえない様子。
- [luisgabrielroldan/chisel](https://github.com/luisgabrielroldan/chisel)を用いてビットマップフォント処理。

```elixir
# IExを起動。
iex

# 最低限の依存関係をインストール。
Mix.install([:chisel, :httpoison])

# BDFフォントをダウンロード。
font_name = "6x13"
font_url = "https://raw.githubusercontent.com/olikraus/u8g2/master/tools/font/bdf/#{font_name}.bdf"
{:ok, %{body: font_data, status_code: 200}} = HTTPoison.get(font_url)

# フォントデータの一時ファイルを作成。
# ファイル拡張子は`.bdf`である必要があるようです。
font_file = Path.join(System.tmp_dir!(), "#{font_name}.bdf")

# 一時ファイルにフォントデータを書き込む。
File.write!(font_file, font_data)

# Chiselが一時ファイルからフォントデータを読み込み、解析する。
{:ok, chisel_font} = Chisel.Font.load(font_file)

# 文字列を座標リストに変換。
{pixels, _, _} = Chisel.Renderer.reduce_draw_text(
  "Merry Xmas",
  0,
  0,
  chisel_font,
  [],
  fn x, y, pixels -> [{x, y} | pixels] end
)

# 座標リストを利用して各ピクセルをプリントする。
for y <- 0..16 do
  for x <- 0..96 do
    if Enum.member?(pixels, {x, y}) do
      "%"
    else
      " "
    end
  end
  |> IO.puts()
end

%   %                               %   %
%   %                               %   %
%% %%                                % %
% % %  %%%  % %%  % %%  %   %        % %  %% %   %%%   %%%
% % % %   % %%  % %%  % %   %         %   % % %     % %   %
%   % %%%%% %     %     %   %        % %  % % %  %%%%  %%
%   % %     %     %     %  %%        % %  % % % %   %    %
%   % %   % %     %      %% %       %   % % % % %  %% %   %
%   %  %%%  %     %         %       %   % %   %  %% %  %%%
                        %   %
                         %%%
```

:tada::tada::tada:

もっと本格的なアスキーアートについて田代さん（@sotashiro）が知見を共有してくださってます。

https://qiita.com/sotashiro/items/07de02582988dac1a666

https://qiita.com/sotashiro/items/f7e59abd97254dbb12dd

:tada::tada::tada:
IBM CloudのOpenShiftクラスターを構築する方法

##はじめに
IBM CloudでOpenShiftクラスターを構築し、Windows10のWSL2のUbuntu環境にocコマンドおよびpodmanコマンドを導入して、CLIでOpenShiftにコンテナをデプロイできる環境を作りました。

IBM Cloudのアカウント後の手順になります。
https://cloud.ibm.com/registration

##IBM CloudにてOpenShiftクラスターを構築する手順


1. メニューから[OpenShift] - [クラスター]を選択する

2. [OpenShiftクラスターの作成]をクリックする
![openshift1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249927/02b4195c-1714-ef1f-8f1c-c0881dec8c4f.jpeg)

3. [手動セットアップ]を選択する
4. OpenShiftのバージョンを選択する
5. [このワーカー・プールの追加ライセンスを購入する]を選択する
![openshift2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249927/6889c3a8-967d-7bfe-6de6-1bf34cd11364.jpeg)

6. [クラッシック]を選択する
7. [アジア太平洋][複数ゾーン][東京]を選択する
![openshift3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249927/ce1ca3f3-d6db-0e26-a0a5-bde237bd6378.jpeg)

8. ゾーンあたりのワーカー・ノードを１に選択する
![openshift4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249927/ded55ac9-1831-9068-71d9-48ea5a12f926.jpeg)
![openshift5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249927/39aab3cc-39ad-0826-c4ab-c64797374891.jpeg)

9. [作成]をクリックする
(※この作成後、利用料金が発生しますのでご注意ください。）
![openshift7.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249927/9742ac9a-33ef-76ea-eb7a-258231441c0b.jpeg)

10. 以下のダイアログが表示される
![openshift8.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249927/a46ae656-346b-2000-9bd6-b2dfa894681a.jpeg)

11. OpenShiftクラスターが作成される
![openshift9.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249927/791c0a8e-e66c-e377-2063-e820cf2a2557.jpeg)

##Windows10のWSL2のUbuntu環境にocコマンドとpodmanコマンドを導入する手順
###ocコマンドの導入手順
以下のサイトからtar.gzファイルをダウンロードし、/usr/local/binディレクトリ配下に展開する
https://console.redhat.com/openshift/downloads
※RedHatアカウントへのログインが必要です
![oc1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249927/2879a89d-649c-52c5-fdcd-f70bacfed02f.jpeg)

###podmanコマンドの導入手順
以下のサイトから[Windows Subsystem for Linux (WSL) 2.0]の内容をもとに進める
https://podman.io/getting-started/installation
以下のサイトの情報をもとに進める
https://www.redhat.com/sysadmin/podman-windows-wsl2

```
$ . /etc/os-release
$ sudo sh -c "echo 'deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/x${NAME}_${VERSION_ID}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list"
$ wget -nv https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/x${NAME}_${VERSION_ID}/Release.key -O Release.key
$ sudo apt-key add - < Release.key
$ sudo apt-get update -qq
$ sudo apt-get -qq -y install podman
$ sudo mkdir -p /etc/containers
$ echo -e "[registries.search]\nregistries = ['docker.io', 'quay.io']" | sudo tee /etc/containers/registries.conf
```

##OpenShiftにコンテナをデプロイ
以上でIBM CloudでOpenShiftクラスターを構築し、Windows10のWSL2のUbuntu環境にocコマンドおよびpodmanコマンドを導入して、CLIでOpenShiftにコンテナをデプロイできる環境ができあがりました。
コンテナをデプロイする方法は後日別の記事を投稿予定です。



##参考情報
Windows10のWSL2のUbuntu環境の構築方法は以下の記事を参考にしてください
[Windows10でWSL2をセットアップしてLinuxコマンドの勉強環境を作る方法](https://qiita.com/itonao99/items/3707f0dc67ca58fe9aee)
数学のすゝめ～数学を使えば勝てる～（書きかけ）

# はじめに
　数学って苦手なイメージを持つ人が多くて，難しい式とかわけわかんない記号とかが乱立していて，非常に困ったちゃんの一面があると思います。
　そんな方のために，数学というものに対する敷居を下げて頂ける機会になればと思います。
　また，統計学は最強の学問といった類の本がございますが，決してそういったものディスリスペクトしているわけではなく，統計学も私個人の認識では広義の数学です。
# 動機付け
　動機付けとは偉そうで申し訳ないですが，個人的には，数学の公式とかは全然覚える必要がないと思っています。ただ，その公式の使い方や，その式の本質を知ることに意味があるだけであって，使い方と本質さえわかってしまえば，答えなんていつか出ます。
　難しい数学の問題（所謂，数学上の未解決問題）については，その学者さんが最終的に解けば良いだけの話で，我々がどうこするようなものではないです。
　我々は小学校で習うような四則演算＋αくらいで十分なのです。後述しますが，基本的には四則演算＋αで全てが成り立っているといっても過言ではありません。

　例えば，株などの取り引きの指標として使われている移動平均線なんかも四則演算+αで成り立っているので，自力で計算することができます。
　ただ，必要なことはそうではなくて，前述のとおり，計算できることには意味はあまりなくて，例えば移動平均線は何を意味しているのか，その本質を学ぶことこそに数学を学ぶ価値があるのです。

# 身近な数学
　さて，身近な数学（算数）といえば皆さん何が思いつくでしょうか。
　皆さんが普段よく使っているスマホなんかは，数学の塊と言えると思います。
　電波で情報を伝送することなんて，この数学がなければ成り立たなかったと思います。

# 2という数字
　素数の中で一番小さい数で，デジタル信号処理でよく使われています（2進数）。
　何かを半分にするのも2分の一ですし，身近によく出てくる数字だと思います。

# 3という数字
　古くからスピリチュアルな数字として認識されてきました。ググるといろいろございますが，何とも不思議な数字です。我々の生活の基盤の一部を担っているといっても良いと思います。
　三角形もすごく不思議な図形で，多角形は全て複数の三角形を内包しているので，何かと3は色々な場面で登場いたします。

　その一つが時計です。厳密には時計は60進法と呼ばれる，60になると1繰り上がるのですが，60は3の倍数なので，3の性質を含んでいます。

# 8はとても不思議な数字
　どの数字も不思議な数字なのですが，8だけは異質だと思います。
　8という数字を分解すると

```
 8 = 2 x 2 x 2
```
　です。
　これはどういうことかというと，まず確実に2の要素を含みます。
　そして，3回同じ数字をかけているので，3の要素も含みます。
　したがって，8という数字は2の要素も3の要素も含む一番小さい数なのです。

# （自分なりに）移動平均線の本質に触れる
　だいぶ主観があるかもしれませんが，現在の自分の考えをまとめたいと思います。
## そもそも平均とは（平均値，中央値）
　平均も数学的にいくとかなり異質なやり方です。
　小学生で習うくらいのレベルなのに，奥がかなり深いです。
　例えば，以下の表をご覧ください。

|  | 身長[cm] |
|:-:|:-:|
|Aさん|152|
|Bさん|182|
|Cさん|196|
|Dさん|168|
|Eさん|152|
|Fさん|171|

では，この6人の平均身長はいくつでしょうか？
という問題が出た場合には，皆さんこうやって計算すると思います。

```
 152 + 182 + 196 + 168 + 152 + 171 = 1,021
 1,021 / 6 = 170.16666....
```
となり，約170[cm]が答えでしょう。

続いて中央値について考えます。
中央値は言葉のとおり，中央の値でデータを小さい順に並べたときに真ん中にくる値です。
上の表を小さい順に並び変えます。

|  | 身長[cm] |
|:-:|:-:|
|Aさん|152|
|Eさん|152|
|Dさん|168|
|Fさん|171|
|Bさん|182|
|Cさん|196|

となりますが，データが偶数個あるときは，真ん中の2つを足して2で割ります。したがって，この例でいくとDさん(168cm)とFさん(171cm)の身長を足して2で割ると中央値が出てきます。

```
168 + 171 = 339
339 / 2 = 169.5
```

　平均値の計算には，すべてのデータが計算の要素に含まれるのに対して，中央値は多くても2つの数しか計算の要素に含まれません。
　なので，何かデータを分析する際に中央値を使うと全体の結果が反映されずに偏った結果となることが容易に想像できます。
　逆に，ノイズが多いデータ（極端に大きいデータがあったり，またはその逆のデータ）に対しては，平均値はもろに影響を受けてしまうので，意図しない結果が出てくることも想像できます。
　今回は，たまたま平均値と中央値がほとんど等しくなりましたが，本質（＝今回は計算の仕方）を知ることで，平均値と中央値の違いを理解することができたと思います。

## 移動平均とは


## 様々な移動平均
こちら←を[]()でリンクにするのはもうやめよう

# はじめに

かつての私もそうなのですが、何かしらリンクを示すときに[こちら](https://blog.qiita.com/qiita-article-ogp-renewal/)と書いていました。

# これからは

QiitaはOGPに対応しているので、↓こんな感じでURLを直貼りでQiita側でOGP表示してもらって出した方が、リンクにクリックしてもらいやすくなると思います。

https://blog.qiita.com/qiita-article-ogp-renewal/

# さいごに

OGPについて詳しく説明されている記事が[こちら](https://qiita.com/engineerhikaru/items/d5e71abcea2faaed674e)に書かれていました。

**・・・・・・ほら↑リンクの箇所がわかりにくいですよね？**

https://qiita.com/engineerhikaru/items/d5e71abcea2faaed674e

こうするとわかりやすいですね。以上！解散！！
neosでlogix その38

#概要

niosでlogixやってみる。
JP ToolのDisplay Graphを使ってみた。

#写真

![2021-12-23 07.40.37.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/927e7e15-a7ea-3066-0d8f-1daa3aae7c02.jpeg)


#手順

- インベントリーを開く
- JP ToolのDisplay Graphをスポーン
- ノードブラウザを表示
- Tをスポーン
- COSをスポーン
- つなぐ

以上。
neosでlogix その37

#概要

niosでlogixやってみる。
Touch Buttonコンポーネントを使ってみた。

#写真

![2021-12-23 05.15.59.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/02c32053-4a8d-78e4-7be7-ca8e5487590b.jpeg)


#手順

- 新規->3d->box
- インスペクターを開く
- Touch Buttonを追加
- Touch Buttonのインターフェースを取り出す
- ノードブラウザを表示
- Button Eventsをスポーン
- つなぐ

以上。
Unityで構造体の配列メンバーが表示されない謎

# はじめに

おはようございます。先程ブログに書いたのですが、かなり気になる現象でしたのでこちらでも記事にします。

https://cyross.sakura.ne.jp/unity_blog/?p=296

# 背景

アニメーション関連の効率化(詳細は本筋とは関係ないので控えます)のために、Inspector上で構造体の配列をやり取りしようと考えました。

環境は以下のとおりです。

```
Unity: Unity2021.2.7f1
OS: Windows10 x64 (最新パッチ適応済み)
CPU: Intel Core i7 4771
GPU: GeForce RTX 3070 Ti
```

# 現象

実装自体は問題ないはずなのですが、メンバーが空白で表示されない状態です。

![スクリーンショット 2021-12-23 231314.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101034/e44c46c7-3f6b-e8cf-3810-4d306faf0437.png)

# 調査

単体で表示させたりするとメンバーが表示されるので、この場合独自の問題のようです。

また、Odinを導入するとメンバーが表示されることも確認しています。
現状では、「Odinを入れる」が最適解の模様です。

# その後の対応

最終的に、Unityのバグではと考えてバグレポートを提出いたしました。
一体、何が原因なのだろう…。
ZOOM会議でのスマートな画面共有

1分で読めます:point_up:

簡単に2つほどご紹介します。


#事前準備

会議前に余裕がもしあれば、準備に時間をかけましょう
・共有する予定のページを開いておく
・ページを一つのウィンドウにまとめておく（別件のページは別ウィンドウによける）
たったこれだけでもうスマートな会議は確約されました。



#公開範囲の選択で、ウィンドウを選ぶ

会議中に別件の連絡ポップアップがでて「あちゃー」となるときがあるかもしれません。
そんなときは**公開範囲をウィンドウ**にしましょう。

ウィンドウ選択画面（画面共有を押下したあとにでてくる）![ウィンドウを選ぶ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1649937/381f0628-c7fe-e2d9-e80d-6bb644943d9a.png)




公開範囲としてデスクトップを選んでしまうと、確かに自分がみている画面を相手にも見せることができますが
それは会議に不要な情報も含まれています。排除しましょう。
ウィンドウを選択しておけば、通知がきても表示されません。
通知をOFFするのを忘れてしまうことを事前に回避しておきましょう。
秘匿性の高い話が多い方へもおすすめの方法です。雑談DMとかもね。
※ 設定で、画面共有時、システム通知を消音にすることも可能です。

###設定方法

![ウィンドウを設定するには.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1649937/c7d0e341-486f-9467-9f97-0f9d565bf231.png)


設定　＞＞　画面共有　＞＞　ミーティングで私の画面を共有する場合　＞＞
「すべての共有オプションを表示」を選択

※ 画面共有時に急を要する場合は、共有する画面の選択するSTEPを省くのも一つだと思います。




#番外編
急な会話が必要になったとき、「こちらにきてくださいー！」と言いたいですよね。
ZOOMのURLをさっと共有できたらとっても楽ちんです。
下記設定より可能です。

![番外編.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1649937/1550895d-986a-3944-2757-455f3596ba00.png)

一般　＞＞　「ミーティングの開始時に招待のリンクをコピー」　にチェック。

こちらの設定で、zoomのアプリを立ち上げておけば
「新規ミーティング」を立ち上げた瞬間に会議のリンクがコピーされているので
Slackなどのコミュニケーションツールでベベっと貼って連絡することが可能です。


![zoomアプリTOP.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1649937/723f4fef-9d0b-4f3d-db9f-9d9900b91cae.png)



立ち上げた際の画面下に、URLコピーできたよ、のお知らせが出ます。

![スクリーンショット 2021-12-24 9.54.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1649937/91ca1d41-1a2c-836f-e0d5-dfc26835f33a.png)


別のツール（ディスコードやハンズオンなど）の方がいいよーとか好みもあると思うので、
番外編としてご紹介させていただきました。

#おわりに
昨今、ZOOMで会議すること多いですよね。
対面での会議よりも空気感が伝わりにくいので、
共通認識を持つために画面共有をよく利用されると思うのですが
スムーズにMTGが進行するために
自分が試してみていることがいくつかあるので
公開してみて、みなさんのおすすめのやり方も学びたいと思い
記事にしてみました。

ここまでお付き合いいただきありがとうございました。

※※ zoomのアップデートによりこちらの情報が古くなると思いますが
いま現在のわたくしのバージョンは**5.8.6 (2879)**でございます。
Visual Studio 設定・操作備忘録

https://docs.microsoft.com/ja-jp/visualstudio/debugger/debugging-absolute-beginners?view=vs-2022&tabs=csharp

## 自動同期機能を有効化
→現在表示されているファイルをソリューションエクスプローラーで選択状態にする設定
1. メニューバーの「ツール (Tools)」**をクリックします。
2.「オプション (Options)」**を選択します。
3.「プロジェクトおよびソリューション > 全般」**を選択します。
4.「アクティブな項目をソリューション エクスプローラーで追跡」**のチェックボックスをオンにします。

## 新しいタブを右側にする
1. メニューバーの「ツール (Tools)」**をクリックします。
2.「オプション (Options)」**を選択します。
3.「環境 > タブとウィンドウ」
4.「既存のタブの右側に新しいタブを挿入する」

## タブを複数行表示する
1. メニューバーの「ツール (Tools)」**をクリックします。
2.「オプション (Options)」**を選択します。
3.「環境 > タブとウィンドウ」
4.「複数の行にタブを表示する」

## 折りたたみショートカット
ショートカットはVSコードと同じくコンボになっていて、
例えば「Ctrl + M, Ctrl + O」の場合はCtrlを押したまま「M」「O」と順番に押下する
「M」「O」を同時に押下するわけではないので注意すること。

|項目|コマンド|
|----|----|
|全てのメソッドを折りたたむ|Ctrl + M, Ctrl + O||
|全てのメソッドを展開する|Ctrl + M, Ctrl + L||
|個別のメソッドを折りたたむ/展開する|Ctrl + M, Ctrl + M||


##  デバッグに関して
|項目|操作方法|備考|
|----|----|----|
|ブレークポイント|行数の横をクリック|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637823/176d3e98-c70e-a620-19ec-505d02073e2d.png)|
|Debugモード|release→Debug|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637823/0abef9aa-7582-aa93-db01-7542eaeb5ee8.png)|
|デバッグ開始|F5||
|ステップ実行|F10||
https://docs.microsoft.com/ja-jp/visualstudio/ide/default-keyboard-shortcuts-in-visual-studio?view=vs-2022

## 検索
|項目|操作方法|備考|
|----|----|----|
|ファイル名検索|ソリューションエクスプローラーの検索<br><b>Ctrl + :</b>|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637823/f08e22c6-c4c9-0de7-f6c8-1219b7432202.png)|
|キーワード検索|<b>Ctrl + F</b>|<b>Ctrl + V</b>からの<b>Ctrl + F</b>とすると、値がセットされた状態になる。|

## デフォルトの色
|項目|説明|備考|
|----|----|----|
|緑|コメント||
|薄オレンジ|文字列||
【C#】SQLの接続文字列をApp.configファイルから取得する　Visual Studio　

<h2>はじめに</h2>
C＃学習１か月目の超初心者です。
つまったところをまとめました。

<h2>やりたいこと</h2>
直にうっているSQLの接続文字列を、
App.Configファイルに記述し、そこから呼び出す。
若干、エラーがでたのでそこも記述します。

#App.Configファイルへの記述

```C#:App.Config

//私の場合　バックスラッシュになってるところは本来は¥マークです。
	<connectionStrings>
		<add name="DB1"
		connectionString="Data Source=CTS-SQL2014\SQL2014;Initial Catalog=Prac_TableName;Persist Security Info=True;User ID=ID;Password=password"
		providerName="System.Data.SqlClient"/>
	</connectionStrings>
```

Catalog=Prac_TableNameやら、ＩＤやらパスワードは適当に打ち換えています。

#参照設定の追加

Visual Studioのソリューションエクスプローラーの「参照」から、
「参照の追加」で、「System.Configuration」を追加します。
ソリューションエクスプローラーの「参照」に追加されてることを確認してください。


#接続文字列の呼び出し

上部に、「using System.Configuration;」を追記します。

```c#:Form1.cs
//上記は省略
//SQLと接続するための記述
using System.Data.SqlClient;
//以下追記します。
using System.Configuration;
```

```c#:Form1.cs
//ここで接続文字列を呼びだし？
string ConnectionStr = ConfigurationManager.ConnectionStrings["DB1"].ConnectionString;
DataTable dt = new DataTable();
//SQLに接続する
SqlConnection cn;
//ここで接続文字列を使用しています。
cn = new SqlConnection(ConnectionStr);
cn.Open();
```


#発生したエラー

Form1.csにSQLの接続文字列を記載していたときは、
データソース名に、`¥`が入っており、それだとエスケープシークエンス？になるので、
`¥`を二つ記載してました。
しかしながら、App.Configファイルで同じように記載すると、
呼び出される接続文字列には`¥`が４つも入っていました。。

そのため、App.Configファイルには、本来通り`¥`マーク一つ記載するようにしたところうまくいきました。
BigQuery よく使うSQL



strをDATETIME型に変換
```
select SAFE.PARSE_DATETIME("%Y-%m-%d %H:%M:%E3S",'2019-01-02 18:11:12’)
```

strをDATE型に変換
```
select SAFE.PARSE_DATE("%Y-%m-%d %H:%M:%E3S",'2019-01-02 18:11:12’)
```

DATE,DATETIMEをstr型に変換
```
select FORMAT_DATE("%Y%m", (select CURRENT_DATE('Asia/Tokyo')))
```

現在の日付を取得できる
```
select CURRENT_DATE('Asia/Tokyo')
select CURRENT_DATETIME('Asia/Tokyo')
→‘Asia/Tokyo’の指定を省略すると、UTC（日本時間ー９時間）になる
```


一か月前の日を取得する
```
select SAFE.DATE_ADD((select CURRENT_DATE('Asia/Tokyo')), INTERVAL -1 MONTH)
SAFE.は、お約束で書いておくと、もしも、エラーが起きてもその行だけがNULLになって、全体は落ちずに動くので便利  

↓でも可
select CURRENT_DATE('Asia/Tokyo')  - INTERVAL 1 MONTH
```


9時間プラスする
```
DATETIME_ADD(PARSE_DATETIME("%Y-%m-%dT%H:%M:%SZ",a.CreationTime), INTERVAL 9　HOUR)
```



月初の日付を取得する
```
select SAFE.DATE_TRUNC((CURRENT_DATE('Asia/Tokyo’)), MONTH)
```



日付の差を取る
```
select SAFE.DATE_DIFF(DATE "2018-12-22", DATE "2017-12-12", DAY)
```


データを削除する
```
truncate table `プロジェクト名.データセット名.テーブル名`;
```


データを復元する(時間を変更する)
```
SELECT
  *
FROM
  `プロジェクト名.データセット名.テーブル名` FOR SYSTEM_TIME AS OF TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR)
```


***
【RestAPI？】初心者向けにざっくり解説

# 目的
最近流行のpython, djangoを触ってAPIを実装しようとしたときに、django_restframeworkなるもので簡単にRESTAPIが実装できると言うことを知ったものの、**RestfulなAPIとはどのようなものか完全には理解できていなかったため**ここで同じように曖昧なイメージを持っている方々に共有してみむとせん。

# 前提
API自体の知識は多少ある方。
RestfulAPIと言う名前を聞いたことがある方

# 概要
REST APIは「REST」と呼ばれるAPIの設計思想に基づいたweb用のapiのこと。
RESTには以下の４原則がある。
①ステートレス
②統一インターフェース
③接続性
④アドレス可能性
以上の四つの原則に従って実装されたAPIのことをRESTAPIと呼ぶ。
具体的に４原則について説明する

# 詳細
### 1. ステートレス
サーバーにユーザーセッションなどの「状態」(ステート)を持たない(レス)前提で情報のやり取りを行う。
→ やり取りが一回ごとに完結する。
→ ２度目のアクセスをしてもサーバー側は何も記憶がない

### 2. 統一インターフェース
例えば
①HTTPメソッドでサーバーにリクエストを送るよ
②JSON形式のデータを送るよ
などのようにブラウザとサーバのインターフェースの定義を統一する

### 3. 接続性
情報の内部に、別の情報の状態へのリンクを含めることができる。

### 4. アドレス可能性
全ての情報が唯一かぶる事のない識別子を持っていて、提供する情報をその識別子で表現可能
具体例で言うとメンバーの情報は一意のidやusernameを持っているため、 「/member/pk/」このようにして指定したメンバーに対して編集などの動作を行うことができる。

### 具体的には
一番大きなポイントとしては以下のように、同じuriでも異なるHTTPメソッドでリクエストを投げる事で異なる動作を行うことができると言う事。
このことによりシンプルでわかりやすいAPIが作成できると言うことだ。

| HTTPメソッド | リソース | 概要 |
|:-:|:-:|:-:|
| GET  | /member/  | メンバーの一覧を取得  |
| GET  | /member/pk/  | pkで指定したメンバーの情報を取得  |
| POST  | /member/  | 新規のメンバーを登録する  |
| PUT  | /member/pk/  | pkで指定したメンバーの情報を更新する/pkにメンバーがなければ追加する  |
| DELETE  | /member/pk/  |  pkで指定したメンバーの情報を削除 |



# 参考文献
https://tech.012grp.co.jp/entry/rest_api_basics
イーサネットLANとは？

<h1>イーサネットとは？</h1>
そもそもイーサネットとはLANで最も利用されているプロトコルです。
(LANとはLocal Area Networkの略で、一定の範囲内で接続できるネットワークのことです。)

元々はIntel、Xerox、DECによって開発され、IEEE 802.3委員会によって標準化されました。
皆さんが普段から耳にしているLANはイーサネットLANであるといっても過言ではありません。

OSI参照モデルでは、<b>データリンク層と物理層</b>、TCP/IPモデルでは<b>リンク層(ネットワークインターフェース層)</b>で動作します。
![8564146B-7DC3-4454-A06D-A4A0104AF622.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/f71f4538-97a7-f776-9150-c58075640f76.png)
(出典：https://www.sbbit.jp/article/cont1/12099#&gid=null&pid=1)

<h1>LANケーブルの種類</h1>
LAN内で機器間を接続するためにケーブルを使用する必要があります。
ネットワークには様々な種類があり、ほとんどの場合では<b>ツイストペアケーブル</b>を使用します。
![0000000200030126_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/66af5dc0-c41a-8796-bb43-6b90d2238ede.jpeg)
ツイストペアケーブルは銅線を2本ずつペアにして撚り合わせたケーブルです。
2対4線式、4対8線式などがあります。
ツイストペアケーブルはノイズに強いシールド付きの<b>STP(Shielded Twisted Pair)</b>とシールドなしの<b>UTP(Unshielded Twisted Pair)</b>がありますが、日本ではUTPが主流です。

<b>ツイストペアケーブルは、一般的にLANケーブルと呼ばれています。</b>

<h1>LANケーブルのピンの割り当て</h1>
LANケーブルのコネクタにある電極一つ一つを<b>ピン</b>と呼びます。
4対8線式のケーブルピンがある場合、1ピン〜8ピンまで構成されています。
![スクリーンショット 2021-12-24 9.14.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/eb80fb26-1d40-39ae-1056-0f9a713f84fc.png)

LANケーブルには、<b>ストレートケーブル</b>と<b>クロスケーブル</b>の2種類があり、このピンの並びに違いがあります。

ストレートケーブルはケーブル内で8本の銅線がストレートに配線されているため、ケーブルの両端でピンの並びが同じとなっています。
一方でクロスケーブルはケーブル内で8本の銅線の一部がクロスして配線されているため、ケーブルの両端でピンの並びが異なっています。
これらのケーブルの使い分けは、どういった機器を接続するのかで変わります。

<h1>MDIとMDI-X</h1>
LANケーブルを差し込むポートには<b>MDI(Medium Dependent Interface)</b>と<b>MDI-X</b>の2種類があり、機器によってどちらの仕様かが決まっています。

<h2>MDI</h2>
MDIは8本あるピンのうち、1番目と2番目を送信、3番目と6番目を受信に使用します。
MDIはルータやPCなどに用いられています。

<h2>MDI-X</h2>
MDI-Xは8本あるピンのうち、1番目と2番目を受信、3番目と6番目を送信に使用します。
MDIはハブやスイッチなどに用いられます。

<hr>

通信する際には送信と受信が向かい合うように接続する必要があります。
MDIのポートを持つ機器同士をそのまま繋いでしまうと、お互いに１、２番ピンを送信、３、６番ピンが受信となってしまいます。
MDI-Xのポートを持つ機器同士も同様に、受信と受信、送信と送信が向かい合ってしまいます。

そこで、先程のストレートケーブルとクロスケーブルを使い分けて、送信と受信が向かい合うようにします。
<b>ストレートケーブルはMDIのポートを持つ機器とMDI-Xのポートを持つ機器を接続する際に使用します。</b>
例えば、ルータとスイッチの接続や、PCとハブの接続の場合です。
![スクリーンショット 2021-12-24 9.30.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/7b0f58d1-a87c-f10a-f9e5-8c1b707e9f78.png)

クロスケーブルは、MDIのポートを持つ機器同士、またはMDI-Xのポートを持つ機器同士を接続する際に使用します。
例えばルータとルータの接続やルータとPCの接続などです。
![スクリーンショット 2021-12-24 9.33.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/44dcd08f-4ebd-e3c8-3165-f6b862b9050a.png)

ストレートに銅線を伸ばすと送信と受信が向かい合わないため、途中で銅線をクロスさせて送信と受信が向かい合うようにします。

なお近年ではMDIのポートとMDI-Xのポートを自動で切り替えることができるAuto MDI/MDI-Xという機能を備えた機器が多くなっています。この機能がある機器ではストレートケーブルでもクロスケーブルでもどちらのケーブルでも問題なく接続することができます。

<hr>

簡単ですが、イーサネットについて簡単にまとめてみました。
レイヤが低いので普段私たちが触れる機会はあまりないのですが、
覚えておいても良い知識だと思います。

以下が参考文献です。
![516ukt9FxlL.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430519/caa74ffe-6ea3-11ab-6723-5e8b9c3fe56b.jpeg)
<a href="https://www.amazon.co.jp/gp/product/B087JKL1JH/ref=ppx_yo_dt_b_d_asin_title_o01?ie=UTF8&psc=1">シスコ技術者認定教科書 CCNA 完全合格テキスト＆問題集</a>

以上です。
閲覧していただき、誠にありがとうございます！
PyTorch向けの深層強化学習ライブラリPFRLを試してみた

## PFRL
「[PFRL](https://www.preferred.jp/ja/news/pr20200730/)」は、株式会社Preferred Networksが開発した、PyTorchユーザー向けの深層強化学習ライブラリです。

## PFRL入門
npakaさんのnote記事「[PFRL入門](https://note.com/npaka/n/n60d3891f1325)」が非常に簡潔にまとまっていると思います。

## 学習させてみた
コードに変更を加えずにそのまま学習させてみました。
200回で最高の200に到達しました。

```bash
episode: 100 R: 13.0
episode: 200 R: 200.0
```

しかし、学習が進むとなぜか学習しなかったレベルまでスコアが突然下がります。

```
episode: 100 R: 13.0
episode: 200 R: 200.0
episode: 300 R: 20.0
episode: 400 R: 165.0
episode: 500 R: 31.0
```
## 何が起きているのか？
何が起きているのか、表示させてみます。
![carpole01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670581/d3a272d9-7f08-191d-57b0-e1a19ad44f00.gif)

どうやら傾きながら、一方向に移動しているようです。手の平の上に棒を乗せて走っていく小学生みたいですね。
棒は倒れませんが、あっという間に範囲外に出てしまっています。

## 学習が進めば改善するのか？
さっきの10倍の5000回学習させます。

```bash
episode: 4800 R: 13.0
episode: 4900 R: 13.0
episode: 5000 R: 38.0
```
嫌な予感がします。むしろ悪化しています！
![carpole02.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670581/e792cd7f-2179-e3bf-6e2b-cec52ecced6c.gif)

## 考察
学習させれば、上達するというわけではない。

上手く行った時に学習をやめさせるべきか？
どこで見極めるのかという別の問題が出てくる。

そもそもcarpoleの観測値と評価（報酬）は？

## 観測データ
[carpole](http://arduinopid.web.fc2.com/Q3-37.html)
観測データについては、下記のように記述されています。
カートとポールの状態を表す4つの値のようです。

>カートを操作した結果、カートとポールの状態がobservationで定義されます。値の範囲は公式で定義されている値を記載しております。
>
>　配列0：カートの位置　-4.8～4.8
>　配列1：カートの速度　-Inf～Inf (無限大)
>　配列2：ポールの角度　-24～24[rad] ← 公式ではdegだがradの間違いであると思われる。
>　配列3：ポールの角速度　-Inf～Inf

コードではobsの値です。

``` python:python
obs, reward, done, _ = env.step(action)
```
## reward(報酬）
reward(報酬）はどうでしょうか。

>＜報酬獲得条件:reward=1＞
>以下条件が全て成立している時に、報酬獲得(reward = 1)します。
>
>　① ポール角度が±0.21以内
>　② カート位置が±2.4以内

つまり１、２に入っていれば、報酬が出るわけで途中の棒の姿勢とかは全く報酬に加味されていないというのがわかります。

## なぜカートの位置によるNGを学習できないのか
学習していないわけではなく、　忘却と学習を繰り返している。
- 1 学習が０の時は、ポールがすぐ倒れる状態なので、ポールの角度維持する学習主に行う
- 2 ポールの維持とカート位置を同時に学習する
- 3 R200を達成する
- 4 カート位置の学習の割合が大幅に減ったことで忘却される。
- 5 すぐ端に着くようになる
- 6 ２に戻る

２-6を繰り返すので学習が振動しているように見える。

## 報酬(reward)を変えてみる
本来の強化学習の目的（アルゴリズムを作ること？）とはズレてしまう部分もあるかもですが、報酬を規定のものから変えてみます。

報酬が1の時はカート位置が中央から外れるほどに報酬を下げてみました。
「円から出たらいけませんよ！」と条件を追加された子供みたいな動きですが、報酬の付け方で、学習結果は大きく変わるということがわかりました。

```
reward = reward - numpy.abs(obs[0])/2.0 if reward == 1 else reward
agent.observe(obs, reward, done, reset)
```
![carpole03.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670581/b76c0fa2-c7c1-86a4-df1e-5b01d953cd1b.gif)

## 報酬(reward)をさらに変えてみる
せっかくなので、報酬をどうすれば良いのか試行錯誤した結果を出しておきます。
500回程度の学習でこれくらい安定します。　
条件は自身で探されると面白いと思います。

正しい指導がいかに重要性かが強化学習から見えてき気がします。
![carpole04.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/670581/ea7f35c2-42ed-eb5a-d1d6-82965839664f.gif)
DatabricksのAuto Loaderを活用してParquetをDeltaに書き込む

[DatabricksのAuto Loader](https://qiita.com/taka_yayoi/items/df143647dcf5942b51c6)に記載されているサンプルはCSVが対象のものでしたが、Parquetを対象としたものが見当たらなかったのでサンプルを作成しました。

このサンプルでは、ファイルのアップロード先となるディレクトリにファイルが格納されると差分ファイルのみを読み込み、Delta Lake形式で書き込みを行うパイプラインを構築しています。

# サンプルの説明

## セットアップ

```py:Python
# ファイルのアップロード先を作成
user_dir = 'takaaki.yayoi@databricks.com'
upload_path = "/FileStore/shared_uploads/" + user_dir + "/parquet_data_upload"
dbutils.fs.mkdirs(upload_path)
```

ここでは、Sparkの構造化ストリーミング(Structured Streaming)を活用しており、差分のみを処理するようにチェックポイントを作成するように設定します。チェックポイントを作成することで、Sparkがどこまで処理を行なったのかを常に記録し、クラスターを停止し、再度起動した後でも差分ファイルのみを処理するように動作します。

```py:Python
# チェックポイントのパス
checkpoint_path = '/tmp/delta/parquet_data/_checkpoints'
# 書き込み先のパス
write_path = '/tmp/delta/parquet_data'
```

## ファイルのアップロード

1つのParquetファイルを上記`upload_path`にアップロードしておきます。ここでは、以下のParquetファイルを使用しています。

[parquet\-dotnet/postcodes\.plain\.parquet at master · elastacloud/parquet\-dotnet](https://github.com/elastacloud/parquet-dotnet/blob/master/src/Parquet.Test/data/postcodes.plain.parquet)

ファイルをアップロードするには、画面上部のメニュー**File > Upload Data**で上記の`upload_path`を選択し、ファイルをドラッグ&ドロップします。
![Screen Shot 2021-12-24 at 9.46.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/b52b850e-1b76-477f-3fb8-137606ec442d.png)

## スキーマの取得

Parquetに対する構造化ストリーミングでは、明示的なスキーマの指定が必要となるので、先ほどアップロードしたParquetファイルを読み込み、スキーマを取得します。

```py:Python
# Parquetファイルのスキーマを取得するために一旦読み込みます
parquetFile = spark.read.parquet(f"{upload_path}/postcodes_plain_1.parquet")
parquetFile.schema
```

## Auto Loaderの起動

upload_pathの場所にアップロードされたファイルを読み込むストリームと、write_pathにDelta Lake形式で書き込むストリームを起動します。

```py:Python
# upload_pathの場所に到着するファイルを読み込むストリームのセットアップ
df = spark.readStream.format('cloudFiles') \
.option('cloudFiles.format', 'parquet') \
.option('header', 'true') \
.schema(parquetFile.schema) \
.load(upload_path)

# ストリームを起動します。
# upload_pathにアップロード済みの全てのファイルの記録を保持するためにcheckpoint_pathを使用します。
# 最後のチェック以降のファイルに対して、新規にアップロードされたファイルのデータをwrite_pathに書き込みます
df.writeStream.format('delta') \
.option('checkpointLocation', checkpoint_path) \
.start(write_path)
```

## Delta Lakeの読み込み

Delta Lake形式でデータが書き込まれていることを確認します。

```py:Python
df_parquet = spark.read.format('delta').load(write_path)

display(df_parquet)
```

![Screen Shot 2021-12-24 at 9.50.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/5aaf593b-6f73-6e5a-cb6b-13da5dfbea11.png)

## クラスターの停止・起動

一度、クラスターを停止します。クラスターを再起動後、上のストリームを起動し、追加のParquetファイル(上のファイルのコピー)を`upload_path`にアップロードします。上でAuto Loaderのチェックポイントを作成しているので、Auto Loaderは新たにアップロードされたファイルのみを処理します。

```py:Python
df_parquet = spark.read.format('delta').load(write_path)

display(df_parquet)
```

![Screen Shot 2021-12-24 at 9.51.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/cbe6168c-122e-148c-ca33-1ba0c96285b7.png)

# サンプル

https://github.com/taka-yayoi/public_repo/tree/main/autoloader_parquet


### Databricks 無料トライアル

[Databricks 無料トライアル](https://databricks.com/jp/try-databricks)
Dart(C#)のEnumを少しでもJava/Kotlinっぽい感じにしてやる


## 更新 2022/05/24
先日 Dart2.17が発表され、その中で Enhanced enums としてEnumにフィールドやstaticメソッド等が実装できるようになりました。以後はそちらの方法が推奨されます。
参考：
https://medium.com/dartlang/dart-2-17-b216bfc80c5d

また、コメントにありました通りC#はアノテーションでフィールドを持つことが多いようなので、そちらが良いかと思われます。

## 以下Dart2.17発表前
JavaやKotlinを使っていると、Enumに値を持たせるという便利な機能が当たり前だと思うのですが、DartやC#のEnumではそれができません。
なので、Enumを拡張して擬似的に値を持てるようにしてやりました。同じ制約を持つC#もEnumの拡張ができるので、同様に書けると思います。
Dart 2.15です



```dart
void main() {
  for (var type in ArticleType.values) {
    print('${type.val.title} ${type.val.description}');
  }
}

/// Javaっぽくしたいenum
enum ArticleType {
  news, // ニュース
  sport, // スポーツ
  entertainment, // エンタメ
  fashion, // ファッション
  anime, // アニメ
}

/// Enumの値用のクラス
class Article {
  const Article({
    required this.title,
    required this.description,
  });

  final String title;
  final String description;
}

/// 拡張してEnumの値を持てるようにしてやる
extension ArticleTypeEx on ArticleType {
  Article get val {
    switch (this) {
      case ArticleType.news:
        return Article(title: 'ニュース', description: '最新のニュースです');
      case ArticleType.sport:
        return Article(title: 'スポーツ', description: 'スポーツのニュースです');
      case ArticleType.entertainment:
        return Article(title: 'エンタメ', description: '流行りのエンタメ情報です');
      case ArticleType.fashion:
        return Article(title: 'ファッション', description: 'トレンドファッションアイテム');
      case ArticleType.anime:
        return Article(title: 'アニメ', description: '今期覇権アニメです');
    }
  }
}
```

## メリット
* enumの値の定義が漏れた場合、コンパイルエラーとなり気づくことができる
* フィールドを増やすたびに拡張を生やさなくていい。switch文はここだけで済む。保守しやすい
海中の通信技術について

本日はクリスマス・イブ。
みなさん、サンタさんにお願いするプレゼントは決まりましたか？
私はリゾートダイバーとして、これをお願いしたいと思います。

**「ダイビング中にスマホが使えますように！」**

5Gがスタートし、都内ではちょこちょこと5Gに繋がってたりしますが
普段私達が使っている高周波な電波は水中では数cmで減衰して使えなくなってしまいます。
海中では長距離通信できる音波を利用することが多いそうですが、情報量は少なく低速だとか。

そこで登場するのが**「青色LED光通信」**光を使って通信する方法です。
速度や情報量は4Gと遜色ないそうなので、これを使えば海中でのスマホ利用も夢じゃない?!

実際にKDDI研究所が海中での通信実験を行って、陸上と海中5mでのLINEでのやり取りが成功したそうです。
海中での通信が一般的に利用出来るようになれば位置情報を利用した海中マップアプリや、
ダイバー同士のやり取り履歴が残せるアプリ、ダイビング中にログを付けられる[^1]アプリなどアプリ制作の構想は広がるばかり。

またレジャーだけでなくNTTが3〜4年後の実用化を目処に研究を行っている**時空間等化技術**やALANコンソーシアムが進める**可視光による無線通信技術**、5Gを連携させたビックデータ化なども港湾や橋梁など浅瀬にある設備の測量・点検・整備や水産業に大きく貢献できる技術として期待されています。

通信ネットワーク未開拓と言われる海中での通信技術が徐々に整いつつある中で、微力ながらアプリ制作という側面で貢献できたらいいなと思いながら、ベットに靴下を掛けるのでした。

[^1]: ログ付けとはダイビン中に見た魚などを履歴として残すこと
Firebaseを使い始めるために必要な手順


VueでFirebaseを使う時に初期化する方法でつまづいたのでメモです。

## 実装環境
macOS Big Sur 11.2.3
TypeScript 4.4.3
Vue 2.6.14
Firebase 10.0.1

## 初期化の方法

### `npm install firebase`をする。

```
npm install firebase
```


### `src/config/firebase-config.ts`ファイルを作成し、以下のようにコードをかく。

```typescript:src/config/firebase-config.ts
import { getStorage } from "firebase/storage";
import { getFirestore } from "firebase/firestore";
import firebase from "firebase/compat/app";

export const firebaseConfig = {
  apiKey: "process.env.VUE_APP_API_KEY",
  authDomain: "process.env.VUE_APP_AUTH_DOMAIN",
  projectId: "process.env.VUE_APP_PROJECT_ID",
  storageBucket: "process.env.VUE_APP_STORAGE_BUCKET",
  messagingSenderId: "process.env.VUE_APP_MESSAGING_SENDING_ID",
  appId: "process.env.VUE_APP_APP_ID"
};

//Firebase.initializeApp()を何度も呼び出すことを防ぐために以下のコードで対応
export const firebaseApp = !firebase.apps.length
  ? firebase.initializeApp(firebaseConfig)
  : firebase.app();

export const storage = getStorage(firebaseApp);
export const db = getFirestore();

```

### Firebase構成オブジェクト情報を隠しファイルに記載する。
Firebase構成オブジェクト情報は隠しておきたい情報なので、`.env`ファイルに以下のように書いておき、上のファイルから読み取れるようにしておく。また、[VueCLI3.x 4.xで環境変数を扱う場合は .env ファイルに VUE_APP_ の接頭語をつける](https://kic-yuuki.hatenablog.com/entry/2019/11/17/171058)ということなのでVUE_APPも忘れずにつけておく。



```typescript:.env
VUE_APP_API_KEY = "自分のapiKey"
VUE_APP_AUTH_DOMAIN = "自分のauthDomain"
VUE_APP_PROJECT_ID= "自分のprojectId"
VUE_APP_STORAGE_BUCKET = "自分のstorageBucket"
VUE_APP_MESSAGING_SENDING_ID = "自分のmessagingSenderId"
APP_ID = "自分のappId"
```

### gitにも上げないようにするため、`.gitigonre`ファイルにも以下のように記載する。

```typescript:.gitignore
./.env.
```

### 必要なものをimportする。
Firebase JavaScript SDK (v9.0.0)が2021年8月25日にリリースされたということで、前のバージョンであるv8系のコードとの互換性の問題がある。[v8系のコードをv9でも使える様にするためには、基本的にimport時のpathにcompatを加えるだけで使える](https://zenn.dev/hiro__dev/articles/605161cd5a7875)とのこと。
Firebaseのサービス共通で必要なのは以下のコード。

```typescript:exaple.vue

import firebase from "firebase/compat/app";
import "firebase/compat/auth";
import { firebaseConfig } from "@/config/firebase-config";

```

storageを使用する時には、


```
const storage = getStorage(firebaseApp);
```

Firestoreを利用する時には、

```
const db = getFirestore();
```

などの定義が必要。

それぞれの変数の使い方については[公式ドキュメント](https://firebase.google.com/docs/build?authuser=0)を参照。

また、`firebase/storage`や`firebase/firestore`などのモジュールから適宜必要なメソッドをインポートする。

# 終わりに
公式ドキュメントの方法だとうまくいかず、手探りで解決策を見つけました。
今のところ動いていますが、より良い方法がありましたらぜひ教えていただきたいです！
[Nuxt.js]generateしたファイルでaxiosのbaseURLがlocalhostになってしまう問題の対処

## 困ったこと
- axiosのURLを相対パスで書くとgenerateした静的ファイルでもlocalhostに向けてリクエストを投げてしまう。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1649366/ff4d312e-5635-9f14-01fb-9fdecd0f19a4.png)

## 解決策

- axiosのbaseURLを書き換えておく。

```js
  this.$axios.defaults.baseURL = window.location.origin
```

## 実装例

- axiosのbaseURLを書き換えるpluginを作成する。

```js:plugins/axios.js
export default function ({ $axios, store, redirect }) {

  // baseURLを書き換えておく
  $axios.defaults.baseURL = window.location.origin

  // ↓は蛇足
  $axios.onRequest((config) => {})
  $axios.onResponse((response) => {})
  $axios.onError((error) => {
    const code = parseInt(error.response && error.response.status)
    if (code === 400) {
      redirect("/400")
    }
  })
}
```

- 呼び出し用のpluginも作っておく。

```js:plugins/api.js
export default function (obj, inject) {
  const api = new API(obj.$axios, obj.store, obj.redirect)
  inject("api", api)
}

class API {
  constructor(axios, store, redirect) {
    this.axios = axios
    this.store = store
    this.redirect = redirect
  }

  async getRequest(url, params) {
    const res = await this.axios.$get(url, {
      headers: {
        "content-type": "application/json",
      },
      params: params,
    })
    return res
  }

  async postRequest(url, params) {
    const res = await this.axios.$post(url, params, {
      headers: {
        "content-type": "application/json",
      },
    })
    return res
  }
}

```

- nuxt.config.js でpluginをロードしておく

```js:nuxt.config.js
  plugins: [
    { src: "@/plugins/axios", ssr: false },
    { src: "@/plugins/api", ssr: false },
  ],
```

- 使うときはこんな感じで

``` js
 const res = await this.$api.postRequest(
     "/apipath",
     {data: hoge}
 )
```

## 参考にさせていただいた記事
[(Qiita)Nuxtのaxios-moduleがbaseURLを決定する仕組みの解説と、Netlify FormsへPOSTするときの対策](https://qiita.com/shozzy/items/69706cd550b407526884)
ClickOnceでインストールしたアプリをコマンドラインから起動する方法

# 方法
アプリケーション起動用のショートカットファイル(ファイルタイプ=Application Reference)に
拡張子.appref-msを付与するだけ。

# PythonでClickOnceアプリを起動するには

```python:Python
subprocess.run(r"rundll32.exe dfshim.dll ShOpenVerbShortcut C:\hogehoge.appref-ms")
AtCoder Beginner Contest 010をやった(Java)

AtCoder Beginner Contest 010をやった。
たぶんC問題解けるからのちに追加予定。


##[A](https://atcoder.jp/contests/abc010/tasks/abc010_1)
あだ名をどう求めるかです。

```Java
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        // Your code here!
        Scanner scan = new Scanner(System.in);
        System.out.println(scan.nextLine().concat("pp"));
    }
}
```

##[B](https://atcoder.jp/contests/abc010/tasks/abc010_2)
いい方法ないかなーと思ったけど、実践的に考えたら「1 <= 入力値 <= 9」らしいので、パターンで判定した

```Java
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        // Your code here!
        Scanner scan = new Scanner(System.in);
        int[] num = new int[scan.nextInt()];
        for(int i = 0; i < num.length; i++){
            num[i] = scan.nextInt();
        }
        int cnt = 0;
        
        for(int i = 0; i < num.length; i++){
            if(num[i] == 2 || num[i] == 4 || num[i] == 8){
                cnt += 1;
            } else if(num[i] == 5){
                cnt += 2;
            } else if(num[i] == 6){
                cnt += 3;
            }
        }
        
        System.out.println(cnt);
    }
}
```
ActiveRecordのNullリレーションが便利そう

# はじめに

Railsガイドを読み返していたら便利そうなものを見つけたので備忘録代わりに残しておきます。

# Nullリレーション

> noneメソッドは、チェーン (chain) 可能なリレーションを返します (レコードは返しません)。このメソッドから返されたリレーションにどのような条件をチェーンさせても、常に空のリレーションが生成されます。これは、メソッドまたはスコープへのチェーン可能な応答が必要で、しかも結果を一切返したくない場合に便利です。

https://railsguides.jp/active_record_querying.html#null%E3%83%AA%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3

```ruby
irb(main):001:0> User.none
=> []
irb(main):002:0> User.none.class
=> User::ActiveRecord_Relation
```

こんな感じで、空のリレーション（Nullリレーション）を返してくれる。

```ruby
irb(main):003:0> User.none.limit(1)
=> []
```

リレーションを期待しているコードに対しても正常に応答するので、より保守性の高いコードが書けそう。
Qiita Daily Survey memo 2021.12.24

# Qiita Daily Survey memo 2021.12.24
Qiita の記事を可能な限り毎日読むために、読んだmemoを公開することでモチベーションを維持しようという試み。（4か月ぶり）

## [永久保存版！エンジニア向け情報収集サイトをまとめてみた【定期更新】](https://qiita.com/tomox1001/items/384e27b6259efdb6064e)
Tomonori Kawano(tomox1001) さんの記事。
タイトルの通り、エンジニアが毎日触れる情報収集におすすめなサイトをまとめてくださっています。
まとめのまとめ、助かります…。
特に自分はアンテナを張るのが苦手なため、おおいに参考にさせていただきたいと思います。
Twitter や YouTube も後日追記とのことなので、正座待機です…！

## [必見！コードレビューするなら絶対に知っておきたい5つのコツと20+αの観点](https://qiita.com/vankobe/items/728d8ce836458d3ce0b7)
江上 真人(vankobe) さんの記事。
自分も現在業務で絶賛苦戦中のコードレビューについて、コツと観点をまとめてくださっている記事です。
レビューの際は一息ついてからがーっと上から読んでいきますが、もっと要領よくレビューする方法が書かれていて、さっそく参考にさせていただきたいと思います。
特にコツ３のところは、思い返してみると「確かに…」と思いました。（実際多い）

また、観点のチェックリストも非常に助かります。
毎回全部をチェックするのは大変そうですが、いくつか注意する点をピックアップしてレビューに臨みたいと思います！

## [アルゴリズムの世界地図](https://qiita.com/square1001/items/6d414167ca95c97bd8b2)
square1001 さんの記事。
アルゴリズムの種類を問題と合わせて紹介いただいている記事です。
大学時代は暗号方式の研究をしていたため、非常に興味深く読ませていただきました。
分かりやすい図を用いて問題とアルゴリズムを解説してくださり、問題の計算量（オーダー）にも触れていただき、とても有意義な記事でした。
よくある（初項＋末項）* 項数 / 2 のアルゴリズムでの計算速度比較で、ぐっと引き込まれてしまいました…そんなに違うものなのですね。
動的計画法のパートがすごい興味があるので、今日のお仕事が終わった後の楽しみに取っておきたいと思いました！
非常に興味深い記事をありがとうございます！

## [デザインに興味があるエンジニア向けのTips10選](https://qiita.com/xrxoxcxox/items/95959d1eab5a9483ba9a)
Keisuke Watanuki(xrxoxcxox) さんの記事。
タイトルの通り、デザインをする上で重要なTipsをまとめてくださっている記事になります。
普通に本になっていてもおかしくないような内容で、図も例もわかりやすく、興味深く読ませていただきました。
特に「錯視・視覚調整」のパートは読んでいて「やばい…」と思う自分のデザインが何個も思い浮かんでしまいました…。
苦手なジャンルなので、何冊か本を読んで勉強はしているのですが…いかんせん、実践が少なくまだまだ身についておりません。
参考にさせていただきます。

## [デザインを武器に戦えるエンジニアを目指す人が読むべき本](https://qiita.com/kichihide_y/items/bdd18c0cf74cfd263117)
kichihide_y さんの記事。
デザインのジャンルから2本目の記事です！
タイトルの通り、デザインに関する読むべき本を紹介してくださいっている記事です。
特徴的なのが、筆者の kichihide_y さんがデザインを学ぶ上で、段階的に時系列順で並べてくださっている点です。
まさに基礎of基礎ならこの本、CSS設計が気になるお年頃ならこの本と、分かりやすく分類してくださっていて助かります。
加えて、デザインを学んでお仕事で使っていると、こんな流れになる…というのがイメージしやすく、そのあたりも勉強になりました。
…まずは一番最初の本を読ませていただこうと思います…はい。
未経験からエンジニアになるまでの軌跡

こんにちは！
最近未経験からエンジニア転職した『まさきち』です
エンジニアを目指して約1年経ちましたので、これまでを振り返ってみようと思います。

## WEB系エンジニアを目指すキッカケ

前職は製造業で設計業務をしていたのですが、
激務で心身共に削られ疲弊しきっていたので転職を決意。
新しい事にチャレンジしてみようと思い前職とは違う業種で探していました。
その中で、エンジニアという職種が選択肢に上がり、
- リモートなど比較的自由な働き方
- レガシーでは無さそう
- 元々プログラミングにも関わる業務をしていた

以上の理由から目指す事にしました。
※スクールやサロンは良い面もあれば改善して欲しい部分も勿論ありますが、大人の事情でとりあえず良い面だけ書いておきます

## プログラミングスクール
調べていく内にプログラミングスクールという物があると知り独学よりも良いだろうと考えて探す事に。
その中で
- 大手スクールの安心感
- 時間をしっかり取ることができたのでみっちり学習できる
- 転職保証がある

以上の理由からテックキャンプに決定
ここで初めてプログラミング言語を学びました。
同じくらいの年代の５人チームで朝から晩までみっちり学ぶ勉強をするのですが、楽しんで勉強できました。
今でも数人ご飯を食べに行ったりします
メンター、エージェントのサポートが手厚く、モチベを高めてくれるので、当時自己肯定感が地の底に落ちていた私にとっては良い環境でした。
ポートフォリオ作成後はwantedlyなど使いながら転職活動していきました。

## 雑食系エンジニア
本やYouTubeで存在を知り、テックキャンプでは出来なかった共同開発出来るという事で参加する事にしました。
３人くらいで共同開発を学び良い経験となりました。
主にSlackチャットコミュニケーションで、たまにケンタさんのライブ配信で質問させていただいたりしてました。


## 闇SES
コロナ過という事もあり、転職活動は苦戦しましたがようやく内定をいただけました。
自社開発にも携われるという事だったのですが、蓋を開けてみればSESでした。
営業面談のとき担当者に「自社の名前は出さないで」と言われた時に一気に不信感が…
スキルシートを埋めて営業活動しましたが案件がなかなか決まらず…研修を受けながら探す事に。最初に紹介された案件は電気施工管理… 2、3ヶ月ほど経っても決まらず待機期間は無給だったので営業部長と専務と揉める。
結局内定辞退し、挙句の果てには研修費を請求される。

### その後も諦めずに勉強しながら転職活動をしていきました。

## デイトラ
時間もあるしWeb制作を学びたいと思い始めました。
コスパが良く、メンターに質問するとすぐ返ってくる印象です
動画教材のクオリティ高めで良くできているなと思いました。

## やんばるエキスパートPHP、Webデザイン
PHPの案件が多いので勉強する事に。
5人くらいのチームで学習し、共同開発も体験
メンターのサポートも手厚くコスパが良い
ポートフォリオのレビューもしてもらえるので助かりました
案件の取り方なども教えてくれました。

## フリーランス
ある日Twitterから業務委託どうですか？と紹介が。
とある会社と契約を結びLaravelで新規開発に携われる事に
1ヶ月は平日毎日フルリモートで快適に仕事をしていました。
しかし、前日に突然「明日から来なくていいよ」と切られてしまいました。弁護士に相談するも契約内容的に仕方が無いとの事。契約書はしっかり確認しましょう。もしくは自分で弁護士に作成してもらうのが良いと思います。

## ZERO To Hero
Twitterで実務の経験を積めるサロンがあると知り、経験を積みたかったので応募しました。1番最初の生徒として取り組むことに。
実務のチケットをこなしながら、メンターがコードレビューやアドバイスをくれるのでとても勉強になりました。
目標設定などもしてくれるのでプログラミング意外も学ぶことが出来ます。質問に丁寧に答えてくれる印象でした
テックキャンプ時代のからの友達みんてぃさんがメンターなのも安心感がありました。

## 都内Web系受託
フォロワーさんの紹介で面接を受けて主にPHP受託の会社に入社する事に。
研修で1ヶ月基礎をしっかり学びました。
自分が分かっていると思っていた事もしっかり理解出来ていないところも多々あり、良い復習にもなりました。

今は実務で保守案件で先輩のコードを見ながら機能の改修したりしてます。
社内にフロントやデザイン出来る人が少なく、求められているみたいなので、ゆくゆくはReactやデザイン、フロント寄りのバックエンドになるかもしれません。

これまで前途多難な日々でしたがTwitterや、ねこプロの皆さんとの絡みで結構救われました。
とても感謝しております。

これからも、日々スキルアップしていく為に仕事以外も学習を続けていきたいと思います。

最後まで読んでいただきありがとうございました。
外部ライブラリを利用時にonChangeが発火しなくて困った話

Qiita株式会社 Advent Calendar 2021の25日目の担当は、プロダクト開発グループの @kiitan です！

https://qiita.com/advent-calendar/2021/qiita

### はじめに
[Qiita Jobs](https://jobs.qiita.com/)ではプロフィール入力を楽にするため[Wantedlyのフォーム自動入力ボタン](https://www.wantedly.com/developers/generate_autofill_tag)を利用しています

[Wantedlyのフォーム自動入力ボタン](https://www.wantedly.com/developers/generate_autofill_tag) の機能に軽く触れておくと転職活動を行う際に必ず入力が必要な名前や年齢などを Wantedly上にプロフィールからワンクリックでフォームにコピーすることができる便利なボタンです

この記事は [Wantedlyのフォーム自動入力ボタン](https://www.wantedly.com/developers/generate_autofill_tag) を React で書かれたフォームに導入しようとして困った話です

### 入力されるがStateに反映されない

説明するより触ってもらったほうが早いので早速みていきましょう

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="bGooJGr" data-user="kiitan" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/kiitan/pen/bGooJGr">
  react hooks useState</a> by kiitan128 (<a href="https://codepen.io/kiitan">@kiitan</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

一見しっかり入力されているようですが `<p>あなたの年齢は{age}歳です</p>` の部分で年齢が表示されていないことで state へ反映されていないことがわかります

原因は Wantedly のフォーム自動入力ボタンで input の value を書き換えても onChange が発火しないことでした
これは Wantedly のフォーム自動入力ボタンが特別 onChange が発火しないのではなく value を javaScript で直接書き換えを行なった場合に発生します

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="QWqqPym" data-user="kiitan" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/kiitan/pen/QWqqPym">
  react hooks useState</a> by kiitan128 (<a href="https://codepen.io/kiitan">@kiitan</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

上記の例では `document.querySelector(".inputStyle").value = ` では `onChange` が発火しないことがわかります
これは通常であれば `document.querySelector(".inputStyle").onchange()` を入れることで解決できる問題です

しかし外部ライブラリを利用する今回の場合ではこの対策をすることができません

### 解決編
最終的なコードは泥臭いものとなりました
下記のコードのように state と value の値を setInterval で監視し差分があれば反映する方法を使いました

``` javascript
React.useEffect(() => 
  setInterval(() => {
    if(age !== ageRef.current.value) {
     setAge(ageRef.current.value)
    }
  }, 1000)
, [])
```

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="qBPPwmd" data-user="kiitan" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/kiitan/pen/qBPPwmd">
  react hooks useState</a> by kiitan128 (<a href="https://codepen.io/kiitan">@kiitan</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

これで無事stateに反映することができました
もっといいやり方やアイデアがあればコメント欄で教えてください:pray:

### 終わりに

明日の投稿を担当するのはこの記事を見た皆さんです
Blazor Sever のプロセス実行時、待ち受けアドレス/ポートを指定する 4 つの方法

Blazor Server アプリは、インターネット上へ公開しての運用のみならず、下記「Blazor serverをラズパイに入れてFAしたい」の記事のように、Blazor Server プログラム (これはつまるところ、コンソールアプリです) をそのまま、内蔵の Kestrel で稼働させる使い方もあります。

https://qiita.com/yamaccu/items/9e86db8d8fa44f43dcc1

さて、一般的な造りの Blazor Server プログラムの場合、"dotnet publish" コマンドなどで発行したプログラムファイルを実行すると、ローカルループバックアドレスの TCP 5000 番および 5001 番 (それぞれ HTTP、HTTPS) で待ち受けするのが既定の動作です。

この既定の動作のままですと、いわゆる "外部" からの HTTP 要求に、この Blazor Server アプリケーションは返答してくれません。
外部からの接続も受け付けるようにするためには、待ち受けアドレスを、例えば "http://+:80/" といった指定にする必要があります。

ではどのようにすれば、Blazor Server アプリケーションの待ち受けアドレスを変更できるでしょうか。

以下では、自分が知っているその方法 x 4つを紹介します。


# 方法 1. Program.cs で指定する

先に紹介の「Blazor serverをラズパイに入れてFAしたい」の記事で行なわれていた方法はこちらです。

Blazor Serevr アプリケーションの初期化時、Web ホストビルダーに対して、`.UseUrls()` 拡張メソッドを呼び出し、その引数に待ち受けアドレスを指定することで実現します。

.NET 6 のトップレベルステートメント/Minimal API スタイルで実装している場合は、下記のようなコードになることでしょう。

```csharp:Program.cs
...
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseUrls("http://+:80/");
...
```

# 方法 2. appsettings.json で指定する

C# ソースコード中で指定する以外に、`appsettings.json` アプリケーション構成ファイル中でも、待ち受けアドレスを指定できます。

下記のように、構成エントリ名 "urls" の値に、待ち受けアドレスを指定してあげれば OK です。

```json:appsettings.json
{
  "urls": "http://+:80/",
   ...
```

# 方法 3. コマンドライン引数で指定する

さてところで、Blazor Server アプリケーションといいますか、.NET ホストの構造に基づく .NET アプリケーション全般では、一般的な造りですと、構成情報は `appsettings.json` のみならず、プログラム実行時のコマンドライン引数からも読み取っています。

具体的には、コマンドライン引数にハイフン x 2つに続けて構成エントリ名を指定し、空白または `=` を挟んで値を書くと、構成情報として取り込まれます。

ということで、待ち受けアドレスを指定する構成エントリの名前は、`urls` なので、以下のようにコマンドライン引数を指定して実行すれば、待ち受けアドレスをプログラム実行時に指定できます。


```bash
$ ./BlazorServerApp --urls https://+:80/
```


# 方法 4. 環境変数で指定する

さらに実は、一般的な造りの .NET ホストアプリケーションでは、`appsettings.json` やコマンドライン引数に加えて、環境変数からも構成情報を読み取っています。

具体的には、"ASPNETCORE_{構成エントリ名}" および "DOTNET_{構成エントリ名}" というように、"ASPNETCORE_" および "DOTNET_" で始まる名前の環境変数が、構成情報として取り込まれます。

各 OS 環境やシェルに応じて適宜読み替えていただければと思いますが、例えば Ubuntu の bash シェル上ですと、以下のいずれかを実行して環境変数を設定したのちに、Blazor Server プログラムを実行すれば、指定した待ち受けアドレスで動作するようになります。


```bash
$ export ASPNETCORE_URLS=http://+:80/
```

```bash
$ export DOTNET_URLS=http://+:80/
```

# 優先順位

さてところで、これら待ち受けアドレスを指定する方法を同時に混ぜて指定するとどうなるでしょうか。

試してみたところ、下記の優先順位 (強い順) になるようです。

1. コマンドライン引数
2. 環境変数 ASPNETCORE_URLS
3. 環境変数 DOTNET_URLS
4. Program.cs (`UserUrl("...")`)
5. appsettings.json

(つまり、上記リスト中、より下の行の方法で指定した待ち受けアドレスよりも、より上の行の方法で指定した待ち受けアドレスのほうが採用されます)

# おわりに

以上、Blazor Server アプリケーションにおいて、待ち受けアドレスを指定する方法 x 4 つを紹介させていただきました。

冒頭のとおり、インターネット上への公開のみならず、工業用途等での Blazor Server アプリケーションの活用において、今回紹介の方法で待ち受けアドレスを構成するなど役に立てればと思います。

_Learn, Practice, Share!_
PHP ファイルパスやURLからファイル名や拡張子等を取得する

# 目的

- ファイルパスやURLから最後のファイル名や拡張子などを取得する方法をまとめる

# 詳細

- 下記のサービスを用いて今回紹介するコードの検証を行った。
    - [https://paiza.io/ja](https://paiza.io/ja)

# 方法

### `/`で区切られた最後のフィールドを取得する。

- `https://qiita.com/miriwo`の`miriwo`の部分を取得したい場合、下記の様に記載する。

    ```php
    <?php
    $path = 'https://qiita.com/miriwo';

    $endFeald = substr($path, strrpos($path, '/') + 1);

    echo $endFeald;
    // 「miriwo」 と出力される
    ```

### ファイルパスからファイルの拡張子を取得する。

- `/etc/nginx/conf.d/default.conf`の`default.conf`ファイルの拡張子部分`conf`のみを取得したい場合、下記の様に記載する。

    ```php
    <?php
    $filePath = '/etc/nginx/conf.d/default.conf';

    $extensions = substr($filePath, strrpos($filePath, '.') + 1);

    echo $extensions;
    // 「conf」と出力される
    ```

# 簡単な解説

- 使用している文字列を操作する関数を分解してどんなことをしているのか詳しく見てみる。

### `strrpos()`

- [strrpos](https://www.php.net/manual/ja/function.strrpos.php)
- 下記の様に呼び出す。

    ```php
    strrpos(評価対象の文字列, 区切り文字列, どこから数え始めるか): 数値か文字列を返す。
    ```

- 下記の様に記載した場合、「$pathの文字列の最後の'/'は$pathに格納されている文字列の1番目から何文字目（何バイト目）である」を出力している事となる。

    ```php
    echo strrpos($path, '/',);
    ```

### `substr()`

- [substr](https://www.php.net/manual/ja/function.substr.php)
- 割とよく使うPHPではおなじみの文字列の中からその一部を抜き出す関数である。
- 下記の様に呼び出す。

    ```php
    substr(評価対象の文字列, 文字列を抜き出す開始位置, 文字列を抜き出す開始位置からの長さ（バイト数）): 抜き出した文字列を返す。
    ```

- ちなみに「文字列を抜き出す開始位置からの長さ（バイト数）」を指定しなかった場合「文字列を抜き出す開始位置」から文字列の最後までを返す。
- 下記の様に記載した場合、「$strの文字列の10文字目（10バイト目）から文字列の最後までを切り出して返す」処理を行っている事となる。

    ```php
    substr($str, 10);
    ```

### 合わせると

- `substr($path, strrpos() + 1)`なので
- `substr($path, strrpos()にて返される最後の区切り文字までの文字数 + 1)`となり
- `substr($path, 最後の区切り文字から+1文字した数値)となるため、`strrpos()`で指定した区切り文字の後ろの文字列を取得する事ができる。
ヒーローズ・リーグ2021「カフェのサイズが覚えられない」

# はじめに
ヒーローズ・リーグ2021へ「[カフェのサイズが覚えられない](https://protopedia.net/prototype/2722)」を応募しました。

- ダイヤモンドさんの「スタバ」ネタが好き。
- チェーン店のカフェのサイズはお店毎に呼び方がちがって覚えにくい。
- 体でサイズを説明しながら楽しくチェーン店のカフェサイズを覚えたい。


![コンテスト応募_タイトル.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543183/640f24c0-baaa-730c-d082-d912a5c55d0c.png)

私がダイヤモンドさんのネタをやりたかっただけとも言えます。

# 応募した作品
元ネタを見てからアプリのデモ動画見ると、よりアプリを楽しめます。
[カフェのサイズが覚えられない](https://protopedia.net/prototype/2722)

元ネタはこちら
漫才『スタバ』/コーヒーのサイズ おもしろ荘優勝ネタ/ダイヤモンド(ダイヤモンドお笑いチャンネル)
<iframe width="880" height="497" src="https://www.youtube.com/embed/qk--6wlRWUY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

アプリのデモ動画はこちら
<iframe width="880" height="495" src="https://www.youtube.com/embed/wkVcHUqtIas" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# アプリを作って嬉しかったこと
ダイヤモンドの野澤輸出さんからツイートにいいねをもらいました！(今はいいねは消えています...)

![カフェのサイズいいね.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543183/b40eaae9-d62c-2757-079c-f7003cf68fb3.png)

## 体験できるカフェ
- スターバックス
- タリーズコーヒー
- ドトールコーヒー
- コメダ珈琲店
- 出世魚
- 竹
- 相撲
- 大仏

## 開発環境
現時点ではAndroidアプリのみです。

- Android Studio(Kotlin)
- MK Lit(Pose detection)
- 音声再生(Android標準)

![カフェのサイズ_仕組み.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543183/88bda39b-8317-d89e-a481-94a37e5bfdb2.png)

## 動作フロー
- 人型のガイドを表示して全身がカメラの画角に入っていることを確認します。
- ○のポーズを検出するまで待ちます。(左右の肩・肘・手首の角度で判定)
- 各サイズを指示し、全身の高さとサイズからスコアを計算します。
    - 現時点では高さのみで、腕の動作はスコア計算には含まれない。
- 登録しているチェーン店を繰り返す。
- 全てのチェーン店が完了したら、合計スコアを表示します。

## 苦労した点
- 全身の骨格推定からサイズ判定するため、カメラで自撮りしながらのデバッグに苦労しました。
    - ログ出力してもどこの動作だったか分かりにくいので最後は勘でデバッグした。
- テストしすぎて筋肉痛になった。
    - 太腿ふとくなっちゃうよ！
- 相撲の音声再生で前頭(まえがしら)を"ぜんとう"と発音されたこと。
    - 急遽よみがなリストを追加して音声再生するようにした。

## 今後追加したいカフェ
ネタにある体験できるカフェを増やしたいです。

- サンマルクカフェ
- ゴーゴーカレー
- コーンなど

# オレトク賞の決勝に選ばれて
ネタアプリとして開発したので期待はしていませんでしたが、オレトク賞の決勝への招待メールが届きました。
正直、えっ？こんなネタアプリで良いのかと思いましたが、せっかくのチャンスを断る理由もありません。体験できるカフェを追加(相撲と大仏)して臨みました。

オレトク賞の決勝へ参加してみると、毎年コンテストに応募して作品スタイルを貫き通している人、パワードスーツを自作してリアルタイムに利用しながら発表する人、車のスマートキーを自作してしまう人など強者揃いで圧倒されました。私はハッカソンやコンテストでの発表経験はありましたが、子犬ちゃんの気分で久々に緊張しました。

結果は残念ながら入賞はしませんでしたが、あとから聞くとウケていたので場は暖められたと思います。
また、審査員の方からは「(ネタが好きで作ったので)これこそオレトク賞だよねー」とお褒めの言葉をいただきました。

# 受賞
特別賞 byヒーローズ・リーグ運営事務局をいただきました！
https://protopedia.net/event/21

12/11の決勝で入賞作品が発表されました。一通り発表が終わり入賞はなかったので、みんなの作品に刺激をもらって来年も頑張ろうとツイートしてビンゴまでの休憩に入りました。
しかし、休憩が開けると、審査員から特別賞があるとアナウンスあり(気分はビンゴ待ち)、なんと「カフェのサイズが覚えられない」が特別賞を受賞しました！また来年〜とツイートした数分後だったので、驚いたのと来年こそと言いながら今年受賞してるやんと笑いました。お笑い芸人のネタをネタアプリで体験できたのが良かったです。

# おわりに
このアプリは家族と動画撮影に協力してくれた友人くらいしか体験できていません。オフラインで会える世の中なら、みんなに忘新年会やクリスマスパーティーでカフェのサイズを競い合ってもらいたいです。
また、ヒーローズ・リーグや動画を見た友人などから、ぜひ体験したいのでアプリをリリースして欲しいとの要望もあり、頑張ってストアに公開しようと思います。(まずはAndroidから)

アプリの構想からコンテスト応募までに1ヶ月くらいかかり、構想したときより予想以上に苦労しました。もっと早くから作り始めてればと思いました。今年の作品からヒントを得たアイデアもあるので、来年もコンテストに応募して入賞したいので、年明けからネタの構想を練って走り出したいと思います。