OmniAICreator/Qiita-1.07M huggingface dataset:
JavaーStreamAPIー２章

### 前置き
最近は暑くて外出する気にもなりませんね...
田舎者の私は川までドライブして涼んでいます(*´Д｀)

今回は前回に引き続き「StreamAPI」についてです。

###「StreamAPIってなに？」
> コレクションを扱う時に要素を一つずつ取り出して、何らかの処理を加え、特定の要素だけを取り出したり、配列に変換したり、コレクションを詰め替えたりすることがよくあります。
StreamAPIではこれらの処理をメソッドとラムダ式で記述することでコードをより簡潔にできます。　--黒本解説より引用

「簡単に書けるってことは理解も簡単ってことよね！？」

>StreamAPIでは取り出しった要素に加える処理のことを「中間操作」、
中間操作を終えた要素の集合に対して行う最終処理を「終端操作」と呼びます。
これらの操作は、StreamAPIの基本となる機能をまとめたjava.util.stream.Streamインターフェースに定義されており、次のような種類があります。  --黒本解説より引用

#### 【中間操作の種類】
| 処理を行うメソッド | 概要 |
|:-:|:-:|
| distinct  | 要素の重複を除いたストリームを戻す  |
| filter  | 引数に指定した条件に一致する要素だけで構成されるストリームを戻す  |
| limit  | ストリームの要素を指定された数に切り詰めた結果のストリームを戻す  |
| map  | 指定された関数を要素に適用した結果のストリームを戻す  |
| peek  | 新しい結果のストリームを生成し、指定された関数を適用して戻す  |
| skip  | 引数で指定した最初のn個の要素を破棄し、残った要素で構成されるストリームを戻す  |
| sorted  | このストリームの要素を自然順序に従ってソートした結果から構成されるストリームを戻す  |

#### 【終端操作の種類】
| 処理を行うメソッド | 概要 |
|:-:|:-:|
| allMatch  | ストリーム内のすべての要素が条件に一致するかどうかを調べる  |
| anyMatch  | ストリーム内のいずれかの要素が条件に一致するかどうかを調べる  |
| collect  | ストリームの要素を持つコレクションを戻す  |
| count  | ストリーム内の要素を数える  |
| findAny  | ストリーム内に要素が残っているかどうかの結果を持つOptionalを戻す  |
| findFirst   |  ストリーム内の最初の要素を持ったOptionalを戻す |
| forEach  | ストリーム内の要素を使って繰り返し処理を実行する  |
| max  | ストリーム内の最大の要素を戻す  |
| min  | ストリーム内の最小の要素を戻す  |
| noneMatch  | ストリーム内の要素で条件に一致するものがないかどうか調べる  |
| reduce  | ストリーム内の要素を累積的に結合していくリダクション処理を実行する |
| toArray  | ストリームの要素を含む配列を戻す  |

「ふむふむ...」

なんかいっぱいあるけど英語から推測して、大体の意味が分かれば問題は解けそう。

「Optionalについても前回で学んだので大体は理解してるぞ！」

前回を見ていない人はOptionalは
「メソッドの処理結果を扱い、処理結果の正常・異常に関わらず、同じ型で扱うことができる」
とだけ覚えればOKです。

### 使い方
```java
class Main {
	public static void main(String[] args) throws Exception {
		List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
		list.stream()
				.filter(n -> n > 2) 			//２より大きいものを抽出→(3,4,5,6,7,8,9,10)
				.limit(5)						//要素数を5に切り詰める→（3,4,5,6,7)
				.map(n -> "残ったものは" + n)	//文字列を加える
				.forEach(System.out::println); 	//出力

	}
}
```

「なんだか簡単に書けるじゃん」
cscの作法 その409

# 概要
cscの作法、調べてみた。
ナップザック問題、やってみた。

# サンプルコード

```
using System;

class Program {
	[STAThread]
	static void Main(string[] args) {
		int N = 6;
		int W = 9;
		int[] dp = new int[W + 1];
		int[] wt = new int[] {
			2,
			1,
			3,
			2,
			1,
			5
 		};
		int[] va = new int[] {
			3,
			2,
			6,
			1,
			3,
			85
		};
		for (int i = 0; i < N; i++)
		{
			for (int j = W; j >= 0; j--)
			{
				if (j - wt[i] >= 0)
				{
					if (dp[j] < dp[j - wt[i]] + va[i])
					{
						dp[j] = dp[j - wt[i]] + va[i];
					}
				}
			}
		}
		Console.WriteLine(dp[W]);
	}
}





```

# 実行結果

```
94
```


以上。
【AWS】CloudFormationでS3バケット削除時にカスタムリソースを使ってオブジェクトも一緒に削除する

CloudFormationを利用してS3を作成する場合、内容よってはオブジェクトも出力されてしまっているというパターンがあるかと思います。このときスタックの削除を試みるとオブジェクトが残っているので、**中身のオブジェクトを先に削除しない**とスタックの削除ができないという悲しい自体が発生します。検証などでリソースの再作成を繰り返してると、これが地味に手間なのです。

これは**カスタムリソース**という手法を利用すれば、スタックの削除一発でオブジェクトの削除まで可能になります。今回はこの方法を書いていたきいと思います:bulb:

## カスタムリソースとは
通常のリソースタイプではできない処理に**機能を追加して処理させる**方法です。今回の例だと、通常S3削除時にはオブジェクトを削除するという動作はできませんが、このカスタムリソースを使用することでオブジェクトを削除するという動作を追加することができます。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-custom-resources.html

## S3オブジェクト削除用lambda
下記S3削除用のlambdaをデプロイします。**Outputs**では、S3の作成が記述されたどこのyamlでも使えるようにエクスポートさせてます。

```yaml:lambda.yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: lambda to delete object in S3

Resources:
  s3deletelambda:
    Type: AWS::IAM::Role
    Properties:
      RoleName: s3_delete_lambda_role
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonS3FullAccess
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: s3delete
      Role: !GetAtt s3deletelambda.Arn
      Runtime: python3.7
      Handler: index.lambda_handler
      Timeout: 600
      Code:
        ZipFile: | #ここから先にlambadaのコードを記載する

...

Outputs: # どこのS3作成先でも使えるようにクロススタック
  LambdaFunction:
    Value: !GetAtt LambdaFunction.Arn
    Export:
      Name: LambdaFunction-cfn-arn

```

lambdaのコードはこちらを参照（動作確認済みです）

https://dev.classmethod.jp/articles/custom-resource-empty-s3-objects/

**クロススタック**がわからないひとはこちらへ

https://qiita.com/hiyanger/items/0881229b233f13b0914f

## S3側
S3を作成してるコードの**Resources**にカスタムリソース使うよのコードを追加してあげます。
```yaml:s3.yaml
AWSTemplateFormatVersion: '2010-09-09'

Resources:
  ...

  LambdaUsedToCleanUp:
     Type: Custom::cleanupbucket
     Properties:
       ServiceToken: {Fn::ImportValue: LambdaFunction-cfn-arn}
       BucketName: !Ref XXX
```

## 検証のときつらかったこと
lambdaはCloudFormationの記述ミスると**1時間レスポンス返ってこなくなります。** その間基本的には待つしかないようなので、できるだけコードは詰めてからスタックは流した方がよいです。

私はハンドラーのところで記述を間違えて、上記事象の罠にしばらくとらわれてしまいました。下記ご参考にどうぞ。

Handler: ファイル名（拡張子なし）.ハンドラー名　→ index.lambda_handler

**※参考**

https://atsushinotes.com/deploy_lambda_from_cloudformation-by-yaml/#index_id10

https://dev.classmethod.jp/articles/fix-lambda-error-no-module-named-lambda_function/
paiza.ioでelixir その244

# 概要
paiza.ioでelixirやってみた。
ナップザック問題、やってみた。

# 参考にしたページ。

https://qiita.com/koyo-miyamura/items/77388ff0d3e03c95897c

# サンプルコード
再帰

```
defmodule Dp do
	def solve([[]], _), do: 0
	def solve([[w, _]], max_w) when w > max_w, do: 0
	def solve([[w, v]], max_w) when w <= max_w, do: v
	def solve([[w, _] | items], max_w) when w > max_w do
		solve(items, max_w)
	end
	def solve([[w, v] | items], max_w) when w <= max_w do
		max(v + solve(items, max_w - w), solve(items, max_w))
	end
end

input = [[2, 3], [5, 85], [1, 2], [3, 6], [2, 1], [1, 3]]
Dp.solve(input, 9)
|> IO.inspect()
```

# 成果物

https://paiza.io/projects/vE13XknApelilqKBQMSXGw

# サンプルコード

ets
```

defmodule Dp do
	def solve(items, max_w) do
		:ets.new(__MODULE__, [:set, :protected, :named_table])
		0..max_w 
		|> Enum.each(fn w -> 
		    insert(0, w, 0) 
		end)
		for {[w, v], i} <- Enum.with_index(items), j <- 0..max_w do
			if w <= j do
				res = max(lookup(i, j - w) + v, lookup(i, j))
				insert(i + 1, j, res)
			else
				insert(i + 1, j, lookup(i, j))
			end
		end
		lookup(length(items), max_w)
	end
	defp insert(i, w, res) do
		:ets.insert(__MODULE__, {{i, w}, res})
		res
	end
	defp lookup(i, w) do
		[{_, res}] = :ets.lookup(__MODULE__, {i, w})
		res
	end
end

input = [[2, 3], [5, 85], [1, 2], [3, 6], [2, 1], [1, 3]]
Dp.solve(input, 9)
|> IO.inspect()

```

