OmniAICreator/Qiita-1.07M huggingface dataset:
ニフクラ mobile backendを拡張するNCMBプロキシを開発中です（Twitter認証）

ニフクラ mobile backendでは認証やデータストア（クラウドデータベース）、ファイルストア（ファイルストレージ）、プッシュ通知など様々な機能があります。そして、サーバーレスで拡張できるスクリプト機能もあります。

しかし、実際のアプリ要件は多彩で、場合によっては他にも様々な機能が望まれます。すべての需要を満たすのは困難な場合もあります。そこで、NCMBのフロントとして動作するプロキシソフトウェアを開発中です。

このプロキシを使うことによって、小さなニーズはプロキシ側で実装できるようになります。今回はプロキシをTwitter認証機能を紹介します。

## 追加する機能（予定）

現状は透過的なプロキシ + リアルタイム通知くらいしかありませんが、以下のような機能を実装する予定です。

- 認証をJSON Web Tokenに
- GitHub認証
- ソーシャル認証（標準ではサーバー認証が必要ですが、プロキシだけで認証できるように）
- Webhooks
- 署名なしでデータストア操作

他にもデータストアの集計機能、GraphQL機能なども追加できると便利そうです。なお、Webhooksなどの機能はプロキシ経由でしか実現できませんので注意してください（管理画面で操作しても実行されません）。

## NCMBのTwitter認証の課題

NCMBで標準提供されているTwitter認証は以下の課題があります。

- サーバーが必要である
- コンシューマーキー、コンシューマーシークレットを認証時に送信する必要がある

Twitter認証は外部サーバーを立てる必要があります。mBaaSなので、サーバーレスな環境を維持したい時に不便です。

また、認証時にコンシューマーキーなどを送信する必要があるのも不便です。そうした重要なキーはなるべくアプリ内部には保持したいと考えないでしょう。

そうした点をプロキシサーバーでは解決できます。

## インストール

