OmniAICreator/Qiita-1.07M huggingface dataset:
自分用　Next.jsのsetup

自分用のNext.jsのsetupをまとめたものです。

## Next.jsのプロジェクト作成
### セットアップ
VSCodeのターミナルの任意のディレクトリで以下のコマンドを入力し、セットアップを行う(自分はyarnコマンド、TypeScriptを使用)。
コマンドを入力後、質問がいくつかあるので答える。
```terminal:terminal
yarn create next-app --typescript
```

### 開発サーバーの立ち上げ
作成したディレクトリに移動した後に、VSCodeのターミナルの任意のディレクトリで以下のコマンドを入力し、開発サーバーを立ち上げる。
```terminal:terminal
yarn dev
```

### Next.jsのフォーマットの削除
開発サーバーを立ち上げると、Vercel(Next.jsを作成した会社)が作成したフォーマットが表示されるので、それを変更してみる。
app/page.tsxを開き、return文の中身を以下の内容に書き換える。再度localhostを確認し、Hello Next.js!と表示されていたらOK。
```javascript:app/page.tsx
<div>Hello Next.js!</div>
```

### title,descriptionの変更
localhostのページのタイトルがCreate Next Appになっているので変更する。app/layout.tsxを開き、4,5行目のtitle,descriptionを任意のものに変更する。再度localhostを確認し、titleが変更されていたらOK。

### tailwindcssのsetup
#### tailwindcssのパッケージのインストール
VSCodeのターミナルで以下の2つのコマンドを入力し、tailwindcssのパッケージをインストールする。
```terminal:terminal
yarn add -D tailwindcss postcss autoprefixer
yarn tailwindcss init -p
```
#### configファイルの編集
tailwind.config.jsを開き、3行目のcontentの中身を以下のものに変更する。
```javascript:tailwind.config.js
content: [
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
```
#### global.cssの編集
app/global.cssの中身を以下のものに変更する。
```javascript:global.css
@tailwind base;
@tailwind components;
@tailwind utilities;
```
#### エラーの修正
global.cssやtailwind.config.tsでエラーが発生していると思うので修正する。
tsconfig.jsを開き、moduleResolutionを以下のように修正する。
app/global.cssの中身を以下のものに変更する。
```javascript:tsconfig.js
"moduleResolution": "node",
```
#### cssを試す
tailwindcssのsetupが完了したか確認してみる。
app/page.tsxを開き、以下のようにcssを当ててみる。
localhostを開き、テキストが赤くなっていたらOK。
```javascript:app/page.tsx
<div className="text-red-400">Hello Next.js!</div>
```

### ディレクトリの構築
ディレクトリの構築については別の記事にまとめているので以下を参照。
GASで、なんでvar表記なのか気になったので調べてみた！

ただ今GASのスプレッドシートとGoogleカレンダーで工数抽出に取り組んでます。

そこで気になったのが、なぜvar表記が多いのかです。

javascriptではconstやletつかうのになぜ？と思ったので調べてみました。

V8以前の旧バージョンではletやconstは使用できないため、従来の変数宣言のvarを使用する必要があります。

2020年2月にV8ランタイムにアップデートされたものの、まだV8ランタイムを有効にすると、スクリプトがエラーしたりすることがあります。

また、利用しているライブラリがV8に対応していなかったり、V8ランタイムだと処理時間をオーバーしてしまうケースがあります。

このように従来のバージョンのGASを利用する可能性がある場合は、varによる変数宣言がオススメだそうです。

V8ランタイムだったけど、上記のような事情で無効化した場合に、letやconstを利用していると書き換えが必要になるからです。

書き換えは面倒くさいので避けたいですね。

以上の理由で、var表記にしといたほうがv8ランタイムを有効化してない方でも、無難にエラーなく、全員つかえるよ！とのことでした。

# 追記

2024/2/19
varは現在非推奨とされています。理由は、スコープが広すぎて、異なるgsからでも再代入できてしまい、どのような値が入ってるかがわかりづらくなるためです。
なのでなるべくvar表記は使わずに、constとletでコーディングしよう。
技適

puphy
氏名：
ＳＨＥＮＺＨＥＮ　ＲＥＯＳＴＵＤＩＯ　ＴＥＣＨＮＯＬＯＧＹ　ＣＯ．，ＬＴＤ

keychron
氏名：
Ｓｈｅｎｚｈｅｎ　Ｂｉｐｕ　Ｔｅｃｈｎｏｌｏｇｙ，　Ｉｎｃ．
※K1のみ（工事設計認証番号：210-139562）
Ｓｈｅｎｚｈｅｎ　Ｂｉｐｕ　Ｔｅｃｈｎｏｌｏｇｙ　Ｃｏ．，　Ｌｔｄ
ABC338の記録

# はじめに
「NEW GAME!」のことはちゃんと好きなので「ぞいです」はちょっと抵抗ある。青葉ちゃんが元ネタなのか知りませんが。

https://atcoder.jp/contests/abc338

成績：ABC3完（600）

# A
全部文字コードに変換して1文字目が大文字か、それ以降が全部小文字かどうかを確かめた。sが1文字目だけのパターンもあるのでそれだけ注意する。

```python:A
s = input()
if 90 >= ord(s[0]) >= 65:
    for i in range(1,len(s)):
        if 90 >= ord(s[i]) >= 65:
            print("No")
            break
    else:
        print("Yes")
else:
    print("No")
```

「大文字か小文字かを判定する関数ってないのかな～」と考えながら解いてたが、あるらしいです。

```python:A'
s = input()
if len(s) == 1:
    if s.isupper() == True:
        print("Yes")
    else:
        print("No")
else:
    if s[0].isupper() and s[1:].islower(): 
        print("Yes")
    else:
        print("No")
```

# B
長さが26の配列を作り、それぞれのアルファベットの登場回数に対応させる。sを1文字ずつ見ていって、登場したアルファベットに対応するリストの要素に1足していく。


```python:B
s = input()
#26個のリストlsを作り、ls[0]がa、ls[1]がb……ls[25]がzの個数となるようにしていく。
ls = [0]*26
#sを一文字ずつ見ていき、ls[ord(s[i])-97]に1を足す。
for i in range(len(s)):
    ls[ord(s[i])-97] += 1
#この最大値が最も多く出現するアルファベット。
ans = ls.index(max(ls))
print(chr(ans+97))
```

# C
まずAの料理を作れるだけ作ってそのあまりの材料で作れるBの料理の数を求めて、合計を出す。
その後、Aの料理が作れなくなるまで1つずつ減らしていき、合計が一番高い時の値を出力する。
作れる料理の数については、今ある食材のリストQと料理に必要な食材のリストAを引数に取り、Qの各要素をAの各要素で割った値の最小値を取る関数を定義することで対応した。

```python:C
n = int(input())
Q = list(map(int,input().split())) #冷蔵庫にある材料の量
A = list(map(int,input().split())) #料理Aを作るのに必要な材料の量
B = list(map(int,input().split())) #料理Bを作るのに必要な材料の量
ls = [0]

def cook(Q,A): #材料リストと必要食材リストを引数に取る関数を定義する。
    Anum = []
    for i in range(n):
        if A[i] == 0:
            Anum.append(10000000)
        else:
            Anum.append(Q[i]//A[i])
    return(min(Anum)) #指定した材料リストで作れる料理の個数を返す。
ans = 0
ansA = cook(Q,A)
Qn = [Q[i]-ansA*A[i] for i in range(n)]
ansB = cook(Qn,B)
if ansA+ansB > ans:
    ans = ansA+ansB
#ansAを1減らしてみるとどうなる？→これをansAが0になるまでやればいい。
while ansA >= 0:
    ansA -= 1
    Qn = [Q[i]-ansA*A[i] for i in range(n)]
    ansB = cook(Qn,B)
    if ansA+ansB > ans:
      ans = ansA+ansB
print(ans)
```

Dは解説をチラ見したところ面白そうなのでなんとか解いてみたい。解いたら追記しようかな。
（2024/01/28 3:00追記）

まず1とNの間の橋を封鎖した場合を考える。この時、ツアーの長さは$X[i+1]-X[i]$を足していけば求まる。
この後封鎖する橋を一つずつ動かしていくが、それぞれの区間について「どの橋を封鎖したときに長さがいくつ変化するか」をあらかじめ計算しておく。
各区間の移動方法は時計回りと反時計回りのいずれかしかないことから、それぞれの区間の移動にかかる長さは$X[i+1]-X[i]$もしくは$N-(X[i+1]-X[i])$なので、長さの変化はこの差から求められる。
この変化の累積和を求めて、その最小値をdと足せばOK。

```python:D
n, m = map(int,input().split())
X = list(map(int,input().split()))
d = 0
for i in range(m-1):
    d += abs(X[i+1]-X[i])
change = [0]*(n+1) #橋の数なのでnだが、島の番号と合わせた方が計算しやすいのでn+1個。
for i in range(m-1): #ツアーの行程の数なのでm-1
    change[min(X[i],X[i+1])] += n-2*(abs(X[i+1]-X[i])) #封鎖後の長さ-封鎖前の長さ
    change[max(X[i],X[i+1])] += 2*(abs(X[i+1]-X[i]))-n #封鎖前の長さ-封鎖後の長さ
ans=[]
for i in change:
    ans.append(d+i)
    d += i
print(min(ans))
```
難しい。封鎖する橋をずらしていくという発想にたどり着いたうえでそこから累積和を使えないといけない。これを数十分で解けるようになるのは目標としては高すぎると思うので、出来ることからコツコツと。
機械学習：AI が歌う時代へ・・・


本日は, ちょっと ~~ネタが切れたので~~ 休憩に, 歌う AI について, 書いてみます (o'(ェ)')人🤖


## 【 Synthesizer V Studio Pro 】

　この Synthesizer V という歌う AI が, すごいです。 ｢人間感｣ 満載です。開発者は, 天才プログラマーと言われている中国出身の Kanru Hua 氏, ボカロを聴いたときに衝撃を受け, その道へ✨
 <br>

