OmniAICreator/Qiita-1.07M huggingface dataset:
Sphinx + myst_parser で日本語タイトル行の id を URLエンコードで作成する方法

Sphinx + myst_parser でマークダウンから HTML を生成する際、標準では、タイトル行の日本語部分は無視されて id が生成されてしまいます。

例えば、以下のようなマークダウンから、html を作成したとします。

```markdown
## Sphinx ドキュメントのひな形の作成

...節の内容

## Sphinx テーマの変更
```

すると標準では、最初のセクションの id が sphinx、２番目のセクションの id が id1 に設定されてしまいます。

[unicode_ids拡張(日本語版説明書) — unicode_ids 2.0.5 documentation](https://h12u.com/sphinx/unicode_ids/README_ja.html)に期待したのですが、Sphinx 1.3.1 用に書かれたものであり、説明の通りでは動作させることができませんでした。

ただ、unicode_ids のソースを読むことで対処方法が判明し、目的を達成できたのでここに記録しておきます。

conf.py に以下を追加します。

```python
from sphinx.util import docutils
from urllib.parse import quote

docutils.nodes.make_id = quote
myst_heading_anchors = 6
myst_heading_slug_func = quote
```

それから ```make html``` を実行すると以下のような日本語を含むタイトルが URL エンコードされた id を生成することができました。

```html
<section id="sphinx%20%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88%E3%81%AE%E3%81%B2%E3%81%AA%E5%BD%A2%E3%81%AE%E4%BD%9C%E6%88%90">
(中略)
</section>
<section id="sphinx%20%E3%83%86%E3%83%BC%E3%83%9E%E3%81%AE%E5%A4%89%E6%9B%B4">

```

行儀の良い方法か分かりませんが…

## 参考リンク

- [SphinxでMarkdown拡張言語のMySTを扱う – Helve Tech Blog](https://helve-blog.com/posts/python/sphinx-myst/)
- [GitHub - executablebooks/MyST-Parser: An extended commonmark compliant parser, with bridges to docutils/sphinx](https://github.com/executablebooks/MyST-Parser)
  - [Configuration](https://myst-parser.readthedocs.io/en/latest/configuration.html)
【Unity】Androidアプリをリリースする時にバージョンコードが違うと言われた場合の対処法【備忘録】

#対処法
ファイル＞ビルド設定＞プレイヤー設定＞プレイヤー＞その他の設定をたどっていき、識別(Identification)にあるバンドルバージョンコードの値を変える（1増やすでいい）。

パッケージ名隠すの面倒くさくて画像省略してます。
ごめんなさい。
LVMのLogical Volumeの拡張

# 前書き
本記事は私的な備忘録です。
記載内容について保証はしません。

# 手順

以下、コマンドの出力結果が拡張後のものです。すみません。

1. 物理ボリューム(PV)とボリュームグループ(VG)の関係を確認する  

   ```bash
   sudo pvdisplay
     --- Physical volume ---
     PV Name               /dev/sda3
     VG Name               ubuntu-vg
     PV Size               <62.00 GiB / not usable 0
     Allocatable           yes (but full)
     PE Size               4.00 MiB
     Total PE              15871
     Free PE               0
     Allocated PE          15871
     PV UUID               ZpyMMc-SDPB-rMNe-2B7b-ir7q-ZDu3-TZWJam
   ```

   この例では、`/dev/sda3`に`ubuntu-vg`が存在することがわかる。

1. ボリュームグループを確認する  
   ```
   sudo vgdisplay
     --- Volume group ---
     VG Name               ubuntu-vg
     System ID
     Format                lvm2
     Metadata Areas        1
     Metadata Sequence No  3
     VG Access             read/write
     VG Status             resizable
     MAX LV                0
     Cur LV                1
     Open LV               1
     Max PV                0
     Cur PV                1
     Act PV                1
     VG Size               <62.00 GiB
     PE Size               4.00 MiB
     Total PE              15871
     Alloc PE / Size       15871 / <62.00 GiB
     Free  PE / Size       0 / 0
     VG UUID               NEomDc-Hxm4-HA2V-7bh4-DE6F-Spnm-xFb2kR
   ```

1. 論理ボリューム(LV)を確認する

   ```
   sudo lvdisplay
    --- Logical volume ---
    LV Path                /dev/ubuntu-vg/ubuntu-lv
    LV Name                ubuntu-lv
    VG Name                ubuntu-vg
    LV UUID                UVxTDt-Ge9o-229h-bwpa-OhTZ-qyUa-z1kN3E
    LV Write Access        read/write
    LV Creation host, time ubuntu-server, 2022-09-02 14:50:27 +0000
    LV Status              available
    # open                 1
    LV Size                <62.00 GiB
    Current LE             15871
    Segments               1
    Allocation             inherit
    Read ahead sectors     auto
    - currently set to     256
    Block device           253:0
   ```

1. LVを拡張する
   
   ```
   sudo lvextend -l +100%FREE <VG Name>/<LV Name>
   ```
   この場合、空き領域すべてを使って拡張される。容量の指定方法は`man lvextend`参照。
1. LVの拡張に合わせてパーティションを拡張する
   ```
   sudo resize2fs <Partition Name>
   ```

1. 確認
   ```
   df -h
   ```
raspberry pi osを起動するとずっとしゃべる音声の正体は…

古いraspberrypiを買いました。
有線キーボードを持たない当方としてはSSHでしか接続できません。
ふと真夜中にraspberrypiとスピーカーをつなげて寝ようとしたところ、
なにやら低い声で喋っている音が聞こえるではありませんか。

やだな〜怖いな〜と思って音のする方に耳を傾けると先程つないだばかりのスピーカーから
「To install the screen reader, press Control Alt Space」
という謎の音声がループしているではありませんか。

ラズパイの電源引っこ抜いて寝ましたがいろいろ調べてみると以下のことらしいです。

https://qiita.com/yuriyuriyuriyuri/items/3ed16e8a549de2348b47

https://raspberrypi.stackexchange.com/questions/118911/how-do-i-stop-the-audio-message-to-install-the-screen-reader-press-control-alt

https://forums.raspberrypi.com/viewtopic.php?t=231557

というわけで以下で解決です。
```bash
sudo rm /etc/xdg/autostart/piwiz.desktop
ps -eaf | grep wiz
sudo kill <<piwizのPID>>
```

ちなみにその音声は以下から聞けます。

https://github.com/raspberrypi-ui/piwiz/blob/master/data/srprompt.wav?raw=true
プログラミングスクールを卒業して思うこと

# はじめまして！　

私はDMM WEBCAMP COMMIT(以下DWC)を8月に卒業しました！
これからスクールに通おうと考えている方や在校生の参考になればと思い記事にしました🙇‍♂️

# 目次
* なぜプログラミングスクールに通おうと思ったか
* 1ヶ月目
* 2ヶ月目
* 3ヶ月目
* 4ヶ月目
* 学習中に取り組んだこと
* 学習中に意識していたこと
* スクールの感想
* プログラミングはスポーツと同じだと思う
* 最後に

# なぜプログラミングスクールに通おうと思ったか
独学でプログラミングを習得できなかった、つまり挫折したことが大きなきっかけです。
学習をしていくなかでエラー解決ができずに1日過ぎてしまうことがあり、
このまま惰性で続けていても、エンジニアになれないと思っていました。

プログラミングスクールは以前から知っていて、興味本位で説明会に出席したのですが、
そこで言われた言葉がとても印象に残っています。

**”エンジニアになれるかは本人の努力次第”**

誰でもエンジニアになれるといった甘い言葉が一切なく、
このまま独学で続けていても厳しいことは分かっていたので、
とにかくやってみよう精神からプログラミングスクールに通うことを決意しました。

# 1ヶ月目
この期間はHTMLやCSSを使った簡単なWebサイトの作成や、簡易的なCRUDアプリの作成をします。

CRUDアプリとは...
C=Create(作成)
R=Read(読出)
U=Update(更新)
D=Delete(削除)

プログラミングでアプリを作る際の4大機能の頭文字を取ったものになります。

体感ですが、3割近くの方が挫折しているように思いました。
なのでやり切る覚悟を持って臨むことが重要です。

# 2ヶ月目
2ヶ月目はチーム開発期間になり、ECサイトを開発するのですが、挑戦したことのない機能を実装するため、メンターにたくさん質問をしました。
また一人ではなくチームでの開発になるため、チーム内でのコミュニケーションを積極的に取ることを心がけました。
ちなみに企業ではチームでの開発がほとんどなので、GitやGithubの使い方を実践的に学べる機会はとても貴重だと思います。

# 3ヶ月目
3ヶ月目は転職活動で企業に提出するポートフォリオアプリの作成をするために、
テーマを選定し、設計、実装をします。
ポートフォリオアプリの機能を実装する前に、

"本当にこの機能は必要か"

"他に優先して実装する機能はないか"

"実装方法はこのやり方が適切か"

などなどユーザーにとって必要な機能は何かを考える過程が面白かったです。
このように論理的に考えながら機能を実装したことでユーザビリティなアプリが作れたと思います。

# 4ヶ月目
4ヶ月目は専門技術としてクラウドかAIを選択し学びます。
私はクラウドを選択し、最終課題として1から環境を構築し、ポートフォリオアプリのデプロイまで行います。
この最終課題を終えると、各自の自習期間になります。


# 学習中に取り組んだこと

### Githubにて草を毎日生やし続けた
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1464771/5426a289-c722-be0d-ba0d-bd4a83fd8a86.png)

### Qiitaにて学んだことを積極的に発信
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1464771/1421370f-e0c0-1fc2-eb66-47fc2afcc2d4.png)

2つの共通点は**継続的に行った**ことです。これは自信にも繋がりましたし、何より達成感がありました。
またTwitterなどで卒業生の方にアポを取り、現役エンジニアの話を聞けたことが大きなモチベーションにも繋がりました。

# 学習中に意識していたこと

**1.仲間を作り、楽しむ**

**2.カリキュラムの理解度が20％でも先に進む**

あくまで私のスタンスですが、上記2つを意識しました。
同期とは卒業した今でも連絡を取り合い、もくもく会を開催し交流しています。
社会に出てからもどこかで再会することもあるかもしれないので、つながりを作ると良いです。

またカリキュラムは初心者にとっても理解しやすいように作成されていますが、最初は躓くことが多いです。
ただ、そこで分からないことを繰り返し復習するより、まずは先に進めることが大事です。

**"Done is better than perfect(完璧を目指すよりまず終わらせろ)"**

Facebookの創業者であるマーク・ザッカーバーグ氏の名言ですが、プログラミングの学習だけでなく
あらゆることに共通した考えだと思います。

# スクールの感想

一言で言うと**コスパは良い**です。
費用は決して安価なものとは言えませんが、この4ヶ月間腰を据えて取り組めば、得られることが多いからです。

メンターについてですが、レベルは高い人が多い印象です。
分からないことに対してすぐに正解を教えるのではなく、解決に至るまでのプロセスを示してくれるので、
次に同じエラーが出たとしても自力で解決できるようになります。

またキャリアアドバイザーという転職活動をサポートしてくれる方が1対1でついてくれるので、
転職活動にかかる負担がかなり減ると思います。私は今転職活動中なのでまだお世話になっているのですが、とても頼りになります😄

# プログラミングはスポーツと同じだと思う

余談ですがプログラミングを学んでいると、スポーツと似ているところがあると思います。
野球で例えると、

1.　正しいバッティングフォームの理屈を頭でしっかり理解する
2.　とにかくバットを振ってボールを打って反復練習し、頭で理解した理屈を体に染み込ませる

という訓練が必要ですが、プログラミングでも同じことが言えると思います。

1. アルゴリズムなど正しいやり方や理屈を理解する
2. たくさんのコードを読み書きして反復練習し、ちょっとしたロジックならすぐに頭に浮かぶように訓練する

ということが必要です。

「これを学び、次はこれを学ぶ」という進め方（上記の1をメインにおくやり方）がいわゆるインプット式の学習方法かと思いますが、
順番に学ぶだけではなかなか身についていかないと思います。
「これを作るためにはこれが必要」という、必要に迫られながら必要な技術を実践で使いアウトプットする(上記の2を念頭におくやり方)ほうが身に付きますし、
このやり方はどのスポーツにも言えることだと思います。

# 最後に
私が尊敬している森岡毅さん(株式会社刀)の言葉を紹介します。
話や行動は合理的ですが、言葉の節々に熱量があり、こんな言葉を残されています。

#### たくさんある吉か小吉か中吉を引けばいい

プログラミングの学び方って色々あると思います。
そうした選択肢のなかにプログラミングスクールがあり、
引いた後に大吉にするかどうかは結局は自分次第ですね。

ここまで長文になりましたが、最後まで読んでいただきありがとうございました！

https://youtu.be/nRzl3sJM3X0
シェルスクリプトの世界から基本正規表現(BRE)をなくそう！

## はじめに

UNIX コマンドが対応している正規表現には、基本正規表現 (BRE) と拡張正規表現 (ERE) の二種類があります。誰もがよく知っている正規表現は拡張正規表現（の拡張版）です。基本的には基本正規表現はもう忘れていいです。ならぜなら、一番の懸念点であった `sed` コマンドの拡張正規表現対応 （`sed -E`) が [POSIX Issue 8 で標準化される](https://qiita.com/ko1nksm/items/6c0d8e6226a29ea41f07)ことになったからです。

シェルスクリプトでよく使う awk、grep、sed が拡張正規表現に対応したことで、基本正規表現はほぼ不要になりました。拡張正規表現に対応していないのは、あまり必要でない expr や、シェルスクリプトでほとんど使われない ed、ex、more ぐらいです。

とはいえ expr で基本正規表現は使うと言えば使います。で、ふと思ったのですが、基本正規表現と拡張正規表現はエスケープする文字が異なるだけで機能はほとんど同じです（下記参考リンク参照）。それなら**拡張正規表現を基本正規表現に変換**してやれば、基本正規表現を使う必要はないですよね？ということで実装してみました。これでもう基本正規表現のことは忘れてしまうことが出来ます。

**参考リンク**
- [シェルスクリプトの正規表現の詳細解説（令和最新版）〜 基本正規表現(BRE)と拡張正規表現(ERE)](https://qiita.com/ko1nksm/items/53abc144558b9bb5629f)

## 実装（サンプル）

**注意** 思いつきでざっくり実装しただけのでテストは不十分です。バグがあるかもしれません。

```sh
#!/bin/sh

set -eu

# 拡張正規表現(ERE)から基本正規表現(BRE)への変換関数
ere2bre() {
  set -- "$1" "$2" ""
  while [ "$2" ]; do
    case $2 in
      "\\+"* | "\\?"* | "\\|"* | "\\{"* | "\\}"* | "\\("* | "\\)"*)
        set -- "$1" "${2#?}" "$3"
        set -- "$1" "${2#?}" "$3${2%"${2#?}"}"
        ;;
      "+"* | "?"* | "|"* | "{"* | "}"* | "("* | ")"*)
        set -- "$1" "${2#?}" "$3\\${2%"${2#?}"}"
        ;;
      *) set -- "$1" "${2#?}" "$3${2%"${2#?}"}" ;;
    esac
  done
  eval "$1=\$3"
}

# 拡張正規表現対応させるための expr のラッパー
expr() {
  if [ "${2:-}" = ":" ]; then
    ere2bre bre "$3"
    set -- "$1" "$2" "$bre"
  fi
  command expr "$@"
}

# 拡張正規表現で呼び出せる
expr "$(id)" : "uid=([0-9]*)\("
echo "-----"

example() {
  ere="$2"; ere2bre bre "$ere"
  echo "=== $ere : $bre ==="
  printf '%s\n' "$1" | grep --color -E "$ere"
  printf '%s\n' "$1" | grep --color "$bre"
}

example 'a\b' 'a\\b'
example 'a(b' 'a\(b'
example 'ab' 'a(b)'
example 'a{b}' 'a\{b\}'
example 'ab' 'a.*'
example 'ac' 'ab?c'
example 'abc' 'ab?c'
example 'a' '(a|b)'
example 'aca' '(a|b)c\1'
example 'az' 'a\z'
```
【AdMob】ネイティブ広告で画像広告だけ表示する

# はじめに
ネイティブ広告には画像広告と動画広告があります。
動画広告は重いのでスクロールがカクついたりします。
ですので、画像広告だけを表示したいと思い、
AdMobの実装関係のドキュメントを一生懸命読んでました。

なかなか見つからないのでないのか。。。と諦めかけた所、マイページで設定ができたので紹介します。

# 設定方法
1, ハンバーガーメニューを開きます。
2, 「広告ユニット」を選択します。
![スクリーンショット 2022-09-03 0.25.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/89f6f1a1-5776-eef4-c0ef-f24d72047031.png)

3, 該当の広告ユニットを選択します。
![スクリーンショット 2022-09-03 0.26.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/c7d57517-bd9c-df79-3be3-9b0f74961dbc.png)

4, メディアタイプを編集します。
![スクリーンショット 2022-09-03 0.23.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/faf309f1-5442-4c9f-57bf-6c73a1471f44.png)

5, 変更が完了したら「保存」を選択します。
![スクリーンショット 2022-09-03 0.28.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/2593e011-b03d-0e3b-6c4c-8ff385f5a8fd.png)

# おわり
めっちゃ簡単でした。
反映も一瞬です。
ECS上のTomcatアクセスログをCloudWatch Logsに出力する

## はじめに

- ECS上で動作するコンテナについては標準出力にログが流れるよう設定する必要がありますが、Tomcatアクセスログはデフォルトで標準出力には流れません。
- 本記事ではTomcatのアクセスログを標準出力とし、CloudWatch Logsに出力する方法を紹介します。

## 環境

- Java11
- tomcat9.0

## 実施内容

方法としては以下です。

- アクセスログを日付毎に出力するのではなく、固定ファイル名とする
- 固定ファイルと標準出力のシンボリックリンクを作成する

::: note info
202402追記： serverl.xmlに directory="/dev" prefix="stdout"と直接標準出力を指定することが可能です。その場合は固定ファイルの作成とシンボリックリンク作成は不要です。
:::

## server.xmlの用意

`server.xml`のAccessLogValveを以下のような設定にする事で、アクセスログを固定ファイル名`access_log.txt`に出力するよう設定します。

```server.xml
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="access_log" suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b"
rotatable="false" fileDateFormat="" />
```

## Dockerfileの用意

`server.xml`の上書きを行い、その上で標準出力と`access_log.txt`のシンボリックリンクを作成します。

```:Dockerfile
FROM tomcat:9.0-jdk11-corretto
RUN yum update -y
COPY server.xml /usr/local/tomcat/conf/server.xml
RUN ln -sf /dev/stdout /usr/local/tomcat/logs/access_log.txt
COPY sample.war /usr/local/tomcat/webapps/sample.war
EXPOSE 8080
```

## Dockerfileをビルドし、ECRにプッシュ

```sh
# ECRの認証
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin アカウントID
.dkr.ecr.ap-northeast-1.amazonaws.com
# Dockerイメージのビルド
docker build -t tomcat-sample .
# イメージのタグ付け
docker tag tomcat-sample:latest アカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/tomcat-sample:latest
# イメージのプッシュ
docker push アカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/tomcat-sample:latest
```

## タスク定義、サービスの作成

ECRにプッシュしたイメージを参考に、tomcatコンテナを起動します。

### tomcatコンテナの設定内容

ECSのタスク定義内のログドライバーはデフォルトの`awslogs`を利用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411902/417fd2f8-e501-d2dd-78ab-62b1f32092be.png)

8080番ポートをホストOSの8080番ポートにバインドします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411902/15e5b993-c1ec-7712-9d93-28804cb6e479.png)

※この際、タスク実行ロールの設定を忘れないようにしましょう

## CloudWatch Logsでアクセスログを確認

`ip:8080`の形でTomcatコンテナにブラウザからアクセスします。
次に、設定したロググループよりログを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411902/61326375-c4a0-e01a-b7d4-d6cc2514bfe2.png)

