OmniAICreator/Qiita-1.07M huggingface dataset:
Swiftでよく調べちゃう記法のTips

#はじめに
コーディングスキルがガクッと下がったように感じる今日この頃、トレーニングも兼ねて学習サイトでコーディングのお勉強をしています。
せっかくなので使ったことがない、経験が薄い言語を使っているのですが、如何せん「あれ、こういうのってどう書くんだっけ…」ということが多いです。
そこで自分用にメモベースにはなってしまいますが、少しずつこちらに残していきたいと思います。
突っ込みどころなどがあれば、ご指摘よろしくお願いいたします:raising_hand:
※SwiftのVersionは 5.0.1 を使用しています。

#文字列操作
```swift

// 入力文字列を受けて分割する
// need import Foundation
let arr:[String] = readLine()!.components(separatedBy: " ")

// 数値→文字列
String(x) 
```

#数値操作
```swift

// 文字列→数値変換
Int(str)!

// 絶対値を求める
abs(num)
```

#BOOL値操作
```swift

// 反転
flag.toggle()
```

#ループ操作
```swift

// 指定した範囲のループ
for i in 0 ..< num {
~
}

// 配列要素でのループ
for num in arr {
~
}

// 文字列を文字に分解してループ
for char in Array(str) {
~
}
```

#配列操作
```swift

// 初期化
// 一次元配列
var arr1 = [Int]()
// 二次元配列
var arr2 = [[Int]]()
// データの追加
arr.append(x)
//データの挿入
arr.insert(x, at: i)
// 二次元配列で、i番目の配列に要素を追加
var table = [[String]]()
table.append([String]())
table[i].append(str)

// 配列要素の中で、最大長の文字列を取得
// 同じ文字数が複数ある場合は、先にある要素が優先される。
// 後発を優先したい場合は、比較条件を「>=」とする。
let max_str = str_arr.max(by: {$1.count > $0.count})

// 配列要素のカウント
arr.count
```

#比較
```swift

// 複数条件　or(num2がnum1かnum3より大きい)
if num1 < num2 || num2 > num3 {
~
}

// 複数条件 and(num2がnum1とnum3より大きい)
if num1 < num2 && num2 > num3 {
~
}

// 文字列比較
if char == "A" {
~
} else if char == "B" {
~
}
```