# 成果物

https://paiza.io/projects/JVk_cIek3155LErDYUUqkw

# サンプルコード

map
```

defmodule Dp do
	def solve(items, max_w) do
		table = %{0 => 0..max_w |> Enum.reduce(%{}, &Map.put(&2, &1, 0))}
		#|> IO.inspect
		table =	Enum.reduce(Enum.with_index(items), table, fn {[w, v], i}, table ->
			add_i =	0..max_w
				|> Enum.reduce(table[i], fn j, tmp ->
					add_j =
						if w <= j do
							max(table[i][j - w] + v, table[i][j])
						else
							table[i][j]
						end
					Map.put(tmp, j, add_j)
				end)
			Map.put(table, i + 1, add_i)
		end)
		|> IO.inspect
		table[length(items)][max_w]
	end
end

input = [[2, 3], [5, 85], [1, 2], [3, 6], [2, 1], [1, 3]]
Dp.solve(input, 9)
|> IO.inspect()




```

# 成果物

https://paiza.io/projects/Ph04FU1XX0RnexcZA0VBZQ

以上。
InDesign、Illustratorの代替フォントのハイライト表示ON/OFF方法

# InDesign
## 設定方法
メニューの「編集」→「環境設定」→「組版」→「ハイライト表示オプション」→「代替フォント」のチェックをON/OFF
![K-20230717-104108 - コピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1184971/b0635896-8cb5-7cdd-ba6f-30955c65cd6f.png)

## チェックON/OFFの例
- チェックONした場合
![K-20230717-104022.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1184971/5ecc0313-0ee9-1b02-7fbf-0298c5a58b35.png)
- チェックOFFした場合
![K-20230717-104010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1184971/babb773d-85fe-84dd-7e9b-12bd35cc3c2e.png)

# Illustrator
## 設定方法
メニューの「編集」→「環境設定」→「テキスト」→「代替フォントの強調表示」のチェックをON/OFF
![K-20230717-104249 - コピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1184971/8f44e1b6-cf60-3e66-4e25-0f44c51bcef2.png)

## チェックON/OFFの例
- チェックONした場合
![K-20230717-103948.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1184971/1180e9fe-6783-4e7d-664d-bba928ceb359.png)
- チェックOFFした場合
![K-20230717-103932.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1184971/2070eec8-2b38-a48a-cb0f-9fc6ff7ebd02.png)
【Rails API】エラーハンドリング処理をJSON形式で返す

Rails　APIについて学習しています。
今回は、Railsアプリケーションを使い、をAPIによる例外処理（エラーハンドリング）を実装していきます。

# 今回やること

- apiのエラーハンドリング処理を実装する。
- 404, 500のレスポンスを返す処理を実装する。
- apiのエラーをjson形式で返す処理を実装する。


# base_controller.rbに追記する

```ruby:app/controllers/api/v1/base_controller.rb
module Api
  module V1
    class BaseController < ApplicationController
      include Api::ExceptionHandler

    end
  end
end
```

# このコードの解説

- このコードでは、Ruby on Railsのアプリケーションにおけるコントローラの基底クラス`BaseController`を定義している。

- `module Api` `module V1`の部分で、「Api」という名前空間とその下に「V1」という名前空間を作っている。これによって、このコントローラが`APIのバージョン1`に関連するものであることを表現している。

- `class BaseController < ApplicationController`で、`BaseController`というクラスを定義していて、`ApplicationController`を継承している。`ApplicationController`は全てのコントローラが共通で持つ処理を書く場所なので、全てのコントローラは`ApplicationController`を継承することになる。

- `include Api::ExceptionHandler`。これは、`Api::ExceptionHandler`モジュールをこの`BaseController`クラスに取り込む、つまり`mix-in`しているという事。`Api::ExceptionHandler`モジュールはエラーハンドリングのためのメソッドを提供しているから、これによって`BaseController`クラスやそのサブクラスはエラーハンドリングの機能を利用できるようになる。


# concernsディレクトリにapiを実装する

```ruby:app/controllers/concerns/api/exception_handler.rb
module Api::ExceptionHandler
  extend ActiveSupport::Concern

  included do
    rescue_from StandardError, with: :render_500
    rescue_from ActiveRecord::RecordNotFound, with: :render_404
  end

  private

  def render_400(exception = nil, messages = nil)
    render_error(400, 'Bad Request', exception&.message, *messages)
  end

  def render_404(exception = nil, messages = nil)
    render_error(404, 'Record Not Found', exception&.message, *messages)
  end

  def render_500(exception = nil, messages = nil)
    render_error(500, 'Internal Server Error', exception&.message, *messages)
  end

  def render_error(code, message, *error_messages)
    response = {
        message: message,
        errors: error_messages.compact
    }

    render json: response, status: code
  end
end
```


# このコードの解説

* このコードは、Ruby on RailsのアプリケーションでAPIのエラーハンドリングを行なっている部分。

* concernsディレクトリに定義している。`concerns`は、複数のモデから利用される処理を共通化したり、まとまった処理を別に切り出すことで、FatコントローラーやFatモデルを解消するなどの効果がある。

* `module Api::ExceptionHandler`は`Api::ExceptionHandler`というモジュールを定義している。このモジュールは、エラーが発生したときの処理をまとめている。

* `extend ActiveSupport::Concern`は、`ActiveSupport::Concern`モジュールを拡張している。これにより、`included`というメソッドを使うことができるようになる。

* `included do〜end`の中で、`rescue_from`メソッドを使ってエラー処理を定義している。例えば、`rescue_from ActiveRecord::RecordNotFound, with: :render_404`は、`ActiveRecord::RecordNotFound`エラーが発生したときに、`render_404`メソッドを実行する、という指定をしている。

* `render_400`、`render_404`、`render_500`メソッドは、それぞれHTTPステータスコード400、404、500のエラーが発生したときのレスポンスを作成している。それぞれのメソッドでは、`render_error`メソッドを呼び出して、エラーメッセージを含んだJSONレスポンスを作成し、クライアントに返している。

* 最後に、`render_error`メソッドは、エラーレスポンスを生成するためのメソッド。引数として受け取ったHTTPステータスコードとエラーメッセージをもとに、エラーレスポンスをJSON形式で生成している。



# 可変長引数やオプション引数について

可変長引数やオプション引数を使っています。これについては、こちらの記事が大変参考になりました。

https://qiita.com/mmaumtjgj/items/5c98ae3d21fa4f27c93c
modcombのクラスを作った

n!、nCr、nPrをmodで割った余りを求めるクラスを作りました。

``` cpp
class mComb {
 private:
  bool _calced = false;
  int _mod = 1000000007;
  vector<long long> _fac, _invf, _inv;

 public:
  void setmod(int m) {
    _mod = m;
  }

  void comb_init(int n) {
    _fac.resize(n + 1);
    _invf.resize(n + 1);
    _inv.resize(n + 1);
    _fac[0] = 1;
    _fac[1] = 1;
    _invf[0] = 1;
    _invf[1] = 1;
    _inv[1] = 1;
    for (int i = 2; i <= n; i++) {
      _fac[i] = _fac[i - 1] * i % _mod;
      _inv[i] = _mod - _inv[_mod % i] * (_mod / i) % _mod;
      _invf[i] = _invf[i - 1] * _inv[i] % _mod;
    }
    _calced = true;
  }

  long long F(int n) {
    return _fac[n];
  }

  long long C(int n, int k) {
    if (!_calced) {
      cerr << "not initialized\n";
      exit(1);
    }
    return _invf[k] * _invf[n - k] % _mod * _fac[n] % _mod;
  }

  long long P(int n, int k) {
    if (!_calced) {
      cerr << "not initialized\n";
      exit(1);
    }
    return _invf[n - k] * _fac[n] % _mod;
  }

  long long H(int n, int k) {
    if (!_calced) {
      cerr << "not initialized\n";
      exit(1);
    }
    return _invf[k] * _invf[n - 1] % _mod * _fac[n + k - 1] % _mod;
  }
};
```
まあ、見ればわかると思います。
Vue 3の勉強 ①

環境の構築も終わったのので少し編集してみました。

JavaScriputもVueもあまり詳しくないので以前までの違いが分からないが結構使い易くなったみたいです。

```app.vue
<scriput setup>
const message = "HelloWorld";
const color = "red";
</script>
<template>
  <p v-html="message" :style="{ color: color, 'background-color': 'blue' }"></p>
</template>
```

ほんの少しの編集ですが、触っていてとても楽しいです。

もう少し学習していこうと思います。
VSCodeでGoの開発環境を簡単に作る

## はじめに

Go入門中の者です。
GoLandに課金するのはちょっと気が引けるので、VSCodeで簡単な実行環境を作ってみました。

色々な記事が出ているけど、結果Microsoftの記事が一番分かりやすく簡単でした。

https://learn.microsoft.com/en-us/azure/developer/go/configure-visual-studio-code

## 前提

VSCodeがインストールされていること
※記事に出てくるコマンドはMacを前提としております

## 手順

### 1. Goをインストールする

下記のサイトから、任意のバージョン/OSのGoをインストールする
https://go.dev/doc/install

余談ですが、私はasdfを使ってインストールしました。
任意のバージョン管理ツールでも何でもGoがインストールできれば問題ないです。


