OmniAICreator/Qiita-1.07M huggingface dataset:
Go：入門者向けにポインタをコードで解説

# はじめに
Go言語のポインタの概念が分かりづらく、理解に時間を要しました。
初心者は、**文章よりもコードを書いて動かしてみる方が理解が深まると思うので**、本記事のコードを参考にしてください！

# ポインタとは
ポインタは、ある値が格納されているメモリアドレスを保持する変数です。
ポインタを使用すると、直接そのメモリアドレスにアクセスして値を読み書きすることができます。
以下の演算子を使用して、ポインタを宣言します。

- &演算子：変数のメモリアドレスを取得します。
- *演算子：ポインタが指すメモリアドレスの値にアクセスします。

# 例：ポインタを使って値を変更する

```go
package main

import "fmt"

func main() {
    number := 10                                // 整数変数の定義
    fmt.Println("元の値:", number)               // 元の値の出力
    fmt.Println("numberのアドレス:", &number)      // numberのメモリアドレスの出力

    ptr := &number                             // numberのアドレスをptrに格納
    *ptr = 20                                  // ptrが指すアドレスの値を20に変更
    fmt.Println("ptrが指す値:", *ptr)           // ptrが指す値の出力
    fmt.Println("ptrの値（アドレス）:", ptr)        // ptrの値（実際はアドレス）の出力

    fmt.Println("変更後の値:", number)          // 変更後の値の出力
}
```

## 実行結果
このコードを実行すると、numberの値がポインタptrを通じて20に変更されます。
ポインタを使用することで、メモリアドレスを介して変数の値を直接操作できることがわかります。
```go
元の値: 10
numberのアドレス: 0xc0000120b8
ptrが指す値: 20
ptrの値（アドレス）: 0xc0000120b8
変更後の値: 20
```

# 例：ポインタを使う関数
ポインタを使用する関数は、引数や戻り値としてポインタを受け渡します。
以下の例では、整数のポインタを引数に取る関数を定義し、その値を変更します。

```go
package main

import "fmt"

// incrementは、整数のポインタを受け取り、その値を1増やし、ポインタを返します。
func increment(number *int) *int {
	fmt.Println("increment内のnumberのアドレス:", number) // increment関数内でのアドレスを表示
	*number += 1
	return number // 増加後の値のポインタを返す
}

func main() {
	myNumber := 10
	fmt.Println("元の値:", myNumber)                     // 元の値を表示
	fmt.Println("main内のmyNumberのアドレス:", &myNumber)   // myNumberのアドレスを表示

	newPtr := increment(&myNumber)                      // 戻り値としてポインタを受け取る
	fmt.Println("increment後の戻り値のアドレス:", newPtr)    // 戻り値のポインタを表示
	fmt.Println("increment後の戻り値の値:", *newPtr)       // 戻り値のポインタが指す値を表示
	fmt.Println("増加後:", myNumber)                    // 増加後の値を表示
}
```

## 実行結果
このコードを実行すると、increment関数がmyNumberの値を変更し、その新しい値（11）を返していることがわかります。
また、myNumberの値も同時に変更されていることが確認できます。

```go
元の値: 10
main内のmyNumberのアドレス: 0xc0000120b8
increment内のnumberのアドレス: 0xc0000120b8
increment後の戻り値のアドレス: 0xc0000120b8
increment後の戻り値の値: 11
増加後: 11
```
ncnnでsegment-anythingを動かしてみる

## 概要

ncnnで、領域分割を行う、segment-anythingを動かしてみる。

![Screenshot from 2023-11-19 11-04-06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73020/1eef10d1-f53a-1221-bf47-826347e0675e.png)

## 環境構築

構築手順に入る前に、まずPCに以下のバージョンのUbuntuをインストールした環境で、構築しました。