無事、アクセスログがCloudWatch上で確認できる状態になりました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411902/6c82d025-8d91-d603-4566-c449a9c0df58.png)

## 参考文献

<https://aws.amazon.com/jp/premiumsupport/knowledge-center/ecs-container-logs-cloudwatch/>

<https://github.com/docker-library/tomcat>
【超凡ミス】Ruby on Railsにfont-awesome-sassのアイコンが表示されない理由

# はじめに
- font-awesome-sassを正しく導入してるはずなんだけど、なぜかアイコンが表示されない、、、
- 公式サイトの説明通りに進めたから間違ってるわけないんだけどなぁ。
- 「困ったら再起動！」と思ってやってみたが結果は同じ、、、。

こういった方の助けになるかもと思い、記事を書きます。

この記事を書いているぼくはSES勤務のシステムエンジニア。
Web系エンジニアへの転職を目指して、ポートフォリオ作成中の身です。

:::note warn
注意
この記事の内容は本当にくだらない凡ミスですが、ご容赦ください。
:::

# 結論：無料のアイコンを選択していなかった
[Font Awesomeの公式サイト](https://fontawesome.com/search)で検索をかけるとアイコンがズラーっと出てきますが、**そのほとんどは有料です。**

無料のアイコンを使うためには、画面左の**Freeにチェックをつける必要があります。**

英語だと意外とわかりにくい、って人も多いと思うのでお忘れなく。
Fedora36 Silverblue に VMware Workstation を入れる

2024-09-28 追記

Michal Kubečekさんは、VMwareがBroadcomに買収されて以来、更新をやめてしまったように見える。

代わりに使えるのはこちら。
https://bytium.com/vmware-workstation-host-module-patch-for-debian-and-ubuntu/

---


大昔はですね、VMware って、カーネル・バージョンの相違からモジュールがビルドできなくってインストール失敗するって、普通だったんですよ。

で、当時は vmware-any-any patch ってーのが出回っていて、それ当てればビルドできるようになったんです。

時が変わって、VMware Inc のサポートが良くなって、仮にビルドできなくても1-2ヶ月程度待つとちゃんと新しい対応リリースが出るようになったんですね。なので、そんなもの不要になっていた。

所が今回 Fedora 36 にしたらビルド失敗する。
待っても対応版が出ない。

変だなと思って改めてサポート・ページみたら、対応 Fedora は 33 迄になってる！
堪らん、参った！！

半ば自分でモジュールのソースを書き換える覚悟を決めつつ、
で、色々探したら、 vmware-any-any patch みたいなのあった。

[Install VMware Workstation Pro on Fedora 36](https://www.itzgeek.com/how-tos/linux/fedora-how-tos/how-to-install-vmware-workstation-pro-on-fedora.html)

[Fedora35 Silverblue に VMware Workstation を入れる](https://qiita.com/Mercury/items/4c1890d48e302469cbc1) に準じて、モジュールをビルドしたらうまく起動できました。
めでたしめでたし。

```
	    mkdir -p /usr/local/lib/modules/$(uname -r)/misc
	    ln -s /usr/local/lib/modules/$(uname -r)/misc /ostree/deploy/fedora/deploy/${NUM}/lib/modules/$(uname -r)
	    ostree admin unlock
	    cf. https://www.itzgeek.com/how-tos/linux/fedora-how-tos/how-to-install-vmware-workstation-pro-on-fedora.html
    		VMWARE_VERSION=workstation-16.2.4
    		TMP_FOLDER=/tmp/patch-vmware
    		rm -fdr $TMP_FOLDER
    		mkdir -p $TMP_FOLDER
    		cd $TMP_FOLDER
    		git clone https://github.com/mkubecek/vmware-host-modules.git
    		cd $TMP_FOLDER/vmware-host-modules
    		git checkout $VMWARE_VERSION
    		git fetch
    		make
    		make install
    		rm /usr/local/lib/vmware/lib/libz.so.1/libz.so.1
	    rsync -HaxvSu /var/tmp/ostree-unlock-ovl*/upper/lib/modules/ /ostree/deploy/fedora/deploy/${NUM}/usr/lib/modules/
```
Vue.jsでストップウォッチを作ってみました。

### 概要

遊びで、時計みたいなものをプログラミングしてみたいと考えました。
砂時計みたいなタイマーを作りたいと最初思いましたが、とりあえずはストップウォッチを開発することで決定！
目標は正確な時間を数えることにしました。

### 工夫したことです。

- 時間を数えるために、例えば時間を1ミリ秒ずつ足していくと、処理自体にかかる時間のせいで時間が合わなくなってしまうのではないかと考えました。
    - そうでした。単純に足し算して時間を数えると、時間がずれてしまいました。
    - それで考えたのが、JavaScriptのライブラリから現時刻を取得して、スタート時刻と比較して、どのぐらい時間が過ぎているか計算する方法です。
        - ネットで調べたら、既にこういう考え方で実装をしたソースコードをいくつか見つけました。
        - 気に入るものが見つからず、自ら考えてアルゴリズムを組むことにしました。
       
### ソースコードです。

```vuejs
<template>
    <table>
        <thead>
            <tr>
                <td colspan="2"><h1> {{ getTimeStr() }} </h1></td>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>
                    <button v-if="status !== 'start'" @click="start()">スタート</button>
                    <button v-else @click="stop()">ストップ</button>
                </td>
                <td>
                    <button @click="reset()">リセット</button>
                </td>
            </tr>
        </tbody>
    </table>
</template>

<script>
export default {
    data () {
        return {
            status: "clear",
            time: 0,
            startTime: null,
            stopTime: 0
        }
    },
    methods: {
        start() {
            this.status = "start"
            
            if (this.startTime === null) {
                this.startTime = Date.now()
            }

            const checkTime = () => {
                this.time = Date.now() - this.startTime + this.stopTime
            }
            this.timer = setInterval(checkTime, 10);
        },
        stop() {
            if (this.timer) {
                clearInterval(this.timer);
            }

            this.status = "stop"
            this.startTime = null
            this.stopTime = this.time
        },
        reset() {
            this.stop()
            this.status = "clear"
            this.time = 0
            this.startTime = null
            this.stopTime = 0
        },
        getTimeStr() {
            // this.time is milliseconds
            // 1秒 = 1000ミリ秒
            // 1分 = 60 * 1000ミリ秒
            // 1時間 = 60 * 60 * 1000ミリ秒
            let milliseconds = this.time % 1000
            let seconds = Math.floor((this.time / 1000) % 60)
            let minutes = Math.floor((this.time / (60 * 1000)) % 60)
            let hours = Math.floor(this.time / (60 * 60 * 1000))

            let millisecondsMultiplyTen = Math.floor(milliseconds / 10)

            millisecondsMultiplyTen = ("0" + millisecondsMultiplyTen).slice(-2)
            seconds = ("0" + seconds).slice(-2)
            minutes = ("0" + minutes).slice(-2)
            hours = hours < 100 ? ("0" + hours).slice(-2) : hours

            return `${hours}:${minutes}:${seconds}.${millisecondsMultiplyTen}`
        }
    }
}
</script>

```

### スクリンショット

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2481192/b22c9560-a833-242e-9d93-7153bbfbaad6.png)
生意気すみません デザインフィードバック to @r19_tech さん

## ①大好きなファーストビュー 余白がある方がいいかも
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1793443/f8d1e53c-936f-96a7-72b4-d319ef685e07.png)

めっちゃ目立って大好きなアニメーションなのですが、
上下左右ともある程度余白がある方が見やすい気がします！いかがでしょう。。。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1793443/6a57f411-255d-69ed-b813-3d8bd22ce7ef.png)

