OmniAICreator/Qiita-1.07M huggingface dataset:
GNU C ライブラリのジョーク（？）関数 "memfrob"

GNU C ライブラリに [memfrob](https://www.gnu.org/software/libc/manual/html_node/Obfuscating-Data.html) という変わった関数があったのでご紹介します。

`memfrob(void *mem, size_t length)` は `mem` からはじまる `length` バイトの領域に対し、1 バイトごとに [42](https://www.google.com/search?q=人生、宇宙、全ての答え) との排他的論理和を計算して上書きします。

つまりこういうことです。

```c
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>

int main(void)
{
    char s[3] = { 9, 12, 5 };

    /* s を表示 */
    for (int i = 0; i < 3; ++i) printf("%d ", s[i]); printf("\n");

    /* 書き換えて表示 */
    memfrob(s, 3);
    for (int i = 0; i < 3; ++i) printf("%d ", s[i]); printf("\n");

    /* もう一回やる */
    memfrob(s, 3);
    for (int i = 0; i < 3; ++i) printf("%d ", s[i]); printf("\n");
}

```

```
9 12 5
35 38 47
9 12 5
```

排他的論理和を取ってるだけなので、二回やると元に戻るのがわかります。

:::note info
[Wiktionary によると](https://en.wiktionary.org/wiki/frob)、"frob" という動詞（あまり一般的ではない）は「ぐちゃぐちゃにする」、「いじくりまわす」という意味で使われるようです。

またシリコンバレーでは「自分にはわかるけど人に説明するのは難しい（あるいはめんどくさい）ことをやる」という意味のスラングとしても用いられるようです。

（Wiktionary から引用）Why don't you go get lunch? I need to **frob** with this thing for about 30 minutes and then we'll be good to go.
（拙訳）昼飯食べてていいぞ。こっちはあと三十分くらいこれを**アレしなきゃ**いけないから。
:::

この関数で文字列のちょっとした暗号化もできます。

```c
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>

int main(void)
{
    /* ASCII 印字可能文字に変換されるのは大文字・小文字アルファベットと @ [ \ ] ^ _ ` { | } ~ のみ */
    /* 空白文字は改行に変換されてしまうのでちょっと扱いにくい */
    char s[] = "HelloWorld";
    puts(s);
    memfrob(s, strlen(s));
    puts(s);
    memfrob(s, strlen(s));
    puts(s);
}
```

```
HelloWorld
bOFFE}EXFN
HelloWorld
```

**言うまでもないことですが**、実用性は一切ない関数です。なんでこんなもん作ったんですかね・・・
Discord.jsで英語学習をサポートするBotを作る

## 意義
英単語を覚えやすくできたらと思い作ってみた

## コーディング開始
環境:
Discord.js: v14.7.1
discordjs/voice: v0.14.0
npm: 9.5.0
Node.js: v19.7.0
OS: Linux Debian

GitHub: https://github.com/Fyphen1223/Simple-LearningEnglishDiscordBot


## 解説
大体は中身を見ればわかりますが、一応解説します。

まずスラッシュコマンドの
/learn startを受け取ると、そのチャンネルのIDを配列に保存します。
そしてそのチャンネルでメッセージが送信されると、node-gttsを利用して音声の生成が行われ、送信した人が参加しているVCでその音声が再生されます。
/learn stopでTTSの解除が可能です。

## インストール
```bash
git clone https://github.com/Fyphen1223/Simple-LearningEnglishDiscordBot
```
でクローンできます。パッケージはインストールしてください。
```bash
npm init -y
npm install discord.js @discordjs/voice node-gtts
```

slashcommand.jsのyour-token-goes-hereとyour-application-id-goes-here、index.jsのyour-token-goes-hereをそれぞれBotのトークンとIDに書き換えてください。


起動は
```bash
node index.js
```
ただしスラッシュコマンドは未登録なので
```bash
node slashcommand.js
```
で登録してください

終了はCtrl + Cです。

以上
pythonでコーディング問題を解く4

https://qiita.com/tosei/items/4bfe7345a7a78abc572c
続き

### 問題
こちらの問題を解いてみた
C - 幅優先探索
https://atcoder.jp/contests/abc007/tasks/abc007_3

```
from collections import deque
import numpy

Row, Column = map(int, input().split())
startY, startX = map(int, input().split())
goalY, goalX = map(int, input().split())

# 位置と配列上の添字はずれているのでここで調整する
startY, startX = startY - 1, startX - 1
goalY, goalX = goalY - 1, goalX - 1

maps = []
distances = []

# マップデータと距離データを初期化する
for i in range(Row) :
    # 一文字ずつ配列にする
    maps.append(list(input()))
    # カラム数分-1で埋める
    distances.append(numpy.full(Column, -1))

def bfs(startY, startX) :
    # 初期設定
    queue = deque([])
    # 起点をキューにpushする
    queue.append((startY, startX))
    distances[startY][startX] = 0
    
    nextPos = [(1,0),(0,-1),(-1,0),(0,1)]
    
    while len(queue) > 0:
        # キューの一番前を取得する
        currentY, currentX = queue.popleft()

        # 隣接する点を調べ、未訪問の通路の距離を更新し、キューに追加する        
        for y, x in nextPos:
            nextY, nextX = currentY + y, currentX + x

            if distances[nextY][nextX] == -1 and maps[nextY][nextX] == '.' :
                distances[nextY][nextX] = distances[currentY][currentX] + 1
                queue.append((nextY, nextX))
    
    # reutrn maps,distances
    return distances[goalY][goalX]

print(bfs(startY, startX))
```
おすすめのChrome拡張機能

Google Chromeには、ウェブ開発やウェブサイトの閲覧などに便利な様々な拡張機能があります。私が使用している拡張機能を紹介します。

## ColorZilla
ColorZillaは、Webサイトのクリックした部分の色からカラーコードを抽出することができる拡張機能です

https://chrome.google.com/webstore/detail/colorzilla/bhlhnicpbhignbdhedgjhgdocnmhomnp?hl=ja

## PerfectPixel
PerfectPixelは、コーディングを行ったサイトとデザインカンプのデザインが一致しているか確認することができる拡張機能です。

https://chrome.google.com/webstore/detail/perfectpixel-by-welldonec/dkaagdgjmgdmbnecmcefdhjekcoceebi?hl=ja

## Awesome Screenshot
Awesome Screenshotは、Webサイトのスクリーンショットを簡単に撮ることができる拡張機能です。

https://chrome.google.com/webstore/detail/awesome-screenshot-and-sc/nlipoenfbbikpbjkfpfillcgkoblgpmj?hl=ja

## Wappalyzer
Wappalyzerは、Webサイトに使用されている技術（使用言語、CMS、ウェブフレームワーク、アプリケーションサーバーなど）を簡単に確認することができる拡張機能です。
https://chrome.google.com/webstore/detail/wappalyzer-technology-pro/gppongmhjkpfnbhagpmjfkannfbllamg?hl=ja

## Google 翻訳
Google 翻訳は、外国語のテキストを翻訳をしてくれる拡張機能です。

https://chrome.google.com/webstore/detail/google-translate/aapbdbdomjkkjkaonfhkkikfgjllcleb?hl=ja

## テキストエンコーディング
テキストエンコーディングは、文字化けしているページを手動で文字コードを変更することで文字化けを解決してくれる拡張機能です。

https://chrome.google.com/webstore/detail/set-character-encoding/bpojelgakakmcfmjfilgdlmhefphglae?hl=ja

## Window Resizer
Window Resizerは、Chromeの表示画面サイズを変更してくれる拡張機能です。

https://chrome.google.com/webstore/detail/window-resizer/kkelicaakdanhinjdeammmilcgefonfh

## Check My Links
Check My Linksは、Webサイトにあるリンク切れを確認してくれる拡張機能です。

https://chrome.google.com/webstore/detail/check-my-links/ojkcdipcgfaekbeaelaapakgnjflfglf?hl=ja&gl=UA
pythonでコーディング問題を解く3

https://qiita.com/tosei/items/244a3f90fb41e9e77a94

### 問題

SNSのユーザー0-6と、他のユーザーとの距離を算出する関数を作成してください。

距離の定義について
A,Bが友達、B,Cが友達の場合
AとB、BとCの距離は1
AとCの距離は2
と定義する。

```
# 辞書配列で各ユーザーの友人関係を定義する
friends = {}
friends[0] = [1, 2]
friends[1] = [0]
friends[2] = [0, 3]
friends[3] = [2, 4]
friends[4] = [3, 6]
friends[5] = [6]
friends[6] = [4, 5]


def checkDistance(origin) :
    # 作業用キュー
    queue = []
    # すでに訪問したユーザーの距離
    distances = {}

    # 起点を格納する
    distances[origin] = 0
    queue.append(origin)

    while len(queue) != 0 :
        user = queue.pop()

        # friends配列にない場合スキップ              
        if not friends.get(user):
            continue

        # 距離が格納されていない、つまり未訪問の場合のみ距離を更新し、隣接する友人をqueueに追加する
        for friend in friends[user]:
            if distances.get(friend) is None :
                distances[friend] = distances.get(user) + 1
                queue.append(friend)

    return distances

print(checkDistance(0))
print(checkDistance(1))
print(checkDistance(2))
print(checkDistance(3))
print(checkDistance(4))
print(checkDistance(5))
print(checkDistance(6))
```
pythonでコーディング問題を解く2

https://qiita.com/tosei/items/cadad781501150e3bddf
続きです

### 問題
( { [ ) } ] ] もしくはそれ以外の文字からなる文字列に対し、カッコが正しく対応しているか判定する関数を作成してください。
カッコが正しく対応しているとは、文字列中で開いたカッコがすべて閉じていることをいいます。

例
[{()}]　→　OK
[{()}　→　閉じていないのでNG
[({)}]　→　閉じていないのでNG
[a{(1)2}] → かっこ同士がとしているのでOK


```
def getLeft(right) :
    if right == "}" :
        return "{"
    elif right == "]" :
        return "["
    elif right == ")" :
        return "("
    else:
        return null

def checkParentheses(str) :
    str = list(str)
    stack = []
    
    while len(str) > 0 :
    
        # 先頭の文字を取得して削除(jsのshiftに当たる処理)
        text = str.pop(0)

        # 左かっこならスタックに追加
        if text == "{" or text == "[" or text == "(" :
            stack.append(text)
    
        # 右かっこならスタックから取り出して閉じている関係になるかをチェックする
        elif text == "}" or text == "]" or text == ")" :
            left = stack.pop()
            right = text

            if left != getLeft(right) :
                return False
    
    # スタックが空ならばtrue
    return len(stack) == 0

print(checkParentheses("[{()}]"))
print(checkParentheses("[{()}"))        
print(checkParentheses("[({)}]"))
print(checkParentheses("[a{(1)2}]"))
```
Tanenbaum-Torvalds Debate 

LINUX is obsolete Andy Tanenbaum 1992/01/29 21:12:50　comp.os.minix
 
https://groups.google.com/g/comp.os.minix/c/wlhw16QWltI

Open Sources: Voices from the Open Source Revolution Appendix A The Tanenbaum-Torvalds Debate

https://www.oreilly.com/openbook/opensources/book/appa.html

Some Notes on the "Who Wrote Linux" Kerfuffle, Release 1.5

https://www.cs.vu.nl/~ast/brown/

Can We Make Operating Systems Reliable and Secure
Andrew S. Tanenbaum, Jorrit N. Herder, Herbert Bos • Computer • Published 2006

https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.112.3028


Tanenbaum-Torvalds Debate Part II

https://www.cs.vu.nl/~ast/reliable-os/

Hybrid (micro)kernels

https://www.realworldtech.com/forum/?threadid=65936&curpostid=65915

Debunking Linus's Latest

https://www.cosy.sbg.ac.at/~clausen/PVSE2006/linus-rebuttal.html

An Open Letter to Intel Andrew S. Tanenbaum

https://www.cs.vu.nl/~ast/intel/

Ken Brown's Motivation, Release 1.2　Andy Tanenbaum, 21 May 2004

https://www.cs.vu.nl/~ast/brown/followup/
[Swift] ローカルな Swift Package でリソースを読み込む


# 初めに

全然情報がなくて詰まったので、tips くらいの感覚で残しておきます。

# 前提

`Swift Packgage Manager`（以後 SPM） はリモート（GitHubなど）から、ライブラリを読み込む際に使われることが多いでしょう。

今回は、ローカルにある Swift Packgage（例えば、自分で作成したオリジナルのパッケージ）を読み込む場合を想定しています。
 
- Example

https://daisuke-t-jp.hatenablog.com/entry/2021/04/27/using-local-swift-package


# 問題

## 環境

発生した環境は Xcode 13.4 からでした。
（それより前は不明）

## 内容

ローカル SPM で開発を進めると、テストもそちらに移行したくなりますが、ここでリソースを読み込む際に問題がありました。

具体的には、以下の記事に従って行なったのですがうまくいかず...

https://zenn.dev/usk2000/articles/62dab200d0de94

Json が読み込まれず、テストで使用できない問題が発生しました。



# 解決策

パッケージのコードを修正します。

元のコード例は記事より引用するとこちらです。

```.swift
import PackageDescription

  targets: [
    .target(
      name: "Example",
      dependencies: [],
      resources: [
        .process("Resources"),
      ]),
    .testTarget(
      name: "ExampleTests",
      dependencies: [Example],
      resources: [
        .copy("Resources"),
      ]),
```

これを以下のようにします。

```diff_swift:Package.swift
import PackageDescription

  targets: [
    .target(
      name: "Example",
      dependencies: [],
      resources: [
        /// これはプロダクションで使用しないならなくても良い ///
        .process("Resources"),
      ]),
    .testTarget(
      name: "ExampleTests",
      dependencies: [Example],
      resources: [
        /// ディレクトリ指定だとファイルが読み込まれないので、ファイルごとに指定する ///
-        .copy("Resources"),
+        .copy("Resources/Json/xxx.json"),
+        .copy("Resources/Json/yyy.json"),
      ]),
```

**１つずつファイルを指定する**ことで読み込まれるようになりました。


上記の記事では、古い Xcode ではディレクトリが 、`Resources` だと読み込まれないとありました。新しい Xcode では特に問題はなかったです。

ちなみに余談ですが、 `Swift Playgrounds` でアプリ開発する場合は、`Resources` が内部の予約後として使用されているので、扱いには注意が必要です。

## 読み込みの注意

`Bundle` を読み込む際にも注意が必要です。

SPM からの場合は `module` からにしないと読み込まれません。 `SwiftGen` のコードを参考にして以下のようにすると良いでしょう。

```.swift
#if SWIFT_PACKAGE
return Bundle.module
#else
return Bundle(for: Xxx.self) // 任意のクラス
#endif
```

# 終わりに

ファイルが増えると Package の中身が大きくなってしまうので、ディレクトリで指定する方法があるはずでは？と思っているので、どなたかご存知であればコメントいただけますとmm

冒頭の通り、忘れたくない自分のメモ書きを記事にしたので、誰かのお役に立てたら幸いですmm
Windows 11 Home に Hyper-V をインストールする

https://learn.microsoft.com/ja-jp/virtualization/hyper-v-on-windows/quick-start/enable-hyper-v#check-requirements によりますと、
```
Hyper-V の役割を Windows 10 Home または Windows 11 Home にインストールすることはできません。
```
とのことですので、本記事の内容は削除しました。
Windows 11 Pro で Hyper-V を有効にする

# はじめに

Windows 11 Pro では設定を変更することにより Hyper-V という仮想環境を使用できますす。

本記事では、Windows 11 Pro を使用して Hyper-V の設定手順を説明します。

# 使用する PC の仕様

参考のため、使用している PC の仕様を記述しておきます。
- TRIGKEY GREEN G2
- Intel Celeron N2095 2.00GHz
- RAM 8GB
- M2 SSD 256GB
- Windows 11 Pro

## 注意事項
Hyper-V を使用する場合には、事前に BIOS の設定で「仮想化機能」を有効にしておかなければなりません。
- BIOS の設定方法がわからない場合には、付属のマニュアル、PC メーカーのホームページなどで検索してみてください。
- 
### BIOS 設定例
参考までに、上記の PC の BIOS の設定方法を記載しておきます。

上記の PC (GREEN G2) の場合には、電源ボタンを押下したのちに以下の操作を行い、「仮想化機能」を有効にします。
1. 「DEL」キーを連打する。
1. BIOS 画面が表示される。
1. 「→」キーで「Advanced」TAB に移動する。
1. 「↓」キーで「CPU Configuration」に移動し「Enter」キーを押下する。
1. 「↓」キーで「Intel (VMX) Virtualization Tecknologi」に移動し、「↑↓」キーで「Enable」を選択して「Enter」キーを押下する。
1. 「ESC」キーを押下して前の BIOS 画面に戻る。
1. 「→」キーで「Seve & Exit」TAB に移動し、「↑↓」キーで「Save Canges and Exit」を選択し、「Enter」キーを押下する。
1. 「Save configuration and exit?」と確認されるので、「←→」キーで「Yes」を選択し、「Enter」キーを押下する。
1. PCが再起動する。

# Hyper-V の設定

## 「Windows の機能」の起動と設定
スタートボタンを右クリックして、「設定」を開きます。
![001-windows-setting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/915655/c5e1dc90-ff4a-91b3-a345-c5d66d8e6e50.png)

左側に表示されている「アプリ」を押下し、右側に表示されている「オプション機能」を押下します。
![002-setting-appli.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/915655/1a74f852-f971-15f7-a744-be5e567524df.png)

「オプション機能」の画面で一番下までスクロールし、「Windows のその他の機能」を押下します。
![003-other-feature.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/915655/c6cc86ed-3973-6055-fa40-d8be27a26d5d.png)

以下のように、「Windows の機能」が表示されます。
![004-enable-hyperv.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/915655/7d62a68b-368f-79b6-4a61-93759f94b2f8.png)
図の赤枠で示されている「Hyper-V」、「Hyper-V プラットフォーム」、「Hyper-V 管理ツール」を有効にし「OK」を押下してください。

## 設定の実行
「OK」を押下すると、以下のように処理中を示す画面が表示されます。
![005-progress.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/915655/05afde43-5f6d-5c0c-34cb-d95af12eceba.png)

#### 再起動の確認
処理が終了すると、以下のように再起動を促す画面が表示されるので、「今すぐ再起動」を押下してコンピューターを再起動します。
- この時、他のアプリケーションが起動されている場合には、それらを終了してから再起動を実行してください。
![006-done.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/915655/4cf4ad12-da5f-3974-532c-43067b3bd415.png)

## Hyper-V の起動
Windows を再起動したらログオンし、スタートボタンを押して「hyper」と入力してください。
![start-button.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/915655/2d22815b-8ea4-ee9f-2dc9-747b28412178.png)

上記の画面のように、が表示されると思います。
- Hyper-V クイック作成
- Hyper-V マネージャー

「Hyper-V クイック作成」を起動すると、以下の情報が表示されたウィンドウ (ダイアログボックス) が表示されるので、「はい」を押下してください。
- このアプリがデバイスに変更を加えることを許可しますか？
- Microsoft Windows
「はい」を押下すると、以下のように「仮想マシンの作成」という画面が表示されます。
![007-quick-create.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/915655/f56acea3-c759-2d18-bce9-7ea393d1d56e.png)

以上です。
pythonでコーディング問題を解く1

pythonを学びたいpython初心者がコーディング問題を1－3月にかけて解いたのでメモを残します。
全部で6回あります。

### 問題1
10進表記された数を反転する関数を作ってください
```
import math

def reverseNumber(value) :
    result = 0

    while value > 0 :
        # 入力値の1桁目を取り出して、それまでの合計を10倍した値に足すのを繰り返す
        reminder = value % 10
        result = result * 10 + reminder
        
        value = math.floor(value / 10)
        
    return result

print(reverseNumber(12345))
print(reverseNumber(10345))
print(reverseNumber(10340))

print(reverseNumber(100))
print(reverseNumber(10200))
```

### 問題2 3進数を7進数に変換してください
```
import math

def convert3to10(value) :
    result = 0
    exp = 1

    while value > 0 :
        reminder = value % 10
        result += reminder * exp

        exp *= 3
        value = math.floor(value / 10)

    return result
    
def convert10to7(value) :
    result = ""
    
    exp = 1
    
    while value > 0 :
        reminder = value % 7
        result = str(reminder) + result
        value = math.floor(value / 7)
        
    return result 
    

print(convert3to10(111), convert10to7(convert3to10(111)))
print(convert3to10(1111), convert10to7(convert3to10(1111)))
print(convert3to10(1000100100), convert10to7(convert3to10(1000100100)))
```

### 問題3 10進数を16進数に変換してください
```
import math

list = ['A','B','C','D','E','F']
def getHexNumber (value) :
    if value >= 10 :
        return list[value - 10]
    else :
        return str(value)

def convert10to16(value) :
    result = ""
    
    exp = 1
    
    while value > 0 :
        result = getHexNumber(value % 16) + result
        value = math.floor(value / 16)
        
    return result 

print(convert10to16(327), hex(327)) #hexは比較用
print(convert10to16(319), hex(319)) #hexは比較用
```
Stable Diffusionで画像を生成する

Stable Diffusionで画像を生成、その生成された画像を利用してSUZURIでTシャツを作成してみた


#### 内容
1：Hugging Faceのトークンを発行
2：googleColaboratoryでStable Diffusionの設定
3：Stable Diffusionで画像を生成
4:生成された画像を使ってTシャツを作成する方法

#### 1：Hugging Faceのトークンを発行
Hugging Faceのアカウントを作成してトークンの発行を行う。こちら[記事](https://gammasoft.jp/support/how-to-access-models-by-logged-into-hugging-face/)を参考に登録
アクセストークンの発行が出来たら保存しておく

#### 2：GoogleColaboratoryでStable Diffusionの設定をする
・GoogleColaboratoryでノートブックを新規作成したら、編集から「ノートブックの設定」を開く
<img width="400" alt="スクリーンショット 2023-02-12 16.22.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256115/2ad171a4-4b09-dd69-e2f9-2926e61e5d62.png">
・ハードウェアアクセラレータの欄で「GPU」を選択して「保存」で設定完了
<img width="400" alt="スクリーンショット 2023-02-12 16.24.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256115/eae32c68-5b2a-1039-31f4-56521e689ab3.png">

#### 3：Stable Diffusionで画像を生成

以下のコードで画像を作成する
```python
pip install diffusers==0.8.0 transformers scipy ftfy
# diffusers==0.2.4だとエラー出たのでdiffusers==0.8.0に修正しエラー解消
YOUR_TOKEN="HuggingFaceで発行したトークン入力"

# パイプラインの準備
from diffusers import StableDiffusionPipeline
pipe = StableDiffusionPipeline.from_pretrained("CompVis/stable-diffusion-v1-4", use_auth_token=YOUR_TOKEN)
# パイプラインの第1引数に指定しているのがStable Diffusionモデル本体。第２の引数にはその上で設定しているトークンを指定する
pipe.to("cuda")
# パイプラインをGPUに移す

# 画像作成の実行
prompt = "Cat in the style of Gogh"
# promptに作成したい画像のキーワードを入れる
image = pipe(prompt)["images"][0]
image.save(f"Cat_gogh.png")
```
これで画像生成が完了。気に入った画像が生成できたら画像を保存する
いろいろ画像つくってみた

ゴッホ風の猫
![スクリーンショット 2023-02-12 17.48.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256115/b834918c-5d2a-c6f2-c719-9c82b72ead43.png)
ゴッホ風の猫
![スクリーンショット 2023-02-26 15.57.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256115/db80eafc-f00c-bdf9-d763-6146ead0345e.png)

カットバック決めてる猫
![AI_cat_cyber3_カットバック.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256115/ab51bec9-a0c4-dfec-e7e3-a0b678b66e46.png)

コロー風の猫
![コロー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256115/8c85599a-2c66-84c9-920c-2e722cf303c8.png)

ゴッホ風の猫
![cat_Gogh2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256115/cf046920-56e4-869c-7839-f3247a1d222f.png)
OpenAIのAPIを使用して、本のテキストを読み込みBotに回答させる

概要
---
OpenAIのAPIを使用して、本のテキストを読み込みBotに回答させる

環境
---
googlecolab

前準備
---
- OpenAIのAPIキー発行
- [参考URL](https://auto-worker.com/blog/?p=6988)
- [参考URL](https://note.com/npaka/n/n8c3867a55837)
- テキストファイルにあらすじを貼り付けて保存
- googlecolabにdataというフォルダを作成して.txtファイルを保存

実装
---
```python
# llama-indexパッケージをインストール
pip install llama-index
# osモジュールをインポートし、OpenAI_API_KEYを環境変数に設定
import os
os.environ["OPENAI_API_KEY"] = "OpenAI_APIkeyを入力"
import logging
import sys

logging.basicConfig(stream=sys.stdout, level=logging.INFO, force=True)
logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))

# llama-indexパッケージから必要なモジュールをインポート
from llama_index import GPTSimpleVectorIndex, SimpleDirectoryReader

# 'data'ディレクトリ内のファイルを読み込み、documentsに格納
documents = SimpleDirectoryReader('data').load_data()
# GPTSimpleVectorIndexクラスにdocumentsを渡し、インデックスを作成
index = GPTSimpleVectorIndex(documents)

# 質問応答
print(index.query("どのような物語か教えて？"))

#回答
この物語は、日露戦争で絶大な武勲を立てた元兵士の杉元佐一が、病気を治療するために砂金を探しているところから始まります。しかし、砂金はすでに掘り尽くされていたため、杉元は途方に暮れていました。そんなある日、杉元は酔っ払いの男から、アイヌが八萬圓（現代の価格で八億円相当）もの金塊を貯蔵しているという噂話を耳にします。続く。。

# 質問応答
print(index.query("「脱獄王」の異名を持つのは誰？"))

#回答
「脱獄王」の異名を持つのは、白石由竹です。

```

あらすじのデータをもとにした回答が得られた
WEBデザイナーになるための登龍門「バナートレース」とは

どうもYumaです。

WEBデザイナーとして働く際、PhotoshopやFigma等のツールは必須スキルですよね。
独学での学習は難しい...そんな中技術を2倍3倍に上げる方法があること教わりました。

それはバナートレース。

学校やスクールに一切通わないでPhotoshop,Figmaの学習を始めた僕ですが、バナートレースのおかげで技術の基礎を学ぶことができました。

## バナートレースとは

文字通りバナー(WEB上での広告)をトレース(すでにあるものをなぞること)します。
よくWEBサイトやアプリを閲覧している時、広告が出てきますよね、それがバナーです。
トレースをすることで、技術だけでなく、デザインスキルも身につきます。

無料なのにも関わらず、とても勉強になる。
それがバナートレースです。

では、実際にどのような流れでトレースをするのかを書いていきます。
（自分がPhotoshopで行ったやり方をご紹介します。）

## バナートレースのやり方
### ①トレース材料を探す

まずはトレース材料を探します。
おすすめなのは[BannerDesignArchive](http://banner.keizine.net/)。
数多くの有名なバナーが集まっているサイトで、僕がバナートレースをする時に使ったサイトです。

トップページでは正方形型のバナーしかありませんが、検索をかければよく見る横長のバナーも探せます。

### ②Photoshopで作成

トレースするバナーが決まったら早速Photoshopを開きましょう。
どのように作成するのか、最初はなかなか分からないですよね...。

実際に僕が行った手順を一から書いていきます。ぜひ参考にしてください。

## 実際にやってみた
### 1 Photoshopを開く

まずはPhotoshopを開きます。
この時、アートボードの大きさはあまり気にしなくて良いです。(後々大きさ変更できるから)
トレース材がしっかり入るように、ここではWEBのWEB一般サイズで作成していきましょう。
![1.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3280199/f474c8d0-3085-fee2-2b34-b7ea61a2b9ca.jpeg)

1366px × 768pxです。

### 2 トレースする画像を開く

今回は例として、とても綺麗だったFANCL化粧品のバナーをトレースします。
ダウンロードしたトレース画像を開きます。

開いたらこんな画面になります。
![2.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3280199/84721e6d-c197-8e23-b776-e348ed33c079.jpeg)

では早速トレースをしていきましょう。

### 3,トレースしていく

トレース時、注意するべきことは以下の3点です。

- 文字は極力同じフォント、全く同じでなくてもよい
- 画像は同じ画像を使うより、自分で似た画像を探す。
- 図形などは1pxのズレも生じないように。

です。

バナートレースの目的はトレースを通して技術&デザイン知識を得るというものです。
なので文字や画像は全く同じものを使用しなくても大丈夫。

### 4 分からないことはググる。

文字幅どうやって変えるんだ？
グラデーションとか影とかどうやってつけるんだ！？

バナートレースを行っていくうちに様々な疑問が出てくると思います。
その都度ググりまくりましょう。そうすることでどんどんPhotoshopの知識が上がっていきます。

### 5 完成

そして完成したのがこちらになります。
![3.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3280199/37e03831-6d93-a0c2-03ca-6bc1d9bc7b7f.jpeg)

最初は調べながらの作成なので1、2時間程かかりますが慣れれば30分ほどで作成できます。

## まとめ

デザインを勉強しようと思っている人にとってバナートレースは本当におすすめです。
今回はFANCLのバナーを作成しましたが過去には30ほどのバナーをトレースしています。
30個程バナーをトレースすればPhotoshopの知識もある程度つきます。

バナートレースを行う前に作成した広告↓
![4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3280199/4226f9ec-f82a-bca3-356f-0c3e3b6d3d93.jpeg)

バナートレースを行った後に作成した広告↓
![5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3280199/d2fc1bb7-3cd3-35e9-83c9-90e2c3cdafa4.jpeg)

見た目が全く違いますよね、バナートレースを経験するとバナーの型を勉強することができます。
まずは30個を目標に作成していきましょう!

読んでいただきありがとうございました。
何か質問等あればTwitterの方で受け付けております。

では!
Stimulus スコープの概念

Stimulusにはあまり知られてませんがスコープの概念があります。

ドキュメントにも少し記述があります。

https://stimulus.hotwired.dev/reference/controllers#nested-scopes

しかしこの辺はドキュメントで語り切れてない挙動もあってちょくちょく本家にissueがたつのでその辺の解説をしようと思います。

## スコープの範囲

コントローラを適応した要素以下子孫全体になります。

しかし、**同名のコントローラをネストした場合**、その子孫側のコントローラのスコープは親側のコントローラのスコープの範囲外になります。

ゆえに子側のコントローラのスコープにあるTargetは親側のコントローラでは参照できません。

試しにその条件でtargetの数を計算するコードを用意してみました。

<p class="codepen" data-height="600" data-default-tab="html,result" data-slug-hash="abaqabq" data-user="nazomikan" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/nazomikan/pen/abaqabq">
  Qiita_stimulus_scope_1</a> by nazomikan (<a href="https://codepen.io/nazomikan">@nazomikan</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

親側のtestコントローラからみるとitemターゲットは一見6つに見えますが、前述の理由で子側のtestコントローラのスコープが範囲外となるので3個ということになります。

## アクションにもスコープの概念は適用される

ターゲットについてはドキュメントでも触れられてるので読んでいればわかるのですが、action周りにもこのスコープの影響があることはあまり知られていません。

https://github.com/hotwired/stimulus/issues/363#issuecomment-757362033

同名のコントローラがネストされてる場合、子側で発生したイベントは親側のdata-actionとしては処理されません。

以下に再現コードを用意しました。

<p class="codepen" data-height="600" data-default-tab="html,result" data-slug-hash="YzOeOGg" data-user="nazomikan" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/nazomikan/pen/YzOeOGg">
  Qiita_stimulus_scope_1</a> by nazomikan (<a href="https://codepen.io/nazomikan">@nazomikan</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

子コントローラのスコープに書かれたdata-actionにはtestコントローラのupdateAnswerメソッドが指定されてるので一見親のtestも動きそうですが、これも前述の通り子コントローラのスコープが親スコープの範囲外として扱われるため処理されません。

## バブリングもスルーされる

最後にややトリッキーな動き。

**同名のコントローラをネストした場合**、その子孫側のコントローラのスコープで発生したイベントは、バブリング可能なイベントであっても親側のアクションをそれをキャッチすることはできません。

以下の例はボタンをクリックした際に発生するクリックイベントのバブリングに期待して親コントローラ側にアクションを設定している例です。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="eYLMzvN" data-user="nazomikan" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/nazomikan/pen/eYLMzvN">
  Qiita_stimulus_scope_2</a> by nazomikan (<a href="https://codepen.io/nazomikan">@nazomikan</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

クリックイベントはバブリング可能なので一見親側までイベントが届いて親のアクションが動きそうですが実際には動きません。

これは前述のアクションにおけるスコープの動きをEvent.targetの所在地から判断してるためで、こちらも仕様です。

https://github.com/hotwired/stimulus/blob/f0af5bd73c3c64387635af2721d32ef95a54fe12/src/core/binding.ts#L92

https://github.com/hotwired/stimulus/blob/af88dbf16ddb4c86cb2d10dcefffc92d9da43de8/src/core/scope.ts#L40
BigQueryで値の補完をする

次のようなテーブルがあるとします。
|id|name|flag|pre|producer|
---|---|---|---|---
|1|ミカン|on|XXX県産|Aさん|
|2|ミカン||不明|Aさん|
|3|リンゴ|off|不明|Cさん|
|4|リンゴ||YYY県産|Bさん|
|5|バナナ||不明|Cさん|

以下のような形になるよう整形していきます。
|id|name|flag|pre|producer|
---|---|---|---|---
|1|ミカン|on|XXX県産|Aさん|
|2|ミカン|on|XXX県産|Aさん|
|3|リンゴ|off|YYY県産|Cさん|
|4|リンゴ|off|YYY県産|Bさん|
|5|バナナ|off|YYY県産|Cさん|

はじめに、flagの空欄を埋めます。
今回は、同じ行内で最後に登場した値を引き継ぐことにします。
```diff_sql
table_flag AS (
  SELECT
    id,
    name,
    CASE
      -- flagがNULLならば、
      WHEN flag IS NULL
        -- id順で並べた際に、flagの行内で最後にNULLではなかった値を現在の行にコピーする。
        THEN LAST_VALUE(flag IGNORE NULLS) OVER (ORDER BY id)
      ELSE flag
    END AS flag0,
    pre,
    producer
  FROM base_table
)
```
※IGNORE NULLS：NULLを値に含まない
※LAST_VALUE ⇔ FIRST_VALUE
|id|name|flag|pre|producer|
---|---|---|---|---
|1|ミカン|on|XXX県産|Aさん|
|2|ミカン|on|不明|Aさん|
|3|リンゴ|off|不明|Cさん|
|4|リンゴ|off|YYY県産|Bさん|
|5|バナナ|off|不明|Cさん|


次に、preが不明になっている欄を補完します。
今回は以下の条件に沿って進めます。
（１）同じ果物なら同じ産地である。
（２）異なる果物でも、同じ生産者なら同じ産地である。
行の順番に依存している部分がありますが、説明のために用意したテーブルなので無視します。

（１）同じ果物なら同じ産地である。
```diff_sql
table_pre1 AS (
  SELECT
    id,
    name,
    flag0 AS flag,
    CASE
      -- 現在の行が「不明」で、
      WHEN pre = "不明"
        -- 現在の行と1つ下の行の果物が同じで、
        AND name = LEAD (name, 1) OVER (ORDER BY id)
        -- 現在の行の1つ下の行の産地が「不明」でないなら、
        AND LEAD (pre, 1) OVER (ORDER BY id) != "不明"
        -- 現在の行の1つ下の行の産地を現在の行にコピーする。
        THEN LEAD (pre, 1) OVER (ORDER BY id)
      -- 現在の行が「不明」で、
      WHEN pre = "不明"
        -- 現在の行と1つ上の行の果物が同じで、
        AND name = LAG (name, 1) OVER (ORDER BY id)
        -- 現在の行の1つ上の行の産地が「不明」でないなら、
        AND LAG (pre, 1) OVER (ORDER BY id) != "不明"
        -- 現在の行の1つ上の行の産地を現在の行にコピーする。
        THEN LAG (pre, 1) OVER (ORDER BY id)
      -- どちらにも当てはまらないなら現在の値のまま
      ELSE pre
    END AS pre0,
    producer
  FROM table_flag
)
```
|id|name|flag|pre|producer|
---|---|---|---|---
|1|ミカン|on|XXX県産|Aさん|
|2|ミカン|on|XXX県産|Aさん|
|3|リンゴ|off|YYY県産|Cさん|
|4|リンゴ|off|YYY県産|Bさん|
|5|バナナ|off|不明|Cさん|

まだ1件「不明」が残っています。

（２）異なる果物でも、同じ生産者なら同じ産地である。
```diff_sql
table_pre2 AS (
  SELECT
    id,
    name,
    flag,
    CASE
      -- table_pre1とほぼ同じなので説明省略
      WHEN pre0 = "不明"
        AND producer = LEAD (producer, 1) OVER (ORDER BY id)
        AND LEAD (pre0, 1) OVER (ORDER BY id) != "不明"
        THEN LEAD (pre0, 1) OVER (ORDER BY id)
      WHEN pre0 = "不明"
        AND producer = LAG (producer, 1) OVER (ORDER BY id)
        AND LAG (pre0, 1) OVER (ORDER BY id) != "不明"
        THEN LAG (pre0, 1) OVER (ORDER BY id)
      ELSE pre0
    END AS pre,
    producer
  FROM table_pre1
)
```
|id|name|flag|pre|producer|
---|---|---|---|---
|1|ミカン|on|XXX県産|Aさん|
|2|ミカン|on|XXX県産|Aさん|
|3|リンゴ|off|YYY県産|Cさん|
|4|リンゴ|off|YYY県産|Bさん|
|5|バナナ|off|YYY県産|Cさん|

これで完成です。
[小ネタ] デカい標準入力をgrepする際はバイナリファイル扱いに注意

みなさん運用してますか？ ログのgrepしてますか？

私はしています。grepなら、↓こんな感じでやるだけです！[^1]

[^1]: grepの引数にファイル名を直接渡せばいいと思うかもしれませんが、ここでは簡単な例として `cat 2023-*.log` としています。難しい例がお好みの方はたとえば複数の JSONL ファイルを `jq` コマンドでパースして単純なログ行に変換して100万行ぐらいgrepに渡す例など考えてみてください。

```bash
(Linux上)
$ cat 2023-*.log | grep 'some_search_words_here'
```

はい。 **上記には罠があります。**

`2023-*.log` の中身のどこかが **「バイナリファイルっぽいな……？」と `grep` に思われたが最後、下記のようなメッセージを出し、以後 `grep` を諦められてしまいます。**

```bash
$ cat 2023-*.log | grep 'some_search_words_here'
...
Binary file (standard input) matches

(以後 grep をサボる👼 単純に標準入力を捨てはじめる)
```

巨大な標準入力などを扱う際に通常の `grep` だとバイナリっぽいものが来ると以後の探索を諦めてしまうんですね。

これでは困るので、 `grep -a` もしくは `grep --text` を使って「ちょっとバイナリファイルっぽくても諦めんなよ」という思いを `grep` に伝えましょう。

```
$ cat 2023-*.log | grep -a 'some_search_words_here'
...
(🔥バイナリっぽいものを見つけても grep を諦めない🔥)
```

とにかく巨大な標準入力などを扱う際に `grep -a` じゃない場合は本当にそれでいいのかちょっと一考したほうがよさそうです。


## grep (GNU Grep) のバイナリかどうかをチェックする基準はどうなっているのか？

[GNU Grepの公式のソースコードリポジトリ](https://git.savannah.gnu.org/cgit/grep.git) によると、[該当部分のソース](https://git.savannah.gnu.org/cgit/grep.git/tree/src/grep.c?h=v3.9)は下記のようになっています。

```c:src/grep.c
 for (bool firsttime = true; ; firsttime = false)
    {
      if (nlines_first_null < 0 && eol && binary_files != TEXT_BINARY_FILES
          && (buf_has_nulls (bufbeg, buflim - bufbeg)
              || (firsttime && file_must_have_nulls (buflim - bufbeg, fd, st))))
        {
          if (binary_files == WITHOUT_MATCH_BINARY_FILES)
            return 0;
          if (!count_matches)
            done_on_match = out_quiet = true;
          nlines_first_null = nlines;
          nul_zapper = eol;
          skip_nuls = skip_empty_lines;
        }
```

いろいろ条件はありそうですが、条件の一つとして「バッファ中にヌルバイト(`\0`)があればバイナリファイル扱いする」という感じがしますね。

実際に実験してみます。 (Mac上で ggrep (GNU Grep 3.9) を使って実験)

```bash:test_grep.sh
#!/bin/bash

log_input () {
    seq 0 100
    printf '\x00'
    echo "DONE"
}

# 0 から 100 まで印字した後にヌルバイトが来るような入力にgrepを噛ませてみる
log_input | ggrep "."
```

上記で実験してみると、マッチを試行する様子もなしに、にべもなく `binary file matches` と言われてしまいました。

```bash
$ bash test_grep.sh
ggrep: (standard input): binary file matches
```

おそらく `seq 0 100` がバッファに収まってしまって、検索する以前にヌルバイトを発見してしまったので、試行をやめたのでしょう。

ではこれを `seq 10000 19500` に入れ替えるとどうでしょうか？

```bash
# seq 10000 19500
$ bash test_grep.sh
ggrep: (standard input): binary file matches
19491
19492
19493
19494
19495
19496
19497
19498
19499
19500
```

今度はちゃんとヌルバイトまでは試行してくれた感じになりました。

ただ、これを何回か繰り返すと、たまに試行すらしない場合がありました。

```bash
# seq 10000 19500
$ bash test_grep.sh
ggrep: (standard input): binary file matches
```

十分に大きなサイズの入力だと確実に試行してくれるっぽいです。

```bash
# seq 10000 30000
$ bash test_grep.sh
ggrep: (standard input): binary file matches
29991
29992
29993
29994
29995
29996
29997
29998
29999
30000
```

**grepのヌルバイト判定（バイナリ判定）に即時入るかどうかは入力サイズによっては再現性がなく不安定なようです。** 面白いですね。


## まとめ

大きなファイルや標準入力をgrepする時には `grep -a` or `grep --text` を使おう。
BigQueryで配列を展開する

次のようなテーブルがあるとします。
name|items|quantities|prices
--- | --- | --- | ---
一郎|[ "リンゴ", "ブドウ", "モモ" ]|[ 3, 5, 1 ]|[ 100, 200, 300 ]
次郎|[ "リンゴ", "バナナ", "スイカ" ]|[ 2, 1, 4 ]|[ 100, 150, 400 ]
三郎|[ "モモ", "スイカ" ]|[ 3, 1 ]|[ 300, 400 ]

以下のような形になるよう整形していきます。
※sum_all＝ユーザーごとの合計金額
|name|item|price|quantity|sum|sum_all|
--- | --- | --- | --- | --- | ---
|一郎|リンゴ|100|3|300|1600|
|一郎|ブドウ|200|5|1000||
|一郎|モモ|300|1|300||
|三郎|モモ|300|3|900|1300|
|三郎|スイカ|400|1|400||
|次郎|リンゴ|100|2|200|1950|
|次郎|バナナ|150|1|150||
|次郎|スイカ|400|4|1600||

はじめに、アイテムごとに数量と値段を展開します。
```diff_sql
table_expand AS (
  SELECT
    offset,
    name,
    item,
    prices[OFFSET(offset)] AS price,
    quantities[OFFSET(offset)] AS quantity
  FROM
    base_table,
    UNNEST(items) AS item WITH OFFSET AS offset
  ORDER BY name, offset
)
```

FROM文で配列に格納されているアイテムを1行ずつに展開し、それぞれの行にOFFSETを付与します。
```diff_sql
UNNEST(items) AS item WITH OFFSET AS offset
```

OFFSETの値を元に、各アイテムに対応した数量と値段を展開します。
```diff_sql
prices[OFFSET(offset)] AS price,
quantities[OFFSET(offset)] AS quantity
```

|offset|name|item|price|quantity|
---|---|---|---|---
|0|一郎|リンゴ|100|3|
|1|一郎|ブドウ|200|5|
|2|一郎|モモ|300|1|
|0|三郎|モモ|300|3|
|1|三郎|スイカ|400|1|
|0|次郎|リンゴ|100|2|
|1|次郎|バナナ|150|1|
|2|次郎|スイカ|400|4|


これで配列の展開が完了しました。

次に、各ユーザー・アイテムごとに合計金額を計算します。
```diff_sql
table_sum AS (
  SELECT
    offset,
    name,
    item,
    price,
    quantity,
    (price * quantity) AS sum
  FROM table_expand
)
```
|offset|name|item|price|quantity|sum|
---|---|---|---|---|---
|0|一郎|リンゴ|100|3|300|
|1|一郎|ブドウ|200|5|1000|
|2|一郎|モモ|300|1|300|
|0|三郎|モモ|300|3|900|
|1|三郎|スイカ|400|1|400|
|0|次郎|リンゴ|100|2|200|
|1|次郎|バナナ|150|1|150|
|2|次郎|スイカ|400|4|1600|

次に、ユーザーごとに合計金額を計算します。
```diff_sql
table_sum_all AS (
  SELECT
    name,
    SUM(sum) AS sum_all
  FROM table_sum
  GROUP BY name
)
```
|name|sum_all|
---|---
|一郎|1600|
|三郎|1300|
|次郎|1950|

最後に、ユーザーごとの合計金額を元のテーブルに結合します。
```diff_sql
table_fix AS (
  SELECT
    table_sum.*,
    table_sum_all.sum_all
  FROM table_sum LEFT OUTER JOIN table_sum_all
    -- 結合条件1：table_sum.nameとtable_sum_all.nameの値が等しい
    ON table_sum.name = table_sum_all.name
    -- 結合条件1：table_sum.offsetの値が0である
    AND table_sum.offset = 0
)
```
|offset|name|item|price|quantity|sum|sum_all|
---|---|---|---|---|---|---
|0|一郎|リンゴ|100|3|300|1600|
|1|一郎|ブドウ|200|5|1000||
|2|一郎|モモ|300|1|300||
|0|三郎|モモ|300|3|900|1300|
|1|三郎|スイカ|400|1|400||
|0|次郎|リンゴ|100|2|200|1950|
|1|次郎|バナナ|150|1|150||
|2|次郎|スイカ|400|4|1600||

抽出項目を整理したら完成です。
話題の画像生成AIをあなたのアプリやサービスに取り込もう〜Stable DiffusionをiPhoneやMacbookでつかう〜

# 画像生成AIをアプリに搭載する
今話題の**画像生成AI**（**文章から画像を生成するAI**)ですが、**サクッとご自身のアプリに取り込んでサービスに活かせる**といいですよね。
たとえば、ユーザープロフィールのアイコンやヘッダーの画像を自動生成したりもできます。
でも、AIってどうやって取り入れればいいの？
実は、**MacやiPhoneやiPad用のアプリに搭載することができます**。
こうすればサーバーを用意する必要もなく、ユーザーのデバイスで画像生成できるので、簡単にサービスに取り込めます。


**(参考)iPhoneで「猫プリンセス」を生成してみた**
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/4b43b840-1ecd-4f3e-0eed-6cf324f67514.png width=400>


**（参考）Macbookで「スーパーヒーロー猫」を生成してみた↓**

![スクリーンショット 2023-03-13 15.52.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/d0bfb702-f9ab-896d-ce51-ab66d96dcbc5.png)


サンプルアプリとして試してみたい方は、AppStoreにアップされているので、ダウンロードすることもできます。

https://apps.apple.com/app/diffusers/id1666309574?mt=12

# 画像生成AIをiPhone／Macアプリに取り入れてつかう方法

アプリに画像生成AIを入れるには、CoreMLという形式に変換します。
Appleが**画像生成AI　Stable DiffusionのCoreMLバージョンを提供してくれています。** これを使います。

https://github.com/apple/ml-stable-diffusion

HuggingFaceが提供しているサンプルプロジェクトをダウンロードして実行します。

https://github.com/huggingface/swift-coreml-diffusers

**テキストを入力して、Generateボタンを押すと画像が生成できます。**

画像生成モデルはいくつかのバージョンから選ぶことができます。
アダルトコンテンツや有害コンテンツをブロックするSafetyCheckのオンオフ機能もあります。

![スクリーンショット 2023-03-13 15.16.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/c34c75dc-0c6e-301b-bec6-c5f9a4f91e50.png)

# 独自のアプリに取り込む方法

サンプルアプリのCoreMLモデルのダウンロード部分とAI実行部分のコードを抜き出して自身のアプリに取り込めば、簡単にAI画像生成機能が追加できます。

### AI実行

以下で、入力されたテキストをtextEncoderモデルでベクトルにしています。

```swift
let promptEmbedding = try textEncoder.encode(prompt)
let negativePromptEmbedding = try textEncoder.encode(negativePrompt)
```

UNetでLatentとテキストベクトルからノイズを生成しています。

```swift
var noise = try unet.predictNoise(
    latents: latentUnetInput,
    timeStep: t,
    hiddenStates: hiddenStates
)
```

最後に、画像生成モデルで画像を生成します。

```swift
try decodeToImages(latents, disableSafety: disableSafety)
```

**（参考）「暗黒の敵役猫」を生成してみた↓**
![スクリーンショット 2023-03-13 15.56.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/304da24b-14d2-38b4-5ef6-82f60a346637.png)


🐣
****
フリーランスエンジニアです。
もし以下のようなご要望をお持ちでしたらお気軽にご相談ください。
AIサービスを開発したい、ビジネスにAIを組み込んで効率化したい、AIを使ったスマホアプリを開発したい、
ARを使ったアプリケーションを作りたい、スマホアプリを作りたいけどどこに相談したらいいかわからない、画像生成AIやChatGPTを自社サービスに活かしたい…

いずれも中間コストを省いたリーズナブルな価格でお請けできます。

お仕事のご相談はこちらまで
rockyshikoku@gmail.com

機械学習やAR技術を使ったアプリケーションを作っています。
機械学習／AR関連の情報を発信しています。

[Twitter](https://twitter.com/JackdeS11)
[Medium](https://medium.com/@rockyshikoku)
[GitHub](https://github.com/john-rocky)
格安IoT向けSIM 1NCE をSIM7080Gで使えるかテストしてみた

知り合いから1NCEのSIMを教えていただいたので、早速購入して、弊社で販売中のRaspberry pi 用LTE-Mハット(SIM7080Gを搭載)でテストしてみました。

<H3>1NCEについて</H3>  
<font color="FF0000">※ChatGPTより</font>

1nceは、IoT（モノのインターネット）およびM2M（マシン間通信）市場向けのグローバルなネットワークおよびコネクティビティサービスを提供する企業です。
1nceのSIMカードは、グローバルなローミングに対応しており、世界約100か国で利用できます。  

また、1nceのSIMカードは、1つのプランで複数の国で利用することができるグローバルなサービスも提供しています。  
1nceのSIMカードは、高品質のネットワーク接続を提供するため、主要な回線キャリアのネットワークを利用しています。
日本国内では、~~NTTドコモ~~、KDDI、ソフトバンクのネットワークを利用することができます。また、1nceはSIMカードの種類によっては、LTE-MやNB-IoTなどのIoT用のネットワークにも対応しています。  
1nceのSIMカードは、IoT用途に最適化された設計となっており、低消費電力で長時間の運用が可能です。  
また、1nceはAPIやポータルなどの開発者向けのツールを提供しており、IoTサービスの開発や運用に必要な機能を簡単に実装することができます。  
1nceのSIMカードは、IoT向けのサービスを展開する企業や、自社製品にIoT機能を搭載する企業などに最適な選択肢となります。1nceのSIMカードを利用することで、グローバルな展開が容易になり、IoTサービスの拡大や新規事業の立ち上げに役立ちます。
後の方に書いていますが **NTTドコモ** のネットワークは使えませんでした。

[1nceのホームページ](https://1nce.com/ja-jp/)
購入もこちらから出来ます。
<h3>料金プラン</h3>
<h4>1NCE IOTフラットレート</h4>  
<font color="#FF0000">※ 1nceのホームページより</font>

全てが2,000円で  
**1NCE IoTフラットレート**は、**2,000円で10年間**のIoT接続ができます。  
しかも追加費用無しの一括払いです。  

* 最大1Mbpsで500MBのデータ量
* 250通のSMS
* SIMカード
* 世界100ヵ国以上のグローバルカバレッジ
* すべての通信規格 (2G、3G、4G、LTE-M、NB-IoT)
* IoTコネクティビティ マネジメント プラットフォーム
* REST API
* APN
* OpenVPN
* 1NCE OS
* カスタマーサービス
柔軟性：ユースケースによって、より多くのデータ量が必要な場合は追加チャージすることができます。  
追加チャージを都度申し込むか、カスタマーポータルでオートチャージを設定できます。  


つまりトータルで**500MBのデータ量**までは購入時の**2,000円**だけで**追加料金無し**で使えるみたいです(ただし10年間)。

500MBのデータ量ってピンとこないかもしれませんが
私が過去に書いた
[さくらセキュアモバイルコネクトにおけるIoT装置のデータ転送量](https://qiita.com/kaz19610303/items/a205e3e47033f9c2a125)  
が少しは参考になると思います。　　

ちなみにSIMは**2,000円**ですが**別途送料が1,500円**と**消費税350円**がかかります。
これは**ドイツのケルン**から送られてくるために高くなっていると思われます。

<h3>通信テスト</h3>
<h4>テスト環境</h4>  
*自社製SIM7080G Raspberry pi用ハット  

*ノートパソコン  
*通信ソフト : Tera Term  
ノートパソコンと通信用ハットをUSBケーブルで接続して  
Tera TermからATコマンドでテストしました。  
![IMG-1041.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/deaad257-4dfe-21d4-8206-d2e8cc7fa80d.jpeg)  

<h4>テストしたコマンド(Teratermのログ)</h4>

```
AT+CGDCONT=1,"IP","iot.1nce.net"

OK
AT+COPS=1,2,"44020"

OK
AT+COPS?

+COPS: 1,2,"44020",7

OK
AT+CPIN?

+CPIN: READY

OK
AT+CGREG?

+CGREG: 0,5

OK
AT+CGNAPN

+CGNAPN: 1,"iot.1nce.net"

OK
AT+CPSI?

+CPSI: LTE CAT-M1,Online,440-20,0x4A58,113648221,143,EUTRAN-BAND8,3750,3,3,-11,-78,-54,14

OK
AT+CNACT=0,1

OK

+APP PDP: 0,ACTIVE
AT+CNACT?

+CNACT: 0,1,"10.141.175.1"
+CNACT: 1,0,"0.0.0.0"
+CNACT: 2,0,"0.0.0.0"
+CNACT: 3,0,"0.0.0.0"

OK
AT+SNPING4="google.com",3,16,1000

+SNPING4: 1,142.250.186.78,316
+SNPING4: 2,142.250.186.78,392
+SNPING4: 3,142.250.186.78,335

OK
```


<h4>キャリア毎の接続テスト</h4>  

```
AT+CGDCONT=1,"IP","iot.1nce.net"

OK
AT+COPS=1,2,"44052"

ERROR
AT+COPS=1,2,"44053"

ERROR
AT+COPS=1,2,"44051"

OK
AT+COPS=1,2,"44020"

OK
AT+COPS=1,2,"44010"

ERROR
AT+COPS=1,2,"44020"

OK
```
44010 : NTTドコモ
44020 : Softbank
44051 : KDDI
44052 : KDDI
44053 : KDDI

以上の結果から
SoftbankとKDDIには接続出来ました。

今まで MEEQのSIMを使うのが一番安いかと思っていましたが、たぶん1nceの方が安くなると思います。
Swiftの.init()とは？

# はじめに
同じ初心者、初学者の方を対象にしてます。また、自分のメモ、備忘録です。
学習中に.init()というのが出てきたので調べました。

# .init()とは
クラスや構造体などのインスタンスを初期化するために使用されるイニシャライザ（initializer）の一種です。
イニシャライザを呼び出す書き方のひとつです。

下記コードのように同じに動きます。

```swift
import Foundation

class Person {
    var name: String
    var age: Int

    init() {
        name = "John Carpenter"
        age = 0
    }
}

let person1 = Person()
print(person1.name) // John Carpenter

let person2: Person = Person()
print(person2.name) // John Carpenter

let person3 = Person.init()
print(person3.name) // John Carpenter

let person4: Person = .init()
print(person4.name) // John Carpenter
```
構造体でも試してみました。
```swift
import Foundation

struct Person {
    var name: String
    var age: Int

    init() {
        name = "John Carpenter"
        age = 0
    }
}

let person1 = Person()
print(person1.name) // John Carpenter

let person2: Person = Person()
print(person2.name) // John Carpenter

let person3 = Person.init()
print(person3.name) // John Carpenter

let person4: Person = .init()
print(person4.name) // John Carpenter

```
# おわりに
[この記事](https://qiita.com/uhooi/items/1cac84c2918be563941b)でどうやって使うべきか詳しく書かれてます！
【Linux】システムの起動まとめ

# この記事について
システムの起動の流れと詳細についてのまとめです。

# 一般的なLinuxシステムのブートプロセス
①電源入れる
②BIOS起動
③ブートローダ起動
④カーネル起動
⑤init起動

# BIOS（Basic I/O System)
最もハードウェアに近い部分のシステム。
物理的なハードウェア（マザーボード）上に書き込まれている。

コンピュータの電源を入れると一番最初に起動するのがBIOS。
BIOSが起動すると、、、
・記憶装置（HDD）などに関して最低限の認識を行う
・起動デバイスの優先順位を決定する
・優先順位に従って各デバイスの先頭セクタにあるMBR（ブートローダが格納されている、ブート用の特殊領域）を読み込み、得られたブートローダに制御を移す。ブートローダが得られない場合は次のデバイスのMBRを読み込む。

起動デバイスの優先順位は、BIOSセットアップ画面で任意の順序に設定変更できる。
※BIOSセットアップ画面はコンピュータの起動時に「DEL」か「F２」などを押すことで呼び出せる。

ちなみに最近のシステムではUEFI（Unified Extetnsible Firmware Interface）への移行が進んでいる。

# ブートローダ
Linuxシステムでは「GRUB」が該当する。
MBRに格納されている第一段階部分と、記憶装置内の別の場所に格納されている第二段階部分がある。
MBRには厳しいサイズ制限があるので、このように分かれている。
ブートローダはカーネルと初期RAMディスク(initramfs)の内容をメモリ上に展開し、記憶装置内のカーネルをロードし、カーネルに制御を移す。
第一段階のブートローダはMBRの先頭446バイトの領域にインストールされる。
![スクリーンショット 2023-03-13 17.59.25（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2273453/00e8c5c1-3182-4fd8-1cb5-3a3ba82e9c27.png)

# カーネル
高度にハードウェアを認識・制御し、ルートファイルシステムのマウントなど様々な初期化処理を行う。
カーネルは、ブートローダがメモリ上に展開した初期RAMディスク内のファイルシステムへアクセスするために必要なドライバやスクリプトを利用してルートファイルシステムをマウントする。

カーネルイメージとカーネルのバージョンに対応する初期RAMディスクは「/boot」ディレクトリに格納される。
初期RAMディスクは展開してイメージないのディレクトリ、ファイルを参照することができる。

# init
ルートファイルシステムから「init」という特別な最初のプロセスを起動する。
PIDは必ず「１」。
SysVinitと呼ばれる従来のinitプログラムを採用しているシステムでは、「/sbin/init」が起動される。
initプロセスは設定ファイルの「/etc/inittab」の記述に基づいて、自動起動すべきプロセスを立ち上げたり、アプリケーションレベルの初期化を行う。

最近のシステムではinitプログラムとして、初期化処理を高速化した「Upstart」や「systemd」を採用している場合がある。
その場合は「/etc/inittab」ファイルは使用されない。

# 最後に
なかなか覚えられなかったのですが、自分でまとめることでだいぶ理解が進みますね。
わからないと思うことほど、じっくり時間をかけて理解してこうと思います。
Pythonの基礎を学び直す


###Pythonの変数と型

```python
a=123      #型はint
b=123.444  #型はfloat
c="おら！"  #型はstr
d=True     #型はbool
e=[1,2,3]  #型hlist
f=1.4e8    #型はfloat。eはx１０の８乗って意味になる
```

てな感じで代入しまくれる。Javaと違っていちいち文字を変えなくていい。楽。
上のa~fが変数。Pythonでは代入した値によって変数のデータ型（上記みたく数値とか文字列）が決まる。
例えば、上記からの例で言うと
変数aに、整数123を代入する。すると変数aのデータ型はint(整数)になる。
ちなみにTrueは１、Falseは０として扱われる。

演算子
```Python
a = 5 + 1
b = 3 > a;  c = 1 < 5 and 2 < 3  #セミコロン（；）は改行省きになる。andとは違う
print(b)
print(c)
```
↓
```
False
True
```

リスト
```Python
a = [1,2,3,4]

b = a[3]  #数え方は左から０スタート
print[b]

a.append(10)  #appendは英語で「追加する」という意味。この場合、リストの末尾に１０の数値を追加
print[a]

a[3] = 1  #リストの左から３番目を１に入れ替える
print[a]
```
↓
```
4
[1,2,3,4,10]
[1,2,3,1,10]
```

タプル
リストと同じく複数の値（単体だけ使いたいなら、(1, ）といった風に「,」を使ってロック！）をまとめて扱える、が！追加、削除、入れ替えができない。要素を変更する予定がない時に使う
```Python
a = (1,2,3,4)  #かっこがリストとは違う
b = a[3]  #４番目の数字ゲット
print[b]
```
↓
4

さらに
```Python
a = [1,2,3]
a1,a2,a3 = a
print(a1,a2,a3)
```
↓
```
1 2 3
```
といった風に簡略化も可能

if文

```Python
a = 50
if a < 10:
    print("証言")
elif a < 12:
    print("愛のままに")
elif a < 20:
    print("OUT LOW")
else a < 100:
    print("Kawasaki Drift")
```
↓
```
OUT LOW
```

てな感じでifに合わなかったら、elifに。elifはいくらでも。それでも無理だったら最終手段、else(それ以外全て)


for文

```Python
for a in [1,5,10]:
    print(a)
for a in range[3]:
    print(a)

array = ["あ","い","う"]
for b in array:
    print(b)
```
↓
```
1
5
10
15
0
1
2
あ
い
う
```

while文
```Python
a = 0
while a < 4:  #条件式
    print("ゴールデンマイク")
    a = a + 1
```
↓
```
ゴールデンマイク (a=0)
ゴールデンマイク (a=1)
ゴールデンマイク (a=2)
ゴールデンマイク (a=3)
```

これはどうなってるのかというと、最初に変数aは0に初期化されている。
そのまま下に行くと a<4　の条件を満たしているのでwhileのブロックに進む
0だと永遠に進まないので、a+1（これをインクリメントと言う）で
変数が３に行くまでwhileの最初に戻っては下にを繰り返す。
変数が4に行くとwhile文の条件が満たされないのでストップ。


あとちなみにこの隙間が
```Python
    print(a)
```
↑
インデント（字下げ）ってやつ
paizaラーニング レベルアップ問題集 巡回セールスマン問題メニュー JavaScript ビットによる集合の表現

ビットによる集合の表現 (paizaランク C 相当)

https://paiza.jp/works/mondai/tsp_problems/tsp_problems__expression_by_bits

## 注意点
k=0の時、２行目が空行になります。
なので、単に
```javascript
const S = lines[1].split(" ").map(Number);
```
とすると、空行の時エラーになります。
lines[1]がk=0の時は実行されない工夫がいります。

# 解答例(C++の場合参考)

ビット論理和`|`を使います。`a|b`は、aとbで、どちらかが1の位置で1で返します。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Bitwise_OR

b を b と 2^s_i の論理和で更新します。
bのビット列の1の位置はそのまま１で、
2^s_iが1、かつ、bが0の位置が1になります。
`b = b | 2^s_i`
2^s_i は、1 を s_i 回左ビットシフトすることで作れる。
すなわち、
`2^s_i = 1 << s_i`
よって、
`b = b | 1 << s_i`
書き方を変えると、
`b |= 1 << s_i`
これはbをbと1<<s_iの論理和で更新するイメージ。


例、s_i=3の時までを表にした。
| s_i | 3 | 2 | 1 | 0 |
|:-:|:-:|:-:|:-:|:-:|
| 2^s_i  | 2^3  | 2^2  | 2^1  | 2^0  |
| 整数値  | 8  | 4  | 2  | 1  |
| 左ビットシフト  | 3回  | 2回  | 1回  | 0回 |
| 1 << s_i  | 1 << 3  | 1 << 2  | 1 << 1  | 1 << 0 |
| ビット表記  | 1000  | 100  | 10  | 1 |
| ビット表記で1になる位  | 8の位  | 4の位 | 2の位  | 1の位 |


```javascript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");

const [n, k] = lines[0].split(" ").map(Number);

let b = 0;
for (let i = 0; i < k; i++) { //k=0の時不実行
  const s = lines[1].split(" ").map(Number)[i];
  b |= 1 << s;
}
console.log(b);
```


# 解答例(Python3の場合参考)
b に 2^s_i を足すやり方。
答えはビット列ではなく整数値で出すので、
都市s_iを整数値2^s_iにして、bに加えていく。

```javascript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");

const [n, k] = lines[0].split(" ").map(Number);

let b = 0;

if (k !== 0) { //k=0の時不実行
  b = lines[1].split(" ").reduce((sum, s_i) => sum += 2 ** Number(s_i), 0);
}

console.log(b);
```

ビット列に足すイメージは以下の通り。
都市を表す数値が、他と被らず積み上がる。

入力例1の時。
集合S = 3 5 0 2 1

bit = 0 //ビット列
sum = 0 //整数値
とすると、

3
1 << 3 = 1000(2) = 2^3 = 8 
bit += 1 << 3 = 1000(2)
sum += 2^3 = 8 

bit = 1000(2)
sum = 8

5
1 << 5 = 100000(2) = 2^5 = 32
bit += 1 << 5 = 100000(2)
sum += 2^5 = 32

bit = 101000(2)
sum = 40

0
1 << 0 = 1(2) = 2^0 = 1
bit += 1 << 0 = 1(2)
sum +== 2^0 = 1

bit = 101001(2)
sum = 41

2
1 << 2 = 100(2) = 2^2 = 4
bit += 1 << 2
sum += 2^2 = 4

bit = 101101(2)
sum = 45

1
1 << 1 = 10(2) = 2^1 = 2
bit += 1 << 1
sum += 2^1 = 2

bit = 101111(2)
sum = 47

となる。
非同期処理の概要とfirestoreから取得したデータでasync/awaitを使用してみる

まず、非同期処理の概要を説明する。

非同期処理は、処理が完了するまで待たずに次の処理を開始することができる処理方式です。JavaScriptでは、非同期処理を実現するために、コールバック関数、Promise、Async/Awaitなどの方法があります。

コールバック関数は、非同期処理が完了した後に呼び出される関数です。しかし、ネストが深くなることがあるため、可読性が低下することがあります。Promiseは、コールバック関数よりも可読性が高く、エラー処理も簡単に行うことができます。Async/Awaitは、Promiseをより簡潔に扱えるようにしたもので、コードの可読性が高くなります。

いつもNotionでメモを取るのだが、非同期処理の概要を説明させたら書きたかった情報を非常に簡潔にまとめてくれた。

これらを具体的なコードとともに解説しているのが以下のYouTubeの動画である。

https://www.youtube.com/watch?v=Vhnz1V-v1cU&t=1077s

非常にわかりやすい。

コールバック関数、Promise、async/awaitの比較があるとasync/awaitの直感的なわかりやすさが際立ち、さらに非同期処理のイメージもつかみやすい。

この動画を参考にして、firestoreから情報を取得して、そのデータをHTMLの記述の中に加えるという処理をする。

```
  const FetchData = async () => {
    const docRef = doc(db, "posts", "G3FgjxEUvGySsLHDjuRu");
    const docSnap = await getDoc(docRef);
    
    if (docSnap.exists()) {
        return docSnap.data();
    }else{
      console.log("No such document!");
    };
  };

  
  const  handleClickFetchButton = async () => {
    const fetched_data = await FetchData();

    document.getElementById("fetched_title").textContent = fetched_data.title;
    document.getElementById("fetched_text").textContent = fetched_data.text;

  }
```
データの取得とデータの書き換えに使われる関数はこのようになる。
onClickで下のhandleClickFetchButtonという関数が起動するようにする。
そして、その処理はfirestoreからデータを取得してくるというFetchDataという関数が終了次第実行するので非同期関数を使用している。

関数の定義に"async"を使用して、終了を待つ処理の関数の前に"await"をつける。

非常に直感的でわかりやすい。

そして、取得したデータをIdを基にして取得したHTMLのタグ内へ書き換える。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2971002/1e5ca3a1-c2e8-e58e-83e5-5dc27533ce47.png)

こうすると取得したデータを表示することが出来る。
このFetchDataの中でfirestoreからオブジェクトを取得するときのIDは引数に持たせて汎用性が高くなるようにしていく。

以上、非同期処理の簡単なまとめでした。

補足

Promise形式でAPIを叩いてその処理をasync/await形式で引き継いで処理を施そうとすると上手くデータの引き渡しが出来なかった。

エラーの原因は分からないが、違う形式で非同期処理を引き継ぐのは良くないのかもしれない。
NETCONFに触れてみよう

１．NETCONFとは？
　ネットワークデバイスと通信するトランスポートプロトコル。
XMLでの設定データを編集し、ネットワークデバイスから運用データを取得するためのメカニズムを提供。
大規模NWの性能や安心を確保するためにはNWのコンフィグレーション設定がとても重要なのである！

２．NETCONFのプロトコル構造

　①コンテント層
　②オペレーション層
　③RPC層
　④トランスポートプロトコル層
　
上記４つの層で構成されている

①クライアントとサーバーが実際に通信するための通信プロトコルが位置する。
②XML/YANGで符号化されたパラメータをRPCメソッドで呼び出す各種オペレーションが位置する。
③階層的なコンテンツのための符号化メカニズムや交換を行う共通言語としてXMLを使用しており、RPCはXMLで記述。
④機器のコンフィグレーションデータ並びに障害管理および性能管理に必要な状態情報が位置する。
Windowsローカル環境で動作させるPythonコードをかくときの注意点


普段Mac上でpythonコードを書くことが多いのですが、
Mac上で動作確認したものをWindows上で動作させる機会があり、コードの変更に手間どりました。
その際のMac->Winへの変更点を、備忘録代わりにまとめます。

- ファイルの書き込み時にエンコーディングを指定する
- ファイルパスの区切り文字をWindows用に変更
- ファイルの出力時にファイルパスをWindows形式に変換

## 対応しないと起こりがちなこと
- ディレクトリパスが通らずファイルが開けない
- エンコードが通らず、ファイルが想定したとおりに開けない
 

## 環境
```
Windows10
python 3.11
```

## ファイルの書き込み時にエンコーディングを指定する

Macでコードを書いている際には文字コードを意識せずとも動作しますが、
Windowsで動作させる際には明示的に文字コードを指定しましょう。

```python
# CSVファイルを書き込みモードで開く Mac
with open(output_file, 'w', newline='') as csvfile:
    writer = csv.writer(csvfile)
```

```python
# CSVファイルを書き込みモードで開く Win
with open(output_file, 'w', newline='', encoding='utf-8') as csvfile:
    writer = csv.writer(csvfile)
```
## ファイルパスの区切り文字をWindows用に変更

Winの区切り文字(¥)は、`\(バックスラッシュ)`で表現されます。
また文字列の頭に`r`（raw文字列）明示して指定することで区切り文字が正しく読み込まれるようにします。

```python
# CSVファイルのパス Mac
csv_file_path = "./screenshot_data.csv"
```
```python
# CSVファイルのパス Win
csv_file_path = r".\screenshot_data.csv"
```

# ファイルの出力時にファイルパスをWindows形式に変換

以下の例は、HTMLファイル内にディレクトリパスを記入する際の前処理です。
こちらも明示的に変換するようにします。

```python
# Mac
image_path = os.path.abspath(os.path.join(root_dir, browser_dir, func_dir, case_dir, filename))
```

```python
# Win
# Windows形式のファイルパスに変換する関数
def convert_to_windows_path(path):
    return os.path.abspath(path).replace('/', '\\')


image_path = os.path.abspath(os.path.join(root_dir, browser_dir, func_dir, case_dir, filename))
# Windows形式に変換
windows_image_path = convert_to_windows_path(image_path)

```

## まとめ
- pythonコードをWindows上で動作させる際の変更点についてまとめました
- 他にもつまづいたことがでてきたら、都度追記していきたいです