[M5UnitV2のクロスコンパイル環境を構築する手順](https://qiita.com/nnn112358/pbulic/9e15450265b689300216)

```
Ubuntu 22.04.3 LTS(x64)
OpenCV ver 0.4.4
ncnn ver 231027
```

## segment-anything-ncnnのダウンロード

GitHubからsegment-anything-ncnnのコードをダウンロードします。

```
$ git clone https://github.com/FeiGeChuanShu/segment-anything-ncnn.git

$ cd segment-anything-ncnn
```

## CMakeLists.txt ファイルの作成

cmakeでコンパイルするために、CMakeLists.txtを作成します。

```
gedit CMakeLists.txt
```

```CMakeLists.txt
SET(CMAKE_BUILD_TYPE "Release")

SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")

if(TARGET_COMPILER STREQUAL "arm")
    message(STATUS "TARGET_COMPILE STREQUAL arm")
    SET(CMAKE_CXX_COMPILER arm-none-linux-gnueabihf-g++)
    SET(CMAKE_C_COMPILER arm-none-linux-gnueabihf-gcc)
    SET(NCNN_INSTALL_DIR /opt/external/ncnn/install/arm/)
    SET(ZBAR_INSTALL_DIR /opt/external/zbar-0.10/build/arm)
    SET(OpenCV_DIR /opt/external/opencv/build/arm)
else()
    message(STATUS "TARGET_COMPILE STREQUAL X64")
    SET(NCNN_INSTALL_DIR /opt/external/ncnn/install/x64/)
    SET(ZBAR_INSTALL_DIR /opt/external/zbar-0.10/build/x64/)
    SET(OpenCV_DIR /opt/external/opencv/build/x64) 
endif()


PROJECT(ncnn_sam)
cmake_minimum_required(VERSION 3.5)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)

if(OpenCV_FOUND)
 message(OPENCV_VERSION)
 message(STATUS "OpenCV_LIBS: ${OpenCV_LIBS}")
 message(STATUS "OpenCV_INCLUDE_DIRS: ${OpenCV_INCLUDE_DIRS}")
endif(OpenCV_FOUND)

find_package(OpenCV REQUIRED)

link_directories(${OpenCV_LIBRARY_DIRS})

include_directories(${OpenCV_INCLUDE_DIRS} )
include_directories(${PROJECT_SOURCE_DIR})
include_directories(${PROJECT_SOURCE_DIR}/include)
include_directories(${NCNN_INSTALL_DIR}/include/ncnn)

add_executable(ncnn_sam src/main.cpp src/segment_anything.cpp src/pipeline.cpp)
target_link_libraries(ncnn_sam ${OpenCV_LIBRARIES})
target_link_libraries(ncnn_sam ${NCNN_INSTALL_DIR}/lib/libncnn.a -fopenmp)
```
## 学習モデルのダウンロード

segment-anything-ncnnのGitHubのリンクから、学習モデルをダウンロードします。

![Screenshot from 2023-11-19 11-08-36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73020/17065793-0916-3b65-6705-e66acf84f55c.png)

modelsフォルダに学習モデルを格納します。

```
$ ls models/
decoder.bin    encoder-einsum.bin    encoder-matmul.bin
decoder.param  encoder-einsum.param  encoder-matmul.param
```

## ビルド
プログラムをコンパイルして実行します。

```shell
$ cmake .
$ make
$ cd bin
$ ./ncnn_sam
start image encoder...
finish image encoder...
automatic mask
processing: 0/32
processing: 1/32
processing: 2/32
processing: 3/32
~~~
processing: 31/32
processing: 32/32
```


## 参考資料
この記事を作成するにあたり、以下のウェブサイトを参考にしました。

[FeiGeChuanShu/segment-anything-ncnn](https://github.com/FeiGeChuanShu/segment-anything-ncnn)

[facebookresearch/segment-anything](https://github.com/facebookresearch/segment-anything)
wslでelixir その106

# 概要
wsl(wsl2じゃない)で、elixirやってみた。
githubに、livebook、アップしたので、Run in Livebookバッジ、作ってみた。

# 手順

# このlivemdを、バッジにします。


https://github.com/ohisama/ohi_Livebook/blob/main/stage1/hello.livemd


# 以下の URL にアクセスします
https://livebook.dev/badge

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/2b68791a-8908-45e7-92cc-5df9a8d88a3d.png)

# 貼り付けてみた。

Markdown

[![Run in Livebook](https://livebook.dev/badge/v1/blue.svg)](https://livebook.dev/run?url=https%3A%2F%2Fgithub.com%2Fohisama%2Fohi_Livebook%2Fblob%2Fmain%2Fstage1%2Fhello.livemd)

HTML

<a href="https://livebook.dev/run?url=https%3A%2F%2Fgithub.com%2Fohisama%2Fohi_Livebook%2Fblob%2Fmain%2Fstage1%2Fhello.livemd">
  <img src="https://livebook.dev/badge/v1/blue.svg" alt="Run in Livebook" />
</a>

以上。
STRSQLでデータベースをチェック

## はじめに

プログラム開発後にその検証として、データベースを確認するという場面は多々あるかと思います。

その際、皆さんはどのようにデータベースを確認していますか？
私はIBM iに携わった当初、主に`WRKQRY`で確認していました。

しかし、コマンド実行→ファイル選択指定→レコードの選択...と手順が多く、ちょっとしたデータを確認するだけでも時間がかかってしまう場面があります。
（操作に慣れていない内は、対話式で分かり易く便利なのですが...）

今では効率的に業務を行うために`STRSQL`も使用しながらデータベースを確認しています。

この記事では、`STRSQL`を使用したデータベースのチェック方法を`WRKQRY`と比較しながらご紹介します。
（IBM iが`STRSQL`を使用できない環境の場合は「IBM i Access Client Solutions」からSQLを実行することができます。）


## STRSQLとWRKQRYでのデータベースの確認方法の違い

（画像１）
![checkDatabaseSTRSQL_01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2962887/7d580d7f-ef9d-eff6-5b1f-4cea6ecc7b83.jpeg)

（画像１）のように`WRKQRY`では4ステップ必要だった手順が、`STRSQL`では2ステップで可能となります。


## おわりに

**コマンド**
1. `STRSQL`
2. `select * from ライブラリー/ファイル`

ご参考になれば幸いです。最後までお読み頂きありがとうございます。

## 関連した投稿

[STRSQLでサンプルデータ作成（１）_データの更新](https://qiita.com/tamaday/items/0740b2840e175e557986)
[STRSQLでサンプルデータ作成（２）_データの追加](https://qiita.com/tamaday/items/dadbd1adf1e8dbbec117)
Step Functionsの超詳細解説

# はじめに
この記事はDevOps on AWS大全の一部です。
DevOps on AWS大全の一覧は[こちら](https://qiita.com/tech4anyone/items/b06f88035d27c6ef13b2)。

この記事ではStep Functionsに関連する内容を超詳細にまとめています。

具体的には以下流れで説明します。

- AWS Step Functionsとは
- AWS Step Functionsの仕組み
- AWS Step Functionsのセキュリティ
- AWS Step Functions for Advance

AWSの区分でいう「Level 200：トピックの入門知識を持っていることを前提に、ベストプラクティス、サービス機能を解説するレベル」の内容です。

# この記事を読んでほしい人
- Step Functionsがどういうサービスか説明できるようになりたい人
- Step Functionsを採用するときのベストプラクティスを説明できるようになりたい人
- AWS Certified DevOps Engineer Professionalを目指している人

# AWS Step Functionsとは

AWS Step Functionsは、クラウド上で分散型および非同期的なアプリケーションワークフローを構築するためのマネージドサービスです。
異なるAWSサービスやAPI呼び出しを含むアプリケーションのステップを組み合わせ、実行することができるツールであり、その柔軟性と効率性により、開発者はビジネスロジックの実装に専念できるようになります。

# AWS Step Functionsの仕組み
## ステート
Step Functionsの基本的な構成要素は「ステート」です。
ステートは単一のタスクや処理を表し、それが成功した場合、次にどのステートに進むかを定義します。
ステートはAWS Lambda関数、Amazon ECSタスク、Amazon SNS通知など、様々なAWSサービスを実行することができ、異なるサービスの統合が容易になり、柔軟性が向上します。

## ステートマシン
ステートはステートマシンによって組み合わせられ、ワークフロー全体を形成します。
ステートマシンはステートの継続フローを制御し、成功や失敗などのイベントに基づいて次に実行するステートを決定します。
これにより、複雑なワークフローを階層的かつ柔軟に構築できます。

## フロー制御
Step Functionsは条件分岐、反復処理、パラレル処理など、様々なフロー制御の機能を提供します。
これにより、複雑なビジネスロジックを柔軟かつ効果的にモデル化することができます。
例えば、特定の条件が満たされた場合に異なるステートに進むような条件分岐や、複数のステートを同時に実行するパラレル処理などが可能です。

具体的なフロー制御の機能はState Typeとして7種類定義されているので以下にまとめています。

| State Type | 説明 |
|--- | ---|
| Task | 単一の処理を行う |
| Wait | 指定した時間の間、処理をストップする |
| Pass | 入力をそのまま出力へ渡す |
| Parallel | 並列に処理を実行する |
| Choice | 一定の条件により分岐する |
| Fail | 実行結果を失敗として終了する |
| Succeed | 実行結果を成功として終了する |

## ロギングとトレーシング
Step Functionsは実行中のワークフローに関する詳細なログを提供し、デバッグやトラブルシューティングをサポートします。
CloudWatch LogsやX-RayなどのAWSサービスと統合されており、実行中のステートやデータのフローを視覚化することができます。
これにより、ワークフロー全体の可視性を向上させ、問題の特定や解決が容易になります。

# AWS Step Functionsのセキュリティ
Step Functionsはセキュリティを確保するためにIAMを活用しています。
適切なIAMロールを定義し、各ステートやLambda関数に与える権限を制御することで、アクセスを管理し、セキュアなワークフローを構築できます。
これにより、機密データへのアクセスを制限し、アプリケーション全体のセキュリティを確保します。

# AWS Step Functions for Advance
## ベストプラクティス
Step Functionsを効果的に活用するためのベストプラクティスにはいくつかの重要なポイントがあります。

まず、スモールステップの利用が挙げられます。
各ステートを単純で小さなタスクに分割することで、可読性とメンテナンス性が向上し、ワークフローの理解が容易になります。

また、トランザクショナルなパターンを採用し、ステートの実行中にエラーが発生した場合のリトライやエラーハンドリングを適切に設定することで、データの整合性を確保します。

補足です。
つい先日まではフローが失敗しても失敗時点からフロー実行することができなかったため冪等性の担保が非常に重要でした。
しかし、2023/11/15のアップデートでredriveと呼ばれる機能が追加され失敗時点からフローを再開できるようなったため、冪等性の担保の重要度はやや低下したといえるでしょう。

これまで、失敗時点からの再開ができないためにAirFlowを選んでいた人はまず、StepFunctionsを検討してみるとAWSコストも管理コストも下がる可能性が高いです。

# まとめ
この記事ではStep Functionsに関連する内容を超詳細にまとめました。

- AWS Step Functionsとは
- AWS Step Functionsの仕組み
- AWS Step Functionsのセキュリティ
- AWS Step Functions for Advance

次回はAWS System Managerについて超詳細解説します。
ReactプロジェクトをGitHub Pagesにデプロイ

## 結論

`% npm run build`で生成された`/build`ファイル配下だけpushされているブランチを公開すれば、GitHub Pagesでも公開できます

## 前提

`create-react-app`をしてReactプロジェクトを作成している

## 準備

公開用のブランチを準備
※ブランチ名の指定は特にないですが、
一般的に`gh-pages`というGitHub Pagesにデプロイする専用のブランチを使用するので、今回は`gh-pages`ブランチを作成します

https://engawapg.net/software-development/2393/github-pages-branch/

```bash
% git checkout -b gh-pages
```

公開用のブランチで`/build`配下をpushできるよう、
`./gitignore`から以下の記述を削除します。

```file:.gitignore
# production
/build
```

また、作業用ブランチ側に戻り、
`package.json`で`homepage`の設定をします。

```json:package.json
{
  // ...
  "homepage": "https://自身のGitHubユーザー名.github.io/リポジトリ名",
  // ...
}
```

これで準備ができたので、作業ブランチで作業をしていきます

## 公開

作業ブランチで作業をした後に`gh-pages`にマージをします

```bash
% git checkout gh-pages
% git merge 作業ブランチ
```

公開用ブランチでReactプロジェクトをビルド

```bash
% npm run build
```

git subtreeを使用して、`/build`ディレクトリ配下をpush

```bash
% git subtree push --prefix build/ origin gh-pages
```

あとはGitHub Pagesの設定から`gh-pages`ブランチを公開すればOKです。

![スクリーンショット_2023-11-19_9_53_43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1953754/a44c5e04-176f-26cd-1436-484547a1d0fc.png)

https://docs.github.com/ja/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site


## 後書き

今回は`npm run build`を使用して公開をしましたが、
事前に`gh-pages`パッケージをインストールして、
`npm run deploy`でそのままGitHub Pagesに公開できるフローもありました。

https://www.npmjs.com/package/gh-pages

https://qiita.com/yuitnnn/items/11375ea29ec023d19fdf

こっちの方が簡単そうだったので、
次はこの手法を使おうかなと思ってます。

## 参考

https://deku.posstree.com/react/github-pages/

https://docs.github.com/ja/pages/getting-started-with-github-pages/creating-a-github-pages-site
wslでnerves その22

# 概要
wsl(wsl2じゃない)でnervesやってみる。
qemu(x86_64エミュレータ、ラズパイじゃない)でやってみた。
生成したnerves_livebook.imgを、Mix.installをテストしてみた。

# テスト1

```
Mix.install([
  {:kino, "~> 0.11.0"}
])

:ok

```

# テスト2

```
Mix.install([
  {:kino_explorer, "~> 0.1.11"}
])

** (RuntimeError) Mix.install/2 is not supported on Nerves Livebook (yet!).

All is not lost, but you will have to rebuild the Nerves Livebook firmware.
Go to https://github.com/nerves-livebook/nerves_livebook and clone the repository.
Add :kino_explorer to the dependencies listed in the `mix.exs` file, build,
and then try again.

    (nerves_livebook 0.11.0) lib/nerves_livebook/mix_install.ex:51: NervesLivebook.MixInstall.check_dep/1
    (elixir 1.15.6) lib/enum.ex:984: Enum."-each/2-lists^foreach/1-0-"/2
    #cell:setup:1: (file)
```

# テスト3

```
Mix.install([
  {:vega_lite, "~> 0.1.6"},
  {:kino_vega_lite, "~> 0.1.10"}
])

:ok
```

# テスト4

```
Mix.install([
  {:kino_maplibre, "~> 0.1.10"},
  {:req, "~> 0.4.5"}
])

:ok
```

以上。
ncnnでUltralight-SimplePoseを動かしてみる

## 概要

ncnnで、Pose推定を行う、Ultralight-SimplePoseを動かしてみる

![Screenshot from 2023-11-19 10-05-36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73020/7c1e276b-21bb-a9cd-c250-93b133628400.png)


## 環境構築

構築手順に入る前に、まずPCに以下のバージョンのUbuntuをインストールした環境で、構築しました。

[M5UnitV2のクロスコンパイル環境を構築する手順](https://qiita.com/nnn112358/pbulic/9e15450265b689300216)

```
Ubuntu 22.04.3 LTS(x64)
OpenCV ver 0.4.4
ncnn ver 231027
```

## Ultralight-SimplePoseのダウンロード

GitHubからUltralight-SimplePoseのコードをダウンロードします。

```
$ git clone https://github.com/dog-qiuqiu/Ultralight-SimplePose
$ cd Ultralight-SimplePose/ncnnsample
```

## CMakeLists.txt ファイルの作成

cmakeでコンパイルするために、CMakeLists.txtを作成します。

```
gedit CMakeLists.txt
```

```CMakeLists.txt

SET(CMAKE_BUILD_TYPE "Release")

SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")

if(TARGET_COMPILER STREQUAL "arm")
    message(STATUS "TARGET_COMPILE STREQUAL arm")
    SET(CMAKE_CXX_COMPILER arm-none-linux-gnueabihf-g++)
    SET(CMAKE_C_COMPILER arm-none-linux-gnueabihf-gcc)
    SET(NCNN_INSTALL_DIR /opt/external/ncnn/install/arm/)
    SET(ZBAR_INSTALL_DIR /opt/external/zbar-0.10/build/arm)
    SET(OpenCV_DIR /opt/external/opencv/build/arm)
else()
    message(STATUS "TARGET_COMPILE STREQUAL X64")
    SET(NCNN_INSTALL_DIR /opt/external/ncnn/install/x64/)
    SET(ZBAR_INSTALL_DIR /opt/external/zbar-0.10/build/x64/)
    SET(OpenCV_DIR /opt/external/opencv/build/x64) 
endif()


PROJECT(ncnnpose)
cmake_minimum_required(VERSION 3.5)

if(OpenCV_FOUND)
 message(OPENCV_VERSION)
endif(OpenCV_FOUND)

find_package(OpenCV REQUIRED)

link_directories(${OpenCV_LIBRARY_DIRS})

include_directories(${OpenCV_INCLUDE_DIRS} )
include_directories(${PROJECT_SOURCE_DIR}/include)
include_directories(${NCNN_INSTALL_DIR}/include/ncnn)

add_executable(ncnnpose ncnnpose.cpp)
target_link_libraries(ncnnpose ${OpenCV_LIBRARIES})
target_link_libraries(ncnnpose ${NCNN_INSTALL_DIR}/lib/libncnn.a -fopenmp)
```

## ビルド

プログラムをコンパイルして実行します。

```
$ cmake .
$ make
$ ./ncnnpose
```

## プログラムの格納先
このプログラムは以下に格納しています。

https://github.com/nnn112358/Ultralight-SimplePose


## 参考資料
この記事を作成するにあたり、以下のウェブサイトを参考にしました。

[dog-qiuqiu/Ultralight-SimplePose](https://github.com/dog-qiuqiu/Ultralight-SimplePose)
マンデルブロ集合の内側を塗る

# 再帰プログラム
```math
5! = 5\times4\times3\times2\times1=120
```

階乗を求めるプログラム

```java
void setup() {
  print(f(5));
}

int f(int n) {
  if (n<=0) {
    return 1;
  }
  return n*f(n-1);
}
```

この再帰構造を参考にマンデルブロ集合を描く

```java
void setup() {
  size(512, 512);
  noLoop();

  double d = 2.5d/(double)width;
  int xx=0,yy=0;
  for (double y = -1.25; y < 1.25; y+=d) {
    for (double x = -2.0; x < 0.5; x+=d) {
      int c = mandel(0, 0, x, y, 255);
      stroke(c);
      point(xx,yy);
      xx++;
    }
    xx=0;
    yy++;
  }
}

int mandel(double zr, double zi, double cr, double ci, int ite) {
  double zr2 = zr*zr - zi*zi + cr;
  double zi2 = 2*zr*zi + ci;  
  if(0==ite || zr2*zr2 + zi2*zi2>4){
    return ite;
  }
  return mandel( zr2, zi2, cr, ci, ite-1);
}
```
![mandel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510257/f2ab8392-475f-858d-c0e4-46097bfd728c.png)

式変形して

![mandel_in_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510257/96c7c697-dc0c-4f99-ee9e-44f8a4a33740.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510257/74b5f871-5292-724a-eef7-4c97647f0d7e.png)

もう少しきれいに３Dで表示したい
z^3とかよくあるバリエーションについても
Django REST frameworkのメモ

# 背景

Chat GPT-3.5にDjango REST frameworkのAPIを作らせようとしたが、  
少し勉強しないとChat GPTが作ったコードの評価できないため、  
Django REST frameworkのチュートリアルなどを一通り読むことにした。

詳細な手順等は公式のチュートリアルを参照すればよいため、個人的に気になったことのみメモする。

# 学習内容

Djangoでプロジェクトやアプリを作成したら、  
Django REST frameworkを使うには下記ファイルを編集or新規作成する。  

- アプリ名/models.py  
    下記のように適当なモデルを追加する。

    ```models.py 
    from django.db import models
    
    class ArticleTag(models.Model):
        """タグ一覧"""
        tag_name = models.CharField(max_length=50)
        created_date = models.DateTimeField(auto_now_add=True)
        updated_date = models.DateTimeField(auto_now=True)
        is_deleted = models.BooleanField(default=False)
    
        class Meta:
            """デフォルトのソート条件"""
            ordering = ['created_date']
    ```
    &nbsp;
- アプリ名/serializers.py  
    モデルとJSONの変換できるように下記を記載する(シリアル化)。  
    本当はカラムごとに型を指定するコードの方がよさそうだが、  
    こちらの方が簡単に記載できる。

    ```serializers.py
    from rest_framework import serializers
    from .models import ArticleTag
    
    class ArticleTagSerializer(serializers.ModelSerializer):
        class Meta:
            model = ArticleTag
            # これで全てのカラムを指定できる
            fields = '__all__'
    ```

   &nbsp;
- アプリ名/views.py  
    下記はのように書けば概ね問題なし。DELETEなどを追加したいなら別途DELETE関数を追加する。  
    ListCreateAPIViewなどを使えばGET関数などを省略できるが、  
    簡略化されすぎてよくわからないため今回は採用しない。
    
    ```views.py
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import status
    from .models import ArticleTag
    from .serializers import ArticleTagSerializer
    
    class ArticleTagAPIView(APIView):
        def get(self, request):
            tags = ArticleTag.objects.all()
            serializer = ArticleTagSerializer(tags, many=True)
            return Response(serializer.data)
    
        def post(self, request):
            serializer = ArticleTagSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status=status.HTTP_201_CREATED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    ```

    
   &nbsp;
- プロジェクト名/setting.py
    
    下記のように`INSTALLED_APPS`にrest_frameworkとアプリ名を追加する。
    
    ```setting.py
    INSTALLED_APPS = [
        ...
        'rest_framework',
        'アプリ名',
    ]
    ```
    
    またページごとに返されるオブジェクト数を制限するために下記も追加しておく。
    
    ```setting.py
    REST_FRAMEWORK = {
        'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
        'PAGE_SIZE': 10
    }
    ```
    
    &nbsp;
    
- プロジェクト名/urls.py  
    下記のように対応したパスを定義する。
    
    ```urls.py  
    from django.urls import path
    from ..アプリ名.views import ArticleTagListView
    
    urlpatterns = [
        path('tags/', ArticleTagListView.as_view(), name='tag-list'),
    ]
    ```
&nbsp;

# 所感
一つのAPIを作成するにも色々な書き方があるため理解しにくかったです。
実際の開発現場では管理が大変そうです。
本記事に間違いがあればご指摘ください。
形式記述言語VDM++の制御構文（call文）をJava C# Re:Mind(自作言語)と比較

# はじめに

要求仕様記述段階での使用が主に想定されている数学的形式手法VDM(Vienna Development Method)[^1]のオブジェクト指向拡張言語VDM++ですが、ロジックの実装も記述できる構文が用意されています。本記事ではVDM++の制御構文（call文）をJavaとC#そして自作言語のRe:Mind[^2] と比較してみます。

## この記事内容の作業目的
VDM++の下記の制御構文[^3][^4]についていくつかの記事に分割して比較検討します。クラスの定義構文についてはこちらの記事[^5]を参照することができます。

・ブロック文
・コメント文
・代入文
・条件文
・ループ文
・**call文**　←本記事対象
・return文
・例外

VDM++はオブジェクト指向言語的な言語要素を持ち、数学的形式手法言語の中では一般のプログラミング言語のオブジェクト指向言語に近い面があります。また、VDMのような数学的形式手法を使わない場合は、一般的に自然言語の日本語で要求仕様が記述されているものと推察されます。本記事では数学的形式手法と自然言語の日本語記述とのはざまで、構造化された日本語が要求仕様記述に使えないか模索しています。


## この記事内容の保証
各言語で書かれた引用ソースの妥当性は保証されません。また、本記事に開示された自作言語の仕様は、開発中の仕様のため予告なく変更される場合があります。

# 構文比較
## VDM++
```vdm:VDM++
    doAnything: () ==> int
    doAnything() ==
    (
       i:int := 1;
       --todo1
       return i
    );
    doAnythingNoReturn: () ==> ()
    doAnythingNoReturn() ==
    (
       --todo1
       return
    );
    doAny: () ==> ()
    doAny() ==
    (
       j:int;
       --todo1
       j := doAnything();
       doAnythingNoReturn();  --call文
       return
    );
```
VDM++のcall文は戻り値のない操作を実行する際の構文でJavaやC#と同じです。上記例は引数がないパターン。戻り値のある関数を同様に記述して変数に代入しない場合どうなるかは未確認。
## Java
```java:Java
    int doAnything()
    {
       int i = 1;
       //todo1
       return i;
    }
    void doAnythingNoReturn()
    {
       //todo1
       return;
    }
    void doAny()
    {
       int i = 1;
       //todo1
       j = doAnything();
       doAnythingNoReturn();  //call文
       return;
    }
```
Javaのcall文は戻り値のない関数の関数名と実引数を記述します。戻り値のある関数を同様に記述して変数に代入しなくてもエラーになりません。
## C#
```cs:C#
    int doAnything()
    {
       int i = 1;
       //todo1
       return i;
    }
    void doAnythingNoReturn()
    {
       //todo1
       return;
    }
    void doAny()
    {
       int j;
       //todo1
       j = doAnything();
       doAnythingNoReturn();  //call文
       return;
    }
```
C#のcall文は戻り値のない関数の関数名と実引数を記述します。戻り値のある関数を同様に記述して変数に代入しなくてもエラーになりません。
## Re:Mind

```mind:Re:Mind
    ▽int doAnything()
        ・int i = 1
        //todo1
        □返す i
    △
    ▽void doAnythingNoReturn()
        //todo1
        □返す
    △
    ▽void doAny()
        ・int j
        //todo1
        □j = doAnything()
        □doAnythingNoReturn()  //call文
        □返す
    △
```
Re:Mindのcall文は戻り値のない関数の関数名と実引数を記述します。戻り値のある関数を同様に記述して変数に代入しなくてもエラーになりません。

# おわりに
VDM++はC/C++やJavaに近くプログラマに馴染みやすいとされていますが、いかがでしたでしょうか？
Re:MindはJavaやC#の経験者向けに設計された日本語プログラミング言語の一種で、ロジック仕様記述言語とトランスコンパイラ言語とで言語仕様を共有しています。ロジック仕様記述言語は疑似言語みたいなものですから、コンパイラを通さないことを前提にするならば未定義の関数を要件表記として使うこともできます。自然言語の日本語で記述するよりは厳密な構造的な記述が可能です。



[^1]: IBMのウィーン研究所で1960年代から70年代にかけて開発された形式手法。その実装には1996年にISO標準（ISO_IEC_13817-1）となったVDM-SLと、そのオブジェクト指向拡張のVDM++がある。
[^2]: Qiita [オープンな設計言語仕様 日本語ロジック仕様記述言語 Re:Mind（リマインド）](https://qiita.com/mylifewithviolin/items/52f806777e6ea031de94) 参照
[^3]: FMVDM　[VDMTools 付属文書](http://fmvdm.org/doc/index-ja.h
[^4]: FMVDM-> github [[PDF] VDMTools VDM 言語マニュアル VDM++](https://github.com/vdmtools/vdmtools/raw/stable/doc-jp/lang-man/langmanpp_a4J.pdf)
[^5]: Qiita [要求仕様 形式記述言語VDM++ との構文比較 ロジック仕様記述言語 Re:Mind（リマインド）](https://qiita.com/mylifewithviolin/items/2a8c6dc66cead04536a8)
【Lravel】リレーション先のリレーションのデータを紐付けて取得

## 結論
このように書く

```php:
    function get_posts(Request $request){
        //リレーション先のaccessesテーブルのtypeで公開ステータス(type=1)に絞り込み
        $posts = Post::>with(['user.profileImage'])->with(['cards'])->get();

        return response()->json( $posts);
    }

```
形式記述言語VDM++の制御構文（代入文）をJava C# Re:Mind(自作言語)と比較

# はじめに

要求仕様記述段階での使用が主に想定されている数学的形式手法VDM(Vienna Development Method)[^1]のオブジェクト指向拡張言語VDM++ですが、ロジックの実装も記述できる構文が用意されています。本記事ではVDM++の制御構文（代入文）をJavaとC#そして自作言語のRe:Mind[^2] と比較してみます。

## この記事内容の作業目的
VDM++の下記の制御構文[^3][^4]についていくつかの記事に分割して比較検討します。クラスの定義構文についてはこちらの記事[^5]を参照することができます。

・ブロック文
・コメント文
・**代入文**　←本記事対象
・条件文
・ループ文
・call文
・return文
・例外

VDM++はオブジェクト指向言語的な言語要素を持ち、数学的形式手法言語の中では一般のプログラミング言語のオブジェクト指向言語に近い面があります。また、VDMのような数学的形式手法を使わない場合は、一般的に自然言語の日本語で要求仕様が記述されているものと推察されます。本記事では数学的形式手法と自然言語の日本語記述とのはざまで、構造化された日本語が要求仕様記述に使えないか模索しています。


## この記事内容の保証
各言語で書かれた引用ソースの妥当性は保証されません。また、本記事に開示された自作言語の仕様は、開発中の仕様のため予告なく変更される場合があります。

# 構文比較
## VDM++
```vdm:VDM++
    doAnything: () ==> ()
    doAnything() ==
    (
       --todo1
       i:= i + 1;
       return i
    );
```
VDM++のブロック文は()で囲われた範囲となります。
## Java
```java:Java
    void doAnything()
    {
       //todo1
       i = i + 1;
       return i;
    }
```
Javaの代入文は左辺と右辺の間に=を記述します。
## C#
```cs:C#
    void doAnything()
    {
       //todo1
       i = i + 1;
       return i;
    }
```
C#の代入文は左辺と右辺の間に=を記述します。
## Re:Mind
```mind:Re:Mind
    ▽void doAnything()
        //todo1
       □i = i + 1
       □返す i
    △
```
Re:Mindの代入文は左辺と右辺の間に=を記述します。定義済の変数に代入する場合は先頭記号は他の実行文と同じ□となります。

# おわりに
いかがでしたでしょうか？Re:MindはJavaやC#の経験者向けに設計された日本語プログラミング言語の一種で、ロジック仕様記述言語とトランスコンパイラ言語とで言語仕様を共有しています。ロジック仕様記述言語は疑似言語みたいなものですから、コンパイラを通さないことを前提にするならば未定義の関数を要件表記として使うこともできます。
自然言語の日本語で記述するよりは厳密な構造的な記述が可能です。


[^1]: IBMのウィーン研究所で1960年代から70年代にかけて開発された形式手法。その実装には1996年にISO標準（ISO_IEC_13817-1）となったVDM-SLと、そのオブジェクト指向拡張のVDM++がある。
[^2]: Qiita [オープンな設計言語仕様 日本語ロジック仕様記述言語 Re:Mind（リマインド）](https://qiita.com/mylifewithviolin/items/52f806777e6ea031de94) 参照
[^3]: FMVDM　[VDMTools 付属文書](http://fmvdm.org/doc/index-ja.h
[^4]: FMVDM-> github [[PDF] VDMTools VDM 言語マニュアル VDM++](https://github.com/vdmtools/vdmtools/raw/stable/doc-jp/lang-man/langmanpp_a4J.pdf)
[^5]: Qiita [要求仕様 形式記述言語VDM++ との構文比較 ロジック仕様記述言語 Re:Mind（リマインド）](https://qiita.com/mylifewithviolin/items/2a8c6dc66cead04536a8)
AtCoder初心者振り返りメモ ABC329

# ABC329の回
https://atcoder.jp/contests/abc329

ABの2完。
振り返ると先にD問題にチャレンジすれば良かったのですが、C問題でTLEを取ることができず時間だけを失ってしまいました。

# A-問題
https://atcoder.jp/contests/abc328/tasks/abc329_a

**問題文**
英大文字からなる文字列 S が与えられます。S の各文字を空白で区切り、その順で 1 文字ずつ出力してください。

**ポイント**
Pythonには、空白区切りで出力する「*S」があるのですぐに回答できました。

````Python:A.py
# 入力 
S = str(input())

print(*S)
````

# B-問題
https://atcoder.jp/contests/abc328/tasks/abc329_B

**問題文**
N 個の整数 A1,A2,…,ANが与えられます。 このうち最大でない整数の中で最大である整数を求めてください。

**ポイント**
最初に最大値を求め、リストAからリストBへ最大値以外を転記してから最大値を求めました。

※コンテスト中は、set に格納してから　2番目の値を求めようとしたが、VScodeでサンプル問題の回答がすぐにできなかったので、上記方針に変更しました。

リスト → set → リスト　で解決できることがわかったので良かったです。

````Python:B.py
MA = max(A)
B = []
for i in range(N):
    if A[i] != MA:
        B.append(A[i])
    else:
        continue

MB = max(B)

print(MB)
````
````Python:B2.py
N = int(input())
A = list(map(int, input().split()))

# 一度 setに格納して重複を削除する
B = set(A)

# もう一度リストにしてソートする
C = list(B)
C.sort()

# 後ろから2番目の値が、最大値の次の値となる
print(C[-2])
````

# C-問題
https://atcoder.jp/contests/abc328/tasks/abc329_C

![329C.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547298/558534b8-7eb6-3162-6670-3a31316bf2e8.png)

**ポイント**
a,aa,aaa の順に検索する実装で正解に辿りつけず。
連続する文字の最大値を集計する方針を思いつきませんでした。

````Python:C.py
N = int(input())
S = input()

# A ～ Z のテーブルを用意する
X = [0] * 26

cnt = 0

# 英小文字の連続を見つけて連続数のMAXをテーブルに記録しておく
for i in range(N):
    if i >= 1 and S[i] == S[i-1]:
        cnt += 1
    else:
        cnt = 1

    # 英小文字を数値化
    t = ord(S[i])-97

    # A-Z テーブルの最大値を記録する
    X[t] = max(X[t], cnt)

# 最大値の集計が、答え　a,aa,aaa  とある場合は、個数 = 最大値
print(sum(X))
````
# D-問題
https://atcoder.jp/contests/abc328/tasks/abc329_D

![329D.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547298/caa185f4-2df3-72cc-a026-d658e2d07ca2.png)

**ポイント**
投票の都度max値を求める実装ではサンプルは正答でも、TLEを崩せませんでした。ここでタイムアップ。
前回最大値投票者と今回投票者の比較だけの判定で計算量が収まる問題でした。

````Python:D.py
N,M = map(int,input().split())
A = list(map(int, input().split()))

C = [0] + [0] * N

ans= 1 #前回の答えとして管理

for i in range(M):
    X = A[i]
    C[X] += 1

#  今回の投票で X 番目の人の数が前の投票の人より多い場合は更新する
    if C[X] > C[ans]:
        ans = X

#　同数の場合
    elif C[X] == C[ans]:
        # かつ番号が若い場合は更新する
        if X < ans:
            ans = X
    
    print(ans)
````
オンラインでプリントアウトするのがとてつもなくダルかった話

おはようございます。@Esperna です。
かなりどうでもいい話です。

# 印刷がとてつもなくダルい

諸事情により、PDFファイルを6ファイルほど印刷する必要がありました。
家にプリンタはないので、net printを使ってセブンイレブンで印刷したのですが、それがとてつもなくダルかったです。

* とあるサイトからPDFをダウンロードする作業が６回
* net printは一度に１ファイルしか印刷ジョブを登録できないので、前述のPDFファイルを間違えないようにしながら印刷ジョブを登録する作業が６回
* セブンイレブンで印刷ジョブ毎にパスワードをタッチパネルで入れて印刷するという作業が６回

# どうしたか

なので、複数のPDFを結合して1つのPDFファイルにしました（これで印刷も１回）。
pdfuniteというコマンドを使うのにpopplerというツールをインストールしました。

```
brew install poppler
pdfunite *.pdf united_onestop.pdf
```

後で気づきましたが[acrobat](https://www.adobe.com/jp/acrobat/online/merge-pdf.html)でもできるようです。

# 所感

印刷って滅多に使わないですよね
STM32G031でソフトウェアSPIの速度を測って遊ぶ1



やく27KBS

![o_cop797.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/d237dfc7-f602-c63b-741b-e14dd907d5d8.jpeg)




```c++



//Soft_SPI_test2_031

#define DW    digitalWrite

#define TFT_MOSI PA12
#define TFT_SCK  PA1
#define SPI_MOSI_HIGH() DW(PA12,HIGH)
#define SPI_MOSI_LOW()  DW(PA12,LOW)
#define SPI_SCK_HIGH()  DW(PA1,HIGH)
#define SPI_SCK_LOW()   DW(PA1,LOW)



void SPI_WRITE8(int s)
{
  for (int bit = 0, x = s; bit < 8; bit++) {
    if (x & 0x80) {
      SPI_MOSI_HIGH();
      SPI_SCK_HIGH();
      SPI_SCK_LOW();
    } else {
      SPI_MOSI_LOW();
      SPI_SCK_HIGH();
      SPI_SCK_LOW();
    }//end if
    x <<= 1;
  }//for
}//SPI_WRITE8


void setup() {

  pinMode(TFT_MOSI, OUTPUT);
  pinMode(TFT_SCK , OUTPUT);


loop1:

  SPI_WRITE8(0xF0);
  SPI_WRITE8(0xF0);
  SPI_WRITE8(0xF0);
  SPI_WRITE8(0xF0);
  SPI_WRITE8(0xF0);

  SPI_WRITE8(0xF0);
  SPI_WRITE8(0xF0);
  SPI_WRITE8(0xF0);
  SPI_WRITE8(0xF0);
  SPI_WRITE8(0xF0);

goto loop1;


}

void loop() {


  SPI_WRITE8(0x00);
  SPI_WRITE8(0x00);
  delay(2000);
  SPI_WRITE8(0x00);
  SPI_WRITE8(0x55);
  delay(2000);
  SPI_WRITE8(0x00);
  SPI_WRITE8(0xaa);
  delay(2000);
  SPI_WRITE8(0x00);
  SPI_WRITE8(0xff);
  delay(2000);

}


```
Pythonで「数値リテラルを記述する」の動作を確認してみた

# 概要

Pythonで「数値リテラルを記述する」の動作を確認してみました。以下のページを参考にしました。

https://www.javadrive.jp/python/num/index1.html

# 実装

以下のファイルを作成しました。
```sample.py
print(18.414)
print(256)
print(3.1e+3)
```

以下のコマンドを実行しました。
```
$ python3 sample.py 
18.414
256
3100.0
```

# まとめ

何かの役に立てばと。
リリース用pull requestの内容をラベルでグルーピングしてわかりやすくする

[git-pr-release](https://github.com/x-motemen/git-pr-release)というリリース用pull requestを作成してくれるGithubアクションがあるのですが、そこで使えるテンプレートに手を加えて気に入ったものが出来たので共有です。

気に入ったらぜひ使ってみてください。

テンプレートを適用して所定のブランチにマージすると、以下のようなpull requestが自動的に生成されます。

![名称未設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198321/03a8f9b0-851e-2757-06ec-8f3bf2ec0b0b.png)

git-pr-releaseはデフォルト状態でも、更新内容を一覧化してくれるのですが、更新内容をラベルによってグルーピングする事で何が更新されたのか、よりわかりやすくなったと思います。

更新内容を振り分ける方法は以下の通りです。

- 新機能: `enhancement` ラベルをPRに付与
- 改善: `improvement` ラベルをPRに付与
- バグ対応: `bug` ラベルをPRに付与

カスタマイズされたラベルを利用している場合は、任意のラベルに書き換えて使ってください。

「詳細を確認する」をクリックすると、マージコミットの詳細（PRの本文）が表示されます。

```ruby:git-pr-release.erb
## Productionリリース <%= Time.now.strftime('%Y-%m-%d %H:%M:%S') %>

## このリリースに含まれる更新内容

<% label_names = { 'enhancement' => '新機能(enhancement)', 'improvement' => '改善(improvement)', 'bug' => 'バグ改修(bug)', 'other' => 'その他' } -%>
<% label_names.each do |label_name, label_display_name| -%>
  <% filtered_prs = pull_requests.select do |pr|
    if label_name == 'other'
      pr.labels.none? { |label| ['improvement', 'bug', 'enhancement'].include?(label.name) }
    else
      pr.labels.any? { |label| label.name == label_name }
    end
  end -%>
  <% next if filtered_prs.empty? -%>

### <%= label_display_name %>
  <% filtered_prs.each do |pr| -%>
- #<%=  pr.number %> <%= pr.mention %>
  <% end -%>
<% end -%>

<details>
<summary>更新内容を振り分ける方法</summary>

更新内容を振り分ける方法は以下の通りです。

- 新機能: `enhancement` ラベルをPRに付与
  - 新たに追加された機能がある場合に付与します
- 改善: `improvement` ラベルをPRに付与
  - 既存の機能の改善がある場合に付与します
  - 既存機能の性能向上、最適化など
- バグ改修: `bug` ラベルをPRに付与
  - 既存機能に関するバグの修正がある場合に付与します

後からラベルを付与することも可能ですが、更新はそのままでは実行されません。
その場合は`git-pr-release` ジョブを手動再実行してください。

</details>

## このリリースに含まれるマージコミットの詳細

<details>
<summary>詳細を確認する</summary>

<% pull_requests.each do |pr| -%>
### <%= pr.merge_commit_sha %> <%= pr.title %> <%= pr.mention %>
<%= pr.body %>
<% end -%>
</details>
```

`git-pr-release.yml`の内容も貼り付けておきます。`--overwrite-description`を使って更新の都度上書きしないと意図した挙動になりません。上書きするので手動で書き換えても次の更新で消えてしまうので注意です。

workflow_dispatchを追加することで、任意のタイミングで手動実行することも出来るので、マージ後にラベルを付与し忘れたことを思い出しても対応する事ができます。

envの値などの詳細はここでは触れていないので、[git-pr-release](https://github.com/x-motemen/git-pr-release)を参照してください。

```yml:git-pr-release.yml
# .github/workflows/git-pr-release.yaml
name: git-pr-release
on:
  push:
    branches:
    - develop
  workflow_dispatch:
jobs:
  git-pr-release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0 # git-pr-release needs the git histories
      - uses: actions/setup-ruby@v1
        with:
          ruby-version: 3.1
      - run: gem install --no-document git-pr-release
      - run: git-pr-release --squashed --overwrite-description
        env:
          GIT_PR_RELEASE_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GIT_PR_RELEASE_BRANCH_PRODUCTION: master
          GIT_PR_RELEASE_BRANCH_STAGING: develop
          GIT_PR_RELEASE_LABELS: pr-release
          GIT_PR_RELEASE_TEMPLATE: .github/git-pr-release.erb
          TZ: Asia/Tokyo
```
make を見直してみよう

この記事は [一分で読める小ネタのカレンダー | Advent Calendar 2023 - Qiita](https://qiita.com/advent-calendar/2023/raki) の１３日目

# make

- [make (UNIX) - Wikipedia](https://ja.wikipedia.org/wiki/Make_(UNIX))
- プログラムのビルドツールと説明されているが、要は指定したラベルに含まれるコマンドを実行してくれるだけ
- Wikipedia にも記載があるとおり、複数の実装と、複数の関連（代替）ツールがある
- 好きなビルドツールを使えばいいと思うけど、一番手軽で情報も多い make は比較的簡単
- リポジトリトップでだけ使えるエイリアスとして使い始めると勉強にもってこい

本文はここまで。以下は時間外ｗ

# Makefile

- 昨日の記事に書いた fortune で遊んでいたら、`f 100% jp_xxx` みたいなことができているはず
- これを shell alias ではなく make ターゲットにする（`make jp_xxx`のようにする。make を m で alias していれば `m jp_xxx` にできる）

```makefile:Makefile
# 最初のタスクが make のデフォルトになる
# default ターゲットをダミーで書いて、後ろに実行するターゲットを書けば並びを気にしなくていい
default: list

# list all targets
# see https://stackoverflow.com/questions/4219255/how-do-you-get-the-list-of-targets-in-a-makefile/26339924#26339924
list:
	@LC_ALL=C $(MAKE) -pRrq -f $(firstword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/(^|\n)# Files(\n|$$)/,/(^|\n)# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | grep -E -v -e '^[^[:alnum:]]' -e '^$@$$'

# ターゲットと同名のファイルやディレクトリがあると make できないのでダミーターゲットとしておくことで回避できる
.PHONY: default list jp_xxx

# 本題の fortune を実行するターゲット
jp_xxx:
	@fortune 100% jp_xxx

```

- このまま使えるので `jp_xxx` だけ合わせればOK
- `alias m="make"` しておくと楽。これは dotfiles で管理して常に使えるようにしておくといい
- `m` デフォルトでは Makefile の先頭のターゲット `default` を呼び出すので `list` ターゲットが実行される
- `m` と `m default` と `m list` の結果は同じになるということ
- `m jp_xxx` で `fortune 100% jp_xxx` が実行される（先頭の `@` は実行コマンドを出力しない）

# やってみよう

- ターゲットを増やしてできることを増やす
- 他の fortune のファイルを呼べるようにする。用途に応じて `m launch` や `m books` のようにする
- `fortune/build.sh` を実行できるようにして、たくさん作ってもすぐ試せるようにする（実務での使い方はまさにこれ）
- my-til に日記を書いていたら、make を使ってテンプレートを出力させてみよう
- 同じく日記を書いていたら、日記を add,commit,push する（その間に pre-commit が入る）ようにしてみよう
- よく使うものを default ターゲットで指定して、`m` で実行できるようにする

# その後

- 先にも書いたとおり、make は代替ツールがたくさんある
- 方言（別実装）はともかく、代替ツールを知るのは大事
- 言語毎にそれなりにビルドツールが存在していて、make ではなくそちらを使うのが業務では一般的
- 特にフロントエンド界隈では毎年のように流行りが変わるので、リポジトリによってツールが変わるのもザラ
- そこで make からそれらツールを呼べるようにすれば、どのリポジトリでも自分の中では `m` で済む
- もっとも、フロント界隈だと IDE から実行させてコマンドを直接叩くことも少ないけど
- チームやリポジトリによって違うコマンドを使うけど、make によって自分の中では同じコマンドで処理できる、ようにできると副業とか多数の業務を抱える時とかも楽できる（かもね）

# 改善依頼

- フロント界隈でフレームワークビルドをラップして make を使っている例（リポジトリ）
- make でこんな処理しているぜ、とか
Twitch APIでクエリの際に使用するIDがややこしかった

## はじめに
どうも、最近Twitch APIを使って色々遊んでいます。

そんな中でAPIのクエリに指定するIDについて少々詰まりました。

この"ID"とは、例えばあるストリーマーの配信情報を得たいときに
"https://api.twitch.tv/helix/streams?user_id="
このURLに続けて入力するストリーマーのIDです。<br><br>

無事、解決したのでメモ書き程度に書いていきます。<br>


## 発生した問題


"https://api.twitch.tv/helix/streams?user_id="
このURLのクエリに続くIDとして

"https://www.twitch.tv/○○" (配信のURL)
この○○を入力してAPIを叩いてみました。

結果、リスポンスは<br>

```
{"data":[],"pagination":{}}
```
<Br>

情報量ほぼ0のJSONが返ってきました。

トークンが無効なのであれば、ステータスコード401が返るのでアクセス権の問題ではなさそう。

そして調べた結果、
クエリに適合した配信が存在しない時にこのような空のJSONが返されるみたいです。配信は確かに存在していたので"user_id"に指定したIDが問題っぽい。

そしてさらに調べた結果、
自分が今までIDだと思っていたものがどうやら"ユーザ名"という扱いで、idは別に存在することがわかりました。

## 正しい"ID"の取得方法

IDを取得するには、以下のAPIを叩く必要があるらしいです。

API : "https://api.twitch.tv/helix/users"
(参照)https://dev.twitch.tv/docs/api/reference/#get-users

これにクエリを加えて
"https://api.twitch.tv/helix/users?login=△△"

この△△にユーザ名を入れてアクセスすることで、以下のようなJSONが返されます。

```
{
  "data": [
    {
      "id": "141981764",
      "login": "twitchdev",
      "display_name": "TwitchDev",
      "type": "",
      "broadcaster_type": "partner",
      "description": "Supporting third-party developers building Twitch integrations from chatbots to game integrations.",
      "profile_image_url": "https://static-cdn.jtvnw.net/jtv_user_pictures/8a6381c7-d0c0-4576-b179-38bd5ce1d6af-profile_image-300x300.png",
      "offline_image_url": "https://static-cdn.jtvnw.net/jtv_user_pictures/3f13ab61-ec78-4fe6-8481-8682cb3b0ac2-channel_offline_image-1920x1080.png",
      "view_count": 5980557,
      "email": "not-real@email.com",
      "created_at": "2016-12-14T20:32:28Z"
    }
  ]
}
```

この"id"に対応する"141981764"がクエリに使うことのできるIDです。

## 勘違いの理由
Twitchにはユーザ名と別に表示名というのがあって、表示名を設定するとユーザの名前は

**表記名(ユーザ名)**

という表記になる。
また、ユーザ名はアルファベットで設定される。

**加藤純一です(kato_junichi0817)**　<font color="#CCCCCC">**名前出してすいません**</font>


実際にこんな感じで表示されるので、感覚的にはこの右側のやつがIDかな～と思ってしまうのは自分だけではないはず。多分。

まさかIDが隠しパラメータ的なやつだとは思いませんでした。。。<br><br>

この記事が自分と同じような勘違いをしている人の参考になれば嬉しいです。それでは。
Pythonのバージョン 

# バージョンの確認
```bash
python -V
Python 3.10.5
```

```bash
py --list
Installed Pythons found by py Launcher for Windows
 -3.10-64 *
 -3.9-64
 -3.8-32
 -3.7-64
 -2.7-64
 ```

`%LOCALAPPDATA%\py.ini`
```text
[defaults]
python=3.8
```

```bash
py --list
Installed Pythons found by py Launcher for Windows
 -3.10-64
 -3.9-64
 -3.8-32 *
 -3.7-64
 -2.7-64

python -V
Python 3.10.5
```
Pythonで標準入力を受け取る方法

## はじめに
本記事は、競プロやpaizaスキルチェックなどで必要な標準入力からのデータ取得について、Pythonでデータを受け取るための備忘録です。

## 標準入力について
標準入力(Standard Input)はユーザからデータを受け取るための方法。
プログラムへのキーボードなどからの入力を受けつける。
input()関数は、ユーザーから文字入力を受け取り、文字列として返す。

## 基本的なデータ取得
```Python
str = input()

# 前後の空白を取り除く
str = input().strip()

# 数としてintやfloatで取得する
int = int(input())
```

## 1行に複数個の入力データの場合
#### 文字列データ
```:Input
りんご みかん ぶどう
```
```Python
strs = input().split()
print(strs)
#Output: ['りんご', 'みかん', 'ぶどう']
```
#### 数値データ
```:Input
2 1 -5
```
```Python
nums = list(map(int, input().split()))
print(nums)
#Output: [2, 1, -5]
```

## 複数行に1つずつの入力データの場合
1行目で入力行数を受け取る。
#### 文字列データ
```:Input
3
りんご
みかん
ぶどう
```
```Python
n = int(input())
strs = [(input()) for _ in range(n)]
print(strs)
#Output: ['りんご', 'みかん', 'ぶどう']
```
#### 数値データ
```:Input
3
2
1
-5
```
```Python
n = int(input())
nums = [int(input()) for _ in range(n)]
print(nums)
#Output: [2, 1, -5]
```

## 複数行に複数個の入力データの場合
```:Input
3
1 4
2 -1 4
-5 3
```
```Python
n = int(input())
lists = [list(map(int, input().split())) for _ in range(n)]
print(lists)
#Output: [[1, 4], [2, -1, 4], [-5, 3]]
```
ICML2023の枝刈り論文

# 概要

この記事では、[ICML2023](https://icml.cc/Conferences/2023)の枝刈り論文を紹介します。[^1]

[^1]: 画像や数式は論文から引用しています。


# Gradient-Free Structured Pruning with Unlabeled Data

https://proceedings.mlr.press/v202/nova23a.html

- 概要：LLMのラベル無しデータによる枝刈り
- 研究機関：Google
- 新規性：ラベル無しデータで枝刈りした
- キモ：R2は、代表点となりうるConvex hullから最適点を探索する。
  D2は、データドリブンの統計量でランキングする。
  KCMは、R2とD2のどちらでフィルタするか決める。
- 評価：BERT

# Fast as CHITA: Neural Network Pruning with Combinatorial Optimization

https://proceedings.mlr.press/v202/benbaki23a.html

- 概要：学習による枝刈り
- 研究機関：Google
- 新規性：OBS(ロスのTaylor展開の2次を刈る)を拡張し、メモリに適した表現へ最適化する。
- キモ：組合せ最適化する。


# A Three-regime Model of Network Pruning

https://proceedings.mlr.press/v202/zhou23p.html

- 概要：枝刈り後の精度をモデル化する
- 新規性：Load-likeとtemperature-likeのパラメータを使い、最初に刈りやすいモデルを学習する方法を実験から3相モデルで整理した。(Figure 1b)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2448931/3d21157a-dafa-369e-276f-83a9433db5dd.png)


# UPSCALE: Unconstrained Channel Pruning

https://proceedings.mlr.press/v202/wan23a.html

- 概要：Channel pruning
- 研究機関：Apple
- 新規性：隣り合うレイヤのchannel数を合わせなくても良いようにした。
- キモ：Figure 2, 4
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2448931/3f832e57-df49-8d74-ef00-333cd548f8ab.png)


# Why Random Pruning Is All We Need to Start Sparse

https://proceedings.mlr.press/v202/gadhikar23a.html

- 概要：強い宝くじ仮説の証明
- 新規性：Denseなネットワークは1/log(1/sparsity)の幅からのランダム枝刈りで近似できることを示した。


# Instant Soup: Cheap Pruning Ensembles in A Single Pass Can Draw Lottery Tickets from Large Models

https://proceedings.mlr.press/v202/jaiswal23b.html

- 概要：宝くじ仮説で当たりくじを見つける方法
- 新規性：ISP. EPOCH数の少ない弱い識別器をアンサンブルすることで、IMPより性能が良くなる


# Pruning via Sparsity-indexed ODE: a Continuous Sparsity Viewpoint

https://proceedings.mlr.press/v202/jaiswal23b.html

- 概要：PSO. 枝刈りフレームワークの提案。
- 新規性：Sperse trainingの枝刈り方法で、従来のIMPやLTHより高速な方法を提案する。
- キモ：微分方程式(9)の解の経路を辿ると最適なマスクが得られる。
日時の値について。どの型を使用するか? ISO 8601? Timestamp?

# 目的

- 日時の値について、以下どちら (または他) を使うかを決めたい。
  - `ISO 8601 Date and time string` (e.g. `2023-11-18T08:43:53+09:00`)
  - `UNIX Timestamp` (e.g. `1700264648`)

## 結論

Timestampを使用する。
理由は、パフォーマンスが ISO 8601 に比べて3倍ほど早いので

## ISO 8601

### Pros

- ログやデバッガで見る時、人間が読みやすい

### Cons

- Timestamp より遅い
- タイムゾーン情報を扱わねばならず、欠落せぬよう実装に注意が必要

## Timestamp

### Pros

- ISO 8601 より速い
- タイムゾーン情報を扱う必要がなく、バグが起きにくい
- Slack や Chatwork の API は Timestamp を採用している
    - ref.
        - https://api.slack.com/methods/conversations.info
        - https://developer.chatwork.com/reference/get-rooms-room_id-messages

### Cons

- ログやデバッガで見る時、人間が読みにくい
- DB が mysql の場合、TIMESTAMP型を使わないよう設計する必要がある
    - ref. [mysqlにおけるtimestamp型2038年問題](https://zenn.dev/kenghaya/articles/89bc9a6e9e8381)

## パフォーマンス測定

以下の処理を ChatGPT 先生に相談してみた

- 日時同士の比較
- Date instance への変換

### code

```js
const measurePerformance = (operation, iterations = 10_000_000) => {
  const start = performance.now()
  for (let i = 0; i < iterations; i++) {
    operation()
  }
  const end = performance.now()
  return end - start
}

// warm up
measurePerformance(() => new Date(), 1_000_000)

// test: comparison
{
  const performanceA = measurePerformance(() => "2023-11-18T08:43:53+09:00" < "2023-11-18T08:43:54+09:00")
  const performanceB = measurePerformance(() => 1700264648 < 1700264649)

  // result
  console.log("# Performance of ISO8601 comparison:")
  console.log(performanceA + " ms")

  console.log("# Performance of Timestamp comparison:")
  console.log(performanceB + " ms")
}

// test: convert to Date instance
{
  const performanceA = measurePerformance(() => new Date("2023-11-18T08:43:53+09:00"))
  const performanceB = measurePerformance(() => new Date(1700264648))

  // result
  console.log("# Performance of ISO8601 convert:")
  console.log(performanceA + " ms")

  console.log("# Performance of Timestamp convert:")
  console.log(performanceB + " ms")
}
```

### 結果例

- MacBook pro M1 で実行した結果、Timestamp の方が 3 倍近く速い

```md
# Performance of ISO8601 comparison:
102.27612501382828 ms
# Performance of Timestamp comparison:
37.01020801067352 ms

# Performance of ISO8601 convert:
1311.1170000135899 ms
# Performance of Timestamp convert:
357.1043750047684 ms
```
（練習）アウトプット手順


# 前置き
自分用にQiita CLIのテスト運用として自分のアウトプット方法をメモしておきます。
本当にメモ用なのでQiita CLIの使い方やGitHub上での管理方法は公式の[リポジトリ](https://github.com/increments/qiita-cli)や[記事](https://qiita.com/Qiita/items/32c79014509987541130)を参考にしてください。


# アウトプット
<dl>
  <dt>1 学びたいもののサイトや本を軽く一回読む</dt>
  <dd>
  深くは読まなくて良いが、一度軽く読んでおくことでこんなことがあったなー程度に思い出す材料になるので後の作業が楽になる。
  </dd>
  <dt>2 実際に挑戦する</dt>
  <dd>
  何事も実際に触ることが大切、読んで触ることでより理解が深まる
  </dd>
  <dt>3 挑戦して得た知見をまとめる</dt>
  <dd>
  今回のようにQiitaを使ってやったことを思い出しながら記事にすることで記憶を記録として変換して自分が忘れてしまったときの参考になるようにする。<br>
  他の人が見ても理解できるように書くことでより理解が深まるようにもなるためちゃんとアウトプットすることが大切。
  </dd>
</dl>

## 後日談?
`.md` ファイルを`npx qiita publish ファイル名` で投稿しようとしたときに、API_TOKENを設定してGithub上でも問題なく設定できていたのに、APIリクエストエラーでpushできませんでした。
原因としては
```
title: 
tags:
  - QiitaCLI
private: false
updated_at: '2023-11-19T02:29:18+09:00'
id: ID
organization_url_name: null
slide: false
ignorePublish: false
---
```
の部分で設定が間違っているためでした。
エラーではAPIトークンがないと言われましたが、実際は違う部分のエラーでした。。

## コマンドやツール
vscodeで`.md`を編集していたのですが、previewを見たいときに`npx qiita preview`より拡張の`Qiita Markdown Preview`を入れて確認しながら作業するほうが楽に確認できるのでおすすめです。
拡張機能を入れたらsettingsの`Qiita-markdown-preview:status`を`enable`にしてください。

:::note info
Qiitaの記事を書く以外は設定を無効にしたほうが良いです。そうしないとQiita独自の設定画反映されてしまうので..
:::

また、自分がCLIをあまり触っていないので基本的な終了コマンドかわからないのですが、`npx qiita preview`の終了するときは`ctr+c`でpreviewを終了できます。
HTML上に画像があるか探す

開いたページから画像が存在するか探す方法のメモ。
HTML上に画像があるか探します。
下のコードは多少変えていますが、[MDN](https://developer.mozilla.org/ja/docs/Web/API/Document/images)のコードです。

```js
const imageList = document.images;

for (let i = 0; i < imageList.length; i++) {
  if (imageList[i].src == "apple.jpg") {
    // 画像が存在した時の処理
  }
}
```

上記コードで処理できるそうですが、私の環境ですと、うまく画像を探すことができませんでした。
いろいろ試したところ、絶対パスを指定することで動作でしました。

```js
if (imageList[i].src == '指定のURL/img/apple.jpg')
```

ただ上記の書き方ですと、使用できる場面が限定的になるので、実装上では`indexOf`を使用するのがベターかなと思いました。

```js
if (imageList[i].src.indexOf('apple.jpg') != -1)
```


#### 参考
https://developer.mozilla.org/ja/docs/Web/API/Document/images

https://shanabrian.com/web/javascript/document-images.php
【最新】TOEFL 英単語参考書完全比較！最適な勉強書はこれだ 📚

第4位: TOEFL TEST究極単語(きわめたん)5000 🏅

👍 例文がある
👎 字が小さい

![Screenshot 2023-11-19 at 1.18.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1900756/e0f7c794-a7c1-9b72-3652-1dbd0a340274.png)

https://amzn.to/47vldYS

第3位: 完全攻略! TOEFL(R)テスト英単語4000 🥉

👍 例文あり
👎 例文が右側にあって見にくい

![Screenshot 2023-11-19 at 1.19.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1900756/020e7a59-5f5f-1ed9-6eef-a6ba4d7c8131.png)

https://amzn.to/49Ldzvw

第2位: TOEFL TEST 必須英単語5600 🥈

👍 長文で覚えることができる

![Screenshot 2023-11-19 at 1.20.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1900756/d0845a3e-0c40-e00a-21a3-bc57d9ac4941.png)


https://amzn.to/3unxfoU

第1位: TOEFLテスト英単語3800 🥇

👍 見やすい例文付きで効果的な覚え方
👍 一文で三つ一気に覚えられる

![Screenshot 2023-11-19 at 1.15.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1900756/55189695-84fc-6ca4-d4af-101acb532c6a.png)

https://amzn.to/3MRglFo
【Flutter】環境構築時の "Android license status unknown" で困った話

Android アプリ開発のために Flutter を導入したのですが，環境構築時につまるところがあったのでメモ．

# 環境
- MacBook Air (m2) - macOS Sonoma 14.0
- Flutter SDK - 3.16.0

# 発生した問題
[公式サイトの Flutter インストール方法](https://docs.flutter.dev/get-started/install) に沿って各種ツールのインストール&パスを通した後に `flutter doctor` コマンドを実行したところ，下記のエラーが出てしまいました．

```
$ flutter doctor

[!] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    ✗ Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/macos#android-setup for
      more details.
```

`Android license status unknown` というところが気になります．
`flutter doctor --android-licenses` というコマンドを打ってみろ，とのことなので試してみます．

```
$ flutter doctor --android-licences

Error: LinkageError occurred while loading main class com.android.sdklib.tool.sdkmanager.SdkManagerCli
    java.lang.UnsupportedClassVersionError: com/android/sdklib/tool/sdkmanager/SdkManagerCli has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 55.0
Android sdkmanager tool was found, but failed to run
(Users/hogehoge/Library/Android/sdk/cmdline-tools/latest/bin/sdkmanager): "exited
code 1".
Try re-installing or updating your Android SDK,
visit https://flutter.dev/docs/get-started/install/macos#android-setup for
detailed instructions.
```

Android SDK を再インストールする必要があるとのことだったので，[こちらの記事](https://qiita.com/pe-ta/items/e5a1813b21de8d446407) の内容を試してみましたが解決せず...

# 解決方法
**`Android SDK Command-line Tools` のバージョンを最新版ではなく `8.0` をインストール** すると解決するとのこと（[Stack Overflow に同様の問題が挙がっていました](https://stackoverflow.com/questions/76882205/error-linkageerror-occurred-while-loading-main-class-com-android-sdklib-tool-sd) ）．

Android Studio の SDK Manager で `Android SDK Command-line Tools` のうち，バージョン `8.0` のみをインストールします．他にインストールしているバージョンがある場合はチェックを外してアンインストールします．
（画像内の赤枠で示した `Show Package Details` にチェックを入れると過去のバージョンを選択できるようになります）

![Screenshot 2023-11-18 at 18.58.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1483756/d8d0116c-52d9-9c1a-ed67-4b691e6431e5.png)

その後，`flutter doctor` コマンドを実行すると `No issues found!` と表示され，問題がないことが確認できました！

なお，[Stack Overflow の記事](https://stackoverflow.com/questions/76882205/error-linkageerror-occurred-while-loading-main-class-com-android-sdklib-tool-sd) では `Android SDK Command-line Tools` のインストールディレクトリの名前を `8.0` から `latest` に変更することが勧められていますが，変更せずに `flutter doctor` を実行しても問題ありませんでした．

# 余談
試しに `Android SDK Command-line Tools` の他のバージョンをインストールしてみましたが，`8.0` から `10.0` までは問題なかったものの，最新版の `11.0` だけは冒頭のエラーが発生しました．

なぜこの事象が発生するのか，その原因については見つけられませんでした．より良い解決方法は他にあるのかもしれません...

# 参考
https://qiita.com/pe-ta/items/e5a1813b21de8d446407

https://stackoverflow.com/questions/76882205/error-linkageerror-occurred-while-loading-main-class-com-android-sdklib-tool-sd
MVCモデルとAPIの融合：初心者向けに解説するウェブ開発のマジック

MVCモデル（Model-View-Controller）とAPI（Application Programming Interface）の組み合わせは、ウェブアプリケーションやソフトウェア開発において一般的です。ここでは、MVCモデルの概念とAPIの使用方法を初心者にもわかりやすく説明します。

### MVCモデルとは？

MVCモデルは、アプリケーションの構造を三つの主要な部分に分ける設計パターンです：

1. **Model（モデル）**:
   - データとビジネスロジックを管理します。
   - データベースとのやり取り、データの検証、データの保存や読み出しなどを行います。

2. **View（ビュー）**:
   - ユーザーインターフェイスを担当します。
   - ユーザーが見る画面を生成し、ユーザーの入力を受け取ります。

3. **Controller（コントローラー）**:
   - モデルとビューの橋渡しをします。
   - ユーザーの入力に基づいてモデルを操作し、その結果をビューに反映させます。

### APIとは？

APIは、異なるソフトウェア間で情報を交換するための手段です。外部のサービスやリソースと通信する際に用います。

### MVCモデルにおけるAPIの流れ：

1. **ユーザーからのリクエスト**:
   - ユーザーがアプリケーションのウェブページにアクセスしたり、フォームを送信したりすると、このリクエストはコントローラーに送られます。

2. **コントローラーの処理**:
   - コントローラーはリクエストを解析し、必要に応じてモデルにデータ操作を依頼します。
   - 例えば、外部APIからデータを取得する必要がある場合、コントローラーはAPIリクエストを行い、応答を受け取ります。

3. **モデルとのやり取り**:
   - モデルはAPIからのデータを処理し（例えばデータベースに保存）、結果をコントローラーに返します。

4. **ビューの更新**:
   - コントローラーはモデルからの結果をビューに渡し、ビューはそれを表示形式に変換してユーザーに提示します。

5. **ユーザーへのレスポンス**:
   - 最終的に、ユーザーは更新されたデータをビューを通じて見ることができます。

このプロセスにより、MVCモデルはコードの整理と保守の容易さを提供し、APIを介した外部リソースとの効率的な通信を可能にします。開発者は、この構造を利用して機能豊富で効率的なアプリケーションを構築できます。