OmniAICreator/Qiita-1.07M huggingface dataset:
ファイルやフォルダを制御、"Filesystem"

[Symfony Component Advent Calendar 2023](https://qiita.com/advent-calendar/2023/symfony-component)の4日目の記事です。

## ファイルやフォルダを制御、"Filesystem"

`Filesystem` はファイルやフォルダを制御する行うコンポーネントです。OS依存を汲み取ってくれます。Symfony以外でも動作します。

### インストール

```shell
composer install symfony/filesystem
```

### 使い方

```php
use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
use Symfony\Component\Filesystem\Filesystem;

$filesystem = new Filesystem();
try {
    $filesystem->dumpFile('data.txt', 'Hello world!'); // ファイル書き込み
    $filesystem->appendToFile('data.txt', 'Hello again'); // 追記
    $filesystem->copy('data.txt', 'duplicate.txt'); // コピー
    $filesystem->remove('data.txt'); // 削除

    $filesystem->mkdir('data'); // mkdir
    $filesystem->chmod('data', 0600); // chmod
    $filesystem->chmod('data', 0600, recursive: true); // 再起的chmod
    $filesystem->symlink('data', 'data-link'); //symlink
} catch (IOExceptionInterface $exception) {
    echo 'error: ' . $exception->getPath();
}
```

### パスの正規化

`/var/www/html/src/../config/services.yaml` みたいなファイルパスを正規化することができる `Path`クラスがあります。

```php
use Symfony\Component\Filesystem\Path;

$path = Path::canonicalize('/var/www/html/src/../config/services.yaml';
// /var/www/html/config/services.yaml

$path = Path::canonicalize('config/packages/doctrine/test/../../../services.yaml');
// config/services.yaml

$path = Path::canonicalize('C:Programs/PhpStorm/..');
// /C:Programs
```


### 絶対パス・相対パス化

相対パスを指定のディレクトリからの絶対パスに変更もできます。その逆も可能です。

```php
$path = Path::makeAbsolute('config/services.yaml', '/var/www/html/');
// /var/www/html/config/services.yaml

$path = Path::makeAbsolute('..', '/var/www/html');
// /var/www

$path = Path::makeRelative('/var/www/html/config/services.yaml', '/var/www/html');
// config/services.yaml
```

### 余談：Symfonyのプロジェクトルートディレクトリ

Symfonyのプロジェクトのルートディレクトリは、パラメータ `kernel.project_dir` で取得できます。よって、基本はDI（オートワイヤリング）して使います。Symfony 6.3から `#[Autowire]` が使えるようになったのでとても楽にDIすることができます。

```php
    public function __construct(#[Autowire(param: 'kernel.project_dir')] private readonly string $projectDir)
    {
        parent::__construct();
    }


    protected function write()
    {
        $filesystem = new Filesystem();
        $filesystem->dumpFile(
            Path::makeAbsolute('var/test.txt', $this->projectDir),
            // /var/www/html/var/test.txt
            'hello world.'
        );
        
    }

```

## まとめ
今回は`Filesystem`でした。Symfonyでファイル触るならこれですね。Symfony6.3からの`#[Autowire]`は超便利なのでぜひ！
LangChainでPDFをベクトル化してローカルに保存してみた

## はじめに
LangChainを使用して、PDF文書をベクトル化し、ローカルのベクトルストアに保存してみました。この方法により、一度ローカルに保存した後はベクトル化を再度行う必要がなくなり、回答時間を短縮することができます。LangChainを使った文書検索を開発している方におすすめです。


## 開発環境
- Windows 11
- Python 3.9.15
- dotenv
- LangChain
- Azure OpenAI Embeddings
- CharacterTextSplitter
- FAISS
- PyPDFLoader

## 実装
### 必要なパッケージのインストール
以下のコマンドで必要なパッケージをインストールします。

```terminal
pip install langchain openai python-dotenv faiss-cpu pypdf PyPDF2 tiktoken
```

### 環境変数の設定と依存関係の読み込み
.envファイルを作成し、Azure Open AIの環境変数の設定をします。
ご自身の環境に合わせて設定してください。

```.env
# APIキー
OPENAI_API_KEY = "XXXXX"

# エンドポイント
AZURE_OPENAI_ENDPOINT = "XXXXX"

# 使用するOpenAI APIのバージョン
OPENAI_API_VERSION = "XXXXX"
```
　　
必要なライブラリをインポートします。
```test.py
import os 

from dotenv import load_dotenv 
load_dotenv() 

from langchain.embeddings import AzureOpenAIEmbeddings
from langchain.text_splitter import CharacterTextSplitter 
from langchain.vectorstores import FAISS 
from langchain.document_loaders import PyPDFLoader 
```

### ベクトル化の準備
ベクトル化に使用するAzure OpenAI Embeddingsを定義します。

```test.py
embeddings = AzureOpenAIEmbeddings(
    azure_deployment="text-embedding-ada-002",
    openai_api_version="2023-05-15"
)

```

### FAISSベクトルストアの初期化

FAISSを使用して、文書を高速に検索可能な形式に変換します。

```test.py
dummy_text, dummy_id = "1", 1
vectorstore = FAISS.from_texts([dummy_text], embeddings, ids=[dummy_id])
vectorstore.delete([dummy_id])
```

### ファイルの読み込みと処理

指定されたディレクトリ内のPDFファイルを読み込み、処理します。
dirnameはご自身のディレクトリ名に合わせてください。

```test.py
dirname = "datasets"
files = []

for filename in os.listdir(dirname):
    full_path = os.path.join(dirname, filename)
    if os.path.isfile(full_path):
        files.append({"name": filename, "path": full_path})
```

### テキストのベクトル化とストアへの保存

読み込んだPDF文書からテキストを抽出し、ベクトル化してストアに保存します。

```test.py
for file in files:
    if file["path"].endswith(".pdf"):
        loader = PyPDFLoader(file["path"])
    else:
        raise ValueError(f"Unsupported file format: {file['path']}")
    pages = loader.load_and_split()
    for page in pages:
        page.metadata["source"] = file["path"]
        page.metadata["name"] = file["name"]

    text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
    docs = text_splitter.split_documents(pages)
    vectorstore.merge_from(FAISS.from_documents(docs, embeddings))

vectorstore.save_local("./vectorstore")
```

これまでのPythonコードを上から順に記述し終わったら、Pythonファイルを実行します。
実行が完了すると、以下のようにvectorstoreディレクトリが作成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124507/e146b9ba-2bd8-667b-b8a6-2dd52aa73899.png)


## おわりに
LangChainを使用して、PDF文書をベクトル化し、ローカルのベクトルストアに保存してみました。文書検索システム開発の際に便利です。ぜひ、試してみてください。

最後までお読みいただき、ありがとうございました！ 記事に関する質問等ございましたら、コメントもしくは以下のDMにてよろしくお願いします。

https://twitter.com/t___tatsuki

## 参考文献
https://python.langchain.com/docs/integrations/vectorstores/faiss
M1MacBookのgitに補完機能を導入する

# git-completionsをM1MacBookに導入する
## 背景
- 長いブランチ名を手入力するのって大変

## 環境
- PC: M1MacBook
- シェル: zsh
- git: git version 2.42.0
- Homebrew 4.1.22

## 方法
- gitの保管をするためのスクリプトがここにある

https://github.com/git/git/tree/master/contrib/completion