## ②画像と画像の間 もう少し余白あってもよいかもです!!

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1793443/125f107e-a57e-953f-b213-68e3289390ef.png)
80ピクセル取られていたらすみません もう少し空いていてもいいかな と感じました

## ③写真+コメント めちゃわかりやすい しいて言うなら
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1793443/8613d499-a692-107d-db98-88bf9e614f02.png)
 林さんからも僕もアドバイスもらったのですが、文字は最低限、情報にフォーカスしてもらうことが
 大切ということで、赤文字で「縁結びアイデアワークとは」と書いてあるので、
 下の詳細箇所ではその文字は省いて「参加者のアイデアを～」から初めても良い気がします!

 渡邊さんがやってて「いいなぁ」と思っていたのですが
 文字の周りに白などで影を付けると、画像と重なった所も輪郭がはっきりして見やすい気がしました。
 オレンジ文字の周り、白などで縁取ると見やすくなるかもしれません(ならなかったらごめんなさい)

　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1793443/f95ed7a5-6a68-a189-871e-cf21eed7182d.png)

## ④問いかけ 目がぐっと止まるし モヤモヤもすごく伝わります!!
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1793443/cbc736e8-ae58-9ecf-fa0d-331cc3e5f9f0.png)

 前後のメッセージめっちゃ素敵なので、1つの画面に両方とも収めるか、
 もしくは後半のメッセージは次のページなるように設計する方がよい気がします。

 上のメッセージを読んでいるページの途中で、下の連続感のあるロゴが見えてくるので
 読み終わる前に、どうしても下に目線が移ってしまう気がします!!

