OmniAICreator/Qiita-1.07M huggingface dataset:
M5NanoC6の変数型のサイズを計って遊ぶ。(知っていると思うけど)


x 過去ログを見ょ!!!
x 「シリアルモニタ」は、なんどか開き直す
x 3.1.1


目的
主な型のサイズを見る
sizeof関数を使って遊ぶ


結果
----


![o_coq784.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/a73e7b47-7291-3109-e8ab-25c6d56066b6.jpeg)



プログラム
----



```c++


void setup() {
  // put your setup code here, to run once:

  //シリアルの初期化
  Serial.begin(9600);
  Serial.println("");
  //シリアルの待ちが0.5*9
  for (int i = 0; i < 9; i++) {
    delay(500);  //接続待ち
    Serial.print(".");
  }  //for
  Serial.println("");

  Serial.printf("char -> (%d)\n",sizeof(char));
  Serial.printf("unsigned char -> (%d)\n",sizeof(unsigned char));
  Serial.printf("char * -> (%d)\n",sizeof(char *));
  Serial.printf("int -> (%d)\n",sizeof(int));
  Serial.printf("long -> (%d)\n",sizeof(long));
  Serial.printf("long long -> (%d)\n",sizeof(long long));
  Serial.printf("float -> (%d)\n",sizeof(float));
  Serial.printf("double -> (%d)\n",sizeof(double));
  Serial.println("");
}

void loop() {
  // put your main code here, to run repeatedly:

}



```
【Ruby】ログイン情報が必要なサイトでスクレイピングをしてみる

この記事では、RubyとSeleniumによりスクレイピングについて説明しています。

前提条件
- MacBook Air M2
- Ruby Version 3.1.3

## Selenium WebDriverの準備
今回のスクレイピングでは、Selenium WebDriverを使用していきます。
https://www.selenium.dev/documentation/webdriver/

Selenium WebDriverを使えば、RubyやPythonなどのプログラミング言語を通じてブラウザの自動操作をすることが可能です。

```Shell.sh
$ gem install selenium-webdriver
```

## ソースコード

```scrape.rb
require "selenium-webdriver"
require 'net/http'

# Chromeブラウザを起動
driver = Selenium::WebDriver.for :chrome

# スクレイピングでデータを取得したいサイトに移動
driver.navigate.to "https://xxxx.com"

# `id`と`password`いうnameを持つ要素を取得
id = driver.find_element(:name, 'id')
password = driver.find_element(:name, 'password')

# `Hello WebDriver!`という文字を、上記で取得したinput要素に入力
id.send_keys "xxx@xxx.com"
password.send_keys "password"

# ログインボタンを取得してClickする。
login_button = driver.find_element(:id, 'log_in')
login_button.click

sleep(3)

h3_element = driver.find_elements(:css, 'h3')

puts h3_element

sleep(3)

# ブラウザを終了させる
driver.quit
```

### ソースコードの説明

#### Cookieの有効化（必要に応じて）
Chromeを起動し、Googleに移動することを確認します。

次にログイン画面に遷移します。
サイトによっては、ここでCookieを有効にする必要があるので、有効にしていきます。

```scrape.rb
options.add_argument('--enable-automation')
options.add_argument('--start-maximized')
options.add_preference(:general, 'default_content_setting_values.cookies', 1)
```

#### ログインIDとパスワードの入力
続いてログインIDとパスワードを入力していきます。

```scrape.rb
user_id = driver.find_element(:name, 'id')
password = driver.find_element(:name, 'password')
```
driverはSelenium WebDriverのインスタンスで、ブラウザを自動操作するために使用されます。

find_element メソッドによって、取得したページのIDとパスワードの要素を取得していきます。
ここはサイトによってHTMLの要素は異なるのでサイトに合わせて修正してください。

#### ログインボタンの入力・押下
```scrape.rb
login_button = driver.find_element(:id, 'log_in')
login_button.click
```
続いてログインボタンの要素を取得して押下します。

#### ログイン後の動作
```scrape.rb
sleep(3)

# 表示されたページの要素をコンソールに出力
h3_element = driver.find_elements(:css, 'h3')

puts h3_element

sleep(3)

# ブラウザを終了させる
driver.quit
```
ログイン後のページの要素を確実に取得するため、3秒間待機させます。

取得できたことが確認できたら、driver.find_elementsで要素を取得しコンソールに表示させます。

サイトによってはスクレイピング防止のため、短期間に高頻度リクエストをするIPを制限する場合があります。

この制限を回避するために
- IPアドレスを複数用意する
- 繰り返しスクレイピングする場合には、期間を空ける
といった対策をしていけるとよいでしょう。

詳しくは下記の記事を読むと詳しく理解できると思うので参考にしてください。
https://qiita.com/kobayashi-masayuki/items/0640a3dd5b12205f3b52
Javaで「テキストフィールドに入力されたテキストを取得する」の動作を確認してみた。

# 概要

Javaで「テキストフィールドに入力されたテキストを取得する」の動作を確認してみました。
以下のページを参考にしました。

https://www.javadrive.jp/tutorial/jtextfield/index9.html

# 実装

以下のファイルを作成しました。

```JSample9_1.java
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JButton;
import java.awt.Container;
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

class JSample9_1 extends JFrame implements ActionListener{
  JLabel label;
  JTextField text;

  public static void main(String args[]){
    JSample9_1 frame = new JSample9_1("MyTitle");
    frame.setVisible(true);
  }

  JSample9_1(String title){
    setTitle(title);
    setBounds(100, 100, 600, 400);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    text = new JTextField(20);
    JButton button = new JButton("Get");
    button.addActionListener(this);

    JPanel p = new JPanel();
    p.add(text);
    p.add(button);

    label = new JLabel();

    Container contentPane = getContentPane();
    contentPane.add(p, BorderLayout.CENTER);
    contentPane.add(label, BorderLayout.SOUTH);
  }

  public void actionPerformed(ActionEvent e){
    label.setText(text.getText());
  }
}
```

以下のコマンドを実行しました。

```
$ javac JSample9_1.java 
$ java JSample9_1
```

# まとめ

何かの役に立てばと。
PHPでvscodeのformat on saveが効かない

## tl;dv
これで動いた
```json
{
  "editor.formatOnSave": true,
  "[php]": {
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "bmewburn.vscode-intelephense-client"
  }
}
```

## Try
1. まずはJSやTSでやる時と同じようにPrettier拡張機能入れる
1. `cmd + ,`でsetting開いて、UserにもWorkspaceにも`format on save`にcheckがついてることを確認
1. 「え、なんで動かないん？」
1. `PHP Intellisense`が入ってなかった。でもまだ動かない
1. `Default Formatter`で`PHP Intellisense`を選択
1. 動いた〜〜！

`settings.json`でメンバーみんなの環境統一した方がみんな苦労しないので、
```
{
  "editor.formatOnSave": true,
  "[php]": {
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "bmewburn.vscode-intelephense-client"
  }
}

```
こんな感じで書いといた
バグとおさらばするのに有効な手段の一つ

# 3層アーキテクチャに関して

# はじめに
バックエンド開発を開始するにあたって3層アーキテクチャなるものに出会ったのでこれの特徴と導入するメリットについて記述しておこうと思う。

# 目次
<!-- タイトルとアンカー名を編集 -->
1. 3層アーキテクチャとは？
1. 導入するメリット

# 3層アーキテクチャとは？
## 概要
・バックエンドで情報を処理する際に**その情報の処理形態を3層に分割している**こと。
それぞれの層の名称は各人によってバラバラであるが、基本的な役割としては同じで以下のようである。

➀フロントエンドとの通信を行う層
➁情報を処理するロジックを記述する層
③DBに情報を保存したり抽出したりする層

ここでは①の層を「controller層」と呼び、②の層を「service層」と呼び、③の層を「repository層」と呼ぶことにする。

データ処理の流れとしては以下の画像のようになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3758326/2bf0c85a-dab3-ec6d-3776-9ea8da6737e8.png)

## 具体例
基本的に私の場合はInterfaceを使用して依存関係を構築する。
具体的には例えば下記にIUserRepositoryというインターフェースがあるとする。ここにはuser_repositoryで使用する関数などが搭載されている。
```go:user_repository.go
type IUserRepository interface {
	FindByID(id uint) (*models.User, error)
	UpdateUser(user *models.User) error
}
```

このインターフェースをrepository層より一つ上のservice層でも使いたいとする。
そこでservice層でコンストラクタを用いてIUserRepositoryを受け継いだ新たなインターフェースである**IUserService**というインターフェースを作成する。

```go:user_service.go
func NewUserService(repository repositories.IUserRepository) IUserService {
	return &UserService{repository:  repository}
}

```

そしてこのrepository層のインターフェースを受け継いで誕生したservice層のインターフェースにもservice層で使用する関数を投入していってあげる。
```go:user_service.go
type IPortfolioService interface {
	CreatePost(ctx *gin.Context, userID uint) error
	GetPostByID(id uint) (*models.Post, error)
	GetPostsByUserID(userID uint) ([]models.Post, error)
	GetAllPosts() ([]models.Post, error)
}
```
今の一連の流れをcontroller層でも行うことでcontroller層でもservice層やrepository層の機能を使用することができるようになる。

# 導入するメリット
### 責務の分離
- **コントローラ層**は「入出力（UIやリクエスト・レスポンス）の受け渡し」に特化
- **サービス層**は「ビジネスロジックの実装とサービスの振る舞い」に特化
- **リポジトリ層**は「DB操作や外部APIへのアクセスなどのデータアクセス」に特化