- 基本的に[git-completion.zsh](https://github.com/git/git/blob/master/contrib/completion/git-completion.zsh)の指示に従う
- 私の環境ではそもそもbashの補完機能を導入していなかったので、それもbrewを用いて導入する
- `source ~/.zshrc`で再読み込みを行ったが、うまくいかなかった
- git-completion.bashもインストールした


```all.sh
cd ~
mkdir .zsh
cd .zsh
curl -o _git https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.zsh
echo "fpath=(~/.zsh \$fpath)" >> ~/.zshrc
brew install bash-completion
source ~/.zshrc
# error

curl -O https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.bash
source ~/.zshrc

# `$ git chec`などとすると補完が効くはず
```

## 参考文献
https://zenn.dev/aruneko/articles/26815f95ea2124
巨大な数の足し算(繰り上がりなし) Python3編

https://paiza.jp/works/mondai/string_primer/advance_step10

巨大な数の足し算。
あれ、、、これじゃだめだっけ？と思ってたけど
OKだった

```py
S = int(input())
T = int(input())
print(S + T)

```
え、これでいいのか？
と思ったが、意図は違ったらしい。

>S , T を数値として扱うとオーバーフローが発生します。

> 答えとなる値も数値として扱うことはできない

なるほど。。。だから文字列でしないとだめなのか。
繰り上がりがないとすると、計算はたしかにこうなるよな。。。
でもそれなら、オーバーフローさせて正解にしないほうがいいんじゃないかな？
まあ、勉強ってことで。。

```py
s = input()
t = input()

ans = ""
for i in range(len(s)):
    ans += str(int(s[i]) + int(t[i]))

print(ans)
```

じゃあ繰り上がりがあるときはどうするのかな？
次の問題もやってみる。

https://paiza.jp/works/mondai/string_primer/advance_step11

おそらくこれは繰り上がりあり。
基本は前のを使うとして、繰り上がりをどうするか。
まず１桁から数えないといけないから前回と同じ方法は使えないのかしら。
逆から考えるってことやんな。
pythonの場合逆ループができるので。。。

繰り上がりはループのはじめにｃとしてその桁のトータルを計算しておきます。

で、繰り上がりがなければ繰り上がりの変数を０にしておきます。

そのあと、判定としてループの最後に行っていて、かつ、繰り上がりがあれば
それを最後に付け足し、後はプリントと。
これでできました

```py
s = input()
t = input()
c = 0
total = 0
ans = ""
for i in reversed(range(len(s))):
    total = int(s[i]) + int(t[i]) + c

    if total > 9:
        tmp = str(total)
        ans = (tmp[1]) + ans
        c = int(tmp[0])
    elif total <= 9:
        ans = str(total) + ans
        c = 0
    if c > 0 and i == 0:
        ans = str(c) + ans
    total = 0

print(ans)
```

答えを見ると。。。
ループの書き方なるほどです。。。
これはpythonじゃないときにつかえるな。
繰り上がりの考え方ですが、１０で割った商が繰り上がりの数、
余りが１桁の答えになりますね。
これを利用したアルゴリズムです。
スライスにするか計算にするか、で考え方が分かれそうです。

```py
s = input()
t = input()

ans = ""
upper = 0
for i in range(len(s) - 1, -1, -1):
    z = int(s[i]) + int(t[i]) + upper
    upper = z // 10
    ans = str(z % 10) + ans

    if i == 0 and upper > 0:
        ans = str(upper) + ans

print(ans)

```

つづいて掛け算にも挑戦。

https://paiza.jp/works/mondai/string_primer/advance_step12

これは前回と考え方は同じなので
ちょっと変えれば。。。

このケースの場合、すべての桁が0になったとき1桁の０に変えないといけないので
その判定も入れています。

でも実は、t=0だったら0をprintするだけでもよかった。むしろこっちのほうが簡単だな。。。。
０にどんな数をかけても０やし。

```py
s = input()
t = input()

ans = ""
upper = 0
for i in range(len(s) - 1, -1, -1):
    z = int(s[i]) * int(t) + upper
    upper = z // 10
    ans = str(z % 10) + ans
    if i == 0 and upper > 0:
        ans = str(upper) + ans
flag = False  
for i in ans:
    if i != "0":
        flag = True
        
if flag == False:
    ans ="0"

print(ans)

```
お手軽正規表現

よく出てくる割に忘れる正規表現を簡単にまとめました。
正規表現の基礎基本はここでは省略しています。
基礎基本の勉強におすすめなサイト
https://www.tohoho-web.com/ex/regexp.html
https://www.secuavail.com/kb/practical-post/regex-introduction/



# 正規表現でよく出てくる記号たち

- `.` ドット - なんでも良い一文字
- `+` プラス - 一つ前のアイテムを1回以上繰り返す
- `*` アスタリスク - 一つ前のアイテムを**0回以上**（0もアリ） 繰り返す
- `?` クエスチョンマーク - 一つ前のアイテムが0もしくは1回出現する
- `^` ハット - 文字列の先頭を表す
- `$` ダラー - 文字列の終端を表す
- `\` バックスラッシュ - エスケープ文字などと呼ばれ、正規表現で意味を持つ記号をそのまま文字として扱いたい場合にその記号の前におくことで、直後の記号を文字として扱うことができるようになる

## 正規表現にて定義されている特殊な表現

数字を表す文字や、空白を表す文字など単純にそれを判別するためということもあるが、その正規表現の狙いなど意図を反映させるときなどに用いるとよりよい気がする。

よく使うものたち↓

- `\d` - すべての半角数字
単純に数字を引きたいときは`[0-9]`とするよりも`\d`となっていた方がその意図がわかりやすい
`[0-9]`だと条件次第では`[0-8]`とかもあるかなと勘ぐってしまう
- `\s` - 空白文字 これは可読性のために積極的に使った方がよいと感じる

## 否定先読み
これを知っているとよりいい感じの正規表現が作れる

```
(?!foo)
```

これで文字列`foo`を含まないという条件で引っ掛けることができるようになります。

# お役立ちの正規表現パターン

- 先方一致
```
^foo.*
```

- 後方一致
```
.*foo$
```
- fooかhoge
```
(foo|hoge)
```

- yamlとymlを両方引っ掛ける
```
ya?ml
```

- TenGigabitじゃないGigabitEthernetを引っ掛ける
```
(?!.*Ten)GigabitEthernet\d(\.\d)?
```
将棋AIと対戦

## 初めに
将棋AIは、近年急速に進化を遂げています。

もはや人間のトップ棋士を凌駕するレベルに達しており、プロ棋戦でも活躍しています。

そんな将棋AIと、低レベルから対戦してみたらどうなるのか？

今回は、将棋AIと低レベルから対戦してみた感想を、ブログにまとめたいと思います。
![AI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630945/08799123-b8c1-09cb-ad6e-40a1fb8da5d4.jpeg)
## なんでやろうと思ったか
将来人類はAIより劣っているという理由から職等を失うといわれている。

この問題に対抗するべく今回はAIと戦おうと思います。

将棋は人生で一回もやったことはないがなんとかなるだろう。

人類のほうが優秀であるということを証明しようと思います。
## 今回戦う相手
将棋AIには、さまざまな種類があります。

今回は**AI将棋ZERO**というアプリで対戦しようと思います。

AI将棋ZEROは、将棋AIと戦えるアプリです。初心者から上級者まで、だれでも使えるので面白いです。
![IMG_7369.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630945/a7e6ae89-898a-236b-8392-aa5e26befa56.png)


## 検証開始
AI将棋ZEROをインストールしたら、すぐに対局を開始することができます。

### 初級編

初級者レベルでは、将棋の基本的なルールを理解していれば、ある程度簡単に勝つことができました。

![IMG_7363.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630945/ed037d7a-c441-a277-f148-25f99400d06c.png)![IMG_7364.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630945/142c524f-8d1d-c01f-d72d-b5bed08dcfed.png)![IMG_7370.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630945/0494274e-55be-fb2a-4975-a27bfb664cc5.png)







### 中級編
課題作成期間が一日しかないのでここからは一回ずつで終わろうと思います。

中級者レベルは勝てませんでした。

ルールを覚えてるだけでは全く歯が立ちませんでした。

![IMG_7372.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630945/f2f4dbcb-8abf-a927-61d5-db23ce1fd71a.png)

### 上級編
上級レベルは何もせずに負けました。
![IMG_7371.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630945/a6aed300-b326-b79a-68e0-67a0e2d0f443.png)


## まとめ
将棋AIと対戦してみて、レベルが低い段階では余裕で勝つことができたが、レベルが上がるにつ
 
 れて段々勝つのが難しくなりました。 最高レベルは、手も足も出ない感じでした。やはり人類はAI
 
 に取って食われる存在なんだということを実感しました。AIは便利だし頭がいいので人類が勝つの
 
 は相当きついと思いました。また、この文章をchatgbtに頼んだら一瞬でできるのかと思ったら虚し
 
 くなってきました。しかし、働かないとやっていけないので何とかAIに負けない力をつけようと思
 
 います。


## この後やる人へ
vscodeではなくQiitaのほうにブログを書くテンプレみたいなやつがあるのでそっちでやったほうが

楽です。多分そっちでやるのが正解だと思います。アカウントを作って認証コード受け取って新規

作成みたいなやつでできました。vscodeでホームページ的なものを作るもんだと思って時間を無駄

にしたので皆さん気を付けて。それじゃ頑張って！！
ZIOのスタックセーフの注意点

この記事は ウェブクルー Advent Calendar 2023 10日目の記事です。
昨日は@wc-ymmtさんの「p5.jsを試してみた」でした。

今年一年もScalaのZIOに携わる時間が比較的長かったので、ZIOの再起処理について新しく知ったことを書こうと思います。

本記事のコードに関しては以下のリポジトリのものになります。

https://github.com/hatuda/scala-recursive

# スタックセーフについて
再起関数において以下の様に特に[Trampoline](https://qiita.com/torao@github/items/4b224489c71bf460ad7c)や`@tailrec`などを使わず、コールスタック量を抑えない状態で、深い再起処理となるように引数を設定し、実行すると、`StackOverflowError`が発生します。

```scala:StackOverFlowExample.scala
def getFibonacciNaive(index: BigInt): BigInt =
  if (index <= 0)
    0
  else if (index == 1)
    1
  else
    getFibonacciNaive(index - 1) + getFibonacciNaive(index - 2)
```

`StackOverflowError`はアプリケーションの実行を止めてしまう可能性があるエラーとなり、このエラーの発生は極力抑えなければなりません。`Trampoline`や`@tailrec`を使い、コールスタック量を抑え、再起処理の深さによらず`StackOverflowError`が発生しなくなった状態を**スタックセーフ**と言います。
スタックセーフの定義は以下です。

:::note info
再起処理の深さによらず、`StackOverflowError`が発生しなくなった状態
:::

> Stack safety is present where a function cannot crash due to overflowing the limit of number of recursive calls.

https://www.scala-algorithms.com/StackSafety/


以下の様にZIOのドキュメント内にこのスタックセーフという言葉があるため、ZIOを使った処理内においてスタックセーフであり、再起処理において`StackOverflowError`の発生を心配する必要はないものと考えていました。

> Also like ZIO, ZPure is stack safe, so we can write recursive programs in terms of ZPure without worrying about stack overflow errors.

https://zio.dev/zio-prelude/zpure/#basic-operations

> These defaults help to guarantee stack safety and cooperative multitasking. They can be changed in Runtime if automatic thread shifting is not desired.

https://zio.dev/reference/fiber/fiber.md/#thread-shifting---jvm

# ZIOで`StackOverflowError`が発生する例

ZIOの[Discordチャンネル](https://discord.gg/2ccFBr4)にあった一例ですが、以下の階乗を計算する再起処理を実行すると`StackOverflowError`が発生します。

```scala:SoeRecursionZIO.scala
  private def factorial(n: Int): ZIO[Any, Throwable, BigInt] =
    if (n == 0)
      ZIO.succeed(1)
    else
      for {
        n2 <- factorial(n - 1)
      } yield n2 * n
```

`StackOverflowError`が起こり得る点については気を付けた方が良いと思いました。

# `StackOverflowError`を発生させないための工夫
上記に以下の様に`ZIO.suspend`を付けると`StackOverflowError`を抑制できます。

```scala:RecursionZIO.scala
      for {
        n2 <- ZIO.suspend(factorial(n - 1))
      } yield n2 * n
```

Discordチャンネルへの投稿者の方も詳しい理由などはわからなかったとのことですが、以下のIssueを参考として取り上げていました。

https://github.com/zio/zio/issues/779

以下の引用文から、実際の計算の際ではなく、ZIOのエフェクトの組み立ての際に`StackOverflowError`が発生するのかと考えました。そのため、そのような組み立てを遅延させるような`ZIO.suspend`を使うと`StackOverflowError`が発生しなくなるのかと考えたのですが、詳細をご存じの方がいましたらコメント頂けると幸いです。

> My intuition for why the SOE happens is: the SOE happens during the construction of the monadic computation, not during evaluation. To prevent that, the construction must be suspended.

# まとめ

ZIOを使った再起処理の場合でも`StackOverflowError`が発生する可能性は考慮する必要があるかと思いました。

明日は@wc-takaharaさんの「TypeScriptのふわっとしている部分を再確認」です。
【Unity初歩】~Physicsを使ったプレイヤの移動~

# はじめに
Pysicsをオブジェクトに適用させる方法を学んだので、Pysicsを使ったジャンプと横移動を実装してみます。

前回の記事

https://qiita.com/usomaru/items/328ef9f64e56993d25d1

# Pysicsを使ったオブジェクトの移動
最初に実装したオブジェクトの移動はオブジェクトの座標を更新するというものでした。
ですが、Pysicsを使ったオブジェクトの移動は、「オブジェクトに力を加える」ものになります。

# ジャンプをさせるスクリプト

🦀オブジェクトに以下のスクリプトを追加してみます。

```c#
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    Rigidbody2D rigidBody2D;
    float jumpForce = 680.0f;

    // Start is called before the first frame update
    void Start()
    {
        this.rigidBody2D = GetComponent<Rigidbody2D>();
    }

    // Update is called once per frame
    void Update()
    {
        // ジャンプ
        if (Input.GetKeyDown(KeyCode.Space))
        {
            this.rigidBody2D.AddForce(transform.up * this.jumpForce);
        }
    }
}
```

`AddForce`…オブジェクトに力を加えられるメソッドです。(そのまんまですね、すみません。)


スクリプトがかけたらオブジェクトにアタッチします。

動かしてみると、ジャンプができました！
すごいジャンプ力でふわふわと帰ってきます。

# 重力の調整
Rigidbodyにかかる重力の大きさをGravity Scaleで調整できます。
数字が大きいほど重力が強くなります。ので、「3」に変更してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/3616ac95-1f58-d67f-ba8c-6aae4efa3112.png)

設定する前よりもジャンプの位置が下がり、より重みを感じる現実的なジャンプになりました。

# 左右移動させるスクリプト
横移動ができるように、🦀オブジェクトにさらに以下のスクリプトを追加してみます。

```c#
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    Rigidbody2D rigidBody2D;
    float jumpForce = 680.0f;
    float walkForce = 30.0f;
    float maxWalkSpeed = 2.0f;

    // Start is called before the first frame update
    void Start()
    {
        this.rigidBody2D = GetComponent<Rigidbody2D>();
    }

    // Update is called once per frame
    void Update()
    {
        // ジャンプ
        if (Input.GetKeyDown(KeyCode.Space))
        {
            this.rigidBody2D.AddForce(transform.up * this.jumpForce);
        }

        // 横移動
        int key = 0;
        if (Input.GetKey(KeyCode.RightArrow)) { key = 1; };
        if (Input.GetKey(KeyCode.LeftArrow)) { key = -1; };

        // 速度
        float speed = Mathf.Abs(this.rigidBody2D.velocity.x);

        // スピード上限
        if (speed < this.maxWalkSpeed)
        {
            this.rigidBody2D.AddForce(transform.right * key * this.walkForce);
        }

    }
}
```

`Mathf.Abs`…絶対値を返却する
`velocity`…速度ベクトル


動かしてみると…
なんか転がる…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/b438f351-053e-1e12-ea63-79f4df73a836.png)


# 🦀が転がらないように工夫する

やったことは2つです。

### コライダの形を円から楕円に変える

Cicle Collider 2Dを選択していたので、Capsule Collider 2Dに変えて、形も🦀に近づくように少し整えました。

### Freeze RotationのZにチェックをつける
Freeze Rotationは指定した軸まわりの回転を防止できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/0ec844d4-fca6-b6e3-2d0b-bdf909f3ceac.png)

これで純粋な横移動ができるようになりました。

# おわりに
転がった時は焦りましたが、横移動も無事にできました。
重力のかかり具合やらを考えながら作るのはやはり大変ですね。
【VueRouter】URLを直接入力して画面遷移するのを防ぐ

`①ユーザー認証画面`=>`②パスワード変更画面`のようにステップを踏んで画面遷移するようなフローがある場合、`②パスワード変更画面`に直接遷移されては困ります。VueRouterで提供されている機能を利用することで、これを防ぐことができます。

## 目標
- URLを直接入力して遷移することを防ぐ

## 前提・バージョンなど
- VueRouter4を使用

## 実装
VueRouterが提供する`START_LOCATION`という定数をインポートして利用します。この定数には、「Vue RouterがどのURLから始まるか」という情報が入っています。「アプリケーション内で、VueRouterが管轄している一番最初のルート」みたいなイメージです。多くの場合は、以下のようなオブジェクトが格納されています。
```javascript
{
    "path": "/",
    "params": {},
    "query": {},
    "hash": "",
    "fullPath": "/",
    "matched": [],
    "meta": {}
}
```
直接遷移されてほしくないルートに、`beforeEnter`を追加します。第二引数の`from`には、遷移前のルート情報が入ります。画面に直接URLを入力して遷移した場合、`from`には`START_LOCATION`と同じ状態のオブジェクトが入ります。
これを利用して、`from`が`START_LOCATION`と同じ場合には、別の画面に遷移させるような処理を追加します。
```javascript
{
  path: "/reset-password/:userId",
  name: "ResetPassword",
  component: () => import("@/views/Users/ResetPassword.vue"),
  beforeEnter: (_, from, next) => {
    if (from === START_LOCATION) {
      next({
        name: "ResetPasswordUserName", // 遷移先のルート名
      });
    } else {
      next();
    }
  },
},
```
これで、ブラウザでURLを直接入力して遷移することができないようになりました👏

:::note warn
この画面にいる状態で、画面をリロードすることもできなくなる点にご注意ください。
:::
2023 年の cybozu developer network リニューアルを振り返る

## はじめに
今年 2023 年は、cybozu developer network にとって2度の大きなリニューアルがあり、変化の年になりました。その内容について振り返ります。

## 2023年2月：1度目のリニューアル
このリニューアルでは、次の内容をおこないました。
- URL の変更
  - トップページ https://developer.cybozu.io/ から  https://cybozu.dev/ へ
- ロゴの変更
- トップページリニューアル
- API ドキュメントの刷新
- チュートリアルの刷新
- プラットフォームの一部変更
  - Zendesk から Hugo へ

足掛け3年のプロジェクトで、なかなか骨が折れました。
もともとはコンテンツのリニューアルのみが対象でしたが、
プラットフォーム変更の要件が入り、トップページも開発者だけでなくエンジニアに向けて訴求していきたいという構想が入り、スコープがお化けに。
フェーズ分けすることで、2月のリニューアルでは API ドキュメントと周辺ドキュメントのみが対象となり、2023年いっぱいかけて、プラットフォームを完全移行していくスケジュールを引きました。

![スクリーンショット 2023-11-30 16.21.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150040/66b41046-e0f3-a39e-3f62-ee739c843ee2.png)
ざっくりしすぎているが逆に守りやすかった最終版スケジュール

## 毎週2時間、 API ドキュメントの議論

毎週2時間とって、プロジェクトの議論チームドキュメントの修正方針をあーでもないこーでもないと語り…
がらっと変更した JavaSript イベントドキュメントのあたりは、3ヶ月間以上議論していました。
議論チーム（=方針決めするチーム）と執筆チーム（=方針にしたがって鬼のように書くチーム）を完全に分けたことが、結構よかったなと思っています。

![スクリーンショット 2023-11-30 16.21.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150040/93cbe1d6-3122-ab7e-bb95-9978f36c462c.png)
こんな感じでカテゴリ構成や記事の細かい表現等も検討していました

## プラットフォームの移行

1度目のリニューアルの大きな柱のひとつとなったのが、プラットフォーム変更に着手したことです。
Zendesk のみの構成から、GitHub + AWS + Hugo の構成に移行しました。
文章やコードの構文を CI で自動チェックしたり、GitHub で変更管理できるようになったりして、品質や保守性が劇的に向上。また、マークダウンで記事を書けるようになって、とても楽になりました。

![スクリーンショット 2023-11-30 16.21.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150040/64828932-e49e-e371-94b8-5e2c8b0f642c.png)
構成図

## 2023年2月：2度目のリニューアル
このリニューアルでは、cybozu developer コミュニティのプラットフォームを Discourse というサービスに移行するというリニューアルをしました。

このフェーズで苦労したのはデータ移行です。
2014年からの投稿データ、どこまで残す・・？ という話になり、コツコツ回答した人にとっては全部消えたら悲しいよね、ということで残すことに。（悲しいからっていうのも何ですが）
それにこれらのデータは Google 検索でヒットしたりするので、有用なナレッジとしても消すにはしのびない。そこで、「Legacy Account」という prefix を付与したアカウントの投稿として、旧プラットフォームのデータもすべて移行しました。

## 2023年12月：最終局面
2度目のリニューアル以降、チームは何をしているのか？ というと、
まだ旧プラットフォームに残っていた主に Tips の記事を、せっせと人海戦術で新プラットフォームに移行しています。
その記事数、およそ400・・・。ある程度ツールで自動で変換もできるものの、手での調整も必要する細かい作業になっています。

チームのもくもく作業時間にはなぜかオルゴールのBGMをかけるお約束になっているのですが、みんなオルゴールを聴くとひとりでに手がマークダウンを書き出すと思います（笑）。

12/15頃を目処に、残ったコンテンツのプラットフォーム移行を完了する予定です。
クリスマスを迎える頃にはすでに終わっているはず！

## おわりに
cybozu developer network は、来年 2024 年でついに 10周年を迎えます。 cybozu developer network の次なる飛躍にご期待ください！
OSC京都2023で秋葉原ロボット部の展示を担当した話

## はじめに
これまでオンラインで参加していた秋葉原ロボット部がOSC京都2023でワークショップを開くということでしたので、展示担当者として参加しました。

## OSC京都2023
OSC京都は、2023年7月22日(土)に京都リサーチパーク(KRP) 東地区 アトリウムで開催されました。
展示を行った出展団体は、協賛企業・団体、参加企業・団体それからコミュニティに分類されます。
秋葉原ロボット部が分類されるコミュニティ枠での出展団体数は25団体でした。

秋葉原ロボット部の展示場所では、温調搭載のはんだごてキットと栽培プラントの自動給水システムの展示、およびはんだ付け体験コーナーが設置されました。
この展示ブースを、kazuedaを含め、3人で担当しました。
kazuedaは、もっぱらぼんやりとした説明担当で、困った時には、nunbwksさんに助けを求めるという状況でした。

展示されたものの中には頒布されるものもあり、展示の目玉のはんだごてキットや自動給水システムのキットの購入者も何人かおられました。

## 参加者も楽しいけど、出展者はもっと楽しい

OSCへの参加は、浜松で行われていた時期に参加者として参加していました。
その頃は、講演を聞いていることが多く、参加者との交流が深まりにくい状況でした。
どちらかというと、情報収集で展示の説明を受ける感じだったと思います。

今回、出展者側で展示を担当しましたので、展示ブースを訪問した参加者との交流ができましたし、同じ展示ブースを共有した展示者間の連携もできたような気がします。

## おわりに
コロナ以前に想像していなかったコミュニティ活動を2023年に体験しました。
2024年度は、参加しているコミュニティのお手伝いだけでなく、自分自身が出展者になる経験をしたいなと思います。
また、現在は国内でのコミュニティ活動だけですけど、海外での活動もできればと考えています。

最後に、展示担当をご快諾いただいた秋葉原ロボット部のNanbwksさんをはじめ、勉強会をご一緒したロボット部の部員方々に感謝いたします。
DockerでMySQLとDB管理ツールのAdminer or phpMyAdminを実行する

# DockerでMySQLとDB管理ツールのAdminerを実行する
## docker-compose.ymlを作成
```
version: '3.8'