#参考文献
[Swift公式](https://developer.apple.com/jp/swift/)
[編集リクエストを頂いたシンタックスハイライト記法について参考にさせて頂いたQiita記事](https://qiita.com/Qiita/items/e84f5aad7757afce82ba)
Ruby on Rails チュートリアル 22日目

今日も[前回](https://qiita.com/flandrescarlet237/items/4d850dcfb534db625729)と同じところで躓いていました。「railsチュートリアル13章　エラー」と検索して、エラーを修正した人たちのサイトを参考にして修正できないかと思い色んなサイトを確認しましたが、自分の詰まっているところの後のものばかりで、参考にすることができませんでした。[13.3.4 マイクロポストを削除する](https://railstutorial.jp/chapters/user_microposts?version=5.1#sec-destroying_microposts)が終わったところまで戻してからもう一度[13.3.5 フィード画面のマイクロポストをテストする](https://railstutorial.jp/chapters/user_microposts?version=5.1#sec-micropost_tests)の内容を書き直しましたが、同じエラー文を吐き出すだけでした
エラー文

```
rails t
Running via Spring preloader in process 12181
Run options: --seed 21615

# Running:

.............................E....................

Finished in 1.756059s, 28.4728 runs/s, 150.3366 assertions/s.

  1) Error:
MicropostsInterfaceTest#test_micropost_interface:
ActionView::Template::Error: Missing partial shared/_micropost_form with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :coffee, :jbuilder]}. Searched in:
  * "/home/ec2-user/environment/sample_app/app/views"

    app/views/static_pages/home.html.erb:8:in `_app_views_static_pages_home_html_erb___3245317371641121828_58712980'
    test/integration/microposts_interface_test.rb:11:in `block in <class:MicropostsInterfaceTest>'

50 runs, 264 assertions, 0 failures, 1 errors, 0 skips

```

```test/integration/microposts_interface_test.rb

require 'test_helper'

class MicropostsInterfaceTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:michael)
  end

  test "micropost interface" do
    log_in_as(@user)
    get root_path
    assert_select 'div.pagination'
    # 無効な送信
    assert_no_difference 'Micropost.count' do
      post microposts_path, params: { micropost: { content: "" } }
    end
    assert_select 'div#error_explanation'
    # 有効な送信
    content = "This micropost really ties the room together"
    assert_difference 'Micropost.count', 1 do
      post microposts_path, params: { micropost: { content: content } }
    end
    assert_redirected_to root_url
    follow_redirect!
    assert_match content, response.body
    # 投稿を削除する
    assert_select 'a', text: 'delete'
    first_micropost = @user.microposts.paginate(page: 1).first
    assert_difference 'Micropost.count', -1 do
      delete micropost_path(first_micropost)
    end
    # 違うユーザーのプロフィールにアクセス (削除リンクがないことを確認)
    get user_path(users(:archer))
    assert_select 'a', text: 'delete', count: 0
  end
end

```

test/integration/microposts_interface_test.rbに上の内容を書く前までは、エラーが発生することがなかったので原因はtest/integration/microposts_interface_test.rbにあるのではないかと推測しましたが、これはチュートリアルに書いてあるものと全く同じものなので、修正箇所がどこにあるのかもわかりません。
このままわからないようならば、明日もう一度ブランチごと書いてみようと思います。


###追記
原因は、マイクロソフト投稿フォームのパーシャルのファイル名の_micropost_form.html.erbが、_micropost_from.html.erbになっていることでした。
Hugo if文とfor(renge)文の書き方まとめ

静的サイトジェネレータHugoのif文とfor文の書き方をまとめてみました。

## if文

```
// トップページなら変数を表示
{{ $var := "Hugo Page" }}
{{ if .IsHome }}
    {{ $var = "Hugo Home" }}
{{ end }}

// パラメータにtitleがある場合はタイトルを表示
{{ if isset .Params "title" }}
  <h4>{{ index .Params "title" }}</h4>
{{ else }}
  <p>タイトルがない</p>
{{ end }}
```

### else if

```
{{ if (isset .Params "description") }}
    {{ index .Params "description" }}
{{ else if (isset .Params "summary") }}
    {{ index .Params "summary" }}
{{ else }}
    {{ .Summary }}
{{ end }}
```

### equal, not equal

- eq: arg1 == arg2
- ne: arg1 != arg2

```
// categoryがblogなら
{{ if eq .Params.category "blog" }}current{{ end }}

// categoryがblogでないなら
{{ if ne .Params.category "blog" }}current{{ end }}
```

### greater, lower

- ge: arg1 >= arg2
- gt: arg1 > arg2
- le: arg1 <= arg2
- lt: arg1 < arg2

```
// 10が5以上なら
{{ if ge 10 5 }}true{{ end }}

// 10が5より大きいなら
{{ if gt 10 5 }}true{{ end }}

// 5が10以下なら
{{ if le 5 10 }}true{{ end }}

// 5が10より小さいなら
{{ if lt 5 10 }}true{{ end }}
```

### and or

カッコで括って複数並べるだけ

```
// and
{{ if (and (or (isset .Params "title") (isset .Params "caption")) (isset .Params "attr")) }}

// or
{{ if or (isset .Params "alt") (isset .Params "caption") }} Caption {{ end }}

// パイプを使って複数重ねることも可能
{{ if isset .Params "caption" | or isset .Params "title" | or isset .Params "attr" }}
Stuff Here
{{ end }}
```

## for(renge)文

繰り返し構文はforではなく、rengeを使います。

データの取得は、一覧ページでは`.Pages`、他のページから一覧を呼び出す場合は`.Site.Pages`。

```html
{{ range .Pages }}
  <li>
    <a href="{{.Permalink}}">{{.Date.Format "2006-01-02"}} | {{.Title}}</a>
  </li>
{{ end }}

// listページ以外から呼び出す場合は、.Siteつける
{{ range .Site.Pages }}
  <li>
    <a href="{{.Permalink}}">{{.Date.Format "2006-01-02"}} | {{.Title}}</a>
  </li>
{{ end }}

// ループ中にグローバルコンテキストにアクセスするために$を使う
<ul>
{{ range .Params.tags }}
  <li>
    <a href="/tags/{{ . | urlize }}">{{ . }}</a>
    - {{ $.Site.Title }}
  </li>
{{ end }}
</ul>
```

### order

表示順序を指定

- ByDate: 作成日
- ByPublishDate: 公開日
- ByLastmod: 更新日
- ByLength: コンテンツのながさ
- ByTitle: 見出し
- ByParam: 特定のパラメータ

```html
{{ range .Site.Pages.ByDate }}
  <li>
    <a href="{{.Permalink}}">{{.Date.Format "2006-01-02"}} | {{.Title}}</a>
  </li>
{{ end }}

// ByParamの場合
{{ range (.Pages.ByParam "rating") }}
  <!-- ... -->
{{ end }}
```

### reverse order

新しい方から順に表示

```html
{{ range .Site.Pages.ByDate.Reverse }}
  <li>
    <h1><a href="{{ .Permalink }}">{{ .Title }}</a></h1>
    <time>{{ .Date.Format "Mon, Jan 2, 2006" }}</time>
  </li>
{{ end }}
```

### limit
取得するデータ量を制限したい場合

- first: 最初のx件
- last: 最後のx件

```
// 最初の10件だけを取得
{{ range first 10 .Site.Pages.ByDate }}
  <li>
    <a href="{{.Permalink}}">{{.Date.Format "2006-01-02"}} | {{.Title}}</a>
  </li>
{{ end }}
```

### where

条件に指定したデータだけ取得する

```
// categoryがblogの記事だけを表示
{{ range where .Site.Pages.ByLastmod "Params.category" "blog" }}

{{ end }}

// limitと組み合わせ
{{ range first 10 ( where .Site.Pages.ByLastmod "Params.category" "blog") }}

{{ end }}
```

### no data

記事（データ）がない場合の表示はelseを使う

```
{{ range $array }}
    {{ . }}
{{else}}
    <!-- This is only evaluated if $array is empty -->
{{ end }}
```
遺伝的アルゴリズムの循環交叉について

#はじめに
この記事は自分の備忘録として書いているため、わかりにくい場合はご了承ください。
#記事の内容
遺伝的アルゴリズムの交叉の中でも特に循環交叉について取り上げます。
#循環交叉の仕組み
循環交叉は順列コーディングに使われる交叉の方式です。
ステップ別に分けて解説していきます。
##STEP0
まず、親1と親2の遺伝子の配列を以下のように与えます。

親1　=[ 3 , 7 , 2 , 5 , 6 , 1 , 4 ]
親2　=[ 1 , 7 , 3 , 6 , 2 , 4 , 5 ]
##STEP1
ランダムに1組の数字を決めます。今回は、

親1　=[ 3 , 7 , **2** , 5 , 6 , 1 , 4 ]
親2　=[ 1 , 7 , **3** , 6 , 2 , 4 , 5 ]

にしました。
この位置を変えずに子に遺伝子をコピーします。

子1　=[ # , # , 2 , # , # , # , # ]
子2　=[ # , # , 3 , # , # , # , # ]

「#」はまだ数字が埋まっていない場所です。
##STEP2
次に先ほどランダムに決めた数字のペアの対になっている数字を子に移します。
子1から見た場合は3で、子2から見た場合は2ですね。
この数字を親と同じ位置にコピーします。
親の数字の位置は以下のようになっています。

親1　=[ **3** , 7 , 2 , 5 , 6 , 1 , 4 ]
親2　=[ 1 , 7 , 3 , 6 , **2** , 4 , 5 ]

この位置を変えずに移すと

子1　=[ **3**, # , 2 , # , # , # , # ]
子2　=[ # , # , 3 , # , **2** , # , # ]

のようにコピーされます。
##STEP3
最後のステップです。
まず考えやすくするために以下のように子のリストを書き換えます。

子1　=[ 3 , #(7) , 2 , #(5) , #(6) , #(1) , #(4) ]
子2　=[ #(1) , #(7) , 3 , #(6) , 2 , #(4) , #(5) ]

（）内の数字は同じ位置に親が持つ数字です。
そしてこの数字を左から順番に子の間で入れ替えていきます。
まず左端だけ入れ替えると、

子1　=[3,**#(1)**,2,#(5),#(6),#(1),#(4)]
子2　=[**#(7)**,#(7),3,#(6),2,#(4),#(5)]

のようになります。
これを右端まで繰り返して「#」を取ると

子1　=[ 3 , 1 , 2 , 7 , 6 , 4 , 5 ]
子2　=[ 7 , 5 , 3 , 6 , 2 , 1 , 4 ]

のように遺伝子がコピーされました。
入れ替えた数字のペアは「子1の数字⇔子2の数字」として「5⇔7」「6⇔6」「1⇔4」「4⇔5」です。
これで完成です。
【2020からPythonを学んでも遅くない】第２回 Pythonの開発環境を作ろう

``` python
your_os = 'Linux'
print(f'Install Python on your {your_os}')
```

## MacOS編
1. [Pythonの開発環境を用意しよう！（Mac）](https://prog-8.com/docs/python-env)
2. [Pythonのインストール方法(Mac編)](https://www.dsp.co.jp/tocreator/engineer/tips-engineer/python_installation/#contents1)
3. [Anacondaを使ったPythonのインストール方法（Mac編）](https://www.dsp.co.jp/tocreator/engineer/tips-engineer/python_installation/#contents5)

![macbook.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605091/d97575b3-ee30-8a9c-feac-a8a4cfb8c1ae.png)

## Windows編
1. [Pythonの開発環境を用意しよう！（Windows）](https://prog-8.com/docs/python-env-win)
2. [Pythonのインストール方法(Windows編)](https://www.dsp.co.jp/tocreator/engineer/tips-engineer/python_installation/#contents3)
3. [Anacondaを使ったPythonのインストール方法（Windows編）](https://www.dsp.co.jp/tocreator/engineer/tips-engineer/python_installation/#contents7)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605091/e616e4b0-7536-c71b-d2cf-a1f2373b0f04.png)

## Linux編
1. [Pythonのインストール方法(Linux編)](https://www.dsp.co.jp/tocreator/engineer/tips-engineer/python_installation/#contents4)
2. [Anacondaを使ったPythonのインストール方法（Linux編）](https://www.dsp.co.jp/tocreator/engineer/tips-engineer/python_installation/#contents8)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605091/71a450bc-c919-2984-587a-530cdfb92e30.png)

## IDEのインストール
>IDE（Integrated Development Environment／統合開発環境）とは、ソースコードを記述するエディタ、ソースコードからプログラムを生成するコンパイラ、コードの不正を発見・修正するデバッガなどのプログラム開発ツールをひとまとめにした開発環境のことをいう。

>https://www.itreview.jp/categories/ide

Pythonコードを書くために、ソースを編集するテキストエディターが必要です。人気のテキストエティターは例えば下記があります。優秀なエティターを使えば生産性も大幅に向上でき、プログラマーにとってとても重要です。 

- Visual Studio Code
- Sublime Text
- Atom
- サクラエディタ
- Notepad++
- NoEditor
- 秀丸エディタ
- Eclipse
- Visual Studio
- Emacs

こちらで**Visual Studio Code**(VS Codeと呼ぶ)をおすすめます。VS CodeはMicrosoft製のオープンソースのテキストエティターです。Windows/Mac/Linuxをすべてサポートされています。ElectronやTypeScriptを用いて開発されており、モダンなエディターです。コードエティターの必須機能が揃っていて、拡張機能により機能を拡充でき、軽量で高速動作できます。 

- VS codeでpython3の実行(windows10)
- VSCodeでPython開発構築の備忘録
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605091/4f94b944-f58f-5668-1b48-12172e8429a3.png)
SwiftUIで画像とテキストを表示する（チュートリアル）

##画像とテキストを表示する
[SwiftUIで写真を表示する](https://qiita.com/Hyperbolic_____/items/aaa9171e81c1683b35a8)と[テキストを表示するHStack,VStack](https://qiita.com/Hyperbolic_____/items/0bc189350a20f9685020)の組み合わせです。
![2020-05-04 10.53のイメージ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549093/67e05cda-5f5a-22b1-813b-13bfae57139b.jpeg)

画像は画像だけを表示するswiftファイルとテキストだけを表示するswiftファイルを作成し、それらをインスタンス化
して表示させています。

```swift
import SwiftUI

struct Introduce: View {
    var body: some View {
        VStack {
            CircleImage()
            ContentView()
        }
    }
}

struct Introduce_Previews: PreviewProvider {
    static var previews: some View {
        Introduce()
    }
}
```

`CircleImage`は


```swift
import SwiftUI

struct CircleImage: View {
    var body: some View {
        Image("8D23A0D7-AB6F-477F-BDED-00D4C8804B99_1_105_c")
        .resizable()
        .clipShape(Circle())
        .overlay(
            Circle().stroke(Color.gray, lineWidth: 4))
            .aspectRatio(contentMode: .fit)
        .shadow(radius: 10)
    }
}

struct CircleImage_Previews: PreviewProvider {
    static var previews: some View {
        CircleImage()
    }
}
```


`ContentView`は

```swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        
        VStack {
            
            VStack(alignment: .leading) {
                
                HStack {
                    Text("タンポポ")
                        .foregroundColor(Color.blue)
                        .font(.title)
                    Text("春の植物")
                }
                
                Text("離弁花類")
            }
        }
    }
    
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

のように別のファイルを用意しています。
WRF1WMV コンバーター

うちの大学のオンライン講義には現在、MicrosoftのTeamsとCiscoのWebexが大学から提供されていて、自分は今のところ多人数にはWebexを使ってます。

録画もできるので、1回目の講義を録画してみたところオンライン録画が領域が厳しいというメッセージが出ていたのでローカルで保存してみました。と、WRFという独自フォーマットになっていて、そのままだと編集や他の場所へのアップロードが困難だったので、変換方法を探しました。

しかし、Ciscoのページで検索してもなかなかたどり着けなくて、いったん英語ページを介してようやく掘り出したので、どこかにメモしてできればシェアしておこうと思ったのですが、どこに置いたらいいのか迷ったので、この辺に転がしておきます。

WRF2WMVコンバーター
https://help.webex.com/ja-jp/WBX000026388/WRF2WMV-Converter

WRFファイルから起動するとコマンドラインで使えと言われますが、一応GUIが起動してきます。そこで入力ファイルを選んで「Start Converting」を押すと変換が始まります。

とりあえず以上です。
PyTorchでマインスイーパを機械学習

#はじめに
練習がてら、PyTorchを使って自作のマインスイーパを機械学習させてみます。
わからないことだらけなので、いろいろと勉強しながらメモ。
一旦メモ書き程度に作成して、必要ならあとから体裁整える。

#目標
Windowsの標準(だった)マインスイーパの初級を、安定してクリアできるようになること。
とりあえずは9割ぐらいの勝率を目指す。

#構成
[こちら](https://book.mynavi.jp/ec/products/detail/id=90706)のDQNを写経しました。
では済まないので、簡単に。

ネットワークはシーケンシャルモデルを使用。
入力層(状態$s$)と出力層($Q_{s,a}$)のニューロン数は盤の目の数。
隠れ層は2層で、各層のニューロン数は盤の目の数×`SIZE_MAG`。
とりあえず盤の目の数に対してスケーリングすればいいかなと(適当)

活性化関数はReLU、最適化手法はAdam(学習率0.001)を使用。

マインスイーパは自作。画像取り込んで頑張ろうかと思ったけど、本題はそこじゃないので。
アルゴリズムは省略。

報酬は次の通り。

|  変数  |  条件  |
| ---- | ---- |
|`reward_win`  |  ゲームクリア  |
|`reward_failed`  |  ゲーム失敗  |
|`reward_miss`|すでに開いているマスを開こうとする|

#経過
###1回目
まず学習できるのかを確認するため、盤のサイズを6×6、地雷の数を5個にしてみる

```python:param
GAMMA = 0.99
NUM_EPISODES = 1000
CAPACITY = 10000
BATCH_SIZE = 200
SIZE_MAG = 8

reward_failed = -100
reward_win = 100
reward_miss = -1
```
極まれに勝てるけど、偶然勝利している感じ。
誤差を見てみると2000stepくらいで4桁ぐらいに吹き飛んでた。えぇ…
単純な報酬和を見ても既に開いてるマスばかり開こうとするし、報酬の問題？

###2回目
というわけで報酬を修正。

```python:reward
reward_failed = -100
reward_win = 100
reward_miss = -10
reward_open = 1
```

`reward_open`はマスを新しく開けたら与える報酬。
誤差はさっきより落ち着いたけど、10くらいでずっと振動。

###n回目
いろいろといじったけど、振動も発散も止まらず。
挙動を見てみても、すでに開いてるマスを相変わらず開こうとする。
Fixed target Q-Networkを導入するか…

###1晩経って…
以下の可能性を考えた。

* ε-greedy法のランダム動作の時、既に開いているマスばかり開こうとしている
* すでに開いているマスをQネットワークが選択した場合、次の状態$s_{t+1}$と前の状態$s_t$が同じになるから、ループする
* ミニバッチを作る際に、すでに開いているマスを開く行動が多く選択されている

試しに既に開いているマスを選択した場合もゲームオーバーにしたら、誤差自体は1未満で推移するようになった。
εの値を小さくする(初期値0.5→0.2)とさらに誤差は小さくなった。(0.1～0.01くらい)
ただ、ミニバッチを作る際の問題は解消されていないため、Prioritized Experience Replayを実装する。
コードはそのまましゃｋ(

ってやってみても、うまくいかない。
そりゃ、コーディングミスってたらねぇ(
target Q-Networkだって初期値のままだったし…

で、修正した結果、うまくいかなかった。
報酬が大きすぎるのか？

```python:reward
reward_failed = -1
reward_win = 1
reward_miss = -1
reward_open = 1
```

やっぱり駄目だったよベイベ…

###ふと思う
これまでは毎回違う盤面に対して学習してきたけど、
ある1つの盤面に対しては学習できるのか…？
→できた。勝率9割になるまでに200～300エピソードくらい。
1回クリアできるとすぐに勝率9割になるのがかわいい。

じゃあ150エピソードに1回変えたら？
→全く勝てない。過去の学習データに引きずられていると思われる。

じゃあいっそのこと毎回盤面変えるように戻そう！
Ruby + OSXでいい感じにGMailである程度テンプレを書く

友人の @SHOMI に教えてもらった。URLの仕様、調べたら記事もあった。以下を参照。とても助かりました。

[なんども（定期的に）同じメールを送る場合、できるだけラクする方法（Gmail） - Qiita](https://qiita.com/hann-solo/items/1bd729fa7ee058ad3adb)

なるほどなるほど。そういうわけでRuby + MacOSでいい感じにGMailを起動するにはこちら。これやったあと、署名を足したりファイルを添付したりして送信すればよい。

マジで完全に自動化できる場合には送っちゃえばいいんだろうけど、そこまでできない系はぜひコレで。（例えば自分のPCからSMTPつながらないとかね）

URLをopenするコマンドをポーティングすればどんなOSでも対応できるはず。ただ、パラメータにアンパサンドがはいるのは確定なので、そこをうまく渡せるようにするのがミソ

``` .rb
uid = 0
params = URI.encode_www_form(
  [
    ['view', 'cm'],
    ['su',   subject],
    ['to',   to],
    ['body', doc],
    ['cc',   cc], 
    ['bcc',  bcc]
  ]
)

`open 'https://mail.google.com/mail/u/#{uid}/?#{params}'`
```

https://gist.github.com/PharaohKJ/a2c07a3f44c35c4fde1ebea868bfc250
arg1 = 1;で無理なら%orig(arg1 = 1);にしろ！

#arg1 = 1;で無理なら%orig(arg1 = 1);にしろ！
初めまして、Minazukiと申します。

Profile
Name :  Minazuki_dev
Twitter : @Minazuki_dev
Repo : https://minazukidev.github.io
Age : 14

まだ中学生なので言葉がおかしいところがありますが許してください…🙏

#arg1 = 1;だと動かなくなった。
iOS13でスイッチャーを2段にしようとした時に起きた不具合を書いていきます。

iOS13でもスイッチャーを2段にしようとしたのですが以前まではarg1 = 2;で動いてたのにiOS13からは何故か動かなくなりました。

※後から気づいたけどiOS12で動いてたのはif構文でreturn %orig;があったからかも

スイッチャーの大きさも変えないといけないのかと考えましたがFlex側ではそんなことしなくても2段になったので悩んでいました。

そんな中%orig([UIColor whiteColor]);というコードをふと思い出しました。ダメ元でarg1 = 2;と書き込んだらできてしまったわけです。

#最後に
ほぼ自分の為のメモのような記事なので何言ってるのかわからないと思います。スイッチャーを2段とは違いますがGitHubのコードを貼っておきます。

https://github.com/MinazukiDev/BigBatteryX

ではさようなら👋
SwiftUIの基本となるHStack,VStack

##テキストを表示
SwiftUIを使ってテキストを表示します。

![2020-05-04 10.37のイメージ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549093/0cc5fadc-d6c9-b0de-d961-4c8044c9adae.jpeg)

```swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading) {
            HStack {
                Text("タンポポ")
                    .foregroundColor(Color.blue)
                    .font(.title)
                Text("春の植物")
            }
            Text("離弁花類")
        }
        .offset(x: 0, y: 50)
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

`struct ContentView: View{
}`を宣言し、その中にオブジェクトを加えていきます。

鉛直方向にオブジェクトを加えるためには`VStack{}`, 水平方向にオブジェクトを加えるためには`HStack`を用います。
タンポポと春の植物は`HStack{}`のなかにあるので水平方向に位置しています。

`タンポポ　春の植物`と`離弁花類`は`VStack{}`のなかにあるので鉛直方向に位置しています。

さらにその`VStack{}`で囲まれたオブジェクトに`.offset()`によって位置を指定しています。
【GAS】Nature Remo Cloud APIを実行して「温度」と「湿度」を取得する

こんにちわ、さかぐち（[@sakaguchi_mamii](https://twitter.com/sakaguchi_mamii)）です。

我が家はスマートリモコン「[Nature Remo](https://nature.global/)」を置いてます。
<img width="1437" alt="スクリーンショット 2020-05-04 10.36.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136275/fb3f0af7-5839-5e71-5d8e-a02a6ea1470a.png">

今回は「Nature Remo」の「[Nature Remo Cloud API](https://developer.nature.global/)」を利用して、部屋の「温度」と「湿度」を取得しようと思います。

#アクセストークン取得

OAuth2を通して取得したアクセストークンが必要です。
下記ページからログインしアクセストークンを取得してください。

https://home.nature.global/

#スクリプト
```javascript
function myFunction() {

  var access_token = 'アクセストークン';

  var url = 'https://api.nature.global/1/devices';
  var params = {
    headers : {
      "Content-Type" : "application/json;",
      'Authorization': 'Bearer ' + access_token
    },
    method : 'get'
  };
  var data = JSON.parse(UrlFetchApp.fetch(url,params));
  console.log(data[0]["newest_events"]["te"]["val"]); // 温度
  console.log(data[0]["newest_events"]["hu"]["val"]); // 湿度

}
```

#テスト

温度と湿度が取れました。
<img width="691" alt="スクリーンショット 2020-05-04 10.27.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136275/9e56a37c-07cb-ab86-6f72-53e0c968d165.png">

#最後に

我が家にハムスターがいるので、温度と湿度の管理はマストです。
現在は「Nature Remo」のアプリで部屋の温度によって冷房を自動でつける設定をしています。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">我が家のハムスターたちが平和に暮らせるように、部屋の温度が26度以上になったら自動で冷房オン！<a href="https://twitter.com/hashtag/NatureRemo?src=hash&amp;ref_src=twsrc%5Etfw">#NatureRemo</a> <a href="https://t.co/1atVGMCM83">pic.twitter.com/1atVGMCM83</a></p>&mdash; さかぐち まみ🌷カスタマーサクセス見習い (@sakaguchi_mamii) <a href="https://twitter.com/sakaguchi_mamii/status/1256909261717700609?ref_src=twsrc%5Etfw">May 3, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

APIを使ってなにかできないか模索中です！
AWSサーバーレスのWebアプリケーションをもっと勉強する

いつも忘れないように、コンセプトから。

###コンセプト
・お金かけてまでやりたくないのでほぼ無料でAWSを勉強する
　→ちょっとしたサービスを起動すると結構高額になりやすい。
・高いレベルのセキュリティ確保を目指す
　→アカウントを不正に使われるととんでもない額を請求されるので防ぐ

昨日は[「AWS Lambdaで動的コンテンツ配信を考えてみる」](https://qiita.com/auf/items/061817b6ae05d9ccdfbc)をやってみたものの、実装はできるが画面（HTML）の実装がイマイチだなという違和感を感じていました。そこれ、[「さらに先日やってみたチュートリアル」](https://qiita.com/auf/items/74578049ef2f668f9d60)をもう一度見てみようと思ったんですが、これがなかなかハードです。ワークショップ関連のプログラムがセットになっていて、関連する部分が
`\aws-serverless-workshops-master\WebApplication\1_StaticWebHosting\website\js`
あたりになるんですが、ソースコードの量も多く難解です。もっとも私にJavascriptの経験がないのもあるので、ハードルがかなり上がっている、という事実もあります。

###クライアントサイドってどうすればいいの？
昨日の[「AWS Lambdaで動的コンテンツ配信を考えてみる」](https://qiita.com/auf/items/061817b6ae05d9ccdfbc)はAPIGatewayでHTML変換を行っていました。これだと複雑な画面は作れないしなと思っているのでもう一度勉強します。AWSのサイトで、
https://aws.amazon.com/jp/serverless/patterns/serverless-pattern/
でサーバーレスのデザインパターンが載っているので見てみます。この中で、サーバーレスのWebアプリケーションは左上で、チュートリアルでやったものです。まあ、雰囲気はわかったんだけど、そうじゃないんだよね、ってことでGoogle先生に質問してみます。

結構調べてみたんですが、やってみた系の記事はAPIGatewayに頼っている実装が多いんですよね。で、さらにキーワードを変えてみるとわかりやすいページが見つかりました。いつもお世話になっているクラメソさんのページです。
[「Angular でサーバーレスSPA： DynamoDB を使って「さらに読みこむ」パターンを実装しよう」](https://dev.classmethod.jp/articles/angular_serverlesss_dynamodb_read_more/)という記事ですが、構成図がしっかり記載されていてわかりやすいです。Javascript系がサッパリですが、クライアントでAngularを使ってました。知らないとはいえ、他にReactやVue.jsなどがあることくらいのキーワードレベルは知っているので、「そうか、クライアント側のフレームワークを使えばいい訳ね」と納得です。確かに、これらをS3上に配置して、そこからAPIGateway経由でサーバーサイドにアクセスさせれば、サーバーレスで大規模にアプリを組むことも可能な感じがします。

![image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480659/dac9ba59-f991-65c7-334a-95a9e1fd4ad4.png)
※クラメソさんの構成図に勝手に追記


###サーバーサイドってどうすればいいの？
クライアントサイドはなんとなくイメージできてきたので、サーバーサイドをこのままでいいのか、ってどころです。AWSブログを見ていると気になるものを見つけました。Chaliceを使うと、APIGatewayとLambdaをうまく管理できそうです。
https://aws.amazon.com/jp/blogs/startup/event-report-chalice-handson/
なるほどねー。みんな考えるようなことは似ているな、と思うわけです。もう15年くらいプログラミングしてないですが、すぐにこういう壁に当たって欲しくなるのは容易に想像できますね。

ちなみにChaliceの説明だと
https://qiita.com/tukkyr/items/b1f54b467375ba29facf
こちらの方の記事のほうがわかりやすかったです。

ここで少し疑問が沸きます。純粋に実装を楽しむエンジニアならChaliceに飛びつけばいいと思いますが、エンプラ経験の長い私はどうしてもベンダーロックインに対してはネガティブです。もうちょっとでいいから汎用性のあるものはないだろうか。ちょっと調べてみましたが、まだまだこれ！ってのが無いですね。見つけた中ではこの記事がニュートラルかなと思いました。
https://www.serverless.com/blog/state-of-serverless-multi-cloud/

ベンダーロックインの話をしだすと話がどんどん逸れていってしまうので、実装することを目的にするのであればChaliceを使ってみたほうがいいかも、と思います。ちなみに、OpenwhiskやKnativeも少し調べてみましたがなんか違うなと。私が考えるサーバーレスってK8sの発展形ではなくて、クラウドのマネージドサービスをどう使いこなせるものか、ってことだと思っています。要はシステム的な抽象化レイヤーをどこに入れるか、なんですが、サーバーレスフレームワークでもいいですし、別のレイヤーでもいいわけです。その話は今日は割愛します。

###現状で”お手軽”かつ”ちゃんとしたWebアプリケーション”を作るには

やり方はいろいろあるとは思いますが、クライアントサイドのフレームワーク選定と、サーバーサイドの実装、APIGatewayでの繋ぎがポイントになりそうですね。基本的にクライアントサイドはJavascript系のフレームワークになると思うので、サーバーサイドもJavascriptでいくかは悩むところです。サーバーサイドの処理や、AIとの相性を考えるとどうしてもPythonに目が行ってしまいます。個人的にJavascriptの経験もないのでPythonのほうがまだいいというか（といっても既に書いたように本格的なプログラミングは10年以上していないのでPythonを語ることはできませんが）。でもそうすると、言語の二刀流使いが必要になります。

もう少し悩んでみようと思いますが、今日の結論としては
・クライアントサイド：Angular、React、Vue.jsから選択
・サーバーサイド：Chaliceを使ってPythonベースでAPIGatewayも含めて管理
というのがベターかなと思っています。もうちょっと調べてサンプルアプリでも作れるといいんですけどね。。。
Webpack を使って Quasar 入門

# はじめに

今回は **`Quasar CLI`** や **`Vue CLI`** を利用せずに **Quasar** を利用する方法について紹介します.

なお、**Vue.js** の導入までは **[前回の記事](https://qiita.com/Midoliy/items/a336a8f5d35cfe21ec00)** とまったく同じなため、そちらを参照してください.


# Quasar の導入

まずは **npm** を利用して **quasar** をインストールします.

```powershell:powershell
npm i -D quasar
```

次に **[Quasar UMD / Standalone](https://quasar.dev/start/umd)** の **Installation** の所で利用したいものを選択し, 出力されている **html** の **link** タグをコピーして, **index.html** にペーストします.

なお, 今回はデフォルト設定のまま利用しています.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125601/6d5a5bfd-fab0-dfec-e2de-d74a353164f2.png)


```html:index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <!-- ↓↓↓ ここから追加 ↓↓↓ -->
        <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet" type="text/css">
        <link href="https://cdn.jsdelivr.net/npm/quasar/dist/quasar.min.css" rel="stylesheet" type="text/css">
        <!-- ↑↑↑ ここまで追加 ↑↑↑ -->
        <title>Vue app</title>
    </head>
    <body>
        <div id="app"></div>
        <script src="bundle.js"></script>
    </body>
</html>
```

最後に **index.js** で **Quasar** を読み込めば利用する準備は完了です.

```js:index.js
import Vue from 'vue'
import App from './components/App'
// ↓↓↓ ここから追加 ↓↓↓
import Quasar from 'quasar/dist/quasar.umd'
//↑↑↑ ここまで追加 ↑↑↑

new Vue({
  el: '#app',
  components: { App },
  template: '<app/>'
})
```

実際に **App.vue** で **Quasar** の **Vue Components** を利用してみます.

```vue:App.vue
<template lang="html">
  <div>
    <p>Hello, world!!</p>
    <!-- ↓↓↓ ここから追加 ↓↓↓ -->
    <q-btn color="primary" label="Push!!"></q-btn>
    <!-- ↑↑↑ ここまで追加 ↑↑↑ -->
  </div>
</template>
```

**npm start** を実行すると, 以下のようにボタンが表示されると思います.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125601/b66fd1db-196e-5879-d16e-c39157c5b6d1.png)


# おわりに

このように **Quasar CLI** や **Vue CLI** を利用せずとも簡単に **Quasar** を導入できます.
対象ファイルがいつ・どのcommitで削除されたかを調べる

# これ何
対象ファイルを削除したcommitを見つける git コマンドをまとめる。

# コマンド
次のコマンドで検索することができる。

```shell
git log -- target_file_path
```

`git log` と `--` の間にoptionも追加することができる。
自分は次のようなコマンドをよく使う

- コミットメッセージを1行で表示したい場合は `--oneline`
- 最後の1commitだけみたい場合は `-n 1`


# 参考文献
- [Gitでファイルがいつ削除されたかを特定する](https://blog.piyo.tech/posts/2018-06-22-git-log-for-file/)
【Python】リストをfor文でループする時にindexのみ利用したい

Pythonでリストをfor文でループする時は，一般的にリストの各要素を利用したい時だと思います。しかし，リストの**indexのみ**利用したい時もあります。いや，一般的にはあまりないでしょうが，少なくとも私にはあります。下記で最も良いと思う方法を残しておきます。

## 私が思う最も良い方法

```py:私が思う最も良い方法
lst = ["A", "B", "C"]
for i, _ in enumerate(lst):
    print(i)
```

## その他の良いと思わない方法

### for index, item in enumerate(lst)
```py:ボツ１
lst = ["A", "B", "C"]
for i, item in enumerate(lst):
    print(i)
```
この例でも目的は達成できるのですが，ループ中で使用しない余計なitemを使ってしまっています。ちょっと負担です。あと`Unused variabe 'item'`なんていう注意が表示されてしまいます（VS Code使用時）。うっとうしいです。ボツです。

### for index in range(len(lst))
```py:ボツ２
lst = ["A", "B", "C"]
for i in range(len(lst)):
    print(i)
```
これでも良いですが，これだと実質lstをループしていません。なんだか寂しいのでボツです。
SwiftUIで画像を表示する(画像の切り抜きと影)

##画像を追加する

swiftUIを使って画像のようなものを作成する。
![2020-05-04 9.56のイメージ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549093/b5f72be8-6ef7-b47d-617e-204dd274d3c7.jpeg)


```swift
import SwiftUI

struct CircleImage: View {
    var body: some View {
        Image("8D23A0D7-AB6F-477F-BDED-00D4C8804B99_1_105_c")
        .resizable()
        .clipShape(Circle())
        .overlay(
            Circle().stroke(Color.gray, lineWidth: 4))
            .aspectRatio(contentMode: .fit)
        .shadow(radius: 10)
    }
}

struct CircleImage_Previews: PreviewProvider {
    static var previews: some View {
        CircleImage()
    }
}

```

お決まりの
`var body: some View {
}`
のなかにimage("")とすることで簡単に画像を追加することができる。
切り抜くのは`clipShape()`で、今回はCircle()としている。
`shadow(radius: )`で影をつけている。
resizable()で画面サイズに合わせて収縮させている。
GitHubのピン留めリポジトリに説明文を追加する

# GitHubのピン留めしたリポジトリに説明文を追加する
GitHubでピン留めしたリポジトリに説明文が表示されるようにしたいなと思ったのですが、
やり方を調べてもすぐには見つけられず、少し迷ったので備忘録として記事に残します。


# 手順
## 1. まず、GitHubの自分のページに行きます
[![Screenshot from Gyazo](https://gyazo.com/c5237459b679fb61b0f3974435b96e6f/raw)](https://gyazo.com/c5237459b679fb61b0f3974435b96e6f)

- 画面右側にピン留めしたリポジトリが表示されています

## 2.今回は右上のtest-appに説明文を追加するので、test-appのリポジトリを選択します
[![Screenshot from Gyazo](https://gyazo.com/acfbed02ebcee75bbccb1b30b0345eff/raw)](https://gyazo.com/acfbed02ebcee75bbccb1b30b0345eff)

## 3."No description, website, or topics provided."と書いてあるあたりをクリックするか右側の"Edit"ボタンをクリックすると入力蘭が表示されます
[![Screenshot from Gyazo](https://gyazo.com/92ab1b4546f3c8c19f38468447618a6e/raw)](https://gyazo.com/92ab1b4546f3c8c19f38468447618a6e)

- ちなみに"No description, website, or topics provided."を某先生に翻訳していただくと「説明、ウェブサイト、トピックは提供されていません。」
- いままでGitHubを使っていて全然気づかなかった。。。

## 4.Descriptionの入力蘭に説明を記述→"Save"ボタンをクリック
[![Screenshot from Gyazo](https://gyazo.com/9d09806206c076e3bb9a66ef807d8a23/raw)](https://gyazo.com/9d09806206c076e3bb9a66ef807d8a23)

- 右側の入力蘭でWebsiteのリンク登録もできますね

## 5.戻って確認
[![Screenshot from Gyazo](https://gyazo.com/0624231bb216fa89d35217a4f2a33632/raw)](https://gyazo.com/0624231bb216fa89d35217a4f2a33632)

- 追加されました


## おまけ　topicsの設定

- ここの"Manage topics"の文字をクリックすると
[![Screenshot from Gyazo](https://gyazo.com/e7b89e64e3480824fd400c41ec599dc2/raw)](https://gyazo.com/e7b89e64e3480824fd400c41ec599dc2)

- "Add topics to categorize your repository and make it more discoverable."
「トピックを追加してリポジトリを分類し、見つけやすくしましょう」と表示されるので
[![Screenshot from Gyazo](https://gyazo.com/27d96cd818115459dadc0ad9b8d44eb2/raw)](https://gyazo.com/27d96cd818115459dadc0ad9b8d44eb2)

- トピックを追加できます
<img width="1000" alt="スクリーンショット 2020-05-04 9.51.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539054/614f66d7-fc10-dc97-7a15-426564160735.png">
[![Screenshot from Gyazo](https://gyazo.com/34ac2a54d9cae5cd8d7244c6a012b2d7/raw)](https://gyazo.com/34ac2a54d9cae5cd8d7244c6a012b2d7)
【JavaScript】Object.keysとforEachを使ったプロパティ取得

#【JavaScript】Object.keysとforEachを使ったプロパティ取得

オブジェクトのプロパティを取得する方法をまとめました。
手順は2つ。

・Object.keysを使い、プロパティと値を取得
・forEachを使い、それぞれを表示

キャラクターの座標情報を格納する定数characterPositionがあったとして

```js

    const characterPosition  = {
        x: 100,
        y: 200,
        z: 9
    };
```
定数keysにプロパティ（x,y,z）と値を格納、出力します。

```js

    const keys = Object.keys(characterPosition);
    keys.forEach(key => {
        console.log(`Key:${key} Value: ${characterPosition[key]}`);//テンプレートリテラル
    });
```
console.logの結果は次の通りになりました。

```js

    Key:x Value: 100
    Key:y Value: 200
    Key:z Value: 9
```

## 追記:Object.entries()を使い更に短く

```js

    Object.entries(characterPosition).forEach(([key,value])=>{
        console.log(`Key:${key} Value: ${value}`);
    });

```

・Object.keysはkeysを使いまわして返す
・Object.entriesはオブジェクトのエントリ（＝中身）を組で返す
1個ずつ渡すか、グループで渡すかの違いと理解した。

参考
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/entries

謝辞：@Mihiraghi様
ありがとうございました。
GoogleAppsScript(GAS)でトリガーをスクリプトからセットする

## はじめに
初めまして。wolf25です。
普段はGASとTypeScriptで開発しています。
初めての記事投稿となりますが、参考になればなと思います。
コメントなども頂けると幸いです。

# 本記事について
GoogleAppsScript(以下GAS)をGoogleFormと合わせて用いてる場合のトリガーをスクリプトからセットする方法を載せています。

トリガーはGUIでぽちぽちやればできるのですが、自動化したかった故、書きました。
スクリプトからトリガーをセットする方法についての記事があまりにも少なかった。
スクリプトからでなくても良い場合はネットにいくらでも転がっていましたので、そちらを参考ください。

## 対象
- GASの**スクリプト**からトリガーをセットしたいと思っている方。
- トリガーをスクリプトから削除したい方
- スクリプトをどうやって書けばいいのかわからない方。

## トリガーのセット
claspで開発しているため、言語はTS(TypeScript)です。
newTrigger()でトリガーを引かれた後、起動させたい関数名を入力します。

``` NewTrigger.ts
public sample() {
  const url: string = 'GoogleFormのURL';
  const formObject: Forms = FormApp.openByUrl(url);
  try {
    ScriptApp.newTrigger('トリガー起動後動かしたい任意の関数名')
      .forForm(formObject)
      .onFormSubmit()
      .create();
    Logger.log('successly create trigger .');
  } catch (error) {
    Logger.log('Miss: cannot set trigger');
  }
}
```

## トリガーの削除
自動でトリガーを付与してくれるのはいいのですが、スクリプトを実行する度に同じトリガーが増えていき、これ以上セットできませんというエラーが出てしまったりする。
それを防ぐために常にトリガーが1つになるよう、トリガー削除のスクリプトが以下です。

```Delete.ts
public deleteTrigger() {
  for (const trigger of ScriptApp.getProjectTriggers()) {
    try {
      ScriptApp.deleteTrigger(trigger);
      Logger.log('successly delete trigger .');
    } catch (error) {
      Logger.log('Miss: cannot delete trigger .');
    }
  }
}
```
ScriptApp.getProjectTriggers()で現在のトリガー一覧を取得できます。  
for文の中で` if(trigger.getHandlerFunction() == "トリガー名") `と縛ることで任意のトリガーを削除することもできます。
"トリガー名"はトリガーセットした関数名です。

## 常にトリガーを1つに保つ
最終的にトリガー作成と削除の両方を動作を行うスクリプトです。

```Trigger.ts
export class Trigger {
  constructor() {}

  /**
   * - Set trigger to launch automatically .
   *  - set new trigger after deleting old trigger .
   */
  public setTrigger(form: Form) {
    this.deleteTrigger();

    const url: string = 'GoogleFormの編集権限付きURL';
    const formObject: Forms = FormApp.openByUrl(url);
    try {
      ScriptApp.newTrigger('トリガー起動後動かしたい任意の関数名')
        .forForm(formObject)
        .onFormSubmit()
        .create();
      Logger.log('successly create trigger .');
    } catch (error) {
      Logger.log('Miss: cannot set trigger');
    }
  }

  /**
   * - Delete trigger to launch automatically .
   */
  private deleteTrigger() {
    for (const trigger of ScriptApp.getProjectTriggers()) {
      try {
        ScriptApp.deleteTrigger(trigger);
        Logger.log('successly delete trigger .');
      } catch (error) {
        Logger.log('Miss: cannot delete trigger .');
      }
    }
  }
}
```

# 余談
「イベントのソースを選択」から「時間主導型」、「カレンダーから」、「フォーム送信後」というカラムがあったのですが、いつの間にか「フォーム送信後」が消えていて「はて。。？」という感じになりました。
つまり「フォーム送信」をトリガーにするにはスクリプトからしかできないのでしょうか。
![trigger.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483644/ee9688c3-d5f7-a15e-871d-0e4800cf03eb.png)


# まとめ
いかがでしたでしょうか。
私自身、スクリプトからトリガーをセットできることは知っていましたが、書き方がいまいちよくわからなかった(そういう記事も少ない)ので、参考になる人がいたら幸いです。
今回はフォーム送信のトリガーを主に紹介しましたが、時間主導型も同じようにできるので時間があれば更新していきたいと思います。

# 参考
- [GAS公式](https://developers.google.com/apps-script/reference/script/trigger-builder#forForm(Form))
RectTransform anchorMin anchorMaxについて

#Anchorについて
>
アンカーとはUI要素を置くための基準となる場所のことで、ボタンオブジェクトを作成したときにセットで作成される**キャンバス（Canvas）のなか**のどこかに設定されます。
**Canvasの中の基準**
**Anchors**を変更することでanchorの位置を変更することができる。

**Canvasの左下からスタート**
>
`Min X	左下のアンカーハンドルの位置に対応,0~1の範囲でX方向の位置を設定
 Min Y	左下のアンカーハンドルの位置に対応,0~1の範囲でY方向の位置を設定
 Max X	右上のアンカーハンドルの位置に対応,0~1の範囲でX方向の位置を設定
 Max Y	右下のアンカーハンドルの位置に対応,0~1の範囲でY方向の位置を設定`

[参考](https://xr-hub.com/archives/11543)
Microsoft Teams | Powershell による チームの一括作成

#はじめに

本記事では、CSVファイルを使って Microsoft Teams のチームを一括作成する方法に関して解説いたします。

#チーム登録用CSVファイルの準備

チーム登録用CSVファイルは、一行目にラベル、2行目以降にデータを入力します。

~~~
ClassName, Owner
量子力学I, nahisaho@msschool.ac.jp
量子力学Ⅱ, nahisaho@msschool.ac.jp
固体物理I,  nahisaho@msschool.ac.jp
~~~

# Powershell で Microsoft Teams を操作するためのモジュールのインストール

# Install-Module

PowerShell で Microsoft Teams を操作するには、Teams を操作するためのモジュールを事前にインストールしておく必要があります。モジュールをインストールするには

~~~ 
PS C:> Install-Module -name <モジュール名>
~~~

を実行すれば、指定したモジュールがインストールされます。Microsoft Teams を操作するためのモジュールは、`MicrosoftTeams` です。従って PowerShell を管理者権限で起動して、

~~~
PS C:> Install-Module -name MicrosoftTeams
~~~

とすれば、Microsoft Teams 用のモジュールがインストールされます。このコマンドレットでインストールされるのは、GAしている Ver.1.0.6 (2020年5月4日時点) です。Ver.1.0.6 では、チームのテンプレート(例えば for Education で利用できる、クラスやPCLなど)をオプションで指定することができません。チーム作成時に Template を指定したい場合には　Preview 版をインストールしておく必要があります。

すでに MicrosoftTeams モジュールをインストールしてしまった方は以下のコマンドで MicrosoftTeams モジュールを削除してください。

~~~
PS C:> Uninstall-Module MicrosoftTeams
~~~

##Preview版のインストール
Preview 版の MicrosoftTeams モジュールをインストールする場合には以下のコマンドを実行します。

~~~
PS C:> Register-PSRepository -Name PSTestGallery -SourceLocation https://www.poshtestgallery.com/api/v2
PS C:> Install-Module -Name MicrosoftTeams -Repository PSTestGallery -Force
~~~

インストールされたモジュールのバージョンを確認します。

~~~
PS C:> get-installedmodule

Version  Name              Repository        Description
-------  ----              ----------        -----------
1.0.22   MicrsoftTeams     PSTestZGallery    Microsoft Teams cmdlets preview module for Windows....
~~~

※Preview 版のリポジトリは以下のURLになります。
　https://www.poshtestgallery.com/packages/MicrosoftTeams


# CSV ファイルに使ったチームの一括登録用スクリプトの作成

以下のスクリプトを作成します。クラスのチームを作成する場合には、`-Template EDU_Class` と指定します。

~~~
addteamsclass.ps1

Import-Csv -Path $Args[0] | foreach {New-Team -DisplayName $_.ClassName -Owner $_.Owner -Template EDU_Class } | Export-Csv -Path $Args[1] -Encoding UTF8
~~~~

# チームの作成

CSV ファイルを使用してチームを作成するには以下のコマンドレットを実行します。

~~~
# Microsoft Teams に接続します (全体管理者もしくは Teams 管理者でログインしてください)。
PS C:> Connect-MicrsoftTeams

# CSVファイルを使ってチームを作成します。
PS C:> addteamsclass.ps1 <Input file> <Output file>
~~~

上記のコマンドで 1,000個のチームを作成するのに、***3時間33分50秒*** かかりました。

# クラスの有効化

作成されたチーム(クラス)は、有効化されていないため学生にはまだ表示されておりません。作成したチーム(クラス)にアクセスして、***有効化***してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78535/d2044742-7fb9-11c4-0d1d-00a472312a5b.png)
Cloud FirestoreでToeic単語帳を作成してみた

# 背景

この前お客さんのAndroidシステムをVueに移行しました。最初の提案段階で、FirebaseをDBとして利用しようと思ったが、契約手続きと時間の問題でやめました。
Firebaseを使う場合は絶対あると思いますので、ゴールデンウイークの休み中、Firebaseを使ってサンプルを作ってみました。

■今回作ったのはTOEICの単語帳です。
https://demo001-e54f3.web.app/

■ソースgithub
https://github.com/lzs0627/vue-english


## 使ったもの
1.vueのマインフレームワーク：2.6.11
2.UIはvuetify：2.2.11
3.vueの状態管理vuex：3.1.2
4.ルーティングvue-router：3.1.5
5.ローカルDBとして、IndexedDBを使うことにしましたので、SQL風に操作できるようにlovefieldでIndexedDBを操作します。
※FirestoreからローカルのIndexedDBにデータをインポートした後、データを表示する時はIndexedDBからデータを取得するアーキテクチャーです。
6.データを保存するため、Firestoreを使います。

## アプリの構成
①単語はレベルを持っている。
②アプリでレベルを選んだ単語を表示する
③ローカルDB（IndexedDB）に該当のレベルの単語がない場合、FirestoreからローカルDBへインポートする
④ローカルDBにレベルごとにインポート時間が持っている。Firestoreからインポートする時、前回インポートした時間より以後のデータのみ（差分）インポートする

## Firestoreの単語データについて
ネットから探した単語です。適当にレベルを付けてみました。
単語の解説ですが、https://eow.alc.co.jp/から一番目のページ内容となります。
curlでアルクから単語を取得するバッチを作成しましたが、
一日400ぐらい単語を取得したら、ブロックされるため、まだ全部の単語を登録中です。
ゼロから始めるLeetCode Day12 「617. Merge Two Binary Trees」

#概要
海外ではエンジニアの面接においてコーディングテストというものが行われるらしく、多くの場合、特定の関数やクラスをお題に沿って実装するという物がメインである。

その対策としてLeetCodeなるサイトで対策を行うようだ。

早い話が本場でも行われているようなコーディングテストに耐えうるようなアルゴリズム力を鍛えるサイト。

せっかくだし人並みのアルゴリズム力くらいは持っておいた方がいいだろうということで不定期に問題を解いてその時に考えたやり方をメモ的に書いていこうかと思います。

[Leetcode](https://leetcode.com/)

[ゼロから始めるLeetCode 目次](https://qiita.com/KueharX/items/6ee2502c3b620c795b70)

前回
[ゼロから始めるLeetCode Day11 「1315. Sum of Nodes with Even-Valued Grandparent」](https://qiita.com/KueharX/items/edd85ca15a78e8b1c335)

基本的にeasyのacceptanceが高い順から解いていこうかと思います。


# 問題
[617. Merge Two Binary Trees](https://leetcode.com/problems/merge-two-binary-trees/)

難易度はeasyで、goodの数が多いですね。

与えられた二つの二分木のそれぞれの数値を足し、新しい木を返すという問題です。

![スクリーンショット 2020-05-04 9.08.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/412789/84d2fd55-78c9-eca0-d2a9-c58b99ed8e23.png)

以上のように、それぞれの一致する箇所の数値が足算された木がoutputとして返されていますね。

# 解法

考え方としてはどちらか一方のノードが存在しない場合はもう一方のノードを返し、両方のノードが存在しない場合は`None`を、そして両方のノードが存在する場合は数値を足す、というものです。

とりあえず網羅的に書いたのがこれです。

```Python
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode:
        if not t1 and not t2:
            return None
        if not t1:
            return t2
        if not t2:
            return t1
        if t1 and t2:
            ans = TreeNode(t1.val + t2.val)
            ans.left = self.mergeTrees(t1.left,t2.left)
            ans.right = self.mergeTrees(t1.right,t2.right)
            return ans
# Runtime: 92 ms, faster than 63.90% of Python3 online submissions for Merge Two Binary Trees.
# Memory Usage: 15.1 MB, less than 5.72% of Python3 online submissions for Merge Two Binary Trees.
```

もうちょっと簡略化してみようって書き直したのがこちら

```Python
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode:
        if not t1:
            return t2
        if not t2:
            return t1
        ans = TreeNode(t1.val + t2.val)
        ans.left = self.mergeTrees(t1.left,t2.left)
        ans.right = self.mergeTrees(t1.right,t2.right)
        return ans
        
# Runtime: 84 ms, faster than 94.01% of Python3 online submissions for Merge Two Binary Trees.
# Memory Usage: 14.9 MB, less than 5.72% of Python3 online submissions for Merge Two Binary Trees.
```

ムラがあるとはいえ速くなりました。
[Angular] inputタグに指定したchangeイベントで再度同じファイルを選択してもイベントを検知する

inputタグにchangeイベントを指定し、ファイルの選択を検知してなにかするよくある処理です。
同じファイルを選択してもイベントの検知をしたい際の解決策となります。

## 同じファイルを選択してもイベント検知できない例

```html:example.component.html
<input type="file" (change)="changeFile($event)" />
```

```js:example.component.ts
export class ExampleComponent {
  changeFile($event: Readonly<Event>): void {
    // 処理
  }
}
```
ファイル選択ダイアログでファイルを選択した後、同じファイルを選択してもchangeイベントは検知できません。



## 解決策

```html:example.component.html
<input #fileInput type="file" (change)="changeFile($event)" />
```

```js:example.component.ts
export class ExampleComponent {
  @ViewChild('fileInput') fileInput: ElementRef;

  changeFile($event: Readonly<Event>): void {
    // 処理
    this.fileInput.nativeElement.value = '';
  }
}
```

`ViewChild`でrefを取得し、直接valueを初期化することでchangeイベントが同じファイルを選択されても検知できるようになります。
PowerShell で生成した自己証明書を使って gRPC の通信を SSL 化する


## このドキュメントの内容

PowerShell を使って開発用の自己証明書を生成し、gRPC の通信を SSL 化します。

## 自己証明書の生成

次のドキュメントを参考にしました。
【Qiita】[Windows 10 Pro の PowerShell で自己署名証明書(SHA-2)を作成する](https://qiita.com/Cotoletta/items/40638d0834470f40d187)
【Qiita】[KubernetesのIngressで使うSSL自己署名証明書をPowerShellで作る](https://qiita.com/okuoku/items/fd6301df0ad520cef656)
【Qiita】[RSA鍵、証明書のファイルフォーマットについて](https://qiita.com/kunichiko/items/12cbccaadcbf41c72735)

次のスクリプトを PowerShell で実行すると

- localhost.pfx
- localhost_key_cer.pem
- localhost_cer.pem

の三つのファイルが生成されます。
gRPC の SslServerCredentials では PEM 形式（文字列）の証明書と秘密鍵が必要になるのですが、残念ながら PowerShell を使って PEM 形式（文字列）のファイルを生成する方法はわかりませんでした。結局、OpenSSL を使って PEM ファイルを生成することにしました。

以下のスクリプトを使ってサーバー用の証明書ファイルを生成します。スクリプト冒頭のホスト名（$dns）に開発マシンのホスト名を指定してください。

```powershell:証明書作成.ps1
# ホスト名
$dns = "localhost"

# 有効期限
$after = (Get-Date).AddYears(100)

# 生成するファイルパス
$pfxfile  = "localhost.pfx"
$keycerfile  = "localhost_key_cer.pem"
$cerfile  = "localhost_cer.pem"

# パスワード
$password = "password"

# 証明書ストアの「個人」に証明書を作成する
$my = "cert:\CurrentUser\My"
$cert = New-SelfSignedCertificate -DnsName $dns -CertStoreLocation $my -NotAfter $after

# pfxファイルをエクスポートする
$sspwd = ConvertTo-SecureString -String $password -Force -AsPlainText 
Export-PfxCertificate -Cert $cert -FilePath $pfxfile -Password $sspwd

# 証明書ストア「個人」から証明書を削除する
Remove-Item -Path ($cert.PSPath)

# OpenSSLを使用してpfxファイルから証明書と秘密鍵を含むpemファイルを生成する
# パスワードが要求される
Start-Process -FilePath openssl -ArgumentList "pkcs12 -in", $pfxfile, "-nodes -out", $keycerfile -Wait

# OpenSSLを使用して証明書だけを別のpemファイルとして取り出す
Start-Process -FilePath openssl -ArgumentList "x509 -in", $keycerfile, " -out", $cerfile -Wait
```

## gRPC の通信を SSL 化する

サーバ側では、 localhost_key_cer.pem に証明書と秘密鍵が両方含まれているので、これだけでCA証明書・サーバ証明書・サーバ秘密鍵を兼用できます。

```c#:サーバー側ポートの生成
string cacert = System.IO.File.ReadAllText("<localhost_key_cer.pemのパス>"); // サーバ証明書に署名した証明書として、自己署名なのでサーバ証明書を指定
string servercert = cacert; // localhost_key_cer.pem にはサーバ証明書が含まれる
string serverkey = cacert; // localhost_key_cer.pem にはサーバ秘密鍵が含まれる

// Credentials を生成
// 今回は SSL 化だけなのでクライアント証明書は要求しない
KeyCertificatePair keypair = new KeyCertificatePair(servercert, serverkey);
SslServerCredentials credentials = new SslServerCredentials(new[] { keypair }, cacert, SslClientCertificateRequestType.DontRequest);

// Credentials を指定してポートを生成
ServerPort port = new ServerPort("localhost", 55001, credentials);
```

クライアント側では、サーバ側の秘密鍵を持ってくるわけにいかないので、証明書部分を取り出した localhost_cer.pem をCA証明書としました。

```c#:クライアント側ポートの生成
string cacert = System.IO.File.ReadAllText("<localhost_cer.pemのパス>"); // サーバ側ポートで指定したcacertの証明書と同じもの

// Credentials を生成
SslCredentials credentials = new SslCredentials(cacert);

// Credentials を指定してチャネルを生成
Channel channel = new Channel("localhost", 55001, credentials)
```

## 参考）X.509 証明書での利用

X509Certificate2 クラスのコンストラクタには証明書ファイルのパスを受け取るものがあります。このとき、pfx, cer ファイルを使用できます。cer ファイルはバイナリ形式、文字列形式のどちらでもよいです。

```c#
var cert = new X509Certificate2("pfxファイルのパス", "パスワード");
var cert = new X509Certificate2("cerファイルのパス");
```
Node.jsインストール備忘録

## 環境
- Windows 10 Pro
- WSL (Windows Subsystem for Linux)

## apt更新

```
$ sudo apt update
$ sudo apt dist-upgrade
$ sudo apt autoremove
```

## nvmインストール

```
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
```
 最新：https://github.com/nvm-sh/nvm

## 最新化

```
$ cd ~/.nvm
$ git pull origin master
```

## 起動

```bash
$ source ~/.bashrc
$ nvm --version
```

## インストール可能なNode.jsの確認

```bash
$ nvm ls-remote
```

## Node.jsのインストール

```bash

$ nvm install 12.16.3
```

## 使用するバージョンを選択

```bash
$ nvm use 12.16.3
```

## npmのアップデート

```bash
npm update -g npm
```

## npm初期化

```bash
$ cd /mnt/d/develop/<プロジェクト名> #プロジェクトのディレクトリを作成して移動
$ npm init                          #初期化
```

- `/mnt/d/` はWindowsから見た場合Dドライブ直下
- npm init は全てEnterで進めてしまってOK
【Rails初学者向け】Railsチュートリアル1.3.1で、Gemのバージョン変更ができない問題をRVMで解決する


Railsチュートリアルに取り組んだところ、1.3.1でいきなりGemのバージョン固定ができないというエラーが出ました。

``$load_defaults': Unknown version "6.0" (RuntimeError)`

調べたところ、[こちらのサイト](bit.ly/3ddI5Qz)で同様エラーの質問は見つかったのですが、エラー文の意味の解説しかされておらず、エラー自体の解決方法は示されていません。


Twitterでヘルプを求めたところ、　とてもありがたいことに「”RVM”というツールを使えばRailsのバージョンをアンインストールやインストールし直すことなく簡単に変更できる」とご教授してくださった方がいました。

指示通り、RVMをインストールをすると、Railsチュートリアル1.3.1のエラーを解決することができましたので、手順を示しておきます。

初学者でどのコマンドがインストールの鍵になったのかわからなかったため、やった手順全てを記しております。
グダグダしますがご容赦くだされば幸いです。



#RVMをインストールする。

以下からインストールすることができます。

✔︎[RVM公式サイトはこちら](http://rvm.io)

✔︎[インストール用のURLはこちら](https://rvm.io/rvm/install)


〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
RVM DL際、少々手間取りましたので、その軌跡を残しておきます。
不要な方は飛ばしてください。
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
RVMインストールの際、
`$gpg2 --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
`
を実行したところ、以下のようなエラーが出ました、
`$-bash: gpg2: command not found`

とりあえずよくわからないけど、その下にある
`$\curl -sSL https://get.rvm.io | bash -s stable`
と、
`$\curl -sSL https://get.rvm.io | bash -s stable --rails`
を実行。

一瞬RVMのインストールが完了したかのようなメッセージが出たので、「あれ？これでインストールできてるのかな〜？」と思い確認のため以下コマンドを実行。

`$rvm list known`

実行した結果、RVMはありませんとのことでした😭

再度
`$gpg2 --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
`
を実行するも、またも同じエラーが。
`$-bash: gpg2: command not found`



再度
そこで、[ググって出てきたサイト](bit.ly/2VWmJ46)を参考に、以下コマンドを実行してから再チャレンジ。
`$brew install gpg2`



実行すると、
`$Warning: gnupg 2.2.20 is already installed and up-to-date
To reinstall 2.2.20, run brew reinstall gnupg`
gpg2はインストールされているとのこと。。


エラーのアドバイス通り、`brew reinstall gnupg`を実行してから、再度RVMインストールのコードを試して見ましたが、
`-bash: gpg2: command not found`
先ほどと同様のエラーが..笑(なんでーー！！)

https://bit.ly/3d5qGcs

そこで、原点の公式サイトに立ち戻ると、TOPページ以外でインストールの手順が示されているリンクを見つけたので、そこの指示通りやってみると



インストールが完了しましたっ！


##RVMの使い方

使い方については[このサイト](https://bit.ly/2SHPOhH)がわかりやすいので、ぜひご参照ください。

`$rvm install 2.4.2`
を実行した際、JDKというものをDLしないとインストールができないと言われたので、[【初心者でもすぐわかる】JDKのインストール方法 Mac編
](https://eng-entrance.com/java-install-jdk-mac)を参考にDLしました
【JavaScript】スプレッド構文、分割代入を使ったオブジェクトの操作

#【JavaScript】スプレッド構文、分割代入を使ったオブジェクトの操作

##スプレッド構文
①「...」を記述することでオブジェクト内に場所を確保
②①のオブジェクトよりも前に代入するオブジェクトを記述
これによりオブジェクトの中にオブジェクトを追加できる。

例.色情報colorをプロパティに持つcharacterColorオブジェクト。

```js

    const characterColor = {
        color:'red'
    }
```

キャラクター座標を格納するcharacterPosition に
...characterColorと書き、characterColorの場所を作る。

```js
    const characterPosition  = {

        x: 100,
        y: 200,
        ...characterColor//スプレッド構文

    };
    console.log(characterPosition);//{x: 100, y: 200, color: "red"}
```

colorプロパティとredが追加されている。

##分割代入
xやy,colorを定数として取り出す方法。
たとえば先の手順で作ったcharacterPositionのxは直接参照することができない。

```js
    console.log(x);//ReferenceErrorとなる
    console.log(characterPosition.x);//100
```
const{} = 定数名
とすることで、xやyを取り出すことができる。

```js

    const {x,y} = characterPosition;
    console.log(x);//100
    console.log(y);//200
```
入出力インターフェースの仕組みについて調べてみた

どうも初めまして！　

最近基本情報技術者の資格取得に向けて勉強中な駆け出しエンジニアのワカです。

#■この記事を書こうと思ったきっかけ
最近書籍を購入して勉強しているのですが、暗記ばかりでなかなか身にならないと思いました。

そこで基本情報に書かれている内容を自分なりに解釈したものをここに備忘録として書き残すことにしました。
そうすれば手も動かせるし人にみられると思うとその分責任感出ますしね！

あと個人的に今自作PCを作りたいとも考えているのでこの機会に詳しくなっとこうと思ったのも理由の一つです笑

もしかすると間違っている点などあるかもしれませんので、その時は指摘していただけると嬉しいです。

#■入出力インターフェース
コンピュータと様々な機器とをつなぐために定められている規格のことを**入出力インターフェース**といいます。

例えばケーブルの差込口やケーブルの種類、信号（シグナル）のパターンなどがそうです。

私たちが普段使っているもので最もポピュラーなのがUSBです。USBはコンピュータにつなぐと自動的に設定が行われる**プラグ・アンド・プレイ**が利用することができます。

##データ転送の方式
入出力インターフェースはデータを転送する方式が2つあります。それが**パラレル**と**シリアル**です。

###パラレルインターフェース

パラレルインターフェース方式の規格は**IDE**や**SCSI**などがあげられます。

**IDE(Integrated Drive Electronics)**
内蔵用ハードディスクを接続するための規格として使われていたインターフェース

**SCSI(Small Computer System Interface)**
ハードディスクやCD-ROM、MOドライブ、イメージスキャナなど、様々な周辺機器の接続に使われていたインターフェース

###シリアルインターフェース
シリアルインターフェースの規格として代表的なのが**USB**と**IEEE1394**です。

**USB(Universal Serial Bus)**
広く使え、凡庸性に主眼が置かれた規格。機器を選ばずに接続することができます。
USBの規格には規格がありそれは以下の通り

| USB規格 | 接続時の速度 |
|:--|:--|
| USB1.1（ロースピードモード）  |1.5Mビット／s  |
| USB1.1（フルスピードモード）  | 12Mビット／s  |
| USB2.0（ハイスピードモード）  | 480Mビット／s  |
| USB3.0（スーパースピードモード）  | 5Gビット／s  |

**IEEE1394**
別称i.LINKやFireWire。主にハードディスクレコーダなどの情報家電やデジタルビデオカメラなどの機器に使われているインターフェースです。

###無線インターフェース
周辺機器に対してケーブルを必要としない、無線で通信するタイプのもがあります。それらの代表例が**IrDA**と**Bluetooth**です。

**IrDA(Ingrared Data Association)**
赤外線を使って通信を行う規格です。通信距離1m程度。間に障害物があると使えないなどと現代にとってはあまり使われない規格です。
※よくテレビのリモコンなどが想像されるのですがそれとは別なので注意すること。


**Bluetooth**
2.4GHｚの電波を使って無線通信を行う規格。通信距離は10m程度。間に障害物があっても関係なし。現代にとってはマウスやイヤホンなどに使われています。

#■最後に

ここまで長々と読んでくださりありがとうございました！
少し説明がわかりにくかった部分があるかと思いますが、もしわからないところがあればコメントしていただけると嬉しいです。

では！次の記事にて！