OmniAICreator/Qiita-1.07M huggingface dataset:
LightsailにWebシステムを構築する手順

Exmentをさくらインターネット上に構築していたんですが、動作がどうも重かったのでAWSに移行することにしました。
その移行手順をメモしておきたいと思います。

# 全体の流れ
1. AWSでLightsailコンソールにログイン
1. 新たにLAMPインスタンスを作成する
1. StaticIPを設定する
1. SSHでログインしてphpmyadminのアクセス設定
1. 旧サーバからDBとファイルを移動
1. httpd.confを設定
1. php.iniを設定
1. DNS設定変更及びSSL証明書の発行

# AWSでLightsailコンソールにログイン
新たにAWSにルートアカウントを登録してLightsailコンソールにログインします。

# 新たにLAMPインスタンスを作成する
新たにLinuxインスタンスを作成するとともにLAMP環境を構築します。
全てLightsailがインスタンス作成と同時に構築してくれます。

https://dev.classmethod.jp/articles/amazon-lightsail-lamp-stack/


# StaticIPを設定する
インスタンスを再起動するとIPアドレスが変わってしまうので、固定IPアドレスを取得して設定します。

https://lightsail.aws.amazon.com/ls/docs/ja_jp/articles/lightsail-create-static-ip

# SSHでログインしてphpmyadminのアクセス設定
自動でインストールされているphpmyadminは、Web経由では基本的にアクセスできません。
Bitnamiのドキュメントによると、sshトンネルをパソコン側に設定しないといけないとのことです。

https://docs.bitnami.com/virtual-machine/faq/get-started/access-phpmyadmin/

都度トンネル設定するのは結構面倒なので、Basic認証を設定した上でどこからでもアクセスできるように設定することにします。
いくつか参考になるサイトはあったのですが、どうも最新版のBitnamiではphpmyadmin.confの場所が変わっているようです(インストールされたBitnamiは7.4.25-6)。

```:/opt/bitnami/apache/conf/bitnami/phpmyadmin.conf
Alias /phpmyadmin "/opt/bitnami/phpmyadmin"
<Directory "/opt/bitnami/phpmyadmin">
  Options -Indexes +FollowSymLinks -MultiViews
  AllowOverride All

  Require all granted
  AuthType Basic
  AuthName phpmyadmin
  AuthUserFile "/opt/bitnami/apache/users"
  Require valid-user
</Directory>
```

Require all granted以下が編集した部分です。
usersファイルを作成して、Basic認証用のIDとパスワードを設定します。
設定が終わったらapacheを再起動します。

```
$ sudo /opt/bitnami/ctlscript.sh restart apache 
```

# 旧サーバからDBとファイルを移動
phpmyadminとFTPを利用して旧サーバのデータベースとファイル群を移動させます。

https://lightsail.info/ftp-client-access/

# httpd.confを設定
httpd.confに新しいファイル群にアクセスできるように設定を追加します。
今回はphpmyadmin.confを参考にexment.confを作成し、httpd.conf内でincludeしました。

```:/opt/bitnami/apache/conf/bitnami/exment.conf
Alias /phpmyadmin "/opt/bitnami/exment"
<Directory "/opt/bitnami/exment">
  Options -Indexes +FollowSymLinks -MultiViews
  AllowOverride All

  Require all granted
</Directory>
```

設定が終わったらapacheを再起動します。

```
$ sudo /opt/bitnami/ctlscript.sh restart apache 
```

# php.iniを設定
exmentを利用するのにメモリやアップロードファイルサイズなどを設定する必要があるので、php.iniを編集しました。
php.iniは/opt/bitnami/php/etc/にあります。
php.iniの修正を反映させるためにはapacheだけでなく、php-fpmの再起動も必要になります。

https://dantora.net/2018/08/20/bitnami%E3%81%A7php-ini%E3%81%AE%E5%A4%89%E6%9B%B4%E3%81%8C%E5%8F%8D%E6%98%A0%E3%81%95%E3%82%8C%E3%81%AA%E3%81%84%E3%81%A8%E3%81%8D/

```
$ sudo /opt/bitnami/ctlscript.sh restart apache 
$ sudo /opt/bitnami/ctlscript.sh restart php-fpm 
```

# DNS設定とSSL証明書発行
既にさくらインターネットでドメインは取っていたので、DNSサーバに設定を追加してドメイン経由でアクセスできるようにAレコードを設定しました。
SSL証明書はLet's Encryptを使用して生成。

https://myscreate.com/migration-sakura-from-aws/

# まとめ
ここまでできれば基本的にドメイン＆https経由でサーバにアクセス可能になります。
試行錯誤しながらなので結局1日かかりました。
ソリューションと評価

ソリューション導入・運用のための個人的検討の覚書です。

# ワンソリューションとマルチソリューションの比較

- 単にソリューションを増やすだけではコストは増加し、品質は劣化する
- 正解/不正解はなく、組織や企業の規模や環境、IT投資可能な予算、外的要因などによって状況は多様に変化する
- 現在はワンソリューションへの集約の方が主流(な気がする)

| モデル | ワンソリューション | マルチソリューション |
|:--|:--|:--|
| 例 | チャットツールは Slack だけ | チャットツールは Slack、Teams など |
| メリット | ・集約によるコスト低減<br>・集約による利便性の向上<br>・リソース集中による高品質維持 | ・多様な働き方を支援し、ユーザの生産性を向上 |
| デメリット | ・多様なユーザニーズに応えられない | ・分散によるコスト増加<br>・リソース分散による品質維持の難易度向上 |

# 対策

1. 職種の特性に合わせたソリューションの提供<br>
例：開発部門は Slack、全社共通は Teams
1. 評価指標の設定とバランスの考慮<br>
・評価指標を設定し、「費用対効果」が出ているかを継続的に評価。<br>
・不要なものは解約し、バランスを取る。<br>
例：<br>
・[KGI] ユーザ満足度平均 XX 以上、1人当りのコスト XX 円以下/月<br>
・[KPI] 呼損率 XX% 以内、新規案件起案 XX 件以上
1. まとめるところはまとめる<br>
例：ID やセキュリティ、データ管理は Google Workspace を利用

:::note
<b>呼損率</b>
通信回線(設備)の容量不足によって、通信または通話がつながらない割合。
要求された全呼量に対し、実際につながらなかった呼量を割った値で表される。
:::
AWS LambdaでCSVを一時領域を利用せずS3に保存する

## はじめに
Lambdaでは一時領域として`/tmp`フォルダが提供されていますが、上限512MBのまでの制限があり大容量のCSVファイル生成があった場合に制限がかかっていました。
csvにストリーム出力してS3に保存する方法があるか調べてみました。

## 変更前
変更前のCSV作成〜s3アップロードは以下になります。
`/tmp`領域にファイル作成してS3にアップロードしていたので512MBを超えるファイルの作成はできませんでした。

```python
import csv
import boto3
import psycopg2
import psycopg2.extras

def get_conn():
    return psycopg2.connect(
        host='hostname',
        port=5432,
        database='xxxxxx',
        user='xxxxx',
        password='xxxxx',
    )

def get_data():
    with get_conn() as conn:
        with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
            sql = 'SELECT 〜'
            cur.execute(sql)
            rows = cur.fetchall()
            return rows

def upload_s3(src, distname, prefix):
    s3 = boto3.client('s3')
    with open(src, 'rb') as f:
        key = "{}{}".format(prefix, distname)
        s3.put_object(Bucket="BuketName", Key=key, Body=f)
    os.remove(src)

def write_tmp_data():
    with open('/tmp/tmp.csv', 'w', newline='', encoding='utf-8-sig') as f:
        csvfile = csv.writer(f)
        data = get_data()

        csvfile.writerow(['column1', 'column2', 'column3']) # タイトル行出力
        csvfile.writerows(data) # データ書き出し
   upload_s3('/tmp/tmp.csv', 'data.csv', '/tmp')

def handler(event, context):
    write_tmp_data()
```

## 変更後

```python
import csv
import boto3
import psycopg2
import psycopg2.extras

def get_conn():
    return psycopg2.connect(
        host='hostname',
        port=5432,
        database='xxxxxx',
        user='xxxxx',
        password='xxxxx',
    )

def get_data():
    with get_conn() as conn:
        with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur:
            sql = 'SELECT 〜'
            cur.execute(sql)
            rows = cur.fetchall()
            return rows

def upload_s3(buffer, distname, prefix):
    s3 = boto3.client('s3')
    with open(src, 'rb') as f:
        key = "{}{}".format(prefix, distname)
        s3.put_object(Bucket="BuketName", Key=key, Body=buffer)

def write_tmp_data():
    raw = io.BytesIO()
    buffered = io.BufferedWriter(raw)

    with io.TextIOWrapper(buffered, encoding='utf-8-sig', errors='strict', newline='') as f:
        csvfile = csv.writer(f)
        data = get_data()

        csvfile.writerow(['column1', 'column2', 'column3']) # タイトル行出力
        csvfile.writerows(data) # データ書き出し

def handler(event, context):
    write_tmp_data()
```
`io.StringIO（）`でもストリームへの書き込みが可能でしたが、エンコードが`UTF-8`固定となってしまいます。
作成したCSVをExcelで文字化けせずに開けるようにするため、エンコード指定が可能な`io. TextIOWrapper()`にしました。

## 最後に
ECSのFargateに移行するかElastic File System(EFS)を導入するか悩みましたが、最小限の修正で大容量ファイルを作成できるようになりました。
あとは、Lambdaのメモリ設定だけを気にすればよいはずです。

