OmniAICreator/Qiita-1.07M huggingface dataset:
いろいろやっていると色々知識がついた

体調の悪い日が続くため、ポエムばかりですが何とか続けたいと思います。
個人的「質はともかく継続する」１8日目です

# いろいろやっていると色々知識がついた

主業務のかたわら、新しいシステムを試してみたり、業務改善に出来ることはないかと試行錯誤する事が多いのですが、そのおかげで色々な知識をつける事が出来ました。
主業務だとどうしても決まったフレームワークを使って決まった手順で作ることが多く、技術の幅が広がりにくいです。
それを補うのが目的では無いのですが、色々と試していると主業務で思わぬ形で役に立つ事がありました。

## DockerからGitHubActions
私のやっている仕事の主業務では使わない技術ですが、試しにGitLabを立てたりfessという全文検索エンジンを立てたりしていました。
そのうち、主業務でGitHub Actionsを使う事になったのですが、この時にDockerの知識が役に立ちました。
GitHubActionsの書き方はyaml形式で、Dockerの書き方ともよく似ています。
そのためスムーズに学ぶことが出来ました。

## GitLabとOpenID
ナレッジを貯めるために、outline wikiというナレッジアプリをdockerで建てて試していました。このoutline wikiは、ログイン認証システムを独自では持っておらず、外部連携する仕組みになっていました。そこでGitLabのOpenID connectを使うことにしました。ここで学んだ認証の技術が、主業務で認証方式を変更する時に役立ちました。

## outline wikiとAmazonS3
outline wikiでは、通常の保存先であるAmazonS3ではなく互換性のあるminioを建てて使っていました。そのおかげでAmazonS3を使う事になった時、それがどういうものなのかすぐに理解する事ができました。

## まとめ
主業務が一番大切ではありますが、何か色々と試してみると、意外な形で主業務に役立ったりする事があります。
手が空いた時に、楽しみながら試してみると良い発見があるかもしれません。
もちろん、セキュリティには要注意ですが。
Claude3.5 Sonnetをスマホで検証 -その1 - チーム管理ツールの開発

# はじめに
Claude3.5 SonnetのArtifacts機能がやばい、と盛り上がってるので、遅ればせながら検証しました。

せっかくなので、スマホで検証します。
これができると、通勤等の移動時間である程度作れてしまいます。

# 要件
まずはシンプルな要件。
- プロジェクトの登録、編集、削除ができる
- メンバーを登録できる
- プロジェクト間でメンバーを移動できる

# 作ってみる

### 最初のプロンプト
:::note info
プロジェクトが頻繁に変わるので、ドラッグ＆ドロップでチーム移動できるようにしたい。
:::

30秒くらいで上がってきた。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/908ed925-3fd7-b4ff-07c9-047c00e91c38.png" width="400px" />

BobをProject A から B へドラッグ＆ドロップで移動。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/879f7c07-7be4-9a16-0161-1b5fb51efa77.png" width="400px" />

ちゃんと動いてますね、、

### 少し改良を依頼してみる
:::note info
ありがとう。プロジェクトとメンバーの作成と削除もできるようにしたい。
:::

30秒後、プロジェクトの作成とメンバー登録できるものが。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/b9b4d959-0e40-17e2-1bd3-2fee42cd2c52.png" width="400px" />

### さらに改良してみる
:::note info
ありがとう。プロジェクト内で、メンバーの順番入れ替えを可能にして。
:::

上がってきたのがこちら。
順番の入れ替え機能が備わってます。。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/cf9d67d9-59ce-5ba0-cf85-ea5da89bd870.png" width="400px" />

ここまで、開始から5分です。

# 結論

Claude3.5 Sonnet、噂以上にやばいです、、、
通勤電車で、動くものが作れます。

満員電車でも作れます🚃

# まとめ

今回は、Claude3.5 Sonnetをスマホで検証しました。
凄い世の中になりましたね！

これからもっと進化すると思うと、ますます楽しくなります。
AIを活用することで、アイデアを形にすることができます。

みなさんも一緒に頑張りましょう🙂
Vue i18nとは

## 🏆 ゴール 🏆

Vue i18nについて簡単な理解と使用方法について知る

:::note info
使用方法については動くかの検証をしていないので本ドキュメントでは実装における大体知識が理解できれば良い
:::

## i18nってなに？

https://zenn.dev/fujishiro/scraps/9ed19d1472363f の記事に書いてる通り

> i18nとは、”Internationalization" （= 国際化対応）　を意味する略語。
> 先頭の「i」と末尾の「n」、これらの間に入る18文字から取られた名称とのこと。

i18n = 国際化対応 ということらしい
なので**vueで国際化対応ができるパッケージ**と理解

## 前提

**Vue 3.0以上**が必要

## できること

言語ごとにJSONデータを用意して**多言語対応**が可能

## 使い方（一例）

### 1.言語切り替え設定をJSONファイルに集約

必要な言語ごとにJSONファイルを作成

```json:~/src/i18n/ja.json
{
  "message": {
    "hello": "こんにちは"
  }
}
```

```json:~/src/i18n/en.json
{
  "message": {
    "hello": "Hello"
  }
}
```

### 2.main.js に組み込み

言語ファイルを読み込み、グローバルに使用可能とする

```js:~/src/main.js
import { createApp } from "vue";
import App from "./App.vue";
import { createI18n } from "vue-i18n";
import ja from "./i18n/ja";
import en from "./i18n/en";

const i18n = createI18n({
  legacy: false,
  locale: "ja",
  messages: { ja, en },
});

createApp(App).use(i18n).mount("#app");
```

### 3.コンポーネントでの対応

```$t(キー)```で先程定義したキーに対応する文字列が得られます

```vue:~src/components/test.vue
<template>
  <div>
    {{ $t("message.hello") }}
  </div>
</template>
```

この場合には
- 日本語の場合 "こんにちは"
- 英語の場合 "Hello"

が表示される

## 言語切り替えについて

i18nのlocaleを変更することで可能

```
$i18n.locale = 'ja'; // 'ja'や'en'など
```

## 注意点

- i18nの設定や使用には、[Vueの公式ドキュメント]("https://vue-i18n.intlify.dev/")などを参照することが推奨されます
- 実際のアプリケーションで使用する際は、動作確認やテストを行うことが重要です
いまさらdockerを始めてみる⑦

# 前回まで
4日前なので内容を忘れてしまった。まあここから取り戻そう。

# Persisting container data
コンテナで作られたファイルはコンテナが消えると同時に消える。
でもデータベースなどはコンテナが消えても保持していたいよね？→persistさせる。volumeに

**volumeの作り方とマウント(=アタッチ)のさせ方**
```docker volume create log-data```
```docker container run -d -p 80:80 -v log-data:/logs docker/welcome-to-docker```
こうすることでdocker上の/logsファイルに書き込まれたデータはvolume: log-dataに保存される

**try it out**
Docker Desktopを起動。問題発生
https://qiita.com/matarillo/items/98d7452967987fe5d633
を参考に仮想マシンの機能を有効化→再起動→無事起動

volumeをマウントしたdockerの作成→削除
同一volumeをマウントした新たなdockerの作成→中身確認
コンテナ作成
```
docker run --name=db -e POSTGRES_PASSWORD=secret 
-d -v postgres_data:/var/lib/postgresql/data postgres
```
psotgresコマンドの実行
```
docker exec -ti db psql -U postgres
```
```
CREATE TABLE tasks (
    id SERIAL PRIMARY KEY,
    description VARCHAR(100)
);
INSERT INTO tasks (description) VALUES ('Finish work'), ('Have fun');
```
```
SELECT * FROM tasks;
```
```
\q
```
コンテナの停止および削除
```
docker stop db
docker rm db
```
新たなコンテナの作成及びvolumeの中身確認
```
docker run --name=new-db -d -v postgres_data:/var/lib/postgresql/data postgres 
```
```
docker exec -ti new-db psql -U postgres -c "SELECT * FROM tasks"
```
新しいコンテナでも前のコンテナで書き込んだのと同じテーブル内容が確認できた

後処理
```
docker rm -f new-db
```
```
docker volume rm postgres_data
```
volumeをマウントしているコンテナがあると削除できないため、最初に削除する

# Sharing local files with containers
-vと-mのどっちでもlocalfileをcontainerとshareすることはできる。
オプションの後がパスかvolumeかで判別するのかな。
-vのほうはマウント先がなかったら勝手に作ってくれるなど新設設計だけど、-mのほうが詳細な設定ができるから
なるだけ-m使えとのこと。
また、read-onlyやread-writeなどfile permissionsも設定できる

**try it out**
コンテナを作る
```
docker run -d -p 8080:80 --name my_site httpd:2.4
```
動作確認
```
curl localhost:8080
```
中を確認したらコンテナを消す

次に適当にディレクトリを作って、中にindex.htmlを入れる
作ったディレクトリにcdして以下のコマンドを実行
```
docker run -d --name my_site -p 8080:80 -v .:/usr/local/apache2/htdocs/ httpd:2.4
```
localhost:8080にアクセスすると、自分で定義したindex.htmlの内容にそったpageが表示される