それぞれの層の役割が明確になることで、コードベースが整理され、可読性・保守性の向上につながる
将来的に要件変更や新機能追加があった場合でも、影響範囲を特定しやすくなり、修正が容易になる。

### テストのしやすさ
3層に分かれているため、**各レイヤーごとに単体テストがしやすい**のが大きなメリット

- サービス層をテストするときは、リポジトリ層をモック化してビジネスロジックだけに注目できる。
- リポジトリ層のテストでは、DB接続部分のみを検証してデータ操作の正当性を確認できる。
結合テストやエンドツーエンドテストに進む前に、層ごとに不具合を検出できるため開発効率が向上する。

### 拡張性・スケーラビリティ
将来的にシステムを**水平分割・垂直分割**する際に、層構造を採用していると柔軟に拡張できる。

- コントローラ層は負荷分散するWebサーバ群でスケールアウト
- DBやリポジトリ層はクラスター構成で耐障害性とパフォーマンスを向上
- サービス層はコンテナ化やマイクロサービス化で独立運用
といったアプローチを取りやすくなる。

### 保守性
サービス層・リポジトリ層など、どこを編集すればよいかが明確なため、新しいメンバーがプロジェクトに参加した際でも把握がしやすく、**チーム開発での混乱を防げる点**も大きなメリット

# さいごに
- このようなものはほかにも様々なものがあるらしいので、なるべく多く知り、どんな開発にどんな手段が有効なのかを知りたい
Pythonで数列に含まれる一意の要素の合計を算出する方法

忘れないためのメモ書きです。
最近、整数からなるsetの和を計算する際に、普通にsum関数が使用できることを知りました。
やはり公式ドキュメントはもっとしっかり読み込むべきですね。
簡易的な検証用のコードを残しておきます。

https://gist.github.com/Taka0007/2d0da14b1b8eff722d453f2988df9242



具体的な使用例としては下記のような状況でしょうか。
> 重複を許して数列 $A$ が与えられる。
> 数列$A$に含まれるユニークな要素の和を求めよ

入力例
```
10
1 2 3 4 5 6 7 7 7 7 
```

出力例
```
28
```
HonoX入門

```text
$ mkdir honox_test
$ cd honox_test
$ npm install hono honox
$ npm create hono@latest .
create-hono version 0.14.3
✔ Using target directory … .
? Which template do you want to use?
  nextjs
  nodejs
  vercel
❯ x-basic
  aws-lambda
  bun
  cloudflare-pages
? Which template do you want to use? x-basic
? Directory not empty. Continue? (y/N) y
? Directory not empty. Continue? yes
? Do you want to install project dependencies? (Y/n) 
? Do you want to install project dependencies? yes
? Which package manager do you want to use? (Use arrow keys)
❯ npm
? Which package manager do you want to use? npm
$ npm run dev
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/330873/bbbbf296-aae0-7257-c161-dcb7b7d23812.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/330873/81e56b2c-e398-d6de-5390-ff1b05fa7516.png)



index.tsxは以下のようになっているため。

```app/routes/index.tsx
import { css } from 'hono/css'
import { createRoute } from 'honox/factory'
import Counter from '../islands/counter'

const className = css`
  font-family: sans-serif;
`

export default createRoute((c) => {
  const name = c.req.query('name') ?? 'Hono'
  return c.render(
    <div class={className}>
      <h1>Hello, {name}!</h1>
      <Counter />
    </div>,
    { title: name }
  )
})
```
macでselenium serverを起動



# インストール
```bash
brew install selenium-server-standalone
brew install chromedriver
```

# 起動
```bash
/opt/homebrew/opt/selenium-server/bin/selenium-server standalone --port 4444
```

# おまけ
`Apple could not verify “chromedriver” is free of malware that may harm your Mac or compromise your privacy.`　とか出てきたら下記を実行しておけば出てこなくなる

```bash
xattr -d com.apple.quarantine `which chromedriver`
```

# chromiumを使いたい場合

chromeとは独立させて利用したい場合、chromiumを導入します

```bash
brew install chromium --no-quarantine
```

同じ方法で起動すると、chromiumが参照されてました。よくわかりませんが動いたのでよｓ
Japanglish Tech Talk #4 プレゼンテーション紹介

こんにちは！Tamtamです。

Naruと一緒に「Japanglish Tech」イベントを主催しています。

# Japanglish Tech Talkとは？

東京で開催されるエンジニア向けの英語オンリーのLTイベントです！

日本では、ほとんどの会話が日本語で行われるため、英語を話す機会が少ないのが現状です。

そこで、東京でLTイベントを通じて気軽に英語を話せる機会を提供するために、このイベントを企画しています。

# Japanglish Tech Talk #4 プレゼンテーション
2024年1月24日にJapanglish Tech Talkを開催しました。

プレゼンテーションの内容を簡単に共有させていただきます。

- [Japanglish Tech Talk#4](https://japanglish.connpass.com/event/335212/)
- [Japanglish Youtube](https://www.youtube.com/watch?v=t7ORiehVb1E)

スポンサーの皆様、ありがとうございます！
本イベントの開催にあたり、Ichizoku様とTimee様にご協力いただき、心より感謝申し上げます。
改めて、ありがとうございました！🙏

Ichizokuブログ: https://ichizoku.io/category/sentry/

Timeeブログ: https://techplay.jp/community/timee/blog

### The world I saw when I changed my job from front-end engineer to PM

- [youtube](https://youtu.be/t7ORiehVb1E?si=xaQpxYfmLURsnevt&t=651)
- Speaker: Natty

フロントエンドエンジニアからPMに転向後、コーディングなどの目に見える成果物だけでなく、問題の未然防止や進捗管理といった目に見えにくい要素でも評価される立場に変わりました。

開発タスクの一部だけでなく、ステークホルダーやチーム全体を俯瞰しながら、優先順位の判断や意思決定を素早く行う必要があります。

結果として、様々なステークホルダーに必要な情報や視点を理解し、より広い視野でプロジェクトを進める能力を身につけました。

### The most efficient way of growing your English vocabulary and the importance of English proficiency in global career

- [youtube](https://youtu.be/t7ORiehVb1E?si=SVj4e9LtopZhoM7S&t=1284)
- Speaker: Musa

英語の語彙学習において、単語リストによる効率的な暗記と、多読・多聴による楽しい入力の両方が重要です。

しかし、どちらの学習方法にも長所と短所があり、効果的な継続学習には復習や未知語の検索が不可欠です。

そこで、Vocabulary AIアプリなどの学習ツールを活用することで、リアルタイムの単語検索や発音確認が可能になり、より効率的で楽しい語彙力向上が実現できます。

### Beyond Coding: The Power of Diverse Paths

- [youtube](https://youtu.be/4GKaSi9t1Uc?si=K_6LQug7sq4rKEmQ&t=489)
- Speaker: Mutsumix

教育業界からIT業界に転向し、最初はドキュメンテーションやソフトウェアテストの基礎スキルを構築しながらJavaやクラウド技術を学び、最終的に執行役員になった事例を通じて、「年齢や前職に関係なく、学び続ければ道は開ける」ことを示しました。

海外での日本語教科書販売や多様な職務経験を活かし、顧客ニーズを理解し技術力と要件のバランスを取る能力が成功の鍵であったと強調しました。

### What I learned from 3years experience as a software engineer

- [youtube](https://youtu.be/4GKaSi9t1Uc?si=t9GCRVCA1b4m4GQb&t=888)
- Speaker: yamaga

ソフトウェアエンジニアは単なるコードを書く人ではなく、顧客の課題を理解し解決策を生み出すドメインエキスパートであるべきだと強調しました。

新しい技術を理解するためには基礎知識が不可欠であると説明しました。

完璧なエンジニアは存在しないこと、特にAIが進化する時代において、人間ならではの価値提供を徐々に見出し、キャリアを構築することが重要だと強調しました。

# 次回イベントも開催予定です！

次回のミートアップは4月に開催予定です！XD

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/026hjj6qh2myedn578fn.jpg)
[Webゲーム開発入門日記①]

初めまして。このアカウントではWebゲーム作成を目標にhtml,Javascript,cssのコードをひらすら乗せ続けていきます。
というのも私はWeb開発初心者なので成長のためにアウトプットしようと思い日記のような感じで書いていきます。
## ①早速コードを書いてみよう
まずはよくみる「Hello!World」と表示させるhtmlファイルから
どこのフォルダ上でもいいのでtextファイルを作成し、拡張子を.htmlにしてください
保存出来たら中にこのコードを打ち込んでください。
```
<html>
    <head>
        <title>日記①</title>
    </head>
    <body>
        Hello!World
    </body>
</html>
```
これを保存し、ダブルクリックしてみるとブラウザでHello!Worldと書かれたサイトが開かれます

***解説***
---
`<html>` webサイトの情報はすべてこの中に書き込みます
`<head>` webサイトの基本情報を書きます(名前やタイトルなど)
`<title>`headタグの中に書きます。サイトの名前ですね。
`<body>` webサイトの中身を書きます(ボタンやテキストボックスなど)

## ➁htmlの基本要素に触れよう
今度はhtmlの基本要素であるbuttonやdivを使用してみましょう。
先ほどのコードをこれに変更しましょう。
```
<html>
    <head>
        <title>日記①</title>
    </head>
    <body>
        <input type="text" id="text">
        <input type="button" id="button" value="ボタン" onClick="onClick">
        <div id="textcontainer"></div>
    </body>