## 参考
- [第4回　「俺様プチencoding」を実装して理解するPython3.0のioとcodec，encodingの機構：Python 3.0 Hacks｜gihyo.jp … 技術評論社](https://gihyo.jp/dev/serial/01/pythonhacks/0004)
Power Apps Portals で詳細フォームを公開する

こんにちは。日本マイクロソフトの向井です。

今回はPower Apps Portals で作成した詳細フォームを公開する手順を紹介します。

この記事では作成済みの詳細フォームに対して
①該当のテーブルのテーブルアクセス許可を作成し
②公開先のWebロールにテーブルアクセス許可を付与し、
③ポータルに詳細フォームを埋め込む
方法を紹介します。

詳細フォームの作成方法については以下の記事を参考にしてください。
[Power Apps Portals で詳細フォームを作成する](https://qiita.com/kumukai/items/5d173d31d3782bbdd02a)

#1. 該当テーブルのテーブルアクセス許可を作成する
ポータル管理アプリを開き、左のメニューからテーブルのアクセス許可を選択し、新規をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/dd57a218-5166-9fc8-18b8-12c7a581f4e3.png)

以下を参考に設定を行います。

名前：任意の名前
テーブル名：作成したフォームで使用しているテーブル
Webサイト：フォームを公開したい作成済みのWebページ
アクセスの種類：[こちらのDocs](https://docs.microsoft.com/ja-jp/powerapps/maker/portals/configure/entity-permissions-studio#available-access-types-in-studio)を参考に相応しいものを選択

Privileges：このフォームに対してユーザーに与えたい権限を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/8f124f4c-d017-993a-139e-ff3eb12cbf6f.png)

保存をして閉じるを選択します。

#2. 作成したテーブルアクセス許可をWebロールに付与する
左のメニューからWebロールを選択し、該当のWebロールを選択または新規作成してください。
Webロールを選択するときは正しいWebサイトのWebロールであるか確認してください。
Webロールについては[こちらのDocs](https://docs.microsoft.com/ja-jp/powerapps/maker/portals/configure/create-web-roles)を参考にしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/8553fbbe-1fef-1c89-c914-f2fdcdad8a33.png)

関連＞テーブルのアクセス許可を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/2625dc85-be72-f382-8efe-8a9bf54165f6.png)

既存のテーブルのアクセス許可をクリックし、先ほど1. で作成したテーブルアクセス許可を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/b9d4b7f7-5166-7dde-80d7-9a8ec6ad2fd8.png)

#3. ポータルに詳細フォームを埋め込む
ポータルスタジオを起動し、詳細フォームを埋め込みたいページに移動します。
フォームを埋め込みたい場所を選択し、右下にある</>マークをクリックしてソースコードエディタを開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/0556844e-ccf3-660f-cac0-1553784a050f.png)

3行目の末尾の<div>の前に、以下のように入力します。
<div>{% webform name: '作成した詳細フォームの名前' %}</div>
入力後、保存をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/1d74a663-a89c-b70b-3afe-42a49fd41da4.png)

最後に同期の構成をクリックして完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/9b95d80d-8c34-d5db-ba4f-ad2b5e82d723.png)

#4. 動作の確認
詳細フォームが表示されていることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/2ad1ee4e-f86b-6fdd-a080-c6957888b358.png)

[こちらの記事](https://qiita.com/kumukai/items/5d173d31d3782bbdd02a)で紹介した通り、詳細フォームでは条件分岐や複数のテーブルにまたがる入力を作成することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/2f34b038-2dbb-ef2b-5c36-1b5dc33311da.png)
GitHubの新しいリポジトリに既存アプリのリソースコードを登録する

# 作業
まず、ローカルでFinderから
既存アプリのコードをコピーして、名前を変更する。

GitHub上のコードをgit　cloneコマンドでコピーすると
.gitignoreで除外されているファイルがコピーできない。

![スクリーンショット 2022-01-05 9.28.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1380401/572f2f46-3c2c-c3a2-2be2-0cbbd1cdf771.png)

続いて、GitHubで新しいリポジトリを作成する。

![スクリーンショット 2022-01-05 9.39.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1380401/20d6bbd7-b56a-edda-faeb-14d5d0681eee.png)


最後に、ターミナルからコマンド実行していく。

```shell:ターミナル

# ディレクトリに移動
% cd new_app

# 既存アプリのファイルが存在するか確認
% ls -a

# 以前までのコミット履歴も確認
% git log

# Gitに設定されているオリジンを確認
% git remote -v

# 同オリジンを削除
% git remote rm origin

# 送信先を新しいリポジトリに変更する
% git remote add origin git@github.com:アカウント名/新しいリポジトリ名.git

# 検証としてファイル追加する
% touch test

# 変更をステージングへ追加する
% git add .

# メッセージ付きでコミットする
% git commit -m '検証'

# GitHubにプッシュする
% git push origin master

```

完
GoogleColabでFlaskアプリを実行する方法(ngrok token必要になった模様)

# <a id ="1">1.概要</a>
2021年12月中旬以降GoogleColaboratoryでngrokを介してwebアプリを立ち上げるにはngorkのtokenが必要になったようです。そこでtokenの取得方法とコード内への記述内容について記します。

【背景】
今までGoogleColab上で使っていたwebアプリ実行のコード(ngrok tokenの記述なし)をそのまま実行すると以下の画面が出てきてWebアプリにアクセスできない。。。→これを解決したい！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/11133213-faaf-1c4b-f7fd-3dbd5e68e231.png)


ちなみに、アクセスしようとすると本文文末(Appendix2)に示すような警告がでるかもしれません。

https://qiita.com/k_koba619/private/32ec04a04d1e37b86465#appendix2%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E6%99%82%E3%81%AE%E8%AD%A6%E5%91%8A



# <a id ="2">2.ngrokuのtoken取得</a>
Webアプリを立ち上げるためには、tokenの取得とコードの追記が必要です。まずtokenの取得をしましょう。

① ngrokにアクセスします。

https://ngrok.com/

②アカウントない方はSign upします。(お持ちの方はLogin、どちらも画面左上リンクあり)

③左メニューの「Your Authtoken」からトークン確認できます
(黄色ハッチで塗りつぶしている部分にtokenの記述があります)
この文字列をどこかに保存しておきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/68b99523-6c8b-5a16-a3ed-87cad5c90288.png)

# <a id ="3">3.webアプリのコード書き換え</a>
上記で取得したtokenを使えばwebアプリを立ち上げることができます。

>- 参照サイト　[Quickly share ML WebApps from Google Colab using ngrok for Free](https://towardsdatascience.com/quickly-share-ml-webapps-from-google-colab-using-ngrok-for-free-ae899ca2661a)

付け足すコードは以下です。

```python
!pip install pyngrok
!ngrok authtoken xxxxx #自分で取得したtokenを入力してください
```


# <a id ="4">4.webアプリのコード実行</a>

コードを実行すると"ngrok.io"を含んだURLが表示されます。これにアクセスするとwebアプリを開けます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/996a40d8-7433-5b21-d9dd-291c152ec428.png)


# <a id ="A1">Appendix1.サンプルコード</a>
私の作ったファイルがあるので、よろしければこちらを使ってお試しください。


https://qiita.com/k_koba619/items/5f10d2e56035a059d9ae#21%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E6%BA%96%E5%82%99

# <a id ="A2">Appendix2.アクセス時の警告</a>
Webアプリはこの先にあるのですが、アクセスすると以下のようなコワい警告が。。。。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613560/2d6b0183-d1e1-6e93-ab92-62bc8806a934.png)

ですがこれを気にせず詳細を表示して「安全でないこのサイトにアクセス」をクリックするとWebアプリにアクセスできます。

※自分がアクセスしておりますが、アクセスは自己責任でお願いいたします。
なお、事前に安全性を確認できるサイトがいくつかあるみたいです
(Googleで「url 安全性確認」で検索したりするとでてきます)

* SecURL

https://securl.nu/

*　gred

https://check.gred.jp/WebscanAction.action?page_type=

以上
wsl1上のスクリプトからSeleniumを使う際にChromeDriverの場所をいちいち指定したくない

seleniumを使ってChromeなどのブラウザを自動操縦する場合、原則Seleniumのドライバーを経由する。Chromeならchromedriverが別途必要。

LinuxにせよMacにせよ、chromedriverをインストールするとほとんどがchromedriverの実行ファイルがパスの通ったところに配置される。

一方でwslの場合、chromedriverはchromedriver.exeというWindowsの実行ファイルなので、wsl上のUbuntuで動くわけではない。

よくある例としてはこのような形でソースが記述される。

```seletest.rb
require "selenium-webdriver"
Selenium::WebDriver::Chrome::Service.driver_path = "./chromedriver.exe"
driver = Selenium::WebDriver.for :chrome
```

driver_pathでchromedriver.exeの場所を直接指定しないといけない。
一方でMacやUbuntu上で動かす際にはこの行が邪魔。
環境によってソースが違うのは悪。

ということで、wslのUbuntu上のパスの通ったところ(例えば `/usr/local/bin`)に、chromedriverというシェルスクリプトを配置し、そこからchromedriver.exeを呼び出せばいい。そのシェルスクリプトには実行権限をつけておくのを忘れずに。

```bash:chromedriver
#!/bin/bash
exec "/mnt/c/driver/chromedriver.exe" "$@"
```

これにより、`Selenium::WebDriver::Chrome::Service.driver_path = "./chromedriver.exe"` の行は不要になり、wsl上のソースと他のプラットフォームのソースが同じになる。

