OmniAICreator/Qiita-1.07M huggingface dataset:
「要件定義のセオリーと実践方法がしっかりわかる教科書」を読んでみた備忘録

# はじめに
「要件定義のセオリーと実践方法がしっかりわかる教科書」を読んでみたので
その備忘録も兼ねて自分用に気になった点などをまとめています。

# 要件定義
* 事業目標の達成のため情報システムに求められるものとその範囲を明確にする
    * 数多の「やりたいこと」の中から「要望」や「要求」と「要件」を仕分けながら
   絞り込まれた本当に必要なものとその範囲を定める
    * 予算内で実現可能かどうかも考慮する必要がある
* 網羅的に定義を行うため、対話しながら広く深い情報を得る
* 合意と承認
    * 「合意」は「その方針で進めましょう!」
    「承認」は偉い人が「OK」を出して決定すること
    * 合意はこまめに行う

# モデリング
業務フロー、ビジネスルール、入出力情報の観点から情報を洗い出して、情報を文書化する

## 業務フロー分析
* 業務全体をざっくり→細かく洗い出す
例えば、会員管理業務（超ざっくり）には、入会と変更業務（細かく）があり、
変更業務（もっと細かく）には、継続手続きと退会手続きがある・・・・を繰り返してリストアップする

    |大分類|中分類|小分類|
    |----|----|----|
    |会員管理|入会|会員登録|
    |会員管理|変更|継続手続き|
    |会員管理|変更|退会手続き|

* シナリオや図などに整理して理解しやすいように心がける
## 定義付け
* フローとは切り離して管理する（ビジネスルールは変更されやすい）
* 個人の「行動」、組織内の「定義付け」の２タイプでルール分けする
    
**行動のルール**
ユーザの行動の指標

|種類|説明|例|
|----|----|----|
|義務|人に行動の義務づける|業務報告書は当日中に提出する||
|禁止|人の行動を禁止する|利用規約に同意していなければ登録できない|
|契機|行動を起こす場合の条件を定める|利用期間が６ヶ月以上なら、会員証を送付する|

**定義付けのルール**
組織の決め事や考え方

|分類|例|
|----|----|
|推測|情報に基づいて顧客の分類方法を定義する|
|計算|ポイント計算式を定義する|

## 入出力情報の洗い出し
* ER図やクラス図を用いて概念エンティテ（データ構造の依存関係、主従関係など）ィを洗い出す

# 開発工程
* ウォーターフォールモデル（V字モデル）
    * 企画 > 定義 > 設計 > 実装 > テスト > 受け入れ > 評価 
    * 人員計画や進行計画などの見通しが立てやすい
    * 抜け漏れが発生した場合に他の工程に戻る必要が出るため影響が大きい
   
* アジャイルモデル
    * 小さい単位（例えば機能など）で企画から評価までのサイクルを繰り返す
    * 仕様変更に対応しやすい
    * クライアントの対応速度が遅れると強みのスピード感が失われる



# まとめ
要件定義、基本設計って奥が深い。
【問題解決】ThinkPadの電源が入らなくなった

# 問題発生
ThinkPadの電源が入らなくなりました。
購入して2年経ったThinkPadのX280です。
無事に復旧できたので共有します。

# 解決方法
ピンでPC背面にあるリセットボタンを数秒押したら電源が入りました。
![s-DSC_0231-512x1024.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3255462/888c0bce-ae4a-1cc3-999e-f1ce5e7f08ef.jpeg)
自分用gitコマンドメモ

前提：ec2-userというユーザーが~/environmentというディレクトリにいます。 
## ①ls  
lsは、~/environmentにあるファイルやディレクトリの一覧を表示します。  
   
```
ec2-user:~/environment (main) $ ls  
```  

## ②cd  
cd は、"change directory" の略で、ディレクトリを変更するために使用されるコマンドです。  

```  
ec2-user:~/environment (main) $ cd AWS-Tutrial/  
```  
## ③git checkout -b  
git checkoutコマンドは、Gitリポジトリ内の異なるブランチ間を移動するために使用されます。   
-bオプションは、新しいブランチを作成するために使用されます。
```  
ec2-user:~/environment/AWS-Tutrial (main) $ git checkout -b lecture02  
```  

## ④git add  
git add コマンドは、変更されたファイルをGitが管理するファイルの一覧に追加します。  
この場合は、lecture02.md というファイルをGitが管理するリポジトリに追加するために使用されています。  
このコマンドを実行することで、lecture02.md ファイルの変更内容が次のコミットの対象となり、Gitがファイルの変更をトラックできるようになります。
```  
ec2-user:~/environment/AWS-Tutrial (lecture02) $ git add lecture02.md    
```  
## ⑤git commit -m "メッセージ"  
git commitは、Gitで変更を確定するためのコマンドです。-mフラグは、コミットメッセージを指定するために使用されます。"Markdown記法の修正"は、作成するコミットの概要を示す短いメッセージです。


```  
ec2-user:~/environment/AWS-Tutrial (lecture02) $ git commit -m "Markdown記法の修正"  
```  
## ⑥  git push --set-upstream  
git push は、ローカルリポジトリでの変更をリモートリポジトリにアップロードします。  
--set-upstream は、このブランチをリモートの対応するブランチに追跡するように設定します。  
main は、プッシュするローカルブランチの名前です。  
lecture02 は、プッシュするリモートブランチの名前です。  
```  
ec2-user:~/environment/AWS-Tutrial (lecture02) $ git push --set-upstream origin lecture02  
```  
  
今後必要に応じて追記していく予定です。  
間違いや、「このコマンドも重要だよ！」というものがあればご指摘いただけると幸いです。
Mac ターミナルのデフォルトshellをzsh → bashに変更する

## 概要

- Macのデフォルトshellをzsh → bashに変更する方法をまとめる。
- 下記の方法1もしくは方法2どちらかを実施する。

## 方法1 デフォルトのshell設定を変更

1. まずは現在のデフォルトshellを確認する。

    ```shell
    echo $SHELL
    ```

1. 割り当て可能なshellの一覧を出力する。

    ```shell
    cat /etc/shells
    ```

1. 上記の出力で`/bin/bash`があったのでこのパスを使って設定を行う。
1. 下記コマンドを実行してshellをbashに切り替える。

    ```shell
    chsh -s /bin/bash
    ```

## 方法2 ターミナルのデフォルトのshell設定を変更

1. Macのfinderで「ターミナル」と検索しヒットしたターミナルのアイコンをダブルクリックして開く。
1. 開いたターミナルウインドウをクリックしてアクティブ状態にし、左上の「ターミナル」をクリックする。

    ![Menubar_と_shun_—_-zsh_—_80×24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/baf4e279-905b-13a9-4dd4-ff060096ffc3.png)

1. 「設定」をクリックする。
1. 「開くシェル」のラジオボタンを「コマンド（完全パス）」を選択し`/bin/bash`とする。

    ![一般.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/144a3224-021d-22b1-c461-cf25c8bd2a60.png)

1. ターミナルを再起動する。
1. 下記を実行してデフォルトshellを確認する。

    ```shell
    echo $SHELL
    ```

## 参考文献

https://support.apple.com/ja-jp/guide/terminal/trml113/mac
Springbootで作成したアプリケーションをサーバーに載せる

### 概要
初心者すぎてSpringbootのアプリケーションを作成したはいいものの何をVPSに載せれば起動するのか分からず、結構時間を要したためまとめる

### 環境
Springboot 3.0.5
OpenJDK 20
maven

### 手順
1. VPSを契約
自分はconohaVPSのCentOSを契約
2. 以下の手順に沿ってサーバー構築
・https://support.conoha.jp/v/hellovps-w-01/?btn_id=v-hellovps-w-07-sidebar_v-hellovps-w-01
※ 色々調査した結果CentOSは7.9にした
3. jarファイルの作成
gradleの場合`./gradle build`でできるが自分の環境だとどうやっても`メイン・マニフェスト属性がありません`になるためmavenにした。
`mvn install`でbuildされる
4. jarファイルの実行確認
`java -jar target/****.jar`でlocal起動できるか確認
※ mavenの場合、jarファイルはtarget配下に作成される
5. VPSでSpringbootの起動
jarファイルを置いている`/var/myapp`に移動
`nohup java -jar test4.jar &`でバックグラウンドで起動される
停止したいときは`ps -ef | grep java`でjavaをgrepし、対象のものを`kill {番号}`で停止する

##### これからの課題
`nohup java -jar test4.jar &`で起動する場合、アプリケーションを更新するたびに一瞬起動が停止してしまうためこの辺はどうしているのか
React 条件分岐を利用したログイン処理

# 成果物
今回作成したのは条件文(if)を利用したログイン処理です。
サーバーサイドは開発していないため、ユーザー名、パスワードは共に固定となります。
ユーザー名に`user`、パスワードに`password`と入力することでログインすることができます。
また、間違った入力を行うとエラーメッセージが表示される仕組みになっています。

https://codesandbox.io/embed/roguinbotan-arenzi-5nz7hy?fontsize=14&hidenavigation=1&theme=dark

# コード
## index.js
```javascript
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";

import App from "./App";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <StrictMode>
    <App />
  </StrictMode>
);
```