</html>
```
変更してページをリロードしてみると
テキストボックスとボタンが表示されていると思います。

## ③Javascriptを使ってみよう
このままでは押しても何も起きないのでjavascriptを用いて処理を追加してみましょう。
Javascriptは外部ファイルとして読み込む方が分かりやすいですが、今回はhtml内に直接書きます。
コードを以下のように変更しましょう。
```
<html>
    <head>
        <title>日記①</title>
    </head>
    <body>
        <input type="text" id="text">
        <input type="button" id="button" value="ボタン" onClick="onClick()">
        <div id="textcontainer"></div>
        <script>
        function onClick(){
            const text = document.getElementById("text").value
            const textcontainer = document.getElementById('textcontainer');
            textcontainer.appendChild(document.createTextNode(text));
            textcontainer.appendChild(document.createElement('br'));
        }    
        </script>
    </body>
</html>
```
このコードを打ってページをリロードするとこんな感じで入力してボタンを押したら、入力した文字が下に表示されていくと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3994566/47d491ec-fd49-ffa7-da63-20296182145a.png)

***解説***
---
`<input>`クライアントから情報を受け取ることができます。
typeはbutton,text,number,e-mail,checkboxなど沢山あります。

`<div>`複数の要素をグループ化する。Javascriptを用いて要素の追加・削除が可能
今回のように文字列を追加したり、写真を追加することもできます。

`<script>`Javascriptのコードをこの中に書きます。`<script src="ファイル名">`とすることで外部のJavascriptフォルダを読み込むことができる

javascriptについての解説は次回にしたいと思います。
とりあえず今回はhtmlの実行方法、主な書き方だけ知ってもらえればいいと思います。

# 最後に
主はまだ中学三年生のJavascript初心者です。おそらく記事を読んでる方の方がhtml,javascriptについて詳しい知識を持っているでしょう。このサイトは主の勉強のためでもあるので、間違いやアドバイスがあれば何でも言ってくれると嬉しいです。それではまた次回!!
VSCode・WSL・Windows環境で`Solargraph gem not found`が出たときの対処方法

## はじめに
ちょっとした理由があり、Rubyの学習を再度始めました。
いままで使えないまま放置していたSolargraphをちゃんと使えるようにしようと思い、対処したので備忘録として残しておきます

### 環境
- VSCode
- WSL2(Ubuntu)
- Windows11

## やりたいこと

- solargraphを使いたい

### やったこと

- solargraphをVSCodeでインストール
- gemをUbuntuでインストール

## エラー内容

```bash
Solargraph gem not found. Run `gem install solargraph` or update your Gemfile
```

## 原因
  - VSCodeはWindows上に、gemはUbuntu上にあり、上手く読み込めていない

## 解決策
GitHubのQAを参照しながら対処しました。
最下部の参考欄にリンクを記載しています。
以下に該当箇所の和訳とちょっとした追記を記載します。

1. bashでwhich solargraphを実行し、パスをコピーする
    - 追記：このときUbuntu上でコマンドを実行する
2. solargraph.batファイルを作成し、以下のスニペットを貼り付ける。 {{SOLARGRAPH_PATH}}を1のパスに置き換えて使用する
    
    ```bash
    @echo off
    bash -c "{{SOLARGRAPH_PATH}} %*"
    
    // e.g.
    @echo off
    bash -c "~/.rbenv/shims/solargraph %*"
    ```
    
    - 重要：パスのbinをwrapperに置き換えてください。
      - (e.g. /home/mtamdev/.rvm/gems/ruby-2.6.0@dev-project/bin/solargraph %* -> /home/mtamdev/.rvm/gems/ruby-2.6.0@dev-project/wrappers/solargraph %*
        - 追記：自分の場合は以下パスが以下だったので、wrapperに置き換えずそのまま使いました
            - `~/.rbenv/shims/solargraph %*`
        - 追記：batファイルはVSCodeが存在するWindows上に作成します
3. solargraph.batファイルを保存し、その完全なファイルパスをコピーします。
4. VSCodeのsettings.jsonに以下を追加する。 パスはコピーしたbatのファイルパスに置き換えてください。
    
    ```yaml
    "solargraph.commandPath": "C:\\Max\\solargraph.bat"
    ```
    
5. VSCodeを再起動する

## なぜ上記の対処法で動くのか？
### そもそもbatファイルとは？
- Windows用のスクリプトファイル
- ファイルを実行すると、PowerShellなどで使うWindowsコマンドを上から順番に実行してくれる

### batの中で何をしているのか
- 前提
  - Solargraphを実行するにはLinuxコマンドが必要
  - VSCodeはWindows上にあるので、そのままだとLinuxコマンドが使えない
- やっていること
    - `bash -c` 
        - bashシェルを呼び出し、-c 以下の箇所をコマンドとして解釈させる
    - `"{{SOLARGRAPH_PATH}} %*"`
        - solargraphのパスを渡して実行させている

### 結論
VSCode上でbatファイルのパスを指定して実行->
batファイルの中身でbashシェルを呼び出してLinux上でsolargraphを実行->
動く

## 参考

- https://github.com/castwide/vscode-solargraph/issues/80
エラトステネスの篩の高速化 (7)

まさかの 5 年ぶりに更新です。今の所もうネタが無い。

- [エラトステネスの篩の高速化](https://qiita.com/peria/items/a4ff4ddb3336f7b81d50)
- [エラトステネスの篩の高速化 (2)](https://qiita.com/peria/items/54499b9ce9d5c1e93e5a)
- [エラトステネスの篩の高速化 (3)](https://qiita.com/peria/items/e0ab38f95d16a5f7cc58)
- [エラトステネスの篩の高速化 (4)](https://qiita.com/peria/items/7c62831ad9781c7c1577)
- [エラトステネスの篩の高速化 (5)](https://qiita.com/peria/items/ffc58b31470ad3297567)
- [エラトステネスの篩の高速化 (6)](https://qiita.com/peria/items/c1d8523342e81bb23375)
- エラトステネスの篩の高速化 (7) ←**今ココ**

# 計算対象
今回は大きな数での篩を高速化する話になります。また、実装はすっ飛ばしてしまったのでやる気が出るまでは理論だけ。

# パフォーマンス計測
パフォーマンス計測、といっても専用のツールを使ってどうこうする話ではありません。篩に使っている素数のうち、前回導入したループアンローリング部分をきちんと使っている(`while` 内を経由している)ものがどれだけあるでしょうか。ざっと見積もってみましょう。
素数 $p$ で篩っているとき、この展開したループを 1 回通過すると `p` バイト分 `j` が進むことになります。つまり区間篩のサイズ `kSegmentSize` が `p` より小さい場合(正確には `k=0` への調整部分もあるのでもう少し多いケースで)は折角アンロールしたループを一切通過しないことが分かります。
サンプルコード内で設定している `kSegmentSize` = 2MB = $2^{21}$ バイトのことを考えると $p \leq \sqrt{x}$ で篩うので

$$
y > (2^{21})^2 = 2^{42} \sim 4\times 10^{12}
$$

では多くの素数でアンロール部分を通過しなくなります。 (割合いとしては $x$ が大きい方が素数は少ないが、絶対数としては多くなるので最大素数の大きさが計算時間において支配的になる) 

これだけなら前回の準備が無駄になるだけなので構わないですが、もっと大きな、例えば $y>10^{13}$ での計算では区間篩の区間内で 1 ビットすら落としていかない素数が増えていくことになります。

# 対策
その様な素数のことを考えると、素数をイテレートしていく作業すら無駄になります。なので「今から篩う区間内で1ビットでも落とす可能性のある素数だけリスト」を作ります。
具体的には、素数の篩う位置を示す情報を持っていたリスト (コード内では `indecies`) について、それぞれの素数で次に篩い落とすビットの位置が

$$
30j+m_k
$$

となるわけですが、これは次の $\lfloor \frac{j}{S}\rfloor$ 番目の区間で篩う (0番目がこれからすぐ篩う区間) 、逆に言えば $\lfloor \frac{j}{S}\rfloor$ 個の区間では計算処理をしなくて良い、ということを意味しています。

このことを利用して、篩う位置リストを分割することを考えましょう。次に篩うビットのバイト位置が区間 $[Si, S(i+1))$ にある素数の位置情報を位置情報リストのリスト、プログラム的には 2 次元配列 `index_list` の `i` 番目 `index_list[i]` にストックしておきます。ただメモリも無限に使えるわけではないので、2 次元配列の 1 次元目を[リングバッファ](https://ja.wikipedia.org/wiki/%E3%83%AA%E3%83%B3%E3%82%B0%E3%83%90%E3%83%83%E3%83%95%E3%82%A1)の様に $N$ 個用意して `index_list[i % N]` に置いておくのが現実的かもしれません。

## 注意点
今回の記事では実装が含まれていないので注意点として書いておきます。

今回の提案ではこれまで定数リストのように使ってきた `indecies` 配列(配列の中身は変わっていくが、配列のサイズや各要素と対応する素数は変化しなかった)をキューの配列として使うことになります。そのため、一旦区間を篩い終わった素数が次に篩う区間に基づいてどのキューに追加していくかの計算を間違えないようにしましょう。特にキューの数を $N$ 個に固定した場合は「本当に今この区間を篩う素数なのか、あと$n$周した後で使う素数なのか」をきちんと区別できるようにしましょう。
GBA エミュレータのウェブ版で「ポケモンアンバウンド」を実行

今日は、皆さんにとても素敵なことをシェアしなければなりません！最近、私はウェブ上で GBA ゲームを遊ぶ方法を探し続けてきましたが、本当に GBA エミュレータのウェブ版のコードを見つけました！
コードを探す過程は簡単ではありませんでした。私は様々なフォーラムやサイトをめぐり、目が疲れるほど検索しました。たくさんのオープンソース版があり、今までに mgba と vba - m を試してみました。
テストしたゲーム ROM は「ポケモンアンバウンド」で、ゲームのデモサイトは：[http://unbound2.com/](http://unbound2.com/) です。
テストして実行したゲームは「ポケモンアンバウンド」です。mgba はかなりスムーズに動作しますが、バグがあり、コンピュータでのみ遊ぶことができます。スマホでゲームを実行すると画面が真っ暗になり、原因はまだわかりません。
仕方なく、vba - m に切り替えてゲームを実行することになりました。正直なところ、ウェブ上で GBA エミュレータを使ってポケモンを遊ぶことがこれほどスムーズにできるとは思いませんでした。コンピュータで遊ぶのとまったく変わりがありません。
ただ、ゲームのチートコードを実行するとエラーが発生します。今も改善中で、改善が完了したら、また皆さんにシェアします。
【0から始めるDB入門 Part1】SQL初級編　SELECT文

# はじめに

SQLabさんの問題を解いていきながら、ステップバイステップでSQLについて学んでいこうと思います。

https://sqlab.net/works/basic/

# 初級編

## SELECT文

今回はSQLの基礎中の基礎である**SELECT文**について学びます！

**基本形**

```sql
SELECT <出力要素> FROM <テーブル名>;
```

---

### 問題1

**書籍一覧を取得してください。**

<details><summary>解答・解説</summary>

```sql
SELECT * FROM books;
```

**解説**

`SELECT *`は、テーブル内のすべての列を選択することを意味します。`FROM books`で、データを取得するテーブルを指定しています。このクエリを実行すると、`books`テーブル内のすべての行とすべての列が取得され、書籍の完全な一覧が表示されます。

</details>

:::note info
**ポイント**

- **すべての列を取得**したいときは、`SELECT`の後に`*`（アスタリスク）を使います。
- データを取得するテーブルを指定するには、`FROM`の後に**テーブル名**を書きます。
:::

---

### 問題2

**書籍名一覧を取得してください。**

出力項目は`name`（書籍名）です。

<details><summary>解答・解説</summary>

```sql
SELECT name FROM books;
```

**解説**

特定の列だけを取得したい場合、`SELECT`の後にその**列名**を指定します。このクエリでは、`books`テーブルから`name`列のみを選択しています。これにより、書籍名だけの一覧が取得できます。

</details>

:::note info
**ポイント**

- 必要な**列だけを取得**する場合は、`SELECT`の後にその列名を指定します。
- データ量を減らし、効率的に情報を取得できます。
:::

---

### 問題3

**書籍名と発行年一覧を取得してください。**

出力項目は`name`（書籍名）と`release_year`（発行年）です。

<details><summary>解答・解説</summary>

```sql
SELECT name, release_year FROM books;
```

**解説**

複数の列を取得する場合、`SELECT`の後に**カンマで区切って列名**を列挙します。このクエリでは、`books`テーブルから`name`と`release_year`の2つの列を選択しています。これにより、各書籍の名前とその発行年の一覧を取得できます。

</details>

:::note info
**ポイント**

- 複数の列を取得する際は、列名を**カンマ（,）で区切って**指定します。
- 複数の情報を同時に取得することで、データの関連性を把握できます。
:::

---

### 問題4

**書籍の発行年一覧を重複行を削除して取得してください。**

出力項目は`release_year`（発行年）です。

<details><summary>解答・解説</summary>

```sql
SELECT DISTINCT release_year FROM books;
```

**解説**

`DISTINCT`キーワードを使用すると、選択した列の中で**重複する行を排除**し、一意の値のみを取得できます。このクエリでは、`books`テーブルから`release_year`列の値を取得しますが、重複する発行年は1つにまとめられます。これにより、発行年の種類を一度ずつ一覧できます。

</details>

:::note info
**ポイント**

- **重複を排除**して一意の値だけを取得したいときは、`SELECT`の後に`DISTINCT`を入れます。
- データ内のユニークな値を知りたい場合に便利です。
:::

---

### 問題5

**書籍名と総ページ数の一覧を取得してください。**

出力項目は書籍名（`name`）と総ページ数（`total_page`）です。

※ `AS`句を使用して**表示するカラム名を変更**してください。

<details><summary>解答・解説</summary>

```sql
SELECT name AS "書籍名", total_page AS "総ページ数"
FROM books;
```

**解説**

`AS`を使うと、列に別名（エイリアス）を付けることができます。これにより、出力結果の列名をより分かりやすい名前に変更できます。このクエリでは、`name`列を「書籍名」、`total_page`列を「総ページ数」として表示します。ユーザーにとって理解しやすい出力結果になります。

</details>

:::note info
**ポイント**
- 出力する列の**別名を指定**したいときは、列名の後に`AS`を使って新しい名前を指定します。
- 別名はダブルクォーテーション `""` で囲むことで、スペースや日本語を含む名前を使用できます。
:::

# さいごに
今回は基本的な`SELECT`文の使い方を学びました。`SELECT`文はSQLの中でも最も基本的で重要な文ですので、しっかりと理解しておきましょう。

# まとめ
- **`SELECT *`**：すべての列を取得
- **`SELECT 列名`**：特定の列を取得
- **カンマ区切りで複数の列を指定**：`SELECT 列1, 列2`
- **`DISTINCT`**：重複を排除して一意の値を取得
- **`AS`句**：出力時の列名を変更


# 次回予告
次回は`WHERE`句を使ったデータの絞り込みについて学んでいきます。お楽しみに！
C++ 64bit整数の乗算のオーバーフロー判定


競プロでたまに符号つき64bit整数の乗算のオーバーフロー判定が必要になることがある。(途中の乗算でオーバーフローすると誤判定するため)
GCCにはビルトイン関数があってそれでできるが、Visual C++にはないので、組み込み関数の乗算を利用して書いてみた。結果は2つの64bitワードとして取得できる。

以下の場合はオーバーフローしていない。
- 上位ワードがゼロで、下位ワードが非負のとき
- 上位ワードが-1で、下位ワードが負のとき

上記以外の場合はオーバーフローしている。
- 上位ワードが1以上なら64bitに収まっていない
- 上位ワードがゼロで下位ワードが負ならLLONG_MAXを超えている
- 最上位ワードが-1より小さい場合は64bitに収まっていない
- 最上位ワードが-1で、下位ワードが正のときは、LLONG_MINより小さい

```cpp
#include <cassert>
#include <climits>
#include <iostream>
#ifdef _MSC_VER
#include <intrin.h>
#pragma intrinsic(_mul128)
#endif

