OmniAICreator/Qiita-1.07M huggingface dataset:
Marketo の複数プログラムを対象にリード情報を一気に取得する方法

Web API Advent Calendar 2020 19日目の記事です。

## Marketo のプログラムメンバーシップ
世界中で使われてるMAツールのMarketo、日本でもたくさんの企業で使われていると思います。
Marketoを使用したマーケティングの取り組みとして、さまざまなプログラム（キャンペーンとかイベント、メールなど）に紐づけてリードを管理していくと思いますが、外部からMarketoにアクセスしてそのリードやプログラムなどのデータを取得するにはどうするかというとWEB APIですよね。

## プログラムメンバーシップを取得する方法
Marketoではちゃんと対象プログラムのリードを取得するエンドポイントが用意されています。

https://developers.marketo.com/rest-api/lead-database/leads/#programs

```
GET /rest/v1/leads/programs/{programId}.json
```
はい、プログラムIdを直に指定する形でプログラムに紐づいているリードを取得できます。
けどプログラムが大量にあったらちょっと扱いずらいという感じですね・・・

というかプログラムIdはどこで確認できるんでしょうか。


## ProgramIdはどうやって取得する？
これもちゃんとエンドポイントがあります。
https://developers.marketo.com/rest-api/assets/programs/  


```
GET /rest/asset/v1/programs.json
```
これを実行してみるとこんなレスポンスが返ってきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378300/91564555-5223-1d3f-7455-1ab3d8fc6ee5.png)
上記キャプチャの赤枠部分が各プログラムのIDとなり、

```
GET /rest/v1/leads/programs/{programId}.json
```
に指定するprogramIdとなります。

ではプログラムIdをセットして実際にリクエストしてみます。　
先ほどのレスポンスにあったProgramId：1119に属しているメンバーを取得してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378300/49f24ce1-32b7-72f5-8bd1-83e8e1b57f42.png)
まぁこのように取得できますよね。

ただプログラムIDを指定する形なので、複数プログラムに紐づくリードを一気に取得して活用したいとき、結構面倒ですよね。いちいちプログラム一覧を取得して、それをセットしてプログラムに紐づいているリードを取得してと、まぁ片手間では終わらなそうです。

エンジニアを抱えていないようなチームではもしかしたら諦めてしまうかもですよね。

ということで、CData Marketo Driverではそういった部分を解消できるよう1個のSQLで複数のプログラムとそれに紐づくリードを検索しにいくことができるようになっています。


## CData Marketo Driver について
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378300/342b5362-f5aa-76ef-6a65-f95c0c365d38.png)
Marketoに対してJDBCでの接続をはじめ、ODBCやADO、Excel などからSQLでアクセスできるドライバーです。
例えば、Lead情報を取得したいときはこんなSQLだけで取得できます。

```sql
select Id,Company,ProgramId,UpdatedAt,MembershipProgressionStatus from Leads
```
こんなふうにテーブル構造にモデリングされて返ってきます。完全にRDBですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378300/290b33c3-5aaf-66db-c407-16f7f0d3b0d1.png)


で、複数プログラムでリードを検索する方法についても、同じようにSQLを使って取得することができます。

## 複数プログラムを対象にリード情報を一気に取得する方法
SQLがかけるのでサブクエリももちろん書けます。
なので、このようなSQLでそれぞれのプログラムを検索しにいってくれます。

```sql
select Id,Company,ProgramId,UpdatedAt,MembershipProgressionStatus 
from Leads 
Where ProgramId in (select Id from Programs)
```
※SELECT句はアスタリスクで全項目もってこれますが、今回は絞ってます

実行結果です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/378300/953b4c6f-a2a6-9aad-cfd3-ca5aa4333287.png)

このようにMarketo側ではプログラムIDを指定してリクエストしてねと決められているので、それならばツールやサービス側で対応するしかないですよね。

では実際にどのようにしてMarketoにアクセスしてデータを取得しているのかみていきます。

### CData Driverの動き
まずはプログラム一覧を取得しにいきます。
SQLのサブクエリ部分ですね。

```
GET https://XXXXXXXXX.mktorest.com/rest/asset/v1/programs.json?
```

次に取得したプログラムID分、プログラムメンバーシップを取得するエンドポイントを呼びます。
なので100個プログラムがあるなら、1001.json の部分を変更して100回エンドポイントを呼びます。
（こればかりはしょうがないところ・・・）

```
POST https://XXXXXXXXX.mktorest.com/rest/v1/leads/programs/1001.json
```

とはいえ、1個のSQLを実行することでこのようにデータを取得することができます。

## おわりに
マーケティングデータは色んな確度でデータが欲しいところですし、これこそデータを取得する部分に時間を割くべきではないですよね。
ちなみにCData Driverを使用したデータ連携ツールのCData Syncというものもあります。これはSaaSデータをDBに定期連携できるELTツールなのですが、こういったものを使用することで、MarketoデータをRDBに格納することができ、好きな時に好きなようにデータを活用することもできます。
PHP 7 で劇的に改善された PHP

# 悪いイメージのある PHP （個人の感想です）

PHP はなんとなく悪いイメージがある、そう思っている人はいませんか？

僕も昔はそうでした。

僕は学生の時に、 Java, Scala, Ruby あたりを使っていました。PHP を触ったのは就職してからです。今学生だとしても、 PHP は勉強せず、 Python, Go あたりを勉強しますね。

しかし、 PHP を使って開発されている Web アプリケーションは非常に多いです。PHP には Web アプリケーション向けの便利な機能が大量にあります。

また、 PHP 7 から型周り改善が多く入り、以前の PHP から見違えるようになりました。

# なぜ PHP には悪いイメージがあるのか

なぜ PHP が嫌いな人が多いのか、それは、「厳密でない比較演算子 `==`」のせいだと思っています。

PHP 8 では、この比較演算子が変更になったことでも話題になりました。

[【PHP8.0】非厳密な比較演算子`==`の挙動が今さら変更になる](https://qiita.com/rana_kualu/items/82cc8295d2102d14b88a)

↑のQiita記事の例にもありますが、

```php
"true" == 0    // true => PHP 8 からはこれが false に
0 == "0"       // true
"true" == "0"  // false
```

これの不思議なところは、 `"true" == 0` であり、 `0 == "0"` なのに、`"true" == "0"` は false になる点である。A = B かつ B = C なのに A = C にならない違和感である。

`"true" == 0` がなぜ true になるのか、これは、文字列を int にキャストしようとした時に、文字列が数字から始まっていない場合は、 `0` にキャストされるからである。

```php
(int)"true"    // 0
(int)"100"     // 100
(int)"100abc"  // 100
(int)"abc100"  // 0
```

これは PHP のドキュメントにも書いてある。

https://www.php.net/manual/ja/language.operators.comparison.php

このキャストの挙動は PHP 8 でも変わっていない。`==` の挙動だけ修正されることになった。

PHP にはカオスな型の比較表があるのは有名だろう。一度見てみると面白いと思う。

https://www.php.net/manual/ja/types.comparisons.php

`===` による厳密な比較の表はいるのか？と思ってしまう。

# PHP 7 になって良くなったこと

関数の引数や戻り値、プロパティなどに型を書けるようになったのだ。


```php
class Something
{
    private int $id;

    public function setId(int $id)
    {
        $this->id = $id;
    }

    public function getId(): int
    {
        return $this->int
    }

}
```

昔と比べると、こんなのほんとにPHPなのか？と思うのかもしれないが、これが今の PHP である。

`==` の挙動は相変わらずだが、型がだいぶきっちりし始めて、ちゃんと書けばかなり使いやすい言語だと感じ始めた。

# PHP 8

更に PHP 8 で色々機能が追加された。

Nullsafe operator や

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29024/2cf6927e-4da9-47b1-a57b-af706a8f3d00.png)

Union types など、

```php
class Number {
    public function getSomething(): int|float
    {
        // int または float を返す
    }
}
```

便利な機能が多く追加されている。

# PHP の良いところ

やはり Web に特化した言語であり、他の言語にはない機能がいっぱいある。

HTMLの特殊文字をエスケープする関数や、

```php
htmlspecialchars('<html>')  // &lt;html&gt;
```

クエリ文字列を生成する関数

```php
http_build_query(['a' => 1, 'b' => 2])  // a=1&b=2
```

JSON に変換する関数に、

```php
json_encode(['a' => 1, 'b' => 2]);  // "{"a":1,"b":2}"
```

日の出の時間を取得する関数など（何に使うんだ）

```php
$now = time();
date_sunrise($now);  // 04:30
```

Web に関する関数なら「こんなものもあるんだ」ってものまであります。 Web アプリを開発していると非常に便利です。

あとは、やっぱり利用者が多いので、 Web に知見が多く転がってるのは素晴らしいですね。ググれば大抵の問題は解決します。

# PHP の良くない点

実際長年 PHP を触ってきましたが、やっぱり実行速度はコンパイル言語には勝てません。PHP 7 になってかなり高速化されました。PHP 8 は JIT コンパイルになってさらなる高速化が見込めますが、非同期処理が苦手なのもあり、やっぱり遅いです。

会社の新卒研修で Scala を使ったアプリケーションを作ったり、 https://lgtmoon.dev/ も Scala で作ったりしましたが、サーバーが超貧弱にもかかわらずかなりのパフォーマンスが出ます。