次にindex.htmlをguiから消す。
作ったコンテナをクリックしてfilesをたどると、index.htmlがあるので、クリックして右上のごみ箱ボタンを押す→コンテナごと消えた。右クリックしてからdeleteでファイルのみ消える。
この状態でlocalhost:8080にアクセスすると Index of/という簡素な内容のHPにすげ変わっている。
また、ホストマシーンのindex.htmlも消えている。
hostとcontainerでファイルをshareしていることが確認できた。
作ったコンテナは不要なので消す

今日はここまで、明日からは何か創造的というかdocumentの後追いでないことをやりたい
Next.jsでReactのmemoを使用した際の"Component definition is missing display name eslint(react/display-name)"の解消

# 概要
Reactを使用して開発中、パフォーマンス最適化のためにmemoを使用したところESLintに
"Component definition is missing display name eslint(react/display-name)"
と怒られてしまったときの解決方法メモ。

# バージョン等
- Next.js v14.2.2(Pages Router)
- コンポーネントにはアロー関数を使用

# 解決方法
ファイルの最後にdisplayNameを明示的に示す。以下例。
``` tsx
ComponentName.displayName = "ComponentName";
```

# なぜdisplayNameが必要なのか
displayNameは主にReactのエラーメッセージで使われ、デバッグに役立つ。もしこれが得られない場合エラーメッセージにはデフォルトでComponentと表示されるため、アプリケーションに１つ以上のコンポーネントがあるとデバッグが非常に困難になってしまうため。