inline bool is_mul_overflow(long long a, long long b) {
#ifdef _MSC_VER
  long long high, low = _mul128(a, b, &high);
  return !((high == 0 && low >= 0) || (high == -1 && low < 0));
#else
  long long low;
  return __builtin_smulll_overflow(a, b, &low);
#endif
}

int main(int argc, char* argv[]) {
  assert(is_mul_overflow(LLONG_MAX, 1) == false);
  assert(is_mul_overflow(LLONG_MAX / 2 + 1, 2) == true);		// LLONG_MAX + 1

  assert(is_mul_overflow(LLONG_MIN, 1) == false);
  assert(is_mul_overflow(LLONG_MIN, 2) == true);		// 0xFFFFFFFF00000000

  return 0;
}
```
Google Play クローズドテストのログ

79日間のクローズドテストのログです。

私は今回、初めてAndroid用アプリを開発しリリース作業をおこないました。
結果、審査3回落ち、4回目で通過しました。

通過したケースも、なぜ通過できたのかよくわからないので
何をおこなってきたかを書き出してみます。

※先に書きますが、結局なにがポイントなのかは不明です。
　ただの参考として読んでください。

クローズドテスト：
規定人数以上（開始時は20人、途中で12人に変更）のテスターが 14日以上連続でアプリのテストを実施する。
審査後、リジェクトの場合はさらに14日以上連続でテストをおこなう


テスターからのフィードバックは、テスター募集したSNS上で集めました。
Google Play Store からのフィードバックは一件もなしです。


リジェクト理由は3回とも
- アプリのクローズド テストにテスターが関与しなかった
- ユーザーからのフィードバックを収集し、アプリのアップデートを通じて対応するなどの、テストにおけるベスト プラクティスを実践しなかった

でした。


以下が79日間でおこなった主な内容です。
「1日のアクティブなデバイス数」が重要という情報があるため、日々のデバイス数と累計を一緒に書き出しました。

4回目のテスト期間で、「1日のアクティブなデバイス数」累計が丁度3桁になっており、節目のよう見えますが
これが理由で通過できたという根拠はありません。
もし、通過した方がいらっしゃればコメントいただけると幸いです。


|日付|インストール済みユーザー数|1日のアクティブなデバイス数|1日のアクティブなデバイス数(累計)|イベント|
|:--:|:--:|:--:|:--:|:--:|
|2024/10/29|1|1|1|クローズドテストフェーズ開始|
|10/30|1|0|1||
|10/31|1|0|1||
|11/1|7|5|6||
|11/2|16|8|14|アプリアップデート１回目|
|11/3|22|7|21|テスター20人達成|
|11/4|23|4|25||
|11/5|27|4|29||
|11/6|27|0|29||
|11/7|24|3|32||
|11/8|25|1|33||
|11/9|26|1|34||
|11/10|23|1|35||
|11/11|25|2|37||
|11/12|26|1|38||
|11/13|25|0|38||
|11/14|25|0|38||
|11/15|22|2|40||
|11/16|24|3|43|アプリアップデート２回目|
|11/17|24|0|43||
|11/18|25|0|43||
|11/19|21|2|45||
|11/20|23|0|45||
|11/21|23|2|47||
|11/22|24|8|55||
|11/23|24|6|61|テスト審査リジェクト。２回目開始|
|11/24|22|5|66||
|11/25|23|3|69||
|11/26|23|2|71||
|11/27|21|1|72||
|11/28|23|1|73||
|11/29|24|1|74||
|11/30|23|0|74||
|12/1|24|2|76||
|12/2|27|1|77||
|12/3|23|0|77||
|12/4|24|1|78||
|12/5|22|1|79||
|12/6|20|1|80||
|12/7|24|0|80||
|12/8|22|0|80||
|12/9|26|0|80|テスト審査リジェクト。３回目開始|
|12/10|22|0|80||
|12/11|21|0|80||
|12/12|23|0|80|※必要テスター人数が12人以上に変更|
|12/13|23|1|81||
|12/14|22|0|81||
|12/15|19|2|83|アプリアップデート３回目|
|12/16|23|4|87||
|12/17|18|0|87||
|12/18|22|0|87||
|12/19|19|2|89|アプリアップデート４回目|
|12/20|19|0|89||
|12/21|22|0|89||
|12/22|21|1|90||
|12/23|22|2|92|アプリアップデート５回目|
|12/24|21|0|92||
|12/25|22|0|92||
|12/26|18|0|92||
|12/27|19|1|93||
|12/28|21|0|93||
|12/29|21|0|93||
|12/30|20|2|95|テスト審査リジェクト。４回目開始|
|12/31|18|3|98||
|2025/1/1|18|1|99||
|1/2|19|1|100||
|1/3|17|2|102||
|1/4|18|2|104||
|1/5|18|0|104||
|1/6|17|1|105||
|1/7|16|1|106||
|1/8|15|2|108||
|1/9|17|1|109||
|1/10|16|3|112||
|1/11|14|2|114|アプリアップデート６回目|
|1/12|14|2|116||
|1/13|14|0|116||
|1/14|15|0|116||
|1/15|16|1|117|クローズドテスト通過|
■times.doを実行した際に余計な値が表示される件

初投稿です！
未経験でエンジニア転職を目指している29歳男です。
初心者ながらに戸惑ったtimes.doの実行結果について簡単にまとめていきたいと思います！

まずは、基本的なtimesメソッドの説明です。
## times do
timesメソッドはRubyの整数クラスのインスタンスメソッドです。
このメソッドは、指定したブロックを指定された回数だけ繰り返し実行します。

```
#例文
    def hello
        5.times do
            puts "こんにちは！"
        end
    end

