OmniAICreator/Qiita-1.07M huggingface dataset:
gzipで言語判定

# はじめに

以前、UNIXに標準搭載されているライブラリを使って日英の言語判定をしたくなったので、gzipを使った言語判定の実験をしました。

# 言語判定とは

文が与えられたときにそれがどの言語で書かれているのか判定するタスクです。

他の自然言語処理タスクで使われるコーパスのクリーニングをしたり、言語によって処理を変えたりするのに有用な技術です。

```
# 言語判定の例
I am Ok. -> 英語と判定
問題ない。 -> 日本語と判定
```

# gzip を使った言語判定の仕組み

gzip はファイル圧縮に使われるライブラリです。そのため、言語判定タスクに使うと聞いてもピンとこないかもしれません。

gzip はファイルの圧縮に **LZ法**という手法を用いています。
これは辞書を用いた圧縮法で、ある文字列が繰り返し出現したとき、その文字列の代わりに辞書中の番号を出力することで元の文を短い長さで表すという仕組みです。

その仕組み上、今まで入力された文字列にないようなフレーズを多く含む文に対しては圧縮の効率が悪くなる性質があります。

gzip を用いた言語判定では、この性質を活かして、判定したい言語ごとにその言語で書かれた文およびそれを圧縮したものを用意し、そこに追加で判定の対象となる文を入れてさらに圧縮したときのファイルサイズの増え方に基づいて言語を判定します。

# 実験設定

## データ