services:
  mysql:
    image: mysql
    restart: always
    ports:
      - 33306:3306
    environment:
      - MYSQL_ROOT_PASSWORD=mysql
      - MYSQL_DATABASE=test_db
      - MYSQL_USER=user
      - MYSQL_PASSWORD=password
    command: ['--default-time-zone=+09:00']
    volumes:
      - ./mysql-data:/var/lib/mysql
  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080
    depends_on:
      - mysql
```
DB管理にAdminerではなくphpMyAdminを使用したい場合は「adminer」を下記に書き換える
```
  phpmyadmin:
    image: phpmyadmin
    restart: always
    ports:
      - 80:80
    depends_on:
      - mysql
    environment:
      - PMA_ARBITRARY=1
      - PMA_HOSTS=mysql
      - PMA_PORT=3306
      - PMA_USER=root
      - PMA_PASSWORD=mysql
```
## 起動コマンド
```
docker-compose up -d
```

## 停止コマンド
```
docker-compose down
```

## Adminerへのアクセス
[http://localhost:8080](http://localhost:8080)へアクセスすると下記画面が表示される

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/361770/460a1b48-4ad8-cdf1-b660-ac1514af8f25.png)

ログインすると下記画面が表示される
![タイトルなし.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/361770/39901c03-c481-0995-46d8-4fbc09242265.jpeg)


# コンテナへのアクセスについて
- ローカルからのアクセス先は「localhost」になる

# コンテナのポートについて
- ポートは「ローカルの転送元：Dockerへの転送先」になる
  - DockerのサービスAとサービスBのポートが被っている場合は、下記のようにポートを設定しておけばそれぞれのサービスに転送される
    - サービスAのポート：10080:80
    - サービスAのポート：10081:80

# コンテナ間の連携について
- コンテナ間の連携はdocker-compose.ymlに記載されたサービス名でアクセスが可能になる
  - adminerサービスからはmysqlサービスへは「mysql:3306」でアクセスする

# データの永続化
- mysqlサービスのvolumesを指定していない場合、停止するたびにDBのデータが初期化される
- volumesの指定は「ローカルの保存先フォルダ:Dockerの保存元フォルダ」になるため、ローカルの「mysql-data」にmysqlのDBデータが保存される

# イメージのバージョン指定
- 「mysql」のバージョンを指定したい場合、「mysql:8.0」のように指定する

# 「restart」とは？
- 「always」指定でコンテナでエラーが発生した場合に自動で再起動する

# 「depends_on」とは？
- 起動順序を指定する
  - adminerサービスに「depends_on: - mysql」が指定されているのでmysql→adminerの順で起動される
ブラックフライデーに1枚の基板を約30円で作製した話

## はじめに
基板製作会社のJLCPCBからブラックフライデーの案内が来ました。
10センチ×10センチより小さい基板だと、2，3日の製造で4ドルのところが2ドルですみました。
（でも、ブラックフライデー以降でも2ドルのままです？）
郵送料が98セントの配送システムを選んだので、結果、3ドル未満で基板が作製できたことになります。

11月18日に注文した基板が、11月26日に一斉に届きましたので、趣味で作る基板だと、それほど待ち時間を感じません。

その中の注文に、8ドルを超える基板が含まれていました。

## クーポンの威力
複数種類の基板注文の中に、複雑な形状だったのか、「Engineering fee」が追加されて8ドルを超える製作コストの基板がありました。
輸送方法も1.98ドルが最安で、結構値段がかかるので、画面を眺めながら注文するかどうか思案していました。
その時、７ドルのディスカウントクーポンのボタンが現れ、すかさず、このクーポンを適用して、表題の価格で基板の作製が可能となりました。

## 終わりに
JLCPCBで類似の基板を作成するとき、「Special Offer」が出るときと出ないときがあって、戸惑うことがあります。
一方で、今回のようにスペシャルクーポンが突如現れたりします。

ブラックフライデー期間のバナーで出てくるクーポンには、5ドル以上の注文何にでも使える、2ドルディスカウントクーポンと、20ドル以上の基板作製で5ドルディスカウントのクーポンがありました。
これら以外のクーポンもあるとわかり、色々な組み合わせで、お得なクーポンを探す楽しみができました。

ただ、「SAVE TO　CART」ボタンを押すと、アップロードしたファイルが消えてしまいました。
いろいろなデータの組み合わせでお安く買おうと検討していると、My Filesに再度ファイルのアップロードする必要となります。
この辺りの対応を考えないと、クーポン探しが面倒になりそうです。
Epub Reader で Amazon S3 に格納されている Epub ファイルを読み取る

## はじめに
はじめまして。
今日は、Bibi Epubリーダーを利用して、Amazon S3 に格納されている Epub ファイルを表示する機会があったので、その際の知見を共有しようと思います。

## なぜAmazonS3にファイルを格納するの？
コストや可用性などの面でメリットは多くありますが、AWSのベストプラクティスなのです。（[AWS ホワイトペーパー](https://docs.aws.amazon.com/ja_jp/whitepapers/latest/best-practices-wordpress/static-content-offload.html)にも記載されてますのでご参考ください。）

## 問題#1：The Origin of the Path of the Book Is Not Allowed

![#1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3622245/db369649-5f6c-4b4b-2d25-69b57861a323.png)

Bibi EPUB リーダーはOrigin-Pathが許可されないと電子書籍を読み取ることができません。

### **対応方針：**
Bibi Epubリーダー のソースの中に `bibi/presets/default.js` ファイルを編集します。
143 行目に `trustworthy-origins` の項目があり、ここに S3 バケットの URL を入力すれば、解決できます。

![#2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3622245/f18c8319-4b59-215d-056f-c4ea8fc8709d.png)

## 問題#2：Failed to open the book (probably CORS Blocked)

![#3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3622245/4b948d48-a063-97cf-0ed9-1def3dc4d3c9.png)

### **対応方針：**
クロスオリジンのため、S3 バケット側にCORS設定で解決できます。

CORS 設定例は以下でご参照ください。
```json:CORS
[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET"
        ],
        "AllowedOrigins": [
            "https://yoursite"
        ],
        "ExposeHeaders": [],
        "MaxAgeSeconds": 3000
    }
]