puts hello
```
予想される例文の結果は以下になります。
```
こんにちは！
こんにちは！
こんにちは！
こんにちは！
こんにちは！
```

しかし、私のVScode上で実行すると、
```
こんにちは！
こんにちは！
こんにちは！
こんにちは！
こんにちは！
5
```


となります。
はい、最後に5が出てくるんですね～、不思議ですね～
見るからに繰り返した回数だよなぁ、そう思いますよね！？

↓

・一体なぜ実行回数が表示されているのか

その秘密を探るべくアマゾンの奥地h((
ｺﾞﾎﾝ、ちょっとだけ調べてみました！！

## なぜ「5」が表示されるのか？
```
#例文
    def hello
        5.times do
            puts "こんにちは！"
        end
    end

puts hello
```

この場合、times メソッドの戻り値が hello メソッドの戻り値になります。
times メソッドは（ブロック付きで呼び出されたとき）レシーバーを返します。
(公式リファレンス：https://docs.ruby-lang.org/ja/3.4/method/Integer/i/times.html)

5.timesブロックは、5回繰り返し実行され、その結果として実行回数である5を返します。


ずばり！！　5.timesは5を返すのです。
（はい、当たり前ですね…）

したがって、メソッド呼び出しputs helloの実行によって、
メソッドhelloが呼び出され、「5」が最後に表示されます。


## どうすれば「5」を表示しないようにできるか？

もしメソッドが最後に何も返さないようにするには、
明示的にnilを返すようにコードを変更することができます。

```
    def hello
        5.times do
            puts "こんにちは！"
        end
        nil
    end

puts hello
```
※「        <font color="#008080">**nil**</font>」は、~~Rubyで「何もない」ことを表す特別な値です。~~
  　→→→「期待したものが存在しなかった」ことなどなどを表すオブジェクトです。
　　　　　元々はラテン語の nihil (何もない、無) が語源。

 ## ちょっとまって、、、、 ##
nilに変えてみて、確かに「5」は消えた、、、。
けど、おかしい。
実行して頂いたらわかるんですが
「5」があった場所にnilでできた改行分の空白が出るんです....。


:no_mouth:


そう、はなから理解が間違っているんですね～ｗ
この記事を投稿してからご指摘頂きましたので追記します！！！！


## 本当の原因は...
問題の元凶は「hello メソッドが 5 を返してしまう」ということではなく，
```
puts hello
```
によって hello の返り値を表示してしまうことでした！！ｗｗ

つまり
```
    def hello
        5.times do
            puts "こんにちは！"
        end
    end

 hello
```
としてあげれば、綺麗な結果が出てくるんですねぇ～ｗ

いやぁ～本当にお恥ずかしい限りです(;´∀｀)
真犯人は最初から登場していたわけですね。

劇場版 名探偵〇〇ン times.do編
真犯人は「puts」ということで終わりたいと思います。


今回は以上です。
自分が初心者ながらに困った実行結果についてまとめてみました。
間違っている部分がありましたら、是非コメントにてご教示頂ければ幸いです。


■追記
※@scivolaさんにご指摘頂き、正しい知識を身に着けることができました！
　中々独学で学んでいると気づきにくく、こうやってご指摘頂けることが有り難いです！
GitBash上でCtrl+Shift+c/vによるコピペをする方法

## 以下の2ステップです。
・GitBashのウィンドウ上で右クリック, "Options" を選択
・一番下の "Ctrl+Shift+letter shortcuts" にチェックを入れてApply→Save

![options.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3994893/49e6982d-827a-db04-7736-f9c3a2364dee.jpeg)
AWS EC2の月額料金の一覧表を作ってみた2025年版

３年前に同じ記事を書きました。それからどうなったかを見ていきたいと思います。

↓ 過去の記事（2022年4月）

https://qiita.com/koji4104/items/58088f5af5300234aaca

# 比較したら安くなっていた

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/68907e25-57de-739b-6d1a-441bffe5731b.jpeg width=60%>
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/d99db3cc-70ff-6e0f-0505-2575e63427e1.jpeg width=60%>

2022年4月　1ドル130.46円
2025年1月　1ドル156.03円

|タイプ|2022|2025|2022|2025|
|--|--:|--:|--:|--:|
|t3.small　|19.86 ドル|:arrow_heading_down: 19.58 ドル|2,581円|:arrow_heading_up: 3,054円|
|t3.medium |39.71 ドル|:arrow_heading_down: 39.16 ドル|5,162円|:arrow_heading_up: 6,108円|
|t3.large  |79.43 ドル|:arrow_heading_down: 78.33 ドル|10,325円|:arrow_heading_up: 12,219円|

1か月720時間の料金です。  
なんとAWSは安くなっているのです (；・`д・´)

# インスタンス別の費用感

Amazon EC2 オンデマンド料金
https://aws.amazon.com/jp/ec2/pricing/on-demand/

東京リージョン　Linux
1ドル156円　1か月720時間（2025年1月調べ）

|インスタンス|vCPU|Mem GiB|Net Gbps|時間 USD|ドル/月|円/月|
|--|--:|--:|--:|--:|--:|--:|
|t3.nano     |2|0.5 GiB|5| 0.0068 |4.89  | 762円|
|t3.micro    |2|1 GiB|5| 0.0136 |9.79  |1,527円|
|t3.small　  |2|2 GiB|5| 0.0272 |19.58 |3,054円|
|t3.medium   |2|4 GiB|5| 0.0544 |39.16 |6,108円|
|t3.large    |2|8 GiB|5| 0.1088 |78.33 |12,219円|
|t3.xlarge   |4|16 GiB|5| 0.2176 |156.67 |24,440円|
|t3.2xlarge  |8|32 GiB|5| 0.4352 |313.34 |48,881円|

**コメント**  
性能が一段階上がるごとに倍になっています。

# タイプ別の費用感

|タイプ|インスタンス|時間 USD|vCPU|メモリ|ストレージ|最大ビット|
|--|--|--:|--:|--:|--:|--:|
|汎用|t3.large|0.1088|2|8 GiB|EBS のみ| 5 ギガ|
||t4g.large|0.0864|2|8 GiB|EBS のみ| 5 ギガ|
||m5a.large|0.1120|2|8 GiB|EBS のみ| 10 ギガ|
||m6a.large|0.1116|2|8 GiB|EBS のみ| 12500 メガ|
||m7a.large|0.1497|2|8 GiB|EBS のみ| 12500 メガ|
|コンピューティング|c5a.large|0.0960|2|4 GiB|EBS のみ| 10 ギガ|
||c6a.large|0.0963|2|4 GiB|EBS のみ| 12500 メガ|
||c7a.large|0.1292|2|4 GiB|EBS のみ| 12500 メガ|
|メモリ最適化|r5a.large|0.1370|2|16 GiB|EBS のみ| 10 ギガ|
||r6a.large|0.1368|2|16 GiB|EBS のみ| 12500 メガ|
||r7a.large|0.1835|2|16 GiB|EBS のみ| 12500 メガ|
|ストレージ最適化|i4i.large|0.201|2|16 GiB|1x468 NVMe SSD| 10 ギガ|