https://www.ah-soft.com/synth-v/

<br>

## 【作詞作曲をしてみました】

　ぼくは AI に興味を持ち Python に出逢いました。作曲の世界にも AI は, かなり浸透してきています。この曲は ｢夢(目標)が叶わず凹んでいる方への応援ソング｣ のような感じで, 書いてみました。AI が歌っています 🤖
 <br>

https://www.youtube.com/watch?v=5C8fv_LpDlg

<br>


## 【トラックのみもあります】

　トラック(カラオケ)のみもありますので, もし, ご興味ありましたら, ぜひ・・・。
<br>

https://www.youtube.com/watch?v=qY8CnkJW0OI

<br>
 
本日も, 最後まで, 読んでいただき, ありがとうございます (o'(ェ)')_🐻✨
<br>
<br>
toc+で追加した目次を追従させる

toc+で追加したサイドバーの目次をスクロールで追従させます。WordPressプラグイン「[Fixed Widget and Sticky Elements for WordPress](https://ja.wordpress.org/plugins/q2w3-fixed-widget/)」を使います。
プラグインを有効化したら、WordPress管理画面 > 外観 > Fixed Widget　> Custom Fixed Elementsでサイドバーに追加したtoc+のidまたはclassを追加します。
これで動作するので、あとは一般設定とStop Elementsで細い動作の調整をすればいいです。
WordPress管理画面 > 外観 > ウィジェット > スクロールしたい対象のFixed widgetにチェックをする方法もあるけど、そっちは細い調整が出来ませんでした。
[ハイライト表示](https://www.beginner-blogger.com/cocoon-sidebar-toc-highlight/)もしたいので、いい感じのコードが書けたら、メモします。

#### 参考
https://netaone.com/wp/q2w3-fixed-widget/
ターミナルで出来る面白いことまとめ


```
curl wttr.in
```
天気予報がみれるよ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1223603/09ea48b7-7fab-8baa-c27e-b9ad2b124012.png)

```
cal
```
カレンダーがみれるよ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1223603/c13dace4-8c7d-4264-d0b7-683e7ddb3cbb.png)


```
top
```
システムのプロセスとリソースの使用状況をリアルタイムでみれるよ

```
banner
```
好きな文字列を画像みたいな感じで出力するよ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1223603/d58753d3-aa23-888b-d207-acaec45726a6.png)
Pythonで、地方競馬DATA ( UmaConn ) のデータを取得する。

# 注意
公式に公開されている内容ではなく、個人的に調査まとめた内容となります。

# やりたいこと
地方競馬DATAという、地方競馬の開催情報や結果を配信しているサービスがある。

https://saikyo.k-ba.com/members/chihou/