```

## 問題#3：Data Invalid as a folder: 403 Forbidden

![#4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3622245/256fbb20-36d4-84e0-2d9a-370c80af48b6.png)

このエラーは、S3バケットにある電子書籍が解凍してないため、Bibi EPUB リーダーで読み取ることができないことを示しています。

### 1. 電子書籍の自動解凍
S3 イベントのトリガーを追加して、Lambda 関数経由で自動的に電子書籍を解凍し、元の S3 バケットへ再アップロードします。

### 2. コンテンツタイプ
S3 バケットへ解凍した電子書籍をアップロードするとファイルのContent-Typeは `binary/octet-stream` になります。XMLやJPG等のファイルも含まれますが、これらは `text/xml`、`image/jpeg` になります。
正しいコンテンツタイプを設定しないと読み取れません。

サンプルコードはページの最後にある［参照リンク］をご参照ください。

### 3. 結果
やっと電子書籍が読みと取れました。👏
![ok.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3622245/553a8d3e-d477-9e95-58bf-832cc4bd7d95.png)

## 最後に
もし上記の問題を解決できなければ、コメントいただけると非常に助かります！
 最後までご覧いただいてありがとうございました。

## 参照リンク
* Bibi ｜Epub Reader：https://bibi.epub.link/
* 電子書籍の解凍のサンプルソースコード：https://github.com/ci-team-cwa/bibi-epub-read-from-s3/blob/main/lambda/lambda_function.py
大体０から始めるRuby入門その１　~Ruby、動く~

この記事は[【完走したい】楽しくいろいろやる Advent Calendar 2023](https://qiita.com/advent-calendar/2023/notbeginner)の1日目です。
<b>注意:学んだ内容に間違いがあることがあります。何か間違っていたら教えてください。勉強になります。</b>

## 去年のカレンダーのこと
去年ミカンのプロジェクトは今年リベンジしようと10日間試行錯誤しましたが、うまくいかなかったのであきらめて次行きます。
ついでに復習もします。もちろん毎日新しい知識を学び、アウトプットします。

:::note info
目標：いろいろ新しいことにチャレンジする。
:::
↓以下本題

Rubyは６月ごろに一回触ったことがありましたが、その後全然触ってなかったので知識をつけていきたいと思います。
今回はとりあえず動かすことを目標にしておきます。
# Rubyとは
↓復習
Webアプリケーションとかを作るのに適しているようです。
ただし入門である今回は、そんなに知識がないのでとりあえず何か動けば良しということで。

# Rubyを動かすには
##### 1.VisualStudioCodeでコードを書く
##### 2.Rubyを使って動かす

こんなかんじです。
VisualStudioCodeでRubyを動かすことはできない？感じでした。
2のRubyは[ここ](https://rubyinstaller.org/downloads/)からインストールできました。
いろいろやれば、Rubyが入ります
Rubyを入れた後はPowershellとかからRubyのプログラムを動かせます。

# Rubyの書き方
C#とは違い、終わりに;をつけないのが基本みたいです。
試しに1+1の計算をしてみます。
```
# Rubyについて知る１（AdventCalender2023用）
A =100
B=3
print(A+B)
```
こんな感じに書くみたいです。
１行目の#の後に書かれているのはコメントアウトしているだけなので気にしないでください。
AとBにそれぞれ数字を入れて、A+Bしたものを出力させました。
ほかの言語と違うのは、intとかstringとかを明記しなくてもいい点です。
例えば本来float型の小数とかも行けます。
実行結果
```
C:\Users\xxxxx\Desktop>ruby Advent2023.rb
103
C:\Users\xxxxx\Desktop>ruby Advent2023.rb
103.2
```
(名前が入っていたので一部伏字にしました。)
一回目の結果が整数の足し算、二回目が小数点を含んだ足し算です。
Aを100.2とかにすれば、ちゃんと103.2と出してくれます。非常に便利です。

# ほかのプログラムを参考にできるか
ほかのrubyのプログラムの中の数や文字を参照したいこともあるでしょう
こんな時は'require'を使うようです。

一つ目の文字列を置いておくほうに
```
class Test
  def Cint
    puts "Test1"
  end