**コメント**  
同じ large だとだいたい費用感も同じになります。

# タイプ別のおさらい

**汎用**  
T系は「バースト」ができます。バーストとはCPUを使用していないときにクレジットが貯まり、しきい値を超えて使用できます。AWS側としては無駄な使用をやめさせるのが狙いでしょう。私はずっとT系を使っています。  

**コンピューティング最適化**  
よいCPUを使っています。科学モデリングや機械学習推論に最適です。  

**メモリ最適化**  
メモリが大きい。  

**ストレージ最適化**  
1 秒あたり数万回の低レイテンシーのランダム I/O オペレーション (IOPS) をアプリケーションに提供するように最適化されています。  

# あとがき

2022年と2025年のAWS料金を比較してみました。ドルでは安くなっているのですね。ただ開発環境も性能が求められるため費用は上がっています。

本記事では触れませんでしたが AWS Graviton が安くて興味があります。AWS Graviton は AWS 製のARM CPUです。MAC も ARM CPU になり数年立ちました。そのため AWS Graviton も数年で対応していくと思われます。

どうなるでしょうかね AWS GCP Azure。なんか最近クラウドで似たようなものばかりを作ること自体が面倒になってモチベダウンです。
DockerのImageをECRにpushするまで

## 目的
DockerのイメージをAmazon ECRにpushする

## 手順
### 1. ECRのリポジトリ作成
リポジトリ名を入力して作成する
![スクリーンショット 2025-01-25 23.54.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2435396/f50ee187-5760-f810-3585-b244c2ed8034.png)

### 2. 認証を行う
pushするECRレジストリ先にDockerクライアントを認証する必要があるので、以下のコマンドで認証を行います。

```
aws ecr get-login-password --region {region} | docker login --username AWS --password-stdin {aws_account_id}.dkr.ecr.{region}.amazonaws.com
```

region: リージョン
aws_account_id: 12桁のid

### 3. docker imageを作成・タグづけをする
docker buildでイメージを作成する
以下のコマンドでbuildと同時にタグづけができる
```
docker build -t {image}:{tag}
```

作成したイメージは以下のコマンドで確認できる
```
docker images
```

### 4. ECRにpushする
```
docker push {aws_account_id}.dkr.ecr.{region}.amazonaws.com/{my-repository}:{tag}
```

my-repository: リポジトリ名
tag: 作成したイメージタグ

ECRレジストリのリポジトリにイメージがpushされていることができたらpushh完了です！！

## 参考
https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/docker-push-ecr-image.html
IntelliJ IDEAやRiderでJSON、HTML、JWTなどの文字列をサッと確認

JSONの中身を確認したい時、どうしていますか？JSONライブラリでパースし、Pretty Print (整形表示)し、ログに出力して確認している方も多いのではないでしょうか？

IntelliJ IDEAやRiderでは、そのようなことをしなくても、Debug機能で簡単にJSONなどの文字列をわかりやすく表示し、確認することができます。

---

RiderとC#のコードを例に説明します。
Riderのバージョン：2024.3.3

JSONフォーマットの文字列が入っている、string型のtargetJsonという変数があります。


![スクリーンショット 2025-01-25 23.40.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4125/dbe100f4-bc36-d2c0-5944-bb2732310907.png)


ブレークポイントを張り、デバッグ実行します。Debug tool window、「Threads & Variables」tab中の、変数targetJsonの横、「View」リンクをクリックします。(下図中、赤枠)

![スクリーンショット 2025-01-25 23.40.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4125/60e95e58-9cfa-60c1-9fbb-9d45d6bd393f.png)


すると、次のようなダイアログが出てきます。

![スクリーンショット 2025-01-25 23.40.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4125/3590d1aa-4ba0-f992-4a6c-677636ecf65d.png)

変数targetJsonの中身が、Pretty Print (整形表示)され、表示されていることに注目してください。わざわざJSONライブラリでパースし、Pretty Print (整形表示)し、ログに出力しなくても、中身をいい感じに確認することができます。

なお、ダイアログはEditor内の変数targetJsonをホバーすると出てくるツールチップ中の「View」リンクをクリックしても表示されます。

---

JSON以外のフォーマットでも、見やすくビジュアライズし確認することができます。

次のスクリーンショットは、URLエンコードされた文字列の例です。

|URL(decoded)タブ|Rawタブ|
|:---:|:---:|
|![スクリーンショット 2025-01-25 23.47.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4125/0d5f23c6-d33d-8b04-88ea-ba9347a4159f.png)|![スクリーンショット 2025-01-25 23.46.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4125/7196684c-db0d-2b10-9790-4065d5bff261.png)|


次のスクリーンショットは、HTMLの例です。

|HTMLタブ|Rawタブ|
|:---:|:---:|
|![スクリーンショット 2025-01-25 23.45.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4125/4faaafbc-1336-6891-d2d5-ccdcc993c38a.png)|![スクリーンショット 2025-01-25 23.46.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4125/c296a960-dec4-e10d-86cf-7c3bb4def368.png)|


他にも、XMLとJWTに対応しているようです。

---


IntelliJ IDEAやRiderでは、Debug機能で簡単にJSON、HTML、XML、JWT、URLエンコードされた文字列などの文字列をわかりやすくビジュアライズし、確認することができます。

わざわざJSONライブラリでパースし、Pretty Print (整形表示)し、ログに出力しなくても良いのです！便利ですね！活用していきましょう！

---

参考リンク

* [What's new 2023.1 - Visualizers for string variables Debugger](https://www.jetbrains.com/rider/whatsnew/2023-1/)

公式ドキュメント、調査中。
勤務地周辺の飲食店紹介アプリを開発しました【Java / Android / Room】

# はじめに
勤務地周辺の飲食店紹介アプリを作成したので、学習・実装内容をまとめました。
昼休憩という限られた時間の中でスムーズに外食ができるように、「食事にかかる時間」にフォーカスした食べログのようなアプリです。

# 目次
[1. ポートフォリオの紹介](#1-ポートフォリオの紹介)
[2. アプリ作成に至るまでの学習・実装時間](#2-アプリ作成に至るまでの学習・実装時間)
[3. 機能要件](#3-機能要件)

# 1. ポートフォリオの紹介
### (1)アプリ概要
<table>
	<tr>
		<th style="text-align: center">ログイン画面</th>
		<th style="text-align: center">お店評価</th>
	</tr>
	<tr>
		<td><img alt="ログイン画面" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3836145/7cb508d4-2b41-ec99-7abc-1770c403980d.gif"></td>
<td><img alt="ログイン画面" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3836145/00bbb5ed-239c-a01f-e686-591afc306c9c.gif"></td>
    </tr>
</table>

#### 1.開発目的  

昼休憩は45分間と短いため、外食する場合には「お店選び」が重要です。お店選びには『Googleマップ』などを利用する場合が多いが、「食事にかかる時間」の情報はほとんどありません。 そこで、「食事にかかる時間」にフォーカスした飲食店紹介アプリを開発することにより、お店選びをスムーズに行うことを目的としました。

#### 2.特徴
* 口コミ評価項目は「お店の滞在時間」と「満足度」
* メイン画面でお店の滞在時間を確認可能

#### 3.実装期間
約３ヶ月

#### 4.使用技術
①IDE
* Android Studio

②言語
* Java 


③テスト
* junit4
* Mockito

④その他
* GitHub
* Room

#### 5.GitHubのリポジトリ
https://github.com/yaya16888/NissayLunch.git


# 2. アプリ作成に至るまでの学習・実装時間
#### JAVA学習(約60時間）
* 研修でのJAVA学習（paiza）・・・50時間
* UdemyJava講座・・・10時間
タイトル「Java知識ゼロOK！プロのAndroid開発者になるためのマスターコース」

#### DB学習(約150時間）
* 研修でのDB学習（paiza）・・・50時間
* 家賃管理ソフトのデータ設計・・・100時間

#### テスト学習(約20時間）
* UdemyJunit講座・・・10時間
タイトル「現役アーキテクトが教える『Javaテスト基礎＆実践』JUnit5、Mockito、DBUnit／CICD／生成AI活用」
* JSTQBの学習（youtubeなど）・・・10時間

#### アプリ実装（約150時間）
* 要件定義・・・10時間
* 実装・・・100時間
* テスト・・・40時間

# 3. 機能要件
#### 1. ユーザー認証機能 



1.1 ユーザー登録 

- ユーザーは、ユーザー名、パスワードを入力して新規登録できる。 

- 登録成功後、ユーザーは自動的にログイン状態になる。 

 

1.2 ログイン 

- 登録済みのユーザーは、ユーザー名とパスワードを入力してログインできる。 

- ログイン成功後、メイン画面に遷移する。 

 

 1.3 ログアウト 

- ログインしているユーザーは、ログアウトボタンをクリックしてログアウトできる。 

- ログアウト成功後、ログイン画面に遷移する。 

 

#### 2. メイン画面機能 

 2.1 お店リスト表示 

- 時間的余裕のあるお店を一目で確認できる。
- お店をタップすると詳細画面に遷移できる。 
- AIにより生成された「お店をの商品をイメージした画像」により、お店のイメージを理解できる。 

2.2 お店詳細表示 

- お店の詳細情報には、場所、ランチの価格帯、滞在時間、五段階評価の４つが表示される。

2.3 評価投稿機能
- 満足度と滞在時間をユーザーが投稿することができる。（満足度は星５段階で評価）
- 満足度と滞在時間は平均値がメイン画面とお店詳細画面に表示される。
【Mac】コマンドでダークモードか確認する方法

以下のコマンドを実行するとダークモードになっている場合には"Dark"が返却され、それ以外の場合にはエラーとなります。

```terminal
defaults read -g AppleInterfaceStyle
```

ダークモードの場合

```terminal
$ defaults read -g AppleInterfaceStyle
Dark
```

ダークモードでない場合

```terminal
$ defaults read -g AppleInterfaceStyle
2025-01-25 10:21:27.778 defaults[14319:355652]
The domain/default pair of (kCFPreferencesAnyApplication, AppleInterfaceStyle) does not exist
```
Amazon BedrockのKnowledge baseで日本語ファイル名の罠【解決策あり】

## はじめに 🌟

以前投稿した「[GraphRAGでポケモン図鑑の構築に挑戦](https://qiita.com/TKG_KM/items/42c31a852470ae4d28db)」の続編として、Amazon Bedrockのナレッジベースでの実装を試みていました。

その検証中に、S3のファイル名に日本語が含まれていると同期エラーが発生することが判明したので、その対処法を備忘録として残します。

## 発生する問題 ⚠️

ナレッジベースのテスト時に「1 つ以上のデータソースが同期されていません。」というエラーが表示され、
質問を投げても「Sorry, I am unable to assist you with this request.」と返ってきて使用できない状態になります。

## エラーの再現手順 🔄

### 1. ナレッジベースの作成
1. Bedrock -> ナレッジベース -> ナレッジベースを作成
2. "Knowledge base with vector store"を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88001/b47350e3-a59c-f6d0-fd76-fd3181ec4230.png)

### 2. 設定手順
1. ステップ1: デフォルトのまま次へ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88001/1a445127-0c56-7e80-870f-3bc44587cbcc.png)

2. ステップ2: S3のURIを指定（※ここで日本語ファイル名を指定すると問題発生）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88001/00f28cc7-90ff-7f94-4d39-b72c55ba0ab9.png)

3. ステップ3: ベクトルデータベースは"Aurora"を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88001/c751090b-3ca1-3835-d4ce-6e05d4826f41.png)

### 3. エラーの確認
- 数分後、テスト画面で「1 つ以上のデータソースが同期されていません。」と表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88001/3921b842-b07d-6fd0-d4e3-a4eb6c6e9543.png)