ただ Scala はあまりにも知見が少ないので、今なら Go を選択しますかね。当時 Go が有名だったら Go を選んでいたと思います。

# まとめ

かなりダラダラと書いてしまいましたが、昔と比べて PHP はかなり良い言語になってきたと思うので、皆さん食わず嫌いせず触ってみるといいかと思います。
String.jaro_distance/2 (Elixir)

この記事は[Elixir その2 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/elixir2) **22日目**です。

---

# はじめに
- [Elixir](https://elixir-lang.org/)楽しんでいますか:bangbang:

# [String.jaro_distance/2](https://hexdocs.pm/elixir/String.html#jaro_distance/2)
- この関数、知っていますか
- 2つの文字列がどのくらい似ているかを0.0〜1.0の間で評価するものだそうです

> Computes the Jaro distance (similarity) between two strings.
> Returns a float value between 0.0 (equates to no similarity) and 1.0 (is an exact match) representing [Jaro](https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance) distance between string1 and string2.

# 具体例

```elixir
iex> String.jaro_distance("awesome", "awesome")
1.0
iex> String.jaro_distance("cat", "fish")     
0.0
iex> String.jaro_distance("osamu", "awesome")  
0.565079365079365
iex> String.jaro_distance("osamu", "ozamu") 
0.8666666666666667
iex> String.jaro_distance("erlang", "elixir")
0.5555555555555555
iex> String.jaro_distance("ruby", "elixir")  
0.0
```

- だいたい動きはわかりました
- <font color="purple">$\huge{で!?  何に使うのジャロ?}$</font>

# 実例のご紹介【2022-03-18 追記】

読者の方から実例についてのお便りをいただきました。
ここに御礼を申し上げるとともに、追記にて実例のご紹介をしておきます。

https://github.com/surface-ui/surface/blob/2298b6fd5ddaa847099ae91f38d92893f232cbbd/lib/surface/compiler/helpers.ex#L117-L124

[Surface](https://github.com/surface-ui/surface)の`Did you mean?`機能で使っているとのことです。

![スクリーンショット 2022-03-18 18.52.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131808/2a8fe12e-4127-6e80-82eb-f4bcd4547aa7.png)



読者の方というのは、 @mnishiguchi さんのことです！
[elixir.jp](https://join.slack.com/t/elixirjp/shared_invite/zt-ae8m5bad-WW69GH1w4iuafm1tKNgd~w) Slackの`#autoracex`チャンネルにてご連絡いただきました。
Thanks a lot!!!です。

# Wrapping Up :christmas_tree::santa::santa_tone1::santa_tone2::santa_tone3::santa_tone4::santa_tone5::christmas_tree: 
- [String.jaro_distance/2](https://hexdocs.pm/elixir/String.html#jaro_distance/2) の使いどころをぜひ教えてください :bangbang:
    - 自分でわかったら更新します :bangbang:
- Enjoy [Elixir](https://elixir-lang.org/) !!! :rocket::rocket::rocket:
Railsでオブジェクト配列をハッシュ化するあれこれ(injectとかindex_byとか)


例えばUserモデルがあり、nameやらemailやらageやらを各オブジェクトが持っていたとする。

ある特定のemailの人たちだけに施したい処理があって、どのemailが対象となるかは既に配列のグループがあったとする。（若干例え悪いけどそんな感じだった）
**※emailは一意性が担保されています**

最初、以下の様な処理を書いていた。

```ruby:sample.rb

# 対象としたいemailアドレス群。
targets = ["xxxx@gmail.com","xxx3@gmail.com"..."xxx25@gmail.com"]

targets.each do |target|
  User.all.each do |user|
    # 一致するものがあれば次の処理へ
    next if target != user.email
    user.nextaction
  end
end
```

これだとeachで二重に回すことになるので、もっと簡単にできないものかと考えた時に、emailをキーとするハッシュつくればいいのでは？？となった。

そして最初、injectでハッシュを生成したのがこちら。

```ruby:sample.rb

# 対象としたいemail群。
targets = ["xxxx@gmail.com","xxx3@gmail.com"..."xxx25@gmail.com"]
# emailをキーとしてUser.allをハッシュ化
users_sort_email = User.all.inject({}) {|hash,user| hash[user.email] = user; hash }
targets.each do |target|
  # targetをキーとした（＝targetと同じ値のemailが存在した）場合次の処理に飛ばす
  next if users_sort_email[target].blank?
  user.nextaction
end
```

これでもできるんだけど、index_byを使うともっとみやすくなりました。

```ruby:sample.rb

# 対象としたいemail群。
targets = ["xxxx@gmail.com","xxx3@gmail.com"..."xxx25@gmail.com"]
# emailをキーとしてUser.allをハッシュ化をなんとこれだけで表している。
users_sort_email = User.all.index_by(&:email)
targets.each do |target|
  next if users_sort_email[target].blank?
  user.nextaction
end
```

非常にみやすくなった。
ただしindex_byはActiveRecordでサポートされているものなので、デフォルトのRubyではinjectionを使いましょうという話。

# 追記

当初上記の記事はemailではなくageをキーとして記載していたのですが、**コメントにて「一意性が担保されていないデータをキーに指定すると、ハッシュのキーが重複してしまい、そのうち一つにしかnextactionを呼ぶことができない**とのご指摘をいただきました。

これはおっしゃって頂いた通りで、実際に実務で使用した際は一意性の担保されているキーで処理を行っていたものの、「だからこそ上記のソートが使える」という認識を十分に理解しておらず記事を記載したため生じたミスでした。

踏まえましてemailをキーとし、emalには一意性が担保されている旨を明記する形で記事を修正させていただきました。ありがとうございます！

また、例えばキーがageの様に一意性が担保されていない物で同様の処理を行いたい場合についても丁寧にコメントをいただくことができました。
詳細はコメント欄をご参照いただければと思うのですが、**ActiveRecordがある前提であれば**、以下の通りの処理で実現が可能とのことです。

```ruby:sample.rb
targets = [1,4,5,6,10,23]
User.where(age: targets).each do |u| 
  u.nextaction
end
```

この場合に発行されるSQLは以下の通りです。

```ruby:SQL.rb
-- User.where(age: targets)はこんなSQLを発行している
SELECT * FROM users WHERE (users.age IN (1,4,5,6,10,23));
```

**ターゲットとなるageをもつUserを全て取得（この時点で抽出されたuserデータは全てnextactionの対象となる）→eachで回すことで「対象となるユーザーのみ全ての人にnextactionを行う」**という形で実現できています。

これについても非常に勉強になり、お時間割いてコメント頂いてありがとうございますという気持ちです・・・勉強になりました！
WSL2でDOCKER_HOSTが指定されているとDockerが動かない

# TL;DR

Windows 10でDocker for Desktopが動いている状態で、WSLのubuntuでは動いていたdockerが、WSL2にしたら動かなくなったのは、環境変数`DOCKER_HOST`が指定されていたため。`unset DOCKER_HOST`すると動く。

# 現象

WSLでdockerが使えていたのに、WSL2にしたら動かなくなった。

```sh
$ docker ps                                                                           [~]
Cannot connect to the Docker daemon at tcp://localhost:2375. Is the docker daemon running?
```

実際にDockerは動いており、ポート2375が空いていることはPower Shellで確かめることができる。

```powershell
$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

$ Test-NetConnection -ComputerName localhost -Port 2375


ComputerName     : localhost
RemoteAddress    : ::1
RemotePort       : 2375
InterfaceAlias   : Loopback Pseudo-Interface 1
SourceAddress    : ::1
TcpTestSucceeded : True
```

WSLのバージョン確認。

```powershell
$ wsl -l -v
  NAME                   STATE           VERSION
* Ubuntu                 Running         2
  docker-desktop-data    Running         2
  docker-desktop         Running         2
```

WSLのバージョンは2で、docker-desktopも動いていますね。

原因は、(僕の場合は.zshrcにて)環境変数`DOCKER_HOST`が指定されていたため。

```zsh
export DOCKER_HOST=tcp://localhost:2375
```

確かWSLではこれが必要だった。しかし、WSL2では不要。これをunsetすると動くようになる。

```zsh
$ unset DOCKER_HOST
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
```

動いた。めでたい。

個人的にものすごく困っていたので、同じ理由で困っている人のためにここに記録を残しておきます。

# 参考

* [WSL2 Cannot connect to the Docker daemon @ stack overflow](https://stackoverflow.com/questions/60708229/wsl2-cannot-connect-to-the-docker-daemon#comment109923517_60708229)
【Swift】Map Kit Viewを使った基本機能のテンプレートと解説

# これはなに

`Map Kit View` を設置したときコントローラに書くテンプレートのようなものとその解説。
ひとつの画面に **Map Kit View** と **Label** があり、マップの特定箇所を長押しするとLebel部分に住所が表示されるという想定。

## テンプレートと解説

```swift:ViewController.swift
import UIKit

// 以下2つが Map Kit View を使用する上で必要
import MapKit
import CoreLocation

// 2つのプロトコルを宣言
class ViewController: UIViewController,CLLocationManagerDelegate, UIGestureRecognizerDelegate {

  // 住所が入る変数を定義
  var addressString = ""
  
  // 長押しを認識させる
  @IBOutlet var longPress: UILongPressGestureRecognizer!
  
  // ストーリーボードから Map Kit View をControlキー長押しでつなぐ
  @IBOutlet weak var mapView: MKMapView!
  
  // 位置情報を取得するインスタンスを作成
  var locationManager:CLLocationManager!
  
  // ストーリーボードから Label をControlキー長押しでつなぐ
  @IBOutlet weak var addressLabel: UILabel!
  
  // 今回特に関係なし
  override func viewDidLoad() {
    super.viewDidLoad()
  }

  // ストーリーボードで設置した Long Press Gesture Recognizer（詳細は当記事の下の方を参照）
  @IBAction func longPressTap(_ sender: UILongPressGestureRecognizer) {
    
    // タップを開始したとき
    if sender.state == .began{
    
    // タップを終了したとき
    } else if sender.state == .ended {
          
      // タップした位置（CGPoint）を指定してMKMapViewの緯度経度を取得
      let tapPoint = sender.location(in: view)
      let center = mapView.convert(tapPoint, toCoordinateFrom: mapView)
      
      // 緯度
      let lat = center.latitude
      
      // 経度
      let log = center.longitude
    
      // 緯度経度から住所に変換するメソッドに引数を渡す
      convert(lat: lat, log: log)
    }
  }

  // 緯度と経度を変換するためのメソッド
  // このあたりに書いたプロパティは当記事の下の方にあるリンクを見ると理解しやすいかも
  func convert(lat:CLLocationDegrees, log:CLLocationDegrees) {

    // 住所から緯度経度に変換
    let geocoder = CLGeocoder()
    // 緯度経度から住所を作成
    let location = CLLocation(latitude: lat, longitude: log)
    
    // クロージャー（原則クロージャーの中に入ってるものは self を付けて書く。値が入ったあとにカッコ内が呼ばれ、値が入るまではカッコの外が呼ばれる）
    // 経度、緯度から逆ジオコーディングして住所を取得する
    geocoder.reverseGeocodeLocation(location) {
      (placeMark, error) in
      
      // 想定通りの住所が取得できた場合
      if let placeMark = placeMark {
        if let pm = placeMark.first {

          // 想定通りの住所が取得できなかった場合は文字列を組み合わせて作る
          if pm.administrativeArea != nil || pm.locality != nil {
            self.addressString = pm.name! + pm.administrativeArea! + pm.locality!
          } else {
            self.addressString = pm.name!
          }
          // Labalに住所を表示
          self.addressLabel.text = self.addressString
        }
      }
    }
  }
}
```

## 注釈と参考

### Long Press Gesture Recognizer

1. ストーリーボードでMap Kit Viewの上に重ねて設置する
2. ストーリーボードのView Controller上部バー（？）に作られたアイコンをcontrolキーを押しながら `View Controller.swift` 上にドラッグドロップしてつなぐ
3. **Type** を下の画像のように **UILongPressGestureRecognizer** に設定してConnectを押す

![f63ae96ff5f16b59d55306b256fe7af5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413699/0bac3fe2-91e8-56a9-7ca4-25852b68ecde.png)

### プロパティと中の値について

参考

**iOS11でCLPlaceMarkのnameで取れる値が変わったと思って検証したら、奇妙なプロパティであることがわかった**
<https://qiita.com/fr0g_fr0g/items/356d88ec906f2004f5f6>

---

こんな感じです。
ぼんやりとした理解でちょっとどんくさいコードのような気もします。
React備忘録

#ReactHooksについて
・useState
・useReducer
・useContext
・action.js
・useEffect
・combineReducers(Redux)
適宜追加していきます。
##useState
```
const [state, setState] = useState(initialState);
```
のような形で、左側は自分で命名したstate、右側はsetの次は大文字にする。右側のset~を使ってstateの値を更新する。useStateの（）には初期値を設定する。
##useReducer
useReducerを用いることで、ステート更新関数をステート非依存にすることを強制できます。実際のアプリ開発においては、アプリが複雑化するにつれて、あるステートと別のステートが関わりを持ち始めるかもしれません。

```App.jsx
 const initialState = {
    events: [],
    operationLogs: []
  };
  const [state, dispatch] = useReducer(reducer, initialState);
```
```reducers/event.js
import { CREATE_EVENT, DELETE_ALL_EVENTS, DELETE_EVENT } from "../actions";
const events = (state = [], action) => {
  switch (action.type) {
    case CREATE_EVENT:
      const event = { title: action.title, body: action.body };
      const length = state.length;
      const id = length === 0 ? 1 : state[length - 1].id + 1;
      return [...state, { id, ...event }];
    case DELETE_EVENT:
      return state.filter((event) => event.id !== action.id);
    case DELETE_ALL_EVENTS:
      return [];
    default:
      return state;
  }
};
export default events;
```
```components/Event.jsx
const addEvent = (e) => {
    e.preventDefault();
    dispatch({
      type: CREATE_EVENT,
      title,
      body
    });
    dispatch({
      type: ADD_OPERATION_LOG,
      description: "イベントを作成しました",
      operatedAt: timeCurrentIso8601
    });
    setTitle("");
    setBody("");
  };
```
###コードの説明
`App.jsx`で
`const [state, dispatch] = useReducer(reducer, initialState);`
を宣言します。そうすることで現在の `state `を` dispatch `メソッドとペアにして返します。
`components/Event.jsx`のfunction内でdispatch呼ぶことで`type`とパラメーターを`reduers/event.js`にactionとして渡します。
こうして渡ってきたactionを`reduers/event.js`ないでcase文で分岐させます。
現在stateにeventsというオブジェクトがあり、その中にデータとして配列で格納されています。
##useContext
useContextを使うメリットはpropsによるバケツリレーをしなくて済むので、不要なコードを書かなくてよくなる。

```contexts/AppContext.js
import { createContext } from "react";
export const AppContext = createContext();
```
```App.jsx
return (
    <>
      <AppContext.Provider value={{ state, dispatch }}>
        <EventForm />
        <Events />
        <OperationLogs />
      </AppContext.Provider>
    </>
  );
```
```components/Events.jsx
import { AppContext } from "../contexts/AppContext";
export const Events = () => {
  const { state } = useContext(AppContext);
```
###コードの説明
`contexts/AppContext.js`でcreateContext();を宣言します。
そして、`App.jsx`で`AppContext.Provider value={{ state, dispatch }}`という形で
以前はcomponentsごとに書いていたpropsを一括して`AppContext`として記述します。
そうすることで、components内でstateだけを呼びたいのであれば
`const { state } = useContext(AppContext);`と記述して
両方呼びたいのであれば
`const { state, dispatch } = useContext(AppContext);`と記述する。
`useContext`を使うことでcomponents内でpropsとして渡さなくてもよくなる。
##useEffect
https://sbfl.net/blog/2019/11/12/react-hooks-introduction/ 
が非常にわかりやすいです。
クリーンナップ関数を設定する理由はアンマウントされた際に前回のuseEffectが残ったまま、重複することになるので、クリーンナップ関数で消してあげる為です。
【Go言語】構造体(struct)について

#構造体等とは何か
色々あるデータを1つにまとめたもの。
Go言語にはクラスというものが存在しないが、構造体を使用することでクラスのような振る舞いをすることができる。

#定義方法
```main.go
package main

func main() {
	type people struct {
		name   string
		age    int
		gender string
	}
}
}
```
#　初期化方法
```main.go
package main

func main() {
	type people struct {
		name   string
		age    int
		gender string
	}

	//people型で変数を定義
	var people1 people

	//それぞれに値を代入
	people1.name = " yamada"
	people1.age = 21
	people1.gender = "man"

     //出力
    fmt.Println(people1.name) //yamada
	fmt.Println(people1.age) //21
	fmt.Println(people1.gender) //man
}

```


 上記のように構造体を使うとクラスっぽい振る舞いができます。
git tagdate コマンドの紹介

あるパッチがどのバージョンのkernelに入ったものかを簡単に知るために、
git tagdate というコマンドがあります。

例えば
commit 2a842acab109f40f0d7d10b38e9ca88390628996
Author: Christoph Hellwig <hch@lst.de>
Date:   Sat Jun 3 09:38:04 2017 +0200

というパッチがメインラインに入ったのは、2017年6月3日だとわかりますが、
$ git tagdate 
(snip)
Sun Sep 3 13:56:28 2017 -0700 refs/tags/v4.13
Sun Aug 27 17:20:50 2017 -0700 refs/tags/v4.13-rc7
Sun Aug 20 14:14:03 2017 -0700 refs/tags/v4.13-rc6
Sun Aug 13 16:01:40 2017 -0700 refs/tags/v4.13-rc5
Sun Aug 6 18:44:56 2017 -0700 refs/tags/v4.13-rc4
Sun Jul 30 12:40:48 2017 -0700 refs/tags/v4.13-rc3
Sun Jul 23 16:15:27 2017 -0700 refs/tags/v4.13-rc2
Sat Jul 15 15:22:25 2017 -0700 refs/tags/v4.13-rc1
Sun Jul 2 16:07:11 2017 -0700 refs/tags/v4.12
Sun Jun 25 18:30:14 2017 -0700 refs/tags/v4.12-rc7
Mon Jun 19 22:19:48 2017 +0800 refs/tags/v4.12-rc6
Sun Jun 11 16:48:27 2017 -0700 refs/tags/v4.12-rc5
Sun Jun 4 16:47:49 2017 -0700 refs/tags/v4.12-rc4　　<= 
Sun May 28 17:20:59 2017 -0700 refs/tags/v4.12-rc3
Sun May 21 19:30:30 2017 -0700 refs/tags/v4.12-rc2
Sat May 13 13:20:00 2017 -0700 refs/tags/v4.12-rc1
Sun Apr 30 19:48:00 2017 -0700 refs/tags/v4.11
(snip)

2017年6月3日は v4.12-rc4のタイミングに入ったというようにわかります。

でも、みなさんの git にはそんなコマンドは用意されていないですよね。
自分で用意しましょう。

~/.gitconfig 
[alias]
	tagdate = tag -l --format='%(taggerdate) %(refname)' --sort=-taggerdate

(注)全てのレポジトリでtagと日付がセットで出てくるとは限りません。
【Go言語】AgoutiでGoogleChromeを自動で操作する

#事前準備
##Agoutiのインストール
以下のコマンドでAgoutiをインストールします。

```
$ go get github.com/sclevine/agouti
```
##Chrome Driverをインストール
続いて自動で操作したブラウザに対応したWebDriverをインストールします。
今回はGoogleChromeを操作したいので、以下のコマンドでGoogleChromeのWebDriver（chromedriver）をインストールします。

```
$ brew install chromedriver
```

GoogleChromeのバージョンによって上手くいかない場合はChromeのバージョンを確認し、以下のサイトよりDriverをダウンロードしてください。

https://chromedriver.storage.googleapis.com/index.html

#自動操作
事前準備が完了したら、ブラウザを自動操作するコードを書いていきます。
今回は、

Go言語の公式ページを開き、Documentsまでの遷移する動きを自動化してみます。

以下、自動操作のためのコードです。
それぞれのコードの意味はコメントアウトで記述しています。

 ```main.go
package main

import (
	"log"

	"github.com/sclevine/agouti"
)

func main() {
	//ChromeDriverを使用するための記述
	driver := agouti.ChromeDriver()

	//WebDriverプロセスを開始する
	err := driver.Start()
	if err != nil {
		log.Fatal(err)
	}

	//WebDriverプロセスを停止する（main関数の最後で停止したいのでdeferで処理)
	defer driver.Stop()
	if err != nil {
		log.Fatal(err)
	}

	//NewPage()でDriverに対応したページを返す。（今回はChrome）
	page, err := driver.NewPage()

	//Navigate()の引数にURLを渡し、ページの遷移を行う
	err = page.Navigate("https://golang.org/")

	//Screenshot()で開いているページのスクリーンショットを撮る。保存名は引数で指定。（保存場所も相対パスor絶対パスで指定可能）
	page.Screenshot("Go言語公式HP.png")

	if err != nil {
		log.Fatal(err)
	}

	//FindByLink() で引数に指定したaタグのテキストを検索し、Click()でクリックする
	page.FindByLink("Documents").Click()

	//上記のScreenshot()と同様の動き
	page.Screenshot("Go言語Documents.png")

}
```




以下、page.Screenshot（）で撮影したスクリーンショットです。

Go言語公式HP.png
![Go言語公式HP.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/950382/4558f4d0-a739-372d-ed40-baee3ab51a1a.png)


Go言語Documents.png
![Go言語Documents.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/950382/04e2290c-4d72-9590-e95f-3f8bffebc17c.png)
【Go言語】logを別ファイルに出力する

#方法
logパッケージの「SetOutput」関数を使う

#書き方
```main.go
package main

import (
	"log"
	"os"
)

func main() {
	// ファイルを開く。（引数はテストの名前 、flag、perm）
	logfile, err := os.OpenFile("./test.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		panic("cannnot open test.log:" + err.Error())
	}
	//main関数の最後にファイルを閉じるためdeferを記述
	defer logfile.Close()

	//標準ロガーの出力先を設定(定義したlogfileに出力するように記述)
	log.SetOutput(logfile)

	log.Println("TestLog")

}
```

main.goを実行すると、ディレクトリ内にtest.logが作成され、中身には「Testlog」と表示されます。

```test.log
2020/12/16 22:18:56 TestLog
```

#補足
上記を試すと、ターミナルには　log.Printlnの出力がされないと思います。
ターミナルにもファイルにもログを出力をさせたい場合ioパッケージのMultiWriterメソッドを使用します。
具体的にはこのように書きます。

```main.go
package main

import (
	"io"
	"log"
	"os"
)

func main() {
	logfile, err := os.OpenFile("./test.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		panic("cannnot open test.log:" + err.Error())
	}
	defer logfile.Close()

// SetOutputの引数を追記
	log.SetOutput(io.MultiWriter(logfile, os.Stdout))

	log.Println("Testlog(ファイルにもターミナルにも出力される)")
}
```

ターミナルにもファイルにも出力できましたでしょうか。
このように書くと、ターミナルにもファイルにもログを出力させることができます。
Notionで2つの日付から期間を取得してスプリントを計算する

アルプ株式会社でエンジニアをしています、集約のエンティティ@pictinyです。
これは[Notion Advent Calendar 2020](https://qiita.com/advent-calendar/2020/notion) 16日目のエントリです。
15日目は[Notionのボードを投票者数でソートする](https://qiita.com/pictiny/items/83267abd2568d34ac486)でした。

# 日付の計算
プロパティの値を使って何か計算をする場合、 `Formula` というプロパティタイプを使います。
`Formula` を使うと、日付の計算を行うことができます。

## 2つの日付から期間を取得する
開発バックログで開発期間の日数を取得したいことがあるかと思います。
開発着手した日と、開発完了した日を、それぞれプロパティとして用意します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427952/6c482774-c2d5-5911-5f6f-7efc537582aa.png)

開発着手から開発完了までの期間を日付で取得する場合、計算式は以下になります。

```c
dateBetween(prop("開発完了"), prop("開発着手"), "days")
```

スクラムを実践していると、一般的には2週間を1スプリントとして開発サイクルを回しているかと思います。
開発期間をスプリントに換算する場合、計算式は以下のようになります。

```c
dateBetween(prop("開発完了"), prop("開発着手"), "weeks") / 2
```

# まとめ
Formulaを使って開発にかかったスプリントを計算することができました。

公式ドキュメントは[Formulas](https://www.notion.so/Formulas-28f3f5c3ae644c59b4d862046ea6a541)にありますので、そちらもご覧ください。
spaCy 業務で使える自然言語処理ライブラリ

こんにちワンコ。
[ちゅらデータ株式会社](https://churadata.okinawa/)の山内です。

この記事は、["ちゅらデータアドベントカレンダー"](https://qiita.com/advent-calendar/2020/churadata)の16日目です。
私の好きな分野の一つ「自然言語処理」について書きたいと思います。


# はじめに
自然言語処理は、データ分析の様々な研究分野の中でも大きく注目されているテーマで、日々たくさんの論文が発表されていますが、最近では、実際の業務レベルでの要望、課題解決の手段としても利用されることが多くなってきました。

この記事は、そのような業務で利用される自然言語処理のライブラリのひとつ「spaCy」について書きたいと思います。

# spaCyについて
[spaCy](https://spacy.io/) は Python と Cython で実装されたオープンソースの自然言語処理ライブラリです。
プロダクト環境で利用するためにデザインされていることが特徴的です。
本家サイトには、自然言語処理の「Ruby on Rails」のようなものだと記載されています（^^;）

私が spaCy を利用しようと考えたのも、「プロダクト環境で利用できる」という点で目についたからです。

NLPのライブラリには、NLTK や CoreNLP などの有名なものがいくつかありますが、研究で利用されることを目的に実装されています。
これらは、BERTやTransformerをお試しで実行するには便利なライブラリですが、
顧客の用意した自由なフォーマットデータに対して前処理を行う場合や、ビジネスロジックに沿ったデータオブジェクトを実装したりするといった用途では使い勝手が良いとは言えません。

spaCyを使うことで、業務コードを便利に実装することができます。

# 具体的な特徴
### 自然言語処理のさまざまな概念が基本APIとして用意されている
下記の図は、spaCyのアーキテクチャ図です。
ドキュメントやトークン、形態素などの基本オブジェクトや、
トーカナイザー、Tagger（品詞のタグ付などに利用）、Dependency Parser（構文解析）、固有表現抽出など、設計段階で構想されています。

これらは、処理を実行するための具体的な実装だけでなく、
ビジネスロジックを拡張実装するためのベースコードとして利用できるようになっています。

![image](https://spacy.io/architecture-bcdfffe5c0b9f221a2f6607f96ca0e4a.svg)
https://spacy.io/api#_title Library architectureより

「Span」は、単語分割がややこしい日本語NLPではとても便利な概念です。
[https://spacy.io/api/span](https://spacy.io/api/span)

### パイプライン処理
NLPで必須な前処理に関しても便利な機能がたくさんあります。
実用で必須なパイプライン処理は、使いやすく独自処理の追加も簡単な実装で実現することができます。
![image](https://d33wubrfki0l68.cloudfront.net/16b2ccafeefd6d547171afa23f9ac62f159e353d/48b91/pipeline-7a14d4edd18f3edfee8f34393bff2992.svg)
### ルールベースの利用
深層学習モデルに注目があつまるNLPですが、
業務要件では、ルールベース（単純な単語マッチなど）や正規表現を利用した手法も課題解決のため必要な実装です。
辞書を用いたMatcherや、辞書や言語資源をを効率的に扱うために、 Vocab , StringStore などのデータ格納のための概念も設計に組み込まれています。
[https://spacy.io/usage/rule-based-matching](https://spacy.io/usage/rule-based-matching)

### モデルの組み込み
統計モデルやML、深層学習系モデルを組み込むことも想定されており、
言語ごと（英語、ドイツ語など）のモデルや、いくつかのアルゴリズムがすぐに利用できる状態で実装されています。
もちろん、自前のモデルも利用することが可能です（使い方にやや癖があります）
[https://spacy.io/models](https://spacy.io/models)

### 処理の効率化
処理速度やメモリ効率化のため、Cythonでの実装を筆頭に、巨大データを扱うための実装や、データ格納時サイズ削減の機構など（Hashing化）、実運用を見据えた様々な工夫がなされています。



# おわりに
以上、本記事はspaCyの紹介のみに終わってしまいますが、
プロダクトレベルで実装で役立つライブラリーとして、spaCyの良さが少しでも伝わっていると幸いです。

弊社では、実際のプロダクトコードとしての利用や、この設計を元に独自ライブラリの実装の参考にしたりと便利に利用しています。

データ分析業界は、「AI」というバズワードが先行していた夢物語のフェーズから、
実際に業務課題に対してどのように実装するか、機能としてどう組み込むかといったフェーズに入っています。

これからは、spaCyのような実務で使えて役に立つOSSライブラリがたくさん開発されていくことでしょう。

ちゅらデータもそういったライブラリやサービスをどんどん開発して顧客やデータ分析業界の力になれるよう日々技術磨いています。
Rails6のPostgreSQLは9.3以上

## TL;DR

- やりたいこと
    - OS: CentOS 7
    - アプリ: Ruby on Rails 6
    - DB: PostgreSQL(別アプリと共用)
- yumでPostgreSQLインストールしたらは9.2系が入った
- Rails6からは9.3以上を使う必要があった
- PostgreSQLを9.6系に上げた
- 9.3以上を使えというメッセージが出た
- は？
- **結論**(忙しい人用)

## Rails6で使えるPostgreSQLは9.3以上

CentOS7上でRails6のアプリを作成し、DBにPostgreSQLを利用しようと諸々インストールを行いました。
ちなみに構築しようとしている環境は開発用なので別サーバで動いている別のアプリが使っているものを流用しようとしました。(*1)
それがすべての元凶とも知らずに。

今回たてるサーバのPostgreSQLはとりあえずyumで入れました。

```bash
$ yum install postgresql-devel
$ psql -v
#=> 9.2.22
```

とりあえず9.2系が入ったらしいのでbundle install諸々やって `bundle exec rails s` で起動

```bash
$ bundle exec rails s
#=> Your version of PostgreSQL (9.2.22) is too old. Active Record supports PostgreSQL >= 9.3.
```

9.3以上のPostgreSQLを使えとのこと。
ちなみにこの辺は[Railsウォッチ20181210](https://techracho.bpsinc.jp/hachi8833/2018_12_10/66145#1-4)にも書かれている。

## 最新版のPostgreSQLを入れる

[CentOS7.3にPostgreSQL9.6.3をインストールしてみた](https://qiita.com/uhooi/items/44ed9370740c7521dce4)を参考にしつつ9.6.3をインストール。

```bash
$ psql -v
#=> 9.6.3
```

## 改めてRailsサーバを起動

```bash
$ bundle exec rails s
#=> Your version of PostgreSQL (9.2.22) is too old. Active Record supports PostgreSQL >= 9.3.
```

**は？**

## 結論

ここでRailsが警告を出していたのはPostgreSQLの**クライアントではなくPostgreSQLのサーバのバージョン**でした。
なので、*1のところで出てきた「別サーバで動いている別のアプリが使っている」PostgreSQLのバージョンを上げる必要がありました。

別で立てるのが面倒だったので流用しようとしたのですが、横着はいけないですね～。
NavigationBar の戻るボタンをタップしたら、特定の VC に戻りたい！


# TL;DR

途中の VC をスタックから外せばいい。

# 本編

例えばこんなこと、考えたことありませんか？まず画面 A から Push 遷移で画面 B へ、次に画面 B から同じく Push 遷移で画面 C へ遷移しました。このあと、画面 C から戻る操作で B を飛ばして直接画面 A に戻りたい！よくあるのは、例えば画面 B が二段階認証の画面の時、この場合画面 C から戻る場合二段階認証の画面に戻るのはナンセンスなので、その画面を飛ばして前の画面に戻りたい時ですね。

## 【効果なし】 `backBarButtonItem` をカスタマイズ

まず考えられるのは NavigationBar の戻るボタンをカスタマイズし、このボタンがタップされたら、自分で `navigationController?.popToViewController` を実行する方法ですね。しかし残念ながら、NavigationBar の戻るボタンは `leftBarButtonItem` ではなく `backBarButtonItem` です。このボタンの動作をカスタマイズするのはできないのです。

あれ？違うよ？`navigationItem.backBarButtonItem` は `{ get set }` 対応だよ？

はい、そうです。しかし、ここで設定できるのは、次の画面に遷移された時の戻るボタンです。つまり、これは戻り先が自分の時の設定です。さらに `backBarButtonItem` に `action` を設定しても、UIKit には無視されます。ここで設定できるのはあくまで `backBarButtonItem` の表示だけです。

ちなみに `UINavigationControllerDelegate` に適合し、`navigationController?.delegate` を自分自身に設定することで戻るボタンの動作のカスタマイズが可能との記事も散見しますが、アレは嘘です。できません。そもそも `UINavigationControllerDelegate` は遷移先のカスタマイズの責務まで持ちません。

## 【イマイチ】 `leftBarButtonItem` を設定

戻るボタンのカスタマイズができないなら、次に考えるのは、`leftBarButtonItem` で置き換える方法です。確かに、これなら `popToViewController` が実行可能なので、特定の VC に戻れます。しかし、デメリットも二つあります。

一つ目は戻るボタン特有の `<` マークが表示されないことです。これは UIKit 共通のマークですので、これをなくすと一貫性がなくなり、特に iOS のヘビーユーザに違和感を覚えられることになります。もちろん無理やり独自で `<` 画像を作って表示させてあげることも可能は可能ですが、やはり純正の `<` マークと比べて何か違和感があります。

もう一つは左端からのエッジスワイプによる戻る操作ができなくなることです。これも UIKit 共通のジェスチャー操作であり、特に大画面の iPhone が主流になった今日では必要不可欠と言っても過言ではない仕様の一つです。これがなくなったら、前の画面に戻りたいときは非常に押しづらい左上のバックボタンを押すしかなくなります。もちろんこれも一応独自でエッジスワイプジェスチャーを実装してあげることも可能は可能ですが、やはり面倒くさいです。

そして上記のデメリット以外に、設計によっては画面 A のタイトルを画面 C の `leftBarButtonItem` で設定しないといけないので、微妙に Fat になったりするのも気持ち悪いですね。

## 【オススメ】遷移後に飛ばしたい画面をスタックから削除

実はアプローチを変えてみれば、これは意外と非常に簡単に解決できる方法があります：飛ばしたい画面をそもそも `navigationController?.viewControllers` 配列から削除しちゃえばいいのです。そもそもの話、画面 B を戻るときに飛ばしたいってことは、画面 B はもう要らないってことになります。でしたら、もう画面 C に遷移したら、一つ前の画面 B を削除しちゃえば、NavigationBar が勝手に画面 A に戻すように設定してくれますので、`<` や画面名の表示とかエッジスワイプのジェスチャー設定とかの面倒な作業は一切やらなくていいので、UIKit の素直な実装で動いてくれるからとても気持ちいいです。

```swift:ViewControllerB
    navigationController?.pushViewController(vc, animated: true) // 次の画面 C への Push 遷移
    navigationController?.viewControllers.removeAll(where: { $0 === self }) // その直後に自身を NavigationController の viewControllers スタックから削除
```

![screen0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63998/db082e82-72a4-704c-323d-d90efa0f4c8d.gif)

もちろんこれが完璧と言うわけでもありません、一つだけ細かい問題があります：それは画面 C に遷移した直後に、一瞬だけ画面 B の名前が戻るボタンの場所に表示されることです。表示は一瞬だけで、遷移が終わったら画面 B はスタックから消えるので、画面 A の名前に変わります。

## 【オルタネイティブ】そもそも Push 遷移しない

ちなみに、アップルの公式アプリでも似たような処理があります、それは設定アプリからパスコードを設定する画面に入るところです。この画面も、パスコードの設定画面に入る前に一回認証処理が挟まり、戻るとき当然ながらその認証画面には行かないで設定一覧画面に戻ります。ではアップルがどうしてるかと言うと、認証画面は Push 遷移ではなく Modal 遷移です、そして認証が通ったら裏でパスコード画面に Push 遷移済みの状態にした上で認証画面を Dismiss します。

このような処理も、見た目として特に違和感なく自然に見えますが、ただしそもそもの話、設定一覧画面から表示されている「遷移先」が「パスコード設定」であり、「認証」ではありません。だからこそ認証画面を Modal 遷移しても特に違和感を覚えません。ユーザは「パスコード設定画面」を「Push 遷移」で期待しているからです。しかし逆に遷移先を「画面 B」と記載しながら Modal 遷移すると、違和感を覚えるでしょう。

# 後書き

難しそうな機能でも、ときにはちょっとアプローチを変えるだけで、すんなり解決することもあります。
gRPC の基本のメモ

２年前に書いたgRPCに関するメモをpublishしておく。


## Basic Concepts

- `gRPC` is a rpc(protocol) framework.
- `Protocol Buffer` is serialization mechanism supported by
    - `IDL(Interface Definition Language)` to write `.proto file`(scheme file) with
    - `protoc(protocol buffer compiler)` which can autogenerate client code & server code
    - Libraries which is used with afformentioned autogenerated scripts




## Steps to use gRPC

1. Write `.proto file`
    - with IDL 
2. Autogenerate client code & server code with `$grpc-gen-grpc` (, which is `protoc` command for ruby). This creates 2 files, which are 
    - `xxx_pb.rb`(`Request class` and `Response class` are defined)
    - `xxx_services_pb.rb`(`Service class` and `Stub class` are defined)
3. Implement `server script` (which uses `Service class)` & `client script (which uses Stub class)` and boot them.


## Technologies behind gRPC

### Protocol Buffers

- As is mentinoned, this is composed of `lang + compiler + libraries`.
- For ruby, compiler command is `protoc-gen-grpc` which provides:
    - plugin to autogenerate message class
    - plugin to autogenerate service class
- Its binary format is compact and quickly (de)serializable.

### HTTP/2

- Stems from SPDY from Google. Transport is totally updated from HTTP/1.1
- Main functionalities are:
    - `Multiplexing`(多重化) which enables parallel responding (by multiple streams).
    - `Header Compression`(ヘッダ圧縮) by HPACK. (Originally in HTTP/1, header was 500-800 bytes)



## References

- https://www.wantedly.com/companies/wantedly/post_articles/219429
Homebrew >=2.6.1 で brew update に失敗したメモ(GitHub からの shallow clone 禁止)

## 環境

* macOS Catalina 10.15.7
* Homebrew 2.6.2

## 何が起きたか

`brew update` しようとしたら以下のエラーが出ました。

```zsh
% brew upgrade
Error: homebrew-core is a shallow clone. To `brew update` first run:
  git -C "/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core" fetch --unshallow
This restriction has been made on GitHub's request because updating shallow
clones is an extremely expensive operation due to the tree layout and traffic of
Homebrew/homebrew-core. We don't do this for you automatically to avoid
repeatedly performing an expensive unshallow operation in CI systems (which
should instead be fixed to not use shallow clones). Sorry for the inconvenience!

```

## 原因と解消方法

GitHub から Homebrew に shallow clone を控えるように リクエストがあったようです。
そのため、full clone する設定が必要なように Homebrew が対応しました。

> Also, as mentioned in Homebrew/brew#9383, this change was made due to a direct request from GitHub to reduce our shallow clone usage.

https://github.com/Homebrew/discussions/discussions/226#discussioncomment-147710

エラーメッセージに書かれている通りに、以下のコマンドを実行。
自分の場合、`homebrew-core` を解決した後に `homebrew-cask` でも同じことで怒られたのでそちらも対応しました。

```zsh
# homebrew-core
git -C "/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core" fetch --unshallow

# homebrew-cask
git -C "/usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask" fetch --unshallow
```

## 参考リンク

この事象についてのディスカッション
https://github.com/Homebrew/discussions/discussions/226
この対応が入った PR
https://github.com/Homebrew/brew/pull/9383
NUCで始めるVMware Tanzu - vSphere with Tanzu紹介

さて、本日はいよいよ本命、[vSphere with Tanzu](https://docs.vmware.com/jp/VMware-vSphere/7.0/vmware-vsphere-with-tanzu/GUID-C163490C-BE03-4DFE-8A03-5316D3245765.html)を紹介したいと思います！
[Day12](https://qiita.com/hirosat/items/fbb906ecbfccede2e226)では、マルチクラウド向けにTKGという製品があることを紹介しましたが、どういう違いがあるのか、みていきましょう。

#vSphere with Tanzu概要
まず最初の大きな特徴として、vSphere 7の新機能の1つとしてリリースされ、vSphereの機能の一部として取り込まれていることが挙げられます。(なお、[発表当時はvSphere with Kubernetesという名称](https://blogs.vmware.com/vsphere/2020/03/vsphere-7-features.html)でしたが、[vSphere 7 Update 1 リリース時に名称変更](https://blogs.vmware.com/vsphere/2020/10/vsphere-with-tanzu-the-fastest-way-to-deliver-kubernetes-on-vsphere.html)となりました。)

これが今までのvSphere6.x までの世界だとすると・・・、
![vsphere6](https://user-images.githubusercontent.com/3906298/102367478-2c73e280-3ffd-11eb-8839-765742519679.png)
vSphere 7 からは、このように拡張されました！
![vsphere7](https://user-images.githubusercontent.com/3906298/102367539-3dbcef00-3ffd-11eb-8331-2fedb031409c.png)
VMwareが、いかにKubernetesとコンテナに力を入れてきているのが分かるかと思います。
ここからは、vSphere with TanzuがどのようにvSphereと統合され、どのようにTKGと違うのか、解説していきます。

#Tanzu Kubernetes Grid Service
[Day12](https://qiita.com/hirosat/items/fbb906ecbfccede2e226)で紹介した、マルチクラウドKubernetesクラスタ基盤の導入ソリューションとしてのTKGに相当するものですが、Serviceと名前が付く分、より高度なものとなっています。今後は呼び分けのため、**TKGS**と呼んでいきます。
### vSphere UIとの統合
TKGでは、自分でTKG CLIのバイナリをダウンロードしてきて、インストーラを立ち上げるといった作業が必要でした。TKGSでは、vCenterのメニューに追加された「ワークロード管理」から、インストールを進める形となります。有効化作業が完了すると、各TKCを管理できるようになります。
さらにTKCが配置されると、KubernetesクラスタやPodのインベントリ、Kubernetesのイベント情報などがvCenter上から確認できます。
### Supervisor Cluster
これは要するに、TKGで出てきた「Managementクラスタ」のことです。ただし、Managementクラスタよりも多機能で、TKCのライフサイクル管理に加え、「vSphere Podの作成（要NSX-T）」、「Supervisor Namespaceの管理」、「Cloud Native Storage(CNS)との統合」、「コンテンツライブラリとの連携」といった機能を持っています。
### Supervisor Namespace
Supervisor Clusterから生成されたNamespaceのことをそう呼びます。実はこれ、ただのK8sのnamespaceではなく、vSphereの**リソースプール**とマッピングしており、この中で、TKCやvSphere PodのVMを管理していきます。そのため、以下のようなリソース管理が可能となります。

- ユーザ管理：vSphere SSOで認証されたユーザに対し、Supervisor Namespace内の権限（Can view/Can edit）を割り当て
- CPU/Memリソース：コンテナに対するリソース予約・制限や、Supervisor Namespace内の利用上限を設定可能
- ストレージリソース：ストレージ容量やポリシーの管理
- vSphere組み込みのHarborの有効化

### vSphere SSO
TKGでは、DEXやGangwayといったユーザ認証ツールを自分で導入することで、K8sクラスタ内でのユーザ管理を行うことができました。TKGSでは、vSphere SSOと統合されているため、K8sとのID連携が手軽に利用可能となっています。
### 組み込みHarbor
Supervisor namespaceに対してHarborを有効化すると、vSphere組み込みのHarborが利用可能となります。自分でHarborを用意する必要がないのと、vSphere SSOとの連携が、特徴となっております。（ただし、2020年12月17日時点の制約として、このHarborはvSphere Podのみに対して利用可能であり、TKCには使えません。）
### vSphere Pod
1Pod(複数コンテナ)のみが起動する小さなVMを提供するサービスです。NSX-Tと統合されているため、TKCと比べて隔離度が高いセキュアなコンテナを提供します。他のPodと連携する要件がなくて、リソースを節約したい時などに向いています。
### Cloud Native Storage
K8sのPV（Persistent Volume）のプロビジョニングとライフサイクル管理を行う機能です。vCenter Serverを介して、First Class Disk(FCD)と呼ばれる名前付き仮想ディスクを自動で割り当ててくれます。

以上で紹介は終わりです。
こうやって挙げていくと、結構TKGとの差異があるもんですね・・:grinning:
なお、K8sクラスタに関しては、TKGとTKGS共通で、TKCを利用する形となります。
まとめると、vSphereとの統合により、よりKubernetesのマネージドサービスらしいサービスになっています！

それでは明日からは、vSphere with Tanzuを触っていきます！
cnt命令（popcount）

# はじめに
 - この記事は[ひとりNEONアドベントカレンダー2020](https://qiita.com/advent-calendar/2020/neon-only) 17日目の記事です
 - [昨日は`reinterpret`命令](https://qiita.com/tomoaki_teshima/private/63769add9735e09f1737)という、イマイチ何が嬉しいのかわからない命令を紹介した
 - 今日は`popcount`命令を紹介する

![popcount.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12162/2fa0fe7d-510a-924f-8bbb-553c067accec.png)

# `popcount`とは
 - [特徴量空間の距離](https://qiita.com/tomoaki_teshima/items/00ab8036195b445d1dde)、[ごちうサーチ ](https://www.slideshare.net/ksasao/ss-72025009)、[ハミング距離の計算](http://wosugi.hatenablog.com/entry/20131208/1386514756)など、様々な場面で、あるベクトル内の2進数表記での`1`の数を数えたくなることがある
 - 詳細は割愛するが、`popcount`命令を使うことで、高速に`1`の数を数えることができ、幸せになれる
 - 以下に、`popcount`の具体例をいくつか紹介

| 入力 | 16進数表記 | 2進数表記 | `popcount`の結果 |
|--:|:-:|:-:|:-:|
| 1  |0x0001 | b0000000000000001| 1 |
| 15 |0x000f | b0000000000001111| 4 |
| 16 |0x0010 | b0000000000010000| 1 |
| 100|0x0064 | b0000000001100100| 3 |
| 500|0x01f4 | b0000000111110100| 6 |
|1000|0x03e8 | b0000001111101000| 6 |

# `cnt`命令
 - [公式リファレンス](https://developer.arm.com/architectures/instruction-sets/simd-isas/neon/intrinsics?search=population)を見てみると、`cnt`命令はレジスタ幅の違いを含んでも以下の6種類しか存在しない

```cpp
vcnt_p8
vcnt_s8
vcnt_u8
vcntq_p8
vcntq_s8
vcntq_u8
```
 - 先程の図でも同じ状況である

![popcount.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12162/2fa0fe7d-510a-924f-8bbb-553c067accec.png)

 - GCCのヘッダ`arm_neon.h`を覗いても状況は同じである

```bash
$ grep ^vcnt /usr/lib/gcc/aarch64-linux-gnu/7.5.0/include/arm_neon.h
vcnt_p8 (poly8x8_t __a)
vcnt_s8 (int8x8_t __a)
vcnt_u8 (uint8x8_t __a)
vcntq_p8 (poly8x16_t __a)
vcntq_s8 (int8x16_t __a)
vcntq_u8 (uint8x16_t __a)
```

 - `p8`型は多項式型なので、ここでは割愛する
 - となると引数は`int8x8_t`、`uint8x8_t`、`int8x16_t`、`uint8x16_t`の4種類しかない
 - で、前述の処理を考えると、符号ありだろうと符号なしだろうと、bitの数を数えることには変わりないので、結果として、`cnt`命令は1種類しかないことになる
 - では、例えば`int32x4_t`型の`popcount`処理はどうすれば良いのか？


# そこで`reinterpret`命令ですよ
 - と、ここで[昨日紹介した、`reineterpret`命令](https://qiita.com/tomoaki_teshima/private/63769add9735e09f1737)が効いてくる
 - `int32x4_t`型の`popcount`のためには以下のようなコードを書く

```cpp
    int32_t src[] = {1, 15, 100, 500};
    int32x4_t vsrc = vld1q_s32(src);
    int32x4_t vdst  = vpaddlq_s16(vpaddlq_s8(vcntq_s8(vreinterpretq_s8_s32(vsrc))));
```
 - 演算結果

```
0:1
1:4
2:3
3:6
```
 - 前述の表と同じ結果が得られている
 - [4年前の記事](https://qiita.com/tomoaki_teshima/items/00ab8036195b445d1dde)でも触れているが、下図のように、NEONでは、8bitごとの`popcount`を求める命令しか存在せず、32bitごとの結果を得るためにはレーン内で水平加算が必要となる

![popcount.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12162/b3f1122d-0412-0178-f51a-3f19362476d6.png)

 - この様にbit単位で独立している命令は、入力の型がなんだろうと中身の処理は同じ、という命令が存在する
 - そのため、`reinterpret`命令でただのbitの集合と解釈してやれば、1つの命令を使い回すだけで演算が済む
 - `8bit`より細かくは分けられないので、`popcount`は8bit単位の結果を返す
    - 32bbitごとに返しちゃうと、それ以上細かい型に適用できないから、最大公約数的な`int8x16_t`を返す`cntq`命令1種類だけ実装されている（のだと思う）

# おわりに
 - 今日は2進数表記でベクトル内の`1`の数を数える`popcount`命令を紹介した
 - 明日も手島の執筆の予定で、固定小数点演算用命令を紹介する予定
#5 Ruby始めました 「たのしいRuby 第６版」第２部基礎を学ぶ 五章　条件判断 ハノイ作成


本日から条件判断＝プログラミングの要域に入ります。
一通り、教本通りきましたので、若干独自要素を加えて、プログラミング練習にハノイを作ってみましょうｗ
アルゴリズムとは条件判断の教科書みたいなものです。あのアルゴリズムアプリ通りに条件判断を作っていきます。

オブジェクトを条件判断のみでプリントするという流れで一つのプログラムを作ってみます。
ごくごく僅かな命令文だけで構成したシンプルな構成でいきます。

手順は七段階
https://daeudaeu.com/hanoi/
http://www13.plala.or.jp/kymats/study/C++/Hanoi/Hanoi.html

###再帰処理の学習

答えの順番が分かっているので、ただのオブジェクト移動だけで良いのですがｗ
再帰処理で、条件判断を入れてダラダラ書かず、戻ってまた処理させるみたいなループ的な感じにしたいと思います。

https://wa3.i-3-i.info/word14899.html


頭＃＃
白＃＃＃
汁＃＃＃＃

で行きたいと思いますｗ

まずはイメージを大切にするためにも、まずは<S>日本語で組み立てます。


一気に思い付き、思い込みで書いても無駄なだけ、コンピューターは動きませんｗ

動作を一つ一つ確認しながら、部品を動作させて構築していくのが良いと思います。
歯車を一個一個作って、機械を動かす方法と同じです。絵に描いた餅では動きませんｗ
部品を作って動かして、拡張して、動かしての繰り返しで構築して行きます。

<h4>料理、機械と全く同じです。<br>
材料と仕込み・部品動作チェック、調理・順番構築、拡張構築と順番に確実に動作させて行くこと重要です。</h4>

##・使うオブジェクト：材料（オブジェクト、条件判断ループなど）

1=頭＃＃
2=白＃＃＃
3=汁＃＃＃＃

A=なべ
B=調理
C=丼

if
else
while

```Ruby
0 = [_]
1 = ["頭＃＃"]
2 = ["白＃＃＃"]
3 = ["汁＃＃＃＃"]

A=["なべ"]
B=["調理"]
C=["丼"]

hanoi = ["_","頭＃＃", "白＃＃＃", "汁＃＃＃＃"]

p hanoi [0]
p hanoi [1]
p hanoi [2]
p hanoi [3]
```

イメージはこんな感じの材料が完成しました。

##仕込み・ステージと部品動作確認チェック

次はステージを用意して上の材料を移動動作させる部品を作ります。


```Ruby
```

##調理・設計、組み立て拡張


・順番
Basicと同じなので上から順番に組んでいくだけです。



```Ruby
#ハノイの塔
#条件判断プログラム練習




```



#コメント
アルゴリズムです。
ここが肝の場所ですからじっくりと学んでいきたいと思いますが、ここだけをちゃんとやると多分一生遊べる場所ですが、まあサラッとｗ

アルゴリズムは昔の本では小難しいことばっかり書いてあって、さっぱり解らなかったですがｗ今は本当に素晴らしくわかりやすものがたくさんあります。
コンピューターとアルゴリズム書籍で一番最初に習うのがハノイの塔ですが、ハノイを応用したプログラムの使い方、プログラムの書き方は書いてませんｗ昔のアルゴリズムとはそういうものでしたｗなのでさっぱり分かりませんでしたｗどう使うの？みたいなｗ

###ハノイの塔(パズル)
ハノイはアルゴリズム学習で最初に学びますが、それは同時にプログラミング学習の基礎学習になります。基礎練習になりますので、ハノイを学習がてら作ってみましょうというのが答えですねｗ

プログラム教育のツールとして簡単なゲームを作るのがハノイの塔の狙いで、別にこれを使って凄いことが出来るみたいなものでは無いのでしょうねｗ

###アルゴリズムアプリ

アルゴリズム図鑑　370円
https://apps.apple.com/jp/app/%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E5%9B%B3%E9%91%91/id1047532631

これを見ているだけで楽しいｗアルゴリズムの本なんかより全然理解度がありますｗ
FortranやっていたNECに行っていたおじさんが言ってましたが、凄腕はアルゴリズムを熟知しているので、処理が速いとのことでした。
プログラマーの腕はやっぱりアルゴリズムだそうです。まあそうですよね。
DLLやEXEのアーキテクチャを特定する方法

目的
-----------
ビルドしたDLLのアーキテクチャを知りたい。

- 過去にビルドしたものがx86 or Any CPU(MSIL) or x64なのかを生成物から調べたい。
- .NET 限定。

詳細情報を取得
-----------

[How can I determine if a .NET assembly was built for x86 or x64?](https://stackoverflow.com/questions/270531/how-can-i-determine-if-a-net-assembly-was-built-for-x86-or-x64) より。
現在のディレクトリにある`TARGET.dll` の情報

```powershell:PowerShell
[reflection.assemblyname]::GetAssemblyName("${pwd}\TARGET.dll") | fl
```

一覧でより簡単に見てみる
----------

現在のディレクトリにあるdllやexe等をかたっぱしから一覧にして、GUIを表示する。powershellすご。
※exeやdll以外の警告がDELLので注意、TODO:フィルタ方法教えてほしい
※TODO:ProcessorArchitectureの説明は、stackoverflow参照

```powershell:PowerShell
Get-ChildItem -Name -File `
| ForEach-Object -Process {[reflection.assemblyname]::GetAssemblyName("${pwd}\" + $_) } `
| Select-Object -Property Name, Version, ProcessorArchitecture `
| Out-GridView
```

```
※上記コマンド中の`はエスケープを表します。そのまま投入しても動作します。
```

参考文献
-----------------

### .NET 関連

- [How can I determine if a .NET assembly was built for x86 or x64?](https://stackoverflow.com/questions/270531/how-can-i-determine-if-a-net-assembly-was-built-for-x86-or-x64) - stackoverflow (英語)
- [【.NET】 私、 [対象プラットフォーム] は [Any CPU] でって言ったよね!](https://qiita.com/libraplanet/items/4fd2b2e86a198f92569a) - Qiita - ビルドプラットフォームのまとめ、気が向いたらWoW回避の処理でも公開しようかな
- [.NETにおける64ビットプロセスと32ビットプロセスについて](https://qiita.com/mima_ita/items/57d7c1101543e214b1d6) - Qiita
- [How to find if a native DLL file is compiled as x64 or x86?](https://stackoverflow.com/questions/480696/how-to-find-if-a-native-dll-file-is-compiled-as-x64-or-x86) - stackoverflow - ネイティブDLL等の判断はこちら


### Powershell関連

- [Out-GridView](https://forsenergy.com/ja-jp/windowspowershellhelp/html/8f657905-0eaf-4d10-ae93-3217eb97e0e3.htm) - forsenergy.com
- [PowerShell の Get-ChildItem の -Filter の挙動](https://qiita.com/ayweak/items/5b8a9a4d4bfa38f7697b) - Qiita - dllとexeで絞りたかったが、やり方がいまいちつかめなかった
- [ファイルの一覧を表示する（Get-ChildItem）](http://suyamasoft.blue.coocan.jp/PowerShell/Tips/File/GetChildItem/index.html) - suyamasoft.blue.coocan.jp
- [ソースコードの式の途中で改行する方法](https://bayashita.com/p/entry/show/87) - バヤシタ[PowerShell]
[Java]HttpServletクラスの継承と構造

#目次
[1. はじめに](#1-はじめに) 
[2. Servlet(サーブレット)とは？](#2-servlet(サーブレット)とは？) 
[3. HttpServletクラスの継承](#3-httpservletクラスの継承)
[4. HttpServletクラスの構造](#4-httpservletクラスの構造)
[5. サンプル](#5-サンプル)
[6. さいごに](#6-さいごに)

##1. はじめに
本記事は以下の様な人向けに作成しました。
・「初めてJavaを使ってみよう」
・「Java使ってWebアプリを作ってみたいな」
・「サーブレットって言葉が出てきたけど・・・」
皆様の学習の何かのお役に立てば幸いです。

以下を参考に記述しています。
<b>引用サイト：[【Java】サーブレットの作成（基本的なコーディング）](https://works.forward-soft.co.jp/blog/detail/10046)</b>

##2. Servlet(サーブレット)とは？
Servlet(サーブレット)とは、サーバー上でウェブページやウェブアプリなどを動的に処理・生成したりするために、Javaで作成されたプログラムです。JavaEEの一機能でもあります。


##3. HttpServletクラスの継承
一般的にサーブレットを作成する場合は、javax.servlet.http.HttpServletクラスを継承します。

```servlet.java
import javax.servlet.http.HttpServlet;

public class SampleServlet extends HttpServlet {

}
```


Javaのライブラリで、サーブレットに必要な機能（HTTP通信に特化した機能）を実装したクラスが提供されているので、開発者はHttpServletを継承し、メソッドをオーバーライドすることで、 自分たちのサーブレットを作成することができます。


##4. HttpServletクラスの構造

HttpServletクラスの構造は以下の様に表現する。
![HttpServletクラスの構造図](https://forward-soft.co.jp/admin/assets/upload/blog/25a9bcc36a690d65b5b206bb53e595f7.png)

<b>・HttpServletクラスの主なメソッド</b>

<b>init()メソッド</b>
サーブレットの初期化処理を行うメソッド。
初期化パラメータの取得。
サーブレットのインスタンス生成時に最初に呼び出される。

<b>service ()メソッド</b>
リクエストを受け付け、レスポンスを返すメソッド。
クライアントからリクエストのたびに実行される。
HttpServletクラスのservice()メソッドは、クライアントのリクエストの種類に応じて、以下の分岐をおこなう。
 ・http <b>GET</b>  method → doGet ()メソッドが実行される
 ・http <b>POST</b> method → doPost ()メソッドが実行される

<b>doGet()メソッド</b>
http GET methodでリクエストを受け付け、レスポンスを返すメソッド。
 service()メソッドから分岐して実行される。

<b>doPost()メソッド</b>
http POST methodでリクエストを受け付け、レスポンスを 返すメソッド。
 service()メソッドから分岐して実行される。

<b>destroy()メソッド</b>
サーブレットの終了時に必要な処理を記述するメソッド。
サーブレットのインスタンスが破棄されるときに呼び出される。


##5. サンプル
doGetメソッドを使用したサンプルです。
HttpServletを継承したクラスを作成し、doGet()メソッドをオーバーライドします。

```doGetSample.java
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        // 処理を記入
        // 例）request の内容を解析し使用する
        // ・・・
        // 例）response に返却したい内容を入れる
    }
}
```

上記の処理記入のところを自由にコーディングし使用してみてください。



##6. さいごに
本記事はServlet（サーブレット）のほんの入り口部分を簡単に紹介したものです。
詳しく調べるともっと深い知識や実装方法がありますので、お試しください。
この記事を作成するにあたり、参考にさせて頂いた記事を以下に記載します。
以上、何かしら皆様のお役に立てば幸いです。

<b>参考記事：[サーブレットの作成（基本的なコーディング）](https://works.forward-soft.co.jp/blog/detail/10046)</b>
nilガードについて理解してみた

Rubyの勉強をしているなかでnilガードというものがでてきたので勉強の記録用として記事にしてみました。

認識違いがあった場合などは是非ご指摘下さい。

#nilガード
Rubyにはnilになってしまう状態を防ぐためにnilガードという書き方が存在します。簡単に説明すると仮に変数がnilだった場合には値を入れるということです。

ということで分かりやすく下記に例を記載したいと思います。

```ruby
# 例1
> number = nil
> number ||= 6
> puts number
=> 6

# 例2
> number = 5
> number ||= 6
> puts number
=> 5

```
初めにnilガードを実装するにパイプ演算子||と=を組み合わせます。
例1ではnumberがnilなら6を代入しています。しかし例2ではnumberがnilではないので代入が行われていません。ここでnilガードというものはnilかfalseでないと代入が行われないと分かりました。

#まとめ
nilガードは変数にnilが入っているかもしれない状況でnilの代わりに何らかのデフォルト値を入れておきたいという場面で、とても便利に利用できます。

#参考
・ 現場で使える Ruby on rails 5 速習実践ガイド
・ https://www.sejuku.net/blog/19044
Google PageSpeed Insights【Links do not have a discernible name】の対処法

Google PageSpeed Insightsにて、Accessibilityの`Links do not have a discernible name`警告の対処法です。

![Links-do-not-have-a-discernible-name.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559347/1076219c-659a-5e55-512a-461a984978a0.jpeg)

## aタグ（link）にテキストが無いのが原因
`Links do not have a discernible name`の警告は、aタグにテキストが無いのが原因になります。

```html
<a href="demo"><i class="fab fa-twitter"></a>
```
上記のように、リンクにアイコンのみ使用している場合に発生することが多いと思います。

```html
<a href="demo"><i class="fab fa-twitter">Twitterへのリンク</a>
```
テキストを記述すれば、警告を消すことができます。

##  aria-labelを指定することでも解消できる
アイコンのみのリンクで、テキストを使いたくない時もあるかと思います。
その場合は、aria-label属性を指定します。

```html
<a href="demo" aria-label="Twitterへのリンク"><i class="fab fa-twitter"></a>
```
上記でも、`Links do not have a discernible name`の警告を解消することができます。

## 参考

[https://web.dev/link-name/?utm_source=lighthouse&utm_medium=devtools#how-to-add-accessible-names-to-links](https://web.dev/link-name/?utm_source=lighthouse&utm_medium=devtools#how-to-add-accessible-names-to-links)