end
```
'Test'というクラスの中に'Cint'を定義(def)して
二つ目の文字列を参照する用
```
require './Advent2023'

C=Test.new
C.Cint
```
まずrequireで一つ目のものを参照し、
Cに一つ目のほうのClassである'Test'を入れて、
Testが入っているCの、Cint(一つ目のやつ)を呼び出す使いかたをするようです。
結果は、
```
C:\Users\xxxxx\Desktop>ruby Advent2.gemspec
Test1
```
ちゃんとTest１を参照できました。

# まとめ
Rubyの使いかた、ほかのファイルの読み込みを行うための'require'の使いかたを学びました。
結構使いやすいです。

Webアプリケーションを作れるよう頑張ってみようと思います。

参考

https://prog-8.com/docs/ruby-env-win

https://www.sejuku.net/blog/107088

https://techtechmedia.com/require-ruby/
LangChainのSparkデータフレームエージェント

今度はこちらのノートブックをウォークスルーします。

https://docs.databricks.com/ja/large-language-models/langchain.html#spark-dataframe-agent

以下のコードでは、Sparkデータフレームエージェントのサンプルを説明します。

## 要件

- このノートブックを使うには、お使いのOpenAI APIトークンを指定してください。
- Databricks Runtime 13.3 ML以降

# ライブラリのインストール

最新の`langchain`と`databricks-sql-connector`を使うことをお勧めします。

:::note
**注意**
もとのノートブックのままではエラーとなるので`langchain_experimental`を追加します。
:::

```py
%pip install --upgrade langchain databricks-sql-connector langchain_experimental
dbutils.library.restartPython()
```

```py
import os
os.environ["OPENAI_API_KEY"] = "<OpenAI APIキー>"
```

# Sparkデータフレームエージェント

このSparkデータフレームエージェントによって、必要に応じてSparkデータフレームを操作できるようになります。シンプルに、LLMと対象のデータフレームを指定して`create_spark_dataframe_agent` をコールするだけです。

```py
from langchain.llms import OpenAI
#from langchain.agents import create_spark_dataframe_agent
from langchain_experimental.agents.agent_toolkits import create_spark_dataframe_agent

df = spark.read.csv("/databricks-datasets/COVID/coronavirusdataset/Region.csv", header=True, inferSchema=True)
display(df)
```
![Screenshot 2023-11-30 at 17.15.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/af98a165-d202-24df-3a3a-b737d6f34269.png)

```py
agent = create_spark_dataframe_agent(llm=OpenAI(temperature=0), df=df, verbose=True)
```

問い合わせます。

```py
agent.run("How many rows are there?")
```
```
> Entering new AgentExecutor chain...
Thought: I need to find out how many rows are in the dataframe
Action: python_repl_ast
Action Input: df.count()
Observation: 244
Thought: I now know the final answer
Final Answer: There are 244 rows in the dataframe.