- 質問を入力すると「Sorry, I am unable to assist you with this request.」とエラー応答
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88001/4b6f2b15-315c-2676-de65-0fb9bbf47b5b.png)

## 解決策 💡

**S3のファイル名を半角英数字のみに変更することで問題が解消します！**

### 具体的な手順
1. S3のファイル名を日本語から半角英数に変更
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88001/a56871ec-90b7-f8fd-c33a-84e7aaedcdbb.png)

2. ナレッジベースを再同期
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88001/531d2a03-d82b-dd51-ae8e-eb8373186d6e.png)

3. テストを実行
### ちゃんとソースまで出てきて偉い！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88001/010ade5c-5435-be9b-214b-5feb8f7fd059.png)
【Github】GithubにSSHでアクセスできなかったときの対処

# GithubにSSHでアクセスできなかったときの対処

## はじめに

状況として、Github アカウントに公開鍵を登録して使っていたのに、アクセスできなかった。これまで使っていたLinux環境から別のLinux環境にうつり、秘密鍵を~/.ssh/ に同じように配置すれば動くのかな、、、と思っていたらダメだった。
SSH Agentというのに登録したら動くようになった、という認識。でメモします。

## 内容

### 最初にSSH-Agentを起動、鍵を登録して動かす

登録している鍵を調べてみようとすると、、、

```
$ ssh-add -l -E sha256
Could not open a connection to your authentication agent.
```

Authentication agentた無いと言われるので、起動します。

```
$ exec ssh-agent bash
```

そして、確認してみると、、、

```
$ ssh-add -l -E sha256
The agent has no identities.
```

登録がないそうです。なので登録します。
自分はkey20240101, 20240101.pub というペアを昔作り、20240101.pub をGithub の SSH keysに登録していました。

```
$ ls ~/.ssh/
config  key20240101  key20240101.pub  known_hosts 
$ ssh-add key20240101
Identity added: key20240101 (username@gmail.com)
```

これで確認ができました。

```
$ ssh-add -l -E sha256
256 SHA256:nZ8jOuDNsn5I/ABCDEFGHHxxxxxxxxx123040293444 username@gmail.com (ED25519)
```

Github の認証は通ったが、shell アクセスはできないと。これで通常のgit push とかできるようになりました。

```
$ ssh -vT git@github.com
OpenSSH_9.6p1 Ubuntu-3ubuntu13.5, OpenSSL 3.0.13 30 Jan 2024
debug1: Reading configuration data /home/xt/.ssh/config
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 19: include /etc/ssh/ssh_config.d/*.conf matched no files
debug1: /etc/ssh/ssh_config line 21: Applying options for *
debug1: Connecting to github.com [20.27.177.113] port 22.
debug1: Connection established.
debug1: identity file /home/xt/.ssh/id_rsa type -1
......
Hi xt! You've successfully authenticated, but GitHub does not provide shell access.
debug1: channel 0: free: client-session, nchannels 1
Transferred: sent 2304, received 2636 bytes, in 0.3 seconds
Bytes per second: sent 6825.9, received 7809.5
debug1: Exit status 1
```

### 自動化

実は、前節で書いた方法は１回限りでした。毎回起動するたびに実行するのは面倒です。

#### SSH Agentが起動しているかの確認

環境変数を調べます。ssh-agent が起動していない場合、この二つは空です。

```bash
echo $SSH_AUTH_SOCK
echo $SSH_AGENT_PID
```

下記を実行するとSSH_AUTH_SOCK 環境変数が設定され、ssh-agent がバックグラウンドで実行されます。

```
eval "$(ssh-agent -s)"
```

これを .bashrc などに書いておくと、新しいターミナルごとにSSH-agent が起動されます。

#### キーリングへの登録

リスクはあるそうですが、下記で登録できるそうです。

```
$ ssh-add -k key20240101
Identity added: key20240101 (username@gmail.com)
```


## 参考情報


https://stackoverflow.com/questions/17846529/could-not-open-a-connection-to-your-authentication-agent

https://docs.github.com/ja/authentication/troubleshooting-ssh/error-permission-denied-publickey?platform=linux

https://docs.github.com/ja/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent

https://hisafi.hatenablog.com/entry/20210206/1612617113

## おわりに

とりあえず問題解決したのでよかった。
(2025/1/25)

追記:
確認、キーリングへの追加を書きました。(2025/2/11)
C++ 学習備忘録 1 - 文字出力編