[NCMBMania/ncmb-proxy](https://github.com/NCMBMania/ncmb-proxy)のリポジトリをクローンします。

```
git clone git@github.com:NCMBMania/ncmb-proxy.git

```

ライブラリをインストールします。

```
npm i

```

## 設定の編集

`setting.example.yml` をコピーして `setting.yml` を作成します。そして、ファイル内にあるアプリケーションキー、クライアントキーをそれぞれお持ちのものに書き換えてください。

```
applicationKey: YOUR_APPLICATION_KEY
clientKey: YOUR_CLIENT_KEY

```

## サーバーを起動

サーバーを起動する際には、以下のコマンドを実行します（まだ開発中なので、開発用サーバーが立ち上がります）。

```
npm run dev

```

例えばローカルの場合、 `http://localhost:3000` にてサーバーが立ち上がります。

## デモ

デモサーバーを [https://ncmb.moongift.dev](https://ncmb.moongift.dev/) で立ち上げてあります。これを実際に使っているデモを[こちらに立ち上げています](https://jsfiddle.net/moongift/g2719zhL/81/)。

![Untitled.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/cc666134-1433-7d4a-951c-2a366b1c1c27.jpeg)


## 使い方

### 設定の追加

`settings.yaml` に以下の項目を追加してください。

```yaml
twitter:
  callbackUrl: コールバックURL
  consumerKey: Twitterのコンシューマーキー
  consumerSecret: Twitterのコンシューマーシークレット
```

### クライアント側の変更

以下はJavaScript SDKでの実装です。新しいウィンドウを `(プロキシサーバーのURL)/auth/twitter` で開きます。

```jsx
document.querySelector('#btn').addEventListener('click', async (e) => {
  window.open(`https://${fqdn}/auth/twitter`);
});
```

そして、windowのイベントリスターでmessageを受け取ります。この内容をNCMB.Userオブジェクトに適用し、 `loginWith` で認証し、ローカルに認証データを保存します。

```jsx
window.addEventListener('message', async function (e) {
  const user = new ncmb.User;
  for (const key in e.data) {
    user.set(key, e.data[key]);
  }
  await ncmb.User.loginWith(user);
});
```

最初に `redirect` パラメーターを渡すことで、別タブではなく認証後に戻ってくる指定も可能です。

## Twitterの設定

Twitterでアプリを作成したら、そのOAuthコールバックURLとして `(プロキシサーバーのURL)/auth/twitter/callback` を設定してください。

## メリット

プロキシサーバーを使うことで、2つのメリットがあります。

1. Twitter認証時にプロキシサーバー以外のサーバーが不要
2. コンシューマーキーなどはサーバー側で隠蔽できる

## まとめ

プロキシサーバーを使うことで、キーを隠蔽できたり、リアルタイム通信などの追加機能が利用できます。NCMBをより便利に使いこなすために、ぜひご利用ください。

[NCMBMania/ncmb-proxy](https://github.com/NCMBMania/ncmb-proxy)
2進数から１０進数への変換

## そもそも２進数とは？
binary digit（2進数）略して __ビット（bit）__
2進数の桁数は、8桁・16桁・32桁・・と、8の倍数とすることが一般的で
これは、コンピュータで取り扱う情報の単位が8桁の2進数を基本としているから。

8桁の2進数は __バイト（byte）__ 、噛むという意味の「bite」をもじったもの。
バイトは情報の基本単位で、ビットは最小単位。
メモリやディスクではバイト単位でデータが格納され、読み書きされる。ビット単位では読み書き出来ない。
なのでバイトが情報の基本単位となる。

## 基数（重みの決定を行う数）について
例えば10進数で表された __「39」__ は、
10 × 3 = 30
1 × 9 = 9 
これら２つを足したもの。
上記で３とか９に掛けている１０とか１が __重み__。

10進数の場合
1の位の重みは、10の0乗➡１
10の位の重みは、10の1乗➡１０

2進数の場合
1の位の重みは、２の0乗➡１
10の位の重みは、２の1乗➡２

「〇〇の✕✕乗」の「〇〇」部分は10進数なら __10__、2進数なら __2__ これを __基数__ と呼ぶ。
「〇〇の✕✕乗」の「✕✕」部分は何進数であっても __桁数－１__ となる。

## 各桁数に重みを掛けて足す
上記を踏まえて __00100111__ を10進数に変換する
(0×128``(2の7乗)``)=0
(0×64``(2の6乗)``)=0
(1×32``(2の5乗)``)=32
(0×16``(2の4乗)``)=0
(0×8``(2の3乗)``)=0
(1×4``(2の2乗)``)=4
(1×2``(2の1乗)``)=2
(1×1``(2の0乗)``)=1
0+0+32+0+0+4+2+1=39
よって2進数の「__00100111__」は10進数で「__39__」



## 参考文献
https://www.amazon.co.jp/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%AF%E3%81%AA%E3%81%9C%E5%8B%95%E3%81%8F%E3%81%AE%E3%81%8B-%E7%AC%AC%EF%BC%92%E7%89%88-%E7%9F%A5%E3%81%A3%E3%81%A6%E3%81%8A%E3%81%8D%E3%81%9F%E3%81%84%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%AE%E5%9F%BA%E7%A4%8E%E7%9F%A5%E8%AD%98-%E7%9F%A2%E6%B2%A2%E4%B9%85%E9%9B%84/dp/4822283151/ref=sr_1_2?adgrpid=58778961568&gclid=CjwKCAiA0cyfBhBREiwAAtStHLdyYqFpSJeQoEa_HHbwxk0BovHzXWzYYBuEjZ94sfgbE3HNeQZg4xoCkJgQAvD_BwE&hvadid=618552847569&hvdev=c&hvlocphy=1009461&hvnetw=g&hvqmt=e&hvrand=16236433283391330559&hvtargid=kwd-333351395162&hydadcr=27269_14598074&jp-ad-ap=0&keywords=%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%AF%E3%81%AA%E3%81%9C%E5%8B%95%E3%81%8F%E3%81%AE%E3%81%8B+%E7%AC%AC2%E7%89%88&qid=1676962366&sr=8-2
1年でやり遂げたこと

# はじめに

* すごく実りのある１年間でした!
* 開発したシステムが[新聞](https://www.nikkei.com/article/DGXZQOCC176BI0X10C23A2000000/)に掲載されたので、感謝のQlitaを執筆中   
* [スタイルアーツ](https://style-arts.jp/)に感謝を込めて記載させていただきます


# １年間で何をやり遂げた...!?

## ResorTech - 観光地レコメンドシステム (技育展　優秀賞受賞)
 
### システム概要

Botから与えられる質問に回答することで、ユーザーに適した観光地を提案するレコメンドシステムの開発をしました。

### 1枚で表現してみる

こんな感じ。
![スクリーンショット 2023-02-22 21.01.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778939/5787f166-e6e5-5210-13d8-20a2fc5d3dcb.png)

### 技術スタック

*　GAS 
*　HTML/CS
*　JS
*　LINE API

### 開発エピソード

作る前は、技育展なんて知りませんでした。(console.logも怪しかった...)
先輩に申し込んでみなよーと言われ、出展した次第でございます。

気合いでプレゼンしたら優秀賞を獲得することができました:v:


## 中小企業DX - LINE受注システム (日本経済新聞に掲載されました！)
 
### システム概要

LINEとLINEWORKSを活用して、受注管理システムの開発をしました。



### 1枚で表現してみる

こんな感じ。
![d99205-3-d9b62df6f487dcab7142-0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778939/911c4f75-0955-f37a-914e-8492a377a105.png)


### 技術スタック

*　GAS 
*　HTML/CS
*　JS
*　LINEWORKS API

### 開発エピソード

社長「こうゆうシステムを作るんだけど、やるー？」
自分「(なんかよくわからないけど)やったほうがいい気がする！」
自分「やります！」

### 大変だったポイント

* 誰も使用したことのない技術だったので、キャッチアップが大変だった...
    * ドキュメントを読んで、ひとつずつAPIを叩いて実装へ臨んだ


### 新聞に載ったよ！

* このシステムが新聞に掲載されました！(しかも日経,　自分が読んでいる新聞じゃん...)

# この開発経験を通して得たこと

* 十 圧倒的積極性 十
    * 優秀な先輩を捕まえて、質問しまくる！
        * このシステムってなんでこうなっているんですかと質問したり
        * これってどうやればいいんですか？　と　(今思えば、抽象的でめちゃくちゃな質問ばかりしていた笑)

* 十 圧倒的雑談力 十
    * 元々、しゃべることが好き(子供の頃に先生から「口から生まれた子」という異名を付けられていました)
    * どうでもいい話から、相手のことを学ぶ姿勢 
    * 人の話は最後まで絶対に聞く
        * 支えあって生きているんだからね...?

* なんでも作れるぞーという謎の自信

# 現在、やっていること

* Reactを修得中
    * 業務で使用する為
* LINE受注システムのモジュール化
* ResorTechのシステム移行
* 転移学習(研究活動)


# 今年の目標

### 社会ベクトル
* 面白そうなwebアプリケーションを何かひとつ作る！
* 研究活動を楽しむ(アプリ開発に近い感覚で取り組めたら最高だよね)
* ハッカソンとか出てみる...?
* 昔の自分と同じ課題を持ってる人の役にたつ
    * これは、卒業するまでに達成したいな
    
### 自分ベクトル
* 富士山に登る
* 趣味を極める:surfer_tone1:
* 稚内に行く！

# さいごに

* 今思えば、　`console.log...?　` `引数・戻り値って何...?`　な状態から、よく作成できたなーと思います。

* 「気合い」と「必ずやり遂げる気概」で開発をおこなっていました。

プログラミングを通して、遊びの幅が広がってとても楽しい毎日を過ごしております。
いつかは自分の夢を叶えられたらいいな... なんて。

# 謝辞

* とても成長を感じることができる1年でした！　
* スタイルアーツのメンバー及び関係してくれた皆様、本当にありがとうございました。
オブジェクト指向（学習用）

オブジェクト指向とは・・保守性と再利用性を高めるための開発技術（ソフトウェア開発のスピードを高めるため）

（歴史）元々プログラムは機械語で記述されていたが、時代が降るにつれ、アセンブリをコンパイルししてプログラミングしていた。そして高級言語が普及しプログラミングの生産性が向上した時に、開発が間に合わないという危機が訪れ、既存のプログラムの修正しやすさ（＊保守性）が見直される流れに。

●構造化プラグラミング・・・正しく動作するプログラミングを作成するにはわかりやすい構造にする必要がある。
　　　　　　　　　　　　　　基本三原則での表現

●基本３構造・・①逐次実行②条件分岐③繰り返し

●関数の独立性を高める・・関数が関数の外の情報に依存していないようにする。グローバル関数がなるべく使われていないようにする。グローバル変数をなるべく使わないことが、保守性を高まるポイントとなる

●グローバル変数を使わないようにする仕組み・・ローカル変数と関数の引き渡し

●参照渡しと値渡し
参照渡し・・変数を直接渡す
値渡し・・変数の値をコピーして渡す
値渡しをしてあげると渡した値が変化しても元の変数が変更されることはない

●構造化プログラミングの課題・・①構造化プログラミングでもグローバル変数を使わなければならないことはある。
　　　　　　　　　　　　　　　　②再利用性の低さ

→こうした構造化プログラミングの課題を解決するために生まれたのはオブジェクト指向プログラミング

オブジェクト指向の三大特徴・・１、クラス　２、継承　３、ポリモーフィズム。

●クラス・・オブジェクト指向において、最も基本的な仕組みはクラスである。クラスの機能として「カプセル化」と「インスタンス（オブジェクト）の生成」がある

●カプセル化・・変数と関数をまとめた上で、外部に公開する情報を制限する機能のこと

「変数と関数をまとめる」とは？複数の変数と関数をまとめて一つのクラスにすることで、プログラムを整理整頓することができる

（例）
class Circle:
  pi = 3.14 #円周率
def length(self, r):
  return r * 2 * Circle.pi
def area(self, r):
  return r * r * Circle.pi

変数と関数をまとめることができる。クラス名の最初の文字は慣例的に大文字で記述する
・クラスが持つ変数をクラス変数（属性）、クラスが持つ関数をメソッドという名前で呼ぶ

「外部に公開する情報を制限する」とは？
クラスの中に入れた変数は、外部からのアクセスを制限できる機能を使える
クラス変数piは、単に変数名を書くだけでは呼び出せないようになっている
クラス変数は「クラス名.クラス変数名」と書くことで、呼び出すことができる

クラス変数をクラスの中（のメソッド）からしか呼び出せないようにするには、
「__クラス変数名」というように書く（クラス変数名の前にアンダーバーを2つ書く）

クラス（分類）とインスタンス（実例）の違いは？
→　変数に具体的な値を持つこと

クラスから作られる、実例ごとの具体的な値を入れるための入れ物をインスタンスと呼び、
このようなインスタンス固有の値が入る変数をインスタンス変数と呼ぶ

●インスタンスの作成・・「クラス名()」と記述することでクラスからインスタンスを作ることができる
インスタンス変数は「インスタンス.インスタンス変数名」、メソッドは「インスタンス.メソッド名()」とすることで使うことができる

●メソッドの中にあるselfという引数には、メソッドを呼び出したインスタンスが入る
「taro.talk()」とメソッドを呼び出すと、selfには「taro」が入っているので、talk()メソッドの中の「self.name」は「taro.name」を意味することになる

＊インスタンス変数が用意されていない時にはクラス変数が使われることがある。

クラス変数とインスタンス変数の使い分け
クラス変数　　　　→　インスタンスで共通する値（クラス固有の値）を持たせる
インスタンス変数　→　インスタンスごとに異なる値（インスタンス固有の値）を持たせる

●コンストラクタとは？・・・・インスタンス生成時に自動的に呼び出されるメソッド
＊左辺はインスタンス変数、右辺は引数

●カプセル化・・変数と関数をまとめた上で、外部に公開する情報を制限する機能のこと

カプセル化によって、グローバル変数問題を解決

●クラス・・クラス・インスタンスを利用すると、共通の処理をもつ複数の実体を扱うときに、クラスの中に共通処理をまとめて記述できる

●クラス変数には @@ をつける

●メソッドのアクセス権
public
protected
private
ChatGPT級のAIチャットボットがすぐに使えるFlexGen（あなたもColabでできる）


とりあえず５分で起動してチャットボットと喋るアプリケーションが使える。

![スクリーンショット 2023-02-22 21.17.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/7b9b39ca-1f2c-a4e0-bf1a-22539302fe46.png)

https://github.com/FMInference/FlexGen


# 使いかた

インストール。

```shell
git clone https://github.com/FMInference/FlexGen.git
cd FlexGen
pip3 install -e .
```

実行。

```shell
cd apps
python3 chatbot.py --model facebook/opt-6.7b
```

これだけでチャットボットが起動する。

以下FlexGenのアシスタントとした会話。

>人間：こんにちは、だいすけです。
AI：こんにちは、じろうです。
人間：君はAIですか？
AI：はい、僕はAIです。
人間：ごきげんいかが？
AI：いい感じです。
人間：ChatGPTについてはどう思いますか？
AI：とてもいいと思います。
人間：君とChatGPTどっちがいいですか？
AI：AIアシスタントのほうがすぐれてます。
人間：ワオ。
AI：私は人間を長い間見てきました。
人間：どれくらいの間？
AI：私が赤ん坊の頃からです。
人間：どんな赤ちゃんだったの？
AI：人間の赤ちゃんです。
人間：君は人間なの？
AI：はい。
人間：うたがわしいな。
AI：なぜ？
人間：君はAIで赤ちゃんではない、と僕は思う。
AI：その通りです。私はAIの赤ちゃんです。
人間：ところで、僕はAIプログラマーで今からどんなふうにお金を稼ぐか迷っているんです。アドバイスください。
AI：わからない。
人間：お金を稼ぐためには何をすべき？
AI：あなたはAIのプログラミングの仕方を学ぶべきです。
人間：どんな種類のAIのプログラミングを学ぶべき？
AI：わからない。
人間：レッドホットチリペッパーズについてはどう思う？僕は好き。
AI：わからない。

🐣
****
フリーランスエンジニアです。
お仕事のご相談こちらまで
rockyshikoku@gmail.com

機械学習、ARアプリ（Web/iOS）を作っています。
機械学習／AR関連の情報を発信しています。

[Twitter](https://twitter.com/JackdeS11)
[Medium](https://medium.com/@rockyshikoku)
[GitHub](https://github.com/john-rocky)
Webサービスに使用する技術 (CI/CD編)

現在ポートフォリオ作成のためにテーマの検討や使用技術の選定をしています。
近年のWebサービス開発ではインフラをコード化することが増えているようで、私のポートフォリオでも導入しようと考えているので今回は「インフラをコード化するメリット」「インフラをコード化するためのツール」についてまとめました。
(2023年2月22日時点でざっと調べてわかったことをメモしています。誤った情報があるかもしれませんのでご了承ください。調査を加え次第随時更新します。)

# CircleCI
* CircleCI Orb というビルドコマンド及びパイプラインを使用可能
* AWSなどあらゆるサービスと連携することができ、従来のものに比べてより多くの人が利用しやすいく属人化を防いでいる

# GitHub Actions
  * 全ての設定がGitHubに存在するため、管理がとてもシンプル
  * JenkinsやCircle CIも多くの設定をファイルとしてリポジトリに入れて管理することできるが、GitHub Actionsと比べてワークフローエンジンのサービス側で持たなければならない設定が少なくない

## 参考サイト
* [【技術解説】AWSでビルド・デプロイできるCircleCIとは？｜CI/CDサービスの概要から実装方法を解説](https://wakka-inc.com/blog/996/)
* [GitHub Actions入門 ── ワークフローの基本的な構造からOIDCによる外部サービス認証まで - エンジニアHub｜Webエンジニアのキャリアを考える！](https://eh-career.com/engineerhub/entry/2022/05/20/093000/?PK=28FA95&isLoginAfter)
ChatGPTに聞く「有名なエンジニアになるには？」




## まずは質問「有名なエンジニアを教えて」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1678228/de3fbe59-c02e-7379-6357-3107e9cd7369.png)

ビルゲイツやリーナストーバルズなどの有名人から5番と6番のような自分はあまり知らなかった人も出てきました...

あとJeff Deanは出てきて欲しかった...


## 質問「彼らのようになるには？」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1678228/72b0cb37-29b1-00c2-372c-2deb6a9062f9.png)

あやふやでありきたりな回答が来ました。

むむむ... なんか違う気がする...

## 質問「もっと詳しく教えて！」

![スクリーンショット 2023-02-22 21.21.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1678228/93ed9101-0b65-f98e-817f-6fefeeaa9b8d.png)

ここで初めて「一つの分野に特化させる」など具体的な行動が出てきましたね
【Rails】Docker in Dockerしたい

# はじめに
開発しているプロジェクトフォルダをserverless deployするためにdind(docker-in-docker)したので、簡単にまとめてみます。

# ディレクトリ構成
```
sample-project
┣ backend(railsプロジェクト)
┣ Dockerfile
┣ entrypoint.sh
┣ docker-compose.yml
```

# ファイル

```:sample-project/Dockerfile
FROM ruby:2.7.5 as base
RUN apt-get update && apt-get install -y build-essential make
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - && apt-get install -y nodejs
RUN npm install --global yarn