> Finished chain.
'There are 244 rows in the dataframe.'
```

OpenAIなので日本語でもOKです。

```py
agent.run("何行ありますか？")
```
```
> Entering new AgentExecutor chain...
Thought: I need to know how many rows are in the dataframe
Action: python_repl_ast
Action Input: df.count()
Observation: 244
Thought: I now know the final answer
Final Answer: データフレームには244行があります。

> Finished chain.
'データフレームには244行があります。'
```

### Databricksクイックスタートガイド

[Databricksクイックスタートガイド](https://www.amazon.co.jp/dp/B09V1YXFVQ/)


### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)
Slack経由でNotionナレッジをChatGPTっぽく回答したい

こんにちは！株式会社Hubbleの藤井と申します。今回 [Hubble Advent Calendar 2023](https://qiita.com/advent-calendar/2023/hubble)と題して、初のアドベントカレンダーに挑戦することとなりました。Hubbleのサービス開発の裏側で、どんなことをしているの？が少しでも伝わると良いなと思います！

# Notion x OpenAI x Slack
HubbleではナレッジデータベースとしてNotionを活用しています。

ドキュメント文化の啓蒙活動を日々定期的に行っており、情報の属人化を防ぐ事を目指しています。とはいえ、日々多忙な業務の中で、ドキュメント化するのも簡単ではなく。。。ということで、ドキュメント化することの大切さをより実感してもらうために、エンジニアとして何か貢献できないかと思い、本記事で紹介するような取り組みを行いました。

- Notionで貯めたナレッジをChatGPT風に問い合わせができるようにする
- Slackコマンドとして提供することで、自らナレッジを探す習慣を形成
- 業務内の疑問がすぐに解消できる体験を提供
- Notionでナレッジを貯めることの大切さを実感してもらう

本記事では簡単なシステム構成と、実際に運用してみた感想を述べています。（詳細な実装方法に関しては後ほど公開予定、、、）

## システム構成

- Azure Subscription
- OpenAI (社内データを扱う際はAzure OpenAIの使用をお勧めします。)
- CognitiveSearch (今回はデータをCognitiveSearch内に格納しました。)
- Notion API (Notionから記事データを取得します。)
- AWS (Notionからの記事取得等はLambdaを定期実行することによって行いました。)
- Slack, Github…等
    
![スクリーンショット 2023-10-13 10.33.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109324/805c1db0-f6c2-7fde-fdd9-20b5940ce389.png)


構成は大まかに上記のようにしました。

Notionにはナレッジ以外にも個人のメモ等、検索対象にされたくないものもあるのでNotion Databaseに随時登録してもらうスタイルを取りました。

![スクリーンショット 2023-10-13 10.38.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109324/f1d763c8-2aa5-4f3a-f4bc-14a63d1fb4b0.png)

結果として以下のようなものが出来上がりました！

![スクリーンショット 2023-11-30 16.59.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109324/b3c6b10b-a33b-f1bb-1767-453696005baf.png)

ぱちぱちぱち。👏


## まとめ(感想)
Hubble社でも実験中で、まだまだ運用が軌道に乗っているとは言い切れませんが、作ってみて感じたことをざっとまとめます。

- 作った時の周りの反応は気持ちいい（すげーーー）
- Slackのチャンネルが汚れる（専用チャンネルを作ることになりましたw）
- ChatGPT周りはアップデートスピードが尋常じゃないので、もっと簡単に作れるようになるかも？
    (Notion X OpenAIもありますが、Notion Database経由で登録データを管理できることはメリット)
- 結局何を作っても、運用力が全て（啓蒙活動頑張ります。。）

Hubbleでは、こんな感じで色んな技術に挑戦したり、何かを作ってみたり、、、と自由に開発できる環境が揃っております！第二回目の投稿は、Hubbleが誇るAngularマスター [@KOHETs](https://qiita.com/KOHETs) です！
terraformのimportブロックを使ってみての感想

メタップスアドベントカレンダー二日目の記事です。

terraformのバージョン1.6が出て約2カ月が経ちました。
今回のメジャーアップデートの注目ポイントとして、importブロックがありました。
こちらを使用してみての感想を書いてみます。
正直舐めてました。

# importブロックとは
terraformにはimportという機能があります。terraformの管理下に無いリソースを管理下に置きたい場合使用します。
importブロックとは、`terraform import`コマンドと同等のことをコードに書けるというものです。
importブロックは
```
import {
  to = xxx.xxx
  id = yyy
}
```
のように記載し、importコマンド
```
terraform import xxx.xxx yyy
```
と同じ結果になります。

# 利点
利点としては、
- importのし忘れを防げる
- importしていない状態でコードとの差分が見られる
- 実際にimportされるのはapplyコマンド実行時

が挙げられます。

### importのし忘れを防げる
apply前にimportをしなければならず、その作業を忘れてしまう可能性は大いにあります。
それを防ぐことができるのは大きな利点と言えそうです。

### importしていない状態でコードとの差分が見られる
importコマンドの場合、実際にimportしなければコードとの差分を確認することはできませんでした。
しかし、importブロックを使えば、実際にimportしていないにも関わらず差分を確認することができます。

### 実際にimportされるのはapplyコマンド実行時
importコマンドの場合、import作業とapply作業には多少なりとも間が空いてしまいますが、importブロックではimportとapplyが1操作でほとんど時間を開けず実行できます。

# 欠点
欠点としては、
- 一度applyしてしまうと不要なブロックになってしまう
- terraform importコマンドと大して手間は変わらない

が挙げられます。

### 一度terraform applyしてしまうと不要なブロックになってしまう
一旦applyしてしまえば不要なコードになってしまうため、どこかのタイミングで消した方が良いです。

### terraform importコマンドと対して手間は変わらない
importブロックによって劇的に手間が省けるようになるかと言われると、特にそういう訳では無いです。
むしろ後々ブロックを消す手間の分余計な作業が増えてしまいます。

# ユースケース
importブロックの恩恵を一番受けられる場面は
「__複数人が並行でコードを変更している__」
時です。

terraform importコマンドの最も怖い部分は、importした後コードに適切な変更が加わらないままapplyしてしまうところだと思います。
これはimportしたリソースを意図せず削除もしくは変更してしまうことになってしまうので避けなければなりません。

ただ、複数人が同時にコードに手を加えている場合、
1. Aの変更のためにterraform importコマンドを実行
2. Bの変更を適用する
3. Aの変更を適用する

のような順番になってしまうとimportしたリソースが削除されてしまいます。
しかし、importしなければコードとの差分を見れないため、importとコード修正をほぼノータイムでできる場面は限られます。

また、PRをmergeしたタイミングで自動で変更が適用されるようなパイプラインを組んでいる場合、他の人がimportしていることに気づかずmergeしてしまうことも十分あり得ます。

このような場合、importブロックは有用です。

実際経験したこととして、リソースをimportしてPRを作成している間に緊急リリースをしなければならない時があり、慌てて緊急リリース用のPRをmergeしたところimportしたリソースが消えるという事がありました。

# できるなら…
`terraform state rm`コマンドもコード化できるようにしてほしい

# まとめ
importブロックは複数人で開発している場合とても便利なものであることがわかりました。

# 感想
最初この新機能を知った時、大して手間が変わらないのになぜこの機能が実装されたのか疑問でしたが、使ってみるとなるほどと唸りました。正直舐めてました。
(リリースノート読めよというツッコミはなしでお願いします)
[チュートリアル4]compose multiplarformのサンプルコードを変更して勉強する

# はじめに
[公式ドキュメント](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-modify-project.html)の和訳と要約＋αです。
前回は[これ](https://qiita.com/shunk0616/items/38177619d4d771685d7e)

# ライブラリを導入する
Kotlin Multiplatformのライブラリを導入する場合には、composeApp/build.gradle.ktsをいじります。
そういうライブラリがないときにはそれぞれのプラットフォーム固有のライブラリを使わないといけないらしい。（難しそう）

ちなみにKotlin Multiplatformのライブラリ一覧をまとめてくれているサイトもあります。
[Kotlin Multiplatform libraries](https://libs.kmp.icerock.dev/)

## 手順
1. composeApp/build.gradle.ktsを開く
2. commonMainのdependencies{ ... }の中に追加したいライブラリを追加
3. 右上に出てくる"Sync Now"ボタンを押す

今回の例だとbuild.gradle.ktsの56行目より下あたりに追加すれば良いです。
コードはこんな感じになっているはず
```
     val commonMain by getting {
            dependencies {
                implementation(compose.runtime)
                implementation(compose.foundation)
                implementation(compose.material)
                @OptIn(ExperimentalComposeLibrary::class)
                implementation(compose.components.resources)

                // 下を追加
                implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.1")
            }
        }