### 2. VSCodeの拡張機能「Go」をインストールする

これ↓です！
![スクリーンショット 2023-07-17 9.43.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526830/9f0fc589-dccf-8e72-2cee-867580d75fd4.png)


### 3. Goのツールをアップデートする

a. `Cmd+Shift+P`でコマンドパレットを起動する
b. `Go: install/update tools`で検索する
c. 7個くらいパッケージが出てくると思うので全て選択してOKをクリック

### 4. Goプロジェクトを作成する

a. プロジェクト用のディレクトリを作成する（今回は`sample-go-app`とします）
b. プロジェクト直下に`main.go`というファイルを作成する
![スクリーンショット 2023-07-17 9.57.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526830/6e1ee4e5-c59a-d5f0-b810-cc2fcfec3d22.png)

c. 下記のコードをコピーして`main.go`にペーストする

```main.go
package main

import "fmt"

func main() {
    name := "Go Developers"
    fmt.Println("Hello World!", name)
}
```

![スクリーンショット 2023-07-17 9.59.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526830/235fd3c7-72ff-88a2-182b-63e4aab0120a.png)

d. Goモジュールを初期化する

画面上メニューのTerminal > New Terminalからターミナルを起動し、`go mod init sample-go-app`を実行する

### 5. コンパイルして実行する

VSCodeの左メニューバーの虫マークの所から`Run and Debug`を実行する（F5クリックでも実行できる）

![スクリーンショット 2023-07-17 10.08.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526830/27f2e71d-ec8b-2d52-5060-2f7ec7d94747.png)

ブレークポイントを仕込んでデバッグもできます。

実行すると、下部のDebug Consoleに`Hello World!Go Developersが表示されるはずです。


## ついでにフォーマッタも設定する

コマンドパレット(Cmd+Shift+P)から`>Preferences: open user settings (json)`でsettings.jsonを開き、下記を追加する

```settings.json
{
    "[go]": {
        "editor.tabSize": 2,
        "editor.formatOnSave": true,
        "editor.defaultFormatter": "golang.go"
    }
}
```

こちらを追加すれば、コード保存時にGoのコードが綺麗にフォーマットされます。

## 参考サイト

https://learn.microsoft.com/en-us/azure/developer/go/configure-visual-studio-code

https://iwasiman.hatenablog.com/entry/20220516-go-env-on-vscode
クレジットカード決済業務の取消と返品の違い


取消（VOID）、RETURN（返品）はいずれも（仮）売上の取消を指すようである。
そのタイミングによる違いがあるようです。
当日だと、取消（VOID）。翌日以降だと返品（RETURN）みたいですね。


https://raku2repeat.com/faq/payment/02-0305/#:~:text=%E3%82%AD%E3%83%A3%E3%83%B3%E3%82%BB%E3%83%AB%E3%81%8C%E5%AE%8C%E4%BA%86%E3%81%97%E3%81%9F%E7%8A%B6%E6%85%8B,%E8%BF%94%E5%93%81%E3%80%8D%E3%81%A8%E8%A1%A8%E7%A4%BA%E3%81%95%E3%82%8C%E3%81%BE%E3%81%99%E3%80%82


https://mp-faq.gmo-pg.com/s/article/D00007
Liveness ProbeとReadiness Probeを今度こそ理解する

# はじめに

Kubernetesでアプリケーション（Deployment）をデプロイする際に、Probeの設定をすることは多いと思います。ドキュメントや色々な記事でLiveness / Readiness Probeの説明がありますが仕組みは分かっても、**なぜ両方設定が必要なのか？** という疑問がふんわりありました。

そこで疑問解決ベースで言語化することで自分を納得させてみようと思いました。

※ 本記事ではStartup Probeについては説明しません。

# 各Probeを軽くおさらい

いずれのProbeも、kubelet（各ノードにあるリソース）からPodにヘルスチェックを発信することで死活監視やトラフィックの制御をする。

## Liveness Probe
コンテナは稼働中か（生存しているか）をチェックするためのもの。
異常と判断された場合、コンテナは再起動される。

## Readiness Probe
コンテナはトラフィックを受け入れるための準備ができているか（Readiness）を確認するために使用する。
異常と判断された場合（例えば起動初期のロード処理や、重いリクエストの処理中で別のリクエストを返せない場合）、そのコンテナを含むPodはServiceのロードバランシング先から切り離される。


# 疑問

## 1. ReadinessProbe は必要か？

多くの人が初めに「 LivenessProbe さえ設定すれば、コンテナの死活監視できてるから十分じゃね？」と思いそうです。

しかしながら、**コンテナ起動後、リクエストを処理できる状態になるまでの準備に時間がかかる場合に、無限再起動に陥る恐れがあります**。
例えば、上記のように準備に時間がかかる場合に、もし LivenessProbe だけを設定すると、常に LivenessProbe に失敗することになります。すると起動から準備を待たずコンテナが再起動され準備が最初からになり、LivenessProbe による無限再起動のループに陥ってしまいます。

そこで、LivenessProbe に`initialDelaySeconds`（コンテナが起動してからProbe開始までの秒数。デフォルト0秒）を設定することで、強引にLivenessProbeだけを設定することも思い付きます。
しかしその場合、秒数だけで厳密に準備状態のチェックをしないため、**何らかの理由でコンテナの準備が遅延しても、トラフィックは送られ始めることになりその間はエラーを返し続けてしまいます**。
可用性の点で不十分です。


## 2. LivenessProbe は必要か？

今度は逆に「 ReadinessProbe だけ設定すれば、リクエストは受けられる状態だから良くね？」と思いそうです。

場合によっては問題ないかもですが、コンテナの特性や ReadinessProbe の設定によっては取りこぼしが発生する可能性があります。
例えば、`exec`であるコマンドを実行するだけだったり、`tcpSocket`で特定ポートのコネクション確立だけだった場合、**対象のプロセスは生きているがコンテナ自体に問題がある可能性があります**。

具体的には、下記のような問題が挙げられます。
- **リソース不足**： コンテナが外部リソースとの接続を問題なく行える状態でも、Podのメモリ・CPU確保量やノードのリソースが不足している場合、実際のリクエストに適切に応答できない可能性がある。このような時、LivenessProbe がタイムアウトやエラーを検出できる可能性がある。
- **DB接続の問題**: コンテナがDBとの接続を確立する必要があり、ReadinessProbe によりその疎通確認がとれていても、実際のクエリに対してエラーを返す可能性がある。この場合も、LivenessProbe がタイムアウトやエラーを検出できる可能性がある。
- **バックグラウンドプロセスのクラッシュ**: バックグラウンドプロセスがそのコンテナの重要な機能を担っている場合、アプリの全体的な機能に影響を与える可能性がある。LivenessProbe が適切であれば、バックグラウンドプロセスのクラッシュも検知する可能性がある。


# さいごに

まとめると、下記のような危険があります。
- LivenessProbe だけを設定した場合
  - コンテナの無限再起動
  - リクエストを受ける準備ができてないのにトラフィックが送られる
- ReadinessProbe だけを設定した場合
  - コンテナ全体で見ると問題があるのにトラフィックが送られる

記事を書いてみて、**Liveness / Readiness Probe はそれぞれ互いにカバーし合う関係にある**と思いました。
コンテナの特性等によっては、これらのProbeは必要なかったり片方で事足りたりするかもしれませんが、基本的にいずれの Probe も設定した方が、アプリケーションの安定運用につながるはずです。


# 参考
- https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
- https://cstoku.dev/posts/2018/k8sdojo-10/
- https://zenn.dev/nekoshita/articles/4e838ae224ed56
Ubuntu22 vimmer のためのキーボード設定

## 参考文献
https://wiki.archlinux.jp/index.php/GNOME/%E3%83%92%E3%83%B3%E3%83%88%E3%81%A8%E3%83%86%E3%82%AF%E3%83%8B%E3%83%83%E3%82%AF
https://wiki.archlinux.jp/index.php/Xorg_%E3%81%A7%E3%81%AE%E3%82%AD%E3%83%BC%E3%83%9C%E3%83%BC%E3%83%89%E8%A8%AD%E5%AE%9A

https://wiki.archlinux.jp/index.php/X_keyboard_extension
https://wiki.archlinux.org/title/X_keyboard_extension
https://www.x.org/wiki/XKB/
https://junology.hatenablog.com/entry/2017/06/24/141322#fn-0b8eec0f

## 設定

`/usr/share/X11/xkb/` が設定ディレクトリになる。
`rules/evdev` の symbols に追記
```
! option        =       symbols
  ...
  jpkey:util            =       +jpkey(util)
  vimmer:caps_arrow             =       +vimmer(caps_arrow)
```

symbols/jpkey を作成
AE13　の行は　vimmer 用にハット右のエスケープを$にしている。
```
hidden partial modifier_keys
xkb_symbols "util" {
    key <AE13> { [ dollar, bar ] };
    key <MUHE> { [ Control_L, Control_L ] };
    key <HENK> { [ Escape, Escape ] };
    key <HKTG> { [ underscore, underscore ] };
    modifier_map Control { <MUHE> };
};
```

symbols/vimmer を作成
```
// ref. https://wiki.archlinux.org/title/X_keyboard_extension#Caps_hjkl_as_vimlike_arrow_keys
// ref. https://wiki.archlinux.org/title/X_keyboard_extension#Level3
xkb_symbols "caps_arrow" {
    //key <LWIN> { [ ISO_Level3_Shift ] };
    key <AC06> {
        type= "THREE_LEVEL",
        symbols= [               h,               H, Left]
    };
    key <AC07> {
        type= "THREE_LEVEL",
        symbols= [               j,               J,               Down]
    };
    key <AC08> {
        type= "THREE_LEVEL",
        symbols= [               k,               K,               Up]
    };
    key <AC09> {
        type= "THREE_LEVEL",
        symbols= [               l,               L,               Right]
    };
    modifier_map Mod5 { <CAPS> };
};