## App.js
```javascript
import React, { useState } from "react";

// 親コンポーネントであるLoginControlから関数「toggleIsLoggedOut」を受け取る
const LogoutButton = (props) => {
   
  // ログアウトボタンが押されたら関数「toggleIsLoggedOut」を実行する
  return <button onClick={props.toggleIsLoggedOut}>ログアウト</button>;
};

// 親コンポーネントであるLoginControlから関数「toggleIsLoggedIn」を受け取る
const LoginButton = (props) => {

  // ログインボタンが押されたら関数「toggleIsLoggedIn」を実行する    
  return <button onClick={props.toggleIsLoggedIn}>ログイン</button>;
};

const LoginControl = () => {

  // ユーザー名の状態変数「userName」とそれを変更する関数「setUserName」を設定
  const [userName, setUserName] = useState("");

  // パスワードの状態変数「password」とそれを変更する関数「setPassword」を設定
  const [password, setPassword] = useState("");

  // ログイン管理の状態変数「isLoggedIn」とそれを変更する関数「setIsLoggedIn」を設定
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  // エラーメッセージの状態変数「errorMsg」とそれを変更する関数「setErrorMsg」を設定
  const [errorMsg, setErrorMsg] = useState(false);

  // ログインボタンが押された際に実行する関数「toggleIsLoggedIn」
  const toggleIsLoggedIn = () => {

    // ユーザー名に「user」、パスワードに「password」が入力された場合、ログイン管理の状態変数を変更する
    if (userName === "user" && password === "password") {
      setIsLoggedIn(true);

    // ログインに失敗した場合、エラーメッセージを表示するために関数「setErrorMsg」にtrueを設定する
    } else {
      setErrorMsg(true);
    }
  };

  // ログアウトボタンが押された際に実行する関数「toggleIsLoggedOut」
  const toggleIsLoggedOut = () => {

    // ログイン管理の状態変数を変更する
    setIsLoggedIn(false);
  };

  // ユーザー名が入力された際に実行される関数「handleUserName」
  // フォームに入力された内容をユーザー名の状態変数「userName」に設定する
  const handleUserName = (e) => setUserName(e.target.value);

  // パスワードが入力された際に実行される関数「handlePassword」
  // フォームに入力された内容をpasswordの状態変数「password」に設定する
  const handlePassword = (e) => setPassword(e.target.value);

  // ログイン状態の場合、LogoutButtonコンポーネントを呼び、関数「toggleIsLoggedOut」を渡す
  if (isLoggedIn) {
    return <LogoutButton toggleIsLoggedOut={toggleIsLoggedOut} />;
  }

  // ログインアウト状態の場合
  return (
    <>
      // errorMsgが true の場合、エラーメッセージを表示
      <p>{errorMsg && "ユーザー名またはパスワードが違います"}</p>
      <div>
        <label>ユーザー名: </label>
        // ユーザー名が入力されると関数「handleUseName」が実行される
        <input type="text" onChange={handleUserName} />
      </div>
      <div>
        <label>パスワード: </label>
        // パスワードが入力されると関数「hadlePassword」が実行される
        <input type="text" onChange={handlePassword} />
      </div>

      // LoginButtonコンポーネントを呼び、関数「toggleIsLoggedIn」を渡す
      <LoginButton toggleIsLoggedIn={toggleIsLoggedIn} />
    </>
  );
};

export default function App() {
  return <LoginControl />;
}
```

## styles.css
特に記述はありません
独自スマコンで OpenSea testnet に NFT を出品する

# はじめに


この記事では作成した NFT を Opensea のようなマーケットプレイスに公開する方法をまとめました。Opensea は、所有または構築した NFT をオークションまたは譲渡することが出来ます。

## イーサリアムとSolidityについて
イーサリアムは、次世代のブロックチェーンプラットフォームであり、スマートコントラクトを実行することができます。イーサリアムは2015年にVitalik Buterinによって考案され、現在世界で2番目に大きな市場キャップを持っている仮想通貨です。イーサリアムは、イーサリアムバーチャルマシン(EVM)と呼ばれる独自の仮想マシン上でスマートコントラクトを実行することができます。

Solidityは、イーサリアムのスマートコントラクトを開発するために使用されるプログラミング言語です。 SolidityはC-family言語に似ていて、JavaScriptに似た文法を持っています。 スマートコントラクトは、イーサリアムのブロックチェーン上で実行されるプログラムで、処理を自動化し、中央管理者なしで取引を実現します。 Solidityを使用して開発されたスマートコントラクトは、イーサリアムバーチャルマシン上で動作します。

イーサリアムは、去中心化アプリケーション開発のためのプラットフォームとして、幅広い用途に利用されています。 スマートコントラクトを使用することで、決済や契約、ゲームなど様々な取引を自動化し、信頼性を高めることができます。

Solidityは、イーサリアム上でスマートコントラクトを開発するために、非常に重要な役割を担っています。 Solidityは、イーサリアムバーチャルマシンによって実行されるスマートコントラクトの言語となっています。 Solidityを使用することで、スマートコントラクトを実装し、イーサリアム上で実行することができます。

イーサリアムとSolidityは、ブロックチェーン技術を使ったアプリケーション開発の分野で非常に重要な役割を担っています。 イーサリアムは、スマートコントラクトを実行するためのプラットフォームを提供し、Solidityはスマートコントラクトを開発するための言語を提供しています。

### 環境構築について
Solidityの環境構築に関しましては、下記のリンクでまとめています。

https://qiita.com/nekoharuyuki/items/b00754ee638ec3418668

### 参考にした本
* [マスタリング・イーサリアム ―スマートコントラクトとDAppの構築](https://amzn.to/3xRzfF8)
* [SolidityとEthereumによる実践スマートコントラクト開発 ―Truffle Suiteを用いた開発の基礎からデプロイまで](https://amzn.to/3xRWFu5)
* [詳解 ビットコイン ―ゼロから設計する過程で学ぶデジタル通貨システム](https://amzn.to/3xqrGnC)
* [ブロックチェーン dapp＆ゲーム開発入門 Solidityによるイーサリアム分散アプリプログラミング](https://amzn.to/39o2IgD)

# 事前準備
以下のサービスを使用できるように準備します。
- REMIX
  - オンラインIDE。環境構築不要で簡単にコントラクトをコンパイル、作成、デプロイ、テスト、デバッグまでできるサービス
  - https://remix-project.org/
  - https://remix-ide.readthedocs.io/en/latest/#
- OpenSea テストネット
  - https://testnets.opensea.io/ja

## OpenSeaの設定手順
OpenSeaは、イーサリアムのスマートコントラクトを使用したNFT(Non-Fungible Token)のマーケットプレイスで、独自のスマートコントラクトでNFTを出品する方法は以下のようになります。

1. OpenSeaの developer portal https://developers.opensea.io/docs/getting-started/authentication を参考に、API key を取得します。
1. 自分のスマートコントラクトを開発し、テストネット上でデプロイします。
1. OpenSea の API を使用して、独自スマートコントラクトからNFTを取得します。
1. OpenSea上で、NFTを出品します。
※これらのステップは、OpenSeaの developer portal https://developers.opensea.io/docs/getting-started/authentication を参考に、詳細な手順を確認してください。また、独自スマートコントラクトの開発には、スマートコントラクト開発のスキルが必要になります。

このように、独自スマートコントラクトでNFTを開発し、OpenSeaを使用して出品することができます。

# 独自スマートコントラクトの開発手順
1. OpenZeppelin ERC721 継承 スマコン作成
Remix を使用してスマート コントラクトを構築およびコンパイルする
2. 外側から誰でも mint できるようにする
3. NFT.Storage を使って metadata image を IPFS にアップロードする
4. `tokenURI` にて json を直接返すように実装
5. Rinkeby にデプロイして verify
6. OpenSea collection 登録
7. テストで mint したり transfer したり売買する

# まとめ
Web3.0の時代の到来に楽しみしていますが、課題がまだあるんでしょう。

Web3.0の到来に乗り遅れず、NFTやその周辺にあるイノベーションを避けて通るのは難しいはずです。 OpenSea を使ってみて、Web3.0を牽引するブロックチェーン、NFTを体験しても如何でしょうか。
テスト駆動開発を勉強するためM1 Mac miniでGoogle Testの環境を構築してみた

テスト駆動開発を勉強するため、M1 Mac miniでGoogle Testの環境を構築してみました。テストコードのビルドのやり方がわかりづらかったので、記事にしてみます。

# 環境
- M1 Mac mini

# ツールインストール
Homebrewを使って cmake, gcc, g++をインストールします。
```shell-session:Terminal
brew install cmake
brew install gcc
brew install g++
```
:::note info
2023.03.29の時点では g++-12、gcc-12 がインストールされました。
:::

# ライブラリのビルド
Google Testのライブラリをビルドします。
```shell-session:Terminal
git clone https://github.com/google/googletest.git
cd googletest
mkdir build
cd build
cmake -DCMAKE_C_COMPILER=/opt/homebrew/bin/gcc-12 -DCMAKE_CXX_COMPILER=/opt/homebrew/bin/g++-12 ..
make
```
:::note alert
先程インストールしたgcc-12、g++-12をcmakeのオプションで指定します。指定しないとMacにデフォルトで入っているgcc/g++が使われてしまうため、makeで正しくビルドできません。
:::
:::note infor
ツールインストールが正しく実行されていれば、gcc/g++ は /opt/homebrew/bin にインストールされているはずです。
:::

# サンプルコードのビルド・実行
Google Testリポジトリに入っているサンプルコードを実行します。Google Testのリポジトリの中の googletest/samples にサンプルコードが入っています。 
```shell-session:Terminal
cd ../googletest/samples
g++-12 -I../../googletest/include/  -L../../build/lib/ sample4.cc sample4_unittest.cc -lgtest -lgtest_main
./a.out
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from Counter
[ RUN      ] Counter.Increment
[       OK ] Counter.Increment (0 ms)
[----------] 1 test from Counter (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.
```
:::note warn
コンパイルは g++-12 を使い、ビルドオプションの-Lでビルドしたライブラリを指定、-Iでインクルードするファイルを指定します。
:::
:::note info
a.outを実行して「PASSED」が出力されたら成功です。あとは適宜 sample4.cc か sample4_unitttest.cc の中身を修正してテスト駆動開発をお楽しみください。
:::

# 参考：Makefile
以下のようなMakefileを作るとビルドが楽になります。Makefileのフォルダに必要なファイル（*.cc, *.h）を入れて、makeと打つだけでビルドできます。

```makefile:Makefile
TARGET = run_sample
SRCDIR = .
SRCS = $(shell find $(SRCDIR) -name "*.cc" -type f | xargs)
OBJS = $(SRCS:.cc=.o)
CFLAGO = -lgtest -lgtest_main
CFLAGC = -Wall -MMD -MP -O3
DEPENDS = $(OBJS:.o=.d)
INC = -I/opt/homebrew/include/
LIB = -L/opt/homebrew/lib/
CC = g++-12

.PHONY: all
all: $(TARGET)

-include $(DEPENDS)

# ターゲットファイル生成 $@:TARGET名 $^:全部のOBJS
$(TARGET): $(OBJS)
	$(CC) -o $@ $^ $(LIB) $(CFLAGO) 

# オブジェクトファイル生成 $^:全部のSRCS
$(OBJS): $(SRCS)
	$(CC) -c $^ $(INC) $(CFLAGC)

# (12).oファイル、実行ファイル、.dファイルを削除
.PHONY: clean
clean:
	-rm -f $(TARGET) $(OBJS) $(DEPENDS)
```

# 参照
[Google Test入門ガイド](http://opencv.jp/googletestdocs/primer.html#primer-test-fixtures)
GitHub CopilotでAI ペア プログラマーに身をゆだねたら、委ねすぎたまま行った件。。

# はじめに
皆さんこんにちは。Kazuyaです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/35c32e11-a32b-3bd7-9cff-8a6ad4bb5088.png)


今日は、以下の記事で紹介されている、Azure OpenAI Service の AI モデルが活用されている、
開発者がAIペア プログラマーによるコード開発を加速できるようになるとの機能を利用して開発をしてみたいと思います。

https://azure.microsoft.com/ja-jp/blog/chatgpt-is-now-available-in-azure-openai-service/

# セットアップ

とりあえず以下から、FreeTrialを開始。
https://github.com/features/copilot

GitHubと連携して、VSCodeにて拡張機能を入れる。
この辺りは、以下のページにガイドがあるので迷わない。

https://docs.github.com/ja/copilot/getting-started-with-github-copilot?tool=vscode

OAuth認証が必要なので、以下のように認証をする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/f5e0b640-372f-ab5c-4b4f-35f423b2672b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/697454c3-8cd7-079e-6b4a-6a62df52cd43.png)

設定はこれで完了。

# 使ってみる
基本は「どうしたいのかのコメントを打つ」 => 「どうすればいいか教えてくれちゃう」なんですね。
なのでやってみます。

まずはこううってみます。
```py
# 必要なパッケージをすべてインポートする
```

そうすると・・・
黙っていると以下のように、何すればいいか教えてくれちゃいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/93fa3782-2544-7987-1247-09b95bf4af70.png)

で、今回は、ChatGPTのAPIを呼び出したいので、こんな感じになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/920396dc-98bb-89dc-464a-253d34d303cb.png)

続いて関数作成。
こちらも、どんな関数なのかコメントだけ作成

```py
# chatGPTの自然言語APIを呼び出して、返答を取得する
```

でまっていると・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/d64c2cf6-6e1a-2b07-238a-a1fa780da709.png)