## 目次
●[1.はじめに](#1はじめに)
●[2.文字を出力](#2文字を出力)
●[3.namespaceの省略](#3namespaceの省略)
●[4.おわり](#4おわり)

## 1.はじめに
最近 C++ をたまに目にするので、一から学習してみました。
学習した瞬間は覚えていても、人間は時間が経てば忘れてしまうもの。
なので、Qiita を備忘録として活用していきたいと思います。

今回はまず初めに文字の出力の仕方から。

## 2.文字を出力
まずはお決まりの Hello World とその他を出力してみた。
C 言語の時は printf() で出力だったけど、
C++ になるとこういう書き方になるようで。

```c++:study1_prog1.cpp
#include <iostream>

int main()
{
    std::cout << "Hello, World!" << std::endl;
    std::cout << 100 << std::endl;
    std::cout << "今日はごはんを" << 3 << "杯食べた。" << std::endl;

    return 0;
}
```

**実行結果**
`Hello, World!`
`100`
`今日はごはんを3杯食べた。`

:::note info
C++ では出力の際には <<（出力演算子）を使用する。
std::cout を出力先として指定しているイメージ。
iostream を include することで cout を使用することができる。
:::

:::note info
iostream は std という namespace に属している。
なので、std::cout << <出力文字> のような書き方になる。
:::

## 3.namespaceの省略
でも、毎回 std::cout << のような書き方をするのは正直面倒。
なので、std:: を省略することが可能。
イメージ的には、使用する namespace を std:: 固定と最初に宣言する感じ。
先ほどのコードで namespace を std:: に固定した例が下記。

```c++:study1_prog2.cpp
#include <iostream>
using namespace std; /*namespace を std に固定*/

int main()
{
    cout << "Hello, World!" << endl;
    cout << 100 << endl;
    cout << "今日はごはんを" << 3 << "杯食べた。" << endl;

    return 0;
}
```

**実行結果**
`Hello, World!`
`100`
`今日はごはんを3杯食べた。`

## 4.おわり
まずは第一弾ということで、Hello World の出力について書いてみました。
今後も引き続き書いていきたいと思っています。
【Kotlin】二重ループforeach内のcontinueやbreakの動きについて調べてみた

Kotlinの二重ループforeach内にcontinueやbreakがある際の動きについて、理解に苦労しました。
そのため、これらがどのような動きをするのか備忘録として残しておきます。

# 検証

検証には[Kotlin Playground](https://play.kotlinlang.org/)を使いました。

## continue,breakともに使用しない場合

``` Kotlin
val nums = listOf(1, 2, 3)
val chars = listOf('a', 'b', 'c')

// 外側のforEachループ
nums.forEach { num ->
    println(num)
    
    // 内側のforEachループ 
    chars.forEach { char ->
        println(char)
    }
}
```

結果は下記のようになります。

```
1
a
b
c
2
a
b
c
3
a
b
c
```

当然ですが、すべての値が出力されます。

## 二重ループの内側でcontinueする場合

``` Kotlin
val nums = listOf(1, 2, 3)
val chars = listOf('a', 'b', 'c')

// 外側のforEachループ
nums.forEach { num ->
    println(num)

    // 内側のforEachループ 
    chars.forEach { char ->
        if (char == 'b') return@forEach
        println(char)
    }
}   
```

Kotlinでcontinueを行うには、`return@forEach`と記載します。

結果は下記のようになります。

```
1
a
c
2
a
c
3
a
c
```

`if (char == 'b') return@forEach`で内側のforEachループがcontinue出来ていますね。


## 二重ループの内側でbreakする場合

```Kotlin
val nums = listOf(1, 2, 3)
val chars = listOf('a', 'b', 'c')

// 外側のforEachループ
nums.forEach { num ->
    println(num)

    // 内側のforEachループ 
    run loop@{
        chars.forEach { char ->
            if (char == 'b') return@loop
            println(char)
        }   
    }
} 
```

Kotlinでbreakを行うには、まずforEachの前に`run loop@`を追加します。
`loop@`はラベルで任意の名前でOKです。これは追加しなくても良いです。
次にbreakしたい箇所に`return@loop`を追加します。

結果は下記のようになります。

```
1
a
2
a
3
a
```

`if (char == 'b') return@loop`で内側のforEachループがbreak出来ていますね。

## 二重ループの外側でcontinueする場合

``` Kotlin
val nums = listOf(1, 2, 3)
val chars = listOf('a', 'b', 'c')

// 外側のforEachループ
nums.forEach { num ->
    if (num == 2) return@forEach
    println(num)

    // 内側のforEachループ 
    chars.forEach { char ->
        println(char)
    }
} 
```

二重ループの内側でcontinueする場合と同じく、`return@forEach`と記載します。

結果は下記のようになります。

```
1
a
b
c
3
a
b
c
```

`if (num == 2) return@forEach`で外側のforEachループがcontinue出来ていますね。


## 二重ループの外側でbreakする場合

``` Kotlin
val nums = listOf(1, 2, 3)
val chars = listOf('a', 'b', 'c')

// 外側のforEachループ
run loop@ {
    nums.forEach { num ->
        if (num == 2) return@loop
        println(num)

        // 内側のforEachループ 
        chars.forEach { char ->
            println(char)
        }
    }   
}
```

こちらも二重ループの内側でbreakするのと同じく、forEachの前に`run loop@`、breakしたい箇所に`return@loop`を追加します。

結果は下記のようになります。

```
1
a
b
c
```

`if (num == 2) return@loop`で外側のforEachループがbreak出来ていますね。

## 二重ループの内側から外側のループをbreakする場合

``` Kotlin
val nums = listOf(1, 2, 3)
val chars = listOf('a', 'b', 'c')

// 外側のforEachループ
run loop@ {
    nums.forEach { num ->
        println(num)

        // 内側のforEachループ 
        chars.forEach { char ->
            if (num == 2) return@loop
            println(char)
        }
    }   
}
```

二重ループの内側から外側のループをbreakで抜ける場合は、内側のループ内に`return@loop`を追加します。

結果は下記のようになります。

```
1
a
b
c
2
```

`if (num == 2) return@loop`で外側のforEachループがbreak出来ていますね。


以上、Kotlinの二重ループforeach内のcontinueやbreakの動きについて調べてみた結果でした
Macでlsコマンドが使えなくなった話

Macのターミナルでlsコマンドを実行した結果、以下のエラーが出力されました。

```zsh:zsh
zsh: command not found: ls
```

・・・lsコマンドが認識されないってどういうこと？？？


# source ~/.zshrcがうまく動かない

原因をネットで調べた結果、PATHを記載している.zshrcの内容が間違っているせいでlsコマンドが使えなくなっていると判明。

パスを修正するために以下のコマンドを実行してみました。

```zsh:zsh
export PATH=$PATH:/user/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
source ~/.zshrc
```

パスを通して、sourceコマンドも実行したのでもう安心。
意気揚々とlsコマンドを実行してみた結果・・・

```zsh:zsh
zsh: command not found: ls
```

直ってない。

# 結論： nanoコマンドを使って~/.zshrcに書き込んだ

chatGPTに聞いてみたところ、以下の手順を踏めば修正できると教えてもらったので試してみました。

1. nano ~/.zshrcを実行
2. .zshrcの編集画面に移行するので、以下のコードをコピペ
    ```
    export PATH=$PATH:/user/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
    ```
3. nanoエディタを閉じる（保存し忘れないように注意）
4. ターミナルに戻ってきたら、設定を反映させるために以下コマンドを実行する
    ```
    source ~/.zshrc
    ```

以上の手順を踏んだ結果、無事lsコマンドが使えるようになりました。
めでたし。

# 余談： そもそもなぜパスがおかしくなったのか

新しい言語の勉強のために環境構築をしていた際に、homebrewがうまく動かなくなる事態に直面しました。

Intel Mac から Apple Silicon (M1/M2/M3) に移行すると、Homebrewのインストールディレクトリが変わるため、正しく動作しなくなることがあるらしいです。

解決策をchatGPTに聞いてみたところ、パスの記載がおかしい可能性があるため、一度.zshrcファイルを見直した方がいいとアドバイスをされる。

そのアドバイスに従った結果、必要以上にパスを削除してしまって、ls等のコマンドすら使えない状態になっていました。

以上、.zshrcファイルへの理解が足りていなかったため発生した事象でした。
【 Java 】SimpleDateFormatのスレッドセーフではない挙動を確認

## ■　はじめに
Java勉強につき、メモを残します。
下記内容は、備忘録レベルで書いてますので間違い等あるかもしれません。
参考にする際にはその点ご認識のほどお願いします。。

## ■ SimpleDateFormatとは
日付をフォーマットしたり解析したりする際に使用されるクラスです。
Data型の日付（例：2025-01-01）をformatメソッドを使用して、String型にフォーマットしたり、String型の日付をparseメソッドで解析してDate型を生成したりなどです。

このSimpleDateFormatはスレッドセーフではないので、マルチスレッドで動かした際に
予期せぬ値を返したり、例外が出るため、Java8以降では使用を控えた方が良いです。
代替として、DateTimeFormatterクラスがスレッドセーフのクラスになるため、
フォーマットや解析にはこちらのクラスを使用することを推奨します。


今回は、SimpleDateFormatをマルチスレッドで動かしてみて確認しました。
私は参考資料の文字だけ見ても理解できなかったので、私と同じようにイメージがしずらい方は、下記を参考にしてみてください。


※DateTimeFormatterクラスの説明は今回しません。。
※下記ソースで使用しているDateクラスもJava8では非推奨メソッドが多いため、新しく追加されたLocalDateクラスなどを使用する方が良いです。

## ■ 検証ソース
```java
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SimpleDateFormatTest {
    public static void main(String[] args) {
        // newでSimpleDateFormatをインスタンス化しフォーマットを用意
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // Calendarクラスを使用し、Date型の2025-01-01 09:00:00を作成
        Calendar calendar = Calendar.getInstance();
        calendar.set(2025, Calendar.JANUARY,25,9,0,0);
        Date date = calendar.getTime();
        // フォーマットを使用して取得したい期待値
        String kitaichi = "2025-01-25 09:00:00";

        // スレッドを10個作成し、複数のスレッドでsimpleDateFormat変数を使用
        ExecutorService executor = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            executor.submit(() -> {
                // Date型からString型に変換
                String sdf = simpleDateFormat.format(date);
                try {
                    // SimpleDateFormatはスレッドセーフでなく内部的に値を保持しているため
                    // 複数のスレッドで引数の値を操作する処理際に、予期せぬ値に変わってしまう
                    Date result = simpleDateFormat.parse(sdf);
                } catch (ParseException e) {
                    System.out.println("例外発生: " + e);
                }
                if (!kitaichi.equals(sdf)) {
                    System.out.println("不整合: " + kitaichi + " : " + sdf);
                }
            });
        }
    }
}

```

出力結果の左側の「2025-01-25 09:00:00」はString型で定義したkitaichi変数です。
右側がformatした値ですが、年月日や分秒が期待値とは全く違う値になっていることが確認できます。
```java:出力結果
不整合: 2025-01-25 09:00:00 : 2024-01-31 09:20:00
不整合: 2025-01-25 09:00:00 : 2107-06-29 09:00:00
不整合: 2025-01-25 09:00:00 : 2025-01-25 09:00:20
不整合: 2025-01-25 09:00:00 : 2024-12-31 09:00:00

```
## ◽️ 参考資料
https://docs.oracle.com/javase/jp/8/docs/api/java/text/SimpleDateFormat.html

※項目：「同期」参照

https://docs.oracle.com/javase/jp/8/docs/api/java/util/Date.html

https://docs.oracle.com/javase/jp/8/docs/api/java/time/format/DateTimeFormatter.html

https://docs.oracle.com/javase/jp/8/docs/api/java/time/LocalDate.html