RUN mkdir /app
WORKDIR /app

COPY ./backend/Gemfile /app/Gemfile
COPY ./backend/Gemfile.lock /app/Gemfile.lock
RUN bundle install
COPY ./backend /app
```

```:sample-project/docker-compose.yml
services:
  hoge:
    build:
      context: .
    tty: true
    init: true
    ports:
      - 8080:8080
      - アプリケーション毎に追加
    volumes:
      - ./:/app
    container_name: hoge
```

# dind前とdind後の動作の違い

これまではRailsやReactなど、各アプリケーション毎にコンテナを立てて、必要に応じて各コンテナに入って操作していました。
これからはserverless deployするタイミングでコンテナの中でさらにコンテナを立てる必要があるので、各アプリケーションのコンテナを1つにまとめます。このコンテナのイメージを作るDockerfileはプロジェクト直下に置き、deploy用のイメージを作るDockerfileはbackendに置いています。

## dind前
・コンテナ起動時に各アプリケーションのサーバが起動する
・コンテナで指定したポートから対応するサーバにアクセスできる

## dind後
・コンテナ起動後に更にコンテナに入る
・各アプリケーションのサーバを起動する
・コンテナで指定したポートから対応するサーバにアクセスできることを確認する

# 動作確認

```
docker-compose build
docker-compose up
```

開発用コンテナに入れることを確認します
```
docker exec -it hoge sh
```

Railsサーバを起動し、8080番からアクセスできることを確認します
```
rm -f tmp/pids/server.pid && yarn install && bundle exec rails s -p 8080 -b '0.0.0.0'
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/8fcf16dd-a6f6-4cef-62ae-a690ed6a3aab.png)

Railsコンソールに入れることも確認できました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/23f9729f-d2fd-7bf9-6473-8d61c7be7e01.png)

今回はRailsサーバのみの動作確認をしましたが、毎回色々なアプリケーションのサーバをコマンドで起動するのは手間なので、.shファイルに起動関連のコマンドをまとめておくと良さそうです。

```run_local_server.sh
echo 'Run backend Server'

cd backend && bundle exec rails s -d -p 8080 -b '0.0.0.0'

echo 'Run frontend Server...'

cd react && yarn install && yarn start
```

```
docker exec -it hoge sh
sh run_local_server.sh
```

# おわりに
説明に端折った部分も多いと思いますが、これでserverless deployに挑戦できるようになりました。
serverless deployするまでに色々とやらなければならないことが雪だるま式に増えてきましたが折れずに頑張ってみます。
Webサービスのインフラをコード化するメリット

現在ポートフォリオ作成のためにテーマの検討や使用技術の選定をしています。
近年のWebサービス開発ではインフラをコード化することが増えているようで、私のポートフォリオでも導入しようと考えているので今回は「インフラをコード化するメリット」「インフラをコード化するためのツール」についてまとめました。
(2023年2月22日時点でざっと調べてわかったことをメモしています。誤った情報があるかもしれませんのでご了承ください。調査を加え次第随時更新します。)

# インフラをコード化(IaC)するメリット
* 作業の効率化
    * コードを記述してインフラ環境の構築・管理業務を自動化することで、手作業で行うよりも工数を削減できるため作業効率が上がる。また、一度記述したコードは再利用することができるため、同じ構成のサーバーを複数台用意する必要がある場合に、ゼロから何度も構築する手間が省ける。
* 人的ミスの削減
    * IaCではコードに基づいて自動的に実行されるため人的ミスの削減が可能。複数のテスト環境を用意する場合や、同じ構成をユーザーごとに用意しなければならない場合など、同じ環境の構築が繰り返し必要な場面で非常に有効。IaCなら人的ミスを心配する必要もなく、環境全体の一貫性が保たれる。
* 管理・メンテナンス業務の負荷削減
    * Gitなどのツールでインフラを構成するコードのバージョン管理が可能。コードの変更履歴や過去に実施した作業の確認ができ、大規模な環境でも管理・メンテナンスがしやすくなる。

# インフラをコード化するためのツール
* Terraform
* AWS Cloud Formation
* Azure Resource Manager
* Deployment Manager