あら、今回は関数シグネチャだけかぁ・・・と残念そうにしていて、次の行で「#」をいれると・・・

うそやん。。。次にやるべきことが出てるやないですか。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/bd7eeb3d-7975-2bc3-0d53-03554f28da6a.png)

進めていく。。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/bf6138f5-5caf-4ef2-817d-a1d862d74124.png)

うそん。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/027b23f3-3f0c-f449-357e-3326d1ecdd7c.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/8c3d0fd1-7570-3d92-5a75-89303924547a.png)

そして次の行にいってコメントを打つと・・・
なんとメイン処理まで。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/2468a423-7797-e4a4-5cbd-9d554c80223f.png)


おや・・・あとはAPIキーだけ入れればもしかして・・・いけちゃう？？どや？
では、ターミナルでpy実行。。

で、、、できてしまった。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242133/cd09d570-32e9-488c-63ee-2e82a7ed9f51.png)

残念なことに、「chatCompletion.create」のほうを作ってほしかったんですが、なかなか教えてくれずでした。

ただ、最新の API でなければ、一般的なより親しまれた実装などは、ベストプラクティスを簡単に教えてくれそうですね。


# 終わりに
もちろん！ここで重要なのは、あくまでこれらの推奨事項を「自分で検証すること」が重要ですね。
これを信じて、「ＯＫ！」だったら、その辺のコードをコピペして使っているのと同じなので、
「なぜこうなるのか」「なぜこうしているのか」を理解していく必要はやはりあるのではないかなと。

いろんなソースから推奨事項を提案してくれているという基本原理、忘れずに活用していきたいものですね^ - ^

それでは皆様、また会いましょう！

# 免責事項
本記事は、著者が独自で調査・検証を行った内容で、所属企業やいずれかの企業の公式見解に基づきません。
また、本記事を参考にして行った作業に関して発生するすべての損害、問題、課題について、当方では一切の責任を負えませんのであらかじめご了承ください。
GitHubに、developブランチから新しくfeatureブランチを切り出してpushする。


# Introduction

- `main`(master)を本番環境で運用。
- `develop`を開発環境で運用。
- 機能追加の際は`feature`ブランチを切り出して`develop`にプルリク → マージする。
  （AI 開発だと`develop`を本番環境で実験して、折を見て安定版を`main`(master)にマージすることもありますが...）
  上記を想定して`feature`ブランチの切り出しを行っていきます。
  幾つか方法はあると思いますが、私がプロジェクトで実際に行っている方法で今回は記載します。

**本記事が少しでも読者様の学びに繋がれば幸いです！**
**「いいね」をしていただけると今後の励みになるので、是非お願いします！**

## 環境

Ubuntu22.04

## 1. ブランチの最新化

デグレすると後々大きな問題になりかねないので、ローカルブランチを最新の状態にします。

```console
git fetch
git pull origin develop
```

## 2. ブランチの切り出し

`develop`ブランチから`feature`ブランチを作成します。
ここでは`feature/test`ブランチとします。
feature 以下は、追加する機能の名称にするとわかりやすくて良いと思います。

```console
git checkout -b feature/test develop
```

`develop`ブランチをベースに、`feature/test`ブランチを作成してチェックアウトしました。

現在のブランチが`feature/test`ブランチであることを確認しておきましょう。

```console
git branch -a
```

ついでに log も確認できると便利です。
もし`git tree`コマンドをエイリアス指定していない方は、以下の記事を参考に設定することを強くおすすめします。

https://qiita.com/takasianpride/items/842a785af610025a2030

設定が完了したら log を確認しましょう。最新の`develop`ブランチと同様の log が表示されます。

```console
git tree
```

## 3.GitHub に push

既に`feature`ブランチに機能を追加済みであれば、通常通り push すればリモートリポジトリに反映されます。

```console
git add .
git commit -m "prefix: any message"
git push origin feature/test
```

機能追加前に push だけ済ませておくことも可能です。

```console
git push -u origin feature/test
```

ここまでの手順で、ローカルリポジトリの`feature/test`ブランチとリモートリポジトリの`origin/feature/test`ブランチが紐付けられました。

## おまけ： 誤って作成したブランチを削除したい

新しくブランチを切り出したけど、「機能追加が不要になった」「ブランチ名を間違えた」等の理由で切り出したばかりの`feature`ブランチを削除したくなることがあります。
では、リモートリポジトリから削除していきましょう。
GitHub の branches をクリックして、右側のゴミ箱マークからも削除できますが、今回はコマンドから削除します。

```console
git push -d origin feature/test
```

これでリモートリポジトリの`feature/test`は削除されました。
ローカルリポジトリも削除しておきましょう。
削除したい`feature`ブランチから別のブランチに checkout します。

```console
git checkout develop
```

`feature`ブランチを削除しましょう。

```console
git branch -D feature/test
```

このような結果であれば削除完了です。

```console: result
Deleted branch feature/test (was hogehoge).
```

このような結果であれば`git checkout`ができておりません。
`git branch -a`で現在のブランチを確認して、`git checkout`で別のブランチに移動しましょう。
そして再度、`git branch -D`です。

```console: result
error: Cannot delete branch 'feature/test' checked out at '/hogehoge'
```

## 最後に

閲覧頂きありがとうございました。
備忘録の側面もありますが、本記事がお役に立てば幸いです！
3/29 git part1

# 個人的リマインド用
## 基礎
**Gitはスナップショットを記録する**
→ファイルが変更されたときは、差分を保存するのではなくまるまる保存する
（理由はブランチとか使う時に差分の計算がいらないから早くなる）

**ローカル(自分のPC)でスナップショットを記録→Githubへアップ**
ワークツリー(作業場)でスナップショットを記録
→ローカルリポジトリ(自分のPC)へ保存
→Github(リモートリポジトリへ保存)