## ラストメッセージ&ネクストアクションめちゃいいです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1793443/04f64319-72b8-8c56-0df1-2206b1be5343.png)

 最初のページと同じくらい好きです。グッときます。
 参加費だけでなく「参加資格」もないよ、が心理的ハードルぐっと下がります!!

:::note warn
 「アイデアの寄り道」ということば、すごく素敵です。CVも分かりやすいです!!
:::


偉そうにコメントしてごめんなさい！！僕もリトライします～👍

おきなか
riverpod_graphのすゝめ(と注意点)

みなさん、Riverpod使ってますよね？
早速ですが、riverpod_graphを使ってみましょう。

https://github.com/rrousselGit/riverpod/tree/master/packages/riverpod_graph

riverpod_graphは、riverpodの依存関係をこんな感じに記述してくれます。
```mermaid
flowchart TB
  subgraph Arrows
    direction LR
    start1[ ] -..->|read| stop1[ ]
    style start1 height:0px;
    style stop1 height:0px;
    start2[ ] --->|listen| stop2[ ]
    style start2 height:0px;
    style stop2 height:0px; 
    start3[ ] ===>|watch| stop3[ ]
    style start3 height:0px;
    style stop3 height:0px; 
  end
  subgraph Type
    direction TB
    ConsumerWidget((widget));
    Provider[[provider]];
  end
  MyHomePage((MyHomePage));
  counterDisplayProvider ==> MyHomePage;
  counterProvider -.-> MyHomePage;
  counterDisplayProvider[[counterDisplayProvider]];
  counterProvider ==> counterDisplayProvider;
  counterProvider[[counterProvider]];

```

やりたいですよね？それでは導入方法です。