# 参考文献
- [What is React Component 'displayName' Used For?](https://stackoverflow.com/questions/41581130/what-is-react-component-displayname-used-for)
- [Disallow missing displayName in a React component definition (react/display-name)](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/display-name.md)
八百屋システムを考える（DXの内製化方法）

# 社内でプログラマを育成する
１．社内でプログラマを育成する場合、１人でやると属人化してしまう。これを防止するため２名以上で取り組む。
２．スプレッドシートで複数枚箇所を参照する少し複雑な式が理解できる人を選ぶ。
　　スプレッドシート間においている式を追いかけることでコードが見えてくる
  　[スプレッドシートで考えるプログラム](https://qiita.com/kawamo55/items/38c538b1350c8790b65a)

３．言語はPythonを使おう。理由は様々なライブラリがありそれぞれで豊富なサンプルが公開されており、学習コストが低い。
４．コードは写経だけではだめで、少なくとも動作を理解することを練習する。AIに作ってもらうことも同じこと・・・（なぜなら応用が効かないから）
５．社内で使っているスプレッドシートを分析するとシステム化の内容が見えてくる。

# 必要な技術その１（UIを作るライブラリ）
１．tkinter
tkinterはTcl/tkというGUIを作るためのシェルをPythonに移植したもの。
昔からあるので記事は多い
２．PyQt
python版のQTライブラリ、現在はQt6で少し難しいけど機能は豊富
３．PySimpleGUI
GUIの定義が簡単で使いやすいがVer５から有償化された。

# UIとしてWebを使う
Webを使うとブラウザ経由で操作ができるので以下のライブラリが使える
１．Flaskを使う
２．Djangoを使う

# データを扱うライブラリ（データの保存加工を行う）
１．データベースとしてPostgreSQLを使うば合う
psycopg2ライブラリを使う
２．データベースとしてsqlite3を使う
sqlite3ライブラリを使う
３．その他データベースを使う場合
pyodbcライブラリを使う
４．メモリとファイルだけで対応する場合
hoge.xlsに記録してpandasのデータフレームとして読書することでデータを管理する。

Pythonでは以上色んな方法が使えます。
この記事に関するお問い合わせは、こちらまで m_kawase@embed-ai.com まで！
社内でテスト駆動開発の体験会を開催したお話【準備編】

# はじめに
日頃テスト駆動開発を行っているのですが、この良さを他のチームにも広めたいという気持ちから開催することにしました。

# 開催目的
- テスト駆動開発を体験してもらい、メリットとデメリットを実感してもらう

- 開発の流れを体験してほしいので、コーディングスキルは求めない


# 準備したこと
- お題
おなじみのFizzBuzz問題にしました。

https://www.youtube.com/watch?v=Q-FJ3XmFlT8

当初は簡単なアプリを用意して実践寄りのお題にする予定でしたが、当日までに間に合わなさそうで断念。
そして開発の流れを体験してほしいのでテストやコード、手順を全てREADMEに記載し、それを参考にしながら進められるように準備しました。


<br>

- 環境と言語
私が担当しているプロジェクトはPythonなのでpytestを書いていますが、
社内ではRuby on Railsが主流なのでRspecにしました。

RailsのDocker環境構築については以前に記事を投稿していますのでよろしければ参考にしてみてください。

https://qiita.com/does_not_exist/items/2cd58c604ae617d3e3a2

<br>

- 資料
パワポ20枚くらいのボリューム。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/d30cc242-397c-a79c-835d-6b1bdf588de5.png)

- 当日使用するBGM
youtubeライブからスタバのBGMをお借りしました。


# 開催後に期待する結果
- 参加者がテスト駆動開発の進め方を理解している
- 参加者が自身のプロジェクトに導入したいかを考えてくれる


# おわりに
急いで準備をしましたが、当日に間に合って良かったです。
【準備編】の次は【当日編】になります！

https://qiita.com/does_not_exist/items/7277556f9e44f106d27a

【当日編】の続きは「振り返り編」です！

https://qiita.com/does_not_exist/items/536322f67881d493a190
MacのUTMでいろんなエミュレーションしてみた

# はじめに

この記事は、Qiita Engineer Festa 2024 4個目の記事です。
以下の記事より、すべての記事をご覧になれます。

https://qiita.com/0_terarin_0/items/b653e3851db100acc931

# 本題

そのままです。現実逃避でUTM上でさまざまなエミュレーションをしてみました。OSはDebian、アーキテクチャはaarch64,riscv64,x86_64,ppc64leです。以下、それぞれのインストール後の様子を載せておきます。

![Screenshot 2024-06-25 at 18.13.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/bcc05cd6-5460-fde5-36a4-fa3dbbcf45a0.png)

## riscv64のDebianのインストール方法

これだけは一からインストールできません。というかインストールの仕方が難しいので、すでに公開されているUTMのイメージを利用します。

https://github.com/utmapp/mac.getutm.app/issues/31

直リンクの掲載はやめておきます。リンク先に掲載されているUTMをインストールしてください。

## x86_64,ppc64le,aarch64のインストール方法

DVD-ROMのイメージをダウンロードしてそれぞれ任意の設定でVMを作ります。ただし、基本ディスプレイじゃなくてシリアルコンソールで起動したほうが良さそうです。

https://cdimage.debian.org/cdimage/weekly-builds/

## いつも通り使うまでの設定

さて、以下はそれぞれいつも通り使うまでの設定をずらっと記しておきます。

```zsh
# rootログイン
su

# repoの追加
nano /etc/apt/sources.list

#以下を追加（バージョンに合わせて）
deb http://deb.debian.org/debian bookworm main non-free-firmware
exec $SHELL -l

#sudoのインストール
apt install sudo

# sudoの追加
gpasswd -a user sudo
logout

# その他必要なもののインストール
sudo apt update --fix-missing && sudo apt install zstd zsh neofetch speedtest-cli
chsh
nano ~/.zshrc
```

僕はこんな感じで設定しました。ちなみに、Emulated VLANでSSHをきちんと設定する必要があったり、他にもShared LANよりもEmulated VLANの方が動作確率は高いので、探検が面倒な方はどうぞEmulated LANを利用してください。

# 最後に

今回はなかなかどうでもいい記事でしたが、意外とRISC-Vの環境とかを構築する方法がWebに載ってなかったりしたので、何かと役立つかもしれません。さて、もうそろそろ現実に戻らないといけないですね。実用的な内容を次からはあげていきたいと思います。こそれでは今後もエンジニアフェスタの記事をお楽しみにしてください。 

https://qiita.com/0_terarin_0/items/b653e3851db100acc931
PythonでPlaces APIを使って場所検索をしてみた


# はじめに

この記事は、Qiita Engineer Festa 2024 3個目の記事です。
以下の記事より、すべての記事をご覧になれます。

https://qiita.com/0_terarin_0/items/b653e3851db100acc931

# 本題

さて、急にぶっ飛んだ話題が来ましたね。今回は学校の都合でPlaces APIを利用して場所の検索をしなければならなくなりました。とりあえず、以下参考にした記事を掲載させていただきます。

https://qiita.com/yoshii0110/items/7938085151f569427af6

以下、コードを貼り付けておきますので、必要に応じて使い分けてください。

```py:main.py
import requests
import json
import time
import pandas as pd

GOOGLE_MAP_KEY = "" #Insert your API key.
FILTER_SEARCH_RADIUS = "40000" #Meter unit
FILTER_SEARCH_CENTER = "34.372798,135.302189"
FILTER_SEARCH_KEYWORD = ""

GET_URL = "https://maps.googleapis.com/maps/api/place/nearbysearch/json"

def getInfo(page_token):
    GET_PARAMS = {
        "key":GOOGLE_MAP_KEY,
        "location":FILTER_SEARCH_CENTER,
        "radius":FILTER_SEARCH_RADIUS,
        "keyword":FILTER_SEARCH_KEYWORD,
    }
    if(page_token != None):
        GET_PARAMS["pagetoken"] = page_token
    return requests.get(GET_URL, params=GET_PARAMS).json()
    
NEXT_PAGE_TOKEN = ""

SEARCH_RESULT = []

try:
    for i in range(100):
        if(i == 0):
            print("Start to get information from Google...")
            req = getInfo(None)
        else:
            req = getInfo(NEXT_PAGE_TOKEN)
        SEARCH_RESULT.extend(req["results"])
        time.sleep(5)
        NEXT_PAGE_TOKEN = req["next_page_token"]
except:
    print("Finished to get!")

pd.json_normalize(SEARCH_RESULT).to_csv(f'out{MODE}.csv', encoding='utf-16')

```

以下、若干の説明を加えておきます。

## 一回のリクエストで請求できる情報は20件まで

Places APIに一回Getリクエストを送ると、Resultsには20件しか入っていませんでした。しかし、絶対に20件以上の検索結果が出てきます。どうしたらいいのでしょうか？

ここで解決してくれるのが、```next_page_token```です。これを次のリクエストの```pagetoken```に指定することで、21件目以降の検索結果を表示することができます。

しかし、ここで問題が出てきます。それは、「検索結果の総計がわからないと何回このリクエストを実施したらいいかわからない」ということです。ここで、```next_page_token```に関する重要な情報として、追加の検索結果がない場合はそもそも```next_page_token```がありません。したがって、僕は今回途方もない大きな数を指定して、```next_page_token```を取り出し次のリクエストをするというループに```try-except```を設置して、```next_page_token```がない時にエラーを吐いて```try-except```を終えるというプログラムを組んでいます。

## CSVで保存する

今回は最終的にQGISで緯度経度を利用する必要があるので、CSVで保存した。Pythonの実行ファイルと同じフォルダに作成させた。ただし、一回の操作で全ての情報を一個のCSVに保存しておきたいので、あらかじめCSVにデータを作成する前に一つの配列に全てのデータを入れておくことにした。

```python:test.py
#配列に追加
SEARCH_RESULT.extend(req["results"])

#CSVに変換
pd.json_normalize(SEARCH_RESULT).to_csv(f'out{MODE}.csv', encoding='utf-16')
```

ここで大事なのが、CSVをMacのExcelで扱うときに文字のエンコードがUTF-16でないと文字化けしますので、エンコードをUTF-16に指定しておきました。

# 最後に

今回も久しぶりの記事で、なおかつちょっと珍しい話題でした。次からは現実逃避でちょっとエミュレーターで遊んでいきたいと思います。こそれでは今後もエンジニアフェスタの記事をお楽しみにしてください。 

https://qiita.com/0_terarin_0/items/b653e3851db100acc931
Laravelで大量データを扱う：chunkメソッドの基礎と実践

## はじめに
最近、大量のデータを扱う対応をいたしました
もちろんそのまま取得すると動作が重くなったりサーバーへの負荷が重すぎてしまいます
そのため、取得数の制限をしたかったのですが、すべてのデータを使用する必要があったのでせめて分割で取得できればなと思いました
そんな中で見つけた`chunk`が今回のような状況にピッタリだったので調べた内容を記事にします

https://readouble.com/laravel/8.x/ja/eloquent.html#:~:text=flight%2D%3Ename%3B%0A%7D-,%E7%B5%90%E6%9E%9C%E3%81%AE%E5%88%86%E5%89%B2,-all%E3%81%BE%E3%81%9F%E3%81%AFget

## `chunk`メソッドとは
- `chunk`メソッドは、指定したサイズごとにクエリ結果を分割して取得して、第2引数のクロージャ関数で処理を実行します
    - 分割したレコードをそのまま参照できるのではなく、分割したうえで何かしらの処理を実行させなければならなりません

```php
$users = User::chunk(100, function($users){});

echo $users;
// 1
```

`chunk`の返り値は`bool`ですので代入しても取得したデータは使えないのです

- 第1引数に分割する件数を設定します
    - 例：100を設定すると、100件取得して処理したら次の100件を取得する
- 第2引数にコールバック関数を設定します
    - 呼び出し元で行う処理が入ります
    - これがfalseなら処理は終了されます

参考Github

https://github.com/laravel/framework/blob/7100359775330c7485cf5e65d78aa279cd483c72/src/Illuminate/Database/Concerns/BuildsQueries.php#L32

### `chunk`の使い方

```php
User::chunk(100, function ($users) {
    foreach ($users as $user) {
        //  ユーザーに関する処理
    }
});
```

上記では、ユーザーデータを100件ずつ分割して処理しています

## `get`メソッドとの比較
- `get`メソッドの問題点
    - 大量のデータを使用する際に`get`メソッドを使用すると、一度にすべてのデータをメモリにロードするため、大量のデータを処理する際にメモリ不足やパフォーマンスの低下を引き起こす可能性があります

`get`メソッドを使用した例
```php
$users = User::get(); // 全ユーザーデータを一度に取得
foreach ($users as $user) {
    // ユーザーに関する処理
}
```

上記の例では、すべてのユーザーデータを一度に取得して処理しています
この方法は少量の（数十件くらい）のデータでは問題ありませんが、数百件以上のデータではメモリ不足や処理の遅延が発生してしまいます

### `chunk`の具体的な使用例
- 大量データのバッチ処理
    - バッチ処理を行う際に、`chunk`メソッドを使うことでメモリ使用量を抑えつつ、大量データを効率的に処理できます

```php
Order::chunk(1000, function ($orders) {
  foreach ($orders as $order) {
    // バッチ処理ロジック
    // 例えば、注文データを処理してレポートを生成する
  }
});
```

- データエクスポート
    - データエクスポートに関しても大量データを効率的に処理できるようになります

```php
Product::chunk(500, function ($products) {
    // CSVエクスポートロジック
    // 例えば、製品データをCSVファイルに書き出す
});
```

## まとめ
大量データを扱う際は可能であればgetではなく`chunk`と使って分割取得し、
サーバー負荷やメモリ不足、パフォーマンスの低下を未然に防ぎましょう！

## 参考リンク

https://readouble.com/laravel/8.x/ja/eloquent.html#:~:text=flight%2D%3Ename%3B%0A%7D-,%E7%B5%90%E6%9E%9C%E3%81%AE%E5%88%86%E5%89%B2,-all%E3%81%BE%E3%81%9F%E3%81%AFget

https://github.com/laravel/framework/blob/7100359775330c7485cf5e65d78aa279cd483c72/src/Illuminate/Database/Concerns/BuildsQueries.php#L32
カラム名.present?の省略方法

下の記事の内容は正しくありません。
~~カラム名.present?の省略方法がActiveRecordで元から用意されていたので、自分用に残しておきます。~~

```ruby
user = User.first
user.name? # user.name.present? と同意
# => true / false
```

~~rails cの補完機能を使っていると見つけました。
このような元から用意されてある動的なメソッドは、まだまだたくさんあるみたいなので、もっと勉強していこうと思います！~~
JavaScriptで選択した画像ファイルをDOMに表示する方法

## はじめに

`<input type="file" />`で選択した画像ファイルをDOMに表示する方法を紹介します。
`FileReader`使い方をすぐ忘れてしまうため、備忘録として残します。

## デモ画像

ファイル選択から犬の画像を選択すると、DOMに表示されます。

![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f313132353532332f34653733393562652d636262632d646433382d333637632d6163643164376566636464352e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1125523/b85b75cd-c954-c039-6613-da2f2bbd034b.png)


## コード

bodyの最後に選択した画像を表示してます。

```JavaScript
const fileInput = document.getElementById('file');

// ファイルを読み込む
const loadFile = (e) => {
  const file = e.target.files[0];

  if (!file) {
    return;
  }

  const reader = new FileReader();

  reader.onload = (e) => {
    // img要素を作成
    const imgElement = document.createElement('img');
    imgElement.src = e.target.result;

    // bodyの最後に追加
    document.body.appendChild(imgElement);
  };

  reader.readAsDataURL(file);
};

// ファイルを選択した時に発火
fileInput.addEventListener('change', loadFile);
```

## 最後に

### GoQSystemでは一緒に働いてくれる仲間を募集中です！

ご興味がある方は以下リンクよりご確認ください。

https://goqsystem.recruit.site?qiita=26405c5d2b1795840ef1
Angular でコンポーネントのテンプレートにプロパティバインディングを追加してみました。

気持ちに波があるのか、少し頑張ってしまいました。
やりたいことが少しだったと言うのもありましたが本日２回目の投稿になります。
少しづつですが何となく Angularを理解したつもりになってきています。
早く何か自分のアプリケーションを作ってみたいななんて思っています。
うっ、「調子に乗るなよ！」なんて神の声が聞こえてきました。
それでは、頑張っていきます。

前回のコンポーネントに入力パラメータを追加した続きになります。

https://qiita.com/monotoneboy/items/1883839685050157a922

#### １．プログラムを更新します。
##### １ー１．app.component.ts のテンプレートのタグを更新します。
``` diff
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { HeaderComponent } from './header/header.component';
import { MainComponent } from './main/main.component';
import { Encyclopedia } from './encyclopedia';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet, HeaderComponent, MainComponent],
  template: `
  <main>
    <app-header></app-header>
-    <app-main></app-main>
+    <app-main [encyclopedia]="encyclopedia"></app-main>
  </main>
`
})
export class AppComponent {
  encyclopedia: Encyclopedia = {
    id: 999,
    name: 'Test Name',
  };
}
```
（アウトプット）
![1-1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3798951/3e35fdcd-2865-2ceb-d46b-927375557845.png)

:::note info
ここで新たに表示されるものはありません。エラーにならないこと確認します。
:::

エラーになっていないので、今日のところはここまでです。
タイ語モデルPhayaThaiBERTは「แม่อย่าเก็บไว้คนเดียว」をどうトークナイズするのか

Panyut Sriwirote, Jalinee Thapiang, Vasan Timtong, Attapol T. Rutherford『[PhayaThaiBERT: Enhancing a Pretrained Thai Language Model with Unassimilated Loanwords](https://arxiv.org/abs/2311.12475)』を読みつつ、この「[PhayaThaiBERT](https://huggingface.co/clicknext/phayathaibert)」のトークナイザがどうなっているのか気になった。論文には

> Since most of the model’s parameters are transfered directly from WangchanBERTa, it is imperative that we also preprocess and tokenize our training data in the same manner. Accordingly, we preprocess and tokenize all of our training data following the procedure described in the Methodology section of the WangchanBERTa paper, with the exception of the tokenization section, where we will use our expanded tokenizer instead.

と書かれているものの、どうもWangchanBERTaの悪いところ(複数の単語をまたいでトークナイズしてしまう)を引き継いでしまっているのではないか、と思えたからだ。

```
>>> from transformers import AutoTokenizer
>>> tkz=AutoTokenizer.from_pretrained("clicknext/phayathaibert")
>>> print(tkz.convert_ids_to_tokens(tkz("แม่อย่าเก็บไว้คนเดียว")["input_ids"]))
['
', '▁แม่', 'อย่า', 'เก็บไว้', 'คนเดียว', '
']
```

「[แม่อย่าเก็บไว้คนเดียว](https://qiita.com/KoichiYasuoka/items/851bbe0299f43c7eb4e9)」をトークナイズしてみたところ、「แม่」「อย่า」は正しくトークナイズできているものの、「เก็บ」「ไว้」「คน」「เดียว」がくっついてしまっている。やはり、WangchanBERTaの悪いところを引き継いでしまっているようだ。この論文は「[LST20 corpus](https://opend-portal.nectec.or.th/it/dataset/lst20-corpus)」を評価対象の一つにしてるのに、どうしてタイ語トークナイザの「品質」に無頓着なんだろ。ちなみに、[4月11日の記事](https://qiita.com/KoichiYasuoka/items/e378e737e2978b988983)の2行目を「`models=["clicknext/phayathaibert","airesearch/wangchanberta-base-att-spm-uncased"]`」に変えて、[UD_Thai-PUD](https://github.com/UniversalDependencies/UD_Thai-PUD)によるトークナイザ・ベンチマークを実行した結果は以下の通り。

```
*** clicknext/phayathaibert
Precision 0.3978728432994564
Recall    0.3770719469581579
F1 Score  0.3871932286036295

*** airesearch/wangchanberta-base-att-spm-uncased
Precision 0.40143651529193697
Recall    0.3880924648329003
F1 Score  0.3946517242950207
```

正直かなり低い。
baserCMS ver.5系でカスタムコンテンツをトップページなどに表示する

## baserCMSの5系の新機能 「カスタムコンテンツ」
baserCMSの5系になってから新たにできた機能 「カスタムコンテンツ」は、
自分で好きな入力項目などが作成でき、マスター・テーブルと紐づけて自由にコンテンツが設計できる強力な機能です

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553367/b472a92c-5bb6-aae2-b5d9-5087c377e717.png)
また、自分で作ったカスタムコンテンツをコンテンツツリーに追加することで、好きなURLを簡単に当てることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553367/b0bc3d93-818d-07ce-41fe-1737520221af.png)

このカスタムコンテンツを使った一番良く使いそうなコンテンツとして、「カスタムフィールド＋ブログ」のようなものを作成しようと考えている方も多いかと思います。
（baserCMSも4系にはCuCustomField（カスタムフィールド）プラグインが存在していましたが、5系ではカスタムコンテンツに統合されてしまっています。）

ブログ的な使用の仕方をする場合、トップページなどに最新記事を何件か表示する事があると思います。
ブログの場合は
```
$this->BcBaser->blogPosts({ブログname}, 5);
```
などで簡単に一覧表示を呼び出すことができました。

ところが、現在baserCMS 5系のカスタムコンテンツに
`$this->BcBaser->blogPosts( $contentsName , $num , [$options] );`
のような機能を持つメソッドは存在しません。

そこで、ちょっと難しいですが、使用テーマ専用のHelperなどを作成し、
テーマ専用で呼び出しメソッドを作るやり方に付いて一例を説明したいと思います。

##
まず、使用テーマ専用のHelperなどを作成します。
（例 plugins/テーマ名/src/View/Helper/ThemeHelper.phpを作成）
その中に下記の様なメソッドを作成します。

```
    /**
     * カスタムエントリーの一覧を取得する
     *
     * @param entity or int $customContent
     * @param int $num
     * @param array $options
     * @return mixed
     *
     * @checked
     * @noTodo
     * @unitTest
     */
    public function getCustomEntries($customContent, int $num = 5, array $options = [])
    {
        // $customContentが entityId だった場合
    	if (is_integer($customContent)) {
    		$customContent = $this->getService(CustomContentsServiceInterface::class)->get($customContent, [
    			'status' => 'publish'
    		]);
    	}
    	$this->entriesService = $this->getService(CustomEntriesServiceInterface::class);
    	$this->entriesService->setup($customContent->custom_table_id);
    	$params = array_merge([
    		'contain' => ['CustomTables' => ['CustomContents' => ['Contents']]],
    		'status' => 'publish',
    		'order' => $customContent->list_order,
    		'direction' => $customContent->list_direction,
    		'limit' => $num
    	], $options);
    	return $this->entriesService->getIndex($params)->toList();
    }
```
このような感じのメソッドを作成すると、
テーマファイル内で
```
$entries = $this->Theme->getCustomEntries(1, 5);
```
ID:1ので呼び出すことができ、
$entriesをループで回して表示できるように組込を行うことで対応は可能です。
ちなみにこのIDは「custom_contents」テーブルのidで、
「カスタムコンテンツ設定編集」画面のurlの末尾の数値と同じです。
例
`/baser/admin/bc-custom-content/custom_contents/edit/1`

この場合は idは1となります。

:::note warn
注意
ヘルパー内で下記読み込みが必須です。
```
use BcCustomContent\Service\CustomContentsServiceInterface;
use BcCustomContent\Service\CustomEntriesServiceInterface;
```
:::
普段テーマ専用のHelperを作成していないとちょっと難しいかもしれませんが、
ぜひともトライしてみてください。
Angular でコンポーネントに入力パラメータを追加してみました。

少しバタバタしていて、中途半端に投稿が止まっていました。
やはり久々に続きをしようと思うと、前回までしてきたところが、
「あれっ？どうするのだっけ？」といろいろ忘れてしまい余計な時間がかかってしまいました。
できる限り続けて勉強しないといけないなと思いました。
そういうことで続き、頑張っていきます。

前回のインターフェイスを作った続きになります。

https://qiita.com/monotoneboy/items/819070ef2ae6c4d53e6e

#### １．main コンポーネントを作成します。
##### １－１．新しい汎用コンポーネント作成するコマンドを実行します。
・コマンドプロンプトで以下のコマンドを実行します。
`ng generate component main`
![2-1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3798951/61549577-67c3-72af-b159-e6dae05b528a.png)
##### １－２．app.component.ts に main コンポーネントをインポートします。
``` diff
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { HeaderComponent } from './header/header.component';
+ import { MainComponent } from './main/main.component';
import { Encyclopedia } from './encyclopedia';

@Component({
  selector: 'app-root',
  standalone: true,
+  imports: [RouterOutlet, HeaderComponent, MainComponent],
  template: `
  <main>
    <app-header></app-header>
+    <app-main></app-main>
  </main>
`
})
export class AppComponent {
  encyclopedia: Encyclopedia = {
    id: 999,
    name: 'Test Name',
  };
}
```
（アウトプット）
![2-1-1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3798951/4f924bf0-c1fa-f124-0509-6f5f3b84f22c.png)
#### ２．Input デコレータと Input プロパティを追加
２－１．main.component.ts に Input デコレーターをインポートします。
``` diff
- import { Component } from '@angular/core';
+ import { Component, Input } from '@angular/core';
+ import { Encyclopedia } from '../encyclopedia';

@Component({
  selector: 'app-main',
  standalone: true,
  imports: [],
  templateUrl: './main.component.html',
  styleUrl: './main.component.css'
})
export class MainComponent {
+  @Input() encyclopedia!: Encyclopedia;
}
```
（アウトプット）
![3-1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3798951/1ba7e2d4-9cb9-3fe8-4899-c46c27013e85.png)

:::note info
ここで新たに表示されるものはありません。エラーにならないこと確認します。
:::

エラーになっていないので、今日のところはここまでです。
GitHub Enterprise ServerでGitHub CLIを使う

# GitHub CLI
GitHubの情報の表示・操作時に、ブラウザを開かずとも、CLI上で情報の出力や操作を可能にしてくれる、GitHub CLIがあります。こちらのツールを用いて様々な作業効率化を図っていきたいと思い、導入してみます。

https://cli.github.com/

## プランや運用種別による初期設定の違い
使用しているGitHubのプランや運用種別によって、GitHub CLIの認証方法が異なっています。
今回はGitHub Enterprise ServerにGitHub CLIをインストールし、認証まで行う方法について解説します。

環境:`MacOS Sonoma14.3.1`

#### GitHub Enterprise Serverを利用している
本記事、また公式Docsを参考にしてください

https://docs.github.com/ja/enterprise-server@3.13/github-cli/github-cli/quickstart

#### GitHub Free / Proを利用している
公式Docs
https://docs.github.com/ja/github-cli/github-cli/quickstart

#### GitHub Enterprise Cloudを利用している
公式Docs
https://docs.github.com/ja/enterprise-cloud@latest/github-cli/github-cli/quickstart

## 設定手順

■ インストール
brewを使用してインストールします。
```shell
brew install gh
```

■ ログイン設定（hostnameの入力）
GitHubの任意のリポジトリをブラウザで開き、URLバーに表示されている、`https://${hostname.com}/repository-name`
の`${hostname.com}`の部分を下記コマンドに貼り付けて実行してください。
```shell
gh auth login --hostname ${hostname.com}
```

コマンドを実行すると複数の質問に答えていく形になります。

■ プロトコル選択
接続するプロトコルの方式をssh or HTTPSで選択します。→ HTTPSを選択
```shell
? What is your preferred protocol for Git operations on this host? 
> ssh / HTTPS
```

■ GitHubクレデンシャルを使用した認証の確認 → Yを選択
```shell
? Authenticate Git with your GitHub credentials? 
> Yes
```
■ ブラウザを用いた認証を行うか → Enter
```shell
? How would you like to authenticate GitHub CLI? Login with a web browser
```
■ 一時的な認証コードを発行されるのでメモしておく（`XXXX-XXXX`）
```shell
! First copy your one-time code: XXXX-XXXX
```
■ 下記メッセージが出たら、Enterを押下
```
Press Enter to open ${hostname.com} in your browser...
```
■ ブラウザが開きます
・先ほどの認証コードを入力してContinueを押下。
![スクリーンショット 2024-06-24 20.30.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415169/803295e3-53c5-5ce9-9922-29291716f279.png)
・下記のように表示されれば、認証できています。
![スクリーンショット 2024-06-24 20.31.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415169/9f95bc2f-bc63-f08e-4956-49075c480716.png)

・ターミナル上では、下記メッセージが出ており、こちらでも認証完了を確認することができました。:tada:
```shell
✓ Authentication complete.
- gh config set -h ${hostname.com} git_protocol https
✓ Configured git protocol
✓ Logged in as ${your.github.account.name}
```

## 動作確認
試しに、下記コマンドを作業ディレクトリで実行してみましょう
（PullRequestを一覧出力してくれる）
```shell
gh pr list
```
下記のように、PullRequestの一覧を表示してくれました:tada:
ブラウザを毎回開いてPullRequestを見に行かずとも、ターミナル内で即座に一覧を見ることができます:tada:
```shell
ID   TITLE             BRANCH      CREATED AT      
#29  Hoge CSSの修正...  fix/design  about 6 days ago
#30  Hoge HTMLの修正... fix/design  about 7 days ago
```

## 終わりに
今回はGitHub Enterprise ServerでGitHub CLIが使えるようになるまでの手順を紹介しました。
インストール後のログインコマンド`gh auth login --hostname ${hostname.com}`が今回のミソとなる部分です。
次は、GitHub CLI活用法や生産性向上についての記事が書ければと思います！
Rust で音声合成して WAV ファイルに保存する

# 動機

下記の記事を最近の windows クレート仕様に書き換えてみたかったのと、音声ファイルとして保存する方法を調べてみました。

https://qiita.com/7shi/items/885501607aecee7613fa

# 環境

rustc 1.79.0
Windows 10 22H2

# 実装

短いので全部載せます。

```toml:Cargo.toml
[package]
name = "speech"
version = "0.1.0"
edition = "2021"

[dependencies]
anyhow = "1.0"

[dependencies.windows]
version = "0.57"
features = [
    "Media_Core",
    "Media_Playback",
    "Media_SpeechSynthesis",
    "Storage_Streams",
    "Win32_System_WinRT",
]
```

```rust:main.rs
use anyhow::Result;
use std::fs;
use std::path::Path;
use std::slice;
use std::sync::mpsc;
use windows::{
    core::{Interface, HSTRING},
    Foundation::TypedEventHandler,
    Media::{
        Core::MediaSource,
        Playback::MediaPlayer,
        SpeechSynthesis::{SpeechSynthesisStream, SpeechSynthesizer},
    },
    Storage::Streams::DataReader,
    Win32::System::WinRT::IBufferByteAccess,
};

fn speech_synthesis_stream(source: &str) -> Result<SpeechSynthesisStream> {
    let utf16 = source.encode_utf16().collect::<Vec<_>>();
    let source = HSTRING::from_wide(&utf16)?;
    let synth = SpeechSynthesizer::new()?;
    let stream = synth.SynthesizeTextToStreamAsync(&source)?.get()?;
    Ok(stream)
}

fn speech(source: &str) -> Result<()> {
    let stream = speech_synthesis_stream(source)?;
    let player = MediaPlayer::new()?;
    let source = MediaSource::CreateFromStream(&stream, &stream.ContentType()?)?;
    player.SetSource(&source)?;
    
    let (tx, rx) = mpsc::channel();
    let tx_clone = tx.clone();

    // イベントハンドラの登録
    // 再生終了時に呼ばれる
    let token_media_ended = player.MediaEnded(&TypedEventHandler::new(move |_, _| {
        tx_clone.send(()).ok();
        Ok(())
    }))?;
    
    // 再生失敗時に呼ばれる？
    let token_media_failed = player.MediaFailed(&TypedEventHandler::new(move |_, _| {
        tx.send(()).ok();
        Ok(())
    }))?;
    
    player.Play()?;
    
    // 再生終了を待つ
    rx.recv()?;

    // イベントハンドラの削除
    player.RemoveMediaEnded(token_media_ended)?;
    player.RemoveMediaFailed(token_media_failed)?;
    
    Ok(())
}

fn save_to_wav<P: AsRef<Path>>(path: P, source: &str) -> Result<()> {
    let stream = speech_synthesis_stream(source)?;
    
    // ストリームがらデータを読む
    let reader = DataReader::CreateDataReader(&stream)?;
    let size = stream.Size()? as u32;
    reader.LoadAsync(size)?.get()?;

    // 生ポインタを取得するために IBuffer を IBufferByteAccess にキャスト
    let buffer: IBufferByteAccess = reader.ReadBuffer(size)?.cast()?;
    let ptr = unsafe { buffer.Buffer()? };

    // Rust の世界のスライスに変換
    let slice = unsafe { slice::from_raw_parts(ptr, size as usize) };
    
    // 保存
    fs::write(path, slice)?;
    
    Ok(())
}

fn main() -> Result<()> {
    // 「こんにちは」って喋る
    speech("こんにちは")?;

    // 「こんばんは」という WAV ファイルを指定したパスに保存
    save_to_wav("test.wav", "こんばんは")?;

    Ok(())
}

```

以上です。
Python venv Error "No Python at ..."

# 実現したいこと
venv 仮想環境を使用している Python プロジェクトを異なる環境に移動し、実行する

# エラーメッセージ
```bash:Terminal
No Python at /path/to/python.exe
```
エラーメッセージ全体は記録を失念していた。

# 原因
venv 仮想環境を使用している Python プロジェクトを異なる環境に移動したため

# 解決方法
1. `deactivate` で仮想環境を無効化する
    `command not found` エラーが返されたため、飛ばして 2. へ
1. `.venv` フォルダを削除する
2. 再度、仮想環境を構築する
`python -m venv .venv`
`.venv\Scripts\activate.bat`
3. `requirements.txt` を使用している場合、
ターミナルで `pip install -r requirements.txt` を叩く。
その後実行して `ModuleNotFoundError` が発生した場合、同じコマンドを叩く

# 環境
Python 3.12.2
Visual Studio Code 1.89.1

# 参考
https://hepokon365.hatenablog.com/entry/2023/04/24/190000
IBM i Services Directory : QSYS2.User_Info ビュー　ログイン失敗回数の表示ほか

## 概要
■ビュー名：QSYS2.USER_INFO
■カテゴリ：セキュリティサービス
■サポート：IBM i 7.2以降
■参照URL（IBM i 7.5）：https://www.ibm.com/docs/ja/i/7.5?topic=services-user-info-view

ユーザー・プロファイルに関する情報を返します。

## 使用例

直近でサイン・オンに失敗したユーザー・プロファイルをリストします。
```
SELECT * FROM QSYS2.USER_INFO
  WHERE SIGN_ON_ATTEMPTS_NOT_VALID > 0
```

### 実行例はこちら

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/2c7625ed-73d1-5835-2311-e7af03e21a6b.png)