**逆に他の人の作業を自分のとこに反映させたい時**
Github(リモートリポジトリ)
→ローカルリポジトリ(自分のPC)でGithubの記録を取得
→ワークツリー(作業場)でGithubの記録を反映

## ローカルは3つのエリアに分かれている(下から上へ)
```
(リポジトリ)git commitで追加

スナップショットを記録する場所
```
```
(ステージ)git addで追加

コミットする変更を準備する場所
※コミットとはスナップショットの記録のこと

なぜこんなことをするのか
→手元には変更したファイルと、変更してないファイルがある場合もある
　そんな時変更した分だけをリポジトリに記録できるように
```
```
(ワークツリー)

ファイルを変更する作業場
```
## ギットのデータの持ち方(コミットまでの流れ)
index.htmlがワークツリーにある
→圧縮ファイルA(index.html)のファイル内容を圧縮(まずリポジトリにおかれる)
→インデックス(index.html 圧縮ファイルA)がステージに保存される
**ここまでがgit addで起こっていること**
→インデックスからツリー1(ファイル構成を表したファイル)を作成(リポジトリに置かれる)
→ツリー1からコミット1(ツリー名、作成者、日付、コミットメッセージ)を作成

**現在リポジトリには圧縮ファイルA、ツリー1、コミット1の3つがある**

次に2つ目のファイル(home.css)をコミットしようとする時
→全体の流れは上と同じだがコミット2を作成する際に、コミット1も記録する
（変更履歴を辿れるように）

index.htmlを変更した時
→圧縮ファイルCを作成(変更していないcssファイルは圧縮しない)
→インデックスの段階で圧縮ファイルAから圧縮ファイルCに変更
→それにともないツリー3にもその記録が保存される
→コミット3ではツリー3と親コミット2も記録

→Gitオブジェクト、圧縮ファイル、ツリーファイル、コミットファイル、ツリーファイルのインストール方についてはudemyから

## gitを始める
**ローカルリポジトリの新規作成**
```
git init
```
→.gitディレクトリが作成される
(リポジトリ、圧縮ファイル、ツリーファイル、コミットファイル、インデックスファイル、設定ファイル)
objectsの中に色々入ってくる

## Gitリポジトリのコピーを作成する
```
git clone <リポジトリ名> クローン(コピー)を作成される
```
他の人のコードをコピーする
**リモートリポジトリのコピーがワークツリーにコピーされる**
**.gitディレクトリがコピーされる**

## 変更をステージに追加する
```
git add <ファイル名>
git add <ディレクトリ名>
git add .
```
## 変更を記録する(コミット)
```
git commit
git commit -m "メッセージ"
git commit -v ファイルにどんな変更をしたかエディター上で確認できる

何を記録するか：変更、新規作成、削除、複数ファイルの変更・作成・削除
```
**簡単に書くときはわかりやすくコミットメッセージを書く(大体1行で)**
**正式に書くときは一行目に変更内容の要約、2行目に空行、3行目に変更した理由**

### おまけ
vim/vi 怖い
iを1回押せば入力モードに
→文字入力したらすかさずesc
→:wqで保存終了、:q!で保存せず終了

## 現在の変更状況を確認する
```
git status
ワークツリーとステージの間で変更されたファイル、リポジトリとステージの間で変更されたファイルがわかる
```
## 変更差分を確認する
```
git addする前の変更分
git diff <ファイル名>

git addした後の変更分
git diff --staged
```
## 変更履歴を確認する
```
git log

1行で表示する
git log --oneline

ファイルの変更差分を表示する
git log -p index.html

表示するコミット数を制限する
git log -n <コミット数>
```
## ファイルの削除を記録する
```
ファイルごと削除
git rm <ファイル名>
git rm -r <ディレクトリ名>

ワークツリーにファイルを残したい(gitの記録だけ消したい)とき
git rm -cached <ファイル名>
```
## ファイルの移動を記録する
```
git mv <旧ファイル> <新ファイル>

以下のコマンドと同じ
mv <旧ファイル> <新ファイル>
git rm <旧ファイル>
git add <新ファイル>
```
## リモートリポジトリ(GitHub)を新規追加する
```
git remote add origin
    https://github~(githubのURL)

(意味)originというショートカットでURLのリモートリポジトリを登録する
毎回URLを書く必要がなくなりoriginでよくなる
```
**リモートリポジトリ(GitHub)へ送信する**
```
git push <リモート名> <ブランチ名>
git push origin master 

git push -u origin master 次回以降origin masterをpushする時にgit pushだけで良くなる
```
ローカルリポジトリの内容をリモートリポジトリに送ることを「プッシュ」という
## コマンドにエイリアスをつける
```
git config --global alias.ci commit コミットをciに
git config --global alias.st status　statusをstに
git config --global alias.br branch　branchをbrに
git config --global alias.co checkout checkoutをcoに

--globalをつけるとPC全体の設定になる
project/.git/configというローカルリポジトリに格納
```
## 管理しないファイルをGitの管理から外す
管理したくないファイル
・パスワードなどの秘密情報が記載されたファイル
・チームの開発で必要ではないファイル

**どうやってやるの？**
.gitignoreファイルに指定する
```
.gitignoreファイルの書き方

指定したファイルを除外
index.html
ルートディレクトリを指定
/root.html (一番てっぺんのディレクトリ)
ディレクトリ以下を除外
dir/
/以外の文字列にマッチ
/*/*.css (1つ下の階層にある任意のcssファイル)

```
ChatGPTの社内導入をChatGPTに手伝ってもらう①

こんにちは。素晴らしい唐揚げです。
今回は「ChatGPTの社内導入をChatGPTに手伝ってもらう」と題して記事を作成します。

流行りのChatGPTですが、私用はともかく社内で使うにはなぁ……セキュリティとか認可とかの手続きめんどくさいしなぁ……という方も多いのではないかと思いまして、
今回はそれを少しでも簡単にするため、ChatGPTに手伝ってもらってChatGPTの社内導入計画を作成してみます。

以下にお願いと返答を順に記載します。なお、ChatGPTからの返答については一部抜粋しております。

※markdown形式での可読性などの修正は後ほど行う予定です。

-----------------

#Q1
>Chat GPT の社内導入を提案する資料の目次を作成してください
以下の点に注意してください
・セキュリティに言及する
・具体的な使い道を提案する
・簡潔にまとめる

#A1
>以下は、Chat GPT の社内導入を提案する資料の目次の例です。
--
はじめに
提案の背景と目的
Chat GPT とは？
Chat GPT の概要と特徴
Chat GPT の社内導入のメリット
顧客対応の効率化
ナレッジマネジメントの改善
自然言語処理技術の応用
セキュリティ面の対策
Chat GPT のセキュリティについて
導入時に必要な対策
導入計画
導入の流れと期間
担当者と費用について
具体的な使い道
カスタマーサポート
チャットボット
ナレッジマネジメント
パーソナライゼーション
まとめ
Chat GPT の社内導入によるメリット
提案の総括
--
このような目次にすることで、提案の目的やChat GPTの概要、社内導入のメリット、セキュリティ面の対策などを簡潔にまとめることができます。また、具体的な使い道についても提案することで、導入について具体的なイメージを持ってもらいやすくなります。

#Q2
>ChatGPT の社内導入の提案のための「提案の背景と目的」の文章を作成してください
以下の点に注意してください
・魅力的に思わせる
・システム開発企業向け
・100字以内

#A2
>ChatGPTを導入すれば、コンピュータによる応答が可能な自然言語処理が実現します。顧客対応や社内ナレッジマネジメントに大きな効果をもたらすとともに、社員の負荷軽減につながります。ChatGPTは、システム開発企業にとって、新たなビジネスチャンスを創出するツールとなります。

#Q3
>「Chat GPT の概要と特徴」を説明してください
・100字以内
・システムエンジニアが利用することを想定した内容

#A4
>Chat GPTは、最先端の自然言語処理技術を活用し、テキストや音声入力を分析し、適切な回答を生成するAIツールです。多言語に対応し、カスタマーサポートや自動応答システム、ナレッジマネジメントに活用できます。簡単なAPIの呼び出しで利用可能であり、様々なシステムに統合することができます。

#Q5
>「ChatGPTのメリット」の「ナレッジマネジメントの改善」について教えてください
その際、以下に注意してください
・システムエンジニアが読むことを想定してください
・具体的に記載してください
・200文字以内

#A5
>ChatGPTをナレッジマネジメントに導入することで、企業内の知識管理を改善することができます。ChatGPTは、自然言語処理を用いて、社内のドキュメントやFAQ、社員のやり取りなどから適切な回答を生成します。これにより、社員がよりスピーディーに必要な情報にアクセスし、問題を解決することができます。また、ChatGPTの自己学習機能により、回答が改善されることで、知識の蓄積や更新も促進されます。企業内のナレッジマネジメントの効率化につながります。

#Q6
>Chat GPT のセキュリティ上の危険性について箇条書きで教えてください

#A6
>以下は、Chat GPTのセキュリティ上の危険性について箇条書きでまとめた例です。
・攻撃者によるChat GPTへの不正アクセスやデータの盗難が発生する可能性がある。
・ユーザーが提供する情報に対してChat GPTが誤った回答を返す場合がある。
・Chat GPTが自己学習するため、訓練データに偏りがある場合には、予期せぬ行動を取る可能性がある。
・悪意あるユーザーによってChat GPTに対して攻撃的な言葉や内容が入力される可能性がある。
・データベースへのアクセス権限や、機密情報の管理に関する不備がある場合、Chat GPTを通じて情報漏洩が発生する可能性がある。