# 導入方法
1. [ここ](https://github.com/bakatsuyuki/riverpod_graph_sample/blob/master/lib/riverpod_graph.dart)からコードをとってきて、lib配下に置く(公式最新版(9/2現在)の処理を1つのファイルにまとめたものです)
2. pabspec.yamlに[analyzer](https://pub.dev/packages/analyzer)を追加
2. `dart lib/riverpod_graph.dart` を実行
2. 出力された[mermaid](https://qiita.com/Qiita/items/c07f3262d8f3b25f06c9)を使って表示する

たったこれだけです。

# 注意点
Providerを下記の書き方で記すのはNGです

```dart
class Counter {
  static final provider = StateProvider((ref) => 0);
}
```

[公式](https://github.com/rrousselGit/riverpod/discussions/814)でも推奨されてたので治るといいのですが・・・
すゝめとか書いておきながら、このバグが原因でメインのプロジェクトには導入できておりません。

# 余談
Riverpodに慣れすぎてカウンターサンプルを5分でRiverpodにリファクタできるようになりました。

# 参考
ソース
https://github.com/bakatsuyuki/riverpod_graph_sample
[SikuliX-2.0.5] IDEを起動せずにRPAを実行する

SikuliX でファイルをたとえば  `rpa_scenario` として指定して保存すると、
`rpa_scenario.sikuli` フォルダが作成され、
フォルダの中に `rpa_scenario.py` ファイルと、`利用した画像ファイル`が保存されます。

作成した `rpa_scenario.sikuli`
SikuliX IDEを起動せずに実行する方法は２通りあります。


## その１：Export as runnable jar (Ctrl+Shift+J)
メニューバーの `[ファイル]` ⇒ `[Export as runnable jar (Ctrl+Shift+J)]` を選択するか、
ショートカット `Ctrl+Shift+J` を押下すると、
`rpa_scenario.sikuli` フォルダと同じ場所に `rpa_scenario_sikuli.jar` が作成されます。

このファイルは SikuliX が無くても 64bit版Java8 以降がインストールされていれば実行可能です。

`rpa_scenario_sikuli.jar` は `rpa_scenario.py` Jython用にコンパイルされた `sample2$py.class`ファイルと画像ファイル、必要最低限のクラスファイルのみが格納されています。


## その２：`-r` オプションをつけてコマンドラインから実行
SikuliX をコマンドラインから起動するとき、`-r` オプションでフォルダを指定する方法でも利用できます。
```cmd: cmd.exe
java -jar sikulixide-2.0.5-win.jar -r path\to\rpa_scenario.sikuli
```
【Laraval】チャットワークのAPIをGuzzleクライアントを使ってメッセージを送る

## 1. チャットワークのAPIトークンを取得する

画面右上のユーザーのアイコンをクリック＞サービス連携＞APIトークンで確認

1. 右上のUIからサービス連携を選択

![](https://storage.googleapis.com/zenn-user-upload/9e28f77d3e85-20220902.png)

2. APIトークンを選んでをメモする
![](https://storage.googleapis.com/zenn-user-upload/3fafe219aa75-20220902.png)

## 2.先ずはpost manで叩いてみる

3. 先ずは送りたいチャットルームのルームIDを取得する（URLの#!ridの後ろの数字をコピー）

```php
https://www.chatwork.com/#!ridxxxxxxxxxxxxx
```

4. API URLをpost manのURLに貼る（メソッドはPOSTを設定）
![](https://storage.googleapis.com/zenn-user-upload/edbf27c3ba7d-20220902.png)


5. Authorizationを設定する(今回はテストで叩くだけなのでNo Authに設定する)
6. headerをセットする
    1. X-ChatWorkTokenに先程コピーしたトークンをコピペする。
    2. Content-Typeはtext/htmlに設定（ほかのやり方でも行けるみたいだがjsonは行けないみたい）
7. bodyをセットする
    1. データはx-www-form-urlencodedに設定
    2. KEY名はbodyに設定
![](https://storage.googleapis.com/zenn-user-upload/99945b692b5c-20220902.png)

1. sendを押下
2. 以下の様なレスポンスが返って来れば成功

```
{
    "message_id": "xxxxxxxxxxxxxxx"
}
```

チャットワークスレッドに投稿が反映されてるか確認
![](https://storage.googleapis.com/zenn-user-upload/e443ab533676-20220902.png)


## 3. Laravelコード上での実装

```php

            $roomId = xxxxxxxxx; 

            $url = "https://api.chatwork.com/v2/rooms/{$roomId}/messages";

    
            $token = xxxxxxxxxxxxxxxxxxxxxxxxxxx
	    $headers = [
                   'X-ChatWorkToken' => $token　//1.でコピーしたtoken,
                   'Content-Type' => 'application/x-www-form-urlencoded'
                ];

            $body = "テストメッセージ";
                $params = [
                  'headers' => $headers 
		  'form_params' => [
                  'body' => $body 
                  ]
                ];
　　　　　　　　　        $client = new \GuzzleHttp\Client();
            $client->request('POST', $url, $params);
```

※GuzzleでAPIリクエストを行う場合、ヘッダーの書き方を

```php
                  $params = [
                    'headers' => $headers,
                    'form_params' => [
                        'body' => $body 
                    ]
                  ];
```

みたいに’form-params’を挟んで記述しないとエラーになるので注意。

## 注意点
チャットワークAPIに関して
APIトークン　= ユーザーIDです。
基本的に参加してるチャットルームしか投稿出来ないですが、それでもルームIDとトークンがあれば簡単に悪用される恐れがあるので、実装はバックエンドで行う、トークンなどの大事な情報は`.env`で管理する様にしましょう！




以上です。
同じようなやり方はたくさんあったが、Guzzleクライアントを使って実装してる記事は見つからなかったので書きました。
誰かの参考になれば幸いです。

### 公式ドキュメント

[Chatwork APIドキュメント](https://developer.chatwork.com/docs)

### 参考記事
[chatwork api PHP - Google Search](https://www.google.com/search?q=chatwork+api+PHP&rlz=1C5CHFA_enJP922JP922&sxsrf=ALiCzsb2qGWi2JM05kDzlr3RVvgUomMz9w%3A1662020240209&ei=kGoQY8q0DN2Sr7wPrI2ykAU&ved=0ahUKEwjKvJvwk_P5AhVdyYsBHayGDFIQ4dUDCA4&uact=5&oq=chatwork+api+PHP&gs_lcp=Cgdnd3Mtd2l6EAMyBQgAEIAEMgYIABAeEAg6BwgAEEcQsAM6BAgAEEM6BAgAEB5KBAhBGABKBAhGGABQvgFY8wpgzw5oAXAAeACAAZABiAGwBJIBAzAuNJgBAKABAcgBCsABAQ&sclient=gws-wiz)

[Chatwork API でメッセージを送信する](https://al-batross.net/2020/09/30/chatworkapi/)
便利なツール2：xmindの始め方と使い方 

## xmindとは?
- 業務や思考の抜け漏れを防いでくれる頼れるツール

## xmindてどんなツール？説明するより見た方が早い
- **例：作れる料理をジャンルごとにまとめたい** 
  - 下記画像のようにまとめれる

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/e76178c8-fabd-b0d0-aaaa-53974630962f.png)


## xmindの3大推しポイント
- **追加事項を入れやすい**
  - Excelだと行や列を追加すると他のもずれて…など面倒がある
  - xmindはEnterやTab押すだけで項目が追加できるから編集しやすい
- **無料**
  - 有料版もあるが、無料版で不自由なく使える
- **使い始めるまでに必要な作業が体感1分で終わる**
  - アドレス登録等もない


## 始め方
1. [ここ](https://jp.xmind.net/download/xmind8)へ移動
1. 自分が使っているPCをクリック
1. ダウンロード完了

## 使い方(上記の「作れる料理」を例に進める)
1. テーマを選択
   - 最初は「マップ」か「ロジック図（右）」が使いやすくてオススメ
2. テーマをクリック
   - 「中心トピック」に「自分が作れる料理」と入力
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/2126590a-f820-3ba0-0196-aa9d250c7188.png)

2. Enterを押すごとに第一階層（主トピック）が出る
   - 「主トピック1~3」を「和食・洋食・中華」と入力
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/b47ca8d6-cf3f-77b3-ff37-50bf0335cdfe.png)


3. Tabを押すごとに第二階層（サブトピック）が出る
   - 「サブトピック1~3」を「餃子や肉野菜炒め」等と入力
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/68c6f970-319a-b8e5-d249-59d79f69174c.png)

4. 完成
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/e76178c8-fabd-b0d0-aaaa-53974630962f.png)

### 完成後に気付く「項目を変えたい、追加したい」という状況になった場合
   - 第一階層（主トピック）にイタリアンを追加するの忘れた!!!
       - イタリアンの項目を追加しよう!
       - そして第二階層（サブトピック）に「ピザ」も作ろう
          - やり方
             - 第一階層（和食・洋食・中華）でEnter → 主トピックが増える → イタリアンに変更
             - さらに、第二階層（サブトピック）をTabをクリックして増やす。「ピザ」を入力

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/e1241390-d363-c947-2be0-052062769c27.png)



## 応用編
### マーカー
- 項目ごとにアイコンを追加できる
   - 進捗確認の時に便利

### エクスポート
- 作ったものをデータとして落とせる

## 最後に
- 紹介したのは無料版で使える機能。
  - 他にも「共有」などオプションは様々あります。
  - 慣れたら触ってみてください!!
IPv6のIPアドレス覚書

# アドレス長
+ IPv4（32bit）
+ IPv6（128bit）

# アドレス表記方法
+ IPv4
32ビットを8bitごとに ピリオド（ . ）で区切り、4つのフィールドに分けて10進数で表記。

|進数|フィールド1|フィールド2|フィールド3|フィールド4|
|:--|:--|:--|:--|:--|
|2進数|11000000.|10101000.|00000000.|00000010|
|10進数|192.|168.|0.|2|

+ IPv6
128ビットを16bitごとにコロン（ : ）で区切り、8つのフィールドに分けて16進数で表記。

|進数|フィールド1|フィールド2|フィールド3|フィールド4|フィールド5|フィールド6|フィールド7|フィールド8|
|:--|:--|:--|:--|:--|:--|:--|:--|:--|
|2進数|0010000000000001:|0000110110111000:|0000000000000000:|0000000000000000:|0001001001010110:|0000000000000000:|0000000000000000:|0000101010111100|
|16進数|2001:|0db8:|0000:|0000:|1256:|0000:|0000:|0abc|

# IPv6アドレスの省略方法
+ 基本形態
128bitを16bitごとに、16進数で表記し、8個のブロックに分けてコロン（ : ）で区切る。
2001:0db8:0000:0000:1256:0000:0000:0abc

+ 省略ルール1
各ブロックの先頭の0は省略してもよい。
2001:db8:0000:0000:1256:0000:0000:abc

+ 省略ルール2
0ばかりのブロックは0と表現できる。
2001:db8:0:0:1256:0:0:abc

+ 省略ルール3
0ばかりのブロックが連続する場合は"::"で表現できる。（ただし、"::"で表現できるのは同一アドレスの中で1回だけ。）
2001:db8::1256:0:0:abc

（NG）2001:db8::1256::abc（"::"を2回使用しているため）

# アドレスプレフィックス

|Prefix（16進表記）|Prefix（2進表記）|割り当て状況|関連RFC|
|:---|:---|:---|:---|
|0::/8|0000 0000|Reserved by IETF|RFC4291|
|100::/8|0000 0001|Reserved by IETF|RFC4291|
|200::/7|0000 001|Reserved by IETF|RFC4048|
|400::/6|0000 01|Reserved by IETF|RFC4291|
|800::/5|0000 1|Reserved by IETF|RFC4291|
|1000::/4|0001|Reserved by IETF|RFC4291|
|2000::/3|001|Global Unicast Addresses|RFC4291|
|4000::/3|010|Reserved by IETF|RFC4291|
|6000::/3|011|Reserved by IETF|RFC4291|
|8000::/3|100|Reserved by IETF|RFC4291|
|A000::/3|101|Reserved by IETF|RFC4291|
|C000::/3|110|Reserved by IETF|RFC4291|
|E000::/4|1110|Reserved by IETF|RFC4291|
|F000::/5|1111 0|Reserved by IETF|RFC4291|
|F800::/6|1111 10|Reserved by IETF|RFC4291|
|FC00::/7|1111 110|Unique Local Unicast Addresses|RFC4193|
|FE00::/9|1111 1110 0|Reserved by IETF|RFC4291|
|FE80::/10|1111 1110 10|Link-Local Unicast Addresses|RFC4291|
|FEC0::/10|1111 1110 11|Reserved by IETF|RFC3879|
|FF00::/8|1111 1111|Multicast Addresses|RFC4291|

最新情報は「[Internet Protocol Version 6 Address Space](https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml "Internet Protocol Version 6 Address Space")」を参照。

---
以上
【Linux】プロンプトに時刻を表示する

## プロンプトに時刻を表示する

|オプション|詳細|
|:---|:---|
|t|時間(24時間表示)|
|T|時間(12時間表示)|
|u|ユーザ名|
|h|ホスト名|
|H|FQDN|
|w|カレントディレクトリ(絶対パス)|
|W|カレントディレクトリ(相対パス)|


```linux:実行例
$ PS1="[\t \u@\h \w]\$ "
```


```linux:実行結果
[12:23:34 username@hostname /user/local]$
```
アプリに撮影機能を実装した時の注意点

# はじめに
アプリに撮影機能を実装した際に挙動がおかしな点が見つかったので備忘録として記事にしました。
# 問題
音量ボタンでシャッターを切る実装をした際に、撮影画面でホーム画面に戻る、または画面ロックすると再度撮影画面に復帰した際に音量ボタンで撮影できなくなってしまいました。
# 原因
https://developer.apple.com/documentation/avfaudio/avaudiosession/1616596-interruptionnotification

上記ドキュメントのNoteにこう書かれていました。
> Starting in iOS 10, the system deactivates an app’s audio session when it suspends the app process.

日本語に翻訳すると、
> iOS 10から、アプリのプロセスを中断する際に、アプリのオーディオセッションを非アクティブにするようになりました。


つまり、音量ボタンで撮影するためにアクティブにした[AudioSession](https://developer.apple.com/documentation/avfaudio/avaudiosession)が、アプリがバックグラウンドに移った時や画面ロックした時などに非アクティブになるため撮影画面に復帰したら音量ボタンでの撮影ができなくなってしまうということです。
# 対策
撮影画面で、アプリがフォアグラウンドになった際に通知を受け取るようNotificationCenterを登録します。
通知を受け取った場合にAudioSessionをアクティブにします。
```ViewController.swift
override func viewDidLoad() {
  super.viewDidLoad()
  NotificationCenter.default.addObserver(self, selector: #selector(self.activateAudioSession), name: UIApplication.willEnterForegroundNotification, object: nil)
}

@objc private func activateAudioSession() {
  try? self.audioSession.setActive(true)
}
```
# おわりに
気づくまでにかなりの時間を費やしてしまいました。。。
ドキュメントにしっかり書いてあるのに。
ドキュメントは偉大ですね。(ちゃんと読め俺)
【SwiftUI】影を特定のViewだけに付与する

# はじめに
SwiftUIを使っていてこれどうやるんだ？と思った事があったので記事にします。

# 現在のコード
```swift
struct ContentView: View {
    var body: some View {
        Text("影のテストです")
            .frame(width: 200, height: 80)
            .background(Color.yellow)
            .shadow(color: .black, radius: 3, x: -5, y: -5)
    }
}
```

# 問題
背景の黄色だけに影を付けたいのにTextにも影が付く
![スクリーンショット 2022-09-02 22.19.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/7a0fe59d-1bd2-15c4-66a2-c9b50df4eede.png)

# 解決方法
`shadow`の前に`compositingGroup`を付ける
```diff_swift
struct ContentView: View {
    var body: some View {
        Text("影のテストです")
            .frame(width: 200, height: 80)
            .background(Color.yellow)
+           .compositingGroup()
            .shadow(color: .black, radius: 3, x: -5, y: -5)
    }
}
```
![スクリーンショット 2022-09-02 22.25.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/8b52e515-7e0b-6299-c948-d39f467e38e2.png)

# おまけ(なんで？)
`cornerRadius`でも同じ事ができます。
```diff_swift
struct ContentView: View {
    var body: some View {
        Text("影のテストです")
            .frame(width: 200, height: 80)
            .background(Color.yellow)
+           .cornerRadius(0)
            .shadow(color: .black, radius: 3, x: -5, y: -5)
    }
}
```

![スクリーンショット 2022-09-02 22.25.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/8b52e515-7e0b-6299-c948-d39f467e38e2.png)

# おわり
影は貫通するんですね
htmlめも

## position

よくやるのが親要素にrelativeつけて
子要素にabsoluteつけるやつ。
そうするとrelativeつけた親要素を起点に子要素がポジショニングしてくれる。

ちなみにbottomとかtopは
positionとセットで使わないと意味を成さない。

```html:test.html
<div class="parent1">
    <div class="parent2">
        <p>指定なし</p>
        <p id="absolute">指定あり</p>
    </div>
</div>
```

```css:test.css
#absolute {
    bottom: 0px;
    position: absolute;
}

p {
    background: aqua;
    width: 100;
    margin: 0px;
}

.parent2 {
    background-color: beige;
    padding: 50px;
    /* position: relative; */ /*コメントを外すと…*/
}

.parent1 {
    /* position: relative; */ /*コメントを外すと…*/
    padding: 50px;
}
```

フッター固定したい時とか使える。
```html:footer_test.html
<div class="wrapper">
    <div class="container">
      <p>現在、このサイトは製作中です。<br>
        現在、このサイトは製作中です。<br>
        現在、このサイトは製作中です。<br>
      </p>
    </div>
    <footer>
      <p>(c)copy right</p>
    </footer>
</div>
```

```css:test.css
.wrapper {
    min-height: 100vh;
    position: relative;
}
footer {
	position: absolute;
	bottom: 0;
	background-color: #89c7de;
	width: 100%;
}
```
参考サイト
https://techacademy.jp/magazine/19410
https://dot-blog.jp/news/css-bottom-fix/
Lights OutをSagemathで解く（その２）

[前回の（その１）](https://qiita.com/masa0599/items/0302ef923eaebf96da64)で求めた行列**A = makeA(5,5)** を使い、今回もこの論文を参考に解答を見つけるアルゴリズムをSagemathを使って書いて行きます。

> [Turning Lights Out with Linear Algebra(MARLOW ANDERSON)](https://people.sc.fsu.edu/~jburkardt/classes/imps_2017/11_28/2690705.pdf)

## 階段行列 (echelon form) Eと変換行列Rを求める

まずAの階数を求めると23（正則なら25）なので、正則ではないことが分かります。

```Python
print(A.rank())
#23 
```
そこでこれを階段行列（echelon form）$E$とその変換行列$R$を求めます。$R$は1行目の式で求まると思ったのですが、うまく行かず色々調べた結果2-4行目を使いました。

```python 
#E0, R = A.echelon_form(transformation=True)
E0, R0 = map(lambda t: matrix(GF(2),t), matrix(ZZ,A).echelon_form(transformation=True))
R = E0*R0
E = R*A
print(R)
# [0 1 1 1 0 0 0 1 0 1 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0]
#[0 1 1 1 0 1 1 1 0 1 0 0 1 1 1 1 0 1 1 1 1 0 1 0 1]
#           :                      :
#[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
#[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
```

## 直交基底ベクトルV1,V2をEから求める

直交基底ベクトルV1, V2はEの右の2列から以下のように求まります。（固有ベクトルを使っても求まりそうです）

```python 
V1 = vector(GF(2),[0,1,1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,0])
V2 = vector(GF(2),[1,0,1,0,1,1,0,1,0,1,0,0,0,0,0,1,0,1,0,1,1,0,1,0,1])
```

## V1,V2を使って解ける問題かどうかを判定する

<img width=35%><img width=30% alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1922159/3f7fe4ba-43db-7640-b59b-4a78e62c2d14.png">

例えばこの問題をベクトル化すると以下のようになります。

```python 
f = vector(GF(2),[0,0,1,0,0, 0,1,1,1,0, 0,0,1,0,0, 0,0,1,0,0, 0,0,1,0,0])
```
問題が解ける条件は

```math
fがV1,V2と直行する \\
すなわち \\
内積 f \cdot V1 = 0 かつ f \cdot V1 = 0
```
内積をチェックすると解けるということが分かります。

```python 
print("f*V1=",f*V1,", f*V2=",f*V2)
# f*V1= 0 , f*V2= 0
```


## Lights Outの解答の1つを求める

上で求めた$R$を使うと以下の式から簡単に答えの1つが求まります。

```math 
x_1 = Rf \ \ \ \  
```
従ってこの解答の1つは以下のようなり。分かりやすいように5 x 5のマトリクスに戻して表示しています。また検算として$A*x1$で元に戻ることも確認できました。

```python 
x1 =  R*f
print("---Answer #1 , --")
print(matrix(GF(2),m,n,x1))
print(".verification .")
print(matrix(GF(2),m,n,A*x1))
#---Answer #1 --
#[0 0 1 1 1]
#[0 1 1 1 0]
#[1 1 1 0 0]
#[0 0 0 0 0]
#[1 1 0 0 0]
#-verification-
#[0 0 1 0 0]
#[0 1 1 1 0]
#[0 0 1 0 0]
#[0 0 1 0 0]
#[0 0 1 0 0]
```
## Lights Outの最適解を求める

上記論文によると5 x 5の場合は解ける場合は4つの答えがあります。（行列の自由度=$25-23=2$なので$2^2=4$）

```math 
x_1 = Rf \\
x_2 = Rf + V1 \\
x_3 = Rf + V2 \\
x_4 = Rf + V1 + V2 \\
```

これらを各々求めて手数の数えて最小のものを選びます。一見ベクトルの要素のsumをとれば良さそうですがそのままだと剰余２の計算になってしまうので**lift()**　プロパティを使って整数に戻す必要があります。

この問題の場合には手数が11の$x_1$が最適解だと言う事が分かりました。

```python 
x1 =  R*f
x2 =  R*f+V1
x3 =  R*f+V2
x4 =  R*f+V1+V2
print(f"Steps: x1={sum(x1.lift())},x2={sum(x2.lift())},x3={sum(x2.lift())},x4={sum(x2.lift())}")
# Steps: x1=11,x2=15,x3=15,x4=15
```

### 解答を見るのは最初の1行でよい

この論文の最後に面白いことが書いてあります

> 解答の最初の1行のボタンを押したら2行目からは上の行の点灯しているものの下のボタンを押して行けばよい

これは例えば今回の例題の場合1行目の答えは右の3つなので、まずこれを押して、2行目からは答えを見なくても上の行の点灯している下のボタンを押して行けばクリアできるということです。もしよければ試してみてください。

```python 
#[0 0 1 1 1]
```

### 次のステップ

今回は5 x 5の問題に関して解くことができるかどうかの判定とその解き方を紹介しました。次回は一般にm x nの問題が必ず解けるのかどうかを調べてみたいと思います。

（開発環境：Cocalc Sage Worksheet）
PowershellでSSL証明書情報取得時にTLSエラーにハマッた件

# はじめに
Powershell でサーバー証明書管理スクリプトでも作ってみた。
その際にまたもやハマッたTLSエラーの対応をここに綴る。※私の投稿はTLSエラーが多いです、、

以下取得コマンドでエラーが発生する。

```Powershell
$stream.AuthenticateAsClient($commonName)

"1"個の引数を指定して"AuthenticateAsClient"を呼び出し中に例外が発生しました:"SSPIへの呼び出しに失敗しました。内部例外を参照してください。"

````

# エラーが起きる環境・前提条件
**Windows Server 2016**
Window 10 21H2 だったら正常にコマンドが通る。
恐らくこれもOS依存のTLSのバージョンの問題。

# 対応
技術系QA teratail に記載されていた情報（投稿主）をもとに以下パラメータを指定したらすんなり通った。
```Powershell 
# 下記を参考にしてオーバーロード
# AuthenticateAsClient(String, X509CertificateCollection, SslProtocols, Boolean)
# Tls12:3072	TLS1.2セキュリティ プロトコルを指定します。
$stream.AuthenticateAsClient($commonName, $null, "3072", $false)
```

因みにteratailの投稿主は上記パラメータでもエラーになっており、そのため投稿していたみたい。
https://teratail.com/questions/329872?sort=2
WindowsでJavaをインストールせずに使う／SikuliXを持ち運び可能にする

+ RPAを導入したいけど、お金はない。
+ SikuliX使いたいけど、Javaをインストールしたくない。

そんな人向けの情報です。

----
## 手順

以下、Oracle OpenJDK を例にします。
1. [jdk.java.net](https://jdk.java.net/) から、Java8 以上で64bit版のWindows用buildをダウンロードします。
   ex) [openjdk-20-ea+13_windows-x64_bin.zip](https://jdk.java.net/20/)
1. ダウンロードしたファイルを解凍して、`jdk-xx` フォルダを取り出します。
   また、`jdk-xx` フォルダと同じ場所に `sikulixide-2.0.5.jar` を配備します。
1. 環境変数`PATH`に`java.exe` を追加するバッチファイルを作成し、バッチファイルから SikuliX を起動します。
   **例）JDK-20 の場合**
```cmd: sikulix_start.bat
@echo off
pushd %~dp0

set SikuliX=sikulixide-2.0.5.jar
set JAVA_HOME=jdk-20
set PATH=%JAVA_HOME%\bin;%JAVA_HOME%\bin\java.exe;%PATH%

java -version
start "" /min java -jar %SikuliX%
```


----
## バッチファイルについて
* `%~dp0` はバッチファイルの置いてあるフォルダを示します。
  このバッチファイルは `pushd %~dp0` としており、ネットワークフォルダ内からでも実行できます。
* バージョンの切り替えを簡単にするため、
  SikuliXファイル名を環境変数`SikuliX`、
  解凍したフォルダ名を環境変数`JAVA_HOME`にセットしています。
* インストール済みのJavaがあった場合も、
  環境変数`PATH` の先頭に `java.exe` を設定することで優先利用します。
* SikuliX の起動は `java -jar sikulixide-2.0.5.jar` だけで可能ですが、
  `start`コマンドと組み合わせることで**コマンドプロンプト**のウィンドウが残らなくなります。
  ただし、SikuliX IDE 以外に **Javaのプロンプトウィンドウ** が表示され、
  このウィンドウを閉じると SikuliX も終了します。ジャマなので`/min`で最小化しています。



----
## 参考情報

#### インストールせずに Java を使う
* https://qiita.com/t-okushima/items/5010b9ab1b3dc8da317f
* https://blogs.osdn.jp/2016/03/28/ExtractJDK.html
* https://www.fit.ac.jp/~m-ishihara/Lectures/JavaProgramming1/howToSetupOnWin10new/howToSetup.html


#### Javaディストリビューション
* https://qiita.com/yamadamn/items/2dd26a014791b9557199
PID(+R)コントローラの離散化のテンプレ

# 古典制御手法の離散化
組み込みフィードバック制御をお前に教える
俺はなるべく双一次変換を使いたいけど貴様は？



## 変数

+ $T_s$は周期時間
+ $T_u$は緩和時間
  + $K_u$は緩和時間$=\dfrac{T_u}{T_s}$


## z変換の実装
sドメインからzドメインに変換する時の双一次変換は

<div align='center' class='scroll'>
$$
    s \to \dfrac{2}{T_s} \dfrac{1-\frac{1}{z}}{1+\frac{1}{z}}
$$
</div>

で定義される



## 離散化制御器の実装
上で得た式をそのまんまテンプレート

<div align='center' class='scroll'>
$$
    H_z = \dfrac{\Sigma_{p=0}^{m}\frac{a_p}{z^p}}{1-\Sigma_{q=0}^{n}\frac{b_q}{z^q}}
$$
</div>

に対応させてIIRにぶち込めばおk
$a$はフィードフォワード，$b$はフィードバック，$z$の次数に対応させて遅延をかければヨシ




# 一覧



## 比例（P）コントローラ
まぁそれはそう

<div align='center' class='scroll'>
$$
    K_P \to K_P
$$
</div>



## 積分（I）コントローラ
1次遅れ系の定常偏差を消去する時に使うやつ

<div align='center' class='scroll'>
$$
    \dfrac{K_I}{s} \to \dfrac{\frac{T_sK_I}{2}+\frac{T_sK_I}{2z}}{1-\frac{1}{z}}
$$
</div>



## 微分（D）コントローラ
分野的に使ったことないからよくわからん<br>
が，擬似微分の実装は大事なので書いておく

<div align='center' class='scroll'>
$$
    \dfrac{\rm{d}}{\rm{d}t} \to \dfrac{s}{1+T_us}
    \to \dfrac{\frac{2}{T_s(1+2K_u)}+\frac{-2}{T_s(1+2K_u)z}}{1-\frac{2K_u-1}{(2K_u+1)z}} \\
    \therefore K_Ds \simeq \dfrac{K_Ds}{1+T_us} \\
    \to \dfrac{\frac{2K_D}{T_s(1+2K_u)}+\frac{-2K_D}{T_s(1+2K_u)z}}{1-\frac{2K_u-1}{(2K_u+1)z}}
$$
</div>



## 共鳴（R）コントローラ
あんま聞かないかもしれんが，特定の周波数成分だけゲインを爆上げできる<br>
通過させる周波数$\omega_c$と通過させる帯域$\omega_b$と帯域ゲイン$K_R$の設定が必要になる
負ループ組んだ時の安定性は勝手に確認してくれ，ボード線図が必要かも

<div align='center' class='scroll'>
$$
    \dfrac{K_R\omega_bs}{s^2+\omega_bs+\omega_c^2}
    \to \dfrac{ \frac{2T_sK_R\omega_b}{4+2T_s\omega_b+T_s^2\omega_c^2} + \frac{0}{z} + \frac{-2T_sK_R\omega_b}{(4+2T_s\omega_b+T_s^2\omega_c^2)z^2} }{ 1 - \frac{8-2T_s^2\omega_c^2}{(4+2T_s\omega_b+T_s^2\omega_c^2)z} - \frac{2T_s\omega_b-4-T_s^2\omega_c^2}{(4+2T_s\omega_b+T_s^2\omega_c^2)z^2} }
$$
</div>
VitrualBoxの仮想ディスクの未使用領域の解放


# 概要
VirtualBoxでDBの再作成を繰り返していたら仮想ディスクのwindows上のサイズが大きくなりすぎたため未使用領域を解放したときのメモです。

約50GB->14GBまで戻りました。

VBoxManage.exeの説明によれば 未使用領域を 0埋め後 「VBoxManage.exe modifymedium uuid  --compact」で未使用領域が解放されるとなっています。[(参考)](https://www.virtualbox.org/manual/ch08.html#vboxmanage-intro:~:text=The%20%2D%2Dcompact%20option)



# 環境
「windows10」の「VirtualBox 6.1」で「almalinux」を実行しています。

# 手順
1. linuxの空き領域をzero埋めし、
2. linuxのshutdoun後
3. windows上より modifymedium --compact　で未使用領域を解放します。

# 解放前の状態

* windowsのディスク容量
```
c:\>dir
2022/09/02  18:55    51,350,863,872 vbox.vdi
```
* linuxのディスク容量
```
> df -h
ファイルシス               サイズ  使用  残り 使用% マウント位置
devtmpfs                     1.9G     0  1.9G    0% /dev
tmpfs                        2.0G   16K  2.0G    1% /dev/shm
tmpfs                        2.0G  8.6M  1.9G    1% /run
tmpfs                        2.0G     0  2.0G    0% /sys/fs/cgroup
/dev/mapper/almalinux-root    47G   14G   34G   29% /
/dev/sda1                   1014M  282M  733M   28% /boot
share                        238G  222G   16G   94% /mnt/share
tmpfs                        390M     0  390M    0% /run/user/0
```

実ディスク容量は約50G、linux上の使用領域は14G


# 実際の作業

1. linuxの空き領域をzero埋めし、
    ```
    >dd if=/dev/zero of=zero bs=4k
    >rm zero
    ```
2. linuxのshutdoun後
対象のVMを停止する。


3. windows上より modifymedium --compact　で未使用領域を解放します。

* VBoxManage.exe" list hdds にて対象ディスクのUUIDを確認する（今回の場合は 80c51a57････）
```
C:\>"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" list hdds

UUID:           80c51a57-d8b0-4be8-b551-a8451902b2b9
Parent UUID:    base
State:          created
Type:           normal (base)
Location:       C:\vbox\vdisk\vbox.vdi
Storage format: VDI
Capacity:       204800 MBytes
Encryption:     disabled
```

* modifymedium --compactを実行しディスクを解放します

```
C:\>"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" modifymedium 80c51a57-d8b0-4be8-b551-a8451902b2b9 --compact
0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
```

# 解放後の状態

* windowsのディスク容量
```
C:\>dir
2022/09/02  19:50    15,730,737,152 vbox.vdi
```

* linuxのディスク容量
```
> df -h
ファイルシス               サイズ  使用  残り 使用% マウント位置
devtmpfs                     1.9G     0  1.9G    0% /dev
tmpfs                        2.0G   16K  2.0G    1% /dev/shm
tmpfs                        2.0G  8.6M  1.9G    1% /run
tmpfs                        2.0G     0  2.0G    0% /sys/fs/cgroup
/dev/mapper/almalinux-root    47G   14G   34G   29% /
/dev/sda1                   1014M  282M  733M   28% /boot
share                        238G  222G   16G   94% /mnt/share
tmpfs                        390M     0  390M    0% /run/user/0
```



# 結果
windows上のディスクを約35GB解放しました。

               
# 参考　               
[VirtualBox - 仮想ディスクを圧縮する](https://freebsd.sing.ne.jp/tool/08/02/11.html)
[VirtualBox公式ドキュメント VBoxManage modifymedium --compact](https://www.virtualbox.org/manual/ch08.html#vboxmanage-intro:~:text=The%20%2D%2Dcompact%20option)
移送プロファイル(DBTYPE)でのHANADB設定値

# 移送プロファイル(DBTYPE)をHANADBを設定しようとするも見つからず...
HANA DBの設定値についての記述がない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821335/4d86bfd1-3015-8ac3-c715-49b324b00cfd.png)


# 結論：HANADBはDBTYPE = hdbで設定すればOK
SAPのOSSでnote見つけました。


**2594555 - HANA DB setting in STMS configuration**
>This descrption has yet to be updated for HANA DB where value 'hdb' should be used

とのことなんで、hdbで設定すればOKです。(S/4HANA2021なんですがいつアップデートされるんでしょうか...)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821335/da6365ff-8977-0d8d-7bd0-dc6217fb95ad.png)


:::note warn 
### 豆知識
HANADBではホスト名は小文字推奨です。(それ説明するためにDBHOSTもスクショ取りました)
【論文読み】行動グラフの畳み込みによるユーザー埋め込み表現を用いた行動予測

## はじめに

こんにちは。論文を読んだ結果の紹介記事です。  
今回紹介するのは、「行動グラフの畳み込みによるユーザー埋め込み表現を用いた行動予測」という論文です。  

- タイトル：行動グラフの畳み込みによるユーザー埋め込み表現を用いた行動予測
- 出典：稲垣青空, et al.; 人工知能学会, 2020   
- URL：https://www.jstage.jst.go.jp/article/pjsai/JSAI2020/0/JSAI2020_2I6GS203/_pdf/-char/ja  


## 概要

- ユーザIDの埋め込み表現により各ユーザに対する予測値を算出すると、
記号としての意味しか持たず、学習データにないユーザIDの埋め込み表現を求められない。  
- 提案手法では、訪れた場所やカテゴリをノード、その間の遷移をエッジとする行動グラフをユーザ毎に作成。
このグラフに対してGCNを適用することで、カテゴリ間の遷移の違い及び訪問したカテゴリの違いを捉えたベクトル表現を得る。
得られた表現をユーザのカテゴリ間の遷移嗜好を表す行動グラフ埋め込み表現として、行動予測に用いる。
- 埋め込み表現を、各カテゴリへの訪問頻度と組み合わせることにより、行動予測モデルがさらに向上する。


## 技術的特徴
- 提案システム構成は以下3つを結合してシグモイドを適用し、各カテゴリ訪問確率を出力する構成である  。
    - 周期：過去３週間の予測日と同じ曜日の履歴に対するCNN  
    - 直近：予測日から過去７日の履歴を用いたGRU+Attention  
    - 行動グラフ：予測日より過去全ての履歴の全結合層と、行動グラフをインプットにしたGCN  
  
- データは995人、1年1カ月分、のデータ。ユーザID、時刻、場所IDを持つ。88カテゴリ。学習データは9カ月、テストデータは4カ月。
  
- 比較手法
    - 基本モデル = 直近＋周期
    - 基本モデル + UserID埋め込み
    - 基本モデル + 訪問頻度のみ
    - 基本モデル + 行動グラフのみ

## 想定される適用例
- 次の行動予測全般  


## 参考
- [行動グラフの畳み込みによるユーザー埋め込み表現を用いた行動予測](https://www.jstage.jst.go.jp/article/pjsai/JSAI2020/0/JSAI2020_2I6GS203/_pdf/-char/ja)
Mediapipeを用いたFlaskアプリのHerokuへのデプロイ方法

# 背景
Mediapipeを扱うアプリを作成したときにデプロイはできたがApplication Errorが起きて正常に動作しなかったので備忘録として置いておく。
MediapipeはOpenCVをもとに作っているので直接仮想環境にOpenCVをインストールしなくてもHeroku上では必要になってくる。

# Heroku側の準備
herokuのsettingAのところのBuildPacksにopencvを追加する
![スクリーンショット 2022-09-02 212727.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1878034/faae2f22-cc4f-5384-ef4c-90014a829b67.png)

追加には以下のURLをコピーして追加する
https://github.com/heroku/heroku-buildpack-apt

# 実行ファイル側の準備
`requirements.txt`に`opencv-python-headless`を追加するだけ。
`requirements.txt`は実行ファイルと同じ階層に作成する。
```text:requirements.text
必要なライブラリの名前とバージョンを書く
opencv-python-headless
```

これでだめなら`Aptfile`を`requirements.txt`と同じ階層に作成して以下の内容を書く。
```:Aptfile
libgl1
```
あとはこれでデプロイするだけ。

# 参考

https://docs.streamlit.io/knowledge-base/dependencies/libgl
関数について

## 基本的なパターン

👇 関数は、引数を取るもの、取らないもの。返り値を戻すもの、戻さないものがあり、  
それ以外にも　いろいろある  

ひとまず、めちゃくちゃな関数を説明する前に、 `関数` と聞いて思いつく、基本的なパターンを示す  

👇 まず、もっとも基本  

```
f()
f(x)
y = f()
y = f(x) 
```

f は関数、 x と y は変数かもしれないし、関数かもしれない  

👇 リストになっている例

```
f(x,y,z)
u,v,w = f()
u,v,w = f(x,y,z)
```

以上の基本的な関数は、**名前のある関数** だ。
`f()` という記述を見ても、関数の中身は分からない、というのが ふつう。  
関数の中身が分からないのに、数学マンやプログラマーは　どうやって関数を使っているのかというと、  
**どこかで使い方を覚えてきて、その集団の中でのルールに従って、決めつけて使う**  

👇 関数に名前がない例

```
    ( ) => { }
    (x) => x + 1
y = ( ) => 1
y = (x) => x + 1
    ---    -----
    11     12
    ------------
    10
10. ここが名前の無い関数
11. 引数
12. 関数の本体
```

`f` という名前は味気ないので　何か付けるとする  

```
do()
set(x)
y = get()
y = convert(x) 
```

基本的に x は変わらず、 y は上書きされる  

しかし　プログラム言語では、この基本は　めちゃくちゃになる  

x は変わる、とすることができる。 例えば、C# の `ref x` など。  
また、 x は渡すのではなく戻ってくるものもある。 例えば、C# の `out x` など。  
さらに、 x は別の変数 z を指していて、 z が変わるなど。  例えば、参照やポインターなど  

## 外部変数

また、外部変数があるケースもある。  
👇 a という変数があるとする  

```
a = 10
```

そして do 関数を呼び出すとする  

```
do()
```

すると a の値は 11 になっているかもしれない  

そんなプログラムを書かれたら何が起こるか分からないが、何が起こるか分からない関数は ふつうによくある。  
行儀のよいプログラム、行儀の悪いプログラムの違いは存在する  

## オブジェクト

👇 疑似コード  

```
class B {
    member a = 10
    member do()
}

b = new B
b.do()
```

b という変数がさらに a という変数と、 do という関数を持っていて、  
b の持っている do という関数を呼び出しても、 a が変わることはあっても  
b の持っていない変数は変わることがない、  

というのだったら　読みやすいし、行儀は良くなる  

しかし、行儀の悪いプログラムは　ふつうによくある  

## 外部メディア

👇 疑似設定ファイル 📄 `taro.txt`

```
family_name = "nihon"
birth = 20220902
weight_kg = 90
```

👇 疑似コード

```
eat()
```

この何をやっているか分からない関数 `eat()` を呼び出すと、  
📄 `taro.txt` の中身が `weight_kg = 91` に変わってるかもしれない  

こんなことは　ふつうによくある  

そんなことが　ふつうによくあって、プログラマーはどうやって　プログラムを読めているのか？  
というと **読めていない** し、 **不具合が表面化したときに気づく**  

## お行儀の悪い関数

👇 疑似コード

```
y = add(2, 3)
```

なんとなく 2 と 3 を足して、 y には 5 が入ると期待するかもしれない。  
しかしソースを見ると  

```
function add(x1, x2)
{
    eat()
    return 2 + 3
}
```

📄 `taro.txt` の中身が `weight_kg = 92` に変わって y に 5 が入るかもしれない  

そんなお行儀の悪いことができるプログラムで　どうやってプログラマーが  
まともにプログラムを組めるかというと、  
**周りのプログラマーがお行儀がよかった** に尽きる  

お行儀の悪いプログラムを書いて何が悪いんだ？　という質問は、  
自分のソースを読む１年後の自分に聞いてほしい。  
他の人は別に困らない。  

## お行儀の良い関数

それがお行儀の良い関数かどうかを　外部から判定する方法が少ない  
ネットワークにアクセスできたり、ファイルを読み書きできる時点でアウト  

どこか　閉じられたネットワークにあるパソコンで　プログラムを動かしてみて  
１年放置してみて、悪いことしなければ　お行儀が良い……  

ということでは　１年１日目に悪いことをするプログラムを見逃して　アウト  

関数の内部が **ブラックボックス** だと　お行儀の悪さ　を避けれないので、  
未来的には、
**ソースのお行儀が悪いか** を濃淡で評価するAIは出てくるように思う