実際のchromedriver.exeの場所は各開発環境によって違うだろうけど、chromedriverコマンド自体はパスが通ってるので、環境による違いは吸収できる。

ただしこれだけで済むのは、WindowsとUbuntuが同一マシンで動いてるwsl1に限った話。wsl2だとWindows側のChromeを使うことよりも、wsl2上のUbuntuでChromeを起動した方がいい。

12/30/2021 時点ではwsl2のプレビューリリースでwsl2上のUbuntu上でGUIアプリとしてChromeが起動できるとのこと。（職場では全部をUbuntu上で動かしてる人もいる）
https://docs.microsoft.com/en-us/windows/wsl/tutorials/gui-apps
【Git】操作コマンド早見表(Mac用)

#背景
Gitを学び始めて、コマンドが色々出てきたので、備忘録として作成(2022年1月5日時点)
macOS : Monterey
git version : 2.34.1
#早見表
学んだ順なので、順番は気にしない・・・
あと、全部は記載していない。

| コマンド | オプション |できること |
|:-:|:-:|:-|
|git init|-|今いるディレクトリをGitリポジトリとして管理できるようにする。|
|git status|-|ワーキングディレクトリとステージングエリアとローカルリポジトリのファイルの差を表示する。|
|git add ファイル名|-|ワーキングディレクトリのファイルをステージングエリアに追加する。|
|git commit|-|ステージングエリアのファイルをローカルリポジトリにコミットする。|
|-|-m "コメント"|"コミットする際のコメント"　を添える。必須|
|git diff|-|ワーキングディレクトリとステージングエリアのファイルの差を表示する。|
|-| --staged|ステージングエリアとローカルリポジトリのファイルの差を表示する。|
|git restore ファイル名|-|ステージングエリアのファイルでワーキングディレクトリのファイルを置き換える（ファイルの変更を取り消せる）。|
|-|--staged|ローカルリポジトリのファイルでステージングエリアのファイルを置き換える（ファイルの変更を取り消せる）。|
|git checkout コミットID|-| 指定したコミットIDをワーキングディレクトリにロードし、そこに移動する。|
|git rm ファイル名||ワーキングディレクトリとステージングエリアの指定したファイルを削除する。|
|-|--cashed|ステージングエリアの指定したファイルを削除する。|
|git mv 移動前ファイル　移動先ファイル|-| ファイルを移動させる。又はファイルの名前を変更する。|
|git log　ファイル名|-|指定したファイル名に対して、変更が加えられたのコミットのログを確認する。ファイルを指定しなければ全てのログを表示する。|
|-|--oneline|各ログを1行で表示する。|
|-|--patch|各ログで何が変更されたのかを表示する。|
|-|--数字|最新のコミットから数字の分だけログを表示する。|
|-|--author="編集者名"|編集者名の人物が編集したコミットのログを表示する。|
|-|--after="日時"|日時に指定した時よりも後のログのみ表示する。|
|-|--grep=""|""で囲まれた文字を含むコミットメッセージを持つコミットのログを表示する。|
|git show コミットID|-|指定したコミットIDのログを表示する。|
|git show HEAD~数字|-|現在のコミットから数字の数だけ前のコミットのログを表示する。|
|git blame ファイル名|-|指定したファイルに変更を加えた人物を表示する。|
|git tag|-|現在指定しているタグの一覧を表示する。|
|git tag タグ名(コミットID)|-|指定したコミットIDのコミットに対してタグを付与する。コミットIDを指定しなければ、最新のコミットにタグが付与される。（軽量タグ）|
|-|-aタグ名(コミットID) -m ""|指定したコミットIDのコミットに対してメッセージをつけたタグを付与する。（注釈付きタグ）|
|-|-d タグ名 |指定したタグを削除する。|

また、Gitでバージョン管理したくないファイルは、.gitignoreファイルを作成し、その中に管理したくないファイルの名前を書く。ただし、.gitignore自体はコミットする必要がある。
言語処理100本ノック俺の解答