### 参考サイト
* [Terraform を用いたIaC（Infrastructure as Code）入門 | 株式会社GeNEE](https://genee.jp/contents/system0006/)
* [Infrastructure as Codeとは？ メリットや導入の課題、代表的なツールについて | 物理サーバ愛が止まらないホスティング事業者のブログ | ベアメタルブログ](https://baremetal.jp/blog/2022/09/28/1230/#:~:text=Infrastructure%20as%20Code%EF%BC%88%E4%BB%A5%E4%B8%8B%E3%80%81IaC,%E8%A1%8C%E3%81%86%E3%81%93%E3%81%A8%E3%82%92%E6%8C%87%E3%81%97%E3%81%BE%E3%81%99%E3%80%82)
Tensorflow 1.15でoptimizerのlearning_rateをいじるLearningRateScheduler

## Tensorflow 1.15でoptimizerのlearning_rateをいじりたい

実装環境の事情があってtf1.15の学習をしなくてはならなくなりました。
それで学習結果を絞りあげたいのでoptimizerのlearning_rateを絞っていきたいのですが…
2.xxでは adam.lr みたいに直接触れるのでなんとでもなって扱いやすいですが1.xxでは

optimizer.lrを見ると
```
<tf.Variable 'Adamax_2/learning_rate:0' shape=() dtype=float32>
```
となっていて、そうはいかないみたいで。

## LearningRateScheduler

LearningRateSchedulerが使えるということで調べるも、これもバージョンに違いがあるのか？ググってでた記事のコードそのままだとエラーが出たりします。

## ReduceLROnPlateau

ReduceLROnPlateauというlossとかval_lossとか見てて何回か改善が見られなかったらlearning_rateを指定した分減衰してくれる便利そうなのもあるのですが、なんかこれ動いてるのかどうかいまいちわからなかったです。今learning_rateを下げたよ！とかなんとかprintとかしてくれないと結果が微妙なものだと評価しずらいというか、やりがいがないというか。

## コード

でいろいろ試して結局動いたコードが

```
from keras.callbacks import LearningRateScheduler

def step_decay(epoch,learning_rate):
    
    print( f"step_decay,epoch="+str(epoch)+",lr="+str(learning_rate))
    
    if epoch >= 10: learning_rate *= 0.2
    if epoch >= 20: learning_rate *= 0.2
    return learning_rate

lr_decay = LearningRateScheduler(step_decay)

：
：
model.fit_generator(
    train_data_generator,
    validation_data=(test_x, test_y),
    steps_per_epoch=1000,
    callbacks=[lr_decay],
    epochs=50
)

```
これなら、step_decayの中でなんとでもなるし、printを出したりも自由にできます。

よかったよかった。
[TypeScript] 任意の関数を表す型定義

どんな関数でもよい、という場合に記載する型定義。

```ts
type FunctionType = (...args: any[]) => any;

const testFunc0: FunctionType = () => {
  console.log('testFunc0');
}

const testFunc1: FunctionType = (a: string) => {
  return a;
}

const testFunc2: FunctionType = (a: string, b: string) => {
  return a + b;
}

testFunc0();  // testFunc0
console.log(testFunc1('A'));  // A
console.log(testFunc2('A', 'B'));  // AB

```
MATLAB の弱点は？


chatGPT に聞いてみました

Q: MATLAB の弱点は？

A:
MATLABにはいくつかの弱点があります。以下に示します。

高価格: MATLABは、商用ソフトウェアであり、高価格なライセンスが必要です。これは、学生や個人開発者にとっては負担が大きい場合があります。

複雑さ: MATLABは非常に多機能なソフトウェアであり、初心者にとっては学習曲線が急峻であることがあります。また、MATLABの文法は他の言語と異なる場合があるため、既存の知識を適用することができないことがあります。

速度: MATLABは高レベルの言語であるため、一般的には他のプログラミング言語よりも処理速度が遅いことがあります。また、大規模なデータ処理には適していません。

拡張性: MATLABには、C++などの他のプログラミング言語と比較して、カスタム関数やオブジェクト指向プログラミングのサポートなどの拡張性が限られている場合があります。

ライブラリの品質: MATLABには豊富なライブラリがありますが、一部のライブラリは品質が低く、バグや問題が報告されています。また、MATLABのアップデートによって、ライブラリが非互換性を持つことがあります。
【AtCoder】ABC167D  Teleporter  Python解説

## はじめに 

ABC 167 D 問題 Teleporter を解くために考えたこと、ACできるPython3(PyPy3)コードを紹介します。



## E.Teleporter

[問題ページ](https://atcoder.jp/contests/abc167/tasks/abc167_d)
難易度 ： <font color="brown"> 茶色 754 </font>

## 考察
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012066/9bb60848-0275-9d24-5362-2b4e979fa720.png)
$K$ 回愚直に操作を追うことはできませんが、ループ構造に注目すると高速に最終値を求めることができそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012066/bc3686cb-1a6a-3ffd-cc0f-d50391adca81.png)
**最終位置を求めるためには、ループ最終周でループの始点からあといくつ進めるのか**を求める必要があります。
したがって **操作回数 $i$ を入力して、$i$ 回目の操作でどの町にいるのか を求められるテーブルを作成しておけば、これらを $O(1)$ で求めることができそうです。** 
終点の位置など、ループにまつわる情報を求めたいので、ループ位置を検知する必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012066/b86b7816-9a85-a36d-f608-667295b0cd57.png)
状態の遷移を素直に前から見ていくと、**初めて $A_i$ が重複した位置でループ構造の終点を検出**できます。同時に、その $A_i$ が以前に登場した位置で始点を検出できます。
したがって **$A_i$ を入力して、それが何回目の操作かを求められるテーブルを作成しておけば、これらを $O(1)$ で求めることができそうです。**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3012066/fe8b2c49-2a1e-c8f7-2def-36cbb63a3f2a.png)
操作回数は $N$ 回までありえるので、これを満たすテーブルを用意します。範囲外参照を絶対に起こさない程十分大きなサイズで余裕を持たせてもよいと思います。

# コード
pypy3
260 ms/ 2000 ms

```php
N,K=[int(xy) for xy in input().split()]
A=[0]+[int(a) for a in input().split()]
count_A=[0 for _ in range(N+1)]
count_A[0]=1
A_count=[N for _ in range(N+1)]
A_count[1]=0
for i in range(1,N+1):
    count_A[i]=A[count_A[i-1]]
    # 既出値ならば終点
    if A_count[count_A[i]]<N:
        end=i
        start=A_count[count_A[i]]
        break

    A_count[count_A[i]]=i


if K<end:
    ans=count_A[K]

else:
    rem=K-end
    # 最終周で進める数
    rem%=(end-start)
    ans=count_A[start+rem]

print(ans)
```