このサービスでは「UmaConn」というソフトウェアを経由して地方競馬の開催データや、結果情報、出走馬情報を取得することができる。
地方競馬版の JV-LINK ( https://jra-van.jp/dlb/sdv/sdk.html )のような動きをしてくれるらしい。

しかし、使い方などは公開されていない。
このサービスから地方競馬のデータを取得したい。

# 結論から先にいうと

以下のコード、操作で読むことができる。
しかし、データのパースが必要なので、C#などで読み替えてSDKを使用して読んだほうが良さそう。

```python
import win32com.client

NVLink1 = win32com.client.Dispatch("NVDTLabLib.NVLink")

return_code = NVLink1.NVInit("UNKNOWN")

# ここにデータを取得する処理を記述する

return_code = NVLink1.NVClose()
```

pywin32 ( https://pypi.org/project/pywin32/ ) を用いて、 地方競馬DATAのDLLをロードする。
Pythonは32bit版である必要があるので注意。

DLLは、UmaConnをインストールした際に 「 C:\Windows\SysWOW64\NVDTLab.dll 」
に配置されているのでそれを読み込むようにする。

あとは、JV-LINKと初期化をして、データをロードして、必要なデータを取り込んでいくことになる。


# 使用例

以下のように「JV-Linkインターフェース仕様書 ( https://jra-van.jp/dlb/sdv/sdk.html ) 」に書いてある「JV」を「NV」に置き換えると、大体似たことができる。

## JRA Data Lab(JV-LINK)の場合
```python
import win32com.client

JVLink1 = win32com.client.Dispatch("JVDTLab.JVLink")
return_code = JVLink1.JVInit("UNKNOWN")
dataspec = 'RACE'
fromtime = 20240126000000
option = 3
returnval = JVLink1.JVOpen(dataspec,fromtime,option, 0,0,'')
return_code = JVLink1.JVClose()

```


## 地方競馬DATA(UmaConn)の場合
```python
# 注意：Pythonは32bit版である必要がある

import win32com.client
# 「NVDTLab」ではなく「NVDTLabLib」とする必要があることに注意
NVLink1 = win32com.client.Dispatch("NVDTLabLib.NVLink")
return_code = NVLink1.NVInit("UNKNOWN")
dataspec = 'RACE'
fromtime = 20240126000000 
option = 3
returnval = NVLink1.NVOpen(dataspec,fromtime,option, 0,0,'')
```


https://jra-van.jp/dlb/sdv/sdk.html

Pythonでやると読み込んだデータのパースなど大変なので、結局該当DLLを同様にC#などで読んで、JV-LINKのSDKと併用して操作するといいかもしれない。


# 参考にした記事・リポジトリ
Pythonから.NET Frameworkで動作するJV-LINKを操作する方法は下記の記事を参考にしました。

https://zenn.dev/nozele/articles/c64e456d0c77e4

https://note.com/mare_ism/n/n8840715b32a4

また、下記リポジトリがC#でJV-LINK、UmaConn両方のデータを読み込むアプリケーションとして大変参考になりました。

https://github.com/kmycode/kmy-keiba
Vim 9.0 patchダイジェスト (2023/12)

[Vim](http://www.vim.org/)のリリースされた[patch](https://github.com/vim/vim/commits/master)の説明です。
([9.0.2139](https://github.com/vim/vim/commit/4f3480c94358e3b938594fc3574477e780525cd7) ～ [9.0.2189](https://github.com/vim/vim/commit/7e4f62a2575e8ce9ebb842d4246288138b11dff3))
**新機能、大幅な仕様変更には、:four_leaf_clover:が付いています。**
runtimeファイルも直接commitされるようになりました。ただ、数が多いので基本的にはここでは取り扱わないことにします。

* [9.0.2189](https://github.com/vim/vim/commit/7e4f62a2575e8ce9ebb842d4246288138b11dff3): `'breakindentopt'`が "sbr" を含み、 `'showbreak'` と `'nobreakindent'` がセットされている場合、間違った表示になる件を修正しました。
* [9.0.2182](https://github.com/vim/vim/commit/ff6f0d5c38e81f742e1161c1504fc6b8e45d9a1e): :four_leaf_clover: クラス/オブジェクトメソッド名において、アンダースコア2つ(`__`)で始まるものを将来用に予約しました。
* [9.0.2170](https://github.com/vim/vim/commit/e5437c542709b77ade084f96e60d84d4e847e6d3): :four_leaf_clover: `final`と`const`がクラス/オブジェクト変数をサポートしました。
* [9.0.2161](https://github.com/vim/vim/commit/d2e1c83962d4b392d1876d341c739d204553d01b): :four_leaf_clover: Vim9: インポートしたインターフェイスとクラスを使用できるようにしました。
* [9.0.2160](https://github.com/vim/vim/commit/2025af165ec68d831f0f0f668a3ceac3f39142ef): :four_leaf_clover: `instanceof()`の第2引数{class}をリストではなく、可変長引数でクラスの型を指定するように変更しました。

---
#### 凡例

|表記|意味|
|:------|:------|
|:four_leaf_clover:|新機能、大幅な仕様変更|
|:alien:|Vim開発者向けの追加、変更|
|`'hoge'`|オプション (`:h options`参照)|
|`:hoge`|Exコマンド (`:h :index`参照)|
|`hoge()`|組み込み関数 (`:h functions`参照)|
|`v:hoge`|Vim定義済変数 (`:h v:`参照)|
|`+hoge`|feature (`:h +feature-list`参照)|

---
#### 方針
[こちら](http://qiita.com/h_east/items/23cd0d10932505072e6b#%E6%96%B9%E9%87%9D)を参照。
はじめてのqiita cli(インストールから記事投稿まで)

# はじめに
qiita cliを利用することでローカルの環境から記事を投稿できるらしいので実際に使ってみたよ、という記事です。
qiita cliをインストールするときにつまづいたので、そのあたりについてまとめたいと思います。

# 動作環境
```
Node     :20.11.0
npm      :10.2.4
qiita-cli:1.3.1
```

# qiita-cliのインストール
基本的には[こちら](https://github.com/increments/qiita-cli)に書いてある順番にしたがって作業をしていけば問題ありません。
ただし,Nodeのversionに注意してください。公式では`Node.js 18.12.0`以上が必要となっていますがLTSを利用した方が良さそうです。詳細は[こちら](https://qiita.com/yuga_k/items/8d3a8078c72064a55459)の記事を見ていただきたいのですが、最新版にするとerrorを吐くようになります。私もこれで困って一旦インストールをあきらめかけました。そもそもLTSを使っておけば問題は発生しないので大人しくそれを使いましょう。

# qiita-cliを利用して記事の執筆しながらpreviewする
無事にInstallが完了したら、記事を執筆していきましょう。作成する記事はqiita-cliをinstallしたディレクトリ直下のpublicにmdファイルとして作成していきます。
そして、作成した記事をpreviewするのに使用するコマンドが`npx qiita preview`です。
このコマンドはqiita-cliをインストールしたディレクトリで実行します。
くれぐれも**publicディレクトリで実行しないようにしてください。**
実行するとわかりますが、publicディレクトリにpublicディレクトリが作成され、そのなかに今まで投稿した記事のファイルが作成されます。さっきまで執筆していた記事はどこにもないので間違えたらディレクトリごと消しておきましょう。

# 最後に
たいしたことないミスばかりだと思いますが、自分の備忘録としても使えると思ったのでまとめてみました。qiitaに記事を書こうと思ってもなかなか重い腰があがらなかったのですが、自分の好きなエディタで編集できるようになったので少しはモチベが上りました。今後は備忘録としてハマったこと、試したことを投稿していきます。
DjangoでPOSTされなくって沼った話

# はじめに
Djangoでアプリ外部からPOSTされる処理を実装したので、テスト前にとりあえずcurlでも叩いて実施したところ、200（成功）でも500（エラー）でもなく403（閲覧禁止）が返ってくる事象があった。
しかし、GETでリクエストするとそのロジックは問題なく実行された。メソッド以外に違いがなくDjangoの仕様で沼ったのでその時の対処法をまとめてみた。

# 実際に書いてたソースのイメージ
以下のように仮に「test」って関数があって、GETかPOSTかによってレスポンス内容を変えて返す関数があり、その関数がurls.pyにルーティングされているとする。

```python:views.py
from django.http import HttpResponse

def test(request):
  if request.method == 'POST':
    return HttpResponse('POST!!')
  else:
    return HttpResponse('GET!!')
```

```python:urls.py
from django.urls import path

from . import views

app_name = 'test'
urlpatterns = [
    path('test', views.test, name ='test')
]
```

# いざ実際にやってみると・・・
403が返ってきてきた。だがgetだと成功した。
![スクリーンショット 2024-01-27 23.22.49.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2640942/46ac5b03-e20e-25b5-55be-9e8fcbcc446e.jpeg)


# ちゃんと調べてみると
どうやら画面外からのリクエストなど、Djangoで発行したtokenがないPOSTのリクエストの場合にはPOSTができないらしい。

https://note.com/kodai_kk/n/n4ae9d91bc53a

加えて念の為Django公式をみると「クロスサイトリクエストフォージェリ (CSRF) 対策」ってことで、アプリ側で独自に発行したtokenを持ってないとPOSTはエラーになるよってのがDjangoの仕様としてあるらしい。すごい。

https://docs.djangoproject.com/ja/4.2/howto/csrf/#using-csrf

# 対応したこと
「@csrf_exempt」をつけて対応した。
ただ、Django公式にも書いてある通りアプリ側で独自発行したtokenを無効にしているので、Djangoで管理しているtemplateからPOSTする時には使わず、またセッション管理している処理には実装しないなど無闇やらに使うものでもない気がする。

```python:views.py
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt #追加

@csrf_exempt #追加
def test(request):
  if request.method == 'POST':
    return HttpResponse('POST!!')
  else:
    return HttpResponse('GET!!')
```

# 再度実行してみた
「@csrf_exempt」をつけて対応したところ、ちゃんと200が返ってきた。やったぜ。
![スクリーンショット 2024-01-27 23.31.39.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2640942/d9946d9e-d132-f451-1a36-0e3269592851.jpeg)

# 最後に
DjangoがデフォルトでCSRFの脆弱性対応をしてくれてたことで沼ってたが、「@csrf_exempt」は無闇やたらに使ってはいけない奥の手な気がする。
気が向いたら他にいいやり方を模索するかも。
macでstable-diffusion-webuiを実行しようとして ERROR: python3-venv is not installed, aborting...

# 環境
pythonのバージョンはpyenvで管理している
```
$ pyenv versions
 system
  3.10.11
* 3.11.4
```

# 原因
repoにある`webui-macos-env.sh`に
```webui-macos-env.sh
python_cmd="python3.10"
```
と記載されているが、
python3.10がインストールされていなかった。

# 解決策
かといってpyenvでバージョン管理している以上brewでpython3.10をインストールしたくはない。
なのでバージョン指定部分を削除してpyenvのglobalを参照するように修正。
```webui-macos-env.sh
python_cmd="python"
```

これでインストールがうまく行った。

# おまけ 実行エラー
画像生成してみようとしたら以下のエラーが発生
```
modules.devices.NansException: A tensor with all NaNs was produced in Unet. This could be either because there's not enough precision to represent the picture, or because your video card does not support half type. Try se
tting the "Upcast cross attention layer to float32" option in Settings > Stable Diffusion or using the --no-half commandline argument to fix this. Use --disable-nan-check commandline argument to disable this check.
```

以下のサイトを参考に、COMMANDLINE_ARGSを修正したら無事生成できるようになった。
https://soroban.highreso.jp/article/article-037

```webui-macos-env.sh
export COMMANDLINE_ARGS="--skip-torch-cuda-test --upcast-sampling --no-half-vae --use-cpu interrogate --no-half"
```
最後に`--no-half`追加しただけ
2023年12月5日以降eonetのOP25B規制回避のメールリレーができない

## 忙しい人のための要約

2023/12/05から
eonet回線(OP25B規制範囲内)のメール送信サーバー
`smtpauth.eonet.ne.jp`
`smtps.eonet.ne.jp`
メールリレーさせる際独自ドメインは使用できない
550 5.7.1 Sender rejected
相手にメールは届かない
回避方法はほぼない
たぶん似たようなプロバイダこれから増える
プロバイダのメールリレーに頼るのは甘え

## 影響を受ける人

* eonet回線(OP25B規制範囲)内に独自ドメインで**送信**を行うメールサーバーを構成している人
* 独自ドメインのメールリレー先に `smtpauth.eonet.ne.jp` や `smtps.eonet.ne.jp` を指定している人

たぶんこの問題の影響を受けるコアな人はめっっっっっちゃ少ないと思う
家庭用でもビジネス用でもメールリレーサーバーは上記2種だけだったのでプラン関係なく全部影響してそう

(オプテージ社の中の人、時間あったらsmtpsとsmtpauthのログ調査してみてほしい。
実は情弱が悪徳ITコンサルに騙されて格安ビジネス用途でメールリレー使っててすげー困ってるかもしれん。)

## 大本営発表(2024/01/27現在)

下記WEBページの下のほうに「差出メールアドレスを変更したメール送信の禁止」とある
(2023年12月5日より禁止の対策を実施)
https://eonet.jp/security/column/column_20231228/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3685999/b1ec2b37-f700-a584-4d86-938348cc0b2a.png)
▲リンク切れのためにスクショ


## よくわからないと定評のある説明

eonet回線(OP25B規制範囲内)にたてているメールサーバーからメールを送信する際には
eonet側の **OP25B規制があるため25番ポートを利用した送信は行えなく** なっている。
(これは2006年9月25日に実施したらしい)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3685999/8ea63749-59e4-0c5f-d9ff-24fdb8e8dd10.png)
▲よくわかるOP25B規制の状態図

OP25B規制範囲内にたてているメールサーバーからは25番ポート(smtp)で直接メールを送れないので、
かわりに`プロバイダのメールサーバー`にメール配送を頼むことになる。
`プロバイダのメールサーバー`だけは唯一OP25B規制の対象外であるため
相手先サーバーの25番ポート(smtp)にメールを配送できる。話のわかる奴だ。
(プロバイダから渡されたsmtpアカウントのIDとパスワードで認証を通して使わせてもらう)

ちなみにOP25B規制自体は割とありふれたものとなっていて、
むしろOP25B規制がない一般プロバイダを見つけることのほうが難しい。

### 2023/12/05まで・・・

上記のOP25B規制を他のプロバイダと同じように、`プロバイダのメールサーバー`を介すことで回避できていた。

自分で取得したドメイン(たとえば、`example.jj`)をメールサーバーで使用して
`iamceo@example.jj` みたいなメールアドレスで送受信が出来、
SPFやDKIMやDMARCなどの上級設定をしておけばgmailとのやり取りも正常に行えていた。

あの頃はよかった・・・。

### 2023/12/05から・・・

OP25B規制を回避するための`プロバイダのメールサーバー`で
**「ドメイン名の制限」** が行われてしまった。

要するに、eonet回線(OP25B規制範囲内)のメール送信サーバーからは、
「`*.eonet.ne.jp`というドメイン名の送信しか認めない！」という固い意思表示である。

2023/12/05まで何も問題がなかった構成で独自ドメインからメールを送ろうとすると、
eonetのメール送信サーバーからは、
「550 5.7.1 Sender rejected」
という無情なメッセージが送信者に返され、相手にメールは届かない。

現時点でeonetには2つのメール送信サーバーがある。
`smtpauth.eonet.ne.jp`(587ポート用) と `smtps.eonet.ne.jp`(587か465ポート用)。

いずれのサーバーも同様で、もちろんポート番号を変更しても無駄。
たどり着く結果は、「550 5.7.1 Sender rejected」。

### 無理矢理送る方法もあるにはあるが・・・

`プロバイダのメールサーバー`が「**ドメイン名の制限**」をしているのであれば、
そのルールの範囲内に無理矢理収めればメールを送信できる。

例えば、`smtps.eonet.ne.jp`では`*.eonet.ne.jp`というドメイン名の送信しか認められていない。

なら、差出人メールアドレスを詐称してしまえばいい。

メール差出人の内容を`*.eonet.ne.jp`というドメイン名に合致するようにすれば
たとえ存在しないメールアドレスであっても送信することは可能になる。

ただし、相手側メールサーバーも馬鹿ではないのでなりすましメールとしてDROPされるだけ。
DKIMやDMARKを思いっきり踏み外しているので高確率でDROPされる。
相手にはメールが届かない。
奇跡的に届いたとしても、「誰こいつ？」ってなるだけなのでゴミ箱行き。

実用的な回避方法ではない。

## 結局どうすれば？

OP25B規制範囲の内側からメールサーバーを出すしかない。

現状eonetにはそのようなサービスもなく、もし存在してもビジネス用なので月5,000円は軽い。
月5,000円出すなら類似のクラウドサービスとかレンタルサーバーで安いのがいっぱいあるし。

## これから似たようなプロバイダどんどん増える

現時点で同様の問題で困っている人はそうそう多くない。
(プロバイダ一般回線の下に自宅サーバーぶら下げてる人がどれだけいるかは知らないが・・・)

ただ、今後どんどん増えていくと思われる。

何故なら、**Google(Gmail)にメールを送った際に受け取ってもらえなくなる**可能性があるから。

2024年2月以降、Google(Gmail)は定めたルール外からのメール受信を拒否するらしい。
https://support.google.com/a/answer/81126?visit_id=638419623086793590-1674363761&rd=1

このルールの中に、「**迷惑メール率0.3％以下**」っていうのがあって
それを達成させるためには、できるだけ信頼できるメールアドレスのみに絞って送信を許可したほうがいい。
自社のメールサーバーから送ったメールがGmailに弾かれるとビジネス上めっちゃ困る会社が多いし。

まぁ時代の流れってことっすね・・・
なんかメールサーバーサービス探すか・・
JavaScript でシェーダーを扱っていて出てくるいくつかの用語を自分用に少し整理する：OpenGL ES、WebGL、GLSL ES など

## はじめに
この記事の内容はタイトルの通り、シェーダーを扱っていると出てくるいろいろな用語の一部を、自分用に少し整理してみるという内容です。

p5.js を使ったプログラムなど JavaScript　のプログラムで、例えば以下のようなシェーダーを使ったプログラムを作るのに、参考になりそうな情報をいろいろ見ていきました。

https://twitter.com/youtoy/status/1750131052453179692

そんな中で、聞いたことがあるキーワードや初めて聞くキーワードなどが多く出てきました。

そのキーワードの中で、これまで耳にしていたキーワードも多かったものの、正確な意味をあまり気にしてなかったものもあったので、キーワード同士の関係性や位置付けの違いで、混乱してき部分がありました。

そこで、以下のキーワードをとりあげて、その関係性などを自分用に整理して書いてみようと思います。

- OpenGL / OpenGL ES
- WebGL （1.0、2.0）
- GLSL / GLSL ES

## OpenGL（Open Graphics Library） / OpenGL ES（OpenGL for Embedded Systems）
OpenGL は、グラフィックスハードウェア向けの2次元/3次元コンピュータグラフィックスライブラリで、多様な描画デバイスを包括するグラフィックスAPI のオープン標準規格として策定が行なわれている。グラフィックスハードウェア（GPU）の機能および性能を活用した高速なグラフィックス描画が可能。

そして、OpenGL ES は、OpenGL のサブセット。OpenGL と同じくグラフィックスハードウェア（GPU）の機能および性能を活用した高速なグラフィックス描画が可能。
iOS や Android などの携帯端末向け OS で採用されていたり、プレステ3 やニンテンドー3DS にも採用された。

### 参考
●OpenGL - The Industry Standard for High Performance Graphics
　https://www.opengl.org/

●OpenGL - Wikipedia
　https://ja.wikipedia.org/wiki/OpenGL

●OpenGL ES - Wikipedia
　https://ja.wikipedia.org/wiki/OpenGL_ES

## WebGL (Web Graphics Library)
WebGLは、互換性があるウェブブラウザーでプラグインを使用せずにインタラクティブな 3D や 2D のグラフィックをレンダリングするための JavaScript API。
HTML の <canvas> 要素へ描画を行う。その描画には、ハードウェアのグラフィックアクセラレーションを利用可能。

WebGL 1.0 は、OpenGL ES 2.0 の派生規格で、WebGL 2.0 は OpenGL ES 3.0 の派生規格。

以下は、「[サンプルで理解するWebGL 2.0 - WebGL 2.0の利点とは - ICS MEDIA](https://ics.media/entry/16060/)」のもの。
　
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/ece2e355-21f1-bcd7-76e1-c271d5060953.png)

### 参考
●WebGL: ウェブの 2D および 3D グラフィック - Web API | MDN
　https://developer.mozilla.org/ja/docs/Web/API/WebGL_API

●WebGL - Wikipedia
　https://ja.wikipedia.org/wiki/WebGL#%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E3%81%AE%E6%87%B8%E5%BF%B5

●WebGL - OpenGL ES 2.0 for the Web
　https://jp.khronos.org/webgl/

●wgld.org | WebGL2: WebGL 2.0 と WebGL 1.0 |
　https://wgld.org/d/webgl2/w001.html

●WebGL Fundamentals
　https://webglfundamentals.org/webgl/lessons/webgl-fundamentals.html

## GLSL（OpenGL Shading Language / OpenGL シェーディング言語） / GLSL ES
GLSL は、C言語の構文をベースとした、グラフィックスパイプラインを制御するための高レベルシェーディング言語。GLSL の派生規格として、組み込み環境向けの OpenGL ES用のシェーダー言語の GLSL ES がある。

WebGL 1.0 では GLSL ES 1.0 が利用されており、WebGL 2.0 からは GLSL ES 3.0 も利用可能。

●GLSL - Wikipedia
　https://ja.wikipedia.org/wiki/GLSL

●wgld.org | WebGL: シェーダの記述と基礎 |
　https://wgld.org/d/webgl/w008.html

●GLSL シェーダー - ゲーム開発 | MDN
　https://developer.mozilla.org/ja/docs/Games/Techniques/3D_on_the_web/GLSL_Shaders

●wgld.org | WebGL2: WebGL 2.0 と GLSL ES 3.0 |
　https://wgld.org/d/webgl2/w003.html

●WebGL Shaders and GLSL
　https://webglfundamentals.org/webgl/lessons/webgl-shaders-and-glsl.html
Githubの使い方　その4

前回[Githubの使い方　その3　(PUSHについて)](https://qiita.com/ki_takada/items/3fbd40b6e3c4b95f85c1)作成時から、時間が経ってしまい記憶も曖昧になっているので、下記の設定で`push`するまでをやり直した。
ローカルリポジトリ名：`git-test`
リモートリポジトリ名：`test_repository`

#### Githubの画面の説明
![スクリーンショット 2024-01-26 22.25.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570384/e693ca88-08ba-5c92-bd19-a8bf7e41c43a.png)

`Raw`	ファイルの中身をそのままブラウザ上に表示できる。
`Blame`	ファイル内の行単位で、いつ誰が変更を加えたか確認できる。
`History`	ファイルのコミット履歴を時系列で確認できる。
Historyの実際の画面↓
![スクリーンショット 2024-01-26 22.28.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570384/38aae0ad-4c7d-e8f9-410d-ac5323b2316c.png)

## 変更を取り消す

### `checkout`コマンド と`git reset`コマンド
#### 注意)ローカルリポジトリまでcommitしてしまうと上記2つコマンドでは取り消せない。
![スクリーンショット 2024-01-27 12.18.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570384/a0e403e5-b2c8-0d3b-d08c-cc86ee2e6b60.png)
![スクリーンショット 2024-01-27 13.09.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570384/ed170f00-e0ea-d309-9d87-e98a8098574a.png)
![スクリーンショット 2024-01-27 12.38.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570384/1f1e5a9d-8343-3d62-9776-f7c5f3cb6ad4.png)
《まとめ》
⚫︎git addする前の変更は`checkout`で消せる
⚫︎git addした後の変更は、`git reset HEAD`でまずインデックス内をgit add前の状態に戻してから、ワークツリー内のデータに対してcheckoutする
取り消しコマンド一覧
```shell
# インデックスの変更をファイルごとに取り消す
git reset HEAD ファイルパス
# インデックスの変更をディレクトリごと取り消す
git reset HEAD ディレクトリパス
# インデックスの変更を全て取り消す
git reset HEAD .
# インデックスの変更を取り消し
git reset HEAD ファイル名
# 直前のコミットメッセージを修正する場合
git commit --amend -m "修正後のコミットメッセージ"
# コミットし忘れたファイルを追加する場合
git add [直前のコミットに追加するファイルパス]
git commit --amend
```
### ファイル名の変更
```shell
#注意点、コメント実行するとファイル名の変更と同時にaddも実行される。
git mv 変更前の new_check.txt
```
『ウアイヌコㇿ コタン アカㇻ』のウウェランカラㇷ゚

思うところあって、国立アイヌ民族博物館 第3回テーマ展示『[ウアイヌコㇿ コタン アカㇻ](https://nam.go.jp/exhibition/floor2/special/akar2022/)』(2022年12月13日～2023年2月12日)の「ウウェランカラㇷ゚」を、文字起こししてみた。

![ウウェランカラㇷ゚(2023年1月26日、安岡孝一撮影)](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428317/dc9b21e0-0fcd-3bd7-1f82-07b715e7f42d.jpeg)

> イランカラㇷ゚テ。
>
> ウアイヌコㇿ コタン オルン シネウパ ウタㇻ、 エチコプンテㇰ ナ。
> アヌココㇿ アイヌ イコロマケンル 第3回テーマ展示
> 『ウアイヌコㇿ コタン アカㇻ ―民族共生象徴空間(ウポポイ)のことばと歴史―』
> セコㇿ チレコ ワ オロ タ エチエカノㇰ。
> 2020パ 7チュㇷ゚ 12ト ワノ
> アイヌ プリ カンナ アシトゥリレ クニネ ウアイヌコㇿ コタン アン。
> タン コタン オルン
> アイヌ ウタㇻ カ シサㇺ ウタㇻ カ レプンクㇽ ウタㇻ カ シネウパ。
> アイヌ プリ エヤイパカㇱヌ。
> “ウポポイ” アナㇰネ ウアイヌコㇿ コタン ポンレ ネ。
> タン トゥンプ オッタ
> “ウアイヌコㇿ コタン ヘマンタ クス アカㇻ ルウェ アン?
> マカナㇰ アカㇻ ルウェ アン?” セコㇿ アン ペ
> エチヌカレ ルウェ ネ。
> ホㇱキノ 1965パ ワノ ポロト オルㇱペ ネヤ
> アヌココㇿ アイヌ イコロマケンル オルㇱペ ネヤ
> エチエコイソイタㇰ ワ
> ウアイヌコㇿ コタン アカㇻ カトゥ エチヌカレ。
> イマカケタ テタ アエイワンケ アシㇼ イタㇰ
> マカナㇰ アエヤイコシラㇺスイパ カトゥ エチヌカレ。
> ウアイヌコㇿ コタン オッタ カ
> イコロマケンル オッタ カ
> イタㇰペ ネヤ オルㇱペ ニソㇱ ネヤ アン。
> アイヌ イタㇰ アニ エネ アカㇻ ヒ エチヌカレ。
> イマカケタ アヌココㇿ アイヌ イコロマケンル アカㇻ パㇰノ
> 東京 札幌 白老 オッタ 「国立アイヌ民族博物館設立準備室」 アン ワ
> ネヒタ イコㇿ トゥンプ オッタ
> イコㇿ ネヤ カンピソㇱ ネヤ イノカ ネヤ ウサ オカイ ペ
> チウォエロㇱキ エトコイキ カトゥ エチヌカレ ルウェ ネ。
> イヨッタ イヨㇱノ
> ポロトコタン アオシレパレ カトゥ
> イランマカカ ピㇼカ ノカハ ネ アウㇰ ワ アカㇻペ
> 「ポロトコタン最後の一日」 セコㇿ アン イノカ エチヌカレ。
> ウアイヌコㇿ コタン アカㇻ カトゥ アヌカㇻ ヤクン
> イコロマケンル アカㇻ イッケウェ カ ピㇼカノ アエラムアン ナンコㇿ。
> テワノ ピㇼカノ ヌカㇻ ワ ウンコレ ヤナニ。
>
> 2022パ 12チュㇷ゚ 13ト
> アヌココㇿ アイヌ イコロマケンル
> サパネクㇽ エトゥナンカㇻ

[2023年1月26日の日記](https://srad.jp/~yasuoka/journal/659785/)にも書いたが、館長の佐々木史郎(エトゥナンカㇻ)の読み上げでは、「2020パ」が「hotne pa ikasma tuasiknewanhotne pa」、「1965パ」が「asikne pa ikasma rehotnepa ikasma wanpe etuasiknewanhotne pa」、「2022パ」が「tu pa ikasma hotne pa ikasma tuasiknewanhotne pa」となっており、かなり悩ましかった。ただ、この読み上げは録音禁止だったので、もう失われてしまったのだろうか。
AdaptiveCard: Action.Submit 毎に Validation を有効／無効を切り替える 

# 背景

質問があがってたので、ちょっと調査してみた記録

個別の Item に対する Validation はこれで対処出来ないけどね。

内容によるとは思うけど、以下のような切り替えをするってのも手かも？

https://qiita.com/baku2san/items/84c4e0732d0df2b660d6

# 概要

- associatedInputs を使うだけ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/7a1c92e6-dec5-231b-eb3c-f9843aa791d7.png)

https://www.adaptivecards.io/explorer/Action.Submit.html

# 利用例

例を見るのが一番かな、と

- 下図のように associatedInputs を "none" に設定すると、validation が機能しなくなる

但し、このままだと何も送信できないので、"data" を設定して送信データを制御してやると良い。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/a517496a-ea2b-2a6e-137b-6cd05ddfd895.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/cb8a9800-4688-6432-77d8-3d8cc32deee6.png)


## sample code

```json: associatedInputs
{
    "type": "AdaptiveCard",
    "version": "1.4",
    "msteams": {
        "width": "full"
    },
    "body": [
        {
            "type": "Input.Text",
            "label": "ItemName を入れてください",
            "isRequired": true,
            "id": "InputName",
            "placeholder": "Alphabetical12",
            "regex": "^\\w+\\d{2}$",
            "errorMessage": "アルファベット一文字以上に続き二桁の数字を入れてください。",
            "maxLength": "8"
        },
        {
            "type": "Input.Number",
            "placeholder": "25",
            "label": "年齢を入れてください",
            "id": "InputAge",
            "errorMessage": "20-99歳で",
            "isRequired": true,
            "min": "20",
            "max": "99"
        },
        {
            "type": "Input.Date",
            "errorMessage": "2021/11/1 より後で",
            "min": "2021/11/1",
            "max": "2021/12/1",
            "id": "InputDate"
        },
        {
            "type": "Input.Time",
            "errorMessage": "12:00 - 14:00 で",
            "min": "12:00",
            "max": "14:00",
            "id": "InputTime"
        },
        {
            "type": "ActionSet",
            "actions": [
                {
                    "type": "Action.Submit",
                    "title": "Validate",
                    "data": {
                        "InputTime": "00:00"
                    },
                    "associatedInputs": "auto"
                },
                {
                    "type": "Action.Submit",
                    "title": "NOT Validate",
                    "data": {
                        "InputName": "default name"
                    },
                    "associatedInputs": 
                        "None"
                }
            ]
        }
    ],
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}
```

# あとがき

"None" or "Auto" ではなくて、対象 ID を設定出来るようにしてくれると、細かい設定が出来るんだけどね・・と :laughing:
ABC338をPythonで（A～E）

AtCoder Beginner Contest 338

https://atcoder.jp/contests/abc338

# A問題
isupperとislowerを使って確かめる
```python:A
s = input()
if s[0].isupper() and (len(s) <= 1 or s[1:].islower()):
    print("Yes")
else:
    print("No")
```
# B問題
問題文の通りに調べる
```python:B
s = input()
lst = [0] * 26
for s_i in s:
    lst[ord(s_i) - ord("a")] += 1

print(chr(ord("a") + lst.index(max(lst))))
```
# C問題
料理の種類が１つだけだったら割れば簡単に求まる
よって、０からaだけで考えた時の上限値までの全通りで残った材料でbを作れる数を計算して
aの数とbの数の和の最大値が答え
```python:C
n = int(input())
q = list(map(int, input().split()))
a = list(map(int, input().split()))
b = list(map(int, input().split()))

ans = 0
limit = min(q[i] // a[i] for i in range(n) if a[i] > 0)
for x in range(limit + 1):
    y = 10 ** 9
    for i in range(n):
        if b[i] == 0:
            continue
        elif q[i] < a[i] * x:
            y = - 10 ** 9
        else:
            y = min(y, (q[i] - a[i] * x) // b[i])

    ans = max(ans, x + y)

print(ans)
```
# D問題
累積和
$x_i$と$x_{i+1}$の間の橋を壊した時の長さと、そうではないほうを壊した時の長さを累積和を使ってうまいこと計算する（下式参照）
```python:D
n, m = map(int, input().split())
x = list(map(lambda x:int(x) - 1, input().split()))

dist = [0] * n
for now, to in zip(x, x[1:]):
    dist[min(now, to)] += n - abs(now - to)
    dist[max(now, to)] -= n - abs(now - to)
    
    if min(now, to) > 0:
        dist[0] += abs(now - to)
        dist[min(now, to)] -=  abs(now - to)
    if max(now, to) < n:
        dist[max(now, to)] +=  abs(now - to)
    
for i in range(n - 1):
    dist[i + 1] += dist[i]

print(min(dist))

```
# E問題
メモ化再帰を使って
1. $(a,b)$間に$a,b$の外側とつながっている組はないか
1. もし間どおしで繋がっている組$(x, y)$があったらそのペアで調べる
1. 次に$y+1$を調べる（2へ）

再帰上限に気を付けて実装する
```python:E
from sys import setrecursionlimit

setrecursionlimit(10 ** 7)

n = int(input())
data = [list(map(lambda x:int(x) - 1, input().split())) for _ in range(n)]

p = [0] * (2 * n)
for a, b in data:
    p[a] = b
    p[b] = a

memo = [False] * (2 * n)
def check(a, b):
    global memo
    if memo[a]:
        return
    else:
        memo[a] = True
        memo[b] = True
        now = a + 1
        while now < b:
            if p[now] < a or b < p[now]:
                exit(print("Yes"))
            elif now < p[now]:
                check(now, p[now])
                now = p[now]
            now += 1


for i in range(2 * n):
    if i < p[i]:
        check(i, p[i])

print("No")
```
RHEL9にJenkinsサーバーをセットアップする

## 前書き
RHEL9にJenkinsサーバーをセットアップする時のメモです。

## 環境確認
- OSを確認：RHEL9 64bit
~~~
$ cat /etc/redhat-release
Red Hat Enterprise Linux release 9.3 (Plow)
~~~
~~~
$ arch
x86_64
~~~

- JAVAバージョンを確認：17.0.10（jenkins-2.426の場合はJava17が必要です）
~~~
$ java -version
openjdk version "17.0.10" 2024-01-16 LTS
OpenJDK Runtime Environment (Red_Hat-17.0.10.0.7-1) (build 17.0.10+7-LTS)
OpenJDK 64-Bit Server VM (Red_Hat-17.0.10.0.7-1) (build 17.0.10+7-LTS, mixed mode, sharing)
~~~
※JAVAがインストールされていない場合は先にJAVAをインストールして下さい。
~~~
# yum install fontconfig java-17-openjdk
~~~

※jenkinsはJDKも必要なので、JDKもインストール
~~~
# yum install java-17-openjdk java-17-openjdk-devel
~~~

※ついでに環境変数も設定する
~~~
# export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
# export PATH=$PATH:$JAVA_HOME/bin
~~~

※JAVAバージョンを切り替える時、以下のコマンドを使ってください。
~~~
#update-alternatives --config 'java'
~~~

- Mavenのバージョン確認:3.6.3
~~~
# mvn --version
Apache Maven 3.6.3 (Red Hat 3.6.3-15)
Maven home: /usr/share/maven
Java version: 11.0.22, vendor: Red Hat, Inc., runtime: /usr/lib/jvm/java-11-openjdk-11.0.22.0.7-2.el9.x86_64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.14.0-362.18.1.el9_3.x86_64", arch: "amd64", family: "unix"
~~~

※Mavenがインストールされていない場合はインストールして下さい。
~~~
$ sudo dnf install maven -y
~~~

- Gitのバージョン確認:2.39.3
~~~
# git --version
git version 2.39.3
~~~

※Gitがインストールされていない場合はインストールして下さい。
~~~
$ sudo yum install git
~~~

## Jenkinsをインストール
基本は公式サイトからコマンドをコピーして実行するが、今回は以下のコマンドを実行してインストールしました。

公式サイト：

https://pkg.jenkins.io/redhat-stable/

- Rootユーザーを切り替え
~~~
$ su
パスワード：
~~~

- jenkinsのリポジトリーを取得
~~~
# wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
/jenkins.repo
# rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key
# yum update
~~~

- jenkinsをインストール
~~~
# yum install jenkins
~~~

- jenkinsを起動
~~~
# service jenkins start
~~~

- Webブラウザで
http://127.0.0.1:8080/
を叩いて以下の画面が出てきたら成功です。

![Screenshot from 2024-01-27 22-27-36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1442793/8254808b-75f8-6f5c-ce56-fe9961c3aa88.png)

初期パスワードは、”/var/lib/jenkins/secrets/initialAdminPassword”に書かれているので、それをコピーしてテキストボックスに貼り付けたらログインできます。
パスワードを確認：
~~~
# cat /var/lib/jenkins/secrets/initialAdminPassword
~~~

パスワードを入力後、ログイン成功の画面：
![Screenshot from 2024-01-27 22-29-23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1442793/5047a10d-8a75-a063-1e1f-b9bf31599dde.png)

Jenkinsの設定（JDK）：
![Screenshot from 2024-01-27 23-23-57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1442793/ecf200da-f809-6005-8eff-81e529c1b90a.png)

Jenkinsの設定（Git）：
![Screenshot from 2024-01-28 08-24-39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1442793/c1236dc4-e88f-2bb0-ad06-e7da805f362b.png)

Jenkinsの設定（Maven）：
![Screenshot from 2024-01-27 23-31-35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1442793/63e92dd9-d967-2550-c5ae-324a97a474f7.png)


ここでRHEL9にJenkinsサーバーをセットアップ完了です。
「破壊的代入を避ける」：よりクリーンなコードのためのリファクタリング戦略

## はじめに

プログラミングにおいて、変数を複数の用途で使い回すアンチパターンはよく見かけます。このアンチパターンを「破壊的代入」と呼びます。では、「破壊的代入」とは何か、そしてそれを避けるためにはどうすれば良いのでしょうか？

## 対象者

- 駆け出しエンジニア
- プログラミング初学者

## 用語の説明

### 破壊的代入
破壊的代入とは、同一の変数に複数の値を順次代入し、その変数の意味を時間の経過とともに変化させることを指します。これにより、コードの可読性が低下し、バグの原因となりやすくなります。

### 説明用の変数の導入
この問題を解決するための一つの手法が、「説明用の変数の導入」です。これは、異なる計算ステップや意味を持つデータごとに新しい変数を用意し、その変数名でコードの意図を明確に示す方法です。

## 結論

異なる目的ごとに専用のローカル変数を用意し、コードの意図を変数名で明確にすることが最善です。これにより、コードの可読性が向上し、将来のエラーを防ぐことができます。

## 具体的な利点
このアプローチには以下のような利点があります。

* 可読性の向上: 各変数の役割が明確になり、コードが読みやすくなります。
* バグの減少: 変数が一意の目的でのみ使用されるため、誤った使用によるバグが減少します。
* 保守性の向上: コードの意図が明確になるため、将来的な変更やデバッグが容易になります。

## 実際のコード

### アンチパターン
```javascript:example.js
let price = quantity * unitPrice;

if (price < 3000) {
    price += 500; // 送料
}

price = price * taxRate();
```

priceを下記3つの用途で使い回しています

* 数量 × 単価の計算
* 送料を加算した結果
* 加算した結果に税率を適用した値

### リファクタリング
```javascript:example.js
const basePrice = quantity * unitPrice;

let shippingCost = 0;
if (basePrice < 3000) {
    shippingCost = 500; // 送料
}

const totalPrice = (basePrice + shippingCost) * taxRate();
```

それぞれの計算ステップごとに変数を用意しました。これにより、それぞれの計算の目的が明確になり、コードの理解が容易になります。

## おわりに
破壊的代入を避けることは、プログラミングにおける重要なスキルの一つです。良いコードを書く開発者になりたい。

## 参考記事
[現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法](https://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E5%BD%B9%E7%AB%8B%E3%81%A4%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E8%A8%AD%E8%A8%88%E3%81%AE%E5%8E%9F%E5%89%87-%E5%A4%89%E6%9B%B4%E3%82%92%E6%A5%BD%E3%81%A7%E5%AE%89%E5%85%A8%E3%81%AB%E3%81%99%E3%82%8B%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E3%81%AE%E5%AE%9F%E8%B7%B5%E6%8A%80%E6%B3%95-%E5%A2%97%E7%94%B0-%E4%BA%A8/dp/477419087X/ref=sr_1_2_sspa?adgrpid=60120324664&gclid=CjwKCAiAzc2tBhA6EiwArv-i6TdrQUyqdDeJmmybFtNJa3Wkq-Sq54bzEiso8V-cFLydARJ6Bf48mRoCg10QAvD_BwE&hvadid=678984364920&hvdev=c&hvlocphy=1009324&hvnetw=g&hvqmt=e&hvrand=4799780119195124792&hvtargid=kwd-330189736777&hydadcr=27268_14738581&jp-ad-ap=0&keywords=%E7%8F%BE%E5%A0%B4%E3%81%A7%E5%BD%B9%E7%AB%8B%E3%81%A4%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E8%A8%AD%E8%A8%88%E3%81%AE%E5%8E%9F%E5%89%87&qid=1706317830&sr=8-2-spons&sp_csd=d2lkZ2V0TmFtZT1zcF9hdGY&psc=1)
TilePaletteでSpriteModeがMultipleのTileが表示されなかったときの解決

ちょっと手こずったので。原因は至極単純だったので完全に備忘録用です。

# 結論
Pivotを調整する。

# 起きた問題
SpriteModeをMultipleにして、CustumでSliceしたSpriteをTileにしようとしたら、TilePaletteにTile画像が表示されなかった。Inspectorで確認すると、確かにTileが格納されてあるが、画像はやはり表示されず、使ってもScene上にも表示されなかった。

# 解決方法
SpriteEditorを確認したところ、SliceしたSprite一つひとつのPivotがCustomの状態になっていた。Centerに直してみたところ、ちゃんと画像がTilePalette上に表示されるようになった。
【Linux】シェルの変数について

# 変数の設定方法
例)TODAY変数を出力すると，dateコマンドの結果が表示されるようにする。
変数は以下の2通りで設定できる。
```ruby:ubuntu
user:~ $TODAY=`date`
user:~ TODAY=$(date)
```

# 変数の出力方法
echoと$を使う
```ruby:ubuntu
user:~ echo $TODAY
2024年 1月 27日 土曜日 20:11:52 JST
```

# 規定の変数
## $?
直前のコマンドの終了ステータスを設定

```ruby:ubuntu
user:~ echo $?
127
```

## $?
現在のシェルのプロセス番号

```ruby:ubuntu
user:~ echo  $$
169
```

## $*
全ての引数が格納される

## ${10}
10番目の引数が格納される

## $0
実行したシェルスクリプト名が設定される
```ruby:ubuntu
user:~ echo $0
-bash
```
[Ubuntu22.04LTS] Windows + Ubuntuデュアルブート環境構築後のUbuntu初期設定

## Ubuntu + Windowsデュアルブートマシン構築
　計算用マシンを構築するとき、先々にWindowsマシンとしても使えるようUbuntuとWindowsのデュアルブートマシンにすることにしている。このデュアルブートマシン構築法とUbuntuの設定まとめ。

## OSインストール
　BiosのBootメニューからUEFIに設定しておく。マシンにはM.2 SSDかSATA SSDを2台取り付け１台にWindowsを、残りにUbuntuをインストールする。後々の設定が楽になるよう先にWindowsをインストールしておく。Windowsが起動できることを確認する。
　UbuntuライブUSBを作成し残りのディスクドライブにUbuntuをインストールする。インストール終了後、再起動するとブートロゴが表示された後、起動するOSを選択できるUbuntu Bootloader画面が現れる。デフォ設定はUbuntuになるのでWindowsを起動したい場合は矢印キーでWindowsを選択すればよい。![UbuntuBootLoader.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636689/4d3ffa33-caa5-8ebc-8427-32bd754f1913.jpeg)
（焦点スレズレなのはすみません）

## 時刻ずれ修正
　デュアルブートにするとWindowsとUbuntuで時刻が９時間ずれてしまう。これはWindowsとUbuntuで時刻管理法が異なるため。なので時刻ずれが生じないよう設定する。これはUbuntu側、Windows側で設定することが可能だがここではUbuntuで設定を変える。ターミナルから下記コマンドを入力するだけ。
```timectl:terminal
$ > sudo timedatectl set-local-rtc true
```
以上。

## Proxy設定
 Proxy設定が必要な環境にある場合は下記を設定する。
```proxy:~/.bashrc
export http_proxy="http://proxy_ip:port/"
export https_proxy="http://proxy_ip:port/"
export ftp_proxy="http://proxy_ip:port/"
```
~/.bashrcファイルを保存後、
```source:terminal
$ > source ~/.bashrc
```
で~/.bashrc設定を再読み込み。

sudo権限でaptのproxy設定。
```proxy:/etc/apt/apt.conf
Acquire::http::Proxy "http://proxy_ip:port";
Acquire::https::Proxy "http://proxy_ip:port";
```

gitインストール済みの場合、
```gitconfig:terminal
$ > git config --global http.proxy http://proxy_ip:port
$ > git config --global https.proxy http://proxy_ip:port
```
内容は~/.gitconfigに書き込まれる。

wgetについては~/.bashrcが設定されればproxyを通ったが、もし問題が生じた場合は下記。
```wget:~/.wgetrc
http_proxy=http://proxy_ip:port/
https_proxy=http://proxy_ip:port/
```

もし独自certificateを設定する必要がある場合は[こちら](https://qiita.com/msi/items/9cb90271836386dafce3 "CA")を参照。Ubuntu18.04と記載されているが22.04でも使える。

あとは必要な環境構築に必要な設定を続ければよい。pyenvとpythonインストールなどは[別ページ](https://qiita.com/k5884/items/f246fd132ba25794d833 "python")で紹介済み。

## まとめ
簡単ですがWindowsとUbuntuのデュアルブート環境を構築した後、最低限のUbuntu設定のまとめでした。
【SwiftUI】コピー&ペーストを制御する（macOS）

# はじめに
copyableの公式ドキュメントにあるサンプルコードを元にコピー&ペーストを制御してみます。

https://developer.apple.com/documentation/swiftui/view/copyable(_:)

今回作成するサンプルアプリはコマンドのコピー(⌘ + c)とペースト(⌘ + v)でのみ反応します。

# サンプルアプリ
![画面収録-2024-01-27-20.43.52.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/60a14662-93f7-6fa0-4535-d8bb90d32f70.gif)

# 実装
```swift
import SwiftUI

struct ContentView: View {
    @State private var strings = ["Alpha", "Beta", "Gamma"]
    @State private var selection: Set<String> = []
    
    var body: some View {
        List(strings, id: \.self, selection: $selection) {
            Text($0)
        }
        // コピー
        .copyable(Array(selection))
        // ペースト
        .pasteDestination(for: String.self) { string in
            strings.append(contentsOf: string)
        }
    }
}
```

# おわり
macOS13から使用できます。
コマンドにしか反応しないので、アプリに組み込むときはアナウンスがないと気づかれないかもしれないですね

# 公式ドキュメント
https://developer.apple.com/documentation/swiftui/view/copyable(_:)

https://developer.apple.com/documentation/swiftui/view/pastedestination(for:action:validator:)
自作PCのメモリ交換後，nvidia-smiでエラーが発生する時の対処法

## はじめに
同様のケースで困った方への共有と備忘録として記事を残します

### やったこと
- 自作PCのメモリを増設するため，「8GB × 4 (32GB)」のメモリから「32GB × 2 (64GB)」のメモリに変更
- メモリ増設後，GPUを使ってコードを回したところGPUが使えないとのエラー発生
- 確認のため`nvidia-smi`コマンドを入力したところエラー発生
- ネット検索で解決策が見つからなかったため試行錯誤で解決



## 対象者
この記事は下記のような人を対象にしています．

- 今まで使えていたGPUや`nvidia-smi`が**メモリ交換後**に突然使えなくなった方

## 結論
- **BIOSの設定を変更する**

## 詳細
### 1．メモリ取り付け
まず，以下のサイトを参考にしてメモリを取り外しと取り付けを行いました．
[【自作PC】メモリーの取り付け方 （初心者向け）](https://achipon.com/memory/)
取り付けた，メモリはこちらです．
![IMG_5192.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3078706/f8d3f111-6d39-7411-471c-cfb61b8f1371.jpeg)


### 2．エラー
PCを起動し，GPUを使ってコードを回したところエラー発生
```
Error: no integrated GPU detected.
```
そこで，ChatGPTに聞いてみてアドバイスを求めました．
```
1. システム仕様を確認する： コンピュータに統合GPUが搭載されているか確認してください。すべてのプロセッサに統合グラフィックスが付いているわけではありません。

2. ドライバーを更新または再インストールする： 古いまたは破損したドライバーが原因でGPUが検出されない場合があります。GPUメーカー（例：インテル、AMD、NVIDIA）のウェブサイトを訪れて、最新のドライバーをダウンロードしてください。

3. BIOS/UEFI設定を確認する： 時々、統合GPUはBIOS/UEFI設定から無効にされています。システム起動時にこれらの設定にアクセスし、統合GPUが有効になっていることを確認してください。

4. ハードウェアの問題： もし以前はGPUが検出されていたが、突然検出されなくなった場合、ハードウェアの問題が考えられます。これは、緩んだコンポーネントやもっと深刻な何かによるものかもしれません。自信があれば、コンピュータを開けてチェックするか、専門家に持って行ってください。

5. オペレーティングシステムの問題： 時々、オペレーティングシステムの最近の更新や変更がハードウェアとの互換性の問題を引き起こすことがあります。最近の更新をロールバックするか、オペレーティングシステムをリセットすることを検討してください。

6. メーカーのサポートを利用する： 完成品のシステムやラップトップを使用している場合、メーカーのサポートリソースやカスタマーサービスが、モデル固有のガイダンスを提供するかもしれません。

7. 専門家に相談する： ハードウェアの問題のトラブルシューティングに自信がない場合は、専門技術者に相談するのが賢明です。

デスクトップ、ラップトップ、特定のブランドやモデルのコンピュータによって、これらのステップは異なる場合がありますので、ご注意ください。
```
この2と3を試すことにしました．

### 2. ドライバーを更新または再インストールする
- メモリの取り替えでnvidia-driverが消えたと思い，ドライバーを入れ直した
- 再起動して`nvidia-smi`コマンドを実行したが，エラーが解決しない

そこで，3を試すことにしました．

### 3. BIOS/UEFI設定を確認する
PCを起動中に，エスケープキー (Esc)を押してBIOS画面に入ります．
その後，以下のような画面が出ればOKです．

:::note warn
注意
お使いのPCによって表示されるBIOS画面は異なります
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3078706/265ef12a-b29d-2b41-4321-4ae1d8773559.png)
[画像引用 ->【WSL】エラー：0x80370102 の対処法：CPU仮想化を有効にする](https://qiita.com/seichi25/items/62877a9dede13ff0e032)


次に，以下の画像に表示されているように設定を変更します．

`Settings\Security\Trusted Computing`
![IMG_5193.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3078706/7ef0e37b-b308-f78c-6aa8-91efc63ee9b0.jpeg)

`Settings\Advanced\Windows OS Configuration\Secure Boot`
![IMG_5194.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3078706/542f6dd8-e46d-5ca8-5fce-964143e441ac.jpeg)

この設定にして，保存します．
その後，再起動すると`nvidia-smi`やGPUが使えるようになりました．

## おわりに
ネット検索しても，明確な解決方法があまりヒットしなかった印象です．
最初は，nvidia-driverやCudaを入れ直してかなり時間がかかりました．

**メモリを取り替えると自動的にBIOSの設定が変更される**ことを知り勉強になりました． 

## 参考サイト
[【自作PC】メモリーの取り付け方 （初心者向け）](https://achipon.com/memory/)
[【WSL】エラー：0x80370102 の対処法：CPU仮想化を有効にする](https://qiita.com/seichi25/items/62877a9dede13ff0e032)
OpenCVの描画機能でアニメーション

# OpenCVで惑星・衛星軌道風アニメーションを作ってみた。
OpenCVでカメラ画像をリアルタイムで表示する記事はよく見かけますが、高速で描画できればアニメーションが作れるはずなので、計算だけで作れるアニメーションを作ってみました。

ご自由に改造してお使いくださいませ。

```python3:cmov
#!/usr/bin/python3

import cv2
import time  as tm
import numpy as np

wname="circle0"
# キャンバスの大きさとキャンバスの生成
cvx = 600
cvy = 600
canv = np.zeros((cvy, cvx, 3), dtype=np.uint8)
# 背景色
bkcolor = (64,0,64)
# 前景色１(惑星色)
fg1color = (128,255,128)
# 前景色２(衛星色)
fg2color = (64,128,128)

# 1000 ×　PI
pi=6282
# 惑星軌道半径 pix
rad1 = 200
# 衛星軌道半径 pix
rad2 = 40
# 角度
the = 0
# 角度ステップ
stp = 5
# 描画間隔(s)
wt=0.01

#  左下を原点
def coord(x,y):
    rx = x
    ry = cvy - y;
    return (rx,ry)

#  衛星の位置
def satpos(n, x, y):
    global the
    sx = int(rad2*np.cos(n*the/1000))+x
    sy = int(rad2*np.sin(n*the/1000))+y
    return coord(sx,sy)

while True:
    cx,cy=(int(cvx/2),int(cvy/2))
    px = int(cx + rad1 * np.cos(the/1000))
    py = int(cy + rad1 * np.sin(the/1000))
    the += stp
    if the > pi:
        the %= pi
    canv[:cvy,:cvx,0:3] = bkcolor
    cv2.circle(canv,coord(px,py),10,fg1color,thickness=3)
    cv2.circle(canv,satpos(20,px,py),4,fg2color,thickness=3)
    #                      ^^---衛星の回転数
    cv2.imshow(wname,canv)
    if cv2.waitKey(int(wt*1000) & 0xff) == ord('q'):
        break;

cv2.destroyWindow(wname)
```

# 書き換え箇所解説動画
https://youtu.be/yobD_8XTOGQ
30代未経験からエンジニア転職したからこそ、逆に良かったこと

## 自己紹介
- 現在40歳のフロントエンドエンジニア
- 高校卒業後、31歳までフリーターだったが、そこからITエンジニアに転職した

## 「良かったこと」の前に
良かったことだけ並べて「みんなも30代からエンジニア転職しよう！」とそそのかしてもよろしくないと思うので、まずは「悪かったこと」から書いていきます。

## 悪かったこと
### そもそも転職活動が困難

エンジニアになろうと思って就職活動をしていたころの私のスペックは以下のとおりです。

- 31歳
- 高卒
- プログラミング歴、独学で1年（HTML・CSS・JavaScript）
- エンジニア実務未経験

転職サイトで50社ほど手当たり次第に応募しましたが、そもそも書類選考で落ちまくりました。

「〇〇さんのご活躍を心からお祈りします」という**お祈りメール**ばかり届きました。

あまりに祈られ過ぎて「ワイは神なんか？」という妄想を抱くほどでした。

面接してもらえたのは、たった3社だけでした。

しかも、その3社も最終的に**不採用**になりました。

高卒で、30代で、なんと正社員の経験もなかった（バイトや派遣社員のみ）ため、当たり前の結果かもしれません。

結果、正社員としてのエンジニア転職は**できませんでした。**

書類選考でワイを落とした会社さん達の「祈り」は、天には届かなかったようです。

それでも私は「どうしても、コード書いて飯を食ってみたいんや！」と思っていたので、なんとかアルバイトとして小さな会社に入れてもらいました。

31歳・既婚者、時給1,000円のアルバイトHTMLコーダーになりました。

ともあれ、エンジニアとしてのキャリアがスタートしました。

### 毎日残業

社員数7名ほどの小さな会社で、エンジニアは私ともう一人しかいませんでした。
そのため人手不足で、毎日22〜23時まで残業をしていました。

病みそうでした。

### 年下の子達がメチャクチャ優秀でつらい

エンジニアとして何年か経験を積み、今の会社（株式会社ゆめみ）に転職しました。

そのとき私は35歳になっていましたが、同僚は20代の人たちが中心でした。
中には18歳の子もいました。

以前ネット上で「最近の若者はスマホやiPadばかりで、PCを触れない」という記事を見たことがあったので「ワイも若者に負けへんやろ」と思っていたのですが、現実は大違いでした。

小中学生の頃からプログラミングをやっている上に、地頭も猛烈に良い。
そんな若者が多く、私は彼らから教えてもらうばかりの毎日でした。

自己肯定感が下がりました。

プライドを捨てて何とか頑張りました。

## 良かったこと

でも、30代からエンジニア転職したからこそ良かったこともあります。

### 過去の経験が意外と活きた

20代のころ、IT系のコールセンターに7年間勤務していたことがあります。

毎日毎日、電話でお客様からの質問に答えていたため、IT系の知識を分かりやすく説明する能力が身についていました。

質問の内容は、レンタルサーバの使用方法やら、インターネットの接続方法、独自ドメイン名の契約方法などです。

コールセンターに電話をかけてきてくれるお客様はご高齢の方が多く、どんなに噛み砕いて説明しても上手く伝わらないことがあり───

> 「うぅ・・・すごく分かりやすく説明したつもりだったのに、実はこんな誤解を生んでしまっていたのか・・・！」

───という、**あらゆる誤解のパターン**を体験しまくりました。

なので「分かりやすい説明とは」と常に考えてしまう職業病にかかっていました。

そのため、何かIT系の新しい知識を手に入れた際には───

> 「もしもこれをお客様に説明するとしたら、どういう言い回しにしたら誤解を生まないだろう」

───と普段から考えていました。

その職業病のおかげで説明スキルが鍛えられたと思います。

エンジニアになってからも、他職種の同僚やクライアント様に対して技術的な説明をする機会は多いので、コールセンター時代の経験は非常に活きています。

あと、コールセンター時代に培ったものがもう一つあります。

それは「会話形式の文章を書く能力」です。

私の勤めていたコールセンターには、以下のルールがありました。

「お客様との通話が終わったら、その内容を短くまとめて、会話形式でパソコンに記録すべし！」

記録した内容は、次に同じお客様から電話がかかったきた時に、参考情報として使用します。

つまり「次に電話を受けたオペレーターが、通話をしながらサクッと読めるもの」でないといけないので、もの凄く簡潔に書かないといけません。

上司から「やめ太郎の書く文、長いんじゃ！お客様と通話しながら脳死で読めるくらい簡潔にまとめんかい！」と指導されたこともありました。

そこで鍛えられた能力のおかげで、会話形式の技術記事をQiitaにたくさん書くことができ、いいねもたくさん貰えました。

Qiitaで活躍できたおかげで、所属企業のブランディングにもなったようで、なんとか社内で存在価値を発揮できて助かりました。

「芸は身を助く」ということわざの意味を、身をもって実感しました。

そのあたりは、別職種からエンジニア転職したからこそのメリットかも？と思っています。

## 金銭的な面

コールセンター時代は時給1,500円だったのが、アルバイトエンジニアになって時給1,000円に下がってしまいました。

なので最初は生活が大変でした。

ですが、3〜4年くらいエンジニアとして働いたら、コールセンター時代の2倍以上の年収になったので、結果的には転職して良かったと思っています。

## 最後に

31歳まで正社員経験なしのフリーターで、将来に繋がるようなキャリアは全く積んでいなかったのですが、どの職場でもとりあえず一生懸命働いていました。

> 「どんな仕事でも、一生懸命やっとけば**問題解決能力**みたいなのは磨かれるやろ！」
> 「それは必ず自分の力になるし、今後どんな職種に移ったとしても役に立つはずや！」

と思っていたからです。

ある職場では、社長と揉めて突然退職してしまったり、やらかしてしまったことも沢山ありますが、とりあえず何でも一所懸命やっておいて良かったなと思います。

あんまり参考になるキャリアではないと思いますが、ご拝読ありがとうございました！
JavaScript：その配列内検索、ArrayよりSetの方がいいかも？

# はじめに

JavaScriptでまとまったデータを管理する際、Array（配列）を使用することも多いと思いますが、特定のケースではSetというオブジェクトの方が優れているよ、というお話です。この記事では、SetとArrayの違いと、それぞれの使用場面について解説します。

## 本題

この記事では下記を解説していきます。

- **Setってそもそも何？**
- **ArrayよりもSetの方が優れている点**


### Setってそもそも何？

SetはJavaScriptの組み込みオブジェクトで、重複を許さない値の集合を管理するために使用されます。Arrayと違ってSetには`重複した値は格納できない`ということです。
Setの基本的な使い方はこんな感じです。
```JavaScript
const uniqueNumbers = new Set([1, 2, 3, 2, 1]);
console.log(uniqueNumbers); // Set(3) {1, 2, 3}

/* 重複した 2 と 1 は格納されない。*/
```

### ArrayよりもSetの方が優れている点

先ほどのSetの基本的な使い方を踏まえ、ArrayよりSetを使った方が良い場面を考えていきましょう。

#### ループ処理での重複排除

例えば、イベントAまたはイベントBのどちらかに参加しているユーザーを算出したいとき、重複削除が役に立ちます。

```JavaScript
// 2つの異なるAPIから取得されたイベント参加ユーザーIDのリスト
const eventA_user = [101, 102, 120, 190, 200];
const eventB_user = [101, 102, 350, 356, 407];

// Setを使用して重複を排除
const uniqueUserIds = new Set([...eventA_user, ...eventB_user]);
console.log(uniqueUserIds); 
//　出力： Set(8) {101, 102, 120, 190, 200, 350, 356, 407}
```

#### 重複した値を入れないと明示できる

Setは重複した値を格納することができないので、コードを見た人が「ああ、これは一意の値を扱っているから同じ値を入れないようにしているんだな」とすぐに理解することができます。

#### 配列内の検索パフォーマンスが高い

<font color="red">僕が今回推したい点はここです。</font>
大量のデータが入った配列に特定の値が存在するかどうかを効率的に調べたい場合はSetの方が良いです。
例として該当のuserIdを見つけるための比較をArrayとSetの両方で見てみましょう。

```javascript
//調べたいID
const targetUserId = 308; 
//検索する箱
const allUserId = [2,10,34,50,66,78,・・・,308・・・続く]

// Arrayの場合・・・findメソッドを使用
const result = allUserId.find(id => id === targetUserId);
console.log(result) // 出力: 308

// Setの場合・・・hasメソッドを使用
const set = new Set(allUserId);
const result2 = set.has(308);
console.log(result2) // 出力: true
```

上記を見ると結果の値が少し違いますね。
Arrayでfindを使った場合は配列から該当の値を返しています。また、Setでhasを使った場合はBoolean型で返ってきています。
※hasメソッドは引数に入っている値が検索対象に存在するかどうかをみています。

**え？むしろSetの方が1行長くなるしArrayでよくない?**
そうですよね。僕も最初はそう思いました。
でも、<font color="red">**検索の効率性**</font>が違います。

`find`メソッドは対象の配列を順番に見ていきます。つまり下記コードでは`allUserId`の中を、[1]の値は308じゃないじゃないから次、[2]の値は308じゃないから次、[3]の値は308じゃないから次、という感じで順々に見ているのです。そして無事308へ辿り着いたときにその値を返してくれます。

```javascript
const targetUserId = 308; 
const allUserId = [2,10,34,50,66,78,・・・,308・・・続く]
const result = allUserId.find(id => id === targetUserId);
```

対して`has`メソッドは、、
<font color="red">**`308`を超高速で見つけてくれます。**</font>

これはArrayのようにデータを要素番号と値のペアで管理しているのではなく、「ハッシュテーブル」というデータ構造で管理しているためです。これによってデータ量に関わらず検索したい値を直接確認できるのです!!

ハッシュテーブルについては下記がわかりやすいです。

https://qiita.com/tenten1010/items/da4084f937ad07e70164


この違いは検索パフォーマンスにおいて雲泥の差になります。検索対象のデータ数が10個とかならそんなに変わりませんがデータが増えれば増えるほどSetのhasメソッドが有利になりますね。
ただし、SetはArrayと違って要素に順番にアクセスする機能が限られています!特定の状況に応じて、ArrayとSetを適切に使い分けることができればグッドですね🙆‍♂️

## まとめ

ここまでの流れを振り返ってArrayとSetの使い分をまとめておきます。

**Arrayの使用シナリオ**
・順序が重要な場合
・重複した値の保持が必要な場合

**Setの使用シナリオ**
重複を許さない値の管理が必要な場合
大量のデータからの高速な検索が必要な場合


読んでいただきありがとうございました。
何かお気づきの点等あれば教えていただけると嬉しいです🙇‍♂️