もう何年もマネジメントばかりして腕が鈍っているので、[言語処理100本ノック](https://nlp100.github.io/ja/)2020 (Rev 2)で感覚を取り戻しつつ最近のトレンドに触れていく。あまり慣れていないPythonで解答しつつ、ここに記録。改善例を教えてもらえると喜びます。Qiitaを書くのも初めて。

- [第1章: 準備運動](https://qiita.com/sangoro/items/c28e327c29825bff70d5)
- [第2章: UNIXコマンド](https://qiita.com/sangoro/items/f7e79f52ffbe1bb8f5b6)
- [第3章: 正規表現](https://qiita.com/sangoro/items/2fa79480007e2aee2f2e)
- [第4章: 形態素解析](https://qiita.com/sangoro/items/24b294817d40f3986599)
- [第5章: 係り受け解析](https://qiita.com/sangoro/items/85fadd8a21e39ec5ca67)
- [第6章: 機械学習](https://qiita.com/sangoro/items/16bd5f046f56fb62f7db)
言語処理100本ノック俺の解答 第1章

[言語処理100本ノック第1章: 準備運動](https://nlp100.github.io/ja/ch01.html)の俺の解答。[その他の章はこちら](https://qiita.com/sangoro/items/9bb6b5c39e756818cce2)。

# 00～04
略

# 05. n-gram
```Python
def ngram(n, s):
    l = []
    for i in range(0, len(s) - (n - 1)):
        l.append(tuple(s[i:i+n]))
    return l
s = "I am an NLPer"
# 単語bigram
print(ngram(2, s.split(" ")))
# 文字bigram
print(ngram(2, list(s)))
```
この解答では扱わなかったけど、n-gramにおいて、BOSやEOSを扱ったほうが実用上はいい場合も多いだろうな、とは、ちょっと思った。例えばbigramでBOSと最初の単語のペアを1要素とする、とか。1トークンしかない文もあるしね。

# 06. 集合
さっきの問題で定義した関数ngram()を使っています。
差集合というのは、X-YとY-Xの両方ありますね。

```Python
s_x = "paraparaparadise"
s_y = "paragraph"
bi_x = set(ngram(2, list(s_x)))
bi_y = set(ngram(2, list(s_y)))
# 和
print(bi_x | bi_y)
# 積
print(bi_x & bi_y)
# 差
print(bi_x - bi_y)
print(bi_y - bi_x)
# seが含まれるかどうか
print(('s', 'e') in bi_x)
print(('s', 'e') in bi_y)
```

# 07. テンプレートによる文生成
x=12, y=”気温”, z=22.4というふうに整数、文字列、浮動小数点数を織り交ぜているのは、変数にどんな型が来てもいいようにしておけよ、という出題者の意図を感じました。

```Python
def nlp100_07(x, y, z):
    return str(x) + "時の" + str(y) + "は" + str(z)
```

# 08. 暗号文
```Python
def cipher(s: str):
    r = []
    for c in list(s):
        if c.islower():
            r.append(chr(219 - ord(c)))
        else:
            r.append(c)
    return ''.join(r)

ciphered = cipher("My name is Sen Yoshida.")
print(ciphered)
print(cipher(ciphered))
```
2回適用すると元に戻るんですね。Y=219-XだからX=219-Y。方程式の変形なんて久しぶりにやって、ちょっと嬉しい。

# 09. Typoglycemia
ここまでの問題は関数を作ればよかったけど、ここで初めてコマンドを作れという問題になっている。Windowsで作業しているのでUNIXシェルどうしようかと色々試し、Cygwinにした。なお開発環境はAnacondaのSpyder。pythonはAnacondaのものを使う。そのためCygwin側の.bash_profileに下記を書いている：
```
export PATH=$PATH:/cygdrive/c/Users/俺/anaconda3:/cygdrive/c/Users/俺/anaconda3/Library/bin'
```
で、解答：
```Python
import sys
import random

def typoglycemia(s: str):
    words = s.split(" ")
    l = []
    for w in words:
        if len(w) > 4:
            t = [w[0]]
            mid = list(w[1:len(w) - 1])
            random.shuffle(mid)
            t += mid
            t.append(w[len(w) - 1])
            l.append(''.join(t))
        else:
            l.append(w)
    return ' '.join(l)

for s in sys.stdin:
    print(typoglycemia(s))
```
この現象を題材にされたのは、いいですね。楽しみながら学ぶことができる。そして私は知らなかったんですけど、[この例文ってデマっぽい感じなんですね…](https://ja.wikipedia.org/wiki/%E3%82%BF%E3%82%A4%E3%83%9D%E3%82%B0%E3%83%AA%E3%82%BB%E3%83%9F%E3%82%A2)
Pythonで引数のデフォルト値として関数呼び出しをするときの注意

### 先に結論
関数を定義した段階で、デフォルト引数で呼び出している関数の結果は確定する。

```python
from time import time
from uuid import uuid4

def some_func(now=time(), uuid=uuid4()):
    print(now, uuid)


some_func()
some_func()
some_func()
some_func()

# >>> 1641341885.5043182 e5ffb9ca-e068-4810-bfa9-3c801734e2b0
# >>> 1641341885.5043182 e5ffb9ca-e068-4810-bfa9-3c801734e2b0
# >>> 1641341885.5043182 e5ffb9ca-e068-4810-bfa9-3c801734e2b0
# >>> 1641341885.5043182 e5ffb9ca-e068-4810-bfa9-3c801734e2b0
```

### 実際に自分が起こした問題。
ユーザーインスタンス作成時に`user_id`の指定がない場合は新しくUUIDを発行したかった。

```python
from uuid import uuid4

class User:
    def __init__(self, user_id=str(uuid4())) -> None:
        self.id = user_id 

for _ in range(5):
    user = User()
    print(user.id)
    
# >>> cdbb6136-2a78-46eb-912b-5518cd701345
# >>> cdbb6136-2a78-46eb-912b-5518cd701345
# >>> cdbb6136-2a78-46eb-912b-5518cd701345
# >>> cdbb6136-2a78-46eb-912b-5518cd701345
# >>> cdbb6136-2a78-46eb-912b-5518cd701345
```


### 参考
- https://qiita.com/7of9/items/5893e79819613d7f3c17
- ~~( 定義時にのみ実行される旨を自分は公式ドキュメントから見つけられなかった )~~  
[コメント](https://qiita.com/Hotsu_mi/items/9ce8581299804e5ce7e9#comment-48c1e7bfc7d9c031a597)で情報提供いただきました。普通に公式ドキュメントに記載ありました。。
（初心者）AWS EduKitでLチカを実行するまで

## はじめに
Edukitを手に入れたのは2021年の11月ごろで、その時に一度Lチカを試してみた。その際にはregistration_helper.pyの実行がうまくいかず、GitHubに質問を投げながら、なんとかかんとか実行できた。
https://github.com/aws-samples/aws-iot-edukit-tutorials/discussions/80
2022年になって、改めて勉強してみようと思ったら、チュートリアルの内容が変わっていて、例に漏れず実行がすんなりできなかったので、引っかかった場所の記録を残しておく。

この記事は、AWS Workshopのチュートリアルに沿ってLチカを実施した時の記録です。チュートリアルは以下のリンクの通りです。  

+ [実際に使ったもの](https://edukit.workshop.aws/en/blinky-hello-world.html)
+ [日本語版](https://edukit.workshop.aws/jp/blinky-hello-world.html  )

## 引っかかったところ
AWS IoT CoreにThingを登録するところで、チュートリアル通りに実行すると登録されたEdukitにCertificateが紐付けされない。

```
Blinky-Hello-World % pio run -e core2foraws-device_reg -t register_thing
```
チュートリアルにある蒸気のコマンドを実行した後で、AWS Consoleを確認すると、Thingは登録されているけれども、Certificateが紐付けされていない。この後のコマンドを実行してMQTTのテストをしても、メッセージが受け取れない。ログを確認すると、以下のメッセージがなんだか怪しい。ResourceAlreadyExistsExceptionと出ているので、過去にLチカ実施した時の何かが悪さをしているように見える。Console上で、過去に登録したThingは全て削除した後も同じメッセージが出てしまう。

```
Try importing certificate...
Exception occurred: An error occurred (ResourceAlreadyExistsException) when calling the RegisterCertificateWithoutCA operation: The certificate is already provisioned or registered
Exception occurred: Parameter validation failed:
Invalid type for parameter principal, value: False, type: <class 'bool'>, valid types: <class 'str'>
MANIFEST_IMPORT         INFO    attach_thing_principal failed.
MANIFEST_IMPORT         FAIL    False
MANIFEST_IMPORT         SUCCESS False   False
```
ResourceAlreadyExistsExceptionと出ているので、過去にLチカ実施した時の何かが悪さをしているように見える。Console上で、過去に登録したThingは全て削除した後も同じメッセージが出てしまう。ちなみに日本語版のチュートリアルには、以下のWarningが書かれている。 
> 過去にこのセットアップをされている場合、すでにAWS IoT Coreに情報が登録されている可能性があります。その場合は、ターミナルに表示される Unique ID: 0123456789ABCD の様なIDの名前のモノや証明書、それに紐づくIoT PolicyをAWS IoT Coreのマネージメントコンソールから削除してから、再度実行してください。

Gitをクローンしなおしたり、PlatformIOを入れ直したり、色々試したけれど、結局Regionをus-west-2からus-east-1に変えて実行すると無事に繋がった。

```
␛[0;32mI (9191278) MAIN: <<Thing ID>>/    Hello from AWS IoT EduKit (QOS0) : 0 ␛[0m
␛[0;32mI (9191308) MAIN: Subscribe callback␛[0m
␛[0;32mI (9191308) MAIN: <<Thing ID>>/    Hello from AWS IoT EduKit (QOS1) : 1 ␛[0m
␛[0;32mI (9194448) MAIN: Subscribe callback␛[0m
␛[0;32mI (9194448) MAIN: <<Thing ID>>/    Hello from AWS IoT EduKit (QOS0) : 0 ␛[0m
␛[0;32mI (9194498) MAIN: Subscribe callback␛[0m
␛[0;32mI (9194498) MAIN: <<Thing ID>>/    Hello from AWS IoT EduKit (QOS1) : 1 ␛[0m
␛[0;32mI (9197638) MAIN: Subscribe callback␛[0m
```
ゲーミング円運動

# はじめに

本記事は筆者が「ぬるぬる動くグラフが作りてえな～」と思って作ったものの副産物です。
解説に誤りがあるかもしれませんので見つけたら教えてください。

# 使用ライブラリ
・matplotlib
　-  .animation
　-  .pyplot

・numpy

## ライブラリ説明
・matplotlib
言わずと知れたグラフ描画ライブラリです。

・.animation
グラフを更新するモジュールみたいです。

・.pyplot
グラフを描画するモジュールみたいです。

・numpy
言わずと知れた配列・数学関数のライブラリです。

# ソースコード

```python:gaming_circle.py

import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation

n = 50
arry = np.linspace(0, 2*np.pi, n)
x = np.cos(arry)
y = np.sin(arry)


fig, axes = plt.subplots()
ims = []
z = [0 for _ in range(n)]
cline = np.linspace(-1, 1, n)

axes.plot(x,y, color='0.5')
axes.plot(cline,z, color='0.9')
axes.plot(z,cline, color='0.9')
axes.set_xlabel('x')
axes.set_ylabel('y')

for i in range(n):

    p1 = axes.plot(x[i], y[i], marker = 'o', markersize = 20)
    p2 = axes.plot(x[i], 0, marker = 'o', markersize = 20)
    p3 = axes.plot(0, y[i], marker = 'o', markersize = 20)    
    ims.append(p1 + p2 + p3)

ani = animation.ArtistAnimation(fig, ims, interval=50)

plt.show()

```

実際に動いているとこです。
![party_plot.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/889282/b3a0299b-079a-7402-5b0c-310fb10b980e.gif)


# 上から順に解説
import節は飛ばします

```
n = 50
arry = np.linspace(0, 2*np.pi, n)
x = np.cos(arry)
y = np.sin(arry)
```
円を描く準備をしています。
0~2πまでの数値を50分割で用意して、xy座標を求めます。


```
fig, axes = plt.subplots()
ims = []
z = [0 for _ in range(n)]
cline = np.linspace(-1, 1, n)
```
グラフの準備をしています。
fig, axes　はグラフの描画領域にあたるものです。
ims にはアニメーションのパーツとなるグラフを格納させます。
z と cline は背景の円と縦横線を描くための配列です。

```
axes.plot(x,y, color='0.5')
axes.plot(cline,z, color='0.9')
axes.plot(z,cline, color='0.9')
axes.set_xlabel('x')
axes.set_ylabel('y')
```
上の用意を元に背景等をセットしています。

```
for i in range(n):
    p1 = axes.plot(x[i], y[i], marker = 'o', markersize = 20)
    p2 = axes.plot(x[i], 0, marker = 'o', markersize = 20)
    p3 = axes.plot(0, y[i], marker = 'o', markersize = 20)    
    ims.append(p1 + p2 + p3)
```
グラフのパーツを作成しています。
p1,p2,p3はそれぞれゲーミング円運動と写像を１ステップ１フレームで作成します。
それをimsに格納していきます。

```
ani = animation.ArtistAnimation(fig, ims, interval=50)
plt.show()
```
グラフを表示させます。
ani で描くグラフと更新頻度を設定し、plt.show()で表示します。

また、

ani.save('party_plot.gif', writer='pillow')

と追加することで簡単にGIF化できます。


# おわりに
クソアプリの元にできそうな気がするんですが、もう一つ何か欲しいですね。
パッと思いつくのは例のゲーミング鳥あたりですが、関数ゲーミング鳥は[先駆者][link1]がいらっしゃいました。
私は発想力が貧困なので今はこれが限界です。
何か思いついたら追記するかもしれません。

[link1]:https://twitter.com/CHARTMANq/status/1261885749630136320
PysparkのDataFrame操作に関するエラー対応

#はじめに
Databricksを学んで1か月の筆者が、Pysparkのデータフレームを操作する際に遭遇したエラーとその対応について、実際のコードとともに記載しております。
本記事が、Pysparkを学習する方の参考になれば幸いです。

#本文

####・Attribute Error: 'DataFrame' object has no attribute 'withcolumn'


```Python:Pyspark
TestDF = DF.withcolumn("stationary", col("item_name").isin("pencil", "eraser"))
```

**原因**：withColumnの**C**が小文字になっているため。
**対処法**：cを大文字に直す。

WithColumnに限らず、複数の単語が結合して出来た関数名については、2単語目以降が大文字から始まることが多い。
例：dropDuplicate, orderBy, isNotNull

使用する関数については、あらかじめ[公式ドキュメント](https://spark.apache.org/docs/2.1.0/api/python/pyspark.sql.html)等で関数名を確かめておくのが無難と思われる。

####・TypeError: 'Column' object is not callable
```Python:Pyspark
TestDF = DF.groupBy(col("item_name").count())
```

**原因**：関数count()がDataFrameではなくカラムに対して適用しようとしているため。
**対処法**：groupByの括弧の外側にcount()を配置し、DataFrameへ適用させる。


```Python:Pyspark
TestDF = DF.groupBy(col("item_name")).count()
```
使用する関数がDataFrameに対応するのか、カラムに対応するのかを把握しておくと、関数の配置に迷わないため、上記エラーの減少につながる。
[pyspark.sql.DataFrame](https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.sql.DataFrame.html)に掲載されている関数はDataFrameにつながるように配置し、
[pyspark.sql.Column](https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.sql.Column.html)に掲載されている関数はカラムに直接つながるように配置する。

####・IndentationError: unexpected indent

```Python:Pyspark
TestDF1 = DF.groupBy("item_name")
            .avg("price")
```
**原因**：2行目のコードが前行とつながっていないものと判断されるため。
**対処法**：行末に`\`を付けるか、右辺の最初と最後を括弧で囲む。

```Python:Pyspark
TestDF1 = DF.groupBy("item_name")\
            .avg("price")
```

```Python:Pyspark
TestDF1 = (DF.groupBy("item_name")
             .avg("price")
          )
```
特にコードが長くなる場合、使用する関数ごとにインテンドする事でコードが見やすくなるが、上記対応がなされていないとエラーが発生するため、注意が必要である。


####・AttributeError: 'DataFrame' object has no attribute 'avg'

```Python:Pyspark
TestDF = (DF.groupBy("item_name")
            .sum("price")
            .avg("price")
          )
```
**原因**：2つの集計関数をそのままドットで繋げてしまっているため。
**対処法**：集計関数をaggメソッドで囲み、コンマ区切りで使用する。

```Python:Pyspark
TestDF = (DF.groupBy("item_name")
            .agg(sum("items.quantity"), avg("items.item_revenue_in_usd"))
         )
```
集計結果のカラムに別名(`alias`)を付ける際や、複数の集計関数を使用したい場合に`agg`メソッドが有効である。
親の名前を再帰的に表示する

[Answersで回答する時に使う情報のまとめに戻る](https://qiita.com/Keiji_otsubo/items/9e6e0e946826133eddc7)


[Donation Report for Parent Campaign with all Child Campaigns](https://trailhead.salesforce.com/ja/trailblazer-community/feed/0D54S00000ElE58SAF)

```
BLANKVALUE(Parent.Parent.Parent.Parent.Parent.Name,
BLANKVALUE(Parent.Parent.Parent.Parent.Name,
BLANKVALUE(Parent.Parent.Parent.Name,
BLANKVALUE(Parent.Parent.Name,
BLANKVALUE(Parent.Name,
Name)))))
```
# 逆のパターン

+ [兄弟レコードから値を取得する数式フィールド](https://trailhead.salesforce.com/trailblazer-community/feed/0D54S00000JejG0)

:::note
I don't think it can be done with formulas, since Contact_Additional_Info and Contact_Additional_Info are only related via parent objects.

Children refer to parents and parents refer to children, but parents cannot refer to children in formulas.

Automated processing is required.
:::
Qiita Daily Survey memo 2021.01.05

# Qiita Daily Survey memo 2021.01.05
Qiita の記事を可能な限り毎日読むために、読んだmemoを公開することでモチベーションを維持しようという試み。

## [英語面接で5歳児みたいなことしか言えないからカッとなってWebサービス作った【個人開発】](Qiita の記事を可能な限り毎日読むために、読んだmemoを公開することでモチベーションを維持しようという試み。)
ちぇんさん(yuno_miyako)の記事。
DeepL を使用して少し複雑な英語がぱっと出てくるように練習するサービスを作成された流れを紹介いただいている記事。
記事を拝見させていただき「確かにこれは欲しい」と、すごく感じました。
「言いたいことはあるけど、どう表現してよいか…」と言葉が出てこないシーンがよくあります。
早速試してみましたが、とてもとても効果が高そうだと感じました。
Webシステムもそうですが、学習のプロセスとしてもとても良いと感じました。

## [残して良かったドキュメント、いまいちだったドキュメント](https://qiita.com/xrxoxcxox/items/a39944c623301151920f)
Keisuke Watanukiさん(xrxoxcxox) の記事。
タイトルの通り、残したほうが良いドキュメントと、そうではないドキュメントを理由付きで紹介してくださっている記事です。
業務の中で「ドキュメントを残せ」とよく言われます。
ただ、自分の残したドキュメントでは「これがあって助かった」というものはありますが、連携いただいたドキュメントでは少ないかな…？と感じました。
そもそもドキュメントの共有がほぼできていないうえに、キックオフや振り返りもほとんど開催されていないため、この記事を読んで「確かにそれはそうか」と納得する部分があり、納得しました。
少なくとも個人レベルでは振り返りを行い、残してよかったと思えるドキュメントを残しておきたいと思います。
オンラインでプランニングポーカーができる、Plapoが楽しかったので紹介する

オンラインでプランニングポーカーができる無料サービス **Plapo** を体験して楽しかったので紹介します。

https://plapo.net/

https://prtimes.jp/main/html/rd/p/000000001.000071891.html


## 3ステップ手順

### 1. テーマを決める

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93824/524c6814-a27c-f3eb-2d06-c1ddd555e25c.png)

URLが作成されるので、参加者に集まってもらいます。

### 2. 見積もりポイントを決める

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93824/f73bc093-510c-c4e1-bbc4-2a8d6df467d9.png)

「フィボナッチ数列」の数字から参加者それぞれがカードを選びます。`1 point = 30分` だとか、`1 point = 1時間` だとか、単位はあらかじめ決めて参加者同士理解しておきます。
アジャイルマニフェスト起草者の一人であるJemes Grenning氏 によると

> それが「花弁の数のように自然な数」であるためだと言います。そもそも、見積もりは正確になりえないものですが、「1、2、3、4・・・」と数字が並んでいると、どうしても正確性を意識せざるを得ません。その点、フィボナッチ数は「ちょうどよい」サイズ

らしいです。参考: [プランニングポーカー・オブジェクトゲームでアジャイルゲーム！～Agile 2011 Conference (1/2)：EnterpriseZine（エンタープライズジン）](https://enterprisezine.jp/iti/detail/3385)


### 3. 答え合わせをする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93824/6655286e-c920-c94a-2d67-859410e48749.png)

全員がカードを引いたら、いっせーのせでオープンします。数値の根拠を語り合い、見積もり数値を決めましょう。`リセットする` ボタンを押せば、プランニング会議中何度も繰り返して使えます。簡単！


## プランニングポーカーとは

アジャイル・スクラム用語の1つです。

https://kawaguti.hateblo.jp/entry/20120218/1329524230

https://enterprisezine.jp/iti/detail/3385

> プランニングポーカーとは、「1、2、3、5・・・」といった数字が書かれたカードを使って、タスクの規模を相対的に見積もる手法です。
> それぞれのタスクに対して開発を担当するメンバーが思いのままに数字のカードを出し合います。

> **ポイントは、メンバーがそれぞれ提示したポイントについてお互いに根拠を述べ合う** ところにあります。「テストデータの作成に時間が掛かる」「テンプレートを使えば簡単」など、各メンバーの考えをシェアすることで、一人で考えている時には漏れていたかもしれない工夫や懸念材料を洗い出すことができます。

大事なのはこの際に初心者もそうでない者もダメ出しをしないこと、皆で納得して数値をすり合わせること、だと思います。なぜなら以下だから。

> カードに書かれた数字は通常、ポイントと表現されます。これは、あくまで各タスクを相対的に評価するための目安で、単位自体に「工数」「難易度」など特定の意味が結びついているわけではありません。例えば、日数で計算したい場合は「終わらせるのに理想と考えている日数」として数値を使うこともできます。


### プランニングポーカーのメリット

初心者と初心者でないメンバーが入り混じってポーカーを行うと結構にブレる (ある人は1や3を出し、他方は8を出す位) のですが、それが「むしろ良い」です。それぞれでその数字を出した理由をためらわず表明します。初心者は「よくわからんから特に根拠はないけど多めの数字にした」と言えば、作業がわかりづらいんだな～ とか、その場の皆が理解でき、それ自体がその作業の解像度を高める、なごやかな共有の場になったりします。

**あくまでポーカーなので、ゲームのような楽しい雰囲気になる** のがポイントだと思います。なんでしょう、月初めのPlanning時に限らず、あるタスクや割り込み課題に対峙させられたときにふと、「…ポーカーしよっか。」と言えて次の瞬間にゲームプレイできる雰囲気が作れるのが開発現場のユースケースとして素敵ではと想定します。


周りの顔色をうかがいながら涙目で最小工数を孤独にはじき出すような見積もりからはもうサヨナラです。是非使ってほしい。




## ツール比較

https://qiita.com/r2-oishi/items/c63d296e96e176f55660#4-planning-poker

Miroにもあるようだ。(上の記事から引用)
<div align="center">
<img width="700" alt="pen" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/345439/9cffec5c-c45c-228c-6b14-22d862c1113e.gif">
</div>

その他
- https://marketplace.visualstudio.com/items?itemName=ms-devlabs.estimate
- https://github.com/search?q=Planning+poker

## 参考

[困った時に参考にしたい！初心者向けおすすめアジャイル・スクラム記事リンク集 - Qiita](https://qiita.com/yaboxi_/items/904b23dce229a09af024#%E3%83%97%E3%83%A9%E3%83%B3%E3%83%8B%E3%83%B3%E3%82%B0%E3%83%9D%E3%83%BC%E3%82%AB%E3%83%BC)
[認定スクラムマスターの俺が正しいスクラムの理論をまとめてみた - Qiita](https://qiita.com/gold-kou/items/90ba982a14ca79d843c9)
[アジャイル・スクラム、色々リファレンス - Qiita](https://qiita.com/e99h2121/items/1b7ac17c2e26d69d1f47)

Happy Planning! 
以上です～
【React】イミュータビリティを実現する方法【JavaScript】

## はじめに
　本記事は、プログラミング初学者が、学習を進めていて疑問に思った点について調べた結果を備忘録も兼ねてまとめたものです。
　そのため、記事の内容に誤りが含まれている可能性があります。ご容赦ください。
　間違いを見つけた方は、お手数ですが、ご指摘いただけますと幸いです。

## 今回の疑問点
　今回の疑問点は、

　　_Reactにおけるイミュータビリティを実現する方法_
　
　です。　

以前、[Reactチュートリアル](https://ja.reactjs.org/tutorial/tutorial.html)に取り組んでいた際に、その中でイミュータビリティの重要性について解説されていました。また、以下のような文章も記載がありましたが、イミュータビリティの実現方法については、理解が不十分だと思い、調べることとしました。
>push() メソッドの方に慣れているかもしれませんが、それと違って concat() は元の配列をミューテートしないため、こちらを利用します。

## 疑問点に対する解説

### イミュータビリティとは
イミュータビリティとは、不変性を意味します。
データを直接書き換えるのではなく、望む変更を加えた新しいデータのコピーで古いデータを置き換えることで実現できます。
具体的には、以下のようなものはミュータブルなアプローチ、

```mutable.js
// ミュータブルなアプローチ
var person = {age: 18, name: 'Taro'};
person.age = 22;
//anotherPerson は {age: 22, name: 'Taro'} に改変される
```
以下のようなものがイミュータブルなアプローチとなります。

```immutable.js
// イミュータブルなアプローチ
var person = {age: 18, name: 'Taro'};

var anotherPerson = {...person, age: 22};
// person に変化はなし, 一方で anotherPerson は {age: 22, name: 'Taro'}
```

### イミュータビリティの重要性
イミュータビリティの重要性については、[Reactチュートリアル](https://ja.reactjs.org/tutorial/tutorial.html#why-immutability-is-important)に詳細な説明があります。
また、Reactチュートリアルに取り組むことでその重要性を体感することができます。

### イミュータビリティを実現する方法
配列を操作する際にイミュータビリティについて特に留意する必要があります。
JavaScriptにおける配列操作には、破壊的なものと非破壊的なものがあります。
破壊的なメソッドの具体例は以下の通りです。

- splice()
- copyWithin()
- fill()
- pop()
- push()
- shift()
- unshift()
- reverse()
- sort()

上述の破壊的、非破壊的という分類がミュータブル、イミュータブルという分類に対応しています。
すなわち、列挙した破壊的なメソッドの使用は避け、非破壊的なメソッドを使用するようにすることでイミュータビリティを実現することができます。
具体例は以下の通りです。

#### 要素の追加
push()やunshift()は使わず、スプレッド構文を使用することで実現します。

```sample.js
const numbers = [1, 2, 3]

const newNumbers = [...numbers, 4]
console.log(newNumbers)
// [1, 2, 3, 4]
```

#### 要素の削除
破壊的なpop()やshift()、splice()は使わず、filter()を使用することで実現します。

```sample.js
const numbers = [ "one", "two", "three"]

const newNumbers = numbers.filter(number => number !== "two")
console.log(newNumbers)
// ["one", "three"]
```

#### 配列の加工
map()又はreduce()を使用することで実現します。

```map.js
var numbers = [1, 4, 9, 16];
const newNumbers = numbers.map(x => x * 2);
console.log(newNumbers);
// [2, 8, 18, 32]

const numbers = [1, 2, 3, 4, 5]
const editNumber = (oldNumber, newNumber, arr) =>
   arr.map(number => (number === oldNumber) ? newNumber : number)
const updatedNumbers = editNumber(3, 100, numbers)
console.log(updatedNumbers)
// [1, 2, 100, 4, 5]
```

```reduce.js
const numbers = [15, 24, 13, 35]
const max = numbers.reduce(
  (max, value) => (value > max) ? value : max, 0)
console.log('max', max);
// max 35

const numbers = ["one", "one", "two", "three", "two"];
const distinctNumbers = numbers.reduce(
  (distinct, number) =>
    (distinct.indexOf(number) !== -1) ?  
     distinct 
     : 
     [...distinct, number],[]
    )
console.log(distinctNumbers)
// ["one", "two", "three"]

```

#### 破壊的なメソッドを使用したいとき
reverse()やsort()等、どうしても破壊的メソッドを使いたい場合には、スプレッド構文等で変更を加えたい配列のコピーを作成し、このコピーに対して変更を行うことで実現します。

```sample.js
const numbers = [3, 2, 1]

const newNumbers = Array.from(numbers).sort();
console.log(newNumbers);
// [1, 2, 3]
```

## まとめ

- イミュータビリティは、破壊的メソッドの使用を避け、非破壊的メソッドを使用することで実現することができる
- 配列の操作の際には、特に注意が必要
VRM Model 仕様について

# はじめに
仕様の文書は作りかけで進展しないですし, 英語版は絶対期待できないので, ノートだけとります.

[GitHub仕様](https://github.com/vrm-c/vrm-specification)

# GLTFパーサ
そもそもGLTBに独自仕様を生やしたものなので, GLTBをパースできれば問題ないです.

# シェーディングモデル
突然`Unity`というベンダー名と依存する仕様がでてくるため辛いです, 

# 実装
フルスペックの仕様を含んだモデルはないらしい, テスト面倒くさいです.
特に, `VRMC_materials_mtoon` 拡張を使用しているモデルが見つからないです. これは必要とされていないのでは？
GLTFではHTML等で使われる, `mime type` が流用されていますが, 実装の中に`image\\/png`など余計なバイトを出力するものがあります.
Azure Storage Blobs を Unity 2021 で使用する際の注意点

# 発生する問題

Unity 2020 を使用しているプロジェクトで Azure Storage Blobs を使っていたのですが、Unity 2021 にアップデートした時に以下のようなエラーが発生しました。

```
Exception : TypeInitializationException
Root Exception : NotImplementedException
Message : The method or operation is not implemented.
```

Unity フォーラムでも同じようなエラーが発生している人がいました。

https://forum.unity.com/threads/attempting-to-connec-to-azure-blobs-results-in-notimplementedexception.1117705/

この `NotImplementedException` エラーは Unity が使用している .NET のバージョンと NuGet で取得できるライブラリのバージョンが一致していないのが原因です。

ちなみに Unity 2020 では以下のバージョンを利用していました。

```
Azure.Core : ver.1.19.0
Azure.Storage.Blobs : ver.12.10.0
Azure.Storage.Common : ver.12.9.0
```

# 解決方法

NuGet で `Azure.Storage.Blobs` をインストールすると `Azure.Core` は `ver.1.19.0` が依存関係でインストールされるのですが、これが Unity と相性が悪かったので以下のようにしました。

```
Azure.Core : ver.1.21.0
Azure.Storage.Blobs : ver.12.10.0
Azure.Storage.Common : ver.12.9.0
```

これで無事に Unity で Azure.Storage.Blobs を利用することができました。
railsチュートリアル rails serverでエラー　Blocked host:〜


#railsチュートリアルの1.3.2rails serverのところで一瞬詰まったのでメモ

まずこの1.3.2ではrails serverというコマンドを実行するだけでRailsアプリを実行できるよーというお話。

コマンドを実行する前にローカルWebサーバーへの接続を許可する必要があるので
config/environments/development.rb
のconfig.file_watcherの下の行に

```
config.hosts.clear
```
を追加

別ターミナルを開いてrails serverを実行しプレビューで確認すると
Blocked host:〜
というプレビュー画面が表示されてしまった。




#解決方法
ターミナルの画面で

```
$ rails server
Could not find gem 'puma (= 4.3.6)' in rubygems repository https://rubygems.org/ or installed locally.
The source contains the following versions of 'puma': 4.3.10
Run `bundle install` to install missing gems.
```
と出ているので
bundleをインストール

```
$ bundle install
```

すると

```
Fetching gem metadata from https://rubygems.org/...........
You have requested:
  listen = 3.1.5

The bundle currently has listen locked at 3.7.0.
Try running `bundle update listen`

If you are updating multiple gems in your Gemfile at once,
try passing them all to `bundle update`
```
と出てくる
1.3.1ではではbundleをインストールしたときに問題がなかったが今回ここでアップデートが必要と出てきたので指示に従ってそのままアップデートを実行

```
$ bundle update
```

あらためてrails server

```
$ rails server
```


![2022-01-05 2.40.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680582/4e4f27e0-623c-5cde-a171-fa9b2239904e.png)


無事にデフォルトのrailsの画面を表示できました！
IntelliJ Go Pluginを設定する

IntelliJ Go Pluginは起動直後はまともに動かないので設定が必要。

## GOROOT
* Languages & Frameworks > Go > GOROOT

Goがインストールされたprefixをいれる。macOSだと `/usr/local/opt/go/libexec` が自動的に指定されるのでそれをセットする。

## GOPATH
* Languages & Frameworks > Go > GOPATH

GOPATHをいれる。僕はGOPATHはHOMEなので `$HOME` にあたるパスをセットする。
Go Modulesを使っている場合は「Index entire GOPATH」のチェックは外しておいた方がいいらしい。

## Go Modules
* Languages & Frameworks > Go > Go Modules

go.mod を使っているリポジトリでインデックスが動くようにするには、Enable Go modules integrationにチェックをいれる。Environmentは空のままでも動く。
ロール、ポリシー関連の整理

# 初めに
ポリシー 、ロールについての整理

# ポリシーとは
JSONフォーマットで「誰が」、「どのAWSサービスの」、「どのリソースに対して」、「どんな操作を」、「許可するか(許可しないか)」を定義したもの。
定義したポリシーをユーザーやAWSのリソース(S3など)と関連づけて、アクセスの制御を行なっている。

```
例)　IAMユーザへのポリシーのアタッチ
「どのAWSサービスの」　->Amazon EC2
「どのリソースに対して」 ->すべて
「どんな操作を」 ->インスタンスの起動
「許可するか(許可しないか)」 ->すべて
 　　↓アタッチ
「誰が」 　-> HOGEさん

 -> HOGEさんにはAmazon EC2サービスのインスタンスの起動の
権限が付与された状態
```
#ロールとは
AWSのサービスのアクセス権を管理するもの
アクセス権を管理したいリソースで作成できるロールは1つ。
例えば、あるEC2でIAMロールを付与したい場合、作成できるロールは1つで、複数のポリシーを割り当てることは可能。


#ユーザー、ポリシー、ロールの関連を図で整理
![snap_IAM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234299/6c2e7c8e-9964-c24b-11bd-ca2bc97db62b.png)

# ポリシーの種類

| ポリシー | 概要 |
|:-----------|:------------:|
| ユーザーベースのポリシー       | IAMユーザー、IAMグループ、IAMロールに紐付けるポリシー        |
| リソースベースのポリシー     | AWSサービスに紐づけるポリシー。「誰が」リソースにアクセスできるのか直接の指定が可能。 アカウントを跨いだリソースへのアクセスも可能(※)   |
| 信頼ポリシー       | IAMロールに紐づけるポリシー。信頼ポリシーを紐付けたIAMロールの「誰か」に権限の移譲が可能        |         |

(※)クロスアカウントでアクセスする場合、ロールによる権限の委任と違い、権限が新たに付与されたアカウントのリソースへのアクセス権限と同時に、各アカウントのユーザーに既存で付与されているアクセス許可を保持できる。
一方、ロールによる権限の委任で、他のアカウントのリソースにアクセスする場合、作業時点での権限は、権限が新たに与えられたアクセス許可しか持たない(既存のユーザーで持っていたアクセス権限は維持できない)
【Flutter】WSLでfvmコマンドが通らないときのTips

## はじめに

表題の通りWSLでfvmをインストールしたときにfvmコマンドが見つからなくなってしまうときの対策です。
たぶんあまりきれいなやり方ではないと思います。
結論はこちら→[対策](#対策)

ちなみに私の環境では`fvm`以外にも`riverpod`コマンドで同様の問題が出ました。もしかすると他のdartコマンドでも起きるかもしれません。


環境はWindows10のWSL2です。

```bash
$ wsl -l -v
  NAME                   STATE           VERSION
* Ubuntu                 Stopped         2
  docker-desktop         Stopped         2
  docker-desktop-data    Stopped         2
```

## 問題

fvmのインストールは以下の記事を参考にしました。

https://qiita.com/idani/items/0e45d037b4c2a93840a7

こちら参考に`dart pub global activate fvm`を叩き、
`C:\Users\xxxx\AppData\Local\Pub\Cache\bin`を環境変数に追加した後にfvmコマンドを叩くと、パスは通っているはずなのにコマンドが見つかりません。

```bash
$ fvm
bash: fvm: command not found
```

一応、パスが示すディレクトリにはツールがインストールされているようです。

```bash
$ ls /c/Users/xxxx/AppData/Local/Pub/Cache/bin
devtools.bat  fvm.bat  riverpod.bat
```

## 原因

以下のissueを見るに、WindowsでのコマンドエイリアスとLinuxでのコマンドエイリアスがごちゃごちゃに認識されているような感じと予想。Windows上で動くLinuxなのでらしいといえばらしい挙動かなと。

https://github.com/leoafarias/fvm/issues/328


## 対策

結論としてはコマンドに`.bat`をつけて起動すると動きました。

```bash
$ fvm.bat --version
2.2.6
```

.bashrcにエイリアスを設定しておくと起動しやすいです。

```:.bashrc
alias fvm='fvm.bat'
```

以上です。
JavaScriptの多次元配列から特定列の重複値の行を削除する方法

# 前提
GASにて二次元配列を操作している際に、はまってしまったので、備忘録。

# 失敗例
当初、最下部の「参照」にて参考にしていたコードを使用していたが、thisがグローバルオブジェクトを指定してしまい、対象の配列データの一部でも先に定義されていた場合、機能しなくなる。

```JavaScript:main.js
function getUniqueArray_2d(arr, num) {
 ​arr = arr.filter(row => {
   ​if (!this[row[num]] && row[num] != "") {
     ​return this[row[num]] = true;
   ​}
 ​});
 ​return arr
}

const arr = [
	["A", "Bill", "1", "AAA"],
	["A", "Alice", "3", "BBB"],
	["A", "Chris", "4", "CCC"],
	["B", "Chris", "5", "DDD"],
	["B", "Chris", "6", "EEE"],
	["C", "Davis", "7", "FFF"]
];

getUniqueArray_2d(arr, 1) // 重複判定する列番号を指定
```

なお、事前に定義されていない場合、正しいデータが取得できる。


# 修正
thisがグローバルオブジェクトを指定してしまうため、thisの代わりのオブジェクトを宣言してから実行する。

```JavaScript:main.js
function getUniqueArray_2d(arr, num) {
  const unique = {} // 空のオブジェクトを宣言
  arr = arr.filter(row => {
    if (!unique[row[num]] && row[num] != "") {
      return unique[row[num]] = true;
    }
  });
  return arr
}
```

これでうまくいきました。

# 参照
https://1-notes.com/javascript-remove-duplicate-values-from-array/
【Node.js】Windows環境におけるCypressのIntegrationフォルダーの場所

# はじまり
CypressをWindows10にインストールした後、サンプルテストコードのファイル（`.spec.js`）がある`integration`フォルダーの場所を調べたら、Windowsに関する記事がすぐ見つからず探すのに時間が掛かったので（最終的にEverythingを使って見つけました・・・）、記事にしました。

# Cypressをインストール
nodeのバージョンは16.3.1です。
以下で、インストールしました。

```dosbatch
npm install cypress --save
```

# Cypressを動作確認
Cypressをverifyして、

```dosbatch
npx cypress verify
```

Cypressを実行確認しました。

```dosbatch
npx cypress open
```

# Where is `integration` folder?
動くことは確認できたので、最後に`integration`フォルダーの場所を確認します。
確認したら、ここにサンプルテストコードのファイルが置いてありました！

```dosbatch
C:\Users\%USERNAME%\cypress\integration
```

# おしまい
ここに自作のテストコードを置けば動くはず・・・
なんだっけ？な用語を書き起こす


##CRUD（くらっど）
Create（登録）
Read（参照）
Update（更新）
Delete（削除）　の頭文字をとったもの。これらはデータを作るために必要な最低限の機能。

##REST（れすと）
設計思想のこと。WEBの大体はこれらしい。
↓こういうやつ。

| やること | URL | HTTP のメソッド |
|-----------|------------|------------|
| 投稿一覧 | ttps://hoge.com/users | GET |
| 投稿新規作成 | ttps://hoge.com/users | POST |
| 投稿の編集 | ttps://hoge.com/users/投稿のID | PUT |
| 投稿削除 | ttps://hoge.com/users/投稿のID | DELETE |

レスポンスがjsonで返ってくる。

##アーキテクチャ
構成、構造。

##ORM（おーあーるえむ）
オブジェクト関係マッピング。
リレーショナルデータベースのレコードをオブジェクトとして簡単に扱えるようにすること。
（それを行うライブラリをO/Rマッパーと呼んでいる。）
SQL意識しなくてもリレーショナルデータベースにアクセスするプログラムが書けるのはそういうこと。

リレーショナルデータベースというのは、テーブルがあってカラムがあってレコードがあるようなデータベースのこと。

##
Python Numpyを使用して平均値や分散などを算出する

#はじめに
こんにちは、この記事はプログラマーでもエンジニアでもなんでもないただのPC初心者がプログラミングの勉強のために書いたものとなっています。今回はpythonのライブラリであるNumpyを使用して、平均値・最大値・最小値・四分位数・分散・標準偏差を求めたいと思います。

#開発環境
・Windows 11
・VScode
・Python 3.9.2
・Numpy 1.21.2

#Numpy
公式ドキュメント
https://numpy.org/doc/1.21/index.html

　配列の計算を得意とするライブラリで、C言語で書かれているため、計算処理が速くなります。

#Numpy配列
とりあえず、Numpyを使った配列を書いてみます。

```python:コード
import numpy as np
#ライブラリのインポート

list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
#numpyを使わない配列
nplist = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
#numpy配列

print(list)
print(nplist)
```
```:出力
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0 1 2 3 4 5 6 7 8 9]
```
Numpy配列を使わないと、配列の要素はコンマで区切られるが、
Numpy配列を使うと、要素は半角の空白で区切られる。

#最大値・最小値・中央値
使用する配列の要素はテストの点数ぽい何か

```python:コード
import numpy as np
#ライブラリのインポート

nplist = np.array([82, 94, 93, 89, 91, 84, 98, 79])
#numpy配列

big = np.max(nplist)
#最大値
small = np.min(nplist)
#最小値
middle = np.median(nplist)
#中央値

print("最大値",big)
print("最小値",small)
print("中央値",middle)
```
```:出力
最大値 98
最小値 79
中央値 90.0
```
numpy.max(): 最大値の算出
numpy.min(): 最小値の算出
numpy.median(): 中央値の算出 


#平均値

```python:コード
import numpy as np
#ライブラリのインポート

nplist = np.array([82, 94, 93, 89, 91, 84, 98, 79])
#numpy配列

a = np.mean(nplist)
#numpy.mean(): 配列の要素の平均値を算出

print("平均値",a)
```
```:出力
平均値 88.75
```
numpy.mean()で配列の要素の平均が算出されます。
#四分位数
```python:コード
import numpy as np
#ライブラリのインポート

nplist = np.array([82, 94, 93, 89, 91, 84, 98, 79])
#numpy配列

q_1 = np.percentile(nplist, 25)
#第一四分位数
q_2 = np.percentile(nplist, 50)
#第二四分位数
q_3 = np.percentile(nplist, 75)
#第三四分位数

print("データ", np.sort(nplist))
#sort: 昇順に並べ替え

print("第一四分位数", q_1)
print("第二四分位数", q_2)
print("第三四分位数", q_3)
```
```:出力
データ [79 82 84 89 91 93 94 98]
第一四分位数 83.5
第二四分位数 90.0
第三四分位数 93.25
```
　分位数を算出するには、numpy.percentile関数を用います。第一四分位数は、データを昇順に並べて半分に分けたとき、下位側のデータの中央値、すなわちデータの25%の位置にある値のことです。よって、第一四分位数を算出したいときは、第二引数に「25」を入力します。第二・第三四分位数の算出も同じです。
　percentile関数の第二引数には、算出したい分位点のパーセントを入力するので、第一～第三四分位数に限らず、色々な分位数の算出が可能です。(下記参照)

```python:コード
import numpy as np
#ライブラリのインポート

np.set_printoptions(precision = 1, floatmode = 'fixed')
#numpy配列の小数点以下の表示桁数指定, 末尾を0で埋める

nplist = np.array([82, 94, 93, 89, 91, 84, 98, 79])
#numpy配列

q_array = np.percentile(nplist, q = [0, 20, 40, 60, 80, 100])
#percentileの第二引数には、配列で渡すことも可能

print("データ", np.sort(nplist))
#sort: 昇順に並べ替え

print(q_array)
```
```:出力
データ [79 82 84 89 91 93 94 98]
[79.0 82.8 88.0 91.4 93.6 98.0]
```
#分散・標準偏差
```python:コード
import numpy as np
#ライブラリのインポート

nplist = np.array([82, 94, 93, 89, 91, 84, 98, 79])
#numpy配列

nplist_var = np.var(nplist)
#分散の算出
nplist_std = np.std(nplist)
#標準偏差の算出

print("分散", nplist_var)
print("標準偏差", nplist_std)
```
```:出力
分散 37.4375
標準偏差 6.118619125260208
```
numpy.var関数で分散(標本分散)、numpy.std関数で標準偏差が算出できる。

#最後に
　今回は単純な使い方しか説明していませんが、ここで紹介した関数には、様々なオプションがついています。機会があればまた書きます。
Ubuntu Server 18.04 でnmtuiコマンド (Network Manager)を使いたい

#経緯
[月額6ドルから始める簡単AnyCastなGSLB](https://qiita.com/r3n/items/4f532446b771ed137b1e)にて、CentOSを使用していたためにnmtuiコマンドを使用して設定したが、Ubuntu 18.04ではnetplanが使用されているので、Ubuntuでもnetplanで設定せずに親しみのあるNetwork Managerを使いたい。

#方法
特に難しいことはなく、NetplanでNetwork Managerのrendererを使用するだけです。

`# nano /etc/netplan/01-netcfg.yaml`
※環境によって異なる。 `/etc/netplan/*.yaml`

```01-netcfg.yaml
network:
  version: 2
  renderer: NetworkManager
```

その後、Network-Managerをインストールし、再起動で完了。

```
# sudo apt install network-manager
# sudo netplan generate
# sudo netplan apply
# reboot
```

Desktop版ではNetwork-Managerが使われているようで、CLIやGUIを使って設定できるようです。Netplanに慣れればいいだけの話なのですが..
位相空間論メモ

# この記事について

* [数学メモ](https://qiita.com/niiku-y/items/a182e03caf85ca799167) から分割した。集合については、[集合論メモ](https://qiita.com/niiku-y/items/cefeb2e7ce0415d16c65) を参照
* 暇あるときに追記していきます

# 位相空間論

## 定義 (位相空間 Topological space)

集合 $X$ の部分集合族 $O=O(X)$ が$X$の開集合系であるとは，以下を満たすことをいう。

(1) $S \in O , \varnothing \in O$
(2) $m \in N, \ O_m \subset X $ とする。$O_1 , ... , O_m \in O \Rightarrow O_1 \cap ... \cap O_m \in O$
(3) 任意の集合 $\Lambda$ をとり， $\lambda \in \Lambda$ から $O_{\lambda} \in O$ という対応を与えたとき，$\underset{\lambda \in \Lambda}{\cup}  O_{\lambda} \in O$

集合 $X$ に開集合系 $O$ が与えられたとき，$O$ は $X$ に位相を定めるといい、
$O=O(X)$ の元を開集合(open set)とよぶ。
この位相構造が定められた集合 $(X,O(X))$ を位相空間(topological space)という．
開集合系 $O$ を省略し、集合 $X$ を位相空間と呼ぶこともある。位相空間 $X$ の元は点とも呼ばれる。

```
$m \in N, O_1 , ... , O_m \in O \Rightarrow O_1 \cap ... \cap O_m \in O$
$\underset{\lambda \in \Lambda}{\cup}  O_{\lambda} \in O$
```


## 定義 (閉集合 closed set, 閉集合系)

位相空間 $X$ において、開集合の補集合を閉集合、閉集合全体の集合を閉集合系と呼ぶ。
位相空間 $X$ の部分集合 $F$ が閉集合であるとは、補集合 $F^c$ が開集合であるということになる。



以上
Typescriptでimportしたりexportしたり

### 定数と変数
```typescript
/**
 * 定数と変数
 */
export const myName = 'Foo'

// 以下はlintに怒られる。
// javascriptでは許されるようだが、そもそも、import先では読み取り専用なのでlet である意味がない
export let yourName = 'Bar'

```
constはつけるが、letは使えなかった。importが読み込み専用なので当然といえば当然。

### 関数
```typescript
/**
 * 関数
 */
export function alertFoo(): void {
    window.alert('Foo')
}

// 戻り値の型推論は効いていたが、unExported型単体を使用することは出来なかった
export function returnUnExportedType(): unExported {
    return { name: 'hoge' }
}

type unExported = {
    name: string
}
```
exportしていない型を戻り値にしても使えた。ただ、unExported型単体を使用することは出来なかった。

### クラス
```typescript
/**
 * クラス
 */
export class Bar {
    name: string
    constructor(name: string) {
        this.name = name
    }

    callName(): void {
        window.alert(this.name)
    }

    static callMyName(): void {
        window.alert('Bar')
    }

    returnFoo(): unExported {
        return { name: 'hoge'}
    }
}
```
クラスメソッドもインスタンスメソッドも使えるし、exportしていない型を戻り値に設定したメソッドも使えた。

### タイプとインターフェース
```typescript
/**
 * タイプ
 */
export type Foo = {
    name: string
    age: number
}

type Foo = {
    name: string
}

export default Foo

/**
 * インターフェース
 */
export interface Baz {
    name: string
    age: number
}

export default interface Foo  {
    name: string,
    age: number
}

```
interfaceは定義とdefault exportを同時にできるけど、typeは定義してからでないとできなかった。ただ、名前付きexportは定義と同時にできた。

### 名前空間
```typescript
/**
 * 名前空間
 */
export namespace FooBar {
    // import先で使用できない
    function callName(): void {
        window.alert('FooBar')
    }

    // import先で利用できる
    export function exportedFunction(): void {
        window.alert('exportedFunction')
    }
}

namespace BarBaz {
    function callName(): void {
        window.alert('BarBaz')
    }

    // どうやって使うのか知りたい
    export function exportedFunction(): void {
        window.alert('exportedFunction')
    }
}

namespace Hoge {
    // import先で使えない
    function hoge(): void {
        console.log('hoge')
    }
　　// import先で使える
    export function fuga(): void {
        console.log('fuga')
    }
}

export default Hoge
```typescript
クラスとは違って、namespace内の関数もそれぞれexportしないといけないみたい。exportしていないnamespace内の関数をexportすることもできてしまったけど、importできなかった。default exportはtypeと同様で、一旦定義しなければいけないみたい。

### import
```typescript
/**
 * 一行で書くこともできるし、複数行で書くこともできる
 */
// import default, { Bar, Foo, Baz } from "../exporter";
import { Bar, Foo, Baz, FooBar } from "../exporter";
import default from "../exporter";

/**
 *  import * as name "path"でインポートすると、そのファイルでexportされているすべてがimportされる
 *  import name "path"でインポートするとdefault exportされているものだけがimportされる
 */
import * as All from "../exporter";
```