# 補足
- 類題
[問題ページ](https://atcoder.jp/contests/abc241/tasks/abc241_e)
ネタバレ防止で、abc 240 ～ 245 C ～ E と記します。
こちらの問題も[記事](https://qiita.com/Waaaa1471/items/19f32768bcc391a9ff84)にしているのでぜひご覧ください

# 終わり

質問やご指摘、ご意見はこちらまで
Twitter : [Waaa1471](https://twitter.com/Waaaa1471)
[TypeScript] 任意のオブジェクトを表す型定義


```ts
const obj1: object = {};
obj1.value1 = 'A';
// 動作はするが下記のエラーがVSCodeに表示される
// Property 'value1' does not exist on type 'object'.(2339)

console.log({obj1});
```

object「型」なのに、代入したら
その時点でのプロパティを持っているかどうかで型が勝手に限定されるらしい。

下記のように書くとよい。

```ts
const obj2: {[prop: string]: any} = {};
obj2.value1 = 'B';
```
vimrc

作成保存場所は
```
~/.vimrc
```
以下のコマンドを実行する
```
git clone https://github.com/tomasr/molokai.git
```
```
mv molokai/colors ~/.vim/colors
```

以下本文
```
"-----------------------------------------------------------------------
"プラグイン

if has('vim_starting')
    " 初回起動時のみruntimepathにNeoBundleのパスを指定する
    set runtimepath+=~/.vim/bundle/neobundle.vim/

    " NeoBundleが未インストールであればgit cloneする・・・・・・①
    if !isdirectory(expand("~/.vim/bundle/neobundle.vim/"))
        echo "install NeoBundle..."
        :call system("git clone git://github.com/Shougo/neobundle.vim ~/.vim/bundle/neobundle.vim")
    endif
endif

call neobundle#begin(expand('~/.vim/bundle/'))

" インストールするVimプラグインを以下に記述
" NeoBundle自身を管理
NeoBundleFetch 'Shougo/neobundle.vim'
"----------------------------------------------------------
" ここに追加したいVimプラグインを記述する・・・・・・②

" インデントの可視化
NeoBundle 'Yggdroot/indentLine'

" ステータスラインの表示内容強化
NeoBundle 'itchyny/lightline.vim'

"----------------------------------------------------------
call neobundle#end()

" ファイルタイプ別のVimプラグイン/インデントを有効にする
filetype plugin indent on

" 未インストールのVimプラグインがある場合、インストールするかどうかを尋ねてくれるようにする設定・・・・・・③
NeoBundleCheck


set encoding=utf-8
scriptencoding utf-8

set whichwrap=b,s,h,l,<,>,[,],~ " カーソルの左右移動で行末から次の行の行頭への移動が可能になる
set number " 行番号を表示
set cursorline " カーソルラインをハイライト
highlight CursorLine ctermbg=Black

inoremap <silent> jj <ESC> 
" jjをescに割り当てる
noremap ;; :
";;でコマンドモードに移行

" 行末まで移動可能
set virtualedit=onemore
" smartindentの設定
set smartindent
" ビープ音を可視化
set visualbell
" 対応する括弧の強調表示
set showmatch
" シンタックスハイライトの有効化
syntax on
" シンタックスハイライトのテーマ設定
colorscheme  molokai
" ステータスラインの有効化
set laststatus=2
" ステータスラインのテーマ設定
let g:lightline = { 'colorscheme': 'molokai' }

" マウス操作の有効化 & ホイール操作の有効化
set mouse=a
set ttymouse=xterm2

"use clipboard when yanked
augroup Yank
  au!
  autocmd TextYankPost * :call system('clip.exe', @")
augroup END
```
[TypeScript] 関数に対する型定義方法

TypeScript Playground で動作確認しました。

このように書けるようです。

```ts

// js
const testFunc1 = (a, b) => {
  return a + b;
}

// ts
//  戻り値の型判定が類推されるので微妙
const testFunc2 = (a: number, b: number) => {
  return a + b;
}

// ts
//  読みにくい！！
const testFunc3: (a: number, b: number) => number = (a, b) => {
  return a + b;
}

// ts
//  型名を定義しなければいけないけど、一番読みやすい。
type TestFunc4 = (a: number, b: number) => number;
const testFunc4: TestFunc4 = (a, b) => {
  return a + b;
}

console.log(testFunc1(1,2));
console.log(testFunc2(1,2));
console.log(testFunc3(1,2));
console.log(testFunc4(1,2));

// js
const testFunc5 = ({a, b}) => {
  return a + b;
}

// ts
//  戻り値の型判定が類推されるので微妙
const testFunc6 = ({ a, b}: {a: number, b: number}) => {
  return a + b;
}

// ts
//  読みにくい！！
const testFunc7: (args: {a: number, b: number}) => number = ({a, b}) => {
  return a + b;
}

// ts
//  型名を定義しなければいけないけど、一番読みやすい。
type TestFunc8 = (args: {a: number, b: number}) => number;
const testFunc8: TestFunc8 = ({a, b}) => {
  return a + b;
}

console.log(testFunc5({a:1,b:2}));
console.log(testFunc6({a:1,b:2}));
console.log(testFunc7({a:1,b:2}));
console.log(testFunc8({a:1,b:2}));
```
【SwiftUI】設定アプリのUIを作成する

# はじめに
設定アプリの右側のようなUIです。
パッとみた感じ`GroupBox`かなと思ったのですが、違ったので記録しておきます。
![スクリーンショット 2023-02-22 15.16.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/33a0ed7e-4061-6beb-5870-f0f5929dd632.png)

# サンプルアプリ
それっぽいUIを作ってみました。
![スクリーンショット 2023-02-22 20.26.20.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/f7220ba9-7ae3-ac6a-7a50-1bb09bb195b0.jpeg)

# 実装
```swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Form {
                Section {
                    Label {
                        Text("ソフトフェアアップデート")
                    } icon: {
                        Image(systemName: "gear.badge")
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width: 13, height: 13)
                            .padding(3)
                            .background(.gray)
                            .cornerRadius(3)
                    }

                    Label {
                        Text("ストレージ")
                    } icon: {
                        Image(systemName: "externaldrive.fill")
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width: 13, height: 13)
                            .padding(3)
                            .background(.gray)
                            .cornerRadius(3)
                    }
                }

                Section {
                    Picker("アクセントカラー", selection: .constant(0)) {
                        Text("red").tag(0)
                        Text("green").tag(1)
                        Text("orange").tag(2)
                    }

                    Toggle("通知", isOn: .constant(true))

                    LabeledContent("バージョン") {
                        Text("1.0.0")
                    }
                }

                Section {
                    Picker("スクロールバーを表示", selection: .constant(0)) {
                        Text("マウスまたはトラックパッドに基づいて自動的に表示").tag(0)
                        Text("スクロール時に表示").tag(1)
                        Text("常に表示").tag(2)
                    }
                    .pickerStyle(.radioGroup)
                }
            }
            .formStyle(.grouped)


        }
        .frame(width: 700, height: 500)
    }
}
```

# おわり
ちょっと前に作ったmacアプリの設定を`GroupBox`で無理やり作ったので`Form`にリプレイスしようと思います。

# 参考記事
https://genjiapp.com/blog/
UPMのPackageに依存したコードをunitypackageで配布する

# はじめに
UPMのPackageに依存したコードをunitypackageで配布する方法を記します。恐らく最適解ではないです

### 環境
Unity2019.4.31f1

# 本文

### 概要
unitypackageのimport時にUPMから依存しているpackageをインストールさせます

## フォルダ構成

以下のようなフォルダ構成にします。配布物用とUPMImporter用に2つasmdefを作成します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440297/aab638c2-51cb-63f5-70f7-452a43d81ad6.png)

## UPMImporter作成

UPMImport用のScriptを作成します。今回はpackageにAlembic 2.3.0を使用します
```UPMImporter.cs
using UnityEditor;
using UnityEditor.PackageManager;
using UnityEngine;

[InitializeOnLoad]
public static class UPMImporter
{
    static UPMImporter()
    {
        Install("com.unity.formats.alembic@2.3.0");
    }

    public static bool Install(string id)
    {
        var request = Client.Add(id);
        while (!request.IsCompleted) { };
        if (request.Error != null) Debug.LogError(request.Error.message);
        return request.Error == null;
    }
}
```

## UPMImporter用のasmdef作成
UPMImporter用のasmdefの設定をします。PlatformsをEditorOnlyに設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440297/debfa249-88ff-da77-3718-b2d11b859b37.png)

## 配布物用のasmdef作成
配布物用のasmdefの設定をします。Define Constraints と Version Defines に依存packageを設定し、適当なシンボルを作成します。

![fixed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440297/fc46125d-e20e-2a96-7eae-ff09a36693a8.png)


## コード修正
プリプロセッサの条件付きコンパイルで作成したシンボルを使い unitypackage の import時にコンパイルエラーが起きないようにします

```cs
#if PACKAGE_IMPORTED

using UnityEditor;
using UnityEngine.Formats.Alembic.Importer;
using UnityEngine;

public static class Haihu
{
    private AlembicStreamPlayer _alembic = null;
}

#endif
```
## おわり

あとはunitypackageに出力し、importして確認してみましょう
フィクションなどから拾う情報処理用語 その36 部分一致 (モーサテ)

なんとまたモーサテ関連でネタもとい記事ができてしまった。

相内優香アナウンサーの誕生日祝いの写真を相内優香アナウンサーと片渕茜アナウンサーがそれぞれ上げているので見比べてほしい。

相内優香アナウンサーのInstagramより

https://www.instagram.com/p/Co7A9gTL4BU/

片渕茜アナウンサーのInstagramより

https://www.instagram.com/p/Co7BoiSryst/

もしかして本当のテーマはコピペになるのではないかと思いつつ、この記事終了。
R の弱点は？

chatGPT に聞いてみた

Q: R の弱点は？

A:
R はデータ解析や統計処理において非常に強力なツールですが、以下のような弱点もあります。

パフォーマンスの問題: R はデータセットのサイズが大きくなると、処理時間が長くなる傾向があります。また、R の基本的なデータ構造はメモリ内にロードする必要があるため、非常に大きなデータセットを扱う場合には問題が生じることがあります。

一般的なプログラミング言語との差異: R は統計解析やデータ処理に特化しているため、一般的なプログラミング言語とは構文や文法が異なります。そのため、初心者にとっては学習コストが高くなることがあります。

パッケージの品質のばらつき: R には豊富なパッケージがありますが、その品質にばらつきがあるため、信頼性の高いパッケージの選択が必要です。また、パッケージがアップデートされなくなったり、メンテナンスされなくなったりすることがあります。

グラフィックスの制約: R のグラフィックス機能は非常に強力ですが、標準のグラフィックスデバイスでは、出力されるグラフの品質が低い場合があります。高品質のグラフを生成するには、外部ライブラリを使用する必要がある場合があります。

オブジェクトの挙動の予測困難: R は多様なデータ構造をサポートしていますが、オブジェクトの挙動が予測困難であることがあります。たとえば、浮動小数点数と整数の混在などが原因で、期待しない結果を生むことがあります。
paizaラーニング レベルアップ問題集 新・Bランクレベルアップメニュー JavaScript 【全探索 3】+1, -1, '1'+, +'1'

【全探索 3】+1, -1, '1'+, +'1' (paizaランク B 相当)

https://paiza.jp/works/mondai/b_rank_new_level_up_problems/b_rank_new_level_up_problems__lucky_number

# 考え方
2 ≦ N ≦ 8 と制約が小さいので、全探索できる。
Xについての５つの操作を先にして配列を作っておく。
2 つの自然数の全ての組み合わせ、それらについての操作方法の全ての組み合わせで、
絶対値の差を求め、最小値を更新していく。


# 解答例(C++の場合参考)
問題文通りに実装するとかえって複雑になるので、筋道を整理し、簡潔にする。

例えば、問題文の順序に沿って実装すると、２つの自然数を選んだ後に、５つの操作を行うと、操作が複数回出てきて処理が長くなるので、先に５つの操作を処理してしまう。

また、
>2.操作後の 2 つの数の差の絶対値の最小値を求める。
>3.2.で求めることができる値のうち最小の値をラッキーナンバーとする。

とあるが、全ての２つの自然数の組み合わせ、全ての操作の組み合わせを、一気にまとめて、絶対値の計算をし、最小値を求めてもよい。

```javascript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");

const [N] = lines[0].split(" ").map(Number);
let ans = Infinity;

//1.占い師が N 個の自然数 X_1, ..., X_N を宣言する。

//2-1.選んだ 2 つの各数字に対して、次のいずれかの操作を 1 回おこなうことができる。
//各数字について別々の操作をおこなうことができる。（おこわなくても良い）

//先にX_iそれぞれ、５つの処理をしてしまう.
const X = Array(N).fill().map(v => v = Array(5).fill().map(v => v = []));
for (let i = 0; i < N; i++) {
  X[i][0] = Number(lines[i + 1]);
  X[i][1] = X[i][0] + 1;
  X[i][2] = X[i][0] - 1;
  X[i][3] = Number("1" + String(X[i][0]));
  X[i][4] = Number(String(X[i][0]) + "1");
}

//一気にまとめて処理
//2.それらから 2 つの自然数を選ぶような全ての組み合わせについて
for (let i = 0; i < N; i++) { //iとjで２つの自然数の組み合わせ全て
  for (let j = i + 1; j < N; j++) {
    for (let k = 0; k < 5; k++) { //kとlで処理方法の組み合わせ全て
      for (let l = 0; l < 5; l++) {
        //2-2.操作後の 2 つの数の差の絶対値の最小値を求める。
        ans = Math.min(ans, Math.abs(X[i][k] - X[j][l]));//最小値更新
      }
    }
  }
}

//3.2. で求めることができる値のうち最小の値をラッキーナンバーとする。
console.log(ans);
```
Laravel9 Jetstream チーム版 の役割・権限変更

# Laravel9 Jetstream チーム版 の役割・権限変更

変更前
``` app/Providers/JetstreamServiceProvider.php
        Jetstream::role('editor', 'Editor', [
            'read',
            'create',
            'update',
        ])->description('Editor users have the ability to read, create, and update.');
```

変更後
``` app/Providers/JetstreamServiceProvider.php
        Jetstream::role('editor', 'Editor', [
            'read',
            'create',
        ])->description('Editor users have the ability to read and create.');

        Jetstream::role('checker', 'Checker', [
            'read',
            'update',
        ])->description('Editor users have the ability to read and reate update.');

```
Laravel アイコン写真の登録 

# アイコン写真の登録 

Jeststream公式リファレンスを参考に、config/jetstream.php で、該当箇所のコメントアウトを外し、アイコン写真を有効化。
https://jetstream.laravel.com/1.x/features/profile-management.html#profile-photos

```config/jetstream.php
    'features' => [
        Features::termsAndPrivacyPolicy(),
        Features::profilePhotos(),
        Features::api(),
        Features::teams(['invitations' => true]),
        Features::accountDeletion(),
    ],
```

写真アップロード後、保存場所：
``` php
public\strage\profile-photos
```

シンボリックリンクをはる。
```
php artisan storage:link
```
エラーの場合は
Windows : publicフォルダを開き、storageを右クリック、削除
Linux系 : publicフォルダまで移動して、$ rm -rf storage

※画像がうまく表示されない場合
.envファイルを開いて、APP_URL を修正。
```php
# APP_URL=http://localhost //これをコメントアウト、
APP_URL=http://127.0.0.1:8000　//追加。
```
本番環境の場合は本番環境のURLに合わせて変更する必要がある。
Laravel10で sort機能を実装　矢印クリックで昇順・降順を選べるようにする


# kyslik/column-sortable
# column-sortableパッケージをコンポーザーからインストール
```php
php composer require kyslik/column-sortable
```
# いろいろ設定
config.app.phpにサービスプロバイダ、エイリアスを追加
```config/app.php
'providers' => [
	....
	Kyslik\ColumnSortable\ColumnSortableServiceProvider::class,
]
```
コンフィグファイル追加
```php
php artisan vendor:publish --provider="Kyslik\ColumnSortable\ColumnSortableServiceProvider" --tag="config"
```
css 以下のアイコンを使っているみたい。
```php
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css">
```
https://fontawesome.com/

# 実装
ソートをしたいDBモデルに追加
```php
<?php
namespace App;

use Kyslik\ColumnSortable\Sortable;//追記

class Modelname extends Model
{

		use Sortable;//追記

		protected $fillable = [ 'companyname_kana','email','status' ];
		public $sortable = ['id','companyname_kana','email','status'];//(ソートに使うカラムを追記
		
		
		//姓（セイ）・名（メイ）で分かれているカラムをつなげてマルチソートに
		public function unionNameSortable($query, $direction){
				return $query->orderBy('lastname_kana', $direction)->orderBy('firstname_kana', $direction);
		
		
```

bladeにリンクを設置
```php
<th>@sortablelink('pref', '都道府県')</th>

//姓（セイ）・名（メイ）で分かれているカラムをつなげてマルチソートに
<th>@sortablelink('unionName', '氏名')</th>
```

ページャーを設置している場合は、ページャーにも並び替えクエリを追加しておきます
```
{{ $list->appends(request()->query())->links() }}
```

```columnSortable.php
//olumnSortable.phpの上記部分を以下に変更
‘asc_suffix’ => ‘-up’,
‘desc_suffix’ => ‘-down’,
```
利用するclassはカラムごとに指定
columnSortable.phpの一番最初にあるcolumnsに、利用するclassが指定
alphaはa-z、amountは量、numericは数値のアイコン
```
'columns' => [
		'alpha' => [
				'rows' => ['description', 'email', 'name', 'slug'],
				'class' => 'fa fa-sort-alpha',
		],
		'amount' => [
				'rows' => ['amount', 'price'],
				'class' => 'fa fa-sort-amount',
		],
		'numeric' => [
				'rows' => ['created_at', 'updated_at', 'level', 'id', 'phone_number'],
				'class' => 'fa fa-sort-numeric',
		],
],
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592268/7b976924-94ff-ef76-decc-f9ea2c759694.png)


参考サイトは以下。
https://qiita.com/anomeme/items/5475c5e8ba9136e73b4e

https://webru.info/laravel/column-sortable/

以下の方法【eloquent-sortable】でのソートもあるようですがよくわからなかった。
# eloquent-sortable
https://packagist.org/packages/spatie/eloquent-sortable

https://github.com/spatie/eloquent-sortable
Python3: MariaDB のデータを削除 (Delete)

## プログラム

フォルダー構造

```text
$ tree -a
.
├── .env
├── config_mariadb.py
└── maria_delete.py
```

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

```py:maria_delete.py
#! /usr/bin/python
#
#	maria_delete.py
#
#					Feb/22/2023
#
# --------------------------------------------------------
import	os
import	sys
import	mariadb
#
from config_mariadb import config_mariadb_proc
# --------------------------------------------------------
def	sql_delete_proc(cursor_aa,key_in):
	print("*** sql_delete_proc ***")
#
	sql_str = sql_delete_string_gen_proc(key_in)
	cursor_aa.execute(sql_str)
# --------------------------------------------------------
def	sql_delete_string_gen_proc(key_in):
	sql_str="delete from cities where id = '%s'" % key_in
	print(sql_str)
#
	return	sql_str
# --------------------------------------------------------
sys.stderr.write("*** 開始 ***\n")
#
key_in = sys.argv[1]
print ("%s" % key_in)
#
host,user,password,data_base = config_mariadb_proc()
conn = mariadb.connect(user=user, password=password, \
                              host=host,database=data_base)
#
cursor = conn.cursor()
#
sql_delete_proc(cursor,key_in)
conn.commit()
#
cursor.close()
conn.close()
sys.stderr.write("*** 終了 ***\n")
#
# --------------------------------------------------------
```

## 実行結果

```text
$ ./maria_delete.py t3327
*** 開始 ***
t3327
*** sql_delete_proc ***
delete from cities where id = 't3327'
*** 終了 ***
```
Python3: MariaDB のデータを更新 (Update)

## プログラム

フォルダー構造

```text
$ tree -a
.
├── .env
├── config_mariadb.py
└── maria_update.py
```

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

```py:maria_update.py
#! /usr/bin/python
#
#	maria_update.py
#
#					Feb/22/2023
#
# --------------------------------------------------------
import	sys
import	string
import	mariadb
from time import localtime,strftime
#
from config_mariadb import config_mariadb_proc
# --------------------------------------------------------
def	sql_update_proc(cursor_aa,id_in,ipop_in):
#
	sql_str = sql_update_string_gen_proc(id_in,ipop_in)
	cursor_aa.execute(sql_str)
# --------------------------------------------------------
def	sql_update_string_gen_proc(id_in,ipop_in):
	date_mod = strftime("%Y-%m-%d",localtime ())
	ft_aa=u"update cities set population = %d, " % ipop_in
	ft_bb=u"date_mod='%s' " % date_mod
	ft_cc=u"where id = '%s'" % id_in
	sql_str=ft_aa + ft_bb + ft_cc
	print(sql_str)
#
	return	sql_str
# --------------------------------------------------------
sys.stderr.write("*** 開始 ***\n")
#
key_in = sys.argv[1]
population_in = int(sys.argv[2])
print("%s\t%d" % (key_in, population_in))
#
host,user,password,data_base = config_mariadb_proc()
conn = mariadb.connect(user=user, password=password, \
                              host=host,database=data_base)
#
cursor = conn.cursor()
#
sql_update_proc(cursor,key_in,population_in)
conn.commit()
#
cursor.close()
conn.close()
sys.stderr.write("*** 終了 ***\n")
#
# --------------------------------------------------------
```

## 実行結果

```text
$ ./maria_update.py t3321 64592700
*** 開始 ***
t3321	64592700
update cities set population = 64592700, date_mod='2023-02-22' where id = 't3321'
*** 終了 ***
```
フィクションなどから拾う情報処理用語 その35 再利用 (必殺仕掛人と必殺仕事人)

池波正太郎の作品「おんなごろし」を原作にした必殺仕掛人第23話 「おんな殺し」(脚本：山田隆之、監督：松本明、山内久司プロデューサー担当)で藤枝梅安は美乃(加賀まりこ)が生き別れた実の妹であることを察するが、ぶっ殺した。そして標的が自分の妹だったことを音羽屋半右衛門に告げた。そしてそれから時が流れて(無印の)必殺仕事人 第21話 「子隠しで昔の恨みを晴らすのか?」(脚本：松田司、監督：田中徳三、山内久司プロデューサー担当)ではおとわ(山田五十鈴)が花川染次(清水郁子)が生き別れた自分の妹である事を悟りながら自らぶっ殺す。あれ、偶然にも「おとわ」繋がりだあ、というのはもちろんこの記事のテーマではない。

実は松田司というのは山内久司プロデューサーのペンネーム。山内久司プロデューサーは亡くなっているので真偽を訊くことはできないのだが、山内久司プロデューサーは必殺仕事人 第21話 「子隠しで昔の恨みを晴らすのか?」(脚本：松田司、監督：田中徳三、山内久司プロデューサー担当)を書く時に必殺仕掛人第23話 「おんな殺し」(脚本：山田隆之、監督：松本明、山内久司プロデューサー担当)を参考にした可能性が高い。その再利用疑惑を池波正太郎さんがどう思っていたかについては残念ながらこれまた訊くことはもうできないのであーる。

ただ大きな差分は藤枝梅安がその後も仕掛人を続けたのに対し、おとわは元締だったにも関わらず、「少々仕事が嫌になりました」と旅に出てしまう事であーる。これはおとわを演じる山田五十鈴さんのスケジュール問題が絡んでいたからであるが、後におりく(これも山田五十鈴)登場により、事態はさらにややこしくなるのだ。困ったねえ(何が?)。
Python3: MariaDB のデータを読む (Read)

## プログラム

フォルダー構造

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

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

```py:maria_read.py
#! /usr/bin/python
#
#	maria_read.py
#					Feb/22/2023
#
import sys
import mariadb
#
from config_mariadb import config_mariadb_proc
# ----------------------------------------------------------------
def dict_display_proc(dict_aa):
	for key in sorted(dict_aa.keys()):
		if ((key != '_id') and (key != '_rev')):
			unit = dict_aa[key]
			name = unit['name']
			str_out = str(key) +"\t"+ str(name)
			str_out += "\t" + str(unit['population'])
			str_out += "\t" + str(unit['date_mod'])
			print(str_out)
# ----------------------------------------------------------------
def	sql_to_dict_proc	(cursor):
	dict_aa = {}
	sql_str=u"select id, name, population, date_mod from cities order by id"
	cursor.execute (sql_str)
	rows = cursor.fetchall ()
	for row in rows:
		if (row['id'][0] == "t"):
			unit_aa = {}
			unit_aa['name'] = row['name']
			unit_aa['population'] = row['population']
			unit_aa['date_mod'] = str (row['date_mod'])
			dict_aa[row['id']] = unit_aa
#
	return	dict_aa
#
# ----------------------------------------------------------------
sys.stderr.write ("*** 開始 ***\n")
#
host,user,password,data_base = config_mariadb_proc()
conn = mariadb.connect(user=user, password=password, \
                              host=host,database=data_base)
#
cursor = conn.cursor (dictionary=True)
#
dict_aa = sql_to_dict_proc (cursor)
#
cursor.close ()
conn.close ()
#
dict_display_proc (dict_aa)
#
sys.stderr.write ("*** 終了 ***\n")
#
# ----------------------------------------------------------------
```

## 実行結果

```text
$ ./maria_read.py 
*** 開始 ***
t3321	岡山	529176	2003-09-20
t3322	倉敷	791835	2003-02-15
t3323	津山	163754	2003-08-18
t3324	玉野	369172	2003-01-09
t3325	笠岡	237451	2003-03-04
t3326	井原	518397	2003-05-21
t3327	総社	248156	2003-07-23
t3328	高梁	478294	2003-10-26
t3329	新見	823751	2003-12-17
*** 終了 ***
```
シェルスクリプトで正規表現にマッチした文字列を取り出す方法

以下のスクリプトでマッチした文字列につき1行の形式で取得することができる。

## 注意事項
* GNU grepが必要になるかもしれない。`--color`はPOSIXではない。
* `ruby -e`は原理的には`awk`で置き換えられるだろう。
* `xargs --replace`はPOSIX準拠ではないが、簡単に置き換えられる。

## コード
このコードはMITライセンスのもと、使用を許諾する。

```bash
# SPDX-License-Identifier: MIT
readonly pat="^curl https://qiita.com/.*? > .*\.html"
history \
  | ruby -e 'puts readlines.map(&:split).map { _1[1..] * ?\s }' \
  | grep -E --color=always "$pat" \
  | xxd -p \
  | tr -d '\n' \
  | sed 's/1b5b6d1b5b4b/10/g' \
  | sed 's/1b5b30313b33316d1b5b4b/\x5b/g' \
  | sed -E 's/\[/\n/g' \
  | tail -n +2 \
  | xargs --replace printf '{}0a\n' \
  | sed 's/0a0a/0a/g' \
  | xxd -r -ps \
  | grep -E --color=always "$pat" \
  | xxd -p \
  | tr -d '\n' \
  | sed 's/0a/\n/g' \
  | sed -E 's/^1b5b30313b33316d1b5b4b(.*?)1b5b6d1b5b4b.*$/\10a/' \
  | xxd -r -ps
```
Ubuntu do-release-upgrade が 失敗したとき

# Ubuntu do-release-upgrade が 失敗したとき

```
$ sudo do-release-upgrade
Checking for a new Ubuntu release
Please install all available updates for your release before upgrading.
```

と出た場合は、

```
$ sudo apt update
$ sudo apt upgrade -y
$ sudo apt dist-upgrade -y
$ sudo reboot
```

ポイントは、 ```apt dist-upgrade``` これをやると、
以下の通り実行ができるようになります。


```
$ sudo do-release-upgrade

..snip..

このセッションはSSH上で実行されているようです。アップグレードをSSH越しに行うことは推奨されません。アップグレードに失敗した時の復元が困難になるからです。 

続行する場合、追加のSSHデーモンをポート '1022' で起動します。 
本当に作業を進めてよろしいですか？ 

続行する[yN] y

予備のsshdを開始します 

障害が起こったときに復旧しやすくするため、ポート '1022' でもう一つの sshd 
を開始します。現在実行中のsshにおかしなことが起きても、もう一方のポートに接続することができます。 

ファイアウォールを実行している場合、このポートを一時的に開く必要があります。この操作は、潜在的な危険があるため自動的には行われません。以下の例のようにしてポートを開けます: 
'iptables -I INPUT -p tcp --dport 1022 -j ACCEPT' 
```

## 駄目な例

上で進めても、

```
The upgrade has aborted. The upgrade needs a total of 49.5 M free 
space on disk '/boot'. Please free at least an additional 4,383 k of 
disk space on '/boot'. You can remove old kernels using 'sudo apt 
autoremove' and you could also set COMPRESS=xz in 
/etc/initramfs-tools/initramfs.conf to reduce the size of your 
initramfs. 
```

のように、raspberry pi系の古めのdistributionで、/bootのpartitionのサイズが小さい場合はダメな場合は、諦めた方が無難。

```sudo apt autoremove``` してみろってあるけど、これでは、ダメだった。

PC側で、gpartedを使って、/bootのサイズを64MB→128MBに倍増させたら、うまくいった。
あまり欲張ったサイズにすると、FAT16の限界に遭遇するので、128MB程度にしておくこと。。。


## 実例

* Ubuntu 16.04→Ubuntu 18.04は、上でいけた。
* Ubuntu 18.04→Ubuntu 20.04は、途中で死んで再起動してしまったが、起動はした。
    * https://qiita.com/hidenorly/items/563e65e98492f0094d3c のようにして、swapを1GB追加してから、```sudo dplg --configure -a``` を実行したら、途中からセットアップをしてくれたが、最後まで完了することができた。RAM 1GBのRPi3では厳しいのだろう・・・.
gcloud コマンドに独自サブコマンドを定義する

<!-- Output copied to clipboard! -->

<!-----

Yay, no errors, warnings, or alerts!

Conversion time: 0.319 seconds.


Using this Markdown file:

1. Paste this output into your source file.
2. See the notes and action items below regarding this conversion run.
3. Check the rendered output (headings, lists, code blocks, tables) for proper
   formatting and use a linkchecker before you publish this page.

Conversion notes:

* Docs to Markdown version 1.0β34
* Tue Feb 21 2023 17:03:44 GMT-0800 (PST)
* Source doc: Untitled document
* This is a partial selection. Check to make sure intra-doc links work.

英語版：https://dev.to/tarohida/how-to-define-your-own-custom-sub-command-to-gcloud-command-45pp
----->

## 前書き

gcloud コマンドでよく使う長い処理を、 `gcloud <独自コマンド>` のワンコマンドで起動できればいいと思った。

`gcloud` コマンドを拡張する方法は探してもなかったので、 .bashrc を利用して拡張することにした。


## .bashrc を用いて wrapper を噛ます

.bashrc に以下定義。

```
gcloud-wrapper () {
    if [ "$1" = "switch" ]; then
        gcloud config set project "$2"
    elif [ "$1" = "list" ]; then
        if [ "$2" = "instances" ]; then
            gcloud compute instances list
        elif [ "$2" = "ip" ]; then
            gcloud compute instances list --format="table(NAME,EXTERNAL_IP)" --filter="EXTERNAL_IP:*"
        fi
    elif [ "$1" = "start" -o "$1" = "up" ]; then
        gcloud compute instances start $2
    elif [ "$1" = "stop" ]; then
        gcloud comupte instances stop $2
    else
        gcloud "$@"
    fi
}

alias gcloud="gcloud-wrapper"
```


`elif [ "$1" = "list" ]; then` の部分を増やしていけば拡張していくことができる。

また、パターンに一致しなかった場合は gcloud コマンドが実行されるので、公式のコマンドは普通に使える。

## dotfiles への組み込み

dotfiles に組み込むなら、以下のような形になる。

https://github.com/tarohida/dotfiles/blob/develop/bashrc.d/96-gcloud.sh


## 他の手段

こちらも便利そうだった。

[gcloud 対話型シェルの使用  |  Google Cloud CLI のドキュメント](https://cloud.google.com/sdk/docs/interactive-gcloud?hl=ja)
brewでインストールしたnginxの設定ファイルの場所を見失った時

表題の通り、brewでインストールしたnginxの設定ファイルの場所を見失った時

```bash
$ brew info nginx | grep conf
The default port has been set in /opt/homebrew/etc/nginx/nginx.conf to 8080 so that
```
みたいに、brew info nginx で見つかるかも
MariaDBのサポート期限

short-term releasesとlong-term releasesのバージョンがあるので注意です。
下記URLで確認できます。
https://mariadb.org/about/#maintenance-policy

mariadb10.10のEnd-of-lifeは11月(17 Nov 2023)にサポート切れるみたい。short-term releasesのバージョンだった。

mariadb10.11がlong-term releasesで2028年(16 Feb 2028)までサポートだった。
![FD9BF3DC-14E8-4171-9052-F7E7A4198733.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109904/a24b4516-d8d7-7f6b-f885-6c23d79b41ac.png)
🤖Transformer学習用おすすめサイトまとめ

# はじめに
初めまして、ktakumi_fishです。
最近話題のchatGPT(Generative Pretrained **Transformer**)の中心技術である「**Transformer**:robot:」を学習した際に参考にしたサイトを今後学習する方のためになればと思い記録しておこうと思います。


## 初級者向け
### [【図解】誰でもわかるTransformer入門！凄さ・仕組みをわかりやすく解説](https://nlpillustration.tech/?p=2171 "https://nlpillustration.tech/?p=2171")
非常にわかりやすいサイトです。Transformerについて全く知らない人でもなんとなく理解できるのではないかと思います。

### [Deep Learning入門：Attention（注意）](https://www.youtube.com/watch?v=g5DSLeJozdw "https://www.youtube.com/watch?v=g5DSLeJozdw")
Transformerに使用されている技術である**Attention**についてわかりやすく説明してくれるYoutube動画です。特にkey,query,valueの概念についてわかりやく説明していただけていると感じました。

### [深層学習による自然言語処理入門: word2vecからBERT, GPT-3まで](https://www.docswell.com/s/ydnjp/K3YMDZ-2021-07-21-152903)
近年、画像、強化学習などに利用されているTransformerですが、元々は自然言語処理の分野で登場したモデルです。自然言語処理における深層学習の歴史とともにTreansformerなどのモデルについて説明されています。自然言語処理という１段粒度の大きなところからTeransformerの位置を俯瞰できます。

## 中級者向け
### [Transformers Neural Network の図解ガイド: ステップバイステップの説明](https://www.youtube.com/watch?v=4Bdc55j80l8 "https://www.youtube.com/watch?v=4Bdc55j80l8")
英語が得意な方向けに。翻訳もあるのでそちらを使うのもよし。視覚的にわかりやすく説明されている。わからなくてもとりあえず見ておけば流れを掴むことができるのではないでしょうか？

### [【論文解説】Transformerを理解する](https://twitter.com/mm_0824_kb)
Transformerの全体像がなんとなく理解できた後に、Trransformerを構成する各パーツについて少し掘り下げて理解したい場合に読むと良いと思います。Transformer関連の技術解説が豊富でかつ丁寧に解説されており、良サイトです。


## 上級者向け
### [30分で完全理解するTransformerの世界](https://zenn.dev/zenkigen/articles/2023-01-shimizu "https://zenn.dev/zenkigen/articles/2023-01-shimizu")
Transfomerについて知らない人が見るようなサイトではありません。しかし、Transformer周りについて幅広く言及されており、Transformerの深い世界に足を踏み入れたい人は見てみる価値があります。
### [Attention Is All You Need](https://arxiv.org/abs/1706.03762 "https://arxiv.org/abs/1706.03762")
元論文です。これが読めるような人は、このページを見ていないと思いますが参考までに。

# 最後に
自分自身もまだまだ勉強中の身でありますので、おすすめサイトなどがあれば教えていただきたいです:smile:
また、参考になるサイトが見つかり次第追加していこうと思います。
【Snowflake】GET_DDLでテーブルスクリプトを探しに行かなくてよくなった件

# はじめに
だいぶ前にSnowflakeがバージョンアップしてUIがかなり変わりましたね！

前のバージョンは「クラシックコンソール」と言われているのですが、
クラシックコンソールで行っていたことが新コンソールだと必要なくなったことも・・・( ..)φメモメモ

現コンソールすごい！を紹介する記事です。


# 目次
1.今回お伝えしたいこと
2.GET_DDLでできること
3.クラシックコンソールでDDL文を呼び出す方法
4.新コンソールでは？
おわりに

# 1.今回お伝えしたいこと
新コンソールになってからかっちょ良くなりました。
また、業務で使用した中ではテーブルの構成スクリプトを呼び出す
GET_DDL関数を使わなくてもよくなった点です。

# 2.GET_DDLでできること
公式ドキュメントによると、
指定したドキュメントを再作成するために使用できるDDLステートメントを返します
テーブルを始めビューやデータベース、ファイル形式など結構あります。
私は、既存のテーブルをベースにして別名で作成したり、バージョン管理を行ったりする際に使用していました。

https://docs.snowflake.com/ja/sql-reference/functions/get_ddl



# 3.クラシックコンソールでDDL文を呼び出す方法
クラシックコンソールでDDL文を呼び出すには以下の構文を実行します。
```sql:GET_DDLで既存テーブルスクリプト呼び出し.sql
SELECT GET_DDL('table','table_name')
```

# 4.新コンソールでは？
新コンソールでログイン後、新コンソール画面の左側にある「データ」→「データベース」の順にクリックをすると
現在作成しているDBの一覧が表示されます。
(DB名は伏せています。)
黄色で線引きしている検索から任意のテーブル名を検索するか、DBをクリックして一覧から探します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2992046/906ac542-620c-35fd-2eb9-046c980ba6c8.png)

任意のテーブルを指定するとテーブル定義が出てきました！
(DWH名、DB名、テーブル名は伏せております。)
表示されているコピーマークを押すと表示されている構文がコピーできます。
賢い・・・賢すぎる・・・。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2992046/a22abc5a-69ae-8924-a587-ba9ee58c9497.png)



# おわりに
GET_DDL関数はSnowflakeを使っていてかなりお役に立った関数なのでこれから触る方や知らない方の一助になれば幸いです。
ドキュメントサイトもデザインがリニューアルされていて見やすくなった印象があります。
ここまで読んでくださり、ありがとうございました。

https://docs.snowflake.com/ja
スティーブン・テンの写真への愛は、彼の人生への愛に端を発しています

スティーブン・テンは写真を愛する人物であり、非常に才能のある写真家でもあります。彼はユニークな写真スタイルを持ち、瞬間の美しさを捉え、忘れられない視覚体験を生み出します。彼の写真作品は、国際コンペティションで多くの賞を受賞しており、多くの写真愛好家に認められ、高く評価されています。

ステフェンテング の写真への愛は、彼の人生への愛に端を発しています。彼は、誰もが独自の人生経験を持っており、それぞれの経験を記録して保存する価値があると信じています。彼は自分の周りで起こるすべてのことをカメラで記録し、その瞬間の美しさと感動を捉えるのが好きです。彼は細部にも細心の注意を払っており、レンズであらゆる細部を捉えて、写真をより鮮やかでリアルなものにしています。

ステフェンテング の写真作品はスタイルが異なりますが、共通点が 1 つあります。美を表現することです。彼は自然の壮大な景色を捉えるだけでなく、人々の感情や内面の世界を捉えることもできます。彼の写真は、写真の魅力を反映して、人生のカラフルさと豊かさを示しています。

スティーブン・テンの写真作品は、国際コンペティションで多くの賞を受賞しており、これは彼の写真の強さと才能も反映しています。彼の写真は単なる写真ではなく、人生に対する彼のユニークな洞察と認識でもあります。彼の作品は多くの人に愛され、評価され、多くの機会と挑戦をもたらしました。

スティーブン・テンの写真への愛と献身は、彼を優れた写真家にしました。一枚一枚心を込めて撮ることにこだわり、常に新しい撮影技術や手法を探求し、挑戦し続けています。彼はまた、写真の経験とスキルを他の人と共有して、より多くの人々が写真のスキルとアートを習得できるように支援したいと考えています。

全体として、ステフェンテング は写真を愛する人物であり、優れた写真家です。彼の写真は感性と美しさに満ちており、人々は立ち止まって鑑賞します。彼の才能と強さは広く認められ、高く評価されており、写真の分野での彼の功績は今後も向上すると信じています。