上記で ***Sign On Attempts Not Valid***がサイン・オンに失敗した回数です。
例えば、ユーザー GOMA2 は直近2回、サイン・オンに失敗しています。

※このカウントは正常にサイン・オンすると0にリセットされます。

それ以外にも以下のようなセキュリティ情報を取得できます。
***STATUS : ENABLED/DISABLED***
***Password Change Date : パスワード最終変更日***

また、セキュリティ以外の情報も取得できますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/3bafd657-5884-1a7a-60bd-f542c2dbbfe6.png)

***Storage Used : このユーザーの所有オブジェクトのサイズ***
***Job Description Name : ジョブ記述名***

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/5021032a-2800-5369-3878-b9fd1131dcc0.png)

***Home Directory : ホームディレクトリ*** も表示できます。

その他にも・・

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/d55ba579-bdd4-c113-00b2-f7c4960b515e.png)

***User Creator : どのユーザーがこのユーザーを作ったか***
***Creation Timestamp : 作成日タイムスタンプ***
***Last Used Timestamp : 最終使用日タイムスタンプ***
***Days Used Count : このユーザーの使用日数***
***Authority Collection Active : 権限収集を取得しているか***

などなど。

ユーザー・プロファイルの基本的な情報を色々管理・取得できそうです。


## User_Info と User_Info_Basic の処理時間をデータとって比較してみた