```

ちなみに追加したライブラリはkotlinx-datetimeというもので、時間を扱うときに使うやつです。
[参考](https://github.com/Kotlin/kotlinx-datetime)


# 追加したライブラリを使ってみる
ライブラリを追加したので、使ってみます。

まずは追加するべきファイルを開きます
プロジェクトのルート/composeApp/src/commonMain/kotlin/App.kt

## 日時を取得する処理を追加
そしてApp()の中に以下のコードを追加します。ファイルの一番下で大丈夫です。
```
    fun todaysDate(): String {
        fun LocalDateTime.format() = toString().substringBefore('T')

        val now = Clock.System.now()
        val zone = TimeZone.currentSystemDefault()
        return now.toLocalDateTime(zone).format()
    }
```

それに伴ってimportも追加します。
ファイルの一番上に下のやつを追加してください。
```
import kotlinx.datetime.Clock
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
```

## 追加した処理を呼び出す
今回はColumnの中に入れてみましょう。
画面のほぼ全体を覆うcolumnの一番上に、先ほどの日時取得処理で取得したデータをデータを表示するテキストウイジェットを入れます。

具体的には、28行目あたりですね、Buttonウイジェットの上の方に挿入します。
挿入するコードはこれです。

```
Text(
    text = "Today's date is ${todaysDate()}",
    modifier = Modifier.padding(20.dp),
    fontSize = 24.sp,
    textAlign = TextAlign.Center
)
```

それに伴いimportも追加します。
```
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
```

これで画面の上の方に現在の日付が表示されているはず。


## 補足：自動import 
ここまででimportを追加するのがめんどくさいと感じた方は、[わかりやすい記事](https://codeforfun.jp/android-studio-how-to-enable-auto-import/)があるのでこれを参考に自動import設定を有効化してください。

ちなみに私はimportしていくのが好きなので有効化してません。

# 参考にしたもの
https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-modify-project.html#rerun-the-application

https://codeforfun.jp/android-studio-how-to-enable-auto-import/

# 次
[次はこれ](https://qiita.com/shunk0616/items/049578112719640d8451)
[チュートリアル3]compose multiplatformのサンプルコードを解説する

# はじめに
[公式ドキュメント](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-explore-composables.html)の和訳+αです。
[前回はこれ](https://qiita.com/shunk0616/items/0ee8f513f953fed468fd)

そしてjetpack composeの基本的な解説になるので、compose multiplatformの解説って感じじゃないですね。知ってる方は飛ばしてください。

一応コードを貼っておきます
```
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.painterResource

@OptIn(ExperimentalResourceApi::class)
@Composable
fun App() {
    MaterialTheme {
        var greetingText by remember { mutableStateOf(GREETING) }
        var showImage by remember { mutableStateOf(false) }
        Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
            Button(onClick = {
                greetingText = "Compose on ${getPlatformName()}: $GREETING"
                showImage = !showImage
            }) {
                Text(greetingText)
            }
            AnimatedVisibility(showImage) {
                Image(
                    painterResource("compose-multiplatform.xml"),
                    null
                )
            }
        }
    }
}
```

# App()関数の構造について
解説するファイルのパスは以下です。
> アプリのルートディレクトリ/composeApp/src/commonMain/kotlin[commonMain]/App.kt

このアプリの最初に開くページを定義しているファイルがApp.ktです。
重要なのは以下です。

### MaterialTheme
21行目あたりですね
アプリケーションの見た目を設定します。色や形、フォントなどをカスタマイズできます。


### Column
その中に入れたものを縦並びに表示するwidgetですね。
ButtonとAnimatedVisibilityを上下に配置していますね。

### Button
ボタンですね。中にはTextウィジェットを持っていて表示する文字列を決めています。
この例ではgreetingTextを表示しており、greetingTextの中身はそれぞれのプラットフォームを取得してその名前を表示してます。

### AnimatedVisibility
アニメーションを使ってImageを表示したり隠したりします。
引数がtrueなら表示、falseなら非表示しています。
今回の例だとshowImageを渡していて、その状態によって表示非表示が切り替わるようになっています。


### painterResource
XMLリソースに保存されたベクターアイコンをロードします。

### Modifier
Columnに渡しているやつです。（24行目あたり）
見た目を細かく調整することができるものです。前のメソッドから渡されたmodifierを変更して呼び出すことができます。


# 状態(state)
ユーザーの操作や時間経過などに合わせて、表示するものを変更したいときには、状態（State）を変更してその状態の変更に合わせてUIを再構成（リコンポジション）します。

このコードではgreetingTextとshowImageが状態です。
mutableStateOf()という関数を使用して作成し、remember関数でラップしてあるので一度作成されるとフレームワークによって保存されます。

例えば端末を横向きにして再描画させたりした場合でも同じものが表示されるのは値を保持しているからです。

# 次
[次はこれ](https://qiita.com/shunk0616/items/627a172416530ccade70)
[チュートリアル2]compose multiplatformでアプリを作るとき最初にやること[Compose multiplatform]

# はじめに
[公式チュートリアル](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-create-first-app.html)の和訳と要約＋αです。
前回は[環境構築](https://qiita.com/shunk0616/items/747d651f2a94f6a6ec9c)

# 手順

## Android Studioで開くまで
1. [kotlin multiplatform wizard](https://kmp.jetbrains.com/?_gl=1*rxwzv*_ga*NDQ4NjkzNTk5LjE2OTY2NTYwODQ.*_ga_9J976DJZ68*MTY5OTI3MjEyMC4xMC4xLjE2OTkyNzI4MjYuNTEuMC4w&_ga=2.179882465.432039280.1699264914-448693599.1696656084)に行く
2. project nameに自分の作りたいアプリの名前を入力
3. androidとdesktopのオプションを選択
4. macならiOSのオプションも選択
5. downloadボタンを押す
6. ファインダーでダウンロードしたzipファイルを見つけて解凍
7. 開発フォルダに移動
8. Android Studioを開く
9. Openボタンから先ほどダウンロードしたフォルダを選択する
10. Android Studioの左上のAndroidって書いてあるボタンをクリックして、Projectを選び、全体のフォルダ構成が見えるようにする
![changeview](https://resources.jetbrains.com/help/img/kotlin-multiplatform-dev/select-project-view.png)

## フォルダ構造の簡単な解説
プロジェクトには「composeApp」と「iosApp」の2つのモジュールがあります。
「composeApp」は、Android、デスクトップ、iOS向けの共通ロジックを持つKotlinモジュールです。
「iosApp」はiOSアプリケーションとしてビルドされるXcodeプロジェクトで、共有モジュールを依存関係として使用します。
「composeApp」モジュールには、4つのソースセットが含まれており、それぞれ異なるプラットフォーム向けのコードを持っています。
共通のKotlinコードは、AndroidライブラリとしてビルドするとKotlin/JVMとして、iOSフレームワークとしてビルドするとKotlin/Nativeとして扱われます。
できるだけ特定のプラットフォームに固有のコードを複製するのではなく、共通のコードを使用して実装することが推奨されています。

## アプリを立ち上げるまで
### Androidアプリ
1. Android StudioのDeviceManagerから適当なエミュレーターを立ち上げるor実機をつなぐ
2. run configurationでcomposeAppを選ぶ
3. Runをクリックする

### iOSアプリ
1. Android Studioのrun configuration(右上の方のプルダウン)でiosAppを選択します
2. その右のrunボタン（緑の三角）を押します。

### desktopアプリ
[このエラー](https://youtrack.jetbrains.com/issue/KT-63092/Multiplatform-Cannot-locate-tasks-that-match-composeAppcompileJava)によって公式ドキュメント通りにやったらうまくいきません。

gradleからrunしていきましょう

1. Android Studioの右上にある歯車マークをクリック
2. でてきたプルダウンの中からrun anythingを選択
3. 実行するタスク名を入力する窓が出てくるので以下を入力してEnter

> gradle :composeApp:run

一度やればあとは上のrun configurationから選べば良くなります。

# 次
[次はこれ](https://qiita.com/shunk0616/items/38177619d4d771685d7e)

# 参考にしたもの
https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-create-first-app.html
[チュートリアル1]compose multiplatformの環境構築[compose multiplatform]

# 自分の環境
Intel版macです。OSはsonoma。
友達に頼んで確認してもらったら、同じ手順でM2でもいけたそうです。

# 手順
めちゃくちゃ簡単に書きます。わからないことがあればコメントお願いします。
1. 環境構築
   1. チェック用のツール
      1. kdoctorをインストール(homebrew経由)
      2. kdoctorコマンドで出てきたエラーを潰していく
   2. java
      1. [ここ](https://www.oracle.com/java/technologies/downloads/#java17)からjava17をインストールしました
   3. Android Studioのインストール
      1. Android Studio Giraffe | 2022.3.1
   4. プラグインのインストール
      1. KMM(Kotlin Multiplatform Mobile)のプラグイン
   5. XCode
      1. アプリストアからインストール
      2. 開いて初回のセットアップを行います。(iOSとかMacOSとかを選んだはず)
   6. CocoaPods
      1. [この記事](https://zenn.dev/flyingbarbarian/articles/8253b3a2b42e6c)わかりやすいですね

> kdoctor
Environment diagnose (to see all details, use -v option):
[✓] Operation System
[✓] Java
[✓] Android Studio
[✓] Xcode
[✓] CocoaPods

この状態になってたら完了です。

# 次
次は[ここ](https://qiita.com/shunk0616/items/0ee8f513f953fed468fd)。

# 参考にしたもの
公式チュートリアル: https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-setup.html#install-the-necessary-tools
Kotlin: エラーハンドリング

こちらの記事を参考にしました。
[サンプルコード3：例外オブジェクトの活用](https://jp-seemore.com/app/14686/#toc11)

## プログラム

```kotlin:try_catch.kt
fun main() {
	System.err.println("*** 開始 ***")

	try {
		val number: Int = "abc".toInt()
		println(number)
	} catch (ee: Exception) {
		println("エラーが発生しました。")
		println(ee)
		println("エラーの型: ${ee::class.qualifiedName}")
		println("エラーメッセージ: ${ee.message}")
		println("*** ee.printStackTrace() ***")
		ee.printStackTrace()
	}

	System.err.println("*** 終了 ***")
}