```


dconf で設定。xkbcomp だとブラウザは設定が反映されるが、terminal では反映されなかった。
```
dconf write /org/gnome/desktop/input-sources/xkb-options "['caps:none', 'jpkey:util', 'vimmer:caps_arrow']"
```


## Tips

`xev` でキーの信号を確認できる。
`xkbcomp $DISPLAY now.xkb` で今の設定を now.xkb に書き出せる。設定の確認や書き方の勉強に使える。（ただし、設定が確実に反映されてるとは断言できるまで検証していない）
`/usr/share/X11/xkb/rules/*.lst` に dconf で設定できる options が一覧できる。
一度ログアウトしてログインし直すと、設定を再読込できる。ただし、うまくいかない場合もあるので、そのときは再起動が必要。

## 補足
`/usr/share/X11/xkb/` に個人の設定を追加するのは、たぶん良くない。
`xkbcomp mine.xkb $DISPLAY` では gnome に上書きされるのか、terminal などで設定が反映されなかった。
IoTシステム技術検定（中級）合格体験記



先月、IoTシステム技術検定（中級）に合格しました。
その記録を残しておこうと思います。

## 前提
・当方、文系事務職です。IoTとか5Gとか、特に業務で深く関わりがあるわけではありません。
・約3年半ほど前にIoTシステム技術検定（基礎）に合格しています。

https://qiita.com/restructure/items/121be07184e1bcd3ccfb


## 学習方法
学習では公式テキストを使いました。テキストを4月中旬に購入し、一読して何となくいけそうな感じがしたので6月初旬の日程で試験を申し込みました。実質的な学習期間は1ヶ月ちょっとという感じです。


https://www.ric.co.jp/book/network/detail/1732

基礎に合格した時は1ヶ月ほど前から学習を開始し、トータルで20〜30時間くらいは勉強しました。
中級の際も、基礎と同様、テキストを読みながらワードで単語の意味のまとめメモを作りました。（A4で30枚程度。）

Studyplusを使って学習時間を管理したのですが、記録されていたのは6時間程度でした。
記録していない部分を考慮しても、おそらく20時間未満だと思います。
（本番の試験もおそらく基礎と重複する箇所があったので、基礎の際の学習の蓄積が多少は活かされたと思います。）

## 当日
CBT方式だったのですが、自信が無い問題はチェックを付けて後から再度確認することができました。
試験時間90分のうち、70分くらいでひととおり回答し、残りの20分くらいで再確認をしたと思います。
記憶している限りセンサ関係が多く出たものの、答えられなかった記憶があります。

自信を持って答えられたのは6割前後だったと思います。
不合格かもしれないと思いましたが、結果を見たら合格でした。
【イベント】PHPカンファレンス福岡2023でLT初登壇しました

6月24日（土）に[PHPカンファレンス福岡2023](https://phpcon.fukuoka.jp/2023/)が開催されました。（4年ぶり！）

カンファレスでLT登壇を行ったので、前後の感想をまとめました。
お気軽にお読みください！

https://fortee.jp/phpconfukuoka-2023/proposal/07dea75c-91f3-4987-8d4b-3529b8e073e1

https://www.youtube.com/@phpphpconfuk

# プロポーザル応募〜採択
自分がエンジニアへ転職したきっかけの一つがカンファレンスなので、登壇は昔からの目標でした。

とはいえ、登壇に応募し始めたのはつい最近です。コロナ禍でオフラインイベントそのものが減っていたこともありますが、過去に参加したどのカンファレンスも登壇のレベルが高くて尻込みしていました。

ですが今年はエンジニア5年目の節目もあり、今後のカンファレンス開催可否も予想がつかないので思い切って応募しました。

採択の連絡をいただいた時は、嬉しい反面プレッシャーを感じました。

# 登壇準備
LTあるあるですが、当初はスライドが多すぎてトークが5分内に収まりませんでした。LT登壇は勉強会で何回か経験しましたが、未だに難しいです。（現在進行形）

最終的に自宅で何度かリハーサルを行い、何とか5分以内に完了するように調整できました。

# カンファレンス当日
登壇者受付で手続きを済ませ、スピーカー専用のTシャツとネームタグを受け取りました。

Tシャツはスタッフがピンク、スポンサーが緑、登壇者が黄色とロールで色が違うので、会場がカラフルでした。

## トーク聴講
イベント当日はほぼ会場にいました。LTは17:00スタートなので、それまでは参加者としてトークを楽しんでいました。

特に印象に残ったのがこちらのトークです。

### レガシー回避のPHP開発術：保守性の高いアプリケーションを作る方法

https://fortee.jp/phpconfukuoka-2023/proposal/3c651de4-9926-453a-98d2-69b71bb194e1

これが関係ない企業って存在するの？レベルの普遍的なテーマです。トーク時間は45分と最長でしたが、それを感じさせない構成もさすがでした。

### フレームワークが生み出す負債や複雑さに対して、PHPUnitと付き合っていく

https://fortee.jp/phpconfukuoka-2023/proposal/e1acbd97-9263-4edc-99b1-ed736b0fad8a

テストに限らずですが、チーム内における認識合わせの重要さが身に沁みます。

偶然、積読中の本が紹介されていました。（読み直そう）

https://book.mynavi.jp/ec/products/detail/id=134252

## スポンサーブース
トークと同じくらい楽しみにしていたのがスポンサーブースとノベルティです。特にスポンサーブースは全部回るつもりでした。（達成しました！）

スポンサーは常連が多く、他のカンファレンスでも見かける企業が多かったです。

所属社員が登壇している企業も何社かありました。スポンサーブース×社員登壇は印象に残るので、技術広報として強いなと感じました。

## LT登壇
壇上での発声がここまで難しいとは・・・予想以上に緊張し常に声が上擦ってしまいました。トークはお聞き苦しいところがあったと思いますが、なんとか時間内に終了できたことが救いでした。

登壇が終わった後は待機席で以降のLTを聞いていました。LTそのものは楽しめたのですが、皆さん堂々とトークをされていて同じ登壇者として改めて周りのレベルの高さを痛感しました。

怖いもの見たさでtwitterでLTの感想を確認しましたが、こちらは好意的なご意見が多く救われた気持ちでした。課題の多い登壇でしたが、少しでも皆さんのお役に立てて良かったです。

## 懇親会
東京のPHP勉強会や他のITイベントでお会いした方、スポンサーさん、スタッフさんなど様々な方達を話をさせていただきました。とても楽しい時間が過ごせました。

また、「登壇資料後で見てみます！」と言ってくださった方もいて嬉しかったです。

# 観光
初の福岡旅行、短い時間でしたが観光も楽しめました！

## グルメ
ひたすら炭水化物を摂取してました。麺類とご飯の組み合わせばかりです。

しかし欲を言えば、カンファレンス会場の目の前にあった[行列のできるラーメン屋](http://www.hakata-issou.com/)にも行きたかったです。

- 6/23（金）昼食：博多豚骨ラーメン + 明太子丼
- 6/23（金）夕食：肉ごぼう天うどん + かしわご飯
- 6/24（土）夕食：カンファレンス懇親会のビュッフェ
- 6/25（日）昼食：ビーフバター焼き（パスタ付き） + 白ご飯

どれも美味しかったです。ごちそうさまでした！

## エンジニアカフェ
ここは全エンジニアにお勧めしたいです。

国の重要文化財である福岡市赤煉瓦文化館の中にあります。コワーキングスペースとして利用可能です。

https://engineercafe.jp/ja/

レトロな空間でプログラムを書くのも乙なものです。贅沢な時間を過ごせます。

さすが[エンジニアフレンドリーシティ福岡](https://efc.fukuoka.jp/)です。

# 反省・知見
## 登壇について
壇上のスライドを見ながら堂々と話したかったのですが、これを行うと5分を超える可能性があり台本を用意せざるを得ませんでした。最後まで時間調整が難しく、自分の経験不足・練習不足を痛感しました。

また、スライド数や文字量が多すぎてトークでカバーしきれない箇所が多かったです。次はトークベースでスライドを作成しようと思いました。

あと、マイクに慣れておけば良かったです。壇上で自分の声が響くと余計に緊張します。カラオケでリハーサルもアリですね。

## カンファレンス前後のイベントについて
カンファレンス前後は非公式の前夜祭・後夜祭が実施されていたようです。

自分も勢いで参加したかったのですが、体力を考えて参加は控えました。イベントが終了した今、これは正解だったと思います。

不慣れな土地におけるイレギュラーな行動はそれなりにリスクを伴います。貴重な経験を「疲れた」ではなく「楽しかった」で終わらせるためには、自分の健康状態やキャパシティをを知っておくことが大事だと感じました。

でも、事前にイベントを調べておけば良かった・・・！（こちらは明らかな調査不足）

# まとめ
初登壇&初福岡でしたが、とても楽しく学びの多い経験になりました。

PHPカンファレンス福岡2023に関わった全ての皆さん、ありがとうございました！！！

# 採用PR
弊社で一緒に働く仲間を募集しています。

Devトークやってます。お気軽にご応募ください(^^)

https://plus.jobs.qiita.com/dev-talk-company-pickup-vivion/

募集要項はこちらをご覧ください！

https://hrmos.co/pages/vivion/jobs?category=1687770395095928832
末尾再起に関する理解

## 前提知識と言葉の定義
プログラムで関数が呼び出されると、OSはコールスタック領域に、パラメータや実行終了後に戻るべきコード内の場所など、関数に関する情報を格納する。
コールスタックとは、プログラム内で行われた関数呼び出しを追跡するために使用されるデータ構造のこと。
このコールスタックは、スタックと呼ばれる構造によって管理されているらしい。スタックは要素が入ってきた順に一列に並べ、後に入れた要素から順に取り出すという規則に従った構造で、2 つの操作 push、pop を行える。
- push：*情報をスタックに積む。
- pop：*情報をスタックから取り出す。
ここでいう *情報は冒頭の文にあるように「パラメータや実行終了後に戻るべきコード内の場所など、関数に関する」情報のこと。だけど、今回はイメージしやすいように「関数そのもの」と捉えた方が良さそう。
## 本題：末尾再起
例えば、以下のような関数を定義する（1~nまでの数の総和を計算して値を返してくれる関数）。
``` javascript
function simpleSummation(n) {
  if(n <= 0) return 0;

  return simpleSummation(n-1) + n;
}
// 引数に5をいれて実行するとき、15が返り値として渡されるまでに実際には以下のような処理が行われている。
simpleSummation(5)
simpleSummation(4) + 5
(simpleSummation(3) + 4) + 5
((simpleSummation(2) + 3) + 4) + 5
(((simpleSummation(1) + 2) + 3) + 4) + 5
(((simpleSummation(0) + 1) + 2) + 3) + 4) + 5
((((0 + 1) + 2) + 3) + 4) + 5
(((1 + 2) + 3) + 4) + 5
((3 + 3) + 4) + 5
(6 + 4) + 5
10 + 5
15
```
この時、simpleSummation(0)がよばれた段階でスタックにはsimpleSummation(0)からsimpleSummation(5)までの計6つスタックがたまっている。

一般化すると、simpleSummation(n)を実行する時、n+1個の関数が順にスタックにpushされる。メモリには物理制限があるので、nがとても大きな値になるとスタックの保存上限を超えて、スタックオーバーフロー(スタックが溢れ出る)が発生する可能性がある。
こんな時、末尾再起によってスタックオーバーフローを回避できる場合があるそう。

:::note
以下のように、"return 再帰関数()" の形、つまりほかのすべての処理の最後で再起処理が行われるものを末尾再起という。(再帰関数とは、自分自身を呼び出す処理が書かれている関数のこと。一応補足)
``` javascript
function exFunc() {
  // 諸々の処理

  return exFunc();
}
```
:::
1~nまでの数の総和を返してくれる関数を末尾再帰的な関数に書き換える。
```
function simpleSummationTail(n){
    return simpleSummationTailHelper(n, 0);
}

// 補助関数
function simpleSummationTailHelper(count, total){
    if(count <= 0 ) {
        return total;
    }

    return simpleSummationTailHelper(count-1, total+count);
}
```
末尾再帰では、計算の途中結果を保存する必要があるため、蓄積値を表す引数を追加する必要がある。その為、補助関数を使って元の関数に引数を追加して、補助関数で再帰処理を実行する。
引数に5をいれて実行すると、15が返り値として渡されるまでに実際には以下のような処理が行われる。
``` javascript
simpleSummationTail(5)
simpleSummationTailHelper(5,0)
simpleSummationTailHelper(4,5)
simpleSummationTailHelper(3,9)
simpleSummationTailHelper(2,12)
simpleSummationTailHelper(1,14)
simpleSummationTailHelper(0,15)
15
```
どちらにせよ、スタック積まれてない？って感じなのだけれど、"末尾呼び出し最適化"という末尾再起がスタックを消費しないように最適化されたコンパイル技術があるらしいらしく、これによりスタックが積み上がることを回避できる。

:::note 
**末尾呼び出し最適化**
再帰関数が呼び出された時、新しいスタックフレーム（スタックデータごとのかたまり）を確保するのではなく、既存のスタックフレームの値が更新される。simpleSummationTail で例えると、simpleSummationTail が呼ばれた時、呼び出された関数が新しいスタックフレームに push されるのではなく、既存のスタックフレームの count と total が新しい値に更新される。結果スタックには、データが1つしか積み上がらないので、メモリを圧迫しないとのこと。
:::

## コメント
間違っている箇所ありましたら、コメントお願いします。
cscの作法 その408

# 概要
cscの作法、調べてみた。
練習問題、やってみた。

# 練習問題
aviファイルをmp4ファイルに変換する、ffmpegのフロントエンドを書け。

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/428c9828-a148-528d-a670-39a8efb08680.png)

# サンプルコード

```
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using System.IO;
using System.Drawing;
using System.Windows.Forms;

namespace App
{
	class form1: Form {
		TextBox textBox0;
		TextBox textBox1;
		TextBox textBox2;
		form1() {
			Text = "ffmpeg";
			ClientSize = new Size(300, 300);
			Label label0 = new Label();
			label0.Location = new Point(20, 50);
			label0.Text = "In avi";
			textBox0 = new TextBox();
			textBox0.Location = new Point(30, 80);
			textBox0.Text = "out.avi";
			Label label1 = new Label();
			label1.Location = new Point(20, 110);
			label1.Text = "In wav";
			textBox1 = new TextBox();
			textBox1.Location = new Point(30, 140);
			textBox1.Text = "output1.wav";
			Label label2 = new Label();
			label2.Location = new Point(20, 170);
			label2.Text = "Out mp4";
			textBox2 = new TextBox();
			textBox2.Location = new Point(30, 200);
			textBox2.Text = "out.mp4";
			Button btn1 = new Button();
			btn1.Location = new Point(50, 230);
			btn1.Text = "start";
			btn1.Click += btn1_Click;
			Button btn2 = new Button();
			btn2.Location = new Point(150, 230);
			btn2.Text = "stop";
			btn2.Click += btn2_Click;
			Controls.AddRange(new Control[] {
				label0, 
				textBox0,
				label2, 
				textBox2,
				btn1
			});
		}
		void btn1_Click(object sender, System.EventArgs e) {
			Process p = new Process();
			p.StartInfo.FileName = "ffmpeg.exe";
			p.StartInfo.Arguments = "-y -i " + textBox0.Text + " " + textBox2.Text;
			Console.WriteLine(p.StartInfo.Arguments);
			p.StartInfo.RedirectStandardOutput = true;
			p.StartInfo.RedirectStandardError = true;
			p.StartInfo.CreateNoWindow = true;
			p.StartInfo.UseShellExecute = false;
			p.Start();
			p.WaitForExit();
			MessageBox.Show("Saved!!");
		}
		void btn2_Click(object sender, System.EventArgs e) {
		}
		[STAThread]
		public static void Main() {
			Application.Run(new form1());
		}
	}
}


```

以上。
cscの作法 その407

# 概要
cscの作法、調べてみた。
練習問題、やってみた。

# 練習問題
aviファイルとwavファイルを合成する、ffmpegのフロントエンドを書け。

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/c0d4b64a-9af0-f992-6366-287c40d99d21.png)

# サンプルコード

```

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using System.IO;
using System.Drawing;
using System.Windows.Forms;

namespace App
{
	class form1: Form {
		TextBox textBox0;
		TextBox textBox1;
		TextBox textBox2;
		form1() {
			Text = "ffmpeg";
			ClientSize = new Size(300, 300);
			Label label0 = new Label();
			label0.Location = new Point(20, 50);
			label0.Text = "In avi";
			textBox0 = new TextBox();
			textBox0.Location = new Point(30, 80);
			textBox0.Text = "image.avi";
			Label label1 = new Label();
			label1.Location = new Point(20, 110);
			label1.Text = "In wav";
			textBox1 = new TextBox();
			textBox1.Location = new Point(30, 140);
			textBox1.Text = "output1.wav";
			Label label2 = new Label();
			label2.Location = new Point(20, 170);
			label2.Text = "Out avi";
			textBox2 = new TextBox();
			textBox2.Location = new Point(30, 200);
			textBox2.Text = "out.avi";
			Button btn1 = new Button();
			btn1.Location = new Point(50, 230);
			btn1.Text = "start";
			btn1.Click += btn1_Click;
			Button btn2 = new Button();
			btn2.Location = new Point(150, 230);
			btn2.Text = "stop";
			btn2.Click += btn2_Click;
			Controls.AddRange(new Control[] {
				label0, 
				textBox0,
				label1, 
				textBox1,
				label2, 
				textBox2,
				btn1
			});
		}
		void btn1_Click(object sender, System.EventArgs e) {
			Process p = new Process();
			p.StartInfo.FileName = "ffmpeg.exe";
			p.StartInfo.Arguments = "-y -i " + textBox0.Text + " -i " + textBox1.Text + " " + textBox2.Text;
			Console.WriteLine(p.StartInfo.Arguments);
			p.StartInfo.RedirectStandardOutput = true;
			p.StartInfo.RedirectStandardError = true;
			p.StartInfo.CreateNoWindow = true;
			p.StartInfo.UseShellExecute = false;
			p.Start();
			p.WaitForExit();
			MessageBox.Show("Saved!!");
		}
		void btn2_Click(object sender, System.EventArgs e) {
		}
		[STAThread]
		public static void Main() {
			Application.Run(new form1());
		}
	}
}

```

以上。
cscの作法 その406

# 概要
cscの作法、調べてみた。
練習問題、やってみた。

# 練習問題
一つの画像からaviファイルを生成する、ffmpegのフロントエンドを書け。

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/0938a39a-addd-ff8a-68e8-2abec09cf994.png)

# サンプルコード

```







using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using System.IO;
using System.Drawing;
using System.Windows.Forms;

namespace App
{
	class form1: Form {
		TextBox textBox0;
		TextBox textBox1;
		TextBox textBox2;
		form1() {
			Text = "ffmpeg";
			ClientSize = new Size(300, 300);
			Label label0 = new Label();
			label0.Location = new Point(20, 50);
			label0.Text = "Image";
			textBox0 = new TextBox();
			textBox0.Location = new Point(30, 80);
			textBox0.Text = "1.png";
			Label label1 = new Label();
			label1.Location = new Point(20, 110);
			label1.Text = "Frame";
			textBox1 = new TextBox();
			textBox1.Location = new Point(30, 140);
			textBox1.Text = "1";
			Label label2 = new Label();
			label2.Location = new Point(20, 170);
			label2.Text = "File";
			textBox2 = new TextBox();
			textBox2.Location = new Point(30, 200);
			textBox2.Text = "image.avi";
			Button btn1 = new Button();
			btn1.Location = new Point(50, 230);
			btn1.Text = "start";
			btn1.Click += btn1_Click;
			Button btn2 = new Button();
			btn2.Location = new Point(150, 230);
			btn2.Text = "stop";
			btn2.Click += btn2_Click;
			Controls.AddRange(new Control[] {
				label0, 
				textBox0,
				label1, 
				textBox1,
				label2, 
				textBox2,
				btn1
			});
		}
		void btn1_Click(object sender, System.EventArgs e) {
			Process p = new Process();
			p.StartInfo.FileName = "ffmpeg.exe";
			p.StartInfo.Arguments = "-y -f image2 -i " + textBox0.Text + " -r 1 -loop 1 -t " + textBox1.Text + " " + textBox2.Text;
			Console.WriteLine(p.StartInfo.Arguments);
			p.StartInfo.RedirectStandardOutput = true;
			p.StartInfo.RedirectStandardError = true;
			p.StartInfo.CreateNoWindow = true;
			p.StartInfo.UseShellExecute = false;
			p.Start();
			p.WaitForExit();
			MessageBox.Show("Saved!!");
		}
		void btn2_Click(object sender, System.EventArgs e) {
		}
		[STAThread]
		public static void Main() {
			Application.Run(new form1());
		}
	}
}




```

以上。
システムハンガリアンでもいいじゃない（よくない）

## 前書き

世間から嫌われているシステムハンガリアン記法を少しだけ擁護します。
またJavaScriptみたいなガチガチに型を意識させない言語を想定しています。

## 追記

コメント欄で教えていただきましたが、本記事で使っている記法はシステムハンガリアン記法ではなくイングリッシュ記法と言われるものだそうです。
という訳で、本文のシステムハンガリアン記法はイングリッシュ記法だと読み替えてください。

なお、システムハンガリアン記法は事前知識がないとなんだこれ？となりイングリッシュ記法よりも分かりにくいなと思いました。
イングリッシュ記法という上位互換がある以上、わざわざシステムハンガリアンを使うことはないでしょう。（手のひら返し）

## 本文

型は適切な変数名で分かるからいらん。
邪魔
見づらい
型が変わったときに変数名を直すのダルい。
システムハンガリアン許さん。このクソを擁護するやつも許さん。

と、思われているだろうシステムハンガリアンですが
私はすべてが駄目で一切使ってはいけない。とは思いません。

例えば次のようなコードはシステムハンガリアン記法を使うのがシンプルで分かりやすいと思います。
（少なくとも私は。）

```javascript
const today = new Date();
const strToday = `${today.getFullYear()}年${today.getMonth() + 1}月${today.getDate()}日`;
```

today, strTodayは宣言後にテキトーに使われると考えといてください。
DBに登録されるとか、画面に表示されるとか、計算に使われるとかするんでしょう。きっと。

要するにシステムハンガリアン記法は、
型が違うけど名前が似てしまう！いい名前が思いつかん！って時に使うと、思考停止で命名できて、型の違いも分かって、普通にシンプルで、変数名も短くていいんでない。
ということです。
（ただtodayをdtTodayと書くのはさすがにないと思います。）

以上、終わり。

システムハンガリアン（笑）みたいな考えだと、仮にシステムハンガリアンが有用な場面が来ても見落としてしまうかもしれないので一応書いておきました。
Astro + Newtな環境をNetlifyでデプロイする

こんにちは。齋藤（[@wtoddev](https://twitter.com/wtoddev)）です。

# はじめに
今回はJSフレームワークの`Astro`とヘッドレスCMSの`Newt`を組み合わせた開発環境をNetlifyでデプロイしようとしたら躓いたのでデプロイ方法を備忘録的に記します。

## 環境

```
├── @types/node@20.4.2
├── astro@2.8.3
├── newt-client-js@3.2.5
└── sass@1.63.6
```
AstroはReactやVueなどの他のUIフレームワークと合わせて開発ができる優れものなのですが、今回の環境ではピュアなAstroを使用しています。

他のフレームワークとの組み合わせについて詳しく知りたい方はこちらをご覧ください。

https://docs.astro.build/en/guides/integrations-guide/

また、ブログのようにコンテンツ配信をするために、ヘッドレスCMSのNewtを使用しています。こちらも詳しくは公式ページをご覧ください。

https://www.newt.so/

# デプロイ手順

## 1: `the Netlify Adapter`をインストール

```
npx astro add netlify
```

## 2: `astro.config.mjs`でoutputを変更

`the Netlify Adapter`をインストールした段階で`astro.config.mjs`に以下が追加されます。

```js:astro.config.mjs
import netlify from '@astrojs/netlify/functions';

export default defineConfig({
  output: 'server',
  adapter: netlify(),
});
```

:::note
Netlify’s experimental Edge Functionsを使用する場合
```js
import netlify from '@astrojs/netlify/edge-functions';
```
:::

デフォルトでは`output`の値がSSRモードである`server`に指定されていますが、SSRの事前レンダリングを反転させる`hybrid`を指定します。

:::note warn
`output: 'hybrid',`と指定しないと、デプロイ時にビルドの段階でエラーになり失敗する現象が発生します。
:::

最終的な`astro.config.mjs`はこちら。

```js:astro.config.mjs
import { defineConfig } from 'astro/config';
import netlify from '@astrojs/netlify/functions';

export default defineConfig({
  output: "hybrid",
  adapter: netlify()
});
```

## 3: pushしてデプロイ
NetlifyとGitHubのリポジトリの連携をすることで、事前に設定したブランチにpushするだけで自動的にデプロイされます。
設定方法は公式ドキュメントをご覧ください。

https://docs.netlify.com/site-deploys/create-deploys/

## 4: デプロイ成功
以上の手順で無事にデプロイすることができました。

# さいごに
今回はAstro + Newtな環境をNetlifyでデプロイする手順についてご紹介しました。Newtが国産CMSであることからなのか、この組み合わせの情報が少なかったので書いてみました。Astroユーザーの皆さんの一助となれば幸いです。

誤りなどがありましたらご指摘頂けますと幸いです。
ご精読ありがとうございました。

## 参考文献
https://docs.astro.build/ja/guides/deploy/netlify/

https://astro.build/blog/hybrid-rendering/
CSVから経度・緯度を抜き出して、LineStringのGeoJsonを作る

# 経緯
[地理院地図](https://maps.gsi.go.jp/#18/36.536095/140.235288/&base=ort&ls=ort&disp=1&vs=c1g1j0h0k0l0u0t0z0r0s0m0f1&d=m) でCSVを読み込ませたら、LineStringにならなかったので、ちょっと作ってみた

どっかにツールありそうだけど。。。

# 実装
ファイルアップロードしたら、X,Y（経度、緯度）を取得してGeoJsonをダウンロードするリンクを作る。

FileUploadのI/Fが変わっていたのはびっくりしたけど。。。
それと、```Data URIスキーム```の用語を忘れていて、リンクのつくり方検索するの手間がかかったけど。。。

```python
import ipywidgets as widgets
import base64
import json
import io
import pandas as pd
from IPython.display import HTML


def create_download_link(data, title = "Download CSV file", filename = "data.csv"):
    b64 = base64.b64encode(data.encode())
    payload = b64.decode()
    html = '<a download="{filename}" href="data:text/csv;base64,{payload}" target="_blank">{title}</a>'
    html = html.format(payload=payload, title=title, filename=filename)
    return HTML(html)

# df = pd.DataFrame(data = [[1,2],[3,4]], columns=['Col 1', 'Col 2'])
# display(create_download_link(df.to_csv()))

class Dict(dict): __getattr__ = dict.__getitem__; __setattr__ = dict.__setitem__

def gen_geojson(df):
    fs = Dict()
    fs.type = 'Feature'
    fs.geometry = Dict()
    fs.geometry.type = 'LineString'
    fs.geometry.coordinates = df[['X', 'Y']].to_numpy().tolist()

    o = Dict()
    o.type = 'FeatureCollection'
    o.features = [fs]
    
    return json.dumps(o)


upload = widgets.FileUpload(
    accept='.csv',
    multiple=False
)
output = widgets.Output()

def upload_file(e):
    if len(upload.value) < 1:
        return
    
    content = upload.value[0]['content'].tobytes()
    df = pd.read_csv(io.BytesIO(content))
    geo = gen_geojson(df)
    with output:
        display(create_download_link(geo, "Download GeoJson file", "B2_VTXP.geojson"))
    
    
    
upload.observe(upload_file, names='value')
display(upload)
display(output)
```
XcodesでXcodeを複数バージョンインストールして管理出来ると知ったのでCLI/GUI双方インストールしてみた

# 概要
筆者はこれまでXcodeはCLI[^xcode-select]の方にはお世話になってきましたが、GUIアプリ版にお世話になることは有りませんでした。しかし、最近Unityの勉強に凝ってしまいました。ところが、Unityで実装したアプリをiPad(iOS)用にビルドする場合、仕上げの段階でXcodeが必要なことが分かりました。迷わずApp Storeからインストールしようと思った矢先、`Xcodes`なるアプリケーションに出会いました。これを用いることで、App Storeからインストールした場合は1バージョン(つまり最新版)しかインストール出来ないXcodeが複数バージョンインストール可能とのこと。`*env`(*はrb, py, go,...)と同様のことが出来る様です。よってCLI/GUI両バージョンをインストールして使ってみることにしました。

[^xcode-select]: xcode-selectコマンド

# 環境情報
* macOS 12.6.7 Monterey

# Xcodes CLI版のインストール
Homebrewを用いてインストールします。以下のコマンドを実行します。

```bash
brew install xcodesorg/made/xcodes
```

# Xcodes GUI版(Xcodes.app)のインストール
こちらもHomebrewを用いてインストールします。以下のコマンドを実行します。本来は片方だけインストールすれば良いのですが、CLI版でインストールした結果をGUI版で確認出来るのと便利だと思ったため、筆者は両方インストールしました。

```bash
brew install --cask xcodes
```

# Xcodesを実際に使ってみる
実際にXcodesを使ってみます。まず、現状インストール可能なバージョンを確認します。

```bash
xcodes list
```

現状バージョン`14.2`がMontereyにインストール可能なリリース版としては最も最新の様です。よってバージョン14.2をインストールします。初回のみApple IDのアカウント情報の入力が求められます。また、インストール最終段階でユーザーパスワードの入力が毎回求められます。

```bash
xcodes install 14.2
```

ここで気になるメッセージが表示されていることに気付きました。XcodeのソースのダウンロードにurlSessionを利用しているが、より早くダウンロードしたいのであればaria2をインストールした方が良いことを示唆しています。

```bash
Downloading with urlSession - for faster downloads install aria2 (`brew install aria2`)
```

双方ともダウンロードマネージャですが、aria2は複数のサーバーから分割ダウンロードすることで高速化を図る特徴が有る様です。

https://ja.wikipedia.org/wiki/Aria2

## urlSessionを利用した場合

折角なので、urlSession利用してダウンロードした場合とaria2を利用してダウンロードした場合の時間を計測してみたいと思います。測定には`time`コマンドを利用します。

```bash
time xcodes install 14.2
```

結果として54分掛かりました。回線の状況によって上下するとは思いますが、約1時間近く掛かっています。

```bash
real    54m20.806s
user    11m33.712s
sys 5m38.195s
```

それでは、一旦インストールしたXcodeをアンインストールします。/Applications/Xcode-14.2.0.appがゴミ箱に移動します。

```bash
xcodes uninstall 14.2
```

## aria2を利用した場合

aria2をHomebrewを用いてインストールします。

```bash
brew install aria2
```

aria2のインストールが完了したら、再度timeコマンドを用いてインストールに要する時間を計測します。

```bash
time xcodes install 14.2
```

インストール開始時に以下のメッセージが表示される様になりました。無事ダウンロードマネージャが切り替わった様です。

```bash
Downloading with aria2 (/opt/homebrew/bin/aria2c)
```

今度は20分でインストール出来ました。約1/3の時間でインストール出来る様になりました。矢張り、aria2をインストールしたことで大幅に高速化出来ました。

```bash
real    20m5.613s
user    10m30.760s
sys 4m57.019s
```

# CLI/GUI双方利用を試す
ここから検証したOSが異なります。

* macOS 13.4.1(c) Ventura

Ventura向けリリース版としては最も最新の14.3.1をインストールします。

```bash
xcodes install 14.3.1
```

インストール後に`xcodes list`コマンドを実行します。リリース版が`Installed`になっていることが分かります。

```bash
$ xcodes list | grep 14.3.1
14.3.1 Release Candidate (14E300b)
14.3.1 (14E300c) (Installed)
```

GUI版で確認してみます。14.3.1がインストール済バージョンとして表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469079/d580eac7-fca4-260f-d5a6-12584b86e16d.png)

# まとめ
Xcodeのバージョン管理ツールとしてXcodesのインストールし、実際に利用する方法、ダウンロードマネージャの変更による高速化を扱いました。折角Xcodeをインストールしたので余裕が有れば、Swiftの勉強も始めてみようかと思った次第です。

# Reference
https://qiita.com/yamatatsu10969/items/439329c8764be477fd29

https://github.com/XcodesOrg/xcodes

https://github.com/XcodesOrg/XcodesApp

https://developer.apple.com/jp/support/xcode/
2023/7/17主にITとかセキュリティの記事

Windows、macOS、Linuxを狙う標的型攻撃をJPCERT/CCが分析、確認を(7/15)
https://news.mynavi.jp/techplus/article/20230715-2726993/

iPhone、iPad、Macの脆弱性悪用について米当局が警告、ただちにアップデートを(7/15)
https://news.mynavi.jp/techplus/article/20230715-2727323/

新着情報 – 規制コンプライアンスとランサムウェア対策を目的として Amazon FSx for NetAPP ONTAP が WORM 保護のサポート提供を開始(7/14)
https://aws.amazon.com/jp/blogs/news/new-amazon-fsx-for-netapp-ontap-now-supports-worm-protection-for-regulatory-compliance-and-ransomware-protection/

マルウェア解析技術に関する特許を取得(7/14)
https://news.nicovideo.jp/watch/nw12982756

DOJ（米国司法省）は、DeFiプラットフォームから900万ドルを吸い上げたとして元セキュリティエンジニアを告発(7/14)
https://nextmoney.jp/?p=59744

ステーブルコインに依存する暗号資産市場、TUSDはリスク：カイコ(7/14)
https://www.coindeskjapan.com/194040/

家庭用ルータが危ない！ サイバー犯罪の「踏み台」にされないために今すぐやるべきこと(7/14)
https://news.yahoo.co.jp/articles/924548796d470d760190b9e0257ddb4146a4a5b5?source=rss

クーコイン（KuCoin）は不安定な仮想通貨市場の中でユーザー数は2,900万人を突破(7/14)
https://nextmoney.jp/?p=59742

台湾有事シミュレーション実施　日米に加え台湾初参加(7/15)
https://www.fnn.jp/articles/-/557506
https://news.yahoo.co.jp/articles/b682df24d99eb28fd7cb599e22751c34719016cd

「FA機器.com」に不正アクセス、5,877名分のカード情報が漏えいした可能性(7/14)
https://scan.netsecurity.ne.jp/article/2023/07/14/49678.html

お客様情報の漏洩可能性に関するお詫びとお知らせについて（再発防止策のご報告）(7/13)
https://bestcarweb.jp/bc-newsrelese/675334

欧州委員会、EU機関におけるサイバーセキュリティ強化のための新規則で政治合意(7/14)
https://esgjournaljapan.com/world-news/30041

ルフィ事件はなぜ防げない？　特殊詐欺の課題と対策を専門家に聞いた(7/15)
https://news.nifty.com/article/magazine/12193-2445860/

マイナ問題で注目される「個人情報保護委員会」　追及の手はどこまで？　所管はこちらも河野太郎デジタル相(7/15)
https://www.tokyo-np.co.jp/article/263255

日欧の安保協力　アジア安定促す枠組みに(7/15)
https://mainichi.jp/articles/20230715/ddm/005/070/164000c

韓国版マイナンバーカードは超便利！在韓日本人が見た“不便すぎる日本”と国民の反応の差(7/15)
https://news.yahoo.co.jp/articles/36552ed419f4a76c0185407a039192f34eac7be0?source=rss
https://news.nifty.com/article/magazine/12126-2446193/

王毅主任　米国務長官との会談でサイバーセキュリティーと薬物乱用取締りで立場表明(7/15)
https://news.nicovideo.jp/watch/nw12983974

世界ハッカー大会で上位総なめ、北朝鮮「ＩＴエリートの卵」(7/15)
https://news.nicovideo.jp/watch/nw12980709
ABC310E DP解のメモ

# ABC310E

https://atcoder.jp/contests/abc310/tasks/abc310_e

# DP解


この記事はkyopro_friendsさんの解説を自分なりに理解した際のメモ。
解放ありきで考えてるので、なぜこういう考えが浮かんでくるのかは説明していない。

https://twitter.com/kyopro_friends/status/1680214147697606657

DP配列`dp[j][k]`を考える
`f(i, j) = k(0 or 1)` になる`i`の個数`(1<=i<=j)`とする。

具体的に$f(i, j)$がどのような値になるか書き出してみる。
入力例 1
$N=5, S=00110$

```math
\begin{align}
f(1, 1) &= A_{1} = 0\\
\\
f(2, 2) &= A_{2} = 0\\
f(1, 2) &= f(1, 1) ⊼ A_{2}\\
        &= A_{1} ⊼ A_{2}\\
\\
f(3, 3) &= A_{3} = 1\\
f(2, 3) &= f(2, 2) ⊼ A_{3}\\
        &= A_{2} ⊼ A_{3}\\
f(1, 3) &= f(1, 2) ⊼ A_{3}\\
        &= A_{1} ⊼ A_{2} ⊼ A_{3}\\
\\
f(4, 4) &= A_{4} = 1\\
f(3, 4) &= f(3, 3) ⊼ A_{4}\\
        &= A_{3} ⊼ A_{4}\\
f(2, 4) &= f(2, 3) ⊼ A_{4}\\
        &= A_{2} ⊼ A_{3} ⊼ A_{4}\\
f(1, 4) &= f(1, 3) ⊼ A_{4}\\
        &= A_{1} ⊼ A_{2} ⊼ A_{3} ⊼ A_{4}\\
\\
f(5, 5) &= A_{5} = 0\\
f(4, 5) &= f(4, 4) ⊼ A_{5}\\
        &= A_{4} ⊼ A_{5}\\
f(3, 5) &= f(3, 4) ⊼ A_{5}\\
        &= A_{3} ⊼ A_{4} ⊼ A_{5}\\
f(2, 5) &= f(2, 4) ⊼ A_{5}\\
        &= A_{2} ⊼ A_{3} ⊼ A_{4} ⊼ A_{5}\\
f(1, 5) &= f(1, 4) ⊼ A_{5}\\
        &= A_{1} ⊼ A_{2} ⊼ A_{3} ⊼ A_{4} ⊼ A_{5}\\
\end{align}
```

ここで、$f(4, 4)$〜$f(1, 4)$を抜き出してみる。

```math
\begin{align}
f(4, 4) &= A_{4} = 1\\
f(3, 4) &= f(3, 3) ⊼ A_{4}\\
        &= A_{3} ⊼ A_{4}\\
f(2, 4) &= f(2, 3) ⊼ A_{4}\\
        &= A_{2} ⊼ A_{3} ⊼ A_{4}\\
f(1, 4) &= f(1, 3) ⊼ A_{4}\\
        &= A_{1} ⊼ A_{2} ⊼ A_{3} ⊼ A_{4}\\
\end{align}
```

$f(4, 4)$〜$f(1, 4)$はDP配列では`DP[4][k]`に該当する。
$f(4, 4)$以外の右辺の最初の項に注目してみると、$f(3, 3)$〜$f(1, 3)$となっている。
これは`DP[3][k]`に該当する。
$f(3, 3)$〜$f(1, 3)$は1か0しか取らないので、
右辺のそれぞれの値は$0⊼A_{4}$と$1⊼A_{4}$の2通りしかない。
そうすると$f(3, 3)$〜$f(1, 3)$に含まれる0と1の個数が欲しい気持ちになってくる。
具体的に$f(3, 3)$や$f(2, 3)$の値がわからなくても、
$f(3, 3)$〜$f(1, 3)$の中の0と1の個数が分かれば$f(3, 4)$〜$f(1, 4)$の中の0と1の個数がわかるのだ。

DPの遷移としては以下のようになる。

```
dp[j][nand(Ai, 0)] += dp[j-1][0];
dp[j][nand(Ai, 1)] += dp[j-1][1];
```

上記の説明だとこれが漏れているのでこれも足そう。
```math
f(4, 4) = A_{4} = 1
```

```
dp[j][Ai] += 1; 
```

さて、ここまでの説明で以下の値を得ることができた。

```
dp[1][0]
dp[1][1]
dp[2][0]
dp[2][1]
dp[3][0]
dp[3][1]
dp[4][0] // f(1, 1)~f(4, 4) のうち0となる数
dp[4][1] // f(1, 1)~f(4, 4) のうち1となる数
dp[5][0]
dp[5][1]
```

最終的に求めたい値は、以下なのであるがこのうち0は足しても意味がないので1だけ考える。
```math
f(1, 1)
+ f(2, 2)〜f(1, 2)
+ f(3, 3)〜f(1, 3)
+ f(4, 4)〜f(1, 4)
+ f(5, 5)〜f(1, 5)
```

つまり求めたい値は以下となる。
```
dp[1][1] + dp[2][1] + dp[3][1] + dp[4][1] + dp[5][1]
```

以上です。
adobe illustrator でファイルを開いた時に、ファイル保存形式のバージョンがわからんと言ってたので作ってみた

ちゃーっす。
adobe製品はillustrator, photoshop, premireしかしらない人間です。

今回はillustratorのファイルを開いた状態で、ファイルの保存形式を確認する方法を模索したという話です。

きっかけは、デザインチームの「入稿時に保存形式が指定されているがわからない」という話題から始まり。
ちょっと面白そうだったので、この連休三日間の暇つぶしで解決してみようかなと思いました。
（qiita記事にもなって一石二鳥）

### 導入： 
ファイルの保存形式というのは、入稿時によくCSのみとか、CCとか指定が入ることが多いようです。
実際「別名保存」=>「illustratorオプション」のところで選択できます。
![Illustrator_オプション_と_名称未設定-1___78_47____CMYK_プレビュー_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516317/9da0a71d-a763-73df-efa5-429c27f4233c.png)

そしてこのファイルの保存形式について、illustrator上では確認しづらいようです。


### 確認方法： 
.aiのファイルをテキストエディタなりで読み取ると確認できるみたい。

今回はvscordだとみやすいようです。
以下画像の中のCreatorという箇所の情報がファイル形式を特定する鍵になります。
![test_ai_—_sandbox.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516317/88afe873-84ba-4f07-6e8b-b0124e8e8731.png)

「Adobe Illustrator(R)」以降の数字を以下の対応表に当てはめると保存している形式がみつかる。
|  表記名  |  バージョン  |
| ---- | ---- |
| 11.0 | CS |
| 12.0 | CS2 |
| 13.0 | CS3 |
| 14.0 | CS4 |
| 15.0 | CS5 |
| 16.0 | CS6 |
| 17.0 | CC (レガシー) |
| 24.0 | CC 2020 |

なので、ファイルを解析するスクリプトを書くといいみたいだということがわかる。

### adobeで使える言語
adobeで使える言語は調べてみると以下のようです。
- applescript（macのみ）
- vbscript(windowsのみ)
- javascript(mac,windows両環境つかえる)

今回は普段使っていて学習コストの少ないjavascriptを使っていきます。
[ExtendScript](https://extendscript.docsforadobe.dev/)というらしい。

補足：ExtendScriptについて
[ここ](https://sppy.stars.ne.jp/blog/789)が詳しく載っていている。
ES3相当しか使えないため、普段使っている手法（アロー関数とか）なしで開発しなれければいけないのと、若干javascriptと解釈が異なる部分があるので注意が必要。ネストする三項分岐とかは特に意識したほうがいいよですね。
以上のことからあまりadobeは、開発に積極的じゃない感がよく伺える。
（サブスク高いし、契約形態が融通性がないから、そこらへんもっと提供しろよという気持ちは置いておこう）


### コード:

もっと改良点はありそうだが、あげておく。
（記事書いてる途中で、try-catchの意味がない気がしてきた。）
```javascript
var version_map = {
    "11.0":"CS",
    "12.0":"CS2",
    "13.0":"CS3",
    "14.0":"CS4",
    "15.0":"CS5",
    "16.0":"CS6",
    "17.0":"CC legacy",
    "24.0":"CC 2020",
} 

var re = /%Creator:\sAdobe\sIllustrator\(R\)\s(\d+\.\d+)/;
var f = File (app.documents[0].fullName);
if (f.open('r')){
    while (!f.eof){
        if(f.readln().match (re)) {
            try {
                alert ('形式バージョン: '+ version_map[RegExp.$1]);
            }
            catch(err){
                alert ('形式バージョン: unknown');
            }
            break;
            }
        }
    }
```

### 使い方:
[このかた](https://hamfactory.net/illustrator/script-basic/)の記事がとてもわかりやすいのでご参考ください。

こんな感じで表示されます。
![名称未設定-1_ai___78_47____CMYK_プレビュー_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516317/2125f28f-2416-5838-417c-6552adb5e84c.png)


### おわりに:
結構簡単にできた。（とかいいつつ1日ぐらい使ったけど）
javascriptでだいたいできる世の中は素敵だ。

こういう「ちょっとだけ痒いところに手が届くツール」を作るのは大好物なので苦にならないから楽しく作れる。

使ってくれるといいな・・・。

### 参考文献：
[JavaScript Tools Guide CC](https://extendscript.docsforadobe.dev/)
kawamoto_α氏　[ExtendScriptについて知ってることをまとめた](https://sppy.stars.ne.jp/blog/789)
hamko氏 [Illustratorでスクリプトを使ってみよう［標準機能編］](https://hamfactory.net/illustrator/script-basic/)