User_Infoには User_Info_Basicという兄弟ビューもあります。処理時間がかなり異なります。

[User_Info_Basicビューの説明はコチラです](https://qiita.com/gomAnomalocaris/items/580c0052efe60a78d337)

[処理時間の比較は、こちらの記事にまとめました](https://qiita.com/gomAnomalocaris/items/7b2f74b07a420f44e8c3)
array_filter関数

プルダウンを使ったarray_filter関数のサンプル

# 配列定義
```php

// フルーツ種類定義 
define("APPLE",  1);
define("BANANA", 2);
define("ORANGR", 3);
define("CHERRY", 4);
define("GRAPE",  5);


// フルーツ配列
define(
	'FRUIT',
	array(
		APPLE  => 'りんご',
		BANANA => 'バナナ',
        ORANGR => 'オレンジ',
        CHERRY => 'さくらんぼ',
        GRAPE  => 'ぶどう',
	)
);
```

# array_filterで範囲を指定
バナナまでの表示に設定
<img width="120" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3648477/cb9404a4-ad04-8311-6ffe-5a01116fd51a.png">

<img width="120" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3648477/57105c8c-537c-a1e3-2b5b-c8f73c2703a4.png">

```php
// リンゴからバナナに指定
$filtered_furuit = array_filter(
    FRUIT,
    function ($key) {
        return $key <= ORANGR;
    },
    ARRAY_FILTER_USE_KEY
);
```



# 表示
```php
<th>フルーツ</th>
<td>
    <select name="furuit" class="chosen-select">
    <option value="">全て</option>
        <?php foreach ($filtered_furuit as $key => $val) { ?>
            <option value="<?php echo $key; ?>"<?php echo (isset($_POST['furuit']) && $_POST['furuit'] == $key) ? 'selected' : ''; ?>><?php echo $val; ?></option>
        <?php } ?>
    </select>
</td>
```
ローカルリポジトリを作成してgithubにリモートリポジトリとしてコマンドで追加する方法

本記事ではローカルリポジトリを作成してgithubにリモートリポジトリとして追加する方法について記載する．

以下のgitの知識は知っていることを前提とする．gitの基礎のついてわからない方は一読することをお勧めする．

https://qiita.com/tarakokko3233/items/ad7e1a1a14d3e2f10da3

# githubCLIのインストール
```
brew install gh 
```

# githubにログインする
```
gh auth login 
```
`GitHub.com`か`GitHub Enterprise Server`かを選択する．個人利用なら`GitHub.com`を選択すれば良い，
```
? What account do you want to log into?  [Use arrows to move, type to filter]
> GitHub.com
  GitHub Enterprise Server
```

`HTTPS`か`SSH`を選択する
```
? What is your preferred protocol for Git operations on this host?  [Use arrows to move, type to filter]
> HTTPS
  SSH
```
`Y`と入力してEnter
```
? Authenticate Git with your GitHub credentials? (Y/n)
```
`Login with a web browser`で良いと思う．ブラウザでgithubへログインすることになる．
```
? How would you like to authenticate GitHub CLI?  [Use arrows to move, type to filter]
> Login with a web browser
  Paste an authentication token
```
その後，以下のように表示されるのでone-time codeを控えておく．

:::note info
0000-0000は英数字で表示され，ユーザ，タイミングによって異なる値である．
:::

```
! First copy your one-time code: 0000-0000
Press Enter to open github.com in your browser...
```
web上でone-time codeを入力し，githubにログインすると以下のように表示される．

```
✓ Authentication complete.
- gh config set -h github.com git_protocol https
✓ Configured git protocol
✓ Logged in as <githubアカウント名>
```

# リモートリポジトリの作成
このコマンドでリモートに新しくリポジトリを作成する．
```md:プライベートリポジトリなら
gh repo create <リモートリポジトリ名> --private
```
```md:パブリックリポジトリなら
gh repo create <リモートリポジトリ名> --public
```

:::note info
自分のGithubアカウントに紐付いた組織アカウントにリモートリポジトリを作成するなら
```md:プライベートリポジトリなら
gh repo create <組織アカウント名>/<リモートリポジトリ名> --private
```

:::

この時点でgithubにアクセスすると自分のリポジトリに作成したものが反映されているはずである．

# ローカルリポジトリの作成
まずディレクトリを作成する
```bash
mkdir yourdirectoryname
cd yourdirectoryname
```

以下のコマンドで`.git`ファイルを作成する．これでローカルリポジトリが作成できる
```bash
git init
```

# ローカリリポジトリとリモートリポジトリの関連付け
ローカルリポジトリがGitHubのリモートリポジトリに関連付けられる．
```bash
git remote add origin https://github.com/<アカウント名>/<リモートリポジトリ名>.git
```
:::note info
`https://github.com/<アカウント名>/<リモートリポジトリ名>.git`は作成したリモートリポジトリのURLなのでgithubからコピペでも良い
:::
# ローカルリポジトリでコミットする
新しく変更差分を作る(ファイルを追加，編集，削除する）
その後，`git add`をする．
```bash
git add <ファイル名>
```
:::note info
`git add .`でカレントディレクトリすべてのファイルを，`git add *`でリポジトリ内すべてのファイルをステージングエリアにaddする．
:::

```bash
git commit -m "コミットメッセージ"
```

# リモートリポジトリにプッシュする
```bash
git push origin main
```
これでgithubにアクセスすればリモートにプッシュした内容が反映されている．


# github.com上のリモートリポジトリにコマンドでアクセスする方法
## githubCLIにログインしている場合
```bash
gh browse
```

## githubCLIにログインしていない場合
以下のコマンドを実行するとローカルリポジトリに結びついたリモートリポジトリにアクセスできる．
**Macでは**
```bash
open $(git remote get-url origin)
```
**Windowsでは（PowerShellで）**
```powershell
start (git remote get-url origin)
```
Unity学習記録 #003 条件分岐（if文）

# 条件分岐に使用される制御構文
条件分岐で使用される構文は2つです。
・if文（条件を満たす場合に実行する処理を記述）
・switch文（変数の値によって処理を分岐）

上記２つの構文はどちらも同じ結果を出力するコードの書き方ができますが、それぞれの構文の核となるイメージを記述しました。
if文は特定の範囲で条件を満たすような値を扱う場合、switch文は値が定まっている場合に使用します。そこに追加情報を加えて、処理する内容を拡張していくイメージです。

ここで言う追加情報とは、「指定した条件を満たさない場合の処理」や「指定した条件以外の時の処理」を指します。

## if文
まずはシンプルなif文を記述します。
所持金が10000以上であれば、コンソールにテキスト文を出力するといった処理を書いてみます。実行するとコンソールに出力結果が表示されます。
```csharp
int money = 12000;  //所持している金額

if (money >= 10000)  //moneyの値が10000以上であれば次の処理を実行
{
    Debug.log("私は温泉旅行に行きます")
}
```
ここで、moneyに設定している値を10000より小さい値にするとどうなるか。
条件を満たさないので、中括弧の中に記述した条件処理の行が実行されません。この場合の出力結果は何も起こらないという結果になります。（パソコンの中ではしっかり判定処理をしています。）
条件を満たした時の出力結果だけが欲しい場合はこの記述でもOKですが、条件を満たさなかったときも何からしら結果を出力して確認するようにした方が、システム開発のミスも減らせると思います。
<br>

次のコードで条件を満たさない場合の処理も記述してみます。
```csharp
int money = 12000;

if (money >= 10000)
{
    Debug.log("私は温泉旅行に行きます")
}
else //上記の条件を満たさない場合は次の処理を実行
{
    Debug.log("私は家でゲームをします")
}
```
else文を使うことで条件を満たさない場合の処理も実行することができるようになります。上記の記述では条件を満す場合と満たさない場合のどちらの出力結果も分かるので、条件分岐が正しく処理されていることを確認することができます。
<br>

次に条件を複数設定する場合を見てみます。
```csharp
int money = 8000;

if (money >= 10000)
{
    Debug.log("私は温泉旅行に行きます")
}
else if (money >= 5000)
{
    Debug.log("私は買い物に行きます")
}
else
{
    Debug.log("私は家でゲームをします")
}
```
プログラムコードは基本的に上の行から順番に実行されます。今回のコードでは１つ目の条件を満たさないので、1つ目の条件で記述している処理は実行されません。2つ目の条件の判定に移ります。2つ目の判定では条件を満たすので、2つ目の処理が実行されます。2つ目の条件が満たされたので、3つ目以降の条件に関しては判定処理は行わずにスキップされます。
<br>

条件の分岐は何個でも設定可能です。上から順に判定処理を行い、条件を満たした時に中括弧の中に記述した処理を実行します。
```csharp
if (条件1)
{
    //条件1が true だった時の処理
}
else if (条件2)
{
    //条件2が true だった時の処理
}
else if (条件3)
{
    //条件3が true だった時の処理
}
else
{
    //どの条件も満たさない場合の処理
}
```
<br>

条件式が1行しかなく、elseの条件も必要ない場合は、中括弧を省略した書き方もできます。
```csharp
int money = 12000;

if (money >= 10000) Debug.log("私は温泉旅行に行きます")
```
開発規模が大きくなるほどシンプルで行数の少ないコードでシステムを動かすスキルが求められるようになってきますので、このような記述方法も徐々に使いこなせるようになると良いかと思います。
Amazon ECRでDockerイメージのプッシュ

Amazon ECR(Elastic Container Registry)のプライベートリポジトリにDockerイメージをプッシュするまでの手順を記録します。手順はAWS CLIのインストール、AWSアカウントの作成、IAM Identity Centerでユーザーの追加・設定、AWS SSOの設定、Amazon ECRでDockerイメージのプッシュに分けます。無料枠での利用を想定しています。

# Amazon ECRとは
>Dockerコンテナイメージを保存、共有、デプロイできるDockerコンテナレジストリ

# 環境
- CentOS7
- Dockerはインストール済み

# 手順
1\. [AWS CLIのインストール](https://qiita.com/aomrikti/private/0908000f4aa58098c761)　
2\. [AWSアカウントの作成](https://qiita.com/aomrikti/private/0e63569eeade661cf7b6)
3\. [IAM Identity Centerでユーザーの追加・設定](https://qiita.com/aomrikti/private/389672d4900989d447ad)
4\. [AWS SSOの設定](https://qiita.com/aomrikti/private/0ebb0bda84d631cd6ea1)
5\. [Amazon ECRでDockerイメージのプッシュ](https://qiita.com/aomrikti/private/241fad7126657439491e) （本記事）

# SSO認証でログイン
- 下記コマンドでSSO認証用に作成したプロフィールでログインする
- デフォルトのブラウザに表示されたコードとターミナルに出力されたコードが同じであることを確認し、「Confirm and continue」をクリックする
```
$ aws sso login --profile <プロフィール名>
```

# Amazon ECRレジストリにログイン
- 下記コマンドを実行し、ログインする
    - `get-login-password`で認証トークンを出力する
    - `docker login` コマンドの `--password-stdin` オプションでトークンを取得する
    - `--username` オプションは `AWS` を指定する
```
$ aws ecr get-login-password --region <リージョン> --profile <プロフィール名> | docker login --username AWS --password-stdin <awsのアカウントID>.dkr.ecr.ap-<リージョン>.amazonaws.com
```
- 下記のメッセージが出力さればログインできたことが確認できる
```
Login Succeeded
```
# リポジトリの作成
- 下記コマンドを実行し、Dockerイメージをプッシュするリポジトリを作成する
```
$ aws ecr create-repository --repository-name <任意のリポジトリ名> --region <リージョン名> --profile <プロフィール名>
```
# タグ付け
- 下記コマンドを実行し、プッシュするイメージの内容を確認する
```
$ docker images
...
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
httpd         latest    bfe6700e6779   2 months ago    147MB
```

- 上記で確認した `httpd:latest` のイメージを、 `<awsアカウントID>.dkr.ecr.<リージョン名>.amzonaws.com/<設定したリポジトリ名>` とタグ付けする
```
$ docker tag httpd:latest <awsアカウントID>.dkr.ecr.<リージョン>.amazonaws.com/<作成したリポジトリ名>
```
# イメージのプッシュ
- 下記コマンドを実行し、作成したリポジトリにイメージをプッシュする
```
$ docker push <awsアカウントID>.dkr.ecr.<リージョン名>.amazonaws.com/<設定したリポジトリ名>
```
- 下記の出力が確認できればイメージのプッシュが完了したことが確認できる
```
The push refers to a repository [<awsアカウントID>.dkr.ecr.<region>.amazonaws.com/<設定したリポジトリ名>] (len: 1)
e9ae3c220b23: Pushed
a6785352b25c: Pushed
0998bf8fb9e9: Pushed
0a85502c06c9: Pushed
latest: digest: sha256:215d7e4121b30157d8839e81c4e0912606fca105775bb0636EXAMPLE size: 6774
```

# 参考
- AWS ECRの操作について
https://docs.aws.amazon.com/AmazonECR/latest/userguide/getting-started-cli.html

- 「get-login-password」について
https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ecr-public/get-login-password.html


- 「docker login」について
https://docs.docker.jp/engine/reference/commandline/login.html#id20

- 「docker tag」について
https://docs.docker.jp/engine/reference/commandline/tag.html
AWSアカウントの作成

AWS ECR(Elastic Container Registry)のプライベートリポジトリにDockerイメージをプッシュするまでの手順を記録します。手順はAWS CLIのインストール、AWSアカウントの作成、IAM Identity Centerでユーザーの追加・設定、AWS SSOの設定、Amazon ECRでDockerイメージのプッシュに分けます。無料枠での利用を想定しています。

# 環境
- CentOS7
- Dockerはインストール済み

# 手順
1\. [AWS CLIのインストール](https://qiita.com/aomrikti/private/0908000f4aa58098c761)
2\. [AWSアカウントの作成](https://qiita.com/aomrikti/private/0e63569eeade661cf7b6)（本記事）
3\. [IAM Identity Centerでユーザーの追加・設定](https://qiita.com/aomrikti/private/389672d4900989d447ad)
4\. [AWS SSOの設定](https://qiita.com/aomrikti/private/0ebb0bda84d631cd6ea1)
5\. [Amazon ECRでDockerイメージのプッシュ](https://qiita.com/aomrikti/private/241fad7126657439491e)

# AWS アカウントの作成
####  [AWS（アマゾンウェブサービス）](https://aws.amazon.com/jp/)にアクセスし、「無料で開始する」をクリックする
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570127/0a109792-b3f7-6b52-076a-9784408b2158.png" width=100%>

#### Eメールアドレス・パスワードを登録
- 「ルートユーザーのEメールアドレス」と「新しいアカウント名」を入力し、「Eメールアドレスを確認」をクリックする
- 画面に従い、メールアドレスを認証後、パスワードを設定する
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570127/e7d5a75c-6bf8-065d-a1fb-408bb6099fd0.png" width=100%>

#### 連絡先情報を登録
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570127/d075dd96-b135-3d35-ee20-ed37123e59c4.png" width=100%>

#### 請求情報を登録
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570127/c5a5dfd1-a002-b5eb-5058-8816fddad2f8.png" width=100%>


#### 本人確認
- 携帯電話番号を「テキストメッセージ」か「音声通話」のいずれかで認証する
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570127/42db6553-4c32-9bba-054e-d22b60cec212.png" width=100%>

#### サポートプランの選択
- 「ベーシックサポート-無料」を選択し、「サインアップを完了」をクリックする
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570127/92df0015-9c14-1e0d-0fae-6b8d8d062cd3.png" width=100%>


####  AWS マネジメントコンソールを開く
- 下記画面の「AWSマネジメントコンソールにお進みください」をクリックする
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570127/b433506e-aa9b-c846-12b8-62b04c528a5a.png" width=100%>

- ルートユーザーでサインインする
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570127/ed5756b4-dd57-254c-146e-60c78fd25da4.png" width=100%>

- サインインすると下記の「コンソールのホーム」が開く
- 画面右上のリージョンを変更する
    - 東京であれば「ap-northeast-1(東京)」に設定する
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570127/1a989060-8a65-4baf-b4f4-9891c19ac6b3.png" width=100%>



# 参考
- AWS アカウントの作成について
https://aws.amazon.com/jp/register-flow/
AtCoder 勉強記録 [C++] その1

AtCoderで入茶を目指して勉強しています。
勉強を継続するために投稿を始めました。
もともとアカウントを作成していましたが、今年の4月から本格的に勉強を始めました。
[私のアカウント](https://atcoder.jp/users/nanami_brother)
[解いた問題](https://kenkoooo.com/atcoder/#/table/nanami_brother)
# 本日解いた問題
## B - Counting Arrays
[B - Counting Arrays](https://atcoder.jp/contests/abc226/tasks/abc226_b)
解答
```c++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using vec = vector<ll>;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)

int main() {
  ll n,l;
  cin >> n;
  set<vector<ll>> a; 
  rep(i,n){
    cin >> l;
    vector<ll> tmp(l);
    rep(j,l){
      cin >> tmp[j];
    }
    a.insert(tmp);
  }
  cout << a.size() << endl;
}
```
### 解法
要素が追加されるたびにすべての要素と比較をすると計算量がオーバーしてしまうためsetを用いて解く。
## B - KEYENCE building
[B - KEYENCE building](https://atcoder.jp/contests/abc227/tasks/abc227_b)
解答
```c++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using vec = vector<ll>;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)

int main() {
  ll n;
  cin >> n;
  vec s(n);
  rep(i,n) cin >> s[i];
  ll res = 0;
  rep(i,n) {
    bool jud = false;
    for(int j=1;j<150;j++) {
      for(int k=1;k<150;k++){
        if(s[i] == 4*j*k + 3*j + 3*k) jud = true;
      }
    }
    if(!jud) res++;
  }
  cout << res << endl;
}
```
### 解法
S=4ab+3a+3bとなるaとbを全探索し、それをn回行う。
## B - Takahashi's Secret
[B - Takahashi's Secret](https://atcoder.jp/contests/abc228/tasks/abc228_b)
解答
```c++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using vec = vector<ll>;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)

int main() {
  ll n, x;
  cin >> n >> x;
  vec a(n);
  for(int i=1;i<n+1;i++) cin >> a[i];
  set<ll> res;
  for(int i=1;i<n+1;i++){
    res.insert(x);
    x = a[x];
  }
  cout << res.size() << endl;
}
```
### 解法
setを用い、秘密を知っている人間を重複しないように数える。
公式の解法を見たところ重複したタイミングで終了していたがこちらでもACできた。
## C - Long Sequence
[C - Long Sequence](https://atcoder.jp/contests/abc220/tasks/abc220_c)
```c++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using vec = vector<ll>;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)

int main() {
  ll n, x;
  cin >> n;
  vec a(n);
  ll sum = 0;
  rep(i,n) {
    cin >> a[i];
    sum += a[i];
  }
  cin >> x;
  ll k = x / sum;
  sum *= k;
  k *= n;
  for(int i=0;i<n;i++){
    sum += a[i];
    k++;
    if(sum > x) break;
  }
  cout << k << endl;
}
```
### 解法
まず、Aの総和sumを求めて、xをsumで割る。割ったものにnを乗算して、そこまでの項を求める。その後Aを1つずつ加算してxより大きくなったタイミングで終了する。
PythonでPDFファイルを圧縮する

PDFファイルを圧縮することにより、我々は効果的にファイルサイズを小さくし、ファイル転送効率を向上させるだけでなく、コンピュータの記憶領域を節約することはできません。PDFファイルを圧縮するためにいくつかの専門的なツールの使用に加えて、我々はまた、自動的にPDFファイルのバッチ処理を達成するために操作を実行するためにPythonを使用することができます。

この記事では、我々は**PythonでPDFファイルを圧縮する**ためのシンプルかつ効果的な方法を共有します。

## PDFファイルを圧縮するPythonライブラリ
ここでは、サードパーティライブラリ – [**Spire.PDF for Python**](https://www.e-iceblue.com/Introduce/pdf-for-python.html)が必要です。これは、PythonアプリケーションでPDFドキュメントを作成、読み込み、操作するための強力なAPIです。

[PyPI](https://pypi.org/project/spire.pdf/)からSpire.PDF for Pythonをインストールできます。pipコマンド:
`pip install Spire.PDF`

## PythonでPDFファイルのサイズを小さくする方法
[Spire.PDF for Python](https://www.e-iceblue.com/Introduce/pdf-for-python.html)ライブラリは、画質を調整し、埋め込まれたフォントを圧縮することで、PDFファイルを圧縮することができます。主な手順と方法は以下の通りです：
1.	必要なモジュールをインポートする
2.	`PdfCompressor` オブジェクトを作成し、圧縮するPDFファイルを渡します。
3.	圧縮オプション（**`OptimizationOptions`**）を取得し、フォントと画像の圧縮を設定します。
**フォントの圧縮**
•	フォント圧縮を有効にします： `SetIsCompressFonts(True)` メソッド
•	または非埋込みフォントを有効にします： `SetIsUnembedFonts(True)`メソッド。
**画像の圧縮**
•	画像品質を設定する： `SetImageQuality(imageQuality: ImageQuality)` メソッド。High/Medium/Lowの画質設定に対応。
•	画像のサイズを変更する： `SetResizeImages(True)` メソッド。
•	画像圧縮を有効にする： `SetIsCompressImage(True)` メソッド。
4.	`CompressToFile()`メ ソ ッ ド を呼び出 し て PDF フ ァ イ ルを圧縮 し 、 それを指定 さ れたパスに保存 し ます。


>PDFを圧縮するサンプルPythonコード：
```python
from spire.pdf.common import *
from spire.pdf import *

# PdfCompressorオブジェクトを作成し、PDFファイルを渡す
compressor = PdfCompressor("input.pdf")

# OptimizationOptionsオブジェクトを取得する
options = compressor.OptimizationOptions

# 圧縮フォント
options.SetIsCompressFonts(True)
# フォントの埋め込み解除を有効にする
# options.SetIsUnembedFonts(True)

# 画質を設定する
options.SetImageQuality(ImageQuality.Medium)
# リサイズ画像
options.SetResizeImages(True)
# 画像を圧縮する
options.SetIsCompressImage(True)

# PDFファイルを圧縮して保存する
compressor.CompressToFile("CompressPDF.pdf")
```

**結論**：Spire.PDF for Pythonライブラリは、私たちの特定のニーズを満たすために圧縮オプションを調整する柔軟性を与えてくれます。圧縮率を最大にするには、画質をLowに設定します。ご質問がある場合は、[**フォーラム**](https://www.e-iceblue.com/forum/spire-pdf-f7.html?sid=9ec2066395f8b9cb605fd089c2bb1a32)をご覧ください。

完全なテストのためのトライアルライセンスをリクエストする:

https://www.e-iceblue.com/TemLicense.html
効率的に仕様を理解する方法

### 効率的に仕様を理解する方法をまとめてみました。（ChatGPTが…）

1. 目的を明確にする：
    - 仕様書を読む前に、プロジェクトの全体像と最終目標を理解することが重要です。これにより、細部に入る前に大まかなコンテキストがつかめます。
1. 段階的に読み解く：
    - 仕様書を一度に全て理解しようとするのではなく、段階的に読み進めると効果的です。まずは概要や目次に目を通し、次に重要なセクションを順番に読んでいきます。
1. メモを取る：
    - 読みながら重要なポイントや疑問点をメモにまとめると、後で振り返る際に便利です。図やフローチャートを描くことで、プロセスや構造が視覚的に理解しやすくなります。
1. キーワードを押さえる：
    - 仕様書に登場するキーワードや専門用語をリストアップし、それぞれの定義を理解しておくと、読み進める際の理解が深まります。
1. 質問をリスト化する：
    - 仕様書を読んでいて不明点や疑問点が出てきたら、それをリストアップしておきます。その後、同僚や上司、クライアントに質問することで理解を深めます。
1. ディスカッションを行う：
    - チームメンバーと仕様についてディスカッションすることは、理解を深めるために非常に有効です。異なる視点からの意見や解釈を聞くことで、自分の理解を補強できます。
1. 実例を確認する：
    - 仕様書に関連する既存のシステムや類似プロジェクトの実例を確認することで、具体的なイメージをつかむことができます。動作しているシステムを見ることは、仕様の理解を助けます。
1. 反復と復習：
    - 仕様書を何度も読み返すことで、理解が深まります。最初は理解できなかった部分も、時間が経つと理解しやすくなることがあります。
1. ツールを活用する：
    - UMLなどのモデリングツールや、仕様管理ツールを使用することで、複雑な仕様を視覚化・整理することができます。


これらの方法を組み合わせて活用することで、仕様の理解を効率的に進めることができます。