OmniAICreator/Qiita-1.07M huggingface dataset:
ElectronのPDF出力でページ番号を付ける


## はじめに

ElectronアプリからPDF出力するときに、PDFにページ番号を付けられるようになりました。これは、Electronのversionが21以降で`printToPDF()`関数の仕様が変わったためです。

いくつか試行錯誤も必要だったので、ここで共有します。

## 条件と必要なこと


- PDFの出力は`webContents.printToPDF()`関数を用いている。
- Electronのversionを21以上にする。一方で、versionを上げることによるAPIの変更もあるので気をつけましょう。公式の[破壊的変更](https://www.electronjs.org/ja/docs/latest/breaking-changes)を参考にするとよいと思います。

## コードの主な修正法

version 20以前のコードで次のようにしてPDFを出力していたとしましょう
```javascript

const wc = mainWindow.webContents;
wc.printToPDF({pageSize:"A4", marginsType:0}).then(data => {
    fs.writeFile(result.filePath, data, (error) => {
        if (error) throw error;
        console.log('Write PDF successfully.');                
    })
}).catch(error => {
    console.log(error);
});  
```

三行目以降は皆さんのアプリでやりたいことに依存していると思いますが、シンプルにすると上記のような感じでしょう。

PDFの出力に関する設定は、2行目の`printToPDF`関数の第一引数でした。Electronのversion 21以降では、ここの書式が変更になりました。例えば

- `marginsType`の廃止
- `margins`の追加
- `headerTemplate`の追加
- `footerTemplate`の追加

などです。今回は、この`headerTemplate`や`footerTemplate`を使って、PDFにページ番号を出力します。これらには文字列としてHTML要素を渡すことができます。

今回はfooterにページ番号を表示し、headerには何も表示しないことにします。私の場合は、上記のコードを次のように変更しました。変更部分は、`printToPDF`関数の第一引数のみです。
```javascript
const headerHTML = "<div></div>"; //空文字ではheaderにdefault文字が出てしまう//
const footerHTML = "<div style=\"width:100%; text-align: center; font-size: 8pt; font-family: 'Times', 'Times New Roman', 'serif';\"><span class=pageNumber></span></div>";
            
const wc = mainWindow.webContents;
wc.printToPDF({pageSize:"A4",  
        margins:{top:0,bottom:0,left:0,right:0},
        displayHeaderFooter: true,
        headerTemplate:headerHTML,
        footerTemplate:footerHTML}).then(data => {
    fs.writeFile(result.filePath, data, (error) => {
        if (error) throw error;
        console.log('Write PDF successfully.');                
    })
}).catch(error => {
    console.log(error);
});  
```

変更点及び注意点は次の通りです。

- `marginsType`の廃止に伴い、`margins`にて0をセットする。実際の余白は、CSSで設定しているためです。
- `displayHeaderFooter: true`これを記述しないと、headerやfooterが表示されません。
- `headerTemplate:headerHTML`今回はheaderには何も表示したくありません。`headerHTML`文字列変数を通じて空の`div`要素を入れることで、表示を空にしています。注意点として、空文字`""`などをセットしても、デフォルトでタイプスタンプやファイル名が指定されてしまいます。
- `footerTemplate:footerHTML`フッター用のHTMLを文字列として渡します。文字列変数`footerHTML`に一度文字列をセットしてから、`printToPDF`関数の引数に渡せます。

さて、`footerTemplate`にセットするフッター用のHTMLでは、「ページ番号」と「スタイル」を両方指定します。

- ページ番号は`<span class=pageNumber></span>`とすることで挿入されます。
- この他にも全ページ数などの情報が埋め込めます。詳しくはElectronのAPIを参照されたし。

また、スタイルに関してはCSSファイルとして渡すことができなかったので、仕方なく`div`タグの`style`プロパティーとして直書きしています。気づいた注意点は以下の点です。

- デフォルトのフォントが非常に小さいので、`font-size`は必ず指定したほうが良い。
- フォントファミリーとして、`Times New Roman`が効かないので、`Times`も指定すること。私の環境では`serif`も効かなかった。

以上で、ページ番号がPDFに埋め込めるようになりました。

## おわりに

PDFへのページ番号の表示は長年待っていた機能だったので嬉しいですね。ただし、直接印刷する`webContents.print()`関数の方では、第一引数のオプションが異なり、ページ番号を入れられるかどうか不明です。
FileMakerからChatGPTAPIを実行して自動応答を実現する方法


**はじめに**

本記事では、FileMakerとChatGPTAPIを連携させる方法を紹介します。FileMakerは、Claris社が提供するビジネス向けデータベースアプリケーションであり、ChatGPTAPIはOpenAIが提供するAPIの1つで、自然言語処理を利用して会話を行うことができます。

具体的には、FileMakerからChatGPTAPIを実行し、ユーザーからの質問に対してChatGPTが回答を返す仕組みを構築します。

**FileMakerとは**

FileMakerは、ビジネス向けデータベースアプリケーションであり、Mac、Windows、iOS、Webなどのプラットフォームで動作します。ビジネスプロセスの自動化や情報管理、分析などに利用されます。

**ChatGPTとは**

ChatGPTは、OpenAIが提供する自然言語処理モデルの1つで、人間のような自然な会話を行うことができます。ChatGPTAPIは、このChatGPTを利用するためのAPIで、RESTful APIを提供しています。
FileMakerからChatGPTAPIを実行する

FileMakerからChatGPTAPIを実行するには、以下の手順を実施します。

   1\. 必要な変数を設定する
   2\. cURLを使用してChatGPTAPIを実行する

**必要な変数を設定する**

FileMakerからChatGPTAPIを実行するためには、以下の4つの変数を設定する必要があります。

 * $OrganizationID : 組織ID
  * $APIKEY : APIキー
  * $URL : ChatGPTAPIのエンドポイント
  * $data : ChatGPTAPIに渡すデータ

以下が、変数を設定するためのスクリプトです。
OrganizationIDとAPIKEYには、それぞれ、組織IDとAPIキーを設定してください。

```
変数を設定[$OrganizationID; 値:""]
変数を設定[$APIKEY; 値:""]
変数を設定[$URL; 値:"https://api.openai.com/v1/chat/completions"]
変数を設定[$data; 値:"{" & 
  \"model\" & ":" & \"gpt-3.5-turbo\" & ","  &  
  \"messages\" & ":[{" & \"role\" & ":" & \"user\" & "," &
  \"content\" & ":" &  
\"
ここに質問事項を書きまっす。
\" )  & "}]" & 
"}"]
変数を設定[$cURL; 値:"-X POST 
 -H \"Content-Type: application/json\"
 -H \"Authorization: Bearer " & $APIKEY & "\"
 -d \"@$data" ]
```

$dataは以下のように構成されています。

    "model":"gpt-3.5-turbo"：使用するモデルを指定します。
    "messages":[{"role":"user","content":"ここに質問事項を書きまっす。"}]：送信するメッセージを指定します。ここではユーザーからの質問を送信しています。

これで、FileMakerからChatGPTAPIを実行することができます。


と書いてきました。
これはChatGPT様に書いて貰いました。
中々いいんじゃないでしょうか。
変数を設定の部分は自分のをコピペしてますが記事の内容自体はChatGPTです。
所々改変してるので、動かなかったらごめんね。
あと1ヶ月前くらいに趣味で遊んだくらいのものなので、突っ込まないでね。(笑)

けどようやく時代がここまできたって感じですね。
共存共栄できるように頑張って行きましょう。
satisfiesオペレーターを使用した型チェックを行う

TypeScript4.9から追加になった`satisfies`オペレーターを使用して、型チェックを行う方法を紹介します。

## 想定する状況
`Personality`型を使って、personalityオブジェクトに対し型チェックを行います。
```ts
type Personality = {
    name: string
    age: number
    hobby: string[] | string
}

const personality = {
    name: 'goq',
    age: 30,
    hobby: ['reading book', 'programming'],
}
```

## 型注釈を使用
まずは型注釈を使用して、型チェックを行います。
型にない`job`プロパティを追加して、エラーが出るか確認します。
```ts
const personality: Personality = {
    name: 'goq',
    age: 30,
    hobby: ['reading book', 'programming'],
    job: '' // Type '{ name: string; age: number; hobby: string[]; job: string; }' is not assignable to type 'Personality'
}
```
しっかりエラーが出ています。
では、`hobby`の値を出力しようとしたらどうなるでしょうか。
```ts
// Property 'forEach' does not exist on type 'string | string[]'.
personality.hobby.forEach(x => {})
```
型注釈では型チェックはできますが、プロパティの中身まで推論することはありません。
よって、stringかstring[]か推論できずにエラーが出てしまっています。

## satisfiesを使用
型注釈のデメリットを補う方法として、`satisfies`機能があります。
設定した値が、型条件を満たすか確認し型推論されます。

型注釈と違うのは、プロパティの型推論を行なっている点にあります。
```ts
const personality = {
    name: 'goq',
    age: 30,
    hobby: ['read books', 'programming'],
} satisfies Personality

personality.hobby.map(x => {}) // (property) hobby: string[]
```

## `as const`による強固な型付け
型チェックはできましたが、このままでは値が書き換えられてしまう可能性があります。
```ts
personality.name = 'a'
console.log(personality.name) // 'a'
```

より強固にするために`as const`を併用していきます。
`as const`については詳しく説明しませんが、それぞれの値をreadonlyに変換し、書き換えできないようにします。また、各値の型を`unknown`にすることで、型の推論を受け入れるようにします。
```ts
type Personality = {
    [key in 'name' | 'age' | 'hobby']: unknown
}

const personality = {
    name: 'goq',
    age: 30,
    hobby: ['read books', 'programming'],
} as const satisfies Personality
```

## まとめ
型チェックをする際には`satisfies`を使用していきましょう。
さらに強固にするならば、`as const`を併用していけば良いでしょう。

## さいごに
**GoQSystemでは一緒に働いてくれる仲間を募集中です！**
ご興味がある方は以下リンクよりご確認ください。

https://goqsystem.recruit.site?qiita=33999fba1b14768e0502
outlookで受信したメールの件名に合わせてmattermostに転送するマクロ

chatgpt さんに作ってもらった

# 件名に応じて投稿

```vb
Sub AutoPostToMattermostChannel(Item As Outlook.MailItem)
    Dim olNs As Outlook.NameSpace
    Dim Inbox As Outlook.MAPIFolder
    Dim olMail As Outlook.MailItem
    Dim mattermostUrl As String
    Dim mattermostChannel As String
    Dim subject As String
    Dim body As String
    
    Set olNs = Application.GetNamespace("MAPI")
    Set Inbox = olNs.GetDefaultFolder(olFolderInbox)
    
    If InStr(1, Item.Subject, "キーワード") > 0 Then
        Set olMail = Item
        
        'マターモストのURLとチャンネルを設定
        mattermostUrl = "https://mattermost.example.com"
        mattermostChannel = "#default"
        
        '件名と本文を取得
        subject = olMail.Subject
        body = olMail.Body
        
        'コードブロックで囲む
        subject = "```\n" & subject & "\n```"
        body = "```\n" & body & "\n```"
        
        'マターモストに投稿
        PostToMattermostChannel mattermostUrl, mattermostChannel, subject & vbCrLf & body
    End If
End Sub

Private Sub PostToMattermostChannel(url As String, channel As String, message As String)
    Dim http As Object
    Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
    http.Open "POST", url & "/hooks/<your-mattermost-incoming-webhook-url>", False
    http.SetRequestHeader "Content-Type", "application/json"
    http.Send ("{""channel"": """ & channel & """, ""text"": """ & message & """}")
End Sub

```

# 本文5行目までにキーワード

```vb
Sub AutoPostToMattermostChannel(Item As Outlook.MailItem)
    Dim olNs As Outlook.NameSpace
    Dim Inbox As Outlook.MAPIFolder
    Dim olMail As Outlook.MailItem
    Dim mattermostUrl As String
    Dim mattermostChannel As String
    Dim subject As String
    Dim body As String
    Dim bodyLines() As String
    Dim i As Integer
    
    Set olNs = Application.GetNamespace("MAPI")
    Set Inbox = olNs.GetDefaultFolder(olFolderInbox)
    
    If InStr(1, Item.Body, "(ご担当者様)") > 0 Then
        Set olMail = Item
        
        'マターモストのURLとチャンネルを設定
        mattermostUrl = "https://mattermost.example.com"
        mattermostChannel = "#ご担当者様"
        
        '件名と本文を取得
        subject = olMail.Subject
        body = olMail.Body
        
        '本文を行ごとに分割
        bodyLines = Split(body, vbCrLf)
        
        '本文の5行目までの文字列を取得
        body = ""
        For i = 0 To UBound(bodyLines)
            If i = 4 Then
                Exit For
            End If
            body = body & bodyLines(i) & vbCrLf
        Next
        
        '件名と本文をコードブロックで囲む
        subject = "```\n" & subject & "\n```"
        body = "```\n" & body & "\n```"
        
        'マターモストに投稿
        PostToMattermostChannel mattermostUrl, mattermostChannel, subject & vbCrLf & body
    End If
End Sub

Private Sub PostToMattermostChannel(url As String, channel As String, message As String)
    Dim http As Object
    Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
    http.Open "POST", url & "/hooks/<your-mattermost-incoming-webhook-url>", False
    http.SetRequestHeader "Content-Type", "application/json"
    http.Send ("{""channel"": """ & channel & """, ""text"": """ & message & """}")
End Sub

```
Webページで使われる画像形式

ネットワーク転送量を減らすためにWebで扱うデータのサイズを小さくする

## JPRG(Joint Photographic Experts Group)

１６７７万色の色を扱うことができる
→写真の画像形式としてよく使われる

人の目が感じにくいデータを削ることでデータサイズを小さくしている
→一部のデータが失われるため元の画像には戻せない（非可逆圧縮）

## GIF(Graphics Interchange Format)

２５６色までしか扱うことができない

データを削らずにデータを整理することにより圧縮
→いつでも元の画像に戻すことができる（可逆圧縮）

特定の色を透過色として扱うことができる

複数のGIF画像をパラパラ漫画のように表示してアニメーションのようにできる

## PNG(Portable Network Graphics)

JPEG同様１６７７万色の色が扱える

GIF同様画質の劣化が起きない

同じ内容でもGIFよりデータサイズが小さくなる

透明度が扱えるためJPRGよりも表現できる幅が広がる
FirebaseのApp Distributionを利用してアプリ配布する（Fastlane設定）

## きっかけ
たままた現場でテスト配布設定を行う作業があったのでメモで残しておきます。

## 作業タスク
- `Firebase`設定
- `Fastlane`設定

## iOS設定方法(Fastlane含む)

`Fastlane`を実行し、自動アップロードまでするには`Firebase CLI`インストール、`plugin`インストール、 トークン取得（設定）が必要です。

当然のことですが、FastlaneでdeployするためにはFirebaseへログイン状態にするのが前提です。
そのため、トークンを取得作業をします。

### `firebase CLI`インストール

```
curl -sL https://firebase.tools | bash
firebase login
```

### `plugin`インストール

```
fastlane add_plugin firebase_app_distribution
```

### `Firebase`トークン取得
```
firebase login:ci
```

## lan設定

```
# 実行時間設定
ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"] = "600" 

ENV["FIREBASE_CLI_TOKEN"] = "[Firebaseから取得したトークン]" 

default_platform(:ios)

platform :ios do
  desc "Firebase lane" 

  current_date = Time.new.strftime('%Y%m%d')
  build_name = "test_app_" + current_date + ".ipa" 

  lane :adhoc do
    gym(
      project: "sample.xcodeproj",
      scheme: "sample-dev",
      configuration: "Release",
      clean: true,
      export_method: "ad-hoc",
      output_directory: "../build",
      output_name: build_name,
      export_options: {
        provisioningProfiles: {
          "jp.co.sample.app" => "sample app" 
        }
      }
    )
    # デプロイ
    firebase_app_distribution(
              app: "1:xxxxxxxxxx:ios:xxxxxxxxxxxxxxxxxx",
              testers: "test1@test.com, test2@test.com",
              release_notes: "test app.",
              firebase_cli_token: ENV["FIREBASE_CLI_TOKEN"],
              firebase_cli_path: "./node_modules/.bin/firebase" 
    )
  end
end

```

## 参考リンク
- https://firebase.google.com/docs/app-distribution/ios/distribute-console?hl=ja

- https://qiita.com/yum_fishing/items/1f0a5eda41426cc1a510
【PHP】Composerのプロキシー設定を行う

他に紹介していらっしゃる方もいますが、環境も変わっていそうなので。
備忘録も兼ねて。

# 環境
OS: CentOS Stream 9
PHP: 8.0.13
Composer: 2.4.4

プロキシー配下です。


# エラー
パッケージのダウンロードで以下のエラーを吐きました。

```
curl error 28 while downloading https://repo.packagist.org/... : Failed to connect to
repo.packagist.org port 443: Connection timed out
```

`composer diagnose`で疎通確認をしたところ、以下のような状態となりました。

```
$ composer diagnose
Checking composer.json: OK
Checking platform settings: OK
Checking git settings: OK
Checking http connectivity to packagist: FAIL
[Composer\Downloader\TransportException] curl error 28 while downloading http://repo.packagist.org/packages.json: Failed to connect to repo.packagist.org port 80: Connection timed out
Checking https connectivity to packagist: FAIL
[Composer\Downloader\TransportException] curl error 28 while downloading https://repo.packagist.org/packages.json: Failed to connect to repo.packagist.org port 443: Connection timed out
Checking github.com rate limit: FAIL
[Composer\Downloader\TransportException] curl error 28 while downloading https://api.github.com/rate_limit: Connection timed out after 10004 milliseconds  
...
```

悲しみです。

# 対処方法
プロキシー設定を変更しました。

```
$ sudo vi /etc/profile
PROXY='http[s]://<proxy_ip>'
export HTTP_PROXY=$PROXY
export HTTPS_PROXY=$PROXY
HTTP_PROXY_REQUEST_FULLURI=1
HTTPS_PROXY_REQUEST_FULLURI=0

$ source /etc/profile
```

`composer diagnose`で疎通確認です。

```
$ composer diagnose 
Checking composer.json: OK
Checking platform settings: OK
Checking git settings: OK
Checking http connectivity to packagist: OK
Checking https connectivity to packagist: OK
Checking HTTP proxy: OK
...
```

よさそうです。
Deno: ChatGPT API の使い方 (ky)

こちらのプログラムを　Deno で ky を使って書きました。
[Curl: ChatGPT API の使い方](https://qiita.com/ekzemplaro/items/6660260e287a9ee9ecf4)

## プログラム

```bash:deno_ky.sh
export OPENAI_API_KEY="sk-****************'
deno run --allow-net --allow-env deno_ky.ts
```

```ts:deno_ky.ts
// ---------------------------------------------------------------
//	deno_ky.ts
//
//					Apr/13/2023
//
// ---------------------------------------------------------------
import process from "node:process"
import ky from "https://cdn.skypack.dev/ky"

console.error ("*** 開始 ***")

const OPENAI_API_KEY = Deno.env.get("OPENAI_API_KEY")

const headers = {
	"Content-Type": "application/json",
	Authorization: `Bearer ${OPENAI_API_KEY}`,
}

const data = {
	model: "gpt-3.5-turbo",
	messages: [{ role: "user", content: "富士山の高さは" }],
}

const url = "https://api.openai.com/v1/chat/completions"

const time_out = 2000

console.error ("\ttime_out = " + String(time_out))

try {
const response = await ky.post(url, { headers, json: data, timeout: time_out },)
const result = await response.json()
console.log(result.choices[0].message)
} catch (err) {
	console.error("*** error *** aaa ***")
	console.error(err.name + ': ' + err.message)
	console.error("*** error *** ccc ***")
	process.exit(-1)
}

console.error ("*** 終了 ***")
// ---------------------------------------------------------------
```

## 実行結果

タイムアウトを起こす場合

```text
$ ./deno_ky.sh 
*** 開始 ***
	time_out = 2000
*** error *** aaa ***
TimeoutError: Request timed out
*** error *** ccc ***
```

タイムアウトを起こさない場合

```
$ ./deno_ky.sh 
*** 開始 ***
	time_out = 2000
{ role: "assistant", content: "3,776メートルです。" }
*** 終了 ***
```

次の値を大きくすれば、タイムアウトを起こさなくなります。

```ts
const time_out = 2000
```

## 参考情報

[ky](https://www.npmjs.com/package/ky/v/0.33.3)

```text
timeout
Type: number | false
Default: 10000

Timeout in milliseconds for getting a response, including any retries. Can not be greater than 2147483647. If set to false, there will be no timeout.
```
Python逆引き小辞典

# はじめに
<!-- 発端や概要を記載 -->
Pythonでこれやるにはどう書くんだっけ？ということをちまちままとめます。
そのうち大辞典になる予定です。

# 目次
<!-- タイトルとアンカー名を編集 -->
1. ファイル操作
    1-1.[ファイルの存在確認](#Chapter1)
    1-2.[ディレクトリの存在確認](#Chapter2)
1. テキスト操作
    2-1.[末尾の文字を削除](#Chapter3)
1. pythonプログラムのexe化(#Chapter4)


<!-- 各チャプター -->
<a id="#Chapter1"></a>
# ファイルの存在確認
```python:chap1.py
import os

path = 'file.txt'
is_file_exist = os.path.isfile(path)

if is_file_exist:
    # ファイルは存在する
    print(f"{path} is a file.")
else:
    # パスが存在しないかファイルではない
    print(f"{path} is not exist.")
```

<a id="#Chapter2"></a>
# ディレクトリの存在確認
```python:chap2.py
import os

path = 'dir_name'
is_dir_exist = os.path.isdir(path)

if is_dir_exist:
    # ディレクトリは存在する
    print(f"{path} is a directory.")
else:
    # パスが存在しないかディレクトリではない
    print(f"{path} is not a directory.") 
```

<a id="#Chapter3"></a>
# 末尾の文字を削除
```python:chap3.py
string = '1234567890'

str = string.rstrip('0')
print(str)
```
<a id="#Chapter4"></a>
# pythonプログラムのexe化
[pycharm]
File→Setting→Python Interpreter→＋ボタンを押す→pyinstaller→install package
pycharmの左下でterminalタブを開き
PS C:\Users\xxx\PycharmProjects\xxx > pyinstaller main.py --onefile
【CRUD】SQL基本構文　なぜか入力されない時に見るやつ

CRUD・・・CREATE READ UPDATE DELETE の略。



【CREATEの書き方】
CREATE TABLE テーブル名 (列名1 型 制約, 列名2 型 制約);



【READの書き方】
SELECT * FROM テーブル名;
 →　テーブル内情報を全件取得

SELECT 列名1, 列名2 FROM テーブル名;
 →　テーブル内情報を列名指定で全件取得

SELECT 列名 FROM テーブル名 WHERE 検索条件;
 →列名指定かつ検索条件で取得するものを選んで取得



【UPDATEの書き方】
UPDATE テーブル名 SET 列名 = 更新データ WHERE 条件;
例)　UPDATE tbl SET a1 = 'AAA', a2 = 'BBB' WHERE quality = 'Na';

解説
tblテーブルに置いてquality列が　Na　の行に関して、
a1列とa2列をそれぞれ AAA と BBB に更新しますよ～



【DELETEの書き方】
DELETE FROM テーブル名 WHERE 条件;
 →行を削除したいときに使用。ちなみに、テーブルを削除したいときはDROP。

DROP TABLE テーブル名;
 →で消えるど。
Googleデータアナリティクス：統計的検定力

# はじめに

本記事は、Googleデータアナリティクスのプロフェッショナル認定証のプログラムより、参照させて頂いています。興味を持った方は、是非受講してみてください。

https://www.coursera.org/professional-certificates/data-analytics-japanese

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/b583930d-fecb-13ad-85a2-3f44e309d8a5.png" width="800">

# 統計的検定力を活用する

***<font color="MediumVioletRed">『統計的検定力とは、有意差検定などのテストで ある効果が認められる確率のこと』</font>***

データアナリストにとって、 プロジェクトはテストや調査から始まります。 仮説検証は、調査や実験が 意味のある結果をもたらすかどうかを 確認する方法です。 

### シナリオ１

あるレストランチェーンで新しく ミルクシェイクのキャンペーンを 企画しているとします。 全国的な広告キャンペーンを行う前に あるグループの顧客に対して 広告をテストします。

テストでは、顧客がキャンペーンを 気に入るかそうでないかを確認します。 このとき、広告以外の要因で 「気に入らない」と言われる可能性を 排除したいところです。

すべての顧客を対象にするのは、 時間とコストがかかりすぎます。 そこで、広告が効果的であると 示すために、必要な顧客の数を 把握する必要があります。 50 人ではおそらく十分ではないでしょう。 50 人をランダムに選んだとしても、 ミルクシェイクがまったく好きではない 顧客ばかりかもしれません。 そうなると、ミルクシェイクを 注文する人がサンプルに含まれないので 広告の効果を測定することができません。 そのため、サンプル サイズを 大きくする必要があるのです。 そうすれば、テストに十分な あらゆるタイプの人を確保できます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/3f604af2-3051-1f85-68f8-9a40e4118c81.png" width="700">

通常、サンプル サイズが 大きければ大きいほど、 テストで統計的に有意な結果が 得られる可能性が高くなります。 これが統計的検定力です。

この場合、できるだけ多くの顧客を 対象にすることで、広告を気に入った グループとそうでないグループとの 違いが、広告を全く考慮せずに 決定した人たちとの間で 明らかになるのです。

:::note warn
統計的検定力を 正確に計算する方法はありますが ここでは割愛します。 データアナリストとして、 ご自身での計算が必要になる場合もあります。
:::

今の時点では、統計的検定力は 通常 1 からの値で示されることを 知っておいてください。 つまり、統計的検定力が 0.6 であれば それは 60% ということです。 ミルクシェイクの広告テストでは 統計的検定力が 60% だった場合、 広告の効果について 統計的に有意な結果が得られる可能性が 60% あることを意味します。つまり、ミルクシェイク広告の テスト結果が信頼でき、 本物である確率は 60% で テスト結果が間違っている確率は 40% ということになります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/d202d692-b281-0723-039d-6aa392cc106b.png" width="700">

***<font color="MediumVioletRed">『通常、結果を統計的に有意とみなすには 少なくとも 0.8 または 80% の 統計的検定力が必要です。』</font>***

テストが統計的に有意であれば テストの結果は本物であり、ランダムな 偶然による誤差ではないことを意味します。

### シナリオ２

あるレストランチェーンで、 新商品のケーキ味のミルクシェイクを 発売することになったとします。

このミルクシェイクは、他の ミルクシェイクよりも製造コストが高くなります。 会社は、この新商品の話題性により より多くの顧客と資金を獲得し このコストを相殺したいと考えています。 では、何カ所のレストランで試せば 自信をもって結果を出せるでしょうか。

 まずは、統計的有意な結果が 得られない可能性を排除する必要があります。 

- 新規顧客を獲得しうる 他のキャンペーンを実施している レストランはないでしょうか？ 
- 常に最新の商品を購入する 顧客がいるレストランは あるでしょうか？ 
- 最近工事が始まって、 顧客がお店に行くのを 妨げるような立地にあるお店は ないでしょうか？

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/bee100fb-1eb7-fb6c-a4f8-52127fb6f76e.png" width="700">


より高い統計的検定力を得るためには これらの要素をすべて考慮した上で、 研究のサンプルサイズに 何店舗含めるかを 決める必要があります。効果が他の要因ではなく ミルクシェイクの新しい味によるものだと 確信したいものです。

ここでの測定可能な効果は サンプル サイズに含まれる店舗での 売上、または顧客数の増加でしょう。
ChatGPTについて考える (4) 論理について

推論の形式には、演繹 (deduction)、帰納 (induction)、アブダクション(abduction) がある。

演繹 (deduction)：大前提「この袋に入っている豆は全て白い(A → C)」+ 小前提「これらの豆はこの袋から取り出された(A)」⇒ 結論「これらの豆は白い(C)」

ChatGPTに聞いてみました。

Q: この袋に入っている豆は全て白いです。これらの豆はこの袋から取り出されました。これから何が分かりますか。

ChatGPT：
> この袋から取り出された豆は全て白いため、この袋に入っている豆は少なくとも一つ以上の白い豆が含まれていることが分かります。ただし、他の袋から混ざり込んで白い豆がすべて取り出された場合は、この袋に入っている豆が白いとは限りません。また、袋に入っている豆が全て白いということから、この袋には黒い豆やその他の色の豆は含まれていないことも分かります。

ちょっと質問が悪かったかな。「他の袋から混ざり込んで白い豆がすべて取り出された場合は」など不適切な仮定、不要な情報が多いです。

Q: この袋に入っている豆は全て白いです。これらの豆はこの袋から取り出されました。これから*これらの*豆について何が分かりますか。

ChatGPT: この袋から取り出された豆は全て白いため、この袋に含まれる豆は全て白いことが分かります。ただし、この袋以外の場所から混ざり込んだ白い豆が含まれている可能性はあります。また、この袋に入っている豆の品種や大きさ、形状、質などについては何も分かりません。袋の中に全て白い豆があるということ以外の情報はありません。

あまり変わりません。最後の分の「これらの豆」が「袋に入っている豆」ではなく「取り出した豆」というエンティティの特定が間違っています。

Q: この袋に入っている豆は全て白いです。この袋から豆を一個取り出しました。この豆の特徴を教えてください。

ChatGPT: この袋に入っている豆は全て白いため、この袋から取り出した豆も白いと思われます。ただし、その豆の品種や大きさ、形状、質などについては何も分かりません。取り出した豆の特徴については、袋から豆を取り出した方法や条件によって異なるため、具体的な特徴についてはお答えできません。

最初の文で期待した答えが出てきました。それ以外は蛇足だが、まあ間違っているわけではありません。

帰納はどうえしょうか。

帰納 (induction)： 小前提「これらの豆はこの袋から取り出された(A)」+ 結論「これらの豆は白い(C)」⇒ 大前提「この袋に入っている豆は全て白い(A → C)」

Q: この袋から取り出された豆はすべて白い豆でした。この袋には何が入っているのでしょうか。

ChatGPT: この袋から取り出された豆がすべて白いということから、この袋には白い豆が入っていることが分かります。ただし、他の袋から混ざり込んで白い豆がすべて取り出された場合や、この袋に入っている白い豆が異なる品種や種類のものである場合もあります。袋に入っている具体的な豆の品種や大きさ、形状、質などについては何も分からないため、この袋に何が入っているのかについては、袋を開けて中身を確認するか、袋についているラベルや情報を確認する必要があります。

帰納に関しては「袋を開けて中身を確認するか、袋についているラベルや情報を確認する必要があります」というように慎重な姿勢です。大規模データで訓練したということから、ChatGPT自身は帰納で出来ているといってもよいくらいなのに。

推論としてはさらに確実性が低くなるアブダクションについてはどうでしょうか。

大前提「この袋に入っている豆は全て白い(A → C)」＋ 結論「これらの豆は白い(C)」⇒ 小前提「これらの豆はこの袋から取り出された(A)

Q: この袋に入っている豆は全て白いことが分かっています。一方ここに白い豆があります。ここにある白い豆はどこから持ってきたのでしょうか。

ChatGPT: この袋に入っている豆は全て白いということから、ここにある白い豆もこの袋から取り出されたものである可能性があります。ただし、ここにある白い豆がこの袋から取り出されたものであるかどうかは、別の袋から取り出されたものであるか、またはこの袋に混ざり込んだ別の品種の白い豆である可能性もあります。袋に入っている豆の種類や、袋に混ざり込んだ豆の有無などの詳細情報がない限り、どこから持ってきたのかを正確に特定することはできません。

「可能性」に言及している点は適切です。他の可能性を考える意味では、余計な情報も意味があると思います。

アブダクションは発想に使われる推論形式なので、うまい質問をすればアイデアを得たい場合に活用できるかもしれません。

全称記号∀や存在記号∃の扱いはどうでしょうか。

Q:「すべての子どもは入場できません」の否定は何ですか.

ChatGPT: 「すべての子どもは入場できません」の否定は「ある子どもは入場できる」という意味になります。具体的には、「少なくとも一人の子どもは入場できる」といった表現になるでしょう。

これらについてのロジックは正しく認識しています。

Q: 「すべての子どもは入場できません」という場所にシャーロット王女は入場できますか

ChatGPT:「すべての子どもは入場できません」という場所において、シャーロット王女は子どもであるため入場できない可能性があります。ただし、その場所が何であるか、その場所の規則についての詳細が不明なため、シャーロット王女が入場できるかどうかを判断するには、その場所に関する追加情報が必要です。

シャーロット王女が子どもであるということは理解できていて、それを演繹推論にはめ込んでいるように見えます。ただし「入場できません」ではなく「入場できない可能性があります」とちょっとトーンが弱いです。

全体的に抑制的な結論を出すようですね。これは責任を避けるためにそのようなトレーニングを受けてきているのかなと思います (このあたり私もだんだん人間と同じような見方になってきています)。

先ほど、照応に関してはうまく特定できていない例が見つかりました。

Q: 食塩を水に溶かしました。それを飲むとしょっぱいです。「それ」は何を指しますか？

ChatGPT: 「それ」は食塩水を指しています。

「食塩水」はコンテキスト中のどこにも出ていないのに、素晴らしい。

Q: 熱いコーヒーにたっぷり牛乳を注ぎました。それはおいしかったです。「それ」は何を指しますか。

ChatGPT: 「それ」は、熱いコーヒーにたっぷり牛乳を注いだ飲み物を指しています。

「カフェオレ」は出てこなかったか。

「[処理対象と処理内容が分離できている](https://qiita.com/ryokan/items/7f28cd6673b9ed1d721d)」ことからも、論理も(ほとんど) 正しく扱えるといっていいでしょう。しかし、論理式を適用するメカニズムがある訳でもありません。

論理式で考えている訳ではないが、正しく論理を扱えるといってよいでしょう。
DBから必要なデータを取得する

DBからデータを取得するときにエラーを生じない方法
```
    public Hospital__c getHospitalById(String id){
        list<Hospital__c> lists =[select id,Hospital_Code__c,Hospital_name__c from Hospital__c where id=:id limit 1];

        if(lists.size() >0){
            return lists[0];
        }
        return new Hospital__c();
    }
```
条件にふさわしいデータない場合は空の配列を返す
springでバッチ処理を簡単に実装してみる

今日も備忘録です。

## このドキュメントについて
下記3点について記載していきます。
・springの@Scheduledを利用したバッチ処理
・cronの利用方法
・@Scheduled利用時の注意点


## 実行環境

・Java17
・springboot 3.0.2

## 実装


```java
    final static LocalDateTime now = LocalDateTime.now();

    @Scheduled(fixedDelay = 2000)
    public void fixedDelay() {
        log.info(String.valueOf(ChronoUnit.MILLIS.between(now, LocalDateTime.now())));
    }
```

①：fixedDelay
処理の実行完了時点から一定の間隔で実行される。
上記を例にすると、処理が終わって2秒後にfixedDelayが実行される。

処理を一定の間隔にしたいなら、fixedDelayを利用するべき。
(処理の実行が被らないようにする)
<br>

```java
    final static LocalDateTime now = LocalDateTime.now();

    @Scheduled(fixedRate = 2, timeUnit = TimeUnit.SECONDS)
    public void fixedRate() {
        log.info(String.valueOf(ChronoUnit.MILLIS.between(now, LocalDateTime.now())));
    }
```
②：fixedRate
処理の実行開始時点から一定の間隔で実行される。
上記を例にすると、処理が開始された2秒後にfixedRateが実行される。

補足：
timeUnit = TimeUnit.SECONDS で単位を指定できる。
デフォルトはms。

<br>

```java
    final static LocalDateTime now = LocalDateTime.now();

    @Scheduled(initialDelay = 3000, fixedRate = 2000)
    public void initialDelay() {
        log.info(String.valueOf(ChronoUnit.MILLIS.between(now, LocalDateTime.now())));
    }
```
③：initialDelay
処理の初期遅延を指定できる。
上記を例にすると、アプリケーション起動後3秒の初期遅延があり、その後2秒間隔でinitialDelayメソッドが実行される。
<br>

```java
    @Scheduled(cron = "0 * * * * *", zone = "Asia/Tokyo" )
    public void cron() {
        log.info(String.valueOf(LocalDateTime.now()));
    }
```
④：cron
秒、分、時間、日、月、曜日などを指定し、処理を定期実行する。
上記を例にすると、毎分0秒にcron()が実行される。


zoneはどこの地域の時間を軸にするのか。

### cronの設定について

秒、分、時、日、月、曜日の順で設定ができる。
月とか曜日は、英語３文字でも設定できるらしい。
例) 月曜日 = Mon、 4月 = Apr 

|  cron |  内容  |
| ---- | ---- |
| 0 * * * * * | 毎分0秒に実行 |
| 0 30 * * * *  |  毎時30分0秒に実行  |
| 0 0 12 * * *   | 毎日12時0分0秒に実行   |
| 0 0 12 */3 * * | 毎月3の倍数の日の12時0分0秒に実行 |
| 0 0 12 1 *3 *   | 3の倍数がつく月の1日12時0分0秒に実行 |
| 0 0 12 * * 1-5 | 月曜日から金曜日の12時0分0秒に実行 |

## 実装時の注意点

以下3つの実装時の注意点を守らないと、定期実行されなかったり、アプリケーション起動時にエラーが起こるので必ず抑えておく。


①：Application起動クラスに@EnableSchedulingを付与する必要がある。

```java
@ConfigurationPropertiesScan
@SpringBootApplication
@EnableScheduling
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}
```



<br>

②：@Scheduledで定期実行させるには、クラスを@Component等でDIコンテナに登録する必要がある。
```java
@Slf4j
@Component
public class TestBatch {

    final static LocalDateTime now = LocalDateTime.now();

    @Scheduled(fixedDelay = 2000)
    public void fixedDelay() {
        log.info(String.valueOf(ChronoUnit.MILLIS.between(now, LocalDateTime.now())));
    }
```



<br>


③：@Scheduledを付与するメソッドはvoidかつ引数がないものにする必要がある



## まとめ
本来バッチは、シェルスクリプトを用意してcronを設定する必要がありますが、springの@Scheduledを利用すれば簡単にバッチ実行できるので、すごく便利だなと思いました。

実務で使う機会があれば使っていきたいなと思います。
Cannot read properties of null のエラー

[まとめページに戻る](https://qiita.com/Keiji_otsubo/items/60afc2a8d211b6f7d89f)
[まとめA～M](https://qiita.com/Keiji_otsubo/items/f3ddedee6793bf1576f8)



+ [レポート：[Cannot read properties of null (reading 'recordTypeId')] error shows when accessing a report.](https://qiita.com/Keiji_otsubo/items/9b1adb7a6cee04ee7c18)
+ [レポート：Cannot read properties of undefined (reading 'sortOrder')](https://qiita.com/Keiji_otsubo/items/f0498b7f3e644eaccdc8)
+ [メールテンプレート：Cannnot read properties of null(reading '0') のエラーの考察](https://qiita.com/Keiji_otsubo/items/b6ab977088973c643cf0)
+ [ Cannot read properties of undefined (reading 'getName'){anonymous}](https://trailhead.salesforce.com/trailblazer-community/feed/0D54S00000PkHUl)

:::note
Clearing the cache and cookies did the trick. 
:::
OneDrive同期アプリがエラー [2002] でサインインできない

OneDrive同期アプリがサインインに失敗し、エラーメッセージ

:::note alert
問題が発生しました。[2002]
:::

が表示されました。
（問題って何だよ。。。もう少し具体的に言ってくれるとか、百歩譲ってログに理由くらい残してくれ）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/1ddd0670-c660-d8a4-e78b-d93f60803a33.png)
このエラーは、**Azure AD＋Intune に参加した状態（*Azure AD joined*）** で発生することが判っています。
ローカルアカウントでWindows端末にサインインした場合は起こりませんでした。
Windows端末をクリーンインストールしても、Intune で端末にポリシーが配布されるとやはり再現します。
稀なインシデントなのか、エラー2002 に関する情報は殆ど得られません。

取り敢えず **ChatGPT** に聞いてみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/573bcfc7-3d9f-3b4b-edde-80389fb9b0d7.png)
一次切り分けとしては十分な回答です。

ChatGPT が提示してくれた手順では解決しませんでしたが、以下の手順を試したところ、現時点でエラーの再発が無いようなので共有します。解決のヒントにして下さい。

# 手順
## Microsoft管理センターに管理者ユーザーでサインイン
**Microsoft Intune admin center（旧エンドポイントマネージャー）** を開きます。

### アプリ保護ポリシーを開く
[アプリ] → [アプリ保護ポリシー] の順にクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/7e323d91-7a60-74ab-8b2d-dc5ef9225523.png)
### アプリ保護ポリシーにOneDriveを追加
ポリシーを選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/dd915282-070e-92e3-d98a-142de75ef7ab.png)
[プロパティ] → 対象アプリ [編集]　をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/cd84b7a4-6135-d83a-770d-9944e23ffe5a.png)
「保護されているアプリケーション」の列「ファイル」に "onedrive.exe" が無かった場合、[追加] をクリックし、一覧から Microsoft OneDrive を選択し、[OK] をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/f9136a9a-400c-f5c5-1df1-a8218ca275ac.png)
最後に [レビューと保存] をクリックします。反映には少々時間がかかるかもしれません。

## ローカルキャッシュの削除
次に、問題が発生しているWindows端末で、Windowsのユーザープロファイルを削除します。
ユーザープロファイルには、アカウントに紐付いた情報やファイルが収められています。削除は自己責任で。
特に、ネットバンキングなどでクライアント証明書をインストールした場合は注意が必要です。
念の為、フォルダ `%UserProfile%` ごとバックアップしておくと安心です。

### システムのプロパティを開く
管理者権限の付いたアカウントでWindowsにサインインし、「システムのプロパティ」を開きます。
「ファイル名を指定して実行」から **sysdm.cpl** と入力して開くのが早いです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/eb382b4e-9a5b-3960-38ec-38354215938f.png)
[詳細設定] → [設定] をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/b213d0ed-04fc-ba1e-01f3-5c778e405ba2.png)
### 削除
問題が発生しているアカウントを選択し、[削除] をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/7033cd6b-0d9d-5496-7f05-1b4d080cc0cc.png)

## 確認
Windows端末を再起動し、問題が発生していたアカウントでWindowsにサインインします。
ユーザープロファイルを削除したので、初回のサインインは少し時間がかかります。

OneDrive同期アプリにサインインできたら成功です。
これでも失敗する場合、Microsoft Intune admin center（旧エンドポイントマネージャー）から、**Windows Information Protection** を無効にすることを検討して下さい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/4b602ac4-77b9-c159-3c73-2f2d5d4600fa.png)

# 参考
https://learn.microsoft.com/ja-jp/windows/whats-new/deprecated-features

https://learn.microsoft.com/ja-jp/mem/intune/apps/windows-information-protection-policy-create#to-add-a-wip-policy
Googleデータアナリティクス：不十分なデータの種類と対応

# はじめに

本記事は、Googleデータアナリティクスのプロフェッショナル認定証のプログラムより、参照させて頂いています。興味を持った方は、是非受講してみてください。

https://www.coursera.org/professional-certificates/data-analytics-japanese

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/b583930d-fecb-13ad-85a2-3f44e309d8a5.png" width="800">

# 不十分なデータの種類と対応

例えば、あなたが観光業界で働いていて どの旅行プランが最も頻繁に 検索されているか調べる必要があるとします。 

### <font color="MediumBlue">１つのソースからのデータのみ限定</font>

1 つの予約サイトの データだけを使用すると、 1 つのソースからのデータに 限定されてしまいます。 他の予約サイトでは、分析の際に 考慮したい異なる傾向が 見られるかもしれません。 

:::note info
このような制限が分析に影響する場合には いったん分析を中断し、ステークホルダーと 相談して計画を練りましょう。 
:::

### <font color="MediumBlue">データが更新され続けている</font>

データセットが更新され続けていたら データはまだ受信中であり、 完全でない可能性があります。 つまり、新しい観光スポットの 関心度や入場者数を分析したい場合などには 傾向を判断するための十分なデータが 存在しない可能性があります。 

:::note info
例えば、データを収集するのに 1 ヶ月待つこともあるでしょう。 あるいは、ステークホルダーに 目的の調整を依頼することもあるでしょう。 例えば、月ごとの傾向ではなく 週ごとの傾向を分析するようにもできます。 また、過去 3 ヶ月の傾向を基に分析し、 「4 ヶ月目の来場者数はこうなるでしょう」 と予測することもできるでしょう。この数字が低すぎるか高すぎるかを知るには、 十分なデータがないかもしれません。 そのような時もステークホルダーには、 今あるデータに基づく最良の推定値です、 と伝えましょう。 
:::

### <font color="MediumBlue">古くなったデータ</font>

一方、データが古く もはや適切でない可能性もあります。 顧客満足度に関する古いデータには、 最新の回答は含まれていないでしょう。 そのため、ホテルや貸別荘のレビューに 頼ることになりますが それらのレビューも、 正確ではないかもしれません。 

:::note info
新しい データセットを探すのが最善策でしょう。
:::

### <font color="MediumBlue">地域が限定されたデータ</font>

地域が限定されたデータも、 信頼性に欠ける可能性があります。 あなたの会社がグローバル企業であれば 一国だけの旅行に限定したデータを 使用することはないでしょう。 

:::note info
すべての国を含むデータセットが必要です。 
:::

# データに問題が見つかった時の対処法

### <font color="Darkcyan">データ上の問題 1：データがない</font>

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/c10fa52d-a1f2-f8d3-a6f6-e992826c6dfa.png" width="800">

### <font color="Darkcyan">データ上の問題 2： データが少なすぎる</font>

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/b16a9d2d-8767-5f3c-e653-370769fa8d59.png" width="800">

### <font color="Darkcyan">誤ったデータ（エラーがあるデータを含む*）</font>

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/6af7479c-e78e-0cdc-fe92-6aff0914e472.png" width="800">
A server is already running. Check /myapp/tmp/pids/server.pid. の対処法

rails sでサーバーを起動させようとしたところ、以下のエラーが出ました。
```
=> Booting Puma
=> Rails 6.0.4.4 application starting in development 
=> Run `rails server --help` for more startup options
A server is already running. Check /myapp/tmp/pids/server.pid.
Exiting
```



# 原因
すでにサーバーが立ち上がっているために起きたエラーです。

通常ならサーバーを切断させたときにプロセスも消えるはずですが、何らかの理由で消えなかったということです。

# 解決方法
プロセスが不正に残ってしまっているので、残ったプロセスを削除すればOKです。

エラーコードの中に、プロセスファイルの場所が記載されています。

```
A server is already running. Check /myapp/tmp/pids/server.pid.
```
```/myapp/tmp/pids/server.pid```の部分です。
これを削除すればOK。

```myapp```の部分はご自身のアプリ名が入っています。

```
$ rm /myapp/tmp/pids/server.pid
```
そのあと、rails sでサーバーを起動させてください。



参考：

https://labo.kon-ruri.co.jp/rails-error-a-server-is-already-running/
サクラエディタのGrep機能で複数のキーワードを検索し、.gitフォルダと.classファイルを除外する方法

サクラエディタのコマンドラインからGrep検索を実行する際に、-GFILEオプションと-GFOLDERオプションで除外パターンを指定することができます。

以下に、サクラエディタのGrep機能で複数のキーワードを検索し、.gitフォルダと.classファイルを除外する方法を示します。

--:bat
@echo off
set "output_file=C:\work\test.txt"
set "grep_folder=C:\hogehoge\sakura"
set "search_folder=C:\hogehoge\src"

cd /d %grep_folder%

> %output_file% echo Results:

for %%k in (KeyWord1 KeyWord2 KeyWord3) do (
    sakura -GREPMODE -GKEY=%%k -GFILE=^|*.git^|^|*.class -GFOLDER=%search_folder% -GCODE=99 -GOPT:SPU1 >> %output_file%
)

このバッチファイルでは、以下のオプションを使用しています。

-GREPMODE: Grep検索モードを有効にします。
-GKEY=キーワード: 検索キーワードを指定します。
-GFILE=^|*.git^|^|*.class: 検索対象とするファイル名パターンを指定します。除外したいファイル名やフォルダ名の前に^|を追加して、パイプ|のエスケープを指定します。
-GFOLDER=検索フォルダ: 検索対象フォルダを指定します。
-GCODE=99: 文字コードセットの自動判別を指定します。
-GOPT:SPU1: 検索結果を標準出力に出力します。
このバッチファイルを実行すると、サクラエディタでGrep検索を行い、.gitフォルダと.classファイルを除外した結果がtest.txtに保存されます。検索キーワードを変更する場合は、forループ内のキーワードリストを編集してください。
TypeScript: BlobをDataURLに変換する6行の関数

こんにちは！今回は、TypeScriptでBlobオブジェクトをDataURLに変換するシンプルな関数を紹介します。この関数は、ファイルのアップロードや画像のプレビュー表示など、様々な場面で役立ちます。さっそくコードを見てみましょう。

```typescript
const blobToDataUrl = (blob: Blob): Promise<string> =>
  new Promise((resolve, reject) => {
    const reader = new FileReader(); // (1)
    reader.onloadend = () => resolve(reader.result as string); // (2)
    reader.onerror = reject; // (3)
    reader.readAsDataURL(blob); // (4)
  });
```

この関数は、わずか6行ですが、その中に重要な処理が詰まっています。それぞれの行について解説していきましょう。

1. `const reader = new FileReader();`  
   `FileReader`オブジェクトを生成します。これにより、Blobオブジェクトを読み込むことができます。詳細については[MDNのFileReaderドキュメント](https://developer.mozilla.org/ja/docs/Web/API/FileReader)を参照してください。
2. `reader.onloadend = () => resolve(reader.result as string);`  
   `FileReader`の`onloadend`イベントハンドラに、結果を解決するコールバック関数を設定します。`reader.result`は`string`型にキャストされ、Promiseが解決されます。
3. `reader.onerror = reject;`  
   エラーが発生した場合、`onerror`イベントハンドラに`reject`関数を設定し、Promiseがリジェクトされます。
4. `reader.readAsDataURL(blob);`  
   最後に、`FileReader`の`readAsDataURL`メソッドを使って、引数で渡されたBlobオブジェクトを読み込みます。これにより、データがDataURLとしてエンコードされ、`onloadend`イベントが発生します。

この関数を使用すれば、Blobオブジェクトを簡単にDataURLに変換することができます。この投稿が役に立てば幸いです。もし質問や懸念があれば、お気軽にコメントください。
エンジニア必見：システムの権限管理

未経験からエンジニアになりたい人は、プログラミングから始める人が多いと思いますが、
エンジニアの仕事は、プログラミングだけではありません。そして、プログラミングだけでは、エンジニアになれません。同時にプログラミングが苦手でも、システム構成や開発の流れを理解するとエンジニアに近づきます！
今回は具体的なシステムの権限管理について更に詳しく調べてみましょう！
![インスタスライド_2023031802.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23130/af4b8adf-94d0-324f-9d20-98c5a779c251.gif)

# サービス利用の権限
- OSへのログオン
- Webサーバー、サービスなどへのアクセス。
- 外出先からサーバーへのアクセス。

# OSのサービスに設定するアクセス
- OSに対して全ての権限をもつ管理者権限
- 一般ユーザー

# ファイルに設定するアクセス権
- ファイル毎のコントロール
- フォルダ毎のコントロール
- サーバー毎のコントロール
- 参照、書込み、削除、実行、フル･コントロール

# データベースに対するアクセス権
- データベース管理システムへのアクセス権
- 各テーブルに対するアクセス権
- 参照、書込み、削除、実行、フル･コントロール


# まとめ
- サービス利用の権限
- OSのサービスに設定するアクセス
- ファイルに設定するアクセス権
- データベースに対するアクセス権
等がある

# 最後に
未経験の皆さん、若手エンジニアの皆さん、勉強方法について悩みがあればなんでも気軽に質問して下さい！
これからも記事を書いていきますので、モチベーションアップのためフォロー、イイねお願いします。
WebAssembly版 OpenCVのビルドでwasmが生成されなかった話

## 問題点

下記チュートリアルに従いWASM版をビルドしたが、.wasmが生成されない。

<https://docs.opencv.org/4.7.0/d4/da1/tutorial_js_setup.html>

## 解決策

オプション`'--disable_single_file'`を追加する。

オプション`'--build_wasm'`だけでは、WASMはbase64化されて単一のjsファイルに埋め込まれて出力される。

4.7.0-devのドキュメントにはこの点についての記載があった。
<https://docs.opencv.org/4.x/d4/da1/tutorial_js_setup.html>

> The build script builds asm.js version by default. To build WebAssembly version, append --build_wasm switch. By default everything is bundled into one JavaScript file by base64 encoding the WebAssembly code. For production builds you can add --disable_single_file which will reduce total size by writing the WebAssembly code to a dedicated .wasm file which the generated JavaScript file will automatically load.

## その他

Dockerでビルドする場合、2023/4/12現在、タグ指定なしだとビルドに失敗するため、_Warning_ に書かれている通り、emscripten/emsdk:2.0.10を使用する。
```:
docker run --rm -v $(pwd):/src -u $(id -u):$(id -g) emscripten/emsdk:2.0.10 emcmake python3 ./platforms/js/build_js.py build_wasm --build_wasm --disable_single_file
```

## 参考

参考にさせていただいた記事

- <https://qiita.com/yoshimi0227/items/932102201299c418a2d3>
横幅の長さwidthの指定について(box-sizing)

要素{
width:900px
paddig:40px
}
を指定した場合

![スクリーンショット 2023-04-13 4.58.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329618/d36a17a3-73ca-9657-2374-93a08d2bff21.png)
横の長さがpadding込みで980pxになってしまう。
この長さをpadding込みでwidthの指定の長さにするのが"box-sizing: border-box"である。

box-sizing: border-boxを追加すると・・・・・
要素{
width:900px
paddig:40px
box-sizing: border-box
}
![スクリーンショット 2023-04-13 5.03.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329618/8c4ce9a1-082e-f955-2c4e-139a3f7a8bb1.png)
横幅がpadding込みでwidthの長さが900pxになる!
スクール通学中、もしくは卒業したばかりの方への共有

唐突なんですが、スクール通学中、もしくは卒業したばかりの方へ共有しておきたいことがありましたのでこちらに書きます。
前提としてスクールに対するクレームではないですので、悪しからず。

私自身、つい先日卒業したばかりなのですが、、、

### スクールで使っている言語などのバージョンがかなり古いものを使っている可能性があるので注意してください。

と言いますのも、私が通っていたスクールはRubyを扱っていたのですが、それぞれのバージョンが

Ruby 2.6.5
Rails 6.0.6.1
MYSQL 5.7

といった形でかなり古いです。
まずもって、これはスクールの教材の仕組み上、仕方のないことだとは思います。

卒業したばかりの方や通学中の方でネット検索する際などに混乱する可能性があるので留意いただければと思いました。

実際私も、privateメソッドについて非常に混乱しました。
2.6.5で自分で検証した際には、privateメソッドをselfで呼び出すとエラーになっていたのが、ruby3.2.2ではselfをつけてもエラーにならないように変わっていたので非常に混乱した次第です。

参考記事

https://blog.tmtms.net/entry/201912/ruby27-self-private
Google Colaboratory TA-Lib のエラー

## Google Colaboratory TA-Lib のエラー
Google Colaboratory の Python のバージョンが 3.9 にアップグレードされたことに伴うエラー

```python
import talib as ta
```

実行時

```
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
__init__.pxd in numpy.import_array()

RuntimeError: module compiled against API version 0x10 but this version of numpy is 0xf . Check the section C-API incompatibility at the Troubleshooting ImportError section at https://numpy.org/devdocs/user/troubleshooting-importerror.html#c-api-incompatibility for indications on how to solve this problem .

During handling of the above exception, another exception occurred:

/usr/local/lib/python3.9/dist-packages/talib/__init__.py in <module>
     91 
     92 
---> 93 from ._ta_lib import (
     94     _ta_initialize, _ta_shutdown, MA_Type, __ta_version__,
     95     _ta_set_unstable_period as set_unstable_period,

talib/_func.pxi in init talib._ta_lib()

__init__.pxd in numpy.import_array()

ImportError: numpy.core.multiarray failed to import

```

なぜが import でエラーが出ます。
numpyをアップデートしても同じ。

## pandas_ta の利用

こちらのライブラリを代わりに利用します。

https://github.com/twopirllc/pandas-ta

```python
!pip install pandas_ta
```

利用例はこんな感じ

TA-Lib のメソッドで大字だったのものは、pandas_taでは小文字になるようです。

```python
import mplfinance as mpf
import datetime as dt
import pandas_ta as ta

df = get_stock_data(3099) # 三越伊勢丹HD
df["ma5"]   = ta.sma(df["Close"], 5) 
df["ma25"]  = ta.sma(df["Close"], 25) 
df["ma75"]  = ta.sma(df["Close"], 75) 

cdf = df[dt.datetime(2021,4,30):dt.datetime(2022,2,20)]
apd = { "MA5": mpf.make_addplot(cdf["ma5"], color="blue"),
         "MA25": mpf.make_addplot(cdf["ma25"], color="green"),
         "MA75": mpf.make_addplot(cdf["ma75"], color="red") }

fig, axes = mpf.plot(cdf, type="candle", figratio=(2,1), addplot=list(apd.values()), returnfig=True)
axes[0].legend([None]*(len(apd)+2))
handles = axes[0].get_legend().legendHandles
axes[0].legend(handles=handles[2:], labels=list(apd.keys()))
fig.show()
```

![スクリーンショット 2023-04-13 3.52.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266239/0cedb061-60f2-ca11-67e3-29ccef92bb55.png)
Sentencepiece の分割を MeCab っぽくする

Sentencepieceは公開から約6年経ち、月間のpipダウンロード数が1000万を超え、開発者として嬉しい限りです。ただ、Sentencepieceを使用する際にMeCabなどの形態素解析器を前処理に使うケースが散見されます。単語分割をしたいというニーズは理解できますが、作者としてはあまり満足していません。多言語処理が一般的になり、しかもSentencepieceは言語非依存のシステムであるにもかかわらず、なぜベタな日本語処理に依存するのでしょうか。このような使い方は、精度が向上するかもしれませんが、以下のようなデメリットもあります。

- 形態素解析が入力した空白と、元からある空白を区別できないため、分割されたデータから元の文を復元することができません。（可逆処理ではない）。
- 形態素解析システムに依存するため、メンテナンス性や可搬性が低下します。正しいバージョンの辞書を維持し、管理することは非常に面倒です（世に出回っているBERTモデルのほとんどがどの辞書のバージョンを使用しているかを明記していません。）。
- 多言語処理が困難になります。

ここでは、Sentencepieceの語彙選択にのみ MeCabを使用する方法を紹介します。これにより、以下の利点が生まれます。

- MeCabの分割箇所がSentencepieceで再現されやすくなります。(サブワード分割であるため、形態素を再分割することはありますが、形態素境界を結合する状況が減ります)
- 分割時にMeCabを使用する必要がなく、Sentencepieceのモデルファイルだけで動作可能になるため、可搬性が高まります。

まず、MeCabの分割箇所を学習データに出現しないユニークなデリミタ文字列でエンコードします。例えば、デリミタとして``||||``を使用してみます。[MeCabの出力フォーマット](https://taku910.github.io/mecab/format.html)機能を使用して、形態素（``%M``）を出力した後に``||||``を出力します。ここでは例として wikipedia のデータを使いました。

````
 mecab -F"%M||||" -E"\n" -b 100000 < wiki.txt  > wiki.txt.tok
````

wiki.txt.tok 中の ``||||`` を空文字列に置換すると ``wiki.txt`` に戻っていれば学習データとして適切です。

- 元の学習データ
> 形態素の一般的な性質や、形態素間の結びつきなどを明らかにする言語学の領域は、形態論と呼ばれる。

- 分割制約付き学習データ
> 形態素||||の||||一般||||的||||な||||性質||||や||||、||||形態素||||間||||の||||結び||||つき||||など||||を||||明らか||||に||||する||||言語||||学||||の||||領域||||は||||、||||形態||||論||||と||||呼ば||||れる||||。||||

次にサブワードモデルを学習します。
 ``spm_train`` の ``--pretokenization_delimiter`` フラグでデリミタを指定します。(この機能は v0.1.98以降で有効です。)

````
spm_train --input=wiki.txt.tok --vocab_size=32000 --model_prefix=model --pretokenization_delimiter="||||"
````

学習ができたら実際に分割してみましょう。
> ▁ 形態素 の 一般 的 な 性質 や 、 形態素 間 の 結びつき など を 明らか に する 言語 学 の 領域 は 、 形態 論 と 呼ば れる 


MeCab の分割が再現できています。

ちなみに、制約なしで学習した場合は以下のようになります。より少ないトークンでエンコードできていてデータ圧縮的には良いのですが、形態素境界とは異なる分割が多くなります。
> ▁ 形態 素 の 一般的な 性質 や 、 形態 素 間の 結びつき など を明らかにする 言語学 の領域 は 、 形態 論 と呼ばれる 

他の例ではどうでしょう。

- 制約あり
> ▁ 1843 年 の 暮れ 、 サンクトペテルブルク を 訪問 した ナッサウ 公 アドルフ と 初めて 出会っ た 。
> ▁ 絵画 、 グラフィック 、 ガラス 作品 : 。
> ▁ スポーツ 分野 の 写真 を 得意 に し 、 海外 取材 など 精力 的 に こなす 。
> ▁ 兄 に フィン ゴン 、 妹 に アレ ゼル 。
> ▁ 代表 的 な 教材 研究 の 手法 を 以下 に 示す 。
> ▁その 点 で 日本 の 書籍 再 販 制度 と は 大きく 異なる 。
> ▁ 元 200 m 日本 チャンピオン の 土 江 良 吉 は 実父 。
> ▁ 総 面積 の 6 . 01 % が 水域 とな って いる 。
> ▁その後 、 楊 氏 は 北 魏 初期 に 武 川 鎮 へ と 移住 し 、 楊 堅 の 父 の 楊 忠 に 至る という 。
> ▁ 『 黒部 の 太陽 』 は 1968 年 の 最大 の ヒット 作品 と なる 。

- 制約なし
> ▁ 18 4 3 年 の 暮れ 、 サンクトペテルブルク を訪問した ナッ サウ 公 アドルフ と 初めて 出会った 。
> ▁ 絵画 、 グラフィック 、 ガラス 作品 : 。
> ▁ スポーツ 分野 の写真 を 得意 に し 、 海外 取材 など 精力的に こなす 。
> ▁ 兄 に フィン ゴン 、 妹 に アレ ゼル 。
> ▁ 代表的な 教材 研究 の 手法 を 以下に示す 。
> ▁ その 点 で 日本の 書籍 再 販 制度 と は 大きく異なる 。
> ▁ 元 200 m 日本 チャンピオン の 土 江 良 吉 は 実父 。
> ▁ 総面積 の 6.0 1% が 水域 となっている 。
> ▁その後 、 楊 氏 は 北魏 初期に 武 川 鎮 へと 移住 し 、 楊 堅 の 父の 楊 忠 に至る という 。
> ▁ 『 黒部 の 太陽 』 は 1968 年 の 最大の ヒット 作品 となる 。

それっぽく動作していますね。


注意点としては、MeCabの分割が完全に再現されるわけではありません。サブワードのアルゴリズム (Unigram/BPE) でMeCabの分割を無理やり再現しようとしているだけなので、モデルの表現能力的に不可能な分割がどうしても存在することに留意してください。
 
> ▁ す も も も も も も も も のうち

さらに、形態素解析分割が文脈依存して一貫性がない場合も、高頻度の分割が好まれます。先程の例の```結びつき``` がそれに該当します。

事前分割制約の機能は、sentencepieceの初期バージョンから導入されていました。しかし、事前分割器のインターフェースのみが提供されていただけで、実際に動かすには分割器をC++で実装し、実体をインジェクトする必要がありました。v0.1.98で、デリミタを指定する機能が導入され、より簡単に使用できるようになりました。 ぜひ使ってみてください。
【出力引数】引数を出力値として扱ってはいけない

Javaのサンプルコードから、引数を出力値として扱ってはいけないことを理解する

## 出力引数とは

以下の`add()`メソッドの引数`sum`は、メソッド内部で値が変更されている
このように出力として用いる引数を**出力引数**と呼ぶ

```java
public class Main {
    public static void main(String[] args) {
        int sum = 0;
        add(3, 5, sum);
        System.out.println(sum); // 出力結果: 8 ←だと思うじゃん？
    }

    public static void add(int a, int b, int sum) {
        sum = a + b;
    }
}

```

## 出力結果は8じゃない

Javaでは**値渡し**が採用されており、引数に渡した変数は`sum`のコピーである
そのため、`sum`に`a`と`b`の和を代入しても、`main()`メソッド内部の`sum`は変更されない

つまり、`add()`メソッドの中で`sum`変数に何を代入しても、`main()`メソッド内部の`sum`には影響せず、<font color="Red">出力結果は0</font>となる

:::note warn
引数は入力値として受け渡すべきである
引数を出力値として扱うと、想定していない挙動が起こる可能性が高まる
:::

『良いコード/悪いコードで学ぶ設計入門』では、以下のように言及されている

> 引数は入力値として受け渡すのが普通です。このように出力値として扱ってしまうと、引数が入力なのか出力なのか、メソッド内部のロジックを読んで確認しなければなりません。メソッドの中身をいちいち気にしなければならない構造は、ロジックを読み解く時間をいたずらに増大させ、可読性の低下を招きます。[^1]

## 参考文献
[良いコード/悪いコードで学ぶ設計入門](
https://www.amazon.co.jp/%E8%89%AF%E3%81%84%E3%82%B3%E3%83%BC%E3%83%89-%E6%82%AA%E3%81%84%E3%82%B3%E3%83%BC%E3%83%89%E3%81%A7%E5%AD%A6%E3%81%B6%E8%A8%AD%E8%A8%88%E5%85%A5%E9%96%80-%E2%80%95%E4%BF%9D%E5%AE%88%E3%81%97%E3%82%84%E3%81%99%E3%81%84-%E6%88%90%E9%95%B7%E3%81%97%E7%B6%9A%E3%81%91%E3%82%8B%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E6%9B%B8%E3%81%8D%E6%96%B9-%E4%BB%99%E5%A1%B2/dp/4297127830/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&keywords=%E8%89%AF%E3%81%84%E3%82%B3%E3%83%BC%E3%83%89%E6%82%AA%E3%81%84%E3%82%B3%E3%83%BC%E3%83%89%E3%81%A7%E5%AD%A6%E3%81%B6%E8%A8%AD%E8%A8%88%E5%85%A5%E9%96%80&qid=1681323513&sr=8-1)

[^1]: 良いコード/悪いコードで学ぶ設計入門 p.70
PHPでディレクトリ内の画像ファイルを被らないようにランダムでHTML出力


ディレクトリ内の画像をランダムに表示したい、さらに表示される画像が被らないようにしたい
という内容のコーディングをしたので備忘録

## やりたいこと

```html:html
<img src="img/random/◯◯◯.png">
<img src="img/random/□□□.png">
<img src="img/random/△△△.png">
```
こんな感じでimgタグで画像を複数表示、更にディレクトリ内にある画像をランダムで表示、更に同じ画像が表示されないようにする
ということをしたかったのでPHPを書きました。

## 完成コード
完成コードはこんな感じ
```php:php
$dir = 'img/random/'; // 画像ファイルが保存されているディレクトリ
$files = array_filter(scandir($dir), function ($file) {
    return !is_dir($file) && preg_match('/\.(jpg|jpeg|png|gif)$/', $file);
}); // 画像ファイルのみを取得

$randomIndexes = array_rand($files, 3); // 3つのランダムな配列インデックス
shuffle($randomIndexes); // 配列をシャッフル

foreach ($randomIndexes as $randomIndex) {
    $randomImage = $dir . $files[$randomIndex]; // ランダムな画像ファイル名
    echo '<img src="' . $randomImage . '">';
}
```

## ディレクトリーの構成
構成はこんな感じ。
jpgやpngも混じっています。
```
img/
　├ random/
  　├ ◯◯◯.png
  　├ □□□.png
  　├ △△△.png
  　├ ×××.jpg
  　├ ...
```

## 解説


### 画像ファイルの取得

```php:php
$dir = 'img/random/'; // 画像ファイルが保存されているディレクトリ
$files = array_filter(scandir($dir), function ($file) {
    return !is_dir($file) && preg_match('/\.(jpg|jpeg|png|gif)$/', $file);
}); // 画像ファイルのみを取得

```
`scandir()`関数指定したディレクトリからすべてのファイルとディレクトリの名前を取得し、 `array_filter()`関数を使用して、ファイルのみを取得しています。

`array_filter()`関数は、指定した配列から条件を満たす要素だけを取り出して新しい配列を作成します。
第1引数に対象の配列を指定し、第2引数に条件判定のためのコールバック関数を指定します。コールバック関数は、配列の各要素に対して実行され、`true`を返した要素だけが新しい配列に含まれます。

この場合、 `$file` には配列の各要素（ファイル名）が順番に渡されます。コールバック関数は、 `is_dir()`関数を使用してディレクトリかどうかを判定し、ファイルの場合は拡張子が jpg、jpeg、png、gif のいずれかであるかを `preg_match()`関数で判定しています。条件を満たすファイルのみが新しい配列に含まれ、それ以外の要素は除外されます。

### 表示させたい画像の数を指定
```php:php
$randomIndexes = array_rand($files, 3);
```

`$files` 配列からランダムに3つの要素を選択し、その要素のインデックスを含む `$randomIndexes` 配列を作成

ランダムな3つの要素を取得することで、パスが重複することなく3つの画像を表示できるようになります。

### ランダムに並び替え

```php:php
shuffle($randomIndexes); // 配列をシャッフル
```

`shuffle()` 関数は、引数として与えた配列の要素をランダムに並び替えます。
`$randomIndexes` 配列の要素をランダムに並び替えることで、3つの要素を取得する順序をランダムにすることができます。

`$randomIndexes` 配列内の要素がランダムに並び替えられるため、 `$randomImages` 配列内の画像ファイルの**表示順序がランダム**になります。


### imgタグとして表示


```php:php
foreach ($randomIndexes as $randomIndex) {
    $randomImage = $dir . $files[$randomIndex]; // ランダムな画像ファイル名
    echo '<img src="' . $randomImage . '">';
}
```
`foreach`を使って`$randomIndexes`配列内の要素の数と同じ回数だけ繰り返されます。
ランダムに選択された画像ファイルのパスを作成し、HTMLの<img>タグを指定の数だけ生成させています

## まとめ

今後、写真が多く掲載されるサイトを作る際に使えるな思いました。
良い写真がたくさんある場合、選んだり先方に選んでもらうより、ランダムに表示させたほうがよさそう
とはいえ同じ写真が表示されるのもイケてないので、被らない表示のさせ方は色々と使えそう気がしてます
[Flutter] ？、！の意味について

## はじめに
Flutterで出てくる？や！の意味がわからなかったのでchatGPTに聞いてみました。
以下のように、ログイン認証の定数定義の時によく出てきます。

```dart:sample.dart
  final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
  final GoogleSignInAuthentication? googleAuth =
      await googleUser?.authentication;
  final credential = GoogleAuthProvider.credential(
    accessToken: googleAuth?.accessToken,
    idToken: googleAuth?.idToken,
```

## 回答
Flutter で出てくる ? は、Dart 言語の中で使われる nullable な変数やオブジェクトを表します。 nullable とは、null が許容されるということです。例えば、String? nameという変数は、null または文字列を保持することができます。また、nullable なオブジェクトにアクセスする際には、プロパティやメソッドの前に ? を付けることで null check を行うことができます。たとえば、user?.nameという式は、user が null でなければ name プロパティにアクセスしますが、null であれば null を返します。

Flutter で出てくる ! は、Dart 言語の中で使用される null safety の非 nullable な変数やオブジェクトを表す述語演算子です。 null safety とは、Dart 2.12 以降のバージョンで導入された機能のことで、null に関するバグを防ぐための機能です。 nullable な変数やオブジェクトには ? を使用し、非 nullable な変数やオブジェクトには ! を使用します。! は、null safety で明示的に null でないことを示すために使用されます。例えば、String name!は名前が null でないことを示します。ただし、非 nullable な変数やオブジェクトについても、null になってしまう可能性がある場合は、null check を行う必要があります。