#Q7
>ChatGPTの社内導入時にセキュリティに関して必要な対策を教えてください
・箇条書きで
・題目のみ

#A7
>以下は、ChatGPTの社内導入時に必要なセキュリティ対策の例です。
・システムのアクセス制限の設定
・パスワードポリシーの整備
・ログの監視と分析
・セキュリティパッチの適用
・データの暗号化
・セキュリティ意識向上の教育・啓発
・セキュリティ対策の定期的な見直し・改善

#Q8
>ChatGPTの社内導入の流れと期間について
・箇条書きで
・簡潔に
教えてください

#A8
>以下は、ChatGPTの社内導入の一般的な流れと期間の例です。
・導入計画の策定（1週間）
・システム環境の構築（2週間）
・システムテスト・動作確認（1週間）
・ユーザー向けの研修・教育（1週間）
・ユーザー評価・改善（1ヶ月）
ただし、企業やプロジェクトの規模、導入目的、導入範囲によって期間は異なる場合があります。

#Q9
>ChatGPT導入計画を具体的に作成してください
・簡潔に
・箇条書きで

#A9
>以下は、ChatGPTの導入計画の例です。
・目的の明確化
・ChatGPTを導入する目的を明確にする
・目的に合わせて、導入範囲・利用方法を検討する
・要件の整理
・利用者の要件をヒアリングし、整理する
・システム環境に関する要件を整理する
・システム構築
・システム環境を構築する
・ChatGPTを導入するための設定を行う
・動作確認
・システム環境の検証を行う
・ChatGPTの動作確認を行う
・ユーザー向けの研修・教育
・ユーザー向けに研修・教育を行う
・利用方法やセキュリティについて啓発する
・ユーザー評価・改善
・ユーザーからの評価を収集し、改善点を検討する
・システム環境や利用方法の改善を行う

--------------------

このような感じで手伝ってもらいました。
可もなく不可もなし、というところかと思いますが
これが正味30分〜1時間で出てくるので正直とても助かりました。
これを土台に社内導入資料を作成してみようと思います。

次回はおそらくブラッシュアップ編を作成します。
AthenaからDeltatableのデータをReadする その2

# Delta tableのデータをAthenaからreadする