## コンパイル

```text:Makefile
try_catch: try_catch.kt
	kotlinc try_catch.kt -include-runtime -d try_catch.jar
clean:
	rm -f try_catch.jar
 ```

 ```text
 $ make
kotlinc try_catch.kt -include-runtime -d try_catch.jar
```

## 実行

実行コマンド

```bash
java -jar try_catch.jar
```

実行結果

```text
$ java -jar try_catch.jar
*** 開始 ***
エラーが発生しました。
java.lang.NumberFormatException: For input string: "abc"
エラーの型: java.lang.NumberFormatException
エラーメッセージ: For input string: "abc"
*** ee.printStackTrace() ***
java.lang.NumberFormatException: For input string: "abc"
	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
	at java.base/java.lang.Integer.parseInt(Integer.java:661)
	at java.base/java.lang.Integer.parseInt(Integer.java:777)
	at Try_catchKt.main(try_catch.kt:5)
	at Try_catchKt.main(try_catch.kt)
*** 終了 ***
```

## IntelliJ で実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/b3cc28f3-6183-21dd-3bae-d60ed136b24d.png)
【AWS】クラウド

# クラウドコンピューティングとは
仮想環境を利用して、オンデマンドにサーバリソースを提供するサービス。
クラウドコンピューティングの種類として以下がある。

| 種類 | 説明 |
| ---- | ---- |
| パブリッククラウド | インターネット経由で提供される。クラウドサービス提供事業者が構築した環境を他の利用者と共同利用するクラウドサービス |
| プライベートクラウド | 企業が仮想化技術を使用して、自社内に構築・設置するクラウドサービス |
| ハイブリッドクラウド | パブリッククラウドとプライベートクラウドを組み合わせた形態 |

# パブリッククラウドの種類

| 種類 | 説明 |
| ---- | ---- |
| IaaS | 主にサーバリソースを提供。 |
| PaaS | サーバリソースとアプリケーションの土台であるミドルウェアを提供 |
| SaaS | 使用するアプリケーションを提供|

# クラウドの特徴
クラウドでは必要な処理や管理作業を一体化させて**マネージドサービス**として提供している。また、クラウドで提供されるサーバリソースは**拡張性**や**耐障害性**にも優れており、従量課金をベースとしているため、利用コストも最適化できる。

:::note info
拡張性
システム等の使用開始時に大きな変更を伴わずに、機能追加や性能向上ができること
:::

# クラウドのメリット
* コストの最小化
* サーバスペックを変更したり、動的にリソースをスケールできる。
* コスト削減
* 運用負担が低い

# クラウドのデメリット
* オンプレミスのような自由なカスタマイズはできない
* リソースを大量消費した際い費用が膨大になる恐れがある
* サービスによっては必要な機能を利用できない
* セキュリティの脆弱性をついた攻撃にさらされやすい


# 参照
https://www.mdsol.co.jp/column/column_123_976.html
https://www.boxsquare.jp/blog/what-is-cloud
AWSの基本・仕組み・重要用語が全部わかる教科書
エンジニアインターンと研究を両立するテク3選

# はじめに

皆さんは学業とエンジニアインターンの両立、うまくいっていますか？

エンジニアインターンで学業を二の次にしていませんか？

自分は、理系修士2年なので少し研究が忙しめなのですが、うまくエンジニアインターンを両立できているかなーと思うので

学業よりもインターンをしたいと言った方に向けて、参考になるかどうかわからないですが、自分のテクニックを3つご紹介しようと思います！

## 対象読者

- 学業とエンジニアインターンを両立したいと思っている学生エンジニア

- エンジニアインターンに興味がある学生エンジニア

# 🙆‍♂️エンジニアインターンと両立するテクニック

自分が思うテクニックは、

- **タスク管理と目標設定**
- **タスク細分化と見積もり**
- **インターンで無理はしない**

です！

## 💪タスク管理と目標設定

### タスク管理

タスク管理ツールを活用して、研究とインターンのタスクを明確に分け管理することが初めに紹介するテクニックです!🙆

特に使っているツールは以下です！

- **Notion**（研究、その他）
- **JIRA**（インターン）

ツールを使うと、生産性が上がりますね！

タスク管理することで、研究とインターンの気持ちの切り替えができるでおすすめポイントです！

極力、研究をやっている時はインターンのことを考えないことですね！（逆も然りです！）

しかし、自分の経験から、タスク管理ツールは以下のように使うと続かないと思います！🙅‍♀️

- ❌ **タスク管理ツールを2つ以上持つこと**
    - これ以上は管理が大変だから
- ❌ **タスクの細分化と工数見積もりをしないタスク管理**
    - タスク消化がなかなかできないから
- ❌ **Notionで自作テンプレートを作ること**
    - テンプレを作って満足してしまうから
    - すでにいいテンプレートがどこかにあるよ！

### 目標設定

インターンと研究では、目標設定が重要だと思っています！

**目標から逆算する**と何をやるべきか明確になると思います！🙆‍♂️

特に研究では、**1週間後の進捗目標を決める**ことが大事かなと思います！（タスク細分化見積もりのはなしと若干被る🙇）

自分がやっていたことで言うと、タスク管理ツールからは少し離れますが、

その1週間後の目標（細分化したタスク）を、付箋に書き、研究室のPCのモニタの下に貼って、達成できたらおいしいものを食べると言うことをしていました！（2,3ヶ月間ですがw）😏

この取り組みと、タスク管理ツールのおかげで、修士1年の冬ごろから、週2,3でインターンをしていますが、修士2年の12月にして修士論文が7割書き終わっています！（その間、学会発表もしました）🙆‍♂️

## 🙌タスク細分化と見積もり

タスク細分化と見積もりをし、重要で優先度高めなタスクから順番に取り組むことで、効率的に時間を使うことが2つめのテクニックです！

字面で見ると、なんか簡単に聞こえますが、実はすごく大変です！💦

ここでは、深く話しませんが、タスク細分化と見積もりについて、以下の記事で一歩踏み込んで書いてあるので、良かったら見てみてください！

これはエンジニアにとって、技術力と同じくらい結構大事なスキルかなと思います！

https://qiita.com/nakampany/items/b043b9ea52ea0d80f29e

## 😴インターンで無理はしない

最後のテクニックは、インターンで無理はしないです！

インターンは楽しいですが、無理をしないことが本当に大切です！

**稼働日で無理をしたり、タスクアサインで無理をする**ことは、おすすめしません！🙅‍♀️

先輩エンジニアからの期待もありますが、無理をするとどこかで限界が来きてしまいます。🥹

インターンで無理をすることはやめた方がいいです！

インターンを週4で行い、留年をした友人がいますが、さすがに笑えないです。😭

追加で、自分は休みやリフレッシュする時間、趣味を見つけて没頭することや、たまには友人と大学生ぽい遊びや飲み会をするのもいいと思います！

最終的に、それが高いパフォーマンス発揮に繋がったりすることがあるので、、、(割と真面目にw)🙆‍♂️


# 🔥インターン参加に迷っている方へ

インターンシップは、実践的なスキルを磨き、キャリアの基礎を築くための貴重な機会だと思ってます！

無理がない程度に積極的に参加すると良いと思います！

きっといい経験になります！とりあえず参加ですね！！💪

※忙しい研究室の方は、やべきことをしっかりやった上で、教授と相談してみるといいかもですね！きっと教授も親身になり応援してくれるはずです！

# まとめ

エンジニアインターンと研究を両立するテクニックを3つ紹介しました！

ぜひ参考にしてみてください！🙆‍♂️

他に自慢のテクニックがあれば、コメントや𝕏（Twitter）でお待ちしています！🚀

# PR
HRBrainでは一緒に働いてくれる仲間を募集しています！😁

https://www.hrbrain.co.jp/recruit

インターンの話をしたので、25卒の長期インターンも募集しています〜！

https://www.wantedly.com/projects/1089305

アドベントカレンダー1記事目もあるので、是非！

https://qiita.com/nakampany/items/91d77f1b87278ed40e81