[Parallel Universal Dependencies](https://universaldependencies.org/treebanks/ja_pud/index.html) ツリーバンクの日本語、英語、フランス語を用いました。

これはニュースやWikipediaの記事から作られた形態素や係り受けの情報が付与されているパラレルコーパスです。

各言語1000文のうち初めの100文を予め圧縮する文、残りの900文を言語判定の評価に用いました。

## 手法

ある入力文があったとき、各言語 $L$ について以下の値を計算し、その値が最も小さな言語をその入力文の言語とします。

```
[言語 L のテキストファイルに判定対象の文を加えたものの圧縮後サイズ]-[言語 L のテキストファイルの圧縮後サイズ]
```

上の値を求める具体的なスクリプトは以下の通りです。

```
#[言語 L のテキストファイルの圧縮後サイズ]
zipped_constant=$(cat textfile.lang.txt | gzip | wc -c)

#[言語 L のテキストファイルに判定対象の文を加えたものの圧縮後サイズ]
zipped_input_text_plus_train_data_size=$(cat textfile.lang.txt <( echo "これは日本語の文です。") | gzip | wc -c)

#[言語判定に用いるスコア]
score=$((${zipped_input_text_plus_train_data_size} - ${zipped_constant}))
```

# 実験結果

下に分類結果を示します。

単純な手法でしたが、どの言語でも高い分類性能を示しています。

|混同行列|英語|フランス語|日本語|
|---|---|---|---|---|
|**英語(予測)**|894|2|0|
|**フランス語（予測）**|2|876|0|
|**日本語（予測）**|4|22|900|


|言語|Precision|Recall|
|---|---|---|
|**英語**|99.78|99.33|
|**フランス語**|97.77|97.33|
|**日本語**|100|100|


# エラー分析

以下に英語の文における分類間違いの例を示します。

結果として、どの文も英語として自然な文であったため、なぜ誤判定したかをはっきり言えるような例ではありませんでした。

今回圧縮ファイルに使った文数は100文ほどと少なかったため、この文数を大きくすれば下のような自然な文に対してもうまく分類ができるようになるかもしれません。

```
# 英語の文をフランス語と予測
Its importance resides in two facts.
# 英語の文を日本語と予測
Major European rivers flow from Switzerland, such as the Rhine, the Rhône, the Inn, 
the Ticino and the Po, all of which have headwaters in the Alps and flow into neighbouring c
ountries, finally emptying into the North Sea, the Mediterranean Sea, the Adriatic Sea and the Black Sea.
```

# さいごに

この記事ではファイル圧縮を用いた言語判定を行い、その性能を評価しました。

UNIXに標準搭載のライブラリしか使えないときの言語判定などにこの記事をご活用いただけると嬉しいです。

# 参考文献

[Language detection in the Bash command line using gzip (aka Comprehension is compression)](https://ileriseviye.wordpress.com/2011/12/19/language-detection-in-the-bash-command-line-using-gzip-aka-comprehension-is-compression/)
【AWS】SystensManager AutomationでリソースID以外でターゲットを指定する方法

# SystemsManager Automationのドキュメントでリソース指定にIDを使いたくない
AWSではSystemsManager Automation(以下、SSM Automation)という機能があります。その機能を利用したEC2バックアップ方法について記事を書きました。
実際はこのドキュメントをメンテナンスウィンドウを利用して定期的に実行しています。

https://qiita.com/chittai/items/dbe9185ae552fd54fac5

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-maintenance.html

ここでの運用はバックアップ対象を直接ドキュメントに記述しています。ですが、これだと困ることがあります。例えば、**EC2/Volumeのリストアを行い、リソースIDが変わるケース**です。
リストアする毎にリソースIDを修正しなければなりません。それは耐えられないので、直接リソースIDを記入する運用を変えたいと思います。

ここで、ポイントとして上げられるのは下記になります。
1. 操作対象のリソースにTagをつける
1. Tagベースでリソースグループを作成する
1. メンテナンスウィンドウのターゲットでリソースグループを指定する
1. メンテナンスウィンドウのタスクで疑似パラメータを使用する
1. 1タスクで操作する対象は1種類のリソースとする(EC2,EBSなどちゃんと分けて管理する)

# 構成について
まず、構成について紐解きます。`Automationのタスクを定期的に実行する`には、`メンテナンスウィンドウを利用`する必要があります。このメンテナンスウィンドウでは
`タスクという単位で操作を登録`することができます。そして、操作対象を`ターゲットという単位で登録`することができます。このターゲットは`リソースグループ単位でもEC2インスタンス単位でも登録できます`。

そして、リソースグループはTagベースでの登録が可能です。なので、操作したい対象に同じタグをつけ、リソースグループでまとめることでリソースIDを直書きせずに
対象を指定することができます。

## 対象リソースにTagをつける
今回は、`linux`, `windows2019`というインスタンスに`Automation:Yes`というタグを追加しました。このタグベースでリソースグループを作成します。

![](https://storage.googleapis.com/zenn-user-upload/f7mfz09c4f4lg0kr95tgwt9v4kl0)

![](https://storage.googleapis.com/zenn-user-upload/cur7ddeewetauh611la3ewuream6)



## Tagベースでリソースグループを作成する
リソースグループ作成画面からTagベースでリソースグループを作成します。

![](https://storage.googleapis.com/zenn-user-upload/car6o4q2lku6zava9x9hr0q151sf)

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


## メンテナンスウィンドウのターゲットでリソースグループを指定する
メンテナンスウィンドウのターゲットで先ほど作成したリソースグループを登録します。

![](https://storage.googleapis.com/zenn-user-upload/692r7spt6qige5bmhbvoutnh6ewd)

![](https://storage.googleapis.com/zenn-user-upload/vju4bft7aoj7wz1uk938nkaowmvh)


## メンテナンスウィンドウのタスクで疑似パラメータを使用する
ここからも大事なポイントとなります。次に、タスクを登録します。今回は`AWS-StopEC2Instance`を実行します。想定される動作は、先程作成したリソースグループに登録されているEC2インスタンスすべてが停止されることです。

![](https://storage.googleapis.com/zenn-user-upload/7upf2n6w629t1kqabaldfdejob8c)

先程登録したリソースグループのターゲットを登録します。

![](https://storage.googleapis.com/zenn-user-upload/o2hmdkh4ptbqjs9p23h43e8688e9)


リソースグループを操作するのに必要な権限をロールに付与します。
それと、EC2インスタンスを停止できる権限があるロールを選択します。(今回はAdministrator権限がついたロールを使用しています)

```json
}
"resource-groups":"GetGroupQuery",
"resource-groups":"GetGroup",
"resource-groups":"GetTags"
}
```

![](https://storage.googleapis.com/zenn-user-upload/w8eg4e04jcmimzlx9qqm6me8qsav)

ここで一番大事なのは、`InstanceId`のボックスに`{{RESOURCE_ID}}`を記述することです。これは疑似パラメータといいます。この疑似パラメータを
記述することで、リソースグループに登録したリソースのID情報が`AWS-StopEC2Instance`に自動で渡されて実行されます。動作として、各リソースに対して
順番に子タスクが作成されそれぞれ実行されます。

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/mw-cli-register-tasks-parameters.html

![](https://storage.googleapis.com/zenn-user-upload/ov14nils071qeznjxrri8kludes5)

以上で完了です。

# 感想
`{{RESOURCE_ID}}`が登録されたリソースを順に置き換わるので、複数のリソースタイプが登録されていると、ドキュメントの引数と型があわずに基本失敗します。
なので、リソースごとにタスクとリソースグループを登録するのが良いかと思います。
MVCについて本気出してまとめてみた

# はじめに
Railsを当たり前のように使っているのですが、
「ところでMVCって何？」と問われると、意外と説明が難しい…と感じたので
自分なりに言語化するために、今一度まとめてみました。

※ここで扱うMVCは、Railsで使われる所謂「MVC2」と呼ばれるものです。


# MVCとは
Model View Controllerの略称で、
プログラム全体を、データの表示部分とデータの処理、データベース管理の3つの要素に分けた、Webアプリケーションのデザインパターンです。


Railsの場合、MVCの処理はざっと、下図(自作…:sweat_smile:)の流れで行われます。

![スクリーンショット 2020-12-18 15.38.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/813419/6e37168b-6766-d6e4-a5e5-d07a1ace22d7.png)


処理の流れに合わせて、MVCの各機能を順に説明します。


## MVCに入る前の…　ルーティングとは
クライアントからまずリクエストを受け取ります。
**リクエスト内容から、対応する処理を持つコントローラーへ行先を指定する「対応表」**のような役割を持ちます。

## C：Controller(コントローラー)
クライアントからの**リクエストに対する処理を制御**する部分です。
リクエストに該当するアクションを実行し、揃えた内容をレスポンスとしてクライアントへ返します。

レスポンスに必要な情報を揃えるために

- モデルに伝えて、必要なデータを用意する
- 画面表示に必要なデータをビューに渡す

といった、他機能との「橋渡し」のような役割を持っています。



## M：Model(モデル)
**データベースとやり取りをする「窓口」**のような機能を持ちます。
コントローラーからの指示を元にデータベースへアクセスし、
必要なデータを取得や、データの加工(挿入・更新・削除)を行う部分です。

また、**データの検証(バリデーション)やテーブル同士の関連付け**など、データベース内に保存されるデータについての細かい設定をする役割も担っています。


## V：View(ビュー)
最終的な**Webページの見た目**を設定する部分です。
コントローラーから渡されたデータをHTMLなどで整形し、レスポンスとして返す画面表示を行います。

また、**入力フォームからデータを取得し、コントローラーへ渡す**こともあります。


## MVCのメリット
下記2点が大きなメリットです。

- 機能毎に分離しており、分業がしやすい
- 他の機能部分の変更による影響を受けにくく、保守性が高まる

# 終わりに/感想
個人開発でなんとなくMVCを分かっているつもりでしたが、
記事にまとめることで、改めて理解を深めることが出来たと感じています。

初学者で拙い記事ですが、少しでもお役に立てると嬉しく思います。
最後まで読んでいただき、誠にありがとうございました。


## 開発環境
Ruby 2.6.5
Rails 6.0.3.4
MySQL
Visual Studio Code
(GoogleChrome)

## 参考記事・書籍
[【書籍】Ruby on Rails6 超入門/掌田津耶乃](https://honto.jp/netstore/pd-book_30097573.html)
[【公式】Railsドキュメント](https://railsdoc.com/)
[【Qiita】MVCという概念をさくっと理解するためのまとめ。](https://qiita.com/gcyata/items/772b6989f4cdab0bd047)
[【Qiita】MVCモデルについて](https://qiita.com/s_emoto/items/975cc38a3e0de462966a)
[【Qiita】RailsのMVCをまとめてみる](https://qiita.com/Tamitchao/items/b1d0de274290be1a01a4)
[【ピカワカ】MVCフレームワークを1から丁寧に解説！](https://pikawaka.com/rails/mvc)
create-react-appで生成したReactのJest実行をVSCodeからブレークが効く状態で実行する

## 概要

create-react-appで生成したReactのJest実行をVSCodeからブレークが効く状態で実行する。

## やり方

[公式](https://jestjs.io/docs/ja/troubleshooting#vs-code%E3%81%A7%E3%83%87%E3%83%90%E3%83%83%E3%82%B0%E3%81%99%E3%82%8B) に書いてあるのでそれ参照。。

[公式](https://jestjs.io/docs/ja/troubleshooting#vs-code%E3%81%A7%E3%83%87%E3%83%90%E3%83%83%E3%82%B0%E3%81%99%E3%82%8B) の `acebookのcreate-react-appを使用しているなら、Jestのテストを以下の設定でデバッグできます:` のコードを `launch.json` にコピペしてそれを実行する。

※テストコードの方もブレークできるし、実装側の方もブレークできる。
【Ruby】レシーバの型を簡単に取得するメソッド .class

個人メモです。

レシーバのクラス（型）を参照する、`class`および、親のクラスを確認する`superclass`

また`BasicObject`についても。

##目次

1. [.class](#1-class)
2. [.superclass](#2-superclass)
3. [.BasicObject](#3-BasicObject)


##1 .class

`.class`メソッドを使うと、レシーバのクラス（型）を簡単に取得することができる。

```ruby:
x = 100

x.class 
=> Integer
```

```ruby:
str = "text"

str.class
=> String
```

```ruby:
obj = {:a=>1, :b=>2, :c=>3}

obj.class
=> Hash
```

```ruby:
arr = [1, 2, 3]

arr.class
=> Array
```

```ruby:
nil.class
=> NilClass
```

```ruby:
(1..4).class
=> Range
```

```ruby:
(1.23).class
=> Float
```

```ruby:
Class.class
=> Class
```

`Class`は名前のないクラス。メタクラスと呼ぶ。
`class クラス名 end`で生成するクラスはこの`Class`を持っている。

```ruby:
class TestClass
  def hello
    msg = "test class"
    p "hello #{msg}"
  end
end

TestClass.class
=> Class
```

```ruby:
Kernel.class
=> Module
```

`Kernel`はすべてのクラスから参照できるメソッドを定義しているモジュール。

```ruby:
module TestModule
  def helloModule
    p "hello Module"
  end
end

TestModule.class
=> Module
```


<br>
##2 .superclass
superclassメソッドを使うとスーパークラスを参照することができる。

数値、文字列、配列、ハッシュ、nil、moduleなどの要素に対しては使えない。

`class`メソッドで取得した型に対してなら使用できる。


###スーパークラスとは？
親クラスのことをスーパークラスと呼ぶ。

```ruby:
class AAA
end

#クラスAAAを継承
class BBB < AAA
end
```

上記のようにクラスを継承した場合、**親となるクラスAAAがスーパークラス**となる。

BBBをサブクラスと呼ぶ。

・`class サブクラス名 < スーパークラス名`




###Numericの場合

```ruby:
100.class
=> Integer

100.class.superclass
=> Numeric

100.class.superclass.superclass
=> Object

100.class.superclass.superclass.superclass
=> BasicObject

100.class.superclass.superclass.superclass.superclass
=> nil
```

IntegerのsuperclassはNumeric。
NumericのsuperclassはObject。

```ruby:
#数値に使うとエラー
100.superclass
NoMethodError (undefined method `superclass' for 100:Integer)
```

<br>
###classの場合


```ruby:
class Aaa
end

Aaa.class
=> Class

Aaa.superclass
=> Object
```

ClassのsuperclassはObjectが返る。


###継承した場合

```ruby:
class Aaa
end

class Bbb < Aaa
end

BBB.superclass
=> Aaa
```


###moduleの場合
moduleに対してsuperclassは使えない。

```ruby:
module Xxx
end

Xxx.class
=> Module

Xxx.superclass
=> NoMethodError (undefined method 'superclass' for Xxx:Module)
```


<br>
##3 .BasicObject
BasicObjectとはクラスの最上位概念。
クラスの識別やメソッド呼び出しなど必要最低限の機能を持ったクラスのこと。


```:クラスの階層構造
BasicObject
↓
Object
↓
String,　Numeric, Module, Class,,,
            ↓
     Integer, Floot
```

RubyではすべてのデータをObjectと呼び、その下に文字列のStringや数値のNumericなどのくくりがある。

上にいくほどより抽象的でシンプルな定義になる。 BasciObjectはその際たるクラス。

<br>
BasicObjectのスーパークラスはnilとなる。

```ruby:
 BasicObject.superclass
=> nil
```
画像認識サービスの調査＆Microsoft Lobe使ってみた

# 画像認識技術の調査＆Microsoft Lobe使ってみた

こんにちは　iXIT株式会社　エンジニアの武見です。
switch版桃太郎電鉄がやりたいのですが肝心のswitch本体がありません。
もし買ったらゲームそっちのけで色々な場所に虫メガネを当ててみたいです。
![577d7728fff9f2366d86ef0e16aacd94.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913829/bcbcc19f-0f2b-7fcd-c37a-f6b745015ff6.jpeg)

##はじめに
最近（というかもう結構前からですが）話題になっている画像認識サービスの調査＆まとめを行いました。
また、最近リリースされたMicrosoft Lobeがとても使いやすそうだったので試してみました。

##画像認識とは（おさらい）
読んで字のごとく、画像/動画の中になにが映っているのかを認識する技術です。
人間であれば猫の画像を見たとき、「猫」が画像内に写っていることを認識できますが、コンピュータに画像を渡してもただのピクセルデータでしかありません。
これを、一連の手順（前処理→学習→認識）を実施することにより、画像に「猫」が映っているとコンピュータに認識させる技術です。

##各企業サービスをざっくりいうと
上記おさらいにて、一連の手順（前処理→学習→認識）と一言で片づけてしまいましたが、これらは複雑な計算＆実装が必要となりイチから作ろうとすると途方もない労力がかかってしまいます。
そこで、各企業から機械学習の専門的技術が無くとも、APIに画像や動画を渡すだけで画像分析や動画分析が簡単に行えるサービスが多数提供されています。
ここでは代表的なプラットフォームについて調査しました。

##*Amazon Rekognition* 
https://aws.amazon.com/jp/rekognition
![amazon.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913829/9c12f354-f7d9-b38b-6064-82f3dca70f72.jpeg)

###課金体系
学習済みの画像認識エンジンが無償トライアルの間（AWS の無料利用枠の間）利用できます。
その後は画像量に応じてそれぞれ課金形態が変化します。

###実装について
現在使用できるプロダクトには４種類があるようです、それぞれさらに細分化されさらに詳細なAPI機能を提供しています。
・Amazon Rekognition Image
・Amazon Rekognition Custom Labels
・Amazon Rekognition Video Stored Video
・Amazon Rekognition Video Streaming Video
https://docs.aws.amazon.com/rekognition/latest/dg/API_Reference.html

##*google Cloud Vision API* 
https://cloud.google.com/vision?hl=ja
（サイト内で画像をアップすることで簡単な動作確認ができます）
![google.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913829/f4868629-c469-e14f-dc22-2359fe23b58b.jpeg)

###課金体系
1,000 ユニット（１ユニット、画像１枚）内は無料。
その後は画像量に応じてそれぞれ課金形態が変化します。

###実装について
現在使用できるプロダクトには４種類があるようです。
・Vision API
・Vision Product Search
・AutoML Vision
・AutoML Vision Edg
https://cloud.google.com/vision?hl=ja#pricing

##*Microsoft Computer Vision API* 
https://azure.microsoft.com/ja-jp/services/cognitive-services/computer-vision/
![microsoft.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/913829/d91446bf-f5a0-1651-3eeb-73847dca3f9d.jpeg)

###課金体系
無料インスタンスでは、月5,000トランザクションまでは無料。
S1インスタンスでは、トランザクションごとに課金形態が変化します。

###実装について
使用できるプロダクトは１のようです、それぞれさらに細分化されさらに詳細なAPI機能を提供しています。
・Computer Vision API
https://westcentralus.dev.cognitive.microsoft.com/docs/services/computer-vision-v3-1-ga/operations/5d986960601faab4bf452005

###Microsoft その他
Microsoftには他にも同様のサービスがあるようですが割愛します。
Azure Face（顔認証に特化）
https://azure.microsoft.com/ja-jp/services/cognitive-services/face/
Microsoft Custom Vision API（独自の画像識別を構築することに特化）
https://docs.microsoft.com/ja-jp/azure/cognitive-services/custom-vision-service/

##*その他のサービス*
*IBM Watson*
https://www.ibm.com/jp-ja/watson
*Salesforce Einstein Vision*
https://www.salesforce.com/jp/products/einstein/features/
*docomo ドコモ画像認識プラットフォーム*
https://www.nttdocomo.co.jp/biz/service/dirp/

他にも色々なプラットフォームから画像認識サービスが提供されています。

##*Microsoft Lobe使ってみた* 
各プラットフォームの画像認識サービスについて大雑把に調査してみましたが、最後にMicrosoft Lobeという画像認識サービスの体験をしてみました。
理由は簡単で、とても手軽そうだからです。

　～間に合わなかったので今週土日に加筆します　 ＿|￣|○ お許しください～
Pythonで学ぶアルゴリズム　第6弾：うるう年

# #Pythonで学ぶアルゴリズム< うるう年 >

##はじめに
基本的なアルゴリズムをPythonで実装し，アルゴリズムの理解を深める．
その第6弾としてうるう年を扱う．

##うるう年
＜条件＞
4で割り切れる年をうるう年という．
※ただし，100で割り切れて，400で割り切れない年はうるう年ではない．
例)
2019年 : 4で割り切れない⇒うるう年でない
2020年 : 4で割り切れる⇒うるう年? → 100で割り切ない⇒うるう年である
1900年 : 4で割り切れる⇒うるう年? → 100で割り切れて，400で割り切れない⇒うるう年でない
2000年 : 4で割り切れる⇒うるう年? → 100で割り切れて400で割り切れる⇒うるう年である

このように条件の注意(※)さえ，満たさなければ，単純に４で割り切れるものはうるう年ということである．これをpythonで実装してみた．
以下にコードと出力を示す．

#####コード

```python:leap_year.py
"""
2020/12/18
@Yuya Shimizu

うるう年
・4で割り切れる年
ただし，100で割り切れて400で割り切れない年はうるう年でない
"""

def leap_year(year):
    if year%100 == 0 and year%400 != 0:
        print(str(year) + "年はうるう年でない．")
    else:
        if year%4 == 0:
            print(str(year) + "年はうるう年である．")
        else:
            print(str(year) + "年はうるう年でない．")

year = int(input("うるう年判定\n>>"))

leap_year(year)
```

#####出力１
```
うるう年判定
>>2000
2000年はうるう年である．
```
#####出力２

```
うるう年判定
>>1900
1900年はうるう年でない．
```

ユーザ入力により判定するものとした．
コードはいたってシンプルで，注意したところは，より特殊な条件をはじめに持ってくることである．

##改編版
@shiracamusのコメントを参考に上記のプログラムを改編した．
具体的には，関数内のprintを省きTrue, Falseを返すようにした．
以下にそのコードと出力を示す．

######コード
```python:leap_year_.py
"""
2020/12/18
@Yuya Shimizu

うるう年
・4で割り切れる年
ただし，100で割り切れて400で割り切れない年はうるう年でない

改編版
@shiracamusの提案を参考に，True, Falseを返すプログラムにした
"""

def leap_year(year):
    if year%100 == 0 and year%400 != 0:
        return False
    else:
        if year%4 == 0:
            return True
        else:
            return False

year = int(input("うるう年判定\n>>"))

print(leap_year(year))
```

#####出力
```
うるう年判定
>>2020
True
```
よりよいものができた．

##感想
今までに2，3回はうるう年のコードを書いたことはあったが，アルゴリズムを通して少し頭の中を整理できたおかげか，割とシンプルにかけたのではないかと思う．

##参考文献
Pythonで始めるアルゴリズム入門　伝統的なアルゴリズムで学ぶ定石と計算量
　　　　　　　　　　　　　　　　　　　　　　　　 増井 敏克　著　　翔泳社
Ubuntu18.04.05 LTS におけるpython仮想環境作成

Anacondaを使用しない仮想環境作成をUbuntuにて実施。

####環境
Ubuntu18.04.05 LTS
python:3.6.9

---

####仮想環境構築


####①アップデート

```
$sudo apt-get update
$sudo apt-get upgrade
$sudo reboot
```
---

####②venvのインストール

```
$sudo apt install python3-venv
```

venv: 仮想環境構築に必要なツール。Python3.3以降が必要らしい。

---
####③仮想環境を作る

```
#まずは仮想環境を作るディレクトリを作成。
$mkdir test_env
$cd test_env
```
mkdir test_env
![Screenshot from 2020-12-18 13-31-24 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607764/d5594329-8e8f-9da1-626a-2ce881633441.png)
`$cd test_env`で作ったディレクトリ内に移動する。

---

```
$python -m venv tensorflow
```

『tensorflow』名で仮想環境用のディレクトリを作成する。

![Screenshot from 2020-12-18 13-31-37 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607764/f18bd3aa-779b-59e9-1af6-6315d4b2fa8a.png)

中身は

![Screenshot from 2020-12-18 13-45-19 (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607764/72cc6f56-645a-4bd7-df0c-c1f4680ca5a2.png)

これで仮想環境が完成。

---

3)仮想環境内に入る

```
$source test_env/tensorflow/bin/activate
```
これで仮想環境として機能します。
私はhomeに仮想環境用の`test_env`ディレクトリを作成して
さらにその下に`tensorflow`と作成しているので長いです。

homeディレクトリに作っていれば`source tensorflow/bin/activate`で環境に入れます。

![Screenshot from 2020-12-18 15-35-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607764/e34685f5-5e6b-23c5-f486-a0fc6c6cfa49.png)
![Screenshot from 2020-12-18 15-35-35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607764/949e0e46-904d-4694-a350-f3ba4545cf38.png)

仮想環境内で

```
(tensorflow)~@~$pip3 install numpy
(tensorflow)~@~$pip3 install tensorflow-gpu==1.14
(tensorflow)~@~$pip3 install matplotlob
```

等々をインストールすれば大丈夫です。
ただ`pip3 install opencv-python`だけはエラーを吐いてうまくいかず。
エラーをコピーし忘れたのが痛いですが
結果としてpipが古くて必要なモジュールがないって吐いていました。

```
pip3 install -U pip 
```
これを打ち込んでアップグレードしてOKとなりました。

その後に

```
pip3 install opencv-python==3.4.11.45
```
と入力してインストール出来ました。

バージョン指定したのはopencv4系の不具合が怖かったからです。

とりあえずはこれで何とか行けました。

####④仮想環境をJupyter等で使用出来る様にする

仮想環境下で

```
(tensorflow)~@~$pip install ipykernel

#installが終わったら
(tensorflow)~@~$ipython kernel install --user --name=tensorflow
```
![Screenshot from 2020-12-18 16-04-53 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607764/c248b8a1-a8a2-d013-f3b2-bb59c720f66f.png)
![Screenshot from 2020-12-18 16-05-53 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607764/edbf3310-d8e8-c141-9a22-c9941037a93c.png)

これでjupyternotebookでカーネルの選択が可能になります。

![Screenshot from 2020-12-18 16-06-34 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607764/92ec805b-e842-da5c-7b8e-76cb2b58b359.png)


まぁ色々と試してる最中です。
ドローン用ESCについて

#概要
これは、プログラミングとほとんど無関係なので、消されるかも知れませんが、そのときは別のところに上げます
datasheetは"30A BLDC ESC - Optimus Digital"と検索すると、pdfがダウンロードできます

#BEC
ESCには、モーター用電源から制御基板用の電源を作るBEC回路があります。
この基板だと、PWM入力の電源供給は必要ありません

#突っかかったところ
サーボのようにPWM信号を送るだけでは動かない。
まず、始めにゼロとなるPWM信号の幅の信号を送る必要がある。つまり
ESC側の電源を入れる際に**幅1ms,50HzのPWM信号**を入れる。
入れると**プププ　　ピー　**となるので、これを確認したら、幅1ms(出力0)~2ms(出力max)でPWMを入れると動き出す。
はじめのゼロ入力操作をしていないと、1s周期でピッと鳴るだけ動かない。

サンプルプログラムを載せます。’ｊ’をシリアルで送ると０、'k'と送ると中間（1.5ms）になります。

```C
#include <MsTimer2.h>
volatile boolean flag = false;

void PWM(){
  flag =true;
}

void setup() {

  pinMode(9,OUTPUT); 
  digitalWrite(9,LOW);
  Serial.begin(250000);
  MsTimer2::set(20, PWM);
  MsTimer2::start();
  
 
}
volatile int k = 0;
void loop() {
 if(flag == true){
  flag = false;
  unsigned long time1 = micros();
  digitalWrite(9,HIGH);
  unsigned long time2 = micros();
  while(time2-time1<k){
    time2 = micros();
  }
  digitalWrite(9,LOW);
 }
 if(Serial.available()>0){
  int a = Serial.read();
  if(a == 'j'){
    k=1000;
  }else if(a=='k'){
    k=1500;
  }
  Serial.println(k);
  
 }
}
```
Flash Advent Calendar 18日目 - OSSを諦めて、有償化に踏み切った話 -

# 有償化に踏み切った話

`swf2js`は2013年から7年間OSSとして地道に活動していたのですが
注目度も低く、継続してメンテナンスを行うが苦しい状態だったので
2019年に自分の所属する会社(ソニックムーブ)と共同で開発する事で有償化に踏み切りました。

最後までOSSとして開発を行いたかったのですが
`swf2js`は誰からも必要とされないライブラリ。っという状況だったので
会社で認めてもらった時はとても嬉しかったです。

FlashPlayerに対しての風当たりも強かったので余計にそう感じたのかもしれません。。。

# 個人のOSS品質

「個人のOSSは、信頼性が低い」っと判断されがちですが
作っているエンジニアは実際に現場で活躍しているエンジニアが多く
実務では商品プロダクトを実装している方が大半だとおもいます。
どうか、個人のOSSという事だけで品質の判断をしないでもらえると嬉しいです。

# 応援の仕方は色々

誰かに必要とされ、何かの役に立っているという事が分かるだけで開発を継続できます。
もし、利用しているOSSがあれば`GitHub`の`star`を押すだけで、開発意欲を十分に満たされると思います。
(他のOSS活動を行っている方の一助になればと思い記事を書きました。)

# 次のチャレンジ

個人でOSS活動する為の心得や、継続するための開発手法などなど
今回とても学ぶ事が多かったです。

次に取り掛かりたいOSSがあり、その時には今回のような途中で挫けるような事がないよう
上手に立ち回れればと思います。

# FlashLover

2021年以降もSWFを配信できる「FlashLover」っというアーカイブサイトを準備しています。
[JswfPlayer × swf2js 「FlashLover」](https://flashlover.jp)

有償版のswf2jsもこのアーカイブサイトであれば無料で利用可能です。
また、将来的には個人の方のAdsenseも配信できる仕組みを導入する予定です。

是非、ご期待いただけばと思います。

今日はOSSの愚痴みたいになってしまったのですが、明日はOSSで得られた事を書こうと思います。
pythonで外部コマンドを実行する(結果を受け取る/受け取らない場合の両方)

python3, MacOSとLinuxで動作確認済み。

```python
import os
import subprocess


def exec_os_system(cmd: str):
    '''
    コマンドを同期実行し、
    標準出力と標準エラー出力は、実行したプロンプトに随時表示する
    戻り値はコマンドのリターンコード
    '''
    # os.systemはwaitで子プロセスの終了を待つため、リターンコードの上位8bitにリターンコードが格納される
    # それを取るため8bit右にシフトする
    # see http://d.hatena.ne.jp/perlcodesample/20090415/1240762619
    rt = os.system(cmd) >> 8
    return rt


def exec_subprocess(cmd: str, raise_error=True):
    '''
    コマンドを同期実行し、
    標準出力と標準エラー出力は最後にまとめて返却する
    raise_errorがTrueかつリターンコードが0以外の場合は例外を出す
    戻り値は3値のタプルで (標準出力(bytes型), 標準エラー出力(bytes型), リターンコード(int))
    '''
    child = subprocess.Popen(cmd, shell=True,
                             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout, stderr = child.communicate()
    rt = child.returncode
    if rt != 0 and raise_error:
        raise Exception(f"command return code is not 0. got {rt}. stderr = {stderr}")

    return stdout, stderr, rt


if __name__ == "__main__":
    exec_os_system("date")

    stdout, stderr, rt = exec_subprocess("date")
    print(stdout)
    print(stderr)
    print(rt)


```

実行結果

```
Fri Dec 18 16:04:49 JST 2020
b'Fri Dec 18 16:04:49 JST 2020\n'
b''
0
```
第20回日本情報オリンピック二次予選敗北記～パンケーキを許さない～

第20回日本情報オリンピックに参加しました。
結果から言うと、二次予選で184点しか取れず、本選に出場できませんでした。
# 記事を読む対象者
JOIで本選を目指す高校生以下の方
# 一次予選
日本情報オリンピックに関する説明は、いろんな記事でされているので省きます。一応日本情報オリンピックのホームページのリンクを貼っておきます。
リンク:https://www.ioi-jp.org/
日本情報オリンピックの一次予選は、配列や、ソート、文字列など、簡単なプログラミング術を知っていれば解くことができます。今年の一次予選の問題は、最も難しい問題でも難易度2であったため、簡単だったと言えます。事情によって、スマホ参加した回もありましたが、いずれの回でも20分以内で全完できました。
# 二次予選
ここからが今回の本題です。前回の第19回大会から、予選は一次予選と二次予選の2つに分かれました。前回のボーダーは、難易度5までは満点を取り、難易度7と9で部分点を取るというものでした。また、今年は自粛期間に精進をしていた人が多いと考えられたため、難易度7まで、ある程度埋めました。写真の付け方が分からないので、これもリンクだけ貼っておきます。どの問題を解いてるか等は、このリンクを見てください。
リンク:https://joi.goodbaton.com/?atcoder_id=cpcznksutbeoa
まだプログラミングを始めて7カ月も経ってなく、JOI対策を本格的にし始めたのが10月に入ってからというのも考えると、精進したほうかなーと思っています。この時は同期の精進量をそんなに見てませんでしたが、難易度8まで埋めている同期もいました。
## 当日
11:30に起床しました。軽くブランチをとり、コーラと音楽を用意して、準備万端！二次予選開始です。戦略としては、Cまでは確実に合わせ、D、Eで少しでも多く部分点を取るというものでした。前回そんな感じでしたからね。ただ、この戦略が後々仇になります。
### 競技開始
まず、Aを見ます。初問なので、愚直解を図りますが、制約が大きく、断念します。競技開始数分後、解法が2つ生えます。解法見たくない人もいるので、詳しい内容は書きません。どっちもあっているだろうと、実装が楽そうな方を実装しますが、サンプルが合いません。40分位試行錯誤したとき、この解法が嘘解法だということに気が付きます。急いでもう一つの解法を実装しますが、焦ってバグを生やし、ACには1時間ほどかかってしまいました。
Bを見ます。な　に　こ　れ
Cを見ます。どうせDPだろうと、43点解法が思いつき、とりあえず実装しますが、ここでもバグらせてしまい、50分溶かして、Dに行きます。このあたりですでにパニック状態に陥っており、ここで8点を確実に取ることをしませんでした。実は、**この8点を通していれば、二次予選通過していたのです。**
Dは問題文が長かったので飛ばし、Eを見ます。あ、HonestOrUnkindじゃーん。7点を取ります。
Bに戻ってきて、BFSで解けることに気づき、74点を取ります。
ここで残り15分です。
Dを見ます。確実に3点を取り、あとの12分で、二分探索を実装します。10分かけて実装しますが、これにバグを埋め込んでいました。試行錯誤するも、バグは取れず、競技終了です。
結果は184点。予選落ちを確信します。ボーダーは、324点を予想していました。ただ、得点表に記入した結果、210点台まで受かるとの予測もありました。
## 結果
結果として、188点以上が本選出場権を得ました。得点表に、185点の方が二人いたので、二次予選落ち3位くらいということでしょうか。悔しい！
# 最後に
来年も二次予選が行われることが予測されますが、傾向はまったくつかめません。ボーダーが満点になるかもしれませんし、100点を下回るかもしれません。今回の僕のように、パニックにならなければ本選に出場できたということもあるので、落ち着いて問題を解きましょう！

日本語力のない文章を最後まで読んでくださり、ありがとうございました。
Nuxtでmountedが2回呼ばれる

## 概要
mountedが2回発火して初期化処理が狂うことがありました
レアケースだと思うけどめっちゃハマったので残しときます

## 対策
nuxtのバージョンを上げましょう
v2.14.1とかv2.14.6なら大丈夫でした

コード的には問題なくて多分nuxt側のバグ

## 現象再現
以下条件を満たせば発生した

- nuxtのバージョンがv2.12.2
- ページ遷移前後でレイアウトが違う
- transitionでmode: 'in-out'が指定されている

#### index.vue
```vue
<template>
  <div>
    <div>
      <button @click="toNextPage">button</button>
    </div>
  </div>
</template>

<script>
export default {
  layout: 'test_layout',
  methods: {
    toNextPage() {
      this.$router.push('/sample')
    },
  },
  transition: { name: 'page', mode: 'in-out' },
}
</script>
```

#### sample.vue
```vue
<template>
  <div>
    <div>
      <nuxt-link to="/">back</nuxt-link>
    </div>
  </div>
</template>

<script>
export default {
  asyncData() {
    console.log('asyncData')
  },
  fetch() {
    console.log('fetch')
  },
  data() {
    console.log('data')
    return {}
  },
  created() {
    console.log('created')
  },
  mounted() {
    console.log('mounted')
  },
  beforeDestroy() {
    console.log('beforeDestroy')
  },
  destroyed() {
    console.log('destroyed')
  },
}
</script>
```

#### indexからsampleへのページ遷移時のログ
mountedどころか破棄されてもう一回作られてますね
でもasyncDataは1回

```
asyncData
data
created
fetch
mounted
beforeDestroy
destroyed
data
created
fetch
mounted
```
【2021年9月1日で】GASでリダイレクトする方法【使えなくなった】

***※追記（2021-09-02）本記事の内容は、昨日（2021-09-10）から使えなくなりました。ChromeのCORS対策のようです。詳細はコメント欄を参照ください。代替案はわかりません。スミマセン。***

GAS（Google Apps Script）でWebアプリを作るとき、SUBMITしたらホームページに戻るようにしたい。

ぼくは同じHTMLを、部分的に＜DIV＞タグを使ってhidden属性を有効化／無効化して、擬似的に複数のページに見せている。
SUBMITを行ったら、URLパラメーターを使ってdoget()関数で取得して操作（スプレッドシートの更新など）を行う。

これまではSUBMITの飛び先を（擬似的な）ホームページにしていたのだが、そうするとアドレスバーにURLパラメーターが丸見えなので、ENTERやF5を押すとまた更新動作が走るという問題があった。

そこで、URLパラメーターをクリアするために、（擬似的な）ホームページにリダイレクトしたいと思った。
いわゆる＜ＭＥＴＡ＞タグにrefreshを使うと、コンピューターが困った顔みたいなアイコンが出て「Connection Refused」と言われる。
JavaScriptを使ってリダイレクトしても同じ現象が起こる。

いろいろググって試行錯誤の末、このやり方ならうまくいった。

```
function doGet() {
  return HtmlService.createHtmlOutput(
    "<script>window.top.location.href='https://開発中のWEBアプリ';</script>"
  );
}
```

原理はよく分からない。
相変わらずひどくてスミマセン。

参考：
https://stackoverflow.com/questions/11315521/automatically-redirecting-to-a-page

**（この後おわり）**
pythonで緯度経度２地点間の距離計算（球面三角法利用）

```python
import math


def get_distance_m(lat1, lon1, lat2, lon2):
    """
    ２点間の距離(m)
    球面三角法を利用した簡易的な距離計算
    GoogleMapAPIのgeometory.computeDistanceBetweenのロジック
    https://www.suzu6.net/posts/167-php-spherical-trigonometry/
    """
    R = 6378137.0  # 赤道半径
    lat1 = math.radians(lat1)
    lon1 = math.radians(lon1)
    lat2 = math.radians(lat2)
    lon2 = math.radians(lon2)
    diff_lon = lon1 - lon2
    dist = math.sin(lat1) * math.sin(lat2) + math.cos(lat1) * math.cos(lat2) * math.cos(diff_lon)
    return R * math.acos(min(max(dist, -1.0), 1.0))


if __name__ == "__main__":
    # 緯度と経度方向に1秒角移動したときの距離を計算する
    distance = get_distance_m(lon1=139.0,
                              lat1=35.0,
                              lon2=139.0 + 1.0 / 3600.0,
                              lat2=35.0 + 1.0 / 3600.0)
    print(distance)
```

実行結果

```
39.972442531744505
```
pythonリトライするデコレータ

```python
from functools import wraps
import time


def retry_decorator(retry_num: int, sleep_sec: int):
    """
    リトライするデコレータを返す
    :param retry_num: リトライ回数
    :param sleep_sec: リトライするまでにsleepする秒数
    :return: デコレータ
    """

    def _retry(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for retry_count in range(1, retry_num + 1):
                try:
                    ret = func(*args, **kwargs)
                    return ret
                except Exception as exp:  # pylint: disable=broad-except
                    if retry_count == retry_num:
                        print(f"Retry_count over ({retry_count}/{retry_num})")
                        raise Exception from exp
                    else:
                        print(f"Retry after {sleep_sec} sec ({retry_count}/{retry_num}). Error = {exp}")
                        time.sleep(sleep_sec)

        return wrapper

    return _retry


@retry_decorator(retry_num=3, sleep_sec=1)
def hoge():
    raise Exception("fail")


if __name__ == "__main__":
    hoge()
```

実行結果

```
Retry after 1 sec (1/3). Error = fail
Retry after 1 sec (2/3). Error = fail
Retry_count over (3/3)
Traceback (most recent call last):
  File "retry.py", line 18, in wrapper
    ret = func(*args, **kwargs)
  File "retry.py", line 35, in hoge
    raise Exception("fail")
Exception: fail

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "retry.py", line 39, in <module>
    hoge()
  File "retry.py", line 23, in wrapper
    raise Exception from exp
Exception
```
Docker Desktopをupdateしたらコンテナを起動できなくなった話

今まで、dockerのアップデートを忘れていたのですが、久々にアップデートしてみたら、アップデート後、`docker-compose up`で以下の様なエラーが発生。その時の解決策です。

```console
ERROR: for node  Cannot start service node: Mounts denied: approving /Users/ryota/Library/Mobile Documents/com~apple~CloudDocs/path/to/file: file does not exist
ERROR: Encountered errors while bringing up the project.
```

## 追記(20/12/20)
Docker Desktopの`Preferences`を開いた後に、`Experimental Features`のタブを選択して、`Use gRPC FUSE for file sharing`を`OFF`にするとこの後解説していますが、ローカルの環境での変更(vscodeなどのエディタで保存した内容)がブラウザに反映されなくなってしまいました。そこで、`Use gRPC FUSE for file sharing`を`ON`にもどしたところ、反映されるようになりました。
ちなみに流れとしては、'OFF'にしてコンテナを起動後に反映されなくなって、コンテナ停止、その後`ON`にして起動し直したら上手く反映されるようになった感じです。

## 解決した方法

![スクリーンショット 2020-12-18 15.27.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509690/d101514d-6ed3-6a78-289c-80ee92359dd1.png)

上記画像の通り、Docker Desktopの`Preferences`を開いた後に、`Experimental Features`のタブを選択して、`Use gRPC FUSE for file sharing`を`OFF`にすれば上手くいきました🐳

ちなみに、バージョンは`3.0.1`でした。
[Cloudian #7] Python(boto3)で、バケットを削除してみる


## はじめに
[Cloudian](https://cloudian.com/jp/)は、AWSのS3完全互換のAPIを持ったオブジェクトストレージです。
[前回](https://qiita.com/yamahiro/items/3be0fe7787a099cf8826)は、Python(boto3)で、オブジェクトを削除してみました。

今回は、Python(boto3)で、オブジェクトストレージのバケットを削除してみようと思います。

## バケットの削除 / delete_bucket()
Cloudianに作成されているバケットを削除します。

以下の例では、バケット「pythonbucket1」を削除しています。


```python:test.py
import boto3

client = boto3.client(
    's3',
    endpoint_url='https://xxx.yyy.com'
)


# バケット名:pythonbucket1 の削除
client.delete_bucket(
    Bucket='pythonbucket1'
)
```


```bash
{'ResponseMetadata': {'RequestId': '9dad3274-0e30-1dbc-a754-06bdfcde1d5e',
  'HostId': '',
  'HTTPStatusCode': 204,
  'HTTPHeaders': {'date': 'Sun, 13 Dec 2020 20:12:33 GMT',
   'x-amz-request-id': '9dad3274-0e30-1dbc-a754-06bdfcde1d5e',
   'server': 'CloudianS3'},
  'RetryAttempts': 0}}
```


> ※注意
> バケットを削除するには、バケット内にオブジェクトが存在しない状態である必要があります。
> バケット内にオブジェクトが存在する状態で delete_bucket()を実行すると、以下のような BucketNotEmpty 例外が発生します。

> ```python
> client.delete_bucket(
>     Bucket='bucket1'
> )
> ```
>
> ```bash
> ClientError                               Traceback (most recent call last)
> <ipython-input-3-0bb1849245e5> in <module>
>       1 client.delete_bucket(
> ----> 2     Bucket='bucket1'
>       3 )
>
> ~/.pyenv/versions/anaconda3-2019.03/lib/python3.7/site-packages/botocore/> client.py in _api_call(self, *args, **kwargs)
>     314                     "%s() only accepts keyword arguments." % py_operation_name)
>     315             # The "self" in this scope is referring to the BaseClient.
> --> 316             return self._make_api_call(operation_name, kwargs)
>     317 
>     318         _api_call.__name__ = str(py_operation_name)
>
> ~/.pyenv/versions/anaconda3-2019.03/lib/python3.7/site-packages/botocore/client.py in _make_api_call(self, operation_name, api_params)
>     624             error_code = parsed_response.get("Error", {}).get("Code")
>     625             error_class = self.exceptions.from_code(error_code)
--> 626             raise error_class(parsed_response, operation_name)
>     627         else:
>     628             return parsed_response
>
> ClientError: An error occurred (BucketNotEmpty) when calling the DeleteBucket operation: The bucket you tried to delete is not empty

> ```


## まとめ
Python(boto3)で、バケットを削除してみました。

[次回](https://qiita.com/yamahiro/items/395437a72df179786b0c)も、Pythonでオブジェクトストレージ/Cloudianをいろいろ操作していきたいと思います。
PowerVS IBM i 日記(31): CGU とか DFU とか IBM Rational Development Studio for i (5770-WDS) のライセンスが必要で AMT ではできないこと

# CGU とか DFU とか IBM Rational Development Studio for i (5770-WDS) のライセンスが必要で AMT ではできないこと

PowerVS IBM i を使う時に、ほとんどのライセンスは標準で提供されるのですが、5770-WDS はユーザー数単位の追加課金になります。 

「[AMT で WDS に課金せずに PDM を利用する](https://qiita.com/6onoda/items/3e4437b71ea24c7d670c)」で書いたとおりに、標準で提供される 5770-AMT で PDM は利用できるのですが、AMT で代替できない機能はいろいろあります。

「[IBM Rational Development Studio for i 7.2 は、最新の IBM i ソリューションを作成するためのツールおよびプログラミング言語を引き続き提供します](https://www-01.ibm.com/common/ssi/printableversion.wss?docURL=/common/ssi/rep_ca/9/760/JAJPJP14-0159/index.html&request_locale=en)」に説明がありました。

>  ADTS のその他のコンポーネントは、 Rational Application Management Toolset for i に一切含まれていません。除外されているコンポーネントは以下のとおりです。
>
>    画面設計機能 (SDA)
    報告書設計ユーティリティー
    データ・ファイル・ユーティリティー (DFU)
    ファイル比較および組み合わせユーティリティー (FCMU)
    対話型ソース・デバッガー (ISDB)
    拡張印刷機能 (APF)
    文字作成ユーティリティー (CGU)
    クライアント/サーバー開発ツールセット (ADTS CS)


開発機能は理解できるのですが、CGU や DFU までとは...。これらは OS の機能かと思っていました。

## CGU のライセンス確認

STRCGU コマンドを確認します。

```
DSPCMD CMD(STRCGU)
```

QSYS に ある STRCGU はプロキシーコマンドで、実態は QPDA にあることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184930/f0121dbd-6402-fef8-f266-fa1a7c52a69e.png)

QPDA は、5770-WDS の オプション 21 であることがわかります。機能コードは 5103 です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184930/4aa064e6-4dac-fc5b-9618-31f390c429f9.png)

5770-WDS を 購入していない PowerVS IBM i を新規に作成しました。

STRCGU 実行します。

```
STRCGU
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184930/5284277f-8338-5b36-5de2-0f9a0c359105.png)

メニューが表示されるので、オプション 1 などの操作を行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184930/9862d69f-4653-e00b-ffe5-424b7e0e065e.png)

JOBLOG を確認すると、使用上限を超えたとのメッセージが出ています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184930/985d7f95-4f35-efe9-2c86-13795b7fdc96.png)

対象は 5770-WDS の 5103。先ほど確認した 5770-WDS の オプション 21 ですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184930/a48b07e0-1e33-3487-3135-0b8465110d2b.png)

## DFU のライセンス確認

DSPCMD CMD(STRDFU) や WRKOBJPDM LIB(QPDA) OBJTYPE(*CMD) で確認しても DFU が WDS 提供によるものであるようには見えませんでした。
「CRTDFUDSPF (DFU 表示装置ファイルの作成)」コマンドは QPDA にありましたが...

思い切って、5770WDS オプション 21 を削除してみました。

```
DLTLICPGM LICPGM(5770WDS) OPTION(21) LNG(*ALL) 
```

DFU を開始します。

```
STRDFU
```

メニューから DFU プログラムの作成・変更・削除がなくなりました。
確かに、DFU も 5770-WDS で提供されているようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184930/52a55222-b815-754e-1c51-5c287fdd5192.png)

---
当日記のIndexは[こちら](https://qiita.com/6onoda/items/f146c78c0c1bbe905a42)です。

---
許可の無い転載を禁じます。
この記事は筆者の個人的な責任で無保証で提供しています。
当記事に関してIBMやビジネスパートナーに問い合わせることは、固くお断りします。
redux-toolkitをreduxなしで使ってみる

こちらはVISITS advent calendar 2020 24日目の記事です。

https://qiita.com/advent-calendar/2020/visits

本日はフロントエンドエンジニアの[pipopotamsau](https://twitter.com/pipopotamasu3)がつとめさせていただきます。

本記事ではredux-toolkitでreduxを使わず、Reactの標準APIのみで使う方法をご紹介しようと思います。

## 1. redux-toolkitとは
redux-toolkitとはredux開発チームが公式に出している、より効率的かつ簡単にreduxを利用できるようにするライブラリになります。

https://redux-toolkit.js.org/

redux-toolkitの詳しい説明は本記事の趣旨ではないため割愛しますが、名前からわかる通り基本的には「redux」と一緒に使うためのライブラリです。

しかし、reduxなしでReactのuseReducerと一緒に使うこともできるので紹介したいと思います。

## 2. redux-toolkitとuseReducerを組み合わせて使ってみる
それでは早速redux-toolkitとuseReducerを組み合わせたサンプルアプリケーションをみていきましょう。

以下のようなカウンターアプリを作ってみます。

![スクリーンショット 0002-12-18 13.57.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199424/fa9e7256-2780-a632-a075-7593f360400c.png)

※ 本記事のサンプルアプリケーションのレポジトリは[こちら](https://github.com/pipopotamasu/redux-toolkit-and-usereducer)です

まずはredux-toolkitの`createSlice`ヘルパーを使用し、reducer、action creator、action typeを生成します。

```js
import { createSlice } from '@reduxjs/toolkit';

const initialState = { counter: 0 };

const slice = createSlice({
  name: 'counter',
  reducers:{
    INCREMENT: (state) => {
      state.counter += 1;
    },
    DECREMENT: (state) => {
      state.counter -= 1;
    },
    RESET: (state) => {
      state.counter = 0;
    },
  },
  initialState
});

const { actions, reducer } = slice;
```

次は上記のcreateSliceで生成したreducerとuseReducerを組み合わせて、stateとdispatch関数を生成します。

```react
const { actions, reducer } = slice;

function CounterApp() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    // return jsx
  );
}
```

ここまででほぼ終わりです。あとはcounterを表示したり、buttonのonClickイベントに上記で生成したdispatch関数とactionを組み合わせたコールバックを仕込むだけです。

```react
const { actions, reducer } = slice;

function CounterApp() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div className="counter-app">
      <p className="counter">Counter: { state.counter }</p>
      <ul className="buttons">
        <li>
          <button onClick={ () => dispatch(actions.INCREMENT) }>+</button>
        </li>
        <li>
          <button onClick={ () => dispatch(actions.DECREMENT) }>-</button>
        </li>
        <li>
          <button onClick={ () => dispatch(actions.RESET) }>reset</button>
        </li>
      </ul>
    </div>
  );
}
```

これで以下のようなカウンターアプリを作ることができました:tada:

![102450650-40a9f500-407a-11eb-8730-f24744af3dd6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199424/c885963b-089a-a70a-9ad3-34782937ded4.gif)


## 3. なぜredux-toolkitをuseReducerと一緒に使うのか？
基本的にreduxでredux-toolkitを使う理由と一緒だと考えています。

効率的にreducerやaction creators, action typeを作るためだったり、書き方を統一する意図で使用しています。

## 4. 終わりに
最後までお読みいただきありがとうございました。

本記事のサンプルコードは以下になります。コード全体がみたいという方はこちらを参照してください。
https://github.com/pipopotamasu/redux-toolkit-and-usereducer

さて、クリスマスまで残すところあと１日です。

VISITS Technologiesのアドベントカレンダーの最後を飾るのは、エンジニアリングマネージャーの[@kotala_b](https://qiita.com/kotala_b)になります！
Pleasanter NetCore をDockerで上げる


## はじめに

国産OSSとして有名なPleasanterですが、前々から興味があったので、試してみました。

が、セットアップ手順がとても難しく、
簡単に立ち上げられて簡単に壊せる環境が作りたかったので docker-compose で上がるようにしてみました。

なお、本記事は私が学習目的で実施した内容で、Pleasanter公式の手順ではない旨ご了承ください。
あと、コメントなどの画像投稿機能が動いていません（ファイルアップロードは動く）。そこら辺分かり次第修正します。

## 実施環境

Windows 10 64bit 上の docker(WSL2) と、
Ubuntu 18.04 64bit 上の docker で動作することを確認しました。

## コード

https://github.com/yoh1496/Implem.Pleasanter.NetCore/tree/feature/docker

ここに上げてます。

## 手順

下記手順は上記ブランチをチェックアウトしてから行うのを前提とします。

```bash
git clone https://github.com/yoh1496/Implem.Pleasanter.NetCore.git
cd Implem.Pleasanter.NetCore
git checkout feature/docker
```

### 起動

下記コマンドでバックグラウンドに起動できます。

```bash
docker-compose up -d
```

### セットアップ

Pleasanterのドキュメントにある CodeDefiner コマンドは下記コマンドで代替できます。

```bash
docker-compose exec pleasanter dotnet Implem.CodeDefiner.NetCore.dll <args>
```

### ブラウザで開く

デフォルトでは `5001` 番ポートで開けます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328319/5f10cac4-4157-bc4e-eb13-7218374b40d0.png)

簡単😋

## 解説

ちょっと備忘録的にDockerfileを書くにあたり検討した内容を記載します。

### マルチステージビルド

```Dockerfile
# 冒頭
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
```

```Dockerfile
# 末尾
FROM build AS publish
RUN dotnet publish "Implem.Pleasanter.NetCore.sln" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
```

今回はこんな感じで「マルチステージビルド」というものをしています。
こうすることで、出来上がるイメージには SDK が含まれず軽いイメージができあがります。

### 設定ファイルの分離

```yml
    volumes:
      - ./Implem.Pleasanter/App_Data/:/app/App_Data/
```

`docker-compose.yml` 内で上記のように App_Data を外出しできるようにしています。「とりあえず」でやってしまったので今後要検証です。

### 永続化？

Pleasanterのデータ保持がどのようになされているのか、まだ理解しきれていないので特に手はつけていません。

### postgreSQL を別コンテナに

App_Data のところ ConnectionString が localhost でベタ書きされていたので、`postgres` でベタ書きしました（よくない）

```js
{
    // 略
    "SaConnectionString": "Server=postgres;Database=postgres;UID=postgres;PWD=SetSaPWD",
    "OwnerConnectionString": "Server=postgres;Database=#ServiceName#;UID=#ServiceName#_Owner;PWD=SetAdminsPWD",
    "UserConnectionString": "Server=postgres;Database=#ServiceName#;UID=#ServiceName#_User;PWD=SetUsersPWD",
    // 略
}
```

### 大文字小文字問題

あまり意図がわかっていなくて恐縮ですが、

- `wwwroot/images` フォルダに `Hayato*.png` と、 `hayato*.png` というファイル
- `dotnet publish` では **それらは同一ファイルとしてみなされ** 小文字のhayato*.png がコピーされない
- **トップページでは hayato*.png を参照**する

というコンボで404が発生してしまったため Hayato*.png を消しています。

https://github.com/Implem/Implem.Pleasanter.NetCore/tree/master/Implem.Pleasanter/wwwroot/images


## 終わりに

.Net Framework 依存のコードが Implem.Pleasanter 内にあるため、
ビルド時に大量にWarningが発生しますが、手元で動かしている分には普通に動いているようです。
（コメントの画像投稿機能は除く）
CUDAとnvidiaドライバの削除する方法

#CUDAとnvidiaドライバの削除方法

 - cudaとnvidiaのドライバをubuntuに間違っていれて削除しようとしたとき，方法がわからず大変だったので備忘録代わりに初めてQiitaで書いてます．

```
sudo apt-get --purge remove nvidia-*
sudo apt-get --purge remove cuda-10.2 
  # cudaは*ではなくバージョンを打ってやらないと削除できなかった．私の場合ver10.2だったため10.2で記載している
```
[Cloudian #6] Python(boto3)で、バケットに格納されているオブジェクトを削除してみる


## はじめに
[Cloudian](https://cloudian.com/jp/)は、AWSのS3完全互換のAPIを持ったオブジェクトストレージです。
[前回](https://qiita.com/yamahiro/items/ee1112a95185becc189f)は、Python(boto3)で、オブジェクトのリスト表示してみました。

今回は、Python(boto3)で、オブジェクトストレージのバケットに格納されているオブジェクトを削除してみようと思います。


## バケットに格納されているオブジェクトの削除 / delete_object()
Cloudianにアップロードされたファイル(オブジェクト)を、delete_object()を使って削します。

以下の例では、バケット「pythonbucket1」に保存されているキー「10mb.dat」の、最新バージョンのオブジェクトを削除しています。


```python:test.py
import boto3

client = boto3.client(
    's3',
    endpoint_url='https://xxx.yyy.com'
)


# バケット名:pythonbucket1 のオブジェクト:10mb.datファイル を削除
client.delete_object(Bucket='pythonbucket1', Key='10mb.dat')
```


```bash
{'ResponseMetadata': {'RequestId': '9dad3b68-0e30-1dbc-a754-06bdfcde1d5e',
  'HostId': '',
  'HTTPStatusCode': 204,
  'HTTPHeaders': {'date': 'Sun, 13 Dec 2020 22:27:45 GMT',
   'x-amz-request-id': '9dad3b68-0e30-1dbc-a754-06bdfcde1d5e',
   'server': 'CloudianS3'},
  'RetryAttempts': 0}}
```


## まとめ
Python(boto3)で、オブジェクトを削除してみました。

[次回](https://qiita.com/yamahiro/items/350b6b31e0a3ea8d16ff)も、Pythonでオブジェクトストレージ/Cloudianをいろいろ操作していきたいと思います。
Debian 10 (buster)へのFlutter導入

<a href="https://flutter.dev/docs/get-started/install/linux">公式ドキュメント</a>に沿って導入します。
<h1>Flutterのインストール</h1>
<h2>依存パッケージの準備</h2>
<p>Flutterには、以下のパッケージが必要です。</p>
<ul>
<li>bash</li><li>curl</li><li>file</li><li>git 2.x</li><li>mkdir</li><li>rm</li><li>unzip</li><li>which</li><li>xz-utils</li><li>zip</li>
</ul>
<p>多くの環境ですべて導入済みだと思います。念の為、インストール済みか(apt search等で)確認して、もし入っていないものがあればインストールしておきましょう。</p>
<p><code># apt search curlとか</code>
[installed]ならOK。</p>
<p><code># apt update<br># apt install bash curl file git unzip xz-utils zip</code>
確認せずにまとめてapt installしてもよし。</p>

<h2>Flutterをインストール</h2>
<p>snapが使えない環境で、ビルドするのが億劫でも、<strong>実行ファイルたちをダウンロードする方法</strong>か、<strong>gitをクローンする方法</strong>でインストールできます。</p>
<p>(Ubuntu20.04LTS等の環境であれば、<code>$ sudo snap install flutter --classic</code>でインストールできます。)</p>

<h3>実行ファイルたちをダウンロード</h3>
<p>curlコマンドで直接ダウンロードします。以下は記事作成時の最新安定版であるver1.22.5の場合です。</p>
<code>$ cd ~/Downloads<br>$ curl https://storage.googleapis.com/flutter_infra/releases/stable/linux/flutter_linux_1.22.5-stable.tar.xz</code>

<p>インストール先のディレクトリ(何処のどんな名前でも構いませんが、この記事では~/developmentとします)を用意し、
そのディレクトリ内でtarボールを展開します。</p>
<code>$ mkdir ~/development<br>$ cd ~/development<br>$ tar xf ~/Downloads/flutter_linux_1.22.5-stable.tar.xz</code>

<h3>gitクローン</h3>

<p>上の方法では更新の度にダウンロードと展開をする必要があります。gitで更新したい場合は、公式リポジトリをクローンしましょう。</p>
<code>$ mkdir ~/development<br>$ cd ~/development<br>$ git clone https://github.com/flutter/flutter.git</code>

<h3>PATHを通す</h3>

<p>展開orクローンしたディレクトリにflutterがインストールされましたが、一々インストール先に来てからから(もしくは長いパスを指定して)実行するのは面倒です。そこで、パスを通しておきます。</p>

<p>一時的にパスを通す場合はこちら(~/development内で実行)</p>
<code>$ export PATH="$PATH:\`pwd\`/flutter/bin"</code>

<p>恒久的にパスを通す場合は、お使いのシェルや環境に合わせて設定する必要があります。以下はデフォルトで使われているbashの場合の例です。PATHを設定したあとは、端末/ターミナル を 再起動/新規ウィンドウ で開き直してください。</p>
<code>$ export PATH="$PATH:/home/ユーザー名/development/flutter/bin"</code>

<p>それでもパスが通らない場合は、<a href="https://flutter.dev/docs/get-started/install/linux#update-path-directly">直接ファイルにPATHを追記する方法</a>を試しましょう。</p>

<h2>インストールできたか確認</h2>
<p>Flutterが導入できたら、以下のコマンドを実行できるはず。</p>
<code>$ flutter help</code>
<p>Fluterを実際に使うためには、いくつか追加設定が必要な場合が多いです。例えば、Androidアプリ向けに開発したい場合は、AndroidStudioのインストールが必要です。</p>
<p>以下のコマンドで、どのパッケージが足りないか、どう設定すればいいかなど、比較的丁寧に教えてくれます。[x]がついているものの中で、必要だなと思ったものを指示に従ってインストールしましょう。</p>
<code>$ flutter doctor</code>
<h2>おわりに</h2>
<p>上手く動かない場合は、<a href="https://flutter.dev/docs/get-started/install/linux">公式ドキュメント</a>が参考になるかもしれません。</p>
Elixirで速度を追い求めるときのプログラミングスタイル

この記事は[Elixir Advent Calendar 2020](https://qiita.com/advent-calendar/2020/elixir)の18日目の記事です。

昨日は @tamanugi さんの[「AtCoder用のmixタスクを作ってみた」](https://qiita.com/tamanugi/items/f6bb83ef45ea0e4ba98d)でした。

さて遅れましたが，今回は「Elixirで速度を追い求めるときのプログラミングスタイル」をお送りします。

追記: [「Elixirで速度を追い求めるときのプログラミングスタイル PartⅡ，Pelemayの近況もあるよ」](https://qiita.com/zacky1972/items/b1051a597a1d62b6f6ab)， @kentaro さんによる [「Enum.mapと再帰スタイルの比較、ふたたび（末尾再帰版）」](https://qiita.com/kentaro/items/50637cddca958eb881bd#_reference-c70d31488ac613d14dc3) も参照ください。末尾再帰にすると高速になります。

# リスト操作について

* リストの先頭から要素を取り出すときと，リストの先頭・末尾に要素を追加するのは高速に出来ます。
* リストから要素を取り出すとき，末尾から取り出すと多大な時間をロスします。

`Enum.reverse/1`によるリストの反転は，`List.delete_at/2`で末尾を削除するよりは高速に実行できます。

検証コード

```elixir:bench/list_add_bench.exs
defmodule ListAddBench do
  use Benchfella

  @list Enum.to_list(1..1_000_000)

  bench "add head" do
    [:a | @list]
  end

  bench "add tail" do
    @list ++ [:a]
  end
end
```

```elixir:bench/list_pop_bench.exs
defmodule ListPopBench do
  use Benchfella

  @list Enum.to_list(1..1_000_000)

  bench "pop head" do
    fn [_head | tail] -> tail end.(@list)
  end

  bench "pop tail" do
    List.delete_at(@list, -1)
  end
end
```

```elixir:bench/list_reverse_bench.exs
defmodule ListReverseBench do
  use Benchfella

  @list Enum.to_list(1..1_000_000)

  bench "reverse" do
  	Enum.reverse(@list)
  end
end
```

結果

```
## ListAddBench
benchmark iterations   average time 
add tail  1000000000   0.01 µs/op
add head  1000000000   0.01 µs/op
## ListPopBench
benchmark iterations   average time 
pop head   100000000   0.02 µs/op
pop tail          50   35337.48 µs/op
## ListReverseBench
benchmark iterations   average time 
reverse          200   8831.18 µs/op
```

(iMac Pro調べ)


# `Enum.map`と再帰スタイルは，ほとんど差がない

```elixir:lib/high_performance_programming.ex
defmodule HighPerformanceProgramming do
  def r_map([], _func), do: []
  def r_map([head | tail], func) do
    [func.(head) | r_map(tail, func)]
  end
end
```

```elixir:bench/enum_bench.exs
defmodule EnumBench do
  use Benchfella

  @list Enum.to_list(1..1_000_000)

  bench "Enum.map" do
  	Enum.map(@list, & &1 * 2)
  end

  bench "recursive" do
  	HighPerformanceProgramming.r_map(@list, & &1 * 2)
  end
end
```

結果

```
## EnumBench
benchmark  iterations   average time 
Enum.map           50   55004.48 µs/op
recursive          50   55315.28 µs/op
```

ほとんど誤差ですね。

2018年ごろに検証したときには再帰スタイルの方が速かったのですけどね！ 変わるものですね。

# まとめ

* リストへの要素の追加は先頭からでも末尾からでも極めて高速
* リストの先頭からの要素の取り出しは高速
* `List.delete_at/2`を使って無理矢理リストの末尾から要素を取り出すと極めて低速
* リストの走査には，`Enum.map/2`を使うのが推奨。以前に比べてパフォーマンス面も改善したようなので，再帰スタイルをあえて使う理由がないです。

明日19日目の[Elixir Advent Calendar 2020](https://qiita.com/advent-calendar/2020/elixir)は @a_utsuki さんの[「Elixirで竹内関数を計測してみた」](https://qiita.com/a_utsuki/items/23381fff2ac630800bf0)です。よろしくお願いします。

本研究成果は、科学技術振興機構研究成果展開事業研究成果最適展開支援プログラム A-STEP トライアウト JPMJTM20H1 の支援を受けた。
JavaScript,10,switch文,case,default,break,初心者向け

10,switch文
JSでのswitch文の書き方を解説しています。
case（ケース）、default（デフォルト）、break（ブレイク）のそれぞれの意味と実際のコーディング例も紹介しています。
if, else ifなどを多く使う場面においては重宝しますので、是非覚えていきましょう！

```
const weathers = ["晴れ", "曇り", "雨", "雪"],
  randomNumber = Math.floor(Math.random() * weathers.length),
  randomWeather = weathers[randomNumber];
  WeathersMessageElement = document.getElementById("WeathersMessage");
```

document.getElementById("weather").textContent = "今日の天気は" + randomWeather + "です";

↓ここから

```
if (randomWeather === "晴れ") {
WeathersMessageElement.textContent = "日傘を持って行った方がいいかも";
} else if (randomWeather === "曇り") {
  WeathersMessageElement.textContent = "今日は一日曇っているみたいです";
} else if (randomWeather === "雨") {
  WeathersMessageElement.textContent = "傘を持って行きましょう";
}else if (randomWeather === "雪") {
  WeathersMessageElement.textContent = "今日は寒そうなのでコートを着て行きましょう";
}
```

↑ここまではswitchできる

```
function insertTextWeatherMessage(text) {
  WeathersMessageElement.textContent =text;
}


switch (randomWeather) {
  case "晴れ":
    insertTextWeatherMessage = ("日傘を持って行った方がいいかも");
    break;
  case "曇り":
    insertTextWeatherMessage = ("今日は一日曇っているみたいです");
    break;
  case "雨":
    insertTextWeatherMessage = ("傘を持って行きましょう");
    break;
  case "雪":
    insertTextWeatherMessage = ("今日は寒そうなのでコートを着て行きましょう");
    break;
    default:
      insertTextWeatherMessage = ("今日はやばい");
}
```

randomWeather === 下のcaseのどれかになりなら実行される
default:はそれ以外
【Swift】UIImageViewを使わずに背景に画像をセットする方法。

#はじめに
背景に画像を使用する際にStoryboradにImageViewをセットすると思っていたのですが、backgroundcolorのセットと同じようにできることがわかったので備忘録をかねて簡単にまとめました。
#準備
・Xcodoに新規プロジェクトを作成
・背景に使用する画像を準備
<img width= "300" alt="スクリーンショット 2020-12-18 13.33.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/930365/4509a420-c86b-c32f-ce0b-b12ccba01562.png">
#実装
```Swift:viewDidLoad
 self.view.backgroundColor = UIColor(patternImage: UIImage(named: "backgoroundImage.jpg")!)
```
#完成
<img width= "300" alt="スクリーンショット 2020-12-18 13.59.45 (1) (1).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/930365/b0140973-942d-6f9f-70cf-c77be1f81ae2.png">

#参考
[swiftを使ってXcodeで背景画像を設定するには?](https://www.366service.com/jp/qa/29320fdf1b13e08e2bca60f6d137e335)
#まとめ
簡単に背景に画像をセットする事ができました。
今回の記事は自分の備忘録的な要素が強くなってしまったのですが、背景に画像をセットする方法を探している人の参考に少しでもなれたら嬉しいです。
セッション管理とスレッドモデル

# リスト

- [セッション管理とスレッドモデル(PHP編)](https://qiita.com/tomoki0sanaki/items/4afa4949e48f3ce33938)
- [セッション管理とスレッドモデル(ClassicASP編)](https://qiita.com/tomoki0sanaki/items/bc6f54b4506da7ca06ad)
- [セッション管理とスレッドモデル(ASP.NET編)](https://qiita.com/tomoki0sanaki/items/5e7bb9baaa388c1e0f72)
- [セッション管理とスレッドモデル(JavaServlet/JSP編)](https://qiita.com/tomoki0sanaki/items/448db4629eb66e718740)

# まとめ

- シングルスレッド : PHP
- シングルスレッド : ClassicASP
- シングルスレッド : ASP.NET
- マルチスレッド : JSP/JavaServlet

# 以上