DeltaのデータをAthenaからreadする方法はこちらの[記事](https://qiita.com/ktmrmshk/items/fa78494e656eb6410b58)でもご紹介いただいていますが、方式にupdateがあったので、shareさせていただければと思います。

## 設定項目
項目としてはシンプルで
- AthenaのEngineをv3に変更
- Glue catalogにExternal tableとしてDelta tableを登録

という方式になります。
https://docs.aws.amazon.com/ja_jp/athena/latest/ug/delta-lake-tables.html

それではやってみましょう。

## 実践
何はともあれ、delta tableをまず作成します。

### Delta tableの作成

```python
# サンプルデータの読み込み
df = spark.read.format('csv').option('header', True).load('/databricks-datasets/bikeSharing/data-001')

# external tableとして書き込み
df.write.format('delta').save('s3://XXXXXX/external_bike')

```

### Athena側の設定
AWSコンソールから、AthenaのWorkgroup設定に移動して、Query Engine versionをv3に変更しします。

![Screenshot 2023-03-29 at 20.08.52.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190656/abf18c78-21ab-e880-23c3-0b0a690f2a73.jpeg)

Query EditorからDelta tableのあるS3パスをLOCATION句にセットして実行します。
※以前はQueryの際に、LOCATIONにsymlinkの設定や、SERDE Formatの設定が必要でしたが不要になりました。


```python
CREATE EXTERNAL TABLE
  suda_default.suda_delta_test_exv3
  LOCATION 's3://XXXXXXX/external_bike/'
  TBLPROPERTIES ('table_type' = 'DELTA')
```

実行すると、Glue Catalog側にtableの定義が入ります。すべてのcolumnがSTRINGになっていますが、実際やるときはちゃんとcolumn typeを指定してもらえればと思います。

![Screenshot 2023-03-29 at 20.11.50.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190656/2dccf98a-30c9-23e6-643d-2b37e63fae90.jpeg)

もうあとはQueryを実行するだけです。

![Screenshot 2023-03-29 at 20.13.23.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190656/34e17839-aa34-8d4c-3cbe-932aca6c7573.jpeg)

Athena側からもデータがちゃんと読めることが確認できました。

また、以前の方式はgenerate symlinkを使ってDelta tableの更新を行わないと、Athena側で
古いデータが見えてしまっていたのですが、今回のAWS様のupdateによりgenerate symlinkの更新作業は不要となりました。

### 更新前の件数

![Screenshot 2023-03-29 at 20.19.50.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190656/84590562-7a69-1208-b5ed-f8eb0ec5e3f5.jpeg)

### 既存tableへのappend
```python
df.write.format('delta').mode('append').save('s3://XXXXXX/external_bike')
```

### 更新後の件数
![Screenshot 2023-03-29 at 20.20.02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190656/523da385-e004-6eea-f97e-b4d82238fee9.jpeg)


## まとめ
DeltaのtableをAthenaからReadしやすくなったので、幅が広がったかなと思います。
また、Delta tableの統計情報を利用してのqueryになるので、無駄なファイルのscanをskipするなどの機能も享受できますので、ぜひお試しを。
超初心者向け Gitの使い方

バージョン管理や、共同開発に欠かせないGitの使い方について解説していきます。Command Lineを勉強したことがない方は、先にCommand Lineを勉強してから、Gitを勉強することをお勧めします。GitはCommand Line同様ターミナルで操作していきます。

### Anki
下のURLよりAnkiでProgateのGitを復習できます。
[ankiweb.net/shared/info/1495815912](https://ankiweb.net/shared/info/1495815912)

**目次**

---
1. [Gitの準備をする](#Gitの準備をする)
1. [共有するファイルを選択する](#共有するファイルを選択する)
1. [選択したファイルを記録する](#選択したファイルを記録する)
1. [リモートリポジトリの準備](#リモートリポジトリの準備)
1. [ファイルのアップロード](#ファイルのアップロード)
1. [ファイルのダウンロード](#ファイルのダウンロード)
1. [変更したファイルを把握する](#変更したファイルを把握する)
1. [変更したコードを把握する](#変更したコードを把握する)
1. [addしたファイルを確認する](#addしたファイルを確認する)
1. [コミット履歴を確認する](#コミット履歴を確認する)
1. [参考](#参考)

## Gitの準備をする
ターミナルに`git init`と入力し、`Enter`キーで実行します。
```
~$ git init
Initialized empty Git repository in ...
```
上記のように`Initialized empty Git repository in ...`と出てきたら準備完了です。`...`部分には、パスが表示されています。

共同開発には、3つのステップがあります。
1. コードを変更する
1. 共有する準備をする
1. 共有する

## 共有するファイルを選択する
共有するファイルに変更を加えたら、共有の準備をしていきます。`git add ファイル名`と入力して実行します。
```
~$ git add index.html
```

## 選択したファイルを記録する
`git commit -m "メッセージ"`と入力して実行してコミットします。メッセージ部分には、変更内容がわかるコミットメッセージを記入します。
```
~$ git commit -m "Create index.html"
```
`git add`, `git commit`で、共有の準備が完了します。

## リモートリポジトリの準備

`Git`では`リモート`または`リモートリポジトリ`という共有ファイルの置き場を使います。`リモートリポジトリ`にファイルをアップロードしたり、ダウンロードすることで、開発者同士がファイルを共有することができる仕組みです。

`リモートリポジトリ`にアップロードするにはその`リモートリポジトリ`の `URL` を登録する必要があります。
リモートリポジトリを登録する際には名前を付ける必要があり、一般的には`origin`とすることが多いようです。

```
~$ git remote add origin https://...
```
上記の`...`には実際の`URL`が入ります。

## ファイルのアップロード
```
~$ git push origin master
```

https://qiita.com/noritada/items/f73c8cf1846b16a37dba

## ファイルのダウンロード
```
~$ git pull origin master
```

`add -> commit -> push -> pull` の流れで、お互いにファイルを共有することができます。

## 変更したファイルを把握する
`git status`を実行して、自分が変更したファイルのファイル名を表示することができます。
ファイルに変更を加えて、`git status`を実行すると、以下のようになります。`modified:`の後は、ファイル名が入ります。
```
~$ git status

        modified:   index.html
        modified:   stylesheet.css
```

## 変更したコードを把握する
`git diff `を使うと追加された部分が緑色で表示され変更を確認することができます。コードを変更した部分では、変更前のコードが赤色、変更後のコードが緑色で表示されます。

```
~$ git diff
```
## addしたファイルを確認する
`git status`を使うことで、どのファイルを`add`していて、どのファイルがまだされていないかを確認することができます。`add` されたファイルが緑色、されていないファイルが赤色で表示されます。

```
~$ git status
```

## コミット履歴を確認する
`git log`というコマンドを使うと、自分や他人のコミットを確認することができます。変更内容が見たい場合は、`git log -p`を実行します。表示内容が多い場合は上下矢印キーで、表示範囲を変え、Qキーを押すことで終了できます。

## 参考
下記のサイトで勉強できます。ぜひ参考にしてみてください。

https://prog-8.com/courses/git

https://prog-8.com/docs/git-env

https://prog-8.com/docs/git-env-win

https://dotinstall.com/lessons/basic_git

https://dotinstall.com/lessons/basic_githubpages_v2
Leetcode 問題番号 2423

なんか詰まったけどwordのサイズが100以下なので二重for文を回しても
```math
10^4
```
くらいなのでまあ良いかと思って2重for文を回して、
最初のfor文でDeleteするIndexを指定し、2つ目のfor文でそれ以外の文字を全探索する。
std::map<char,int>に要素をKeyにその数をValueに取って足し上げていく。
それでmapに対してfor文を回してValueの数が1だった場合にtrueを返す。
計算量は最大で以下の通りになるかな？
```matn
10^2\times(10^2\times26)
```
もっと良いやり方あったら教えてください。
コードは以下の通りです。


```C++
/*
 * @lc app=leetcode id=2423 lang=cpp
 *
 * [2423] Remove Letter To Equalize Frequency
 */

// @lc code=start
#include <bits/stdc++.h>
class Solution
{
public:
    bool equalFrequency(std::string word)
    {
        for (int i = 0; i < word.size(); ++i)
        {
            std::map<char, int> memo;
            for (int j = 0; j < word.size(); ++j)
            {

                if (j == i)
                {
                    continue;
                }
                if (memo.count(word[j]))
                {
                    memo[word[j]]++;
                }
                else
                {
                    memo[word[j]] = 1;
                }
            }
            std::set<int> st;
            for (auto i : memo)
            {
                st.insert(i.second);
            }
            if (st.size() == 1)
            {
                return true;
            }
        }
        return false;
    }
};
// @lc code=end
```
脱Wraptas

# はじめに
WraptasはNotionで作成したドキュメントからサイト立ち上げられるのでめっちゃ便利です。
作って運用してみたものの滅多に更新しない場合はAWSに移動したくなりますよね。
ってなわけで引っ越しをしました。

# 構成
S3 + CloudFrontで実装します。
S3とCloudFrontは普通にデフォルトの設定のままで基本大丈夫です。

心配な人は他の資料見てください。

先にS3のBucketを作ります。
公開するBucketですがS3から直接公開するわけではないので「Block all public access」のままでOKです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210754/dc05f2f7-6b8f-8408-bee3-298a7a80a9cd.png" width="400">
他もぜーんぶデフォルトでOKです。
Bucketが作成できたらファイルをアップロードしますが、下記のHTMLの変換をしてからが良いです。

次にCloudFrontの箱を作ります。
Origin domainのプルダウンに先程作ったBucketが表示されるのでそれを選択します。
他の設定はそのままで大丈夫です。
CloudfrontでS3連携の設定するとS3の設定を変更しろって言われるので、
S3のBucketのPermissions > Bucket policyからpolicyの設定をしてあげてください。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210754/edea80e8-2d19-e832-3b1d-c65d5c5fbc0f.png" width="400">

ドメインが新規取得でしたらRoute 53でドメインの設定をしてください。
AWS Certificate Manager (ACM)の取得も必要です。
特にCloudFrontはus-east-1で取得したACMしか設定できないのでご注意ください。
設定しないとSSLアクセスでエラーになります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210754/4cf5f8bf-6d66-319f-1ac8-0a1f0989b1d5.png" width="200">

あとは再度CloudFrontの設定にうつって
Alternate domain name (CNAME) - optional
で受け入れるドメインの設定を
Custom SSL certificate - optional
でSSL証明書の設定を
Default root object - optional
でindex.html的な設定をするのですが後述のHTML変換後で大丈夫です。

ここまで設定していざアクセスすると日本語のファイル名がうまくS3で動いてくれません。
URLEncode的なやつっぽいのですが面倒なのでファイルごと変換しちゃいます。

# HTMLの変換
先述の通りNotionで出力したHTMLはファイル名が日本語でS3 + CloudFrontで上手く処理できないのでファイルを一括変換しちゃいます。
また、WraptasはスマフォやSEO最適化の処理が入ってます。
なので最低限ではありつつも変換処理しちゃいます。
もっとやれると思うのですがいったん簡単な処理までで。。。

```PHP
<?php
$dir = "./";
$hash_length = 32;
$ext_length = 5;
$css = "
@media (max-width: 640px) {
  .column-list {flex-direction: column;}
  .column-list > *, .column > * {
    width: 100% !important;
    box-sizing: border-box;
    padding: 0;
  }
}
article {margin: 0 10px;}
";
$html = '<meta name="viewport" content="width=device-width"/>';

$dh = opendir($dir);
while (($file = readdir($dh)) !== false) {
    if (substr($file, -1 * $ext_length, $ext_length) != '.html' || strlen($file) == ($hash_length + $ext_length)) {
        continue;
    }
    $new_file = substr($file, -1 * ($hash_length + $ext_length), ($hash_length + $ext_length));
    rename($file, $new_file);
    $contents = file_get_contents($new_file);
    $contents = preg_replace('/a href="([a-zA-Z0-9%\(\)]+)%20([a-zA-Z0-9]{32})\.html"/', 'a href="$2.html"', $contents);
    $contents = str_replace("</style></head><body>", $css . "</style></head><body>", $contents);
    $contents = str_replace("<head>", "<head>" . $html, $contents);
    file_put_contents($new_file, $contents);
}
closedir($dh);
```

を該当フォルダに置いて実行するだけです。
（実行は自己責任でお願いします）

これをS3にアップロードして、
Default root object - optional
を最初のページのhash値.htmlにしてあげればOKです。
Angularで非シングルトンサービスを作りたい

Angularのサービスはシングルトンですが、サービス内でスレッドアンセーフなライブラリを使用している場合等は非シングルトン化したいためそのメモです。


module.tsのprovidersに下記のように設定すればOKです。

```typescript:app.module.ts
@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
  ],
  // 下記のように、都度新しいインスタンスを返却するようなFactoryを記載
  providers: [
    {
      // どういう名前でDIさせるか
      provide: NotSingletonService,
      useFactory: () => {
        return new NotSingletonService();
      },
      deps: [],
    },
  ],
})
```

ちなみに、useFactoryでインスタンス化したServiceがさらにDIで他クラスをコンストラクタインジェクションしている場合は下記のように書けばOKです
```typescript:app.module.ts
@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
  ],
  // useFactoryにDIしたい依存サービスを渡せばOK
  providers: [
    {
      provide: NotSingletonService,
      useFactory: (s1: DependencyService1, s2: DependencyService2) => {
        return new NotSingletonService(s1, s2);
      },
      deps: [],
    },
  ],
})
```

以上です。
あんまり使うシーンはないかもです。
次世代プロジェクト管理ツールの構想を練る（１）

ひとまず、Redmineに疲れたので、ここらで重い腰を上げることにした。

# コンセプト
まずはBingのチャットに相談してみる。

### プロジェクトやタスクの管理アプリケーション、積み上げられたカード、扱われた3Dオブジェクト。デザインのアイデアをください。

* なるほど。積み上げられたカードと扱われた3Dオブジェクトをインターフェイス要素として使用した、プロジェクトとタスクの管理アプリケーションを作りたいのですね。ここで、私が考えたデザインのアイデアを紹介します：

* メイン画面には3Dワークスペースが表示され、ユーザーはさまざまなプロジェクトを表すカードの山を見ることができます。ユーザーは、ジェスチャーやマウス操作で、カードの山を拡大・縮小したり、回転させたり、移動させたりすることができます。
* 各カードは、プロジェクト内のタスクを表します。カードには、タスク名、ステータス、優先度、期限、担当者が表示されます。カードをめくって詳細を見たり、タスク情報を編集したりすることもできます。
* カードを山から山へドラッグ＆ドロップすることで、プロジェクトやステータスを変更することができます。また、空いたスペースをタップまたはクリックすることで、新しいカードや山を作成することができます。
* ユーザーは、山を選択すると、プロジェクト名、説明、進捗状況、予算、チームメンバーなどのプロジェクト情報の概要を確認することができます。また、画面上部のメニューバーから、チャット、カレンダー、レポート、設定などの機能にアクセスすることができます。

# 出来たイメージ
![OIG.hfcv8OsAmH1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212914/814929b3-498a-2e11-fc54-a0937238b4ef.jpeg)
![OIG.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212914/9127d08a-8fdf-3385-0623-8cfcd5452fdf.jpeg)
# 複雑すぎて嫌だ。
* 私はそれが好きではありません、それはあまりにも複雑です。それは残念ですね。どこが複雑すぎると思いますか😞？
* 山やカードが多すぎて、必要なものを探すのが大変です。

# 改善提案
* なるほどね。必要なものを見つけやすくするための機能を追加することで、デザインを改善できるかもしれませんね。例えば：
* キーワードを入力して、名前、ステータス、優先度、期限、担当者などで山やカードを絞り込むことができる検索ボックスを追加することができるんだ。
* アルファベット順、期限、進捗状況、重要度など、さまざまな基準で山やカードを並べることができるソートオプションを追加できます。
* プロジェクトの種類、チーム、クライアント、場所など、さまざまなカテゴリーで山やカードを整理できるグループ化オプションを追加することができます。
* 色分けオプションを追加して、ステータス、優先度、担当者に基づいて、山やカードに異なる色を割り当てることができます。
* これらの機能によって、私のデザインはより複雑でなく、より使いやすくなると思いますか😊？

# イメージ

![OIG (2).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212914/91b46672-4905-a76f-f945-76d8fa017389.jpeg)

これでもかと言うぐらい、山積みにされてしまった。
仕方がないので、Bluewillowにも描かせてみました。
```
project board, gantchart, an image of a project and task management application that uses piled cards and handled 3D objects as the interface elements, with added features for searching, sorting, grouping, and color-coding 
```

今回はプロジェクトボード、ガントチャートを強調してみました。
![c0e10892-fa34-43da-b1af-92c0e24580a4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212914/c1fe3fb2-c353-c3ca-603c-b334c2b39d15.jpeg)


# 所感

* 個人的には斜視の３Ｄがお気に入り

* 叩くと垂直に飛び出してくる

* 高さ方向に、進捗度合いなどの情報を割り付けることで、二次元では見にくいものが可視化できる


# なかなか、いいですね～ 
　デザインが楽しくなってきました。
Ubuntu 22.04 LTSにフラットCMSのGravをインストールする

## はじめに

自分でブログやWebサイトを作るとして、無料・有料ブログサービスを使わず、自分でサーバーを借りて環境を構築するなら、何を使うのが良いかを考えていました。

ざっくりとしていますが、要件です。

* インストールが楽
* DBを使わないで済めば、そのほうが良い
* できればMarkdownで書きたい
* 管理画面はほしい

WordPressは定番ですけれど、プラグインに振り回されそうなので避けようと思いました。

また、サイトジェネレーターを使うことも考えて、いくつか試してみましたが（Hugo、Hexoなど）、ページやカテゴリなどの管理は管理画面で行えたほうが良いと思いました。VS Codeを使えばMarkdownが書きやすいのは良かったのですけれど。

そうやって探しているうちに、GravというフラットCMSの存在を知りました。

https://getgrav.org/

これなら要件を満たせそうだと思えたので、家庭内サーバーで試しにインストールしてみることにしました。

## 環境

* OSはUbuntu Server 22.04 LTS
* Webサーバーはnginx

## 手順

### Gravのインストールに必要なパッケージをインストールしておく

```
$ sudo apt install php php-fpm nginx php-curl php-gd php-mbstring php-zip unzip
```

PHP、FPM（PHPにおけるFastCGI実装）の他、php-で始まるパッケージはGravが必要としているパッケージです。

あと、Zipファイルを解凍するのに必要なので、unzipをインストールします。

### Gravのパッケージをダウンロードする

https://getgrav.org/downloads からダウンロードします。

管理用のプラグインがあったほうが良いので、「Grav Core + Admin plugin」をダウンロードします。

なお、ブラウザでダウンロードすると、ダウンロードしたときのファイル名は「grav-admin-v1.7.40.zip」となりますが、wgetでダウンロードすると「1.7.40」というようにバージョンがファイル名になりますので、-oオプションでファイル名をつけるようにしました。

```
$ wget https://getgrav.org/download/core/grav-admin/1.7.40 -o grav-admin-v1.7.40.zip
```

### GravのZipファイルを展開・配置する

/var/www/html/gravディレクトリに置いて、「http(s)://<サーバー名>/grav」でアクセスできるようにします。

```
// Zipファイルを/var/www/htmlに展開する
$ sudo unzip grav-admin-v1.7.40.zip -d /var/www/html/

// ディレクトリ名をgrav-adminからgravに変更する
$ sudo mv /var/www/html/grav-admin /var/www/html/grav
```

### /var/www/html/grav/の所有者をwww-dataに変更する

これをしておかないと、/var/www/html/grav/内のファイル・ディレクトリにある設定ファイルへの書き込みができずにエラーとなります。

```
$ sudo chown -R www-data:www-data /var/www/html/grav/
```

### nginxの設定

/etc/nginx/sites-available/grav を以下のように設定します。

```
server {
    listen 80;
    server_name _;
    root /var/www/html;

    location /grav {
        index index.php;
        try_files $uri $uri/ /grav/index.php?$query_string;
    }

    location ~ \.php$ {
        # Caution: be sure the php8.1-fpm.sock matches your version
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}
```

この後、サイトの有効化と設定ファイルのチェックを行い、設定ファイルチェックがOKとなったのを確認して、nginxを再起動しました。

```
$ sudo ln -s /etc/nginx/sites-available/grav /etc/nginx/sites-enabled/grav
$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ sudo systemctl restart nginx.service
```

### Gravの初期設定

「http(s)://<サーバー名>/grav」にアクセスして、以下の画面が表示されればGravのインストールは成功です。
なお、URLは「http(s)://<サーバー名>/grav/admin」にリダイレクトされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2544399/58491f82-0f7a-8a81-2e77-6e3989e2be7a.png)

Title以外は必須項目なので、各項目を入力します。
パスワードはアルファベットの大文字・小文字の混在で、8文字以上とします。

入力を終えたら「Create User」ボタンを押下します。

### Gravのダッシュボード

ユーザーの作成に成功すると、Gravのダッシュボードが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2544399/a643d962-b62b-702d-d282-b64a0416e1ce.png)
React propsを利用した値の受け渡し

# 成果物

3つのカウンターが用意されており、それぞれ独立してボタンを押すたびに数字が変化します。

https://codesandbox.io/embed/ji-suan-botan-arenzi-c2dl2i?fontsize=14&hidenavigation=1&theme=dark

# プログラム概要
- [ ] App.js:メインの処理を記述するプログラム
- [ ] index.js:AppコンポーネントをDOMに変換し、HTMLにレンダリングする
- [ ] Styles.css:デザイン

# props
propsとは親コンポーネントから子コンポーネントへ渡される属性値(データ)であり、子コンポーネントでは引数で受け取った`値`や`関数`を利用することができます。

# コード
## コンポーネントの構成
コンポーネントの構成は以下のようになります。
AppコンポーネントからCounterコンポーネントへ`name`を渡し、CounterコンポーネントからCounterTextコンポーネントへ`count`とAppコンポーネントから渡された`name`を渡します。

![スクリーンショット 2023-03-29 19.22.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884987/0df195a9-aa0b-743c-3dea-4b638e5fce84.png)

## index.js
```javascript
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";

import App from "./App";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <StrictMode>
    <App />
  </StrictMode>
);
```

## App.js
```javascript
import React, { useState } from "react";
import "./styles.css";

// Counterコンポーネントからnameとcountを受け取る
const CounterText = (props) => {
  return (
    <p>
    　  // props.xxxと記述することで親コンポーネントから渡された値、関数を使用できる
      {props.name} : {props.count}
    </p>
  );
};

// useStateに利用するための初期値を設定
const INITIAL_COUNT = 0;

// Appコンポーネントから渡されたnameを受け取る
const Counter = (props) => {
  
  // 状態変数countと状態変数を変更する関数setCountを設定 
  const [count, setCount] = useState(INITIAL_COUNT);

  // 「+1 ボタン」が押された際に実行する関数、カウントを+1する
  const countAdd = () => setCount((prevCount) => prevCount + 1);

  // 「-1 ボタン」が押された際に実行する関数、カウントを-1する
  const countSub = () => setCount((prevCount) => prevCount - 1);

  // 「リセットボタン」が押された際に実行する関数、カウントをリセットする
  const countReset = () => setCount(INITIAL_COUNT);

  return (
    <>
      // CounterTextコンポーネントに count と name を渡す
      <CounterText count={count} name={props.name} />

      // ボタンが押された際にcountAdd関数を実行
      <button onClick={countAdd}>＋1 ボタン</button>

      // ボタンが押された際にcountSub関数を実行
      <button onClick={countSub}>-1 ボタン</button>

      // ボタンが押された際にcountReset関数を実行
      <button onClick={countReset}>リセットボタン</button>
    </>
  );
};

export default function App() {
  return (
    <>
      // Countrコンポーネントを3つ作成する(画面にはname違いのコンポーネントが3つ出力される)
      // Counterコンポーネントに name を渡す（値はそれぞれ"カウント1", "カウント2", "カウント3"）
      // ３つのCounterコンポーネントはそれぞれ独立しており、別のコンポーネントで値が変更されても他のコンポーネントには影響しない
      <Counter name="カウント1" />
      <Counter name="カウント2" />
      <Counter name="カウント3" />
    </>
  );
}
```

## styles.css
```css
.App {
  font-family: sans-serif;
  text-align: center;
}
```
【驚き】macOSでの音声入力がこんなに快適だとは知らなかった！

## はじめに

最近、音声入力の便利さに目覚めました。使っていくなかで、macOSでの単語登録方法や音声入力特有の不具合の対処法（シェルコマンドの活用）など、いくつかのハックを発見しました。

3〜4年前より、若い人たちから音声入力を勧められていたものの、聞き流していました。キーボード入力のスピードが速いことについて、まあまあ自信があったからです。しかし音声入力を実際に使ってみて、その便利さに驚きました。

そこで、この記事では、発見した音声入力の便利な使い方や、問題を解決するための方法について、簡潔にまとめてご紹介したいと思います。

## 音声入力の便利さについて

音声入力を便利に使うためには、多少考え方を変える必要があると思います。

1つ目は、とりあえず話し始めること＋遠慮なく途中で止まる（音声入力を待たせる）こと。

2つ目は、音声入力の精度をあまり気にしないことです。音声入力の精度は感覚的には70%〜80%ぐらいです。手直しをせずに使えるほど正確ではありません。でも、変換操作がないのは、想像以上に快適です。とりあえずざくっと入力し、キーボードを使って修正する手順の方が、ストレスが少ない実感があります。

## 音声入力を開始するショートカットキー

macOSの場合、「システム環境設定」でショートカットキーが設定できます。デフォルトは地球マーク（グローブキー）の２回押しです。Happy Hacking Keyboardにグローブキーがないので、カスタムのショートカットキーの設定が必要になります。どのキーの組み合わせが良いかは、ChatGPTに相談しました。おすすめのなかから`option + S`を選びました。これはとてもいいです。

## macOSでの単語登録方法は？

macOSでの音声入力の最大の欠点は、単語登録ができないことです。「システム環境設定」で単語登録をしても反映されません。仕方がないので、連絡先を利用しています。 連絡先に書かれている名前と読み仮名を反映しようとする挙動を利用して、よく使う単語を連絡先に登録しています。

![SS 2023-03-29 19.19.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184128/80aec912-fecf-4fde-70af-b066389e60d5.png)

これは本来、macOSで音声入力の単語登録をできるように作るべきところです。あまり筋の良い回避策ではないと思うのですが、単語登録できない事は、あまりにも不便なので、このような暫定的な措置をとっています。将来的には不要になるはずなので、通常の連絡先と混ざらないように「音声入力の単語登録用」とリストを作ってここにまとめています。

ちなみに、macOS用に作っておくとiPhoneでも登録単語が使えるのも、地味に便利です。

## 音声入力特有の不具合と対処法

macOSの音声入力で困っていることは、しょっちゅうしょっちゅう半角スペースが入ること。「かっこ」と言えば「(」を入力してくれるのですが、半角の「(」になってしまうことです。半角スペースのほうは、英語を想定すると当然そのような挙動になるのですが、日本語にとっては不便でしかありません。「(」が半角であることは納得できませんが、変更方法が見つからないので、どうにもなりません。

これを手で直すのは、毎回毎回100%発生することなので、あまりにもめんどくさいです。そこで簡単なシェルスクリプトを作って、Alfredから `shift + command + delete` で起動するようにしました。

![SS 2023-03-29 19.31.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184128/92cbd5a9-7a13-6280-21b4-703186b1db88.png)


```bash
# 第1引数から半角スペースを除去し、変数no_space_inputに代入する。
no_space_input="${1// /}" 

# 連続する2つの改行を1つにする
single_linebreak=$(echo "$no_space_input" | awk 'NF{print $0"\n"}' | awk -v RS= -v ORS='\n\n' '1')

# 全角文字に変換
fullwidth_open_paren="${single_linebreak//(/（}"
fullwidth_close_paren="${fullwidth_open_paren//)/）}"

# 結果を出力
echo -n "$fullwidth_close_paren"
```

こういうコードも、ChatGPTの力を借りると簡単に作れました。職業的プログラマではない人間が、カジュアルにコードを書くのは本当に便利になりました。

## まとめ：音声入力でストレスフリーな文章入力を手に入れよう！

音声入力を使うことで、キーボード入力よりもストレスが少なく、より快適に文章を入力することができます。しかし、音声入力には精度に関する問題があります。そのため、手直しを減らせるような、使い方の工夫が必要です。

macOSでは、単語登録ができないという欠点がありますが、この問題を解決するために、連絡先に名前と読み仮名を登録することで対応しました。

また、音声入力には半角スペースが入り込んでしまうという問題もありますが、シェルスクリプトを使ってこの問題を解決することができます。Alfredと組み合わせることで、ショートカットキーを利用して簡単にスクリプトを起動することもできます。

今後も、より使いやすい方法を模索していきたいと思っています。音声入力を活用することで、より快適な入力環境を手に入れましょう。

### 環境

- Alfred 5.0.6
- macOS Venture 13.1
- MacBook Pro (14-inch, 2021)
Rustにおけるシングルトンの実装方法

オブジェクト指向プログラミングにおいて、シングルトンはクラスのインスタンス化を1つのインスタンスに制限し、そのインスタンスへのグローバルなアクセスポイントを提供するデザインパターンです。Rustでは、`static`キーワードと`unsafe`ブロックを使用してシングルトンを実装することができます。

以下のコードを見てみましょう。

```rust
use derive_getters::Getters;
use std::env;

#[derive(Debug, Getters)]
pub struct SingletonInteger {
    value: i64,
}

pub fn singleton_integer() -> &'static SingletonInteger {
    unsafe {
        if SINGLETON_INTEGER.is_none() {
            SINGLETON_INTEGER = Some(SingletonInteger { value: 123 });
        }
        SINGLETON_INTEGER.as_ref().unwrap()
    }
}

static mut SINGLETON_INTEGER: Option<SingletonInteger> = None;
```



このコードは、単一の`i64`型の`value`フィールドを含む`SingletonInteger`構造体を宣言しています。`Getters`マクロを使用して、`value`フィールドに対するgetterを生成します。

`singleton_integer()`関数は、静的`SingletonInteger`インスタンスへの参照を返します。`unsafe`ブロックが使用されているのは、静的可変変数を変更しているためです。 `SINGLETON_INTEGER`変数が`None`の場合、新しい`SingletonInteger`インスタンスを作成し、`value`フィールドを`123`に設定します。

## なぜ`value`フィールドがプライベートなのか？

Rustでは、フィールドをプライベートにしてアクセサを提供することが推奨されています。これは、フィールドのアクセス方法を制御し、不変条件を強制することができるためです。

この場合、`SingletonInteger`インスタンスを1度しかインスタンス化しないようにし、`value`フィールドが設定された後に変更されないようにしたいため、`value`フィールドをプライベートにしています。

`Getters`マクロは、プライベートな`value`フィールドにgetterを生成するために使用されます。これにより、直接`value`フィールドを公開することなく、読み取り専用で`value`フィールドにアクセスすることができます。

## クライアントコードでシングルトンを使用する方法

クライアントコードで`singleton_integer()`関数を使用するには、モジュールをインポートし、関数を呼び出します。

```rust
use my_module::singleton_integer;

fn main() {
    let value = singleton_integer().value();
    println!("The value is {}", value);
}
```

この例では、`singleton_integer()`関数を呼び出して`SingletonInteger`インスタンスへの参照を取得し、`get_value()`メソッドを呼び出して`value`フィールドの値を取得しています。

## まとめ

この記事では、`static`キーワードと`unsafe`ブロックを使用してRustでシングルトンを実装する方法を学びました。また、フィールドをプライベートにし、アクセサを提供することが推奨される理由、そしてプライベートフィールドにgetterを生成するための`Getters`マクロを使用する方法についても学びました。

シングルトンを実装することで、クラスのインスタンスを1つだけ作成し、そのインスタンスへのグローバルアクセスを提供することができます。これは、設定やデータベース接続など、アプリケーション全体で1つの状態を維持したい場合などに役立ちます。
Pythonの処理時間計測をwith構文とデコレータで実装する

何番煎じかわかりませんが、もっとうまい方法を見つけるまでしばらくは何度も使い回しそうなのでメモ。

## 処理時間計測の課題

どんな言語を使っていても、作ったプログラムの実行時間を計測したい状況には度々遭遇します。

場当たり的には以下のように計測コードを埋め込んでコメントアウトをして……を繰り返しますが、測りたい部分が増えるにつれコードが汚くなっていきます。また、処理に関係ないコードが放置されているのは読み手の可読性を損なう可能性があります。


```python
import time

# あるあるソース
t1 = time.time()
・・・（そこそこ時間がかかる処理）・・・
t2 = time.time()
print(f"{t2 - t1}秒")
```

Pythonの場合は、with構文とデコレータを使うことで比較的コードをきれいにしたまま時間計測のコードを組み込むことができます。

## With構文を使った例

次のようなクラスを定義します。

```python
import time
import logging

class Timer:

    def __init__(self, name):
        self.name = name

    def __enter__(self):
        self.t_start = time.time()

    def __exit__(self, ex_type, ex_value, trace):
        self.t_end = time.time()
        logger.debug(f"{self.name}: {self.t_end - self.t_start} 秒")
```

```__enter__```と```__exit__```を定義することで、作ったクラスをwith構文で利用できるようになり、with構文に入ったときに```__enter__```が呼ばれ、with構文から抜けたときに```__exit__```が呼ばれるようになります。この仕組みを利用し、```__enter__```で計測開始時間、```__exit__```で計測終了時間を取得し、ついでにログに出力するようにすることで、時間計測とログ出力を共通化できるようになります。

例えば次のように使用します。

```python
with Timer("time.sleep の時間計測"):
    logger.info("1秒間寝ます。")
    time.sleep(1)
    logger.info("起きました。")
```

この場合、次のような出力が得られます。

```
2023-03-28 21:10:11,199 [INFO    ] 1秒間寝ます。
2023-03-28 21:10:12,207 [INFO    ] 起きました。
2023-03-28 21:10:12,208 [DEBUG   ] time.sleep の時間計測: 1.009138584136963 秒
```

## デコレータを使った例

時間計測を関数単位で行いたい場合は、デコレータを使ったほうがより簡潔に記述できます。

まずデコレータを作成します。ここでも先程作ったTimerクラスを使用していますが、直接```time.time()```を呼んでしまってもいいです。

```python
def timer_dec(func):
    def wrapper(*args, **kwargs):
        with Timer(f"関数 {func.__name__} の実行時間"):
            return func(*args, **kwargs)
    return wrapper
```

デコレータを使うときは、関数定義の直前に```@```記号を使用してデコレータの呼び出しを宣言します。

```python
@timer_dec
def sleep():
    logger.info("1秒間寝ます。")
    time.sleep(1)
    logger.info("起きました。")
```

この場合も、次のような出力が得られます。便利。

```
2023-03-28 21:10:12,209 [INFO    ] 1秒間寝ます。
2023-03-28 21:10:13,219 [INFO    ] 起きました。
2023-03-28 21:10:13,220 [DEBUG   ] 関数 sleep の実行時間: 1.0108270645141602 秒
```

## おわりに

with構文の場合インデントが1段下がるのがちょっと気になるので、もっといい書き方あれば教えてください。。。